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
1,456 changes: 1,456 additions & 0 deletions specs_test/test_spec/twilio_multiresponse_v1.yaml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public Map<String, ModelsMap> postProcessAllModels(final Map<String, ModelsMap>
//ResourceCache.clearAllModelsByDefaultGenerator();
// Update allModels from Default generator in ResourceCache.
Utility.addModelsToLocalModelList(results, cache2.getAllModelsByDefaultGenerator());
Utility.addModelsToLocalCodegenModelMap(results, cache2.getAllModelsMapByDefaultGenerator());
directoryStructureService.postProcessAllModels(results, modelFormatMap);

// Return an empty collection so no model files get generated.
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/com/twilio/oai/common/Utility.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -17,6 +18,7 @@
import com.twilio.oai.java.cache.ResourceCacheContext;
import lombok.experimental.UtilityClass;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.CodegenMediaType;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.CodegenProperty;
Expand Down Expand Up @@ -53,6 +55,16 @@ public void addModelsToLocalModelList(final Map<String, ModelsMap> modelMap, Lis
}
}

public void addModelsToLocalCodegenModelMap(final Map<String, ModelsMap> modelMap, Map<String, CodegenModel> localModelMap){
for (final ModelsMap mods : modelMap.values()) {
final List<ModelMap> modList = mods.getModels();
modList.stream()
.map(ModelMap::getModel)
.map(CodegenModel.class::cast)
.forEach(model -> localModelMap.put(com.twilio.oai.common.StringUtils.toPascalCase(model.name), model));
}
}

