From 6b7208c6fe16ee223c0a3abf80d6c89e19d3ab65 Mon Sep 17 00:00:00 2001 From: "adam.zakrzewski" Date: Mon, 16 Dec 2024 12:37:53 +0100 Subject: [PATCH] initial tests for request validation against OpenApi spec --- live-ingester/pom.xml | 5 + .../OpenApiRequestValidationTest.java | 103 ++++++++++++++++++ .../util/insight_api/HxInsightRequest.java | 6 + .../util/insight_api/RequestLoader.java | 49 +++++++++ ...eate-document-request-empty-properties.yml | 17 +++ ...ate-document-request-without-source-id.yml | 38 +++++++ .../create-document-request.yml | 39 +++++++ .../delete-document-request.yml | 14 +++ .../get-presigned-urls-request.yml | 7 ++ .../update-document-request.yml | 69 ++++++++++++ 10 files changed, 347 insertions(+) create mode 100644 live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/domain/usecase/e2e/repository/OpenApiRequestValidationTest.java create mode 100644 live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/util/insight_api/HxInsightRequest.java create mode 100644 live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/util/insight_api/RequestLoader.java create mode 100644 live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request-empty-properties.yml create mode 100644 live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request-without-source-id.yml create mode 100644 live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request.yml create mode 100644 live-ingester/src/integration-test/resources/expected-hxinsight-requests/delete-document-request.yml create mode 100644 live-ingester/src/integration-test/resources/expected-hxinsight-requests/get-presigned-urls-request.yml create mode 100644 live-ingester/src/integration-test/resources/expected-hxinsight-requests/update-document-request.yml diff --git a/live-ingester/pom.xml b/live-ingester/pom.xml index cedbe3713..cd95b38d9 100644 --- a/live-ingester/pom.xml +++ b/live-ingester/pom.xml @@ -130,6 +130,11 @@ lombok provided + + org.openapi4j + openapi-operation-validator + 1.0.7 + diff --git a/live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/domain/usecase/e2e/repository/OpenApiRequestValidationTest.java b/live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/domain/usecase/e2e/repository/OpenApiRequestValidationTest.java new file mode 100644 index 000000000..662e7f85a --- /dev/null +++ b/live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/domain/usecase/e2e/repository/OpenApiRequestValidationTest.java @@ -0,0 +1,103 @@ +package org.alfresco.hxi_connector.live_ingester.domain.usecase.e2e.repository; + +import org.alfresco.hxi_connector.live_ingester.util.insight_api.HxInsightRequest; +import org.alfresco.hxi_connector.live_ingester.util.insight_api.RequestLoader; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.openapi4j.operation.validator.model.Request; +import org.openapi4j.operation.validator.model.impl.Body; +import org.openapi4j.operation.validator.model.impl.DefaultRequest; +import org.openapi4j.operation.validator.validation.OperationValidator; +import org.openapi4j.parser.OpenApi3Parser; +import org.openapi4j.parser.model.v3.OpenApi3; +import org.openapi4j.schema.validator.ValidationData; + +import java.net.URL; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openapi4j.operation.validator.model.Request.Method.POST; + +public class OpenApiRequestValidationTest { + + private static final String SPECIFICATION_URL = "http://hxai-data-platform-dev-swagger-ui.s3-website-us-east-1.amazonaws.com/docs/insight-ingestion-api-swagger.json"; + private static URL specificationUrl; + + @BeforeAll + public static void setUp() throws Exception { + specificationUrl = new URL(SPECIFICATION_URL); + } + + @Test + public void testRequestToPresignedUrls() throws Exception { + + OperationValidator operationValidator = loadOperationValidator(specificationUrl, "presignedUrls"); + + HxInsightRequest hxInsightRequest = RequestLoader.load("/expected-hxinsight-requests/get-presigned-urls-request.yml"); + + Request request = makeRequest(hxInsightRequest); + + checkRequest(request, operationValidator); + } + + @Test + public void testCreateRequestToIngestionEvents() throws Exception { + + OperationValidator operationValidator = loadOperationValidator(specificationUrl, "ingestionEvents"); + + HxInsightRequest hxInsightRequest = RequestLoader.load("/expected-hxinsight-requests/create-document-request.yml"); + + Request request = makeRequest(hxInsightRequest); + + checkRequest(request, operationValidator); + } + + @Test + public void testCreateRequestToIngestionEventsWithoutSourceId() throws Exception { + + OperationValidator operationValidator = loadOperationValidator(specificationUrl, "ingestionEvents"); + + HxInsightRequest hxInsightRequest = RequestLoader.load("/expected-hxinsight-requests/create-document-request-without-source-id.yml"); + + Request request = makeRequest(hxInsightRequest); + + checkRequest(request, operationValidator); + } + + @Test + public void testCreateRequestToIngestionEventsWithEmptyProperties() throws Exception { + + OperationValidator operationValidator = loadOperationValidator(specificationUrl, "ingestionEvents"); + + HxInsightRequest hxInsightRequest = RequestLoader.load("/expected-hxinsight-requests/create-document-request-empty-properties.yml"); + + Request request = makeRequest(hxInsightRequest); + + checkRequest(request, operationValidator); + } + + protected OperationValidator loadOperationValidator(URL specification, String operationId) throws Exception { + OpenApi3 openApi = new OpenApi3Parser().parse(specification, true); + + return new OperationValidator( + openApi, + openApi.getPathItemByOperationId(operationId), + openApi.getOperationById(operationId)); + } + + private static Request makeRequest(HxInsightRequest hxInsightRequest) { + DefaultRequest.Builder builder = new DefaultRequest.Builder(hxInsightRequest.url(), POST); + hxInsightRequest.headers().forEach(builder::header); + if (hxInsightRequest.body() != null) { + builder.body(Body.from(hxInsightRequest.body())); + } + return builder.build(); + } + + protected void checkRequest(Request sentRequest, OperationValidator operationValidator) { + ValidationData validation = new ValidationData<>(); + operationValidator.validateBody(sentRequest, validation); + operationValidator.validateHeaders(sentRequest, validation); + + assertTrue(validation.isValid(), validation.results().toString()); + } + } diff --git a/live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/util/insight_api/HxInsightRequest.java b/live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/util/insight_api/HxInsightRequest.java new file mode 100644 index 000000000..885bff059 --- /dev/null +++ b/live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/util/insight_api/HxInsightRequest.java @@ -0,0 +1,6 @@ +package org.alfresco.hxi_connector.live_ingester.util.insight_api; + +import java.util.Map; + +public record HxInsightRequest(String url, Map headers, String body) { +} diff --git a/live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/util/insight_api/RequestLoader.java b/live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/util/insight_api/RequestLoader.java new file mode 100644 index 000000000..d69d7b256 --- /dev/null +++ b/live-ingester/src/integration-test/java/org/alfresco/hxi_connector/live_ingester/util/insight_api/RequestLoader.java @@ -0,0 +1,49 @@ +package org.alfresco.hxi_connector.live_ingester.util.insight_api; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import org.alfresco.hxi_connector.live_ingester.domain.exception.LiveIngesterRuntimeException; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class RequestLoader { + private final static ObjectMapper JSON_MAPPER = new ObjectMapper(); + private final static ObjectMapper YAML_MAPPER = new ObjectMapper(new YAMLFactory()); + + public static HxInsightRequest load(String path) + { + Map data; + try (InputStream stream = HxInsightRequest.class.getResourceAsStream(path)) + { + data = YAML_MAPPER.readValue(stream.readAllBytes(), HashMap.class); + } + catch (IOException e) + { + throw new UncheckedIOException(e); + } + Object body = data.get("body"); + String bodyAsString = null; + if (body != null) + { + try + { + bodyAsString = JSON_MAPPER.writeValueAsString(body); + } + catch (JsonProcessingException e) + { + throw new LiveIngesterRuntimeException(e); + } + } + return new HxInsightRequest((String) data.get("url"), (Map) data.get("headers"), bodyAsString); + } +} + diff --git a/live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request-empty-properties.yml b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request-empty-properties.yml new file mode 100644 index 000000000..87388f872 --- /dev/null +++ b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request-empty-properties.yml @@ -0,0 +1,17 @@ +url: /v1/ingestion-events +headers: + authorization: string + content-type: application/json + hxp-environment: string + user-agent: string +body: [ + { + "objectId": "d71dd823-82c7-477c-8490-04cb0e826e65", + "sourceId" : "alfresco-dummy-source-id-0a63de491876", + "eventType": "create", + "sourceTimestamp": 1611227656423, + "properties": { + + } + } +] diff --git a/live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request-without-source-id.yml b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request-without-source-id.yml new file mode 100644 index 000000000..236b322c3 --- /dev/null +++ b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request-without-source-id.yml @@ -0,0 +1,38 @@ +url: /v1/ingestion-events +headers: + authorization: string + content-type: application/json + hxp-environment: string + user-agent: string +body: [ + { + "objectId": "d71dd823-82c7-477c-8490-04cb0e826e65", + "eventType": "create", + "sourceTimestamp": 1611227656423, + "properties": { + "cm:autoVersion": {"value": true}, + "createdAt": {"value": 1611227655695}, + "modifiedAt": {"value" : 1611227655695}, + "cm:versionType": {"value": "MAJOR"}, + "aspectsNames": {"value": ["cm:versionable", "cm:auditable"]}, + "cm:name": { + "value": "purchase-order-scan.doc", + "annotation" : "name" + }, + "type": {"value": "cm:content"}, + "createdBy": {"value": "admin"}, + "modifiedBy": {"value": "admin"}, + "cm:content": { + "file": { + "content-metadata": { + "size": 531152, + "name": "purchase-order-scan.doc", + "content-type": "application/msword" + } + } + }, + "ALLOW_ACCESS": {"value": ["GROUP_EVERYONE"]}, + "DENY_ACCESS": {"value": []} + } + } +] diff --git a/live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request.yml b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request.yml new file mode 100644 index 000000000..206e1cf68 --- /dev/null +++ b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/create-document-request.yml @@ -0,0 +1,39 @@ +url: /v1/ingestion-events +headers: + authorization: string + content-type: application/json + hxp-environment: string + user-agent: string +body: [ + { + "objectId": "d71dd823-82c7-477c-8490-04cb0e826e65", + "sourceId" : "alfresco-dummy-source-id-0a63de491876", + "eventType": "create", + "sourceTimestamp": 1611227656423, + "properties": { + "cm:autoVersion": {"value": true}, + "createdAt": {"value": 1611227655695}, + "modifiedAt": {"value" : 1611227655695}, + "cm:versionType": {"value": "MAJOR"}, + "aspectsNames": {"value": ["cm:versionable", "cm:auditable"]}, + "cm:name": { + "value": "purchase-order-scan.doc", + "annotation" : "name" + }, + "type": {"value": "cm:content"}, + "createdBy": {"value": "admin"}, + "modifiedBy": {"value": "admin"}, + "cm:content": { + "file": { + "content-metadata": { + "size": 531152, + "name": "purchase-order-scan.doc", + "content-type": "application/msword" + } + } + }, + "ALLOW_ACCESS": {"value": ["GROUP_EVERYONE"]}, + "DENY_ACCESS": {"value": []} + } + } +] diff --git a/live-ingester/src/integration-test/resources/expected-hxinsight-requests/delete-document-request.yml b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/delete-document-request.yml new file mode 100644 index 000000000..7d5d285ea --- /dev/null +++ b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/delete-document-request.yml @@ -0,0 +1,14 @@ +url: /v1/ingestion-events +headers: + authorization: string + content-type: application/json + hxp-environment: string + user-agent: string +body: [ + { + "objectId": "d71dd823-82c7-477c-8490-04cb0e826e65", + "sourceId" : "alfresco-dummy-source-id-0a63de491876", + "eventType": "delete", + "sourceTimestamp": 1611656982995 + } +] diff --git a/live-ingester/src/integration-test/resources/expected-hxinsight-requests/get-presigned-urls-request.yml b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/get-presigned-urls-request.yml new file mode 100644 index 000000000..e2a221118 --- /dev/null +++ b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/get-presigned-urls-request.yml @@ -0,0 +1,7 @@ +url: /v1/presigned-urls +headers: + authorization: string + content-type: application/json + hxp-environment: string + user-agent: string + count: string diff --git a/live-ingester/src/integration-test/resources/expected-hxinsight-requests/update-document-request.yml b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/update-document-request.yml new file mode 100644 index 000000000..d2fd8c838 --- /dev/null +++ b/live-ingester/src/integration-test/resources/expected-hxinsight-requests/update-document-request.yml @@ -0,0 +1,69 @@ +url: /v1/ingestion-events +headers: + authorization: string + content-type: application/json + hxp-environment: string + user-agent: string +body: [ + { + "objectId": "d71dd823-82c7-477c-8490-04cb0e826e65", + "sourceId": "alfresco-dummy-source-id-0a63de491876", + "eventType": "update", + "sourceTimestamp": 1611656982995, + "properties": { + "cm:title": { + "value": "Purchase Order" + }, + "aspectsNames": { + "value": [ + "cm:versionable", + "cm:author", + "cm:titled" + ] + }, + "modifiedBy": { + "value": "abeecher" + }, + "createdAt": { + "value": 1611227655695 + }, + "modifiedAt": { + "value": 1611227655695 + }, + "cm:versionLabel": { + "value": "1.0" + }, + "createdBy": { + "value": "admin" + }, + "ALLOW_ACCESS": { + "value": [ + "GROUP_EVERYONE" + ] + }, + "cm:name": { + "value": "purchase-order-scan.pdf", + "annotation": "name" + }, + "type": { + "value": "cm:content" + }, + "DENY_ACCESS": { + "value": [] + }, + "cm:content": { + "file": { + "content-metadata": { + "size": 531152, + "name": "purchase-order-scan.pdf", + "content-type": "application/pdf" + } + } + } + }, + "removedProperties": [ + "cm:versionType", + "cm:description" + ] + } +]