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

feat(java): Include version headers in request options #5460

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.fern.java.utils;

public final class ApiVersionConstants {
public static final String CURRENT_API_VERSION = "CURRENT";
}
52 changes: 52 additions & 0 deletions generators/java/sdk/src/main/java/com/fern/java/client/Cli.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
import com.fern.ir.model.auth.AuthScheme;
import com.fern.ir.model.auth.OAuthScheme;
import com.fern.ir.model.commons.ErrorId;
import com.fern.ir.model.commons.Name;
import com.fern.ir.model.commons.NameAndWireValue;
import com.fern.ir.model.commons.SafeAndUnsafeString;
import com.fern.ir.model.ir.HeaderApiVersionScheme;
import com.fern.ir.model.ir.IntermediateRepresentation;
import com.fern.ir.model.types.EnumTypeDeclaration;
import com.fern.ir.model.types.EnumValue;
import com.fern.java.AbstractGeneratorCli;
import com.fern.java.AbstractPoetClassNameFactory;
import com.fern.java.DefaultGeneratorExecClient;
Expand All @@ -32,6 +38,7 @@
import com.fern.java.client.generators.SuppliersGenerator;
import com.fern.java.client.generators.TestGenerator;
import com.fern.java.generators.DateTimeDeserializerGenerator;
import com.fern.java.generators.EnumGenerator;
import com.fern.java.generators.ObjectMappersGenerator;
import com.fern.java.generators.PaginationCoreGenerator;
import com.fern.java.generators.StreamGenerator;
Expand All @@ -45,6 +52,7 @@
import com.fern.java.output.gradle.GradleDependency;
import com.fern.java.output.gradle.GradleDependencyType;
import com.fern.java.output.gradle.ParsedGradleDependency;
import com.fern.java.utils.ApiVersionConstants;
import com.palantir.common.streams.KeyedStream;
import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -183,6 +191,50 @@ public GeneratedRootClient generateClient(
GeneratedEnvironmentsClass generatedEnvironmentsClass = environmentGenerator.generateFile();
this.addGeneratedFile(generatedEnvironmentsClass);

ir.getApiVersion()
.flatMap(apiVersion -> apiVersion.getHeader().map(HeaderApiVersionScheme::getValue))
.ifPresent(irDeclaration -> {
EnumTypeDeclaration.Builder enumTypeDeclaration =
EnumTypeDeclaration.builder().from(irDeclaration);

irDeclaration.getDefault().ifPresent(defaultValue -> {
Name current = Name.builder()
.originalName(ApiVersionConstants.CURRENT_API_VERSION)
.camelCase(SafeAndUnsafeString.builder()
.unsafeName(ApiVersionConstants.CURRENT_API_VERSION)
.safeName(ApiVersionConstants.CURRENT_API_VERSION)
.build())
.pascalCase(SafeAndUnsafeString.builder()
.unsafeName(ApiVersionConstants.CURRENT_API_VERSION)
.safeName(ApiVersionConstants.CURRENT_API_VERSION)
.build())
.snakeCase(SafeAndUnsafeString.builder()
.unsafeName(ApiVersionConstants.CURRENT_API_VERSION)
.safeName(ApiVersionConstants.CURRENT_API_VERSION)
.build())
.screamingSnakeCase(SafeAndUnsafeString.builder()
.unsafeName(ApiVersionConstants.CURRENT_API_VERSION)
.safeName(ApiVersionConstants.CURRENT_API_VERSION)
.build())
.build();

enumTypeDeclaration.addValues(EnumValue.builder()
.from(defaultValue)
.name(NameAndWireValue.builder()
.from(defaultValue.getName())
.name(current)
.build())
.build());
});

EnumGenerator apiVersionsGenerator = new EnumGenerator(
context.getPoetClassNameFactory().getApiVersionsClassName(),
context,
enumTypeDeclaration.build());
GeneratedJavaFile generatedApiVersions = apiVersionsGenerator.generateFile();
this.addGeneratedFile(generatedApiVersions);
});

RequestOptionsGenerator requestOptionsGenerator = new RequestOptionsGenerator(
context, context.getPoetClassNameFactory().getRequestOptionsClassName());
GeneratedJavaFile generatedRequestOptions = requestOptionsGenerator.generateFile();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ public ClassName getResponseBodyReaderClassName() {
return ClassName.get(getCorePackage(), "ResponseBodyReader");
}

public ClassName getApiVersionsClassName() {
return ClassName.get(getCorePackage(), "ApiVersion");
}

public ClassName getRequestOptionsClassName() {
return ClassName.get(getCorePackage(), "RequestOptions");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import java.util.Map;
import java.util.Optional;
import org.immutables.value.Value;

@Value.Immutable
Expand All @@ -34,6 +35,8 @@ public abstract class GeneratedClientOptions extends AbstractGeneratedJavaFile {

public abstract MethodSpec httpClientWithTimeout();

public abstract Optional<MethodSpec> version();

public abstract Map<VariableId, MethodSpec> variableGetters();

public abstract ClassName builderClassName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package com.fern.java.client.generators;

import com.fern.generator.exec.model.config.GeneratorConfig;
import com.fern.ir.model.ir.ApiVersionScheme;
import com.fern.ir.model.ir.HeaderApiVersionScheme;
import com.fern.ir.model.ir.PlatformHeaders;
import com.fern.ir.model.variables.VariableDeclaration;
import com.fern.ir.model.variables.VariableId;
Expand All @@ -25,6 +27,8 @@
import com.fern.java.client.GeneratedEnvironmentsClass;
import com.fern.java.generators.AbstractFileGenerator;
import com.fern.java.output.GeneratedJavaFile;
import com.fern.java.utils.ApiVersionConstants;
import com.google.common.collect.ImmutableList;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
Expand Down Expand Up @@ -80,6 +84,7 @@ public final class ClientOptionsGenerator extends AbstractFileGenerator {
private final FieldSpec environmentField;
private final GeneratedJavaFile requestOptionsFile;
private final ClientGeneratorContext clientGeneratorContext;
private final FieldSpec apiVersionField;

public ClientOptionsGenerator(
ClientGeneratorContext clientGeneratorContext,
Expand All @@ -95,12 +100,19 @@ public ClientOptionsGenerator(
.build();
this.requestOptionsFile = requestOptionsFile;
this.clientGeneratorContext = clientGeneratorContext;
this.apiVersionField = FieldSpec.builder(
clientGeneratorContext.getPoetClassNameFactory().getApiVersionsClassName(),
"version",
Modifier.PRIVATE,
Modifier.FINAL)
.build();
}

@Override
public GeneratedClientOptions generateFile() {
MethodSpec environmentGetter = createGetter(environmentField);
MethodSpec headersFromRequestOptions = headersFromRequestOptions();
Optional<MethodSpec> versionsGetter;
Optional<MethodSpec> headersFromIdempotentRequestOptions = headersFromIdempotentRequestOptions();
MethodSpec httpClientGetter = createGetter(OKHTTP_CLIENT_FIELD);
Map<VariableId, FieldSpec> variableFields = getVariableFields();
Expand Down Expand Up @@ -145,6 +157,58 @@ public GeneratedClientOptions generateFile() {
.addStatement("this.$L = $L", OKHTTP_CLIENT_FIELD.name, OKHTTP_CLIENT_FIELD.name)
.addStatement("this.$L = $L", TIMEOUT_FIELD.name, TIMEOUT_FIELD.name);

if (clientGeneratorContext.getIr().getApiVersion().isPresent()) {
ApiVersionScheme apiVersionScheme =
clientGeneratorContext.getIr().getApiVersion().get();

apiVersionScheme.visit(new ApiVersionScheme.Visitor<Void>() {
@Override
public Void visitHeader(HeaderApiVersionScheme headerApiVersionScheme) {
if (headerApiVersionScheme.getValue().getDefault().isPresent()) {
constructorBuilder.addParameter(ParameterSpec.builder(
ParameterizedTypeName.get(
ClassName.get(Optional.class),
clientGeneratorContext
.getPoetClassNameFactory()
.getApiVersionsClassName()),
apiVersionField.name)
.build());
constructorBuilder.addStatement(
"this.$L = $L.orElse($L)",
apiVersionField.name,
apiVersionField.name,
CodeBlock.of(
"$T.$L",
clientGeneratorContext
.getPoetClassNameFactory()
.getApiVersionsClassName(),
ApiVersionConstants.CURRENT_API_VERSION));
} else {
constructorBuilder.addParameter(ParameterSpec.builder(
clientGeneratorContext
.getPoetClassNameFactory()
.getApiVersionsClassName(),
apiVersionField.name)
.build());
constructorBuilder.addStatement("this.$L = $L", apiVersionField.name, apiVersionField.name);
}

constructorBuilder.addStatement(
"this.$L.put($S,$L)",
HEADERS_FIELD.name,
headerApiVersionScheme.getHeader().getName().getWireValue(),
CodeBlock.of("this.$L.toString()", apiVersionField.name));

return null;
}

@Override
public Void _visitUnknown(Object _o) {
throw new IllegalArgumentException("Received unknown API versioning schema type in IR.");
}
});
}

variableFields
.values()
.forEach(fieldSpec -> constructorBuilder.addStatement("this.$N = $N", fieldSpec, fieldSpec));
Expand All @@ -161,6 +225,12 @@ public GeneratedClientOptions generateFile() {
.addMethod(environmentGetter)
.addMethod(headersFromRequestOptions);

if (clientGeneratorContext.getIr().getApiVersion().isPresent()) {
clientOptionsBuilder.addField(apiVersionField);
versionsGetter = Optional.of(createGetter(apiVersionField));
clientOptionsBuilder.addMethod(versionsGetter.get());
}

if (headersFromIdempotentRequestOptions.isPresent()) {
clientOptionsBuilder.addMethod(headersFromIdempotentRequestOptions.get());

Expand Down Expand Up @@ -219,7 +289,7 @@ public GeneratedClientOptions generateFile() {
.returns(builderClassName)
.addStatement("return new $T()", builderClassName)
.build())
.addType(createBuilder(variableFields))
.addType(createBuilder(clientGeneratorContext, variableFields))
.build();

JavaFile environmentsFile =
Expand Down Expand Up @@ -269,8 +339,8 @@ private MethodSpec.Builder constructHeadersMethod() {
.addStatement("return values");
}

private TypeSpec createBuilder(Map<VariableId, FieldSpec> variableFields) {
return TypeSpec.classBuilder(builderClassName)
private TypeSpec createBuilder(ClientGeneratorContext context, Map<VariableId, FieldSpec> variableFields) {
TypeSpec.Builder builder = TypeSpec.classBuilder(builderClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.addField(FieldSpec.builder(environmentField.type, environmentField.name)
.addModifiers(Modifier.PRIVATE)
Expand All @@ -296,9 +366,22 @@ private TypeSpec createBuilder(Map<VariableId, FieldSpec> variableFields) {
.addStatement("this.$L = $L", TIMEOUT_FIELD.name, TIMEOUT_FIELD.name)
.addStatement("return this")
.build())
.addMethods(getVariableBuilders(variableFields))
.addMethod(getBuildMethod(variableFields))
.build();
.addMethods(getVariableBuilders(variableFields));

if (context.getIr().getApiVersion().isPresent()) {
builder.addField(FieldSpec.builder(
ParameterizedTypeName.get(
ClassName.get(Optional.class),
context.getPoetClassNameFactory().getApiVersionsClassName()),
apiVersionField.name,
Modifier.PRIVATE)
.build());
builder.addMethod(getVersionBuilder());
}

builder.addMethod(getBuildMethod(context, variableFields));

return builder.build();
}

private MethodSpec getEnvironmentBuilder() {
Expand All @@ -322,6 +405,18 @@ private MethodSpec getHeaderBuilder() {
.build();
}

private MethodSpec getVersionBuilder() {
return MethodSpec.methodBuilder(apiVersionField.name)
.addModifiers(Modifier.PUBLIC)
.returns(builderClassName)
.addParameter(
clientGeneratorContext.getPoetClassNameFactory().getApiVersionsClassName(),
apiVersionField.name)
.addStatement("this.$L = $T.of($L)", apiVersionField.name, Optional.class, apiVersionField.name)
.addStatement("return this")
.build();
}

private MethodSpec getHeaderSupplierBuilder() {
return MethodSpec.methodBuilder("addHeader")
.addModifiers(Modifier.PUBLIC)
Expand Down Expand Up @@ -382,7 +477,20 @@ private Map<VariableId, MethodSpec> getVariableGetters(Map<VariableId, FieldSpec
}));
}

private MethodSpec getBuildMethod(Map<VariableId, FieldSpec> variableFields) {
private MethodSpec getBuildMethod(ClientGeneratorContext context, Map<VariableId, FieldSpec> variableFields) {
ImmutableList.Builder<Object> argsBuilder = ImmutableList.builder();
argsBuilder.add(
className, environmentField.name, HEADERS_FIELD.name, HEADER_SUPPLIERS_FIELD.name, "okhttpClient");

String returnString = "return new $T($L, $L, $L, $L, this.timeout";

if (context.getIr().getApiVersion().isPresent()) {
argsBuilder.add(apiVersionField.name);
returnString = "return new $T($L, $L, $L, $L, this.timeout, $L";
}

Object[] args = argsBuilder.build().toArray();

if (variableFields.isEmpty()) {
return MethodSpec.methodBuilder("build")
.addModifiers(Modifier.PUBLIC)
Expand All @@ -397,13 +505,7 @@ private MethodSpec getBuildMethod(Map<VariableId, FieldSpec> variableFields) {
.add("\n .callTimeout(this.timeout, $T.SECONDS)", TimeUnit.class)
.add("\n .build()")
.build())
.addStatement(
"return new $T($L, $L, $L, $L, this.timeout)",
className,
environmentField.name,
HEADERS_FIELD.name,
HEADER_SUPPLIERS_FIELD.name,
"okhttpClient")
.addStatement(returnString + ")", args)
.build();
} else {
String variableArgs = variableFields.values().stream()
Expand All @@ -422,14 +524,7 @@ private MethodSpec getBuildMethod(Map<VariableId, FieldSpec> variableFields) {
.add("\n .callTimeout(this.timeout, $T.SECONDS)", TimeUnit.class)
.add("\n .build()")
.build())
.addStatement(
"return new $T($L, $L, $L, $L, this.timeout, $L)",
className,
environmentField.name,
HEADERS_FIELD.name,
HEADER_SUPPLIERS_FIELD.name,
"okhttpClient",
variableArgs)
.addStatement(returnString + ", " + variableArgs + ")", args)
.build();
}
}
Expand Down
Loading
Loading