public String removeEnumName(final String dataType) {
return dataType == null
? null
Expand Down Expand Up @@ -265,6 +277,7 @@ public static void main(String[] args) {
System.out.println(getModelFromRef(ref));
}
public static CodegenModel getModelFromRef(String ref) {
if (ref == null) throw new RuntimeException("Ref can not be null for fetching Model");
String schemaName = ref.replaceFirst("#/components/schemas/", "");
List<CodegenModel> allModels = ResourceCacheContext.get().getAllModelsByDefaultGenerator();
for (CodegenModel model: allModels) {
Expand All @@ -274,4 +287,15 @@ public static CodegenModel getModelFromRef(String ref) {
}
return null;
}

public static CodegenModel getPropertyFromMediaType(CodegenMediaType codegenMediaType) {
CodegenProperty codegenProperty = codegenMediaType.getSchema();

String ref = codegenProperty.getRef();
if (ref == null) {
throw new RuntimeException("Ref is null in Response schema");
}
CodegenModel responseModel = Utility.getModelFromRef(ref);
return responseModel;
}
}
1 change: 0 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,7 +18,6 @@ public class JavaApiResource {
List<CodegenOperation> operations;
Set<CodegenProperty> response;
String namespaceSubPart;

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public JavaApiResourceBuilder resourceName() {

public JavaApiResourceBuilder recordKey() {
this.recordKey = Utility.getRecordKey(ResourceCacheContext.get().getAllModelsByDefaultGenerator(), operations);
ResourceCacheContext.get().setRecordKey(this.recordKey);
return this;
}

Expand Down
21 changes: 20 additions & 1 deletion src/main/java/com/twilio/oai/java/cache/ResourceCache2.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ public class ResourceCache2 {
@Setter
private String resourceName;

@Getter
@Setter
private String recordKey;

@Getter
@Setter
private Set<CodegenProperty> response = new TreeSet<>((p1, p2) -> p1.baseName.compareTo(p2.baseName));
Expand All @@ -31,7 +35,14 @@ public class ResourceCache2 {

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


@Getter
private Set<MustacheModel> responses = new HashSet<>();

// Note: Key is stored in PascalCase using com.twilio.oai.common.StringUtils.toPascalCase(string_val)
@Getter
private Map<String, CodegenModel> allModelsMapByDefaultGenerator = new HashMap<>();

@Getter
@Setter
private boolean isV1;
Expand All @@ -43,6 +54,10 @@ public void setAllModelsByDefaultGenerator(ArrayList<CodegenModel> allModelsByDe
public ArrayList<CodegenModel> getAllModelsByDefaultGenerator() {
return this.allModelsByDefaultGenerator;
}

public Map<String, CodegenModel> getAllModelsMapByDefaultGenerator() {
return this.allModelsMapByDefaultGenerator;
}
public void addToModelClasses(MustacheModel mustacheModel) {
this.modelClassesForMustache.add(mustacheModel);
}
Expand All @@ -55,6 +70,10 @@ public void addToOneOfInterfaces(MustacheOneOfIface oneOfIFace) {
this.oneOfInterfaces.add(oneOfIFace);
}

public void addToResponse(MustacheModel mustacheModel) {
this.responses.add(mustacheModel);
}

// Clear at group of operation level, at method: postProcessOperationsWithModels
public void clear() {
resourceName = "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class MustacheConstants {
public static final String X_IS_LIST_OP = "x-is-list-op";
public static final String ACTION_TYPE = "x-common-action-type";
public static final String ACTION_METHOD = "x-common-action-method";
public static final String X_RESPONSE_DATATYPE = "x-response-datatype";

public static final Map<String, String> serializaationMapping = Map.of(
"application/x-www-form-urlencoded", "if ($paramName != null) { request.addPostParam($stringCapParamName, $paramName.toString())}",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.twilio.oai.java.processor.responsebody;

import com.twilio.oai.common.ApplicationConstants;
import com.twilio.oai.common.StringUtils;
import com.twilio.oai.java.cache.ResourceCacheContext;
import com.twilio.oai.java.constants.MustacheConstants;
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 org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.CodegenResponse;

import java.util.Map;

public class JsonMultipleResponseProcessor extends JsonResponseAbstractProcessor 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;

if (codegenOperation.operationId.toLowerCase().startsWith("list")) {
processResponseWithPagination(codegenOperation);
} else {
processResponseWithoutPagination(codegenOperation);
}

}

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(CodegenOperation codegenOperation) {
System.out.println(codegenOperation.operationId);
CodegenProperty codegenProperty = getCodegenProperty(codegenOperation);
if (codegenProperty == null) {
codegenOperation.vendorExtensions.put(MustacheConstants.X_RESPONSE_DATATYPE, "void");
return;
}
recursiveModelProcessor.process(codegenProperty);
codegenOperation.vendorExtensions.put(MustacheConstants.X_RESPONSE_DATATYPE, codegenProperty.vendorExtensions.get(ApplicationConstants.X_DATATYPE));
}

private void processResponseWithPagination(CodegenOperation codegenOperation) {
// check if pagination exists, if no, go to processResponseWithoutPagination
Map<String, CodegenModel> codegenModelMap = ResourceCacheContext.get().getAllModelsMapByDefaultGenerator();
CodegenProperty responseProperty = null;
for (CodegenResponse response: codegenOperation.responses) {
CodegenModel codegenModel = codegenModelMap.get(StringUtils.toPascalCase(response.baseType));
for (CodegenProperty codegenProperty: codegenModel.vars) {
if (codegenProperty.name.equals(ResourceCacheContext.get().getRecordKey())) {
responseProperty = codegenProperty;
}
}
}

if (responseProperty == null) {
codegenOperation.vendorExtensions.put(MustacheConstants.X_RESPONSE_DATATYPE, "void");
return;
}
recursiveModelProcessor.process(responseProperty);
String listResponseDatatype = (String)responseProperty.vendorExtensions.get(ApplicationConstants.X_DATATYPE);
listResponseDatatype = listResponseDatatype.replaceFirst("^List(?=<)", "ResourceSet");
codegenOperation.vendorExtensions.put(MustacheConstants.X_RESPONSE_DATATYPE, listResponseDatatype);
}

@Override
public String getContentType() {
return "application/json";
}

boolean isPaginatedModel(CodegenProperty codegenProperty) {
System.out.println(codegenProperty);
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.twilio.oai.java.processor.responsebody;

import com.twilio.oai.common.StringUtils;
import com.twilio.oai.common.Utility;
import com.twilio.oai.java.cache.ResourceCacheContext;
import org.openapitools.codegen.CodegenMediaType;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.CodegenResponse;

import java.util.Map;

public abstract class JsonResponseAbstractProcessor {
protected CodegenModel getModel(final CodegenOperation codegenOperation) {
if (codegenOperation.responses != null && !codegenOperation.responses.isEmpty()) {
for (CodegenResponse codegenResponse: codegenOperation.responses) {
if (codegenResponse.is2xx || codegenResponse.is3xx) {
if (codegenResponse == null || codegenResponse.getContent() == null) return null;
CodegenMediaType codegenMediaType = codegenResponse.getContent().get(getContentType()); // covers application/json
if (codegenMediaType == null) {
codegenMediaType = codegenResponse.getContent().get("application/scim+json"); // special case for Orgs API
if (codegenMediaType == null) return null;
}

if (codegenMediaType.getSchema().isContainer) {
// It covers special case in which response is list
// TODO: Handle in future.
}
String ref = codegenMediaType.getSchema().getRef();
if (ref == null) return null;
CodegenModel model = Utility.getModelFromRef(ref);
return model;

}
}
}
return null;
}

protected CodegenProperty getCodegenProperty(final CodegenOperation codegenOperation) {
Map<String, CodegenModel> codegenModelMap = ResourceCacheContext.get().getAllModelsMapByDefaultGenerator();
if (codegenOperation.responses != null && !codegenOperation.responses.isEmpty()) {
for (CodegenResponse codegenResponse: codegenOperation.responses) {
if (codegenResponse.is2xx || codegenResponse.is3xx) {
if (codegenResponse == null || codegenResponse.getContent() == null) return null;
CodegenMediaType codegenMediaType = codegenResponse.getContent().get(getContentType());
if (codegenMediaType == null) {
codegenMediaType = codegenResponse.getContent().get("application/scim+json"); // special case for Orgs API
if (codegenMediaType == null) return null;
}

if (codegenMediaType.getSchema().isContainer) {
// It covers special case in which response is list
// TODO: Handle in future.
}
return codegenMediaType.getSchema();
}
}
}
return null;
}

public String getContentType() {
return "application/json";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import java.util.Map;

public class JsonResponseProcessor implements ResponseProcessor {
public class JsonResponseProcessor extends JsonResponseAbstractProcessor implements ResponseProcessor {
EnumProcessorFactory enumProcessorFactory = EnumProcessorFactory.getInstance();
RecursiveModelProcessor recursiveModelProcessor = new RecursiveModelProcessor();
@Override
Expand Down 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 All @@ -75,29 +76,5 @@ public String getContentType() {

// if $ref, model name: codegenOperation.responses.get(0).content.get("application/json").schema.getRef()
// output: #/components/schemas/api.v2010.account.message
private CodegenModel getModel(final CodegenOperation codegenOperation) {
if (codegenOperation.responses != null && !codegenOperation.responses.isEmpty()) {
for (CodegenResponse codegenResponse: codegenOperation.responses) {
if (codegenResponse.is2xx || codegenResponse.is3xx) {
if (codegenResponse == null || codegenResponse.getContent() == null) return null;
CodegenMediaType codegenMediaType = codegenResponse.getContent().get(getContentType());
if (codegenMediaType == null) {
codegenMediaType = codegenResponse.getContent().get("application/scim+json");
if (codegenMediaType == null) return null;
}

if (codegenMediaType.getSchema().isContainer) {
// It covers special case in which response is list
// TODO: Handle in future.
}
String ref = codegenMediaType.getSchema().getRef();
if (ref == null) return null;
CodegenModel model = Utility.getModelFromRef(ref);
return model;

}
}
}
return null;
}
}
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ domainName: example api, video, chat, etc. These can be found in Domains.java in
vendorExtensions.x-content-type: content type of the request, example: application/json, application/x-www-form-urlencoded
}}
@Override
public {{resourceName}} {{vendorExtensions.x-common-action-method}}(final TwilioRestClient client) {
public
{{#vendorExtensions.x-response-datatype}} {{{vendorExtensions.x-response-datatype}}} {{/vendorExtensions.x-response-datatype}}
{{^vendorExtensions.x-response-datatype}} {{resourceName}} {{/vendorExtensions.x-response-datatype}}
{{vendorExtensions.x-common-action-method}}(final TwilioRestClient client) {
{{>common/generateUri}}

Request request = new Request(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ domainName: example api, video, chat, etc. These can be found in Domains.java in
vendorExtensions.x-content-type: content type of the request, example: application/json, application/x-www-form-urlencoded
}}
@Override
public {{resourceName}} fetch(final TwilioRestClient client) {
public
{{#vendorExtensions.x-response-datatype}} {{{vendorExtensions.x-response-datatype}}} {{/vendorExtensions.x-response-datatype}}
{{^vendorExtensions.x-response-datatype}} {{resourceName}} {{/vendorExtensions.x-response-datatype}}
fetch(final TwilioRestClient client) {
{{>common/generateUri}}

Request request = new Request(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ httpMethod: http method associated in the current operation.
domainName: example api, video, chat, etc. These can be found in Domains.java in twilio-java
}}
@Override
public ResourceSet<{{resourceName}}> read(final TwilioRestClient client) {
public
{{#vendorExtensions.x-response-datatype}} {{{vendorExtensions.x-response-datatype}}} {{/vendorExtensions.x-response-datatype}}
{{^vendorExtensions.x-response-datatype}} ResourceSet<{{resourceName}}> {{/vendorExtensions.x-response-datatype}}
read(final TwilioRestClient client) {
return new ResourceSet<>(this, client, firstPage(client));
}

Expand Down
Loading
Loading