From 89200f3d67d4605d2a11db0417663afc203bb431 Mon Sep 17 00:00:00 2001 From: Eric Wittmann Date: Fri, 10 Jan 2025 08:57:22 -0500 Subject: [PATCH] Implemented AddSchemaDefinitionCommand and AddSecurityRequirementCommand --- .../datamodels/cmd/CommandFactory.java | 22 ++ .../commands/AddSchemaDefinitionCommand.java | 241 ++++++++++++++++++ .../AddSecurityRequirementCommand.java | 122 +++++++++ .../DeleteAllSecurityRequirementsCommand.java | 181 +------------ .../OpenApi20to30TransformationVisitor.java | 10 +- .../transform/SecurityRequirementCreator.java | 118 --------- ...tyRequirementUniquenessValidationRule.java | 7 +- ...SecurityRequirementsFromParentVisitor.java | 32 --- .../specs/asyncapi/asyncapi-2.0.yaml | 5 + .../specs/asyncapi/asyncapi-2.1.yaml | 5 + .../specs/asyncapi/asyncapi-2.2.yaml | 5 + .../specs/asyncapi/asyncapi-2.3.yaml | 5 + .../specs/asyncapi/asyncapi-2.4.yaml | 5 + .../specs/asyncapi/asyncapi-2.5.yaml | 5 + .../specs/asyncapi/asyncapi-2.6.yaml | 5 + .../resources/specs/openapi/openapi-2.0.yaml | 6 + .../resources/specs/openapi/openapi-3.0.yaml | 6 + .../resources/specs/openapi/openapi-3.1.yaml | 6 + .../apicurio/datamodels/util/CommandUtil.ts | 4 + src/test/resources/fixtures/cmd/tests.json | 10 +- 20 files changed, 468 insertions(+), 332 deletions(-) create mode 100755 src/main/java/io/apicurio/datamodels/cmd/commands/AddSchemaDefinitionCommand.java create mode 100755 src/main/java/io/apicurio/datamodels/cmd/commands/AddSecurityRequirementCommand.java delete mode 100755 src/main/java/io/apicurio/datamodels/transform/SecurityRequirementCreator.java delete mode 100755 src/main/java/io/apicurio/datamodels/visitors/SecurityRequirementsFromParentVisitor.java diff --git a/src/main/java/io/apicurio/datamodels/cmd/CommandFactory.java b/src/main/java/io/apicurio/datamodels/cmd/CommandFactory.java index 18bd8a2cc..2b0020ee9 100644 --- a/src/main/java/io/apicurio/datamodels/cmd/CommandFactory.java +++ b/src/main/java/io/apicurio/datamodels/cmd/CommandFactory.java @@ -6,6 +6,8 @@ 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.AddSchemaDefinitionCommand; +import io.apicurio.datamodels.cmd.commands.AddSecurityRequirementCommand; import io.apicurio.datamodels.cmd.commands.ChangeContactCommand; import io.apicurio.datamodels.cmd.commands.ChangeDescriptionCommand; import io.apicurio.datamodels.cmd.commands.ChangeLicenseCommand; @@ -27,10 +29,13 @@ import io.apicurio.datamodels.cmd.commands.DeleteExtensionCommand; import io.apicurio.datamodels.cmd.commands.DeleteLicenseCommand; import io.apicurio.datamodels.cmd.commands.DeleteMediaTypeCommand; +import io.apicurio.datamodels.models.Document; import io.apicurio.datamodels.models.Extensible; import io.apicurio.datamodels.models.Info; import io.apicurio.datamodels.models.Node; import io.apicurio.datamodels.models.Schema; +import io.apicurio.datamodels.models.SecurityRequirement; +import io.apicurio.datamodels.models.SecurityRequirementsParent; import io.apicurio.datamodels.models.asyncapi.AsyncApiServer; import io.apicurio.datamodels.models.openapi.OpenApiDocument; import io.apicurio.datamodels.models.openapi.OpenApiExamplesParent; @@ -71,6 +76,23 @@ public static ICommand createAddResponseDefinitionCommand(String definitionName, return new AddResponseDefinitionCommand(definitionName, from); } + public static ICommand createAddSchemaDefinitionCommand(String definitionName, ObjectNode from) { + return new AddSchemaDefinitionCommand(definitionName, from); + } + + public static ICommand createAddDocumentSecurityRequirementCommand( + OpenApiDocument document, SecurityRequirement requirement) { + return new AddSecurityRequirementCommand((SecurityRequirementsParent) document, requirement); + } + public static ICommand createAddOperationSecurityRequirementCommand( + OpenApiOperation operation, SecurityRequirement requirement) { + return new AddSecurityRequirementCommand((SecurityRequirementsParent) operation, requirement); + } + public static ICommand createAddServerSecurityRequirementCommand( + AsyncApiServer server, SecurityRequirement requirement) { + return new AddSecurityRequirementCommand((SecurityRequirementsParent) server, requirement); + } + public static ICommand createChangePropertyCommand(Node node, String property, T newValue) { return new ChangePropertyCommand(node, property, newValue); } diff --git a/src/main/java/io/apicurio/datamodels/cmd/commands/AddSchemaDefinitionCommand.java b/src/main/java/io/apicurio/datamodels/cmd/commands/AddSchemaDefinitionCommand.java new file mode 100755 index 000000000..ec13f38db --- /dev/null +++ b/src/main/java/io/apicurio/datamodels/cmd/commands/AddSchemaDefinitionCommand.java @@ -0,0 +1,241 @@ +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.OpenApiSchema; +import io.apicurio.datamodels.models.openapi.v20.OpenApi20Document; +import io.apicurio.datamodels.models.openapi.v20.OpenApi20Schema; +import io.apicurio.datamodels.models.openapi.v30.OpenApi30Document; +import io.apicurio.datamodels.models.openapi.v30.OpenApi30Schema; +import io.apicurio.datamodels.models.openapi.v31.OpenApi31Document; +import io.apicurio.datamodels.models.openapi.v31.OpenApi31Schema; +import io.apicurio.datamodels.util.LoggerUtil; +import io.apicurio.datamodels.util.ModelTypeUtil; + +/** + * A command used to add a new definition in a document. Source for the new + * definition must be provided. This source will be converted to an OAS + * definition object and then added to the data model. + * @author eric.wittmann@gmail.com + */ +public class AddSchemaDefinitionCommand extends AbstractCommand { + + public boolean _defExisted; + public String _newDefinitionName; + public ObjectNode _newDefinitionObj; + public boolean _nullDefinitionsParent; + + private transient AddSchemaDefinitionCommandHelper _helper; + + public AddSchemaDefinitionCommand() { + } + + public AddSchemaDefinitionCommand(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("[AddSchemaDefinitionCommand] Executing."); + this._helper = createHelper(document); + + OpenApiDocument doc = (OpenApiDocument) document; + + // Do nothing if the definition already exists. + if (this._helper.defExists(doc)) { + LoggerUtil.info("[AddSchemaDefinitionCommand] Definition with name %s already exists.", this._newDefinitionName); + this._defExisted = true; + return; + } + + this._nullDefinitionsParent = this._helper.prepareDocumentForDef(doc); + + OpenApiSchema definition = this._helper.createSchemaDefinition(doc); + this._helper.addDefinition(doc, definition); + } + + /** + * @see io.apicurio.datamodels.cmd.ICommand#undo(Document) + */ + @Override + public void undo(Document document) { + LoggerUtil.info("[AddSchemaDefinitionCommand] Reverting."); + if (this._defExisted) { + return; + } + + OpenApiDocument doc = (OpenApiDocument) document; + + this._helper.removeDefinition(doc); + } + + private AddSchemaDefinitionCommandHelper 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 AddSchemaDefinitionCommandHelper { + boolean defExists(OpenApiDocument document); + + boolean prepareDocumentForDef(OpenApiDocument document); + + OpenApiSchema createSchemaDefinition(OpenApiDocument document); + + void addDefinition(OpenApiDocument document, OpenApiSchema definition); + + void removeDefinition(OpenApiDocument document); + } + + private class OpenApi20Helper implements AddSchemaDefinitionCommandHelper { + + @Override + public boolean defExists(OpenApiDocument document) { + OpenApi20Document doc20 = (OpenApi20Document) document; + if (isNullOrUndefined(doc20.getDefinitions())) { + return false; + } + return !isNullOrUndefined(doc20.getDefinitions().getItem(_newDefinitionName)); + } + + @Override + public boolean prepareDocumentForDef(OpenApiDocument document) { + OpenApi20Document doc20 = (OpenApi20Document) document; + if (isNullOrUndefined(doc20.getDefinitions())) { + doc20.setDefinitions(doc20.createDefinitions()); + return true; + } + return false; + } + + @Override + public OpenApiSchema createSchemaDefinition(OpenApiDocument document) { + OpenApi20Document doc20 = (OpenApi20Document) document; + OpenApi20Schema definition = doc20.getDefinitions().createSchema(); + Library.readNode(_newDefinitionObj, definition); + return definition; + } + + @Override + public void addDefinition(OpenApiDocument document, OpenApiSchema definition) { + OpenApi20Document doc20 = (OpenApi20Document) document; + OpenApi20Schema def20 = (OpenApi20Schema) definition; + doc20.getDefinitions().addItem(_newDefinitionName, def20); + } + + @Override + public void removeDefinition(OpenApiDocument document) { + OpenApi20Document doc20 = (OpenApi20Document) document; + if (_nullDefinitionsParent) { + doc20.setDefinitions(null); + } else { + doc20.getDefinitions().removeItem(_newDefinitionName); + } + } + } + + private class OpenApi30Helper implements AddSchemaDefinitionCommandHelper { + @Override + public boolean defExists(OpenApiDocument document) { + OpenApi30Document doc30 = (OpenApi30Document) document; + if (isNullOrUndefined(doc30.getComponents())) { + return false; + } + return !isNullOrUndefined(doc30.getComponents().getSchemas().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 OpenApiSchema createSchemaDefinition(OpenApiDocument document) { + OpenApi30Document doc30 = (OpenApi30Document) document; + OpenApi30Schema definition = (OpenApi30Schema) doc30.getComponents().createSchema(); + Library.readNode(_newDefinitionObj, definition); + return definition; + } + + @Override + public void addDefinition(OpenApiDocument document, OpenApiSchema definition) { + OpenApi30Document doc30 = (OpenApi30Document) document; + doc30.getComponents().addSchema(_newDefinitionName, definition); + } + + @Override + public void removeDefinition(OpenApiDocument document) { + OpenApi30Document doc30 = (OpenApi30Document) document; + if (_nullDefinitionsParent) { + doc30.setComponents(null); + } else { + doc30.getComponents().removeSchema(_newDefinitionName); + } + } + } + + private class OpenApi31Helper implements AddSchemaDefinitionCommandHelper { + @Override + public boolean defExists(OpenApiDocument document) { + OpenApi31Document doc31 = (OpenApi31Document) document; + if (isNullOrUndefined(doc31.getComponents())) { + return false; + } + return !isNullOrUndefined(doc31.getComponents().getSchemas().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 OpenApiSchema createSchemaDefinition(OpenApiDocument document) { + OpenApi31Document doc31 = (OpenApi31Document) document; + OpenApi31Schema definition = (OpenApi31Schema) doc31.getComponents().createSchema(); + Library.readNode(_newDefinitionObj, definition); + return definition; + } + + @Override + public void addDefinition(OpenApiDocument document, OpenApiSchema definition) { + OpenApi31Document doc31 = (OpenApi31Document) document; + doc31.getComponents().addSchema(_newDefinitionName, definition); + } + + @Override + public void removeDefinition(OpenApiDocument document) { + OpenApi31Document doc31 = (OpenApi31Document) document; + if (_nullDefinitionsParent) { + doc31.setComponents(null); + } else { + doc31.getComponents().removeSchema(_newDefinitionName); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/io/apicurio/datamodels/cmd/commands/AddSecurityRequirementCommand.java b/src/main/java/io/apicurio/datamodels/cmd/commands/AddSecurityRequirementCommand.java new file mode 100755 index 000000000..d8b57dd65 --- /dev/null +++ b/src/main/java/io/apicurio/datamodels/cmd/commands/AddSecurityRequirementCommand.java @@ -0,0 +1,122 @@ +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.Node; +import io.apicurio.datamodels.models.SecurityRequirement; +import io.apicurio.datamodels.models.SecurityRequirementsParent; +import io.apicurio.datamodels.paths.NodePath; +import io.apicurio.datamodels.paths.NodePathUtil; +import io.apicurio.datamodels.util.LoggerUtil; + +import java.util.List; + +/** + * A command used to create a new security requirement in a document. + * @author eric.wittmann@gmail.com + */ +public class AddSecurityRequirementCommand extends AbstractCommand { + + public NodePath _parentPath; + public ObjectNode _requirement; + + public boolean _added; + + public AddSecurityRequirementCommand() { + } + + public AddSecurityRequirementCommand(SecurityRequirementsParent parent, SecurityRequirement requirement) { + this._parentPath = Library.createNodePath((Node) parent); + this._requirement = Library.writeNode(requirement); + } + + /** + * @see io.apicurio.datamodels.cmd.ICommand#execute(Document) + */ + @Override + public void execute(Document document) { + LoggerUtil.info("[AddSecurityRequirementCommand] Executing."); + this._added = false; + + SecurityRequirementsParent parent = (SecurityRequirementsParent) NodePathUtil.resolveNodePath(this._parentPath, document); + if (this.isNullOrUndefined(parent)) { + return; + } + SecurityRequirement requirement = parent.createSecurityRequirement(); + Library.readNode(this._requirement, requirement); + parent.addSecurity(requirement); + this._added = true; + } + + /** + * @see io.apicurio.datamodels.cmd.ICommand#undo(Document) + */ + @Override + public void undo(Document document) { + LoggerUtil.info("[AddSecurityRequirementCommand] Reverting."); + if (!this._added) { + return; + } + + SecurityRequirementsParent parent = (SecurityRequirementsParent) NodePathUtil.resolveNodePath(this._parentPath, document); + if (this.isNullOrUndefined(parent)) { + return; + } + + List security = parent.getSecurity(); + if (this.isNullOrUndefined(security)) { + LoggerUtil.info("[AddSecurityRequirementCommand] Security requirement not found, skipping undo."); + return; + } + + SecurityRequirement requirement = parent.createSecurityRequirement(); + Library.readNode(this._requirement, requirement); + + int idx = this.indexOfRequirement(security, requirement); + if (idx != -1) { + security.remove(idx); + } + } + + protected int indexOfRequirement(List requirements, SecurityRequirement requirement) { + int idx = 0; + for (SecurityRequirement r : requirements) { + if (this.isEqual(r, requirement)) { + return idx; + } + idx++; + } + return -1; + } + + protected boolean isEqual(SecurityRequirement req1, SecurityRequirement req2) { + List names1 = req1.getItemNames(); + List names2 = req2.getItemNames(); + if (names1.size() != names2.size()) { + return false; + } + boolean rval = true; + for (String name1 : names1) { + if (names2.indexOf(name1) == -1 || !areScopesEqual(req1.getItem(name1), req2.getItem(name1))) { + rval = false; + } + } + return rval; + } + + protected boolean areScopesEqual(List scopes1, List scopes2) { + if(scopes1.size() != scopes2.size()) { + return false; + } + boolean rval = true; + for (String scope1 : scopes1) { + if (scopes2.indexOf(scope1) == -1) { + rval = false; + } + } + return rval; + } + +} diff --git a/src/main/java/io/apicurio/datamodels/cmd/commands/DeleteAllSecurityRequirementsCommand.java b/src/main/java/io/apicurio/datamodels/cmd/commands/DeleteAllSecurityRequirementsCommand.java index 88e5014c8..81913aec7 100755 --- a/src/main/java/io/apicurio/datamodels/cmd/commands/DeleteAllSecurityRequirementsCommand.java +++ b/src/main/java/io/apicurio/datamodels/cmd/commands/DeleteAllSecurityRequirementsCommand.java @@ -5,27 +5,8 @@ import io.apicurio.datamodels.cmd.AbstractCommand; import io.apicurio.datamodels.models.Document; import io.apicurio.datamodels.models.Node; -import io.apicurio.datamodels.models.Operation; import io.apicurio.datamodels.models.SecurityRequirement; -import io.apicurio.datamodels.models.Server; -import io.apicurio.datamodels.models.asyncapi.v20.AsyncApi20SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v20.AsyncApi20Server; -import io.apicurio.datamodels.models.asyncapi.v21.AsyncApi21SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v21.AsyncApi21Server; -import io.apicurio.datamodels.models.asyncapi.v22.AsyncApi22SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v22.AsyncApi22Server; -import io.apicurio.datamodels.models.asyncapi.v23.AsyncApi23SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v23.AsyncApi23Server; -import io.apicurio.datamodels.models.asyncapi.v24.AsyncApi24SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v24.AsyncApi24Server; -import io.apicurio.datamodels.models.asyncapi.v25.AsyncApi25SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v25.AsyncApi25Server; -import io.apicurio.datamodels.models.asyncapi.v26.AsyncApi26SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v26.AsyncApi26Server; -import io.apicurio.datamodels.models.openapi.OpenApiDocument; -import io.apicurio.datamodels.models.openapi.OpenApiOperation; -import io.apicurio.datamodels.models.openapi.OpenApiSecurityRequirement; -import io.apicurio.datamodels.models.visitors.CombinedVisitorAdapter; +import io.apicurio.datamodels.models.SecurityRequirementsParent; import io.apicurio.datamodels.paths.NodePath; import io.apicurio.datamodels.paths.NodePathUtil; import io.apicurio.datamodels.util.LoggerUtil; @@ -71,12 +52,7 @@ public void execute(Document document) { }); } - clearSecurityRequirements(parent); - } - - private void clearSecurityRequirements(Node parent) { - ClearSecurityRequirementsVisitor csrv = new ClearSecurityRequirementsVisitor(); - parent.accept(csrv); + clearSecurityRequirements((SecurityRequirementsParent) parent); } /** @@ -100,159 +76,22 @@ public void undo(Document document) { NodeUtil.setProperty(parent, "security", requirements); } for (ObjectNode oldSecurityRequirement : this._oldSecurityRequirements) { - SecurityRequirement requirement = createSecurityRequirement(parent); + SecurityRequirement requirement = createSecurityRequirement((SecurityRequirementsParent) parent); Library.readNode(oldSecurityRequirement, requirement); - addSecurityRequirement(parent, requirement); + addSecurityRequirement((SecurityRequirementsParent) parent, requirement); } } - private SecurityRequirement createSecurityRequirement(Node parent) { - CreateSecurityRequirementVisitor csrv = new CreateSecurityRequirementVisitor(); - parent.accept(csrv); - return csrv._requirement; - } - - private void addSecurityRequirement(Node parent, SecurityRequirement requirement) { - AddSecurityRequirementVisitor asrv = new AddSecurityRequirementVisitor(requirement); - parent.accept(asrv); + private void clearSecurityRequirements(SecurityRequirementsParent parent) { + parent.clearSecurity(); } - private class ClearSecurityRequirementsVisitor extends CombinedVisitorAdapter { - - public ClearSecurityRequirementsVisitor() { - } - - @Override - public void visitDocument(Document node) { - ((OpenApiDocument) node).clearSecurity(); - } - - @Override - public void visitOperation(Operation node) { - ((OpenApiOperation) node).clearSecurity(); - } - - @Override - public void visitServer(Server node) { - switch (node.root().modelType()) { - case ASYNCAPI20: - ((AsyncApi20Server) node).clearSecurity(); - break; - case ASYNCAPI21: - ((AsyncApi21Server) node).clearSecurity(); - break; - case ASYNCAPI22: - ((AsyncApi22Server) node).clearSecurity(); - break; - case ASYNCAPI23: - ((AsyncApi23Server) node).clearSecurity(); - break; - case ASYNCAPI24: - ((AsyncApi24Server) node).clearSecurity(); - break; - case ASYNCAPI25: - ((AsyncApi25Server) node).clearSecurity(); - break; - case ASYNCAPI26: - ((AsyncApi26Server) node).clearSecurity(); - break; - default: - break; - } - } - } - - private class CreateSecurityRequirementVisitor extends CombinedVisitorAdapter { - SecurityRequirement _requirement; - - public CreateSecurityRequirementVisitor() { - } - - @Override - public void visitDocument(Document node) { - this._requirement = ((OpenApiDocument) node).createSecurityRequirement(); - } - - @Override - public void visitOperation(Operation node) { - this._requirement = ((OpenApiOperation) node).createSecurityRequirement(); - } - - @Override - public void visitServer(Server node) { - switch (node.root().modelType()) { - case ASYNCAPI20: - this._requirement = ((AsyncApi20Server) node).createSecurityRequirement(); - break; - case ASYNCAPI21: - this._requirement = ((AsyncApi21Server) node).createSecurityRequirement(); - break; - case ASYNCAPI22: - this._requirement = ((AsyncApi22Server) node).createSecurityRequirement(); - break; - case ASYNCAPI23: - this._requirement = ((AsyncApi23Server) node).createSecurityRequirement(); - break; - case ASYNCAPI24: - this._requirement = ((AsyncApi24Server) node).createSecurityRequirement(); - break; - case ASYNCAPI25: - this._requirement = ((AsyncApi25Server) node).createSecurityRequirement(); - break; - case ASYNCAPI26: - this._requirement = ((AsyncApi26Server) node).createSecurityRequirement(); - break; - default: - break; - } - } + private SecurityRequirement createSecurityRequirement(SecurityRequirementsParent parent) { + return parent.createSecurityRequirement(); } - private class AddSecurityRequirementVisitor extends CombinedVisitorAdapter { - SecurityRequirement _requirement; - - public AddSecurityRequirementVisitor(SecurityRequirement requirement) { - this._requirement = requirement; - } - - @Override - public void visitDocument(Document node) { - ((OpenApiDocument) node).addSecurity((OpenApiSecurityRequirement) this._requirement); - } - - @Override - public void visitOperation(Operation node) { - ((OpenApiOperation) node).addSecurity((OpenApiSecurityRequirement) this._requirement); - } - - @Override - public void visitServer(Server node) { - switch (node.root().modelType()) { - case ASYNCAPI20: - ((AsyncApi20Server) node).addSecurity((AsyncApi20SecurityRequirement) this._requirement); - break; - case ASYNCAPI21: - ((AsyncApi21Server) node).addSecurity((AsyncApi21SecurityRequirement) this._requirement); - break; - case ASYNCAPI22: - ((AsyncApi22Server) node).addSecurity((AsyncApi22SecurityRequirement) this._requirement); - break; - case ASYNCAPI23: - ((AsyncApi23Server) node).addSecurity((AsyncApi23SecurityRequirement) this._requirement); - break; - case ASYNCAPI24: - ((AsyncApi24Server) node).addSecurity((AsyncApi24SecurityRequirement) this._requirement); - break; - case ASYNCAPI25: - ((AsyncApi25Server) node).addSecurity((AsyncApi25SecurityRequirement) this._requirement); - break; - case ASYNCAPI26: - ((AsyncApi26Server) node).addSecurity((AsyncApi26SecurityRequirement) this._requirement); - break; - default: - break; - } - } + private void addSecurityRequirement(SecurityRequirementsParent parent, SecurityRequirement requirement) { + parent.addSecurity(requirement); } } diff --git a/src/main/java/io/apicurio/datamodels/transform/OpenApi20to30TransformationVisitor.java b/src/main/java/io/apicurio/datamodels/transform/OpenApi20to30TransformationVisitor.java index 0299d3ea6..6b9eac6b6 100755 --- a/src/main/java/io/apicurio/datamodels/transform/OpenApi20to30TransformationVisitor.java +++ b/src/main/java/io/apicurio/datamodels/transform/OpenApi20to30TransformationVisitor.java @@ -41,6 +41,7 @@ import io.apicurio.datamodels.models.Referenceable; import io.apicurio.datamodels.models.Schema; import io.apicurio.datamodels.models.SecurityRequirement; +import io.apicurio.datamodels.models.SecurityRequirementsParent; import io.apicurio.datamodels.models.SecurityScheme; import io.apicurio.datamodels.models.Tag; import io.apicurio.datamodels.models.openapi.OpenApiExample; @@ -507,12 +508,9 @@ public void visitExternalDocumentation(ExternalDocumentation node) { @Override public void visitSecurityRequirement(SecurityRequirement node) { OpenApi20SecurityRequirement req = (OpenApi20SecurityRequirement) node; - - Node parent30 = this.lookup(req.parent()); - - SecurityRequirementCreator securityRequirementCreator = new SecurityRequirementCreator(); - parent30.accept(securityRequirementCreator); - OpenApi30SecurityRequirement securityRequirement30 = (OpenApi30SecurityRequirement) securityRequirementCreator.securityRequirement; + SecurityRequirementsParent parent30 = (SecurityRequirementsParent) this.lookup(req.parent()); + OpenApi30SecurityRequirement securityRequirement30 = (OpenApi30SecurityRequirement) parent30.createSecurityRequirement(); + parent30.addSecurity(securityRequirement30); req.getItemNames().forEach( name -> { securityRequirement30.addItem(name, req.getItem(name)); diff --git a/src/main/java/io/apicurio/datamodels/transform/SecurityRequirementCreator.java b/src/main/java/io/apicurio/datamodels/transform/SecurityRequirementCreator.java deleted file mode 100755 index 336230f5c..000000000 --- a/src/main/java/io/apicurio/datamodels/transform/SecurityRequirementCreator.java +++ /dev/null @@ -1,118 +0,0 @@ -package io.apicurio.datamodels.transform; - -import io.apicurio.datamodels.models.Document; -import io.apicurio.datamodels.models.Operation; -import io.apicurio.datamodels.models.SecurityRequirement; -import io.apicurio.datamodels.models.Server; -import io.apicurio.datamodels.models.asyncapi.AsyncApiSecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.AsyncApiServer; -import io.apicurio.datamodels.models.asyncapi.v20.AsyncApi20SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v20.AsyncApi20Server; -import io.apicurio.datamodels.models.asyncapi.v21.AsyncApi21SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v21.AsyncApi21Server; -import io.apicurio.datamodels.models.asyncapi.v22.AsyncApi22SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v22.AsyncApi22Server; -import io.apicurio.datamodels.models.asyncapi.v23.AsyncApi23SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v23.AsyncApi23Server; -import io.apicurio.datamodels.models.asyncapi.v24.AsyncApi24SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v24.AsyncApi24Server; -import io.apicurio.datamodels.models.asyncapi.v25.AsyncApi25SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v25.AsyncApi25Server; -import io.apicurio.datamodels.models.asyncapi.v26.AsyncApi26SecurityRequirement; -import io.apicurio.datamodels.models.asyncapi.v26.AsyncApi26Server; -import io.apicurio.datamodels.models.openapi.OpenApiDocument; -import io.apicurio.datamodels.models.openapi.OpenApiOperation; -import io.apicurio.datamodels.models.openapi.OpenApiSecurityRequirement; -import io.apicurio.datamodels.models.visitors.CombinedVisitorAdapter; - -public class SecurityRequirementCreator extends CombinedVisitorAdapter { - - SecurityRequirement securityRequirement; - - @Override - public void visitDocument(Document node) { - OpenApiDocument doc = (OpenApiDocument) node; - securityRequirement = doc.createSecurityRequirement(); - doc.addSecurity((OpenApiSecurityRequirement) securityRequirement); - } - - @Override - public void visitServer(Server node) { - AsyncApiServer server = (AsyncApiServer) node; - AsyncApiSecurityRequirementParent srp = new AsyncApiSecurityRequirementParent(server); - securityRequirement = srp.createSecurityRequirement(); - srp.addSecurity((AsyncApiSecurityRequirement) securityRequirement); - } - - @Override - public void visitOperation(Operation node) { - OpenApiOperation op = (OpenApiOperation) node; - securityRequirement = op.createSecurityRequirement(); - op.addSecurity((OpenApiSecurityRequirement) securityRequirement); - } - - /** - * Note: this is ugly and needed because AsyncApi 3 removed security requirements. The generated - * data model is not very good when commonalities between the versions of specs are not present. I - * think we need improvements in the generated data model. Perhaps we can generate XyzParent - * interfaces for properties? Maybe only when necessary? I'm open to other ideas. - */ - private class AsyncApiSecurityRequirementParent { - AsyncApiServer server; - - private AsyncApiSecurityRequirementParent(AsyncApiServer server) { - this.server = server; - } - - public AsyncApiSecurityRequirement createSecurityRequirement() { - if (server instanceof AsyncApi20Server) { - return ((AsyncApi20Server) server).createSecurityRequirement(); - } - if (server instanceof AsyncApi21Server) { - return ((AsyncApi21Server) server).createSecurityRequirement(); - } - if (server instanceof AsyncApi22Server) { - return ((AsyncApi22Server) server).createSecurityRequirement(); - } - if (server instanceof AsyncApi23Server) { - return ((AsyncApi23Server) server).createSecurityRequirement(); - } - if (server instanceof AsyncApi24Server) { - return ((AsyncApi24Server) server).createSecurityRequirement(); - } - if (server instanceof AsyncApi25Server) { - return ((AsyncApi25Server) server).createSecurityRequirement(); - } - if (server instanceof AsyncApi26Server) { - return ((AsyncApi26Server) server).createSecurityRequirement(); - } - return null; - } - - public void addSecurity(AsyncApiSecurityRequirement securityRequirement) { - if (server instanceof AsyncApi20Server) { - ((AsyncApi20Server) server).addSecurity((AsyncApi20SecurityRequirement) securityRequirement); - } - if (server instanceof AsyncApi21Server) { - ((AsyncApi21Server) server).addSecurity((AsyncApi21SecurityRequirement) securityRequirement); - } - if (server instanceof AsyncApi22Server) { - ((AsyncApi22Server) server).addSecurity((AsyncApi22SecurityRequirement) securityRequirement); - } - if (server instanceof AsyncApi23Server) { - ((AsyncApi23Server) server).addSecurity((AsyncApi23SecurityRequirement) securityRequirement); - } - if (server instanceof AsyncApi24Server) { - ((AsyncApi24Server) server).addSecurity((AsyncApi24SecurityRequirement) securityRequirement); - } - if (server instanceof AsyncApi25Server) { - ((AsyncApi25Server) server).addSecurity((AsyncApi25SecurityRequirement) securityRequirement); - } - if (server instanceof AsyncApi26Server) { - ((AsyncApi26Server) server).addSecurity((AsyncApi26SecurityRequirement) securityRequirement); - } - } - - } - -} diff --git a/src/main/java/io/apicurio/datamodels/validation/rules/other/SecurityRequirementUniquenessValidationRule.java b/src/main/java/io/apicurio/datamodels/validation/rules/other/SecurityRequirementUniquenessValidationRule.java index 1f0942dae..542bf936a 100755 --- a/src/main/java/io/apicurio/datamodels/validation/rules/other/SecurityRequirementUniquenessValidationRule.java +++ b/src/main/java/io/apicurio/datamodels/validation/rules/other/SecurityRequirementUniquenessValidationRule.java @@ -21,9 +21,9 @@ import java.util.List; import io.apicurio.datamodels.models.SecurityRequirement; +import io.apicurio.datamodels.models.SecurityRequirementsParent; import io.apicurio.datamodels.validation.ValidationRule; import io.apicurio.datamodels.validation.ValidationRuleMetaData; -import io.apicurio.datamodels.visitors.SecurityRequirementsFromParentVisitor; /** * @author cfoskin@redhat.com @@ -93,9 +93,8 @@ private static boolean checkForDuplicates(SecurityRequirement currentSecurityReq */ @Override public void visitSecurityRequirement(SecurityRequirement node) { - SecurityRequirementsFromParentVisitor visitor = new SecurityRequirementsFromParentVisitor(); - node.parent().accept(visitor); - List securityRequirements = visitor.getSecurityRequirements(); + SecurityRequirementsParent parent = (SecurityRequirementsParent) node.parent(); + List securityRequirements = parent.getSecurity(); if (securityRequirements.size() <= 1) { return; diff --git a/src/main/java/io/apicurio/datamodels/visitors/SecurityRequirementsFromParentVisitor.java b/src/main/java/io/apicurio/datamodels/visitors/SecurityRequirementsFromParentVisitor.java deleted file mode 100755 index c9a77dff6..000000000 --- a/src/main/java/io/apicurio/datamodels/visitors/SecurityRequirementsFromParentVisitor.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.apicurio.datamodels.visitors; - -import io.apicurio.datamodels.models.Document; -import io.apicurio.datamodels.models.Operation; -import io.apicurio.datamodels.models.SecurityRequirement; -import io.apicurio.datamodels.models.openapi.OpenApiDocument; -import io.apicurio.datamodels.models.openapi.OpenApiOperation; -import io.apicurio.datamodels.models.visitors.CombinedVisitorAdapter; - -import java.util.List; - -public class SecurityRequirementsFromParentVisitor extends CombinedVisitorAdapter { - - private List securityRequirements; - - public List getSecurityRequirements() { - return securityRequirements; - } - - @Override - public void visitDocument(Document node) { - OpenApiDocument doc = (OpenApiDocument) node; - securityRequirements = doc.getSecurity(); - } - - @Override - public void visitOperation(Operation node) { - OpenApiOperation op = (OpenApiOperation) node; - securityRequirements = op.getSecurity(); - } - -} diff --git a/src/main/resources/specs/asyncapi/asyncapi-2.0.yaml b/src/main/resources/specs/asyncapi/asyncapi-2.0.yaml index 02bfd03e8..fdd1eec69 100755 --- a/src/main/resources/specs/asyncapi/asyncapi-2.0.yaml +++ b/src/main/resources/specs/asyncapi/asyncapi-2.0.yaml @@ -16,6 +16,10 @@ traits: properties: - name: '$ref' type: string + - name: SecurityRequirementsParent + properties: + - name: security + type: '[SecurityRequirement]' entities: - name: Document @@ -101,6 +105,7 @@ entities: - name: Server traits: - Extensible + - SecurityRequirementsParent properties: - name: url type: string diff --git a/src/main/resources/specs/asyncapi/asyncapi-2.1.yaml b/src/main/resources/specs/asyncapi/asyncapi-2.1.yaml index 0c58a4785..5f962f808 100755 --- a/src/main/resources/specs/asyncapi/asyncapi-2.1.yaml +++ b/src/main/resources/specs/asyncapi/asyncapi-2.1.yaml @@ -16,6 +16,10 @@ traits: properties: - name: '$ref' type: string + - name: SecurityRequirementsParent + properties: + - name: security + type: '[SecurityRequirement]' entities: - name: Document @@ -101,6 +105,7 @@ entities: - name: Server traits: - Extensible + - SecurityRequirementsParent properties: - name: url type: string diff --git a/src/main/resources/specs/asyncapi/asyncapi-2.2.yaml b/src/main/resources/specs/asyncapi/asyncapi-2.2.yaml index e61ff63d5..3811e3f8a 100755 --- a/src/main/resources/specs/asyncapi/asyncapi-2.2.yaml +++ b/src/main/resources/specs/asyncapi/asyncapi-2.2.yaml @@ -16,6 +16,10 @@ traits: properties: - name: '$ref' type: string + - name: SecurityRequirementsParent + properties: + - name: security + type: '[SecurityRequirement]' entities: - name: Document @@ -101,6 +105,7 @@ entities: - name: Server traits: - Extensible + - SecurityRequirementsParent properties: - name: url type: string diff --git a/src/main/resources/specs/asyncapi/asyncapi-2.3.yaml b/src/main/resources/specs/asyncapi/asyncapi-2.3.yaml index 46daf5331..780f2f127 100755 --- a/src/main/resources/specs/asyncapi/asyncapi-2.3.yaml +++ b/src/main/resources/specs/asyncapi/asyncapi-2.3.yaml @@ -16,6 +16,10 @@ traits: properties: - name: '$ref' type: string + - name: SecurityRequirementsParent + properties: + - name: security + type: '[SecurityRequirement]' entities: - name: Document @@ -102,6 +106,7 @@ entities: traits: - Extensible - Referenceable + - SecurityRequirementsParent properties: - name: url type: string diff --git a/src/main/resources/specs/asyncapi/asyncapi-2.4.yaml b/src/main/resources/specs/asyncapi/asyncapi-2.4.yaml index 41ea1f7dd..66b820364 100755 --- a/src/main/resources/specs/asyncapi/asyncapi-2.4.yaml +++ b/src/main/resources/specs/asyncapi/asyncapi-2.4.yaml @@ -16,6 +16,10 @@ traits: properties: - name: '$ref' type: string + - name: SecurityRequirementsParent + properties: + - name: security + type: '[SecurityRequirement]' entities: - name: Document @@ -102,6 +106,7 @@ entities: traits: - Extensible - Referenceable + - SecurityRequirementsParent properties: - name: url type: string diff --git a/src/main/resources/specs/asyncapi/asyncapi-2.5.yaml b/src/main/resources/specs/asyncapi/asyncapi-2.5.yaml index 61272ad47..5d2cfc6a6 100755 --- a/src/main/resources/specs/asyncapi/asyncapi-2.5.yaml +++ b/src/main/resources/specs/asyncapi/asyncapi-2.5.yaml @@ -16,6 +16,10 @@ traits: properties: - name: '$ref' type: string + - name: SecurityRequirementsParent + properties: + - name: security + type: '[SecurityRequirement]' entities: - name: Document @@ -102,6 +106,7 @@ entities: traits: - Extensible - Referenceable + - SecurityRequirementsParent properties: - name: url type: string diff --git a/src/main/resources/specs/asyncapi/asyncapi-2.6.yaml b/src/main/resources/specs/asyncapi/asyncapi-2.6.yaml index 4867a2a6b..380afb8d5 100755 --- a/src/main/resources/specs/asyncapi/asyncapi-2.6.yaml +++ b/src/main/resources/specs/asyncapi/asyncapi-2.6.yaml @@ -16,6 +16,10 @@ traits: properties: - name: '$ref' type: string + - name: SecurityRequirementsParent + properties: + - name: security + type: '[SecurityRequirement]' entities: - name: Document @@ -102,6 +106,7 @@ entities: traits: - Extensible - Referenceable + - SecurityRequirementsParent properties: - name: url type: string diff --git a/src/main/resources/specs/openapi/openapi-2.0.yaml b/src/main/resources/specs/openapi/openapi-2.0.yaml index b2d8f5f2a..90eec7d0b 100755 --- a/src/main/resources/specs/openapi/openapi-2.0.yaml +++ b/src/main/resources/specs/openapi/openapi-2.0.yaml @@ -20,6 +20,10 @@ traits: properties: - name: parameters type: '[Parameter]' + - name: SecurityRequirementsParent + properties: + - name: security + type: '[SecurityRequirement]' - name: SchemaLike transparent: true properties: @@ -61,6 +65,7 @@ entities: root: true traits: - Extensible + - SecurityRequirementsParent properties: - name: swagger type: string @@ -183,6 +188,7 @@ entities: traits: - Extensible - ParametersParent + - SecurityRequirementsParent properties: - name: tags type: '[string]' diff --git a/src/main/resources/specs/openapi/openapi-3.0.yaml b/src/main/resources/specs/openapi/openapi-3.0.yaml index 35529bf33..ee2904c0b 100755 --- a/src/main/resources/specs/openapi/openapi-3.0.yaml +++ b/src/main/resources/specs/openapi/openapi-3.0.yaml @@ -34,6 +34,10 @@ traits: properties: - name: parameters type: '[Parameter]' + - name: SecurityRequirementsParent + properties: + - name: security + type: '[SecurityRequirement]' entities: @@ -42,6 +46,7 @@ entities: traits: - Extensible - ServersParent + - SecurityRequirementsParent properties: - name: openapi type: string @@ -214,6 +219,7 @@ entities: - Extensible - ServersParent - ParametersParent + - SecurityRequirementsParent properties: - name: tags type: '[string]' diff --git a/src/main/resources/specs/openapi/openapi-3.1.yaml b/src/main/resources/specs/openapi/openapi-3.1.yaml index 3869596ca..24c68f9f8 100755 --- a/src/main/resources/specs/openapi/openapi-3.1.yaml +++ b/src/main/resources/specs/openapi/openapi-3.1.yaml @@ -32,6 +32,10 @@ traits: properties: - name: parameters type: '[Parameter]' + - name: SecurityRequirementsParent + properties: + - name: security + type: '[SecurityRequirement]' entities: - name: Document @@ -39,6 +43,7 @@ entities: traits: - Extensible - ServersParent + - SecurityRequirementsParent properties: - name: openapi type: string @@ -221,6 +226,7 @@ entities: - Extensible - ServersParent - ParametersParent + - SecurityRequirementsParent properties: - name: tags type: '[string]' diff --git a/src/main/ts/src/io/apicurio/datamodels/util/CommandUtil.ts b/src/main/ts/src/io/apicurio/datamodels/util/CommandUtil.ts index 43040d15d..1dd34e153 100644 --- a/src/main/ts/src/io/apicurio/datamodels/util/CommandUtil.ts +++ b/src/main/ts/src/io/apicurio/datamodels/util/CommandUtil.ts @@ -6,6 +6,8 @@ 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 {AddSchemaDefinitionCommand} from "../cmd/commands/AddSchemaDefinitionCommand"; +import {AddSecurityRequirementCommand} from "../cmd/commands/AddSecurityRequirementCommand"; import {ChangeDescriptionCommand} from "../cmd/commands/ChangeDescriptionCommand"; import {ChangePropertyCommand} from "../cmd/commands/ChangePropertyCommand"; @@ -48,6 +50,8 @@ const commandSuppliers: { [key: string]: Supplier } = { "AddExampleCommand": () => { return new AddExampleCommand(); }, "AddPathItemCommand": () => { return new AddPathItemCommand(); }, "AddResponseDefinitionCommand": () => { return new AddResponseDefinitionCommand(); }, + "AddSchemaDefinitionCommand": () => { return new AddSchemaDefinitionCommand(); }, + "AddSecurityRequirementCommand": () => { return new AddSecurityRequirementCommand(); }, "ChangeDescriptionCommand": () => { return new ChangeDescriptionCommand(); }, "ChangePropertyCommand": () => { return new ChangePropertyCommand(); }, diff --git a/src/test/resources/fixtures/cmd/tests.json b/src/test/resources/fixtures/cmd/tests.json index 86dfcaba4..a2c1484d0 100755 --- a/src/test/resources/fixtures/cmd/tests.json +++ b/src/test/resources/fixtures/cmd/tests.json @@ -71,5 +71,13 @@ { "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" } + { "name": "[OpenAPI 3] {Add Response Definition} - Clone Response Definition", "test": "commands/add-response-definition/openapi-3/clone-response-definition" }, + { "name": "[OpenAPI 2] {Add Schema Definition} - Add Definition", "test": "commands/add-definition/openapi-2/add-definition" }, + { "name": "[OpenAPI 2] {Add Schema Definition} - Clone Definition", "test": "commands/add-definition/openapi-2/clone-definition" }, + { "name": "[OpenAPI 3] {Add Schema Definition} - Add Definition", "test": "commands/add-definition/openapi-3/add-definition" }, + { "name": "[OpenAPI 3] {Add Schema Definition} - Clone Definition", "test": "commands/add-definition/openapi-3/clone-definition" }, + { "name": "[OpenAPI 2] {Add Security Requirement} - Add Security Req", "test": "commands/add-security-requirement/openapi-2/add-security-requirement" }, + { "name": "[OpenAPI 3] {Add Security Requirement} - Add Security Req Doc2", "test": "commands/add-security-requirement/openapi-3/add-security-requirement-doc2" }, + { "name": "[OpenAPI 3] {Add Security Requirement} - Add Security Req Op", "test": "commands/add-security-requirement/openapi-3/add-security-requirement-op" }, + { "name": "[OpenAPI 3] {Add Security Requirement} - Add Security Req", "test": "commands/add-security-requirement/openapi-3/add-security-requirement" } ] \ No newline at end of file