From 9baaa1e2d4f6af36a69dbcf11c6eae3dce25b896 Mon Sep 17 00:00:00 2001 From: Pavlo Smahin Date: Wed, 24 Jul 2024 15:16:37 +0300 Subject: [PATCH] feat(instance-date-types): implement instance-date-types endpoint (#1049) Closes: MODINVSTOR-1235 --- NEWS.md | 2 + descriptors/ModuleDescriptor-template.json | 30 +++- .../instanceDateTypePatch.json | 3 + .../instanceDateTypes.json | 25 +++ ramls/instance-date-type.raml | 84 ++++++++++ .../instance-date-types/instanceDateType.json | 59 +++++++ .../instanceDateTypePatch.json | 13 ++ .../instanceDateTypes.json | 19 +++ ...ontinuing-resource-ceased-publication.json | 10 ++ ...ntinuing-resource-currently-published.json | 10 ++ .../continuing-resource-status-unknown.json | 10 ++ .../date-of-distribution.json | 10 ++ .../instance-date-types/dates-unknown.json | 10 ++ .../instance-date-types/detailed-date.json | 10 ++ .../inclusive-dates-of-collection.json | 10 ++ .../instance-date-types/multiple-dates.json | 10 ++ .../no-attempt-to-code.json | 10 ++ .../instance-date-types/no-dates.json | 10 ++ .../instance-date-types/publication-date.json | 10 ++ .../questionable-date.json | 10 ++ .../range-of-years-of-bulk-of-collection.json | 10 ++ .../instance-date-types/reissue-date.json | 10 ++ .../single-known-date.json | 10 ++ .../persist/InstanceDateTypeRepository.java | 12 ++ .../folio/rest/impl/InstanceDateTypeApi.java | 38 +++++ .../instance/InstanceDateTypeService.java | 44 ++++++ .../db_scripts/addInstanceDateTypes.sql | 18 +++ .../templates/db_scripts/schema.json | 10 ++ .../folio/rest/impl/BaseIntegrationTest.java | 4 + .../folio/rest/impl/InstanceDateTypesIT.java | 144 ++++++++++++++++++ 30 files changed, 653 insertions(+), 2 deletions(-) create mode 100644 ramls/examples/instance-date-types/instanceDateTypePatch.json create mode 100644 ramls/examples/instance-date-types/instanceDateTypes.json create mode 100644 ramls/instance-date-type.raml create mode 100644 ramls/instance-date-types/instanceDateType.json create mode 100644 ramls/instance-date-types/instanceDateTypePatch.json create mode 100644 ramls/instance-date-types/instanceDateTypes.json create mode 100644 reference-data/instance-date-types/continuing-resource-ceased-publication.json create mode 100644 reference-data/instance-date-types/continuing-resource-currently-published.json create mode 100644 reference-data/instance-date-types/continuing-resource-status-unknown.json create mode 100644 reference-data/instance-date-types/date-of-distribution.json create mode 100644 reference-data/instance-date-types/dates-unknown.json create mode 100644 reference-data/instance-date-types/detailed-date.json create mode 100644 reference-data/instance-date-types/inclusive-dates-of-collection.json create mode 100644 reference-data/instance-date-types/multiple-dates.json create mode 100644 reference-data/instance-date-types/no-attempt-to-code.json create mode 100644 reference-data/instance-date-types/no-dates.json create mode 100644 reference-data/instance-date-types/publication-date.json create mode 100644 reference-data/instance-date-types/questionable-date.json create mode 100644 reference-data/instance-date-types/range-of-years-of-bulk-of-collection.json create mode 100644 reference-data/instance-date-types/reissue-date.json create mode 100644 reference-data/instance-date-types/single-known-date.json create mode 100644 src/main/java/org/folio/persist/InstanceDateTypeRepository.java create mode 100644 src/main/java/org/folio/rest/impl/InstanceDateTypeApi.java create mode 100644 src/main/java/org/folio/services/instance/InstanceDateTypeService.java create mode 100644 src/main/resources/templates/db_scripts/addInstanceDateTypes.sql create mode 100644 src/test/java/org/folio/rest/impl/InstanceDateTypesIT.java diff --git a/NEWS.md b/NEWS.md index 0cbca7f17..8f6fc9b4d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,7 @@ * Required sourceId field in holdings record ([MODINVSTOR-1161](https://folio-org.atlassian.net/browse/MODINVSTOR-1161)) ### New APIs versions +* Provides `instance-date-types 1.0` * Provides `instance-storage 10.1` * Requires `holdings-storage 6.1` @@ -13,6 +14,7 @@ * Implement domain event production for campus create/update/delete ([MODINVSTOR-1217](https://issues.folio.org/browse/MODINVSTOR-1217)) * Implement domain event production for institution create/update/delete ([MODINVSTOR-1218](https://issues.folio.org/browse/MODINVSTOR-1218)) * Implement a POST request to get Holdings and Instances ([MODINVSTOR-1223](https://folio-org.atlassian.net/browse/MODINVSTOR-1223)) +* Implement instance-date-types endpoint ([MODINVSTOR-1235](https://folio-org.atlassian.net/browse/MODINVSTOR-1235)) ### Bug fixes * Unintended update of instance records \_version (optimistic locking) whenever any of its holdings or items are created, updated or deleted. ([MODINVSTOR-1186](https://folio-org.atlassian.net/browse/MODINVSTOR-1186)) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 1d6273151..6c32540dd 100755 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -53,6 +53,21 @@ } ] }, + { + "id": "instance-date-types", + "version": "1.0", + "handlers": [ + { + "methods": ["GET"], + "pathPattern": "/instance-date-types", + "permissionsRequired": ["inventory-storage.instance-date-types.collection.get"] + }, { + "methods": ["PATCH"], + "pathPattern": "/instance-date-types/{id}", + "permissionsRequired": ["inventory-storage.instance-date-types.item.patch"] + } + ] + }, { "id": "item-storage-batch-sync", "version": "1.0", @@ -2458,7 +2473,16 @@ "displayName": "inventory storage - submit migration job", "description": "submit migration instance job" }, - + { + "permissionName": "inventory-storage.instance-date-types.collection.get", + "displayName": "inventory storage - get list of instance-date-types", + "description": "get list of instance-date-types" + }, + { + "permissionName": "inventory-storage.instance-date-types.item.patch", + "displayName": "inventory storage - patch instance-date-type", + "description": "patch instance-date-type" + }, { "permissionName": "inventory-storage.all", "displayName": "inventory storage module - all permissions", @@ -2687,7 +2711,9 @@ "inventory-storage.migration.job.item.delete", "inventory-storage.migration.job.post", "inventory-storage.migration.job.item.get", - "inventory-storage.migration.item.get" + "inventory-storage.migration.item.get", + "inventory-storage.instance-date-types.collection.get", + "inventory-storage.instance-date-types.item.patch" ] } ], diff --git a/ramls/examples/instance-date-types/instanceDateTypePatch.json b/ramls/examples/instance-date-types/instanceDateTypePatch.json new file mode 100644 index 000000000..c74f24822 --- /dev/null +++ b/ramls/examples/instance-date-types/instanceDateTypePatch.json @@ -0,0 +1,3 @@ +{ + "name": "new name" +} \ No newline at end of file diff --git a/ramls/examples/instance-date-types/instanceDateTypes.json b/ramls/examples/instance-date-types/instanceDateTypes.json new file mode 100644 index 000000000..377d5ab2e --- /dev/null +++ b/ramls/examples/instance-date-types/instanceDateTypes.json @@ -0,0 +1,25 @@ +{ + "instanceDateTypes": [ + { + "id": "c0b423e1-a885-4c09-a965-4d6979a97e53", + "name": "Detailed date", + "code": "e", + "displayFormat": { + "delimiter": ",", + "keepDelimiter": true + }, + "source": "folio" + }, + { + "id": "2a0915ff-7246-463b-96a4-4473cd027c6b", + "name": "Range of years of bulk of collection", + "code": "k", + "displayFormat": { + "delimiter": "-", + "keepDelimiter": false + }, + "source": "folio" + } + ], + "totalRecords": 2 +} diff --git a/ramls/instance-date-type.raml b/ramls/instance-date-type.raml new file mode 100644 index 000000000..c36487812 --- /dev/null +++ b/ramls/instance-date-type.raml @@ -0,0 +1,84 @@ +#%RAML 1.0 +title: Inventory Storage Instance Date Type API +version: v1.0 +protocols: [ HTTP, HTTPS ] +baseUri: http://localhost + +traits: + pageable: !include raml-util/traits/pageable.raml + searchable: !include raml-util/traits/searchable.raml + validate: !include raml-util/traits/validation.raml + +types: + instanceDateTypes: !include instance-date-types/instanceDateTypes.json + instanceDateTypePatch: !include instance-date-types/instanceDateTypePatch.json + errors: !include raml-util/schemas/errors.schema + +/instance-date-types: + displayName: Instances Date Types API + get: + description: Retrieve a list of instances date type items. + is: [ + searchable: {description: "with valid searchable fields", example: "code=a"}, + pageable + ] + responses: + 200: + description: "Returns a list of instances date type items" + body: + application/json: + type: instanceDateTypes + example: + strict: false + value: examples/instance-date-types/instanceDateTypes.json + 400: + description: "Bad request, e.g. malformed request body or query parameter. Details of the error (e.g. name of the parameter or line/character number with malformed data) provided in the response." + body: + text/plain: + example: "unable to list instances date type -- malformed parameter 'query', syntax error at column 6" + 401: + description: "Not authorized to perform requested action" + body: + text/plain: + example: "unable to list instances date type -- unauthorized" + 500: + description: "Internal server error, e.g. due to misconfiguration" + body: + text/plain: + example: "internal server error, contact administrator" + /{id}: + patch: + description: "Update Instances Date Type item with given id" + is: [ validate ] + body: + application/json: + type: instanceDateTypePatch + example: + strict: false + value: examples/instance-date-types/instanceDateTypePatch.json + responses: + 204: + description: "Update accepted" + 400: + description: "Bad request, e.g. malformed request body or query parameter. Details of the error (e.g. name of the parameter or line/character number with malformed data) provided in the response." + body: + text/plain: + example: | + "unable to list instances date type -- malformed parameter 'query', syntax error at column 6" + 401: + description: "Not authorized to perform requested action" + body: + text/plain: + example: | + "unable to list instances date type -- unauthorized" + 404: + description: "Item with a given ID not found" + body: + text/plain: + example: | + "instances date type not found" + 500: + description: "Internal server error, e.g. due to misconfiguration" + body: + text/plain: + example: "internal server error, contact administrator" diff --git a/ramls/instance-date-types/instanceDateType.json b/ramls/instance-date-types/instanceDateType.json new file mode 100644 index 000000000..b35dfe2fd --- /dev/null +++ b/ramls/instance-date-types/instanceDateType.json @@ -0,0 +1,59 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "An instance date type that indicates the type of dates given in Date 1 and Date 2", + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique ID of the instance date type; a UUID", + "readonly": true + }, + "name": { + "type": "string", + "description": "Name of the instance date type", + "readonly": true + }, + "code": { + "type": "string", + "description": "Code of the instance date type", + "maxLength": 1, + "readonly": true + }, + "displayFormat": { + "type": "object", + "description": "Describes how to format Date 1 and Date 2", + "properties": { + "delimiter": { + "type": "string", + "description": "Delimiter that will be used to format Date 1 and Date 2", + "example": ",", + "readonly": true + }, + "keepDelimiter": { + "type": "boolean", + "description": "Define if formated date string should keep delimiter if one of dates is not exist", + "example": false, + "readonly": true + } + }, + "readonly": true, + "additionalProperties": false + }, + "source": { + "type": "string", + "description": "label indicating where the instance date type entry originates from, i.e. 'folio' or 'local'", + "enum": [ + "folio", + "local" + ], + "readonly": true + }, + "metadata": { + "type": "object", + "$ref": "../raml-util/schemas/metadata.schema", + "readonly": true + } + }, + "additionalProperties": false +} + diff --git a/ramls/instance-date-types/instanceDateTypePatch.json b/ramls/instance-date-types/instanceDateTypePatch.json new file mode 100644 index 000000000..00a67b431 --- /dev/null +++ b/ramls/instance-date-types/instanceDateTypePatch.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Object that represents patch operation for instance date type", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the instance date type" + } + }, + "additionalProperties": false +} + diff --git a/ramls/instance-date-types/instanceDateTypes.json b/ramls/instance-date-types/instanceDateTypes.json new file mode 100644 index 000000000..c2e10ea3a --- /dev/null +++ b/ramls/instance-date-types/instanceDateTypes.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "A collection of instance date types", + "type": "object", + "properties": { + "instanceDateTypes": { + "description": "List of instance date types", + "type": "array", + "items": { + "type": "object", + "$ref": "instanceDateType.json" + } + }, + "totalRecords": { + "description": "Estimated or exact total number of records", + "type": "integer" + } + } +} diff --git a/reference-data/instance-date-types/continuing-resource-ceased-publication.json b/reference-data/instance-date-types/continuing-resource-ceased-publication.json new file mode 100644 index 000000000..1590bced8 --- /dev/null +++ b/reference-data/instance-date-types/continuing-resource-ceased-publication.json @@ -0,0 +1,10 @@ +{ + "id": "42dac21e-3c81-4cb1-9f16-9e50c81bacc4", + "name": "Continuing resource ceased publication", + "code": "d", + "displayFormat": { + "delimiter": "-", + "keepDelimiter": true + }, + "source": "folio" +} diff --git a/reference-data/instance-date-types/continuing-resource-currently-published.json b/reference-data/instance-date-types/continuing-resource-currently-published.json new file mode 100644 index 000000000..856535456 --- /dev/null +++ b/reference-data/instance-date-types/continuing-resource-currently-published.json @@ -0,0 +1,10 @@ +{ + "id": "0750f52b-3bfc-458d-9307-e9afc8bcdffa", + "name": "Continuing resource currently published", + "code": "c", + "displayFormat": { + "delimiter": "-", + "keepDelimiter": true + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/continuing-resource-status-unknown.json b/reference-data/instance-date-types/continuing-resource-status-unknown.json new file mode 100644 index 000000000..8a23e7e96 --- /dev/null +++ b/reference-data/instance-date-types/continuing-resource-status-unknown.json @@ -0,0 +1,10 @@ +{ + "id": "5a1a1adb-de71-45e6-ba94-1c0838969f04", + "name": "Continuing resource status unknown", + "code": "e", + "displayFormat": { + "delimiter": "-", + "keepDelimiter": true + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/date-of-distribution.json b/reference-data/instance-date-types/date-of-distribution.json new file mode 100644 index 000000000..a8e0d3f73 --- /dev/null +++ b/reference-data/instance-date-types/date-of-distribution.json @@ -0,0 +1,10 @@ +{ + "id": "5f84208a-0aa6-4694-a58f-5310b654f012", + "name": "Date of distribution/release/issue and production/recording session when different", + "code": "p", + "displayFormat": { + "delimiter": ",", + "keepDelimiter": false + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/dates-unknown.json b/reference-data/instance-date-types/dates-unknown.json new file mode 100644 index 000000000..cf40a5bf6 --- /dev/null +++ b/reference-data/instance-date-types/dates-unknown.json @@ -0,0 +1,10 @@ +{ + "id": "e77bb7ed-2e53-4c62-8b06-5907b8934ba7", + "name": "Dates unknown", + "code": "n", + "displayFormat": { + "delimiter": ",", + "keepDelimiter": false + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/detailed-date.json b/reference-data/instance-date-types/detailed-date.json new file mode 100644 index 000000000..8fc861fae --- /dev/null +++ b/reference-data/instance-date-types/detailed-date.json @@ -0,0 +1,10 @@ +{ + "id": "9669a463-5971-42dc-9eee-046bbd678fb1", + "name": "Detailed date", + "code": "e", + "displayFormat": { + "delimiter": ",", + "keepDelimiter": false + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/inclusive-dates-of-collection.json b/reference-data/instance-date-types/inclusive-dates-of-collection.json new file mode 100644 index 000000000..1ab0c745b --- /dev/null +++ b/reference-data/instance-date-types/inclusive-dates-of-collection.json @@ -0,0 +1,10 @@ +{ + "id": "6de732c5-c29b-4a10-9db0-0729ca960f12", + "name": "Inclusive dates of collection", + "code": "i", + "displayFormat": { + "delimiter": "-", + "keepDelimiter": true + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/multiple-dates.json b/reference-data/instance-date-types/multiple-dates.json new file mode 100644 index 000000000..5d5803916 --- /dev/null +++ b/reference-data/instance-date-types/multiple-dates.json @@ -0,0 +1,10 @@ +{ + "id": "8fa6d067-41ff-4362-96a0-96b16ddce267", + "name": "Multiple dates", + "code": "m", + "displayFormat": { + "delimiter": ",", + "keepDelimiter": false + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/no-attempt-to-code.json b/reference-data/instance-date-types/no-attempt-to-code.json new file mode 100644 index 000000000..12353296a --- /dev/null +++ b/reference-data/instance-date-types/no-attempt-to-code.json @@ -0,0 +1,10 @@ +{ + "id": "6f8cd9a8-26ac-4df6-8709-62fe2c0d04f8", + "name": "No attempt to code", + "code": "|", + "displayFormat": { + "delimiter": ",", + "keepDelimiter": false + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/no-dates.json b/reference-data/instance-date-types/no-dates.json new file mode 100644 index 000000000..4d4616695 --- /dev/null +++ b/reference-data/instance-date-types/no-dates.json @@ -0,0 +1,10 @@ +{ + "id": "77a09c3c-37bd-4ad3-aae4-9d86fc1b33d8", + "name": "No dates given; BC date involved", + "code": "b", + "displayFormat": { + "delimiter": ",", + "keepDelimiter": false + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/publication-date.json b/reference-data/instance-date-types/publication-date.json new file mode 100644 index 000000000..11d1a372e --- /dev/null +++ b/reference-data/instance-date-types/publication-date.json @@ -0,0 +1,10 @@ +{ + "id": "3a4296bf-504b-451b-9355-5806f8d88253", + "name": "Publication date and copyright date", + "code": "t", + "displayFormat": { + "delimiter": ",", + "keepDelimiter": false + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/questionable-date.json b/reference-data/instance-date-types/questionable-date.json new file mode 100644 index 000000000..95a3f5199 --- /dev/null +++ b/reference-data/instance-date-types/questionable-date.json @@ -0,0 +1,10 @@ +{ + "id": "4afa7d3d-e6f5-4134-9ab5-32ad377d2432", + "name": "Questionable date", + "code": "q", + "displayFormat": { + "delimiter": ",", + "keepDelimiter": false + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/range-of-years-of-bulk-of-collection.json b/reference-data/instance-date-types/range-of-years-of-bulk-of-collection.json new file mode 100644 index 000000000..6af92b475 --- /dev/null +++ b/reference-data/instance-date-types/range-of-years-of-bulk-of-collection.json @@ -0,0 +1,10 @@ +{ + "id": "ccc293d5-9e88-4222-ac04-d058351ddb7b", + "name": "Range of years of bulk of collection", + "code": "k", + "displayFormat": { + "delimiter": "-", + "keepDelimiter": true + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/reissue-date.json b/reference-data/instance-date-types/reissue-date.json new file mode 100644 index 000000000..89404ddea --- /dev/null +++ b/reference-data/instance-date-types/reissue-date.json @@ -0,0 +1,10 @@ +{ + "id": "47622598-61eb-4348-899a-1208275c3882", + "name": "Reprint/reissue date and original date", + "code": "r", + "displayFormat": { + "delimiter": ",", + "keepDelimiter": false + }, + "source": "folio" +} \ No newline at end of file diff --git a/reference-data/instance-date-types/single-known-date.json b/reference-data/instance-date-types/single-known-date.json new file mode 100644 index 000000000..3a969a8be --- /dev/null +++ b/reference-data/instance-date-types/single-known-date.json @@ -0,0 +1,10 @@ +{ + "id": "24a506e8-2a92-4ecc-bd09-ff849321fd5a", + "name": "Single known date/probable date", + "code": "s", + "displayFormat": { + "delimiter": ",", + "keepDelimiter": false + }, + "source": "folio" +} \ No newline at end of file diff --git a/src/main/java/org/folio/persist/InstanceDateTypeRepository.java b/src/main/java/org/folio/persist/InstanceDateTypeRepository.java new file mode 100644 index 000000000..662b0465a --- /dev/null +++ b/src/main/java/org/folio/persist/InstanceDateTypeRepository.java @@ -0,0 +1,12 @@ +package org.folio.persist; + +import org.folio.rest.jaxrs.model.InstanceDateType; +import org.folio.rest.persist.PostgresClient; + +public class InstanceDateTypeRepository extends AbstractRepository { + + public InstanceDateTypeRepository(PostgresClient postgresClient, String tableName, + Class recordType) { + super(postgresClient, tableName, recordType); + } +} diff --git a/src/main/java/org/folio/rest/impl/InstanceDateTypeApi.java b/src/main/java/org/folio/rest/impl/InstanceDateTypeApi.java new file mode 100644 index 000000000..14f32abd1 --- /dev/null +++ b/src/main/java/org/folio/rest/impl/InstanceDateTypeApi.java @@ -0,0 +1,38 @@ +package org.folio.rest.impl; + +import static io.vertx.core.Future.succeededFuture; +import static org.folio.rest.support.EndpointFailureHandler.handleFailure; + +import io.vertx.core.AsyncResult; +import io.vertx.core.Context; +import io.vertx.core.Handler; +import java.util.Map; +import javax.ws.rs.core.Response; +import org.folio.rest.annotations.Validate; +import org.folio.rest.jaxrs.model.InstanceDateTypePatch; +import org.folio.rest.jaxrs.resource.InstanceDateTypes; +import org.folio.services.instance.InstanceDateTypeService; + +public class InstanceDateTypeApi implements InstanceDateTypes { + + @Validate + @Override + public void getInstanceDateTypes(String query, String totalRecords, int offset, int limit, + Map okapiHeaders, Handler> asyncResultHandler, + Context vertxContext) { + new InstanceDateTypeService(vertxContext, okapiHeaders) + .getInstanceDateTypes(query, offset, limit) + .onSuccess(response -> asyncResultHandler.handle(succeededFuture(response))) + .onFailure(handleFailure(asyncResultHandler)); + } + + @Validate + @Override + public void patchInstanceDateTypesById(String id, InstanceDateTypePatch entity, Map okapiHeaders, + Handler> asyncResultHandler, Context vertxContext) { + new InstanceDateTypeService(vertxContext, okapiHeaders) + .patchInstanceDateTypes(id, entity) + .onSuccess(response -> asyncResultHandler.handle(succeededFuture(response))) + .onFailure(handleFailure(asyncResultHandler)); + } +} diff --git a/src/main/java/org/folio/services/instance/InstanceDateTypeService.java b/src/main/java/org/folio/services/instance/InstanceDateTypeService.java new file mode 100644 index 000000000..fafc2cc4f --- /dev/null +++ b/src/main/java/org/folio/services/instance/InstanceDateTypeService.java @@ -0,0 +1,44 @@ +package org.folio.services.instance; + +import static org.folio.rest.persist.PgUtil.postgresClient; + +import io.vertx.core.Context; +import io.vertx.core.Future; +import java.util.Map; +import javax.ws.rs.core.Response; +import org.folio.persist.InstanceDateTypeRepository; +import org.folio.rest.jaxrs.model.InstanceDateType; +import org.folio.rest.jaxrs.model.InstanceDateTypePatch; +import org.folio.rest.jaxrs.model.InstanceDateTypes; +import org.folio.rest.jaxrs.resource.InstanceDateTypes.GetInstanceDateTypesResponse; +import org.folio.rest.jaxrs.resource.InstanceDateTypes.PatchInstanceDateTypesByIdResponse; +import org.folio.rest.persist.PgUtil; + +public class InstanceDateTypeService { + + public static final String INSTANCE_DATE_TYPE_TABLE = "instance_date_type"; + + private final Context vertxContext; + private final Map okapiHeaders; + private final InstanceDateTypeRepository repository; + + public InstanceDateTypeService(Context vertxContext, Map okapiHeaders) { + + this.vertxContext = vertxContext; + this.okapiHeaders = okapiHeaders; + + final var postgresClient = postgresClient(vertxContext, okapiHeaders); + this.repository = new InstanceDateTypeRepository(postgresClient, INSTANCE_DATE_TYPE_TABLE, InstanceDateType.class); + } + + public Future getInstanceDateTypes(String query, int offset, int limit) { + return PgUtil.get(INSTANCE_DATE_TYPE_TABLE, InstanceDateType.class, InstanceDateTypes.class, query, + "exact", offset, limit, okapiHeaders, vertxContext, GetInstanceDateTypesResponse.class); + } + + public Future patchInstanceDateTypes(String id, InstanceDateTypePatch entity) { + return repository.getById(id) + .compose(instanceDateType -> PgUtil.put(INSTANCE_DATE_TYPE_TABLE, instanceDateType.withName(entity.getName()), + id, okapiHeaders, vertxContext, PatchInstanceDateTypesByIdResponse.class)); + } +} diff --git a/src/main/resources/templates/db_scripts/addInstanceDateTypes.sql b/src/main/resources/templates/db_scripts/addInstanceDateTypes.sql new file mode 100644 index 000000000..e9cde70ab --- /dev/null +++ b/src/main/resources/templates/db_scripts/addInstanceDateTypes.sql @@ -0,0 +1,18 @@ +INSERT INTO ${myuniversity}_${mymodule}.instance_date_type (id, jsonb) +VALUES + ('42dac21e-3c81-4cb1-9f16-9e50c81bacc4', '{"id":"42dac21e-3c81-4cb1-9f16-9e50c81bacc4","name":"Continuing resource ceased publication","code":"d","displayFormat":{"delimiter":"-","keepDelimiter":true},"source":"folio"}'), + ('0750f52b-3bfc-458d-9307-e9afc8bcdffa', '{"id":"0750f52b-3bfc-458d-9307-e9afc8bcdffa","name":"Continuing resource currently published","code":"c","displayFormat":{"delimiter":"-","keepDelimiter":true},"source":"folio"}'), + ('5a1a1adb-de71-45e6-ba94-1c0838969f04', '{"id":"5a1a1adb-de71-45e6-ba94-1c0838969f04","name":"Continuing resource status unknown","code":"e","displayFormat":{"delimiter":"-","keepDelimiter":true},"source":"folio"}'), + ('5f84208a-0aa6-4694-a58f-5310b654f012', '{"id":"5f84208a-0aa6-4694-a58f-5310b654f012","name":"Date of distribution/release/issue and production/recording session when different","code":"p","displayFormat":{"delimiter":",","keepDelimiter":false},"source":"folio"}'), + ('e77bb7ed-2e53-4c62-8b06-5907b8934ba7', '{"id":"e77bb7ed-2e53-4c62-8b06-5907b8934ba7","name":"Dates unknown","code":"n","displayFormat":{"delimiter":",","keepDelimiter":false},"source":"folio"}'), + ('9669a463-5971-42dc-9eee-046bbd678fb1', '{"id":"9669a463-5971-42dc-9eee-046bbd678fb1","name":"Detailed date","code":"e","displayFormat":{"delimiter":",","keepDelimiter":false},"source":"folio"}'), + ('6de732c5-c29b-4a10-9db0-0729ca960f12', '{"id":"6de732c5-c29b-4a10-9db0-0729ca960f12","name":"Inclusive dates of collection","code":"i","displayFormat":{"delimiter":"-","keepDelimiter":true},"source":"folio"}'), + ('8fa6d067-41ff-4362-96a0-96b16ddce267', '{"id":"8fa6d067-41ff-4362-96a0-96b16ddce267","name":"Multiple dates","code":"m","displayFormat":{"delimiter":",","keepDelimiter":false},"source":"folio"}'), + ('6f8cd9a8-26ac-4df6-8709-62fe2c0d04f8', '{"id":"6f8cd9a8-26ac-4df6-8709-62fe2c0d04f8","name":"No attempt to code","code":"|","displayFormat":{"delimiter":",","keepDelimiter":false},"source":"folio"}'), + ('77a09c3c-37bd-4ad3-aae4-9d86fc1b33d8', '{"id":"77a09c3c-37bd-4ad3-aae4-9d86fc1b33d8","name":"No dates given; BC date involved","code":"b","displayFormat":{"delimiter":",","keepDelimiter":false},"source":"folio"}'), + ('3a4296bf-504b-451b-9355-5806f8d88253', '{"id":"3a4296bf-504b-451b-9355-5806f8d88253","name":"Publication date and copyright date","code":"t","displayFormat":{"delimiter":",","keepDelimiter":false},"source":"folio"}'), + ('4afa7d3d-e6f5-4134-9ab5-32ad377d2432', '{"id":"4afa7d3d-e6f5-4134-9ab5-32ad377d2432","name":"Questionable date","code":"q","displayFormat":{"delimiter":",","keepDelimiter":false},"source":"folio"}'), + ('ccc293d5-9e88-4222-ac04-d058351ddb7b', '{"id":"ccc293d5-9e88-4222-ac04-d058351ddb7b","name":"Range of years of bulk of collection","code":"k","displayFormat":{"delimiter":"-","keepDelimiter":true},"source":"folio"}'), + ('47622598-61eb-4348-899a-1208275c3882', '{"id":"47622598-61eb-4348-899a-1208275c3882","name":"Reprint/reissue date and original date","code":"r","displayFormat":{"delimiter":",","keepDelimiter":false},"source":"folio"}'), + ('24a506e8-2a92-4ecc-bd09-ff849321fd5a', '{"id":"24a506e8-2a92-4ecc-bd09-ff849321fd5a","name":"Single known date/probable date","code":"s","displayFormat":{"delimiter":",","keepDelimiter":false},"source":"folio"}') +ON CONFLICT (id) DO UPDATE SET jsonb=EXCLUDED.jsonb; diff --git a/src/main/resources/templates/db_scripts/schema.json b/src/main/resources/templates/db_scripts/schema.json index 494fe5c0e..40f6fe9a7 100644 --- a/src/main/resources/templates/db_scripts/schema.json +++ b/src/main/resources/templates/db_scripts/schema.json @@ -865,6 +865,11 @@ "withMetadata": false, "withAuditing": false }, + { + "tableName": "instance_date_type", + "withMetadata": true, + "withAuditing": false + }, { "tableName": "authority_source_file", "mode": "delete", @@ -1171,6 +1176,11 @@ "run": "after", "snippetPath": "updateIdentifierTypeCanceledLCCN.sql", "fromModuleVersion": "27.2.0" + }, + { + "run": "after", + "snippetPath": "addInstanceDateTypes.sql", + "fromModuleVersion": "27.2.0" } ] } diff --git a/src/test/java/org/folio/rest/impl/BaseIntegrationTest.java b/src/test/java/org/folio/rest/impl/BaseIntegrationTest.java index e875652ec..f351b8fff 100644 --- a/src/test/java/org/folio/rest/impl/BaseIntegrationTest.java +++ b/src/test/java/org/folio/rest/impl/BaseIntegrationTest.java @@ -68,6 +68,10 @@ protected static Future doPut(HttpClient client, String requestUri return doRequest(client, HttpMethod.PUT, requestUri, body); } + protected static Future doPatch(HttpClient client, String requestUri, JsonObject body) { + return doRequest(client, HttpMethod.PATCH, requestUri, body); + } + protected static Future doDelete(HttpClient client, String requestUri) { return doRequest(client, HttpMethod.DELETE, requestUri, null); } diff --git a/src/test/java/org/folio/rest/impl/InstanceDateTypesIT.java b/src/test/java/org/folio/rest/impl/InstanceDateTypesIT.java new file mode 100644 index 000000000..20f71d618 --- /dev/null +++ b/src/test/java/org/folio/rest/impl/InstanceDateTypesIT.java @@ -0,0 +1,144 @@ +package org.folio.rest.impl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.folio.HttpStatus.HTTP_NO_CONTENT; +import static org.folio.utility.RestUtility.TENANT_ID; + +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpClient; +import io.vertx.junit5.VertxTestContext; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.UnaryOperator; +import org.folio.rest.jaxrs.model.DisplayFormat; +import org.folio.rest.jaxrs.model.InstanceDateType; +import org.folio.rest.jaxrs.model.InstanceDateTypePatch; +import org.folio.rest.jaxrs.model.InstanceDateTypes; +import org.folio.rest.jaxrs.model.Metadata; +import org.folio.rest.persist.PostgresClient; +import org.folio.rest.persist.cql.CQLWrapper; +import org.folio.services.instance.InstanceDateTypeService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class InstanceDateTypesIT extends BaseReferenceDataIntegrationTest { + + @Override + protected String referenceTable() { + return InstanceDateTypeService.INSTANCE_DATE_TYPE_TABLE; + } + + @Override + protected String resourceUrl() { + return "/instance-date-types"; + } + + @Override + protected Class targetClass() { + return InstanceDateType.class; + } + + @Override + protected Class collectionClass() { + return InstanceDateTypes.class; + } + + @Override + protected InstanceDateType sampleRecord() { + return new InstanceDateType() + .withId(UUID.randomUUID().toString()) + .withName("name") + .withCode("c") + .withDisplayFormat(new DisplayFormat().withDelimiter(",").withKeepDelimiter(false)) + .withSource(InstanceDateType.Source.FOLIO); + } + + @Override + protected Function> collectionRecordsExtractor() { + return InstanceDateTypes::getInstanceDateTypes; + } + + @Override + protected List> recordFieldExtractors() { + return List.of(InstanceDateType::getName, InstanceDateType::getCode, InstanceDateType::getDisplayFormat); + } + + @Override + protected Function idExtractor() { + return InstanceDateType::getId; + } + + @Override + protected Function metadataExtractor() { + return InstanceDateType::getMetadata; + } + + @Override + protected UnaryOperator recordModifyingFunction() { + return instanceDateType -> instanceDateType.withName("updated"); + } + + @Override + protected List queries() { + return List.of("code==c"); + } + + @Override + void getCollection_shouldReturn200AndEmptyCollection(Vertx vertx, VertxTestContext ctx) { + Assertions.assertTrue(true); + } + + @Override + void get_shouldReturn200AndRecordById(Vertx vertx, VertxTestContext ctx) { + Assertions.assertTrue(true); + } + + @Override + void post_shouldReturn201AndCreatedRecord(Vertx vertx, VertxTestContext ctx) { + Assertions.assertTrue(true); + } + + @Override + void put_shouldReturn204AndRecordIsUpdated(Vertx vertx, VertxTestContext ctx) { + Assertions.assertTrue(true); + } + + @Override + void delete_shouldReturn204AndRecordIsDeleted(Vertx vertx, VertxTestContext ctx) { + Assertions.assertTrue(true); + } + + @BeforeEach + void setUp(Vertx vertx, VertxTestContext ctx) { + var postgresClient = PostgresClient.getInstance(vertx, TENANT_ID); + postgresClient.delete(referenceTable(), (CQLWrapper) null) + .onComplete(event -> ctx.completeNow()); + } + + @Test + void patch_shouldReturn204AndRecordIsUpdated(Vertx vertx, VertxTestContext ctx) { + HttpClient client = vertx.createHttpClient(); + + var postgresClient = PostgresClient.getInstance(vertx, TENANT_ID); + + var newRecord = sampleRecord(); + + postgresClient.save(referenceTable(), newRecord) + .compose(id -> { + var updatedRecord = new InstanceDateTypePatch().withName("Updated"); + return doPatch(client, resourceUrlById(id), pojo2JsonObject(updatedRecord)) + .onComplete(verifyStatus(ctx, HTTP_NO_CONTENT)) + .compose(r -> postgresClient.getById(referenceTable(), id, targetClass()) + .onComplete(ctx.succeeding(dbRecord -> ctx.verify(() -> { + verifyRecordHasSameId(dbRecord, id, "Verify updated record exists in database"); + + assertThat(dbRecord) + .extracting(InstanceDateType::getName) + .isEqualTo(updatedRecord.getName()); + ctx.completeNow(); + })))); + }); + } +}