Skip to content

Commit

Permalink
Implemented AddResponseDefinitionCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
EricWittmann committed Jan 9, 2025
1 parent 5c0414e commit 1a613f2
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 9 deletions.
5 changes: 5 additions & 0 deletions src/main/java/io/apicurio/datamodels/cmd/CommandFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.apicurio.datamodels.cmd.commands.AddChannelItemCommand;
import io.apicurio.datamodels.cmd.commands.AddExampleCommand;
import io.apicurio.datamodels.cmd.commands.AddPathItemCommand;
import io.apicurio.datamodels.cmd.commands.AddResponseDefinitionCommand;
import io.apicurio.datamodels.cmd.commands.ChangeContactCommand;
import io.apicurio.datamodels.cmd.commands.ChangeDescriptionCommand;
import io.apicurio.datamodels.cmd.commands.ChangeLicenseCommand;
Expand Down Expand Up @@ -66,6 +67,10 @@ public static ICommand createAddHeaderExampleCommand(OpenApiMediaType mediaType,
return new AddExampleCommand((OpenApiExamplesParent) mediaType, example, exampleName, exampleSummary, exampleDescription);
}

public static ICommand createAddResponseDefinitionCommand(String definitionName, ObjectNode from) {
return new AddResponseDefinitionCommand(definitionName, from);
}

