Skip to content

Commit f23ae7d

Browse files
authored
Replaced the old "/test" rest api endpoint with a "dryRun" query parameter where appropriate (#4722)
1 parent 22c3dbc commit f23ae7d

File tree

10 files changed

+121
-184
lines changed

10 files changed

+121
-184
lines changed

app/src/main/java/io/apicurio/registry/rest/v3/GroupsResourceImpl.java

+42-30
Original file line numberDiff line numberDiff line change
@@ -423,29 +423,6 @@ public void deleteArtifactRule(String groupId, String artifactId, RuleType rule)
423423
storage.deleteArtifactRule(new GroupId(groupId).getRawGroupIdWithNull(), artifactId, rule);
424424
}
425425

426-
/**
427-
* @see io.apicurio.registry.rest.v3.GroupsResource#testUpdateArtifact(java.lang.String, java.lang.String, java.io.InputStream)
428-
*/
429-
@Override
430-
@Authorized(style = AuthorizedStyle.GroupAndArtifact, level = AuthorizedLevel.Write)
431-
public void testUpdateArtifact(String groupId, String artifactId, InputStream data) {
432-
requireParameter("groupId", groupId);
433-
requireParameter("artifactId", artifactId);
434-
ContentHandle content = ContentHandle.create(data);
435-
if (content.bytes().length == 0) {
436-
throw new BadRequestException(EMPTY_CONTENT_ERROR_MESSAGE);
437-
}
438-
439-
String ct = getContentType();
440-
if (ContentTypeUtil.isApplicationYaml(ct)) {
441-
content = ContentTypeUtil.yamlToJson(content);
442-
}
443-
444-
String artifactType = lookupArtifactType(groupId, artifactId);
445-
rulesService.applyRules(new GroupId(groupId).getRawGroupIdWithNull(), artifactId, artifactType, content,
446-
RuleApplicationType.UPDATE, Collections.emptyList(), Collections.emptyMap()); //TODO:references not supported for testing update
447-
}
448-
449426
/**
450427
* @see io.apicurio.registry.rest.v3.GroupsResource#getArtifactVersionContent(java.lang.String, java.lang.String, java.lang.String, io.apicurio.registry.rest.v3.beans.HandleReferencesType)
451428
*/
@@ -651,9 +628,9 @@ public void deleteArtifactsInGroup(String groupId) {
651628
}
652629

