Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AAI UserAgent #105

Merged
merged 7 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .fernignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ src/main/java/com/assemblyai/api/resources/realtime/types/SessionTerminated.java
# Ignore ClientOptions to support custom timeouts for LeMUR.
src/main/java/com/assemblyai/api/AssemblyAIBuilder.java
src/main/java/com/assemblyai/api/core/ClientOptions.java
src/main/java/com/assemblyai/api/core/UserAgent.java
src/main/java/com/assemblyai/api/core/UserAgentInterceptor.java
src/main/java/com/assemblyai/api/core/UserAgentItem.java

sample-app/src/main/java/sample/App.java
sample-app/src/main/resources/

# Ignore tests
src/test

LICENSE
README.md

Expand Down
28 changes: 25 additions & 3 deletions src/main/java/com/assemblyai/api/AssemblyAIBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@

import com.assemblyai.api.core.ClientOptions;
import com.assemblyai.api.core.Environment;
import com.assemblyai.api.core.UserAgent;

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

private String apiKey = null;
private UserAgent userAgent = UserAgent.getDefault();

private Environment environment = Environment.DEFAULT;

/**
* Sets apiKey
* Sets API key
*/
public AssemblyAIBuilder apiKey(String apiKey) {
this.apiKey = apiKey;
Expand All @@ -27,6 +29,18 @@ public AssemblyAIBuilder environment(Environment environment) {
return this;
}

/**
* Merges AssemblyAI user agent with the default AssemblyAI user agent.
* If null, sets the AssemblyAI user agent to null.
*
* @param userAgent The AssemblyAI user agent
* @return AssemblyAIBuilder
*/
public AssemblyAIBuilder userAgent(UserAgent userAgent) {
this.userAgent = userAgent;
return this;
}

public AssemblyAIBuilder url(String url) {
this.environment = Environment.custom(url);
return this;
Expand All @@ -36,10 +50,18 @@ public AssemblyAI build() {
if (apiKey == null) {
throw new RuntimeException("Please provide apiKey");
}

this.clientOptionsBuilder.addHeader("Authorization", this.apiKey);
clientOptionsBuilder.environment(this.environment);
clientOptionsBuilder
.environment(this.environment)
.userAgent(userAgent);

this.lemurClientOptionsBuilder.addHeader("Authorization", this.apiKey);
lemurClientOptionsBuilder.environment(this.environment).disableTimeouts();
lemurClientOptionsBuilder
.environment(this.environment)
.userAgent(userAgent)
.disableTimeouts();

return new AssemblyAI(clientOptionsBuilder.build(), lemurClientOptionsBuilder.build());
}
}
40 changes: 36 additions & 4 deletions src/main/java/com/assemblyai/api/core/ClientOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

import okhttp3.OkHttpClient;

import static com.assemblyai.api.core.Constants.SDK_VERSION;

public final class ClientOptions {
private final Environment environment;

Expand All @@ -30,7 +32,7 @@ private ClientOptions(
this.headers.putAll(new HashMap<String, String>() {
{
put("X-Fern-SDK-Name", "com.assemblyai.fern:api-sdk");
put("X-Fern-SDK-Version", "1.1.3");
put("X-Fern-SDK-Version", SDK_VERSION);
put("X-Fern-Language", "JAVA");
}
});
Expand Down Expand Up @@ -77,6 +79,7 @@ public static Builder builder() {

public static final class Builder {
private Environment environment;
private UserAgent userAgent = UserAgent.getDefault();

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

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

/**
* This is a temporary measure ot disable timeouts for LeMUR client.
* Merges AssemblyAI user agent with the default AssemblyAI user agent.
* If null, sets the AssemblyAI user agent to null.
*
* @param userAgent The AssemblyAI user agent
* @return ClientOptionsBuilder
*/
public Builder userAgent(UserAgent userAgent) {
if (userAgent == null) {
this.userAgent = null;
return this;
}

// if current and incoming user agent is default user agent, no-op
if(userAgent == UserAgent.getDefault() && this.userAgent == UserAgent.getDefault()) {
return this;
}

// create a new user agent that merges existing and incoming user agent
this.userAgent = new UserAgent(this.userAgent, userAgent);
return this;
}

/**
* This is a temporary measure to disable timeouts for LeMUR client.
* Don't use this method.
*
* @return ClientOptionsBuilder
Expand All @@ -113,15 +139,21 @@ public Builder disableTimeouts() {

public ClientOptions build() {
OkHttpClient.Builder okhttpClientBuilder = new OkHttpClient.Builder()
.addInterceptor(new RetryInterceptor(3));
.addInterceptor(new RetryInterceptor(3))
.addInterceptor(new UserAgentInterceptor(this.userAgent));
if (this.disableTimeouts) {
okhttpClientBuilder
.callTimeout(0, TimeUnit.SECONDS)
.connectTimeout(0, TimeUnit.SECONDS)
.writeTimeout(0, TimeUnit.SECONDS)
.readTimeout(0, TimeUnit.SECONDS);
}
return new ClientOptions(environment, headers, headerSuppliers, okhttpClientBuilder.build());
return new ClientOptions(
environment,
headers,
headerSuppliers,
okhttpClientBuilder.build()
);
}
}
}
5 changes: 5 additions & 0 deletions src/main/java/com/assemblyai/api/core/Constants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.assemblyai.api.core;

public class Constants {
public static final String SDK_VERSION = "1.1.3";
}
1 change: 1 addition & 0 deletions src/main/java/com/assemblyai/api/core/Environment.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public String getUrl() {
return this.url;
}


public static Environment custom(String url) {
return new Environment(url);
}
Expand Down
94 changes: 94 additions & 0 deletions src/main/java/com/assemblyai/api/core/UserAgent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.assemblyai.api.core;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import static com.assemblyai.api.core.Constants.SDK_VERSION;

public class UserAgent {
private static final UserAgent defaultUserAgent;

static {
defaultUserAgent = createDefaultUserAgent();
}

private final Map<String, UserAgentItem> userAgent;

public UserAgent(Map<String, UserAgentItem> userAgent) {
this.userAgent = userAgent;
}

public UserAgent(UserAgent a, UserAgent b) {
this.userAgent = UserAgent.merge(a.userAgent, b.userAgent);
}

public String toAssemblyAIUserAgentString() {
StringBuilder sb = new StringBuilder();
if (this.userAgent == null) {
return sb.toString();
}

sb.append(" AssemblyAI/1.0 (");
sb.append(
this.userAgent
.entrySet()
.stream()
.map(entry -> String.format(
"%s=%s/%s",
entry.getKey(),
entry.getValue().getName(),
entry.getValue().getVersion()
))
.collect(Collectors.joining(" "))
);
sb.append(")");
return sb.toString();
}

public static UserAgent getDefault(){
return defaultUserAgent;
}

private static UserAgent createDefaultUserAgent() {
HashMap<String, UserAgentItem> defaultUserAgent = new HashMap<>();
defaultUserAgent.put(
"sdk",
new UserAgentItem(
"Java",
SDK_VERSION
)
);
defaultUserAgent.put(
"runtime_env",
new UserAgentItem(
System.getProperty("java.runtime.name"),
System.getProperty("java.runtime.version")
)
);

return new UserAgent(defaultUserAgent);
}

private UserAgent merge(UserAgent other) {
return new UserAgent(UserAgent.merge(this.userAgent, other.userAgent));
}

private static Map<String, UserAgentItem> merge(Map<String, UserAgentItem> a, Map<String, UserAgentItem> b) {
Map<String, UserAgentItem> newUserAgent = new HashMap<>();

// user this user agent
newUserAgent.putAll(a);
// override with incoming user agent
newUserAgent.putAll(b);

// remove all null values
for (Map.Entry<String, UserAgentItem> entry : b.entrySet()) {
if (entry.getValue() == null) {
newUserAgent.remove(entry.getKey());
}
}
return newUserAgent;
}
}

47 changes: 47 additions & 0 deletions src/main/java/com/assemblyai/api/core/UserAgentInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.assemblyai.api.core;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;

public class UserAgentInterceptor implements Interceptor {
private final UserAgent userAgent;

public UserAgentInterceptor(UserAgent userAgent) {
this.userAgent = userAgent;
}

@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
if (userAgent == null) {
return chain.proceed(chain.request());
}

Request originalRequest = chain.request();
String userAgentString = originalRequest.headers().get("User-Agent");
if (userAgentString != null) {
// if already contains AssemblyAI UA, skip
if (userAgentString.contains("AssemblyAI/")) {
return chain.proceed(chain.request());
}
} else {
userAgentString = "";
}

String assemblyAIUserAgentString = userAgent.toAssemblyAIUserAgentString();
// if AAI UA null or empty, skip
if (assemblyAIUserAgentString == null || assemblyAIUserAgentString.isEmpty()) {
return chain.proceed(chain.request());
}

userAgentString += " " + assemblyAIUserAgentString;
Request requestWithUserAgent = originalRequest.newBuilder()
.header("User-Agent", userAgentString)
.build();
return chain.proceed(requestWithUserAgent);
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/assemblyai/api/core/UserAgentItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.assemblyai.api.core;

public class UserAgentItem{
private final String name;
private final String version;

public UserAgentItem(String name, String version) {
this.name = name;
this.version = version;
}

public String getName() {
return name;
}

public String getVersion() {
return version;
}
}
20 changes: 20 additions & 0 deletions src/test/java/com/assemblyai/api/UserAgentTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.assemblyai.api;

import com.assemblyai.api.core.UserAgent;
import org.junit.jupiter.api.Test;

import static com.assemblyai.api.core.Constants.SDK_VERSION;

public final class UserAgentTest {
@Test
public void ShouldCreateDefaultUserAgent() {
UserAgent agent = UserAgent.getDefault();
assert agent != null;
String userAgentString = agent.toAssemblyAIUserAgentString();
assert userAgentString != null;
assert userAgentString.contains("AssemblyAI/1.0 (");
assert userAgentString.endsWith(")");
assert userAgentString.contains("sdk=Java/" + SDK_VERSION);
assert userAgentString.contains("runtime_env=OpenJDK Runtime Environment/");
}
}
Loading