Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
3 changes: 2 additions & 1 deletion bin/configs/java-jersey2-8-oas2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ library: jersey2
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml
additionalProperties:
artifactId: petstore-jersey2-java8
hideGenerationTimestamp: "true"
hideGenerationTimestamp: true
serverPort: "8082"
dateLibrary: java8
useOneOfDiscriminatorLookup: true
3 changes: 2 additions & 1 deletion bin/configs/java-jersey2-8.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ library: jersey2
inputSpec: modules/openapi-generator/src/test/resources/3_0/python-experimental/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml
additionalProperties:
artifactId: petstore-openapi3-jersey2-java8
hideGenerationTimestamp: "true"
hideGenerationTimestamp: true
serverPort: "8082"
dateLibrary: java8
useOneOfDiscriminatorLookup: true
Original file line number Diff line number Diff line change
Expand Up @@ -1649,10 +1649,24 @@ public void setAdditionalModelTypeAnnotations(final List<String> additionalModel

@Override
protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Schema schema) {
super.addAdditionPropertiesToCodeGenModel(codegenModel, schema);
if (!supportsAdditionalPropertiesWithComposedSchema) {
// The additional (undeclared) propertiees are modeled in Java as a HashMap.
//
// 1. supportsAdditionalPropertiesWithComposedSchema is set to false:
// The generated model class extends from the HashMap. That does not work
// with composed schemas that also use a discriminator because the model class
// is supposed to extend from the generated parent model class.
// 2. supportsAdditionalPropertiesWithComposedSchema is set to true:
// The HashMap is a field.
super.addAdditionPropertiesToCodeGenModel(codegenModel, schema);
}

// See https://github.com/OpenAPITools/openapi-generator/pull/1729#issuecomment-449937728
codegenModel.additionalPropertiesType = getSchemaType(getAdditionalProperties(schema));
addImport(codegenModel, codegenModel.additionalPropertiesType);
Schema s = getAdditionalProperties(schema);
// 's' may be null if 'additionalProperties: false' in the OpenAPI schema.
if (s != null) {
codegenModel.additionalPropertiesType = getSchemaType(s);
addImport(codegenModel, codegenModel.additionalPropertiesType);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ public JavaClientCodegen() {
// inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values,
// and the discriminator mapping schemas in the OAS document.
this.setLegacyDiscriminatorBehavior(false);

}

@Override
Expand Down Expand Up @@ -371,6 +372,14 @@ public void processOpts() {
}
supportingFiles.add(new SupportingFile("AbstractOpenApiSchema.mustache", (sourceFolder + File.separator + modelPackage().replace('.', File.separatorChar)).replace('/', File.separatorChar), "AbstractOpenApiSchema.java"));
forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON);

// Composed schemas can have the 'additionalProperties' keyword, as specified in JSON schema.
// In principle, this should be enabled by default for all code generators. However due to limitations
// in other code generators, support needs to be enabled on a case-by-case basis.
// The flag below should be set for all Java libraries, but the templates need to be ported
// one by one for each library.
supportsAdditionalPropertiesWithComposedSchema = true;

} else if (NATIVE.equals(getLibrary())) {
setJava8Mode(true);
additionalProperties.put("java8", "true");
Expand Down Expand Up @@ -587,38 +596,6 @@ public int compare(CodegenParameter one, CodegenParameter another) {
objs = AbstractJavaJAXRSServerCodegen.jaxrsPostProcessOperations(objs);
}

if (JERSEY2.equals(getLibrary())) {
// index the model
HashMap<String, CodegenModel> modelMaps = new HashMap<String, CodegenModel>();
for (Object o : allModels) {
HashMap<String, Object> h = (HashMap<String, Object>) o;
CodegenModel m = (CodegenModel) h.get("model");
modelMaps.put(m.classname, m);
}

// check if return type is oneOf/anyeOf model
Map<String, Object> operations = (Map<String, Object>) objs.get("operations");
List<CodegenOperation> operationList = (List<CodegenOperation>) operations.get("operation");
for (CodegenOperation op : operationList) {
if (op.returnType != null) {
// look up the model to see if it's anyOf/oneOf
if (modelMaps.containsKey(op.returnType) && modelMaps.get(op.returnType) != null) {
CodegenModel cm = modelMaps.get(op.returnType);

if (cm.oneOf != null && !cm.oneOf.isEmpty()) {
op.vendorExtensions.put("x-java-return-type-one-of", true);
}

if (cm.anyOf != null && !cm.anyOf.isEmpty()) {
op.vendorExtensions.put("x-java-return-type-any-of", true);
}
} else {
//LOGGER.error("cannot lookup model " + op.returnType);
}
}
}
}

return objs;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,29 @@ public abstract class AbstractOpenApiSchema {
this.isNullable = isNullable;
}

/***
* Get the list of schemas allowed to be stored in this object
/**
* Get the list of oneOf/anyOf composed schemas allowed to be stored in this object
*
* @return an instance of the actual schema/object
*/
public abstract Map<String, GenericType> getSchemas();

/***
/**
* Get the actual instance
*
* @return an instance of the actual schema/object
*/
@JsonValue
public Object getActualInstance() {return instance;}

/***
/**
* Set the actual instance
*
* @param instance the actual instance of the schema/object
*/
public void setActualInstance(Object instance) {this.instance = instance;}

/***
/**
* Get the schema type (e.g. anyOf, oneOf)
*
* @return the schema type
Expand Down Expand Up @@ -101,7 +101,7 @@ public abstract class AbstractOpenApiSchema {
return Objects.hash(instance, isNullable, schemaType);
}

/***
/**
* Is nullalble
*
* @return true if it's nullable
Expand All @@ -113,4 +113,7 @@ public abstract class AbstractOpenApiSchema {
return Boolean.FALSE;
}
}

{{>libraries/jersey2/additional_properties}}

}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ import {{invokerPackage}}.auth.ApiKeyAuth;
{{#hasOAuthMethods}}
import {{invokerPackage}}.auth.OAuth;
{{/hasOAuthMethods}}
import {{invokerPackage}}.model.AbstractOpenApiSchema;

{{>generatedAnnotation}}
public class ApiClient {
Expand Down Expand Up @@ -894,67 +893,6 @@ public class ApiClient {
}
}

public AbstractOpenApiSchema deserializeSchemas(Response response, AbstractOpenApiSchema schema) throws ApiException{

Object result = null;
int matchCounter = 0;
ArrayList<String> matchSchemas = new ArrayList<>();

if (schema.isNullable()) {
response.bufferEntity();
if ("{}".equals(String.valueOf(response.readEntity(String.class))) ||
"".equals(String.valueOf(response.readEntity(String.class)))) {
// schema is nullable and the response body is {} or empty string
return schema;
}
}

for (Map.Entry<String, GenericType> entry : schema.getSchemas().entrySet()) {
String schemaName = entry.getKey();
GenericType schemaType = entry.getValue();

if (schemaType instanceof GenericType) { // model
try {
Object deserializedObject = deserialize(response, schemaType);
if (deserializedObject != null) {
result = deserializedObject;
matchCounter++;

if ("anyOf".equals(schema.getSchemaType())) {
break;
} else if ("oneOf".equals(schema.getSchemaType())) {
matchSchemas.add(schemaName);
} else {
throw new ApiException("Unknowe type found while expecting anyOf/oneOf:" + schema.getSchemaType());
}
} else {
// failed to deserialize the response in the schema provided, proceed to the next one if any
}
} catch (Exception ex) {
// failed to deserialize, do nothing and try next one (schema)
// Logging the error may be useful to troubleshoot why a payload fails to match
// the schema.
log.log(Level.FINE, "Input data does not match schema '" + schemaName + "'", ex);
}
} else {// unknown type
throw new ApiException(schemaType.getClass() + " is not a GenericType and cannot be handled properly in deserialization.");
}

}

if (matchCounter > 1 && "oneOf".equals(schema.getSchemaType())) {// more than 1 match for oneOf
throw new ApiException("Response body is invalid as it matches more than one schema (" + StringUtil.join(matchSchemas, ", ") + ") defined in the oneOf model: " + schema.getClass().getName());
} else if (matchCounter == 0) { // fail to match any in oneOf/anyOf schemas
throw new ApiException("Response body is invalid as it does not match any schemas (" + StringUtil.join(schema.getSchemas().keySet(), ", ") + ") defined in the oneOf/anyOf model: " + schema.getClass().getName());
} else { // only one matched
schema.setActualInstance(result);
return schema;
}

}



/**
* Deserialize response body to Java object according to the Content-Type.
* @param <T> Type
Expand Down Expand Up @@ -1062,7 +1000,6 @@ public class ApiClient {
* @param contentType The request's Content-Type header
* @param authNames The authentications to apply
* @param returnType The return type into which to deserialize the response
* @param schema An instance of the response that uses oneOf/anyOf
* @return The response body in type of string
* @throws ApiException API exception
*/
Expand All @@ -1078,8 +1015,7 @@ public class ApiClient {
String accept,
String contentType,
String[] authNames,
GenericType<T> returnType,
AbstractOpenApiSchema schema)
GenericType<T> returnType)
throws ApiException {

// Not using `.target(targetURL).path(path)` below,
Expand Down Expand Up @@ -1178,12 +1114,10 @@ public class ApiClient {
if (response.getStatusInfo() == Status.NO_CONTENT) {
return new ApiResponse<T>(statusCode, responseHeaders);
} else if (response.getStatusInfo().getFamily() == Status.Family.SUCCESSFUL) {
if (returnType == null) return new ApiResponse<T>(statusCode, responseHeaders);
else if (schema == null) {
if (returnType == null) {
return new ApiResponse<T>(statusCode, responseHeaders);
} else {
return new ApiResponse<T>(statusCode, responseHeaders, deserialize(response, returnType));
} else { // oneOf/anyOf
return new ApiResponse<T>(
statusCode, responseHeaders, (T) deserializeSchemas(response, schema));
}
} else {
String message = "error";
Expand Down Expand Up @@ -1229,8 +1163,8 @@ public class ApiClient {
* @deprecated Add qualified name of the operation as a first parameter.
*/
@Deprecated
public <T> ApiResponse<T> invokeAPI(String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String accept, String contentType, String[] authNames, GenericType<T> returnType, AbstractOpenApiSchema schema) throws ApiException {
return invokeAPI(null, path, method, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames, returnType, schema);
public <T> ApiResponse<T> invokeAPI(String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String accept, String contentType, String[] authNames, GenericType<T> returnType) throws ApiException {
return invokeAPI(null, path, method, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames, returnType);
}

/**
Expand Down
Loading