public static <T> ICommand createChangePropertyCommand(Node node, String property, T newValue) {
return new ChangePropertyCommand<T>(node, property, newValue);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
package io.apicurio.datamodels.cmd.commands;

import com.fasterxml.jackson.databind.node.ObjectNode;
import io.apicurio.datamodels.Library;
import io.apicurio.datamodels.cmd.AbstractCommand;
import io.apicurio.datamodels.models.Document;
import io.apicurio.datamodels.models.openapi.OpenApiDocument;
import io.apicurio.datamodels.models.openapi.OpenApiResponse;
import io.apicurio.datamodels.models.openapi.v20.OpenApi20Document;
import io.apicurio.datamodels.models.openapi.v20.OpenApi20Response;
import io.apicurio.datamodels.models.openapi.v30.OpenApi30Document;
import io.apicurio.datamodels.models.openapi.v30.OpenApi30Response;
import io.apicurio.datamodels.models.openapi.v31.OpenApi31Document;
import io.apicurio.datamodels.models.openapi.v31.OpenApi31Response;
import io.apicurio.datamodels.util.LoggerUtil;
import io.apicurio.datamodels.util.ModelTypeUtil;

/**
* A command used to add a new response definition in a document. Source for the new
* definition must be provided. This source will be converted to an openapi
* response definition object and then added to the data model.
* @author eric.wittmann@gmail.com
*/
public class AddResponseDefinitionCommand extends AbstractCommand {

public boolean _defExisted;
public String _newDefinitionName;
public ObjectNode _newDefinitionObj;
public boolean _nullDefinitionsParent;

private transient AddResponseDefinitionCommandHelper _helper;

public AddResponseDefinitionCommand() {
}

public AddResponseDefinitionCommand(String definitionName, ObjectNode obj) {
this._newDefinitionName = definitionName;
this._newDefinitionObj = obj;
}

/**
* @see io.apicurio.datamodels.cmd.ICommand#execute(Document)
*/
@Override
public void execute(Document document) {
LoggerUtil.info("[AddResponseDefinitionCommand] Executing.");
this._helper = createHelper(document);

OpenApiDocument doc = (OpenApiDocument) document;

// Do nothing if the definition already exists.
if (this._helper.defExists(doc)) {
LoggerUtil.info("[AddResponseDefinitionCommand] Definition with name %s already exists.", this._newDefinitionName);
this._defExisted = true;
return;
}

this._nullDefinitionsParent = this._helper.prepareDocumentForDef(doc);

OpenApiResponse definition = this._helper.createResponseDefinition(doc);
this._helper.addDefinition(doc, definition);
}

/**
* @see io.apicurio.datamodels.cmd.ICommand#undo(Document)
*/
@Override
public void undo(Document document) {
LoggerUtil.info("[AddResponseDefinitionCommand] Reverting.");
if (this._defExisted) {
return;
}

this._helper = createHelper(document);

OpenApiDocument doc = (OpenApiDocument) document;
this._helper.removeDefinition(doc);
}

private AddResponseDefinitionCommandHelper createHelper(Document document) {
if (ModelTypeUtil.isOpenApi2Model(document)) {
return new OpenApi20Helper();
}
if (ModelTypeUtil.isOpenApi30Model(document)) {
return new OpenApi30Helper();
}
if (ModelTypeUtil.isOpenApi31Model(document)) {
return new OpenApi31Helper();
}
throw new RuntimeException("Unsupported model type: " + document.root().modelType());
}

private interface AddResponseDefinitionCommandHelper {

boolean defExists(OpenApiDocument document);
boolean prepareDocumentForDef(OpenApiDocument document);
OpenApiResponse createResponseDefinition(OpenApiDocument document);
void addDefinition(OpenApiDocument document, OpenApiResponse definition);
void removeDefinition(OpenApiDocument document);

}

private class OpenApi20Helper implements AddResponseDefinitionCommandHelper {
@Override
public boolean defExists(OpenApiDocument document) {
OpenApi20Document doc = (OpenApi20Document) document;
if (!isNullOrUndefined(doc.getResponses())) {
return !isNullOrUndefined(doc.getResponses().getItem(_newDefinitionName));
}
return false;
}

@Override
public boolean prepareDocumentForDef(OpenApiDocument document) {
OpenApi20Document doc20 = (OpenApi20Document) document;
if (isNullOrUndefined(doc20.getResponses())) {
doc20.setResponses(doc20.createResponseDefinitions());
return true;
}
return false;
}

@Override
public OpenApiResponse createResponseDefinition(OpenApiDocument document) {
OpenApi20Document doc20 = (OpenApi20Document) document;
OpenApi20Response definition = doc20.getResponses().createResponse();
Library.readNode(_newDefinitionObj, definition);
return definition;
}

@Override
public void addDefinition(OpenApiDocument document, OpenApiResponse definition) {
OpenApi20Document doc20 = (OpenApi20Document) document;
OpenApi20Response def20 = (OpenApi20Response) definition;
doc20.getResponses().addItem(_newDefinitionName, def20);
}

@Override
public void removeDefinition(OpenApiDocument document) {
OpenApi20Document doc20 = (OpenApi20Document) document;
if (_nullDefinitionsParent) {
doc20.setResponses(null);
} else {
doc20.getResponses().removeItem(_newDefinitionName);
}
}
}

private class OpenApi30Helper implements AddResponseDefinitionCommandHelper {
@Override
public boolean defExists(OpenApiDocument document) {
OpenApi30Document doc30 = (OpenApi30Document) document;
if (isNullOrUndefined(doc30.getComponents())) {
return false;
}
return !isNullOrUndefined(doc30.getComponents().getResponses().get(_newDefinitionName));
}

@Override
public boolean prepareDocumentForDef(OpenApiDocument document) {
OpenApi30Document doc30 = (OpenApi30Document) document;
if (isNullOrUndefined(doc30.getComponents())) {
doc30.setComponents(doc30.createComponents());
return true;
}
return false;
}

@Override
public OpenApiResponse createResponseDefinition(OpenApiDocument document) {
OpenApi30Document doc30 = (OpenApi30Document) document;
OpenApi30Response definition = (OpenApi30Response) doc30.getComponents().createResponse();
Library.readNode(_newDefinitionObj, definition);
return definition;
}

@Override
public void addDefinition(OpenApiDocument document, OpenApiResponse definition) {
OpenApi30Document doc30 = (OpenApi30Document) document;
doc30.getComponents().addResponse(_newDefinitionName, definition);
}

@Override
public void removeDefinition(OpenApiDocument document) {
OpenApi30Document doc30 = (OpenApi30Document) document;
if (_nullDefinitionsParent) {
doc30.setComponents(null);
} else {
doc30.getComponents().removeResponse(_newDefinitionName);
}
}
}

private class OpenApi31Helper implements AddResponseDefinitionCommandHelper {
@Override
public boolean defExists(OpenApiDocument document) {
OpenApi31Document doc31 = (OpenApi31Document) document;
if (isNullOrUndefined(doc31.getComponents())) {
return false;
}
return !isNullOrUndefined(doc31.getComponents().getResponses().get(_newDefinitionName));
}

@Override
public boolean prepareDocumentForDef(OpenApiDocument document) {
OpenApi31Document doc31 = (OpenApi31Document) document;
if (isNullOrUndefined(doc31.getComponents())) {
doc31.setComponents(doc31.createComponents());
return true;
}
return false;
}

@Override
public OpenApiResponse createResponseDefinition(OpenApiDocument document) {
OpenApi31Document doc31 = (OpenApi31Document) document;
OpenApi31Response definition = (OpenApi31Response) doc31.getComponents().createResponse();
Library.readNode(_newDefinitionObj, definition);
return definition;
}

@Override
public void addDefinition(OpenApiDocument document, OpenApiResponse definition) {
OpenApi31Document doc31 = (OpenApi31Document) document;
doc31.getComponents().addResponse(_newDefinitionName, definition);
}

@Override
public void removeDefinition(OpenApiDocument document) {
OpenApi31Document doc31 = (OpenApi31Document) document;
if (_nullDefinitionsParent) {
doc31.setComponents(null);
} else {
doc31.getComponents().removeResponse(_newDefinitionName);
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@

package io.apicurio.datamodels.validation.rules.mutex;

import java.util.List;

import io.apicurio.datamodels.models.Node;
import io.apicurio.datamodels.models.Operation;
import io.apicurio.datamodels.models.openapi.OpenApiParameter;
import io.apicurio.datamodels.models.openapi.OpenApiParametersParent;
import io.apicurio.datamodels.models.openapi.OpenApiPathItem;
import io.apicurio.datamodels.util.NodeUtil;
import io.apicurio.datamodels.validation.ValidationRule;
import io.apicurio.datamodels.validation.ValidationRuleMetaData;

import java.util.List;

/**
* Implements the Body and Form Data Mutual Exclusivity Rule.
* @author eric.wittmann@gmail.com
Expand All @@ -40,9 +40,9 @@ public OasBodyAndFormDataMutualExclusivityRule(ValidationRuleMetaData ruleInfo)
super(ruleInfo);
}

private void visitParameterParent(Node paramParent) {
private void visitParameterParent(OpenApiParametersParent paramParent) {
@SuppressWarnings("unchecked")
List<OpenApiParameter> parameters = (List<OpenApiParameter>) NodeUtil.getNodeProperty(paramParent, "parameters");
List<OpenApiParameter> parameters = paramParent.getParameters();
if (hasValue(parameters)) {
boolean hasBodyParam = false;
boolean hasFormDataParam = false;
Expand All @@ -54,7 +54,7 @@ private void visitParameterParent(Node paramParent) {
hasFormDataParam = true;
}
}
this.reportIf(hasBodyParam && hasFormDataParam, paramParent, "in", map());
this.reportIf(hasBodyParam && hasFormDataParam, (Node) paramParent, "in", map());
}
}

Expand All @@ -63,15 +63,15 @@ private void visitParameterParent(Node paramParent) {
*/
@Override
public void visitOperation(Operation node) {
visitParameterParent(node);
visitParameterParent((OpenApiParametersParent) node);
}

/**
* @see io.apicurio.datamodels.models.visitors.CombinedVisitorAdapter#visitPathItem(io.apicurio.datamodels.models.openapi.OpenApiPathItem)
*/
@Override
public void visitPathItem(OpenApiPathItem node) {
visitParameterParent(node);
visitParameterParent((OpenApiParametersParent) node);
}

}
2 changes: 2 additions & 0 deletions src/main/ts/src/io/apicurio/datamodels/util/CommandUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {NodePathUtil} from "../paths/NodePathUtil";
import {AddChannelItemCommand} from "../cmd/commands/AddChannelItemCommand";
import {AddExampleCommand} from "../cmd/commands/AddExampleCommand";
import {AddPathItemCommand} from "../cmd/commands/AddPathItemCommand";
import {AddResponseDefinitionCommand} from "../cmd/commands/AddResponseDefinitionCommand";

import {ChangeDescriptionCommand} from "../cmd/commands/ChangeDescriptionCommand";
import {ChangePropertyCommand} from "../cmd/commands/ChangePropertyCommand";
Expand Down Expand Up @@ -46,6 +47,7 @@ const commandSuppliers: { [key: string]: Supplier } = {
"AddChannelItemCommand": () => { return new AddChannelItemCommand(); },
"AddExampleCommand": () => { return new AddExampleCommand(); },
"AddPathItemCommand": () => { return new AddPathItemCommand(); },
"AddResponseDefinitionCommand": () => { return new AddResponseDefinitionCommand(); },

"ChangeDescriptionCommand": () => { return new ChangeDescriptionCommand(); },
"ChangePropertyCommand": () => { return new ChangePropertyCommand(); },
Expand Down
6 changes: 5 additions & 1 deletion src/test/resources/fixtures/cmd/tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,9 @@
{ "name": "[OpenAPI 2] {Add Path Item} - Add Path Item", "test": "commands/add-path-item/openapi-2/add-path-item" },
{ "name": "[OpenAPI 2] {Add Path Item} - Clone Path Item", "test": "commands/add-path-item/openapi-2/clone-path-item" },
{ "name": "[OpenAPI 3] {Add Path Item} - Add Path Item", "test": "commands/add-path-item/openapi-3/add-path-item" },
{ "name": "[OpenAPI 3] {Add Path Item} - Clone Path Item", "test": "commands/add-path-item/openapi-3/clone-path-item" }
{ "name": "[OpenAPI 3] {Add Path Item} - Clone Path Item", "test": "commands/add-path-item/openapi-3/clone-path-item" },
{ "name": "[OpenAPI 2] {Add Response Definition} - Add Response Definition", "test": "commands/add-response-definition/openapi-2/add-response-definition" },
{ "name": "[OpenAPI 2] {Add Response Definition} - Clone Response Definition", "test": "commands/add-response-definition/openapi-2/clone-response-definition" },
{ "name": "[OpenAPI 3] {Add Response Definition} - Add Response Definition", "test": "commands/add-response-definition/openapi-3/add-response-definition" },
{ "name": "[OpenAPI 3] {Add Response Definition} - Clone Response Definition", "test": "commands/add-response-definition/openapi-3/clone-response-definition" }
]

0 comments on commit 1a613f2

Please sign in to comment.