Skip to content

Commit

Permalink
Add compile time codegen tool
Browse files Browse the repository at this point in the history
  • Loading branch information
samvaity committed Dec 18, 2024
1 parent 3a8a4f8 commit 4683e22
Show file tree
Hide file tree
Showing 21 changed files with 2,658 additions and 0 deletions.
2 changes: 2 additions & 0 deletions eng/versioning/external_dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ com.microsoft.azure.functions:azure-functions-java-library;2.2.0
com.mysql:mysql-connector-j;9.0.0
com.squareup.okhttp3:mockwebserver;4.12.0
com.squareup.okhttp3:okhttp;4.12.0
com.squareup:javapoet;1.13.0
commons-codec:commons-codec;1.15
commons-net:commons-net;3.9.0
io.clientcore.tools:sdk-codegen-tool;1.0.0-beta.1
io.cloudevents:cloudevents-api;2.2.0
io.cloudevents:cloudevents-core;2.2.0
io.fabric8:kubernetes-client;6.12.1
Expand Down
1 change: 1 addition & 0 deletions sdk/clientcore/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
<module>core</module>
<module>http-okhttp3</module>
<module>http-stress</module>
<module>tools</module>
</modules>
</project>
14 changes: 14 additions & 0 deletions sdk/clientcore/tools/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.clientcore</groupId>
<artifactId>clientcore-tools-service</artifactId>
<packaging>pom</packaging>
<version>1.0.0</version> <!-- Need not change for every release-->
<modules>
<module>sdk-codegen-tool</module>
</modules>
</project>
135 changes: 135 additions & 0 deletions sdk/clientcore/tools/sdk-codegen-tool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Codegen Compile time generator Plugin

A Java annotation processor for generating HTTP service implementations based on annotated interfaces.

## Usage

1. Add the plugin dependency:
```xml
<dependencies>
<dependency>
<groupId>io.clientcore.tools</groupId>
<artifactId>sdk-codegen-tool</artifactId>
<version>1.0.0.beta.1</version> <!-- {x-version-update;io.clientcore.tools:sdk-codegen-tool;external_dependency} -->
<scope>provided</scope>
</dependency>
</dependencies>
```
1.1. Add the plugin configuration to your `pom.xml`:
```xml
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version> <!-- {x-version-update;org.apache.maven.plugins:maven-compiler-plugin;external_dependency} -->
<configuration>
<generatedSourcesDirectory>${project.build.directory}/generated-sources/</generatedSourcesDirectory>
<annotationProcessors>
<annotationProcessor>io.generation.tools.codegen.AnnotationProcessor</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
</plugins>
```
2. Annotate your interfaces with `@ServiceInterface`, `@HttpRequestInformation` and
`@UnexpectedResponseExceptionDetail` such annotations:
```java
@ServiceInterface(name = "ExampleClient", host = "{endpoint}/example")
public interface ExampleService {
@HttpRequestInformation(method = HttpMethod.GET, path = "/user/{userId}", expectedStatusCodes = { 200 })
@UnexpectedResponseExceptionDetail(exceptionTypeName = "CLIENT_AUTHENTICATION", statusCode = { 401 })
@UnexpectedResponseExceptionDetail(exceptionTypeName = "RESOURCE_NOT_FOUND", statusCode = { 404 })
@UnexpectedResponseExceptionDetail(exceptionTypeName = "RESOURCE_MODIFIED", statusCode = { 409 })
User getUser(@PathParam("userId") String userId);
}
```

