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

Issue-79 - To be able to Add Examples for Parameters, Schemas ans Request #188

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
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
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public ApiConfiguration mergeWithCommonApiConfiguration(final CommonApiConfigura
merged.extraSchemaClasses = copy.extraSchemaClasses;
merged.customResponseTypeAnnotation = copy.customResponseTypeAnnotation;
merged.defaultErrors = copy.defaultErrors;
merged.customExamples = copy.customExamples;
merged.openapiModels = copy.openapiModels;
merged.modelsAssociations = copy.modelsAssociations;
merged.defaultNonNullableFields = copy.defaultNonNullableFields;
Expand Down Expand Up @@ -166,6 +167,9 @@ public ApiConfiguration mergeWithCommonApiConfiguration(final CommonApiConfigura
if(defaultErrors != null) {
merged.setDefaultErrors(defaultErrors);
}
if(customExamples != null) {
merged.setCustomExamples(customExamples);
}
if(openapiModels != null) {
merged.setOpenapiModels(openapiModels);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ public class CommonApiConfiguration {
@Parameter
protected String defaultErrors;

@Parameter
protected String customExamples;

@Parameter
protected String openapiModels;

Expand Down Expand Up @@ -129,6 +132,7 @@ public CommonApiConfiguration(final CommonApiConfiguration commonApiConfiguratio
this.library = commonApiConfiguration.library;
this.customResponseTypeAnnotation = commonApiConfiguration.customResponseTypeAnnotation;
this.defaultErrors = commonApiConfiguration.defaultErrors;
this.customExamples = commonApiConfiguration.customExamples;
this.openapiModels = commonApiConfiguration.openapiModels;
this.modelsAssociations = commonApiConfiguration.modelsAssociations;
this.defaultNonNullableFields = commonApiConfiguration.defaultNonNullableFields;
Expand Down Expand Up @@ -352,6 +356,14 @@ public void setDefaultErrors(final String defaultErrors) {
this.defaultErrors = defaultErrors;
}

public String getCustomExamples() {
return customExamples;
}

public void setCustomExamples(String customExamples) {
this.customExamples = customExamples;
}

public String getOpenapiModels() {
return openapiModels;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,27 @@ public static void mergeInternal(final JsonNode mutatedNode,
}
}

/**In the case that the Json Node is a Array and it's needed to merge all elements as a one object*/
public static JsonNode mergeJsonArray (JsonNode jsonArray){
ObjectNode mergedNode = jsonObjectMapper.createObjectNode();
if (jsonArray.isArray()) {
for (JsonNode jsonNode : jsonArray) {
if (jsonNode.isObject()) {
jsonNode.fields().forEachRemaining(entry -> {
String fieldName = entry.getKey();
JsonNode fieldValue = entry.getValue();
mergedNode.set(fieldName, fieldValue);
});
}
}
}
return mergedNode;
}
/**Create a new Json Node and then add the jsonNode as a Child*/
public static JsonNode encapsulate (String name, JsonNode jsonNode){
ObjectNode encapsulatedNode = jsonObjectMapper.createObjectNode();
encapsulatedNode.put(name, jsonNode);
return encapsulatedNode;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ public class YamlWriter {

private Optional<JsonNode> freefields = Optional.empty();
private Map<String, JsonNode> defaultErrors;
private Map<String, JsonNode> parameterCustomExamples;
private Map<String, JsonNode> schemaCustomExamples;
private Map<String, JsonNode> requestBodycustomExamples;
private Map<String, JsonNode> responseCustomExamples;

public YamlWriter(final MavenProject mavenProject, final ApiConfiguration apiConfiguration) {
this.apiConfiguration = apiConfiguration;
Expand Down Expand Up @@ -121,6 +125,11 @@ public void write(final File file, final TagLibrary tagLibrary) throws IOExcepti
iterator.forEachRemaining(entry -> defaultErrors.put(entry.getKey(), entry.getValue()));
}

final Optional<JsonNode> customExamplesNode = JsonParserUtils.parse(
CommonParserUtils.getContentFromFileOrText(mavenProject, apiConfiguration.getCustomExamples()));

populateExamplesNodes(customExamplesNode);

final Specification specification = new Specification();
final Info info = new Info(mavenProject.getName(), mavenProject.getVersion(), freefields);
specification.setInfo(info);
Expand Down Expand Up @@ -259,7 +268,7 @@ private Map<String, Map<String, Operation>> createPaths(final TagLibrary tagLibr
parameterElement.setIn(parameter.getLocation().toString().toLowerCase(Locale.ENGLISH));
parameterElement.setRequired(parameter.isRequired());
final Property schema = new Property(Content.fromDataObject(parameter).getSchema());

addExamplesInParameterIfExists(operation.getOperationId(),parameter.getName(), schema);
// array in path parameters are not supported
if(OpenApiDataType.ARRAY == parameter.getOpenApiResolvedType().getType()
&& ParameterLocation.PATH == parameter.getLocation()) {
Expand Down Expand Up @@ -314,6 +323,7 @@ private Map<String, Map<String, Operation>> createPaths(final TagLibrary tagLibr
final RequestBody requestBody = new RequestBody();
operation.setRequestBody(requestBody);
final Content requestBodyContent = Content.fromDataObject(body);
addExamplesInBodyIfExists(operation.getOperationId(),requestBodyContent);
if(body.getFormats() != null) {
for(final String format : body.getFormats()) {
requestBody.getContent().put(format, requestBodyContent);
Expand All @@ -337,7 +347,6 @@ private Map<String, Map<String, Operation>> createPaths(final TagLibrary tagLibr
"Parameter documentation found for endpoint body " + body.getName() + " ? "
+ parameterDoc.isPresent());
}

}

// -------------------------
Expand All @@ -348,6 +357,7 @@ private Map<String, Map<String, Operation>> createPaths(final TagLibrary tagLibr
response.setCode(endpoint.getResponseCode(), apiConfiguration.getDefaultSuccessfulOperationDescription());
if(endpoint.getResponseObject() != null) {
final Content responseContent = Content.fromDataObject(endpoint.getResponseObject());
addExamplesInResponseIfExists(operation.getOperationId(), responseContent);
if(endpoint.getResponseFormats() != null) {
for(final String format : endpoint.getResponseFormats()) {
response.getContent().put(format, responseContent);
Expand Down Expand Up @@ -408,6 +418,7 @@ private Map<String, Object> createSchemaSection(final TagLibrary library) {
for(final DataObject dataObject : ordered) {
final Set<String> exploredSignatures = new HashSet<>();
final Schema schema = new Schema(dataObject, true, exploredSignatures, null, null);
addExamplesInSchemaIfExists(dataObject,schema);
schemas.put(dataObject.getOpenApiResolvedType().isCompleteNode() ? dataObject.getOpenApiResolvedType().getModelName()
: dataObject.getSchemaReferenceName(), schema);
}
Expand All @@ -419,5 +430,90 @@ private Map<String, Object> createSchemaSection(final TagLibrary library) {
}
return schemas;
}

private void populateExamplesNodes(Optional<JsonNode> customExamplesNode) {
if(customExamplesNode.isPresent()) {
parameterCustomExamples = new LinkedHashMap<>();
schemaCustomExamples = new LinkedHashMap<>();
requestBodycustomExamples = new LinkedHashMap<>();
responseCustomExamples = new LinkedHashMap<>();

JsonNode parameterNode = customExamplesNode.get().get("PARAMETER");
JsonNode schemaNode = customExamplesNode.get().get("SCHEMA");
JsonNode requestBodyNode = customExamplesNode.get().get("REQUESTBODY");
JsonNode responseNode = customExamplesNode.get().get("RESPONSE");

if (parameterNode != null) {
final Iterator<Map.Entry<String, JsonNode>> iterator = parameterNode.fields();
iterator.forEachRemaining(entry -> parameterCustomExamples.put(entry.getKey(), entry.getValue()));
}

if (schemaNode != null) {
final Iterator<Map.Entry<String, JsonNode>> iterator = schemaNode.fields();
iterator.forEachRemaining(entry -> schemaCustomExamples.put(entry.getKey(), entry.getValue()));
}

if (requestBodyNode != null) {
final Iterator<Map.Entry<String, JsonNode>> iterator = requestBodyNode.fields();
iterator.forEachRemaining(entry -> requestBodycustomExamples.put(entry.getKey(), entry.getValue()));
}

if (responseNode != null) {
final Iterator<Map.Entry<String, JsonNode>> iterator = responseNode.fields();
iterator.forEachRemaining(entry -> responseCustomExamples.put(entry.getKey(), entry.getValue()));
}
}
}

private void addExamplesInParameterIfExists(String operationId, String name, Schema property) {
if (parameterCustomExamples!=null && property !=null){
JsonNode operationNode = parameterCustomExamples.get(operationId);
if (operationNode != null) {
addExampleInSchema(operationNode.get(name), property);
}
}
}
private void addExamplesInSchemaIfExists(DataObject dataObject, Schema schema) {
if (schemaCustomExamples!=null && schema!=null){
String simpleName = dataObject.getJavaClass().getSimpleName();
addExampleInSchema(schemaCustomExamples.get(simpleName), schema);
}
}

private void addExampleInSchema(JsonNode example, Schema property) {
if (example!=null) {
property.setExample(example);
}
}



private void addExamplesInBodyIfExists(String operationId, Content requestBodyContent) {
addExampleInContent(requestBodycustomExamples, operationId, requestBodyContent);
}

private void addExamplesInResponseIfExists(String operationId, Content responseBodyContent) {
addExampleInContent(responseCustomExamples, operationId, responseBodyContent);
}

private void addExampleInContent(Map<String, JsonNode> customExamples, String operationId,
Content content) {
if (customExamples != null) {
JsonNode example = customExamples.get(operationId);
if (example != null && content != null) {
if (example.isArray()) {
content.setExamples(JsonParserUtils.mergeJsonArray(example));
} else {
/**In the OpenApi the $ref can only by used inside the examples tag, thus needing to have a inner object*/
if (example.get("$ref")!=null){
content.setExamples(JsonParserUtils.encapsulate("ref", example));
}else {
content.setExample(example);
}
}
}
}
}

}

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.kbuntrock.yaml.model;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.github.kbuntrock.model.DataObject;
import io.github.kbuntrock.model.ParameterObject;
import io.github.kbuntrock.utils.OpenApiDataType;
Expand All @@ -13,8 +15,14 @@

public class Content {

@JsonIgnore
private Schema schema;

@JsonIgnore
protected Object example;
@JsonIgnore
protected Object examples;

public static Content fromDataObject(final ParameterObject parameterObject) {

final Content content = fromDataObject((DataObject) parameterObject);
Expand Down Expand Up @@ -55,4 +63,36 @@ public Schema getSchema() {
return schema;
}

public Object getExample() {
return example;
}

public void setExample(Object example) {
this.example = example;
}

public Object getExamples() {
return examples;
}

public void setExamples(Object examples) {
this.examples = examples;
}

@JsonAnyGetter
public Map<String, Object> getJsonObject() {
final Map<String, Object> map = new LinkedHashMap<>();
if(schema != null) {
map.put("schema", schema);
}
if(example != null ) {
map.put("example", example);
}
if(examples != null ) {
map.put("examples", examples);
}

return map;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ public class Schema {
@JsonIgnore
protected Map<String, Property> properties;
@JsonIgnore
protected Object example;
@JsonIgnore
protected Object examples;
@JsonIgnore
protected List<String> enumValues;
@JsonIgnore
protected List<String> enumNames;
Expand Down Expand Up @@ -364,6 +368,22 @@ public void setProperties(final Map<String, Property> properties) {
this.properties = properties;
}

public Object getExample() {
return example;
}

public void setExample(Object example) {
this.example = example;
}

public Object getExamples() {
return examples;
}

public void setExamples(Object examples) {
this.examples = examples;
}

public List<String> getEnumValues() {
return enumValues;
}
Expand Down Expand Up @@ -456,6 +476,12 @@ public Map<String, Object> getJsonObject() {
if(uniqueItems != null) {
map.put("uniqueItems", uniqueItems);
}
if(example != null ) {
map.put("example", example);
}
if(examples != null ) {
map.put("examples", examples);
}
return map;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import io.github.kbuntrock.resources.endpoint.enumeration.TestEnumeration6Controller;
import io.github.kbuntrock.resources.endpoint.enumeration.TestEnumeration7Controller;
import io.github.kbuntrock.resources.endpoint.error.SameOperationController;
import io.github.kbuntrock.resources.endpoint.examples.TestExamplesController;
import io.github.kbuntrock.resources.endpoint.file.FileUploadController;
import io.github.kbuntrock.resources.endpoint.file.StreamResponseController;
import io.github.kbuntrock.resources.endpoint.generic.ExtendsGenericObjectMap;
Expand Down Expand Up @@ -82,12 +83,14 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.apache.commons.io.IOUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
Expand Down Expand Up @@ -944,6 +947,25 @@ public void nested_dtos() throws MojoFailureException, IOException, MojoExecutio
checkGenerationResult(mojo.documentProject());
}

@Test
public void custom_examples_schema1() throws MojoFailureException, IOException, MojoExecutionException {

final DocumentationMojo mojo = createBasicMojo(TestExamplesController.class.getCanonicalName());

ApiConfiguration apiConfiguration = mojo.getApis().get(0);

final InputStream freeFieldsFileStream = this.getClass().getClassLoader()
.getResourceAsStream("ut/JavadocParserTest/freeFields/custom_examples_free_fields.txt");
apiConfiguration.setFreeFields(IOUtils.toString(freeFieldsFileStream, StandardCharsets.UTF_8));


final InputStream customExamplesFileStream = this.getClass().getClassLoader()
.getResourceAsStream("ut/JavadocParserTest/freeFields/custom_examples.txt");
apiConfiguration.setCustomExamples(IOUtils.toString(customExamplesFileStream, StandardCharsets.UTF_8));

checkGenerationResult(mojo.documentProject());
}


private ScanResult scanResult(Class<?> clazz) {
return new ClassGraph()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.github.kbuntrock.resources.dto.example;

public enum ExampleEnum {
EXAMPLE_A,
EXAMPLE_B,
EXAMPLE_C
}
Loading