diff --git a/provider/indexer-ibm/azure-build.yml b/provider/indexer-ibm/azure-build.yml new file mode 100644 index 0000000000000000000000000000000000000000..705d5f2168a7f1c6a077d21d9c69066ed673f303 --- /dev/null +++ b/provider/indexer-ibm/azure-build.yml @@ -0,0 +1,55 @@ +# Maven +# Build your Java project and run tests with Apache Maven. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/java + +trigger: + branches: + include: + - master + paths: + exclude: + - README.md + - .gitignore + +pool: + name: Hosted Ubuntu 1604 + demands: maven + +steps: +- task: Maven@3 + displayName: 'build, test, code coverage' + inputs: + mavenPomFile: 'pom.xml' + options: '--settings ./indexer-core/maven/settings.xml -DVSTS_FEED_TOKEN=$(VSTS_FEED_TOKEN)' + testResultsFiles: '**/*/TEST-*.xml' + codeCoverageToolOption: JaCoCo + goals: 'install' + +- task: Maven@3 + displayName: 'build, test, code coverage' + inputs: + mavenPomFile: 'pom.xml' + options: '--settings ./provider/indexer-azure/maven/settings.xml -DVSTS_FEED_TOKEN=$(VSTS_FEED_TOKEN) -P indexer-azure' + testResultsFiles: '**/*/TEST-*.xml' + codeCoverageToolOption: JaCoCo + goals: 'package' + +- task: CopyFiles@2 + displayName: 'Copy Azure artifacts for maven deploy to: $(build.artifactstagingdirectory)' + inputs: + SourceFolder: + Contents: | + pom.xml + provider/indexer-azure/maven/settings.xml + provider/indexer-azure/pom.xml + provider/indexer-azure/target/*-spring-boot.jar + TargetFolder: '$(build.artifactstagingdirectory)' + +- task: PublishBuildArtifacts@1 + displayName: 'Publish Artifact: drop' + inputs: + PathtoPublish: '$(build.artifactstagingdirectory)' + ArtifactName: 'drop' + publishLocation: 'Container' + condition: succeededOrFailed() diff --git a/provider/indexer-ibm/maven/settings.xml b/provider/indexer-ibm/maven/settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..79c58d344efccb52d3f026e28ce38a368da983aa --- /dev/null +++ b/provider/indexer-ibm/maven/settings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2019 IBM Corp. All Rights Reserved. + + 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. + --> + +<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> + <servers> + <server> + <id>azure-auth</id> + <configuration> + <tenant>${AZURE_DEPLOY_TENANT}</tenant> + <client>${AZURE_DEPLOY_CLIENT_ID}</client> + <key>${AZURE_DEPLOY_CLIENT_SECRET}</key> + <environment>AZURE</environment> + </configuration> + </server> + </servers> +</settings> \ No newline at end of file diff --git a/provider/indexer-ibm/pom.xml b/provider/indexer-ibm/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..fa907326f3cd04c65cb8921f0b442527b949a6e6 --- /dev/null +++ b/provider/indexer-ibm/pom.xml @@ -0,0 +1,221 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2019 IBM Corp. All Rights Reserved. + + 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> + + <parent> + <groupId>org.opengroup.osdu.indexer</groupId> + <artifactId>indexer-service</artifactId> + <version>1.0.2</version> + <relativePath>../..</relativePath> + </parent> + + <artifactId>indexer-ibm</artifactId> + <version>1.0.0</version> + <name>indexer-ibm</name> + <description>Indexer Service IBM</description> + <packaging>jar</packaging> + + <properties> + <azure.version>2.1.7</azure.version> + <azure.appservice.resourcegroup></azure.appservice.resourcegroup> + <azure.appservice.plan></azure.appservice.plan> + <azure.appservice.appname></azure.appservice.appname> + <azure.appservice.subscription></azure.appservice.subscription> + </properties> + + <dependencies> + + <!-- OSDU core service dependencies --> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>os-core-common</artifactId> + <version>0.0.8</version> + </dependency> + <dependency> + <groupId>org.opengroup.osdu.indexer</groupId> + <artifactId>indexer-core</artifactId> + <version>1.0.2</version> + </dependency> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>os-core-lib-ibm</artifactId> + <version>0.1.1</version> + </dependency> + + + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.security.oauth</groupId> + <artifactId>spring-security-oauth2</artifactId> + <version>2.3.6.RELEASE</version> + </dependency> + + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-jwt</artifactId> + <version>1.0.10.RELEASE</version> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-oauth2-client</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-oauth2-jose</artifactId> + </dependency> + + <dependency> + <groupId>org.elasticsearch</groupId> + <artifactId>elasticsearch</artifactId> + <version>6.6.2</version> + </dependency> + <dependency> + <groupId>org.elasticsearch.client</groupId> + <artifactId>elasticsearch-rest-client</artifactId> + <version>6.6.2</version> + </dependency> + <dependency> + <groupId>org.elasticsearch.client</groupId> + <artifactId>elasticsearch-rest-high-level-client</artifactId> + <version>6.6.2</version> + </dependency> + + <!-- Test Dependencies --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-test</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>com.auth0</groupId> + <artifactId>java-jwt</artifactId> + <version>3.8.1</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + <!-- https://mvnrepository.com/artifact/org.powermock/powermock-api-mockito2 --> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito2</artifactId> + <version>2.0.2</version> + <scope>test</scope> + </dependency> + + <!-- https://mvnrepository.com/artifact/org.powermock/powermock-module-junit4 --> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-junit4</artifactId> + <version>2.0.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>3.0.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.codehaus.mojo</groupId> + <artifactId>cobertura-maven-plugin</artifactId> + <version>2.7</version> + <scope>test</scope> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + <configuration> + <classifier>spring-boot</classifier> + <mainClass> + org.opengroup.osdu.indexer.ibm.IndexerAzureApplication + </mainClass> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>com.microsoft.azure</groupId> + <artifactId>azure-webapp-maven-plugin</artifactId> + <version>1.7.0</version> + <configuration> + <schemaVersion>V2</schemaVersion> + <authentication> + <serverId>azure-auth</serverId> + </authentication> + <subscriptionId>${azure.appservice.subscription}</subscriptionId> + <resourceGroup>${azure.appservice.resourcegroup}</resourceGroup> + <appServicePlanName>${azure.appservice.plan}</appServicePlanName> + <appName>${azure.appservice.appname}</appName> + <appSettings> + <property> + <name>JAVA_OPTS</name> + <value>-Dserver.port=80</value> + </property> + </appSettings> + <deployment> + <resources> + <resource> + <directory>${project.basedir}/target</directory> + <includes> + <include>*spring-boot.jar</include> + </includes> + </resource> + </resources> + </deployment> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.4.2</version> + <configuration> + <useSystemClassLoader>false</useSystemClassLoader> + <threadCount>1</threadCount> + </configuration> + </plugin> + </plugins> + </build> + + +</project> diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/IndexerIBMApplication.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/IndexerIBMApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..9d00f3c4fe8c822a9f8c13b5ae38271cad36d9e4 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/IndexerIBMApplication.java @@ -0,0 +1,30 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@SpringBootApplication +@ComponentScan({"org.opengroup.osdu.core.common", "org.opengroup.osdu.indexer"}) +public class IndexerIBMApplication { + + public static void main(String[] args) { + SpringApplication.run(IndexerIBMApplication.class, args); + } + +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/ServletInitializer.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/ServletInitializer.java new file mode 100644 index 0000000000000000000000000000000000000000..434f5e70980b6d220956f3de3b0e01c4a4ad8091 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/ServletInitializer.java @@ -0,0 +1,26 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +public class ServletInitializer extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(IndexerIBMApplication.class); + } +} + diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/api/AADController.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/api/AADController.java new file mode 100644 index 0000000000000000000000000000000000000000..9b4ed05b81c7b4a9c4f1e13d586c485f892745c8 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/api/AADController.java @@ -0,0 +1,42 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.api; + +import org.opengroup.osdu.indexer.ibm.util.ServiceAccountJwtClientImpl; + +import org.springframework.http.*; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.inject.Inject; + +@Controller +public class AADController { + + @Inject + ServiceAccountJwtClientImpl service; + + + @RequestMapping("/obo_api") + @PostMapping + public ResponseEntity<String> callOboApi() throws Throwable { + + String token = service.getIdToken("common"); + + return new ResponseEntity<String>(token, HttpStatus.OK); + } +} + diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/AttributesCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/AttributesCache.java new file mode 100644 index 0000000000000000000000000000000000000000..b62709872251c5c34c2df8ac8f5986489d568b3b --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/AttributesCache.java @@ -0,0 +1,55 @@ +// 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.indexer.ibm.cache; + +import org.opengroup.osdu.core.common.model.core.cache.VmCache; +import org.opengroup.osdu.core.common.spi.coreis.IAttributesCache; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Set; + +@Component +public class AttributesCache implements IAttributesCache<String,Set> { + + private VmCache<String, Set> cache; + + public AttributesCache(@Value("${INDEX_CACHE_EXPIRATION}") final String INDEX_CACHE_EXPIRATION, + @Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) { + cache = new VmCache<>(Integer.parseInt(INDEX_CACHE_EXPIRATION) * 60, + Integer.parseInt(MAX_CACHE_VALUE_SIZE)); + } + + @Override + public void put(String key, Set value) { + this.cache.put(key, value); + } + + @Override + public Set get(String key) { + return this.cache.get(key); + } + + @Override + public void delete(String key) { + this.cache.delete(key); + } + + @Override + public void clearAll() { + this.cache.clearAll(); + } + +} \ No newline at end of file diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/CosmosDBSchemaCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/CosmosDBSchemaCache.java new file mode 100644 index 0000000000000000000000000000000000000000..2ed935d6094604433dbf5064b08c2b95e12816c3 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/CosmosDBSchemaCache.java @@ -0,0 +1,27 @@ +//// Copyright 2019 IBM Corp. All Rights Reserved. +//// +//// 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.indexer.ibm.cache; +// +// +//import org.opengroup.osdu.core.common.model.core.cache.VmCache; +//import org.opengroup.osdu.core.common.model.storage.Schema; +//import org.springframework.stereotype.Component; +// +//@Component +//public class CosmosDBSchemaCache extends VmCache<String, Schema> { +// public CosmosDBSchemaCache() { +// super(5 * 60, 1000); +// } +//} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/ElasticCredentialsCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/ElasticCredentialsCache.java new file mode 100644 index 0000000000000000000000000000000000000000..709095c67415a6f250b8ab08ffa2f5dc2790ceb2 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/ElasticCredentialsCache.java @@ -0,0 +1,39 @@ +package org.opengroup.osdu.indexer.ibm.cache; + +import org.opengroup.osdu.core.common.model.core.cache.VmCache; +import org.opengroup.osdu.core.common.model.core.ClusterSettings; +import org.opengroup.osdu.core.common.spi.coreis.IElasticCredentialsCache; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class ElasticCredentialsCache implements IElasticCredentialsCache<String, ClusterSettings> { + + private VmCache<String, ClusterSettings> cache; + + public ElasticCredentialsCache(@Value("${ELASTIC_CACHE_EXPIRATION}") final String ELASTIC_CACHE_EXPIRATION, + @Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) { + cache = new VmCache<>(Integer.parseInt(ELASTIC_CACHE_EXPIRATION) * 60, + Integer.parseInt(MAX_CACHE_VALUE_SIZE)); + } + + @Override + public void put(String s, ClusterSettings o) { + this.cache.put(s,o); + } + + @Override + public ClusterSettings get(String s) { + return this.cache.get(s); + } + + @Override + public void delete(String s) { + this.cache.delete(s); + } + + @Override + public void clearAll() { + this.cache.clearAll(); + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/IndexCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/IndexCache.java new file mode 100644 index 0000000000000000000000000000000000000000..df452efe3f75ceec4289fb43661f48b8903bdc72 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/IndexCache.java @@ -0,0 +1,37 @@ +package org.opengroup.osdu.indexer.ibm.cache; + +import org.opengroup.osdu.core.common.model.core.cache.VmCache; +import org.opengroup.osdu.core.common.spi.coreis.IIndexCache; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class IndexCache implements IIndexCache<String, Boolean> { + private VmCache<String, Boolean> cache; + + public IndexCache(@Value("${INDEX_CACHE_EXPIRATION}") final String INDEX_CACHE_EXPIRATION, + @Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) { + cache = new VmCache<>(Integer.parseInt(INDEX_CACHE_EXPIRATION) * 60, + Integer.parseInt(MAX_CACHE_VALUE_SIZE)); + } + + @Override + public void put(String s, Boolean o) { + this.cache.put(s, o); + } + + @Override + public Boolean get(String s) { + return this.cache.get(s); + } + + @Override + public void delete(String s) { + this.cache.delete(s); + } + + @Override + public void clearAll() { + this.cache.clearAll(); + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/JwtCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/JwtCache.java new file mode 100644 index 0000000000000000000000000000000000000000..170be5bcc6cea742867c5060557db1f7fff9912c --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/JwtCache.java @@ -0,0 +1,39 @@ +package org.opengroup.osdu.indexer.ibm.cache; + +import org.opengroup.osdu.core.common.model.core.cache.VmCache; +import org.opengroup.osdu.core.common.model.coreis.IdToken; +import org.opengroup.osdu.core.common.spi.coreis.IJwtCache; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class JwtCache implements IJwtCache<String, IdToken> { + private VmCache<String, IdToken> cache; + + // Azure service account id_token can be requested only for 1 hr + private final static int EXPIRED_AFTER = 59; + + public JwtCache(@Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) { + cache = new VmCache<>(EXPIRED_AFTER * 60, Integer.parseInt(MAX_CACHE_VALUE_SIZE)); + } + + @Override + public void put(String s, IdToken o) { + this.cache.put(s, o); + } + + @Override + public IdToken get(String s) { + return this.cache.get(s); + } + + @Override + public void delete(String s) { + this.cache.delete(s); + } + + @Override + public void clearAll() { + this.cache.clearAll(); + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/KindsCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/KindsCache.java new file mode 100644 index 0000000000000000000000000000000000000000..7effffb32b9a665cd3d451e9a1dea12b17a3a1a6 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/KindsCache.java @@ -0,0 +1,39 @@ +package org.opengroup.osdu.indexer.ibm.cache; + +import org.opengroup.osdu.core.common.model.core.cache.VmCache; +import org.opengroup.osdu.core.common.spi.coreis.IKindsCache; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Set; + +@Component +public class KindsCache implements IKindsCache<String, Set> { + private VmCache<String, Set> cache; + + public KindsCache(@Value("${KINDS_CACHE_EXPIRATION}") final String KINDS_CACHE_EXPIRATION, + @Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) { + cache = new VmCache<>(Integer.parseInt(KINDS_CACHE_EXPIRATION) * 60, + Integer.parseInt(MAX_CACHE_VALUE_SIZE)); + } + + @Override + public void put(String s, Set o) { + this.cache.put(s, o); + } + + @Override + public Set get(String s) { + return this.cache.get(s); + } + + @Override + public void delete(String s) { + this.cache.delete(s); + } + + @Override + public void clearAll() { + this.cache.clearAll(); + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/SchemaCache.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/SchemaCache.java new file mode 100644 index 0000000000000000000000000000000000000000..7610b1d51c8ef4df20cf42cdc2f8d5935720e025 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/cache/SchemaCache.java @@ -0,0 +1,37 @@ +package org.opengroup.osdu.indexer.ibm.cache; + +import org.opengroup.osdu.core.common.model.core.cache.VmCache; +import org.opengroup.osdu.core.common.spi.indexer.ISchemaCache; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class SchemaCache implements ISchemaCache<String, String> { + private VmCache<String, String> cache; + + public SchemaCache(@Value("${SCHEMA_CACHE_EXPIRATION}") final String SCHEMA_CACHE_EXPIRATION, + @Value("${MAX_CACHE_VALUE_SIZE}") final String MAX_CACHE_VALUE_SIZE) { + cache = new VmCache<>(Integer.parseInt(SCHEMA_CACHE_EXPIRATION) * 60, + Integer.parseInt(MAX_CACHE_VALUE_SIZE)); + } + + @Override + public void put(String s, String o) { + this.cache.put(s, o); + } + + @Override + public String get(String s) { + return this.cache.get(s); + } + + @Override + public void delete(String s) { + this.cache.delete(s); + } + + @Override + public void clearAll() { + this.cache.clearAll(); + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/DpsLogFactory.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/DpsLogFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..53594660ffd1fabcc9848154703ab7b77711b067 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/DpsLogFactory.java @@ -0,0 +1,35 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.di; + +import org.opengroup.osdu.core.common.model.core.DpsLog; +import org.opengroup.osdu.core.common.service.core.Log; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +@Component +@RequestScope +public class DpsLogFactory implements FactoryBean<DpsLog> { + @Override + public DpsLog getObject() throws Exception { + return new Log(); + } + + @Override + public Class<?> getObjectType() { + return DpsLog.class; + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/EntitlementsFactoryIBM.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/EntitlementsFactoryIBM.java new file mode 100644 index 0000000000000000000000000000000000000000..3310e78b20ee7a74d85caf0725e5dd4749b336ce --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/EntitlementsFactoryIBM.java @@ -0,0 +1,30 @@ +//// Copyright 2019 IBM Corp. All Rights Reserved. +//// +//// 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.indexer.ibm.di; +// +//import org.opengroup.osdu.core.common.model.core.DpsHeaders; +//import org.opengroup.osdu.core.common.service.core.entitlements.IEntitlementsFactory; +//import org.opengroup.osdu.core.common.service.core.entitlements.IEntitlementsService; +//import org.springframework.context.annotation.Primary; +//import org.springframework.stereotype.Component; +// +//@Component +//@Primary +//public class EntitlementsFactoryIBM implements IEntitlementsFactory { +// @Override +// public IEntitlementsService create(DpsHeaders dpsHeaders) { +// return new EntitlementsServiceIBM(dpsHeaders); +// } +//} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/EntitlementsServiceIBM.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/EntitlementsServiceIBM.java new file mode 100644 index 0000000000000000000000000000000000000000..e9fbe77dd0c8675ef4ac451e2ec18ce955084119 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/EntitlementsServiceIBM.java @@ -0,0 +1,115 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.di; + +import org.apache.http.HttpStatus; + +import org.opengroup.osdu.core.common.model.core.DpsHeaders; +import org.opengroup.osdu.core.common.model.core.entitlements.*; +import org.opengroup.osdu.core.common.service.core.entitlements.IEntitlementsService; +import org.opengroup.osdu.core.common.service.core.HttpResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class EntitlementsServiceIBM implements IEntitlementsService { + //service principals don't have UPN or email + static final String INTEGRATION_TEST_ADMIN_ROLE = "data.test.admin@opendes.ibm.com"; + public static final String PREFIX = "ROLE_"; + DpsHeaders headers; + + public EntitlementsServiceIBM(DpsHeaders headers){ + this.headers = headers; + } + + @Override + public MemberInfo addMember(GroupEmail groupEmail, MemberInfo memberInfo) throws EntitlementsException { + return null; + } + + @Override + public Members getMembers(GroupEmail groupEmail, GetMembers getMembers) throws EntitlementsException { + return null; + } + + @Override + public Groups getGroups() throws EntitlementsException { + //TODO implement + return null; + } + +// @Override +// public Groups getGroups() throws EntitlementsException { +// final Authentication auth = SecurityContextHolder.getContext().getAuthentication(); +// final UserPrincipal current = (UserPrincipal) auth.getPrincipal(); +// String email = current.getUpn(); +// +// List<GroupInfo> giList = new ArrayList(); +// Collection<? extends GrantedAuthority> authorities = auth.getAuthorities(); +// for(GrantedAuthority authority : authorities) +// { +// GroupInfo gi = new GroupInfo(); +// String role = authority.getAuthority(); +// if (role.startsWith(PREFIX)){ +// role = role.substring(PREFIX.length()); +// } +// gi.setName(role); +// if ((email == null || email.isEmpty()) && role.equalsIgnoreCase(INTEGRATION_TEST_ADMIN_ROLE)) { +// email = INTEGRATION_TEST_ADMIN_ROLE; +// gi.setEmail(email); +// giList.add(0, gi); +// } +// else { +// gi.setEmail(email); +// giList.add(gi); +// } +// } +// if (giList.size() > 0) +// { +// Groups groups = new Groups(); +// groups.setGroups(giList); +// groups.setDesId(email); +// return groups; +// } +// +// HttpResponse response = new HttpResponse(); +// response.setResponseCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); +// throw new EntitlementsException("no authorities found", response); +// } + + @Override + public GroupInfo createGroup(CreateGroup createGroup) throws EntitlementsException { + return null; + } + + @Override + public void deleteMember(String s, String s1) throws EntitlementsException { + + } + + @Override + public Groups authorizeAny(String... strings) throws EntitlementsException { + return null; + } + + @Override + public void authenticate() throws EntitlementsException { + + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantFactoryImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..2e828556cb0e4b0c715bd733ebc458530bf65292 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantFactoryImpl.java @@ -0,0 +1,114 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.di; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; + +import org.opengroup.osdu.core.common.model.core.ICache; +import org.opengroup.osdu.core.common.model.core.ITenantFactory; +import org.opengroup.osdu.core.common.model.core.TenantInfo; +import org.opengroup.osdu.core.ibm.cloudant.IBMCloudantClientFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +import com.cloudant.client.api.Database; + +@Component +@RequestScope +public class TenantFactoryImpl implements ITenantFactory { + + private static final Logger logger = LoggerFactory.getLogger(TenantFactoryImpl.class); + + @Autowired + private IBMCloudantClientFactory cloudantFactory; + private Database db; + + @PostConstruct + public void init(){ + try { + db = cloudantFactory.getClient().database(TenantInfoDoc.DB_NAME, true); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + + + private Map<String, TenantInfo> tenants; + + public boolean exists(String tenantName) + { + if (this.tenants == null) + initTenants(); + return this.tenants.containsKey(tenantName); + } + + public TenantInfo getTenantInfo(String tenantName) { + if (this.tenants == null) + initTenants(); + return this.tenants.get(tenantName); + } + + public Collection<TenantInfo> listTenantInfo() { + if (this.tenants == null) + initTenants(); + return this.tenants.values(); + } + + public <V> ICache<String, V> createCache(String tenantName, String host, int port, int expireTimeSeconds, Class<V> classOfV) + { + return null; + } + + public void flushCache() {} + + private void initTenants() { + this.tenants = new HashMap<>(); + + + // TODO implement limit, cursor + // TODO refactor a more efficient way, with index + + + List<TenantInfoDoc> allDocs = null; + try { + allDocs = db.getAllDocsRequestBuilder().build().getResponse().getDocsAs(TenantInfoDoc.class); + } catch (IOException e) { + e.printStackTrace(); + } + + if (allDocs == null) { + logger.error("Could not get all documents for " + TenantInfoDoc.class.getCanonicalName() + " in the DB."); + return; + } + + allDocs.forEach(doc -> { + TenantInfo ti = new TenantInfo(); + String tenantName = doc.getId(); + ti.setName(tenantName); + ti.setServiceAccount(doc.getServiceprincipalAppId()); + this.tenants.put(tenantName, ti) ; + }); + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantInfoDoc.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantInfoDoc.java new file mode 100644 index 0000000000000000000000000000000000000000..4fe1fa25eac53b1fdb7777fd4b0448dee0f9cbff --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantInfoDoc.java @@ -0,0 +1,33 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.di; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TenantInfoDoc { + + public static final String DB_NAME = "TenantInfo"; //collection name + + @Id + private String id; + private String serviceprincipalAppId; +} + diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantInfoFactory.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantInfoFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..5c45b59c8c0e5ad2a5b7af579bb8be6d7425ef2c --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/di/TenantInfoFactory.java @@ -0,0 +1,44 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.di; + +import javax.inject.Inject; + +import org.opengroup.osdu.core.common.model.core.DpsHeaders; +import org.opengroup.osdu.core.common.model.core.ITenantFactory; +import org.opengroup.osdu.core.common.model.core.TenantInfo; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.AbstractFactoryBean; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +@Component +@RequestScope +public class TenantInfoFactory extends AbstractFactoryBean<TenantInfo> { + @Inject + private ITenantFactory tenantFactory; + @Inject + @Qualifier("dpsHeaderFactorySearch") + private DpsHeaders headers; + @Override + protected TenantInfo createInstance() throws Exception { + String id = this.headers.getPartitionIdWithFallbackToAccountId(); + return this.tenantFactory.getTenantInfo(id); + } + @Override + public Class<?> getObjectType() { + return TenantInfo.class; + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/model/AADConfiguration.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/model/AADConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..de8a58f27216827338d69abc8390dce86fde40b3 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/model/AADConfiguration.java @@ -0,0 +1,38 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.model; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Getter +@Setter +@Component +@ConfigurationProperties("aad") +public class AADConfiguration { + String clientId; + String authority; + String secretKey; + String oboApi; + + public String getAuthority(){ + if (!authority.endsWith("/")) { + authority += "/"; + } + return authority; + } +} \ No newline at end of file diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/model/ElasticSettingSchema.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/model/ElasticSettingSchema.java new file mode 100644 index 0000000000000000000000000000000000000000..a43caf08d835f9691b9e20981d191b7bea30df20 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/model/ElasticSettingSchema.java @@ -0,0 +1,40 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.model; + +import javax.validation.constraints.NotEmpty; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ElasticSettingSchema { + + @NotEmpty + private String host; + + @NotEmpty + private String port; + + @NotEmpty + private String usernameAndPassword; + + @NotEmpty + private boolean isHttps; + +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticRepositoryBYOC.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticRepositoryBYOC.java new file mode 100644 index 0000000000000000000000000000000000000000..0cc67b97bf949b625a8a65c278695ccb79bfe5af --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticRepositoryBYOC.java @@ -0,0 +1,67 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.persistence; + +import org.apache.http.HttpStatus; + +import org.opengroup.osdu.core.common.model.core.TenantInfo; +import org.opengroup.osdu.indexer.ibm.model.ElasticSettingSchema; +import org.opengroup.osdu.core.common.model.core.ClusterSettings; +import org.opengroup.osdu.core.common.spi.coreis.ElasticRepository; +import org.opengroup.osdu.core.common.model.coreis.AppException; +import org.opengroup.osdu.core.common.service.coreis.Preconditions; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import javax.inject.Inject; + +@Component +public class ElasticRepositoryBYOC implements ElasticRepository { + + @Value("${ELASTIC_DATASTORE_KIND}") + private String ELASTIC_DATASTORE_KIND; + + @Value("${ELASTIC_DATASTORE_ID}") + private String ELASTIC_DATASTORE_ID; + + @Inject + private ISchemaRepository schemaRepository; + + + @Override + public ClusterSettings getElasticClusterSettings(TenantInfo tenantInfo) { + + if(tenantInfo == null) + throw new AppException(HttpStatus.SC_NOT_FOUND, "TenantInfo is null", ""); + + String settingId = tenantInfo.getName().concat("-").concat(ELASTIC_DATASTORE_ID); + ElasticSettingSchema schema = this.schemaRepository.get(settingId); + + if (schema == null) { + throw new AppException(HttpStatus.SC_NOT_FOUND, "Elastic setting not found", "The requested cluster setting was not found in CosmosDB.", String.format("Elastic setting with key: '%s' does not exist in CosmostDB.", ELASTIC_DATASTORE_KIND)); + } + + String host = schema.getHost(); + String portString = schema.getPort(); + String usernameAndPassword = schema.getUsernameAndPassword(); + + Preconditions.checkNotNullOrEmpty(host, "host cannot be null"); + Preconditions.checkNotNullOrEmpty(portString, "port cannot be null"); + Preconditions.checkNotNullOrEmpty(usernameAndPassword, "configuration cannot be null"); + + int port = Integer.parseInt(portString); + + return new ClusterSettings(host, port, usernameAndPassword, schema.isHttps(), schema.isHttps()); + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingSchemaRepositoryImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingSchemaRepositoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..77e58c696df6f25501596fccea28368555a31a2e --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingSchemaRepositoryImpl.java @@ -0,0 +1,74 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.persistence; + +import java.net.MalformedURLException; + +import javax.annotation.PostConstruct; + +import org.opengroup.osdu.core.ibm.cloudant.IBMCloudantClientFactory; +import org.opengroup.osdu.indexer.ibm.model.ElasticSettingSchema; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import org.slf4j.Logger; +import com.cloudant.client.api.Database; + + +@Repository +public class ElasticSettingSchemaRepositoryImpl implements ISchemaRepository { + + private static final Logger logger = LoggerFactory.getLogger(ElasticSettingSchemaRepositoryImpl.class); + + @Autowired + private IBMCloudantClientFactory cloudantFactory; + private Database db; + + @PostConstruct + public void init(){ + try { + db = cloudantFactory.getClient().database(ElasticSettingsDoc.DB_NAME, true); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + + @Override + public void add(ElasticSettingSchema schema, String id) { + ElasticSettingsDoc sd = new ElasticSettingsDoc(); + sd.setId(id); + sd.setSettingSchema(schema); + db.save(sd); + } + + @Override + public ElasticSettingSchema get(String id) { + if (db.contains(id)) { + ElasticSettingsDoc sd = db.find(ElasticSettingsDoc.class, id); + ElasticSettingSchema newSchema = new ElasticSettingSchema(); + newSchema.setPort(sd.getSettingSchema().getPort()); + newSchema.setHost(sd.getSettingSchema().getHost()); + newSchema.setUsernameAndPassword(sd.getSettingSchema().getUsernameAndPassword()); + newSchema.setHttps(sd.getSettingSchema().isHttps()); + return newSchema; + } else { + logger.error(ElasticSettingsDoc.class + " with id " + id + " was not found in the database."); + return null; + } + + + + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingsDoc.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingsDoc.java new file mode 100644 index 0000000000000000000000000000000000000000..3c320cf351a4d8bfa68f9a366e0ccd2742e54117 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ElasticSettingsDoc.java @@ -0,0 +1,36 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.persistence; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.opengroup.osdu.indexer.ibm.model.ElasticSettingSchema; +import org.springframework.data.annotation.Id; + +@Data +@AllArgsConstructor +@NoArgsConstructor + +public class ElasticSettingsDoc { + + public static final String DB_NAME = "SearchSettings"; //collection name + + @Id + private String id; + private ElasticSettingSchema settingSchema; +} + +//interface CosmosDBElasticSettings extends DocumentDbRepository<ElasticSettingsDoc, String> {} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ISchemaRepository.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ISchemaRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..6b5f6157de45164de6dd7d8dfcae83bb7f63b5ec --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/persistence/ISchemaRepository.java @@ -0,0 +1,30 @@ + +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.persistence; + +import org.opengroup.osdu.indexer.ibm.model.ElasticSettingSchema; + +public interface ISchemaRepository { + String SCHEMA_KIND = "IndexerSchema"; + + String SCHEMA = "schema"; + String KIND = "KIND"; + + void add(ElasticSettingSchema schema, String id); + + ElasticSettingSchema get(String id); +} + diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/publish/PublisherImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/publish/PublisherImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..285c4bbdfa434b5835f98d68002ff55ea2478ad7 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/publish/PublisherImpl.java @@ -0,0 +1,111 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.publish; + + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import org.elasticsearch.common.Strings; +import org.opengroup.osdu.core.common.model.core.DpsHeaders; +import org.opengroup.osdu.core.common.model.coreis.RecordChangedMessages; +import org.opengroup.osdu.core.common.model.indexer.JobStatus; +import org.opengroup.osdu.core.common.model.indexer.RecordStatus; +import org.opengroup.osdu.core.common.service.coreis.JaxRsDpsLog; +import org.opengroup.osdu.core.common.spi.indexer.IPublisher; +import org.opengroup.osdu.core.ibm.messagebus.IRabbitMQFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; + +@Component +@RequestScope +public class PublisherImpl implements IPublisher { + + + @Inject + IRabbitMQFactory rabbitMQ; + + @Inject + private JaxRsDpsLog logger; + + @Override + public void publishStatusChangedTagsToTopic(DpsHeaders headers, JobStatus indexerBatchStatus) throws Exception { + + String tenant = headers.getPartitionId(); + if (Strings.isNullOrEmpty(tenant)) + tenant = headers.getAccountId(); + + + Map<String, String> message = new HashMap<>(); + message.put(tenant, headers.getPartitionIdWithFallbackToAccountId()); + headers.addCorrelationIdIfMissing(); + message.put(DpsHeaders.CORRELATION_ID, headers.getCorrelationId()); + + + + RecordChangedMessages recordChangedMessages = getRecordChangedMessage(headers, indexerBatchStatus); + message.put("data", recordChangedMessages.toString()); + + try { + logger.info("Indexer publishes message " + headers.getCorrelationId()); + rabbitMQ.sendMessage(message.toString()); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } + + private RecordChangedMessages getRecordChangedMessage(DpsHeaders headers, JobStatus indexerBatchStatus) { + + Gson gson = new GsonBuilder().create(); + Map<String, String> attributesMap = new HashMap<>(); + Type listType = new TypeToken<List<RecordStatus>>() { + }.getType(); + + JsonElement statusChangedTagsJson = gson.toJsonTree(indexerBatchStatus.getStatusesList(), listType); + String statusChangedTagsData = (statusChangedTagsJson.toString()); + + String tenant = headers.getPartitionId(); + // This code it to provide backward compatibility to slb-account-id + if (!Strings.isNullOrEmpty(tenant)) { + attributesMap.put(DpsHeaders.DATA_PARTITION_ID, headers.getPartitionIdWithFallbackToAccountId()); + } else { + attributesMap.put(DpsHeaders.ACCOUNT_ID, headers.getPartitionIdWithFallbackToAccountId()); + } + headers.addCorrelationIdIfMissing(); + attributesMap.put(DpsHeaders.CORRELATION_ID, headers.getCorrelationId()); + + + RecordChangedMessages recordChangedMessages = new RecordChangedMessages(); + // statusChangedTagsData is not ByteString but String + recordChangedMessages.setData(statusChangedTagsData); + recordChangedMessages.setAttributes(attributesMap); + + + + + return recordChangedMessages; + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/BasicAuthSecurityConfig.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/BasicAuthSecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..60678a6e3e1fdb98f36a4622ba35978a47e88b5b --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/BasicAuthSecurityConfig.java @@ -0,0 +1,50 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.security; + +import com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import javax.inject.Inject; + +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class BasicAuthSecurityConfig extends WebSecurityConfigurerAdapter { + @Inject + private AADAppRoleStatelessAuthenticationFilter appRoleAuthFilter; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER) + .and() + .authorizeRequests() + .antMatchers("/", "/index.html","/obo_api", + "/index-worker", "/_dps/task-handlers", "/_dps/task-handlers/**", + "/reindex", + "/v2/api-docs", + "/swagger-resources/**", + "/configuration/security", + "/swagger", + "/swagger-ui.html", + "/webjars/**").permitAll() + .anyRequest().authenticated() + .and().addFilterBefore(appRoleAuthFilter, UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/WhoamiController.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/WhoamiController.java new file mode 100644 index 0000000000000000000000000000000000000000..dc969170721deb8407881f43d8e777c2e122736d --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/WhoamiController.java @@ -0,0 +1,39 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.security; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class WhoamiController { + @RequestMapping(value = {"/", "/whoami"}) + @ResponseBody + public String whoami() { + final Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + String userName = auth.getName(); + String roles = String.valueOf(auth.getAuthorities()); + String details = String.valueOf(auth.getPrincipal()); + + return "user: " + userName + "<BR>" + + "roles: " + roles + "<BR>" + + "details: " + details; + } +} + diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/HeadersInfoIBMImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/HeadersInfoIBMImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..782cfeb304028f1b1811f3356911a754a8af0e24 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/HeadersInfoIBMImpl.java @@ -0,0 +1,97 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.util; + +import lombok.extern.java.Log; +import org.opengroup.osdu.core.common.model.core.DpsHeaders; +import org.opengroup.osdu.core.common.model.coreis.SlbHeaders; +import org.opengroup.osdu.core.common.spi.coreis.IHeadersInfo; +import org.opengroup.osdu.core.common.service.coreis.Preconditions; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +import javax.inject.Inject; +import java.util.HashSet; +import java.util.Map; +import java.util.stream.Collectors; + +@Log +@Component +@RequestScope +public class HeadersInfoIBMImpl implements IHeadersInfo { + + @Inject + @Qualifier("dpsHeaderFactorySearch") + private DpsHeaders headersMap; + + @Value("${indexer.queue.key}") + private String queueKey; + + //ToDo this should be moved to Azure client-lib + public static final String INDEXER_QUEUE_KEY = "x-functions-key"; + + private static final HashSet<String> FORBIDDEN_FROM_LOGGING = new HashSet<>(); + static { + FORBIDDEN_FROM_LOGGING.add(DpsHeaders.AUTHORIZATION); + FORBIDDEN_FROM_LOGGING.add(DpsHeaders.ON_BEHALF_OF); + // FORBIDDEN_FROM_LOGGING.add(INDEXER_QUEUE_KEY); + } + + @Override + public DpsHeaders getHeaders() { + if (headersMap == null) { + log.warning("Headers Map DpsHeaders is null"); + } + DpsHeaders headers = this.getCoreServiceHeaders(headersMap.getHeaders()); + return headers; + } + + @Override + public String getUser() { + return getHeaders().getUserEmail(); + } + + @Override + public String getPartitionId() { + return getHeaders().getPartitionIdWithFallbackToAccountId(); + } + + @Override + public String getPrimaryPartitionId() { + return getHeadersMap().get(SlbHeaders.PRIMARY_PARTITION_ID); + } + + @Override + public Map<String, String> getHeadersMap() { + return getHeaders().getHeaders(); + } + + @Override + public DpsHeaders getCoreServiceHeaders(Map<String, String> input) { + Preconditions.checkNotNull(input, "input headers cannot be null"); + + DpsHeaders output = DpsHeaders.createFromMap(input); + output.put(INDEXER_QUEUE_KEY,queueKey); + return output; + } + + @Override + public String toString() { + return this.getHeadersMap().entrySet().stream().filter(map -> !FORBIDDEN_FROM_LOGGING.contains(map.getKey().toLowerCase())).map(Map.Entry::toString).collect(Collectors.joining(" | ")); + } + +} \ No newline at end of file diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/RequestInfoImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/RequestInfoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..01f94956590ac0cc6c664ca336c12463c9bd7243 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/RequestInfoImpl.java @@ -0,0 +1,110 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.util; + +import com.google.common.base.Strings; +import org.apache.http.HttpStatus; +import org.opengroup.osdu.core.common.model.core.DpsHeaders; +import org.opengroup.osdu.core.common.model.core.TenantInfo; +import org.opengroup.osdu.core.common.model.coreis.AppException; +import org.opengroup.osdu.core.common.model.coreis.DeploymentEnvironment; +import org.opengroup.osdu.core.common.spi.coreis.IHeadersInfo; +import org.opengroup.osdu.core.common.spi.coreis.IRequestInfo; +import org.opengroup.osdu.core.common.spi.coreis.IServiceAccountJwtClient; +import org.opengroup.osdu.core.common.model.coreis.AppException; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +import javax.inject.Inject; +import java.util.Map; + +import static org.opengroup.osdu.core.common.model.core.DpsHeaders.AUTHORIZATION; +import static org.opengroup.osdu.indexer.ibm.util.HeadersInfoIBMImpl.INDEXER_QUEUE_KEY; + + +@Component +@RequestScope +public class RequestInfoImpl implements IRequestInfo { + + @Inject + @Qualifier("dpsHeaderFactorySearch") + private DpsHeaders dpsHeaders; + + @Inject + private IHeadersInfo headersInfo; + @Inject + private IServiceAccountJwtClient serviceAccountJwtClient; + + @Inject + @Qualifier("TenantInfoFactorySearch") + private TenantInfo tenantInfo; + + @Value("${DEPLOYMENT_ENVIRONMENT}") + private String DEPLOYMENT_ENVIRONMENT; + + + @Override + public DpsHeaders getHeaders() { + + return this.headersInfo.getHeaders(); + } + + @Override + public String getPartitionId() { + return this.headersInfo.getPartitionId(); + } + + @Override + public Map<String, String> getHeadersMap() { + return this.headersInfo.getHeadersMap(); + } + + @Override + public Map<String, String> getHeadersMapWithDwdAuthZ() { + return getHeadersWithDwdAuthZ().getHeaders(); + } + + @Override + public DpsHeaders getHeadersWithDwdAuthZ() { + this.dpsHeaders.put(AUTHORIZATION, this.checkOrGetAuthorizationHeader()); + return this.headersInfo.getHeaders(); + } + + @Override + public boolean isCronRequest() { return false;} + + @Override + public boolean isTaskQueueRequest() { + if (!this.headersInfo.getHeadersMap().containsKey(INDEXER_QUEUE_KEY)) return false; + +// String queueId = this.headersInfo.getHeadersMap().get(AppEngineHeaders.TASK_QUEUE_NAME); +// return queueId.endsWith(Constants.INDEXER_QUEUE_IDENTIFIER); + return false; + } + + public String checkOrGetAuthorizationHeader() { + if (DeploymentEnvironment.valueOf(DEPLOYMENT_ENVIRONMENT) == DeploymentEnvironment.LOCAL) { + String authHeader = this.headersInfo.getHeaders().getAuthorization(); + if (Strings.isNullOrEmpty(authHeader)) { + throw new AppException(HttpStatus.SC_UNAUTHORIZED, "Invalid authorization header", "Authorization token cannot be empty"); + } + return authHeader; + } else { + return "Bearer " + this.serviceAccountJwtClient.getIdToken(tenantInfo.getName()); + } + } +} diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/ServiceAccountJwtClientImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/ServiceAccountJwtClientImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..6ca3101dd4ae52350f465cf0b6dfd1a3e9f8bae2 --- /dev/null +++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/ServiceAccountJwtClientImpl.java @@ -0,0 +1,119 @@ +// Copyright 2019 IBM Corp. All Rights Reserved. +// +// 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.indexer.ibm.util; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.microsoft.aad.adal4j.AuthenticationContext; +import com.microsoft.aad.adal4j.AuthenticationResult; +import com.microsoft.aad.adal4j.ClientCredential; +import org.apache.http.HttpStatus; +import org.opengroup.osdu.core.common.model.core.DpsHeaders; +import org.opengroup.osdu.core.common.model.core.ITenantFactory; +import org.opengroup.osdu.core.common.model.core.TenantInfo; +import org.opengroup.osdu.core.common.model.coreis.AppException; +import org.opengroup.osdu.core.common.service.coreis.JaxRsDpsLog; +import org.opengroup.osdu.indexer.ibm.model.AADConfiguration; + +import org.opengroup.osdu.core.common.model.coreis.IdToken; +import org.opengroup.osdu.core.common.spi.coreis.IJwtCache; +import org.opengroup.osdu.core.common.spi.coreis.IHeadersInfo; +import org.opengroup.osdu.core.common.spi.coreis.IServiceAccountJwtClient; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +import javax.inject.Inject; +import java.net.MalformedURLException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +@Component +@RequestScope +public class ServiceAccountJwtClientImpl implements IServiceAccountJwtClient { + + @Inject + private ITenantFactory tenantInfoServiceProvider; + @Inject + private IHeadersInfo headersInfoAzure; + @Inject + @Qualifier("dpsHeaderFactorySearch") + private DpsHeaders dpsHeaders; + @Inject + private IJwtCache cacheService; + @Inject + private JaxRsDpsLog log; + + @Inject + private AADConfiguration configuration; + + public String getIdToken(String tenantName) { + this.log.info("Tenant name received for auth token is: " + tenantName); + TenantInfo tenant = this.tenantInfoServiceProvider.getTenantInfo(tenantName); + if (tenant == null) { + this.log.error("Invalid tenant name receiving from azure"); + throw new AppException(HttpStatus.SC_BAD_REQUEST, "Invalid tenant Name", "Invalid tenant Name from azure"); + } + String ACCESS_TOKEN = ""; + try { + + IdToken cachedToken = (IdToken) this.cacheService.get(tenant.getServiceAccount()); + this.headersInfoAzure.getHeaders().put(DpsHeaders.USER_EMAIL, tenant.getServiceAccount()); + this.dpsHeaders.put(DpsHeaders.USER_EMAIL, tenant.getServiceAccount()); + + if (!IdToken.refreshToken(cachedToken)) { + return cachedToken.getTokenValue(); + } + + ExecutorService service = Executors.newFixedThreadPool(1); + AuthenticationContext context = null; + + try { + context = new AuthenticationContext(configuration.getAuthority(), false, service); + ClientCredential credential = new ClientCredential(configuration.getClientId(), configuration.getSecretKey()); + Future<AuthenticationResult> future = context.acquireToken(configuration.getOboApi(), credential, null); + + ACCESS_TOKEN = future.get().getAccessToken(); + + if (future == null) { + log.error(String.format("Azure Authentication: %s", future.get().getAccessToken())); + throw new AppException(HttpStatus.SC_FORBIDDEN, "Access denied", "The user is not authorized to perform this action"); + } + IdToken idToken = IdToken.builder().tokenValue(ACCESS_TOKEN).expirationTimeMillis(JWT.decode(ACCESS_TOKEN).getExpiresAt().getTime()).build(); + + this.cacheService.put(tenant.getServiceAccount(), idToken); + + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } finally { + service.shutdown(); + } + } catch (JWTDecodeException e) { + throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Persistence error", "Invalid token, error decoding", e); + } catch (AppException e) { + throw e; + } catch (Exception e) { + throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Persistence error", "Error generating token", e); + } + + return ACCESS_TOKEN; + } +} diff --git a/provider/indexer-ibm/src/main/resources/application.properties b/provider/indexer-ibm/src/main/resources/application.properties new file mode 100644 index 0000000000000000000000000000000000000000..a79e32ddcde40517593b25bcef9d90b778ce2c65 --- /dev/null +++ b/provider/indexer-ibm/src/main/resources/application.properties @@ -0,0 +1,86 @@ +#server.servlet.contextPath=/api/indexer/v2/ +logging.level.org.springframework.web=DEBUG +server.port=8080 +JAVA_HEAP_OPTS=-Xms4096M -Xmx4096M +JAVA_GC_OPTS=-XX:+UseG1GC -XX:+UseStringDeduplication -XX:InitiatingHeapOccupancyPercent=45 + +DEFAULT_DATA_COUNTRY=US + +AUTHORIZE_API=https://opendesproxy.azurewebsites.net/entitlements/v1 +AUTHORIZE_API_KEY=dummy +LEGALTAG_API=https://opendesproxy.azurewebsites.net/legal/v1 +LEGALTAG_API_KEY=dummy +CRS_API=https://opendesproxy.azurewebsites.net/crs/v1 +CRS_API_KEY=dummy + +#Default Cache Settings +SCHEMA_CACHE_EXPIRATION=60 +INDEX_CACHE_EXPIRATION=60 +ELASTIC_CACHE_EXPIRATION=1440 +CURSOR_CACHE_EXPIRATION=60 +# Kinds Cache expiration 2*24*60 +KINDS_CACHE_EXPIRATION=2880 +# Attributes Cache expiration 2*24*60 +ATTRIBUTES_CACHE_EXPIRATION=2880 +# Maximum size of cache value +MAX_CACHE_VALUE_SIZE=1000 + +KINDS_REDIS_DATABASE=1 +CRON_INDEX_CLEANUP_THRESHOLD_DAYS=3 +CRON_EMPTY_INDEX_CLEANUP_THRESHOLD_DAYS=7 + +STORAGE_SCHEMA_HOST=https://opendesmvp.azurewebsites.net/schemas +STORAGE_QUERY_RECORD_HOST=https://opendesmvp.azurewebsites.net/query/records +STORAGE_QUERY_RECORD_FOR_CONVERSION_HOST=https://opendesmvp.azurewebsites.net/query/records:batch +STORAGE_RECORDS_BATCH_SIZE=20 + +INDEXER_QUEUE_HOST=https://requeuefunction-cd1.azurewebsites.net/api/re-enqueue + +#MSAL AAD +aad.authority=https://login.microsoftonline.com/1668106e-2ae0-456a-bb7d-64b52104db99/oauth2/token +aad.clientId=b70451c6-936f-4430-8680-0985817ac751 +aad.secretKey=b5GZtbS.bB7iSzLTb+Nb-AOb9G@1@LZo +aad.oboApi=api://ffb32b1a-c6fe-4ed0-819b-6ca483de3640 + +spring.security.oauth2.client.registration.azure.client-id=b70451c6-936f-4430-8680-0985817ac751 +spring.security.oauth2.client.registration.azure.client-secret=b5GZtbS.bB7iSzLTb+Nb-AOb9G@1@LZo +spring.security.oauth2.client.registration.azure.client-name=Azure + + +#spring.security.user.name=opendes@byoc.local +#spring.security.user.password=123 +#spring.security.user.roles=service.indexer.admin + + +# Azure AD configuration +azure.activedirectory.client-id=ffb32b1a-c6fe-4ed0-819b-6ca483de3640 +azure.activedirectory.tenant-id=1668106e-2ae0-456a-bb7d-64b52104db99 +azure.activedirectory.AppIdUri=api://${azure.activedirectory.client-id} +azure.activedirectory.session-stateless=true + +#azure.activedirectory.active-directory-groups=ADMIN, VIEWER + +# Azure CosmosDB configuration +azure.cosmosdb.uri=https://opendescosmosdb.documents.azure.com:443/ +azure.cosmosdb.key=AQK0VbnMGn76h9Ypw5aoirKMwoAYJBRY2XENn1dAYypoea83weVyjYcGaRDeEX7HqXIVggaDuFAhQ4Y1Y9lKDQ== +azure.cosmosdb.database=opendesdb + + +# Azure Service Bus configuration +azure.servicebus.connection-string=Endpoint=sb://pliuopendes.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=km8Nscc0gf299Ck6npmM3D14VU5Tx1lJYRdlHcExIvY= +azure.servicebus.topic-name=recordstopic + +#Indexer-Queue-header +indexer.queue.key=abcd + +REDIS_GROUP_HOST=127.0.0.1 +REDIS_GROUP_PORT=6379 +REDIS_SEARCH_HOST=localhost +REDIS_SEARCH_PORT=6379 + +ELASTIC_DATASTORE_KIND=SearchSettings +ELASTIC_DATASTORE_ID=indexer-service + +GAE_SERVICE=indexer + +DEPLOYMENT_ENVIRONMENT=CLOUD \ No newline at end of file diff --git a/provider/indexer-ibm/src/main/resources/logback-spring.xml b/provider/indexer-ibm/src/main/resources/logback-spring.xml new file mode 100644 index 0000000000000000000000000000000000000000..e70b3be45b9f89b107f0e0197dede6b2afa5da2a --- /dev/null +++ b/provider/indexer-ibm/src/main/resources/logback-spring.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2019 IBM Corp. All Rights Reserved. + + 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. + --> + +<configuration> + + <appender name="Console" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern> + %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable + </Pattern> + </layout> + </appender> + + <appender name="aiAppender" + class="com.microsoft.applicationinsights.logback.ApplicationInsightsAppender"> + <instrumentationKey>${appinsights_key}</instrumentationKey> + </appender> + + <!-- servicebus throws exceptions when idle --> + <logger name="com.microsoft.azure.servicebus.primitives.RequestResponseLink" level="error" /> + + <root level="warn"> + <appender-ref ref="Console" /> + <appender-ref ref="aiAppender" /> + </root> + +</configuration> \ No newline at end of file diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/CronServiceImplTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/CronServiceImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..58d97805083f4552d49de81cefc3524a90d55265 --- /dev/null +++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/CronServiceImplTest.java @@ -0,0 +1,141 @@ +// 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.indexer.ibm.service; + +import com.google.common.collect.Lists; +import org.elasticsearch.client.RestHighLevelClient; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.opengroup.osdu.core.common.model.core.DpsHeaders; +import org.opengroup.osdu.indexer.service.CronServiceImpl; +import org.opengroup.osdu.core.common.service.coreis.JaxRsDpsLog; +import org.opengroup.osdu.core.common.model.coreis.IndexInfo; +import org.opengroup.osdu.core.common.spi.coreis.IRequestInfo; +import org.opengroup.osdu.core.common.service.coreis.IndicesService; +import org.opengroup.osdu.core.common.service.coreis.Config; +import org.opengroup.osdu.is.core.util.ElasticClientHandler; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.powermock.api.mockito.PowerMockito.mockStatic; + + +@Ignore +@RunWith(SpringRunner.class) +@PrepareForTest({RestHighLevelClient.class}) +public class CronServiceImplTest { + + @Mock + private RestHighLevelClient restHighLevelClient; + @Mock + private IndicesService indicesService; + @Mock + private ElasticClientHandler elasticClientHandler; + @Mock + private IRequestInfo requestInfo; + @Mock + private JaxRsDpsLog log; + @InjectMocks + private CronServiceImpl sut; + + @InjectMocks + private DpsHeaders dpsHeaders; + + @Before + public void setup() { + mockStatic(Config.class); + + when(this.requestInfo.getHeaders()).thenReturn(dpsHeaders); + + when(Config.getIndexCleanupThresholdDays()).thenReturn(3); + when(Config.getEmptyIndexCleanupThresholdDays()).thenReturn(7); + } + + @Test + public void run_cleanup_when_cron_job_runs_with_correct_pattern() throws Exception { + final String indexPattern = "tenant1-index-*"; + + IndexInfo info = IndexInfo.builder().name("tenant1-index-1.0.0").documentCount("10").creationDate(Long.toString(Instant.now().minus(4, ChronoUnit.DAYS).toEpochMilli())).build(); + + when(this.requestInfo.getPartitionId()).thenReturn("tenant1"); + when(this.elasticClientHandler.createRestClient()).thenReturn(this.restHighLevelClient); + when(this.indicesService.getIndexInfo(this.restHighLevelClient, indexPattern)).thenReturn(Lists.newArrayList(info)); + + this.sut.cleanupIndices(indexPattern); + + verify(this.indicesService, times(1)).deleteIndex(restHighLevelClient, "tenant1-index-1.0.0"); + verify(this.indicesService, times(1)).getIndexInfo(restHighLevelClient, indexPattern); + } + + @Test(expected = IOException.class) + public void run_cleanup_when_cron_job_runs_with_wrong_pattern() throws Exception { + IOException exception = new IOException("blah"); + when(this.elasticClientHandler.createRestClient()).thenReturn(this.restHighLevelClient); + when(this.indicesService.getIndexInfo(this.restHighLevelClient, "tenant1-test-*")).thenThrow(exception); + + this.sut.cleanupIndices("tenant1-test-*"); + + verify(this.indicesService, times(0)).deleteIndex(any(), any()); + } + + @Test + public void run_cleanup_when_backend_does_not_have_empty_stale_indices() throws Exception { + IndexInfo info = IndexInfo.builder().name("tenant1-index-1.0.0").documentCount("10").creationDate(Long.toString(Instant.now().minus(8, ChronoUnit.DAYS).toEpochMilli())).build(); + + when(this.requestInfo.getPartitionId()).thenReturn("tenant1"); + when(this.elasticClientHandler.createRestClient()).thenReturn(this.restHighLevelClient); + when(this.indicesService.getIndexInfo(this.restHighLevelClient, null)).thenReturn(Lists.newArrayList(info)); + + this.sut.cleanupEmptyStaleIndices(); + + verify(this.indicesService, times(0)).deleteIndex(restHighLevelClient, null); + verify(this.indicesService, times(1)).getIndexInfo(restHighLevelClient, null); + } + + @Test + public void run_cleanup_when_backend_have_empty_stale_indices() throws Exception { + IndexInfo info = IndexInfo.builder().name("tenant1-index-1.0.0").documentCount("0").creationDate(Long.toString(Instant.now().minus(8, ChronoUnit.DAYS).toEpochMilli())).build(); + + when(this.requestInfo.getPartitionId()).thenReturn("tenant1"); + when(this.elasticClientHandler.createRestClient()).thenReturn(this.restHighLevelClient); + when(this.indicesService.getIndexInfo(this.restHighLevelClient, null)).thenReturn(Lists.newArrayList(info)); + + this.sut.cleanupEmptyStaleIndices(); + + verify(this.indicesService, times(1)).deleteIndex(restHighLevelClient, "tenant1-index-1.0.0"); + verify(this.indicesService, times(1)).getIndexInfo(restHighLevelClient, null); + } + + @Test(expected = IOException.class) + public void run_cleanup_when_backend_throws_exception() throws Exception { + IOException exception = new IOException("blah"); + when(this.elasticClientHandler.createRestClient()).thenReturn(this.restHighLevelClient); + when(this.indicesService.getIndexInfo(this.restHighLevelClient, null)).thenThrow(exception); + + this.sut.cleanupEmptyStaleIndices(); + + verify(this.indicesService, times(0)).deleteIndex(any(), any()); + } +} \ No newline at end of file diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/ElasticSettingServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/ElasticSettingServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..67e481d3065b7ba39b0d2cb830abba556b63bbab --- /dev/null +++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/ElasticSettingServiceTest.java @@ -0,0 +1,112 @@ +// 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.indexer.ibm.service; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.opengroup.osdu.core.common.model.core.TenantInfo; +import org.opengroup.osdu.indexer.service.ElasticSettingServiceImpl; +import org.opengroup.osdu.core.common.service.coreis.JaxRsDpsLog; +import org.opengroup.osdu.core.common.model.core.ClusterSettings; +import org.opengroup.osdu.core.common.spi.coreis.IElasticCredentialsCache; +import org.opengroup.osdu.core.common.spi.coreis.ElasticRepository; +import org.opengroup.osdu.core.common.spi.coreis.IHeadersInfo; +import org.opengroup.osdu.core.common.service.coreis.TenantInfoService; +import org.opengroup.osdu.core.common.model.coreis.AppException; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +@RunWith(SpringRunner.class) +public class ElasticSettingServiceTest { + + @Mock + private TenantInfoService tenantInfoService; + @Mock + private ElasticRepository elasticRepository; + @Mock + private IElasticCredentialsCache elasticCredentialCache; + @Mock + private TenantInfo tenantInfo; + @InjectMocks + private ElasticSettingServiceImpl sut; + @Mock + private ClusterSettings clusterSettings; + @Mock + private IHeadersInfo headersInfo; + + @Mock + private JaxRsDpsLog log; + + + public String GAE_SERVICE = "indexer"; + + private final String host = "db5c51c1.us-central1.gcp.cloud.es.io"; + private final int port = 9243; + private final String credentials = "name:password"; + + String cacheKey = ""; + + + @Before + public void setup() { + when(tenantInfo.getName()).thenReturn("tenant1"); + when(this.headersInfo.getPartitionId()).thenReturn("tenant1"); + when(this.tenantInfoService.getTenantInfo()).thenReturn(tenantInfo); + sut.GAE_SERVICE = "indexer"; + clusterSettings = ClusterSettings.builder().host(host).port(port).userNameAndPassword(credentials).build(); + cacheKey = String.format("%s-%s", GAE_SERVICE, tenantInfo.getName()); + } + + @Test + public void should_getValid_clusterSettings_fromCache() { + + when(this.elasticCredentialCache.get(cacheKey)).thenReturn(clusterSettings); + + ClusterSettings response = this.sut.getElasticClusterInformation(); + assertNotNull(response); + assertEquals(response.getHost(), host); + assertEquals(response.getPort(), port); + assertEquals(response.getUserNameAndPassword(), credentials); + } + + @Test + public void should_getValid_clusterSettings_fromCosmosDB() { + + when(this.elasticCredentialCache.get(cacheKey)).thenReturn(clusterSettings); + + when(this.elasticRepository.getElasticClusterSettings(tenantInfo)).thenReturn(clusterSettings); + + ClusterSettings response = this.sut.getElasticClusterInformation(); + assertNotNull(response); + assertEquals(response.getHost(), host); + assertEquals(response.getPort(), port); + assertEquals(response.getUserNameAndPassword(), credentials); + } + + @Test(expected = AppException.class) + public void should_throwAppException_when_tenantClusterInfo_not_found() throws AppException { + + when(this.elasticRepository.getElasticClusterSettings(tenantInfo)).thenReturn(null); + + this.sut.getElasticClusterInformation(); + + } +} diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexCopyServiceImplTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexCopyServiceImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6ae884ff6d18155d6f69fc4376ec97dad9fe8ec5 --- /dev/null +++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexCopyServiceImplTest.java @@ -0,0 +1,194 @@ +// 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.indexer.ibm.service; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.apache.http.HttpEntity; +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestHighLevelClient; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.opengroup.osdu.core.common.model.core.DpsHeaders; +import org.opengroup.osdu.core.common.model.indexer.AuditLogger; +import org.opengroup.osdu.indexer.service.IndexCopyServiceImpl; +import org.opengroup.osdu.indexer.service.IndexerMappingService; +import org.opengroup.osdu.core.common.model.core.ClusterSettings; +import org.opengroup.osdu.core.common.spi.coreis.IHeadersInfo; +import org.opengroup.osdu.core.common.spi.coreis.IRequestInfo; +import org.opengroup.osdu.is.core.service.ElasticSettingService; +import org.opengroup.osdu.core.common.service.coreis.IndicesService; +import org.opengroup.osdu.core.common.model.coreis.AppException; +import org.opengroup.osdu.is.core.util.ElasticClientHandler; +import org.opengroup.osdu.core.common.service.coreis.ElasticIndexNameResolver; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(SpringRunner.class) +@PrepareForTest({RestHighLevelClient.class, Response.class, RestClient.class, HttpEntity.class, EntityUtils.class}) +public class IndexCopyServiceImplTest { + private final String correlationId = UUID.randomUUID().toString(); + + @Mock + private HttpEntity httpEntity; + @Mock + private HttpEntity httpEntityRequest; + @Mock + private IRequestInfo requestInfo; + @Mock + private IHeadersInfo headersInfo; + @Mock + private RestClient restClient; + @Mock + private RestHighLevelClient restHighLevelClient; + @Mock + private IndicesService indicesService; + @Mock + private IndexerMappingService mappingService; + @Mock + private ElasticClientHandler elasticClientHandler; + @Mock + private ElasticIndexNameResolver elasticIndexNameResolver; + @Mock + private Response response; + @Mock + private ElasticSettingService elasticSettingService; + @Mock + private AuditLogger auditLogger; + @Mock + private Map<String, String> httpHeaders; + @InjectMocks + private IndexCopyServiceImpl sut; + + private ClusterSettings commonCluster; + + private Map<String, Object> correctMap; + + @Before + public void setup() { + + commonCluster = ClusterSettings.builder().host("commonhost").port(8080).userNameAndPassword("username:pwd").build(); + + httpHeaders = new HashMap<>(); + httpHeaders.put(DpsHeaders.AUTHORIZATION, "testAuth"); + httpHeaders.put(DpsHeaders.CORRELATION_ID, correlationId); + DpsHeaders standardHeaders = DpsHeaders.createFromMap(httpHeaders); + when(headersInfo.getHeaders()).thenReturn(standardHeaders); + when(requestInfo.getHeadersMapWithDwdAuthZ()).thenReturn(httpHeaders); + when(response.getEntity()).thenReturn(httpEntity); + + Type mapType = new TypeToken<Map<String, Object>>() {}.getType(); + String afterFormat = "{\"properties\":{\"id\":{\"type\":\"keyword\"}}}"; + correctMap = new Gson().fromJson(afterFormat, mapType); + + restHighLevelClient = mock(RestHighLevelClient.class); + + } + + @Test(expected = IOException.class) + public void should_throwIOException_when_indexMappingNotFound() throws Exception { + IOException exception = new IOException("Fail to get mapping for the given index from common cluster."); + + when(this.mappingService.getIndexMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenThrow(exception); + + this.sut.copyIndex("common:metadata:entity:1.0.0"); + } + + @Test(expected = IllegalArgumentException.class) + public void should_throwIllegalArgExceptionCopyIndexRequest_copyIndexTest() { + try { + this.sut.copyIndex(null); + } catch (IOException e) { + fail("Should not throw IOException but illegalArgumentException."); + } + } + + @Test + public void should_returnIndexMapping_getIndexMappingFromCommonClustertest() { + String mappingJson = "{\"common-metadata-entity-1.0.0\":{\"mappings\":{\"entity\":{\"properties\":{\"id\":{\"type\":\"keyword\"}}}}}}"; + when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient); + try { + when(this.mappingService.getIndexMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(mappingJson); + Map<String, Object> resultMap = this.sut.getIndexMappingsFromCommonCluster("test", "test"); + Assert.assertEquals(resultMap, correctMap); + } catch (Exception ignored) { + } + } + + @Test + public void should_returnClusterInfo_getCommonClusterInformationtest() { + try { + String[] correctCommonCluster = {"https://commonhost:8080", "username", "pwd"}; + + when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient); + + when(elasticSettingService.getElasticClusterInformation()).thenReturn(commonCluster); + + String[] resultCommonCluster = this.sut.getCommonClusterInformation(); + Assert.assertEquals(correctCommonCluster[0], resultCommonCluster[0]); + Assert.assertEquals(correctCommonCluster[1], resultCommonCluster[1]); + Assert.assertEquals(correctCommonCluster[2], resultCommonCluster[2]); + } catch (IOException ignored) { + fail("Should not throw this exception " + ignored.getMessage()); + } + } + + @Test(expected = AppException.class) + public void should_throwException_failToCreateIndexInTenantCluster_createIndexInTenantClustertest() { + try { + when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient); + when(indicesService.createIndex(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(false); + this.sut.createIndexInTenantCluster("test", "test", "test", correctMap); + } catch (IOException ignored) { + fail("Should not throw this exception " + ignored.getMessage()); + } + } + + @Ignore + public void should_returnTaskIdResponse_reindexRequestSucceed_reindexInTenantClustertest() { + //TODO: fix the null Response from restHighLevelClient.getLowLevelClient().performRequest(). + try { + String[] correctCommonCluster = {"https://commonhost:8080", "username", "pwd"}; + Request request = new Request("POST", "/_reindex?wait_for_completion=false"); + request.setEntity(httpEntityRequest); + when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient); + when(indicesService.createIndex(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(false); + when(restHighLevelClient.getLowLevelClient()).thenReturn(restClient); + when(restClient.performRequest(request)).thenReturn(response); + when(response.getEntity()).thenReturn(httpEntity); + Assert.assertEquals(httpEntity, this.sut.reindexInTenantCluster("test", "test", correctCommonCluster)); + } catch (IOException ignored) { + } + } +} diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerMappingServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerMappingServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..754677b3bf3ae463f49891cf1a31763a4f9d4a61 --- /dev/null +++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerMappingServiceTest.java @@ -0,0 +1,316 @@ +// 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.indexer.ibm.service; + +import org.apache.http.StatusLine; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse; +import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse.FieldMappingMetaData; +import org.elasticsearch.action.bulk.BulkItemResponse.Failure; +import org.elasticsearch.action.support.master.AcknowledgedResponse; +import org.elasticsearch.client.*; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.index.reindex.BulkByScrollResponse; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.opengroup.osdu.core.common.model.indexer.IndexSchema; +import org.opengroup.osdu.indexer.service.IndexerMappingServiceImpl; +import org.opengroup.osdu.core.common.model.coreis.RecordMetaAttribute; +import org.opengroup.osdu.core.common.model.coreis.AppException; +import org.opengroup.osdu.is.core.util.ElasticClientHandler; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; +import java.util.*; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; + +@Ignore +@RunWith(SpringRunner.class) +@PrepareForTest({ RestHighLevelClient.class, IndicesClient.class }) +public class IndexerMappingServiceTest { + + private final String kind = "tenant:test:test:1.0.0"; + private final String index = "tenant-test-test-1.0.0"; + private final String type = "test"; + private final String mappingValid = "{\"dynamic\":false,\"properties\":{\"data\":{\"properties\":{\"Location\":{\"type\":\"geo_point\"}}},\"id\":{\"type\":\"keyword\"}}}"; + + @Mock + private RestClient restClient; + @Mock + private Response response; + @Mock + private StatusLine statusLine; + + @InjectMocks + private IndexerMappingServiceImpl sut; + + @Mock + private ElasticClientHandler elasticClientHandler; + + @InjectMocks + private RestHighLevelClient restHighLevelClient; + + @InjectMocks + private IndexSchema indexSchema; + @InjectMocks + private IndicesClient indicesClient; + + @InjectMocks + private AcknowledgedResponse mappingResponse; + + @Before + public void setup() throws IOException { + Map<String, String> dataMapping = new HashMap<>(); + dataMapping.put("Location", "geo_point"); + Map<String, Object> metaMapping = new HashMap<>(); + metaMapping.put(RecordMetaAttribute.ID.getValue(), "keyword"); + this.indexSchema = IndexSchema.builder().kind(kind).type(type).dataSchema(dataMapping).metaSchema(metaMapping) + .build(); + + this.indicesClient = PowerMockito.mock(IndicesClient.class); + this.restHighLevelClient = PowerMockito.mock(RestHighLevelClient.class); + + when(this.restHighLevelClient.getLowLevelClient()).thenReturn(restClient); + when(this.restClient.performRequest(ArgumentMatchers.any())).thenReturn(response); + when(this.response.getStatusLine()).thenReturn(statusLine); + when(this.statusLine.getStatusCode()).thenReturn(200); + } + + @Test + public void should_returnValidMapping_givenFalseMerge_createMappingTest() { + try { + String mapping = this.sut.createMapping(restHighLevelClient, indexSchema, index, false); + assertEquals(mappingValid, mapping); + } catch (Exception e) { + fail("Should not throw this exception" + e.getMessage()); + } + } + + @Test + public void should_returnValidMapping_givenTrueMerge_createMappingTest() { + try { + doReturn(this.indicesClient).when(this.restHighLevelClient).indices(); + doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + + String mapping = this.sut.createMapping(this.restHighLevelClient, this.indexSchema, this.index, true); + assertEquals(this.mappingValid, mapping); + } catch (Exception e) { + fail("Should not throw this exception" + e.getMessage()); + } + } + + @Test + public void should_returnValidMapping_givenExistType_createMappingTest() { + try { + doReturn(this.indicesClient).when(this.restHighLevelClient).indices(); + doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + + IndexerMappingServiceImpl indexerMappingServiceLocal = PowerMockito.spy(new IndexerMappingServiceImpl()); + doReturn(false).when(indexerMappingServiceLocal).isTypeExist(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()); + String mapping = this.sut.createMapping(this.restHighLevelClient, this.indexSchema, this.index, true); + assertEquals(this.mappingValid, mapping); + } catch (Exception e) { + fail("Should not throw this exception" + e.getMessage()); + } + } + + @Test + public void should_update_indices_field_with_keyword_when_valid_indices() throws Exception { + try { + Set<String> indices = new HashSet<String>(); + indices.add("indices 1"); + GetFieldMappingsResponse getFieldMappingsResponse = mock(GetFieldMappingsResponse.class); + doReturn(this.indicesClient).when(this.restHighLevelClient).indices(); + when(this.indicesClient.getFieldMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(getFieldMappingsResponse); + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + builder.field("any field", new HashMap()); + builder.endObject(); + BytesReference bytesReference = BytesReference.bytes(builder); + FieldMappingMetaData mappingMetaData = new FieldMappingMetaData(index, bytesReference); + Map<String, FieldMappingMetaData> mapBuilder = new HashMap<>(); + mapBuilder.put("data.any field", mappingMetaData); + Map<String, Map<String, FieldMappingMetaData>> mappingBuilder = new HashMap<>(); + mappingBuilder.put("any index 1", mapBuilder); + mappingBuilder.put("any index 2", mapBuilder); + Map<String, Map<String, Map<String, FieldMappingMetaData>>> mapping = new HashMap<>(); + mapping.put("indices 1", mappingBuilder); + when(getFieldMappingsResponse.mappings()).thenReturn(mapping); + doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + BulkByScrollResponse response = mock(BulkByScrollResponse.class); + doReturn(response).when(this.restHighLevelClient).updateByQuery(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + when(response.getBulkFailures()).thenReturn(new ArrayList<Failure>()); + when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient); + + this.sut.updateIndexMappingForIndicesOfSameType( indices,"any field"); + } catch (Exception e) { + fail("Should not throw this exception" + e.getMessage()); + } + } + + @Test(expected = AppException.class) + public void should_throw_exception_if_someIndex_is_invalid_andWeIndexfield_with_keyword() throws Exception { + try { + Set<String> indices = new HashSet<String>(); + indices.add("invalid 1"); + GetFieldMappingsResponse getFieldMappingsResponse = mock(GetFieldMappingsResponse.class); + doReturn(this.indicesClient).when(this.restHighLevelClient).indices(); + when(this.indicesClient.getFieldMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(getFieldMappingsResponse); + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + builder.field("any field", new HashMap()); + builder.endObject(); + BytesReference bytesReference = BytesReference.bytes(builder); + FieldMappingMetaData mappingMetaData = new FieldMappingMetaData(index, bytesReference); + Map<String, FieldMappingMetaData> mapBuilder = new HashMap<>(); + mapBuilder.put("data.any field", mappingMetaData); + Map<String, Map<String, FieldMappingMetaData>> mappingBuilder = new HashMap<>(); + mappingBuilder.put("any index 1", mapBuilder); + mappingBuilder.put("any index 2", mapBuilder); + Map<String, Map<String, Map<String, FieldMappingMetaData>>> mapping = new HashMap<>(); + mapping.put("indices 1", mappingBuilder); + when(getFieldMappingsResponse.mappings()).thenReturn(mapping); + doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + BulkByScrollResponse response = mock(BulkByScrollResponse.class); + doReturn(response).when(this.restHighLevelClient).updateByQuery(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + when(response.getBulkFailures()).thenReturn(new ArrayList<Failure>()); + when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient); + + this.sut.updateIndexMappingForIndicesOfSameType(indices,"any field"); + } catch (Exception e) { + throw e; + } + } + + @Test(expected = AppException.class) + public void should_throw_exception_if_type_of_index_is_invalid_andWeIndexfield_with_keyword() throws Exception { + try { + Set<String> indices = new HashSet<String>(); + indices.add("indices 1"); + GetFieldMappingsResponse getFieldMappingsResponse = mock(GetFieldMappingsResponse.class); + doReturn(this.indicesClient).when(this.restHighLevelClient).indices(); + when(this.indicesClient.getFieldMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(getFieldMappingsResponse); + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + builder.field("any field", new HashMap()); + builder.endObject(); + BytesReference bytesReference = BytesReference.bytes(builder); + FieldMappingMetaData mappingMetaData = new FieldMappingMetaData(index, bytesReference); + Map<String, FieldMappingMetaData> mapBuilder = new HashMap<>(); + mapBuilder.put("data.any field", mappingMetaData); + Map<String, Map<String, FieldMappingMetaData>> mappingBuilder = new HashMap<>(); + mappingBuilder.put("any index 1", mapBuilder); + mappingBuilder.put("any index 2", mapBuilder); + Map<String, Map<String, Map<String, FieldMappingMetaData>>> mapping = new HashMap<>(); + mapping.put("indices 1", mappingBuilder); + when(getFieldMappingsResponse.mappings()).thenReturn(mapping); + doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + BulkByScrollResponse response = mock(BulkByScrollResponse.class); + doReturn(response).when(this.restHighLevelClient).updateByQuery(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + when(response.getBulkFailures()).thenReturn(new ArrayList<Failure>()); + when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient); + this.sut.updateIndexMappingForIndicesOfSameType(indices,"any field invalid"); + } catch (Exception e) { + throw e; + } + } + + @Test(expected = AppException.class) + public void should_throw_exception_if_elastic_search_failedToFetch_andWeIndexfield_with_keyword() throws Exception { + try { + + Set<String> indices = new HashSet<String>(); + indices.add("indices 1"); + indices.add("indices Invalid"); + GetFieldMappingsResponse getFieldMappingsResponse = mock(GetFieldMappingsResponse.class); + doReturn(this.indicesClient).when(this.restHighLevelClient).indices(); + when(this.indicesClient.getFieldMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenThrow(new ElasticsearchException("")); + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + builder.field("any field", new HashMap()); + builder.endObject(); + BytesReference bytesReference = BytesReference.bytes(builder); + FieldMappingMetaData mappingMetaData = new FieldMappingMetaData(index, bytesReference); + Map<String, FieldMappingMetaData> mapBuilder = new HashMap<>(); + mapBuilder.put("data.any field", mappingMetaData); + Map<String, Map<String, FieldMappingMetaData>> mappingBuilder = new HashMap<>(); + mappingBuilder.put("any index 1", mapBuilder); + mappingBuilder.put("any index 2", mapBuilder); + Map<String, Map<String, Map<String, FieldMappingMetaData>>> mapping = new HashMap<>(); + mapping.put("indices 1", mappingBuilder); + when(getFieldMappingsResponse.mappings()).thenReturn(mapping); + doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + BulkByScrollResponse response = mock(BulkByScrollResponse.class); + doReturn(response).when(this.restHighLevelClient).updateByQuery(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + when(response.getBulkFailures()).thenReturn(new ArrayList<Failure>()); + when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient); + this.sut.updateIndexMappingForIndicesOfSameType(indices,"any field"); + } catch (AppException e) { + throw e; + } + } + + @Test(expected = AppException.class) + public void should_throw_exception_when_elastic_failedToIndex_indices_field_with_keyword() { + try { + Set<String> indices = new HashSet<String>(); + indices.add("indices 1"); + indices.add("indices Invalid"); + GetFieldMappingsResponse getFieldMappingsResponse = mock(GetFieldMappingsResponse.class); + doReturn(this.indicesClient).when(this.restHighLevelClient).indices(); + when(this.indicesClient.getFieldMapping(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(getFieldMappingsResponse); + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + builder.field("any field", new HashMap()); + builder.endObject(); + BytesReference bytesReference = BytesReference.bytes(builder); + FieldMappingMetaData mappingMetaData = new FieldMappingMetaData(index, bytesReference); + Map<String, FieldMappingMetaData> mapBuilder = new HashMap<>(); + mapBuilder.put("data.any field", mappingMetaData); + Map<String, Map<String, FieldMappingMetaData>> mappingBuilder = new HashMap<>(); + mappingBuilder.put("any index 1", mapBuilder); + mappingBuilder.put("any index 2", mapBuilder); + Map<String, Map<String, Map<String, FieldMappingMetaData>>> mapping = new HashMap<>(); + mapping.put("indices 1", mappingBuilder); + when(getFieldMappingsResponse.mappings()).thenReturn(mapping); + doReturn(mappingResponse).when(this.indicesClient).putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + BulkByScrollResponse response = mock(BulkByScrollResponse.class); + doReturn(response).when(this.restHighLevelClient).updateByQuery(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class)); + when(response.getBulkFailures()).thenReturn(new ArrayList<Failure>()); + when(this.indicesClient.putMapping(ArgumentMatchers.any(), ArgumentMatchers.any(RequestOptions.class))).thenThrow(new ElasticsearchException("")); + when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient); + this.sut.updateIndexMappingForIndicesOfSameType(indices,"any field"); + } catch (AppException e) { + throw e; + } catch (Exception e) { + fail("Should not throw this exception" + e.getMessage()); + } + } +} diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerSchemaServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerSchemaServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8427f8c0b8b6f5811725e58d2b73af0a119b5d6a --- /dev/null +++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerSchemaServiceTest.java @@ -0,0 +1,329 @@ +// 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.indexer.ibm.service; + +import org.apache.http.HttpStatus; +import org.elasticsearch.client.RestHighLevelClient; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.opengroup.osdu.core.common.model.indexer.IndexSchema; +import org.opengroup.osdu.core.common.model.indexer.OperationType; +import org.opengroup.osdu.core.common.spi.indexer.ISchemaCache; +import org.opengroup.osdu.indexer.service.IndexSchemaServiceImpl; +import org.opengroup.osdu.indexer.service.IndexerMappingService; +import org.opengroup.osdu.indexer.service.StorageService; +import org.opengroup.osdu.core.common.model.coreis.RequestStatus; +import org.opengroup.osdu.core.common.service.coreis.JaxRsDpsLog; +import org.opengroup.osdu.core.common.service.coreis.IndicesService; +import org.opengroup.osdu.core.common.model.coreis.AppException; +import org.opengroup.osdu.is.core.util.ElasticClientHandler; +import org.opengroup.osdu.core.common.service.coreis.ElasticIndexNameResolver; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.initMocks; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; + +@Ignore +@RunWith(SpringRunner.class) +@PrepareForTest({RestHighLevelClient.class}) +public class IndexerSchemaServiceTest { + + private final String kind = "tenant:test:test:1.0.0"; + private final String emptySchema = null; + private final String someSchema = "{\"kind\":\"tenant:test:test:1.0.0\", \"schema\":[{\"path\":\"test-path\", \"kind\":\"tenant:test:test:1.0.0\"}]}"; + + @Mock + private JaxRsDpsLog log; + @Mock + private StorageService storageService; + @Mock + private ElasticClientHandler elasticClientHandler; + @Mock + private ElasticIndexNameResolver elasticIndexNameResolver; + @Mock + private IndexerMappingService mappingService; + @Mock + private IndicesService indicesService; + @Mock + private ISchemaCache schemaCache; + @InjectMocks + private IndexSchemaServiceImpl sut; + + @Before + public void setup() { + initMocks(this); + RestHighLevelClient restHighLevelClient = mock(RestHighLevelClient.class); + when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient); + } + + @Test + public void should_returnNull_givenEmptySchema_getIndexerInputSchemaSchemaTest() throws Exception { + when(storageService.getStorageSchema(any())).thenReturn(emptySchema); + + IndexSchema indexSchema = this.sut.getIndexerInputSchema(kind); + + Assert.assertNotNull(indexSchema); + } + + @Test + public void should_returnValidResponse_givenValidSchema_getIndexerInputSchemaTest() throws Exception { + when(storageService.getStorageSchema(any())).thenReturn(someSchema); + + IndexSchema indexSchema = this.sut.getIndexerInputSchema(kind); + + Assert.assertEquals(kind, indexSchema.getKind()); + } + + @Test + public void should_returnValidResponse_givenValidSchemaWithCacheHit_getIndexerInputSchemaTest() throws Exception { + when(storageService.getStorageSchema(any())).thenReturn(someSchema); + when(this.schemaCache.get(kind + "_flattened")).thenReturn(someSchema); + + IndexSchema indexSchema = this.sut.getIndexerInputSchema(kind); + + Assert.assertEquals(kind, indexSchema.getKind()); + } + + @Test + public void should_throw500_givenInvalidSchemaCacheHit_getIndexerInputSchemaTest() { + try { + String invalidSchema = "{}}"; + when(storageService.getStorageSchema(any())).thenReturn(invalidSchema); + + this.sut.getIndexerInputSchema(kind); + fail("Should throw exception"); + } catch (AppException e) { + Assert.assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getError().getCode()); + Assert.assertEquals("An error has occurred while normalizing the schema.", e.getError().getMessage()); + } catch (Exception e) { + fail("Should not throw exception" + e.getMessage()); + } + } + + @Test + public void should_return_basic_schema_when_storage_returns_no_schema() { + IndexSchema returnedSchema = this.sut.getIndexerInputSchema(kind); + + assertNotNull(returnedSchema.getDataSchema()); + assertNotNull(returnedSchema); + assertEquals(kind, returnedSchema.getKind()); + } + + @Test + public void should_create_schema_when_storage_returns_valid_schema() throws IOException, URISyntaxException { + String kind = "tenant1:avocet:completion:1.0.0"; + String storageSchema = "{" + + " \"kind\": \"tenant1:avocet:completion:1.0.0\"," + + " \"schema\": [" + + " {" + + " \"path\": \"status\"," + + " \"kind\": \"string\"" + + " }," + + " {" + + " \"path\": \"startDate\"," + + " \"kind\": \"string\"" + + " }," + + " {" + + " \"path\": \"endDate\"," + + " \"kind\": \"string\"" + + " }," + + " {" + + " \"path\": \"type \"," + + " \"kind\": \"string\"" + + " }," + + " {" + + " \"path\": \"itemguid\"," + + " \"kind\": \"string\"" + + " }" + + " ]" + + "}"; + Map<String, OperationType> schemaMessages = new HashMap<>(); + schemaMessages.put(kind, OperationType.create_schema); + + when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-")); + when(this.schemaCache.get(kind)).thenReturn(null); + when(this.indicesService.isIndexExist(any(), any())).thenReturn(false); + when(this.storageService.getStorageSchema(kind)).thenReturn(storageSchema); + + this.sut.processSchemaMessages(schemaMessages); + + verify(this.mappingService, times(1)).getIndexMappingFromRecordSchema(any()); + verify(this.indicesService, times(1)).createIndex(any(), any(), any(), any(), any()); + verifyNoMoreInteractions(this.mappingService); + } + + @Test + public void should_merge_mapping_when_storage_returns_valid_schema() throws IOException, URISyntaxException { + String kind = "tenant1:avocet:completion:1.0.0"; + String storageSchema = "{" + + " \"kind\": \"tenant1:avocet:completion:1.0.0\"," + + " \"schema\": [" + + " {" + + " \"path\": \"status\"," + + " \"kind\": \"string\"" + + " }," + + " {" + + " \"path\": \"startDate\"," + + " \"kind\": \"string\"" + + " }" + + " ]" + + "}"; + Map<String, OperationType> schemaMessages = new HashMap<>(); + schemaMessages.put(kind, OperationType.create_schema); + + when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-")); + when(this.schemaCache.get(kind)).thenReturn(null); + when(this.indicesService.isIndexExist(any(), any())).thenReturn(true); + when(this.storageService.getStorageSchema(kind)).thenReturn(storageSchema); + + this.sut.processSchemaMessages(schemaMessages); + + verify(this.indicesService, times(0)).createIndex(any(), any(), any(), any(), any()); + verify(this.mappingService, times(1)).createMapping(any(), any(), any(), anyBoolean()); + verifyNoMoreInteractions(this.mappingService); + } + + @Test + public void should_throw_mapping_conflict_when_elastic_backend_cannot_process_schema_changes() throws IOException, URISyntaxException { + String kind = "tenant1:avocet:completion:1.0.0"; + String reason = String.format("Could not create type mapping %s/completion.", kind.replace(":", "-")); + String storageSchema = "{" + + " \"kind\": \"tenant1:avocet:completion:1.0.0\"," + + " \"schema\": [" + + " {" + + " \"path\": \"status\"," + + " \"kind\": \"string\"" + + " }" + + " ]" + + "}"; + Map<String, OperationType> schemaMessages = new HashMap<>(); + schemaMessages.put(kind, OperationType.create_schema); + + when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-")); + when(this.schemaCache.get(kind)).thenReturn(null); + when(this.indicesService.isIndexExist(any(), any())).thenReturn(true); + when(this.storageService.getStorageSchema(kind)).thenReturn(storageSchema); + when(this.mappingService.createMapping(any(), any(), any(), anyBoolean())).thenThrow(new AppException(HttpStatus.SC_BAD_REQUEST, reason, "")); + + try { + this.sut.processSchemaMessages(schemaMessages); + } catch (AppException e){ + assertEquals(e.getError().getCode(), RequestStatus.SCHEMA_CONFLICT); + assertEquals(e.getError().getMessage(), "error creating or merging index mapping"); + assertEquals(e.getError().getReason(), reason); + } catch (Exception e) { + fail("Should not throw this exception " + e.getMessage()); + } + } + + @Test + public void should_throw_genericAppException_when_elastic_backend_cannot_process_schema_changes() throws IOException, URISyntaxException { + String kind = "tenant1:avocet:completion:1.0.0"; + String reason = String.format("Could not create type mapping %s/completion.", kind.replace(":", "-")); + String storageSchema = "{" + + " \"kind\": \"tenant1:avocet:completion:1.0.0\"," + + " \"schema\": [" + + " {" + + " \"path\": \"status\"," + + " \"kind\": \"string\"" + + " }" + + " ]" + + "}"; + Map<String, OperationType> schemaMessages = new HashMap<>(); + schemaMessages.put(kind, OperationType.create_schema); + + when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-")); + when(this.schemaCache.get(kind)).thenReturn(null); + when(this.indicesService.isIndexExist(any(), any())).thenReturn(true); + when(this.storageService.getStorageSchema(kind)).thenReturn(storageSchema); + when(this.mappingService.createMapping(any(), any(), any(), anyBoolean())).thenThrow(new AppException(HttpStatus.SC_FORBIDDEN, reason, "blah")); + + try { + this.sut.processSchemaMessages(schemaMessages); + } catch (AppException e){ + assertEquals(e.getError().getCode(), HttpStatus.SC_FORBIDDEN); + assertEquals(e.getError().getMessage(), "blah"); + assertEquals(e.getError().getReason(), reason); + } catch (Exception e) { + fail("Should not throw this exception " + e.getMessage()); + } + } + + + @Test + public void should_log_and_do_nothing_when_storage_returns_invalid_schema() throws IOException, URISyntaxException { + String kind = "tenant1:avocet:completion:1.0.0"; + String storageSchema = "{" + + " \"kind\": \"tenant1:avocet:completion:1.0.0\"" + + "}"; + Map<String, OperationType> schemaMessages = new HashMap<>(); + schemaMessages.put(kind, OperationType.create_schema); + + when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-")); + when(this.schemaCache.get(kind)).thenReturn(null); + when(this.indicesService.isIndexExist(any(), any())).thenReturn(true); + when(this.storageService.getStorageSchema(kind)).thenReturn(storageSchema); + + this.sut.processSchemaMessages(schemaMessages); + + verify(this.log).warning(eq("schema not found for kind: tenant1:avocet:completion:1.0.0")); + } + + @Test + public void should_invalidateCache_when_purge_schema_and_schema_found_in_cache() throws IOException { + String kind = "tenant1:avocet:completion:1.0.0"; + Map<String, OperationType> schemaMessages = new HashMap<>(); + schemaMessages.put(kind, OperationType.purge_schema); + + when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-")); + when(this.indicesService.isIndexExist(any(), any())).thenReturn(true); + when(this.schemaCache.get(kind)).thenReturn("schema"); + when(this.schemaCache.get(kind + "_flattened")).thenReturn("flattened schema"); + + this.sut.processSchemaMessages(schemaMessages); + + verify(this.schemaCache, times(2)).get(anyString()); + verify(this.schemaCache, times(2)).delete(anyString()); + } + + @Test + public void should_log_warning_when_purge_schema_and_schema_not_found_in_cache() throws IOException { + String kind = "tenant1:avocet:completion:1.0.0"; + Map<String, OperationType> schemaMessages = new HashMap<>(); + schemaMessages.put(kind, OperationType.purge_schema); + + when(this.elasticIndexNameResolver.getIndexNameFromKind(kind)).thenReturn(kind.replace(":", "-")); + when(this.indicesService.isIndexExist(any(), any())).thenReturn(false); + + this.sut.processSchemaMessages(schemaMessages); + + verify(this.log).warning(eq(String.format("Kind: %s not found", kind))); + } +} diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0043a0d898b93ace6e7a49c7f2e67b6f17f41726 --- /dev/null +++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerServiceTest.java @@ -0,0 +1,337 @@ +package org.opengroup.osdu.indexer.ibm.service;//// 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.opendes.indexer.service; +// +//import com.google.gson.Gson; +//import com.google.gson.reflect.TypeToken; +//import org.elasticsearch.action.bulk.BulkItemResponse; +//import org.elasticsearch.action.bulk.BulkResponse; +//import org.elasticsearch.client.RequestOptions; +//import org.elasticsearch.client.RestHighLevelClient; +//import org.junit.Before; +//import org.junit.Ignore; +//import org.junit.Test; +//import org.junit.runner.RunWith; +//import org.mockito.InjectMocks; +//import org.mockito.Mock; +//import org.mockito.Spy; +//import org.opendes.client.api.DpsHeaders; +//import org.opendes.core.logging.JaxRsDpsLog; +//import org.opendes.core.model.DeploymentEnvironment; +//import org.opendes.core.model.RecordChangedMessages; +//import org.opendes.core.service.IndicesService; +//import org.opendes.core.util.Config; +//import org.opendes.core.util.ElasticClientHandler; +//import org.opendes.core.util.ElasticIndexNameResolver; +//import org.opendes.core.util.HeadersUtil; +//import org.opendes.indexer.logging.AuditLogger; +//import org.opendes.indexer.model.*; +//import org.opendes.indexer.publish.IPublisher; +//import org.opendes.indexer.util.IRequestInfo; +//import org.opendes.indexer.util.IndexerQueueTaskBuilder; +//import org.opendes.indexer.util.JobStatus; +//import org.opendes.indexer.util.RecordInfo; +//import org.powermock.core.classloader.annotations.PrepareForTest; +//import org.springframework.context.annotation.Lazy; +//import org.springframework.test.context.junit4.SpringRunner; +// +//import javax.inject.Inject; +//import java.io.IOException; +//import java.lang.reflect.Type; +//import java.util.*; +// +//import static java.util.Collections.singletonList; +//import static org.junit.Assert.*; +//import static org.mockito.Matchers.any; +//import static org.mockito.Mockito.verify; +//import static org.mockito.Mockito.when; +//import static org.powermock.api.mockito.PowerMockito.mock; +//import static org.powermock.api.mockito.PowerMockito.mockStatic; +// +//@Ignore +//@RunWith(SpringRunner.class) +//@PrepareForTest({RestHighLevelClient.class, BulkResponse.class, StorageAcl.class, HeadersUtil.class, Config.class}) +//public class IndexerServiceTest { +// +// private final String pubsubMsg = "[{\"id\":\"tenant1:doc:test1\",\"kind\":\"tenant1:testindexer1:well:1.0.0\",\"op\":\"update\"}," + +// "{\"id\":\"tenant1:doc:test2\",\"kind\":\"tenant1:testindexer2:well:1.0.0\",\"op\":\"create\"}]"; +// private final String kind1 = "tenant1:testindexer1:well:1.0.0"; +// private final String kind2 = "tenant1:testindexer2:well:1.0.0"; +// private final String recordId1 = "tenant1:doc:test1"; +// private final String recordId2 = "tenant1:doc:test2"; +// private final String failureMassage = "test failure"; +// +// @Mock +// private IndexSchemaService indexSchemaService; +// @Mock +// private IndicesService indicesService; +// @Mock +// private IndexerMappingService indexerMappingService; +// @Mock +// private StorageService storageService; +// @Mock +// private IPublisher publisherImpl; +// @Mock +// private RestHighLevelClient restHighLevelClient; +// @Mock +// private ElasticClientHandler elasticClientHandler; +// @Mock +// private BulkResponse bulkResponse; +// @Mock +// private IRequestInfo requestInfo; +// @Mock +// private ElasticIndexNameResolver elasticIndexNameResolver; +// @Mock +// private AttributeParsingServiceImpl attributeParsingServiceImpl; +// @Mock +// private IndexerQueueTaskBuilder indexerQueueTaskBuilder; +// @Mock +// private JaxRsDpsLog log; +// @Mock +// private AuditLogger auditLogger; +// @InjectMocks +// private IndexerServiceImpl sut; +// @InjectMocks @Spy +// private JobStatus jobStatus = new JobStatus(); +// +// @Inject +// @Lazy +// private DpsHeaders dpsHeaders; +// private RecordChangedMessages recordChangedMessages; +// private List<RecordInfo> recordInfos; +// +// @Before +// public void setup() throws IOException { +// +// mockStatic(StorageAcl.class); +// mockStatic(Config.class); +// +// when(Config.getDeploymentEnvironment()).thenReturn(DeploymentEnvironment.LOCAL); +// when(Config.getElasticClusterName()).thenReturn("CLUSTER"); +// when(Config.getElasticServerAddress()).thenReturn("testsite"); +// +// dpsHeaders = new DpsHeaders(); +// dpsHeaders.put(AppEngineHeaders.TASK_QUEUE_RETRY_COUNT, "1"); +// dpsHeaders.put(DpsHeaders.AUTHORIZATION, "testAuth"); +// when(requestInfo.getHeaders()).thenReturn(dpsHeaders); +// when(requestInfo.getHeadersMapWithDwdAuthZ()).thenReturn(dpsHeaders.getHeaders()); +// +// Type listType = new TypeToken<List<RecordInfo>>() {}.getType(); +// recordInfos = (new Gson()).fromJson(pubsubMsg, listType); +// +// when(elasticClientHandler.createRestClient()).thenReturn(restHighLevelClient); +// when(restHighLevelClient.bulk(any(), any(RequestOptions.class))).thenReturn(bulkResponse); +// +// BulkItemResponse[] responses = new BulkItemResponse[]{prepareResponseFail(), prepareResponseSuccess()}; +// when(bulkResponse.getItems()).thenReturn(responses); +// Map<String, String> attr = new HashMap<>(); +// attr.put(DpsHeaders.ACCOUNT_ID, "slb"); +// recordChangedMessages = RecordChangedMessages.builder().attributes(attr).messageId("xxxx").publishTime("2000-01-02T10:10:44+0000").data("{}").build(); +// when(StorageAcl.flattenAcl(any())).thenReturn(null); +// } +// +// @Test +// public void should_returnNull_givenEmptyJobSubInfo_processRecordChangedMessageTest() throws Exception { +// JobStatus jobStatus = this.sut.processRecordChangedMessages(recordChangedMessages, new ArrayList<>()); +// +// assertNull(jobStatus); +// } +// +// @Test +// public void should_returnValidJobStatus_givenNullSchema_processRecordChangedMessageTest() { +// try { +// indexSchemaServiceMock(kind1, null); +// indexSchemaServiceMock(kind2, null); +// List<ConversionStatus> conversionStatus = new LinkedList<>(); +// List<Records.Entity> validRecords = new ArrayList<>(); +// Map<String, Object> storageData = new HashMap<>(); +// storageData.put("schema1", "test-value"); +// storageData.put("schema2", "test-value"); +// storageData.put("schema3", "test-value"); +// storageData.put("schema4", "test-value"); +// storageData.put("schema5", "test-value"); +// storageData.put("schema6", "test-value"); +// validRecords.add(Records.Entity.builder().id(recordId2).kind(kind2).data(storageData).build()); +// Records storageRecords = Records.builder().records(validRecords).conversionStatuses(conversionStatus).build(); +// +// when(storageService.getStorageRecords(any())).thenReturn(storageRecords); +// when(indicesService.createIndex(any(), any(), any(), any(), any())).thenReturn(true); +// +// JobStatus jobStatus = this.sut.processRecordChangedMessages(recordChangedMessages, recordInfos); +// +// assertEquals(2, jobStatus.getStatusesList().size()); +// assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.FAIL).size()); +// assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.WARN).size()); +// } catch (Exception e) { +// fail("Should not throw this exception" + e.getMessage()); +// } +// } +// +// @Test +// public void should_returnValidJobStatus_givenFailedUnitsConversion_processRecordChangedMessageTest() { +// try { +// indexSchemaServiceMock(kind1, null); +// indexSchemaServiceMock(kind2, null); +// List<ConversionStatus> conversionStatuses = new LinkedList<>(); +// List<String> status=new ArrayList<>(); +// status.add("crs bla bla"); +// ConversionStatus conversionStatus=ConversionStatus.builder().status("ERROR").errors(status).id(recordId2).build(); +// conversionStatuses.add(conversionStatus); +// List<Records.Entity> validRecords = new ArrayList<>(); +// Map<String, Object> storageData = new HashMap<>(); +// storageData.put("schema1", "test-value"); +// storageData.put("schema2", "test-value"); +// storageData.put("schema3", "test-value"); +// storageData.put("schema4", "test-value"); +// storageData.put("schema5", "test-value"); +// storageData.put("schema6", "test-value"); +// validRecords.add(Records.Entity.builder().id(recordId2).kind(kind2).data(storageData).build()); +// Records storageRecords = Records.builder().records(validRecords).conversionStatuses(conversionStatuses).build(); +// +// when(storageService.getStorageRecords(any())).thenReturn(storageRecords); +// when(indicesService.createIndex(any(), any(), any(), any(), any())).thenReturn(true); +// +// JobStatus jobStatus = this.sut.processRecordChangedMessages(recordChangedMessages, recordInfos); +// +// assertEquals(2, jobStatus.getStatusesList().size()); +// assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.FAIL).size()); +// assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.WARN).size()); +// assertTrue(jobStatus.getJobStatusByRecordId(jobStatus.getIdsByIndexingStatus(IndexingStatus.WARN).get(0)).getIndexProgress().getTrace().contains("crs bla bla")); +// } catch (Exception e) { +// fail("Should not throw this exception" + e.getMessage()); +// } +// } +// +// @Test +// public void should_returnValidJobStatus_givenNullSchemaForARecord_processRecordChangedMessageTest() { +// try { +// List<Records.Entity> validRecords = new ArrayList<>(); +// List<ConversionStatus> conversionStatus = new LinkedList<>(); +// Map<String, Object> storageData = new HashMap<>(); +// storageData.put("schema1", "test-value"); +// storageData.put("schema2", "test-value"); +// storageData.put("schema3", "test-value"); +// storageData.put("schema4", "test-value"); +// storageData.put("schema5", "test-value"); +// storageData.put("schema6", "test-value"); +// validRecords.add(Records.Entity.builder().id(recordId2).kind(kind2).data(storageData).build()); +// Records storageRecords = Records.builder().records(validRecords).conversionStatuses(conversionStatus).build(); +// when(storageService.getStorageRecords(any())).thenReturn(storageRecords); +// +// Map<String, String> schema = createSchema(); +// indexSchemaServiceMock(kind1, schema); +// indexSchemaServiceMock(kind2, null); +// when(elasticIndexNameResolver.getIndexNameFromKind(kind2)).thenReturn("tenant1-testindexer2-well-1.0.0"); +// when(indicesService.createIndex(any(), any(), any(), any(), any())).thenReturn(true); +// JobStatus jobStatus = sut.processRecordChangedMessages(recordChangedMessages, recordInfos); +// +// assertEquals(2, jobStatus.getStatusesList().size()); +// assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.FAIL).size()); +// assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.WARN).size()); +// assertEquals("Indexed Successfully", jobStatus.getStatusesList().get(1).getIndexProgress().getTrace().pop()); +// assertEquals("schema not found", jobStatus.getStatusesList().get(1).getIndexProgress().getTrace().pop()); +// } catch (Exception e) { +// fail("Should not throw this exception" + e.getMessage()); +// } +// } +// +// @Test +// public void should_returnValidJobStatus_givenValidCreateAndUpdateRecords_processRecordChangedMessagesTest() { +// try { +// Map<String, Object> storageData = new HashMap<>(); +// storageData.put("schema1", "test-value"); +// List<ConversionStatus> conversionStatus = new LinkedList<>(); +// List<Records.Entity> validRecords = new ArrayList<>(); +// validRecords.add(Records.Entity.builder().id(recordId2).kind(kind2).data(storageData).build()); +// Records storageRecords = Records.builder().records(validRecords).conversionStatuses(conversionStatus).build(); +// +// when(storageService.getStorageRecords(any())).thenReturn(storageRecords); +// when(indicesService.createIndex(any(), any(), any(), any(), any())).thenReturn(true); +// Map<String, String> schema = createSchema(); +// indexSchemaServiceMock(kind2, schema); +// indexSchemaServiceMock(kind1, null); +// JobStatus jobStatus = sut.processRecordChangedMessages(recordChangedMessages, recordInfos); +// +// assertEquals(2, jobStatus.getStatusesList().size()); +// assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.FAIL).size()); +// assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.SUCCESS).size()); +// } catch (Exception e) { +// fail("Should not throw this exception" + e.getMessage()); +// } +// } +// +// @Test +// public void should_properlyUpdateAuditLogs_givenValidCreateAndUpdateRecords() { +// try { +// Map<String, Object> storageData = new HashMap<>(); +// List<ConversionStatus> conversionStatus = new LinkedList<>(); +// +// storageData.put("schema1", "test-value"); +// List<Records.Entity> validRecords = new ArrayList<>(); +// validRecords.add(Records.Entity.builder().id(recordId2).kind(kind2).data(storageData).build()); +// Records storageRecords = Records.builder().records(validRecords).conversionStatuses(conversionStatus).build(); +// +// when(this.storageService.getStorageRecords(any())).thenReturn(storageRecords); +// when(this.indicesService.createIndex(any(), any(), any(), any(), any())).thenReturn(true); +// Map<String, String> schema = createSchema(); +// indexSchemaServiceMock(kind2, schema); +// indexSchemaServiceMock(kind1, null); +// JobStatus jobStatus = this.sut.processRecordChangedMessages(recordChangedMessages, recordInfos); +// +// assertEquals(2, jobStatus.getStatusesList().size()); +// assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.FAIL).size()); +// assertEquals(1, jobStatus.getIdsByIndexingStatus(IndexingStatus.SUCCESS).size()); +// +// verify(this.auditLogger).indexCreateRecordSuccess(singletonList("RecordStatus(id=tenant1:doc:test2, kind=tenant1:testindexer2:well:1.0.0, operationType=create, status=SUCCESS)")); +// verify(this.auditLogger).indexUpdateRecordFail(singletonList("RecordStatus(id=tenant1:doc:test1, kind=tenant1:testindexer1:well:1.0.0, operationType=update, status=FAIL)")); +// } catch (Exception e) { +// fail("Should not throw this exception" + e.getMessage()); +// } +// } +// +// private BulkItemResponse prepareResponseFail() { +// BulkItemResponse responseFail = mock(BulkItemResponse.class); +// when(responseFail.isFailed()).thenReturn(true); +// when(responseFail.getFailureMessage()).thenReturn(failureMassage); +// when(responseFail.getId()).thenReturn(recordId1); +// when(responseFail.getFailure()).thenReturn(new BulkItemResponse.Failure("failure index", "failure type", "failure id", new Exception("test failure"))); +// return responseFail; +// } +// +// private BulkItemResponse prepareResponseSuccess() { +// BulkItemResponse responseSuccess = mock(BulkItemResponse.class); +// when(responseSuccess.getId()).thenReturn(recordId2); +// return responseSuccess; +// } +// +// private void indexSchemaServiceMock(String kind, Map<String, String> schema) { +// if (schema == null) { +// IndexSchema indexSchema = IndexSchema.builder().kind(kind).dataSchema(null).build(); +// when(indexSchemaService.getIndexerInputSchema(kind)).thenReturn(indexSchema); +// } else { +// IndexSchema indexSchema = IndexSchema.builder().kind(kind).dataSchema(schema).build(); +// when(indexSchemaService.getIndexerInputSchema(kind)).thenReturn(indexSchema); +// } +// } +// +// private Map<String, String> createSchema() { +// Map<String, String> schema = new HashMap<>(); +// schema.put("schema1", "keyword"); +// schema.put("schema2", "boolean"); +// schema.put("schema3", "date"); +// schema.put("schema6", "object"); +// return schema; +// } +//} diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/ReindexServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/ReindexServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..56134e570be0cbfedd61fcb8c229505cc826374a --- /dev/null +++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/ReindexServiceTest.java @@ -0,0 +1,145 @@ +// 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.indexer.ibm.service; + + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.opengroup.osdu.core.common.model.core.DpsHeaders; +import org.opengroup.osdu.core.common.model.indexer.RecordQueryResponse; +import org.opengroup.osdu.core.common.model.indexer.RecordReindexRequest; +import org.opengroup.osdu.indexer.service.ReindexServiceImpl; +import org.opengroup.osdu.indexer.service.StorageService; +import org.opengroup.osdu.indexer.util.IndexerQueueTaskBuilder; +import org.opengroup.osdu.core.common.service.coreis.JaxRsDpsLog; +import org.opengroup.osdu.core.common.spi.coreis.IRequestInfo; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.*; + +import static org.junit.Assert.fail; +import static org.mockito.MockitoAnnotations.initMocks; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; + +@Ignore +@RunWith(SpringRunner.class) +public class ReindexServiceTest { + + private final String cursor = "100"; + + private final String correlationId = UUID.randomUUID().toString(); + + @Mock + private StorageService storageService; + + @Mock + private Map<String, String> httpHeaders; + @Mock + private IRequestInfo requestInfo; + @Mock + private IndexerQueueTaskBuilder indexerQueueTaskBuilder; + @Mock + private JaxRsDpsLog log; + @InjectMocks + private ReindexServiceImpl sut; + + private RecordReindexRequest recordReindexRequest; + private RecordQueryResponse recordQueryResponse; + + @Before + public void setup() { + initMocks(this); + + mockStatic(UUID.class); + + recordReindexRequest = RecordReindexRequest.builder().kind("tenant:test:test:1.0.0").cursor(cursor).build(); + recordQueryResponse = new RecordQueryResponse(); + + httpHeaders = new HashMap<>(); + httpHeaders.put(DpsHeaders.AUTHORIZATION, "testAuth"); + httpHeaders.put(DpsHeaders.CORRELATION_ID, correlationId); + DpsHeaders standardHeaders = DpsHeaders.createFromMap(httpHeaders); + when(requestInfo.getHeaders()).thenReturn(standardHeaders); + when(requestInfo.getHeadersMapWithDwdAuthZ()).thenReturn(httpHeaders); + } + + @Test + public void should_returnNull_givenNullResponseResult_reIndexRecordsTest() { + try { + recordQueryResponse.setResults(null); + when(storageService.getRecordsByKind(ArgumentMatchers.any())).thenReturn(recordQueryResponse); + + String response = sut.reindexRecords(recordReindexRequest); + + Assert.assertNull(response); + } catch (Exception e) { + fail("Should not throw this exception" + e.getMessage()); + } + } + + @Test + public void should_returnNull_givenEmptyResponseResult_reIndexRecordsTest() { + try { + recordQueryResponse.setResults(new ArrayList<>()); + when(storageService.getRecordsByKind(ArgumentMatchers.any())).thenReturn(recordQueryResponse); + + String response = sut.reindexRecords(recordReindexRequest); + + Assert.assertNull(response); + } catch (Exception e) { + fail("Should not throw this exception" + e.getMessage()); + } + } + + @Test + public void should_returnRecordQueryRequestPayload_givenValidResponseResult_reIndexRecordsTest() { + try { + recordQueryResponse.setCursor(cursor); + List<String> results = new ArrayList<>(); + results.add("test1"); + recordQueryResponse.setResults(results); + when(storageService.getRecordsByKind(ArgumentMatchers.any())).thenReturn(recordQueryResponse); + + String taskQueuePayload = sut.reindexRecords(recordReindexRequest); + + Assert.assertEquals("{\"kind\":\"tenant:test:test:1.0.0\",\"cursor\":\"100\"}", taskQueuePayload); + } catch (Exception e) { + fail("Should not throw exception" + e.getMessage()); + } + } + + @Test + public void should_returnRecordChangedMessage_givenValidResponseResult_reIndexRecordsTest() { + try { + List<String> results = new ArrayList<>(); + results.add("test1"); + recordQueryResponse.setResults(results); + when(storageService.getRecordsByKind(ArgumentMatchers.any())).thenReturn(recordQueryResponse); + + String taskQueuePayload = sut.reindexRecords(recordReindexRequest); + + Assert.assertEquals(String.format("{\"data\":\"[{\\\"id\\\":\\\"test1\\\",\\\"kind\\\":\\\"tenant:test:test:1.0.0\\\",\\\"op\\\":\\\"create\\\"}]\",\"attributes\":{\"slb-correlation-id\":\"%s\"}}", correlationId), taskQueuePayload); + } catch (Exception e) { + fail("Should not throw exception" + e.getMessage()); + } + } +} diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/StorageServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/StorageServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..68f50a26dd70274ac79305478a134d14003fd651 --- /dev/null +++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/StorageServiceTest.java @@ -0,0 +1,211 @@ +// 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.indexer.ibm.service; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.opengroup.osdu.core.common.model.indexer.RecordInfo; +import org.opengroup.osdu.core.common.model.indexer.RecordQueryResponse; +import org.opengroup.osdu.core.common.model.indexer.RecordReindexRequest; +import org.opengroup.osdu.core.common.model.indexer.Records; +import org.opengroup.osdu.indexer.service.StorageServiceImpl; +import org.opengroup.osdu.core.common.model.indexer.JobStatus; +import org.opengroup.osdu.core.common.model.coreis.HttpResponse; +import org.opengroup.osdu.core.common.service.coreis.JaxRsDpsLog; +import org.opengroup.osdu.core.common.spi.coreis.IRequestInfo; +import org.opengroup.osdu.core.common.service.coreis.UrlFetchService; +import org.opengroup.osdu.core.common.model.coreis.AppException; +import org.springframework.http.HttpStatus; +import org.springframework.test.context.junit4.SpringRunner; + +import java.lang.reflect.Type; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; + +@Ignore +@RunWith(SpringRunner.class) +public class StorageServiceTest { + + @Mock + private UrlFetchService urlFetchService; + @Mock + private JobStatus jobStatus; + @Mock + private JaxRsDpsLog log; + @Mock + private IRequestInfo requestInfo; + @InjectMocks + private StorageServiceImpl sut; + + private List<String> ids; + + @Before + public void setup() { + + String recordChangedMessages = "[{\"id\":\"tenant1:doc:1dbf528e0e0549cab7a08f29fbfc8465\",\"kind\":\"tenant1:testindexer1528919679710:well:1.0.0\",\"op\":\"purge\"}," + + "{\"id\":\"tenant1:doc:1dbf528e0e0549cab7a08f29fbfc8465\",\"kind\":\"tenant1:testindexer1528919679710:well:1.0.0\",\"op\":\"create\"}]"; + + when(this.requestInfo.getHeadersMap()).thenReturn(new HashMap<>()); + + Type listType = new TypeToken<List<RecordInfo>>() {}.getType(); + + List<RecordInfo> msgs = (new Gson()).fromJson(recordChangedMessages, listType); + jobStatus.initialize(msgs); + ids = Arrays.asList("tenant1:doc:1dbf528e0e0549cab7a08f29fbfc8465", "tenant1:doc:1dbf528e0e0549cab7a08f29fbfc8465"); + } + + @Test + public void should_return404_givenNullData_getValidStorageRecordsTest() throws URISyntaxException { + + HttpResponse httpResponse = mock(HttpResponse.class); + Mockito.when(httpResponse.getBody()).thenReturn(null); + + when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse); + + should_return404_getValidStorageRecordsTest(); + } + + @Test + public void should_return404_givenEmptyData_getValidStorageRecordsTest() throws URISyntaxException { + + String emptyDataFromStorage = "{\"records\":[],\"notFound\":[]}"; + + HttpResponse httpResponse = mock(HttpResponse.class); + Mockito.when(httpResponse.getBody()).thenReturn(emptyDataFromStorage); + + when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse); + + should_return404_getValidStorageRecordsTest(); + } + + @Test + public void should_returnOneValidRecords_givenValidData_getValidStorageRecordsTest() throws URISyntaxException { + + String validDataFromStorage = "{\"records\":[{\"id\":\"testid\", \"version\":1, \"kind\":\"tenant:test:test:1.0.0\"}],\"notFound\":[\"invalid1\"]}"; + + HttpResponse httpResponse = mock(HttpResponse.class); + Mockito.when(httpResponse.getBody()).thenReturn(validDataFromStorage); + + when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse); + Records storageRecords = this.sut.getStorageRecords(ids); + + assertEquals(1, storageRecords.getRecords().size()); + } + + @Test + public void should_returnValidResponse_givenValidRecordQueryRequest_getRecordListByKind() throws Exception { + + RecordReindexRequest recordReindexRequest = RecordReindexRequest.builder().kind("tenant:test:test:1.0.0").cursor("100").build(); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setBody(new Gson().toJson(recordReindexRequest, RecordReindexRequest.class)); + + when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse); + + RecordQueryResponse recordQueryResponse = this.sut.getRecordsByKind(recordReindexRequest); + + assertEquals("100", recordQueryResponse.getCursor()); + assertNull(recordQueryResponse.getResults()); + } + + @Test + public void should_returnValidResponse_givenValidKind_getSchemaByKind() throws Exception { + + String validSchemaFromStorage = "{" + + " \"kind\": \"tenant:test:test:1.0.0\"," + + " \"schema\": [" + + " {" + + " \"path\": \"msg\"," + + " \"kind\": \"string\"" + + " }," + + " {" + + " \"path\": \"references.entity\"," + + " \"kind\": \"string\"" + + " }" + + " ]," + + " \"ext\": null" + + "}"; + String kind = "tenant:test:test:1.0.0"; + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setResponseCode(HttpStatus.OK.value()); + httpResponse.setBody(validSchemaFromStorage); + + when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse); + + String recordSchemaResponse = this.sut.getStorageSchema(kind); + + assertNotNull(recordSchemaResponse); + } + + @Test + public void should_returnNullResponse_givenAbsentKind_getSchemaByKind() throws Exception { + + String kind = "tenant:test:test:1.0.0"; + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setResponseCode(HttpStatus.NOT_FOUND.value()); + + when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse); + + String recordSchemaResponse = this.sut.getStorageSchema(kind); + + assertNull(recordSchemaResponse); + } + + @Test + public void should_returnOneValidRecords_givenValidData_getValidStorageRecordsWithInvalidConversionTest() throws URISyntaxException { + + String validDataFromStorage = "{\"records\":[{\"id\":\"testid\", \"version\":1, \"kind\":\"tenant:test:test:1.0.0\"}],\"notFound\":[\"invalid1\"],\"conversionStatuses\": [{\"id\":\"testid\",\"status\":\"ERROR\",\"errors\":[\"conversion error occured\"] } ]}"; + + HttpResponse httpResponse = mock(HttpResponse.class); + Mockito.when(httpResponse.getBody()).thenReturn(validDataFromStorage); + + when(this.urlFetchService.sendRequest(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(httpResponse); + Records storageRecords = this.sut.getStorageRecords(ids); + + assertEquals(1, storageRecords.getRecords().size()); + + assertEquals(1, storageRecords.getConversionStatuses().get(0).getErrors().size()); + + assertEquals("conversion error occured", storageRecords.getConversionStatuses().get(0).getErrors().get(0)); + } + + private void should_return404_getValidStorageRecordsTest() { + try { + this.sut.getStorageRecords(ids); + fail("Should throw exception"); + } catch (AppException e) { + assertEquals(HttpStatus.NOT_FOUND, e.getError().getCode()); + } catch (Exception e) { + fail("Should not throw this exception" + e.getMessage()); + } + } +} diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/TenantInfoServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/TenantInfoServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ba94fb4a96805a474e7c9bc82dcfa4ac13d225b4 --- /dev/null +++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/TenantInfoServiceTest.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.indexer.ibm.service; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.opengroup.osdu.core.common.model.core.DpsHeaders; +import org.opengroup.osdu.core.common.model.core.ITenantFactory; +import org.opengroup.osdu.core.common.model.core.TenantInfo; +import org.opengroup.osdu.indexer.service.TenantInfoServiceImpl; +import org.opengroup.osdu.core.common.spi.coreis.IHeadersInfo; +import org.opengroup.osdu.core.common.model.coreis.AppException; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +@RunWith(SpringRunner.class) +public class TenantInfoServiceTest { + + private static final String HEADER_NAME = "ANY_HEADER"; + private static final String HEADER_VALUE = "ANY_VALUE"; + + @Mock + private ITenantFactory tenantFactory; + @Mock + private IHeadersInfo headersInfo; + @InjectMocks + private TenantInfoServiceImpl sut; + + @Mock + private TenantInfo info; + + @Mock + private HttpHeaders httpHeaders; + + @InjectMocks + private DpsHeaders HEADERS; + + @Before + public void setup() { + HEADERS.put(HEADER_NAME, HEADER_VALUE); + } + + @Ignore + @Test + public void should_return_validTenant_given_validAccountId() { + + when(this.info.getName()).thenReturn("tenant1"); + when(tenantFactory.getTenantInfo("tenant1")).thenReturn(info); + + when(this.headersInfo.getHeaders()).thenReturn(HEADERS); + + when(this.headersInfo.getPartitionId()).thenReturn("tenant1"); + + when(this.sut.getTenantInfo()).thenReturn(info); + + assertNotNull(this.sut.getTenantInfo()); + assertEquals("tenant1", this.sut.getTenantInfo().getName()); + } + + @Test(expected = AppException.class) + public void should_throwException_given_invalidAccountId() { + + when(this.info.getName()).thenReturn("tenant2"); + when(tenantFactory.getTenantInfo("tenant1")).thenReturn(null); + + when(this.sut.getTenantInfo()).thenReturn(info); + + assertNotNull(this.sut.getTenantInfo()); + } +} \ No newline at end of file diff --git a/testing/indexer-test-ibm/pom.xml b/testing/indexer-test-ibm/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..ab4667f601481135b68eeeb2c1db0256429b84be --- /dev/null +++ b/testing/indexer-test-ibm/pom.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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.indexer</groupId> + <artifactId>indexer-test-ibm</artifactId> + <version>0.0.1</version> + <packaging>jar</packaging> + + <properties> + <maven.compiler.target>1.8</maven.compiler.target> + <maven.compiler.source>1.8</maven.compiler.source> + <cucumber.version>1.2.5</cucumber.version> + </properties> + + <dependencies> + <dependency> + <groupId>com.google.api-client</groupId> + <artifactId>google-api-client</artifactId> + <version>1.28.0</version> + <exclusions> + <exclusion> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + </exclusion> + </exclusions> + </dependency> + + <dependency> + <groupId>org.opengroup.osdu.indexer</groupId> + <artifactId>indexer-test-core</artifactId> + <version>0.0.1</version> + </dependency> + + <!-- Cucumber --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>info.cukes</groupId> + <artifactId>cucumber-java</artifactId> + <version>${cucumber.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>info.cukes</groupId> + <artifactId>cucumber-junit</artifactId> + <version>${cucumber.version}</version> + <scope>test</scope> + </dependency> + + <!-- Gson: Java to Json conversion --> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.8.5</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.jaxrs</groupId> + <artifactId>jackson-jaxrs-json-provider</artifactId> + <version>2.9.9</version> + </dependency> + + <dependency> + <groupId>org.glassfish</groupId> + <artifactId>javax.json</artifactId> + <version>1.1.4</version> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>1.19.4</version> + </dependency> + + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>1.18.2</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.6</version> + </dependency> + + <!--Elasticsearch--> + <dependency> + <groupId>org.elasticsearch</groupId> + <artifactId>elasticsearch</artifactId> + <version>6.6.2</version> + </dependency> + <dependency> + <groupId>org.elasticsearch.client</groupId> + <artifactId>elasticsearch-rest-client</artifactId> + <version>6.6.2</version> + </dependency> + <dependency> + <groupId>org.elasticsearch.client</groupId> + <artifactId>elasticsearch-rest-high-level-client</artifactId> + <version>6.6.2</version> + </dependency> + + <!--Logging--> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-to-slf4j</artifactId> + <version>2.11.2</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-jdk14</artifactId> + <version>1.8.0-beta4</version> + </dependency> + + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>27.1-jre</version> + </dependency> + </dependencies> +</project> diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/RunTest.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/RunTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4978ddfccad197628432d4ccb8b5985b4af5017e --- /dev/null +++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/RunTest.java @@ -0,0 +1,13 @@ +package org.opengroup.osdu.step_definitions.index.record; + +import cucumber.api.CucumberOptions; +import cucumber.api.junit.Cucumber; +import org.junit.runner.RunWith; + +@RunWith(Cucumber.class) +@CucumberOptions( + features = "classpath:features/indexrecord/IndexRecord.feature", + glue = {"classpath:org.opengroup.osdu.step_definitions/index/record"}, + plugin = {"pretty", "junit:target/cucumber-reports/TEST-indexrecord.xml"}) +public class RunTest { +} \ No newline at end of file diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java new file mode 100644 index 0000000000000000000000000000000000000000..2d402c72f1fa06162e6f37deeae286498f3d30d1 --- /dev/null +++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java @@ -0,0 +1,52 @@ +package org.opengroup.osdu.step_definitions.index.record; + +import lombok.extern.java.Log; +import org.opengroup.osdu.common.RecordSteps; +import org.opengroup.osdu.util.IBMHTTPClient; + +import cucumber.api.Scenario; +import cucumber.api.java.Before; +import cucumber.api.DataTable; +import cucumber.api.java.en.Given; +import cucumber.api.java.en.Then; +import cucumber.api.java.en.When; + +@Log +public class Steps extends RecordSteps { + + public Steps() { + super(new IBMHTTPClient()); + } + + @Before + public void before(Scenario scenario) { + this.scenario = scenario; + this.httpClient = new IBMHTTPClient(); + } + + @Given("^the schema is created with the following kind$") + public void the_schema_is_created_with_the_following_kind(DataTable dataTable) { + super.the_schema_is_created_with_the_following_kind(dataTable); + } + + @When("^I ingest records with the \"(.*?)\" with \"(.*?)\" for a given \"(.*?)\"$") + public void i_ingest_records_with_the_for_a_given(String record, String dataGroup, String kind) { + super.i_ingest_records_with_the_for_a_given(record, dataGroup, kind); + } + + @Then("^I should get the (\\d+) documents for the \"([^\"]*)\" in the Elastic Search$") + public void i_should_get_the_documents_for_the_in_the_Elastic_Search(int expectedCount, String index) throws Throwable { + super.i_should_get_the_documents_for_the_in_the_Elastic_Search(expectedCount, index); + } + + @Then("^I should get the elastic \"(.*?)\" for the \"([^\"]*)\" and \"([^\"]*)\" in the Elastic Search$") + public void i_should_get_the_elastic_for_the_tenant_testindex_timestamp_well_in_the_Elastic_Search(String expectedMapping, String type, String index) throws Throwable { + super.i_should_get_the_elastic_for_the_tenant_testindex_timestamp_well_in_the_Elastic_Search(expectedMapping, type, index); + } + + @Then("^I should get the (\\d+) documents for the \"([^\"]*)\" in the Elastic Search with out \"(.*?)\"$") + public void iShouldGetTheNumberDocumentsForTheIndexInTheElasticSearchWithOutSkippedAttribute(int expectedCount, String index, String skippedAttributes) throws Throwable { + super.iShouldGetTheNumberDocumentsForTheIndexInTheElasticSearchWithOutSkippedAttribute(expectedCount, index, skippedAttributes); + } + +} \ No newline at end of file diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/IBMHTTPClient.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/IBMHTTPClient.java new file mode 100644 index 0000000000000000000000000000000000000000..6affdc4049b0d8884d3c7bb395978c2e8adfc41a --- /dev/null +++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/IBMHTTPClient.java @@ -0,0 +1,23 @@ +package org.opengroup.osdu.util; + +import lombok.ToString; +import lombok.extern.java.Log; + +import java.io.IOException; + +@Log +@ToString +public class IBMHTTPClient extends HTTPClient { + + private static String token = null; + + @Override + public synchronized String getAccessToken() { + if(token == null) { + //FIXME need to get this from somewhere + token = "Basic " + Config.getStorageBasicAuthToken(); + return token; + } + return token; + } +} \ No newline at end of file diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/JwtTokenUtil.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/JwtTokenUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..7c13aaae6539701a93daf5793be42e12a2e9da3b --- /dev/null +++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/util/JwtTokenUtil.java @@ -0,0 +1,103 @@ +package org.opengroup.osdu.util; + +import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.json.webtoken.JsonWebSignature; +import com.google.api.client.json.webtoken.JsonWebToken; +import com.google.api.client.util.Clock; +import com.google.common.base.Strings; +import com.google.gson.Gson; +import lombok.Data; +import org.apache.commons.io.Charsets; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.message.BasicNameValuePair; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; + +class JwtTokenUtil { + + private static String accessToken; + + static String getAccessToken() throws IOException { + + if (Strings.isNullOrEmpty(accessToken)) { + accessToken = getServiceAccountAccessToken(getJwtForIntegrationTesterAccount()); + } + return accessToken; + } + + private static String getServiceAccountAccessToken(String key) throws IOException { + + try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + + List<NameValuePair> parameters = new ArrayList<>(); + parameters.add(new BasicNameValuePair("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")); + parameters.add(new BasicNameValuePair("assertion", key)); + + HttpPost postRequest = new HttpPost("https://www.googleapis.com/oauth2/v4/token"); + postRequest.addHeader("Content-Type", "application/x-www-form-urlencoded"); + postRequest.setEntity(new UrlEncodedFormEntity(parameters)); + + HttpResponse response = httpClient.execute(postRequest); + String responseContent = IOUtils.toString(response.getEntity().getContent(), Charsets.toCharset("UTF-8")); + + JwtTokenUtil.ResponseToken responseToken = new Gson().fromJson(responseContent, JwtTokenUtil.ResponseToken.class); + + return responseToken.getId_token(); + } + } + + private static String getJwtForIntegrationTesterAccount() throws IOException { + String serviceAccountFile = Config.getKeyValue(); + return getJwt(serviceAccountFile); + } + + private static String getJwt(String serviceAccountFile) throws IOException { + + String targetAudience = Config.getTargetAudience(); + long currentTime = Clock.SYSTEM.currentTimeMillis(); + + InputStream stream = new ByteArrayInputStream(Base64.getDecoder().decode(serviceAccountFile)); + GoogleCredential credential = GoogleCredential.fromStream(stream); + + JsonWebSignature.Header header = new JsonWebSignature.Header(); + header.setAlgorithm("RS256"); + header.setType("JWT"); + header.setKeyId(credential.getServiceAccountPrivateKeyId()); + + JsonWebSignature.Payload payload = new JsonWebToken.Payload(); + payload.setIssuedAtTimeSeconds(currentTime / 1000); + payload.setExpirationTimeSeconds(currentTime / 1000 + 3600); + payload.setAudience("https://www.googleapis.com/oauth2/v4/token"); + payload.setIssuer(credential.getServiceAccountId()); + payload.set("target_audience", targetAudience); + + JsonFactory jsonFactory = JacksonFactory.getDefaultInstance(); + String signedJwt = null; + try { + signedJwt = JsonWebSignature.signUsingRsaSha256(credential.getServiceAccountPrivateKey(), jsonFactory, header, payload); + } catch (GeneralSecurityException e) { + e.printStackTrace(); + } + + return signedJwt; + } + + @Data + class ResponseToken { + public String id_token; + } +}