3. Build your project and the plugin will generate an implementation of the annotated interface.
The processor would generate an implementation:
```java
public class ExampleServiceImpl implements ExampleService {
private static final ClientLogger LOGGER = new ClientLogger(OpenAIClientServiceImpl.class);

private final HttpPipeline defaultPipeline;

private final ObjectSerializer serializer;

private final String endpoint;

private final OpenAIServiceVersion serviceVersion;

private String apiVersion;

public OpenAIClientServiceImpl(HttpPipeline defaultPipeline, ObjectSerializer serializer,
String endpoint, OpenAIServiceVersion serviceVersion) {
this.defaultPipeline = defaultPipeline;
this.serializer = serializer;
this.endpoint = endpoint;
this.apiVersion = serviceVersion.getVersion();
this.serviceVersion = serviceVersion;
}

public String getEndpoint() {
return endpoint;
}

public HttpPipeline getPipeline() {
return defaultPipeline;
}

public OpenAIServiceVersion getServiceVersion() {
return serviceVersion;
}

private final HttpPipeline pipeline;

public ExampleServiceImpl(HttpPipeline pipeline) {
this.pipeline = pipeline;
}

public Response<BinaryData> getUser(String userId, Context context) {
return getUser(endpoint, apiVersion, userId, context);
}

@Override
private Response<BinaryData> getUser(String endpoint, String apiVersion, String userId, Context context) {
HttpPipeline pipeline = this.getPipeline();
String host = endpoint + "/example/users/" + userId + "?api-version=" + apiVersion;

// create the request
HttpRequest httpRequest = new HttpRequest(HttpMethod.GET, host);

// set the headers
HttpHeaders headers = new HttpHeaders();
httpRequest.setHeaders(headers);

// add RequestOptions to the request
httpRequest.setRequestOptions(requestOptions);

// set the body content if present

// send the request through the pipeline
Response<?> response = pipeline.send(httpRequest);

final int responseCode = response.getStatusCode();
boolean expectedResponse = responseCode == 200;
if (!expectedResponse) {
throw new RuntimeException("Unexpected response code: " + responseCode);
}
ResponseBodyMode responseBodyMode = ResponseBodyMode.IGNORE;
if (requestOptions != null) {
responseBodyMode = requestOptions.getResponseBodyMode();
}
if (responseBodyMode == ResponseBodyMode.DESERIALIZE) {
BinaryData responseBody = response.getBody();
HttpResponseAccessHelper.setValue((HttpResponse<?>) response, responseBody);
} else {
BinaryData responseBody = response.getBody();
HttpResponseAccessHelper.setBodyDeserializer((HttpResponse<?>) response, (body) -> responseBody);
}
return (Response<BinaryData>) response;
}
}
```
This implementation eliminates reflection and integrates directly with your HTTP client infrastructure.

112 changes: 112 additions & 0 deletions sdk/clientcore/tools/sdk-codegen-tool/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>io.clientcore.tools</groupId>
<artifactId>sdk-codegen-tool</artifactId>
<version>1.0.0-beta.1</version> <!-- {x-version-update;io.clientcore.tools:sdk-codegen-tool;external_dependency} -->

<name>Clientcore codegen tool</name>
<description>A Java annotation processor tool for generating HTTP service implementations</description>

<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>

<url>https://github.com/azure/azure-sdk-for-java</url>
<organization>
<name>Microsoft Corporation</name>
<url>http://microsoft.com</url>
</organization>
<licenses>
<license>
<name>The MIT License (MIT)</name>
<url>http://opensource.org/licenses/MIT</url>
<distribution>repo</distribution>
</license>
</licenses>

<developers>
<developer>
<id>microsoft</id>
<name>Microsoft Corporation</name>
</developer>
</developers>

<issueManagement>
<system>GitHub</system>
<url>https://github.com/Azure/azure-sdk-for-java/issues</url>
</issueManagement>

<scm>
<url>https://github.com/Azure/azure-sdk-for-java</url>
<connection>scm:git:https://github.com/Azure/azure-sdk-for-java.git</connection>
<developerConnection/>
<tag>HEAD</tag>
</scm>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<packageOutputDirectory>${project.build.directory}</packageOutputDirectory>
</properties>

<dependencies>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.13.0</version> <!-- {x-version-update;com.squareup:javapoet;external_dependency} -->
</dependency>
<dependency>
<groupId>io.clientcore</groupId>
<artifactId>core</artifactId>
<version>1.0.0-beta.1</version> <!-- {x-version-update;io.clientcore:core;dependency} -->
<scope>compile</scope>
</dependency>

<!-- Unit Test -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.2</version> <!-- {x-version-update;org.junit.jupiter:junit-jupiter-api;external_dependency} -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.11.2</version> <!-- {x-version-update;org.junit.jupiter:junit-jupiter-engine;external_dependency} -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.11.2</version> <!-- {x-version-update;org.junit.jupiter:junit-jupiter-params;external_dependency} -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.11.0</version> <!-- {x-version-update;org.mockito:mockito-core;external_dependency} -->
<scope>test</scope>
</dependency>
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
Loading

0 comments on commit 4683e22

Please sign in to comment.