diff --git a/.spectral.yaml b/.spectral.yaml index 5c1da01..9ed308c 100644 --- a/.spectral.yaml +++ b/.spectral.yaml @@ -61,6 +61,7 @@ rules: info-license: off license-url: off contact-properties: off + oas3-valid-media-example: off docs-descriptions: given: - "#DescribableObjects" diff --git a/mod-record-specifications-server/src/main/java/org/folio/rspec/exception/IllegalSpecificationChangeException.java b/mod-record-specifications-server/src/main/java/org/folio/rspec/exception/IllegalSpecificationChangeException.java deleted file mode 100644 index caa3d0a..0000000 --- a/mod-record-specifications-server/src/main/java/org/folio/rspec/exception/IllegalSpecificationChangeException.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.folio.rspec.exception; - -import lombok.Getter; -import org.folio.rspec.domain.dto.Scope; - -public class IllegalSpecificationChangeException extends RuntimeException { - - private static final String MESSAGE_BASE_TEMPLATE = "%s action is prohibited for %s scope."; - private static final String MESSAGE_FULL_TEMPLATE = MESSAGE_BASE_TEMPLATE + " %s"; - - protected IllegalSpecificationChangeException(ActionType action, Scope scope) { - super(String.format(MESSAGE_BASE_TEMPLATE, action.name().toLowerCase(), scope.getValue())); - } - - protected IllegalSpecificationChangeException(ActionType action, Scope scope, String additionalInfo) { - super(String.format(MESSAGE_FULL_TEMPLATE, action.name().toLowerCase(), scope.getValue(), additionalInfo)); - } - - public static IllegalSpecificationChangeException forDelete(Scope scope) { - return new IllegalSpecificationChangeException(ActionType.DELETE, scope); - } - - public static IllegalSpecificationChangeException forUpdate(Scope scope) { - return new IllegalSpecificationChangeException(ActionType.UPDATE, scope); - } - - public static IllegalSpecificationChangeException forTagChange(Scope scope) { - return new IllegalSpecificationChangeException(ActionType.UPDATE, scope, - AdditionalRestriction.TAG_CHANGE.getAdditionalInfo()); - } - - public static IllegalSpecificationChangeException forRequiredChange(Scope scope) { - return new IllegalSpecificationChangeException(ActionType.UPDATE, scope, - AdditionalRestriction.REQUIRED_CHANGE.getAdditionalInfo()); - } - - public static IllegalSpecificationChangeException forRepeatableChange(Scope scope) { - return new IllegalSpecificationChangeException(ActionType.UPDATE, scope, - AdditionalRestriction.REPEATABLE_CHANGE.getAdditionalInfo()); - } - - protected enum ActionType { - DELETE, UPDATE - } - - @Getter - protected enum AdditionalRestriction { - TAG_CHANGE("tag value is unmodifiable"), - REQUIRED_CHANGE("required is unmodifiable"), - REPEATABLE_CHANGE("repeatable is unmodifiable"), - DEPRECATED_CHANGE("deprecated is unmodifiable") - ; - - private final String additionalInfo; - - AdditionalRestriction(String additionalInfo) { - this.additionalInfo = additionalInfo; - } - } -} diff --git a/mod-record-specifications-server/src/main/java/org/folio/rspec/service/SpecificationFieldService.java b/mod-record-specifications-server/src/main/java/org/folio/rspec/service/SpecificationFieldService.java index c43ef07..9d9ce71 100644 --- a/mod-record-specifications-server/src/main/java/org/folio/rspec/service/SpecificationFieldService.java +++ b/mod-record-specifications-server/src/main/java/org/folio/rspec/service/SpecificationFieldService.java @@ -8,7 +8,6 @@ import org.folio.rspec.domain.dto.SpecificationFieldDtoCollection; import org.folio.rspec.domain.entity.Specification; import org.folio.rspec.domain.repository.FieldRepository; -import org.folio.rspec.exception.IllegalSpecificationChangeException; import org.folio.rspec.exception.ResourceNotFoundException; import org.folio.rspec.service.mapper.SpecificationFieldMapper; import org.springframework.stereotype.Service; @@ -40,9 +39,6 @@ public SpecificationFieldDto createLocalField(Specification specification, Speci @Transactional public void deleteField(UUID id) { var fieldEntity = fieldRepository.findById(id).orElseThrow(() -> ResourceNotFoundException.forField(id)); - if (fieldEntity.getScope() != Scope.CUSTOM) { - throw IllegalSpecificationChangeException.forDelete(fieldEntity.getScope()); - } fieldRepository.delete(fieldEntity); } diff --git a/mod-record-specifications-server/src/test/java/org/folio/api/SpecificationStorageFieldsApiIT.java b/mod-record-specifications-server/src/test/java/org/folio/api/SpecificationStorageFieldsApiIT.java new file mode 100644 index 0000000..8d35897 --- /dev/null +++ b/mod-record-specifications-server/src/test/java/org/folio/api/SpecificationStorageFieldsApiIT.java @@ -0,0 +1,97 @@ +package org.folio.api; + +import static org.folio.support.ApiEndpoints.fieldPath; +import static org.folio.support.ApiEndpoints.specificationFieldsPath; +import static org.folio.support.TestConstants.BIBLIOGRAPHIC_SPECIFICATION_ID; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.jayway.jsonpath.JsonPath; +import java.io.UnsupportedEncodingException; +import java.util.Map; +import java.util.UUID; +import org.folio.rspec.domain.dto.SpecificationFieldChangeDto; +import org.folio.rspec.exception.ResourceNotFoundException; +import org.folio.spring.testing.type.IntegrationTest; +import org.folio.support.IntegrationTestBase; +import org.folio.support.TestConstants; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +@IntegrationTest +class SpecificationStorageFieldsApiIT extends IntegrationTestBase { + + @BeforeAll + static void beforeAll() { + setUpTenant(); + } + + @Test + void deleteField_shouldReturn204AndDeleteLocalField() throws Exception { + var createdFieldId = createLocalField(getLocalFieldDto()); + + doDelete(fieldPath(createdFieldId)); + + doGet(specificationFieldsPath(BIBLIOGRAPHIC_SPECIFICATION_ID)) + .andExpect(jsonPath("$.fields.[*].id", not(hasItem(createdFieldId)))); + } + + @Test + void deleteField_shouldReturn404WhenFieldNotExist() throws Exception { + var notExistId = UUID.randomUUID(); + tryDelete(fieldPath(notExistId)) + .andExpect(status().isNotFound()) + .andExpect(exceptionMatch(ResourceNotFoundException.class)) + .andExpect(errorMessageMatch(is("field definition with ID [%s] was not found".formatted(notExistId)))); + } + + @Test + void updateField_shouldReturn201AndUpdateLocalField() throws Exception { + var localTestField = getLocalFieldDto(); + var createdFieldId = createLocalField(localTestField); + + localTestField.setDeprecated(true); + localTestField.setUrl("http://www.viverra.com"); + + doPut(fieldPath(createdFieldId), localTestField); + + doGet(specificationFieldsPath(BIBLIOGRAPHIC_SPECIFICATION_ID)) + .andExpect(jsonPath("$.fields.[*]", hasItem(Matchers.<Map<String, Object>>allOf( + hasEntry("id", createdFieldId), + hasEntry("deprecated", true), + hasEntry("url", "http://www.viverra.com") + )))); + } + + @Test + void updateField_shouldReturn404WhenFieldNotExist() throws Exception { + var notExistId = UUID.randomUUID(); + tryPut(fieldPath(notExistId), getLocalFieldDto()) + .andExpect(status().isNotFound()) + .andExpect(exceptionMatch(ResourceNotFoundException.class)) + .andExpect(errorMessageMatch(is("field definition with ID [%s] was not found".formatted(notExistId)))); + } + + private SpecificationFieldChangeDto getLocalFieldDto() { + return new SpecificationFieldChangeDto() + .tag("998") + .required(true) + .deprecated(false) + .required(false) + .label("Local Test Field"); + } + + private String createLocalField(SpecificationFieldChangeDto localTestField) throws UnsupportedEncodingException { + return JsonPath.read( + doPost(specificationFieldsPath(TestConstants.BIBLIOGRAPHIC_SPECIFICATION_ID), localTestField) + .andReturn() + .getResponse().getContentAsString(), + "$.id").toString(); + } + +} diff --git a/mod-record-specifications-server/src/test/java/org/folio/support/ApiEndpoints.java b/mod-record-specifications-server/src/test/java/org/folio/support/ApiEndpoints.java index 6ce68da..db327a5 100644 --- a/mod-record-specifications-server/src/test/java/org/folio/support/ApiEndpoints.java +++ b/mod-record-specifications-server/src/test/java/org/folio/support/ApiEndpoints.java @@ -7,10 +7,12 @@ @UtilityClass public class ApiEndpoints { - public static final String SPECIFICATIONS_PATH = "/specification-storage/specifications"; + public static final String SPECIFICATION_STORAGE_PATH = "/specification-storage"; + public static final String SPECIFICATIONS_PATH = SPECIFICATION_STORAGE_PATH + "/specifications"; public static final String SPECIFICATION_RULES_PATH = SPECIFICATIONS_PATH + "/%s/rules"; public static final String SPECIFICATION_RULE_PATH = SPECIFICATION_RULES_PATH + "/%s"; public static final String SPECIFICATION_FIELDS_PATH = SPECIFICATIONS_PATH + "/%s/fields"; + public static final String FIELD_PATH = SPECIFICATION_STORAGE_PATH + "/fields/%s"; public static String specificationsPath() { return SPECIFICATIONS_PATH; @@ -44,6 +46,14 @@ public static String specificationFieldsPath(UUID specId) { return specificationFieldsPath(specId.toString()); } + public static String fieldPath(String fieldId) { + return FIELD_PATH.formatted(fieldId); + } + + public static String fieldPath(UUID fieldId) { + return fieldPath(fieldId.toString()); + } + private static String addQueryParams(String path, QueryParams queryParams) { if (queryParams.isEmpty()) { return path;