Skip to content

Commit 61073c9

Browse files
authored
Merge pull request #105 from AssemblyAI/niels/user-agent
Add AAI UserAgent
2 parents 47e4a41 + e3f65d6 commit 61073c9

File tree

9 files changed

+256
-7
lines changed

9 files changed

+256
-7
lines changed

.fernignore

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ src/main/java/com/assemblyai/api/AssemblyAI.java
33
src/main/java/com/assemblyai/api/PollingTranscriptsClient.java
44
src/main/java/com/assemblyai/api/Transcriber.java
55
src/main/java/com/assemblyai/api/RealtimeTranscriber.java
6+
src/main/java/com/assemblyai/api/core/Constants.java
67

78
# Ignore SpeechModel to manually mark conformer-2 as deprecated
89
src/main/java/com/assemblyai/api/resources/transcripts/types/SpeechModel.java
@@ -19,9 +20,17 @@ src/main/java/com/assemblyai/api/resources/realtime/types/SessionTerminated.java
1920
src/main/java/com/assemblyai/api/AssemblyAIBuilder.java
2021
src/main/java/com/assemblyai/api/core/ClientOptions.java
2122

23+
# Ignore UserAgent
24+
src/main/java/com/assemblyai/api/core/UserAgent.java
25+
src/main/java/com/assemblyai/api/core/UserAgentInterceptor.java
26+
src/main/java/com/assemblyai/api/core/UserAgentItem.java
27+
2228
sample-app/src/main/java/sample/App.java
2329
sample-app/src/main/resources/
2430

31+
# Ignore tests
32+
src/test
33+
2534
LICENSE
2635
README.md
2736

src/main/java/com/assemblyai/api/AssemblyAIBuilder.java

+25-3
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@
55

66
import com.assemblyai.api.core.ClientOptions;
77
import com.assemblyai.api.core.Environment;
8+
import com.assemblyai.api.core.UserAgent;
89

