diff --git a/.gitignore b/.gitignore index 6e408509a217113a28e975536bb60e2a6e9e57e6..345414100643f71ae1a17b45ad34413f10ac85da 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ build/ ### Integration tests ### .gradle +**/allure-results/ ### Environment Configuration ### *.env diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a57fda27b072fa10653eb9818967b8fdb4f0de1b..b25beafe41df2996ee999643f6c0ec0190e4abf6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,6 +34,7 @@ variables: IBM_HELM_DEPLOY_PATH: devops/ibm/ibm-storage-deploy CORE_BUILD_SUBDIR: storage-core + ACCEPTANCE_TEST_DIR: "storage-acceptance-test" CORE_COVERAGE_THRESHOLD: "80" include: - project: "osdu/platform/ci-cd-pipelines" @@ -247,6 +248,94 @@ azure_test: aws-test-java: image: $CI_REGISTRY/osdu/platform/deployment-and-operations/base-containers-aws/aws-maven/aws-maven:v2.0 +core-acceptance-test: + stage: acceptance + extends: core-test + needs: ["core-test"] + variables: + OPA_INTEGRATION_ENABLED: "true" + ROOT_USER_OPENID_PROVIDER_CLIENT_ID: $DATA_ROOT_OPENID_PROVIDER_CLIENT_ID + ROOT_USER_OPENID_PROVIDER_CLIENT_SECRET: $DATA_ROOT_OPENID_PROVIDER_CLIENT_SECRET + NO_ACCESS_USER_OPENID_PROVIDER_CLIENT_ID: $TEST_NO_ACCESS_OPENID_PROVIDER_CLIENT_ID + NO_ACCESS_USER_OPENID_PROVIDER_CLIENT_SECRET: $TEST_NO_ACCESS_OPENID_PROVIDER_CLIENT_SECRET + PRIVILEGED_USER_OPENID_PROVIDER_CLIENT_ID: $TEST_OPENID_PROVIDER_CLIENT_ID + PRIVILEGED_USER_OPENID_PROVIDER_CLIENT_SECRET: $TEST_OPENID_PROVIDER_CLIENT_SECRET + ENTITLEMENTS_DOMAIN: $GROUP_ID + script: + - > + $MAVEN_BUILD . test-results.log + verify -DdisableXmlReport=true + --quiet + --file $ACCEPTANCE_TEST_DIR/pom.xml + --update-snapshots + +aws-acceptance-test: + extends: + - .maven + - .aws + - .aws_common_variables + - .aws_variables + stage: acceptance + image: $CI_REGISTRY/osdu/platform/deployment-and-operations/base-containers-aws/aws-maven/aws-maven:v2.1 + needs: [{ job: 'aws-update-tf', optional: true }] + before_script: + - !reference [.maven, before_script] + - !reference [.aws, before_script] + - !reference [.aws_variables, before_script] + variables: + OPA_INTEGRATION_ENABLED: "true" + script: + - export GROUP_ID=$DOMAIN + - export COGNITO_AUTH_TOKEN_URI=$(aws ssm get-parameter --name "/osdu/cognito/${COGNITO_NAME}/oauth/token-uri" --query Parameter.Value --output text --region $AWS_REGION) + - export COGNITO_ALLOWED_SCOPES=$(aws ssm get-parameter --name "/osdu/cognito/${COGNITO_NAME}/oauth/allowed-scopes" --query Parameter.Value --output text --region $AWS_REGION) + - export AWS_CLIENT_CREDENTIALS_CLIENT_ID=$(aws ssm get-parameter --name "/osdu/cognito/${COGNITO_NAME}/client/client-credentials/id" --query Parameter.Value --output text --region $AWS_REGION) + - export AWS_CLIENT_CREDENTIALS_CLIENT_SECRET=$(aws secretsmanager get-secret-value --secret-id /osdu/cognito/${COGNITO_NAME}/client-credentials-secret --query SecretString --output json --region $AWS_REGION | sed -e 's/\\\"/\"/g' -e 's/^.//g' -e 's/.$//g' | jq -r '.client_credentials_client_secret') + - export AWS_SERVICE_PRINCIPAL_AUTHORIZATION=$(echo -n "${AWS_CLIENT_CREDENTIALS_CLIENT_ID}:${AWS_CLIENT_CREDENTIALS_CLIENT_SECRET}" | base64) + - export PRIVILEGED_USER_TOKEN=$(aws cognito-idp initiate-auth --region ${AWS_REGION} --auth-flow ${AWS_COGNITO_AUTH_FLOW} --client-id ${AWS_COGNITO_CLIENT_ID} --auth-parameters USERNAME=${AWS_COGNITO_AUTH_PARAMS_USER},PASSWORD=${AWS_COGNITO_AUTH_PARAMS_PASSWORD} --query AuthenticationResult.AccessToken --output text) + + - export ROOT_USER_TOKEN=$(curl --location ${COGNITO_AUTH_TOKEN_URI} --header "Content-Type:application/x-www-form-urlencoded" --header "Authorization:Basic ${AWS_SERVICE_PRINCIPAL_AUTHORIZATION}" --data-urlencode "grant_type=client_credentials" --data-urlencode ${COGNITO_ALLOWED_SCOPES} --http1.1 | jq -r '.access_token') + - export NO_ACCESS_USER_TOKEN=$(aws cognito-idp initiate-auth --region ${AWS_REGION} --auth-flow ${AWS_COGNITO_AUTH_FLOW} --client-id ${AWS_COGNITO_CLIENT_ID} --auth-parameters USERNAME=${AWS_COGNITO_AUTH_PARAMS_USER_NO_ACCESS},PASSWORD=${AWS_COGNITO_AUTH_PARAMS_PASSWORD} --query AuthenticationResult.AccessToken --output text) + - > + $MAVEN_BUILD . test-results.log + verify -DdisableXmlReport=true + --quiet + --file $ACCEPTANCE_TEST_DIR/pom.xml + --update-snapshots + allow_failure: true + only: + variables: + - $AWS == '1' + +azure-acceptance-test: + stage: acceptance + script: + - echo "This job is expected to fail" + - exit 1 + allow_failure: true + only: + variables: + - $AZURE == '1' + +gc-acceptance-test: + stage: acceptance + script: + - echo "This job is expected to fail" + - exit 1 + allow_failure: true + only: + variables: + - $GC == '1' + +ibm-acceptance-test: + stage: acceptance + script: + - echo "This job is expected to fail" + - exit 1 + allow_failure: true + only: + variables: + - $IBM == '1' + fossa-analyze: image: $CI_REGISTRY/divido/fossa-with-cache:v0.9-jdk17 fossa-check-notice: diff --git a/storage-acceptance-test/docs/README.md b/storage-acceptance-test/docs/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0efa62efcb2e8fcc0054d4eedb819cb4a6aea0f7 --- /dev/null +++ b/storage-acceptance-test/docs/README.md @@ -0,0 +1,84 @@ +### Running E2E Tests + +You will need to have the following environment variables defined. + +| name | value | description | sensitive? | source | +|-----------------------|------------------------------------------------|------------------------------------------------|------------|--------| +| `ENTITLEMENTS_DOMAIN` | ex`opendes-gc.projects.com` | OSDU R2 entitlements domain to run tests under | no | - | +| `LEGAL_URL` | ex`http://localhost:8080/api/legal/v1/` | Legal API endpoint | no | - | +| `STORAGE_URL` | ex`http://localhost:8080/api/storage/v2/` | Endpoint of storage service | no | - | +| `TENANT_NAME` | ex `opendes` | OSDU tenant used for testing | no | -- | +| `ENTITLEMENTS_URL` | ex`http://localhost:8080/api/entitlements/v2/` | Endpoint of entitlements service | no | - | + +Authentication can be provided as OIDC config: + +| name | value | description | sensitive? | source | +|-------------------------------------------------|-----------------------------------------|-------------------------------|------------|--------| +| `ROOT_USER_OPENID_PROVIDER_CLIENT_ID` | `********` | ROOT_USER Client Id | yes | - | +| `ROOT_USER_OPENID_PROVIDER_CLIENT_SECRET` | `********` | ROOT_USER Client secret | yes | - | +| `NO_ACCESS_USER_OPENID_PROVIDER_CLIENT_ID` | `********` | NO_ACCESS_USER Client Id | yes | - | +| `NO_ACCESS_USER_OPENID_PROVIDER_CLIENT_SECRET` | `********` | NO_ACCESS_USER Client secret | yes | - | +| `PRIVILEGED_USER_OPENID_PROVIDER_CLIENT_ID` | `********` | PRIVILEGED_USER Client Id | yes | - | +| `PRIVILEGED_USER_OPENID_PROVIDER_CLIENT_SECRET` | `********` | PRIVILEGED_USER Client secret | yes | - | +| `TEST_OPENID_PROVIDER_URL` | `https://keycloak.com/auth/realms/osdu` | OpenID provider url | yes | - | + +Or tokens can be used directly from env variables: + +| name | value | description | sensitive? | source | +|-------------------------|------------|-----------------------|------------|--------| +| `PRIVILEGED_USER_TOKEN` | `********` | PRIVILEGED_USER Token | yes | - | +| `NO_ACCESS_USER_TOKEN` | `********` | NO_ACCESS_USER Token | yes | - | +| `ROOT_USER_TOKEN` | `********` | ROOT_USER Token | yes | - | + + +Feature testing is controlled with the following environment variables: + +| name | value | description | +|---------------------------|-------------------|---------------------------------------------------------------------------| +| `TEST_REPLAY_ENABLED` | `true` OR `false` | Controls Replay API tests. | +| `COLLABORATION_ENABLED` | `true` OR `false` | Controls collaboration feature tests. | +| `OPA_INTEGRATION_ENABLED` | `true` OR `false` | Used to adjust assertions if integration with OPA\Policy enabled\disabled | + + + +**Entitlements configuration for integration accounts** + +| PRIVILEGED_USER | NO_ACCESS_USER | ROOT_USER | +|----------------------------|---------------------------|---------------------------| +| users | users | users | +| service.entitlements.user | service.entitlements.user | users.data.root | +| service.entitlements.admin | service.storage.admin | service.entitlements.user | +| service.storage.admin | | service.storage.viewer | +| service.storage.creator | | | +| service.storage.viewer | | | +| service.legal.admin | | | +| service.legal.editor | | | +| data.test1 | | | +| data.integration.test | | | + +Execute following command to build code and run all the integration tests: + + ```bash + # Note: this assumes that the environment variables for integration tests as outlined + # above are already exported in your environment. + # build + install integration test core + $ (cd storage-acceptance-test && mvn clean test) + ``` + +## License + +Copyright © Google LLC + +Copyright © EPAM Systems + +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](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. diff --git a/storage-acceptance-test/pom.xml b/storage-acceptance-test/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..997268ec154eae79c7c1220dbcda19b2f891057b --- /dev/null +++ b/storage-acceptance-test/pom.xml @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2017-2019, Schlumberger + + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <groupId>org.opengroup.osdu.storage</groupId> + <artifactId>storage-acceptance-test</artifactId> + <version>0.28.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <properties> + <maven.compiler.target>17</maven.compiler.target> + <maven.compiler.source>17</maven.compiler.source> + <java.version>17</java.version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.main.basedir>${project.basedir}</project.main.basedir> + <allure.version>2.29.0</allure.version> + <aspectj.version>1.9.22</aspectj.version> + <log4j.version>2.23.0</log4j.version> + <jackson.version>2.16.1</jackson.version> + <jackson-databind.version>2.16.1</jackson-databind.version> + <os-core-common.version>1.0.0</os-core-common.version> + <maven-surefire-plugin.version>3.2.5</maven-surefire-plugin.version> + <argLine> + --add-opens java.base/java.lang=ALL-UNNAMED + </argLine> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>io.qameta.allure</groupId> + <artifactId>allure-bom</artifactId> + <version>${allure.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>1.19.4</version> + </dependency> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt</artifactId> + <version>0.9.1</version> + <exclusions> + <exclusion> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>1.18.30</version> + </dependency> + <dependency> + <groupId>io.qameta.allure</groupId> + <artifactId>allure-junit5</artifactId> + </dependency> + <dependency> + <groupId>io.qameta.allure</groupId> + <artifactId>allure-generator</artifactId> + <version>2.30.0</version> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>5.10.4</version> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-console</artifactId> + <version>1.10.4</version> + </dependency> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>os-core-common</artifactId> + <version>${os-core-common.version}</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.14.0</version> + </dependency> + <dependency> + <groupId>org.apache.httpcomponents.client5</groupId> + <artifactId>httpclient5</artifactId> + <version>5.2.3</version> + </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.9.1</version> + </dependency> + <dependency> + <groupId>jakarta.ws.rs</groupId> + <artifactId>jakarta.ws.rs-api</artifactId> + <version>3.1.0</version> + </dependency> + <dependency> + <groupId>com.nimbusds</groupId> + <artifactId>oauth2-oidc-sdk</artifactId> + <version>9.15</version> + </dependency> + </dependencies> + + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>${maven-surefire-plugin.version}</version> + </plugin> + </plugins> + </build> + + <repositories> + <repository> + <id>${repo.releases.id}</id> + <url>${repo.releases.url}</url> + </repository> + </repositories> + + <distributionManagement> + <repository> + <id>${publish.releases.id}</id> + <url>${publish.releases.url}</url> + </repository> + <snapshotRepository> + <id>${publish.snapshots.id}</id> + <url>${publish.snapshots.url}</url> + </snapshotRepository> + </distributionManagement> + + <profiles> + <profile> + <id>Default</id> + <activation> + <property> + <name>!repo.releases.id</name> + </property> + </activation> + <properties> + <repo.releases.id>community-maven-repo</repo.releases.id> + <publish.snapshots.id>community-maven-via-job-token</publish.snapshots.id> + <publish.releases.id>community-maven-via-job-token</publish.releases.id> + <repo.releases.url>https://community.opengroup.org/api/v4/groups/17/-/packages/maven</repo.releases.url> + <publish.snapshots.url>https://community.opengroup.org/api/v4/projects/44/packages/maven</publish.snapshots.url> + <publish.releases.url>https://community.opengroup.org/api/v4/projects/44/packages/maven</publish.releases.url> + </properties> + </profile> + </profiles> +</project> diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/api/HealthCheckApiIntegrationTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/api/HealthCheckApiIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bae65090176418ed1ee5d605eb8bd524a5de2838 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/api/HealthCheckApiIntegrationTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2020-2024 Google LLC + * Copyright 2020-2024 EPAM Systems, 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 org.opengroup.osdu.storage.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class HealthCheckApiIntegrationTest extends TestBase { + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + @Test + public void should_returnOk() throws Exception { + CloseableHttpResponse response = + TestUtils.send( + "liveness_check", + "GET", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), null), + "", + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/headervalidations/ValidateRequiredHeaders.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/headervalidations/ValidateRequiredHeaders.java new file mode 100644 index 0000000000000000000000000000000000000000..b024973980e04247eabd58bffd7727d368285ad8 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/headervalidations/ValidateRequiredHeaders.java @@ -0,0 +1,116 @@ +/* + * Copyright 2020-2024 Google LLC + * Copyright 2020-2024 EPAM Systems, 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 org.opengroup.osdu.storage.headervalidations; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.ws.rs.HttpMethod; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.core.common.model.http.AppError; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class ValidateRequiredHeaders extends TestBase { + private static final String RECORDS = "records"; + private static final String KIND_ONE = TenantUtils.getTenantName() + ":test:endtoend:1.1." + + System.currentTimeMillis(); + private static final String KIND_ID_ONE = TenantUtils.getTenantName() + ":endtoend:1.1." + + System.currentTimeMillis(); + private static final String KIND_VERSION_ID = TenantUtils.getTenantName() + ":endtoend:1.2." + + System.currentTimeMillis(); + private static final String LEGAL_TAG_NAME = LegalTagUtils.createRandomName(); + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + ValidateRequiredHeaders.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + ValidateRequiredHeaders.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG_NAME, token); + } + + public static void classTearDown(String token) throws Exception { + TestUtils.send( + RECORDS + "/" + KIND_ID_ONE, HttpMethod.DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send(RECORDS + "/" + KIND_VERSION_ID, HttpMethod.DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + + LegalTagUtils.delete(LEGAL_TAG_NAME, token); + } + + private CloseableHttpResponse createTestRecordWithoutAuth(String kind, String id, String legalName) throws Exception { + String jsonInputRecord = RecordUtil.createDefaultJsonRecord(id, kind, legalName); + return TestUtils.send( + RECORDS, HttpMethod.PUT, HeaderUtils.getHeadersWithoutAuth(TenantUtils.getTenantName(), testUtils.getToken()), jsonInputRecord, ""); + } + + private CloseableHttpResponse createTestRecordWithoutDataPartitionID(String kind, String id, String legalName) throws Exception { + String jsonInputRecord = RecordUtil.createDefaultJsonRecord(id, kind, legalName); + return TestUtils.send( + RECORDS, HttpMethod.PUT, HeaderUtils.getHeadersWithoutDataPartitionId(TenantUtils.getTenantName(), testUtils.getToken()), jsonInputRecord, ""); + } + + @Test + public void ValidateMissingAuthHeaderReturnsUnauthorizedError() throws Exception { + CloseableHttpResponse recordResponse = createTestRecordWithoutAuth(KIND_ONE, KIND_ID_ONE, LEGAL_TAG_NAME); + //validate that the error code is either 401/403 since for some its 403 I guess at some + //other level like istio etc. + assertTrue(recordResponse.getCode() == HttpStatus.SC_UNAUTHORIZED || recordResponse.getCode() == HttpStatus.SC_FORBIDDEN); + } + + @Test + public void ValidateMissingDataPartitionHeaderReturnsBadRequestError() throws Exception { + CloseableHttpResponse recordResponse = createTestRecordWithoutDataPartitionID(KIND_ONE, KIND_ID_ONE, LEGAL_TAG_NAME); + AppError recordResult = TestUtils.getResult(recordResponse, HttpStatus.SC_BAD_REQUEST, + AppError.class); + + AppError expectedError = new AppError(HttpStatus.SC_BAD_REQUEST, "Bad Request", "data-partition-id header is missing"); + + assertEquals(recordResult, expectedError); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/legal/PopulateLegalInfoFromParentRecordsTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/legal/PopulateLegalInfoFromParentRecordsTests.java new file mode 100644 index 0000000000000000000000000000000000000000..7175788531b49bfd50293fa5a27c6a7d5a750672 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/legal/PopulateLegalInfoFromParentRecordsTests.java @@ -0,0 +1,263 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.legal; + +import static org.apache.http.HttpStatus.SC_BAD_REQUEST; +import static org.apache.http.HttpStatus.SC_CREATED; +import static org.apache.http.HttpStatus.SC_OK; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opengroup.osdu.storage.util.LegalTagUtils.createRandomName; + +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import io.jsonwebtoken.lang.Collections; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.DummyRecordsHelper.CreateRecordResponse; +import org.opengroup.osdu.storage.util.DummyRecordsHelper.RecordResultMock; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class PopulateLegalInfoFromParentRecordsTests extends TestBase { + + private static final String KIND = TenantUtils.getTenantName() + ":parent:inttest:1.0." + + System.currentTimeMillis(); + private static String LEGAL_TAG_PARENT_ONE; + private static String LEGAL_TAG_PARENT_TWO; + private static String LEGAL_TAG_CHILD; + private static String LEGAL_TAG_CHILD_THAT_WILL_NOT_BE_CREATED; + private static String PARENT_ID_ONE; + private static String PARENT_ID_TWO; + private static String CHILD_ID; + private static String CHILD_ID_THAT_IS_NOT_CREATED; + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + PopulateLegalInfoFromParentRecordsTests.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + PopulateLegalInfoFromParentRecordsTests.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LEGAL_TAG_PARENT_ONE = createRandomName() + "parent"; + Thread.sleep(1); + LEGAL_TAG_PARENT_TWO = createRandomName() + "parent"; + LEGAL_TAG_CHILD = createRandomName() + "child"; + Thread.sleep(1); + LEGAL_TAG_CHILD_THAT_WILL_NOT_BE_CREATED = createRandomName() + "child"; + PARENT_ID_ONE = TenantUtils.getTenantName() + ":inttest:" + System.currentTimeMillis(); + Thread.sleep(1); + PARENT_ID_TWO = TenantUtils.getTenantName() + ":inttest:" + System.currentTimeMillis(); + CHILD_ID = TenantUtils.getTenantName() + ":inttest:" + System.currentTimeMillis(); + Thread.sleep(1); + CHILD_ID_THAT_IS_NOT_CREATED = TenantUtils.getTenantName() + ":inttest:" + System.currentTimeMillis(); + LegalTagUtils.create(LEGAL_TAG_PARENT_ONE, token); + LegalTagUtils.create(LEGAL_TAG_PARENT_TWO, token); + LegalTagUtils.create(LEGAL_TAG_CHILD, token); + + createAndAssertRecord(PARENT_ID_ONE, LEGAL_TAG_PARENT_ONE, "parent1", Lists.newArrayList("BR", "IT"), null, token); + createAndAssertRecord(PARENT_ID_TWO, LEGAL_TAG_PARENT_TWO, "parent2", Lists.newArrayList("DE", "DK"), null, token); + } + + public static void classTearDown(String token) throws Exception { + purgeRecord(PARENT_ID_ONE, token); + purgeRecord(PARENT_ID_TWO, token); + purgeRecord(CHILD_ID, token); + + LegalTagUtils.delete(LEGAL_TAG_PARENT_ONE, token); + LegalTagUtils.delete(LEGAL_TAG_PARENT_TWO, token); + LegalTagUtils.delete(LEGAL_TAG_CHILD, token); + } + + @Test + public void should_appendOrdcAndLegalTagsWithParents_when_creatingRecordWithParentsSupplied() throws Exception { + RecordResultMock parentRecord1 = this.retrieveRecord(PARENT_ID_ONE); + RecordResultMock parentRecord2 = this.retrieveRecord(PARENT_ID_TWO); + + createAndAssertRecord(CHILD_ID, LEGAL_TAG_CHILD, "chiiiiiild", Lists.newArrayList("FR", "US", "CA"), + Lists.newArrayList(PARENT_ID_ONE + ":" + parentRecord1.version, + PARENT_ID_TWO + ":" + parentRecord2.version), testUtils.getToken()); + RecordResultMock record = this.retrieveRecord(CHILD_ID); + + assertEquals(CHILD_ID, record.id); + assertEquals(1, record.data.size()); + assertEquals("chiiiiiild", record.data.get("name")); + assertNotNull(record.version); + assertEquals(KIND, record.kind); + assertArrayEquals(new String[] { TestUtils.getAcl() }, record.acl.viewers); + assertArrayEquals(new String[] { TestUtils.getAcl() }, record.acl.owners); + assertEquals(3, record.legal.legaltags.length); + assertTrue(ArrayUtils.contains(record.legal.legaltags, LEGAL_TAG_CHILD)); + assertTrue(ArrayUtils.contains(record.legal.legaltags, LEGAL_TAG_PARENT_ONE)); + assertTrue(ArrayUtils.contains(record.legal.legaltags, LEGAL_TAG_PARENT_TWO)); + assertTrue(ArrayUtils.contains(record.legal.otherRelevantDataCountries, "BR")); + assertTrue(ArrayUtils.contains(record.legal.otherRelevantDataCountries, "IT")); + assertTrue(ArrayUtils.contains(record.legal.otherRelevantDataCountries, "FR")); + assertTrue(ArrayUtils.contains(record.legal.otherRelevantDataCountries, "US")); + assertTrue(ArrayUtils.contains(record.legal.otherRelevantDataCountries, "CA")); + assertTrue(ArrayUtils.contains(record.legal.otherRelevantDataCountries, "DE")); + assertTrue(ArrayUtils.contains(record.legal.otherRelevantDataCountries, "DK")); + assertEquals(2, record.ancestry.parents.length); + assertTrue(ArrayUtils.contains(record.ancestry.parents, PARENT_ID_ONE + ":" + parentRecord1.version)); + assertTrue(ArrayUtils.contains(record.ancestry.parents, PARENT_ID_TWO + ":" + parentRecord2.version)); + } + + @Test + public void should_returnErrorCode400_when_anInvalidChildLegalTagProvided() throws Exception { + String childBody = createBody(CHILD_ID_THAT_IS_NOT_CREATED, "childname", + Lists.newArrayList(LEGAL_TAG_CHILD_THAT_WILL_NOT_BE_CREATED), Lists.newArrayList("FR", "US", "CA"), + null); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), childBody, ""); + assertEquals(SC_BAD_REQUEST, response.getCode()); + } + + @Test + public void should_return400_when_noParentRecordAndNoChildLegalTagsProvided() throws Exception { + String body = createBody(CHILD_ID_THAT_IS_NOT_CREATED, "childname", null, Lists.newArrayList("FR", "US"), null); + CloseableHttpResponse response = TestUtils.send("records", "PUT", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body, ""); + assertEquals(SC_BAD_REQUEST, response.getCode()); + } + + @Test + public void should_returnErrorCode400_when_noParentRecordAndNoORDCValuesProvided() throws Exception { + String body = createBody(CHILD_ID_THAT_IS_NOT_CREATED, "childname", Lists.newArrayList(LEGAL_TAG_PARENT_ONE), + null, null); + CloseableHttpResponse response = TestUtils.send("records", "PUT", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body, ""); + assertEquals(SC_BAD_REQUEST, response.getCode()); + } + + protected RecordResultMock retrieveRecord(String recordId) throws Exception { + System.out.println("Retrieving record=" + recordId); + CloseableHttpResponse response = TestUtils.send("records/" + recordId, "GET", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + String responseBody = EntityUtils.toString(response.getEntity()); + System.out.println(" responseBody=" + responseBody); + assertEquals(SC_OK, response.getCode()); + + return GSON.fromJson(responseBody, RecordResultMock.class); + } + + protected static String createBody(String id, String dataValue, List<String> legalTags, List<String> ordc, + List<String> parents) { + JsonObject data = new JsonObject(); + data.addProperty("name", dataValue); + + JsonObject acl = new JsonObject(); + JsonArray acls = new JsonArray(); + acls.add(TestUtils.getAcl()); + acl.add("viewers", acls); + acl.add("owners", acls); + + JsonArray tags = new JsonArray(); + if (legalTags != null) { + legalTags.forEach(t -> tags.add(t)); + } + + JsonArray ordcJson = new JsonArray(); + if (ordc != null) { + ordc.forEach(o -> ordcJson.add(o)); + } + + JsonObject legal = new JsonObject(); + if (legalTags != null) { + legal.add("legaltags", tags); + } + legal.add("otherRelevantDataCountries", ordcJson); + + JsonObject record = new JsonObject(); + record.addProperty("id", id); + record.addProperty("kind", KIND); + record.add("acl", acl); + record.add("legal", legal); + record.add("data", data); + + if (!Collections.isEmpty(parents)) { + JsonArray parentsJson = new JsonArray(); + parents.forEach(p -> parentsJson.add(p)); + + JsonObject ancestry = new JsonObject(); + ancestry.add("parents", parentsJson); + + record.add("ancestry", ancestry); + } + + JsonArray records = new JsonArray(); + records.add(record); + + return records.toString(); + } + + protected static void createAndAssertRecord(String parentId, String legalTagForParent, String dataValue, + ArrayList<String> ordc, List<String> parents, String token) throws Exception { + String parentBody = createBody(parentId, dataValue, Lists.newArrayList(legalTagForParent), ordc, parents); + System.out.println("createAndAssertRecord"); + System.out.println("parentBody=" + parentId + " " + parentBody); + CloseableHttpResponse response = TestUtils.send("records", "PUT", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), parentBody, ""); + + String responseBody = EntityUtils.toString(response.getEntity()); + System.out.println("responseBody=" + parentId + " " + responseBody); + assertEquals(SC_CREATED, response.getCode()); + assertTrue(response.getEntity().getContentType().contains("application/json")); + + CreateRecordResponse result = GSON.fromJson(responseBody, CreateRecordResponse.class); + + assertEquals(1, result.recordCount); + assertEquals(1, result.recordIds.length); + assertEquals(1, result.recordIdVersions.length); + assertEquals(parentId, result.recordIds[0]); + } + + protected static void purgeRecord(String recordId, String token) throws Exception { + TestUtils.send("records/" + recordId, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/misc/StorageCorsTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/misc/StorageCorsTests.java new file mode 100644 index 0000000000000000000000000000000000000000..788c46fe03831adf0e620b2a3a6f63a9671347cc --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/misc/StorageCorsTests.java @@ -0,0 +1,70 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.misc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class StorageCorsTests extends TestBase { + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + @Test + @Disabled + public void should_returnProperStatusCodeAndResponseHeaders_when_sendingPreflightOptionsRequest() throws Exception { + CloseableHttpResponse response = TestUtils.send("query/kinds", "OPTIONS", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", "?limit=1"); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + assertEquals("*", response.getHeaders("Access-Control-Allow-Origin")[0]); + assertEquals( + "origin, content-type, accept, authorization, data-partition-id, correlation-id, appkey", + response.getHeaders("Access-Control-Allow-Headers")[0]); + assertEquals("GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH", + response.getHeaders("Access-Control-Allow-Methods")[0]); + assertEquals("true", response.getHeaders("Access-Control-Allow-Credentials")[0]); + assertEquals("DENY", response.getHeaders("X-Frame-Options")[0]); + assertEquals("1; mode=block", response.getHeaders("X-XSS-Protection")[0]); + assertEquals("nosniff", response.getHeaders("X-Content-Type-Options")[0]); + assertEquals("no-cache, no-store, must-revalidate", response.getHeaders("Cache-Control")[0]); + assertEquals("default-src 'self'", response.getHeaders("Content-Security-Policy")[0]); + assertTrue(response.getHeaders("Strict-Transport-Security")[0].getValue().contains("max-age=31536000")); + assertTrue(response.getHeaders("Strict-Transport-Security")[0].getValue().contains("includeSubDomains")); + assertEquals("0", response.getHeaders("Expires")[0]); + assertNotNull(response.getHeaders("correlation-id")[0]); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/misc/StressTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/misc/StressTests.java new file mode 100644 index 0000000000000000000000000000000000000000..bd40cde37b0d04f759ff2cacf6751ff859e844fc --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/misc/StressTests.java @@ -0,0 +1,154 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.misc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.gson.Gson; +import java.util.ArrayList; +import java.util.List; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.records.RecordsApiAcceptanceTests; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class StressTests extends TestBase { + + private static final String RECORD_ID = TenantUtils.getTenantName() + + ":WG-Multi-Client:flatten-full-seismic-2d-shape_survey_2d_0623_Survey2D_Angola_Lower_Congo_2D_Repro_AWG98_26_1"; + + private static String LEGAL_TAG_NAME = LegalTagUtils.createRandomName(); + + private static final String KIND = TenantUtils.getTenantName() + ":ds:inttest:1.0." + + System.currentTimeMillis(); + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + StressTests.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + StressTests.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG_NAME, token); + } + + public static void classTearDown(String token) throws Exception { + TestUtils.send("records/", "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", RECORD_ID); + LegalTagUtils.delete(LEGAL_TAG_NAME, token); + } + + @Test + public void should_create100Records_when_givenValidRecord() throws Exception { + this.performanceTestCreateAndUpdateRecord(100); + } + + @Test + public void should_create10Records_when_givenValidRecord() throws Exception { + this.performanceTestCreateAndUpdateRecord(10); + } + + @Test + public void should_create1Records_when_givenValidRecord() throws Exception { + this.performanceTestCreateAndUpdateRecord(1); + } + + protected void performanceTestCreateAndUpdateRecord(int capacity) throws Exception { + String json = ""; + List<String> ids = new ArrayList<>(capacity); + for (int i = 0; i < capacity; i++) { + String id1 = TenantUtils.getTenantName() + ":inttest:" + System.currentTimeMillis() + i; + json += RecordsApiAcceptanceTests.singleEntityBody(id1, "ash ketchum", KIND, LEGAL_TAG_NAME); + if (i != capacity - 1) { + json += ","; + } + ids.add(id1); + } + + json = "[" + json + "]"; + + long startMillis = System.currentTimeMillis(); + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), json, ""); + long totalMillis = System.currentTimeMillis() - startMillis; + System.out.println(String.format("Took %s milliseconds to Create %s 1KB records", totalMillis, ids.size())); + + String responseJson = EntityUtils.toString(response.getEntity()); + System.out.println(responseJson); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + assertTrue(response.getEntity().getContentType().toString().contains("application/json")); + Gson gson = new Gson(); + DummyRecordsHelper.CreateRecordResponse result = gson.fromJson(responseJson, + DummyRecordsHelper.CreateRecordResponse.class); + assertEquals(capacity, result.recordCount); + assertEquals(capacity, result.recordIds.length); + assertEquals(capacity, result.recordIdVersions.length); + + startMillis = System.currentTimeMillis(); + response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), json, "?skipdupes=false"); + totalMillis = System.currentTimeMillis() - startMillis; + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + System.out.println(String.format("Took %s milliseconds to Update %s 1KB records", totalMillis, ids.size())); + + startMillis = System.currentTimeMillis(); + response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), json, "?skipdupes=false"); + totalMillis = System.currentTimeMillis() - startMillis; + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + System.out.println(String.format("Took %s milliseconds to Update %s 1KB records when when skipdupes is true", + totalMillis, ids.size())); + + startMillis = System.currentTimeMillis(); + response = TestUtils.send("records/" + ids.get(0), "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + totalMillis = System.currentTimeMillis() - startMillis; + assertEquals(HttpStatus.SC_OK, response.getCode()); + System.out.println(String.format("Took %s milliseconds to GET 1 1KB record", totalMillis)); + + ids.parallelStream().forEach((id) -> { + try { + TestUtils.send("records/" + id, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + } catch (Exception e) { + } + }); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/misc/SwaggerIntegrationTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/misc/SwaggerIntegrationTests.java new file mode 100644 index 0000000000000000000000000000000000000000..6deb49caa9e3ec208a771651617c7a59b1395427 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/misc/SwaggerIntegrationTests.java @@ -0,0 +1,58 @@ +/* + * Copyright 2020-2024 Google LLC + * Copyright 2020-2024 EPAM Systems, 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 org.opengroup.osdu.storage.misc; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashMap; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; + +public final class SwaggerIntegrationTests extends TestBase { + + public static final String SWAGGER_API_PATH = "swagger-ui/index.html"; + public static final String SWAGGER_API_DOCS_PATH = "api-docs"; + + @Override + public void setup() throws Exception { + // the test suite does not require pre-run configuration + } + + @Override + public void tearDown() throws Exception { + // the test suite does not require post-run tear-down procedures + } + + @Test + public void shouldReturn200_whenSwaggerApiIsCalled() throws Exception { + CloseableHttpResponse response = TestUtils + .send(SWAGGER_API_PATH, "GET", new HashMap<>(), "", ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + } + + @Test + public void shouldReturn200_whenSwaggerApiDocsIsCalled() throws Exception { + CloseableHttpResponse response = TestUtils + .send(SWAGGER_API_DOCS_PATH, "GET", new HashMap<>(), "", ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + } + +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/CreatedRecordInStorage.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/CreatedRecordInStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..64539824356e5b830a545f27fd20cdf727ef02d8 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/CreatedRecordInStorage.java @@ -0,0 +1,58 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.model; + +import java.util.Arrays; +import java.util.Map; +import lombok.Data; + +@Data +public class CreatedRecordInStorage { + public int recordCount; + public String[] recordIds; + public String[] skippedRecordIds; + + @Override + public String toString() { + return "CreatedRecordInStorage{" + + "recordCount=" + recordCount + + ", recordIds=" + Arrays.toString(recordIds) + + ", skippedRecordIds=" + Arrays.toString(skippedRecordIds) + + '}'; + } + public class RecordResult { + public String id; + public String version; + public String kind; + public RecordAcl acl; + public Map<String, Object> data; + public RecordLegal legal; + public RecordAncestry ancestry; + } + + public class RecordAcl { + public String[] viewers; + public String[] owners; + } + + public class RecordLegal { + public String[] legaltags; + public String[] otherRelevantDataCountries; + } + + public class RecordAncestry { + public String[] parents; + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/FetchRecordResponse.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/FetchRecordResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..7dea558f1f636c2fef0e3616df2dea0609e7d52e --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/FetchRecordResponse.java @@ -0,0 +1,24 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.model; + +import lombok.Data; + +@Data +public class FetchRecordResponse { + public String[] records; + public String[] invalidRecords; + public String[] retryRecords; +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ForbiddenNotFoundResponse.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ForbiddenNotFoundResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..457e19b2f5e43f1bacd574c0b6d2eef96dfa3995 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ForbiddenNotFoundResponse.java @@ -0,0 +1,24 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.model; + +import lombok.Data; + +@Data +public class ForbiddenNotFoundResponse { + public int code; + public String reason; + public String message; +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/GetCursorValue.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/GetCursorValue.java new file mode 100644 index 0000000000000000000000000000000000000000..c9ec5040f346f14a1cfef00d7b4da504ab5a6535 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/GetCursorValue.java @@ -0,0 +1,23 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.model; + +import lombok.Data; + +@Data +public class GetCursorValue { + public String cursor; + public String[] results; +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ReplayFilter.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ReplayFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..a202289fe98e8ac5aa5f11f7a1f6e7e7fd037ce2 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ReplayFilter.java @@ -0,0 +1,31 @@ +// Copyright © Microsoft Corporation +// +// 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 org.opengroup.osdu.storage.model; + + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ReplayFilter { + + private List<String> kinds; +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ReplayStatus.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ReplayStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..3331d8d6041e38a0c10270d28c3f9c5ac5b833a0 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ReplayStatus.java @@ -0,0 +1,40 @@ +// Copyright © Microsoft Corporation +// +// 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 org.opengroup.osdu.storage.model; + +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ReplayStatus { + + private String kind; + + private Long totalRecords; + + private Long processedRecords; + + private String state; + + private Date startedAt; + + private String elapsedTime; +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ReplayStatusResponseHelper.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ReplayStatusResponseHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..615c847776ca1bb2f0c23e301bfa23fb823f1397 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/model/ReplayStatusResponseHelper.java @@ -0,0 +1,47 @@ +// Copyright © Microsoft Corporation +// +// 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 org.opengroup.osdu.storage.model; + +import java.util.Date; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ReplayStatusResponseHelper { + + private String replayId; + + private String operation; + + private Long totalRecords; + + private Date startedAt; + + private String elapsedTime; + + private Long processedRecords; + + private String overallState; + + private ReplayFilter filter; + + private List<ReplayStatus> status; +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/pubsubendpoint/PubsubEndpointTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/pubsubendpoint/PubsubEndpointTest.java new file mode 100644 index 0000000000000000000000000000000000000000..68e22a87e39df0895e3e771f0ea1f928c45fdff7 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/pubsubendpoint/PubsubEndpointTest.java @@ -0,0 +1,162 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.pubsubendpoint; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class PubsubEndpointTest extends TestBase { + private static final long NOW = System.currentTimeMillis(); + private static final long FIVE_SECOND_LATER = NOW + 5000L; + private static final String LEGAL_TAG_1 = LegalTagUtils.createRandomName(); + private static final String LEGAL_TAG_2 = LEGAL_TAG_1 + "random2"; + + private static final String KIND = TenantUtils.getTenantName() + ":test:endtoend:1.1." + NOW; + private static final String RECORD_ID = TenantUtils.getTenantName() + ":endtoend:1.1." + NOW; + private static final String RECORD_ID_2 = TenantUtils.getTenantName() + ":endtoend:1.1." + + FIVE_SECOND_LATER; + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + PubsubEndpointTest.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + PubsubEndpointTest.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG_1, token); + String record1 = RecordUtil.createDefaultJsonRecord(RECORD_ID, KIND, LEGAL_TAG_1); + CloseableHttpResponse responseValid = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), record1, ""); + assertEquals(HttpStatus.SC_CREATED, responseValid.getCode()); + + LegalTagUtils.create(LEGAL_TAG_2, token); + String record2 = RecordUtil.createDefaultJsonRecord(RECORD_ID_2, KIND, LEGAL_TAG_2); + CloseableHttpResponse responseValid2 = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), record2, ""); + assertEquals(HttpStatus.SC_CREATED, responseValid2.getCode()); + } + + public static void classTearDown(String token) throws Exception { + TestUtils.send("records/" + RECORD_ID, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID_2, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + + LegalTagUtils.delete(LEGAL_TAG_1, token); + LegalTagUtils.delete(LEGAL_TAG_2, token); + } + + @Test + public void should_deleteIncompliantLegaltagAndInvalidateRecordsAndNotIngestAgain_whenIncompliantMessageSentToEndpoint() + throws Exception { + LegalTagUtils.delete(LEGAL_TAG_1, testUtils.getToken()); + // wait until cache of opa will be rebuild + Thread.sleep(100000); + + List<String> legalTagNames = new ArrayList<>(); + legalTagNames.add(LEGAL_TAG_1); + legalTagNames.add(LEGAL_TAG_2); + + CloseableHttpResponse responseRecordQuery = + TestUtils.send( + "records/" + RECORD_ID, + "GET", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + "", + ""); + assertEquals(HttpStatus.SC_NOT_FOUND, responseRecordQuery.getCode()); + + long now = System.currentTimeMillis(); + long later = now + 2000L; + String recordIdTemp1 = TenantUtils.getTenantName() + ":endtoend:1.1." + now; + String kindTemp = TenantUtils.getTenantName() + ":test:endtoend:1.1." + now; + String recordTemp1 = RecordUtil.createDefaultJsonRecord(recordIdTemp1, kindTemp, LEGAL_TAG_1); + String recordIdTemp2 = TenantUtils.getTenantName() + ":endtoend:1.1." + later; + String recordTemp2 = RecordUtil.createDefaultJsonRecord(recordIdTemp2, kindTemp, LEGAL_TAG_2); + + CloseableHttpResponse responseInvalid = + TestUtils.send( + "records", + "PUT", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + recordTemp1, + ""); + assertEquals(HttpStatus.SC_BAD_REQUEST, responseInvalid.getCode()); + assertEquals( + "Invalid legal tags", this.getResponseReasonFromRecordIngestResponse(responseInvalid)); + CloseableHttpResponse responseValid3 = + TestUtils.send( + "records", + "PUT", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + recordTemp2, + ""); + assertEquals(HttpStatus.SC_CREATED, responseValid3.getCode()); + TestUtils.send( + "records/" + recordIdTemp2, + "DELETE", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + "", + ""); + } + + protected String getResponseReasonFromRecordIngestResponse(CloseableHttpResponse response) { + JsonObject json = null; + try { + json = new JsonParser().parse(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + return json.get("reason").getAsString(); + } + +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/GetQueryInfoIntegrationTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/GetQueryInfoIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d0a2da46d8b099d96cd0ec6b6da4e044adb00d6a --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/GetQueryInfoIntegrationTest.java @@ -0,0 +1,82 @@ +/* + * Copyright 2020-2024 Google LLC + * Copyright 2020-2024 EPAM Systems, 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 org.opengroup.osdu.storage.query; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; +import org.opengroup.osdu.storage.util.VersionInfoUtils; + + +public final class GetQueryInfoIntegrationTest extends TestBase { + + private static final VersionInfoUtils VERSION_INFO_UTILS = new VersionInfoUtils(); + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + GetQueryRecordsIntegrationTest.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + GetQueryRecordsIntegrationTest.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + @Test + public void should_returnInfo() throws Exception { + CloseableHttpResponse response = TestUtils + .send("info", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), + testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + VersionInfoUtils.VersionInfo responseObject = VERSION_INFO_UTILS.getVersionInfoFromResponse(response); + + assertNotNull(responseObject.groupId); + assertNotNull(responseObject.artifactId); + assertNotNull(responseObject.version); + assertNotNull(responseObject.buildTime); + assertNotNull(responseObject.branch); + assertNotNull(responseObject.commitId); + assertNotNull(responseObject.commitMessage); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/GetQueryKindsIntegrationTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/GetQueryKindsIntegrationTests.java new file mode 100644 index 0000000000000000000000000000000000000000..a2cd320c73f5babb3ae47e0ecd053c489461641c --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/GetQueryKindsIntegrationTests.java @@ -0,0 +1,86 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.query; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + + +public final class GetQueryKindsIntegrationTests extends TestBase { + + private static final DummyRecordsHelper RECORD_HELPER = new DummyRecordsHelper(); + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + @Test + public void should_returnMax1000Results_when_settingLimitToAValueLessThan1() throws Exception { + if (configUtils != null && configUtils.getIsSchemaEndpointsEnabled()) { + CloseableHttpResponse response = TestUtils.send("query/kinds", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", "?limit=0"); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.QueryResultMock responseObject = RECORD_HELPER.getQueryResultMockFromResponse(response); + + assertTrue(responseObject.results.length > 1 && responseObject.results.length <= 1000); + } + } + + @Test + public void should_return400ErrorResult_when_givingAnInvalidCursorParameter() throws Exception { + if (configUtils != null && configUtils.getIsSchemaEndpointsEnabled()) { + CloseableHttpResponse response = TestUtils.send("query/kinds", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", + "?cursor=badCursorString"); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + + assertEquals( + "{\"code\":400,\"reason\":\"Cursor invalid\",\"message\":\"The requested cursor does not exist or is invalid\"}", + EntityUtils.toString(response.getEntity())); + } + } + + @Test + public void should_return2Results_when_requesting2Items() throws Exception { + if (configUtils != null && configUtils.getIsSchemaEndpointsEnabled()) { + CloseableHttpResponse response = TestUtils.send("query/kinds", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", "?limit=2"); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.QueryResultMock responseObject = RECORD_HELPER.getQueryResultMockFromResponse(response); + + assertEquals(2, responseObject.results.length); + } + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/GetQueryRecordsIntegrationTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/GetQueryRecordsIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..76e54a2dce0cbde1115386e93c9b010833685899 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/GetQueryRecordsIntegrationTest.java @@ -0,0 +1,202 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.query; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.fail; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class GetQueryRecordsIntegrationTest extends TestBase { + + private static final long NOW = System.currentTimeMillis(); + + private static final String RECORD_ID = TenantUtils.getTenantName() + ":query:" + NOW; + private static final String KIND = TenantUtils.getTenantName() + ":ds:query:1.0." + NOW; + private static final String LEGAL_TAG = LegalTagUtils.createRandomName(); + private static final DummyRecordsHelper RECORDS_HELPER = new DummyRecordsHelper(); + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + GetQueryRecordsIntegrationTest.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + GetQueryRecordsIntegrationTest.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG, token); + String jsonInput = RecordUtil.createDefaultJsonRecords(5, RECORD_ID, KIND, LEGAL_TAG); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + } + + public static void classTearDown(String token) throws Exception { + TestUtils.send("records/" + RECORD_ID + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID + 1, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID + 2, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID + 3, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID + 4, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + + LegalTagUtils.delete(LEGAL_TAG, token); + } + + @Test + public void should_return5Ids_when_requestingKindThatHas5Entries() throws Exception { + CloseableHttpResponse response = TestUtils.send("query/records", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", "?kind=" + KIND); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.QueryResultMock responseObject = RECORDS_HELPER.getQueryResultMockFromResponse(response); + assertEquals(5, responseObject.results.length); + } + + @Test + public void should_incrementThroughIds_when_requestingKindThatHasMoreThanOneEntry_and_limitIsSetTo2_and_usingPreviousCursorPos() + throws Exception { + + Set<String> result = new HashSet<>(); + + // first call + CloseableHttpResponse response = TestUtils.send("query/records", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", "?limit=2&kind=" + KIND); + if (response.getCode() != HttpStatus.SC_OK) { + fail(formResponseCheckingMessage(response)); + } + DummyRecordsHelper.QueryResultMock responseObject = RECORDS_HELPER.getQueryResultMockFromResponse(response); + assertEquals(2, responseObject.results.length); + assertFalse(StringUtils.isEmpty(responseObject.cursor)); + + result.add(responseObject.results[0]); + result.add(responseObject.results[1]); + + String cursor = responseObject.cursor; + + // second call + response = TestUtils.send("query/records", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", + "?limit=2&cursor=" + cursor + "&kind=" + KIND); + if (response.getCode() != HttpStatus.SC_OK) { + fail(formResponseCheckingMessage(response)); + } + responseObject = RECORDS_HELPER.getQueryResultMockFromResponse(response); + assertEquals(2, responseObject.results.length); + assertFalse(StringUtils.isEmpty(responseObject.cursor)); + + result.add(responseObject.results[0]); + result.add(responseObject.results[1]); + + cursor = responseObject.cursor; + + // third call + response = TestUtils.send("query/records", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", + "?limit=2&cursor=" + cursor + "&kind=" + KIND); + if (response.getCode() != HttpStatus.SC_OK) { + fail(formResponseCheckingMessage(response)); + } + responseObject = RECORDS_HELPER.getQueryResultMockFromResponse(response); + assertEquals(1, responseObject.results.length); + + result.add(responseObject.results[0]); + + assertEquals(5, result.size()); + } + + @Test + public void should_returnError400_when_usingKindThatHasBadFormat() throws Exception { + CloseableHttpResponse response = TestUtils.send("query/records", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", "?limit=1&kind=bad:kind"); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + } + + @Test + public void should_returnNoResults_when_usingKindThatDoesNotExist() throws Exception { + CloseableHttpResponse response = TestUtils.send("query/records", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", + "?limit=1&kind=nonexisting:kind:formatted:1.0.0"); + assertEquals(HttpStatus.SC_OK, response.getCode()); + DummyRecordsHelper.QueryResultMock responseObject = RECORDS_HELPER.getQueryResultMockFromResponse(response); + + assertEquals(0, responseObject.results.length); + } + + @Test + public void should_returnError400_when_usingInvalidCursorParameter() throws Exception { + CloseableHttpResponse response = TestUtils.send("query/records", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", + "?limit=1&cursor=MY_BAD_CURSOR&kind=" + RECORDS_HELPER.KIND); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + } + + @Test + public void should_returnError400_when_notProvidingKindParameter() throws Exception { + CloseableHttpResponse response = TestUtils.send("query/records", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", "?limit=1&kind="); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + + response = TestUtils.send("query/records", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", "?limit=1"); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + } + + protected String formResponseCheckingMessage(CloseableHttpResponse response) { + JsonObject json; + try { + json = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + StringBuilder output = new StringBuilder(); + output.append("API is not acting properly, responde code is: ") + .append(String.valueOf(response.getCode())) + .append(". And the reason is: ") + .append(json.get("reason").getAsString()) + .append(response.getHeaders("correlation-id")[0]); + return output.toString(); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/PostFetchRecordsIntegrationTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/PostFetchRecordsIntegrationTests.java new file mode 100644 index 0000000000000000000000000000000000000000..56c47c1b054f5b31d43a415bfa2d4522cd0125dd --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/PostFetchRecordsIntegrationTests.java @@ -0,0 +1,894 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.query; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + + +public final class PostFetchRecordsIntegrationTests extends TestBase { + private static final long NOW = System.currentTimeMillis(); + + private static final String RECORD_ID_PREFIX = TenantUtils.getFirstTenantName() + ":query:"; + private static final String KIND = TenantUtils.getTenantName() + ":ds:query:1.0." + NOW; + private static final String LEGAL_TAG = LegalTagUtils.createRandomName(); + private static final DummyRecordsHelper RECORDS_HELPER = new DummyRecordsHelper(); + + private static final String PERSISTABLE_REFERENCE = "%7B%22LB_CRS%22%3A%22%257B%2522WKT%2522%253A%2522PROJCS%255B%255C%2522British_National_Grid%255C%2522%252CGEOGCS%255B%255C%2522GCS_OSGB_1936%255C%2522%252CDATUM%255B%255C%2522D_OSGB_1936%255C%2522%252CSPHEROID%255B%255C%2522Airy_1830%255C%2522%252C6377563.396%252C299.3249646%255D%255D%252CPRIMEM%255B%255C%2522Greenwich%255C%2522%252C0.0%255D%252CUNIT%255B%255C%2522Degree%255C%2522%252C0.0174532925199433%255D%255D%252CPROJECTION%255B%255C%2522Transverse_Mercator%255C%2522%255D%252CPARAMETER%255B%255C%2522False_Easting%255C%2522%252C400000.0%255D%252CPARAMETER%255B%255C%2522False_Northing%255C%2522%252C-100000.0%255D%252CPARAMETER%255B%255C%2522Central_Meridian%255C%2522%252C-2.0%255D%252CPARAMETER%255B%255C%2522Scale_Factor%255C%2522%252C0.9996012717%255D%252CPARAMETER%255B%255C%2522Latitude_Of_Origin%255C%2522%252C49.0%255D%252CUNIT%255B%255C%2522Meter%255C%2522%252C1.0%255D%252CAUTHORITY%255B%255C%2522EPSG%255C%2522%252C27700%255D%255D%2522%252C%2522Type%2522%253A%2522LBCRS%2522%252C%2522EngineVersion%2522%253A%2522PE_10_3_1%2522%252C%2522AuthorityCode%2522%253A%257B%2522Authority%2522%253A%2522EPSG%2522%252C%2522Code%2522%253A%252227700%2522%257D%252C%2522Name%2522%253A%2522British_National_Grid%2522%257D%22%2C%22TRF%22%3A%22%257B%2522WKT%2522%253A%2522GEOGTRAN%255B%255C%2522OSGB_1936_To_WGS_1984_Petroleum%255C%2522%252CGEOGCS%255B%255C%2522GCS_OSGB_1936%255C%2522%252CDATUM%255B%255C%2522D_OSGB_1936%255C%2522%252CSPHEROID%255B%255C%2522Airy_1830%255C%2522%252C6377563.396%252C299.3249646%255D%255D%252CPRIMEM%255B%255C%2522Greenwich%255C%2522%252C0.0%255D%252CUNIT%255B%255C%2522Degree%255C%2522%252C0.0174532925199433%255D%255D%252CGEOGCS%255B%255C%2522GCS_WGS_1984%255C%2522%252CDATUM%255B%255C%2522D_WGS_1984%255C%2522%252CSPHEROID%255B%255C%2522WGS_1984%255C%2522%252C6378137.0%252C298.257223563%255D%255D%252CPRIMEM%255B%255C%2522Greenwich%255C%2522%252C0.0%255D%252CUNIT%255B%255C%2522Degree%255C%2522%252C0.0174532925199433%255D%255D%252CMETHOD%255B%255C%2522Position_Vector%255C%2522%255D%252CPARAMETER%255B%255C%2522X_Axis_Translation%255C%2522%252C446.448%255D%252CPARAMETER%255B%255C%2522Y_Axis_Translation%255C%2522%252C-125.157%255D%252CPARAMETER%255B%255C%2522Z_Axis_Translation%255C%2522%252C542.06%255D%252CPARAMETER%255B%255C%2522X_Axis_Rotation%255C%2522%252C0.15%255D%252CPARAMETER%255B%255C%2522Y_Axis_Rotation%255C%2522%252C0.247%255D%252CPARAMETER%255B%255C%2522Z_Axis_Rotation%255C%2522%252C0.842%255D%252CPARAMETER%255B%255C%2522Scale_Difference%255C%2522%252C-20.489%255D%252CAUTHORITY%255B%255C%2522EPSG%255C%2522%252C1314%255D%255D%2522%252C%2522Type%2522%253A%2522STRF%2522%252C%2522EngineVersion%2522%253A%2522PE_10_3_1%2522%252C%2522AuthorityCode%2522%253A%257B%2522Authority%2522%253A%2522EPSG%2522%252C%2522Code%2522%253A%25221314%2522%257D%252C%2522Name%2522%253A%2522OSGB_1936_To_WGS_1984_Petroleum%2522%257D%22%2C%22Type%22%3A%22EBCRS%22%2C%22EngineVersion%22%3A%22PE_10_3_1%22%2C%22Name%22%3A%22OSGB+1936+*+UKOOA-Pet+%2F+British+National+Grid+%5B27700%2C1314%5D%22%2C%22AuthorityCode%22%3A%7B%22Authority%22%3A%22MyCompany%22%2C%22Code%22%3A%2227700006%22%7D%7D"; + private static final String PERSISTABLE_REFERENCE_CRS = "{\"lateBoundCRS\":{\"wkt\":\"PROJCS[\\\"ED_1950_UTM_Zone_32N\\\",GEOGCS[\\\"GCS_European_1950\\\",DATUM[\\\"D_European_1950\\\",SPHEROID[\\\"International_1924\\\",6378388.0,297.0]],PRIMEM[\\\"Greenwich\\\",0.0],UNIT[\\\"Degree\\\",0.0174532925199433]],PROJECTION[\\\"Transverse_Mercator\\\"],PARAMETER[\\\"False_Easting\\\",500000.0],PARAMETER[\\\"False_Northing\\\",0.0],PARAMETER[\\\"Central_Meridian\\\",9.0],PARAMETER[\\\"Scale_Factor\\\",0.9996],PARAMETER[\\\"Latitude_Of_Origin\\\",0.0],UNIT[\\\"Meter\\\",1.0],AUTHORITY[\\\"EPSG\\\",23032]]\",\"ver\":\"PE_10_3_1\",\"name\":\"ED_1950_UTM_Zone_32N\",\"authCode\":{\"auth\":\"EPSG\",\"code\":\"23032\"},\"type\":\"LBC\"},\"singleCT\":{\"wkt\":\"GEOGTRAN[\\\"ED_1950_To_WGS_1984_23\\\",GEOGCS[\\\"GCS_European_1950\\\",DATUM[\\\"D_European_1950\\\",SPHEROID[\\\"International_1924\\\",6378388.0,297.0]],PRIMEM[\\\"Greenwich\\\",0.0],UNIT[\\\"Degree\\\",0.0174532925199433]],GEOGCS[\\\"GCS_WGS_1984\\\",DATUM[\\\"D_WGS_1984\\\",SPHEROID[\\\"WGS_1984\\\",6378137.0,298.257223563]],PRIMEM[\\\"Greenwich\\\",0.0],UNIT[\\\"Degree\\\",0.0174532925199433]],METHOD[\\\"Position_Vector\\\"],PARAMETER[\\\"X_Axis_Translation\\\",-116.641],PARAMETER[\\\"Y_Axis_Translation\\\",-56.931],PARAMETER[\\\"Z_Axis_Translation\\\",-110.559],PARAMETER[\\\"X_Axis_Rotation\\\",0.893],PARAMETER[\\\"Y_Axis_Rotation\\\",0.921],PARAMETER[\\\"Z_Axis_Rotation\\\",-0.917],PARAMETER[\\\"Scale_Difference\\\",-3.52],AUTHORITY[\\\"EPSG\\\",1612]]\",\"ver\":\"PE_10_3_1\",\"name\":\"ED_1950_To_WGS_1984_23\",\"authCode\":{\"auth\":\"EPSG\",\"code\":\"1612\"},\"type\":\"ST\"},\"ver\":\"PE_10_3_1\",\"name\":\"ED50 * EPSG-Nor N62 2001 / UTM zone 32N [23032,1612]\",\"authCode\":{\"auth\":\"SLB\",\"code\":\"23032023\"},\"type\":\"EBC\"}"; + private static final String PERSISTABLE_REFERENCE_UNIT_Z = "{\"baseMeasurement\":{\"ancestry\":\"Length\",\"type\":\"UM\"},\"scaleOffset\":{\"offset\":0.0,\"scale\":0.3048},\"symbol\":\"ft\",\"type\":\"USO\"}"; + private static final String DATETIME_PERSISTABLE_REFERENCE = "{\"type\":\"DAT\",\"format\":\"YYYY-MM-DD\"}"; + private static final String UNIT_PERSISTABLE_REFERENCE = "{\"abcd\":{\"a\":0.0,\"b\":0.3048,\"c\":1.0,\"d\":0.0},\"symbol\":\"ft\",\"baseMeasurement\":{\"ancestry\":\"L\",\"type\":\"UM\"},\"type\":\"UAD\"}"; + private static final String UNIT_OF_MEASURE_ID = String.format("%s:reference-data--UnitOfMeasure:ft:", TenantUtils.getTenantName()); + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + PostFetchRecordsIntegrationTests.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + PostFetchRecordsIntegrationTests.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG, token); + } + + public static void classTearDown(String token) throws Exception { + LegalTagUtils.delete(LEGAL_TAG, token); + } + + @Test + public void should_returnSingleRecordMatching_when_noConversionRequired() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithReference(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE, "CRS"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "none"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(0, responseObject.conversionStatuses.size()); + + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(recordId + 0, responseObject.records[0].id); + assertEquals(3, responseObject.records[0].data.size()); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse.getCode()); + } + + @Test + public void should_returnRecordMatchingAndRecordNotFound_when_noConversionRequired() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithReference(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE, "CRS"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + records.add("nonexisting:id"); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "none"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + + assertEquals(1, responseObject.records.length); + assertEquals(1, responseObject.notFound.length); + assertEquals(0, responseObject.conversionStatuses.size()); + + assertEquals("nonexisting:id", responseObject.notFound[0]); + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(recordId + 0, responseObject.records[0].id); + assertEquals(3, responseObject.records[0].data.size()); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse.getCode()); + } + + @Test + public void should_return400BadRequest_when_moreThan20RecordsRequiredAndNoConversionRequired() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + + JsonArray records = new JsonArray(); + for (int i = 0; i < 21; i++) { + records.add(recordId + String.valueOf(i)); + } + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "none"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(), + ""); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + } + + @Test + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + public void should_returnConvertedRecords_whenConversionRequiredAndNoError() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithReference(2, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE, "CRS"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + records.add(recordId + 1); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(2, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(2, responseObject.conversionStatuses.size()); + + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(3, responseObject.records[0].data.size()); + + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + CloseableHttpResponse deleteResponse2 = TestUtils.send("records/" + recordId + 1, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse2.getCode()); + + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnConvertedRecords_whenConversionRequiredAndNoErrorWithMultiplePairOfCoordinates() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithMultiplePairOfCoordinates(2, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE, "CRS"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + records.add(recordId + 1); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(2, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(5, responseObject.records[0].data.size()); + + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + CloseableHttpResponse deleteResponse2 = TestUtils.send("records/" + recordId + 1, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse2.getCode()); + + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnOriginalRecordsAndConversionStatusAsNoMeta_whenConversionRequiredAndNoMetaBlockInRecord() throws Exception{ + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordNoMetaBlock(2, recordId, KIND, LEGAL_TAG); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + records.add(recordId + 1); + records.add("nonexisting:id"); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(2, responseObject.records.length); + assertEquals(1, responseObject.notFound.length); + assertEquals(2, responseObject.conversionStatuses.size()); + assertEquals("nonexisting:id", responseObject.notFound[0]); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(3, responseObject.records[0].data.size()); + List<DummyRecordsHelper.RecordStatusMock> conversionStatuses = responseObject.conversionStatuses; + assertEquals("CRS Conversion: Meta Block is missing or empty in this record, no conversion applied.", conversionStatuses.get(0).errors.get(0)); + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + CloseableHttpResponse deleteResponse2 = TestUtils.send("records/" + recordId + 1, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse2.getCode()); + } + + @Test + public void should_returnRecordsAndConversionStatus_whenConversionRequiredAndConversionErrorExists() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordMissingValue(2, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE, "CRS"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + records.add(recordId + 1); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(2, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(2, responseObject.conversionStatuses.size()); + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(2, responseObject.records[0].data.size()); + List<DummyRecordsHelper.RecordStatusMock> conversionStatuses = responseObject.conversionStatuses; + assertEquals("CRS conversion: Unknown coordinate pair 'z'.", conversionStatuses.get(0).errors.get(1)); + assertEquals("CRS conversion: property 'Y' is missing in datablock, no conversion applied to this property and its corresponding pairing property.", conversionStatuses.get(0).errors.get(0)); + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + CloseableHttpResponse deleteResponse2 = TestUtils.send("records/" + recordId + 1, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse2.getCode()); + } + + @Test + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + public void should_returnRecordsAndConversionStatus_whenConversionRequiredAndNestedPropertyProvidedInMetaBlock() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithNestedProperty(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE, "CRS"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse.getCode()); + } + //@Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAndConversionStatus_whenConversionRequiredAndNestedPropertyProvidedInMetaBlock1() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithNestedProperty(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE, "CRS"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse.getCode()); + } + + @Test + public void should_returnRecordsAndConversionStatus_whenDateAndFormatProvidedInMetaBlock() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordsWithDateFormat(1, recordId, KIND, LEGAL_TAG, "yyyy-MM-dd", "creationDate", "2019-08-03", DATETIME_PERSISTABLE_REFERENCE); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAndConversionStatus_whenNestedArrayOfPropertiesProvidedWithoutError() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + + String jsonInput = RecordUtil.createJsonRecordWithNestedArrayOfProperties(1, recordId, KIND, LEGAL_TAG, UNIT_PERSISTABLE_REFERENCE, "Unit", UNIT_OF_MEASURE_ID); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 12); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertNotNull(responseObject.records); + assertEquals(1, responseObject.records.length); + assertNotNull(responseObject.notFound); + assertEquals(0, responseObject.notFound.length); + assertNotNull(responseObject.conversionStatuses); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(2, responseObject.records[0].data.size()); + List<DummyRecordsHelper.RecordStatusMock> conversionStatuses = responseObject.conversionStatuses; + assertEquals("SUCCESS", conversionStatuses.get(0).status); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/" + recordId + 12, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAndConversionStatus_whenNestedArrayOfPropertiesProvidedWithInvalidValues() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithNestedArrayOfPropertiesAndInvalidValues(1, recordId, KIND, LEGAL_TAG, UNIT_PERSISTABLE_REFERENCE, "Unit", UNIT_OF_MEASURE_ID); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 12); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(2, responseObject.records[0].data.size()); + List<DummyRecordsHelper.RecordStatusMock> conversionStatuses = responseObject.conversionStatuses; + assertEquals("ERROR", conversionStatuses.get(0).status); + assertEquals("Unit conversion: illegal value for property markers[1].measuredDepth", conversionStatuses.get(0).errors.get(0)); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/" + recordId + 12, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAndConversionStatus_whenInhomogeneousNestedArrayOfPropertiesProvidedWithoutError() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithInhomogeneousNestedArrayOfProperties(1, recordId, KIND, LEGAL_TAG, UNIT_PERSISTABLE_REFERENCE, "Unit", UNIT_OF_MEASURE_ID); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 13); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(2, responseObject.records[0].data.size()); + List<DummyRecordsHelper.RecordStatusMock> conversionStatuses = responseObject.conversionStatuses; + assertEquals("SUCCESS", conversionStatuses.get(0).status); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/" + recordId + 13, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAndConversionStatus_whenInhomogeneousNestedArrayOfPropertiesProvidedWithInvalidValues() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithInhomogeneousNestedArrayOfPropertiesAndInvalidValues(1, recordId, KIND, LEGAL_TAG, UNIT_PERSISTABLE_REFERENCE, "Unit", UNIT_OF_MEASURE_ID); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 13); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(2, responseObject.records[0].data.size()); + List<DummyRecordsHelper.RecordStatusMock> conversionStatuses = responseObject.conversionStatuses; + assertEquals("ERROR", conversionStatuses.get(0).status); + assertEquals("Unit conversion: illegal value for property markers[1].measuredDepth", conversionStatuses.get(0).errors.get(0)); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/" + recordId + 13, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAndConversionStatus_whenInhomogeneousNestedArrayOfPropertiesProvidedWithIndexOutOfBoundary() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithInhomogeneousNestedArrayOfPropertiesAndIndexOutOfBoundary(1, recordId, KIND, LEGAL_TAG, UNIT_PERSISTABLE_REFERENCE, "Unit", UNIT_OF_MEASURE_ID); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 13); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(2, responseObject.records[0].data.size()); + List<DummyRecordsHelper.RecordStatusMock> conversionStatuses = responseObject.conversionStatuses; + assertEquals("SUCCESS", conversionStatuses.get(0).status); + assertEquals("Unit conversion: property markers[2].measuredDepth missing", conversionStatuses.get(0).errors.get(0)); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/" + recordId + 13, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAfterCrsConversion__whenProvidedRecordWithAsIngestedCoordinatesBlockTypePoint() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithAsIngestedCoordinates(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE_CRS, PERSISTABLE_REFERENCE_UNIT_Z, "AnyCrsPoint", "SpatialLocation"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals("SUCCESS", responseObject.conversionStatuses.get(0).status); + + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(1, responseObject.records[0].data.size()); + + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAfterCrsConversion__whenProvidedRecordWithAsIngestedCoordinatesBlockTypeMultiPoint() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithAsIngestedCoordinates(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE_CRS, PERSISTABLE_REFERENCE_UNIT_Z, "AnyCrsMultiPoint", "SpatialLocation"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals("SUCCESS", responseObject.conversionStatuses.get(0).status); + + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(1, responseObject.records[0].data.size()); + + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAfterCrsConversion__whenProvidedRecordWithAsIngestedCoordinatesBlockTypePolygon() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithAsIngestedCoordinates(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE_CRS, PERSISTABLE_REFERENCE_UNIT_Z, "AnyCrsPolygon", "SpatialLocation"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals("SUCCESS", responseObject.conversionStatuses.get(0).status); + + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(1, responseObject.records[0].data.size()); + + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAfterCrsConversion__whenProvidedRecordWithAsIngestedCoordinatesBlockTypeMultiPolygon() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithAsIngestedCoordinates(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE_CRS, PERSISTABLE_REFERENCE_UNIT_Z, "AnyCrsMultiPolygon", "SpatialLocation"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals("SUCCESS", responseObject.conversionStatuses.get(0).status); + + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(1, responseObject.records[0].data.size()); + + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAfterCrsConversion__whenProvidedRecordWithAsIngestedCoordinatesBlockTypeLineString() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithAsIngestedCoordinates(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE_CRS, PERSISTABLE_REFERENCE_UNIT_Z, "AnyCrsLineString", "SpatialLocation"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals("SUCCESS", responseObject.conversionStatuses.get(0).status); + + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(1, responseObject.records[0].data.size()); + + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAfterCrsConversion__whenProvidedRecordWithAsIngestedCoordinatesBlockTypeMultiLineString() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithAsIngestedCoordinates(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE_CRS, PERSISTABLE_REFERENCE_UNIT_Z, "AnyCrsMultiLineString", "SpatialLocation"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals("SUCCESS", responseObject.conversionStatuses.get(0).status); + + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(1, responseObject.records[0].data.size()); + + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnRecordsAfterCrsConversion__whenProvidedRecordWithAsIngestedCoordinatesBlockTypeGeometryCollection() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithAsIngestedCoordinates(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE_CRS, PERSISTABLE_REFERENCE_UNIT_Z, "AnyCrsGeometryCollection", "SpatialLocation"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals("SUCCESS", responseObject.conversionStatuses.get(0).status); + + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(1, responseObject.records[0].data.size()); + + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnConvertedRecords_whenConversionRequiredWithAsIngestedCoordinatesBlockWithError() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithInvalidAsIngestedCoordinates(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE_CRS, PERSISTABLE_REFERENCE_UNIT_Z, "AnyCrsPoint", "SpatialLocation"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals("CRS conversion: 'features' missing, no conversion applied.", responseObject.conversionStatuses.get(0).errors.get(0)); + + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(1, responseObject.records[0].data.size()); + + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + } + + // @Ignore // Ignoring the test for now, once we have CRS converter we should enable this test + @Test + public void should_returnConvertedRecords_whenConversionNotRequiredWithAsIngestedCoordinatesAndWgs84CoordinatesBlocks() throws Exception { + String recordId = RECORD_ID_PREFIX + UUID.randomUUID().toString(); + String jsonInput = RecordUtil.createJsonRecordWithWGS84Coordinates(1, recordId, KIND, LEGAL_TAG, PERSISTABLE_REFERENCE_CRS, PERSISTABLE_REFERENCE_UNIT_Z, "AnyCrsPoint", "SpatialLocation"); + CloseableHttpResponse createResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, createResponse.getCode()); + + JsonArray records = new JsonArray(); + records.add(recordId + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + headers.put("frame-of-reference", "units=SI;crs=wgs84;elevation=msl;azimuth=true north;dates=utc;"); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", headers, body.toString(),""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.notFound.length); + assertEquals(1, responseObject.conversionStatuses.size()); + assertEquals("CRS conversion: 'Wgs84Coordinates' block exists, no conversion applied.", responseObject.conversionStatuses.get(0).errors.get(0)); + + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(1, responseObject.records[0].data.size()); + + CloseableHttpResponse deleteResponse1 = TestUtils.send("records/" + recordId + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse1.getCode()); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/PostQueryRecordsIntegrationTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/PostQueryRecordsIntegrationTests.java new file mode 100644 index 0000000000000000000000000000000000000000..eabc47d653a096d6c7b301822cbc15319a33fda4 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/PostQueryRecordsIntegrationTests.java @@ -0,0 +1,191 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.query; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import java.util.Arrays; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + + +public final class PostQueryRecordsIntegrationTests extends TestBase { + + private static final long NOW = System.currentTimeMillis(); + + private static final String RECORD_ID = TenantUtils.getTenantName() + ":query:" + NOW; + private static final String KIND = TenantUtils.getTenantName() + ":ds:query:1.0." + NOW; + private static final String LEGAL_TAG = LegalTagUtils.createRandomName(); + private static final DummyRecordsHelper RECORDS_HELPER = new DummyRecordsHelper(); + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + PostQueryRecordsIntegrationTests.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + PostQueryRecordsIntegrationTests.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG, token); + String jsonInput = RecordUtil.createDefaultJsonRecords(3, RECORD_ID, KIND, LEGAL_TAG); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), jsonInput, ""); + CloseableHttpResponse modifyRecordsResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + assertEquals(HttpStatus.SC_CREATED, modifyRecordsResponse.getCode()); + } + + public static void classTearDown(String token) throws Exception { + TestUtils.send("records/" + RECORD_ID + 0, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID + 1, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID + 2, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + + LegalTagUtils.delete(LEGAL_TAG, token); + } + + @Test + public void should_returnSingleRecordMatching_when_givenIdAndNoAttributes() throws Exception { + JsonArray attributes = new JsonArray(); + JsonArray records = new JsonArray(); + records.add(RECORD_ID + 0); + + JsonObject body = new JsonObject(); + body.add("records", records); + body.add("attributes", attributes); + + CloseableHttpResponse response = TestUtils.send("query/records", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.RecordsMock responseObject = RECORDS_HELPER.getRecordsMockFromResponse(response); + + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.invalidRecords.length); + assertEquals(0, responseObject.retryRecords.length); + + assertEquals(TestUtils.getAcl(), responseObject.records[0].acl.viewers[0]); + assertEquals(RECORD_ID + 0, responseObject.records[0].id); + assertEquals(KIND, responseObject.records[0].kind); + assertTrue(responseObject.records[0].createUser != null && responseObject.records[0].createTime != null); + assertTrue(responseObject.records[0].modifyUser != null && responseObject.records[0].modifyTime != null); + assertTrue(responseObject.records[0].version != null && !responseObject.records[0].version.isEmpty()); + assertEquals(3, responseObject.records[0].data.size()); + } + + @Test + public void should_returnOnlyRequestedDataProperties_when_specificAttributesAreGiven() throws Exception { + JsonArray attributes = new JsonArray(); + attributes.add("data.count"); + JsonArray records = new JsonArray(); + records.add(RECORD_ID + 1); + + JsonObject body = new JsonObject(); + body.add("records", records); + body.add("attributes", attributes); + + CloseableHttpResponse response = TestUtils.send("query/records", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.RecordsMock responseObject = RECORDS_HELPER.getRecordsMockFromResponse(response); + + assertEquals(1, responseObject.records[0].data.size()); + assertEquals("1.23456789E8", responseObject.records[0].data.get("count").toString()); + } + + @Test + public void should_returnMultipleRecordsMatchingGivenIds_when_noAttributesAreGiven() throws Exception { + JsonArray attributes = new JsonArray(); + JsonArray records = new JsonArray(); + records.add(RECORD_ID + 0); + records.add(RECORD_ID + 1); + records.add(RECORD_ID + 2); + + JsonObject body = new JsonObject(); + body.add("records", records); + body.add("attributes", attributes); + + CloseableHttpResponse response = TestUtils.send("query/records", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.RecordsMock responseObject = RECORDS_HELPER.getRecordsMockFromResponse(response); + + assertEquals(3, responseObject.records.length); + assertEquals(0, responseObject.invalidRecords.length); + assertEquals(0, responseObject.retryRecords.length); + + String[] ids = ArrayUtils.addAll(new String[] { responseObject.records[0].id }, responseObject.records[1].id, + responseObject.records[2].id); + assertTrue(Arrays.asList(ids).contains(RECORD_ID + 0)); + assertTrue(Arrays.asList(ids).contains(RECORD_ID + 1)); + assertTrue(Arrays.asList(ids).contains(RECORD_ID + 2)); + } + + @Test + public void should_returnInvalidRecord_when_nonExistingIDGiven() throws Exception { + JsonArray attributes = new JsonArray(); + JsonArray records = new JsonArray(); + records.add("nonexisting:id"); + + JsonObject body = new JsonObject(); + body.add("records", records); + body.add("attributes", attributes); + + CloseableHttpResponse response = TestUtils.send("query/records", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body.toString(), + ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.RecordsMock responseObject = RECORDS_HELPER.getRecordsMockFromResponse(response); + + assertEquals(0, responseObject.records.length); + assertEquals(1, responseObject.invalidRecords.length); + assertEquals("nonexisting:id", responseObject.invalidRecords[0]); + assertEquals(0, responseObject.retryRecords.length); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/StorageQuerySuccessfulTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/StorageQuerySuccessfulTest.java new file mode 100644 index 0000000000000000000000000000000000000000..63725e18c23dd9141eeedcf332411e4735084c44 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/query/StorageQuerySuccessfulTest.java @@ -0,0 +1,150 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.query; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import jakarta.ws.rs.HttpMethod; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.model.CreatedRecordInStorage; +import org.opengroup.osdu.storage.model.GetCursorValue; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + + +public final class StorageQuerySuccessfulTest extends TestBase { + + private static final String RECORD = "records"; + private static final String KIND_ONE = TenantUtils.getTenantName() + ":test:endtoend:1.1." + + System.currentTimeMillis(); + private static final String KIND_ID_ONE = TenantUtils.getTenantName() + ":endtoend:1.1." + + System.currentTimeMillis(); + private static final String KIND_VERSION_ID = TenantUtils.getTenantName() + ":endtoend:1.2." + + System.currentTimeMillis(); + private static final String LEGAL_TAG_NAME = LegalTagUtils.createRandomName(); + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + StorageQuerySuccessfulTest.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + StorageQuerySuccessfulTest.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG_NAME, token); + } + + public static void classTearDown(String token) throws Exception { + TestUtils.send(RECORD + "/" + KIND_ID_ONE, HttpMethod.DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send(RECORD + "/" + KIND_VERSION_ID, HttpMethod.DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + + LegalTagUtils.delete(LEGAL_TAG_NAME, token); + } + + @Test + public void should_retrieveAllKinds_when_toCursorIdIsGiven() throws Exception { + if (configUtils != null && configUtils.getIsSchemaEndpointsEnabled()) { + CloseableHttpResponse recordResponse = TestUtils.send("query/kinds?limit=10", HttpMethod.GET, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + "", ""); + GetCursorValue getCursorValue = TestUtils.getResult(recordResponse, HttpStatus.SC_OK, GetCursorValue.class); + String cursorValue = getCursorValue.getCursor(); + assertEquals(HttpStatus.SC_OK, recordResponse.getCode()); + assertEquals(cursorValue, getCursorValue.getCursor()); + CloseableHttpResponse recordResponseWithCursorValue = TestUtils.send("query/kinds?cursor=" + cursorValue + "&limit=10", + HttpMethod.GET, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_OK, recordResponseWithCursorValue.getCode()); + } + } + + @Test + public void should_retrieveAllRecords_when_kindIsGiven() throws Exception { + CloseableHttpResponse recordResponse = createTestRecord(KIND_ONE, KIND_ID_ONE, LEGAL_TAG_NAME); + assertEquals(HttpStatus.SC_CREATED, recordResponse.getCode()); + CloseableHttpResponse recordResponseGet = TestUtils.send("query/records?kind=" + KIND_ONE, HttpMethod.GET, + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_OK, recordResponseGet.getCode()); + } + + @Test + public void should_queryToFetchMultipleRecords_when_recordIsGiven() throws Exception { + CloseableHttpResponse recordResponse = createTestRecord(KIND_ONE, KIND_ID_ONE, LEGAL_TAG_NAME); + CreatedRecordInStorage recordResult = TestUtils.getResult(recordResponse, HttpStatus.SC_CREATED, + CreatedRecordInStorage.class); + JsonArray recordIDS = new JsonArray(); + recordIDS.add(recordResult.recordIds[0]); + JsonArray attribute = new JsonArray(); + attribute.add(""); + JsonObject createSearchRecordPayload = new JsonObject(); + createSearchRecordPayload.add("records", recordIDS); + createSearchRecordPayload.add("attributes", attribute); + String path = "query/records"; + CloseableHttpResponse recordResponsePost = TestUtils.send(path, HttpMethod.POST, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + createSearchRecordPayload.toString(), ""); + assertEquals(HttpStatus.SC_OK, recordResponsePost.getCode()); + } + + @Test + public void should_queryToFetchMultipleRecords_when_recordIsGiven_and_trailingSlash() throws Exception { + CloseableHttpResponse recordResponse = createTestRecord(KIND_ONE, KIND_ID_ONE, LEGAL_TAG_NAME); + CreatedRecordInStorage recordResult = TestUtils.getResult(recordResponse, HttpStatus.SC_CREATED, + CreatedRecordInStorage.class); + JsonArray recordIDS = new JsonArray(); + recordIDS.add(recordResult.recordIds[0]); + JsonArray attribute = new JsonArray(); + attribute.add(""); + JsonObject createSearchRecordPayload = new JsonObject(); + createSearchRecordPayload.add("records", recordIDS); + createSearchRecordPayload.add("attributes", attribute); + String path = "query/records/"; + CloseableHttpResponse recordResponsePost = TestUtils.send(path, HttpMethod.POST, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + createSearchRecordPayload.toString(), ""); + assertEquals(HttpStatus.SC_OK, recordResponsePost.getCode()); + } + + private CloseableHttpResponse createTestRecord(String kind, String id, String legalName) throws Exception { + String jsonInputRecord = RecordUtil.createDefaultJsonRecord(id, kind, legalName); + return TestUtils.send(RECORD, HttpMethod.PUT, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInputRecord, ""); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationRecordsPurgeTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationRecordsPurgeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f13b11762971c572ab8c736255c3ef0cec28279d --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationRecordsPurgeTest.java @@ -0,0 +1,137 @@ +/* + * Copyright 2020-2024 Google LLC + * Copyright 2020-2024 EPAM Systems, 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 org.opengroup.osdu.storage.records; + +import static org.apache.http.HttpStatus.SC_CREATED; +import static org.apache.http.HttpStatus.SC_NOT_FOUND; +import static org.apache.http.HttpStatus.SC_NO_CONTENT; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.api.client.util.Strings; +import java.io.IOException; +import java.util.Map; +import java.util.UUID; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.ConfigUtils; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class CollaborationRecordsPurgeTest extends TestBase { + + static final String COLLABORATION1_ID = UUID.randomUUID().toString(); + + private static boolean isCollaborationEnabled = false; + private static final String COLLABORATION_HEADER = "x-collaboration"; + private static final String APPLICATION_NAME = "storage service integration test"; + private static final String TENANT_NAME = TenantUtils.getTenantName(); + private static final long CURRENT_TIME_MILLIS = System.currentTimeMillis(); + private static final String RECORD_PURGE_ID = TENANT_NAME + ":inttestpurge:1" + CURRENT_TIME_MILLIS; + private static final String COLLABORATION2_ID = UUID.randomUUID().toString(); + private static final String KIND1 = TENANT_NAME + ":ds:inttest:1" + CURRENT_TIME_MILLIS; + private static Long RECORD_PURGE_V1; + private static Long RECORD_PURGE_V2; + private static Long RECORD_PURGE_V3; + private static String LEGAL_TAG_NAME_A; + + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + this.configUtils = new ConfigUtils("test.properties"); + + if (configUtils != null && !configUtils.getIsCollaborationEnabled()) { + return; + } + isCollaborationEnabled = true; + LEGAL_TAG_NAME_A = LegalTagUtils.createRandomName(); + LegalTagUtils.create(LEGAL_TAG_NAME_A, testUtils.getToken()); + + RECORD_PURGE_V1 = createRecord(RECORD_PURGE_ID, COLLABORATION1_ID, KIND1, testUtils.getToken()); + RECORD_PURGE_V2 = createRecord(RECORD_PURGE_ID, COLLABORATION1_ID, KIND1, testUtils.getToken()); + RECORD_PURGE_V3 = createRecord(RECORD_PURGE_ID, COLLABORATION2_ID, KIND1, testUtils.getToken()); + } + + @AfterEach + public void tearDown() throws Exception { + if (!isCollaborationEnabled) return; + TestUtils.send("records/" + RECORD_PURGE_ID, "DELETE", getHeadersWithxCollaboration(COLLABORATION1_ID, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_PURGE_ID, "DELETE", getHeadersWithxCollaboration(COLLABORATION2_ID, testUtils.getToken()), "", ""); + LegalTagUtils.delete(LEGAL_TAG_NAME_A, testUtils.getToken()); + + this.testUtils = null; + this.configUtils = null; + } + + @Test + public void should_purgeAllRecordVersionsOnlyInCollaborationContext() throws Exception { + if (!isCollaborationEnabled) return; + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_PURGE_ID, "DELETE", getHeadersWithxCollaboration(COLLABORATION1_ID, testUtils.getToken()), "", ""); + assertEquals(SC_NO_CONTENT, response.getCode()); + response = TestUtils.send("records/" + RECORD_PURGE_ID, "GET", getHeadersWithxCollaboration(COLLABORATION1_ID, testUtils.getToken()), "", ""); + assertEquals(SC_NOT_FOUND, response.getCode()); + response = TestUtils.send("records/" + RECORD_PURGE_ID, "GET", getHeadersWithxCollaboration(COLLABORATION2_ID, testUtils.getToken()), "", ""); + assertRecordVersion(response, RECORD_PURGE_V3); + } + + private static Long createRecord(String recordId, String collaborationId, String kind, String token) throws Exception { + String jsonInput = RecordUtil.createDefaultJsonRecord(recordId, kind, LEGAL_TAG_NAME_A); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", getHeadersWithxCollaboration(collaborationId, token), jsonInput, ""); + assertEquals(SC_CREATED, response.getCode()); + assertTrue(response.getEntity().getContentType().contains("application/json")); + + String responseBody = EntityUtils.toString(response.getEntity()); + DummyRecordsHelper.CreateRecordResponse result = GSON.fromJson(responseBody, DummyRecordsHelper.CreateRecordResponse.class); + + return Long.parseLong(result.recordIdVersions[0].split(":")[3]); + } + + private static Map<String, String> getHeadersWithxCollaboration(String collaborationId, String token) { + Map<String, String> headers = HeaderUtils.getHeaders(TENANT_NAME, token); + if (!Strings.isNullOrEmpty(collaborationId)) { + headers.put(COLLABORATION_HEADER, "id=" + collaborationId + ",application=" + APPLICATION_NAME); + } + return headers; + } + + private static void assertRecordVersion(CloseableHttpResponse response, Long expectedVersion) { + assertEquals(HttpStatus.SC_OK, response.getCode()); + + String responseBody = null; + try { + responseBody = EntityUtils.toString(response.getEntity()); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + DummyRecordsHelper.RecordResultMock result = GSON.fromJson(responseBody, DummyRecordsHelper.RecordResultMock.class); + assertEquals(expectedVersion.longValue(), Long.parseLong(result.version)); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationRecordsRetrieveTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationRecordsRetrieveTest.java new file mode 100644 index 0000000000000000000000000000000000000000..faaa079bc2669af630653e5ce06838a4a7860618 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationRecordsRetrieveTest.java @@ -0,0 +1,263 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.apache.http.HttpStatus.SC_OK; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.opengroup.osdu.storage.util.HeaderUtils.getHeadersWithxCollaboration; +import static org.opengroup.osdu.storage.util.TestUtils.assertRecordVersion; +import static org.opengroup.osdu.storage.util.TestUtils.createRecordInCollaborationContext_AndReturnVersion; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.ConfigUtils; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class CollaborationRecordsRetrieveTest extends TestBase { + + private static boolean isCollaborationEnabled = false; + private static final DummyRecordsHelper RECORDS_HELPER = new DummyRecordsHelper(); + private static final String APPLICATION_NAME = "storage service integration test"; + private static final String TENANT_NAME = TenantUtils.getTenantName(); + private static final long CURRENT_TIME_MILLIS = System.currentTimeMillis(); + private static final String COLLABORATION1_ID = UUID.randomUUID().toString(); + private static final String COLLABORATION2_ID = UUID.randomUUID().toString(); + private static final String RECORD_ID_1 = TENANT_NAME + ":inttest:1" + CURRENT_TIME_MILLIS; + private static final String RECORD_ID_2 = TENANT_NAME + ":inttest:2" + CURRENT_TIME_MILLIS; + private static final String RECORD_ID_3 = TENANT_NAME + ":inttest:3" + CURRENT_TIME_MILLIS; + private static final String KIND1 = TENANT_NAME + ":ds:inttest:1" + CURRENT_TIME_MILLIS; + private static final String KIND2 = TENANT_NAME + ":ds:inttest:2" + CURRENT_TIME_MILLIS; + private static final String KIND3 = TENANT_NAME + ":ds:inttest:3" + CURRENT_TIME_MILLIS; + private static Long RECORD1_V1; + private static Long RECORD1_V2; + private static Long RECORD1_V3; + private static Long RECORD1_V4; + private static Long RECORD2_V1; + private static Long RECORD2_V2; + private static Long RECORD3_V1; + private static Long RECORD3_V2; + private static String LEGAL_TAG_NAME_A; + + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + this.configUtils = new ConfigUtils("test.properties"); + + if (configUtils != null && !configUtils.getIsCollaborationEnabled()) { + return; + } + isCollaborationEnabled = true; + LEGAL_TAG_NAME_A = LegalTagUtils.createRandomName(); + LegalTagUtils.create(LEGAL_TAG_NAME_A, testUtils.getToken()); + + RECORD1_V1 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_1, KIND1, LEGAL_TAG_NAME_A, null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + RECORD1_V2 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_1, KIND1, LEGAL_TAG_NAME_A, COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + RECORD1_V3 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_1, KIND1, LEGAL_TAG_NAME_A, COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + RECORD1_V4 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_1, KIND1, LEGAL_TAG_NAME_A, COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + + RECORD2_V1 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_2, KIND1, LEGAL_TAG_NAME_A, null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + RECORD2_V2 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_2, KIND1, LEGAL_TAG_NAME_A, COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + + RECORD3_V1 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_3, KIND2, LEGAL_TAG_NAME_A, COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + RECORD3_V2 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_3, KIND2, LEGAL_TAG_NAME_A, COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + } + + @AfterEach + public void tearDown() throws Exception { + if (!isCollaborationEnabled) return; + TestUtils.send("records/" + RECORD_ID_1, "DELETE", getHeadersWithxCollaboration(null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_1, "DELETE", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_1, "DELETE", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_2, "DELETE", getHeadersWithxCollaboration(null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_2, "DELETE", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_3, "DELETE", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_3, "DELETE", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + LegalTagUtils.delete(LEGAL_TAG_NAME_A, testUtils.getToken()); + + this.testUtils = null; + this.configUtils = null; + } + + @Test + public void should_getLatestVersion_when_validRecordIdAndCollaborationIdAreProvided() throws Exception { + if (!isCollaborationEnabled) return; + //get record1 --> v1 + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID_1, "GET", getHeadersWithxCollaboration(null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertRecordVersion(response, RECORD1_V1); + //get record1 with guid1 --> v3 + response = TestUtils.send("records/" + RECORD_ID_1, "GET", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertRecordVersion(response, RECORD1_V3); + //get record1 with guid2 --> v4 + response = TestUtils.send("records/" + RECORD_ID_1, "GET", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertRecordVersion(response, RECORD1_V4); + //get record2 with guid1 --> 404 + response = TestUtils.send("records/" + RECORD_ID_2, "GET", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + } + + @Test + public void should_getCorrectRecordVersion_when_validRecordIdAndCollaborationIdAndRecordVersionAreProvided() throws Exception { + if (!isCollaborationEnabled) return; + //get record1 with v2 with context guid1 + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID_1 + "/" + RECORD1_V2, "GET", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertRecordVersion(response, RECORD1_V2); + //get 404 for record1 with v2 with context guid2 + response = TestUtils.send("records/" + RECORD_ID_1 + "/" + RECORD1_V2, "GET", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + } + + @Test + public void should_getAllRecordVersions_when_validRecordIdAndCollaborationIdAreProvided() throws Exception { + if (!isCollaborationEnabled) return; + //I will get only v1 for record1 with no context + CloseableHttpResponse response = TestUtils.send("records/versions/" + RECORD_ID_1, "GET", getHeadersWithxCollaboration(null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + RecordsApiAcceptanceTests.GetVersionsResponse versionsResponse = TestUtils.getResult(response, HttpStatus.SC_OK, RecordsApiAcceptanceTests.GetVersionsResponse.class); + assertEquals(1, versionsResponse.versions.length); + assertEquals(RECORD1_V1, versionsResponse.versions[0]); + + //I will get v2 and v3 for record1 with context guid1 + response = TestUtils.send("records/versions/" + RECORD_ID_1, "GET", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + versionsResponse = TestUtils.getResult(response, HttpStatus.SC_OK, RecordsApiAcceptanceTests.GetVersionsResponse.class); + assertEquals(2, versionsResponse.versions.length); + List<Long> versions = Arrays.asList(versionsResponse.versions); + assertTrue(versions.contains(RECORD1_V2)); + assertTrue(versions.contains(RECORD1_V3)); + } + + @Test + public void should_getRecordsOnlyInCollaborationContext_whenQueryByKind() throws Exception { + if (!isCollaborationEnabled) return; + CloseableHttpResponse response = TestUtils.send("query/records", "GET", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", "?kind=" + KIND1); + assertEquals(SC_OK, response.getCode()); + DummyRecordsHelper.QueryResultMock responseObject = RECORDS_HELPER.getQueryResultMockFromResponse(response); + assertEquals(2, responseObject.results.length); + assertTrue(Arrays.stream(responseObject.results).anyMatch(RECORD_ID_1::equals)); + assertTrue(Arrays.stream(responseObject.results).anyMatch(RECORD_ID_2::equals)); + + response = TestUtils.send("query/records", "GET", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", "?kind=" + KIND1); + assertEquals(SC_OK, response.getCode()); + responseObject = RECORDS_HELPER.getQueryResultMockFromResponse(response); + assertEquals(1, responseObject.results.length); + assertTrue(Arrays.stream(responseObject.results).anyMatch(RECORD_ID_1::equals)); + + response = TestUtils.send("query/records", "GET", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", "?kind=" + KIND3); + assertEquals(SC_OK, response.getCode()); + responseObject = RECORDS_HELPER.getQueryResultMockFromResponse(response); + assertEquals(0, responseObject.results.length); + } + + @Test + public void should_getEmptyRecordsInNoCollaborationContext_whenQueryByKind() throws Exception { + if (!isCollaborationEnabled) return; + CloseableHttpResponse response = TestUtils.send("query/records", "GET", getHeadersWithxCollaboration(null, null, TENANT_NAME, testUtils.getToken()), "", "?kind=" + KIND2); + assertEquals(SC_OK, response.getCode()); + DummyRecordsHelper.QueryResultMock responseObject = RECORDS_HELPER.getQueryResultMockFromResponse(response); + assertEquals(0, responseObject.results.length); + } + + @Test + public void should_fetchCorrectRecords_when_validRecordIdsAndCollaborationIdAreProvided() throws Exception { + if (!isCollaborationEnabled) return; + //If I fetch records 1, 2,and 3 in context guid1,I should get a 200 with records 1 and 3 + JsonArray records = new JsonArray(); + records.add(RECORD_ID_1); + records.add(RECORD_ID_2); + records.add(RECORD_ID_3); + JsonObject body = new JsonObject(); + body.add("records", records); + CloseableHttpResponse response = TestUtils.send("query/records:batch", "POST", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), body.toString(), ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(2, responseObject.records.length); + assertEquals(1, responseObject.notFound.length); + assertEquals(0, responseObject.conversionStatuses.size()); + for (DummyRecordsHelper.RecordResultMock record : responseObject.records) { + if (record.id.equals(RECORD_ID_1)) assertEquals(RECORD1_V3, Long.valueOf(record.version)); + else if (record.id.equals(RECORD_ID_2)) fail("should not contain record 2: " + RECORD_ID_2); + else if (record.id.equals(RECORD_ID_3)) assertEquals(RECORD3_V1, Long.valueOf(record.version)); + else fail(String.format("should only contain record 1 %s, and record 3 %s", RECORD_ID_1, RECORD_ID_3)); + } + + // If I fetch records 1, 2, and 3 in no context, I should get a 200 with records 1 and 2 + response = TestUtils.send("query/records:batch", "POST", getHeadersWithxCollaboration(null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), body.toString(), ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + responseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(response); + assertEquals(2, responseObject.records.length); + assertEquals(1, responseObject.notFound.length); + assertEquals(0, responseObject.conversionStatuses.size()); + for (DummyRecordsHelper.RecordResultMock record : responseObject.records) { + if (record.id.equals(RECORD_ID_1)) assertEquals(RECORD1_V1, Long.valueOf(record.version)); + else if (record.id.equals(RECORD_ID_2)) assertEquals(RECORD2_V1, Long.valueOf(record.version)); + else if (record.id.equals(RECORD_ID_3)) fail("should not contain record 3: " + RECORD_ID_3); + else fail(String.format("should only contain record 1 %s, and record 2 %s", RECORD_ID_1, RECORD_ID_2)); + } + } + + @Test + public void should_queryAllRecords_when_validRecordIdsAndCollaborationIdAreProvided() throws Exception { + if (!isCollaborationEnabled) return; + // If I query records 1,2 and 3 in context guid2, I should get 200 with records 1,2 and 3 + JsonArray records = new JsonArray(); + records.add(RECORD_ID_1); + records.add(RECORD_ID_2); + records.add(RECORD_ID_3); + JsonObject body = new JsonObject(); + body.add("records", records); + CloseableHttpResponse response = TestUtils.send("query/records", "POST", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), body.toString(), ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.RecordsMock responseObject = RECORDS_HELPER.getRecordsMockFromResponse(response); + assertEquals(3, responseObject.records.length); + assertEquals(0, responseObject.invalidRecords.length); + assertEquals(0, responseObject.retryRecords.length); + for (DummyRecordsHelper.RecordResultMock record : responseObject.records) { + if (record.id.equals(RECORD_ID_1)) assertEquals(RECORD1_V4, Long.valueOf(record.version)); + else if (record.id.equals(RECORD_ID_2)) assertEquals(RECORD2_V2, Long.valueOf(record.version)); + else if (record.id.equals(RECORD_ID_3)) assertEquals(RECORD3_V2, Long.valueOf(record.version)); + else fail(String.format("should only contain record 1 %s, 2 %s and record 3 %s", RECORD_ID_1, RECORD_ID_2, RECORD_ID_3)); + } + + // If I query records 1, 2 and 3 in context guid1, I should get 2xx with records 1 and 3 + response = TestUtils.send("query/records", "POST", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), body.toString(), ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + responseObject = RECORDS_HELPER.getRecordsMockFromResponse(response); + assertEquals(2, responseObject.records.length); + assertEquals(1, responseObject.invalidRecords.length); + assertEquals(0, responseObject.retryRecords.length); + for (DummyRecordsHelper.RecordResultMock record : responseObject.records) { + if (record.id.equals(RECORD_ID_1)) assertEquals(RECORD1_V3, Long.valueOf(record.version)); + else if (record.id.equals(RECORD_ID_2)) fail("should not contain record 2: " + RECORD_ID_2); + else if (record.id.equals(RECORD_ID_3)) assertEquals(RECORD3_V1, Long.valueOf(record.version)); + else fail(String.format("should only contain record 1 %s, and record 3 %s", RECORD_ID_1, RECORD_ID_3)); + } + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationRecordsSoftDeleteTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationRecordsSoftDeleteTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0208bc239b903a291f70f772146e300233ea41a7 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationRecordsSoftDeleteTest.java @@ -0,0 +1,136 @@ +/* + * Copyright 2020-2024 Google LLC + * Copyright 2020-2024 EPAM Systems, 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opengroup.osdu.storage.util.HeaderUtils.getHeadersWithxCollaboration; +import static org.opengroup.osdu.storage.util.TestUtils.assertRecordVersion; +import static org.opengroup.osdu.storage.util.TestUtils.createRecordInCollaborationContext_AndReturnVersion; + +import com.google.gson.JsonArray; +import java.util.UUID; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.ConfigUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class CollaborationRecordsSoftDeleteTest extends TestBase { + private static boolean isCollaborationEnabled = false; + private static final String APPLICATION_NAME = "storage service integration test for soft delete"; + private static final String TENANT_NAME = TenantUtils.getTenantName(); + private static final long CURRENT_TIME_MILLIS = System.currentTimeMillis(); + private static final String COLLABORATION1_ID = UUID.randomUUID().toString(); + private static final String COLLABORATION2_ID = UUID.randomUUID().toString(); + private static final String RECORD_ID_1 = TENANT_NAME + ":inttest:1" + CURRENT_TIME_MILLIS; + private static final String RECORD_ID_2 = TENANT_NAME + ":inttest:2" + CURRENT_TIME_MILLIS; + private static final String RECORD_ID_3 = TENANT_NAME + ":inttest:3" + CURRENT_TIME_MILLIS; + private static final String KIND = TENANT_NAME + ":ds:inttest:" + CURRENT_TIME_MILLIS; + private static Long RECORD1_V1; + private static Long RECORD1_V2; + private static Long RECORD1_V3; + private static Long RECORD1_V4; + private static Long RECORD2_V1; + private static Long RECORD2_V2; + private static Long RECORD3_V1; + private static Long RECORD3_V2; + private static String LEGAL_TAG_NAME; + + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + this.configUtils = new ConfigUtils("test.properties"); + + if (configUtils != null && !configUtils.getIsCollaborationEnabled()) { + return; + } + isCollaborationEnabled = true; + LEGAL_TAG_NAME = LegalTagUtils.createRandomName(); + LegalTagUtils.create(LEGAL_TAG_NAME, testUtils.getToken()); + + RECORD1_V1 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_1, KIND, LEGAL_TAG_NAME, COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + RECORD1_V2 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_1, KIND, LEGAL_TAG_NAME, COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + RECORD1_V3 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_1, KIND, LEGAL_TAG_NAME, null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + RECORD1_V4 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_1, KIND, LEGAL_TAG_NAME,COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + + RECORD2_V1 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_2, KIND, LEGAL_TAG_NAME, COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + RECORD2_V2 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_2, KIND, LEGAL_TAG_NAME, COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + + RECORD3_V1 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_3, KIND, LEGAL_TAG_NAME, COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + RECORD3_V2 = createRecordInCollaborationContext_AndReturnVersion(RECORD_ID_3, KIND, LEGAL_TAG_NAME, COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + } + + @AfterEach + public void tearDown() throws Exception { + if (!isCollaborationEnabled) return; + TestUtils.send("records/" + RECORD_ID_1, "DELETE", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_1, "DELETE", getHeadersWithxCollaboration(null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_1, "DELETE", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_2, "DELETE", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_2, "DELETE", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_3, "DELETE", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_3, "DELETE", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + LegalTagUtils.delete(LEGAL_TAG_NAME, testUtils.getToken()); + + this.testUtils = null; + this.configUtils = null; + } + + @Test + public void should_softDeleteSingleRecordWithinCollaborationContext_when_validRecordIdsAndCollaborationIdAreProvided() throws Exception { + if (!isCollaborationEnabled) return; + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID_1 + ":delete", "POST", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); + + response = TestUtils.send("records/" + RECORD_ID_1, "GET", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + + response = TestUtils.send("records/" + RECORD_ID_1, "GET", getHeadersWithxCollaboration(null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertRecordVersion(response, RECORD1_V3); + + response = TestUtils.send("records/" + RECORD_ID_1, "GET", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertRecordVersion(response, RECORD1_V4); + } + + @Test + public void should_bulkSoftDeleteWithinCollaborationContext_when_validRecordIdsAndCollaborationIdAreProvided() throws Exception { + if (!isCollaborationEnabled) return; + JsonArray body = new JsonArray(); + body.add(RECORD_ID_2); + body.add(RECORD_ID_3); + CloseableHttpResponse response = TestUtils.send("records/delete", "POST", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), body.toString(), ""); + assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); + + response = TestUtils.send("records/" + RECORD_ID_2, "GET", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + + response = TestUtils.send("records/" + RECORD_ID_3, "GET", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + + response = TestUtils.send("records/" + RECORD_ID_2, "GET", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertRecordVersion(response, RECORD2_V2); + + response = TestUtils.send("records/" + RECORD_ID_3, "GET", getHeadersWithxCollaboration(COLLABORATION2_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + assertRecordVersion(response, RECORD3_V2); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationUpdateRecordsMetadataTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationUpdateRecordsMetadataTest.java new file mode 100644 index 0000000000000000000000000000000000000000..aeb3c8132aff51d8942ff818b3b43c854d284f26 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CollaborationUpdateRecordsMetadataTest.java @@ -0,0 +1,107 @@ +/* + * Copyright 2020-2024 Google LLC + * Copyright 2020-2024 EPAM Systems, 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 org.opengroup.osdu.storage.records; + +import static org.apache.http.HttpStatus.SC_OK; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opengroup.osdu.storage.records.CollaborationRecordsPurgeTest.COLLABORATION1_ID; +import static org.opengroup.osdu.storage.records.UpdateRecordsMetadataTest.TAG_KEY; +import static org.opengroup.osdu.storage.records.UpdateRecordsMetadataTest.TAG_VALUE1; +import static org.opengroup.osdu.storage.util.HeaderUtils.getHeadersWithxCollaboration; +import static org.opengroup.osdu.storage.util.TestUtils.assertRecordVersionAndReturnResponseBody; +import static org.opengroup.osdu.storage.util.TestUtils.createRecordInCollaborationContext_AndReturnVersion; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.ConfigUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class CollaborationUpdateRecordsMetadataTest extends TestBase { + private static boolean isCollaborationEnabled = false; + private static final String APPLICATION_NAME = "storage service integration test for update records metadata"; + private static final String TENANT_NAME = TenantUtils.getTenantName(); + private static final long CURRENT_TIME_MILLIS = System.currentTimeMillis(); + private static String LEGAL_TAG_NAME; + private static final String KIND = TENANT_NAME + ":ds:patchtest:1" + CURRENT_TIME_MILLIS; + private static final String RECORD_PATCH_ID = TENANT_NAME + ":patchtest:1" + CURRENT_TIME_MILLIS; + private static Long RECORD_PATCH_V1; + private static Long RECORD_PATCH_V2; + + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + this.configUtils = new ConfigUtils("test.properties"); + + if (configUtils != null && !configUtils.getIsCollaborationEnabled()) { + return; + } + isCollaborationEnabled = true; + LEGAL_TAG_NAME = LegalTagUtils.createRandomName(); + LegalTagUtils.create(LEGAL_TAG_NAME, testUtils.getToken()); + + //create records in different collaboration context + RECORD_PATCH_V1 = createRecordInCollaborationContext_AndReturnVersion(RECORD_PATCH_ID, KIND, LEGAL_TAG_NAME, null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + RECORD_PATCH_V2 = createRecordInCollaborationContext_AndReturnVersion(RECORD_PATCH_ID, KIND, LEGAL_TAG_NAME, COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()); + + //update record with tags in one collaboration context + JsonObject updateBody = RecordUtil.buildUpdateTagBody(RECORD_PATCH_ID, "add", TAG_KEY + ":" + TAG_VALUE1); + CloseableHttpResponse patchResponse = TestUtils.send("records", "PATCH", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TenantUtils.getTenantName(), testUtils.getToken()), updateBody.toString(), ""); + assertEquals(SC_OK, patchResponse.getCode()); + } + + @Test + public void shouldMaintainAndUpdateRecordInRespctiveCollaborationContext() throws Exception { + if (!isCollaborationEnabled) return; + //assert record with no collaboration context + CloseableHttpResponse getResponse = TestUtils.send("records/" + RECORD_PATCH_ID, "GET", getHeadersWithxCollaboration(null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + String responseBody = assertRecordVersionAndReturnResponseBody(getResponse, RECORD_PATCH_V1); + JsonObject resultObject = bodyToJsonObject(responseBody); + assertNull(resultObject.get("tags")); + //assert record with collaboration context + getResponse = TestUtils.send("records/" + RECORD_PATCH_ID, "GET", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + responseBody = assertRecordVersionAndReturnResponseBody(getResponse, RECORD_PATCH_V2); + resultObject = bodyToJsonObject(responseBody); + assertTrue(resultObject.get("tags").getAsJsonObject().has(TAG_KEY)); + assertEquals(TAG_VALUE1, resultObject.get("tags").getAsJsonObject().get(TAG_KEY).getAsString()); + } + + @AfterEach + public void tearDown() throws Exception { + if (!isCollaborationEnabled) return; + TestUtils.send("records/" + RECORD_PATCH_ID, "DELETE", getHeadersWithxCollaboration(null, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_PATCH_ID, "DELETE", getHeadersWithxCollaboration(COLLABORATION1_ID, APPLICATION_NAME, TENANT_NAME, testUtils.getToken()), "", ""); + LegalTagUtils.delete(LEGAL_TAG_NAME, testUtils.getToken()); + + this.testUtils = null; + this.configUtils = null; + } + + private static JsonObject bodyToJsonObject(String json) { + return JsonParser.parseString(json).getAsJsonObject(); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CopyRecordReferencesTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CopyRecordReferencesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1fa7442eabf3269a8affe1446e58d027a6324333 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/CopyRecordReferencesTest.java @@ -0,0 +1,465 @@ +/* + * Copyright 2020-2024 Google LLC + * Copyright 2020-2024 EPAM Systems, 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.opengroup.osdu.storage.util.HeaderUtils.getHeadersWithxCollaborationWithoutId; +import static org.opengroup.osdu.storage.util.TestUtils.getCopyRecordRequest; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.util.UUID; +import lombok.extern.java.Log; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.ConfigUtils; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +@Log +public final class CopyRecordReferencesTest extends TestBase { + + private static final String APPLICATION_NAME = "storage service integration test"; + + private static final String COLLABORATION_ID = "25c25830-8588-4b12-a0be-7263f2e43a09"; + private static final String COLLABORATION_ID_WIP_TO_WIP = "cfa0c1b0-421a-4f51-a2ac-84ff8a968736"; + + private static final String RECORD_ID_SOR_TO_WIP = + TenantUtils.getTenantName() + ":getrecord:" + UUID.randomUUID(); + + private static final String RECORD_ID_WIP_TO_SOR = + TenantUtils.getTenantName() + ":getrecord:" + UUID.randomUUID(); + + private static final String RECORD_ID_WIP_TO_WIP = + TenantUtils.getTenantName() + ":getrecord:" + UUID.randomUUID(); + + private static final String RECORD_ID_EXIST_IN_TARGET = + TenantUtils.getTenantName() + ":getrecord:" + UUID.randomUUID(); + + private static final String KIND = TenantUtils.getTenantName() + ":ds:getrecord:1.0." + + System.currentTimeMillis(); + private static String LEGAL_TAG_NAME_A; + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + ConfigUtils configUtils = new ConfigUtils("test.properties"); + assumeTrue(configUtils.getIsCollaborationEnabled()); + CopyRecordReferencesTest.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + CopyRecordReferencesTest.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + System.out.println(String.format("Test Ids: %s, %s, %s, %s", RECORD_ID_SOR_TO_WIP, RECORD_ID_WIP_TO_SOR, RECORD_ID_WIP_TO_WIP, RECORD_ID_EXIST_IN_TARGET)); + LEGAL_TAG_NAME_A = LegalTagUtils.createRandomName(); + LegalTagUtils.create(LEGAL_TAG_NAME_A, token); + Thread.sleep(100); + + String jsonInputSorToWip = RecordUtil.createDefaultJsonRecord(RECORD_ID_SOR_TO_WIP, KIND, + LEGAL_TAG_NAME_A); + + CloseableHttpResponse responseSorToWip = TestUtils.send("records", "PUT", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), jsonInputSorToWip, ""); + + JsonObject jsonResponseSorToWip = JsonParser.parseString( + EntityUtils.toString(responseSorToWip.getEntity())) + .getAsJsonObject(); + + assertEquals( + HttpStatus.SC_CREATED, + responseSorToWip.getCode(), + "Creating record for copy from SOR to WIP" + ); + + assertTrue( + responseSorToWip.getEntity().getContentType().contains("application/json"), + "Creating record for copy from SOR to WIP" + ); + + assertEquals( + RECORD_ID_SOR_TO_WIP, + jsonResponseSorToWip.get("recordIds").getAsString(), + "Creating record for copy from SOR to WIP" + ); + + String jsonInputWipToSor = RecordUtil.createDefaultJsonRecord(RECORD_ID_WIP_TO_SOR, KIND, + LEGAL_TAG_NAME_A); + + CloseableHttpResponse responseWipToSor = TestUtils.send("records", "PUT", + HeaderUtils.getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), token), jsonInputWipToSor, ""); + JsonObject jsonResponseWipToSor = JsonParser.parseString( + EntityUtils.toString(responseWipToSor.getEntity())) + .getAsJsonObject(); + + assertEquals( + HttpStatus.SC_CREATED, + responseWipToSor.getCode(), + "Creating record for copy from WIP to SOR" + ); + + assertTrue( + responseWipToSor.getEntity().getContentType().contains("application/json"), + "Creating record for copy from WIP to SOR" + ); + + assertEquals( + RECORD_ID_WIP_TO_SOR, + jsonResponseWipToSor.get("recordIds").getAsString(), + "Creating record for copy from WIP to SOR" + ); + + String jsonInputWipToWip = RecordUtil.createDefaultJsonRecord(RECORD_ID_WIP_TO_WIP, KIND, + LEGAL_TAG_NAME_A); + + CloseableHttpResponse responseWipToWip = TestUtils.send("records", "PUT", + HeaderUtils.getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), token), jsonInputWipToWip, ""); + JsonObject jsonResponseWipToWip = JsonParser.parseString( + EntityUtils.toString(responseWipToWip.getEntity())) + .getAsJsonObject(); + + assertEquals( + HttpStatus.SC_CREATED, + responseWipToWip.getCode(), + "Creating record for copy from WIP to WIP" + ); + + assertTrue( + responseWipToWip.getEntity().getContentType().contains("application/json"), + "Creating record for copy from WIP to WIP" + ); + + assertEquals( + RECORD_ID_WIP_TO_WIP, + jsonResponseWipToWip.get("recordIds").getAsString(), + "Creating record for copy from WIP to WIP" + ); + + String jsonInputExistInTarget = RecordUtil.createDefaultJsonRecord(RECORD_ID_EXIST_IN_TARGET, + KIND, + LEGAL_TAG_NAME_A); + + CloseableHttpResponse responseExistInTarget = TestUtils.send("records", "PUT", + HeaderUtils.getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), token), jsonInputExistInTarget, ""); + JsonObject jsonResponseExistInTarget = JsonParser.parseString( + EntityUtils.toString(responseExistInTarget.getEntity())) + .getAsJsonObject(); + + assertEquals( + HttpStatus.SC_CREATED, + responseExistInTarget.getCode(), + "Creating record for check existing in target" + ); + + assertTrue( + responseExistInTarget.getEntity().getContentType().contains("application/json"), + "Creating record for check existing in target" + ); + + assertEquals( + RECORD_ID_EXIST_IN_TARGET, + jsonResponseExistInTarget.get("recordIds").getAsString(), + "Creating record for check existing in target" + ); + } + + public static void classTearDown(String token) throws Exception { + TestUtils.send("records/" + RECORD_ID_SOR_TO_WIP, "DELETE", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID_WIP_TO_SOR, "DELETE", + HeaderUtils.getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID_WIP_TO_WIP, "DELETE", + HeaderUtils.getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), token), "", ""); + Thread.sleep(100); + LegalTagUtils.delete(LEGAL_TAG_NAME_A, token); + } + + @Test + public void should_copyRecord_from_sor_to_wip() throws Exception { + // check namespace before copy + CloseableHttpResponse responseGet = TestUtils.send("records/" + RECORD_ID_SOR_TO_WIP, "GET", + getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + + assertEquals( + HttpStatus.SC_NOT_FOUND, + responseGet.getCode(), + "Check that record absent in target when copy from SOR to WIP" + ); + + // copy checks + JsonObject copyBody = getCopyRecordRequest(COLLABORATION_ID, RECORD_ID_SOR_TO_WIP); + String body = copyBody.toString(); + + CloseableHttpResponse responseCopy = TestUtils.send("records/copy", "PUT", + getHeadersWithxCollaborationWithoutId("", APPLICATION_NAME, TenantUtils.getTenantName(), + testUtils.getToken()), body, ""); + + assertEquals( + HttpStatus.SC_OK, + responseCopy.getCode(), + "Check response after copy SOR to WIP" + ); + + // check namespace after copy + CloseableHttpResponse responseGetCopy = TestUtils.send("records/" + RECORD_ID_SOR_TO_WIP, "GET", + getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + JsonObject jsonCopy = JsonParser.parseString(EntityUtils.toString(responseGetCopy.getEntity())) + .getAsJsonObject(); + + assertEquals( + HttpStatus.SC_OK, + responseGetCopy.getCode(), + "Get copied record from WIP when copy SOR to WIP" + ); + + assertEquals( + RECORD_ID_SOR_TO_WIP, + jsonCopy.get("id").getAsString(), + "Get copied record from WIP when copy SOR to WIP" + ); + + // delete from WIP + CloseableHttpResponse responseDelete = TestUtils.send("records/" + RECORD_ID_SOR_TO_WIP, + "DELETE", getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), testUtils.getToken()), body, ""); + + assertEquals( + HttpStatus.SC_NO_CONTENT, + responseDelete.getCode(), + "Check that record deleted when copy SOR to WIP" + ); + + } + + @Test + public void should_copyRecord_from_wip_to_sor() throws Exception { + // check namespace before copy + CloseableHttpResponse responseGet = TestUtils.send("records/" + RECORD_ID_WIP_TO_SOR, "GET", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + + assertEquals( + HttpStatus.SC_NOT_FOUND, + responseGet.getCode(), + "Check that record absent in target when copy WIP to SOR" + ); + + // copy checks + JsonObject copyBody = getCopyRecordRequest("", RECORD_ID_WIP_TO_SOR); + String body = copyBody.toString(); + + CloseableHttpResponse responseCopy = TestUtils.send("records/copy", "PUT", + getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), + testUtils.getToken()), body, ""); + + assertEquals( + HttpStatus.SC_OK, + responseCopy.getCode(), + "Check response after copy WIP to SOR" + ); + + // check namespace after copy + CloseableHttpResponse responseGetCopy = TestUtils.send("records/" + RECORD_ID_WIP_TO_SOR, "GET", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + JsonObject jsonCopy = JsonParser.parseString(EntityUtils.toString(responseGetCopy.getEntity())) + .getAsJsonObject(); + + assertEquals( + HttpStatus.SC_OK, + responseGetCopy.getCode(), + "Get copied record from WIP when copy WIP to SOR" + ); + + assertEquals( + RECORD_ID_WIP_TO_SOR, + jsonCopy.get("id").getAsString(), + "Get copied record from WIP when copy WIP to SOR" + ); + + // delete from SOR + CloseableHttpResponse responseDelete = TestUtils.send("records/" + RECORD_ID_WIP_TO_SOR, + "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + body, ""); + + assertEquals( + HttpStatus.SC_NO_CONTENT, + responseDelete.getCode(), + "Check that record deleted when copy WIP to SOR" + ); + } + + @Test + public void should_copyRecord_from_wip_to_wip() throws Exception { + // check namespace before copy + CloseableHttpResponse responseGet = TestUtils.send("records/" + RECORD_ID_WIP_TO_WIP, "GET", + getHeadersWithxCollaborationWithoutId(COLLABORATION_ID_WIP_TO_WIP, APPLICATION_NAME, + TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + + assertEquals( + HttpStatus.SC_NOT_FOUND, + responseGet.getCode(), + "Check that record absent in target copy WIP to WIP" + ); + + // copy checks + JsonObject copyBody = getCopyRecordRequest(COLLABORATION_ID_WIP_TO_WIP, RECORD_ID_WIP_TO_WIP); + String body = copyBody.toString(); + + CloseableHttpResponse responseCopy = TestUtils.send("records/copy", "PUT", + getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), + testUtils.getToken()), body, ""); + + assertEquals( + HttpStatus.SC_OK, + responseCopy.getCode(), + "Check response after copy WIP to WIP" + ); + + // check namespace after copy + CloseableHttpResponse responseGetCopy = TestUtils.send("records/" + RECORD_ID_WIP_TO_WIP, "GET", + getHeadersWithxCollaborationWithoutId(COLLABORATION_ID_WIP_TO_WIP, APPLICATION_NAME, + TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + JsonObject jsonCopy = JsonParser.parseString(EntityUtils.toString(responseGetCopy.getEntity())) + .getAsJsonObject(); + + assertEquals( + HttpStatus.SC_OK, + responseGetCopy.getCode(), + "Get copied record from WIP when copy WIP to WIP" + ); + + assertEquals( + RECORD_ID_WIP_TO_WIP, + jsonCopy.get("id").getAsString(), + "Get copied record from WIP when copy WIP to WIP" + ); + + // delete from WIP + CloseableHttpResponse responseDelete = TestUtils.send("records/" + RECORD_ID_WIP_TO_WIP, + "DELETE", + getHeadersWithxCollaborationWithoutId(COLLABORATION_ID_WIP_TO_WIP, APPLICATION_NAME, + TenantUtils.getTenantName(), testUtils.getToken()), body, ""); + + assertEquals( + HttpStatus.SC_NO_CONTENT, + responseDelete.getCode(), + "Check that record deleted when copy WIP to WIP" + ); + } + + @Test + public void should_return409_when_try_to_copy_sor_to_sor() throws Exception { + JsonObject copyBody = getCopyRecordRequest("", RECORD_ID_WIP_TO_WIP); + String body = copyBody.toString(); + + CloseableHttpResponse responseCopy = TestUtils.send("records/copy", "PUT", + getHeadersWithxCollaborationWithoutId("", APPLICATION_NAME, TenantUtils.getTenantName(), + testUtils.getToken()), body, ""); + + assertEquals(HttpStatus.SC_CONFLICT, responseCopy.getCode()); + } + + @Test + public void should_return404_when_record_absent_in_source() throws Exception { + JsonObject copyBody = getCopyRecordRequest(COLLABORATION_ID, RECORD_ID_WIP_TO_SOR); + String body = copyBody.toString(); + + CloseableHttpResponse responseCopy = TestUtils.send("records/copy", "PUT", + getHeadersWithxCollaborationWithoutId("", APPLICATION_NAME, TenantUtils.getTenantName(), + testUtils.getToken()), body, ""); + assertEquals(HttpStatus.SC_NOT_FOUND, responseCopy.getCode()); + } + + @Test + public void should_return409_when_record_exist_in_target() throws Exception { + JsonObject copyBody = getCopyRecordRequest("", RECORD_ID_EXIST_IN_TARGET); + String body = copyBody.toString(); + + CloseableHttpResponse responseCopy = TestUtils.send("records/copy", "PUT", + getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), + testUtils.getToken()), body, ""); + + assertEquals( + HttpStatus.SC_OK, + responseCopy.getCode(), + "Check that record created in WIP when check exception about existing in target" + ); + + JsonObject copyBodyTest = getCopyRecordRequest("", RECORD_ID_EXIST_IN_TARGET); + String bodyTest = copyBodyTest.toString(); + + CloseableHttpResponse responseCopyTest = TestUtils.send("records/copy", "PUT", + getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), + testUtils.getToken()), bodyTest, ""); + + assertEquals( + HttpStatus.SC_CONFLICT, + responseCopyTest.getCode(), + "The already exists when check exception about existing in target" + ); + + // delete from SOR + CloseableHttpResponse responseDelete = TestUtils.send("records/" + RECORD_ID_EXIST_IN_TARGET, + "DELETE", getHeadersWithxCollaborationWithoutId(COLLABORATION_ID, APPLICATION_NAME, + TenantUtils.getTenantName(), testUtils.getToken()), body, ""); + assertEquals( + HttpStatus.SC_NO_CONTENT, + responseDelete.getCode(), + "Check that record deleted when check exception about existing in target" + ); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/DataRootAccessTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/DataRootAccessTest.java new file mode 100644 index 0000000000000000000000000000000000000000..244b641d99c9a3ba5fa71e299135e7d4a1e41393 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/DataRootAccessTest.java @@ -0,0 +1,156 @@ +/* + * Copyright 2020-2023 Google LLC + * Copyright 2020-2023 EPAM Systems, 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import java.util.Map; +import java.util.stream.Stream; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.ConfigUtils; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.EntitlementsUtil; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class DataRootAccessTest extends TestBase { + + private static long NOW = System.currentTimeMillis(); + private static String LEGAL_TAG = LegalTagUtils.createRandomName(); + private static String KIND = TenantUtils.getTenantName() + ":data-root-test:no:1.1." + NOW; + private static String RECORD_ID = TenantUtils.getTenantName() + ":data-root-test:1.1." + NOW; + private static String DATA_GROUP_ID = "data.test-users-data-root." + NOW; + private static String GROUP_DESCRIPTION = "Used in ACL, to test that users.data.root have access to any data group."; + private static String GROUP_EMAIL; + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + DataRootAccessTest.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + DataRootAccessTest.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + this.configUtils = new ConfigUtils("test.properties"); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG, token); + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), token); + GROUP_EMAIL = createDataGroup(headers); + String createRecordBody = RecordUtil.createJsonRecordWithCustomAcl(RECORD_ID, KIND, LEGAL_TAG, + GROUP_EMAIL); + CloseableHttpResponse response = TestUtils.send( + "records", + "PUT", + headers, + createRecordBody, + "" + ); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + } + + public static void classTearDown(String token) throws Exception { + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), token); + TestUtils.send( + "records/" + RECORD_ID, "DELETE", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), + "", + "" + ); + deleteDataGroup(headers, GROUP_EMAIL); + LegalTagUtils.delete(LEGAL_TAG, token); + } + + @Test + public void shouldHaveAccessToNewlyCreatedDataGroupWhenBelongsToUsersDataRoot() throws Exception { + JsonArray records = new JsonArray(); + records.add(RECORD_ID); + + JsonObject body = new JsonObject(); + body.add("records", records); + + Map<String, String> headersWithUsersDataRootAccess = HeaderUtils.getHeaders( + TenantUtils.getTenantName(), + testUtils.getDataRootUserToken()); + + CloseableHttpResponse queryResponse = TestUtils.send( + "query/records", + "POST", + headersWithUsersDataRootAccess, + body.toString(), + "" + ); + + DummyRecordsHelper.RecordsMock responseObject = new DummyRecordsHelper().getRecordsMockFromResponse( + queryResponse); + + assertEquals(HttpStatus.SC_OK, queryResponse.getCode()); + assertEquals(1, responseObject.records.length); + assertEquals(RECORD_ID, Stream.of(responseObject.records).findFirst().get().id); + } + + protected static String createDataGroup(Map<String, String> headersWithValidAccessToken) + throws Exception { + CloseableHttpResponse entitlementsGroup = EntitlementsUtil.createEntitlementsGroup( + headersWithValidAccessToken, + DATA_GROUP_ID, + GROUP_DESCRIPTION + ); + assertTrue(entitlementsGroup.getEntity().getContentType().contains("application/json")); + String json = EntityUtils.toString(entitlementsGroup.getEntity()); + Gson gson = new Gson(); + JsonObject groupEntity = gson.fromJson(json, JsonObject.class); + return groupEntity.get("email").getAsString(); + } + + protected static void deleteDataGroup(Map<String, String> headersWithValidAccessToken, + String groupEmail) throws Exception { + EntitlementsUtil.deleteEntitlementsGroup(headersWithValidAccessToken, groupEmail); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/DeleteRecordLogicallyAndItsVersionsTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/DeleteRecordLogicallyAndItsVersionsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b225c8049b2000b51b19a332a0579f7344b3acee --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/DeleteRecordLogicallyAndItsVersionsTest.java @@ -0,0 +1,90 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class DeleteRecordLogicallyAndItsVersionsTest extends TestBase { + + private static final Long NOW = System.currentTimeMillis(); + private static final String LEGAL_TAG = LegalTagUtils.createRandomName(); + + private static final String KIND = TenantUtils.getTenantName() + ":test:endtoend:1.1." + + NOW; + private static final String RECORD_ID = TenantUtils.getTenantName() + ":endtoend:1.1." + + NOW; + + @BeforeEach + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + LegalTagUtils.create(LEGAL_TAG, testUtils.getToken()); + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createJsonRecordWithData(RECORD_ID, KIND, LEGAL_TAG, "v1"), ""); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + } + + @AfterEach + public void tearDown() throws Exception { + TestUtils.send("records/" + RECORD_ID, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + LegalTagUtils.delete(LEGAL_TAG, testUtils.getToken()); + this.testUtils = null; + } + + @Test + public void should_deleteRecordAndAllVersionsLogically_when_userIsAuthorized() throws Exception { + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createJsonRecordWithData(RECORD_ID, KIND, LEGAL_TAG, "v2"), ""); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + + CloseableHttpResponse versionResponse = TestUtils.send("records/versions/" + RECORD_ID, "GET", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_OK, versionResponse.getCode()); + + String versions = TestUtils.getResult(versionResponse, HttpStatus.SC_OK, String.class); + JsonObject content = new JsonParser().parse(versions).getAsJsonObject(); + JsonArray versionArray = content.get("versions").getAsJsonArray(); + + String versionOne = versionArray.get(0).toString(); + String versionTwo = versionArray.get(1).toString(); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + "{'anything':'anything'}", RECORD_ID + ":delete"); + + assertEquals(HttpStatus.SC_NO_CONTENT, deleteResponse.getCode()); + + response = TestUtils.send("records/" + RECORD_ID + "/" + versionOne, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + + response = TestUtils.send("records/" + RECORD_ID + "/" + versionTwo, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/GetRecordsIntegrationTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/GetRecordsIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9b297384570495471d6ce8008eb89406d672482d --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/GetRecordsIntegrationTest.java @@ -0,0 +1,197 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class GetRecordsIntegrationTest extends TestBase { + private static final String RECORD_ID = TenantUtils.getTenantName() + ":getrecord:" + System.currentTimeMillis(); + private static final String ANOTHER_RECORD_ID = TenantUtils.getTenantName() + ":getrecordnodup:" + System.currentTimeMillis(); + + private static final String KIND = TenantUtils.getTenantName() + ":ds:getrecord:1.0." + + System.currentTimeMillis(); + + private static String LEGAL_TAG_NAME_A; + private static String LEGAL_TAG_NAME_B; + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + GetRecordsIntegrationTest.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + GetRecordsIntegrationTest.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LEGAL_TAG_NAME_A = LegalTagUtils.createRandomName(); + Thread.sleep(100); + LEGAL_TAG_NAME_B = LegalTagUtils.createRandomName(); + + LegalTagUtils.create(LEGAL_TAG_NAME_A, token); + LegalTagUtils.create(LEGAL_TAG_NAME_B, token); + + String jsonInput = RecordUtil.createDefaultJsonRecord(RECORD_ID, KIND, LEGAL_TAG_NAME_A); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + assertTrue(response.getEntity().getContentType().contains("application/json")); + } + + public static void classTearDown(String token) throws Exception { + TestUtils.send("records/" + RECORD_ID, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + LegalTagUtils.delete(LEGAL_TAG_NAME_A, token); + Thread.sleep(100); + LegalTagUtils.delete(LEGAL_TAG_NAME_B, token); + } + + @Test + public void should_getRecord_when_validRecordIdIsProvided() throws Exception { + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + JsonObject json = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + JsonObject dataJson = json.get("data").getAsJsonObject(); + JsonObject acl = json.get("acl").getAsJsonObject(); + + assertEquals(RECORD_ID, json.get("id").getAsString()); + assertEquals(KIND, json.get("kind").getAsString()); + assertEquals(TestUtils.getAcl(), acl.get("owners").getAsString()); + assertEquals(TestUtils.getAcl(), acl.get("viewers").getAsString()); + + assertEquals("58377304471659395", dataJson.get("int-tag").getAsJsonObject().get("score-int").toString()); + assertEquals("5.837730447165939E7", + dataJson.get("double-tag").getAsJsonObject().get("score-double").toString()); + assertEquals("123456789", dataJson.get("count").toString()); + } + + @Test + public void should_getRecord_withoutDuplicates_when_duplicateAclAndLegaltagsAreProvided() throws Exception { + String jsonInputWithDuplicates = RecordUtil.createRecordWithDuplicateAclAndLegaltags(ANOTHER_RECORD_ID, KIND, LEGAL_TAG_NAME_A); + CloseableHttpResponse putResponse = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInputWithDuplicates, ""); + assertEquals(HttpStatus.SC_CREATED, putResponse.getCode()); + assertTrue(putResponse.getEntity().getContentType().contains("application/json")); + + CloseableHttpResponse response = TestUtils.send("records/" + ANOTHER_RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + JsonObject json = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + JsonObject acl = json.get("acl").getAsJsonObject(); + JsonObject legal = json.get("legal").getAsJsonObject(); + + assertEquals(ANOTHER_RECORD_ID, json.get("id").getAsString()); + assertEquals(KIND, json.get("kind").getAsString()); + assertEquals(LEGAL_TAG_NAME_A, legal.get("legaltags").getAsString()); + assertEquals(TestUtils.getAcl(), acl.get("owners").getAsString()); + assertEquals(TestUtils.getAcl(), acl.get("viewers").getAsString()); + } + + @Test + public void should_getOnlyTheCertainDataFields_when_attributesAreProvided() throws Exception { + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", + "?attribute=data.count&attribute=data.int-tag.score-int"); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + JsonObject json = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + JsonObject dataJson = json.get("data").getAsJsonObject(); + JsonObject acl = json.get("acl").getAsJsonObject(); + + assertEquals(RECORD_ID, json.get("id").getAsString()); + assertEquals(KIND, json.get("kind").getAsString()); + assertEquals(TestUtils.getAcl(), acl.get("owners").getAsString()); + assertEquals(TestUtils.getAcl(), acl.get("viewers").getAsString()); + + assertEquals("58377304471659395", dataJson.get("int-tag.score-int").getAsString()); + assertNull(dataJson.get("double-tag")); + assertEquals("123456789", dataJson.get("count").toString()); + } + + @Test + public void should_notReturnFieldsAlreadyInDatastore_when_returningRecord() throws Exception { + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + JsonObject json = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + + assertNotNull(json.get("id")); + assertNotNull(json.get("kind")); + assertNotNull(json.get("acl")); + assertNotNull(json.get("version")); + assertNotNull(json.get("data")); + assertNotNull(json.get("createTime")); + + assertNull(json.get("bucket")); + assertNull(json.get("status")); + assertNull(json.get("modifyUser")); + assertNull(json.get("modifyTime")); + } + + @Test + public void should_legaltagChange_when_updateRecordWithLegaltag() throws Exception { + String newJsonInput = RecordUtil.createDefaultJsonRecord(RECORD_ID, KIND, LEGAL_TAG_NAME_B); + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), newJsonInput, "?skipdupes=false"); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + + response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + JsonObject json = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + + assertEquals(RECORD_ID, json.get("id").getAsString()); + assertEquals(KIND, json.get("kind").getAsString()); + + JsonArray legaltags = json.get("legal").getAsJsonObject().get("legaltags").getAsJsonArray(); + String updatedLegaltag = legaltags.get(0).getAsString(); + assertEquals(1, legaltags.size()); + assertEquals(LEGAL_TAG_NAME_B, updatedLegaltag); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/IngestRecordNotFoundTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/IngestRecordNotFoundTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c4c24b3d550317524a35cfcc8327f9f7cd2bf0b4 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/IngestRecordNotFoundTest.java @@ -0,0 +1,91 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class IngestRecordNotFoundTest extends TestBase { + + private static final long NOW = System.currentTimeMillis(); + private static final String LEGAL_TAG = LegalTagUtils.createRandomName(); + + private static final String KIND = TenantUtils.getTenantName() + ":test:endtoend:1.1." + NOW; + private static final String RECORD_ID = TenantUtils.getTenantName() + ":endtoend:1.1." + NOW; + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG, token); + } + + public static void classTearDown(String token) throws Exception { + LegalTagUtils.delete(LEGAL_TAG, token); + } + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + IngestRecordNotFoundTest.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + IngestRecordNotFoundTest.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + @Test + public void should_returnBadRequest_when_userGroupDoesNotExist() throws Exception { + + String group = String.format("data.thisDataGrpDoesNotExsist@%s", TestUtils.getAclSuffix()); + + String record = RecordUtil.createDefaultJsonRecord(RECORD_ID, KIND, LEGAL_TAG).replace(TestUtils.getAcl(), group); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), record, ""); + + String result = TestUtils.getResult(response, HttpStatus.SC_BAD_REQUEST, String.class); + JsonObject jsonResponse = JsonParser.parseString(result).getAsJsonObject(); + assertEquals("Error on writing record", jsonResponse.get("reason").getAsString()); + assertEquals("Could not find group \"" + group + "\".", + jsonResponse.get("message").getAsString()); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/LogicalBatchRecordsDeleteTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/LogicalBatchRecordsDeleteTests.java new file mode 100644 index 0000000000000000000000000000000000000000..c74ef227e102b27d41319d656bc596fa429cf46f --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/LogicalBatchRecordsDeleteTests.java @@ -0,0 +1,165 @@ +// Copyright 2017-2021, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.apache.http.HttpStatus.SC_MULTI_STATUS; +import static org.apache.http.HttpStatus.SC_NOT_FOUND; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.util.List; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class LogicalBatchRecordsDeleteTests extends TestBase { + + private static final long NOW = System.currentTimeMillis(); + private static final String KIND = TenantUtils.getTenantName() + ":delete:inttest:1.0." + NOW; + private static final String LEGAL_TAG = LegalTagUtils.createRandomName(); + private static final String RECORD_ID_1 = TenantUtils.getTenantName() + ":testint:" + NOW; + private static final String RECORD_ID_2 = TenantUtils.getTenantName() + ":testint:" + NOW; + private static final String NOT_EXISTED_RECORD_ID = TenantUtils.getFirstTenantName() + ":notexisted:" + NOW; + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + this.setup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.tearDown(TOKEN_TEST_UTILS.getToken()); + this.testUtils = null; + } + + @Test + public void should_deleteRecordsLogically_successfully() throws Exception { + String requestBody = String.format("[\"%s\",\"%s\"]", RECORD_ID_1, RECORD_ID_2); + + CloseableHttpResponse response = TestUtils.send("records/delete", "POST", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), requestBody, EMPTY); + assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); + + response = TestUtils.send("records/" + RECORD_ID_1, "GET", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + + response = TestUtils.send("records/" + RECORD_ID_2, "GET", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + } + + @Test + public void should_deleteRecordsLogically_withPartialSuccess_whenOneRecordNotFound() throws Exception { + String requestBody = String.format("[\"%s\",\"%s\",\"%s\"]", RECORD_ID_1, RECORD_ID_2, NOT_EXISTED_RECORD_ID); + + CloseableHttpResponse deleteResponse = TestUtils.send("records/delete", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + requestBody, EMPTY); + + assertEquals(SC_MULTI_STATUS, deleteResponse.getCode()); + + JsonArray jsonBody = JsonParser.parseString(EntityUtils.toString(deleteResponse.getEntity())).getAsJsonArray(); + + assertEquals(1, jsonBody.size()); + assertEquals(getValueFromDeleteResponseJsonArray(jsonBody, "notDeletedRecordId"), NOT_EXISTED_RECORD_ID); + assertEquals(getValueFromDeleteResponseJsonArray(jsonBody, "message"), "Record with id '" + NOT_EXISTED_RECORD_ID + "' not found"); + + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID_1, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(SC_NOT_FOUND, response.getCode()); + + response = TestUtils.send("records/" + RECORD_ID_2, "GET",HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(SC_NOT_FOUND, response.getCode()); + } + + public void setup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG, token); + + String firstBody = createBody(RECORD_ID_1, "anything", Lists.newArrayList(LEGAL_TAG), Lists.newArrayList("BR", "IT")); + String secondBody = createBody(RECORD_ID_2, "anything", Lists.newArrayList(LEGAL_TAG), Lists.newArrayList("BR", "IT")); + + CloseableHttpResponse firstResponse = TestUtils.send("records", "PUT", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), firstBody, ""); + CloseableHttpResponse secondResponse = TestUtils.send("records", "PUT", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), secondBody, ""); + + assertEquals(HttpStatus.SC_CREATED, firstResponse.getCode()); + assertEquals(HttpStatus.SC_CREATED, secondResponse.getCode()); + } + + public void tearDown(String token) throws Exception { + TestUtils.send("records/" + RECORD_ID_1, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID_2, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + + LegalTagUtils.delete(LEGAL_TAG, token); + } + + protected static String createBody(String id, String dataValue, List<String> legalTags, List<String> ordc) { + JsonObject data = new JsonObject(); + data.addProperty("name", dataValue); + + JsonObject acl = new JsonObject(); + JsonArray acls = new JsonArray(); + acls.add(TestUtils.getAcl()); + acl.add("viewers", acls); + acl.add("owners", acls); + + JsonArray tags = new JsonArray(); + legalTags.forEach(t -> tags.add(t)); + + JsonArray ordcJson = new JsonArray(); + ordc.forEach(o -> ordcJson.add(o)); + + JsonObject legal = new JsonObject(); + legal.add("legaltags", tags); + legal.add("otherRelevantDataCountries", ordcJson); + + JsonObject record = new JsonObject(); + record.addProperty("id", id); + record.addProperty("kind", KIND); + record.add("acl", acl); + record.add("legal", legal); + record.add("data", data); + + JsonArray records = new JsonArray(); + records.add(record); + + return records.toString(); + } + + private String getValueFromDeleteResponseJsonArray(JsonArray jsonBody, String propertyName) { + if(jsonBody.isEmpty()){ + throw new RuntimeException("Not able to fetch property: %s from response body. Response body is empty.".formatted(propertyName)); + } + return jsonBody.get(0).getAsJsonObject().get(propertyName).getAsString(); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/LogicalRecordDeleteTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/LogicalRecordDeleteTests.java new file mode 100644 index 0000000000000000000000000000000000000000..0075fd559aeec9562deb2c054450f99377eca36e --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/LogicalRecordDeleteTests.java @@ -0,0 +1,151 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.common.collect.Lists; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import java.util.List; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.DummyRecordsHelper.CreateRecordResponse; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class LogicalRecordDeleteTests extends TestBase { + + private static final long NOW = System.currentTimeMillis(); + private static final String KIND = TenantUtils.getTenantName() + ":delete:inttest:1.0." + NOW; + private static final String LEGAL_TAG = LegalTagUtils.createRandomName(); + private static final String RECORD_ID = TenantUtils.getTenantName() + ":inttest:" + NOW; + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + LogicalRecordDeleteTests.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + LogicalRecordDeleteTests.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG, token); + + String body = createBody(RECORD_ID, "anything", Lists.newArrayList(LEGAL_TAG), Lists.newArrayList("BR", "IT")); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), body, ""); + + String responseBody = EntityUtils.toString(response.getEntity()); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + assertTrue(response.getEntity().getContentType().contains("application/json")); + + Gson gson = new Gson(); + CreateRecordResponse result = gson.fromJson(responseBody, CreateRecordResponse.class); + + assertEquals(1, result.recordCount); + assertEquals(1, result.recordIds.length); + assertEquals(1, result.recordIdVersions.length); + assertEquals(RECORD_ID, result.recordIds[0]); + } + + public static void classTearDown(String token) throws Exception { + TestUtils.send("records/" + RECORD_ID, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + + LegalTagUtils.delete(LEGAL_TAG, token); + } + + @Test + public void should_notRetrieveRecord_and_notDeleteRecordAgain_when_deletingItLogically() throws Exception { + String queryParam = String.format("records/%s:delete", RECORD_ID); + + // deleting + CloseableHttpResponse response = TestUtils.send(queryParam, "POST", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "{'anything':'teste'}", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); + + // trying to get + response = TestUtils.send("records/" + RECORD_ID, "GET", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + + // trying to delete again + response = TestUtils.send(queryParam, "POST", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "{'anything':'teste'}", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + } + + protected static String createBody(String id, String dataValue, List<String> legalTags, List<String> ordc) { + JsonObject data = new JsonObject(); + data.addProperty("name", dataValue); + + JsonObject acl = new JsonObject(); + JsonArray acls = new JsonArray(); + acls.add(TestUtils.getAcl()); + acl.add("viewers", acls); + acl.add("owners", acls); + + JsonArray tags = new JsonArray(); + legalTags.forEach(t -> tags.add(t)); + + JsonArray ordcJson = new JsonArray(); + ordc.forEach(o -> ordcJson.add(o)); + + JsonObject legal = new JsonObject(); + legal.add("legaltags", tags); + legal.add("otherRelevantDataCountries", ordcJson); + + JsonObject record = new JsonObject(); + record.addProperty("id", id); + record.addProperty("kind", KIND); + record.add("acl", acl); + record.add("legal", legal); + record.add("data", data); + + JsonArray records = new JsonArray(); + records.add(record); + + return records.toString(); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/ParentRecordValidationTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/ParentRecordValidationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..95ddb5cdfba7e2c0ec1755f0bc8cbe60ddf42954 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/ParentRecordValidationTest.java @@ -0,0 +1,105 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import java.util.Arrays; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class ParentRecordValidationTest extends TestBase { + + private static long NOW = System.currentTimeMillis(); + private static String LEGAL_TAG = LegalTagUtils.createRandomName(); + private static String KIND = TenantUtils.getFirstTenantName() + ":bulkupdate:test:1.1." + NOW; + private static String RECORD_ID = TenantUtils.getFirstTenantName() + ":test:1.1." + NOW; + private static String RECORD_ID_2 = TenantUtils.getFirstTenantName() + ":test:1.2." + NOW; + private static String RECORD_ID_3 = TenantUtils.getFirstTenantName() + ":test:1.3." + NOW; + + @BeforeEach + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + LegalTagUtils.create(LEGAL_TAG, testUtils.getToken()); + } + + @AfterEach + public void tearDown() throws Exception { + LegalTagUtils.delete(LEGAL_TAG, testUtils.getToken()); + for (String record_id : Arrays.asList(RECORD_ID, RECORD_ID_2, RECORD_ID_3)) + { + TestUtils.send( + "records/" + record_id, + "DELETE", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + "", + "" + ); + } + this.testUtils = null; + } + + @Test + public void shouldReturn200_whenRecordContainsValidAncestry() throws Exception { + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createDefaultJsonRecord(RECORD_ID, KIND, LEGAL_TAG), ""); + + String responseString = EntityUtils.toString(response.getEntity()); + String parentIdWithVersion = JsonParser + .parseString(responseString) + .getAsJsonObject() + .get("recordIdVersions") + .getAsJsonArray() + .get(0).getAsString(); + + CloseableHttpResponse response2 = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createDefaultJsonRecordWithParentId(RECORD_ID_2, KIND, LEGAL_TAG, parentIdWithVersion), ""); + + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + assertEquals(HttpStatus.SC_CREATED, response2.getCode()); + } + + @Test + public void shouldReturn404_whenRecordAncestryNotExisted() throws Exception { + + String parentIdWithVersion = "opendes:test:1.1.1000000000000:1000000000000000"; + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createDefaultJsonRecordWithParentId(RECORD_ID_3, KIND, LEGAL_TAG, parentIdWithVersion), ""); + + + String expectedErrorMessage = "The record 'RecordIdWithVersion(recordId=opendes:test:1.1.1000000000000, recordVersion=1000000000000000)' was not found"; + String actualErrorMessage = JsonParser + .parseString(EntityUtils.toString(response.getEntity())) + .getAsJsonObject() + .get("message") + .getAsString(); + + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + assertEquals(expectedErrorMessage, actualErrorMessage); + } +} + diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/PatchRecordsTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/PatchRecordsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..293ff6b273a904f5fef56ca68a2f8436f14ba892 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/PatchRecordsTest.java @@ -0,0 +1,330 @@ +// Copyright 2017-2023, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class PatchRecordsTest extends TestBase { + + private static long NOW = System.currentTimeMillis(); + private static String LEGAL_TAG = LegalTagUtils.createRandomName(); + private static String LEGAL_TAG_TO_BE_PATCHED = LegalTagUtils.createRandomName() + "1"; + private static String KIND = TenantUtils.getFirstTenantName() + ":bulkupdate:test:1.1." + NOW; + private static String KIND_TO_BE_PATCHED = TenantUtils.getFirstTenantName() + ":bulkupdate:test:1.2." + NOW; + private static String RECORD_ID1 = TenantUtils.getFirstTenantName() + ":test:1.1." + NOW; + private static String RECORD_ID2 = TenantUtils.getFirstTenantName() + ":test:1.2." + NOW; + private static final int MAX_OP_NUMBER = 100; + + private static final DummyRecordsHelper RECORDS_HELPER = new DummyRecordsHelper(); + + @BeforeEach + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + LegalTagUtils.create(LEGAL_TAG, testUtils.getToken()); + LegalTagUtils.create(LEGAL_TAG_TO_BE_PATCHED, testUtils.getToken()); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createDefaultJsonRecord(RECORD_ID1, KIND, LEGAL_TAG), ""); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + + response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createDefaultJsonRecord(RECORD_ID2, KIND, LEGAL_TAG), ""); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + } + + @AfterEach + public void tearDown() throws Exception { + LegalTagUtils.delete(LEGAL_TAG, testUtils.getToken()); + LegalTagUtils.delete(LEGAL_TAG_TO_BE_PATCHED, testUtils.getToken()); + TestUtils.send("records/" + RECORD_ID1, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID2, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + this.testUtils = null; + } + + @Test + public void should_updateOnlyMetadata_whenOnlyMetadataIsPatched() throws Exception { + List<String> records = new ArrayList<>(); + records.add(RECORD_ID1); + records.add(RECORD_ID2); + CloseableHttpResponse queryResponse = queryRecordsResponse(records); + assertEquals(HttpStatus.SC_OK, queryResponse.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock queryResponseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse); + assertQueryResponse(queryResponseObject, 2); + String currentVersionRecord1 = queryResponseObject.records[0].version; + String currentVersionRecord2 = queryResponseObject.records[1].version; + assertEquals(null, queryResponseObject.records[0].modifyTime); + assertEquals(null, queryResponseObject.records[0].modifyUser); + + CloseableHttpResponse patchResponse = TestUtils.sendWithCustomMediaType("records", "PATCH", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "application/json-patch+json", getPatchPayload(records, true, false), ""); + assertEquals(HttpStatus.SC_OK, patchResponse.getCode()); + + queryResponse = queryRecordsResponse(records); + assertEquals(HttpStatus.SC_OK, queryResponse.getCode()); + + queryResponseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse); + //modifyUser and modifyTime are not reflected appropriately, please refer to this issue https://community.opengroup.org/osdu/platform/system/storage/-/issues/171 + assertEquals(currentVersionRecord1, queryResponseObject.records[0].version); + assertEquals(currentVersionRecord2, queryResponseObject.records[1].version); + assertEquals(2, queryResponseObject.records.length); + assertEquals(KIND_TO_BE_PATCHED, queryResponseObject.records[0].kind); + assertEquals(KIND_TO_BE_PATCHED, queryResponseObject.records[1].kind); + assertEquals(TestUtils.getAcl(), queryResponseObject.records[0].acl.viewers[0]); + assertEquals(TestUtils.getAcl(), queryResponseObject.records[1].acl.viewers[0]); + assertEquals(TestUtils.getIntegrationTesterAcl(), queryResponseObject.records[0].acl.owners[0]); + assertEquals(TestUtils.getIntegrationTesterAcl(), queryResponseObject.records[1].acl.owners[0]); + assertTrue(Arrays.stream(queryResponseObject.records[0].legal.legaltags).anyMatch(LEGAL_TAG::equals)); + assertTrue(Arrays.stream(queryResponseObject.records[1].legal.legaltags).anyMatch(LEGAL_TAG::equals)); + assertTrue(Arrays.stream(queryResponseObject.records[0].legal.legaltags).anyMatch(LEGAL_TAG_TO_BE_PATCHED::equals)); + assertTrue(Arrays.stream(queryResponseObject.records[1].legal.legaltags).anyMatch(LEGAL_TAG_TO_BE_PATCHED::equals)); + Map<String, String> tags = queryResponseObject.records[0].tags; + assertTrue(tags.containsKey("tag1")); + assertTrue(tags.containsKey("tag2")); + assertEquals("value1", tags.get("tag1")); + assertEquals("value2", tags.get("tag2")); + tags = queryResponseObject.records[1].tags; + assertTrue(tags.containsKey("tag1")); + assertTrue(tags.containsKey("tag2")); + assertEquals("value1", tags.get("tag1")); + assertEquals("value2", tags.get("tag2")); + } + + @Test + public void should_updateDataAndMetadataVersion_whenOnlyDataIsPatched() throws Exception { + List<String> records = new ArrayList<>(); + records.add(RECORD_ID1); + CloseableHttpResponse queryResponse = queryRecordsResponse(records); + assertEquals(HttpStatus.SC_OK, queryResponse.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock queryResponseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse); + assertQueryResponse(queryResponseObject, 1); + String currentVersionRecord1 = queryResponseObject.records[0].version; + + CloseableHttpResponse patchResponse = TestUtils.sendWithCustomMediaType("records", "PATCH", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "application/json-patch+json", getPatchPayload(records, false, true), ""); + assertEquals(HttpStatus.SC_OK, patchResponse.getCode()); + + queryResponse = queryRecordsResponse(records); + assertEquals(HttpStatus.SC_OK, queryResponse.getCode()); + + queryResponseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse); + assertNotEquals(currentVersionRecord1, queryResponseObject.records[0].version); + assertEquals(KIND, queryResponseObject.records[0].kind); + assertTrue(queryResponseObject.records[0].data.containsKey("data")); + assertTrue(queryResponseObject.records[0].data.get("data").toString().equals("{message=test data}")); + assertQueryResponse(queryResponseObject, 1); + } + + @Test + public void should_updateBothMetadataAndData_whenDataAndMetadataArePatched() throws Exception { + List<String> records = new ArrayList<>(); + records.add(RECORD_ID1); + CloseableHttpResponse queryResponse = queryRecordsResponse(records); + assertEquals(HttpStatus.SC_OK, queryResponse.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock queryResponseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse); + assertQueryResponse(queryResponseObject, 1); + String currentVersionRecord = queryResponseObject.records[0].version; + + CloseableHttpResponse patchResponse = TestUtils.sendWithCustomMediaType("records", "PATCH", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "application/json-patch+json", getPatchPayload(records, true, true), ""); + assertEquals(HttpStatus.SC_OK, patchResponse.getCode()); + + queryResponse = queryRecordsResponse(records); + assertEquals(HttpStatus.SC_OK, queryResponse.getCode()); + + queryResponseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse); + assertEquals(1, queryResponseObject.records.length); + assertNotEquals(currentVersionRecord, queryResponseObject.records[0].version); + assertEquals(KIND_TO_BE_PATCHED, queryResponseObject.records[0].kind); + assertEquals(TestUtils.getAcl(), queryResponseObject.records[0].acl.viewers[0]); + assertEquals(TestUtils.getIntegrationTesterAcl(), queryResponseObject.records[0].acl.owners[0]); + assertTrue(Arrays.stream(queryResponseObject.records[0].legal.legaltags).anyMatch(LEGAL_TAG::equals)); + assertTrue(Arrays.stream(queryResponseObject.records[0].legal.legaltags).anyMatch(LEGAL_TAG_TO_BE_PATCHED::equals)); + Map<String, String> tags = queryResponseObject.records[0].tags; + assertTrue(tags.containsKey("tag1")); + assertTrue(tags.containsKey("tag2")); + assertEquals("value1", tags.get("tag1")); + assertEquals("value2", tags.get("tag2")); + assertTrue(queryResponseObject.records[0].data.containsKey("data")); + assertTrue(queryResponseObject.records[0].data.get("data").toString().equals("{message=test data}")); + + } + + @Test + public void should_update_whenNumberOfPatchOperationsIsMaximum() throws Exception { + List<String> records = new ArrayList<>(); + records.add(RECORD_ID1); + CloseableHttpResponse queryResponse = queryRecordsResponse(records); + assertEquals(HttpStatus.SC_OK, queryResponse.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock queryResponseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse); + assertQueryResponse(queryResponseObject, 1); + String currentVersionRecord = queryResponseObject.records[0].version; + + CloseableHttpResponse patchResponse = TestUtils.sendWithCustomMediaType("records", "PATCH", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "application/json-patch+json", getMaximumPatchOperationsPayload(records), ""); + assertEquals(HttpStatus.SC_OK, patchResponse.getCode()); + + queryResponse = queryRecordsResponse(records); + assertEquals(HttpStatus.SC_OK, queryResponse.getCode()); + + queryResponseObject = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse); + assertEquals(1, queryResponseObject.records.length); + assertEquals(currentVersionRecord, queryResponseObject.records[0].version); + Map<String, String> tags = queryResponseObject.records[0].tags; + assertTrue(tags.containsKey("testTag0")); + assertTrue(tags.containsKey("testTag99")); + assertEquals("value0", tags.get("testTag0")); + assertEquals("value99", tags.get("testTag99")); + } + + //TODO: add a test to validate same 'op' and 'path' and assert expected behavior + + private CloseableHttpResponse queryRecordsResponse(List<String> recordIds) throws Exception { + JsonArray records = new JsonArray(); + for (String recordId : recordIds) { + records.add(recordId); + } + JsonObject queryBody = new JsonObject(); + queryBody.add("records", records); + + Map<String, String> queryHeader = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + queryHeader.put("frame-of-reference", "none"); + return TestUtils.send("query/records:batch", "POST", queryHeader, queryBody.toString(), ""); + } + + private void assertQueryResponse(DummyRecordsHelper.ConvertedRecordsMock queryResponse, int expectedRecordCount) { + assertEquals(expectedRecordCount, queryResponse.records.length); + assertEquals(TestUtils.getAcl(), queryResponse.records[0].acl.viewers[0]); + assertEquals(TestUtils.getAcl(), queryResponse.records[0].acl.owners[0]); + } + + private String getPatchPayload(List<String> records, boolean isMetaUpdate, boolean isDataUpdate) { + JsonArray recordsJson = new JsonArray(); + for (String record : records) { + recordsJson.add(record); + } + + JsonArray ops = new JsonArray(); + if (isMetaUpdate) { + ops.add(getAddTagsPatchOp()); + ops.add(getReplaceAclOwnersPatchOp()); + ops.add(getAddLegaltagsPatchOp()); + ops.add(getReplaceKindPatchOp()); + } + if (isDataUpdate) { + ops.add(getReplaceDataPatchOp()); + } + + return getPatchrequestBody(recordsJson, ops); + } + + private JsonObject getAddTagsPatchOp() { + JsonObject tagsValue = new JsonObject(); + tagsValue.addProperty("tag1", "value1"); + tagsValue.addProperty("tag2", "value2"); + JsonObject addTagsPatch = new JsonObject(); + addTagsPatch.addProperty("op", "add"); + addTagsPatch.addProperty("path", "/tags"); + addTagsPatch.add("value", tagsValue); + return addTagsPatch; + } + + private JsonObject getReplaceAclOwnersPatchOp() { + JsonArray newAclValue = new JsonArray(); + newAclValue.add(TestUtils.getIntegrationTesterAcl()); + JsonObject replaceAclPatch = new JsonObject(); + replaceAclPatch.addProperty("op", "replace"); + replaceAclPatch.addProperty("path", "/acl/owners"); + replaceAclPatch.add("value", newAclValue); + return replaceAclPatch; + } + + private JsonObject getAddLegaltagsPatchOp() { + JsonObject replaceAclPatch = new JsonObject(); + replaceAclPatch.addProperty("op", "add"); + replaceAclPatch.addProperty("path", "/legal/legaltags/-"); + replaceAclPatch.addProperty("value", LEGAL_TAG_TO_BE_PATCHED); + return replaceAclPatch; + } + + private JsonObject getReplaceKindPatchOp() { + JsonObject replaeKindPatch = new JsonObject(); + replaeKindPatch.addProperty("op", "replace"); + replaeKindPatch.addProperty("path", "/kind"); + replaeKindPatch.addProperty("value", KIND_TO_BE_PATCHED); + return replaeKindPatch; + } + + private JsonObject getReplaceDataPatchOp() { + JsonObject newDataValue = new JsonObject(); + JsonObject innerDataValue = new JsonObject(); + innerDataValue.addProperty("message", "test data"); + newDataValue.add("data", innerDataValue); + JsonObject replaceDataPatch = new JsonObject(); + replaceDataPatch.addProperty("op", "replace"); + replaceDataPatch.addProperty("path", "/data"); + replaceDataPatch.add("value", newDataValue); + return replaceDataPatch; + } + + private String getMaximumPatchOperationsPayload(List<String> records) { + JsonArray recordsJson = new JsonArray(); + for (String record : records) { + recordsJson.add(record); + } + JsonArray ops = new JsonArray(); + for (int i = 0; i < MAX_OP_NUMBER; i++) { + JsonObject addTagOperation = new JsonObject(); + addTagOperation.addProperty("op", "add"); + addTagOperation.addProperty("path", "/tags/testTag" + i); + addTagOperation.addProperty("value", "value" + i); + ops.add(addTagOperation); + } + return getPatchrequestBody(recordsJson, ops); + } + + private String getPatchrequestBody(JsonArray recordsJson, JsonArray ops) { + JsonObject query = new JsonObject(); + query.add("ids", recordsJson); + + JsonObject updateBody = new JsonObject(); + updateBody.add("query", query); + updateBody.add("ops", ops); + + return updateBody.toString(); + } + +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/PurgeRecordsIntegrationTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/PurgeRecordsIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..569507eef6b6ccf4138252458986ee3322d1c589 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/PurgeRecordsIntegrationTest.java @@ -0,0 +1,253 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class PurgeRecordsIntegrationTest extends TestBase { + + private static final long NOW = System.currentTimeMillis(); + private static final String RECORD_ID = TenantUtils.getTenantName() + ":getrecord:" + NOW; + private static final String RECORD_ID1 = TenantUtils.getTenantName() + ":getrecord1:" + NOW; + private static final String RECORD_ID2 = TenantUtils.getTenantName() + ":getrecord2:" + NOW; + private static final String RECORD_ID3 = TenantUtils.getTenantName() + ":getrecord3:" + NOW; + private static final String RECORD_ID4 = TenantUtils.getTenantName() + ":getrecord4:" + NOW; + private static final String KIND = TenantUtils.getTenantName() + ":ds:getrecord:1.0." + NOW; + private static final String LEGAL_TAG = LegalTagUtils.createRandomName(); + public static final String HTTP_DELETE = "DELETE"; + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + PurgeRecordsIntegrationTest.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + PurgeRecordsIntegrationTest.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG, token); + String jsonInput = RecordUtil.createDefaultJsonRecord(RECORD_ID, KIND, LEGAL_TAG); + String jsonInput1 = RecordUtil.createDefaultJsonRecord(RECORD_ID1, KIND, LEGAL_TAG); + String jsonInput2 = RecordUtil.createDefaultJsonRecord(RECORD_ID2, KIND, LEGAL_TAG); + String jsonInput3 = RecordUtil.createDefaultJsonRecord(RECORD_ID3, KIND, LEGAL_TAG); + String jsonInput4 = RecordUtil.createDefaultJsonRecord(RECORD_ID4, KIND, LEGAL_TAG); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), jsonInput, ""); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + assertTrue(response.getEntity().getContentType().contains("application/json")); + + for(int i=0; i < 4; i++) { + // Create 4 record versions - for limit scenario + CloseableHttpResponse response1 = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), jsonInput1, ""); + assertEquals(HttpStatus.SC_CREATED, response1.getCode()); + assertTrue(response1.getEntity().getContentType().contains("application/json")); + + // Create 4 record versions - for versionIds scenario + CloseableHttpResponse response2 = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), jsonInput2, ""); + assertEquals(HttpStatus.SC_CREATED, response2.getCode()); + assertTrue(response2.getEntity().getContentType().contains("application/json")); + + // Create 4 record versions - for fromVersion scenario + CloseableHttpResponse response3 = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), jsonInput3, ""); + assertEquals(HttpStatus.SC_CREATED, response3.getCode()); + assertTrue(response3.getEntity().getContentType().contains("application/json")); + + // Create 4 record versions - for bad requests scenario + CloseableHttpResponse response4 = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), jsonInput4, ""); + assertEquals(HttpStatus.SC_CREATED, response4.getCode()); + assertTrue(response4.getEntity().getContentType().contains("application/json")); + + } + } + + public static void classTearDown(String token) throws Exception { + TestUtils.send("records/" + RECORD_ID1, HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID2, HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID3, HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID4, HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + + LegalTagUtils.delete(LEGAL_TAG, token); + } + + @Test + public void should_ReturnHttp204_when_purgingRecordSuccessfully() throws Exception { + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID, HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); + + response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getCode()); + } + + @Test + public void shouldReturnHttp204_whenPurgeRecordVersions_byLimit_isSuccess() throws Exception { + String queryParams= "?limit=2"; + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID1 + "/versions", HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", queryParams); + assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); + + CloseableHttpResponse getVersionResponse = TestUtils.send("records/versions/" + RECORD_ID1, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + JsonObject json = JsonParser.parseString(EntityUtils.toString(getVersionResponse.getEntity())).getAsJsonObject(); + JsonArray versions = json.get("versions").getAsJsonArray(); + // Record version : 4, limit: 2, Deleted version paths: 2 + assertEquals(HttpStatus.SC_OK, getVersionResponse.getCode()); + assertEquals(RECORD_ID1, json.get("recordId").getAsString()); + assertEquals(2, versions.size()); + } + + @Test + public void shouldReturnHttp204_whenPurgeRecordVersions_byVersionIds_isSuccess() throws Exception { + CloseableHttpResponse getVersionResponse = TestUtils.send("records/versions/" + RECORD_ID2, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + JsonObject json = JsonParser.parseString(EntityUtils.toString(getVersionResponse.getEntity())).getAsJsonObject(); + JsonArray versions = json.get("versions").getAsJsonArray(); + String versionIds = versions.get(0).getAsString() + "," + versions.get(1).getAsString(); + + String queryParams = "?limit=2&versionIds="+versionIds; + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID2 + "/versions", HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", queryParams); + assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); + + CloseableHttpResponse getVersionResponse1 = TestUtils.send("records/versions/" + RECORD_ID2, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + JsonObject json1 = JsonParser.parseString(EntityUtils.toString(getVersionResponse1.getEntity())).getAsJsonObject(); + JsonArray versions1 = json1.get("versions").getAsJsonArray(); + // Record version : 4, versionIds to delete: 2, Deleted version paths: 2 + assertEquals(HttpStatus.SC_OK, getVersionResponse1.getCode()); + assertEquals(RECORD_ID2, json1.get("recordId").getAsString()); + assertEquals(2, versions1.size()); + } + + @Test + public void shouldReturnHttp204_whenPurgeRecordVersions_byFromVersion_isSuccess() throws Exception { + CloseableHttpResponse getVersionResponse = TestUtils.send("records/versions/" + RECORD_ID3, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + JsonObject json = JsonParser.parseString(EntityUtils.toString(getVersionResponse.getEntity())).getAsJsonObject(); + JsonArray versions = json.get("versions").getAsJsonArray(); + String fromVersion = versions.get(1).getAsString(); + + String queryParams = "?from="+fromVersion; + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID3 + "/versions", HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", queryParams); + assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); + + CloseableHttpResponse getVersionResponse1 = TestUtils.send("records/versions/" + RECORD_ID3, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + JsonObject json1 = JsonParser.parseString(EntityUtils.toString(getVersionResponse1.getEntity())).getAsJsonObject(); + JsonArray versions1 = json1.get("versions").getAsJsonArray(); + // Record version : 4, versionIds to delete: 2, Deleted version paths: 2 + assertEquals(HttpStatus.SC_OK, getVersionResponse1.getCode()); + assertEquals(RECORD_ID3, json1.get("recordId").getAsString()); + assertEquals(2, versions1.size()); + } + + @Test + public void shouldReturnHttp400BadRequest_whenPurgeRecordVersions_forInvalidVersionIds() throws Exception { + Long versionId1 = 404L; + Long versionId2 = 405L; + String invalidVersionIds = versionId1+","+versionId2; + String queryParams= "?versionIds="+invalidVersionIds; + + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID4 + "/versions", HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", queryParams); + JsonObject jsonObject = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + String errorMessage = String.format("Invalid Version Ids. The versionIds contains non existing version(s) '%s'", invalidVersionIds); + + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + assertEquals(HttpStatus.SC_BAD_REQUEST, jsonObject.get("code").getAsInt()); + assertEquals(errorMessage, jsonObject.get("message").getAsString()); + } + + @Test + public void shouldReturnHttp400BadRequest_whenPurgeRecordVersions_forLimitExceedsRecordVersions() throws Exception { + int totalVersions = 4; + int limitValue = 5; + String queryParams= "?limit="+limitValue; + + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID4 + "/versions", HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", queryParams); + JsonObject jsonObject = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + String errorMessage = String.format("The record '%s' version count (excluding latest version) is : %d , which is less than limit value : %d ", RECORD_ID4, totalVersions - 1, limitValue); + + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + assertEquals(HttpStatus.SC_BAD_REQUEST, jsonObject.get("code").getAsInt()); + assertEquals("Invalid limit.", jsonObject.get("reason").getAsString()); + assertEquals(errorMessage, jsonObject.get("message").getAsString()); + } + + @Test + public void shouldReturnHttp400BadRequest_whenPurgeRecordVersions_forInvalidFromVersion() throws Exception { + Long fromVersion = 404L; + String queryParams= "?from="+fromVersion; + + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID4 + "/versions", HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", queryParams); + JsonObject jsonObject = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + String errorMessage = String.format("Invalid 'from' version. The record version does not contains specified from version '%d'", fromVersion); + + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + assertEquals(HttpStatus.SC_BAD_REQUEST, jsonObject.get("code").getAsInt()); + assertEquals("Invalid 'from' version.", jsonObject.get("reason").getAsString()); + assertEquals(errorMessage, jsonObject.get("message").getAsString()); + } + + @Test + public void shouldReturnHttp400BadRequest_whenPurgeRecordVersions_forInvalidLimitAndValidFromVersion() throws Exception { + + CloseableHttpResponse getVersionResponse = TestUtils.send("records/versions/" + RECORD_ID4, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + JsonObject json = JsonParser.parseString(EntityUtils.toString(getVersionResponse.getEntity())).getAsJsonObject(); + JsonArray versions = json.get("versions").getAsJsonArray(); + Long fromVersion = versions.get(1).getAsLong(); + + int limitValue = 3; + String queryParams= "?limit="+limitValue+"&from="+fromVersion; + + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID4 + "/versions", HTTP_DELETE, HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", queryParams); + JsonObject jsonObject = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + String errorMessage = String.format("Invalid limit. Given limit count %d, exceeds the record versions count specified by the given 'from' version '%d'", limitValue, fromVersion); + + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + assertEquals(HttpStatus.SC_BAD_REQUEST, jsonObject.get("code").getAsInt()); + assertEquals("Invalid limit.", jsonObject.get("reason").getAsString()); + assertEquals(errorMessage, jsonObject.get("message").getAsString()); + } + + +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordAccessAuthorizationTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordAccessAuthorizationTests.java new file mode 100644 index 0000000000000000000000000000000000000000..0fc37ad74fdcd92f1ef4886c80b7699d3e540a81 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordAccessAuthorizationTests.java @@ -0,0 +1,214 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.IOException; +import java.util.Map; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + + +public final class RecordAccessAuthorizationTests extends TestBase { + + private static long NOW = System.currentTimeMillis(); + private static String LEGAL_TAG = LegalTagUtils.createRandomName(); + private static String KIND = TenantUtils.getTenantName() + ":dataaccess:no:1.1." + NOW; + private static String RECORD_ID = TenantUtils.getTenantName() + ":no:1.1." + NOW; + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + RecordAccessAuthorizationTests.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + RecordAccessAuthorizationTests.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG, token); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), + RecordUtil.createDefaultJsonRecord(RECORD_ID, KIND, LEGAL_TAG), ""); + + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + } + + public static void classTearDown(String token) throws Exception { + LegalTagUtils.delete(LEGAL_TAG, token); + + TestUtils.send("records/" + RECORD_ID, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + } + + @Test + public void should_receiveHttp403_when_userIsNotAuthorizedToGetLatestVersionOfARecord() throws Exception { + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), + testUtils.getNoDataAccessToken()); + + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID, "GET", headers, "", ""); + + this.assertNotAuthorized(response); + } + + @Test + public void should_receiveHttp403_when_userIsNotAuthorizedToListVersionsOfARecord() throws Exception { + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), + testUtils.getNoDataAccessToken()); + + CloseableHttpResponse response = TestUtils.send("records/versions/" + RECORD_ID, "GET", headers, "", ""); + + this.assertNotAuthorized(response); + } + + @Test + public void should_receiveHttp403_when_userIsNotAuthorizedToGetSpecificVersionOfARecord() throws Exception { + Map<String, String> withDataAccessHeader = HeaderUtils.getHeaders(TenantUtils.getTenantName(), + testUtils.getToken()); + + CloseableHttpResponse response = TestUtils.send("records/versions/" + RECORD_ID, "GET", withDataAccessHeader, "", ""); + JsonObject json = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + String version = json.get("versions").getAsJsonArray().get(0).toString(); + + Map<String, String> withoutDataAccessHeader = HeaderUtils.getHeaders(TenantUtils.getTenantName(), + testUtils.getNoDataAccessToken()); + + response = TestUtils.send("records/" + RECORD_ID + "/" + version, "GET", withoutDataAccessHeader, "", ""); + + this.assertNotAuthorized(response); + } + + @Test + public void should_receiveHttp403_when_userIsNotAuthorizedToDeleteRecord() throws Exception { + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), + testUtils.getNoDataAccessToken()); + + CloseableHttpResponse response = TestUtils.send("records/", "POST", headers, "{'anything':'anything'}", + RECORD_ID + ":delete"); + + this.assertNotAuthorized(response); + } + + @Test + public void should_receiveHttp403_when_userIsNotAuthorizedToPurgeRecord() throws Exception { + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), + testUtils.getNoDataAccessToken()); + + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID, "DELETE", headers, "", ""); + + assertEquals(HttpStatus.SC_FORBIDDEN, response.getCode()); + JsonObject json = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + assertEquals(HttpStatus.SC_FORBIDDEN, json.get("code").getAsInt()); + assertEquals("Access denied", json.get("reason").getAsString()); + } + + @Test + public void should_receiveHttp403_when_userIsNotAuthorizedToUpdateARecord() throws Exception { + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), + testUtils.getNoDataAccessToken()); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", headers, + RecordUtil.createDefaultJsonRecord(RECORD_ID, KIND, LEGAL_TAG), ""); + + this.assertNotAuthorized(response); + } + + @Test + public void should_NoneRecords_when_fetchingMultipleRecords_and_notAuthorizedToRecords() + throws Exception { + + // Creates a new record + String newRecordId = TenantUtils.getTenantName() + ":no:2.2." + NOW; + + Map<String, String> headersWithValidAccessToken = HeaderUtils.getHeaders(TenantUtils.getTenantName(), + testUtils.getToken()); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", headersWithValidAccessToken, + RecordUtil.createDefaultJsonRecord(newRecordId, KIND, LEGAL_TAG), ""); + + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + + // Query for original record (no access) and recently created record (with + // access) + Map<String, String> headersWithNoDataAccessToken = HeaderUtils.getHeaders(TenantUtils.getTenantName(), + testUtils.getNoDataAccessToken()); + + JsonArray records = new JsonArray(); + records.add(RECORD_ID); + records.add(newRecordId); + + JsonObject body = new JsonObject(); + body.add("records", records); + + response = TestUtils.send("query/records", "POST", headersWithNoDataAccessToken, body.toString(), ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.RecordsMock responseObject = new DummyRecordsHelper().getRecordsMockFromResponse(response); + + assertEquals(0, responseObject.records.length); + assertEquals(0, responseObject.invalidRecords.length); + assertEquals(0, responseObject.retryRecords.length); + + TestUtils.send("records/" + newRecordId, "DELETE", headersWithNoDataAccessToken, "", ""); + } + + protected void assertNotAuthorized(CloseableHttpResponse response) { + assertEquals(HttpStatus.SC_FORBIDDEN, response.getCode()); + JsonObject json = null; + try { + json = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + assertEquals(HttpStatus.SC_FORBIDDEN, json.get("code").getAsInt()); + assertEquals("Access denied", json.get("reason").getAsString()); + assertEquals("The user is not authorized to perform this action", json.get("message").getAsString()); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordWithEntV2OnlyAclTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordWithEntV2OnlyAclTest.java new file mode 100644 index 0000000000000000000000000000000000000000..dbc7282df058397cfd04b66027da84c62ed3afaa --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordWithEntV2OnlyAclTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2020-2024 Google LLC + * Copyright 2020-2024 EPAM Systems, 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opengroup.osdu.storage.util.TestUtils.STORAGE_TEST_GROUP_ENT_V_2; +import static org.opengroup.osdu.storage.util.TestUtils.STORAGE_TEST_GROUP_ENT_V_2_DESCRIPTION; + +import java.util.Map; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.EntitlementsUtil; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class RecordWithEntV2OnlyAclTest extends TestBase { + + private static final long NOW = System.currentTimeMillis(); + private static final String LEGAL_TAG = LegalTagUtils.createRandomName(); + private static final String KIND = TenantUtils.getTenantName() + ":test:inttest:1.1." + NOW; + private static final String RECORD_ID = TenantUtils.getTenantName() + ":inttest:" + NOW; + + + @BeforeEach + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + LegalTagUtils.create(LEGAL_TAG, testUtils.getToken()); + Map<String, String> headers = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + CloseableHttpResponse createGroupResponse = EntitlementsUtil.createEntitlementsGroup( + headers, + STORAGE_TEST_GROUP_ENT_V_2, + STORAGE_TEST_GROUP_ENT_V_2_DESCRIPTION + ); + int responseCode = createGroupResponse.getCode(); + assertTrue(responseCode == HttpStatus.SC_CREATED || responseCode == HttpStatus.SC_CONFLICT); + } + + @AfterEach + public void tearDown() throws Exception { + LegalTagUtils.delete(LEGAL_TAG, testUtils.getToken()); + TestUtils.send("records/" + RECORD_ID, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + this.testUtils = null; + } + + @Test + public void should_allow_recordWithAclThatExistsOnlyInEntV2() throws Exception{ + //create record + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createJsonRecordWithEntV2OnlyAcl(RECORD_ID, KIND, LEGAL_TAG, RECORD_ID), ""); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + } + +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordWithNullFieldTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordWithNullFieldTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ad4ad4fa55cb42527c4bb71f81567dafaa851c15 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordWithNullFieldTest.java @@ -0,0 +1,131 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.gson.JsonArray; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.DummyRecordsHelper.RecordResultMock; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class RecordWithNullFieldTest extends TestBase { + + private static final Long NOW = System.currentTimeMillis(); + private static final String LEGAL_TAG = LegalTagUtils.createRandomName(); + + private static final String KIND = TenantUtils.getTenantName() + ":test:endtoend:1.1." + + NOW; + private static final String RECORD_ID = TenantUtils.getTenantName() + ":endtoend:1.1." + + NOW; + + @BeforeEach + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + LegalTagUtils.create(LEGAL_TAG, testUtils.getToken()); + } + + @AfterEach + public void tearDown() throws Exception { + LegalTagUtils.delete(LEGAL_TAG, testUtils.getToken()); + TestUtils.send( + "records/" + RECORD_ID, + "DELETE", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + "", + "" + ); + this.testUtils = null; + } + + @Test + public void should_returnRecordWithoutNullFields_when_recordIsIngestedWithNullFields() throws Exception { + + // create record with null field + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createJsonRecordWithData(RECORD_ID, KIND, LEGAL_TAG, null), ""); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + + // get record + response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + JsonObject json = JsonParser.parseString(EntityUtils.toString(response.getEntity())).getAsJsonObject(); + JsonObject dataJson = json.get("data").getAsJsonObject(); + + assertEquals("58377304471659395", dataJson.get("score-int").toString()); + assertEquals("5.837730447165939E7", dataJson.get("score-double").toString()); + assertEquals(JsonNull.INSTANCE, dataJson.get("custom")); + + // query records without attribute + JsonArray attributes = new JsonArray(); + JsonArray records = new JsonArray(); + records.add(RECORD_ID); + + JsonObject body = new JsonObject(); + body.add("records", records); + body.add("attributes", attributes); + + response = TestUtils.send("query/records", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body.toString(), ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + DummyRecordsHelper.RecordsMock responseObject = new DummyRecordsHelper().getRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.invalidRecords.length); + assertEquals(0, responseObject.retryRecords.length); + + RecordResultMock result = responseObject.records[0]; + + assertEquals(5.8377304471659392E16, result.data.get("score-int")); + assertEquals("5.837730447165939E7", result.data.get("score-double").toString()); + assertTrue(result.data.containsKey("custom")); + assertEquals(null, result.data.get("custom")); + + // query records with attribute + attributes.add("data.custom"); + + response = TestUtils.send("query/records", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body.toString(), ""); + assertEquals(HttpStatus.SC_OK, response.getCode()); + + responseObject = new DummyRecordsHelper().getRecordsMockFromResponse(response); + assertEquals(1, responseObject.records.length); + assertEquals(0, responseObject.invalidRecords.length); + assertEquals(0, responseObject.retryRecords.length); + + result = responseObject.records[0]; + + assertFalse(result.data.containsKey("score-int")); + assertFalse(result.data.containsKey("score-double")); + assertTrue(result.data.containsKey("custom")); + assertEquals(null, result.data.get("custom")); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordsApiAcceptanceTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordsApiAcceptanceTests.java new file mode 100644 index 0000000000000000000000000000000000000000..0ddbca60e3e3fa7fa74936ad4a3c95d21d13b452 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/RecordsApiAcceptanceTests.java @@ -0,0 +1,484 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.common.base.Strings; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.ConfigUtils; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class RecordsApiAcceptanceTests extends TestBase { + + private static final String RECORD_ID = TenantUtils.getTenantName() + ":inttest:" + System.currentTimeMillis(); + private static final String RECORD_NEW_ID = TenantUtils.getTenantName() + ":inttest:" + + System.currentTimeMillis(); + + private static final String RECORD_ID_3 = TenantUtils.getTenantName() + ":inttest:testModifyTimeUser-" + System.currentTimeMillis(); + + static final String KIND = TenantUtils.getTenantName() + ":ds:inttest:1.0." + + System.currentTimeMillis(); + private static final String KIND_WITH_OTHER_TENANT = "tenant1" + ":ds:inttest:1.0." + + System.currentTimeMillis(); + + static String LEGAL_TAG = LegalTagUtils.createRandomName(); + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + RecordsApiAcceptanceTests.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + RecordsApiAcceptanceTests.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + this.configUtils = new ConfigUtils("test.properties"); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG, token); + String jsonInput = createJsonBody(RECORD_ID, "tian"); + + TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), jsonInput, ""); + } + + public static void classTearDown(String token) throws Exception { + // attempt to cleanup both records used during tests no matter what state they + // are in + TestUtils.send("records/" + RECORD_ID, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_NEW_ID, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + TestUtils.send("records/" + RECORD_ID_3, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + LegalTagUtils.delete(LEGAL_TAG, token); + } + + @Test + public void should_createNewRecord_when_givenValidRecord_and_verifyNoAncestry() throws Exception { + String jsonInput = createJsonBody(RECORD_NEW_ID, "Flor�"); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + String json = EntityUtils.toString(response.getEntity()); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + assertTrue(response.getEntity().getContentType().contains("application/json")); + + Gson gson = new Gson(); + DummyRecordsHelper.CreateRecordResponse result = gson.fromJson(json, + DummyRecordsHelper.CreateRecordResponse.class); + + assertEquals(1, result.recordCount); + assertEquals(1, result.recordIds.length); + assertEquals(1, result.recordIdVersions.length); + assertEquals(RECORD_NEW_ID, result.recordIds[0]); + + response = TestUtils.send("records/" + RECORD_NEW_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetRecordResponse recordResult = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + assertEquals("Flor?", recordResult.data.get("name")); + assertEquals(null, recordResult.data.get("ancestry")); + } + + @Test + public void should_updateRecordsWithSameData_when_skipDupesIsFalse() throws Exception { + + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetRecordResponse recordResult = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + + String jsonInput = createJsonBody(RECORD_ID, "tianNew"); + + // make update with different name + response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, "?skipdupes=true"); + DummyRecordsHelper.CreateRecordResponse result = TestUtils.getResult(response, HttpStatus.SC_CREATED, + DummyRecordsHelper.CreateRecordResponse.class); + assertNotNull(result); + assertEquals(1, result.recordCount); + assertEquals(1, result.recordIds.length); + assertEquals(1, result.recordIdVersions.length); + assertEquals(0, result.skippedRecordIds.length); + assertEquals(RECORD_ID, result.recordIds[0]); + + response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetRecordResponse recordResult2 = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + assertNotEquals(recordResult.version, recordResult2.version); + assertEquals("tianNew", recordResult2.data.get("name")); + + // use skip dupes to skip update + response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, "?skipdupes=true"); + result = TestUtils.getResult(response, HttpStatus.SC_CREATED, DummyRecordsHelper.CreateRecordResponse.class); + assertNotNull(result); + assertEquals(1, result.recordCount); + assertNull(result.recordIds); + assertNull(result.recordIdVersions); + assertEquals( + 1, + result.skippedRecordIds.length, + "Expected to skip the update when the data was the same as previous update and skipdupes is true" + ); + assertEquals(RECORD_ID, result.skippedRecordIds[0]); + + response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetRecordResponse recordResult3 = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + assertEquals(recordResult2.version, recordResult3.version); + assertEquals("tianNew", recordResult3.data.get("name")); + + // set skip dupes to false to make the update with same data + response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, "?skipdupes=false"); + result = TestUtils.getResult(response, HttpStatus.SC_CREATED, DummyRecordsHelper.CreateRecordResponse.class); + assertNotNull(result); + assertEquals(1, result.recordCount); + assertEquals(1, result.recordIds.length); + assertEquals(1, result.recordIdVersions.length); + assertEquals( + 0, + result.skippedRecordIds.length, + "Expected to NOT skip the update when data is the same but skipdupes is false" + ); + assertEquals(RECORD_ID, result.recordIds[0]); + + response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + recordResult3 = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + assertNotEquals(recordResult2.version, recordResult3.version); + assertEquals("tianNew", recordResult3.data.get("name")); + } + + @Test + public void should_getAnOlderVersion_and_theMostRecentVersion_and_retrieveAllVersions() + throws Exception { + + CloseableHttpResponse response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetRecordResponse originalRecordResult = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + + String jsonInput = createJsonBody(RECORD_ID, "tianNew2"); + + // add an extra version + response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + TestUtils.getResult(response, HttpStatus.SC_CREATED, DummyRecordsHelper.CreateRecordResponse.class); + + // get a specific older version and validate it is the same + response = TestUtils.send("records/" + RECORD_ID + "/" + originalRecordResult.version, "GET", + HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetRecordResponse recordResultVersion = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + assertEquals(originalRecordResult.id, recordResultVersion.id); + assertEquals(originalRecordResult.version, recordResultVersion.version); + assertEquals(originalRecordResult.data.get("name"), recordResultVersion.data.get("name")); + + // get the latest version by using id and validate it has the latest data + response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetRecordResponse newRecordResult = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + assertEquals(originalRecordResult.id, newRecordResult.id); + assertNotEquals(originalRecordResult.version, newRecordResult.version); + assertEquals("tianNew2", newRecordResult.data.get("name")); + + // older version and new version should be found + response = TestUtils.send("records/versions/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetVersionsResponse versionsResponse = TestUtils.getResult(response, HttpStatus.SC_OK, GetVersionsResponse.class); + assertEquals(RECORD_ID, versionsResponse.recordId); + List<Long> versions = Arrays.asList(versionsResponse.versions); + assertTrue(versions.contains(originalRecordResult.version)); + assertTrue(versions.contains(newRecordResult.version)); + } + + @Test + public void should_deleteAllVersionsOfARecord_when_deletingARecordById() throws Exception { + String idToDelete = RECORD_ID + 1; + + String jsonInput = createJsonBody(idToDelete, "tianNew2"); + + // add an extra version + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + TestUtils.getResult(response, HttpStatus.SC_CREATED, DummyRecordsHelper.CreateRecordResponse.class); + + response = TestUtils.send("records/" + idToDelete, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); + + response = TestUtils.send("records/" + idToDelete, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + String notFoundResponse = TestUtils.getResult(response, 404, String.class); + assertEquals("{\"code\":404,\"reason\":\"Record not found\",\"message\":\"The record" + " '" + idToDelete + "' " + + "was not found\"}", notFoundResponse); + } + + @Test + public void should_ingestRecord_when_noRecordIdIsProvided() throws Exception { + String body = createJsonBody(null, "Foo"); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body, ""); + String responseString = TestUtils.getResult(response, HttpStatus.SC_CREATED, String.class); + JsonObject responseJson = new JsonParser().parse(responseString).getAsJsonObject(); + + assertEquals(1, responseJson.get("recordCount").getAsInt()); + assertEquals(1, responseJson.get("recordIds").getAsJsonArray().size()); + assertTrue(responseJson.get("recordIds").getAsJsonArray().get(0).getAsString() + .startsWith(TenantUtils.getTenantName() + ":")); + } + + @Test + public void should_returnWholeRecord_when_recordIsIngestedWithAllFields() throws Exception { + final String RECORD_ID = TenantUtils.getTenantName() + ":inttest:wholerecord-" + System.currentTimeMillis(); + + String body = createJsonBody(RECORD_ID, "Foo"); + + // injesting record + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body, ""); + TestUtils.getResult(response, HttpStatus.SC_CREATED, String.class); + + // getting record + response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + String responseString = TestUtils.getResult(response, HttpStatus.SC_OK, String.class); + + JsonObject responseJson = JsonParser.parseString(responseString).getAsJsonObject(); + + assertEquals(RECORD_ID, responseJson.get("id").getAsString()); + + assertEquals(KIND, responseJson.get("kind").getAsString()); + + JsonObject acl = responseJson.get("acl").getAsJsonObject(); + assertEquals(TestUtils.getAcl(), acl.get("owners").getAsString()); + assertEquals(TestUtils.getAcl(), acl.get("viewers").getAsString()); + + assertEquals("Foo", responseJson.getAsJsonObject("data").get("name").getAsString()); + } + + @Test + public void should_returnWholeRecord_when_recordIsIngestedWithOtherTenantInKind() throws Exception { + final String RECORD_ID = TenantUtils.getTenantName() + ":inttest:wholerecord-" + System.currentTimeMillis(); + String body = createJsonBody(RECORD_ID, "Foo", KIND_WITH_OTHER_TENANT); + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body, ""); + TestUtils.getResult(response, HttpStatus.SC_CREATED, String.class); + response = TestUtils.send("records/" + RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + String responseString = TestUtils.getResult(response, HttpStatus.SC_OK, String.class); + JsonObject responseJson = JsonParser.parseString(responseString).getAsJsonObject(); + assertEquals(RECORD_ID, responseJson.get("id").getAsString()); + assertEquals(KIND_WITH_OTHER_TENANT, responseJson.get("kind").getAsString()); + JsonObject acl = responseJson.get("acl").getAsJsonObject(); + assertEquals(TestUtils.getAcl(), acl.get("owners").getAsString()); + assertEquals(TestUtils.getAcl(), acl.get("viewers").getAsString()); + assertEquals("Foo", responseJson.getAsJsonObject("data").get("name").getAsString()); + } + + @Test + public void should_insertNewRecord_when_skipDupesIsTrue() throws Exception { + final String RECORD_ID = TenantUtils.getTenantName() + ":inttest:wholerecord-" + System.currentTimeMillis(); + String body = createJsonBody(RECORD_ID, "Foo"); + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), body, "?skipdupes=true"); + DummyRecordsHelper.CreateRecordResponse result = TestUtils.getResult(response, HttpStatus.SC_CREATED, DummyRecordsHelper.CreateRecordResponse.class); + assertNotNull(result); + assertEquals(1, result.recordCount); + assertEquals( + 1, + result.recordIds.length, + "Expected to insert the new record when skipdupes is true" + ); + assertEquals(1, result.recordIdVersions.length); + assertEquals(RECORD_ID, result.recordIds[0]); + response = TestUtils.send("records/" + RECORD_ID, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); + } + + @Test + public void should_createNewRecord_withSpecialCharacter_ifEnabled() throws Exception { + final long currentTimeMillis = System.currentTimeMillis(); + final String RECORD_ID = TenantUtils.getTenantName() + ":inttest:testSpecialChars%abc%2Ffoobar-" + currentTimeMillis; + final String ENCODED_RECORD_ID = TenantUtils.getTenantName() + ":inttest:testSpecialChars%25abc%252Ffoobar-" + currentTimeMillis; + + String jsonInput = createJsonBody(RECORD_ID, "TestSpecialCharacters"); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, ""); + String json = EntityUtils.toString(response.getEntity()); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + assertTrue(response.getEntity().getContentType().contains("application/json")); + + Gson gson = new Gson(); + DummyRecordsHelper.CreateRecordResponse result = gson.fromJson(json, + DummyRecordsHelper.CreateRecordResponse.class); + + assertEquals(1, result.recordCount); + assertEquals(1, result.recordIds.length); + assertEquals(1, result.recordIdVersions.length); + assertEquals(RECORD_ID, result.recordIds[0]); + + response = TestUtils.send("records/" + ENCODED_RECORD_ID, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + + // If encoded percent is true, the request should go through and should be able to get a successful response. + if (configUtils != null && configUtils.getBooleanProperty("enableEncodedSpecialCharactersInURL", "false")) { + GetRecordResponse recordResult = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + assertEquals("TestSpecialCharacters", recordResult.data.get("name")); + } else { + // Service does not allow URLs with suspicious characters, Which is the default setting. + // Different CSPs are responding with different status code for this error when a special character like %25 is present in the URL. + // Hence the Assert Statement is marked not to be 200. + // More details - https://community.opengroup.org/osdu/platform/system/storage/-/issues/61 + assertNotEquals(HttpStatus.SC_OK, response.getCode()); + } + + } + + @Test + public void should_updateModifyTimeWithRecordUpdate() throws Exception { + + String jsonInput = createJsonBody(RECORD_ID_3, "tianNew"); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, "?skipdupes=false"); + DummyRecordsHelper.CreateRecordResponse result = TestUtils.getResult(response, HttpStatus.SC_CREATED, + DummyRecordsHelper.CreateRecordResponse.class); + assertNotNull(result); + assertEquals(1, result.recordCount); + assertEquals(1, result.recordIds.length); + assertEquals(1, result.recordIdVersions.length); + assertEquals(0, result.skippedRecordIds.length); + assertEquals(RECORD_ID_3, result.recordIds[0]); + String firstVersionNumber = StringUtils.substringAfterLast(result.recordIdVersions[0],":"); + + response = TestUtils.send("records/" + RECORD_ID_3, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetRecordResponse recordResult1 = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + + //No modify user and time in 1st version of record + assertNull(recordResult1.modifyTime); + assertNull(recordResult1.modifyUser); + + // make update-1 + response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, "?skipdupes=false"); + DummyRecordsHelper.CreateRecordResponse result2 = TestUtils.getResult(response, HttpStatus.SC_CREATED, + DummyRecordsHelper.CreateRecordResponse.class); + assertNotNull(result2); + assertEquals(1, result2.recordCount); + assertEquals(1, result2.recordIds.length); + assertEquals(1, result2.recordIdVersions.length); + assertEquals(0, result2.skippedRecordIds.length); + assertEquals(RECORD_ID_3, result2.recordIds[0]); + String secondVersionNumber = StringUtils.substringAfterLast(result2.recordIdVersions[0],":"); + + // make update-2 + response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), jsonInput, "?skipdupes=false"); + DummyRecordsHelper.CreateRecordResponse result3 = TestUtils.getResult(response, HttpStatus.SC_CREATED, DummyRecordsHelper.CreateRecordResponse.class); + assertNotNull(result3); + assertEquals(1, result3.recordCount); + assertEquals(1, result3.recordIds.length); + assertEquals(1, result3.recordIdVersions.length); + assertEquals(0, result3.skippedRecordIds.length); + assertEquals(RECORD_ID_3, result3.recordIds[0]); + + String thirdLastVersionNumber = StringUtils.substringAfterLast(result3.recordIdVersions[0],":"); + response = TestUtils.send("records/" + RECORD_ID_3+"/"+firstVersionNumber, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetRecordResponse recordResult2 = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + + //No modify user and time in 1st version of record + assertNull(recordResult2.modifyTime); + assertNull(recordResult2.modifyUser); + + response = TestUtils.send("records/" + RECORD_ID_3+"/"+secondVersionNumber, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetRecordResponse recordResult3 = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + + response = TestUtils.send("records/" + RECORD_ID_3+"/"+thirdLastVersionNumber, "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + GetRecordResponse recordResult4 = TestUtils.getResult(response, HttpStatus.SC_OK, GetRecordResponse.class); + + //modify time is different for each version of record + assertNotEquals(recordResult4.modifyTime, recordResult3.modifyTime); + + + } + + protected static String createJsonBody(String id, String name) { + return "[" + singleEntityBody(id, name, KIND, LEGAL_TAG) + "]"; + } + + protected static String createJsonBody(String id, String name, String kind) { + return "[" + singleEntityBody(id, name, kind, LEGAL_TAG) + "]"; + } + + public class RecordAncestry { + public String[] parents; + } + + protected class GetVersionsResponse { + String recordId; + Long versions[]; + } + + protected class GetRecordResponse { + String id; + long version; + Map<String, Object> data; + String modifyTime; + String modifyUser; + } + + public static String singleEntityBody(String id, String name, String kind, String legalTagName) { + + JsonObject data = new JsonObject(); + data.addProperty("name", name); + + JsonObject acl = new JsonObject(); + JsonArray acls = new JsonArray(); + acls.add(TestUtils.getAcl()); + acl.add("viewers", acls); + acl.add("owners", acls); + + JsonObject legal = new JsonObject(); + JsonArray legals = new JsonArray(); + legals.add(legalTagName); + legal.add("legaltags", legals); + JsonArray ordc = new JsonArray(); + ordc.add("BR"); + legal.add("otherRelevantDataCountries", ordc); + + JsonObject record = new JsonObject(); + if (!Strings.isNullOrEmpty(id)) { + record.addProperty("id", id); + } + + record.addProperty("kind", kind); + record.add("acl", acl); + record.add("legal", legal); + record.add("data", data); + + return record.toString(); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/UpdateRecordsMetadataTest.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/UpdateRecordsMetadataTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ca0dd11a9056981902c6096641d2ed02d6153ddf --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/records/UpdateRecordsMetadataTest.java @@ -0,0 +1,462 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.records; + +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.apache.http.HttpStatus.SC_BAD_REQUEST; +import static org.apache.http.HttpStatus.SC_OK; +import static org.apache.http.HttpStatus.SC_PARTIAL_CONTENT; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.util.Map; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.RecordUtil; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class UpdateRecordsMetadataTest extends TestBase { + static final String TAG_KEY = "tagkey1"; + static final String TAG_VALUE1 = "tagvalue1"; + private static final String TAG_VALUE2 = "tagvalue2"; + + private static long NOW; + private static String ACL = TestUtils.getIntegrationTesterAcl(); + private static String ACL_2 = TestUtils.getAcl(); + private static String LEGAL_TAG; + private static String LEGAL_TAG_2; + private static String LEGAL_TAG_3; + private static String LEGAL_TAG_4; + private static String KIND; + private static String RECORD_ID; + private static String RECORD_ID_2; + private static String RECORD_ID_3; + private static String RECORD_ID_4; + private static String NOT_EXISTED_RECORD_ID; + private static final DummyRecordsHelper RECORDS_HELPER = new DummyRecordsHelper(); + + @BeforeEach + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + + LEGAL_TAG = LegalTagUtils.createRandomName(); + LEGAL_TAG_2 = LegalTagUtils.createRandomName() + "2"; + LEGAL_TAG_3 = LegalTagUtils.createRandomName() + "3"; + LEGAL_TAG_4 = LegalTagUtils.createRandomName() + "4"; + NOW = System.currentTimeMillis(); + KIND = TenantUtils.getFirstTenantName() + ":bulkupdate:test:1.1." + NOW; + RECORD_ID = TenantUtils.getFirstTenantName() + ":test:1.1." + NOW; + RECORD_ID_2 = TenantUtils.getFirstTenantName() + ":test:1.2." + NOW; + RECORD_ID_3 = TenantUtils.getFirstTenantName() + ":test:1.3." + NOW; + RECORD_ID_4 = TenantUtils.getFirstTenantName() + ":test:1.4." + NOW; + NOT_EXISTED_RECORD_ID = TenantUtils.getFirstTenantName() + ":bulkupdate:1.6." + NOW; + LegalTagUtils.create(LEGAL_TAG, testUtils.getToken()); + LegalTagUtils.create(LEGAL_TAG_2, testUtils.getToken()); + LegalTagUtils.create(LEGAL_TAG_3, testUtils.getToken()); + LegalTagUtils.create(LEGAL_TAG_4, testUtils.getToken()); + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createDefaultJsonRecord(RECORD_ID, KIND, LEGAL_TAG), ""); + CloseableHttpResponse response2 = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createDefaultJsonRecord(RECORD_ID_2, KIND, LEGAL_TAG_2), ""); + CloseableHttpResponse response3 = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createDefaultJsonRecord(RECORD_ID_3, KIND, LEGAL_TAG_3), ""); + CloseableHttpResponse response4 = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), + RecordUtil.createDefaultJsonRecord(RECORD_ID_4, KIND, LEGAL_TAG_4), ""); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + assertEquals(HttpStatus.SC_CREATED, response2.getCode()); + assertEquals(HttpStatus.SC_CREATED, response3.getCode()); + assertEquals(HttpStatus.SC_CREATED, response4.getCode()); + + } + + @AfterEach + public void tearDown() throws Exception { + LegalTagUtils.delete(LEGAL_TAG, testUtils.getToken()); + LegalTagUtils.delete(LEGAL_TAG_2, testUtils.getToken()); + LegalTagUtils.delete(LEGAL_TAG_3, testUtils.getToken()); + LegalTagUtils.delete(LEGAL_TAG_4, testUtils.getToken()); + TestUtils.send("records/" + RECORD_ID, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_2, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_3, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + TestUtils.send("records/" + RECORD_ID_4, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + + this.testUtils = null; + } + + @Test + public void should_return200andUpdateMetadata_whenValidRecordsProvided() throws Exception { + //1. query 2 records, check the acls + JsonArray records = new JsonArray(); + records.add(RECORD_ID); + records.add(RECORD_ID_2); + + JsonObject queryBody = new JsonObject(); + queryBody.add("records", records); + + Map<String, String> queryHeader = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + queryHeader.put("frame-of-reference", "none"); + CloseableHttpResponse queryResponse1 = TestUtils.send("query/records:batch", "POST", queryHeader, queryBody.toString(), + ""); + assertEquals(HttpStatus.SC_OK, queryResponse1.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock queryResponseObject1 = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse1); + assertEquals(2, queryResponseObject1.records.length); + assertEquals(TestUtils.getAcl(), queryResponseObject1.records[0].acl.viewers[0]); + assertEquals(TestUtils.getAcl(), queryResponseObject1.records[0].acl.owners[0]); + + //2. bulk update requests, change acls + JsonArray value = new JsonArray(); + value.add(TestUtils.getIntegrationTesterAcl()); + JsonObject op1 = new JsonObject(); + op1.addProperty("op", "replace"); + op1.addProperty("path", "/acl/viewers"); + op1.add("value", value); + JsonObject op2 = new JsonObject(); + op2.addProperty("op", "replace"); + op2.addProperty("path", "/acl/owners"); + op2.add("value", value); + JsonArray ops = new JsonArray(); + ops.add(op1); + ops.add(op2); + + JsonObject query = new JsonObject(); + query.add("ids", records); + + JsonObject updateBody = new JsonObject(); + updateBody.add("query", query); + updateBody.add("ops", ops); + + CloseableHttpResponse bulkUpdateResponse = TestUtils.send("records", "PATCH", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), updateBody.toString(), + ""); + assertEquals(HttpStatus.SC_OK, bulkUpdateResponse.getCode()); + + //3. query 2 records again, check acls + CloseableHttpResponse queryResponse2 = TestUtils.send("query/records:batch", "POST", queryHeader, queryBody.toString(), + ""); + assertEquals(HttpStatus.SC_OK, queryResponse2.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock queryResponseObject2 = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse2); + assertEquals(2, queryResponseObject2.records.length); + assertEquals(TestUtils.getIntegrationTesterAcl(), queryResponseObject2.records[0].acl.viewers[0]); + assertEquals(TestUtils.getIntegrationTesterAcl(), queryResponseObject2.records[0].acl.owners[0]); + } + + @Test + public void should_return206andUpdateMetadata_whenOneRecordProvided() throws Exception { + //1. query 2 records, check the acls + JsonArray records = new JsonArray(); + records.add(RECORD_ID_3); + records.add(RECORD_ID_4); + + JsonObject queryBody = new JsonObject(); + queryBody.add("records", records); + + Map<String, String> queryHeader = HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()); + queryHeader.put("slb-frame-of-reference", "none"); + CloseableHttpResponse queryResponse1 = TestUtils.send("query/records:batch", "POST", queryHeader, queryBody.toString(), + ""); + assertEquals(HttpStatus.SC_OK, queryResponse1.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock queryResponseObject1 = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse1); + assertEquals(2, queryResponseObject1.records.length); + assertEquals(TestUtils.getAcl(), queryResponseObject1.records[0].acl.viewers[0]); + assertEquals(TestUtils.getAcl(), queryResponseObject1.records[0].acl.owners[0]); + + //2. bulk update requests, change acls + JsonArray value = new JsonArray(); + value.add(TestUtils.getIntegrationTesterAcl()); + JsonObject op1 = new JsonObject(); + op1.addProperty("op", "replace"); + op1.addProperty("path", "/acl/viewers"); + op1.add("value", value); + JsonObject op2 = new JsonObject(); + op2.addProperty("op", "replace"); + op2.addProperty("path", "/acl/owners"); + op2.add("value", value); + JsonArray ops = new JsonArray(); + ops.add(op1); + ops.add(op2); + + JsonArray records2 = new JsonArray(); + records2.add(RECORD_ID_3); + records2.add(RECORD_ID_4); + records2.add(TenantUtils.getFirstTenantName() + ":not:found"); + JsonObject query = new JsonObject(); + query.add("ids", records2); + + JsonObject updateBody = new JsonObject(); + updateBody.add("query", query); + updateBody.add("ops", ops); + + CloseableHttpResponse bulkUpdateResponse = TestUtils.send("records", "PATCH", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), updateBody.toString(), + ""); + assertEquals(HttpStatus.SC_PARTIAL_CONTENT, bulkUpdateResponse.getCode()); + + //3. query 2 records again, check acls + CloseableHttpResponse queryResponse2 = TestUtils.send("query/records:batch", "POST", queryHeader, queryBody.toString(), + ""); + assertEquals(HttpStatus.SC_OK, queryResponse2.getCode()); + + DummyRecordsHelper.ConvertedRecordsMock queryResponseObject2 = RECORDS_HELPER.getConvertedRecordsMockFromResponse(queryResponse2); + assertEquals(2, queryResponseObject2.records.length); + assertEquals(TestUtils.getIntegrationTesterAcl(), queryResponseObject2.records[0].acl.viewers[0]); + assertEquals(TestUtils.getIntegrationTesterAcl(), queryResponseObject2.records[0].acl.owners[0]); + } + + @Test + public void should_return200AndUpdateTagsMetadata_whenValidRecordsProvided() throws Exception { + //add operation + JsonObject updateBody = RecordUtil.buildUpdateTagBody(RECORD_ID, "add", TAG_KEY + ":" + TAG_VALUE1); + + CloseableHttpResponse updateResponse = sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + CloseableHttpResponse recordResponse = sendRequest("GET", "records/" + RECORD_ID, EMPTY, testUtils.getToken()); + + assertEquals(SC_OK, updateResponse.getCode()); + assertEquals(SC_OK, recordResponse.getCode()); + + JsonObject resultObject = bodyToJsonObject(EntityUtils.toString(updateResponse.getEntity())); + assertEquals(RECORD_ID, resultObject.get("recordIds").getAsJsonArray().get(0).getAsString()); + + resultObject = bodyToJsonObject(EntityUtils.toString(recordResponse.getEntity())); + assertEquals(TAG_VALUE1, resultObject.get("tags").getAsJsonObject().get(TAG_KEY).getAsString()); + + //replace operation + updateBody = RecordUtil.buildUpdateTagBody(RECORD_ID, "replace", TAG_KEY + ":" + TAG_VALUE2); + sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + recordResponse = sendRequest("GET", "records/" + RECORD_ID, EMPTY, testUtils.getToken()); + + resultObject = bodyToJsonObject(EntityUtils.toString(recordResponse.getEntity())); + assertEquals(TAG_VALUE2, resultObject.get("tags").getAsJsonObject().get(TAG_KEY).getAsString()); + + //remove operation + updateBody = RecordUtil.buildUpdateTagBody(RECORD_ID,"remove", TAG_KEY); + sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + recordResponse = sendRequest("GET", "records/" + RECORD_ID, EMPTY, testUtils.getToken()); + + resultObject = JsonParser.parseString(EntityUtils.toString(recordResponse.getEntity())).getAsJsonObject(); + assertNull(resultObject.get("tags")); + } + + @Test + public void should_return206andUpdateTagsMetadata_whenNotExistedRecordProvided() throws Exception { + JsonObject updateBody = RecordUtil.buildUpdateTagBody(NOT_EXISTED_RECORD_ID, "replace", TAG_KEY + ":" + TAG_VALUE1); + + CloseableHttpResponse updateResponse = sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + + assertEquals(SC_PARTIAL_CONTENT, updateResponse.getCode()); + JsonObject resultObject = bodyToJsonObject(EntityUtils.toString(updateResponse.getEntity())); + + System.out.println(resultObject.toString()); + assertEquals(NOT_EXISTED_RECORD_ID, resultObject.get("notFoundRecordIds").getAsJsonArray().getAsString()); + } + + private static CloseableHttpResponse sendRequest(String method, String path, String body, String token) throws Exception { + return TestUtils + .send(path, method, HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), body, ""); + } + + private String toJson(Object object) { + return new Gson().toJson(object); + } + + private JsonObject bodyToJsonObject(String json) { + return JsonParser.parseString(json).getAsJsonObject(); + } + + @Test + public void should_return200AndUpdateLegalMetadataOr400ForRemoveRestriction_whenValidRecordsProvided() throws Exception { + //add operation + JsonObject updateBody = buildUpdateLegalBody(RECORD_ID, "add", LEGAL_TAG); + + CloseableHttpResponse updateResponse = sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + CloseableHttpResponse recordResponse = sendRequest("GET", "records/" + RECORD_ID, EMPTY, testUtils.getToken()); + + assertEquals(SC_OK, updateResponse.getCode()); + assertEquals(SC_OK, recordResponse.getCode()); + + JsonObject resultObject = bodyToJsonObject(EntityUtils.toString(updateResponse.getEntity())); + assertEquals(RECORD_ID, resultObject.get("recordIds").getAsJsonArray().get(0).getAsString()); + + resultObject = bodyToJsonObject(EntityUtils.toString(recordResponse.getEntity())); + assertEquals(LEGAL_TAG, resultObject.get("legal").getAsJsonObject().get("legaltags").getAsString()); + + //replace operation + updateBody = buildUpdateLegalBody(RECORD_ID, "replace", LEGAL_TAG_2); + sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + recordResponse = sendRequest("GET", "records/" + RECORD_ID, EMPTY, testUtils.getToken()); + + resultObject = bodyToJsonObject(EntityUtils.toString(recordResponse.getEntity())); + assertEquals(LEGAL_TAG_2, resultObject.get("legal").getAsJsonObject().get("legaltags").getAsString()); + + //remove operation + updateBody = buildUpdateLegalBody(RECORD_ID,"remove", LEGAL_TAG_2); + updateResponse= sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + + assertEquals(SC_BAD_REQUEST,updateResponse.getCode()); + } + + @Test + public void should_return200AndUpdateAclViewersMetadataOr400ForRemoveRestriction_whenValidRecordsProvided() throws Exception { + //add operation + JsonObject updateBody = buildUpdateAclBody(RECORD_ID, "add","/acl/viewers", ACL); + + CloseableHttpResponse updateResponse = sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + CloseableHttpResponse recordResponse = sendRequest("GET", "records/" + RECORD_ID, EMPTY, testUtils.getToken()); + + assertEquals(SC_OK, updateResponse.getCode()); + assertEquals(SC_OK, recordResponse.getCode()); + + JsonObject resultObject = bodyToJsonObject(EntityUtils.toString(updateResponse.getEntity())); + assertEquals(RECORD_ID, resultObject.get("recordIds").getAsJsonArray().get(0).getAsString()); + + resultObject = bodyToJsonObject(EntityUtils.toString(recordResponse.getEntity())); + assertEquals(ACL, resultObject.get("acl").getAsJsonObject().get("viewers").getAsJsonArray().get(0).getAsString()); + + //replace operation + updateBody = buildUpdateAclBody(RECORD_ID, "replace","/acl/viewers", ACL_2); + sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + recordResponse = sendRequest("GET", "records/" + RECORD_ID, EMPTY, testUtils.getToken()); + + resultObject = bodyToJsonObject(EntityUtils.toString(recordResponse.getEntity())); + assertEquals(ACL_2, resultObject.get("acl").getAsJsonObject().get("viewers").getAsJsonArray().get(0).getAsString()); + + //remove operation + updateBody = buildUpdateAclBody(RECORD_ID,"remove","/acl/viewers", ACL_2); + updateResponse = sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + + assertEquals(SC_BAD_REQUEST,updateResponse.getCode()); + } + + @Test + public void should_return200AndUpdateAclOwnersMetadataOr400ForRemoveRestriction_whenValidRecordsProvided() throws Exception { + //add operation + JsonObject updateBody = buildUpdateAclBody(RECORD_ID, "add","/acl/owners", ACL); + + CloseableHttpResponse updateResponse = sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + CloseableHttpResponse recordResponse = sendRequest("GET", "records/" + RECORD_ID, EMPTY, testUtils.getToken()); + + assertEquals(SC_OK, updateResponse.getCode()); + assertEquals(SC_OK, recordResponse.getCode()); + + JsonObject resultObject = bodyToJsonObject(EntityUtils.toString(updateResponse.getEntity())); + assertEquals(RECORD_ID, resultObject.get("recordIds").getAsJsonArray().get(0).getAsString()); + + resultObject = bodyToJsonObject(EntityUtils.toString(recordResponse.getEntity())); + assertEquals(ACL, resultObject.get("acl").getAsJsonObject().get("owners").getAsJsonArray().get(0).getAsString()); + + //remove operation + updateBody = buildUpdateAclBody(RECORD_ID,"remove","/acl/owners", ACL_2); + sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + + recordResponse = sendRequest("GET", "records/" + RECORD_ID, EMPTY, testUtils.getToken()); + resultObject = JsonParser.parseString(EntityUtils.toString(recordResponse.getEntity())).getAsJsonObject(); + + assertEquals(ACL, resultObject.get("acl").getAsJsonObject().get("owners").getAsJsonArray().get(0).getAsString()); + + //replace operation + updateBody = buildUpdateAclBody(RECORD_ID, "replace","/acl/owners ", ACL); + sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + recordResponse = sendRequest("GET", "records/" + RECORD_ID, EMPTY, testUtils.getToken()); + + resultObject = bodyToJsonObject(EntityUtils.toString(recordResponse.getEntity())); + assertEquals(ACL, resultObject.get("acl").getAsJsonObject().get("owners").getAsJsonArray().get(0).getAsString()); + } + + @Test + public void should_return206andUpdateLegalMetadata_whenNotExistedRecordProvided() throws Exception { + JsonObject updateBody = buildUpdateLegalBody(NOT_EXISTED_RECORD_ID, "replace", LEGAL_TAG); + + CloseableHttpResponse updateResponse = sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + + assertEquals(SC_PARTIAL_CONTENT, updateResponse.getCode()); + JsonObject resultObject = bodyToJsonObject(EntityUtils.toString(updateResponse.getEntity())); + + System.out.println(resultObject.toString()); + assertEquals(NOT_EXISTED_RECORD_ID, resultObject.get("notFoundRecordIds").getAsJsonArray().getAsString()); + } + + @Test + public void should_return206andUpdateAclMetadata_whenNotExistedRecordProvided() throws Exception { + JsonObject updateBody = buildUpdateAclBody(NOT_EXISTED_RECORD_ID, "replace","/acl/viewers", ACL); + + CloseableHttpResponse updateResponse = sendRequest("PATCH", "records", toJson(updateBody), testUtils.getToken()); + + assertEquals(SC_PARTIAL_CONTENT, updateResponse.getCode()); + JsonObject resultObject = bodyToJsonObject(EntityUtils.toString(updateResponse.getEntity())); + + System.out.println(resultObject.toString()); + assertEquals(NOT_EXISTED_RECORD_ID, resultObject.get("notFoundRecordIds").getAsJsonArray().getAsString()); + } + + private JsonObject buildUpdateLegalBody(String id, String op, String val) { + JsonArray records = new JsonArray(); + records.add(id); + + JsonArray value = new JsonArray(); + value.add(val); + JsonObject operation = new JsonObject(); + operation.addProperty("op", op); + operation.addProperty("path", "/legal/legaltags"); + operation.add("value", value); + JsonArray ops = new JsonArray(); + ops.add(operation); + + JsonObject query = new JsonObject(); + query.add("ids", records); + + JsonObject updateBody = new JsonObject(); + updateBody.add("query", query); + updateBody.add("ops", ops); + + return updateBody; + } + + private JsonObject buildUpdateAclBody(String id, String op, String path, String val) { + JsonArray records = new JsonArray(); + records.add(id); + + JsonArray value = new JsonArray(); + value.add(val); + JsonObject operation = new JsonObject(); + operation.addProperty("op", op); + operation.addProperty("path", path); + operation.add("value", value); + JsonArray ops = new JsonArray(); + ops.add(operation); + + JsonObject query = new JsonObject(); + query.add("ids", records); + + JsonObject updateBody = new JsonObject(); + updateBody.add("query", query); + updateBody.add("ops", ops); + + return updateBody; + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/replay/ReplayEndpointsTests.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/replay/ReplayEndpointsTests.java new file mode 100644 index 0000000000000000000000000000000000000000..010e4d1e57b8fb2a97e3ce24f92504d6a56885b4 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/replay/ReplayEndpointsTests.java @@ -0,0 +1,367 @@ +// Copyright © Microsoft Corporation +// +// 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 org.opengroup.osdu.storage.replay; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import com.google.gson.Gson; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.opengroup.osdu.storage.model.ReplayStatusResponseHelper; +import org.opengroup.osdu.storage.records.RecordsApiAcceptanceTests; +import org.opengroup.osdu.storage.util.ConfigUtils; +import org.opengroup.osdu.storage.util.DummyRecordsHelper; +import org.opengroup.osdu.storage.util.HeaderUtils; +import org.opengroup.osdu.storage.util.LegalTagUtils; +import org.opengroup.osdu.storage.util.ReplayUtils; +import org.opengroup.osdu.storage.util.TenantUtils; +import org.opengroup.osdu.storage.util.TestBase; +import org.opengroup.osdu.storage.util.TestUtils; +import org.opengroup.osdu.storage.util.TokenTestUtils; + +public final class ReplayEndpointsTests extends TestBase { + private static String LEGAL_TAG_NAME = LegalTagUtils.createRandomName(); + + private static final String INVALID_KIND = TenantUtils.getTenantName() + ":ds:1.0." + + System.currentTimeMillis(); + + private static final TokenTestUtils TOKEN_TEST_UTILS = new TokenTestUtils(); + + @BeforeAll + public static void classSetup() throws Exception { + ReplayEndpointsTests.classSetup(TOKEN_TEST_UTILS.getToken()); + } + + @AfterAll + public static void classTearDown() throws Exception { + ReplayEndpointsTests.classTearDown(TOKEN_TEST_UTILS.getToken()); + } + + @BeforeEach + @Override + public void setup() throws Exception { + this.testUtils = new TokenTestUtils(); + this.configUtils = new ConfigUtils("test.properties"); + assumeTrue(configUtils.isTestReplayEnabled()); + } + + @AfterEach + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } + + public static void classSetup(String token) throws Exception { + LegalTagUtils.create(LEGAL_TAG_NAME, token); + } + + public static void classTearDown(String token) throws Exception { + LegalTagUtils.delete(LEGAL_TAG_NAME, token); + } + + @Test + public void should_return_400_when_givenNoOperationNameIsNotInRequest() throws Exception { + + String requestBody = ReplayUtils.createJsonEmpty(); + CloseableHttpResponse response = TestUtils.send("replay", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), requestBody, ""); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + String actualErrorMessage = ReplayUtils.getFieldFromResponse(response, "message"); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + assertEquals("Operation field is required. The valid operations are: 'replay', 'reindex'.", actualErrorMessage); + } + + @Test + public void should_return_400_when_givenKindIsEmpty() throws Exception { + + String requestBody = ReplayUtils.createJsonWithKind("reindex", new ArrayList<>()); + CloseableHttpResponse response = TestUtils.send("replay", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), requestBody, ""); + String actualErrorMessage = ReplayUtils.getFieldFromResponse(response, "message"); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + assertEquals("Currently restricted to a single valid kind.", actualErrorMessage); + } + + @Test + public void should_return_400_when_givenKindSizeIsGreaterDenOne() throws Exception { + + List<String> kindList = new ArrayList<>(); + kindList.add(getKind()); + kindList.add(getKind()); + + String requestBody = ReplayUtils.createJsonWithKind("reindex", kindList); + CloseableHttpResponse response = TestUtils.send("replay", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), requestBody, ""); + String actualErrorMessage = ReplayUtils.getFieldFromResponse(response, "message"); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + assertEquals("Currently restricted to a single valid kind.", actualErrorMessage); + } + + @Test + public void Should_return_400_when_givenInvalidKind() throws Exception { + + List<String> kindList = new ArrayList<>(); + kindList.add(INVALID_KIND); + String requestBody = ReplayUtils.createJsonWithKind("reindex", kindList); + CloseableHttpResponse response = TestUtils.send("replay", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), requestBody, ""); + String actualErrorMessage = ReplayUtils.getFieldFromResponse(response, "message"); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + assertEquals("The requested kind does not exist.", actualErrorMessage); + } + + @Test + public void Should_return_400_when_givenInvalidOperationName() throws Exception { + + String requestBody = ReplayUtils.createJsonWithOperationName("invalidOperation"); + CloseableHttpResponse response = TestUtils.send("replay", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), requestBody, ""); + String actualErrorMessage = ReplayUtils.getFieldFromResponse(response, "message"); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + assertEquals("Not a valid operation. The valid operations are: [reindex, replay]", actualErrorMessage); + } + + @Test + public void should_return_200_GivenReplayAll() throws Exception { + + if (configUtils != null && configUtils.getIsTestReplayAllEnabled()) { + + String kind_1 = getKind(); + String kind_2 = getKind(); + List<String> givenKindList = Arrays.asList(kind_1, kind_2); + List<String> totalRecordIds = this.createTestRecordForGivenCapacityAndKinds(500, 100, givenKindList); + + DummyRecordsHelper dummyRecordsHelper = new DummyRecordsHelper(); + + CloseableHttpResponse response = TestUtils.send("query/kinds", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", "?limit=10"); + assertEquals(HttpStatus.SC_OK, response.getCode()); + DummyRecordsHelper.QueryResultMock responseObject = dummyRecordsHelper.getQueryResultMockFromResponse(response); + List<String> kindList = new ArrayList<>(Arrays.asList(responseObject.results)); + kindList.add(kind_1); + kindList.add(kind_2); + + performValidationBeforeOrAfterReplay(kindList, givenKindList, "*:*:*:*", 2000); + String requestBody = ReplayUtils.createJsonWithOperationName("reindex"); + ReplayStatusResponseHelper replayStatusResponseHelper = this.performReplay(requestBody); + assertEquals("reindex", replayStatusResponseHelper.getOperation()); + + assertNull(replayStatusResponseHelper.getFilter()); + assertEquals(1, replayStatusResponseHelper.getStatus().size()); + performValidationBeforeOrAfterReplay(kindList, givenKindList, "*:*:*:*", 2000); + } + } + + @Test + @Timeout(2) + public void should_return_200_givenSingleKind() throws Exception { + + if (configUtils != null && configUtils.getIsTestReplayAllEnabled()) { + + String kind_1 = getKind(); + List<String> kindList = new ArrayList<>(); + kindList.add(kind_1); + List<String> ids = this.createTestRecordForGivenCapacityAndKinds(1, 1, kindList); + + performValidationBeforeOrAfterReplay(kindList, kindList, kind_1, 1); + String requestBody = ReplayUtils.createJsonWithKind("reindex", kindList); + ReplayStatusResponseHelper replayStatusResponseHelper = performReplay(requestBody); + + assertEquals("reindex", replayStatusResponseHelper.getOperation()); + assertEquals(1, replayStatusResponseHelper.getFilter().getKinds().size()); + assertEquals(1, replayStatusResponseHelper.getStatus().size()); + assertEquals(kind_1, replayStatusResponseHelper.getFilter().getKinds().get(0)); + + performValidationBeforeOrAfterReplay(kindList, kindList, kind_1, 1); + deleteRecords(ids); + } + } + + public List<String> createTestRecordForGivenCapacityAndKinds(int n, int factor, List<String> kinds) throws Exception { + + int totalRecordCount = n * kinds.size(); + long startTime = System.currentTimeMillis(); + List<String> totalIds = new ArrayList<>(); + for (String kind : kinds) { + int counter = n; + while (counter > 0) { + List<String> listIds = create_N_TestRecordForGivenKind(factor, kind); + totalIds = Stream.concat(totalIds.stream(), listIds.stream()).collect(Collectors.toList()); + counter -= factor; + Thread.sleep(1000); + } + + } + + Thread.sleep(40000); + return totalIds; + } + + @Test + public void should_return_400_when_givenEmptyJSONIsSent() throws Exception { + + String requestBody = ReplayUtils.createJsonEmpty(); + CloseableHttpResponse response = TestUtils.send("replay", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), requestBody, ""); + assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); + } + + protected List<String> create_N_TestRecordForGivenKind(int n, String kind) throws Exception { + + String json = ""; + List<String> ids = new ArrayList<>(n); + for (int i = 0; i < n; i++) { + String id1 = TenantUtils.getTenantName() + ":inttest:" + System.currentTimeMillis() + i; + json += RecordsApiAcceptanceTests.singleEntityBody(id1, "ash ketchum", kind, LEGAL_TAG_NAME); + if (i != n - 1) { + json += ","; + } + ids.add(id1); + } + + json = "[" + json + "]"; + + CloseableHttpResponse response = TestUtils.send("records", "PUT", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), json, ""); + + String responseJson = EntityUtils.toString(response.getEntity()); + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + Gson gson = new Gson(); + DummyRecordsHelper.CreateRecordResponse result = gson.fromJson( + responseJson, + DummyRecordsHelper.CreateRecordResponse.class + ); + assertEquals(n, result.recordCount); + assertEquals(n, result.recordIds.length); + assertEquals(n, result.recordIdVersions.length); + + return ids; + } + + private void performValidationBeforeOrAfterReplay(List<String> kinds, List<String> givenKindList, String kindType, int totalReplayAllRecord) throws Exception { + + long startTime = System.currentTimeMillis(); + CloseableHttpResponse response = null; + + int initialRecordCount = 0; + int countNoOfAPICalls = 0; + while ((initialRecordCount = getIndexedRecordCount(givenKindList)) != totalReplayAllRecord) { + if (countNoOfAPICalls > 10) + fail(); + + Thread.sleep(configUtils.getTimeoutForReplay()); + countNoOfAPICalls++; + } + + assertEquals(totalReplayAllRecord, initialRecordCount); + + System.out.println("Total count for Kind " + kindType + " is " + initialRecordCount); + + for (String kind : kinds) { + response = TestUtils.send(ReplayUtils.getIndexerUrl(), "index?kind=" + kind, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + Thread.sleep(1000); + } + + int countOfRecord = getIndexedRecordCount(givenKindList); + System.out.println("Total count for Kind " + kindType + " after deletion is " + countOfRecord); + assertEquals(0, countOfRecord); + + System.out.println("The end time for performValidationBeforeOrAfterReplay for KindType " + kindType + "is " + (System.currentTimeMillis() - startTime)); + } + + private ReplayStatusResponseHelper performReplay(String requestBody) throws Exception { + + CloseableHttpResponse response = TestUtils.send("replay", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), requestBody, ""); + + if (response.getCode() == HttpStatus.SC_INTERNAL_SERVER_ERROR) + System.out.println("Error in replay call " + ReplayUtils.getFieldFromResponse(response, "message")); + + assertEquals(202, response.getCode()); + + String replayId = ReplayUtils.getFieldFromResponse(response, "replayId"); + response = TestUtils.send("replay/status/", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", replayId); + + ReplayStatusResponseHelper replayStatusResponseHelper = ReplayUtils.getConvertedReplayStatusResponseFromResponse(response); + System.out.println("Total Number of Record to be Processed for Replay " + replayStatusResponseHelper.getTotalRecords()); + + int countNoOfAPICalls = 0; + + while (!replayStatusResponseHelper.getOverallState().equals("COMPLETED")) { + assertNotEquals("FAILED", replayStatusResponseHelper.getOverallState()); + response = TestUtils.send("replay/status/", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", replayId); + replayStatusResponseHelper = ReplayUtils.getConvertedReplayStatusResponseFromResponse(response); + if (replayStatusResponseHelper.getStatus() != null && !replayStatusResponseHelper.getStatus().isEmpty()) + System.out.println("Number of Record to be Processed for Replay " + replayStatusResponseHelper.getStatus().get(0).getProcessedRecords()); + + if (countNoOfAPICalls > 10) + fail(); + + Thread.sleep(configUtils.getTimeoutForReplay()); + countNoOfAPICalls++; + } + + assertEquals(replayId, replayStatusResponseHelper.getReplayId()); + return replayStatusResponseHelper; + } + + protected void deleteRecords(List<String> ids) { + + long startTime = System.currentTimeMillis(); + + ids.parallelStream().forEach((id) -> { + try { + TestUtils.send("records/" + id, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", ""); + } catch (Exception e) { + } + }); + + System.out.println("The totalTime for delete Records for size " + ids.size() + "is " + (System.currentTimeMillis() - startTime)); + } + + private int getIndexedRecordCount(List<String> kinds) throws Exception { + + int recordCountIndexed = 0; + for (String kind : kinds) { + String requestBody = ReplayUtils.getSearchCountQueryForKind(kind); + CloseableHttpResponse response = TestUtils.send(ReplayUtils.getSearchUrl(), "query", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), requestBody, ""); + recordCountIndexed += Integer.parseInt(ReplayUtils.getFieldFromResponse(response, "totalCount")); + + } + return recordCountIndexed; + } + + @Test + public void should_return_404_when_givenInvalidReplayID() throws Exception { + + CloseableHttpResponse response = TestUtils.send("replay/status/", "GET", HeaderUtils.getHeaders(TenantUtils.getTenantName(), testUtils.getToken()), "", "1234"); + String actualErrorMessage = ReplayUtils.getFieldFromResponse(response, "message"); + assertEquals("The replay ID 1234 is invalid.", actualErrorMessage); + assertEquals(404, response.getCode()); + } + + public static String getKind() throws InterruptedException { + + Thread.sleep(1); + return TenantUtils.getTenantName() + ":ds:inttest:1.0." + System.nanoTime(); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/ConfigUtils.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/ConfigUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..673183446e2756bba4e4fb09091e22c582ee7a86 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/ConfigUtils.java @@ -0,0 +1,67 @@ +/* + * Copyright 2020 Microsoft Corporation + * + * 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 + * + * https://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 org.opengroup.osdu.storage.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public class ConfigUtils { + + private final Properties properties; + + public ConfigUtils(String propertiesFileName) throws IOException { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + try (InputStream input = loader.getResourceAsStream(propertiesFileName)) { + properties = new Properties(); + // load a properties file + properties.load(input); + } + } + + public boolean getBooleanProperty(String propertyName, String defaultValue) { + String propValue = properties.getProperty(propertyName, defaultValue); + String envValue = getEnvValue(propertyName); + return (envValue == null || envValue.isEmpty()) ? Boolean.parseBoolean(propValue) + : Boolean.parseBoolean(envValue); + } + + public long getLongProperty(String propertyName, String defaultValue) { + String propValue = properties.getProperty(propertyName, defaultValue); + String envValue = getEnvValue(propertyName); + return (envValue == null || envValue.isEmpty()) ? Long.parseLong(propValue) + : Long.parseLong(envValue); + } + + public boolean getIsSchemaEndpointsEnabled() { + return !getBooleanProperty("schema.endpoints.disabled", "true"); + } + public boolean getIsCollaborationEnabled() { + return getBooleanProperty("collaboration.enabled", "false"); + } + + public boolean isTestReplayEnabled(){ return getBooleanProperty("test.replay.enabled", "false");} + + public boolean getIsTestReplayAllEnabled() { return getBooleanProperty("test.replayAll.enabled", "false");} + + public long getTimeoutForReplay() { return getLongProperty("test.replayAll.timeout", "60");} + + private static String getEnvValue(String propertyName) { + return System.getenv(propertyName.toUpperCase().replaceAll("\\.", "_")); + } + +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/CustomHttpClientResponseHandler.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/CustomHttpClientResponseHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..ac8478e6a6f13d2a52bbdaa949875c09c817280f --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/CustomHttpClientResponseHandler.java @@ -0,0 +1,50 @@ +/* + * Copyright 2020-2024 Google LLC + * Copyright 2020-2024 EPAM Systems, 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 org.opengroup.osdu.storage.util; + +import java.io.IOException; +import lombok.extern.slf4j.Slf4j; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.HttpClientResponseHandler; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.entity.StringEntity; + +@Slf4j +public class CustomHttpClientResponseHandler implements HttpClientResponseHandler<CloseableHttpResponse> { + + @Override + public CloseableHttpResponse handleResponse(ClassicHttpResponse classicHttpResponse) { + HttpEntity entity = classicHttpResponse.getEntity(); + if(classicHttpResponse.getCode() != HttpStatus.SC_NO_CONTENT) { + String body = ""; + try { + body = EntityUtils.toString(entity); + } catch (IOException | ParseException e) { + log.error("unable to parse response"); + } + HttpEntity newEntity = new StringEntity(body, ContentType.parse(entity.getContentType())); + classicHttpResponse.setEntity(newEntity); + } + return (CloseableHttpResponse) classicHttpResponse; + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/DummyRecordsHelper.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/DummyRecordsHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..e1116aefc394069088eca61e9aee2dd292ba09ec --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/DummyRecordsHelper.java @@ -0,0 +1,133 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.util; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.gson.Gson; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; + +public class DummyRecordsHelper { + + protected static final long NOW = System.currentTimeMillis(); + + public final String KIND = TenantUtils.getTenantName() + ":storage:inttest:1.0.0" + NOW; + + public QueryResultMock getQueryResultMockFromResponse(CloseableHttpResponse response) { + assertTrue(response.getEntity().getContentType().contains("application/json")); + String json; + try { + json = EntityUtils.toString(response.getEntity()); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + Gson gson = new Gson(); + return gson.fromJson(json, QueryResultMock.class); + } + + public RecordsMock getRecordsMockFromResponse(CloseableHttpResponse response) { + assertTrue(response.getEntity().getContentType().contains("application/json")); + String json = null; + try { + json = EntityUtils.toString(response.getEntity()); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + Gson gson = new Gson(); + return gson.fromJson(json, RecordsMock.class); + } + + public ConvertedRecordsMock getConvertedRecordsMockFromResponse(CloseableHttpResponse response) { + assertTrue(response.getEntity().getContentType().contains("application/json")); + String json = null; + try { + json = EntityUtils.toString(response.getEntity()); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + Gson gson = new Gson(); + return gson.fromJson(json, ConvertedRecordsMock.class); + } + + public class QueryResultMock { + public String cursor; + public String[] results; + } + + public class RecordsMock { + public RecordResultMock[] records; + public String[] invalidRecords; + public String[] retryRecords; + } + + public class ConvertedRecordsMock { + public RecordResultMock[] records; + public String[] notFound; + public List<RecordStatusMock> conversionStatuses; + } + + public class RecordStatusMock { + public String id; + public String status; + public List<String> errors; + } + + public class RecordResultMock { + public String id; + public String version; + public String kind; + public RecordAclMock acl; + public Map<String, Object> data; + public RecordLegalMock legal; + public RecordAncestryMock ancestry; + public Map<String, String> tags; + public String modifyTime; + public String modifyUser; + public String createTime; + public String createUser; + } + + public class RecordAclMock { + public String[] viewers; + public String[] owners; + } + + public class RecordLegalMock { + public String[] legaltags; + public String[] otherRelevantDataCountries; + } + + public class RecordAncestryMock { + public String[] parents; + } + + public class CreateRecordResponse { + public int recordCount; + public String[] recordIds; + public String[] skippedRecordIds; + public String[] recordIdVersions; + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/EntitlementsUtil.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/EntitlementsUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..a79a1e529e9365fb5bc690595b6d8877fabbec5d --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/EntitlementsUtil.java @@ -0,0 +1,68 @@ +/* + * Copyright 2020-2023 Google LLC + * Copyright 2020-2023 EPAM Systems, 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 org.opengroup.osdu.storage.util; + +import com.google.gson.JsonObject; +import java.util.Map; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.opengroup.osdu.core.common.http.HttpRequest; + +public class EntitlementsUtil { + + public static final String GROUPS_ENDPOINT = "groups"; + + public static CloseableHttpResponse createEntitlementsGroup(Map<String, String> headers, + String groupName, + String groupDescription) throws Exception { + String body = getCreateGroupBody(groupName, groupDescription); + CloseableHttpResponse response = TestUtils.send( + getEntitlementsUrl(), + GROUPS_ENDPOINT, + HttpRequest.POST, + headers, + body, + "" + ); + return response; + } + + public static CloseableHttpResponse deleteEntitlementsGroup(Map<String, String> headers, + String groupEmail) + throws Exception { + CloseableHttpResponse response = TestUtils.send( + getEntitlementsUrl(), + GROUPS_ENDPOINT, + HttpRequest.DELETE, + headers, + "", + groupEmail + ); + return response; + } + + protected static String getCreateGroupBody(String name, String groupDescription) { + JsonObject groupBody = new JsonObject(); + groupBody.addProperty("name", name); + groupBody.addProperty("description", groupDescription); + return groupBody.toString(); + } + + protected static String getEntitlementsUrl() { + return System.getProperty("ENTITLEMENTS_URL", System.getenv("ENTITLEMENTS_URL")); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/HeaderUtils.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/HeaderUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..60d38f2d510c79038f2328b5774d865c600a8555 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/HeaderUtils.java @@ -0,0 +1,91 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.util; + +import com.google.api.client.util.Strings; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class HeaderUtils { + + protected static final String COLLABORATION_HEADER = "x-collaboration"; + + public static Map<String, String> getHeaders(String tenantName, String token) { + Map<String, String> headers = new HashMap<>(); + if (tenantName == null || tenantName.isEmpty()) { + tenantName = TenantUtils.getTenantName(); + } + headers.put("Data-Partition-Id", tenantName); + headers.put("Authorization", token); + + final String correlationId = UUID.randomUUID().toString(); + System.out.printf("Using correlation-id for the request: %s \n", correlationId); + headers.put("correlation-id", correlationId); + + return headers; + } + + public static Map<String, String> getHeadersWithxCollaboration(String collaborationId, + String applicationName, String tenantName, String token) { + Map<String, String> headers = getHeaders(tenantName, token); + if (!Strings.isNullOrEmpty(collaborationId)) { + headers.put(COLLABORATION_HEADER, + "id=" + collaborationId + ",application=" + applicationName); + } + return headers; + } + + public static Map<String, String> getHeadersWithxCollaborationWithoutId(String collaborationId, + String applicationName, String tenantName, String token) { + Map<String, String> headers = getHeaders(tenantName, token); + if (Strings.isNullOrEmpty(collaborationId)) { + headers.put(COLLABORATION_HEADER, "application=" + applicationName); + }else { + headers.put(COLLABORATION_HEADER, "id=" + collaborationId + ",application=" + applicationName); + } + return headers; + } + + public static Map<String, String> getHeadersWithoutAuth(String tenantName, String token) { + Map<String, String> headers = new HashMap<>(); + if (tenantName == null || tenantName.isEmpty()) { + tenantName = TenantUtils.getTenantName(); + } + headers.put("Data-Partition-Id", tenantName); + + final String correlationId = UUID.randomUUID().toString(); + System.out.printf("Using correlation-id for the request: %s \n", correlationId); + headers.put("correlation-id", correlationId); + + return headers; + } + + public static Map<String, String> getHeadersWithoutDataPartitionId(String tenantName, + String token) { + Map<String, String> headers = new HashMap<>(); + if (tenantName == null || tenantName.isEmpty()) { + tenantName = TenantUtils.getTenantName(); + } + headers.put("Authorization", token); + + final String correlationId = UUID.randomUUID().toString(); + System.out.printf("Using correlation-id for the request: %s \n", correlationId); + headers.put("correlation-id", correlationId); + + return headers; + } + +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/LegalTagUtils.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/LegalTagUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..f24a1691cb5306b83af24f457811662f24b4f9a0 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/LegalTagUtils.java @@ -0,0 +1,78 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.http.HttpStatus; + +public class LegalTagUtils { + public static String createRandomName() { + return TenantUtils.getTenantName() + "-storage-" + System.currentTimeMillis(); + } + + public static CloseableHttpResponse create(String legalTagName, String token) throws Exception { + return create("US", legalTagName, "2099-01-25", "Public Domain Data", token); + } + + protected static CloseableHttpResponse create(String countryOfOrigin, String name, String expDate, String dataType, String token) + throws Exception { + String body = getBody(countryOfOrigin, name, expDate, dataType); + CloseableHttpResponse response = TestUtils.send(getLegalUrl(), "legaltags", "POST", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), body, + ""); + + assertEquals(HttpStatus.SC_CREATED, response.getCode()); + Thread.sleep(100); + return response; + } + + public static CloseableHttpResponse delete(String legalTagName, String token) throws Exception { + return TestUtils.send(getLegalUrl(), "legaltags/" + legalTagName, "DELETE", HeaderUtils.getHeaders(TenantUtils.getTenantName(), token), "", ""); + } + + protected static String getLegalUrl() { + String legalUrl = System.getProperty("LEGAL_URL", System.getenv("LEGAL_URL")); + if (legalUrl == null || legalUrl.contains("-null")) { + legalUrl = "https://os-legal-dot-opendes.appspot.com/api/legal/v1/"; + } + return legalUrl; + } + + protected static String getBody(String countryOfOrigin, String name, String expDate, String dataType) { + + JsonArray coo = new JsonArray(); + coo.add(countryOfOrigin); + + JsonObject properties = new JsonObject(); + properties.add("countryOfOrigin", coo); + properties.addProperty("contractId", "A1234"); + properties.addProperty("expirationDate", expDate); + properties.addProperty("dataType", dataType); + properties.addProperty("originator", "MyCompany"); + properties.addProperty("securityClassification", "Public"); + properties.addProperty("exportClassification", "EAR99"); + properties.addProperty("personalData", "No Personal Data"); + + JsonObject tag = new JsonObject(); + tag.addProperty("name", name); + tag.addProperty("description", "test for " + name); + tag.add("properties", properties); + + return tag.toString(); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/OpenIDTokenProvider.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/OpenIDTokenProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..d87386ad095ea136e37baf33b089b5e25c461f84 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/OpenIDTokenProvider.java @@ -0,0 +1,116 @@ +/* + * Copyright 2020-2023 Google LLC + * Copyright 2020-2023 EPAM Systems, 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 + * + * https://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 org.opengroup.osdu.storage.util; + +import com.nimbusds.oauth2.sdk.AuthorizationGrant; +import com.nimbusds.oauth2.sdk.ClientCredentialsGrant; +import com.nimbusds.oauth2.sdk.ParseException; +import com.nimbusds.oauth2.sdk.Scope; +import com.nimbusds.oauth2.sdk.TokenRequest; +import com.nimbusds.oauth2.sdk.TokenResponse; +import com.nimbusds.oauth2.sdk.auth.ClientAuthentication; +import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic; +import com.nimbusds.oauth2.sdk.auth.Secret; +import com.nimbusds.oauth2.sdk.id.ClientID; +import com.nimbusds.openid.connect.sdk.OIDCTokenResponseParser; +import java.io.IOException; +import java.net.URI; +import java.util.Objects; +import net.minidev.json.JSONObject; +import org.opengroup.osdu.storage.util.conf.OpenIDProviderConfig; + +public class OpenIDTokenProvider { + + private static final OpenIDProviderConfig openIDProviderConfig = OpenIDProviderConfig.Instance(); + private static final String ID_TOKEN = "id_token"; + private final AuthorizationGrant clientGrant = new ClientCredentialsGrant(); + private final URI tokenEndpointURI; + private final Scope scope; + private final ClientAuthentication clientAuthentication; + private final ClientAuthentication noAccessClientAuthentication; + private final ClientAuthentication dataRootAuthentication; + + public OpenIDTokenProvider() { + this.tokenEndpointURI = openIDProviderConfig.getProviderMetadata().getTokenEndpointURI(); + this.scope = new Scope(openIDProviderConfig.getScopes()); + this.clientAuthentication = + new ClientSecretBasic( + new ClientID(openIDProviderConfig.getClientId()), + new Secret(openIDProviderConfig.getClientSecret())); + this.noAccessClientAuthentication = + new ClientSecretBasic( + new ClientID(openIDProviderConfig.getNoAccessClientId()), + new Secret(openIDProviderConfig.getNoAccessClientSecret())); + this.dataRootAuthentication = + new ClientSecretBasic( + new ClientID(openIDProviderConfig.getDataRootClientId()), + new Secret(openIDProviderConfig.getDataRootClientSecret())); + } + + public String getToken() { + try { + TokenRequest request = + new TokenRequest( + this.tokenEndpointURI, this.clientAuthentication, this.clientGrant, this.scope); + return requestToken(request); + } catch (ParseException | IOException e) { + throw new RuntimeException("Unable get credentials from INTEGRATION_TESTER variables", e); + } + } + + public String getNoAccessToken() { + try { + TokenRequest request = + new TokenRequest( + this.tokenEndpointURI, + this.noAccessClientAuthentication, + this.clientGrant, + this.scope); + return requestToken(request); + } catch (ParseException | IOException e) { + throw new RuntimeException("Unable get credentials from INTEGRATION_TESTER variables", e); + } + } + + public String getDataRootToken() { + try { + TokenRequest request = + new TokenRequest( + this.tokenEndpointURI, this.dataRootAuthentication, this.clientGrant, this.scope); + return requestToken(request); + } catch (ParseException | IOException e) { + throw new RuntimeException("Unable get credentials from INTEGRATION_TESTER variables", e); + } + } + + private String requestToken(TokenRequest tokenRequest) throws ParseException, IOException { + + TokenResponse parse = OIDCTokenResponseParser.parse(tokenRequest.toHTTPRequest().send()); + + if (!parse.indicatesSuccess()) { + throw new RuntimeException("Unable get credentials from INTEGRATION_TESTER variables"); + } + + JSONObject jsonObject = parse.toSuccessResponse().toJSONObject(); + String idTokenValue = jsonObject.getAsString(ID_TOKEN); + if (Objects.isNull(idTokenValue) || idTokenValue.isEmpty()) { + throw new RuntimeException("Unable get credentials from INTEGRATION_TESTER variables"); + } + return idTokenValue; + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/RecordUtil.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/RecordUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..bcf1875d1f246105c648d5244a49a742b2a0391f --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/RecordUtil.java @@ -0,0 +1,795 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.util; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import org.opengroup.osdu.core.common.Constants; + +public class RecordUtil { + private static final String UNIT_OF_MEASURE_ID = "unitOfMeasureID"; + + public static String createDefaultJsonRecord(String id, String kind, String legalTag) { + JsonObject record = getDefaultRecordWithDefaultData(id, kind, legalTag); + JsonArray records = new JsonArray(); + records.add(record); + return records.toString(); + } + + public static String createRecordWithDuplicateAclAndLegaltags(String id, String kind, String legalTag) { + JsonObject record = getDefaultRecordWithDefaultDataAndDuplicateAclAndLegaltags(id, kind, legalTag); + JsonArray records = new JsonArray(); + records.add(record); + return records.toString(); + } + + public static String createDefaultJsonRecordWithParentId(String id, String kind, String legalTag, String parentId) { + JsonObject record = getDefaultRecordWithDefaultData(id, kind, legalTag); + JsonObject ancestryObject = new JsonObject(); + JsonArray parents = new JsonArray(); + parents.add(parentId); + ancestryObject.add("parents", parents); + record.add("ancestry", ancestryObject); + JsonArray records = new JsonArray(); + records.add(record); + return records.toString(); + } + + public static String createDefaultJsonRecords(int recordsCount, String id, String kind, String legalTag) { + JsonArray records = new JsonArray(); + for (int i = 0; i < recordsCount; i++) { + JsonObject record = getDefaultRecordWithDefaultData(id +i, kind, legalTag); + records.add(record); + } + return records.toString(); + } + + public static String createJsonRecordWithData(String id, String kind, String legalTag, String data) { + + JsonObject dataJson = new JsonObject(); + dataJson.addProperty("custom", data); + dataJson.addProperty("score-int", 58377304471659395L); + dataJson.addProperty("score-double", 58377304.471659395); + + JsonObject record = getRecordWithInputData(id, kind, legalTag, dataJson); + + JsonArray records = new JsonArray(); + records.add(record); + + return records.toString(); + } + + public static String createJsonRecordWithEntV2OnlyAcl(String id, String kind, String legalTag, String data) { + JsonObject dataJson = new JsonObject(); + dataJson.addProperty("custom", data); + dataJson.addProperty("score-int", 58377304471659395L); + dataJson.addProperty("score-double", 58377304.471659395); + + JsonObject record = getRecordWithInputDataAndAcl(id, kind, legalTag, dataJson); + + JsonArray records = new JsonArray(); + records.add(record); + + return records.toString(); + + } + + public static String createJsonRecordWithReference(int recordsCount, String id, String kind, String legalTag, String fromCrs, String conversionType) { + + JsonArray records = new JsonArray(); + + for (int i = 0; i < recordsCount; i++) { + + JsonObject data = new JsonObject(); + data.addProperty("X", 16.00); + data.addProperty("Y", 10.00); + data.addProperty("Z", 0.0); + + JsonArray propertyNames = new JsonArray(); + propertyNames.add("X"); + propertyNames.add("Y"); + propertyNames.add("Z"); + + JsonObject meta = new JsonObject(); + meta.addProperty(Constants.KIND, conversionType); + meta.addProperty(Constants.PERSISTABLE_REFERENCE, fromCrs); + meta.add(Constants.PROPERTY_NAMES, propertyNames); + + JsonArray metaBlocks = new JsonArray(); + metaBlocks.add(meta); + + JsonObject record = getRecordWithInputData(id + i, kind, legalTag, data); + record.add(Constants.META, metaBlocks); + + records.add(record); + } + + return records.toString(); + } + + public static String createJsonRecordMissingValue(int recordsCount, String id, String kind, String legalTag, String fromCrs, String conversionType) { + + JsonArray records = new JsonArray(); + + for (int i = 0; i < recordsCount; i++) { + + JsonObject data = new JsonObject(); + data.addProperty("X", 16.00); + data.addProperty("Z", 0.0); + + JsonArray propertyNames = new JsonArray(); + propertyNames.add("X"); + propertyNames.add("Y"); + propertyNames.add("Z"); + + JsonObject meta = new JsonObject(); + meta.addProperty(Constants.KIND, conversionType); + meta.addProperty(Constants.PERSISTABLE_REFERENCE, fromCrs); + meta.add(Constants.PROPERTY_NAMES, propertyNames); + + JsonArray metaBlocks = new JsonArray(); + metaBlocks.add(meta); + + JsonObject record = getRecordWithInputData(id + i, kind, legalTag, data); + record.add(Constants.META, metaBlocks); + + records.add(record); + } + + return records.toString(); + } + + public static String createJsonRecordNoMetaBlock(int recordsCount, String id, String kind, String legalTag) { + + JsonArray records = new JsonArray(); + + for (int i = 0; i < recordsCount; i++) { + JsonObject data = new JsonObject(); + data.addProperty("X", 16.00); + data.addProperty("Y", 16.00); + data.addProperty("Z", 0.0); + + JsonObject record = getRecordWithInputData(id + i, kind, legalTag, data); + records.add(record); + } + + return records.toString(); + } + + public static String createJsonRecordsWithDateFormat(int recordsCount, String id, String kind, String legalTag, String format, String propertyName, String date, String persistableReference) { + + JsonArray records = new JsonArray(); + + for (int i = 0; i < recordsCount; i++) { + JsonObject data = new JsonObject(); + data.addProperty(propertyName, date); + + JsonArray propertyNames = new JsonArray(); + propertyNames.add(propertyName); + + JsonObject meta = new JsonObject(); + meta.addProperty(Constants.PERSISTABLE_REFERENCE, persistableReference); + meta.addProperty(Constants.KIND, "DateTime"); + meta.add(Constants.PROPERTY_NAMES, propertyNames); + + JsonArray metas = new JsonArray(); + metas.add(meta); + + JsonObject record = getRecordWithInputData(id + i, kind, legalTag, data); + record.add(Constants.META, metas); + records.add(record); + } + + return records.toString(); + } + + public static String createJsonRecordWithNestedProperty(int recordsNumber, String id, String kind, String legalTag, String fromCrs, String conversionType) { + + JsonArray records = new JsonArray(); + + for (int i = 0; i < 8 + recordsNumber; i++) { + + JsonArray pointValues1 = new JsonArray(); + pointValues1.add(16.00); + pointValues1.add(10.00); + JsonArray pointValues2 = new JsonArray(); + pointValues2.add(16.00); + pointValues2.add(10.00); + JsonArray points = new JsonArray(); + points.add(pointValues1); + points.add(pointValues2); + + JsonObject nestedProperty = new JsonObject(); + nestedProperty.addProperty("crsKey", "Native"); + nestedProperty.add(Constants.POINTS, points); + + JsonObject data = new JsonObject(); + data.addProperty("message", "integration-test-record"); + data.add("projectOutlineLocalGeographic", nestedProperty); + + JsonArray propertyNames = new JsonArray(); + propertyNames.add("projectOutlineLocalGeographic"); + + JsonObject meta = new JsonObject(); + meta.addProperty(Constants.KIND, conversionType); + meta.addProperty(Constants.PERSISTABLE_REFERENCE, fromCrs); + meta.add(Constants.PROPERTY_NAMES, propertyNames); + + JsonArray metaBlocks = new JsonArray(); + metaBlocks.add(meta); + + JsonObject record = getRecordWithInputData(id + i, kind, legalTag, data); + record.add(Constants.META, metaBlocks); + + records.add(record); + } + + return records.toString(); + } + + public static String createJsonRecordWithNestedArrayOfProperties(int recordsNumber, String id, String kind, String legalTag, String fromRef, String conversionType, String unitOfMeasureID) { + JsonArray records = new JsonArray(); + + for (int i = 12; i < 12 + recordsNumber; i++) { + + JsonArray nestedArray = new JsonArray(); + JsonObject item1 = new JsonObject(); + item1.addProperty("measuredDepth", 10.0); + item1.addProperty("otherField", "testValue1"); + JsonObject item2 = new JsonObject(); + item2.addProperty("measuredDepth", 20.0); + item2.addProperty("otherField", "testValue2"); + nestedArray.add(item1); + nestedArray.add(item2); + + JsonObject record = createJsonObjectRecordWithNestedArray(nestedArray, id + i, kind, legalTag, conversionType, fromRef, "markers[].measuredDepth", unitOfMeasureID); + + records.add(record); + } + + return records.toString(); + } + + public static String createJsonRecordWithNestedArrayOfPropertiesAndInvalidValues(int recordsNumber, String id, String kind, String legalTag, String fromRef, String conversionType, String unitOfMeasureID) { + JsonArray records = new JsonArray(); + + for (int i = 12; i < 12 + recordsNumber; i++) { + + JsonArray nestedArray = new JsonArray(); + JsonObject item1 = new JsonObject(); + item1.addProperty("measuredDepth", 10.0); + item1.addProperty("otherField", "testValue1"); + JsonObject item2 = new JsonObject(); + item2.addProperty("measuredDepth", "invalidValue"); + item2.addProperty("otherField", "testValue2"); + nestedArray.add(item1); + nestedArray.add(item2); + + JsonObject record = createJsonObjectRecordWithNestedArray(nestedArray, id + i, kind, legalTag, conversionType, fromRef, "markers[].measuredDepth", unitOfMeasureID); + + records.add(record); + } + + return records.toString(); + } + + public static String createJsonRecordWithInhomogeneousNestedArrayOfProperties(int recordsNumber, String id, String kind, String legalTag, String fromRef, String conversionType, String unitOfMeasureID) { + JsonArray records = new JsonArray(); + + for (int i = 13; i < 13 + recordsNumber; i++) { + + JsonArray nestedArray = new JsonArray(); + JsonObject item1 = new JsonObject(); + item1.addProperty("measuredDepth", 10.0); + item1.addProperty("otherField", "testValue1"); + JsonObject item2 = new JsonObject(); + item2.addProperty("measuredDepth", 20.0); + item2.addProperty("otherField", "testValue2"); + nestedArray.add(item1); + nestedArray.add(item2); + + JsonObject record = createJsonObjectRecordWithNestedArray(nestedArray, id + i, kind, legalTag, conversionType, fromRef, "markers[1].measuredDepth", unitOfMeasureID); + + records.add(record); + } + + return records.toString(); + } + + public static String createJsonRecordWithInhomogeneousNestedArrayOfPropertiesAndInvalidValues(int recordsNumber, String id, String kind, String legalTag, String fromRef, String conversionType, String unitOfMeasureID) { + JsonArray records = new JsonArray(); + + for (int i = 13; i < 13 + recordsNumber; i++) { + + JsonArray nestedArray = new JsonArray(); + JsonObject item1 = new JsonObject(); + item1.addProperty("measuredDepth", 10.0); + item1.addProperty("otherField", "testValue1"); + JsonObject item2 = new JsonObject(); + item2.addProperty("measuredDepth", "invalidValue"); + item2.addProperty("otherField", "testValue2"); + nestedArray.add(item1); + nestedArray.add(item2); + + JsonObject record = createJsonObjectRecordWithNestedArray(nestedArray, id + i, kind, legalTag, conversionType, fromRef, "markers[1].measuredDepth", unitOfMeasureID); + + records.add(record); + } + + return records.toString(); + } + + public static String createJsonRecordWithInhomogeneousNestedArrayOfPropertiesAndIndexOutOfBoundary(int recordsNumber, String id, String kind, String legalTag, String fromRef, String conversionType, String unitOfMeasureID) { + JsonArray records = new JsonArray(); + + for (int i = 13; i < 13 + recordsNumber; i++) { + + JsonArray nestedArray = new JsonArray(); + JsonObject item1 = new JsonObject(); + item1.addProperty("measuredDepth", 10.0); + item1.addProperty("otherField", "testValue1"); + JsonObject item2 = new JsonObject(); + item2.addProperty("measuredDepth", "20.0"); + item2.addProperty("otherField", "testValue2"); + nestedArray.add(item1); + nestedArray.add(item2); + + JsonObject record = createJsonObjectRecordWithNestedArray(nestedArray, id + i, kind, legalTag, conversionType, fromRef, "markers[2].measuredDepth", unitOfMeasureID); + + records.add(record); + } + + return records.toString(); + } + + private static JsonObject createJsonObjectRecordWithNestedArray(JsonArray nestedArray, String id, String kind, String legalTag, String conversionType, String fromRef, String propertyName, String unitOfMeasureID) { + JsonObject data = new JsonObject(); + data.addProperty("message", "integration-test-record"); + data.add("markers", nestedArray); + + JsonArray propertyNames = new JsonArray(); + propertyNames.add(propertyName); + + JsonObject meta = new JsonObject(); + meta.addProperty(Constants.KIND, conversionType); + meta.addProperty(Constants.PERSISTABLE_REFERENCE, fromRef); + meta.addProperty(UNIT_OF_MEASURE_ID, unitOfMeasureID); + meta.add(Constants.PROPERTY_NAMES, propertyNames); + + JsonArray metaBlocks = new JsonArray(); + metaBlocks.add(meta); + + JsonObject record = getRecordWithInputData(id, kind, legalTag, data); + record.add(Constants.META, metaBlocks); + + return record; + } + + private static JsonObject createJsonObjectRecordWithNestedArray(JsonArray nestedArray, String id, String kind, String legalTag, String conversionType, String fromRef, String unitOfMeasureID) { + JsonObject data = new JsonObject(); + data.addProperty("message", "integration-test-record"); + data.add("markers", nestedArray); + + JsonArray propertyNames = new JsonArray(); + propertyNames.add("markers[].measuredDepth"); + + JsonObject meta = new JsonObject(); + meta.addProperty(Constants.KIND, conversionType); + meta.addProperty(Constants.PERSISTABLE_REFERENCE, fromRef); + meta.addProperty(UNIT_OF_MEASURE_ID, unitOfMeasureID); + meta.add(Constants.PROPERTY_NAMES, propertyNames); + + JsonArray metaBlocks = new JsonArray(); + metaBlocks.add(meta); + + JsonObject record = getRecordWithInputData(id, kind, legalTag, data); + record.add(Constants.META, metaBlocks); + + return record; + } + + public static String createJsonRecordWithMultiplePairOfCoordinates(int recordsNumber, String id, String kind, String legalTag, String fromCrs, String conversionType) { + JsonArray records = new JsonArray(); + for (int i = 0; i < recordsNumber; i++) { + + JsonObject data = new JsonObject(); + data.addProperty("X", 16.00); + data.addProperty("Y", 10.00); + data.addProperty("LON", 16.00); + data.addProperty("LAT", 10.00); + + JsonArray propertyNames = new JsonArray(); + propertyNames.add("X"); + propertyNames.add("Y"); + propertyNames.add("LON"); + propertyNames.add("LAT"); + + JsonObject meta = new JsonObject(); + meta.addProperty(Constants.KIND, conversionType); + meta.addProperty(Constants.PERSISTABLE_REFERENCE, fromCrs); + meta.add(Constants.PROPERTY_NAMES, propertyNames); + + JsonArray metaBlocks = new JsonArray(); + metaBlocks.add(meta); + + JsonObject record = getDefaultRecord(id + i, kind, legalTag); + record.add(Constants.DATA, data); + record.add(Constants.META, metaBlocks); + + records.add(record); + } + return records.toString(); + } + + public static String createJsonRecordWithAsIngestedCoordinates(int recordsNumber, String id, String kind, String legalTag, String prCRS, String prUNITZ, String geometryType, String attributeType) { + JsonArray records = new JsonArray(); + Gson gson = new Gson(); + for (int i = 0; i < recordsNumber; i++) { + JsonObject data = new JsonObject(); + + JsonObject properties = new JsonObject(); + JsonObject geometry = new JsonObject(); + + switch (geometryType) { + case Constants.ANY_CRS_POINT: + geometry.add(Constants.COORDINATES, gson.toJsonTree(createCoordinates1(1,2))); + break; + case Constants.ANY_CRS_MULTIPOINT: + case Constants.ANY_CRS_LINE_STRING: + geometry.add(Constants.COORDINATES, gson.toJsonTree(createCoordinates2(1,2))); + break; + case Constants.ANY_CRS_MULTILINE_STRING: + case Constants.ANY_CRS_POLYGON: + geometry.add(Constants.COORDINATES, gson.toJsonTree(createCoordinates3(1,2))); + break; + case Constants.ANY_CRS_MULTIPOLYGON: + geometry.add(Constants.COORDINATES, gson.toJsonTree(createCoordinates4(1,2))); + break; + case Constants.ANY_CRS_GEOMETRY_COLLECTION: + JsonArray geometries = new JsonArray(); + JsonObject geometriesObj = new JsonObject(); + geometriesObj.addProperty(Constants.BBOX, (Boolean) null); + geometriesObj.addProperty(Constants.TYPE, Constants.POINT); + geometriesObj.add(Constants.COORDINATES, gson.toJsonTree(createCoordinates1(1,2))); + geometries.add(geometriesObj); + geometry.add(Constants.GEOMETRIES, geometries); + break; + } + geometry.addProperty(Constants.TYPE, geometryType); + geometry.addProperty(Constants.BBOX, (Boolean) null); + + JsonArray features = new JsonArray(); + JsonObject feature = new JsonObject(); + feature.addProperty(Constants.BBOX, (Boolean) null); + feature.addProperty(Constants.TYPE, Constants.ANY_CRS_FEATURE); + feature.add(Constants.PROPERTIES, properties); + feature.add(Constants.GEOMETRY, geometry); + features.add(feature); + + JsonObject asIngestedCoordinates = new JsonObject(); + asIngestedCoordinates.addProperty(Constants.PERSISTABLE_REFERENCE_CRS, prCRS); + asIngestedCoordinates.addProperty(Constants.PERSISTABLE_REFERENCE_UNIT_Z, prUNITZ); + asIngestedCoordinates.addProperty(Constants.TYPE, Constants.ANY_CRS_FEATURE_COLLECTION); + asIngestedCoordinates.add(Constants.PROPERTIES, properties); + asIngestedCoordinates.add(Constants.FEATURES, features); + + JsonObject validAttribute = new JsonObject(); + validAttribute.add(Constants.AS_INGESTED_COORDINATES, asIngestedCoordinates); + + data.add(attributeType, validAttribute); + + JsonObject record = getDefaultRecord(id + i, kind, legalTag); + record.add(Constants.DATA, data); + records.add(record); + } + return records.toString(); + } + + public static String createJsonRecordWithInvalidAsIngestedCoordinates(int recordsNumber, String id, String kind, String legalTag, String prCRS, String prUNITZ, String geometryType, String attributeType) { + JsonArray records = new JsonArray(); + Gson gson = new Gson(); + for (int i = 0; i < recordsNumber; i++) { + JsonObject data = new JsonObject(); + JsonObject geometry = new JsonObject(); + geometry.add(Constants.COORDINATES, gson.toJsonTree(createCoordinates1(1,2))); + geometry.addProperty(Constants.TYPE, geometryType); + geometry.addProperty(Constants.BBOX, (Boolean) null); + + JsonArray features = new JsonArray(); + JsonObject feature = new JsonObject(); + feature.addProperty(Constants.BBOX, (Boolean) null); + feature.addProperty(Constants.TYPE, Constants.ANY_CRS_FEATURE); + feature.add(Constants.GEOMETRY, geometry); + features.add(feature); + + JsonObject asIngestedCoordinates = new JsonObject(); + asIngestedCoordinates.addProperty(Constants.PERSISTABLE_REFERENCE_CRS, prCRS); + asIngestedCoordinates.addProperty(Constants.PERSISTABLE_REFERENCE_UNIT_Z, prUNITZ); + asIngestedCoordinates.addProperty(Constants.TYPE, Constants.ANY_CRS_FEATURE_COLLECTION); + asIngestedCoordinates.add("features1", features); + + JsonObject validAttribute = new JsonObject(); + validAttribute.add(Constants.AS_INGESTED_COORDINATES, asIngestedCoordinates); + + data.add(attributeType, validAttribute); + + JsonObject record = getDefaultRecord(id + i, kind, legalTag); + record.add(Constants.DATA, data); + records.add(record); + } + return records.toString(); + } + + public static JsonObject buildUpdateTagBody(String id, String op, String val) { + JsonArray records = new JsonArray(); + records.add(id); + + JsonArray value = new JsonArray(); + value.add(val); + JsonObject operation = new JsonObject(); + operation.addProperty("op", op); + operation.addProperty("path", "/tags"); + operation.add("value", value); + JsonArray ops = new JsonArray(); + ops.add(operation); + + JsonObject query = new JsonObject(); + query.add("ids", records); + + JsonObject updateBody = new JsonObject(); + updateBody.add("query", query); + updateBody.add("ops", ops); + + return updateBody; + } + + public static String createJsonRecordWithWGS84Coordinates(int recordsNumber, String id, String kind, String legalTag, String prCRS, String prUNITZ, String geometryType, String attributeType) { + JsonArray records = new JsonArray(); + Gson gson = new Gson(); + for (int i = 0; i < recordsNumber; i++) { + JsonObject data = new JsonObject(); + + JsonArray coordinates = new JsonArray(); + coordinates.add(313405.9477893702); + coordinates.add(6544797.620047403); + coordinates.add(6.561679790026246); + + JsonObject geometry = new JsonObject(); + geometry.add(Constants.COORDINATES, gson.toJsonTree(createCoordinates1(1,2))); + geometry.addProperty(Constants.TYPE, geometryType); + geometry.addProperty(Constants.BBOX, (Boolean) null); + + JsonArray features = new JsonArray(); + JsonObject feature = new JsonObject(); + feature.addProperty(Constants.BBOX, (Boolean) null); + feature.addProperty(Constants.TYPE, Constants.ANY_CRS_FEATURE); + feature.add(Constants.GEOMETRY, geometry); + features.add(feature); + + JsonObject asIngestedCoordinates = new JsonObject(); + asIngestedCoordinates.addProperty(Constants.PERSISTABLE_REFERENCE_CRS, prCRS); + asIngestedCoordinates.addProperty(Constants.PERSISTABLE_REFERENCE_UNIT_Z, prUNITZ); + asIngestedCoordinates.addProperty(Constants.TYPE, Constants.ANY_CRS_FEATURE_COLLECTION); + asIngestedCoordinates.add(Constants.FEATURES, features); + + JsonArray wgsCoordinates = new JsonArray(); + wgsCoordinates.add(5.7500000010406245); + wgsCoordinates.add(59.000000000399105); + wgsCoordinates.add(1.9999999999999998); + + JsonObject wgsGeometry = new JsonObject(); + wgsGeometry.add(Constants.COORDINATES, wgsCoordinates); + wgsGeometry.addProperty(Constants.TYPE, Constants.POINT); + wgsGeometry.addProperty(Constants.BBOX, (Boolean) null); + + JsonArray wgsFeatures = new JsonArray(); + JsonObject wgsFeature = new JsonObject(); + wgsFeature.addProperty(Constants.BBOX, (Boolean) null); + wgsFeature.add(Constants.GEOMETRY, wgsGeometry); + wgsFeatures.add(wgsFeature); + + JsonObject wgs84Coordinates = new JsonObject(); + wgs84Coordinates.addProperty(Constants.PERSISTABLE_REFERENCE_CRS, (Boolean) null); + wgs84Coordinates.addProperty(Constants.PERSISTABLE_REFERENCE_UNIT_Z, prUNITZ); + wgs84Coordinates.addProperty(Constants.TYPE, Constants.FEATURE_COLLECTION); + wgs84Coordinates.add(Constants.FEATURES, wgsFeatures); + + JsonObject validAttribute = new JsonObject(); + validAttribute.add(Constants.AS_INGESTED_COORDINATES, asIngestedCoordinates); + validAttribute.add(Constants.WGS84_COORDINATES, wgs84Coordinates); + + data.add(attributeType, validAttribute); + + JsonObject record = getDefaultRecord(id + i, kind, legalTag); + record.add(Constants.DATA, data); + records.add(record); + } + return records.toString(); + } + + public static String createJsonRecordWithCustomAcl(String newRecordId, String kind, + String legalTag, String dataGroupEmail) { + JsonObject record = getDefaultRecordWithCustomAcl(newRecordId, kind, legalTag, dataGroupEmail); + JsonObject dataJson = new JsonObject(); + dataJson.addProperty("score-int", 58377304471659395L); + dataJson.addProperty("score-double", 58377304.471659395); + record.add("data", dataJson); + JsonArray records = new JsonArray(); + records.add(record); + return records.toString(); + } + + private static double[][][][] createCoordinates4(int mode, int dimension) { + double[][][][] pts_s = new double[2][2][5][dimension]; + for (int l = 0; l < 2; l++) { + for (int k = 0; k < pts_s[0].length; k++) { + double[][][] pts = createCoordinates3(mode, dimension); + for (int j = 0; j < pts[0].length; j++) { + for (int i = 0; i < dimension; i++) { + pts_s[l][k][j][i] = pts[k][j][i] + (k + l) * 4; + } + } + } + } + return pts_s; + } + + private static double[] createCoordinates1(int mode, int dimension) { + double[] pt_ac = new double[]{500000, 6500000, 1000}; + double[] pt_gj = new double[]{3, 60, 2000}; + double[] pts = new double[dimension]; + if (mode == 0) System.arraycopy(pt_gj, 0, pts, 0, dimension); + else System.arraycopy(pt_ac, 0, pts, 0, dimension); + return pts; + } + + private static double[][] createCoordinates2(int mode, int dimension) { + double[][] s = new double[][]{{-1, 1, 10}, {1, 1, 10}, {1, -1, 20}, {-1, -1, 20}, {-1, 1, 10}}; + double[][] pts = new double[5][dimension]; + double[] pt = createCoordinates1(mode, dimension); + for (int j = 0; j < 5; j++) { + for (int i = 0; i < dimension; i++) { + pts[j][i] = pt[i] + s[j][i]; + } + } + return pts; + } + + private static double[][][] createCoordinates3(int mode, int dimension) { + double[][][] pts_s = new double[2][5][dimension]; + for (int k = 0; k < pts_s.length; k++) { + double[][] pts = createCoordinates2(mode, dimension); + for (int j = 0; j < pts.length; j++) { + for (int i = 0; i < dimension; i++) { + pts_s[k][j][i] = pts[j][i] + k * 4; + } + } + } + return pts_s; + } + + private static JsonObject getDefaultRecord(String id, String kind, String legalTag) { + JsonArray acls = new JsonArray(); + acls.add(TestUtils.getAcl()); + return getDefaultRecordFromAcl(id, kind, legalTag, acls); + } + + private static JsonObject getDefaultRecordWithDuplicateAclsAndLegaltags(String id, String kind, String legalTag) { + JsonArray acls = new JsonArray(); + acls.add(TestUtils.getAcl()); + acls.add(TestUtils.getAcl()); + return getDefaultRecordWithDuplicateAclAndLegaltags(id, kind, legalTag, acls); + } + + private static JsonObject getDefaultRecordWithCustomAcl(String id, String kind, String legalTag, String acl) { + JsonArray acls = new JsonArray(); + acls.add(acl); + return getDefaultRecordFromAcl(id, kind, legalTag, acls); + } + + private static JsonObject getDefaultRecordWithEntV2OnlyAcl(String id, String kind, String legalTag) { + JsonArray acls = new JsonArray(); + acls.add(TestUtils.getEntV2OnlyAcl()); + return getDefaultRecordFromAcl(id, kind, legalTag, acls); + } + + private static JsonObject getDefaultRecordFromAcl(String id, String kind, String legalTag, JsonArray acls) { + JsonObject acl = new JsonObject(); + acl.add("viewers", acls); + acl.add("owners", acls); + + JsonArray tags = new JsonArray(); + tags.add(legalTag); + + JsonArray ordcJson = new JsonArray(); + ordcJson.add("BR"); + + JsonObject legal = new JsonObject(); + legal.add("legaltags", tags); + legal.add("otherRelevantDataCountries", ordcJson); + + JsonObject record = new JsonObject(); + record.addProperty("id", id); + record.addProperty("kind", kind); + record.add("acl", acl); + record.add("legal", legal); + return record; + } + + private static JsonObject getDefaultRecordWithDuplicateAclAndLegaltags(String id, String kind, String legalTag, JsonArray acls) { + JsonObject acl = new JsonObject(); + acl.add("viewers", acls); + acl.add("owners", acls); + + JsonArray tags = new JsonArray(); + tags.add(legalTag); + tags.add(legalTag); + + JsonArray ordcJson = new JsonArray(); + ordcJson.add("BR"); + + JsonObject legal = new JsonObject(); + legal.add("legaltags", tags); + legal.add("otherRelevantDataCountries", ordcJson); + + JsonObject record = new JsonObject(); + record.addProperty("id", id); + record.addProperty("kind", kind); + record.add("acl", acl); + record.add("legal", legal); + return record; + } + + private static JsonObject getDefaultRecordWithDefaultData(String id, String kind, String legalTag) { + JsonObject data = new JsonObject(); + data.add("int-tag", getNumberPropertyObject("score-int", 58377304471659395L)); + data.add("double-tag", getNumberPropertyObject("score-double", 58377304.471659395)); + data.addProperty("count", 123456789L); + return getRecordWithInputData(id, kind, legalTag, data); + } + + private static JsonObject getDefaultRecordWithDefaultDataAndDuplicateAclAndLegaltags(String id, String kind, String legalTag) { + JsonObject data = new JsonObject(); + data.add("int-tag", getNumberPropertyObject("score-int", 58377304471659395L)); + data.add("double-tag", getNumberPropertyObject("score-double", 58377304.471659395)); + data.addProperty("count", 123456789L); + return getRecordWithInputDataAndDuplicateAclsAndLegaltags(id, kind, legalTag, data); + } + + private static JsonObject getRecordWithInputData(String id, String kind, String legalTag, JsonObject data) { + JsonObject record = getDefaultRecord(id, kind, legalTag); + record.add("data", data); + return record; + } + + private static JsonObject getRecordWithInputDataAndDuplicateAclsAndLegaltags(String id, String kind, String legalTag, JsonObject data) { + JsonObject record = getDefaultRecordWithDuplicateAclsAndLegaltags(id, kind, legalTag); + record.add("data", data); + return record; + } + + private static JsonObject getRecordWithInputDataAndAcl(String id, String kind, String legalTag, JsonObject data) { + JsonObject record = getDefaultRecordWithEntV2OnlyAcl(id, kind, legalTag); + record.add("data", data); + return record; + } + + private static JsonObject getNumberPropertyObject(String propertyName, Number intValue) { + JsonObject numberProperty = new JsonObject(); + numberProperty.addProperty(propertyName, intValue); + return numberProperty; + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/ReplayUtils.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/ReplayUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..62eb3cda5e28cb5ff76d9242ec66ba25730bf21f --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/ReplayUtils.java @@ -0,0 +1,119 @@ +// Copyright © Microsoft Corporation +// +// 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 org.opengroup.osdu.storage.util; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.IOException; +import java.util.List; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.ProtocolException; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.opengroup.osdu.storage.model.ReplayStatusResponseHelper; +public class ReplayUtils { + + public static String createJsonEmpty() { + + JsonObject requestBody = new JsonObject(); + return requestBody.toString(); + } + + public static String createJsonWithoutOperationName(List<String> kindList) { + + JsonObject requestBody = new JsonObject(); + JsonObject filter = new JsonObject(); + requestBody.add("filter", filter); + JsonArray kindArray = new JsonArray(); + filter.add("kinds", kindArray); + for (String kind : kindList) + kindArray.add(kind); + return requestBody.toString(); + } + + + public static String createJsonWithOperationName(String operation) { + + JsonObject requestBody = new JsonObject(); + requestBody.addProperty("operation", operation); + return requestBody.toString(); + } + + public static String createJsonWithEmptyFilter(String operation) { + + JsonObject requestBody = new JsonObject(); + requestBody.addProperty("operation", operation); + JsonObject filter = new JsonObject(); + requestBody.add("filter", filter); + return requestBody.toString(); + } + + public static String createJsonWithKind(String operation, List<String> kindList) { + + JsonObject requestBody = new JsonObject(); + JsonObject filter = new JsonObject(); + JsonArray kindArray = new JsonArray(); + requestBody.addProperty("operation", operation); + requestBody.add("filter", filter); + filter.add("kinds", kindArray); + for (String kind : kindList) + kindArray.add(kind); + return requestBody.toString(); + } + + public static String getFieldFromResponse(CloseableHttpResponse response, String field) throws IOException, ParseException { + + return JsonParser.parseString(EntityUtils.toString(response.getEntity())) + .getAsJsonObject() + .get(field) + .getAsString(); + } + + public static String getSearchCountQueryForKind(String kind) { + + JsonObject requestBody = new JsonObject(); + requestBody.addProperty("kind", kind); + requestBody.addProperty("limit", 1); + requestBody.addProperty("trackTotalCount", true); + return requestBody.toString(); + } + + public static String getSearchUrl() { + + String searchUrl = System.getProperty("SEARCH_URL", System.getenv("SEARCH_URL")); + if (searchUrl == null || searchUrl.contains("-null")) { + throw new IllegalArgumentException("Invalid SEARCH_URL: " + searchUrl); + } + return searchUrl; + } + + public static String getIndexerUrl() { + + String indexerUrl = System.getProperty("INDEXER_URL", System.getenv("INDEXER_URL")); + if (indexerUrl == null || indexerUrl.contains("-null")) { + throw new IllegalArgumentException("Invalid INDEXER_URL: " + indexerUrl); + } + return indexerUrl; + } + + public static ReplayStatusResponseHelper getConvertedReplayStatusResponseFromResponse(CloseableHttpResponse response) throws ProtocolException, IOException { + + String json = EntityUtils.toString(response.getEntity()); + Gson gson = new Gson(); + return gson.fromJson(json, ReplayStatusResponseHelper.class); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TenantUtils.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TenantUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..d9427a3cef17554c98add1a705abfe85eec1efa3 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TenantUtils.java @@ -0,0 +1,26 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.util; + +public class TenantUtils { + + public static String getTenantName() { + return System.getProperty("TENANT_NAME", System.getenv("TENANT_NAME")); + } + + public static String getFirstTenantName() { + return getTenantName(); + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TestBase.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TestBase.java new file mode 100644 index 0000000000000000000000000000000000000000..b6e871962196768399d0f13ae44009c155463309 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TestBase.java @@ -0,0 +1,30 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.util; + +import com.google.gson.Gson; + +public abstract class TestBase { + + protected TestUtils testUtils = null; + protected ConfigUtils configUtils = null; + + public static final Gson GSON = new Gson(); + + public abstract void setup() throws Exception; + + public abstract void tearDown() throws Exception; + +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TestUtils.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..75cb9af4ecd93885b289602a02a69ebc65d8eae5 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TestUtils.java @@ -0,0 +1,239 @@ +// Copyright 2017-2019, Schlumberger +// +// 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 org.opengroup.osdu.storage.util; + +import static org.apache.http.HttpStatus.SC_CREATED; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opengroup.osdu.storage.util.HeaderUtils.getHeadersWithxCollaboration; +import static org.opengroup.osdu.storage.util.TestBase.GSON; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import jakarta.ws.rs.core.MediaType; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.NotImplementedException; +import org.apache.hc.client5.http.config.ConnectionConfig; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; + +public abstract class TestUtils { + + public static final String STORAGE_TEST_GROUP_ENT_V_2 = "data.storage-integration-test-acl.ent-v2"; + public static final String STORAGE_TEST_GROUP_ENT_V_2_DESCRIPTION = "EntV2 Storage test group."; + + protected static String token = null; + protected static String noDataAccesstoken = null; + protected static String dataRootToken = null; + private static Gson gson = new Gson(); + + protected static String groupId = System.getProperty("ENTITLEMENTS_DOMAIN", System.getenv("ENTITLEMENTS_DOMAIN")); + + public static final String getGroupId() { + return groupId; + } + + public static final String getAclSuffix() { + return String.format("%s.%s", TenantUtils.getTenantName(), groupId); + } + + public static final String getAcl() { + return String.format("data.test1@%s", getAclSuffix()); + } + + public static final String getEntV2OnlyAcl() { + return String.format(STORAGE_TEST_GROUP_ENT_V_2 + "@%s", getAclSuffix()); + } + + public static final String getIntegrationTesterAcl() { + return String.format("data.integration.test@%s", getAclSuffix()); + } + + public static String getApiPath(String api) throws MalformedURLException { + String baseUrl = System.getProperty("STORAGE_URL", System.getenv("STORAGE_URL")); + if (baseUrl == null || baseUrl.contains("-null")) { + baseUrl = "https://localhost:8443/api/storage/v2/"; + } + URL mergedURL = new URL(baseUrl + api); + System.out.println(mergedURL.toString()); + return mergedURL.toString(); + } + + public static void assertRecordVersion(CloseableHttpResponse response, Long expectedVersion) { + assertEquals(HttpStatus.SC_OK, response.getCode()); + + String responseBody; + try { + responseBody = EntityUtils.toString(response.getEntity()); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + DummyRecordsHelper.RecordResultMock result = gson.fromJson(responseBody, DummyRecordsHelper.RecordResultMock.class); + assertEquals(expectedVersion.longValue(), Long.parseLong(result.version)); + } + + public static String assertRecordVersionAndReturnResponseBody(CloseableHttpResponse response, Long expectedVersion) { + assertEquals(HttpStatus.SC_OK, response.getCode()); + + String responseBody; + try { + responseBody = EntityUtils.toString(response.getEntity()); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + DummyRecordsHelper.RecordResultMock result = gson.fromJson(responseBody, DummyRecordsHelper.RecordResultMock.class); + assertEquals(expectedVersion.longValue(), Long.parseLong(result.version)); + return responseBody; + } + + public abstract String getToken() throws Exception; + + public abstract String getNoDataAccessToken() throws Exception; + + public String getDataRootUserToken() throws Exception{ + throw new NotImplementedException(); + } + + public static CloseableHttpResponse sendWithCustomMediaType(String path, String httpMethod, Map<String, String> headers, String contentType, String requestBody, + String query) throws Exception { + + log(httpMethod, TestUtils.getApiPath(path + query), headers, requestBody); + BasicHttpClientConnectionManager cm = createBasicHttpClientConnectionManager(); + headers.put("Content-Type", contentType); + ClassicHttpRequest httpRequest = createHttpRequest(TestUtils.getApiPath(path + query), httpMethod, requestBody, headers); + + try (CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(cm).build()) { + return httpClient.execute(httpRequest, new CustomHttpClientResponseHandler()); + } + } + + public static CloseableHttpResponse send(String path, String httpMethod, Map<String, String> headers, String requestBody, + String query) throws Exception { + + log(httpMethod, TestUtils.getApiPath(path + query), headers, requestBody); + + BasicHttpClientConnectionManager cm = createBasicHttpClientConnectionManager(); + headers.put("Content-Type", MediaType.APPLICATION_JSON); + headers.put("Accept-Charset","utf-8"); + ClassicHttpRequest httpRequest = createHttpRequest(TestUtils.getApiPath(path + query), httpMethod, requestBody, headers); + + try (CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(cm).build()) { + return httpClient.execute(httpRequest, new CustomHttpClientResponseHandler()); + } + } + + public static CloseableHttpResponse send(String url, String path, String httpMethod, Map<String, String> headers, + String requestBody, String query) throws Exception { + + log(httpMethod, url + path, headers, requestBody); + + BasicHttpClientConnectionManager cm = createBasicHttpClientConnectionManager(); + headers.put("Content-Type", MediaType.APPLICATION_JSON); + ClassicHttpRequest httpRequest = createHttpRequest(url + path, httpMethod, requestBody, headers); + + try (CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(cm).build()) { + return httpClient.execute(httpRequest, new CustomHttpClientResponseHandler()); + } + } + + private static void log(String method, String url, Map<String, String> headers, String body) { + System.out.println(String.format("%s: %s", method, url)); + System.out.println(body); + } + + @SuppressWarnings("unchecked") + public static <T> T getResult(CloseableHttpResponse response, int exepectedStatus, Class<T> classOfT) { + assertEquals(exepectedStatus, response.getCode()); + if (exepectedStatus == HttpStatus.SC_NO_CONTENT) { + return null; + } + + assertTrue(response.getEntity().getContentType().contains("application/json")); + String json; + try { + json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + if (classOfT == String.class) { + return (T) json; + } + return gson.fromJson(json, classOfT); + } + + public static Long createRecordInCollaborationContext_AndReturnVersion(String recordId, String kind, String legaltag, String collaborationId, String applicationName, String tenant_name, String token) throws Exception { + String jsonInput = RecordUtil.createDefaultJsonRecord(recordId, kind, legaltag); + + CloseableHttpResponse response = TestUtils.send("records", "PUT", getHeadersWithxCollaboration(collaborationId, applicationName, tenant_name, token), jsonInput, ""); + assertEquals(SC_CREATED, response.getCode()); + assertTrue(response.getEntity().getContentType().contains("application/json")); + + String responseBody = EntityUtils.toString(response.getEntity()); + DummyRecordsHelper.CreateRecordResponse result = GSON.fromJson(responseBody, DummyRecordsHelper.CreateRecordResponse.class); + + return Long.parseLong(result.recordIdVersions[0].split(":")[3]); + } + + private static ClassicHttpRequest createHttpRequest(String path, String httpMethod, String requestBody, + Map<String, String> headers) throws MalformedURLException { + ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.create(httpMethod) + .setUri(path) + .setEntity(requestBody, ContentType.APPLICATION_JSON); + headers.forEach(classicRequestBuilder::addHeader); + return classicRequestBuilder.build(); + } + + + private static BasicHttpClientConnectionManager createBasicHttpClientConnectionManager() { + ConnectionConfig connConfig = ConnectionConfig.custom() + .setConnectTimeout(1500000, TimeUnit.MILLISECONDS) + .setSocketTimeout(1500000, TimeUnit.MILLISECONDS) + .build(); + BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(); + cm.setConnectionConfig(connConfig); + return cm; + } + + public static JsonObject getCopyRecordRequest(String target, String stringId) { + JsonObject data = new JsonObject(); + JsonArray array = new JsonArray(); + JsonObject records = new JsonObject(); + records.addProperty("id", stringId); + array.add(records); + data.addProperty("target", target); + data.add("records", array); + return data; + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TokenTestUtils.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TokenTestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..892f70e48a8f42b279041ee4f5497608261eb678 --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/TokenTestUtils.java @@ -0,0 +1,62 @@ +/* + * Copyright 2020-2023 Google LLC + * Copyright 2020-2023 EPAM Systems, 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 + * + * https://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 org.opengroup.osdu.storage.util; + +import com.google.common.base.Strings; + +public class TokenTestUtils extends TestUtils { + + public static final String INTEGRATION_TESTER_TOKEN = "PRIVILEGED_USER_TOKEN"; + public static final String NO_DATA_ACCESS_TOKEN = "NO_ACCESS_USER_TOKEN"; + public static final String DATA_ROOT_TOKEN = "ROOT_USER_TOKEN"; + private OpenIDTokenProvider openIDTokenProvider; + + public TokenTestUtils() { + token = System.getProperty(INTEGRATION_TESTER_TOKEN, System.getenv(INTEGRATION_TESTER_TOKEN)); + noDataAccesstoken = System.getProperty(NO_DATA_ACCESS_TOKEN, System.getenv(NO_DATA_ACCESS_TOKEN)); + dataRootToken = System.getProperty(DATA_ROOT_TOKEN, System.getenv(DATA_ROOT_TOKEN)); + + if (Strings.isNullOrEmpty(token) || Strings.isNullOrEmpty(noDataAccesstoken) || Strings.isNullOrEmpty(dataRootToken)) { + openIDTokenProvider = new OpenIDTokenProvider(); + } + } + + @Override + public synchronized String getToken() throws Exception { + if (Strings.isNullOrEmpty(token)) { + token = openIDTokenProvider.getToken(); + } + return "Bearer " + token; + } + + @Override + public synchronized String getNoDataAccessToken() throws Exception { + if (Strings.isNullOrEmpty(noDataAccesstoken)) { + noDataAccesstoken = openIDTokenProvider.getNoAccessToken(); + } + return "Bearer " + noDataAccesstoken; + } + + @Override + public String getDataRootUserToken() throws Exception { + if (Strings.isNullOrEmpty(dataRootToken)) { + dataRootToken = openIDTokenProvider.getDataRootToken(); + } + return "Bearer " + dataRootToken; + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/VersionInfoUtils.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/VersionInfoUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..f686e0ef0eadcd99e2c06edd0d8fd910e04b778c --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/VersionInfoUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright 2020-2024 Google LLC + * Copyright 2020-2024 EPAM Systems, 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 org.opengroup.osdu.storage.util; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.gson.Gson; +import java.io.IOException; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; + +public class VersionInfoUtils { + + public VersionInfo getVersionInfoFromResponse(CloseableHttpResponse response) { + assertTrue(response.getEntity().getContentType().contains("application/json")); + String json = null; + try { + json = EntityUtils.toString(response.getEntity()); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + Gson gson = new Gson(); + return gson.fromJson(json, VersionInfo.class); + } + + public class VersionInfo { + public String groupId; + public String artifactId; + public String version; + public String buildTime; + public String branch; + public String commitId; + public String commitMessage; + } +} diff --git a/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/conf/OpenIDProviderConfig.java b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/conf/OpenIDProviderConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..411ef726e261f30955b24ffbcc4bc8c232a733ed --- /dev/null +++ b/storage-acceptance-test/src/test/java/org/opengroup/osdu/storage/util/conf/OpenIDProviderConfig.java @@ -0,0 +1,128 @@ +/* + * Copyright 2020-2023 Google LLC + * Copyright 2020-2023 EPAM Systems, 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 + * + * https://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 org.opengroup.osdu.storage.util.conf; + +import com.nimbusds.oauth2.sdk.http.HTTPRequest; +import com.nimbusds.oauth2.sdk.http.HTTPResponse; +import com.nimbusds.oauth2.sdk.id.Issuer; +import com.nimbusds.openid.connect.sdk.op.OIDCProviderConfigurationRequest; +import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata; + +public class OpenIDProviderConfig { + + public static final String TEST_OPENID_PROVIDER_CLIENT_ID = "PRIVILEGED_USER_OPENID_PROVIDER_CLIENT_ID"; + public static final String TEST_OPENID_PROVIDER_CLIENT_SECRET = + "PRIVILEGED_USER_OPENID_PROVIDER_CLIENT_SECRET"; + public static final String TEST_NO_ACCESS_OPENID_PROVIDER_CLIENT_ID = + "NO_ACCESS_USER_OPENID_PROVIDER_CLIENT_ID"; + public static final String TEST_NO_ACCESS_OPENID_PROVIDER_CLIENT_SECRET = + "NO_ACCESS_USER_OPENID_PROVIDER_CLIENT_SECRET"; + public static final String DATA_ROOT_OPENID_PROVIDER_CLIENT_ID = + "ROOT_USER_OPENID_PROVIDER_CLIENT_ID"; + public static final String DATA_ROOT_OPENID_PROVIDER_CLIENT_SECRET = + "ROOT_USER_OPENID_PROVIDER_CLIENT_SECRET"; + public static final String TEST_OPENID_PROVIDER_URL = "TEST_OPENID_PROVIDER_URL"; + + private String clientId; + private String clientSecret; + private String noAccessClientId; + private String noAccessClientSecret; + private String dataRootClientId; + private String dataRootClientSecret; + private String url; + private final String[] scopes = {"openid"}; + private static final OpenIDProviderConfig openIDProviderConfig = new OpenIDProviderConfig(); + private static OIDCProviderMetadata providerMetadata; + + public static OpenIDProviderConfig Instance() { + try { + openIDProviderConfig.clientId = + System.getProperty( + TEST_OPENID_PROVIDER_CLIENT_ID, System.getenv(TEST_OPENID_PROVIDER_CLIENT_ID)); + + openIDProviderConfig.clientSecret = + System.getProperty( + TEST_OPENID_PROVIDER_CLIENT_SECRET, + System.getenv(TEST_OPENID_PROVIDER_CLIENT_SECRET)); + + openIDProviderConfig.noAccessClientId = + System.getProperty( + TEST_NO_ACCESS_OPENID_PROVIDER_CLIENT_ID, + System.getenv(TEST_NO_ACCESS_OPENID_PROVIDER_CLIENT_ID)); + + openIDProviderConfig.noAccessClientSecret = + System.getProperty( + TEST_NO_ACCESS_OPENID_PROVIDER_CLIENT_SECRET, + System.getenv(TEST_NO_ACCESS_OPENID_PROVIDER_CLIENT_SECRET)); + + openIDProviderConfig.url = + System.getProperty(TEST_OPENID_PROVIDER_URL, System.getenv(TEST_OPENID_PROVIDER_URL)); + + openIDProviderConfig.dataRootClientId = + System.getProperty( + DATA_ROOT_OPENID_PROVIDER_CLIENT_ID, + System.getenv(DATA_ROOT_OPENID_PROVIDER_CLIENT_ID)); + + openIDProviderConfig.dataRootClientSecret = + System.getProperty( + DATA_ROOT_OPENID_PROVIDER_CLIENT_SECRET, + System.getenv(DATA_ROOT_OPENID_PROVIDER_CLIENT_SECRET)); + + Issuer issuer = new Issuer(openIDProviderConfig.url); + OIDCProviderConfigurationRequest request = new OIDCProviderConfigurationRequest(issuer); + HTTPRequest httpRequest = request.toHTTPRequest(); + HTTPResponse httpResponse = httpRequest.send(); + providerMetadata = OIDCProviderMetadata.parse(httpResponse.getContentAsJSONObject()); + } catch (Exception e) { + throw new RuntimeException("Malformed token provider configuration", e); + } + return openIDProviderConfig; + } + + public String getClientId() { + return clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public String getNoAccessClientId() { + return noAccessClientId; + } + + public String getNoAccessClientSecret() { + return noAccessClientSecret; + } + + public String[] getScopes() { + return scopes; + } + + public OIDCProviderMetadata getProviderMetadata() { + return providerMetadata; + } + + public String getDataRootClientId() { + return dataRootClientId; + } + + public String getDataRootClientSecret() { + return dataRootClientSecret; + } +} diff --git a/storage-acceptance-test/src/test/resources/test.properties b/storage-acceptance-test/src/test/resources/test.properties new file mode 100644 index 0000000000000000000000000000000000000000..2674162d436b19dda249978c2701708aedb565c5 --- /dev/null +++ b/storage-acceptance-test/src/test/resources/test.properties @@ -0,0 +1,4 @@ +enableEncodedSpecialCharactersInURL=true +opa.integration.enabled=true +collaboration.enabled=false +test.replay.enabled=false