Skip to content

Commit

Permalink
Implemented DeleteAllChildSchemasCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
EricWittmann committed Jan 2, 2025
1 parent cbeaeff commit 95dbbd1
Show file tree
Hide file tree
Showing 5 changed files with 359 additions and 3 deletions.
8 changes: 6 additions & 2 deletions src/main/java/io/apicurio/datamodels/cmd/CommandFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@
import io.apicurio.datamodels.cmd.commands.ChangePropertyCommand;
import io.apicurio.datamodels.cmd.commands.ChangeTitleCommand;
import io.apicurio.datamodels.cmd.commands.ChangeVersionCommand;
import io.apicurio.datamodels.cmd.commands.DeleteAllChildSchemasCommand;
import io.apicurio.datamodels.cmd.commands.DeleteContactCommand;
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.Extensible;
import io.apicurio.datamodels.models.Info;
import io.apicurio.datamodels.models.Node;
import io.apicurio.datamodels.models.Schema;
import io.apicurio.datamodels.models.openapi.OpenApiMediaType;
import io.apicurio.datamodels.util.CommandUtil;

import java.security.cert.Extension;

public class CommandFactory {

public static ICommand create(String cmdType) {
Expand Down Expand Up @@ -74,4 +74,8 @@ public static final ICommand createDeleteMediaTypeCommand(OpenApiMediaType media
return new DeleteMediaTypeCommand(mediaType);
}

public static final ICommand createDeleteAllChildSchemasCommand(Schema parent, String type) {
return new DeleteAllChildSchemasCommand(parent, type);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright 2019 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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.Node;
import io.apicurio.datamodels.models.Referenceable;
import io.apicurio.datamodels.models.Schema;
import io.apicurio.datamodels.models.asyncapi.AsyncApiSchema;
import io.apicurio.datamodels.models.openapi.v30.OpenApi30Schema;
import io.apicurio.datamodels.models.openapi.v31.OpenApi31Schema;
import io.apicurio.datamodels.util.ModelTypeUtil;
import io.apicurio.datamodels.util.NodeUtil;

import java.util.List;

/**
* @author [email protected]
*/
public abstract class AbstractSchemaInhCommand extends AbstractCommand {

public static final String TYPE_NONE = "none";
public static final String TYPE_ALL_OF = "allOf";
public static final String TYPE_ONE_OF = "oneOf";
public static final String TYPE_ANY_OF = "anyOf";

/**
* Determines the current inheritance type for the given schema.
* @param schema
*/
static String getInheritanceType(Schema schema) {
if (ModelTypeUtil.isOpenApiModel(schema)) {
if (NodeUtil.isDefined(schema.getAllOf())) {
return TYPE_ALL_OF;
}
if (ModelTypeUtil.isOpenApi3Model(schema)) {
if (NodeUtil.isDefined(NodeUtil.getNodeProperty(schema, TYPE_ANY_OF))) {
return TYPE_ANY_OF;
}
if (NodeUtil.isDefined(NodeUtil.getNodeProperty(schema, TYPE_ONE_OF))) {
return TYPE_ONE_OF;
}
}
} else if (ModelTypeUtil.isAsyncApiModel(schema)) {
if (NodeUtil.isDefined(((AsyncApiSchema) schema).getAllOf())) {
return TYPE_ALL_OF;
}
if (NodeUtil.isDefined(((AsyncApiSchema) schema).getAnyOf())) {
return TYPE_ANY_OF;
}
if (NodeUtil.isDefined(((AsyncApiSchema) schema).getOneOf())) {
return TYPE_ONE_OF;
}
}

return TYPE_NONE;
}

/**
* Creates a schema of a particular type (allOf, anyOf, etc).
* @param parentSchema
* @param inheritanceType
*/
protected Schema createSchema(Schema parentSchema, String inheritanceType) {
if (NodeUtil.equals(TYPE_ALL_OF, inheritanceType)) {
return createAllOfSchema(parentSchema);
}
if (NodeUtil.equals(TYPE_ANY_OF, inheritanceType)) {
return createAnyOfSchema(parentSchema);
}
if (NodeUtil.equals(TYPE_ONE_OF, inheritanceType)) {
return createOneOfSchema(parentSchema);
}
// TODO is it possible to get here? if so what should we do?
return createAllOfSchema(parentSchema);
}

/**
* Copies the given list of schemas to the appropriate property on the model
* @param schemas
* @param targetSchema
* @param inheritanceType
*/
protected void copySchemaJsTo(List<ObjectNode> schemas, Schema targetSchema, String inheritanceType) {
if (NodeUtil.equals(TYPE_ALL_OF, inheritanceType)) {
schemas.forEach(ser -> {
addAllOfSchema(targetSchema, (Schema) Library.readNode(ser, createAllOfSchema(targetSchema)));
});
}
if (NodeUtil.equals(TYPE_ANY_OF, inheritanceType)) {
schemas.forEach(ser -> {
addAnyOfSchema(targetSchema, (Schema) Library.readNode(ser, createAnyOfSchema(targetSchema)));
});
}
if (NodeUtil.equals(TYPE_ONE_OF, inheritanceType)) {
schemas.forEach(ser -> {
addOneOfSchema(targetSchema, (Schema) Library.readNode(ser, createOneOfSchema(targetSchema)));
});
}
}

private static void addAllOfSchema(Schema to, Schema from) {
to.addAllOf(from);
}

private static Schema createAllOfSchema(Schema schema) {
return schema.createSchema();
}

private static void addAnyOfSchema(Schema to, Schema from) {
if (ModelTypeUtil.isOpenApi30Model(to)) {
((OpenApi30Schema) to).addAnyOf((OpenApi30Schema) from);
} else if (ModelTypeUtil.isOpenApi31Model(to)) {
((OpenApi31Schema) to).addAnyOf((OpenApi31Schema) from);
} else if (ModelTypeUtil.isAsyncApiModel(to)) {
((AsyncApiSchema) to).addAnyOf((AsyncApiSchema) from);
}
}

private static Schema createAnyOfSchema(Schema schema) {
return schema.createSchema();
}

private static void addOneOfSchema(Schema to, Schema from) {
if (ModelTypeUtil.isOpenApi30Model(to)) {
((OpenApi30Schema) to).addOneOf((OpenApi30Schema) from);
} else if (ModelTypeUtil.isOpenApi31Model(to)) {
((OpenApi31Schema) to).addOneOf((OpenApi31Schema) from);
} else if (ModelTypeUtil.isAsyncApiModel(to)) {
((AsyncApiSchema) to).addOneOf((AsyncApiSchema) from);
}
}

private static Schema createOneOfSchema(Schema schema) {
return schema.createSchema();
}

protected boolean has$Ref(Node node) {
if (node != null && node instanceof Referenceable) {
String ref = ((Referenceable) node).get$ref();
return !NodeUtil.isNullOrUndefined(ref);
}
return false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
* Copyright 2019 Red Hat
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.apicurio.datamodels.cmd.commands;

import com.fasterxml.jackson.databind.node.ObjectNode;
import io.apicurio.datamodels.Library;
import io.apicurio.datamodels.models.Document;
import io.apicurio.datamodels.models.Node;
import io.apicurio.datamodels.models.Schema;
import io.apicurio.datamodels.models.asyncapi.AsyncApiSchema;
import io.apicurio.datamodels.models.openapi.v30.OpenApi30Schema;
import io.apicurio.datamodels.models.openapi.v31.OpenApi31Schema;
import io.apicurio.datamodels.paths.NodePath;
import io.apicurio.datamodels.paths.NodePathUtil;
import io.apicurio.datamodels.util.LoggerUtil;
import io.apicurio.datamodels.util.ModelTypeUtil;
import io.apicurio.datamodels.util.NodeUtil;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
* A command used to delete all child schemas from a schema.
* @author [email protected]
*/
public class DeleteAllChildSchemasCommand extends AbstractSchemaInhCommand {

public NodePath _schemaPath;
public String _childSchemaType;

public List<ObjectNode> _oldSchemas;

public DeleteAllChildSchemasCommand() {
}

public DeleteAllChildSchemasCommand(Schema parent, String type) {
this._schemaPath = Library.createNodePath((Node) parent);
this._childSchemaType = type;
}

@Override
public void execute(Document document) {
LoggerUtil.info("[DeleteAllChildSchemasCommand] Executing.");
this._oldSchemas = new ArrayList<>();

Schema schema = (Schema) NodePathUtil.resolveNodePath(this._schemaPath, document);
if (this.isNullOrUndefined(schema)) {
return;
}

// Pull the schemas out (if allOf, anyOf, or oneOf)
List<? extends Schema> schemas = new ArrayList<>();
if (NodeUtil.equals(TYPE_ALL_OF, this._childSchemaType)) {
schemas = new LinkedList<>(schema.getAllOf());
for (int i = 0; i < schemas.size(); i++) {
Schema s = schemas.get(i);
if (has$Ref(s)) {
schema.removeAllOf(s);
addOldSchema(s);
}
}
if (schema.getAllOf().isEmpty()) {
NodeUtil.setProperty(schema, TYPE_ALL_OF, null);
}
}
if (NodeUtil.equals(TYPE_ANY_OF, this._childSchemaType)) {
schemas = new LinkedList<>(getAnyOfSchemas(schema));
for (int i = 0; i < schemas.size(); i++) {
Schema s = schemas.get(i);
if (has$Ref(s)) {
removeAnyOfSchema(schema, s);
addOldSchema(s);
}
}
if (getAnyOfSchemas(schema).isEmpty()) {
NodeUtil.setProperty(schema, TYPE_ANY_OF, null);
}
}
if (NodeUtil.equals(TYPE_ONE_OF, this._childSchemaType)) {
schemas = new LinkedList<>(getOneOfSchemas(schema));
for (int i = 0; i < schemas.size(); i++) {
Schema s = schemas.get(i);
if (has$Ref(s)) {
removeOneOfSchema(schema, s);
addOldSchema(s);
}
}
if (getOneOfSchemas(schema).isEmpty()) {
NodeUtil.setProperty(schema, TYPE_ONE_OF, null);
}
}
}

@Override
public void undo(Document document) {
LoggerUtil.info("[DeleteAllChildSchemasCommand] Reverting.");

if (this.isNullOrUndefined(this._oldSchemas) || this._oldSchemas.size() == 0) {
return;
}

Schema schema = (Schema) NodePathUtil.resolveNodePath (this._schemaPath, document);
if (this.isNullOrUndefined(schema)) {
return;
}

// TODO: remember the index of each schema so we can restore them to the right positions.
this.copySchemaJsTo(_oldSchemas, schema, _childSchemaType);
}

private void addOldSchema(Schema schema) {
if (this.isNullOrUndefined(schema)) {
return;
}
this._oldSchemas.add(Library.writeNode(schema));
}

private List<? extends Schema> getAnyOfSchemas(Schema schema) {
if (ModelTypeUtil.isOpenApi30Model(schema)) {
OpenApi30Schema schema30 = (OpenApi30Schema) schema;
return schema30.getAnyOf();
} else if (ModelTypeUtil.isOpenApi31Model(schema)) {
OpenApi31Schema schema31 = (OpenApi31Schema) schema;
return schema31.getAnyOf();
} else if (ModelTypeUtil.isAsyncApiModel(schema)) {
AsyncApiSchema s = (AsyncApiSchema) schema;
return s.getAnyOf();
}
return null;
}

private List<? extends Schema> getOneOfSchemas(Schema schema) {
if (ModelTypeUtil.isOpenApi30Model(schema)) {
OpenApi30Schema schema30 = (OpenApi30Schema) schema;
return schema30.getOneOf();
} else if (ModelTypeUtil.isOpenApi31Model(schema)) {
OpenApi31Schema schema31 = (OpenApi31Schema) schema;
return schema31.getOneOf();
} else if (ModelTypeUtil.isAsyncApiModel(schema)) {
AsyncApiSchema s = (AsyncApiSchema) schema;
return s.getOneOf();
}
return null;
}

private static void removeAnyOfSchema(Schema parent, Schema child) {
if (ModelTypeUtil.isOpenApi30Model(parent)) {
OpenApi30Schema schema30 = (OpenApi30Schema) parent;
schema30.removeAnyOf((OpenApi30Schema) child);
} else if (ModelTypeUtil.isOpenApi31Model(parent)) {
OpenApi31Schema schema31 = (OpenApi31Schema) parent;
schema31.removeAnyOf((OpenApi31Schema) child);
} else if (ModelTypeUtil.isAsyncApiModel(parent)) {
AsyncApiSchema schema = (AsyncApiSchema) parent;
schema.removeAnyOf((AsyncApiSchema) child);
}
}

private static void removeOneOfSchema(Schema parent, Schema child) {
if (ModelTypeUtil.isOpenApi30Model(parent)) {
OpenApi30Schema schema30 = (OpenApi30Schema) parent;
schema30.removeOneOf((OpenApi30Schema) child);
} else if (ModelTypeUtil.isOpenApi31Model(parent)) {
OpenApi31Schema schema31 = (OpenApi31Schema) parent;
schema31.removeOneOf((OpenApi31Schema) child);
} else if (ModelTypeUtil.isAsyncApiModel(parent)) {
AsyncApiSchema schema = (AsyncApiSchema) parent;
schema.removeOneOf((AsyncApiSchema) child);
}
}

}
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 @@ -11,6 +11,7 @@ import {ChangeVersionCommand} from "../cmd/commands/ChangeVersionCommand";
import {ChangeContactCommand} from "../cmd/commands/ChangeContactCommand";
import {ChangeLicenseCommand} from "../cmd/commands/ChangeLicenseCommand";

import {DeleteAllChildSchemasCommand} from "../cmd/commands/DeleteAllChildSchemasCommand";
import {DeleteContactCommand} from "../cmd/commands/DeleteContactCommand";
import {DeleteExtensionCommand} from "../cmd/commands/DeleteExtensionCommand";
import {DeleteLicenseCommand} from "../cmd/commands/DeleteLicenseCommand";
Expand Down Expand Up @@ -38,6 +39,7 @@ const commandSuppliers: { [key: string]: Supplier } = {
"ChangeContactCommand": () => { return new ChangeContactCommand(); },
"ChangeLicenseCommand": () => { return new ChangeLicenseCommand(); },

"DeleteAllChildSchemasCommand": () => { return new DeleteAllChildSchemasCommand(); },
"DeleteContactCommand": () => { return new DeleteContactCommand(); },
"DeleteExtensionCommand": () => { return new DeleteExtensionCommand(); },
"DeleteLicenseCommand": () => { return new DeleteLicenseCommand(); },
Expand Down
Loading

0 comments on commit 95dbbd1

Please sign in to comment.