910
public final class AssemblyAIBuilder {
1011
private ClientOptions.Builder clientOptionsBuilder = ClientOptions.builder();
1112
private ClientOptions.Builder lemurClientOptionsBuilder = ClientOptions.builder();
1213

1314
private String apiKey = null;
15+
private UserAgent userAgent = UserAgent.getDefault();
1416

1517
private Environment environment = Environment.DEFAULT;
1618

1719
/**
18-
* Sets apiKey
20+
* Sets API key
1921
*/
2022
public AssemblyAIBuilder apiKey(String apiKey) {
2123
this.apiKey = apiKey;
@@ -27,6 +29,18 @@ public AssemblyAIBuilder environment(Environment environment) {
2729
return this;
2830
}
2931

32+
/**
33+
* Merges AssemblyAI user agent with the default AssemblyAI user agent.
34+
* If null, sets the AssemblyAI user agent to null.
35+
*
36+
* @param userAgent The AssemblyAI user agent
37+
* @return AssemblyAIBuilder
38+
*/
39+
public AssemblyAIBuilder userAgent(UserAgent userAgent) {
40+
this.userAgent = userAgent;
41+
return this;
42+
}
43+
3044
public AssemblyAIBuilder url(String url) {
3145
this.environment = Environment.custom(url);
3246
return this;
@@ -36,10 +50,18 @@ public AssemblyAI build() {
3650
if (apiKey == null) {
3751
throw new RuntimeException("Please provide apiKey");
3852
}
53+
3954
this.clientOptionsBuilder.addHeader("Authorization", this.apiKey);
40-
clientOptionsBuilder.environment(this.environment);
55+
clientOptionsBuilder
56+
.environment(this.environment)
57+
.userAgent(userAgent);
58+
4159
this.lemurClientOptionsBuilder.addHeader("Authorization", this.apiKey);
42-
lemurClientOptionsBuilder.environment(this.environment).disableTimeouts();
60+
lemurClientOptionsBuilder
61+
.environment(this.environment)
62+
.userAgent(userAgent)
63+
.disableTimeouts();
64+
4365
return new AssemblyAI(clientOptionsBuilder.build(), lemurClientOptionsBuilder.build());
4466
}
4567
}

src/main/java/com/assemblyai/api/core/ClientOptions.java

+36-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
import okhttp3.OkHttpClient;
1212

13+
import static com.assemblyai.api.core.Constants.SDK_VERSION;
14+
1315
public final class ClientOptions {
1416
private final Environment environment;
1517

@@ -30,7 +32,7 @@ private ClientOptions(
3032
this.headers.putAll(new HashMap<String, String>() {
3133
{
3234
put("X-Fern-SDK-Name", "com.assemblyai.fern:api-sdk");
33-
put("X-Fern-SDK-Version", "1.1.3");
35+
put("X-Fern-SDK-Version", SDK_VERSION);
3436
put("X-Fern-Language", "JAVA");
3537
}
3638
});
@@ -77,6 +79,7 @@ public static Builder builder() {
7779

7880
public static final class Builder {
7981
private Environment environment;
82+
private UserAgent userAgent = UserAgent.getDefault();
8083

8184
private final Map<String, String> headers = new HashMap<>();
8285

@@ -100,7 +103,30 @@ public Builder addHeader(String key, Supplier<String> value) {
100103
}
101104

102105
/**
103-
* This is a temporary measure ot disable timeouts for LeMUR client.
106+
* Merges AssemblyAI user agent with the default AssemblyAI user agent.
107+
* If null, sets the AssemblyAI user agent to null.
108+
*
109+
* @param userAgent The AssemblyAI user agent
110+
* @return ClientOptionsBuilder
111+
*/
112+
public Builder userAgent(UserAgent userAgent) {
113+
if (userAgent == null) {
114+
this.userAgent = null;
115+
return this;
116+
}
117+
118+
// if current and incoming user agent is default user agent, no-op
119+
if(userAgent == UserAgent.getDefault() && this.userAgent == UserAgent.getDefault()) {
120+
return this;
121+
}
122+
123+
// create a new user agent that merges existing and incoming user agent
124+
this.userAgent = new UserAgent(this.userAgent, userAgent);
125+
return this;
126+
}
127+
128+
/**
129+
* This is a temporary measure to disable timeouts for LeMUR client.
104130
* Don't use this method.
105131
*
106132
* @return ClientOptionsBuilder
@@ -113,15 +139,21 @@ public Builder disableTimeouts() {
113139

114140
public ClientOptions build() {
115141
OkHttpClient.Builder okhttpClientBuilder = new OkHttpClient.Builder()
116-
.addInterceptor(new RetryInterceptor(3));
142+
.addInterceptor(new RetryInterceptor(3))
143+
.addInterceptor(new UserAgentInterceptor(this.userAgent));
117144
if (this.disableTimeouts) {
118145
okhttpClientBuilder
119146
.callTimeout(0, TimeUnit.SECONDS)
120147
.connectTimeout(0, TimeUnit.SECONDS)
121148
.writeTimeout(0, TimeUnit.SECONDS)
122149
.readTimeout(0, TimeUnit.SECONDS);
123150
}
124-
return new ClientOptions(environment, headers, headerSuppliers, okhttpClientBuilder.build());
151+
return new ClientOptions(
152+
environment,
153+
headers,
154+
headerSuppliers,
155+
okhttpClientBuilder.build()
156+
);
125157
}
126158
}
127159
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.assemblyai.api.core;
2+
3+
public class Constants {
4+
public static final String SDK_VERSION = "1.1.3";
5+
}

src/main/java/com/assemblyai/api/core/Environment.java

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public String getUrl() {
1616
return this.url;
1717
}
1818

19+
1920
public static Environment custom(String url) {
2021
return new Environment(url);
2122
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.assemblyai.api.core;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import java.util.stream.Collectors;
6+
7+
import static com.assemblyai.api.core.Constants.SDK_VERSION;
8+
9+
public class UserAgent {
10+
private static final UserAgent defaultUserAgent;
11+
12+
static {
13+
defaultUserAgent = createDefaultUserAgent();
14+
}
15+
16+
private final Map<String, UserAgentItem> userAgent;
17+
18+
public UserAgent(Map<String, UserAgentItem> userAgent) {
19+
this.userAgent = userAgent;
20+
}
21+
22+
public UserAgent(UserAgent a, UserAgent b) {
23+
this.userAgent = UserAgent.merge(a.userAgent, b.userAgent);
24+
}
25+
26+
public String toAssemblyAIUserAgentString() {
27+
StringBuilder sb = new StringBuilder();
28+
if (this.userAgent == null) {
29+
return sb.toString();
30+
}
31+
32+
sb.append(" AssemblyAI/1.0 (");
33+
sb.append(
34+
this.userAgent
35+
.entrySet()
36+
.stream()
37+
.map(entry -> String.format(
38+
"%s=%s/%s",
39+
entry.getKey(),
40+
entry.getValue().getName(),
41+
entry.getValue().getVersion()
42+
))
43+
.collect(Collectors.joining(" "))
44+
);
45+
sb.append(")");
46+
return sb.toString();
47+
}
48+
49+
public static UserAgent getDefault(){
50+
return defaultUserAgent;
51+
}
52+
53+
private static UserAgent createDefaultUserAgent() {
54+
HashMap<String, UserAgentItem> defaultUserAgent = new HashMap<>();
55+
defaultUserAgent.put(
56+
"sdk",
57+
new UserAgentItem(
58+
"Java",
59+
SDK_VERSION
60+
)
61+
);
62+
defaultUserAgent.put(
63+
"runtime_env",
64+
new UserAgentItem(
65+
System.getProperty("java.runtime.name"),
66+
System.getProperty("java.runtime.version")
67+
)
68+
);
69+
70+
return new UserAgent(defaultUserAgent);
71+
}
72+
73+
private UserAgent merge(UserAgent other) {
74+
return new UserAgent(UserAgent.merge(this.userAgent, other.userAgent));
75+
}
76+
77+
private static Map<String, UserAgentItem> merge(Map<String, UserAgentItem> a, Map<String, UserAgentItem> b) {
78+
Map<String, UserAgentItem> newUserAgent = new HashMap<>();
79+
80+
// user this user agent
81+
newUserAgent.putAll(a);
82+
// override with incoming user agent
83+
newUserAgent.putAll(b);
84+
85+
// remove all null values
86+
for (Map.Entry<String, UserAgentItem> entry : b.entrySet()) {
87+
if (entry.getValue() == null) {
88+
newUserAgent.remove(entry.getKey());
89+
}
90+
}
91+
return newUserAgent;
92+
}
93+
}
94+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.assemblyai.api.core;
2+
3+
import okhttp3.Interceptor;
4+
import okhttp3.Request;
5+
import okhttp3.Response;
6+
import org.jetbrains.annotations.NotNull;
7+
8+
import java.io.IOException;
9+
10+
public class UserAgentInterceptor implements Interceptor {
11+
private final UserAgent userAgent;
12+
13+
public UserAgentInterceptor(UserAgent userAgent) {
14+
this.userAgent = userAgent;
15+
}
16+
17+
@NotNull
18+
@Override
19+
public Response intercept(@NotNull Chain chain) throws IOException {
20+
if (userAgent == null) {
21+
return chain.proceed(chain.request());
22+
}
23+
24+
Request originalRequest = chain.request();
25+
String userAgentString = originalRequest.headers().get("User-Agent");
26+
if (userAgentString != null) {
27+
// if already contains AssemblyAI UA, skip
28+
if (userAgentString.contains("AssemblyAI/")) {
29+
return chain.proceed(chain.request());
30+
}
31+
} else {
32+
userAgentString = "";
33+
}
34+
35+
String assemblyAIUserAgentString = userAgent.toAssemblyAIUserAgentString();
36+
// if AAI UA null or empty, skip
37+
if (assemblyAIUserAgentString == null || assemblyAIUserAgentString.isEmpty()) {
38+
return chain.proceed(chain.request());
39+
}
40+
41+
userAgentString += " " + assemblyAIUserAgentString;
42+
Request requestWithUserAgent = originalRequest.newBuilder()
43+
.header("User-Agent", userAgentString)
44+
.build();
45+
return chain.proceed(requestWithUserAgent);
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.assemblyai.api.core;
2+
3+
public class UserAgentItem{
4+
private final String name;
5+
private final String version;
6+
7+
public UserAgentItem(String name, String version) {
8+
this.name = name;
9+
this.version = version;
10+
}
11+
12+
public String getName() {
13+
return name;
14+
}
15+
16+
public String getVersion() {
17+
return version;
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.assemblyai.api;
2+
3+
import com.assemblyai.api.core.UserAgent;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static com.assemblyai.api.core.Constants.SDK_VERSION;
7+
8+
public final class UserAgentTest {
9+
@Test
10+
public void ShouldCreateDefaultUserAgent() {
11+
UserAgent agent = UserAgent.getDefault();
12+
assert agent != null;
13+
String userAgentString = agent.toAssemblyAIUserAgentString();
14+
assert userAgentString != null;
15+
assert userAgentString.contains("AssemblyAI/1.0 (");
16+
assert userAgentString.endsWith(")");
17+
assert userAgentString.contains("sdk=Java/" + SDK_VERSION);
18+
assert userAgentString.contains("runtime_env=OpenJDK Runtime Environment/");
19+
}
20+
}

0 commit comments

Comments
 (0)