Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
15 changes: 13 additions & 2 deletions examples/java/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
FROM openjdk:8
FROM eclipse-temurin:8-jdk

RUN apt-get update && apt-get install maven -y
# Install Maven with proper error handling
RUN apt-get update && \
apt-get install -y --no-install-recommends maven && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

WORKDIR /app
# Install git
RUN apt-get update && \
apt-get install -y --no-install-recommends git && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

# Use HTTPS instead of SSH for git clone
RUN git clone https://github.com/twilio/twilio-java.git
WORKDIR /app/twilio-java/

Expand Down
95 changes: 95 additions & 0 deletions examples/test_spec/twilio_pagination_v1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# This spec tests the token pagination strategy as per Twilio API Standards V1.0

info:
contact:
email: [email protected]
name: Twilio Support
url: https://support.twilio.com
description: This is the public Twilio REST API.
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
termsOfService: https://www.twilio.com/legal/tos
title: Twilio - REQUEST BODY TEST
version: 1.11.0
x-twilio:
apiStandards: v1.0
openapi: 3.0.1

components:
securitySchemes:
accountSid_authToken:
scheme: basic
type: http
schemas:
SampleResponseObject:
type: object
properties:
id:
type: string
name:
type: string
paths:
/v2/Services:
servers:
- url: https://testparameter.twilio.com
get:
operationId: ListService
description: Retrieve a list of all Services
parameters:
- name: pageSize
in: query
description: Maximum number of items to return in a single response
required: false
schema:
type: integer
minimum: 1
maximum: 1000
example: 50
- name: pageToken
in: query
description: A URL-safe, base64-encoded token representing the page of results to return
required: false
schema:
type: string
example: "eyJwYWdlIjoyLCJxdWVyeSI6ImJvb2tzIn0="
security:
- accountSid_authToken: [ ]
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
required:
- services
- meta
properties:
services:
type: array
minItems: 0
items:
$ref: '#/components/schemas/SampleResponseObject'
meta:
type: object
required:
- key
- pageSize
properties:
key:
type: string
description: The key of the list property contains the actual data items
example: "services"
pageSize:
type: integer
description: The actual number of items returned in this response
example: 20
previousToken:
type: string
description: Token to fetch the previous page of results
example: "eyJwYWdlIjowLCJxdWVyeSI6ImJvb2tzIn0="
nextToken:
type: string
description: Token to fetch the next page of results
example: "eyJwYWdlIjoyLCJxdWVyeSI6ImJvb2tzIn0="
9 changes: 9 additions & 0 deletions src/main/java/com/twilio/oai/TwilioJavaGeneratorModern.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache.Lambda;
import com.twilio.oai.common.ApplicationConstants;
import com.twilio.oai.common.EnumConstants;
import com.twilio.oai.common.Utility;
import com.twilio.oai.java.JavaApiResource;
Expand All @@ -15,13 +16,15 @@
import com.twilio.oai.templating.mustache.ReplaceHyphenLambda;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.Schema;
import java.util.Optional;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.languages.JavaClientCodegen;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.ModelsMap;
import org.openapitools.codegen.model.OperationsMap;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -64,6 +67,12 @@ public void processOpts() {
// Run once per spec
@Override
public void processOpenAPI(final OpenAPI openAPI) {
Optional<String> apiStdVersionOpt = openAPI.getInfo().getExtensions() != null ?
Optional.ofNullable((String)((LinkedHashMap)openAPI.getInfo().getExtensions().get("x-twilio")).get("apiStandards"))
: Optional.empty();
String apiStdVersion = apiStdVersionOpt.orElse("unknown");
boolean isV1 = ApplicationConstants.isV1.test(apiStdVersion);
ResourceCacheContext.get().setV1(isV1);
String domain = twilioCodegen.getDomainFromOpenAPI(openAPI);
String version = twilioCodegen.getVersionFromOpenAPI(openAPI);
twilioCodegen.setDomain(domain);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ public class ApplicationConstants {
public static final String X_MODEL_TYPE = "x-model-type"; // EnumConstants.OpenApiEnumType
public static final String X_IS_MODEL = "x-is-model";
public static final String X_REQUEST_CONTENT_TYPE = "x-request-content-type";


public static final Predicate<String> isV1 = input -> input != null && input.startsWith("v1"); // Usage: isV1.test("v1.0");

}
3 changes: 2 additions & 1 deletion src/main/java/com/twilio/oai/java/JavaApiResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public class JavaApiResource {
List<CodegenOperation> operations;
Set<CodegenProperty> response;
String namespaceSubPart;

Boolean responseFlag = null; // true or NUll
Boolean isApiV1 = null; // true or NUll

public JavaApiResource(JavaApiResourceBuilder builder) {
resourceName = ResourceCacheContext.get().getResourceName();
Expand All @@ -30,6 +30,7 @@ public JavaApiResource(JavaApiResourceBuilder builder) {
this.response = ResourceCacheContext.get().getResponse();
if (response != null && !response.isEmpty()) responseFlag = true;
this.namespaceSubPart = builder.namespaceSubPart;
if (ResourceCacheContext.get().isV1()) isApiV1 = true;
}
}

4 changes: 4 additions & 0 deletions src/main/java/com/twilio/oai/java/cache/ResourceCache2.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public class ResourceCache2 {

@Getter
private Set<MustacheEnum> enumsClassesForMustache = new HashSet<>();

@Getter
@Setter
private boolean isV1;

public void setAllModelsByDefaultGenerator(ArrayList<CodegenModel> allModelsByDefaultGenerator) {
this.allModelsByDefaultGenerator = new ArrayList<>(allModelsByDefaultGenerator);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.twilio.oai.java.processor.responsebody;

import com.twilio.oai.common.ApplicationConstants;
import com.twilio.oai.java.cache.ResourceCacheContext;
import com.twilio.oai.java.format.Deserializer;
import com.twilio.oai.java.processor.enums.EnumProcessorFactory;
import com.twilio.oai.java.processor.requestbody.RecursiveModelProcessor;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation;

import java.util.Map;

public class JsonMultipleResponseProcessor implements ResponseProcessor {
EnumProcessorFactory enumProcessorFactory = EnumProcessorFactory.getInstance();
RecursiveModelProcessor recursiveModelProcessor = new RecursiveModelProcessor();
@Override
public void process(final CodegenOperation codegenOperation) {
/* There are 5 types of operation we are supporting.
* delete -- does not have response body
* fetch, create, update --> have body
* list --> has pagination and body
*/
if (codegenOperation.operationId.toLowerCase().startsWith("delete")) return;

}

public boolean shouldProcess(final CodegenOperation codegenOperation) {
if (!ResourceCacheContext.get().isV1()) return false;

if (codegenOperation.produces != null && !codegenOperation.produces.isEmpty()) {
for (Map<String, String> contentType : codegenOperation.produces) {
if (getContentType().equals(contentType.get("mediaType")) || "application/scim+json".equals(contentType.get("mediaType"))) {
return true;
}
}
}
return false;
}

private void processResponseWithoutPagination() {

}

private void processResponseWithPagination() {

}

@Override
public String getContentType() {
return "application/json";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ private CodegenModel getModelFromListOperation(CodegenModel codegenModel) {
}

public boolean shouldProcess(final CodegenOperation codegenOperation) {
System.out.println(codegenOperation.operationId);
if (ResourceCacheContext.get().isV1()) return false;

if (codegenOperation.produces != null && !codegenOperation.produces.isEmpty()) {
for (Map<String, String> contentType : codegenOperation.produces) {
if (getContentType().equals(contentType.get("mediaType")) || "application/scim+json".equals(contentType.get("mediaType"))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class ResponseProcessorFactory {

private ResponseProcessorFactory() {
processors.add(new JsonResponseProcessor());
processors.add(new JsonMultipleResponseProcessor());
}

public static ResponseProcessorFactory getInstance() {
Expand Down
35 changes: 22 additions & 13 deletions src/main/resources/twilio-java/common/operationMethod.mustache
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{!
{{!
resourceName: Api Name as identified by Directory Structure service
x-common-action-method: used to define operation method and can have values: create, read, update, delete, fetch
x-common-action-method: used to define operation method and can have values: create, read, update, delete, fetch
Example: https://github.com/twilio/twilio-java/blob/9c2ba4dbc185c5576e67fbeb82ec6f4899093e79/src/main/java/com/twilio/rest/api/v2010/account/MessageCreator.java#L309

httpMethod: http method associated in the current operation.
Expand All @@ -10,7 +10,7 @@ vendorExtensions.x-content-type: content type of the request, example: applicati
@Override
public {{resourceName}} {{vendorExtensions.x-common-action-method}}(final TwilioRestClient client) {
{{>common/generateUri}}

Request request = new Request(
HttpMethod.{{httpMethod}},
Domains.{{#lambda.uppercase}}{{domainName}}{{/lambda.uppercase}}.toString(),
Expand All @@ -31,21 +31,30 @@ vendorExtensions.x-content-type: content type of the request, example: applicati
{{#bodyParams.0}}
addPostParams(request, client);
{{/bodyParams.0}}

Response response = client.request(request);

if (response == null) {
throw new ApiConnectionException("{{resourceName}} creation failed: Unable to connect to server");
} else if (!TwilioRestClient.SUCCESS.test(response.getStatusCode())) {
} {{^isApiV1}}else if (!TwilioRestClient.SUCCESS.test(response.getStatusCode())) {
RestException restException = RestException.fromJson(
response.getStream(),
client.getObjectMapper()
);
response.getStream(),
client.getObjectMapper());
if (restException == null) {
throw new ApiException("Server Error, no content", response.getStatusCode());
throw new ApiException("Server Error, no content", response.getStatusCode());
}
throw new ApiException(restException);
}

} {{! end of else if }}{{/isApiV1}}
{{#isApiV1}}
else if (!TwilioRestClient.SUCCESS.test(response.getStatusCode())) {
RestExceptionV10 restException = RestExceptionV10.fromJson(
response.getStream(),
client.getObjectMapper());
if (restException == null) {
throw new ApiException("Server Error, no content", response.getStatusCode());
}
throw new ApiException(restException);
{{/isApiV1}}

return {{resourceName}}.fromJson(response.getStream(), client.getObjectMapper());
}
}
35 changes: 22 additions & 13 deletions src/main/resources/twilio-java/creator/operationMethod.mustache
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{!
{{!
resourceName: Api Name as identified by Directory Structure service
x-common-action-method: used to define operation method and can have values: create, read, update, delete, fetch
x-common-action-method: used to define operation method and can have values: create, read, update, delete, fetch
Example: https://github.com/twilio/twilio-java/blob/9c2ba4dbc185c5576e67fbeb82ec6f4899093e79/src/main/java/com/twilio/rest/api/v2010/account/MessageCreator.java#L309

httpMethod: http method associated in the current operation.
Expand All @@ -10,7 +10,7 @@ vendorExtensions.x-content-type: content type of the request, example: applicati
@Override
public {{resourceName}} {{vendorExtensions.x-common-action-method}}(final TwilioRestClient client) {
{{>common/generateUri}}

Request request = new Request(
HttpMethod.{{httpMethod}},
Domains.{{#lambda.uppercase}}{{domainName}}{{/lambda.uppercase}}.toString(),
Expand All @@ -34,21 +34,30 @@ vendorExtensions.x-content-type: content type of the request, example: applicati
{{#bodyParams.0}}
addPostParams(request, client);
{{/bodyParams.0}}

Response response = client.request(request);

if (response == null) {
throw new ApiConnectionException("{{resourceName}} creation failed: Unable to connect to server");
} else if (!TwilioRestClient.SUCCESS.test(response.getStatusCode())) {
} {{^isApiV1}}else if (!TwilioRestClient.SUCCESS.test(response.getStatusCode())) {
RestException restException = RestException.fromJson(
response.getStream(),
client.getObjectMapper()
);
response.getStream(),
client.getObjectMapper());
if (restException == null) {
throw new ApiException("Server Error, no content", response.getStatusCode());
throw new ApiException("Server Error, no content", response.getStatusCode());
}
throw new ApiException(restException);
}

} {{! end of else if }}{{/isApiV1}}
{{#isApiV1}}
else if (!TwilioRestClient.SUCCESS.test(response.getStatusCode())) {
RestExceptionV10 restException = RestExceptionV10.fromJson(
response.getStream(),
client.getObjectMapper());
if (restException == null) {
throw new ApiException("Server Error, no content", response.getStatusCode());
}
throw new ApiException(restException);
{{/isApiV1}}

return {{resourceName}}.fromJson(response.getStream(), client.getObjectMapper());
}
}
Loading
Loading