653630
@Override
654-
@Audited(extractParameters = {"0", KEY_GROUP_ID, "1", KEY_IF_EXISTS, "2", KEY_CANONICAL})
631+
@Audited(extractParameters = {"0", KEY_GROUP_ID, "1", KEY_IF_EXISTS, "2", KEY_CANONICAL, "3", "dryRun"})
655632
@Authorized(style = AuthorizedStyle.GroupOnly, level = AuthorizedLevel.Write)
656-
public CreateArtifactResponse createArtifact(String groupId, IfArtifactExists ifExists, Boolean canonical, CreateArtifact data) {
633+
public CreateArtifactResponse createArtifact(String groupId, IfArtifactExists ifExists, Boolean canonical, Boolean dryRun, CreateArtifact data) {
657634
requireParameter("groupId", groupId);
658635
if (data.getFirstVersion() != null) {
659636
requireParameter("body.firstVersion.content", data.getFirstVersion().getContent());
@@ -723,7 +700,6 @@ public CreateArtifactResponse createArtifact(String groupId, IfArtifactExists if
723700
// ArtifactVersionMetaDataDto vmd = storage.createArtifactWithMetadata(new GroupId(groupId).getRawGroupIdWithNull(), artifactId,
724701
// xRegistryVersion, artifactType, content, metaData, referencesAsDtos);
725702

726-
727703
// Create the artifact (with optional first version)
728704
EditableArtifactMetaDataDto artifactMetaData = EditableArtifactMetaDataDto.builder()
729705
.description(data.getDescription())
@@ -748,6 +724,25 @@ public CreateArtifactResponse createArtifact(String groupId, IfArtifactExists if
748724
.build();
749725
firstVersionBranches = data.getFirstVersion().getBranches();
750726
}
727+
728+
// Don't actually do anything if "dryRun" is 'true'
729+
if (dryRun != null && dryRun) {
730+
return CreateArtifactResponse.builder()
731+
.artifact(ArtifactMetaData.builder()
732+
.groupId(groupId)
733+
.artifactId(artifactId)
734+
.createdOn(new Date())
735+
.owner(securityIdentity.getPrincipal().getName())
736+
.modifiedBy(securityIdentity.getPrincipal().getName())
737+
.modifiedOn(new Date())
738+
.name(artifactMetaData.getName())
739+
.description(artifactMetaData.getDescription())
740+
.labels(artifactMetaData.getLabels())
741+
.type(artifactType)
742+
.build())
743+
.build();
744+
}
745+
751746
Pair<ArtifactMetaDataDto, ArtifactVersionMetaDataDto> storageResult = storage.createArtifact(
752747
new GroupId(groupId).getRawGroupIdWithNull(),
753748
artifactId, artifactType, artifactMetaData, firstVersion, firstVersionContent,
@@ -796,18 +791,16 @@ public VersionSearchResults listArtifactVersions(String groupId, String artifact
796791
}
797792

798793
@Override
799-
@Audited(extractParameters = {"0", KEY_GROUP_ID, "1", KEY_ARTIFACT_ID})
794+
@Audited(extractParameters = {"0", KEY_GROUP_ID, "1", KEY_ARTIFACT_ID, "2", "dryRun"})
800795
@Authorized(style = AuthorizedStyle.GroupAndArtifact, level = AuthorizedLevel.Write)
801-
public VersionMetaData createArtifactVersion(String groupId, String artifactId, CreateVersion data) {
796+
public VersionMetaData createArtifactVersion(String groupId, String artifactId, Boolean dryRun, CreateVersion data) {
802797
requireParameter("content", data.getContent());
803798
requireParameter("groupId", groupId);
804799
requireParameter("artifactId", artifactId);
805800
requireParameter("body.content", data.getContent());
806801
requireParameter("body.content.content", data.getContent().getContent());
807802
requireParameter("body.content.contentType", data.getContent().getContentType());
808803

809-
// TODO deal with ifExists!
810-
811804
ContentHandle content = ContentHandle.create(data.getContent().getContent());
812805
if (content.bytes().length == 0) {
813806
throw new BadRequestException(EMPTY_CONTENT_ERROR_MESSAGE);
@@ -833,6 +826,25 @@ public VersionMetaData createArtifactVersion(String groupId, String artifactId,
833826
.content(content)
834827
.references(referencesAsDtos)
835828
.build();
829+
830+
// Don't actually do anything if "dryRun" is 'true'
831+
if (dryRun != null && dryRun) {
832+
return VersionMetaData.builder()
833+
.groupId(groupId)
834+
.artifactId(artifactId)
835+
.version(data.getVersion() == null ? "0" : data.getVersion())
836+
.createdOn(new Date())
837+
.owner(securityIdentity.getPrincipal().getName())
838+
.contentId(-1L)
839+
.name(metaDataDto.getName())
840+
.description(metaDataDto.getDescription())
841+
.labels(metaDataDto.getLabels())
842+
.state(VersionState.ENABLED)
843+
.globalId(-1L)
844+
.type(artifactType)
845+
.build();
846+
}
847+
836848
ArtifactVersionMetaDataDto vmd = storage.createArtifactVersion(new GroupId(groupId).getRawGroupIdWithNull(), artifactId, data.getVersion(),
837849
artifactType, contentDto, metaDataDto, data.getBranches());
838850

common/src/main/resources/META-INF/openapi.json

+22-58
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,14 @@
14101410
"type": "boolean"
14111411
},
14121412
"in": "query"
1413+
},
1414+
{
1415+
"name": "dryRun",
1416+
"description": "When set to `true`, the operation will not result in any changes. Instead, it\nwill return a result based on whether the operation **would have succeeded**.",
1417+
"schema": {
1418+
"type": "boolean"
1419+
},
1420+
"in": "query"
14131421
}
14141422
],
14151423
"responses": {
@@ -1435,7 +1443,7 @@
14351443
},
14361444
"operationId": "createArtifact",
14371445
"summary": "Create artifact",
1438-
"description": "Creates a new artifact. The body of the request should be a `CreateArtifact` \nobject, which includes the metadata of the new artifact and, optionally, the \nmetadata and content of the first version.\n\nIf the artifact type is not provided, the registry attempts to figure out what \nkind of artifact is being added from the\nfollowing supported list:\n\n* Avro (`AVRO`)\n* Protobuf (`PROTOBUF`)\n* JSON Schema (`JSON`)\n* Kafka Connect (`KCONNECT`)\n* OpenAPI (`OPENAPI`)\n* AsyncAPI (`ASYNCAPI`)\n* GraphQL (`GRAPHQL`)\n* Web Services Description Language (`WSDL`)\n* XML Schema (`XSD`)\n\nAn artifact will be created using the unique artifact ID that can optionally be \nprovided in the request body. If not provided in the request, the server will\ngenerate a unique ID for the artifact. It is typically recommended that callers\nprovide the ID, because it is typically a meaningful identifier, and as such\nfor most use cases should be supplied by the caller.\n\nIf an artifact with the provided artifact ID already exists, the default behavior\nis for the server to reject the content with a 409 error. However, the caller can\nsupply the `ifExists` query parameter to alter this default behavior. The `ifExists`\nquery parameter can have one of the following values:\n\n* `FAIL` (*default*) - server rejects the content with a 409 error\n* `UPDATE` - server updates the existing artifact and returns the new metadata\n* `RETURN` - server does not create or add content to the server, but instead \nreturns the metadata for the existing artifact\n* `RETURN_OR_UPDATE` - server returns an existing **version** that matches the \nprovided content if such a version exists, otherwise a new version is created\n\nThis operation may fail for one of the following reasons:\n\n* An invalid `ArtifactType` was indicated (HTTP error `400`)\n* No `ArtifactType` was indicated and the server could not determine one from the content (HTTP error `400`)\n* Provided content (request body) was empty (HTTP error `400`)\n* An artifact with the provided ID already exists (HTTP error `409`)\n* The content violates one of the configured global rules (HTTP error `409`)\n* A server error occurred (HTTP error `500`)\n"
1446+
"description": "Creates a new artifact. The body of the request should be a `CreateArtifact` \nobject, which includes the metadata of the new artifact and, optionally, the \nmetadata and content of the first version.\n\nIf the artifact type is not provided, the registry attempts to figure out what \nkind of artifact is being added from the\nfollowing supported list:\n\n* Avro (`AVRO`)\n* Protobuf (`PROTOBUF`)\n* JSON Schema (`JSON`)\n* Kafka Connect (`KCONNECT`)\n* OpenAPI (`OPENAPI`)\n* AsyncAPI (`ASYNCAPI`)\n* GraphQL (`GRAPHQL`)\n* Web Services Description Language (`WSDL`)\n* XML Schema (`XSD`)\n\nAn artifact will be created using the unique artifact ID that can optionally be \nprovided in the request body. If not provided in the request, the server will\ngenerate a unique ID for the artifact. It is typically recommended that callers\nprovide the ID, because it is typically a meaningful identifier, and as such\nfor most use cases should be supplied by the caller.\n\nIf an artifact with the provided artifact ID already exists, the default behavior\nis for the server to reject the content with a 409 error. However, the caller can\nsupply the `ifExists` query parameter to alter this default behavior. The `ifExists`\nquery parameter can have one of the following values:\n\n* `FAIL` (*default*) - server rejects the content with a 409 error\n* `UPDATE` - server updates the existing artifact and returns the new metadata\n* `RETURN` - server does not create or add content to the server, but instead \nreturns the metadata for the existing artifact\n* `RETURN_OR_UPDATE` - server returns an existing **version** that matches the \nprovided content if such a version exists, otherwise a new version is created\n\nThis operation may fail for one of the following reasons:\n\n* An invalid `ArtifactType` was indicated (HTTP error `400`)\n* No `ArtifactType` was indicated and the server could not determine one from the content (HTTP error `400`)\n* Provided content (request body) was empty (HTTP error `400`)\n* An artifact with the provided ID already exists (HTTP error `409`)\n* The content violates one of the configured global rules (HTTP error `409`)\n* A server error occurred (HTTP error `500`)\n\nNote that if the `dryRun` query parameter is set to `true`, then this operation\nwill not actually make any changes. Instead it will succeed or fail based on \nwhether it **would have worked**. Use this option to, for example, check if an\nartifact is valid or if a new version passes configured compatibility checks."
14391447
},
14401448
"delete": {
14411449
"tags": [
@@ -1465,62 +1473,6 @@
14651473
}
14661474
]
14671475
},
1468-
"/groups/{groupId}/artifacts/{artifactId}/test": {
1469-
"summary": "Test whether content would pass update rules.",
1470-
"put": {
1471-
"requestBody": {
1472-
"description": "The content of the artifact being tested. This is often, but not always, JSON data\nrepresenting one of the supported artifact types:\n\n* Avro (`AVRO`)\n* Protobuf (`PROTOBUF`)\n* JSON Schema (`JSON`)\n* Kafka Connect (`KCONNECT`)\n* OpenAPI (`OPENAPI`)\n* AsyncAPI (`ASYNCAPI`)\n* GraphQL (`GRAPHQL`)\n* Web Services Description Language (`WSDL`)\n* XML Schema (`XSD`)\n",
1473-
"content": {
1474-
"*/*": {
1475-
"schema": {
1476-
"$ref": "#/components/schemas/FileContent"
1477-
}
1478-
}
1479-
},
1480-
"required": true
1481-
},
1482-
"tags": [
1483-
"Artifact rules"
1484-
],
1485-
"responses": {
1486-
"204": {
1487-
"description": "When successful, returns \"No Content\" to indicate that the rules passed, and the\ncontent was not updated."
1488-
},
1489-
"404": {
1490-
"$ref": "#/components/responses/NotFound"
1491-
},
1492-
"409": {
1493-
"$ref": "#/components/responses/RuleViolationConflict"
1494-
},
1495-
"500": {
1496-
"$ref": "#/components/responses/ServerError"
1497-
}
1498-
},
1499-
"operationId": "testUpdateArtifact",
1500-
"summary": "Test update artifact",
1501-
"description": "Tests whether an update to the artifact's content *would* succeed for the provided content.\nUltimately, this applies any rules configured for the artifact against the given content\nto determine whether the rules would pass or fail, but without actually updating the artifact\ncontent.\n\nThe body of the request should be the raw content of the artifact. This is typically in \nJSON format for *most* of the supported types, but may be in another format for a few \n(for example, `PROTOBUF`).\n\nThe update could fail for a number of reasons including:\n\n* Provided content (request body) was empty (HTTP error `400`)\n* No artifact with the `artifactId` exists (HTTP error `404`)\n* The new content violates one of the rules configured for the artifact (HTTP error `409`)\n* The provided artifact type is not recognized (HTTP error `404`)\n* A server error occurred (HTTP error `500`)\n\nWhen successful, this operation simply returns a *No Content* response. This response\nindicates that the content is valid against the configured content rules for the \nartifact (or the global rules if no artifact rules are enabled)."
1502-
},
1503-
"parameters": [
1504-
{
1505-
"name": "groupId",
1506-
"description": "The artifact group ID. Must be a string provided by the client, representing the name of the grouping of artifacts. Must follow the \".{1,512}\" pattern.",
1507-
"schema": {
1508-
"$ref": "#/components/schemas/GroupId"
1509-
},
1510-
"in": "path",
1511-
"required": true
1512-
},
1513-
{
1514-
"name": "artifactId",
1515-
"description": "The artifact ID. Can be a string (client-provided) or UUID (server-generated), representing the unique artifact identifier. Must follow the \".{1,512}\" pattern.",
1516-
"schema": {
1517-
"$ref": "#/components/schemas/ArtifactId"
1518-
},
1519-
"in": "path",
1520-
"required": true
1521-
}
1522-
]
1523-
},
15241476
"/groups": {
15251477
"summary": "Collection of the groups in the registry.",
15261478
"get": {
@@ -1956,6 +1908,16 @@
19561908
"tags": [
19571909
"Versions"
19581910
],
1911+
"parameters": [
1912+
{
1913+
"name": "dryRun",
1914+
"description": "When set to `true`, the operation will not result in any changes. Instead, it\nwill return a result based on whether the operation **would have succeeded**.",
1915+
"schema": {
1916+
"type": "boolean"
1917+
},
1918+
"in": "query"
1919+
}
1920+
],
19591921
"responses": {
19601922
"200": {
19611923
"content": {
@@ -4462,7 +4424,9 @@
44624424
"type": "string"
44634425
}
44644426
},
4465-
"example": {}
4427+
"example": {
4428+
"snapshotId": "snp-1137292771"
4429+
}
44664430
}
44674431
},
44684432
"responses": {

0 commit comments

Comments
 (0)