diff --git a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml index 6591bf3c5b55..73c16b4b3ee0 100755 --- a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml +++ b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml @@ -492,6 +492,10 @@ + + + + + + + + + + + diff --git a/eng/jacoco-test-coverage/pom.xml b/eng/jacoco-test-coverage/pom.xml index 9a27624aa7cd..fb41d318fa92 100644 --- a/eng/jacoco-test-coverage/pom.xml +++ b/eng/jacoco-test-coverage/pom.xml @@ -282,6 +282,11 @@ azure-spring-data-cosmos 3.0.0-beta.1 + + com.azure + azure-spring-data-gremlin + 2.3.1-beta.1 + diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index 9ca59be403ec..78b1791f1b19 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -25,8 +25,8 @@ com.microsoft.azure:azure-servicebus-jms;0.0.2 com.microsoft.azure:qpid-proton-j-extensions;1.2.3 com.microsoft.rest:client-runtime;1.7.4 com.microsoft.rest.v2:client-runtime;2.1.1 -com.microsoft.spring.data.gremlin:spring-data-gremlin;2.2.3 com.microsoft.azure:spring-data-cosmosdb;2.3.0 +com.microsoft.spring.data.gremlin:spring-data-gremlin;2.3.0 com.squareup.okhttp3:okhttp;4.2.2 commons-codec:commons-codec;1.13 io.micrometer:micrometer-core;1.2.0 @@ -52,11 +52,13 @@ net.minidev:json-smart;2.3 org.apache.ant:ant;1.9.4 org.apache.avro:avro;1.9.2 org.apache.avro:avro-maven-plugin;1.9.2 +org.apache.commons:commons-lang3;3.8.1 org.apache.httpcomponents:httpclient;4.3.6 org.apache.logging.log4j:log4j-api;2.11.1 org.apache.logging.log4j:log4j-core;2.11.1 org.apache.logging.log4j:log4j-slf4j-impl;2.13.0 org.apache.qpid:proton-j;0.33.4 +org.apache.tinkerpop:gremlin-driver;3.2.4 org.asynchttpclient:async-http-client;2.10.5 org.codehaus.groovy:groovy-eclipse-batch;2.5.8-01 org.codehaus.groovy:groovy-eclipse-compiler;3.4.0-01 @@ -82,19 +84,20 @@ org.springframework.boot:spring-boot-starter-web;2.3.0.RELEASE org.springframework.boot:spring-boot-starter;2.3.0.RELEASE org.springframework.boot:spring-boot;2.3.0.RELEASE org.springframework.boot:spring-boot-starter-parent;2.3.0.RELEASE +org.springframework.data:spring-data-commons;2.3.0.RELEASE org.springframework.security:spring-security-config;5.3.2.RELEASE org.springframework.security:spring-security-core;5.3.2.RELEASE org.springframework.security:spring-security-web;5.3.2.RELEASE org.springframework.security:spring-security-oauth2-client;5.3.2.RELEASE org.springframework.security:spring-security-oauth2-core;5.3.2.RELEASE org.springframework.security:spring-security-oauth2-jose;5.3.2.RELEASE -org.springframework:spring-web;5.2.6.RELEASE -org.springframework:spring-jms;5.2.6.RELEASE -org.springframework.data:spring-data-commons;2.3.0.RELEASE org.springframework:spring-beans;5.2.6.RELEASE +org.springframework:spring-context;5.2.6.RELEASE org.springframework:spring-core;5.2.6.RELEASE org.springframework:spring-expression;5.2.6.RELEASE +org.springframework:spring-jms;5.2.6.RELEASE org.springframework:spring-tx;5.2.6.RELEASE +org.springframework:spring-web;5.2.6.RELEASE pl.pragmatists:JUnitParams;1.1.1 ## Test dependency versions @@ -152,7 +155,6 @@ org.junit.platform:junit-platform-testkit;1.6.2 org.junit.vintage:junit-vintage-engine;5.6.2 org.openjdk.jmh:jmh-core;1.22 org.openjdk.jmh:jmh-generator-annprocess;1.22 -org.springframework:spring-context;5.2.6.RELEASE org.spockframework:spock-core;1.3-groovy-2.5 org.testng:testng;6.14.3 uk.org.lidalia:slf4j-test;1.2.0 @@ -174,7 +176,6 @@ net.java.dev.jna:jna-platform;5.4.0 net.jonathangiles.tools:dependencyChecker-maven-plugin;1.0.4 net.jonathangiles.tools:whitelistgenerator-maven-plugin;1.0.1 org.apache.commons:commons-collections4;4.2 -org.apache.commons:commons-lang3;3.8.1 org.apache.commons:commons-text;1.6 org.apache.maven.plugins:maven-antrun-plugin;1.8 org.apache.maven.plugins:maven-assembly-plugin;3.2.0 diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index 292138b72111..6e632a1c76f3 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -54,6 +54,7 @@ com.azure:azure-spring-boot-test-aad;1.0.0;1.0.0 com.azure:azure-spring-boot-test-core;1.0.0;1.0.0 com.azure:azure-spring-boot-test-cosmosdb;1.0.0;1.0.0 com.azure:azure-spring-boot-test-keyvault;1.0.0;1.0.0 +com.azure:azure-spring-data-gremlin;2.3.1-beta.1;2.3.1-beta.1 com.microsoft.azure:azure-spring-boot;2.3.2;2.3.3-beta.1 com.microsoft.azure:azure-spring-boot-starter;2.3.2;2.3.3-beta.1 com.microsoft.azure:azure-active-directory-spring-boot-starter;2.3.2;2.3.3-beta.1 @@ -63,7 +64,6 @@ com.microsoft.azure:azure-data-gremlin-spring-boot-starter;2.3.2;2.3.3-beta.1 com.microsoft.azure:azure-keyvault-secrets-spring-boot-starter;2.3.2;2.3.3-beta.1 com.microsoft.azure:azure-servicebus-jms-spring-boot-starter;2.3.2;2.3.3-beta.1 com.microsoft.azure:azure-spring-boot-metrics-starter;2.3.2;2.3.3-beta.1 -com.microsoft.azure:spring-data-cosmosdb;3.0.0-beta.1;3.0.0-beta.1 # Unreleased dependencies: Copy the entry from above, prepend "unreleased_" and remove the current # version. Unreleased dependencies are only valid for dependency versions. diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/pom.xml b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/pom.xml new file mode 100644 index 000000000000..23daf6c19758 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.3.0.RELEASE + + + com.azure + azure-spring-data-sample-gremlin-web-service + Spring Data gremlin - Web Service + + + + org.springframework.boot + spring-boot + + + + org.springframework.boot + spring-boot-autoconfigure + + + + org.springframework.boot + spring-boot-starter-web + + + + com.jayway.jsonpath + json-path + + + + com.azure + azure-spring-data-gremlin + 2.3.1-beta.1 + + + + + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/Application.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/Application.java new file mode 100644 index 000000000000..f8dab928682b --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/Application.java @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.web.service; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/config/GremlinProperties.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/config/GremlinProperties.java new file mode 100644 index 000000000000..d1d3f7e2deb4 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/config/GremlinProperties.java @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.web.service.config; + +import org.apache.tinkerpop.gremlin.driver.ser.Serializers; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("gremlin") +public class GremlinProperties { + private String endpoint; + + private int port; + + private String username; + + private String password; + + private boolean sslEnabled; + + private boolean telemetryAllowed = true; + + private String serializer = Serializers.GRAPHSON.toString(); + + private int maxContentLength; + + public GremlinProperties() { + } + + public GremlinProperties(String endpoint, int port, String username, String password, boolean sslEnabled, + boolean telemetryAllowed, String serializer, int maxContentLength) { + this.endpoint = endpoint; + this.port = port; + this.username = username; + this.password = password; + this.sslEnabled = sslEnabled; + this.telemetryAllowed = telemetryAllowed; + this.serializer = serializer; + this.maxContentLength = maxContentLength; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public boolean isSslEnabled() { + return sslEnabled; + } + + public void setSslEnabled(boolean sslEnabled) { + this.sslEnabled = sslEnabled; + } + + public boolean isTelemetryAllowed() { + return telemetryAllowed; + } + + public void setTelemetryAllowed(boolean telemetryAllowed) { + this.telemetryAllowed = telemetryAllowed; + } + + public String getSerializer() { + return serializer; + } + + public void setSerializer(String serializer) { + this.serializer = serializer; + } + + public int getMaxContentLength() { + return maxContentLength; + } + + public void setMaxContentLength(int maxContentLength) { + this.maxContentLength = maxContentLength; + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/config/UserRepositoryConfiguration.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/config/UserRepositoryConfiguration.java new file mode 100644 index 000000000000..7af294f9358a --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/config/UserRepositoryConfiguration.java @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.web.service.config; + +import com.microsoft.spring.data.gremlin.common.GremlinConfig; +import com.microsoft.spring.data.gremlin.config.AbstractGremlinConfiguration; +import com.microsoft.spring.data.gremlin.repository.config.EnableGremlinRepositories; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@EnableGremlinRepositories(basePackages = "com.microsoft.azure.spring.data.gremlin.web.service.repository") +@EnableConfigurationProperties(GremlinProperties.class) +@PropertySource("classpath:application.properties") +public class UserRepositoryConfiguration extends AbstractGremlinConfiguration { + + @Autowired + private GremlinProperties gremlinProps; + + @Override + public GremlinConfig getGremlinConfig() { + return GremlinConfig.defaultBuilder() + .endpoint(gremlinProps.getEndpoint()) + .port(gremlinProps.getPort()) + .username(gremlinProps.getUsername()) + .password(gremlinProps.getPassword()) + .sslEnabled(gremlinProps.isSslEnabled()) + .telemetryAllowed(gremlinProps.isTelemetryAllowed()) + .serializer(gremlinProps.getSerializer()) + .maxContentLength(gremlinProps.getMaxContentLength()) + .build(); + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/domain/MicroService.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/domain/MicroService.java new file mode 100644 index 000000000000..1fd5baf7da98 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/domain/MicroService.java @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.web.service.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import org.springframework.data.annotation.Id; + +import java.util.HashMap; +import java.util.Map; + +@Vertex +public class MicroService { + + @Id + private String id; + + private Map properties = new HashMap<>(); + + public MicroService() { + } + + public MicroService(String id, Map properties) { + this.id = id; + this.properties = properties; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } +} + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/domain/ServicesDataFlow.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/domain/ServicesDataFlow.java new file mode 100644 index 000000000000..9d8e6c419d2d --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/domain/ServicesDataFlow.java @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.web.service.domain; + +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; +import org.springframework.data.annotation.Id; + +import java.util.HashMap; +import java.util.Map; + +@Edge +public class ServicesDataFlow { + + @Id + private String id; + + @EdgeFrom + private MicroService serviceFrom; + + @EdgeTo + private MicroService serviceTo; + + private Map properties = new HashMap<>(); + + public ServicesDataFlow() { + } + + public ServicesDataFlow(String id, MicroService serviceFrom, MicroService serviceTo, + Map properties) { + this.id = id; + this.serviceFrom = serviceFrom; + this.serviceTo = serviceTo; + this.properties = properties; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public MicroService getServiceFrom() { + return serviceFrom; + } + + public void setServiceFrom(MicroService serviceFrom) { + this.serviceFrom = serviceFrom; + } + + public MicroService getServiceTo() { + return serviceTo; + } + + public void setServiceTo(MicroService serviceTo) { + this.serviceTo = serviceTo; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } +} + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/domain/SpringCloudServiceNetwork.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/domain/SpringCloudServiceNetwork.java new file mode 100644 index 000000000000..bb5c2e6913e4 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/domain/SpringCloudServiceNetwork.java @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.web.service.domain; + +import com.microsoft.spring.data.gremlin.annotation.EdgeSet; +import com.microsoft.spring.data.gremlin.annotation.Graph; +import com.microsoft.spring.data.gremlin.annotation.VertexSet; +import org.springframework.data.annotation.Id; + +import java.util.ArrayList; +import java.util.List; + +@Graph +public class SpringCloudServiceNetwork { + + @Id + private String id; + + @EdgeSet + private List edges = new ArrayList<>(); + + @VertexSet + private List vertexes = new ArrayList<>(); + + public List getEdges() { + return edges; + } + + public List getVertexes() { + return vertexes; + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/repository/MicroServiceRepository.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/repository/MicroServiceRepository.java new file mode 100644 index 000000000000..7036cbca53e4 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/repository/MicroServiceRepository.java @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.web.service.repository; + +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import com.microsoft.spring.data.gremlin.web.service.domain.MicroService; +import org.springframework.stereotype.Repository; + +@Repository +public interface MicroServiceRepository extends GremlinRepository { +} + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/repository/ServicesDataFlowRepository.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/repository/ServicesDataFlowRepository.java new file mode 100644 index 000000000000..e6a906c87826 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/repository/ServicesDataFlowRepository.java @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.web.service.repository; + +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import com.microsoft.spring.data.gremlin.web.service.domain.ServicesDataFlow; +import org.springframework.stereotype.Repository; + +@Repository +public interface ServicesDataFlowRepository extends GremlinRepository { +} + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/repository/SpringCloudServiceNetworkRepository.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/repository/SpringCloudServiceNetworkRepository.java new file mode 100644 index 000000000000..295e187c4f65 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/repository/SpringCloudServiceNetworkRepository.java @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.web.service.repository; + +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import com.microsoft.spring.data.gremlin.web.service.domain.SpringCloudServiceNetwork; +import org.springframework.stereotype.Repository; + +@Repository +public interface SpringCloudServiceNetworkRepository extends GremlinRepository { +} + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/web/Controller/WebController.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/web/Controller/WebController.java new file mode 100644 index 000000000000..f0ca51af9a0e --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/web/Controller/WebController.java @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.web.service.web.Controller; + +import com.microsoft.spring.data.gremlin.web.service.repository.ServicesDataFlowRepository; +import com.microsoft.spring.data.gremlin.web.service.web.domain.Greeting; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import com.microsoft.spring.data.gremlin.web.service.domain.MicroService; +import com.microsoft.spring.data.gremlin.web.service.domain.ServicesDataFlow; +import com.microsoft.spring.data.gremlin.web.service.repository.MicroServiceRepository; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +@RestController +public class WebController { + + @Autowired + private MicroServiceRepository microServiceRepo; + + @Autowired + private ServicesDataFlowRepository dataFlowRepo; + + private final AtomicLong counter = new AtomicLong(); + + @RequestMapping(value = "/greeting", method = RequestMethod.GET) + public Greeting greeting() { + return new Greeting(String.valueOf(this.counter.incrementAndGet()), "Greetings to User."); + } + + @RequestMapping(value = "/services/{id}", method = RequestMethod.GET) + public MicroService getService(@PathVariable String id) { + + final Optional foundService = this.microServiceRepo.findById(id); + + return foundService.orElse(null); + } + + @RequestMapping(value = "/services/{id}", method = RequestMethod.PUT) + public MicroService putService(@PathVariable String id, @RequestBody MicroService service) { + + if (!service.getId().equals(id)) { + service.setId(id); + } + + this.microServiceRepo.save(service); + + return service; + } + + @RequestMapping(value = "/services/{id}", method = RequestMethod.DELETE) + public void deleteService(@PathVariable String id) { + this.microServiceRepo.deleteById(id); + } + + @RequestMapping(value = "/services/", method = RequestMethod.DELETE) + public void deleteService(@RequestBody MicroService service) { + this.microServiceRepo.delete(service); + } + + @RequestMapping(value = "/services/all", method = RequestMethod.DELETE) + public void deleteServicesAll() { + this.microServiceRepo.deleteAll(); + } + + @RequestMapping(value = "/services/", method = RequestMethod.PUT) + public MicroService putService(@RequestBody MicroService service) { + + this.microServiceRepo.save(service); + + return service; + } + + @RequestMapping(value = "/services/", method = RequestMethod.GET) + public List getServiceList() { + return (List) this.microServiceRepo.findAll(MicroService.class); + } + + @RequestMapping(value = "/services/create/{id}", method = RequestMethod.PUT) + public MicroService createService(@PathVariable String id, @RequestBody MicroService service) { + return this.putService(id, service); + } + + @RequestMapping(value = "/services/create/", method = RequestMethod.PUT) + public MicroService createService(@RequestBody MicroService service) { + return this.putService(service); + } + + @RequestMapping(value = "/dataflow/{id}", method = RequestMethod.GET) + public ServicesDataFlow getServicesDataFlow(@PathVariable String id) { + + final Optional foundDataFlow = this.dataFlowRepo.findById(id); + + return foundDataFlow.orElse(null); + } + + @RequestMapping(value = "/dataflow/{id}", method = RequestMethod.PUT) + public ServicesDataFlow putServicesDataFlow(@PathVariable String id, @RequestBody ServicesDataFlow dataFlow) { + + if (!dataFlow.getId().equals(id)) { + dataFlow.setId(id); + } + + this.dataFlowRepo.save(dataFlow); + + return dataFlow; + } + + @RequestMapping(value = "/dataflow/", method = RequestMethod.PUT) + public ServicesDataFlow putServicesDataFlow(@RequestBody ServicesDataFlow dataFlow) { + + this.dataFlowRepo.save(dataFlow); + + return dataFlow; + } + + @RequestMapping(value = "/dataflow/", method = RequestMethod.GET) + public List getServicesDataFlowList() { + return (List) this.dataFlowRepo.findAll(ServicesDataFlow.class); + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/web/domain/Greeting.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/web/domain/Greeting.java new file mode 100644 index 000000000000..ff8bd6b4e652 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/java/com/microsoft/spring/data/gremlin/web/service/web/domain/Greeting.java @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.web.service.web.domain; + + +public class Greeting { + + private String id; + + private String content; + + public Greeting() { + } + + public Greeting(String id, String content) { + this.id = id; + this.content = content; + } + + public String getId() { + return id; + } + + public String getContent() { + return content; + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/resources/application.properties b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/resources/application.properties new file mode 100644 index 000000000000..f722d685d53c --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service/src/main/resources/application.properties @@ -0,0 +1,7 @@ +gremlin.endpoint=localhost +gremlin.port=8889 +gremlin.username=your-username +gremlin.password=your-password +gremlin.sslEnabled=false +gremlin.telemetryAllowed=true +gremlin.maxContentLength=1000 diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/pom.xml b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/pom.xml new file mode 100644 index 000000000000..ba8b3089ed1f --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.3.0.RELEASE + + + com.azure + azure-spring-data-sample-gremlin + Spring Data gremlin - Sample + + + + org.springframework.boot + spring-boot + + + + org.springframework.boot + spring-boot-autoconfigure + + + + org.springframework.boot + spring-boot-starter-data-rest + + + + com.azure + azure-spring-data-gremlin + 2.3.1-beta.1 + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/Application.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/Application.java new file mode 100644 index 000000000000..c47b5d4e1634 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/Application.java @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin; + +import com.microsoft.spring.data.gremlin.common.GremlinFactory; +import com.microsoft.spring.data.gremlin.domain.Network; +import com.microsoft.spring.data.gremlin.domain.Person; +import com.microsoft.spring.data.gremlin.domain.Relation; +import com.microsoft.spring.data.gremlin.repository.NetworkRepository; +import com.microsoft.spring.data.gremlin.repository.PersonRepository; +import com.microsoft.spring.data.gremlin.repository.RelationRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import javax.annotation.PostConstruct; + +@SpringBootApplication +public class Application { + + private static final String PERSON_ID = "89757"; + private static final String PERSON_ID_0 = "0123456789"; + private static final String PERSON_ID_1 = "666666"; + private static final String PERSON_NAME = "person-name"; + private static final String PERSON_NAME_0 = "person-No.0"; + private static final String PERSON_NAME_1 = "person-No.1"; + private static final String PERSON_AGE = "4"; + private static final String PERSON_AGE_0 = "18"; + private static final String PERSON_AGE_1 = "27"; + + private static final String RELATION_ID = "2333"; + private static final String RELATION_NAME = "brother"; + + private final Person person = new Person(PERSON_ID, PERSON_NAME, PERSON_AGE); + private final Person person0 = new Person(PERSON_ID_0, PERSON_NAME_0, PERSON_AGE_0); + private final Person person1 = new Person(PERSON_ID_1, PERSON_NAME_1, PERSON_AGE_1); + private final Relation relation = new Relation(RELATION_ID, RELATION_NAME, person0, person1); + private final Network network = new Network(); + + @Autowired + private PersonRepository personRepo; + + @Autowired + private RelationRepository relationRepo; + + @Autowired + private NetworkRepository networkRepo; + + @Autowired + private GremlinFactory factory; + + public static void main(String... args) { + SpringApplication.run(Application.class, args); + } + + @PostConstruct + public void setup() { + this.networkRepo.deleteAll(); + + this.network.getEdges().add(this.relation); + this.network.getVertexes().add(this.person); + this.network.getVertexes().add(this.person0); + this.network.getVertexes().add(this.person1); + + this.networkRepo.save(this.network); + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/GremlinProperties.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/GremlinProperties.java new file mode 100644 index 000000000000..02a3c692d510 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/GremlinProperties.java @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.config; + +import org.apache.tinkerpop.gremlin.driver.ser.Serializers; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("gremlin") +public class GremlinProperties { + + private String endpoint; + + private int port; + + private String username; + + private String password; + + private boolean sslEnabled; + + private boolean telemetryAllowed = true; + + private String serializer = Serializers.GRAPHSON.toString(); + + private int maxContentLength; + + public GremlinProperties() { + } + + public GremlinProperties(String endpoint, int port, String username, String password, boolean sslEnabled, + boolean telemetryAllowed, String serializer, int maxContentLength) { + this.endpoint = endpoint; + this.port = port; + this.username = username; + this.password = password; + this.sslEnabled = sslEnabled; + this.telemetryAllowed = telemetryAllowed; + this.serializer = serializer; + this.maxContentLength = maxContentLength; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public boolean isSslEnabled() { + return sslEnabled; + } + + public void setSslEnabled(boolean sslEnabled) { + this.sslEnabled = sslEnabled; + } + + public boolean isTelemetryAllowed() { + return telemetryAllowed; + } + + public void setTelemetryAllowed(boolean telemetryAllowed) { + this.telemetryAllowed = telemetryAllowed; + } + + public String getSerializer() { + return serializer; + } + + public void setSerializer(String serializer) { + this.serializer = serializer; + } + + public int getMaxContentLength() { + return maxContentLength; + } + + public void setMaxContentLength(int maxContentLength) { + this.maxContentLength = maxContentLength; + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/UserRepositoryConfiguration.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/UserRepositoryConfiguration.java new file mode 100644 index 000000000000..7c29028f7c62 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/UserRepositoryConfiguration.java @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.config; + +import com.microsoft.spring.data.gremlin.common.GremlinConfig; +import com.microsoft.spring.data.gremlin.repository.config.EnableGremlinRepositories; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@EnableGremlinRepositories(basePackages = "com.microsoft.azure.spring.data.gremlin.repository") +@EnableConfigurationProperties(GremlinProperties.class) +@PropertySource("classpath:application.properties") +public class UserRepositoryConfiguration extends AbstractGremlinConfiguration { + + @Autowired + private GremlinProperties gremlinProps; + + @Override + public GremlinConfig getGremlinConfig() { + return GremlinConfig.defaultBuilder() + .endpoint(gremlinProps.getEndpoint()) + .port(gremlinProps.getPort()) + .username(gremlinProps.getUsername()) + .password(gremlinProps.getPassword()) + .sslEnabled(gremlinProps.isSslEnabled()) + .telemetryAllowed(gremlinProps.isTelemetryAllowed()) + .serializer(gremlinProps.getSerializer()) + .maxContentLength(gremlinProps.getMaxContentLength()) + .build(); + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/domain/Network.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/domain/Network.java new file mode 100644 index 000000000000..6eeb3094dd49 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/domain/Network.java @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.domain; + +import com.microsoft.spring.data.gremlin.annotation.EdgeSet; +import com.microsoft.spring.data.gremlin.annotation.Graph; +import com.microsoft.spring.data.gremlin.annotation.VertexSet; +import org.springframework.data.annotation.Id; + +import java.util.ArrayList; +import java.util.List; + +@Graph +public class Network { + + @Id + private String id; + + public Network() { + this.edges = new ArrayList<>(); + this.vertexes = new ArrayList<>(); + } + + @EdgeSet + private List edges; + + @VertexSet + private List vertexes; + + public List getEdges() { + return edges; + } + + public List getVertexes() { + return vertexes; + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/domain/Person.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/domain/Person.java new file mode 100644 index 000000000000..950ee1f6e01a --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/domain/Person.java @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import org.springframework.data.annotation.Id; + +@Vertex +public class Person { + + @Id + private String id; + + private String name; + + private String age; + + public Person() { + } + + public Person(String id, String name, String age) { + this.id = id; + this.name = name; + this.age = age; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getAge() { + return age; + } + + public void setId(String id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setAge(String age) { + this.age = age; + } +} + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/domain/Relation.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/domain/Relation.java new file mode 100644 index 000000000000..3b89d64a4f6a --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/domain/Relation.java @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.domain; + +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; +import org.springframework.data.annotation.Id; + +@Edge +public class Relation { + + @Id + private String id; + + private String name; + + @EdgeFrom + private Person personFrom; + + @EdgeTo + private Person personTo; + + public Relation() { + } + + public Relation(String id, String name, Person personFrom, Person personTo) { + this.id = id; + this.name = name; + this.personFrom = personFrom; + this.personTo = personTo; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Person getPersonFrom() { + return personFrom; + } + + public void setPersonFrom(Person personFrom) { + this.personFrom = personFrom; + } + + public Person getPersonTo() { + return personTo; + } + + public void setPersonTo(Person personTo) { + this.personTo = personTo; + } +} + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/NetworkRepository.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/NetworkRepository.java new file mode 100644 index 000000000000..5118e31471bd --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/NetworkRepository.java @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.domain.Network; +import org.springframework.stereotype.Repository; + +@Repository +public interface NetworkRepository extends GremlinRepository { +} + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/PersonRepository.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/PersonRepository.java new file mode 100644 index 000000000000..d65f44596dda --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/PersonRepository.java @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.domain.Person; +import org.springframework.data.rest.webmvc.RepositoryRestController; +import org.springframework.stereotype.Repository; + +@Repository +@RepositoryRestController +public interface PersonRepository extends GremlinRepository { +} + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/RelationRepository.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/RelationRepository.java new file mode 100644 index 000000000000..0b7b1cf8cabc --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/RelationRepository.java @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.domain.Relation; +import org.springframework.stereotype.Repository; + +@Repository +public interface RelationRepository extends GremlinRepository { +} + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/resources/application.properties b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/resources/application.properties new file mode 100644 index 000000000000..f722d685d53c --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/main/resources/application.properties @@ -0,0 +1,7 @@ +gremlin.endpoint=localhost +gremlin.port=8889 +gremlin.username=your-username +gremlin.password=your-password +gremlin.sslEnabled=false +gremlin.telemetryAllowed=true +gremlin.maxContentLength=1000 diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/test/java/com/microsoft/spring/data/gremlin/GremlinRepositoryIntegrationTest.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/test/java/com/microsoft/spring/data/gremlin/GremlinRepositoryIntegrationTest.java new file mode 100644 index 000000000000..8a69fe6bc49d --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/test/java/com/microsoft/spring/data/gremlin/GremlinRepositoryIntegrationTest.java @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin; + +import com.microsoft.spring.data.gremlin.domain.Network; +import com.microsoft.spring.data.gremlin.domain.Person; +import com.microsoft.spring.data.gremlin.domain.Relation; +import com.microsoft.spring.data.gremlin.repository.NetworkRepository; +import com.microsoft.spring.data.gremlin.repository.PersonRepository; +import com.microsoft.spring.data.gremlin.repository.RelationRepository; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Optional; + +@SpringBootTest +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = RepositoryConfiguration.class) +@Ignore +public class GremlinRepositoryIntegrationTest { + private static final String PERSON_ID = "89757"; + private static final String PERSON_ID_0 = "0123456789"; + private static final String PERSON_ID_1 = "666666"; + private static final String PERSON_NAME = "person-name"; + private static final String PERSON_NAME_0 = "person-No.0"; + private static final String PERSON_NAME_1 = "person-No.1"; + private static final String PERSON_AGE = "4"; + private static final String PERSON_AGE_0 = "18"; + private static final String PERSON_AGE_1 = "27"; + + private static final String RELATION_ID = "2333"; + private static final String RELATION_NAME = "brother"; + + private final Person person = new Person(PERSON_ID, PERSON_NAME, PERSON_AGE); + private final Person person0 = new Person(PERSON_ID_0, PERSON_NAME_0, PERSON_AGE_0); + private final Person person1 = new Person(PERSON_ID_1, PERSON_NAME_1, PERSON_AGE_1); + private final Relation relation = new Relation(RELATION_ID, RELATION_NAME, person0, person1); + private final Network network = new Network(); + + @Autowired + private PersonRepository personRepo; + + @Autowired + private RelationRepository relationRepo; + + @Autowired + private NetworkRepository networkRepo; + + @Before + public void setup() { + this.networkRepo.deleteAll(); + } + + @After + public void cleanup() { + this.networkRepo.deleteAll(); + } + + @Test + public void testRepository() { + this.network.getVertexes().add(this.person); + this.network.getVertexes().add(this.person0); + this.network.getVertexes().add(this.person1); + this.network.getEdges().add(this.relation); + + this.networkRepo.save(this.network); + + final Optional personOptional = this.personRepo.findById(this.person.getId()); + Assert.assertTrue(personOptional.isPresent()); + + final Person personFound = personOptional.get(); + Assert.assertEquals(personFound.getId(), this.person.getId()); + Assert.assertEquals(personFound.getName(), this.person.getName()); + Assert.assertEquals(personFound.getAge(), this.person.getAge()); + + final Optional relationOptional = this.relationRepo.findById(this.relation.getId()); + Assert.assertTrue(relationOptional.isPresent()); + + final Relation relationFound = relationOptional.get(); + + Assert.assertEquals(relationFound.getId(), this.relation.getId()); + Assert.assertEquals(relationFound.getName(), this.relation.getName()); + } +} + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/test/java/com/microsoft/spring/data/gremlin/RepositoryConfiguration.java b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/test/java/com/microsoft/spring/data/gremlin/RepositoryConfiguration.java new file mode 100644 index 000000000000..d94d8cdb6d31 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/test/java/com/microsoft/spring/data/gremlin/RepositoryConfiguration.java @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin; + +import com.microsoft.spring.data.gremlin.common.GremlinConfig; +import com.microsoft.spring.data.gremlin.config.AbstractGremlinConfiguration; +import com.microsoft.spring.data.gremlin.config.GremlinProperties; +import com.microsoft.spring.data.gremlin.repository.config.EnableGremlinRepositories; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + + +@Configuration +@EnableGremlinRepositories(basePackages = "com.microsoft.azure.spring.data.gremlin.repository") +@EnableConfigurationProperties(GremlinProperties.class) +@PropertySource("classpath:application.yml") +public class RepositoryConfiguration extends AbstractGremlinConfiguration { + + @Autowired + private GremlinProperties gremlinProps; + + @Override + public GremlinConfig getGremlinConfig() { + return GremlinConfig.defaultBuilder() + .endpoint(gremlinProps.getEndpoint()) + .port(gremlinProps.getPort()) + .username(gremlinProps.getUsername()) + .password(gremlinProps.getPassword()) + .sslEnabled(gremlinProps.isSslEnabled()) + .telemetryAllowed(gremlinProps.isTelemetryAllowed()) + .serializer(gremlinProps.getSerializer()) + .maxContentLength(gremlinProps.getMaxContentLength()) + .build(); + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/test/resources/application.yml b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/test/resources/application.yml new file mode 100644 index 000000000000..85d68b1b5cc9 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-data-sample-gremlin/src/test/resources/application.yml @@ -0,0 +1,7 @@ +gremlin: + endpoint: your-endpoint.gremlin.cosmosdb.azure.com + port: 443 + username: your-username + password: your-password + sslEnabled: false + telemetryAllowed: true diff --git a/sdk/spring/azure-spring-boot-starter-data-gremlin/pom.xml b/sdk/spring/azure-spring-boot-starter-data-gremlin/pom.xml index ea7b66233318..41e808004868 100644 --- a/sdk/spring/azure-spring-boot-starter-data-gremlin/pom.xml +++ b/sdk/spring/azure-spring-boot-starter-data-gremlin/pom.xml @@ -37,7 +37,7 @@ com.microsoft.spring.data.gremlin spring-data-gremlin - 2.2.3 + 2.3.0 com.fasterxml.jackson.core @@ -58,7 +58,7 @@ com.microsoft.azure:azure-spring-boot:[2.3.3-beta.1] com.fasterxml.jackson.core:jackson-core:[2.10.1] - com.microsoft.spring.data.gremlin:spring-data-gremlin:[2.2.3] + com.microsoft.spring.data.gremlin:spring-data-gremlin:[2.3.0] org.springframework.boot:spring-boot-starter:[2.3.0.RELEASE] org.springframework.boot:spring-boot-starter-validation:[2.3.0.RELEASE] diff --git a/sdk/spring/azure-spring-boot-test-aad/CHANGELOG.md b/sdk/spring/azure-spring-boot-test-aad/CHANGELOG.md new file mode 100644 index 000000000000..3bae488de73c --- /dev/null +++ b/sdk/spring/azure-spring-boot-test-aad/CHANGELOG.md @@ -0,0 +1,3 @@ +# Release History + +## 1.0.0 (Unreleased) diff --git a/sdk/spring/azure-spring-boot-test-cosmosdb/CHANGELOG.md b/sdk/spring/azure-spring-boot-test-cosmosdb/CHANGELOG.md new file mode 100644 index 000000000000..3bae488de73c --- /dev/null +++ b/sdk/spring/azure-spring-boot-test-cosmosdb/CHANGELOG.md @@ -0,0 +1,3 @@ +# Release History + +## 1.0.0 (Unreleased) diff --git a/sdk/spring/azure-spring-boot-test-keyvault/CHANGELOG.md b/sdk/spring/azure-spring-boot-test-keyvault/CHANGELOG.md new file mode 100644 index 000000000000..3bae488de73c --- /dev/null +++ b/sdk/spring/azure-spring-boot-test-keyvault/CHANGELOG.md @@ -0,0 +1,3 @@ +# Release History + +## 1.0.0 (Unreleased) diff --git a/sdk/spring/azure-spring-boot/pom.xml b/sdk/spring/azure-spring-boot/pom.xml index c77a7827b6f6..66615f467ec5 100644 --- a/sdk/spring/azure-spring-boot/pom.xml +++ b/sdk/spring/azure-spring-boot/pom.xml @@ -106,11 +106,11 @@ true - + com.microsoft.spring.data.gremlin spring-data-gremlin - 2.2.3 + 2.3.0 true @@ -268,7 +268,7 @@ com.microsoft.azure:azure-servicebus-jms:[0.0.2] com.microsoft.azure:msal4j:[1.6.1] com.microsoft.azure:spring-data-cosmosdb:[2.3.0] - com.microsoft.spring.data.gremlin:spring-data-gremlin:[2.2.3] + com.microsoft.spring.data.gremlin:spring-data-gremlin:[2.3.0] com.nimbusds:nimbus-jose-jwt:[7.9] io.micrometer:micrometer-core:[1.3.0] io.micrometer:micrometer-registry-azure-monitor:[1.3.0] diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/gremlin/GremlinAutoConfiguration.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/gremlin/GremlinAutoConfiguration.java index a872ec1a738a..1c0b5b920e4f 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/gremlin/GremlinAutoConfiguration.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/gremlin/GremlinAutoConfiguration.java @@ -28,6 +28,7 @@ import static com.microsoft.azure.telemetry.TelemetryData.SERVICE_NAME; import static com.microsoft.azure.telemetry.TelemetryData.getClassPackageSimpleName; + /** * To create Gremlin factory and template for auto-configure Gremlin properties. */ @@ -62,7 +63,8 @@ private void sendTelemetry() { @Bean @ConditionalOnMissingBean public GremlinConfig getGremlinConfig() { - return GremlinConfig.builder(properties.getEndpoint(), properties.getUsername(), properties.getPassword()) + return GremlinConfig.builder(properties.getEndpoint(), properties.getUsername(), + properties.getPassword()) .port(properties.getPort()) .sslEnabled(properties.isSslEnabled()) .telemetryAllowed(properties.isTelemetryAllowed()) diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/gremlin/GremlinAutoConfigurationUnitTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/gremlin/GremlinAutoConfigurationUnitTest.java index 8a94a8d10e33..c5b13e9d373a 100644 --- a/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/gremlin/GremlinAutoConfigurationUnitTest.java +++ b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/gremlin/GremlinAutoConfigurationUnitTest.java @@ -11,7 +11,11 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import static com.microsoft.azure.spring.autoconfigure.gremlin.PropertiesUtil.*; +import static com.microsoft.azure.spring.autoconfigure.gremlin.PropertiesUtil.GREMLIN_ENDPOINT_CONFIG; +import static com.microsoft.azure.spring.autoconfigure.gremlin.PropertiesUtil.GREMLIN_PASSWORD_CONFIG; +import static com.microsoft.azure.spring.autoconfigure.gremlin.PropertiesUtil.GREMLIN_PORT_CONFIG; +import static com.microsoft.azure.spring.autoconfigure.gremlin.PropertiesUtil.GREMLIN_TELEMETRY_CONFIG_ALLOWED; +import static com.microsoft.azure.spring.autoconfigure.gremlin.PropertiesUtil.GREMLIN_USERNAME_CONFIG; public class GremlinAutoConfigurationUnitTest { diff --git a/sdk/spring/azure-spring-data-gremlin/CHANGELOG.md b/sdk/spring/azure-spring-data-gremlin/CHANGELOG.md new file mode 100644 index 000000000000..d98c3d0f2282 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/CHANGELOG.md @@ -0,0 +1,3 @@ +# Release History + +## 2.3.1-beta.1 (Unreleased) diff --git a/sdk/spring/azure-spring-data-gremlin/DESIGN.md b/sdk/spring/azure-spring-data-gremlin/DESIGN.md new file mode 100644 index 000000000000..f06e8e581500 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/DESIGN.md @@ -0,0 +1,93 @@ +# Spring Data Gremlin Design + +### Orientation + +Gremlin is a functional, data-flow language that enables users to succinctly express complex +traversals on (or queries of) their application's property graph. It hides the details of backend +database implementation (like azure cosmosdb support Graph API). + +Apache Tinkerpop gremlin java driver allows you to launch gremlin query, but it is not easy to +get familiar with gremlin syntax. You have to generate all gremlin queries by yourself as following. + +```java + static final String[] gremlinQueries = new String[] { + "g.V().drop()", + "g.addV('person').property('id', '1').property('name', 'pli').property('age', 31)", + "g.addV('person').property('id', '4').property('name', 'incarnation').property('age', 27)", + "g.addV('software').property('id', '2').property('name', 'spring-boot-sample').property('lang', 'java')", + "g.V('1').addE('created').to(g.V('2')).property('weight', 0.8)", + "g.V('1').addE('contributed').to(g.V('2')).property('weight', 0.1)", + "g.V('4').addE('contributed').to(g.V('2')).property('weight', 0.4)" + }; +``` + +We'd like to make things easier by hiding the process of mapping Java instance to graph database +persistent entity, with the help of spring-data-commons. + +### From Users' View +How do users use graph db? They can use gremlin driver to generate the query literal in Java class instance. +It's not impossible but yet isn't easy. We want to figure out a more Spring natural way by leveraging +Spring annotations to map Java instance to database entity. + +### From Graph database View +As we know, there may be some concept like `Vertex`, `Edge` and `Graph` when we talk about +graph. Naturally the object instance needs to be mapped to one and the only one of these +element in graph. Simply we add some annotations to reach this. + +```java + @Vertex // maps an Object to a Vertex + @VertexSet // maps a set of Vertex in Graph + @Edge // maps an Object to an Edge + @EdgeSet // maps to a set of Edge in Graph + @EdgeFrom // maps to the head Vertex of an Edge + @EdgeTo // maps to the tail Vertex of an Edge + @Graph // maps to an Object to a Graph +``` + +### CRUD based query +The `@GremlinRepository` extends `@CrudReposiotry` is providing basic queries, like insert, +save, find, delete and count. Let's take insert as example to the details of implementation. + +##### Some Constrictions +* Gremlin describes the `Vertex` and `Edge` in a flat layout, with fixed property `id`, `label`, +and any other properties organized as key-value pair. +* Gremlin properties name must be `String`, and values can be `Number`, `Boolean` and `String`. +* No nested structure in `Vertex` and `Edge`. +* `Edge` is directed. + +##### GremlinSource +Before we start to insert a `Vertex` instance to database, we need one abstract layer into +isolate the dependency between instance and entity in database. We define one class to +represent all instance stored in database. This class not only needs to hold all information from +instance object, but also has the flat structure like entity in database. It is the bridge between +instance in java and persistent entity in database. Here we call it `GremlinSource`. + +With the above constriction of gremlin, `GremlinSource` has field `id` and `label` for mapping, +and keep all other fields into one `Map`. When we try to insert a instance from +java to `GremlinSource`, we perform a **WRITE** operation and convert the instance to a `GremlinSource`. +Operation **WRITE** will convert all the data of one instance to `GremlinSource`, and take care of +`id` or `@Id` field. + +The `GremlinSourceWriter` converts Java instance to `GremlinSource`. + +##### GremlinResult +There must be one **READ** operation for retrieving instance from database entity. For example, +the `find` query will return the persistent data from database with the type `Result`, provided +by apache SDK. For insulating the dependency between instance and `Result`, just like what we do +in **WRITE**. We also use `GremlinSource` as the bridge from `Result` to instance. Simply there +are two steps after query from database. First the `Result` from database will be converted to +`GremlinSource`. And then the `GremlinSource` will be converted to Java instance, just like what +we do in **WRITE** operation. + +The `GremlinResultReader` converts `Result` to `GremlinSource`. +The `GremlinSourceReader` converts `GremlinSource` to Java instance. + +##### GremlinScript +GremlinScript will generate the query based on `GremlinSource`. It converts the data like +`id`, `label` and `properites` into gremlin query literal Strings. For type `String`, `Number` +and `Boolean`, the query will store them as primitive types (supported by gremlin). And any +other type of instance field will be converted to Json-like `String` except type `Date`, it will +be converted to milliSeconds and stored as `Number`. Then the gremlin client will execute the query +to access database. + +The `GremlinScriptLiteral` generates literal query based on `GremlinSource`. diff --git a/sdk/spring/azure-spring-data-gremlin/README.md b/sdk/spring/azure-spring-data-gremlin/README.md new file mode 100644 index 000000000000..537933410868 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/README.md @@ -0,0 +1,193 @@ +# Azure Spring Data Gremlin client library for Java + +## Key concepts + +**Spring Data Gremlin** provides initial Spring Data support for those databases using Gremlin query language. With annotation oriented programming model, it simplified the mapping to the database entity. It also provides supports for basic and custom query. + +This project works with *any Gremlin-compatible* data store, and also with [Azure Cosmos DB](https://docs.microsoft.com/azure/cosmos-db/introduction). Cosmos is a globally-distributed database service that allows developers to work with data using a variety of standard APIs, such as Graph, MongoDB, and SQL. Spring Data Gremlin provides a delightful experience to interact with Azure Cosmos DB Graph API. + +### Feature List +- Spring Data CRUDRepository basic CRUD functionality + - save + - findAll + - findById + - deleteAll + - deleteById +- Spring Data [@Id](https://github.com/spring-projects/spring-data-commons/blob/master/src/main/java/org/springframework/data/annotation/Id.java) annotation. + There're 2 ways to map a field in domain class to `id` field of a database entity. + - annotate a field in domain class with `@Id` + - set name of this field to `id` +- Default annotation + - `@Vertex` maps an `Object` to a `Vertex` + - `@VertexSet` maps a set of `Vertex` + - `@Edge` maps an `Object` to an `Edge` + - `@EdgeSet` maps to a set of `Edge` + - `@EdgeFrom` maps to the head `Vertex` of an `Edge` + - `@EdgeTo` maps to the tail `Vertex` of an `Edge` + - `@Graph` maps to an `Object` to a `Graph` +- Supports advanced operations + - ` T findVertexById(Object id, Class domainClass);` + - ` T findEdgeById(Object id, Class domainClass);` + - ` boolean isEmptyGraph(T object)` + - `long vertexCount()` + - `long edgeCount()` +- Supports [Spring Data custom query](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.query-methods.details) find operation, e.g., `findByAFieldAndBField` +- Supports any class type in domain class including collection and nested type. + + +### Spring Data Version Support +This repository only supports Spring Data 2.x. Version mapping between spring boot and spring-data-gremlin: + +| Spring boot version | spring-data-gremlin version | +|:-----------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| +| ![version](https://img.shields.io/badge/version-2.3.x-blue) | [![Maven Central](https://img.shields.io/maven-central/v/com.microsoft.spring.data.gremlin/spring-data-gremlin/2.3.svg)](https://search.maven.org/search?q=g:com.microsoft.spring.data.gremlin%20AND%20a:spring-data-gremlin%20AND%20v:2.3.*) | +| ![version](https://img.shields.io/badge/version-2.2.x-blue) | [![Maven Central](https://img.shields.io/maven-central/v/com.microsoft.spring.data.gremlin/spring-data-gremlin/2.2.svg)](https://search.maven.org/search?q=g:com.microsoft.spring.data.gremlin%20AND%20a:spring-data-gremlin%20AND%20v:2.2.*) | +| ![version](https://img.shields.io/badge/version-2.1.x-blue) | [![Maven Central](https://img.shields.io/maven-central/v/com.microsoft.spring.data.gremlin/spring-data-gremlin/2.1.svg)](https://search.maven.org/search?q=g:com.microsoft.spring.data.gremlin%20AND%20a:spring-data-gremlin%20AND%20v:2.1.*) | +| ![version](https://img.shields.io/badge/version-2.0.x-blue) | [![Maven Central](https://img.shields.io/maven-central/v/com.microsoft.spring.data.gremlin/spring-data-gremlin/2.0.svg)](https://search.maven.org/search?q=g:com.microsoft.spring.data.gremlin%20AND%20a:spring-data-gremlin%20AND%20v:2.0.*) | + +## Getting started + +### Add the dependency +`spring-data-gremlin` is published on Maven Central Repository. +If you are using Maven, add the following dependency. + +[//]: # ({x-version-update-start;com.azure:azure-spring-data-gremlin;current}) +```xml + + com.azure + azure-spring-data-gremlin + 2.3.1-beta.1 + +``` +[//]: # ({x-version-update-end}) + +### Setup Configuration +Setup `application.yml` file.(Use Azure Cosmos DB Graph as an example.) + +```yml +gremlin: + endpoint: url-of-endpoint + port: 443 + username: /dbs/your-db-name/colls/your-collection-name + password: your-password + telemetryAllowed: true # set false to disable telemetry + +``` + +### Define an entity +Define a simple Vertex entity with `@Vertex`. + + +```java +@Vertex +public class Person { + + @Id + private String id; + + private String name; + + private String age; + + public Person() { + + } + + public Person(String id, String name, String age) { + this.id = id; + this.name = name; + this.age = age; + } +} +``` + +Define a simple Edge entity with `@Edge`. + +```java +@Edge +public class Relation { + + @Id + private String id; + + private String name; + + @EdgeFrom + private Person personFrom; + + @EdgeTo + private Person personTo; + +} +``` +Define a simple Graph entity with `@Graph`. + +```java +@Graph +public class Network { + + @Id + private String id; + + public Network() { + this.edges = new ArrayList<>(); + this.vertexes = new ArrayList<>(); + } + + @EdgeSet + private List edges; + + @VertexSet + private List vertexes; + +} +``` + +### Create repositories +Extends GremlinRepository interface, which provides Spring Data repository support. + +```java +@Repository +public interface PersonRepository extends GremlinRepository { + + List findByName(String name); + +} +``` +`findByName` method is custom query method, it will find the person with the `name` property. + +### Create an application +Here create an application class with all the components + +```java +@SpringBootApplication +public class SampleApplication implements CommandLineRunner { + + @Autowired + private PersonRepository repository; + + public static void main(String[] args) { + SpringApplication.run(SampleApplication.class, args); + } + + public void run(String... var1) { + + final Person testUser = new Person("PERSON_ID", "PERSON_NAME", "PERSON_AGE"); + repository.deleteAll(); + repository.save(testUser); + } + +} +``` +Autowired UserRepository interface, then can do save, delete and find operations. + +## Examples +Please refer to [sample project](./azure-spring-boot-samples/azure-spring-data-sample-gremlin/) and [web sample project]((./azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service)). + +## Data / Telemetry + +This project collects usage data and sends it to Microsoft to help improve our products and services. Read our [privacy](https://privacy.microsoft.com/privacystatement) statement to learn more. + +## Contributing +## Troubleshooting +## Next steps diff --git a/sdk/spring/azure-spring-data-gremlin/config/gremlin-server-ci.properties b/sdk/spring/azure-spring-data-gremlin/config/gremlin-server-ci.properties new file mode 100644 index 000000000000..65cd869df34e --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/config/gremlin-server-ci.properties @@ -0,0 +1,2 @@ +gremlin.graph=org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph +gremlin.tinkergraph.vertexIdManager=ANY diff --git a/sdk/spring/azure-spring-data-gremlin/config/gremlin-server-ci.yaml b/sdk/spring/azure-spring-data-gremlin/config/gremlin-server-ci.yaml new file mode 100644 index 000000000000..d7947fba31b0 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/config/gremlin-server-ci.yaml @@ -0,0 +1,19 @@ +host: localhost +port: 8889 +gremlinPool: 8 +threadPoolWorker: 8 +scriptEvaluationTimeout: 30000 +channelizer: org.apache.tinkerpop.gremlin.server.channel.WebSocketChannelizer +graphs: { graph: conf/gremlin-server-ci.properties } + +scriptEngines: { + gremlin-groovy: { + plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {}, + org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {}, + org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]}, + org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/empty-sample.groovy]}}}} + +serializers: + - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0, config: { serializeResultToString: true }} +ssl: { enabled: false } + diff --git a/sdk/spring/azure-spring-data-gremlin/package/apache-tinkerpop-gremlin-server-minimal-3.3.4.tar.gz b/sdk/spring/azure-spring-data-gremlin/package/apache-tinkerpop-gremlin-server-minimal-3.3.4.tar.gz new file mode 100644 index 000000000000..e2b7f019f4b5 Binary files /dev/null and b/sdk/spring/azure-spring-data-gremlin/package/apache-tinkerpop-gremlin-server-minimal-3.3.4.tar.gz differ diff --git a/sdk/spring/azure-spring-data-gremlin/pom.xml b/sdk/spring/azure-spring-data-gremlin/pom.xml new file mode 100644 index 000000000000..9cb45194fefd --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/pom.xml @@ -0,0 +1,163 @@ + + + 4.0.0 + + + com.azure + azure-client-sdk-parent + 1.7.0 + ../../parents/azure-client-sdk-parent + + + com.azure + azure-spring-data-gremlin + 2.3.1-beta.1 + + Spring Data Gremlin + Gremlin support for Spring Data + https://github.com/Azure/azure-sdk-for-java + + + + + org.springframework + spring-core + 5.2.6.RELEASE + + + commons-logging + commons-logging + + + + + + + org.springframework + spring-context + 5.2.6.RELEASE + + + + + org.springframework + spring-tx + 5.2.6.RELEASE + + + + + org.springframework + spring-web + 5.2.6.RELEASE + + + + + org.springframework.data + spring-data-commons + 2.3.0.RELEASE + + + + + org.springframework.boot + spring-boot-starter-test + 2.3.0.RELEASE + test + + + + javax.annotation + javax.annotation-api + 1.3.2 + + + + + org.apache.tinkerpop + gremlin-driver + 3.2.4 + + + + + org.apache.commons + commons-lang3 + 3.8.1 + + + + + org.mockito + mockito-core + 3.0.0 + test + + + + + com.fasterxml.jackson.core + jackson-databind + 2.10.1 + + + + + com.google.code.findbugs + jsr305 + 3.0.2 + provided + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M3 + + + + + com.fasterxml.jackson.core:jackson-databind:[2.10.1] + org.apache.commons:commons-lang3:[3.8.1] + org.apache.tinkerpop:gremlin-driver:[3.2.4] + javax.annotation:javax.annotation-api:[1.3.2] + org.springframework:spring-context:[5.2.6.RELEASE] + org.springframework:spring-core:[5.2.6.RELEASE] + org.springframework:spring-tx:[5.2.6.RELEASE] + org.springframework:spring-web:[5.2.6.RELEASE] + org.springframework.data:spring-data-commons:[2.3.0.RELEASE] + org.springframework.boot:spring-boot-starter-test:[2.3.0.RELEASE] + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + + com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentProperty.java + + + + org.projectlombok + lombok + 1.18.12 + + + + + + + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/Edge.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/Edge.java new file mode 100644 index 000000000000..914d5c6852c3 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/Edge.java @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.annotation; + +import com.microsoft.spring.data.gremlin.common.Constants; +import org.springframework.data.annotation.Persistent; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies the class as edge in graph, with one optional label(String). + */ +@Persistent +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Edge { + /** + * The label(gremlin reserved) of given Edge, can add Edge by label. + * + * @return class name if not specify. + */ + String label() default Constants.DEFAULT_EDGE_LABEL; +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/EdgeFrom.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/EdgeFrom.java new file mode 100644 index 000000000000..6792d1dd8941 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/EdgeFrom.java @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.annotation; + +import org.springframework.data.annotation.Persistent; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies the field as source of one edge. + */ +@Persistent +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface EdgeFrom { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/EdgeSet.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/EdgeSet.java new file mode 100644 index 000000000000..c6cdece82683 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/EdgeSet.java @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.annotation; + +import org.springframework.data.annotation.Persistent; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies the field as EdgeSet of graph. + */ +@Persistent +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface EdgeSet { +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/EdgeTo.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/EdgeTo.java new file mode 100644 index 000000000000..7d54df8071cf --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/EdgeTo.java @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.annotation; + +import org.springframework.data.annotation.Persistent; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies the field as target of one edge. + */ +@Persistent +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface EdgeTo { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/GeneratedValue.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/GeneratedValue.java new file mode 100644 index 000000000000..9deb31eead21 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/GeneratedValue.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies that a field's value is to be generated (and not explicitly specified in the domain object). + * This annotation should only be used on an id field. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface GeneratedValue { + +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/Graph.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/Graph.java new file mode 100644 index 000000000000..1d0a56f0e303 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/Graph.java @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.annotation; + +import com.microsoft.spring.data.gremlin.common.Constants; +import org.springframework.data.annotation.Persistent; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies the domain class as graph, with one optional collection(String). + */ +@Persistent +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface Graph { + /** + * The collection of given Graph. + * + * @return class name if not specify. + */ + String collection() default Constants.DEFAULT_COLLECTION_NAME; +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/Vertex.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/Vertex.java new file mode 100644 index 000000000000..53a5919df7e3 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/Vertex.java @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.annotation; + +import com.microsoft.spring.data.gremlin.common.Constants; +import org.springframework.data.annotation.Persistent; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies the class as vertex in graph, with one optional label(String). + */ +@Persistent +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Vertex { + /** + * The label(gremlin reserved) of given Vertex, can add Vertex by label. + * + * @return class name if not specify. + */ + String label() default Constants.DEFAULT_VERTEX_LABEL; +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/VertexSet.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/VertexSet.java new file mode 100644 index 000000000000..ce48a8303310 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/annotation/VertexSet.java @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.annotation; + +import org.springframework.data.annotation.Persistent; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies the field as VertexSet of graph. + */ +@Persistent +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface VertexSet { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/Constants.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/Constants.java new file mode 100644 index 000000000000..bb3afdf56520 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/Constants.java @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +public final class Constants { + + public static final String PROPERTY_ID = "id"; + public static final String PROPERTY_LABEL = "label"; + public static final String PROPERTY_TYPE = "type"; + public static final String PROPERTY_VALUE = "value"; + public static final String PROPERTY_PROPERTIES = "properties"; + public static final String PROPERTY_INV = "inV"; + public static final String PROPERTY_OUTV = "outV"; + + public static final String RESULT_TYPE_VERTEX = "vertex"; + public static final String RESULT_TYPE_EDGE = "edge"; + + public static final String DEFAULT_VERTEX_LABEL = ""; + public static final String DEFAULT_EDGE_LABEL = ""; + public static final String DEFAULT_COLLECTION_NAME = ""; + public static final int DEFAULT_ENDPOINT_PORT = 443; + public static final String DEFAULT_REPOSITORY_IMPLEMENT_POSTFIX = "Impl"; + + public static final String GREMLIN_MODULE_NAME = "Gremlin"; + public static final String GREMLIN_MODULE_PREFIX = "gremlin"; + public static final String GREMLIN_MAPPING_CONTEXT = "gremlinMappingContext"; + + public static final String GREMLIN_PRIMITIVE_GRAPH = "g"; + public static final String GREMLIN_PRIMITIVE_INVOKE = "."; + public static final String GREMLIN_PRIMITIVE_DROP = "drop()"; + + public static final String GREMLIN_PRIMITIVE_EDGE_ALL = "E()"; + + public static final String GREMLIN_PRIMITIVE_VERTEX_ALL = "V()"; + + public static final String GREMLIN_PRIMITIVE_HAS_STRING = "has('%s', '%s')"; + public static final String GREMLIN_PRIMITIVE_HAS_NUMBER = "has('%s', %d)"; + public static final String GREMLIN_PRIMITIVE_HAS_BOOLEAN = "has('%s', %b)"; + + public static final String GREMLIN_PRIMITIVE_PROPERTY_STRING = "property('%s', '%s')"; + public static final String GREMLIN_PRIMITIVE_PROPERTY_NUMBER = "property('%s', %d)"; + public static final String GREMLIN_PRIMITIVE_PROPERTY_BOOLEAN = "property('%s', %b)"; + + public static final String GREMLIN_PRIMITIVE_AND = "and()"; + public static final String GREMLIN_PRIMITIVE_OR = "or()"; + public static final String GREMLIN_PRIMITIVE_WHERE = "where(%s)"; + + public static final String GREMLIN_QUERY_BARRIER = "barrier"; + + public static final String GREMLIN_PRIMITIVE_VALUES = "values('%s')"; + public static final String GREMLIN_PRIMITIVE_IS = "is(%s)"; + public static final String GREMLIN_PRIMITIVE_GT = "gt(%d)"; + public static final String GREMLIN_PRIMITIVE_LT = "lt(%d)"; + public static final String GREMLIN_PRIMITIVE_BETWEEN = "between(%d, %d)"; + + public static final String GREMLIN_PRIMITIVE_IS_GT = String.format(GREMLIN_PRIMITIVE_IS, GREMLIN_PRIMITIVE_GT); + public static final String GREMLIN_PRIMITIVE_IS_LT = String.format(GREMLIN_PRIMITIVE_IS, GREMLIN_PRIMITIVE_LT); + public static final String GREMLIN_PRIMITIVE_IS_BETWEEN = String.format( + GREMLIN_PRIMITIVE_IS, + GREMLIN_PRIMITIVE_BETWEEN + ); + + public static final String GREMLIN_SCRIPT_EDGE_ALL = String.join(GREMLIN_PRIMITIVE_INVOKE, + GREMLIN_PRIMITIVE_GRAPH, + GREMLIN_PRIMITIVE_EDGE_ALL + ); + + public static final String GREMLIN_SCRIPT_VERTEX_ALL = String.join(GREMLIN_PRIMITIVE_INVOKE, + GREMLIN_PRIMITIVE_GRAPH, + GREMLIN_PRIMITIVE_VERTEX_ALL + ); + + public static final String GREMLIN_SCRIPT_EDGE_DROP_ALL = String.join(GREMLIN_PRIMITIVE_INVOKE, + GREMLIN_PRIMITIVE_GRAPH, + GREMLIN_PRIMITIVE_EDGE_ALL, + GREMLIN_PRIMITIVE_DROP + ); + + public static final String GREMLIN_SCRIPT_VERTEX_DROP_ALL = String.join(GREMLIN_PRIMITIVE_INVOKE, + GREMLIN_PRIMITIVE_GRAPH, + GREMLIN_PRIMITIVE_VERTEX_ALL, + GREMLIN_PRIMITIVE_DROP + ); + + public static final String GREMLIN_PROPERTY_CLASSNAME = "_classname"; + + public static final int DEFAULT_MAX_CONTENT_LENGTH = 65536; + +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinConfig.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinConfig.java new file mode 100644 index 000000000000..9a08642d50bd --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinConfig.java @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +import org.apache.tinkerpop.gremlin.driver.ser.Serializers; + +import java.beans.ConstructorProperties; + +public final class GremlinConfig { + + private String endpoint; + + private int port; + + private String username; + + private String password; + + private boolean sslEnabled; + + private boolean telemetryAllowed; + + private String serializer; + + private int maxContentLength; + + @ConstructorProperties({"endpoint", "port", "username", "password", "sslEnabled", "telemetryAllowed", "serializer", + "maxContentLength"}) + private GremlinConfig(String endpoint, int port, String username, String password, boolean sslEnabled, + boolean telemetryAllowed, String serializer, int maxContentLength) { + this.endpoint = endpoint; + this.port = port; + this.username = username; + this.password = password; + this.sslEnabled = sslEnabled; + this.telemetryAllowed = telemetryAllowed; + this.serializer = serializer; + this.maxContentLength = maxContentLength; + } + + public static GremlinConfig.GremlinConfigBuilder defaultBuilder() { + return new GremlinConfig.GremlinConfigBuilder(); + } + + public static GremlinConfigBuilder builder(String endpoint, String username, String password) { + return defaultBuilder() + .endpoint(endpoint) + .username(username) + .password(password) + .port(Constants.DEFAULT_ENDPOINT_PORT) + .sslEnabled(true) + .serializer(Serializers.GRAPHSON.toString()) + .telemetryAllowed(true); + } + + public static class GremlinConfigBuilder { + private String endpoint; + private int port; + private String username; + private String password; + private boolean sslEnabled; + private boolean telemetryAllowed; + private String serializer; + private int maxContentLength; + + public GremlinConfig.GremlinConfigBuilder endpoint(String endpoint) { + this.endpoint = endpoint; + return this; + } + + public GremlinConfig.GremlinConfigBuilder port(int port) { + this.port = port; + return this; + } + + public GremlinConfig.GremlinConfigBuilder username(String username) { + this.username = username; + return this; + } + + public GremlinConfig.GremlinConfigBuilder password(String password) { + this.password = password; + return this; + } + + public GremlinConfig.GremlinConfigBuilder sslEnabled(boolean sslEnabled) { + this.sslEnabled = sslEnabled; + return this; + } + + public GremlinConfig.GremlinConfigBuilder telemetryAllowed(boolean telemetryAllowed) { + this.telemetryAllowed = telemetryAllowed; + return this; + } + + public GremlinConfig.GremlinConfigBuilder serializer(String serializer) { + this.serializer = serializer; + return this; + } + + public GremlinConfig.GremlinConfigBuilder maxContentLength(int maxContentLength) { + this.maxContentLength = maxContentLength; + return this; + } + + public GremlinConfig build() { + return new GremlinConfig(this.endpoint, this.port, this.username, this.password, this.sslEnabled, this.telemetryAllowed, this.serializer, this.maxContentLength); + } + + public String toString() { + return "GremlinConfig.GremlinConfigBuilder(endpoint=" + this.endpoint + ", port=" + this.port + ", username=" + this.username + ", password=" + this.password + ", sslEnabled=" + this.sslEnabled + ", telemetryAllowed=" + this.telemetryAllowed + ", serializer=" + this.serializer + ", maxContentLength=" + this.maxContentLength + ")"; + } + } + + public String getEndpoint() { + return endpoint; + } + + public int getPort() { + return port; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public boolean isSslEnabled() { + return sslEnabled; + } + + public boolean isTelemetryAllowed() { + return telemetryAllowed; + } + + public String getSerializer() { + return serializer; + } + + public int getMaxContentLength() { + return maxContentLength; + } + + public void setPort(int port) { + this.port = port; + } + + public void setMaxContentLength(int maxContentLength) { + this.maxContentLength = maxContentLength; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinEntityType.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinEntityType.java new file mode 100644 index 000000000000..744826a6ddec --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinEntityType.java @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceGraph; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import org.springframework.lang.NonNull; + +import java.util.function.Supplier; + +public enum GremlinEntityType { + + VERTEX(GremlinSourceVertex::new), + EDGE(GremlinSourceEdge::new), + GRAPH(GremlinSourceGraph::new); + + private Supplier> creator; + + GremlinEntityType(@NonNull Supplier> creator) { + this.creator = creator; + } + + public GremlinSource createGremlinSource() { + return this.creator.get(); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinFactory.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinFactory.java new file mode 100644 index 000000000000..a1436808c5b2 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinFactory.java @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +import com.microsoft.spring.data.gremlin.exception.GremlinIllegalConfigurationException; +import com.microsoft.spring.data.gremlin.telemetry.TelemetrySender; +import org.apache.tinkerpop.gremlin.driver.Client; +import org.apache.tinkerpop.gremlin.driver.Cluster; +import org.apache.tinkerpop.gremlin.driver.ser.Serializers; +import org.springframework.lang.NonNull; + +import javax.annotation.PostConstruct; + + +public class GremlinFactory { + + private Cluster gremlinCluster; + + private GremlinConfig gremlinConfig; + + public GremlinFactory(@NonNull GremlinConfig gremlinConfig) { + final int port = gremlinConfig.getPort(); + if (port <= 0 || port > 65535) { + gremlinConfig.setPort(Constants.DEFAULT_ENDPOINT_PORT); + } + + final int maxContentLength = gremlinConfig.getMaxContentLength(); + if (maxContentLength <= 0) { + gremlinConfig.setMaxContentLength(Constants.DEFAULT_MAX_CONTENT_LENGTH); + } + + this.gremlinConfig = gremlinConfig; + } + + private Cluster createGremlinCluster() throws GremlinIllegalConfigurationException { + final Cluster cluster; + + try { + cluster = Cluster.build(this.gremlinConfig.getEndpoint()) + .serializer(Serializers.valueOf(this.gremlinConfig.getSerializer()).simpleInstance()) + .credentials(this.gremlinConfig.getUsername(), this.gremlinConfig.getPassword()) + .enableSsl(this.gremlinConfig.isSslEnabled()) + .maxContentLength(this.gremlinConfig.getMaxContentLength()) + .port(this.gremlinConfig.getPort()) + .create(); + } catch (IllegalArgumentException e) { + throw new GremlinIllegalConfigurationException("Invalid configuration of Gremlin", e); + } + + return cluster; + } + + public Client getGremlinClient() { + + if (this.gremlinCluster == null) { + this.gremlinCluster = this.createGremlinCluster(); + } + + return this.gremlinCluster.connect(); + } + + @PostConstruct + private void sendTelemetry() { + + if (gremlinConfig.isTelemetryAllowed()) { + final TelemetrySender sender = new TelemetrySender(); + + sender.send(this.getClass().getSimpleName()); + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinUtils.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinUtils.java new file mode 100644 index 000000000000..efbbc066b73a --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/common/GremlinUtils.java @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +import com.microsoft.spring.data.gremlin.annotation.GeneratedValue; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.exception.GremlinIllegalConfigurationException; +import com.microsoft.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import com.microsoft.spring.data.gremlin.repository.support.GremlinEntityInformation; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.tinkerpop.shaded.jackson.databind.MapperFeature; +import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; +import org.springframework.data.annotation.Id; +import org.springframework.lang.NonNull; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class GremlinUtils { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + static { + MAPPER.configure(MapperFeature.AUTO_DETECT_FIELDS, false); + } + + public static ObjectMapper getObjectMapper() { + return MAPPER; + } + + public static T createInstance(@NonNull Class type) { + final T instance; + + try { + instance = type.newInstance(); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("can not access type constructor", e); + } catch (InstantiationException e) { + throw new IllegalArgumentException("failed to create instance of given type", e); + } + + return instance; + } + + public static Field getIdField(@NonNull Class domainClass) { + final Field idField; + final List idFields = FieldUtils.getFieldsListWithAnnotation(domainClass, Id.class); + final List generatedValueFields = + FieldUtils.getFieldsListWithAnnotation(domainClass, GeneratedValue.class); + + if (generatedValueFields.size() > 1) { + throw new GremlinIllegalConfigurationException("Only one field, the id field, can have the optional " + + "@GeneratedValue annotation!"); + } + + if (idFields.isEmpty()) { + idField = ReflectionUtils.findField(domainClass, Constants.PROPERTY_ID); + } else if (idFields.size() == 1) { + idField = idFields.get(0); + } else { + throw new GremlinInvalidEntityIdFieldException("only one @Id field is allowed"); + } + + if (idField == null) { + throw new GremlinInvalidEntityIdFieldException("no field named id in class"); + } else if (idField.getType() != String.class + && idField.getType() != Long.class && idField.getType() != Integer.class) { + throw new GremlinInvalidEntityIdFieldException("the type of @Id/id field should be String/Integer/Long"); + } + + if (generatedValueFields.size() == 1 && !generatedValueFields.get(0).equals(idField)) { + throw new GremlinIllegalConfigurationException("Only the id field can have the optional " + + "@GeneratedValue annotation!"); + } + + return idField; + } + + public static long timeToMilliSeconds(@NonNull Object time) { + if (time instanceof Date) { + return ((Date) time).getTime(); + } else { + throw new UnsupportedOperationException("Unsupported time type"); + } + } + + public static long toPrimitiveLong(@NonNull Object object) { + if (object instanceof Date) { + return timeToMilliSeconds(object); + } else if (object instanceof Integer) { + return (long) (int) object; + } else if (object instanceof Long) { + return (long) object; + } else { + throw new UnsupportedOperationException("Unsupported object type to long"); + } + } + + public static GremlinSource toGremlinSource(@NonNull Class domainClass) { + return new GremlinEntityInformation<>(domainClass).createGremlinSource(); + } + + public static List> toParallelQueryList(@NonNull List queries) { + final List> parallelQueries = new ArrayList<>(); + List parallelQuery = new ArrayList<>(); + + for (final String query : queries) { + if (query.equals(Constants.GREMLIN_QUERY_BARRIER)) { + parallelQueries.add(parallelQuery); + parallelQuery = new ArrayList<>(); + } else { + parallelQuery.add(query); + } + } + + parallelQueries.add(parallelQuery); + + return parallelQueries; + } + + public static Class toEntityClass(@NonNull String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + throw new GremlinUnexpectedSourceTypeException("failed to retrieve class: " + className, e); + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/AbstractGremlinConfiguration.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/AbstractGremlinConfiguration.java new file mode 100644 index 000000000000..04090613bfcd --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/AbstractGremlinConfiguration.java @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.config; + +import com.microsoft.spring.data.gremlin.common.GremlinConfig; +import com.microsoft.spring.data.gremlin.common.GremlinFactory; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.query.GremlinTemplate; +import org.springframework.context.annotation.Bean; + +public abstract class AbstractGremlinConfiguration extends GremlinConfigurationSupport { + + public abstract GremlinConfig getGremlinConfig(); + + @Bean + public GremlinFactory gremlinFactory() { + return new GremlinFactory(getGremlinConfig()); + } + + @Bean + public MappingGremlinConverter mappingGremlinConverter() throws ClassNotFoundException { + return new MappingGremlinConverter(gremlinMappingContext()); + } + + @Bean + public GremlinTemplate gremlinTemplate(GremlinFactory factory) throws ClassNotFoundException { + return new GremlinTemplate(factory, mappingGremlinConverter()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/GremlinConfigurationSupport.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/GremlinConfigurationSupport.java new file mode 100644 index 000000000000..cf52ba657cba --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/config/GremlinConfigurationSupport.java @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.config; + +import com.microsoft.spring.data.gremlin.mapping.GremlinMappingContext; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.data.annotation.Persistent; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public abstract class GremlinConfigurationSupport { + + protected Collection getMappingBasePackages() { + final Package basePackage = this.getClass().getPackage(); + + return Collections.singleton(basePackage == null ? null : basePackage.getName()); + } + + protected Set> scanEntities(@NonNull String basePackage) throws ClassNotFoundException { + if (!StringUtils.hasText(basePackage)) { + return Collections.emptySet(); + } + + final Set> entitySet = new HashSet<>(); + final ClassPathScanningCandidateComponentProvider provider = + new ClassPathScanningCandidateComponentProvider(false); + + provider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); + + for (final BeanDefinition candidate : provider.findCandidateComponents(basePackage)) { + final String className = candidate.getBeanClassName(); + Assert.notNull(GremlinConfigurationSupport.class.getClassLoader(), "Class loader cannot be null"); + + entitySet.add(ClassUtils.forName(className, GremlinConfigurationSupport.class.getClassLoader())); + } + + return entitySet; + } + + protected Set> getInitialEntitySet() throws ClassNotFoundException { + final Set> entitySet = new HashSet<>(); + + for (final String basePackage : this.getMappingBasePackages()) { + entitySet.addAll(this.scanEntities(basePackage)); + } + + return entitySet; + } + + @Bean + public GremlinMappingContext gremlinMappingContext() throws ClassNotFoundException { + final GremlinMappingContext context = new GremlinMappingContext(); + + context.setInitialEntitySet(this.getInitialEntitySet()); + + return context; + } + +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/MappingGremlinConverter.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/MappingGremlinConverter.java new file mode 100644 index 000000000000..8ccfb53e6397 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/MappingGremlinConverter.java @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion; + +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentEntity; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentProperty; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.data.convert.EntityConverter; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PersistentPropertyAccessor; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mapping.model.ConvertingPropertyAccessor; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +public class MappingGremlinConverter + implements EntityConverter, GremlinPersistentProperty, Object, GremlinSource>, + ApplicationContextAware { + + protected final MappingContext, GremlinPersistentProperty> mappingContext; + protected GenericConversionService conversionService; + private ApplicationContext applicationContext; + + public MappingGremlinConverter(MappingContext, GremlinPersistentProperty> + context) { + this.mappingContext = context; + this.conversionService = new GenericConversionService(); + } + + public ApplicationContext getApplicationContext() { + return this.applicationContext; + } + + @Override + public MappingContext, GremlinPersistentProperty> getMappingContext() { + return this.mappingContext; + } + + @Override + public void setApplicationContext(ApplicationContext context) { + this.applicationContext = context; + } + + @Override + public ConversionService getConversionService() { + return this.conversionService; + } + + @Override + public T read(Class domainClass, @NonNull GremlinSource source) { + @SuppressWarnings("unchecked") final GremlinSource gremlinSource = (GremlinSource) source; + + return gremlinSource.doGremlinSourceRead(domainClass, this); + } + + @Override + public void write(@NonNull Object domain, @NonNull GremlinSource source) { + source.doGremlinSourceWrite(domain, this); + } + + public ConvertingPropertyAccessor getPropertyAccessor(@NonNull T domain) { + final GremlinPersistentEntity persistentEntity = this.getPersistentEntity(domain.getClass()); + Assert.notNull(persistentEntity, "persistentEntity should not be null"); + + final PersistentPropertyAccessor accessor = persistentEntity.getPropertyAccessor(domain); + + return new ConvertingPropertyAccessor<>(accessor, this.conversionService); + } + + public GremlinPersistentEntity getPersistentEntity(@NonNull Class domainClass) { + return mappingContext.getPersistentEntity(domainClass); + } + + private String getIdFieldName(@NonNull Object domain) { + return GremlinUtils.getIdField(domain.getClass()).getName(); + } + + private Object getFieldValue(@NonNull T domain, @NonNull String fieldName) { + final ConvertingPropertyAccessor accessor = this.getPropertyAccessor(domain); + final GremlinPersistentEntity persistentEntity = this.getPersistentEntity(domain.getClass()); + final PersistentProperty property = persistentEntity.getPersistentProperty(fieldName); + + return property != null ? accessor.getProperty(property) : null; + } + + public Object getIdFieldValue(@NonNull Object domain) { + return this.getFieldValue(domain, this.getIdFieldName(domain)); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/AbstractGremlinResultReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/AbstractGremlinResultReader.java new file mode 100644 index 000000000000..9b57d88161f7 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/AbstractGremlinResultReader.java @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.result; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Map; + +// TODO: seems only for Vertex. +public abstract class AbstractGremlinResultReader implements GremlinResultsReader { + + /** + * properties's organization is a little complicated. + *

+ * properties is {@link LinkedHashMap}<K, V>
+ * K is {@link String}
+ * V is {@link ArrayList}<T>
+ * T is {@link LinkedHashMap}<{@link String}, {@link String}> + */ + private Object readProperty(@NonNull Object value) { + Assert.isInstanceOf(ArrayList.class, value, "should be instance of ArrayList"); + + @SuppressWarnings("unchecked") final ArrayList> mapList + = (ArrayList>) value; + + Assert.isTrue(mapList.size() == 1, "should be only 1 element in ArrayList"); + + return mapList.get(0).get(Constants.PROPERTY_VALUE); + } + + protected void readResultProperties(@NonNull Map properties, @NonNull GremlinSource source) { + source.getProperties().clear(); + properties.forEach((key, value) -> source.setProperty(key, this.readProperty(value))); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultEdgeReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultEdgeReader.java new file mode 100644 index 000000000000..be85f5d503ac --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultEdgeReader.java @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.result; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import org.apache.tinkerpop.gremlin.driver.Result; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +import java.util.List; +import java.util.Map; + +public class GremlinResultEdgeReader extends AbstractGremlinResultReader { + + private void readProperties(@NonNull GremlinSource source, @Nullable Map properties) { + Assert.notNull(source, "source should not be null"); + if (properties != null) { + properties.forEach(source::setProperty); + } + } + + private void validate(List results, GremlinSource source) { + Assert.notNull(results, "Results should not be null."); + Assert.notNull(source, "GremlinSource should not be null."); + Assert.isTrue(results.size() == 1, "Edge should contain only one result."); + + final Result result = results.get(0); + + Assert.isInstanceOf(Map.class, result.getObject(), "should be one instance of Map"); + + @SuppressWarnings("unchecked") final Map map = (Map) result.getObject(); + + Assert.isTrue(map.containsKey(Constants.PROPERTY_ID), "should contain id property"); + Assert.isTrue(map.containsKey(Constants.PROPERTY_LABEL), "should contain label property"); + Assert.isTrue(map.containsKey(Constants.PROPERTY_TYPE), "should contain type property"); + Assert.isTrue(map.containsKey(Constants.PROPERTY_INV), "should contain inV property"); + Assert.isTrue(map.containsKey(Constants.PROPERTY_OUTV), "should contain outV property"); + Assert.isTrue(map.get(Constants.PROPERTY_TYPE).equals(Constants.RESULT_TYPE_EDGE), "must be vertex type"); + } + + @Override + @SuppressWarnings("unchecked") + public void read(@NonNull List results, @NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceEdge)) { + throw new GremlinUnexpectedSourceTypeException("Should be instance of GremlinSourceEdge"); + } + + validate(results, source); + + final GremlinSourceEdge sourceEdge = (GremlinSourceEdge) source; + final Map map = (Map) results.get(0).getObject(); + + this.readProperties(source, (Map) map.get(Constants.PROPERTY_PROPERTIES)); + + final String className = source.getProperties().get(Constants.GREMLIN_PROPERTY_CLASSNAME).toString(); + + sourceEdge.setIdField(GremlinUtils.getIdField(GremlinUtils.toEntityClass(className))); + sourceEdge.setId(map.get(Constants.PROPERTY_ID)); + sourceEdge.setLabel(map.get(Constants.PROPERTY_LABEL).toString()); + sourceEdge.setVertexIdFrom(map.get(Constants.PROPERTY_OUTV)); + sourceEdge.setVertexIdTo(map.get(Constants.PROPERTY_INV)); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultVertexReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultVertexReader.java new file mode 100644 index 000000000000..60d047041462 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultVertexReader.java @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.result; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import org.apache.tinkerpop.gremlin.driver.Result; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.util.List; +import java.util.Map; + +public class GremlinResultVertexReader extends AbstractGremlinResultReader { + + private void validate(List results, GremlinSource source) { + Assert.notNull(results, "Results should not be null."); + Assert.notNull(source, "GremlinSource should not be null."); + Assert.isTrue(results.size() == 1, "Vertex should contain only one result."); + + final Result result = results.get(0); + + Assert.isInstanceOf(Map.class, result.getObject(), "should be one instance of Map"); + + @SuppressWarnings("unchecked") final Map map = (Map) result.getObject(); + + Assert.isTrue(map.containsKey(Constants.PROPERTY_ID), "should contain id property"); + Assert.isTrue(map.containsKey(Constants.PROPERTY_LABEL), "should contain label property"); + Assert.isTrue(map.containsKey(Constants.PROPERTY_TYPE), "should contain type property"); + Assert.isTrue(map.containsKey(Constants.PROPERTY_PROPERTIES), "should contain properties property"); + Assert.isTrue(map.get(Constants.PROPERTY_TYPE).equals(Constants.RESULT_TYPE_VERTEX), "must be vertex type"); + + Assert.isInstanceOf(Map.class, map.get(Constants.PROPERTY_PROPERTIES), "should be one instance of Map"); + } + + @Override + @SuppressWarnings("unchecked") + public void read(@NonNull List results, @NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceVertex)) { + throw new GremlinUnexpectedSourceTypeException("Should be instance of GremlinSourceVertex"); + } + + validate(results, source); + + final Map map = (Map) results.get(0).getObject(); + final Map properties = (Map) map.get(Constants.PROPERTY_PROPERTIES); + + super.readResultProperties(properties, source); + + final String className = source.getProperties().get(Constants.GREMLIN_PROPERTY_CLASSNAME).toString(); + + source.setIdField(GremlinUtils.getIdField(GremlinUtils.toEntityClass(className))); + source.setId(map.get(Constants.PROPERTY_ID)); + source.setLabel(map.get(Constants.PROPERTY_LABEL).toString()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsGraphReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsGraphReader.java new file mode 100644 index 000000000000..93ec56f8c92c --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsGraphReader.java @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.result; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceGraph; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import org.apache.tinkerpop.gremlin.driver.Result; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.util.List; +import java.util.Map; + +import static java.util.Collections.singletonList; + +public class GremlinResultsGraphReader extends AbstractGremlinResultReader { + + private final GremlinResultVertexReader vertexResultReader; + private final GremlinResultEdgeReader edgeResultReader; + + public GremlinResultsGraphReader() { + vertexResultReader = new GremlinResultVertexReader(); + edgeResultReader = new GremlinResultEdgeReader(); + } + + @Override + public void read(@NonNull List results, @NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceGraph)) { + throw new GremlinUnexpectedSourceTypeException("Should be instance of GremlinSourceGraph"); + } + + final GremlinSourceGraph graphSource = (GremlinSourceGraph) source; + + graphSource.getVertexSet().clear(); + graphSource.getEdgeSet().clear(); + + results.stream().map(this::processResult).forEach(graphSource::addGremlinSource); + } + + private GremlinSource processResult(Result result) { + final GremlinSource source; + final Object obj = result.getObject(); + + Assert.isInstanceOf(Map.class, obj, "should be an instance of Map"); + @SuppressWarnings("unchecked") final Map map = (Map) result.getObject(); + + Assert.isTrue(map.containsKey(Constants.PROPERTY_TYPE), "should contain a type property"); + final String type = (String) map.get(Constants.PROPERTY_TYPE); + + switch (type) { + case Constants.RESULT_TYPE_VERTEX: + source = new GremlinSourceVertex<>(); + vertexResultReader.read(singletonList(result), source); + break; + case Constants.RESULT_TYPE_EDGE: + source = new GremlinSourceEdge<>(); + edgeResultReader.read(singletonList(result), source); + break; + default: + throw new GremlinUnexpectedEntityTypeException("Unexpected result type: " + type); + } + + return source; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsReader.java new file mode 100644 index 000000000000..c6def579ccaf --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsReader.java @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.result; + +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import org.apache.tinkerpop.gremlin.driver.Result; + +import java.util.List; + +public interface GremlinResultsReader { + /** + * Read the Gremlin returned Results to GremlinSource. + * + * @param The type of the source domain. + * @param results Results retrieved from the Gremlin server. + * @param source The GremlinSource of the results. + */ + void read(List results, GremlinSource source); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteral.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteral.java new file mode 100644 index 000000000000..1a54eb107eb3 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteral.java @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.script; + +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; + +import java.util.List; + +/** + * Provider interface to generate different query to gremlin server. + * The scripts return queries in steps, organized by List. + */ +public interface GremlinScriptLiteral { + /** + * Generate the insert query from source (Vertex, Edge or Graph). + * @param The type of domain. + * @param source the gremlin source + * @return insert script + */ + List generateInsertScript(GremlinSource source); + + /** + * Generate the deleteAll query from source (Vertex, Edge or Graph). + * + * @return deleteAll script + */ + List generateDeleteAllScript(); + + /** + * Generate the deleteAll By Domain Class query from source (Vertex, Edge or Graph). + * @param The type of domain. + * @param source the gremlin source + * @return deleteAllByClass script + */ + List generateDeleteAllByClassScript(GremlinSource source); + + /** + * Generate the findById query from source (Vertex, Edge). + * + * @param The type of domain. + * @param source the gremlin source + * @return findById script script + */ + List generateFindByIdScript(GremlinSource source); + + /** + * Generate the update query from source (Vertex, Edge or Graph). + * + * @param The type of domain. + * @param source the gremlin source + * @return update script + */ + List generateUpdateScript(GremlinSource source); + + /** + * Generate the findAll query from source (Vertex, Edge or Graph). + * + * @param The type of domain. + * @param source the gremlin source + * @return findAll script + */ + List generateFindAllScript(GremlinSource source); + + /** + * Generate the DeleteById query from source (Vertex, Edge or Graph). + * + * @param The type of domain. + * @param source the gremlin source + * @return deleteById script + */ + List generateDeleteByIdScript(GremlinSource source); + + /** + * Generate the Count query from Source (Vertex, Edge) + * + * @param The type of domain. + * @param source the gremlin source + * @return count query + */ + List generateCountScript(GremlinSource source); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralEdge.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralEdge.java new file mode 100644 index 000000000000..29d76ec72f45 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralEdge.java @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.script; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class GremlinScriptLiteralEdge implements GremlinScriptLiteral { + + private static final String FROM_ALIAS = "from"; + private static final String TO_ALIAS = "to"; + + private String generateEdgeDirection(@NonNull String from, @NonNull String to) { + Assert.notNull(from, "from should not be null"); + Assert.notNull(to, "to should not be null"); + return String.format("from('%s').to('%s')", from, to); + } + + @Override + public List generateInsertScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceEdge)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge"); + } + + final GremlinSourceEdge sourceEdge = (GremlinSourceEdge) source; + final List scriptList = new ArrayList<>(); + + scriptList.add(Constants.GREMLIN_PRIMITIVE_GRAPH); // g + scriptList.add(GremlinScriptLiteralHelper.generateEntityWithRequiredId(sourceEdge.getVertexIdFrom(), GremlinEntityType.VERTEX)); // V(id) + scriptList.add(GremlinScriptLiteralHelper.generateAsWithAlias(FROM_ALIAS)); // from('from') + scriptList.add(GremlinScriptLiteralHelper.generateEntityWithRequiredId(sourceEdge.getVertexIdTo(), GremlinEntityType.VERTEX)); // V(id) + scriptList.add(GremlinScriptLiteralHelper.generateAsWithAlias(TO_ALIAS)); // to('to') + scriptList.add(GremlinScriptLiteralHelper.generateAddEntityWithLabel(sourceEdge.getLabel(), GremlinEntityType.EDGE)); // addE(label) + scriptList.add(generateEdgeDirection(FROM_ALIAS, TO_ALIAS)); // from('from').to('to') + + source.getId().ifPresent(id -> scriptList.add(GremlinScriptLiteralHelper.generatePropertyWithRequiredId(id))); // property(id, xxx) + + scriptList.addAll(GremlinScriptLiteralHelper.generateProperties(source.getProperties())); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateDeleteAllScript() { + return Collections.singletonList(Constants.GREMLIN_SCRIPT_EDGE_DROP_ALL); + } + + @Override + public List generateDeleteAllByClassScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceEdge)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge"); + } + + final List scriptList = Arrays.asList( + Constants.GREMLIN_PRIMITIVE_GRAPH, // g + Constants.GREMLIN_PRIMITIVE_EDGE_ALL, // E() + GremlinScriptLiteralHelper.generateHasLabel(source.getLabel()), // has(label, 'label') + Constants.GREMLIN_PRIMITIVE_DROP // drop() + ); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateFindByIdScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceEdge)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge"); + } + + Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id."); + + final List scriptList = Arrays.asList( + Constants.GREMLIN_PRIMITIVE_GRAPH, // g + Constants.GREMLIN_PRIMITIVE_EDGE_ALL, // E() + GremlinScriptLiteralHelper.generateHasId(source.getId().get(), source.getIdField()) // hasId(xxx) + ); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateUpdateScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceEdge)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge"); + } + + final List scriptList = new ArrayList<>(); + + Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id."); + + scriptList.add(Constants.GREMLIN_PRIMITIVE_GRAPH); // g + scriptList.add(GremlinScriptLiteralHelper.generateEntityWithRequiredId(source.getId().get(), GremlinEntityType.EDGE)); // E(id) + + scriptList.addAll(GremlinScriptLiteralHelper.generateProperties(source.getProperties())); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateFindAllScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceEdge)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge"); + } + + final String className = source.getProperties().get(Constants.GREMLIN_PROPERTY_CLASSNAME).toString(); + Assert.notNull(className, "GremlinSource should contain predefined className"); + + final List scriptList = Arrays.asList( + Constants.GREMLIN_PRIMITIVE_GRAPH, // g + Constants.GREMLIN_PRIMITIVE_EDGE_ALL, // E() + GremlinScriptLiteralHelper.generateHasLabel(source.getLabel()), // has(label, 'label') + GremlinScriptLiteralHelper.generateHas(Constants.GREMLIN_PROPERTY_CLASSNAME, className) // has(_classname, 'xxxxxx') + ); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateDeleteByIdScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceEdge)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge"); + } + + Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id."); + + final List scriptList = Arrays.asList( + Constants.GREMLIN_PRIMITIVE_GRAPH, // g + Constants.GREMLIN_PRIMITIVE_EDGE_ALL, // E() + GremlinScriptLiteralHelper.generateHasId(source.getId().get(), source.getIdField()), // hasId(xxx) + Constants.GREMLIN_PRIMITIVE_DROP // drop() + ); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateCountScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceEdge)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge"); + } + + return Collections.singletonList(Constants.GREMLIN_SCRIPT_EDGE_ALL); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralGraph.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralGraph.java new file mode 100644 index 000000000000..033cf2f0204c --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralGraph.java @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.script; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceGraph; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +public class GremlinScriptLiteralGraph implements GremlinScriptLiteral { + + private final GremlinScriptLiteralVertex scriptVertex = new GremlinScriptLiteralVertex(); + + private final GremlinScriptLiteralEdge scriptEdge = new GremlinScriptLiteralEdge(); + + @Override + public List generateInsertScript(@NonNull GremlinSource source) { + return generateInsertUpdateScript(source, + scriptVertex::generateInsertScript, + scriptEdge::generateInsertScript); + } + + @Override + public List generateDeleteAllScript() { + return Arrays.asList(Constants.GREMLIN_SCRIPT_EDGE_DROP_ALL, Constants.GREMLIN_QUERY_BARRIER, Constants.GREMLIN_SCRIPT_VERTEX_DROP_ALL); + } + + @Override + public List generateDeleteAllByClassScript(@NonNull GremlinSource source) { + return generateDeleteAllScript(); + } + + @Override + public List generateFindByIdScript(@Nullable GremlinSource source) { + throw new UnsupportedOperationException("Gremlin graph cannot findById by single query."); + } + + @Override + public List generateUpdateScript(@NonNull GremlinSource source) { + return generateInsertUpdateScript(source, + scriptVertex::generateUpdateScript, + scriptEdge::generateUpdateScript); + } + + private List generateInsertUpdateScript(@NonNull GremlinSource source, + @NonNull Function, List> vertexHandler, + @NonNull Function, List> edgeHandler) { + if (!(source instanceof GremlinSourceGraph)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceGraph"); + } + + final List scriptList = new ArrayList<>(); + final GremlinSourceGraph sourceGraph = (GremlinSourceGraph) source; + final List> vertexes = sourceGraph.getVertexSet(); + final List> edges = sourceGraph.getEdgeSet(); + + vertexes.forEach(vertex -> scriptList.addAll(vertexHandler.apply(vertex))); + scriptList.add(Constants.GREMLIN_QUERY_BARRIER); + edges.forEach(edge -> scriptList.addAll(edgeHandler.apply(edge))); + + return scriptList; + } + + @Override + public List generateDeleteByIdScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceGraph)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceGraph"); + } + + return this.generateDeleteAllScript(); + } + + @Override + public List generateFindAllScript(@NonNull GremlinSource source) { + throw new UnsupportedOperationException("Gremlin graph cannot be findAll."); + } + + public List generateIsEmptyScript() { + final List scriptList = Arrays.asList(Constants.GREMLIN_PRIMITIVE_GRAPH, Constants.GREMLIN_PRIMITIVE_VERTEX_ALL); + final String query = String.join(Constants.GREMLIN_PRIMITIVE_INVOKE, scriptList); + + return Collections.singletonList(query); + } + + @Override + public List generateCountScript(@NonNull GremlinSource source) { + throw new UnsupportedOperationException("Gremlin graph counting is not available."); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralHelper.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralHelper.java new file mode 100644 index 000000000000..b9f94ae98412 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralHelper.java @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.script; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.annotation.GeneratedValue; +import com.microsoft.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException; +import org.apache.tinkerpop.shaded.jackson.core.JsonProcessingException; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; + +public class GremlinScriptLiteralHelper { + + static String generateEntityWithRequiredId(@NonNull Object id, GremlinEntityType type) { + Assert.notNull(id, "id should not be null"); + Assert.isTrue(type == GremlinEntityType.EDGE || type == GremlinEntityType.VERTEX, "should be edge/vertex type"); + + final String prefix = (type == GremlinEntityType.VERTEX) ? "V" : "E"; + + if (id instanceof String) { + return prefix + String.format("('%s')", id); + } else if (id instanceof Integer) { + return prefix + String.format("(%d)", id); + } else if (id instanceof Long) { + return prefix + String.format("(%d)", id); + } + + throw new GremlinInvalidEntityIdFieldException("Only String/Integer/Long of id is supported"); + } + + static String generatePropertyWithRequiredId(@NonNull Object id) { + if (id instanceof String) { + return String.format("property(id, '%s')", id); + } else if (id instanceof Integer) { + return String.format("property(id, %d)", id); + } else if (id instanceof Long) { + return String.format("property(id, %d)", id); + } + + throw new GremlinInvalidEntityIdFieldException("Only String/Integer/Long of id is supported"); + } + + static String generateAsWithAlias(@NonNull String alias) { + return String.format("as('%s')", alias); + } + + static String generateAddEntityWithLabel(@NonNull String label, GremlinEntityType type) { + Assert.isTrue(type == GremlinEntityType.EDGE || type == GremlinEntityType.VERTEX, "should be edge/vertex type"); + + final String prefix = (type == GremlinEntityType.VERTEX) ? "addV" : "addE"; + + return prefix + String.format("('%s')", label); + } + + static List completeScript(@NonNull List scriptList) { + return Collections.singletonList(String.join(Constants.GREMLIN_PRIMITIVE_INVOKE, scriptList)); + } + + public static String generateHasLabel(@NonNull String label) { + return String.format("has(label, '%s')", label); + } + + public static String generateHasId(@NonNull Object id) { + if (id instanceof String) { + return String.format("hasId('%s')", id); + } else if (id instanceof Integer) { + return String.format("hasId(%d)", id); + } else if (id instanceof Long) { + return String.format("hasId(%d)", id); + } else { + throw new GremlinInvalidEntityIdFieldException("the type of @Id/id field should be String/Integer/Long"); + } + } + + public static String generateHasId(@NonNull Object id, @NonNull Field idFiled) { + if (!idFiled.isAnnotationPresent(GeneratedValue.class)) { + return generateHasId(id); + } else if (id instanceof String) { + return String.format("hasId('%s')", id); + } else if (id instanceof Integer) { + return String.format("hasId(%dL)", id); + } else if (id instanceof Long) { + return String.format("hasId(%dL)", id); + } else { + throw new GremlinInvalidEntityIdFieldException("the type of @Id/id field should be String/Integer/Long"); + } + } + + private static String generateProperty(@NonNull String name, @NonNull String value) { + return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_STRING, name, value); + } + + private static String generateProperty(@NonNull String name, @NonNull Integer value) { + return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_NUMBER, name, value); + } + + private static String generateProperty(@NonNull String name, @NonNull Boolean value) { + return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_BOOLEAN, name, value); + } + + private static String generateProperty(@NonNull String name, @NonNull Long value) { + return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_NUMBER, name, value); + } + + private static String generateProperty(@NonNull String name, @NonNull Object value) { + if (value instanceof Integer) { + return generateProperty(name, (Integer) value); + } else if (value instanceof Boolean) { + return generateProperty(name, (Boolean) value); + } else if (value instanceof String) { + return generateProperty(name, (String) value); + } else if (value instanceof Date) { + return generateProperty(name, GremlinUtils.timeToMilliSeconds(value)); + } else { + final String propertyScript; + + try { + propertyScript = generateProperty(name, GremlinUtils.getObjectMapper().writeValueAsString(value)); + } catch (JsonProcessingException e) { + throw new GremlinUnexpectedEntityTypeException("Failed to write object to String", e); + } + + return propertyScript; + } + } + + protected static List generateProperties(@NonNull final Map properties) { + final List scripts = new ArrayList<>(); + + properties.entrySet().stream().filter(e -> e.getValue() != null) + .forEach(e -> scripts.add(generateProperty(e.getKey(), e.getValue()))); + + return scripts; + } + + private static String generateHas(@NonNull String name, @NonNull Integer value) { + return String.format(Constants.GREMLIN_PRIMITIVE_HAS_NUMBER, name, value); + } + + private static String generateHas(@NonNull String name, @NonNull Boolean value) { + return String.format(Constants.GREMLIN_PRIMITIVE_HAS_BOOLEAN, name, value); + } + + private static String generateHas(@NonNull String name, @NonNull String value) { + return String.format(Constants.GREMLIN_PRIMITIVE_HAS_STRING, name, value); + } + + private static String generateHas(@NonNull String name, @NonNull Long value) { + return String.format(Constants.GREMLIN_PRIMITIVE_HAS_NUMBER, name, value); + } + + // TODO: should move to query method part. + public static String generateHas(@NonNull String name, @NonNull Object value) { + + if (value instanceof Integer) { + return generateHas(name, (Integer) value); + } else if (value instanceof Boolean) { + return generateHas(name, (Boolean) value); + } else if (value instanceof String) { + return generateHas(name, (String) value); + } else if (value instanceof Date) { + return generateHas(name, GremlinUtils.timeToMilliSeconds(value)); + } else { + final String hasScript; + + try { + hasScript = generateHas(name, GremlinUtils.getObjectMapper().writeValueAsString(value)); + } catch (JsonProcessingException e) { + throw new GremlinUnexpectedEntityTypeException("Failed to write object to String", e); + } + + return hasScript; + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralVertex.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralVertex.java new file mode 100644 index 000000000000..44dedb456946 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralVertex.java @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.script; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class GremlinScriptLiteralVertex implements GremlinScriptLiteral { + + @Override + public List generateInsertScript(@NonNull GremlinSource source) { + Assert.notNull(source, "source should not be null"); + if (!(source instanceof GremlinSourceVertex)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex"); + } + + final List scriptList = new ArrayList<>(); + + scriptList.add(Constants.GREMLIN_PRIMITIVE_GRAPH); // g + scriptList.add(GremlinScriptLiteralHelper.generateAddEntityWithLabel(source.getLabel(), GremlinEntityType.VERTEX)); // addV('label') + + source.getId().ifPresent(id -> scriptList.add(GremlinScriptLiteralHelper.generatePropertyWithRequiredId(id))); // property(id, xxx) + + scriptList.addAll(GremlinScriptLiteralHelper.generateProperties(source.getProperties())); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateDeleteAllScript() { + return Collections.singletonList(Constants.GREMLIN_SCRIPT_VERTEX_DROP_ALL); + } + + @Override + public List generateDeleteAllByClassScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceVertex)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex"); + } + + final List scriptList = Arrays.asList( + Constants.GREMLIN_PRIMITIVE_GRAPH, // g + Constants.GREMLIN_PRIMITIVE_VERTEX_ALL, // V() + GremlinScriptLiteralHelper.generateHasLabel(source.getLabel()), // has(label, 'label') + Constants.GREMLIN_PRIMITIVE_DROP // drop() + ); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateFindByIdScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceVertex)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex"); + } + + Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id."); + + final List scriptList = Arrays.asList( + Constants.GREMLIN_PRIMITIVE_GRAPH, // g + Constants.GREMLIN_PRIMITIVE_VERTEX_ALL, // V() + GremlinScriptLiteralHelper.generateHasId(source.getId().get(), source.getIdField()) // hasId(xxx) + ); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateUpdateScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceVertex)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex"); + } + + final List scriptList = new ArrayList<>(); + + Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id."); + + scriptList.add(Constants.GREMLIN_PRIMITIVE_GRAPH); // g + scriptList.add(GremlinScriptLiteralHelper.generateEntityWithRequiredId(source.getId().get(), GremlinEntityType.VERTEX)); // V(id) + scriptList.addAll(GremlinScriptLiteralHelper.generateProperties(source.getProperties())); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateFindAllScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceVertex)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex"); + } + + final String classname = source.getProperties().get(Constants.GREMLIN_PROPERTY_CLASSNAME).toString(); + Assert.notNull(classname, "GremlinSource should contain predefined classname"); + + final List scriptList = Arrays.asList( + Constants.GREMLIN_PRIMITIVE_GRAPH, // g + Constants.GREMLIN_PRIMITIVE_VERTEX_ALL, // V() + GremlinScriptLiteralHelper.generateHasLabel(source.getLabel()), // has(label, 'label') + GremlinScriptLiteralHelper.generateHas(Constants.GREMLIN_PROPERTY_CLASSNAME, classname) // has(_classname, 'xxxxxx') + ); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateDeleteByIdScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceVertex)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex"); + } + + Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id."); + + final List scriptList = Arrays.asList( + Constants.GREMLIN_PRIMITIVE_GRAPH, // g + Constants.GREMLIN_PRIMITIVE_VERTEX_ALL, // E() + GremlinScriptLiteralHelper.generateHasId(source.getId().get(), source.getIdField()), // hasId(xxx) + Constants.GREMLIN_PRIMITIVE_DROP // drop() + ); + + return GremlinScriptLiteralHelper.completeScript(scriptList); + } + + @Override + public List generateCountScript(@NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceVertex)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex"); + } + + return Collections.singletonList(Constants.GREMLIN_SCRIPT_VERTEX_ALL); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/AbstractGremlinSource.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/AbstractGremlinSource.java new file mode 100644 index 000000000000..602b04aa5b4b --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/AbstractGremlinSource.java @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.annotation.GeneratedValue; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.conversion.result.GremlinResultsReader; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteral; +import com.microsoft.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException; +import org.apache.tinkerpop.gremlin.driver.Result; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public abstract class AbstractGremlinSource implements GremlinSource { + + private Object id; + + private String label; + + private Field idField; + + private Class domainClass; + + private Map properties; + + private GremlinScriptLiteral scriptLiteral; + + private GremlinSourceWriter sourceWriter; + + private GremlinSourceReader sourceReader; + + private GremlinResultsReader resultReader; + + protected AbstractGremlinSource() { + this.properties = new HashMap<>(); + } + + protected AbstractGremlinSource(Class domainClass) { + this.domainClass = domainClass; + this.properties = new HashMap<>(); + + setProperty(Constants.GREMLIN_PROPERTY_CLASSNAME, domainClass.getName()); + } + + @Override + public Optional getId() { + return Optional.ofNullable(this.id); + } + + /** + * The type of Id keep the consistency with the result from gremlin server, for generate query correctly. So if the + * id is ${@link GeneratedValue}, which may have different type against entity id field. + * + * @param id the given id from query. + */ + @Override + public void setId(Object id) { + final Field idField = getIdField(); + + if (idField == null) { + throw new GremlinInvalidEntityIdFieldException("Id Field of GremlinSource cannot be null"); + } + + if (idField.isAnnotationPresent(GeneratedValue.class) && id instanceof String) { + try { + this.id = Long.valueOf((String) id); // Gremlin server default id type is Long. + } catch (NumberFormatException ignore) { + this.id = id; + } + } else { + this.id = id; + } + } + + @Override + public void setGremlinScriptStrategy(@NonNull GremlinScriptLiteral script) { + this.scriptLiteral = script; + } + + @Override + public void setGremlinSourceWriter(@NonNull GremlinSourceWriter writer) { + this.sourceWriter = writer; + } + + @Override + public void setGremlinSourceReader(@NonNull GremlinSourceReader reader) { + this.sourceReader = reader; + } + + @Override + public void setGremlinResultReader(@NonNull GremlinResultsReader reader) { + this.resultReader = reader; + } + + @Override + public GremlinScriptLiteral getGremlinScriptLiteral() { + return this.scriptLiteral; + } + + @Override + public void doGremlinSourceWrite(@NonNull Object domain, @NonNull MappingGremlinConverter converter) { + Assert.notNull(this.sourceWriter, "the sourceWriter must be set before do writing"); + + this.sourceWriter.write(domain, converter, this); + } + + @Override + public T doGremlinSourceRead(@NonNull Class domainClass, @NonNull MappingGremlinConverter converter) { + Assert.notNull(this.sourceReader, "the sourceReader must be set before do reading"); + + return this.sourceReader.read(domainClass, converter, this); + } + + @Override + public void doGremlinResultRead(@NonNull List results) { + Assert.notNull(this.resultReader, "the resultReader must be set before do reading"); + + this.resultReader.read(results, this); + } + + private boolean hasProperty(String key) { + return this.properties.get(key) != null; + } + + @Override + public void setProperty(String key, Object value) { + if (this.hasProperty(key) && value == null) { + this.properties.remove(key); + } else { + this.properties.put(key, value); + } + } + + public void setIdField(Field idField) { + this.idField = idField; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public void setLabel(String label) { + this.label = label; + } + + @Override + public Field getIdField() { + return idField; + } + + @Override + public Class getDomainClass() { + return domainClass; + } + + @Override + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/AbstractGremlinSourceReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/AbstractGremlinSourceReader.java new file mode 100644 index 000000000000..61d0c05af195 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/AbstractGremlinSourceReader.java @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentProperty; +import com.microsoft.spring.data.gremlin.exception.GremlinEntityInformationException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException; +import org.apache.tinkerpop.shaded.jackson.databind.JavaType; +import org.apache.tinkerpop.shaded.jackson.databind.type.TypeFactory; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Date; + +public abstract class AbstractGremlinSourceReader implements GremlinSourceReader { + + protected Object readProperty(@NonNull PersistentProperty property, + @Nullable Object value) { + Assert.notNull(property, "property should not be null"); + final Class type = property.getTypeInformation().getType(); + final JavaType javaType = TypeFactory.defaultInstance().constructType(property.getType()); + + if (value == null) { + return null; + } else if (type == int.class || type == Integer.class + || type == Boolean.class || type == boolean.class + || type == String.class) { + return value; + } else if (type == Date.class) { + Assert.isTrue(value instanceof Long, "Date store value must be instance of long"); + return new Date((Long) value); + } else { + final Object object; + + try { + object = GremlinUtils.getObjectMapper().readValue(value.toString(), javaType); + } catch (IOException e) { + throw new GremlinUnexpectedEntityTypeException("Failed to read String to Object", e); + } + + return object; + } + } + + protected Object getGremlinSourceId(@NonNull GremlinSource source) { + Assert.notNull(source, "source should not be null"); + if (!source.getId().isPresent()) { + return null; + } + + final Object id = source.getId().get(); + final Field idField = source.getIdField(); + + if (idField.getType() == String.class) { + return id.toString(); + } else if (idField.getType() == Integer.class) { + Assert.isTrue(id instanceof Integer, "source Id should be Integer."); + return id; + } else if (idField.getType() == Long.class && id instanceof Integer) { + return Long.valueOf((Integer) id); + } else if (idField.getType() == Long.class && id instanceof Long) { + return id; + } + + throw new GremlinEntityInformationException("unsupported id field type: " + id.getClass().getSimpleName()); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSource.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSource.java new file mode 100644 index 000000000000..57bcdcf06fbe --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSource.java @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.conversion.result.GremlinResultsReader; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteral; +import org.apache.tinkerpop.gremlin.driver.Result; +import org.springframework.lang.NonNull; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Provider interface to obtain and store information from domain class. + * For Vertex and Edge, they consist of id (String, Reserved), label (String, Reserved) and + * a set of properties. + * The property key should be String, and value can be one of String, number and boolean. + * + * @param The type of domain. + */ +public interface GremlinSource { + + /** + * Set the property map of domain + * + * @param key The key of the property. + * @param value The value of the property. + */ + void setProperty(String key, Object value); + + /** + * Get the id of domain + * + * @return the Optional of id + */ + Optional getId(); + + /** + * Set the id of domain + * + * @param id The id of the domain object. + */ + void setId(Object id); + + /** + * Get the id Field of domain + * + * @return will never be null + */ + Field getIdField(); + + /** + * Set the id of domain + * + * @param id The id field. + */ + void setIdField(Field id); + + /** + * Get the label of domain + * + * @return will never be null + */ + @NonNull + String getLabel(); + + /** + * Set the label of domain + * + * @param label The label of the domain object. + */ + void setLabel(String label); + + /** + * Get the Class type of domain + * + * @return will never be null + */ + @NonNull + Class getDomainClass(); + + /** + * Get the properties of domain + * + * @return The properties map of the domain, and it will never be null. + */ + Map getProperties(); + + /** + * do the real write from domain to GremlinSource + * + * @param domain The domain object which needed to be written to GremlinSource. + * @param converter The entity converter. + */ + void doGremlinSourceWrite(Object domain, MappingGremlinConverter converter); + + /** + * do the real reading from Result to GremlinSource + * + * @param results The results retrieved from Gremlin server. + */ + void doGremlinResultRead(List results); + + /** + * do the real reading from GremlinSource to domain + * + * @param domainClass The class type of domain object. + * @param converter The entity converter. + * + * @return The domain object read from gremlin source. + */ + T doGremlinSourceRead(Class domainClass, MappingGremlinConverter converter); + + /** + * @return the GremlinScriptLiteral + */ + GremlinScriptLiteral getGremlinScriptLiteral(); + + /** + * Set the script Strategy of GremlinSource + * + * @param script The script literal. + */ + void setGremlinScriptStrategy(GremlinScriptLiteral script); + + /** + * Set the SourceWriter of GremlinSource + * + * @param writer The source writer to use. + */ + void setGremlinSourceWriter(GremlinSourceWriter writer); + + /** + * Set the ResultReader for reading data from Gremlin Result to GremlinSource + * + * @param reader The results reader to use. + */ + void setGremlinResultReader(GremlinResultsReader reader); + + /** + * Set the SourceReader for reading data from GremlinSource to domain + * + * @param reader The source reader to use. + */ + void setGremlinSourceReader(GremlinSourceReader reader); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceEdge.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceEdge.java new file mode 100644 index 000000000000..ca02337774aa --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceEdge.java @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.conversion.result.GremlinResultEdgeReader; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteralEdge; + +public class GremlinSourceEdge extends AbstractGremlinSource { + + private Object vertexIdFrom; + + private Object vertexIdTo; + + public GremlinSourceEdge() { + super(); + initializeGremlinStrategy(); + } + + public GremlinSourceEdge(Class domainClass) { + super(domainClass); + initializeGremlinStrategy(); + } + + private void initializeGremlinStrategy() { + this.setGremlinScriptStrategy(new GremlinScriptLiteralEdge()); + this.setGremlinResultReader(new GremlinResultEdgeReader()); + this.setGremlinSourceReader(new GremlinSourceEdgeReader<>()); + this.setGremlinSourceWriter(new GremlinSourceEdgeWriter<>()); + } + + public Object getVertexIdFrom() { + return vertexIdFrom; + } + + public void setVertexIdFrom(Object vertexIdFrom) { + this.vertexIdFrom = vertexIdFrom; + } + + public Object getVertexIdTo() { + return vertexIdTo; + } + + public void setVertexIdTo(Object vertexIdTo) { + this.vertexIdTo = vertexIdTo; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceEdgeReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceEdgeReader.java new file mode 100644 index 000000000000..422f098c7f8b --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceEdgeReader.java @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentProperty; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentEntity; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.model.ConvertingPropertyAccessor; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.lang.reflect.Field; + +public class GremlinSourceEdgeReader extends AbstractGremlinSourceReader { + + @Override + public T read(@NonNull Class domainClass, @NonNull MappingGremlinConverter converter, + @NonNull GremlinSource source) { + Assert.notNull(domainClass, "domainClass should not be null"); + Assert.notNull(converter, "converter should not be null"); + Assert.notNull(source, "source should not be null"); + if (!(source instanceof GremlinSourceEdge)) { + throw new GremlinUnexpectedSourceTypeException("should be instance of GremlinSourceEdge"); + } + + final T domain = GremlinUtils.createInstance(domainClass); + final ConvertingPropertyAccessor accessor = converter.getPropertyAccessor(domain); + final GremlinPersistentEntity persistentEntity = converter.getPersistentEntity(domainClass); + + for (final Field field : FieldUtils.getAllFields(domainClass)) { + final PersistentProperty property = persistentEntity.getPersistentProperty(field + .getName()); + if (property == null) { + continue; + } + if (field.getName().equals(Constants.PROPERTY_ID) || field.getAnnotation(Id.class) != null) { + accessor.setProperty(property, super.getGremlinSourceId(source)); + continue; + } else if (field.getAnnotation(EdgeFrom.class) != null || field.getAnnotation(EdgeTo.class) != null) { + // We cannot do that here as the gremlin will not tell more information about vertex except Id. After + // the query of Edge end, we can get the Id of vertex from/to. And then we will do extra 2 query to + // obtain the 2 vertex and complete the edge. + // + // That work will be wrapped in GremlinTemplate insert, and skip the property here. + continue; + } + + final Object value = super.readProperty(property, source.getProperties().get(field.getName())); + accessor.setProperty(property, value); + } + + return domain; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceEdgeWriter.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceEdgeWriter.java new file mode 100644 index 000000000000..960ac78cd166 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceEdgeWriter.java @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentProperty; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.exception.GremlinEntityInformationException; +import com.microsoft.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentEntity; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.model.ConvertingPropertyAccessor; +import org.springframework.lang.NonNull; + +import java.lang.reflect.Field; + +public class GremlinSourceEdgeWriter implements GremlinSourceWriter { + + private Object getIdValue(@NonNull Object object, @NonNull MappingGremlinConverter converter) { + if (object instanceof String || object instanceof Long || object instanceof Integer) { + return object; + } else if (object.getClass().isPrimitive()) { + throw new GremlinUnexpectedEntityTypeException("only String type of primitive is allowed"); + } else { + return converter.getIdFieldValue(object); + } + } + + @Override + @SuppressWarnings("unchecked") + public void write(@NonNull Object domain, @NonNull MappingGremlinConverter converter, + @NonNull GremlinSource source) throws GremlinInvalidEntityIdFieldException { + if (!(source instanceof GremlinSourceEdge)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge"); + } + + source.setId(converter.getIdFieldValue(domain)); + + final GremlinSourceEdge sourceEdge = (GremlinSourceEdge) source; + final GremlinPersistentEntity persistentEntity = converter.getPersistentEntity(domain.getClass()); + final ConvertingPropertyAccessor accessor = (ConvertingPropertyAccessor) converter.getPropertyAccessor(domain); + + for (final Field field : FieldUtils.getAllFields(domain.getClass())) { + final PersistentProperty property = persistentEntity.getPersistentProperty(field + .getName()); + if (property == null) { + continue; + } + + final Object object = accessor.getProperty(property); + + if (field.getName().equals(Constants.PROPERTY_ID) || field.getAnnotation(Id.class) != null) { + continue; + } else if (field.getName().equals(Constants.GREMLIN_PROPERTY_CLASSNAME)) { + throw new GremlinEntityInformationException("Domain Cannot use pre-defined field name: " + + Constants.GREMLIN_PROPERTY_CLASSNAME); + } else if (field.getAnnotation(EdgeFrom.class) != null) { + final Object vertexId = this.getIdValue(object, converter); + if (vertexId == null) { + throw new GremlinInvalidEntityIdFieldException("The vertex id for the from vertex cannot be null!"); + } + sourceEdge.setVertexIdFrom(vertexId); + } else if (field.getAnnotation(EdgeTo.class) != null) { + final Object vertexId = this.getIdValue(object, converter); + if (vertexId == null) { + throw new GremlinInvalidEntityIdFieldException("The vertex id for the to vertex cannot be null!"); + } + sourceEdge.setVertexIdTo(vertexId); + } + source.setProperty(field.getName(), accessor.getProperty(property)); + } + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceGraph.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceGraph.java new file mode 100644 index 000000000000..005a0d3665ac --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceGraph.java @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.conversion.result.GremlinResultsGraphReader; +import com.microsoft.spring.data.gremlin.conversion.result.GremlinResultsReader; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteralGraph; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; + +import java.util.ArrayList; +import java.util.List; + +public class GremlinSourceGraph extends AbstractGremlinSource { + + private List> vertexSet = new ArrayList<>(); + + private List> edgeSet = new ArrayList<>(); + + private GremlinResultsReader resultsReader; + + public GremlinSourceGraph() { + super(); + initializeGremlinStrategy(); + this.setGremlinSourceReader(new GremlinSourceGraphReader<>()); + this.resultsReader = new GremlinResultsGraphReader(); + } + + public GremlinSourceGraph(Class domainClass) { + super(domainClass); + initializeGremlinStrategy(); + this.setGremlinSourceReader(new GremlinSourceGraphReader()); + this.resultsReader = new GremlinResultsGraphReader(); + } + + public void addGremlinSource(GremlinSource source) { + if (source instanceof GremlinSourceVertex) { + this.vertexSet.add(source); + } else if (source instanceof GremlinSourceEdge) { + this.edgeSet.add(source); + } else { + throw new GremlinUnexpectedSourceTypeException("source type can only be Vertex or Edge"); + } + } + + private void initializeGremlinStrategy() { + this.setGremlinScriptStrategy(new GremlinScriptLiteralGraph()); + this.setGremlinSourceWriter(new GremlinSourceGraphWriter<>()); + } + + public List> getVertexSet() { + return vertexSet; + } + + public List> getEdgeSet() { + return edgeSet; + } + + public GremlinResultsReader getResultsReader() { + return resultsReader; + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceGraphReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceGraphReader.java new file mode 100644 index 000000000000..54a78c66c653 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceGraphReader.java @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentProperty; +import com.microsoft.spring.data.gremlin.annotation.EdgeSet; +import com.microsoft.spring.data.gremlin.annotation.VertexSet; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentEntity; +import com.microsoft.spring.data.gremlin.repository.support.GremlinEntityInformation; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.model.ConvertingPropertyAccessor; +import org.springframework.lang.NonNull; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +public class GremlinSourceGraphReader extends AbstractGremlinSourceReader { + + @Override + public T read(@NonNull Class type, @NonNull MappingGremlinConverter converter, + @NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceGraph)) { + throw new GremlinUnexpectedSourceTypeException("Should be instance of GremlinSourceGraph"); + } + + final GremlinSourceGraph graphSource = (GremlinSourceGraph) source; + final T entity = GremlinUtils.createInstance(type); + final ConvertingPropertyAccessor accessor = converter.getPropertyAccessor(entity); + final GremlinPersistentEntity persistentEntity = converter.getPersistentEntity(type); + + for (final Field field : FieldUtils.getAllFields(type)) { + final PersistentProperty property = persistentEntity.getPersistentProperty(field + .getName()); + if (property == null) { + continue; + } + + if ((field.getName().equals(Constants.PROPERTY_ID) || field.getAnnotation(Id.class) != null)) { + accessor.setProperty(property, super.getGremlinSourceId(graphSource)); + } else if (field.isAnnotationPresent(VertexSet.class)) { + accessor.setProperty(property, readEntitySet(graphSource.getVertexSet(), converter)); + } else if (field.isAnnotationPresent(EdgeSet.class)) { + accessor.setProperty(property, readEntitySet(graphSource.getEdgeSet(), converter)); + } + } + + return entity; + } + + private List readEntitySet(List> sources, MappingGremlinConverter converter) { + final List domainObjects = new ArrayList<>(); + + for (final GremlinSource source : sources) { + domainObjects.add(doGremlinSourceRead(source, converter)); + } + + return domainObjects; + } + + @SuppressWarnings("unchecked") + private T doGremlinSourceRead(GremlinSource source, MappingGremlinConverter converter) { + try { + Class domainClass = (Class) Class.forName((String) source.getProperties() + .get(Constants.GREMLIN_PROPERTY_CLASSNAME)); + source.setIdField(new GremlinEntityInformation<>(domainClass).getIdField()); + return source.doGremlinSourceRead(domainClass, converter); + } catch (ClassNotFoundException e) { + throw new GremlinUnexpectedSourceTypeException("No Java class found for source property " + + Constants.GREMLIN_PROPERTY_CLASSNAME, e); + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceGraphWriter.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceGraphWriter.java new file mode 100644 index 000000000000..5cd0d46be243 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceGraphWriter.java @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentProperty; +import com.microsoft.spring.data.gremlin.annotation.EdgeSet; +import com.microsoft.spring.data.gremlin.annotation.VertexSet; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentEntity; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.model.ConvertingPropertyAccessor; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.lang.reflect.Field; +import java.util.List; + +public class GremlinSourceGraphWriter implements GremlinSourceWriter { + + private void writeGraphSet(@NonNull List objectList, @NonNull MappingGremlinConverter mappingConverter, + @NonNull GremlinSourceGraph sourceGraph) { + Assert.notNull(objectList, "objectList should not be null"); + Assert.notNull(mappingConverter, "mappingConverter should not be null"); + Assert.notNull(sourceGraph, "sourceGraph should not be null"); + Assert.isInstanceOf(GremlinSourceGraph.class, sourceGraph, "should be instance of GremlinSourceGraph "); + + for (final Object object : objectList) { + final GremlinSource source = GremlinUtils.toGremlinSource(object.getClass()); + source.doGremlinSourceWrite(object, mappingConverter); + sourceGraph.addGremlinSource(source); + } + } + + @Override + @SuppressWarnings("unchecked") + public void write(@NonNull Object domain, @NonNull MappingGremlinConverter converter, + @NonNull GremlinSource source) { + Assert.notNull(domain, "domain should not be null"); + Assert.notNull(converter, "converter should not be null"); + Assert.notNull(source, "source should not be null"); + if (!(source instanceof GremlinSourceGraph)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge"); + } + + final GremlinSourceGraph sourceGraph = (GremlinSourceGraph) source; + final GremlinPersistentEntity persistentEntity = converter.getPersistentEntity(domain.getClass()); + final ConvertingPropertyAccessor accessor = converter.getPropertyAccessor((T) domain); + + for (final Field field : FieldUtils.getAllFields(domain.getClass())) { + final PersistentProperty property = persistentEntity.getPersistentProperty(field + .getName()); + if (property == null) { + continue; + } + + if (field.getName().equals(Constants.PROPERTY_ID) || field.getAnnotation(Id.class) != null) { + continue; + } + + @SuppressWarnings("unchecked") final List objects = (List) accessor.getProperty(property); + + if (field.getAnnotation(VertexSet.class) != null || field.getAnnotation(EdgeSet.class) != null) { + this.writeGraphSet(objects, converter, sourceGraph); + } + } + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceReader.java new file mode 100644 index 000000000000..f8190073e36e --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceReader.java @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; + +/** + * Provider Entity type dependent read method. + */ +public interface GremlinSourceReader { + /** + * Read data from GremlinSource to domain + * + * @param domainClass The class type for the domain object. + * @param converter The entity converter. + * @param source The gremlin source object. + * + * @return The domain object read from gremlin source. + */ + T read(Class domainClass, MappingGremlinConverter converter, GremlinSource source); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceVertex.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceVertex.java new file mode 100644 index 000000000000..22cb310027d9 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceVertex.java @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.conversion.result.GremlinResultVertexReader; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteralVertex; + +public class GremlinSourceVertex extends AbstractGremlinSource { + + public GremlinSourceVertex() { + super(); + initializeGremlinStrategy(); + } + + public GremlinSourceVertex(Class domainClass) { + super(domainClass); + initializeGremlinStrategy(); + } + + private void initializeGremlinStrategy() { + this.setGremlinScriptStrategy(new GremlinScriptLiteralVertex()); + this.setGremlinResultReader(new GremlinResultVertexReader()); + this.setGremlinSourceReader(new GremlinSourceVertexReader<>()); + this.setGremlinSourceWriter(new GremlinSourceVertexWriter<>()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceVertexReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceVertexReader.java new file mode 100644 index 000000000000..ad735025dfce --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceVertexReader.java @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentProperty; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentEntity; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.model.ConvertingPropertyAccessor; +import org.springframework.lang.NonNull; + +import java.lang.reflect.Field; + +public class GremlinSourceVertexReader extends AbstractGremlinSourceReader { + + @Override + public T read(@NonNull Class domainClass, @NonNull MappingGremlinConverter converter, + @NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceVertex)) { + throw new GremlinUnexpectedSourceTypeException("should be instance of GremlinSourceVertex"); + } + + final T domain = GremlinUtils.createInstance(domainClass); + final ConvertingPropertyAccessor accessor = converter.getPropertyAccessor(domain); + final GremlinPersistentEntity persistentEntity = converter.getPersistentEntity(domainClass); + + for (final Field field : FieldUtils.getAllFields(domainClass)) { + final PersistentProperty property = persistentEntity.getPersistentProperty(field + .getName()); + if (property == null) { + continue; + } + + if (field.getName().equals(Constants.PROPERTY_ID) || field.getAnnotation(Id.class) != null) { + accessor.setProperty(property, super.getGremlinSourceId(source)); + } else { + final Object value = super.readProperty(property, source.getProperties().get(field.getName())); + accessor.setProperty(property, value); + } + } + + return domain; + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceVertexWriter.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceVertexWriter.java new file mode 100644 index 000000000000..70009ce48e52 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceVertexWriter.java @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentProperty; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.exception.GremlinEntityInformationException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentEntity; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.model.ConvertingPropertyAccessor; +import org.springframework.lang.NonNull; + +import java.lang.reflect.Field; + +public class GremlinSourceVertexWriter implements GremlinSourceWriter { + + @Override + @SuppressWarnings("unchecked") + public void write(@NonNull Object domain, @NonNull MappingGremlinConverter converter, + @NonNull GremlinSource source) { + if (!(source instanceof GremlinSourceVertex)) { + throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex"); + } + + source.setId(converter.getIdFieldValue(domain)); + + final GremlinPersistentEntity persistentEntity = converter.getPersistentEntity(domain.getClass()); + final ConvertingPropertyAccessor accessor = (ConvertingPropertyAccessor) converter.getPropertyAccessor(domain); + + for (final Field field : FieldUtils.getAllFields(domain.getClass())) { + final PersistentProperty property = persistentEntity.getPersistentProperty(field.getName()); + if (property == null) { + continue; + } + + if (field.getName().equals(Constants.PROPERTY_ID) || field.getAnnotation(Id.class) != null) { + continue; + } else if (field.getName().equals(Constants.GREMLIN_PROPERTY_CLASSNAME)) { + throw new GremlinEntityInformationException("Domain Cannot use pre-defined field name: " + + Constants.GREMLIN_PROPERTY_CLASSNAME); + } + + source.setProperty(field.getName(), accessor.getProperty(property)); + } + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceWriter.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceWriter.java new file mode 100644 index 000000000000..4bdf4f0876fc --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceWriter.java @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; + +/** + * Provider Entity type dependent write method. + */ +public interface GremlinSourceWriter { + /** + * Write the domain class information to GremlinSource + * + * @param domain The domain object needed to be written into the gremlin source. + * @param converter The entity converter. + * @param source The gremlin source to write the information from domain object. + */ + void write(Object domain, MappingGremlinConverter converter, GremlinSource source); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinEntityInformationException.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinEntityInformationException.java new file mode 100644 index 000000000000..68b4d4eac343 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinEntityInformationException.java @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.exception; + +import org.springframework.dao.TypeMismatchDataAccessException; + +public class GremlinEntityInformationException extends TypeMismatchDataAccessException { + + public GremlinEntityInformationException(String msg) { + super(msg); + } + + public GremlinEntityInformationException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinIllegalConfigurationException.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinIllegalConfigurationException.java new file mode 100644 index 000000000000..7ca91c9db1b3 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinIllegalConfigurationException.java @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.exception; + +import org.springframework.dao.InvalidDataAccessApiUsageException; + +public class GremlinIllegalConfigurationException extends InvalidDataAccessApiUsageException { + + public GremlinIllegalConfigurationException(String msg) { + super(msg); + } + + public GremlinIllegalConfigurationException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinInvalidEntityIdFieldException.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinInvalidEntityIdFieldException.java new file mode 100644 index 000000000000..09b4d2105867 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinInvalidEntityIdFieldException.java @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.exception; + +public class GremlinInvalidEntityIdFieldException extends GremlinEntityInformationException { + + public GremlinInvalidEntityIdFieldException(String msg) { + super(msg); + } + + public GremlinInvalidEntityIdFieldException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinQueryException.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinQueryException.java new file mode 100644 index 000000000000..9efc480647bc --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinQueryException.java @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.exception; + +import org.springframework.dao.DataAccessResourceFailureException; + +public class GremlinQueryException extends DataAccessResourceFailureException { + + public GremlinQueryException(String msg) { + super(msg); + } + + public GremlinQueryException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinUnexpectedEntityTypeException.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinUnexpectedEntityTypeException.java new file mode 100644 index 000000000000..f71c99c27b82 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinUnexpectedEntityTypeException.java @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.exception; + +public class GremlinUnexpectedEntityTypeException extends GremlinEntityInformationException { + + public GremlinUnexpectedEntityTypeException(String msg) { + super(msg); + } + + public GremlinUnexpectedEntityTypeException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinUnexpectedSourceTypeException.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinUnexpectedSourceTypeException.java new file mode 100644 index 000000000000..b993111c53bd --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/exception/GremlinUnexpectedSourceTypeException.java @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.exception; + +import org.springframework.dao.TypeMismatchDataAccessException; + +public class GremlinUnexpectedSourceTypeException extends TypeMismatchDataAccessException { + + public GremlinUnexpectedSourceTypeException(String msg) { + super(msg); + } + + public GremlinUnexpectedSourceTypeException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentEntity.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentEntity.java new file mode 100644 index 000000000000..24721498d33c --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentEntity.java @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.mapping; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.expression.BeanFactoryAccessor; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.data.mapping.model.BasicPersistentEntity; +import org.springframework.data.util.TypeInformation; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +public class BasicGremlinPersistentEntity extends BasicPersistentEntity + implements GremlinPersistentEntity, ApplicationContextAware { + + private final StandardEvaluationContext context; + + public BasicGremlinPersistentEntity(TypeInformation information) { + super(information); + + this.context = new StandardEvaluationContext(); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.context.addPropertyAccessor(new BeanFactoryAccessor()); + this.context.setBeanResolver(new BeanFactoryResolver(applicationContext)); + this.context.setRootObject(applicationContext); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentProperty.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentProperty.java new file mode 100644 index 000000000000..2c6125f0de52 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentProperty.java @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.mapping; + +import com.microsoft.spring.data.gremlin.common.Constants; +import org.springframework.data.mapping.Association; +import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; +import org.springframework.data.mapping.model.Property; +import org.springframework.data.mapping.model.SimpleTypeHolder; + +public class BasicGremlinPersistentProperty extends AnnotationBasedPersistentProperty + implements GremlinPersistentProperty { + + public BasicGremlinPersistentProperty(Property property, GremlinPersistentEntity owner, + SimpleTypeHolder holder) { + super(property, owner, holder); + } + + @Override + protected Association createAssociation() { + return new Association<>(this, null); + } + + @Override + public boolean isIdProperty() { + return super.isIdProperty() || getName().equals(Constants.PROPERTY_ID); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/GremlinMappingContext.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/GremlinMappingContext.java new file mode 100644 index 000000000000..68c7b3a2249e --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/GremlinMappingContext.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.mapping; + +import org.springframework.context.ApplicationContext; +import org.springframework.data.mapping.context.AbstractMappingContext; +import org.springframework.data.mapping.model.Property; +import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.data.util.TypeInformation; + +public class GremlinMappingContext + extends AbstractMappingContext, GremlinPersistentProperty> { + private ApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext context) { + this.context = context; + } + + @Override + public GremlinPersistentProperty createPersistentProperty(Property property, + BasicGremlinPersistentEntity owner, + SimpleTypeHolder holder) { + return new BasicGremlinPersistentProperty(property, owner, holder); + } + + @Override + protected BasicGremlinPersistentEntity createPersistentEntity(TypeInformation typeInformation) { + final BasicGremlinPersistentEntity entity = new BasicGremlinPersistentEntity<>(typeInformation); + + if (this.context != null) { + entity.setApplicationContext(this.context); + } + + return entity; + } + +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/GremlinPersistentEntity.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/GremlinPersistentEntity.java new file mode 100644 index 000000000000..14f5fa0e062d --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/GremlinPersistentEntity.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.mapping; + +import org.springframework.data.mapping.PersistentEntity; + +public interface GremlinPersistentEntity extends PersistentEntity { + +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/GremlinPersistentProperty.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/GremlinPersistentProperty.java new file mode 100644 index 000000000000..c36b586f89a2 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/mapping/GremlinPersistentProperty.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.mapping; + +import org.springframework.data.mapping.PersistentProperty; + +public interface GremlinPersistentProperty extends PersistentProperty { + +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/GremlinEntityMetadata.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/GremlinEntityMetadata.java new file mode 100644 index 000000000000..28f00d0b3924 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/GremlinEntityMetadata.java @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query; + +import org.springframework.data.repository.core.EntityMetadata; + +public interface GremlinEntityMetadata extends EntityMetadata { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/GremlinOperations.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/GremlinOperations.java new file mode 100644 index 000000000000..78f15cb58815 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/GremlinOperations.java @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.query.query.GremlinQuery; + +import java.util.List; + +/** + * Provider interface for basic Operations with Gremlin + */ +public interface GremlinOperations { + + void deleteAll(); + + void deleteAll(GremlinEntityType type); + + void deleteAll(GremlinSource source); + + boolean isEmptyGraph(GremlinSource source); + + boolean existsById(Object id, GremlinSource source); + + void deleteById(Object id, GremlinSource source); + + T insert(T object, GremlinSource source); + + T findById(Object id, GremlinSource source); + + T findVertexById(Object id, GremlinSource source); + + T findEdgeById(Object id, GremlinSource source); + + T update(T object, GremlinSource source); + + T save(T object, GremlinSource source); + + List findAll(GremlinSource source); + + long vertexCount(); + + long edgeCount(); + + List find(GremlinQuery query, GremlinSource source); + + MappingGremlinConverter getMappingConverter(); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/GremlinTemplate.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/GremlinTemplate.java new file mode 100644 index 000000000000..e6d7978af4cf --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/GremlinTemplate.java @@ -0,0 +1,389 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentProperty; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; +import com.microsoft.spring.data.gremlin.annotation.GeneratedValue; +import com.microsoft.spring.data.gremlin.common.GremlinFactory; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteral; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteralEdge; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteralGraph; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteralVertex; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceGraph; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import com.microsoft.spring.data.gremlin.exception.GremlinEntityInformationException; +import com.microsoft.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException; +import com.microsoft.spring.data.gremlin.exception.GremlinQueryException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException; +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentEntity; +import com.microsoft.spring.data.gremlin.query.query.GremlinQuery; +import com.microsoft.spring.data.gremlin.query.query.QueryFindScriptGenerator; +import com.microsoft.spring.data.gremlin.query.query.QueryScriptGenerator; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.tinkerpop.gremlin.driver.Client; +import org.apache.tinkerpop.gremlin.driver.Result; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.model.ConvertingPropertyAccessor; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +import static java.util.stream.Collectors.toList; + +public class GremlinTemplate implements GremlinOperations, ApplicationContextAware { + + private final GremlinFactory factory; + private final MappingGremlinConverter mappingConverter; + + private Client gremlinClient; + private ApplicationContext context; + + public GremlinTemplate(@NonNull GremlinFactory factory, @NonNull MappingGremlinConverter converter) { + this.factory = factory; + this.mappingConverter = converter; + } + + @Override + public MappingGremlinConverter getMappingConverter() { + return this.mappingConverter; + } + + public ApplicationContext getApplicationContext() { + return this.context; + } + + @Override + public void setApplicationContext(@NonNull ApplicationContext context) throws BeansException { + this.context = context; + } + + public Client getGremlinClient() { + if (this.gremlinClient == null) { + this.gremlinClient = this.factory.getGremlinClient(); + } + + return this.gremlinClient; + } + + @NonNull + private List executeQuery(@NonNull List queries) { + final List> parallelQueries = GremlinUtils.toParallelQueryList(queries); + + return parallelQueries.stream().flatMap(q -> executeQueryParallel(q).stream()).collect(toList()); + } + + @NonNull + private List executeQueryParallel(@NonNull List queries) { + return queries.parallelStream() + .map(q -> getGremlinClient().submit(q).all()) + .collect(toList()).parallelStream().flatMap(f -> { + try { + return f.get().stream(); + } catch (InterruptedException | ExecutionException e) { + throw new GremlinQueryException("unable to complete query from gremlin", e); + } + }) + .collect(toList()); + } + + @Override + public void deleteAll() { + final GremlinScriptLiteral script = new GremlinScriptLiteralGraph(); + final List queryList = script.generateDeleteAllScript(); + + executeQuery(queryList); + } + + @Override + public void deleteAll(GremlinEntityType type) { + final GremlinSource source = type.createGremlinSource(); + + executeQuery(source.getGremlinScriptLiteral().generateDeleteAllScript()); + } + + @Override + public void deleteAll(GremlinSource source) { + executeQuery(source.getGremlinScriptLiteral().generateDeleteAllByClassScript(source)); + } + + private List insertInternal(@NonNull T object, @NonNull GremlinSource source) { + this.mappingConverter.write(object, source); + + return executeQuery(source.getGremlinScriptLiteral().generateInsertScript(source)); + } + + @Override + public T insert(@NonNull T object, GremlinSource source) { + final boolean entityGraph = source instanceof GremlinSourceGraph; + + if (!entityGraph && source.getIdField().isAnnotationPresent(GeneratedValue.class) + && source.getId().isPresent()) { + throw new GremlinInvalidEntityIdFieldException("The entity meant to be created has a non-null id " + + "that is marked as @GeneratedValue"); + } + + // The current implementation doesn't support creating graphs that contain both edges + // and vertices that have null (generated) ids. In this case, vertex and edge creation + // need to be performed in two consecutive steps. + // TODO(SOON) Add this verification in the GremlinSourceGraphWriter + + final List results = insertInternal(object, source); + + if (!results.isEmpty()) { + if (entityGraph) { + return recoverGraphDomain((GremlinSourceGraph) source, results); + } else { + return recoverDomain(source, results); + } + } + + return null; + } + + @Override + public T findVertexById(@NonNull Object id, GremlinSource source) { + if (source instanceof GremlinSourceVertex) { + source.setId(id); + return this.findByIdInternal(source); + } + + throw new GremlinUnexpectedEntityTypeException("should be vertex domain for findVertexById"); + } + + private Object getEdgeAnnotatedFieldValue(@NonNull Field field, @NonNull Object vertexId) { + if (field.getType() == String.class || field.getType() == Long.class || field.getType() == Integer.class) { + return vertexId; + } else if (field.getType().isPrimitive()) { + throw new GremlinUnexpectedEntityTypeException("only String/Long/Integer type of Id Field is allowed"); + } else { + return this.findVertexById(vertexId, GremlinUtils.toGremlinSource(field.getType())); + } + } + + @NonNull + private Field getEdgeAnnotatedField(@NonNull Class domainClass, + @NonNull Class annotationClass) { + final List fields = FieldUtils.getFieldsListWithAnnotation(domainClass, annotationClass); + + if (fields.size() != 1) { + throw new GremlinEntityInformationException("should be only one Annotation"); + } + + return fields.get(0); + } + + /** + * Find Edge need another two query to obtain edgeFrom and edgeTo. + * This function will do that and make edge domain completion. + */ + @SuppressWarnings("unchecked") + private void completeEdge(@NonNull T domain, @NonNull GremlinSourceEdge source) { + final ConvertingPropertyAccessor accessor = this.mappingConverter.getPropertyAccessor(domain); + final GremlinPersistentEntity persistentEntity = (GremlinPersistentEntity) this.mappingConverter + .getPersistentEntity(domain.getClass()); + + final Field fromField = this.getEdgeAnnotatedField(domain.getClass(), EdgeFrom.class); + final Field toField = this.getEdgeAnnotatedField(domain.getClass(), EdgeTo.class); + + final PersistentProperty propertyFrom = persistentEntity + .getPersistentProperty(fromField.getName()); + final PersistentProperty propertyTo = persistentEntity + .getPersistentProperty(toField.getName()); + + Assert.notNull(propertyFrom, "persistence property should not be null"); + Assert.notNull(propertyTo, "persistence property should not be null"); + + accessor.setProperty(propertyFrom, this.getEdgeAnnotatedFieldValue(fromField, source.getVertexIdFrom())); + accessor.setProperty(propertyTo, this.getEdgeAnnotatedFieldValue(toField, source.getVertexIdTo())); + } + + @Override + public T findEdgeById(@NonNull Object id, @NonNull GremlinSource source) { + if (source instanceof GremlinSourceEdge) { + return this.findById(id, source); + } + + throw new GremlinUnexpectedEntityTypeException("should be edge domain for findEdge"); + } + + private T findByIdInternal(@NonNull GremlinSource source) { + final List queryList = source.getGremlinScriptLiteral().generateFindByIdScript(source); + final List results = this.executeQuery(queryList); + + if (results.isEmpty()) { + return null; + } + + return recoverDomain(source, results); + } + + @Override + public T findById(@NonNull Object id, @NonNull GremlinSource source) { + if (source instanceof GremlinSourceGraph) { + throw new UnsupportedOperationException("Gremlin graph cannot be findById."); + } + + source.setId(id); + + return findByIdInternal(source); + } + + private T updateInternal(@NonNull T object, @NonNull GremlinSource source) { + this.mappingConverter.write(object, source); + + final List queryList = source.getGremlinScriptLiteral().generateUpdateScript(source); + + executeQuery(queryList); + + return object; + } + + @Override + public T update(@NonNull T object, @NonNull GremlinSource source) { + final Optional optional = source.getId(); + + if (!(source instanceof GremlinSourceGraph) + && (!optional.isPresent() || notExistsById(optional.get(), source))) { + throw new GremlinQueryException("cannot update the object doesn't exist"); + } + + return this.updateInternal(object, source); + } + + @Override + public T save(@NonNull T object, @NonNull GremlinSource source) { + final Optional optional = source.getId(); + final boolean entityGraph = source instanceof GremlinSourceGraph; + + if (entityGraph && this.isEmptyGraph(source)) { + return insert(object, source); + } else if (!entityGraph && (!optional.isPresent() || notExistsById(optional.get(), source))) { + return insert(object, source); + } else { + return updateInternal(object, source); + } + } + + @Override + public List findAll(@NonNull GremlinSource source) { + if (source instanceof GremlinSourceGraph) { + throw new UnsupportedOperationException("Gremlin graph cannot be findAll."); + } + + final List queryList = source.getGremlinScriptLiteral().generateFindAllScript(source); + final List results = executeQuery(queryList); + + if (results.isEmpty()) { + return Collections.emptyList(); + } + + return recoverDomainList(source, results); + } + + @Override + public void deleteById(@NonNull Object id, @NonNull GremlinSource source) { + source.setId(id); + + final List queryList = source.getGremlinScriptLiteral().generateDeleteByIdScript(source); + + executeQuery(queryList); + } + + @Override + public boolean isEmptyGraph(@NonNull GremlinSource source) { + if (source instanceof GremlinSourceGraph) { + final GremlinScriptLiteralGraph literalGraph = (GremlinScriptLiteralGraph) source.getGremlinScriptLiteral(); + final List queryList = literalGraph.generateIsEmptyScript(); + final List results = this.executeQuery(queryList); + + return results.isEmpty(); + } + + throw new GremlinQueryException("only graph domain is allowed."); + } + + @Override + public long vertexCount() { + final GremlinScriptLiteral script = new GremlinScriptLiteralVertex(); + final List queryList = script.generateCountScript(new GremlinSourceVertex<>()); + final List results = this.executeQuery(queryList); + + return results.size(); + } + + @Override + public long edgeCount() { + final GremlinScriptLiteral script = new GremlinScriptLiteralEdge(); + final List queryList = script.generateCountScript(new GremlinSourceEdge<>()); + final List results = this.executeQuery(queryList); + + return results.size(); + } + + private T recoverDomain(@NonNull GremlinSource source, @NonNull List results) { + final T domain; + final Class domainClass = source.getDomainClass(); + + source.doGremlinResultRead(results); + domain = this.mappingConverter.read(domainClass, source); + + if (source instanceof GremlinSourceEdge) { + this.completeEdge(domain, (GremlinSourceEdge) source); + } + + return domain; + } + + private List recoverDomainList(@NonNull GremlinSource source, @NonNull List results) { + return results.stream().map(r -> recoverDomain(source, Collections.singletonList(r))).collect(toList()); + } + + private T recoverGraphDomain(@NonNull GremlinSourceGraph source, @NonNull List results) { + final T domain; + final Class domainClass = source.getDomainClass(); + + source.getResultsReader().read(results, source); + domain = source.doGremlinSourceRead(domainClass, mappingConverter); + return domain; + } + + private boolean notExistsById(@NonNull Object id, @NonNull GremlinSource source) { + return !existsById(id, source); + } + + @Override + public boolean existsById(@NonNull Object id, @NonNull GremlinSource source) { + return findById(id, source) != null; + } + + @Override + public List find(@NonNull GremlinQuery query, @NonNull GremlinSource source) { + final QueryScriptGenerator generator = new QueryFindScriptGenerator<>(source); + final List queryList = generator.generate(query); + final List results = this.executeQuery(queryList); + + if (results.isEmpty()) { + return Collections.emptyList(); + } + + return this.recoverDomainList(source, results); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/SimpleGremlinEntityMetadata.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/SimpleGremlinEntityMetadata.java new file mode 100644 index 000000000000..cfad80f8387b --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/SimpleGremlinEntityMetadata.java @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query; + +public class SimpleGremlinEntityMetadata implements GremlinEntityMetadata { + + private final Class type; + + public SimpleGremlinEntityMetadata(Class type) { + this.type = type; + } + + public Class getJavaType() { + return this.type; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/criteria/Criteria.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/criteria/Criteria.java new file mode 100644 index 000000000000..a41c84d215bc --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/criteria/Criteria.java @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.criteria; + +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.List; + +public final class Criteria { + + private String subject; + private List subValues; + private final CriteriaType type; + private final List subCriteria; + + private Criteria(CriteriaType type) { + this.type = type; + this.subCriteria = new ArrayList<>(); + } + + private static boolean isBinaryOperation(CriteriaType type) { + switch (type) { + case AND: + case OR: + return true; + default: + return false; + } + } + + private static boolean isUnaryOperation(CriteriaType type) { + switch (type) { + case EXISTS: + case AFTER: + case BEFORE: + case BETWEEN: + case IS_EQUAL: + return true; + default: + return false; + } + } + + public static Criteria getUnaryInstance(CriteriaType type, @NonNull String subject, @NonNull List values) { + Assert.isTrue(isUnaryOperation(type), "type should be Unary operation"); + + final Criteria criteria = new Criteria(type); + + criteria.subject = subject; + criteria.subValues = values; + + return criteria; + } + + public static Criteria getBinaryInstance(CriteriaType type, @NonNull Criteria left, @NonNull Criteria right) { + Assert.isTrue(isBinaryOperation(type), "type should be Binary operation"); + + final Criteria criteria = new Criteria(type); + + criteria.subCriteria.add(left); + criteria.subCriteria.add(right); + + Assert.isTrue(criteria.getSubCriteria().size() == 2, "Binary should contain 2 subCriteria"); + + return criteria; + } + + public String getSubject() { + return subject; + } + + public List getSubValues() { + return subValues; + } + + public CriteriaType getType() { + return type; + } + + public List getSubCriteria() { + return subCriteria; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/criteria/CriteriaType.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/criteria/CriteriaType.java new file mode 100644 index 000000000000..5c29af70194c --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/criteria/CriteriaType.java @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.criteria; + +import com.microsoft.spring.data.gremlin.common.Constants; + +public enum CriteriaType { + IS_EQUAL, + OR, + AND, + EXISTS, + AFTER, + BEFORE, + BETWEEN; + + public static String criteriaTypeToGremlin(CriteriaType type) { + switch (type) { + case OR: + return Constants.GREMLIN_PRIMITIVE_OR; + case AND: + return Constants.GREMLIN_PRIMITIVE_AND; + case AFTER: + return Constants.GREMLIN_PRIMITIVE_IS_GT; + case BEFORE: + return Constants.GREMLIN_PRIMITIVE_IS_LT; + case BETWEEN: + return Constants.GREMLIN_PRIMITIVE_IS_BETWEEN; + default: + throw new UnsupportedOperationException("Unsupported criteria type."); + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParameter.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParameter.java new file mode 100644 index 000000000000..848b11048948 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParameter.java @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.paramerter; + +import org.springframework.core.MethodParameter; +import org.springframework.data.repository.query.Parameter; + +public class GremlinParameter extends Parameter { + + public GremlinParameter(MethodParameter parameter) { + super(parameter); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParameterAccessor.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParameterAccessor.java new file mode 100644 index 000000000000..dfe4490ecd9f --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParameterAccessor.java @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.paramerter; + +import org.springframework.data.repository.query.ParameterAccessor; + +public interface GremlinParameterAccessor extends ParameterAccessor { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParameters.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParameters.java new file mode 100644 index 000000000000..f52a3573e594 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParameters.java @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.paramerter; + +import org.springframework.core.MethodParameter; +import org.springframework.data.repository.query.Parameters; + +import java.lang.reflect.Method; +import java.util.List; + +public class GremlinParameters extends Parameters { + + public GremlinParameters(Method method) { + super(method); + } + + private GremlinParameters(List parameters) { + super(parameters); + } + + @Override + protected GremlinParameters createFrom(List parameters) { + return new GremlinParameters(parameters); + } + + @Override + protected GremlinParameter createParameter(MethodParameter parameter) { + return new GremlinParameter(parameter); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParametersParameterAccessor.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParametersParameterAccessor.java new file mode 100644 index 000000000000..99406a011362 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/paramerter/GremlinParametersParameterAccessor.java @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.paramerter; + +import com.microsoft.spring.data.gremlin.query.query.GremlinQueryMethod; +import org.springframework.data.repository.query.ParametersParameterAccessor; + +public class GremlinParametersParameterAccessor extends ParametersParameterAccessor + implements GremlinParameterAccessor { + + public GremlinParametersParameterAccessor(GremlinQueryMethod method, Object[] values) { + super(method.getParameters(), values); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/AbstractGremlinQuery.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/AbstractGremlinQuery.java new file mode 100644 index 000000000000..d152e9264406 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/AbstractGremlinQuery.java @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.query; + +import com.microsoft.spring.data.gremlin.query.GremlinOperations; +import com.microsoft.spring.data.gremlin.query.paramerter.GremlinParameterAccessor; +import com.microsoft.spring.data.gremlin.query.paramerter.GremlinParametersParameterAccessor; +import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.data.repository.query.ResultProcessor; +import org.springframework.lang.NonNull; + +public abstract class AbstractGremlinQuery implements RepositoryQuery { + + private final GremlinQueryMethod method; + private final GremlinOperations operations; + + public AbstractGremlinQuery(@NonNull GremlinQueryMethod method, @NonNull GremlinOperations operations) { + this.method = method; + this.operations = operations; + } + + protected abstract GremlinQuery createQuery(GremlinParameterAccessor accessor); + + protected boolean isDeleteQuery() { + // panli: always return false as only take care find in one PR. + return false; + } + + @Override + public Object execute(@NonNull Object[] parameters) { + final GremlinParameterAccessor accessor = new GremlinParametersParameterAccessor(this.method, parameters); + + final GremlinQuery query = this.createQuery(accessor); + final ResultProcessor processor = method.getResultProcessor().withDynamicProjection(accessor); + final GremlinQueryExecution execution = this.getExecution(); + + return execution.execute(query, processor.getReturnedType().getDomainType()); + } + + @Override + @NonNull + public GremlinQueryMethod getQueryMethod() { + return this.method; + } + + @NonNull + private GremlinQueryExecution getExecution() { + if (this.isDeleteQuery()) { + throw new UnsupportedOperationException("Not implemented yet"); + } else { + return new GremlinQueryExecution.FindExecution(this.operations); + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQuery.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQuery.java new file mode 100644 index 000000000000..bd6875312186 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQuery.java @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.query; + +import com.microsoft.spring.data.gremlin.query.criteria.Criteria; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +public class GremlinQuery { + + private final Criteria criteria; + + public GremlinQuery(@NonNull Criteria criteria) { + Assert.notNull(criteria, "criteria should not be null"); + this.criteria = criteria; + } + + public Criteria getCriteria() { + return criteria; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQueryCreator.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQueryCreator.java new file mode 100644 index 000000000000..ebae283ae145 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQueryCreator.java @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.query; + +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentProperty; +import com.microsoft.spring.data.gremlin.query.criteria.Criteria; +import com.microsoft.spring.data.gremlin.query.criteria.CriteriaType; +import com.microsoft.spring.data.gremlin.query.paramerter.GremlinParameterAccessor; +import org.springframework.data.domain.Sort; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.query.parser.AbstractQueryCreator; +import org.springframework.data.repository.query.parser.Part; +import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class GremlinQueryCreator extends AbstractQueryCreator { + + private final MappingContext mappingContext; + private static final Map CRITERIA_MAP; + + static { + final Map map = new HashMap<>(); + + map.put(Part.Type.AFTER, CriteriaType.AFTER); + map.put(Part.Type.BEFORE, CriteriaType.BEFORE); + map.put(Part.Type.BETWEEN, CriteriaType.BETWEEN); + map.put(Part.Type.SIMPLE_PROPERTY, CriteriaType.IS_EQUAL); + map.put(Part.Type.EXISTS, CriteriaType.EXISTS); + + CRITERIA_MAP = Collections.unmodifiableMap(map); + } + + public GremlinQueryCreator(@NonNull PartTree partTree, @NonNull GremlinParameterAccessor accessor, + @NonNull MappingContext mappingContext) { + super(partTree, accessor); + + this.mappingContext = mappingContext; + } + + @Override // Note (panli): side effect here, this method will change the iterator status of parameters. + protected Criteria create(@NonNull Part part, @NonNull Iterator parameters) { + final Part.Type type = part.getType(); + final String subject = this.mappingContext.getPersistentPropertyPath(part.getProperty()).toDotPath(); + final List values = new ArrayList<>(); + + if (!CRITERIA_MAP.containsKey(type)) { + throw new UnsupportedOperationException("Unsupported keyword: " + type.toString()); + } + + for (int i = 0; i < part.getNumberOfArguments(); i++) { + Assert.isTrue(parameters.hasNext(), "should not reach the end of iterator"); + values.add(parameters.next()); + } + + return Criteria.getUnaryInstance(CRITERIA_MAP.get(type), subject, values); + } + + @Override + protected Criteria and(@NonNull Part part, @NonNull Criteria base, @NonNull Iterator parameters) { + final Criteria right = this.create(part, parameters); + + return Criteria.getBinaryInstance(CriteriaType.AND, base, right); + } + + @Override + protected Criteria or(@NonNull Criteria base, @NonNull Criteria criteria) { + return Criteria.getBinaryInstance(CriteriaType.OR, base, criteria); + } + + @Override + protected GremlinQuery complete(@NonNull Criteria criteria, @NonNull Sort sort) { + return new GremlinQuery(criteria); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQueryExecution.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQueryExecution.java new file mode 100644 index 000000000000..8d417ccd7032 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQueryExecution.java @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.query; + +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.query.GremlinOperations; +import org.springframework.lang.NonNull; + +public interface GremlinQueryExecution { + Object execute(GremlinQuery query, Class type); + + final class FindExecution implements GremlinQueryExecution { + + private final GremlinOperations operations; + + public FindExecution(@NonNull GremlinOperations operations) { + this.operations = operations; + } + + @Override + public Object execute(@NonNull GremlinQuery query, @NonNull Class domainClass) { + final GremlinSource source = GremlinUtils.toGremlinSource(domainClass); + + return this.operations.find(query, source); + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQueryMethod.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQueryMethod.java new file mode 100644 index 000000000000..0fafe5a93616 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/GremlinQueryMethod.java @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.query; + +import com.microsoft.spring.data.gremlin.query.GremlinEntityMetadata; +import com.microsoft.spring.data.gremlin.query.SimpleGremlinEntityMetadata; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.repository.core.EntityMetadata; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.query.QueryMethod; + +import java.lang.reflect.Method; + +public class GremlinQueryMethod extends QueryMethod { + + private GremlinEntityMetadata metadata; + + public GremlinQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory) { + super(method, metadata, factory); + } + + @Override + public EntityMetadata getEntityInformation() { + @SuppressWarnings("unchecked") final Class domainClass = (Class) super.getDomainClass(); + + this.metadata = new SimpleGremlinEntityMetadata<>(domainClass); + + return this.metadata; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/PartTreeGremlinQuery.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/PartTreeGremlinQuery.java new file mode 100644 index 000000000000..368babbd6520 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/PartTreeGremlinQuery.java @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.query; + +import com.microsoft.spring.data.gremlin.mapping.GremlinPersistentProperty; +import com.microsoft.spring.data.gremlin.query.GremlinOperations; +import com.microsoft.spring.data.gremlin.query.paramerter.GremlinParameterAccessor; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.query.ResultProcessor; +import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.lang.NonNull; + +public class PartTreeGremlinQuery extends AbstractGremlinQuery { + + private final PartTree partTree; + private final ResultProcessor processor; + private final MappingContext mappingContext; + + public PartTreeGremlinQuery(@NonNull GremlinQueryMethod method, @NonNull GremlinOperations operations) { + super(method, operations); + + this.processor = method.getResultProcessor(); + this.partTree = new PartTree(method.getName(), processor.getReturnedType().getDomainType()); + this.mappingContext = operations.getMappingConverter().getMappingContext(); + } + + @Override + protected GremlinQuery createQuery(@NonNull GremlinParameterAccessor accessor) { + final GremlinQueryCreator creator = new GremlinQueryCreator(this.partTree, accessor, this.mappingContext); + + if (this.partTree.isLimiting()) { + throw new UnsupportedOperationException("Limitation is not supported yet"); + } + + return creator.createQuery(); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/QueryFindScriptGenerator.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/QueryFindScriptGenerator.java new file mode 100644 index 000000000000..ace11eab3348 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/QueryFindScriptGenerator.java @@ -0,0 +1,164 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.query; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteralHelper; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import com.microsoft.spring.data.gremlin.query.criteria.Criteria; +import com.microsoft.spring.data.gremlin.query.criteria.CriteriaType; +import org.springframework.lang.NonNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class QueryFindScriptGenerator implements QueryScriptGenerator { + + private final GremlinSource source; + + public QueryFindScriptGenerator(@NonNull GremlinSource source) { + this.source = source; + } + + private String getCriteriaSubject(@NonNull Criteria criteria) { + String subject = criteria.getSubject(); + + if (subject.equals(this.source.getIdField().getName())) { + subject = Constants.PROPERTY_ID; // If subject is @Id/id field, use id property in database. + } + + return subject; + } + + private String generateIsEqual(@NonNull Criteria criteria) { + final String subject = getCriteriaSubject(criteria); + + if (subject.equals(Constants.PROPERTY_ID)) { + return String.format(Constants.GREMLIN_PRIMITIVE_WHERE, GremlinScriptLiteralHelper.generateHasId(criteria.getSubValues().get(0))); + } else { + return String.format(Constants.GREMLIN_PRIMITIVE_WHERE, GremlinScriptLiteralHelper.generateHas(subject, criteria.getSubValues().get(0))); + } + } + + /** + * Generate script with only one subject and no subValue, like findByActiveExists(). + * + * @param criteria given query represent a query subject + * @return simple script with keyword from criteria type + */ + private String generateEmptyScript(@NonNull Criteria criteria) { + final String subject = this.getCriteriaSubject(criteria); + final String has = GremlinScriptLiteralHelper.generateHas(subject, true); + + return String.format(Constants.GREMLIN_PRIMITIVE_WHERE, has); + } + + /** + * Generate script with only one subject and only one subValue, like findByCreateAtBefore(Date start). + * + * @param criteria given query represent a query subject + * @return simple script with keyword from criteria type + */ + private String generateSingleScript(@NonNull Criteria criteria) { + final CriteriaType type = criteria.getType(); + final String subject = this.getCriteriaSubject(criteria); + final long milliSeconds = GremlinUtils.timeToMilliSeconds(criteria.getSubValues().get(0)); + + final String values = String.format(Constants.GREMLIN_PRIMITIVE_VALUES, subject); + final String query = String.format(CriteriaType.criteriaTypeToGremlin(type), milliSeconds); + final String content = String.join(Constants.GREMLIN_PRIMITIVE_INVOKE, values, query); + + return String.format(Constants.GREMLIN_PRIMITIVE_WHERE, content); + } + + /** + * Generate script with only one subject and two subValue, like findByCreateAtBetween(Date start, Date end). + * + * @param criteria given query represent a query subject + * @return simple script with keyword from criteria type + */ + private String generateDoubleScript(Criteria criteria) { + final CriteriaType type = criteria.getType(); + final String subject = this.getCriteriaSubject(criteria); + final long start = GremlinUtils.toPrimitiveLong(criteria.getSubValues().get(0)); + final long end = GremlinUtils.toPrimitiveLong(criteria.getSubValues().get(1)); + + final String values = String.format(Constants.GREMLIN_PRIMITIVE_VALUES, subject); + final String query = String.format(CriteriaType.criteriaTypeToGremlin(type), start, end); + final String content = String.join(Constants.GREMLIN_PRIMITIVE_INVOKE, values, query); + + return String.format(Constants.GREMLIN_PRIMITIVE_WHERE, content); + } + + /** + * Generate script combined by AND/OR keyword. + * + * @param left sub script on left + * @param right sub script on right + * @param type should be AND/OR + * @return combined script with AND/OR + */ + private String generateCombinedScript(@NonNull String left, @NonNull String right, CriteriaType type) { + final String operation = CriteriaType.criteriaTypeToGremlin(type); + final String content = String.join(Constants.GREMLIN_PRIMITIVE_INVOKE, left, operation, right); + + return String.format(Constants.GREMLIN_PRIMITIVE_WHERE, content); + } + + + private String generateScriptTraversal(@NonNull Criteria criteria) { + final CriteriaType type = criteria.getType(); + + switch (type) { + case IS_EQUAL: + return this.generateIsEqual(criteria); + case AND: + case OR: + final String left = this.generateScriptTraversal(criteria.getSubCriteria().get(0)); + final String right = this.generateScriptTraversal(criteria.getSubCriteria().get(1)); + + return this.generateCombinedScript(left, right, type); + case AFTER: + case BEFORE: + return this.generateSingleScript(criteria); + case BETWEEN: + return this.generateDoubleScript(criteria); + case EXISTS: + return this.generateEmptyScript(criteria); + default: + throw new UnsupportedOperationException("unsupported Criteria type"); + } + } + + private List generateScript(@NonNull GremlinQuery query) { + final Criteria criteria = query.getCriteria(); + final List scriptList = new ArrayList<>(); + + scriptList.add(Constants.GREMLIN_PRIMITIVE_GRAPH); + + if (this.source instanceof GremlinSourceVertex) { + scriptList.add(Constants.GREMLIN_PRIMITIVE_VERTEX_ALL); + } else if (this.source instanceof GremlinSourceEdge) { + scriptList.add(Constants.GREMLIN_PRIMITIVE_EDGE_ALL); + } else { + throw new UnsupportedOperationException("Cannot generate script from graph entity"); + } + + scriptList.add(GremlinScriptLiteralHelper.generateHasLabel(this.source.getLabel())); + scriptList.add(this.generateScriptTraversal(criteria)); + + return scriptList; + } + + @Override + public List generate(@NonNull GremlinQuery query) { + final List scriptList = new ArrayList<>(this.generateScript(query)); + return Collections.singletonList(String.join(Constants.GREMLIN_PRIMITIVE_INVOKE, scriptList)); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/QueryScriptGenerator.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/QueryScriptGenerator.java new file mode 100644 index 000000000000..17f5dd0021b2 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/query/query/QueryScriptGenerator.java @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.query; + +import java.util.List; + +public interface QueryScriptGenerator { + + List generate(GremlinQuery query); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/GremlinRepository.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/GremlinRepository.java new file mode 100644 index 000000000000..8bb77a651dd3 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/GremlinRepository.java @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.NoRepositoryBean; + +import java.io.Serializable; + +@NoRepositoryBean +public interface GremlinRepository extends CrudRepository { + + Iterable findAll(Class domainClass); + + void deleteAll(GremlinEntityType type); + + void deleteAll(Class domainClass); + + long vertexCount(); + + long edgeCount(); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/config/EnableGremlinRepositories.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/config/EnableGremlinRepositories.java new file mode 100644 index 000000000000..583e96cf4904 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/config/EnableGremlinRepositories.java @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.config; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.repository.support.GremlinRepositoryFactoryBean; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Import; +import org.springframework.data.repository.config.DefaultRepositoryBaseClass; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import(GremlinRepositoryRegistrar.class) +public @interface EnableGremlinRepositories { + + /** + * @return Alias for basePackages(). + */ + String[] value() default {}; + + /** + * @return Base packages to scan for components with annotations. + */ + String[] basePackages() default {}; + + /** + * @return Type-safe alternative to basePackages() for specifying the packages to scan. The package of each class + * specified will be scanned. + */ + Class[] basePackageClasses() default {}; + + /** + * @return Types are eligible for component scanning. + */ + Filter[] includeFilters() default {}; + + /** + * @return Types are not eligible for component scanning. + */ + Filter[] excludeFilters() default {}; + + /** + * @return Specifics the postfix to be used for custom repository implementation class name. + */ + String repositoryImplementationPostfix() default Constants.DEFAULT_REPOSITORY_IMPLEMENT_POSTFIX; + + /** + * @return Configures the repository base class to be used to create repository. + */ + Class repositoryBaseClass() default DefaultRepositoryBaseClass.class; + + /** + * @return Configures whether nested repository interface. + */ + boolean considerNestedRepositories() default false; + + /** + * @return Configure the class of repository factory bean. + */ + Class repositoryFactoryBeanClass() default GremlinRepositoryFactoryBean.class; + + /** + * @return Specific the namedQuery location. + */ + String namedQueriesLocation() default ""; +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryConfigurationExtension.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryConfigurationExtension.java new file mode 100644 index 000000000000..cd8837ba2781 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryConfigurationExtension.java @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.config; + +import com.microsoft.spring.data.gremlin.common.Constants; +import com.microsoft.spring.data.gremlin.mapping.GremlinMappingContext; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import org.apache.commons.lang3.NotImplementedException; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; +import org.springframework.data.repository.config.RepositoryConfigurationSource; + +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.Collections; + +public class GremlinRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport { + + @Override + public String getModuleName() { + return Constants.GREMLIN_MODULE_NAME; + } + + @Override + public String getModulePrefix() { + return Constants.GREMLIN_MODULE_PREFIX; + } + + @Override + public String getRepositoryFactoryBeanClassName() { + throw new NotImplementedException("Gremlin RepositoryFactoryBean is not implemented"); + } + + @Override + public Collection> getIdentifyingTypes() { + return Collections.singleton(GremlinRepository.class); + } + + @Override + public Collection> getIdentifyingAnnotations() { + return Collections.emptyList(); + } + + @Override + public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource config) { + super.registerBeansForRoot(registry, config); + + if (!registry.containsBeanDefinition(Constants.GREMLIN_MAPPING_CONTEXT)) { + final RootBeanDefinition definition = new RootBeanDefinition(GremlinMappingContext.class); + + definition.setRole(AbstractBeanDefinition.ROLE_INFRASTRUCTURE); + definition.setSource(config.getSource()); + + registry.registerBeanDefinition(Constants.GREMLIN_MAPPING_CONTEXT, definition); + } + } + + @Override + public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) { + super.postProcess(builder, source); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryRegistrar.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryRegistrar.java new file mode 100644 index 000000000000..50d9d9f7f9e6 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryRegistrar.java @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.config; + +import org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport; +import org.springframework.data.repository.config.RepositoryConfigurationExtension; + +import java.lang.annotation.Annotation; + +public class GremlinRepositoryRegistrar extends RepositoryBeanDefinitionRegistrarSupport { + + @Override + protected Class getAnnotation() { + return EnableGremlinRepositories.class; + } + + @Override + protected RepositoryConfigurationExtension getExtension() { + return new GremlinRepositoryConfigurationExtension(); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/GremlinEntityInformation.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/GremlinEntityInformation.java new file mode 100644 index 000000000000..dd1a133bf6f5 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/GremlinEntityInformation.java @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.support; + +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceGraph; +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.annotation.GeneratedValue; +import com.microsoft.spring.data.gremlin.annotation.Graph; +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import com.microsoft.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException; +import org.springframework.data.repository.core.support.AbstractEntityInformation; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Field; + + +public class GremlinEntityInformation extends AbstractEntityInformation { + + private final Field idField; + + public GremlinEntityInformation(@NonNull Class domainClass) { + super(domainClass); + + this.idField = this.getIdField(domainClass); + } + + private GremlinSource createGremlinSource(@NonNull Class domainClass, @NonNull Field idField) { + final String label; + final String domainClassName = domainClass.getSimpleName(); + final Vertex vertex = domainClass.getAnnotation(Vertex.class); + final Edge edge = domainClass.getAnnotation(Edge.class); + final Graph graph = domainClass.getAnnotation(Graph.class); + final GremlinSource source; + + if (vertex != null && edge == null && graph == null) { + source = new GremlinSourceVertex<>(domainClass); + label = vertex.label().isEmpty() ? domainClassName : vertex.label(); + } else if (edge != null && vertex == null && graph == null) { + source = new GremlinSourceEdge<>(domainClass); + label = edge.label().isEmpty() ? domainClassName : edge.label(); + } else if (graph != null && vertex == null && edge == null) { + source = new GremlinSourceGraph<>(domainClass); + label = ""; + } else { + throw new GremlinUnexpectedEntityTypeException("Unexpected gremlin entity type"); + } + + source.setLabel(label); + source.setIdField(idField); + + return source; + } + + public GremlinSource createGremlinSource() { + return createGremlinSource(super.getJavaType(), idField); + } + + @Override + @Nullable + public ID getId(T entity) { + final Field idField = this.idField; + @SuppressWarnings("unchecked") final ID id = (ID) ReflectionUtils.getField(idField, entity); + + if (id == null && !(super.getJavaType().isAnnotationPresent(Graph.class)) + && !idField.isAnnotationPresent(GeneratedValue.class)) { + throw new GremlinInvalidEntityIdFieldException("A non-generated id field cannot be null!"); + } + return id; + } + + @Override + public Class getIdType() { + @SuppressWarnings("unchecked") final Class idClass = (Class) this.idField.getType(); + + return idClass; + } + + @NonNull + private Field getIdField(@NonNull Class domainClass) { + final Field idField = GremlinUtils.getIdField(domainClass); + + ReflectionUtils.makeAccessible(idField); + + return idField; + } + + public Field getIdField() { + return idField; + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactory.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactory.java new file mode 100644 index 000000000000..874c4e8dec35 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactory.java @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.support; + +import com.microsoft.spring.data.gremlin.query.GremlinOperations; +import com.microsoft.spring.data.gremlin.query.query.GremlinQueryMethod; +import com.microsoft.spring.data.gremlin.query.query.PartTreeGremlinQuery; +import org.springframework.context.ApplicationContext; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.repository.core.EntityInformation; +import org.springframework.data.repository.core.NamedQueries; +import org.springframework.data.repository.core.RepositoryInformation; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.core.support.RepositoryFactorySupport; +import org.springframework.data.repository.query.QueryLookupStrategy; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.Optional; + +public class GremlinRepositoryFactory extends RepositoryFactorySupport { + + private final ApplicationContext context; + private final GremlinOperations operations; + + public GremlinRepositoryFactory(@NonNull GremlinOperations operations, ApplicationContext context) { + this.operations = operations; + this.context = context; + } + + @Override + protected Class getRepositoryBaseClass(RepositoryMetadata metadata) { + return SimpleGremlinRepository.class; + } + + @Override + protected Object getTargetRepository(RepositoryInformation information) { + final EntityInformation entityInfo = this.getEntityInformation(information.getDomainType()); + + return getTargetRepositoryViaReflection(information, entityInfo, this.context); + } + + @Override + public EntityInformation getEntityInformation(Class domainClass) { + return new GremlinEntityInformation<>(domainClass); + } + + @Override + protected Optional getQueryLookupStrategy( + QueryLookupStrategy.Key key, QueryMethodEvaluationContextProvider provider) { + return Optional.of(new GremlinQueryLookupStrategy(this.operations)); + } + + private static class GremlinQueryLookupStrategy implements QueryLookupStrategy { + + private final GremlinOperations operations; + + GremlinQueryLookupStrategy(@NonNull GremlinOperations operations) { + this.operations = operations; + } + + @Override + public RepositoryQuery resolveQuery(@NonNull Method method, RepositoryMetadata metadata, + ProjectionFactory factory, NamedQueries namedQueries) { + final GremlinQueryMethod queryMethod = new GremlinQueryMethod(method, metadata, factory); + + Assert.notNull(queryMethod, "queryMethod should not be null"); + Assert.notNull(this.operations, "operations should not be null"); + + return new PartTreeGremlinQuery(queryMethod, this.operations); + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactoryBean.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactoryBean.java new file mode 100644 index 000000000000..9ae405f0719c --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactoryBean.java @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.support; + +import com.microsoft.spring.data.gremlin.mapping.GremlinMappingContext; +import com.microsoft.spring.data.gremlin.query.GremlinOperations; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; +import org.springframework.data.repository.core.support.RepositoryFactorySupport; + +import java.io.Serializable; + +public class GremlinRepositoryFactoryBean, S, ID extends Serializable> + extends RepositoryFactoryBeanSupport implements ApplicationContextAware { + + private ApplicationContext context; + private GremlinOperations operations; + private boolean mappingContextConfigured = false; + + public GremlinRepositoryFactoryBean(Class repositoryInterface) { + super(repositoryInterface); + } + + @Autowired + public void setGremlinOperations(GremlinOperations operations) { + this.operations = operations; + } + + protected RepositoryFactorySupport getFactoryInstance(ApplicationContext context) { + return new GremlinRepositoryFactory(this.operations, context); + } + + @Override + protected final RepositoryFactorySupport createRepositoryFactory() { + return this.getFactoryInstance(this.context); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.context = context; + } + + @Override + protected void setMappingContext(MappingContext mappingContext) { + super.setMappingContext(mappingContext); + + this.mappingContextConfigured = true; + } + + @Override + public void afterPropertiesSet() { + super.afterPropertiesSet(); + + if (!this.mappingContextConfigured) { + if (this.operations == null) { + this.setMappingContext(new GremlinMappingContext()); + } else { + this.setMappingContext(this.operations.getMappingConverter().getMappingContext()); + } + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/SimpleGremlinRepository.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/SimpleGremlinRepository.java new file mode 100644 index 000000000000..3c1b5b2e8de8 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/repository/support/SimpleGremlinRepository.java @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.support; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceGraph; +import com.microsoft.spring.data.gremlin.query.GremlinOperations; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import org.springframework.context.ApplicationContext; +import org.springframework.lang.NonNull; + +import java.io.Serializable; +import java.util.List; +import java.util.Optional; +import java.util.stream.StreamSupport; + +import static java.util.stream.Collectors.toList; + +public class SimpleGremlinRepository implements GremlinRepository { + + private final GremlinEntityInformation information; + + private final GremlinOperations operations; + + public SimpleGremlinRepository(GremlinEntityInformation information, @NonNull ApplicationContext context) { + this(information, context.getBean(GremlinOperations.class)); + } + + public SimpleGremlinRepository(GremlinEntityInformation information, @NonNull GremlinOperations operations) { + this.operations = operations; + this.information = information; + } + + @Override + @SuppressWarnings("unchecked") + public S save(@NonNull S domain) { + final GremlinSource source = this.information.createGremlinSource(); + + source.setId(this.information.getId(domain)); + + return (S) this.operations.save(domain, source); + } + + @Override + public Iterable saveAll(@NonNull Iterable domains) { + return StreamSupport.stream(domains.spliterator(), true).map(this::save).collect(toList()); + } + + @Override + public Iterable findAll() { + final GremlinSource source = this.information.createGremlinSource(); + + if (source instanceof GremlinSourceGraph) { + throw new UnsupportedOperationException("findAll of Graph is not supported"); + } + + return this.operations.findAll(source); + } + + @Override + public List findAllById(@NonNull Iterable ids) { + return StreamSupport.stream(ids.spliterator(), true).map(this::findById) + .filter(Optional::isPresent).map(Optional::get).collect(toList()); + } + + @Override + public Optional findById(@NonNull ID id) { + final T domain = this.operations.findById(id, this.information.createGremlinSource()); + + return domain == null ? Optional.empty() : Optional.of(domain); + } + + @Override + public Iterable findAll(@NonNull Class domainClass) { + return findAll(); + } + + @Override + public long vertexCount() { + return this.operations.vertexCount(); + } + + @Override + public long edgeCount() { + return this.operations.edgeCount(); + } + + /** + * The total number of vertex and edge, vertexCount and edgeCount is also available. + * + * @return the count of both vertex and edge. + */ + @Override + public long count() { + return this.vertexCount() + this.edgeCount(); + } + + @Override + public void delete(@NonNull T domain) { + this.operations.deleteById(this.information.getId(domain), this.information.createGremlinSource()); + } + + @Override + public void deleteById(@NonNull ID id) { + this.operations.deleteById(id, this.information.createGremlinSource()); + } + + @Override + public void deleteAll() { + this.operations.deleteAll(); + } + + @Override + public void deleteAll(GremlinEntityType type) { + this.operations.deleteAll(type); + } + + @Override + public void deleteAll(@NonNull Iterable domains) { + domains.forEach(this::delete); + } + + @Override + public void deleteAll(@NonNull Class domainClass) { + this.operations.deleteAll(this.information.createGremlinSource()); + } + + @Override + public boolean existsById(@NonNull ID id) { + return this.operations.existsById(id, this.information.createGremlinSource()); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/MacAddress.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/MacAddress.java new file mode 100644 index 000000000000..1d2024f1ebb2 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/MacAddress.java @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* + * Disclaimer: + * This class is copied from https://github.com/Microsoft/azure-tools-for-java/ with minor modification (fixing + * static analysis error). + * Location in the repo: /Utils/azuretools-core/src/com/microsoft/azuretools/azurecommons/util/MacAddress.java + */ +package com.microsoft.spring.data.gremlin.telemetry; + +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public class MacAddress { + + private static final String UNKNOWN_MAC_ADDRESS = "Unknown-Mac-Address"; + private static final String MAC_REGEX = "([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}"; + private static final String MAC_REGEX_ZERO = "([0]{2}[:-]){5}[0]{2}"; + private static final String HASHED_MAC_REGEX = "[0-9a-f]{64}"; + + public static boolean isValidHashMacFormat(@NonNull String hashMac) { + Assert.notNull(hashMac, "hashMac should not be null"); + if (hashMac.isEmpty()) { + return false; + } + + return Pattern.compile(HASHED_MAC_REGEX).matcher(hashMac).matches(); + } + + private static String getRawMac() { + final List commands; + final String os = System.getProperty("os.name"); + final StringBuilder macBuilder = new StringBuilder(); + + if (os != null && !os.isEmpty() && os.toLowerCase(Locale.US).startsWith("win")) { + commands = Collections.singletonList("getmac"); + } else { + commands = Arrays.asList("ifconfig", "-a"); + } + + try { + String tmp; + final ProcessBuilder builder = new ProcessBuilder(commands); + final Process process = builder.start(); + try (InputStreamReader streamReader = new InputStreamReader(process.getInputStream(), UTF_8); + BufferedReader reader = new BufferedReader(streamReader)) { + while ((tmp = reader.readLine()) != null) { + macBuilder.append(tmp); + } + } + } catch (IOException e) { + return ""; + } + return macBuilder.toString(); + } + + private static String getHexDigest(byte digest) { + final String hex = Integer.toString((digest & 0xff) + 0x100, 16); + + return hex.substring(1); + } + + private static String hash(@NonNull String mac) { + Assert.notNull(mac, "mac should not be null"); + if (mac.isEmpty()) { + return ""; + } + + final StringBuilder builder = new StringBuilder(); + + try { + final MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + + messageDigest.update(mac.getBytes(UTF_8)); + + final byte[] digestBytes = messageDigest.digest(); + + for (final byte digest : digestBytes) { + builder.append(getHexDigest(digest)); + } + } catch (NoSuchAlgorithmException ex) { + return ""; + } + + Assert.isTrue( + + isValidHashMacFormat(builder.toString()), "Invalid format for HashMac"); + + return builder.toString(); + } + + public static String getHashMac() { + final String rawMac = getRawMac(); + + if (rawMac.isEmpty()) { + return UNKNOWN_MAC_ADDRESS; + } + + final Pattern pattern = Pattern.compile(MAC_REGEX); + final Pattern patternZero = Pattern.compile(MAC_REGEX_ZERO); + final Matcher matcher = pattern.matcher(rawMac); + + String mac = ""; + + while (matcher.find()) { + mac = matcher.group(0); + + if (!patternZero.matcher(mac).matches()) { + break; + } + } + + final String hashMac = hash(mac); + + if (StringUtils.hasText(hashMac)) { + return hashMac; + } + + return UNKNOWN_MAC_ADDRESS; + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/PropertyLoader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/PropertyLoader.java new file mode 100644 index 000000000000..8ffb663411d3 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/PropertyLoader.java @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.telemetry; + +import org.springframework.lang.NonNull; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public final class PropertyLoader { + + private PropertyLoader() { + + } + + private static final String PROJECT_PROPERTY_FILE = "/META-INF/project.properties"; + + private static final String TELEMETRY_CONFIG_FILE = "/telemetry.config"; + + public static String getProjectVersion() { + return getPropertyByName("project.version", PROJECT_PROPERTY_FILE); + } + + public static String getTelemetryInstrumentationKey() { + return getPropertyByName("telemetry.instrumentationKey", TELEMETRY_CONFIG_FILE); + } + + private static String getPropertyByName(@NonNull String name, @NonNull String filename) { + final Properties properties = new Properties(); + final InputStream inputStream = PropertyLoader.class.getResourceAsStream(filename); + + if (inputStream == null) { + return null; + } + + try { + properties.load(inputStream); + } catch (IOException e) { + // Omitted + } finally { + try { + inputStream.close(); + } catch (IOException e) { + // Omitted + } + } + + return properties.getProperty(name); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/TelemetryEventData.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/TelemetryEventData.java new file mode 100644 index 000000000000..87ad6ba2b9b9 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/TelemetryEventData.java @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.telemetry; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import java.time.Instant; +import java.util.Map; + +public class TelemetryEventData { + + private final String name; + + @JsonProperty("iKey") + private final String instrumentationKey; + + private final Tags tags = new Tags("Spring-on-azure", "Java-maven-plugin"); + + private final EventData data = new EventData("EventData"); + + private final String time; + + public TelemetryEventData(String eventName, @NonNull Map properties) { + Assert.notNull(properties, "properties should not be null"); + Assert.hasText(eventName, "Event name should contain text."); + + name = "Microsoft.ApplicationInsights.Event"; + instrumentationKey = PropertyLoader.getTelemetryInstrumentationKey(); + + data.getBaseData().setName(eventName); + data.getBaseData().setProperties(properties); + time = Instant.now().toString(); + } + + private static class Tags { + + @JsonProperty("ai.cloud.roleInstance") + private final String aiCloudRoleInstance; + + @JsonProperty("ai.internal.sdkVersion") + private final String aiInternalSdkVersion; + + Tags(String instance, String sdkVersion) { + aiCloudRoleInstance = instance; + aiInternalSdkVersion = sdkVersion; + } + + public String getAiCloudRoleInstance() { + return aiCloudRoleInstance; + } + + public String getAiInternalSdkVersion() { + return aiInternalSdkVersion; + } + } + + private static class EventData { + + private final String baseType; + + private final CustomData baseData = new CustomData(); + + EventData(String baseType) { + this.baseType = baseType; + } + + private static class CustomData { + + private final Integer ver = 2; + + private String name; + + private Map properties; + + public Integer getVer() { + return ver; + } + + public String getName() { + return name; + } + + public Map getProperties() { + return properties; + } + + private void setName(String name) { + this.name = name; + } + + private void setProperties(Map properties) { + this.properties = properties; + } + } + + public String getBaseType() { + return baseType; + } + + public CustomData getBaseData() { + return baseData; + } + } + + public String getName() { + return name; + } + + public String getInstrumentationKey() { + return instrumentationKey; + } + + public Tags getTags() { + return tags; + } + + public EventData getData() { + return data; + } + + public String getTime() { + return time; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/TelemetrySender.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/TelemetrySender.java new file mode 100644 index 000000000000..3bc2cc002f11 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/telemetry/TelemetrySender.java @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.telemetry; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.Map; + +import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON; + +public class TelemetrySender { + + private static final Logger LOGGER = LoggerFactory.getLogger(TelemetrySender.class); + + private static final String PROPERTY_INSTALLATION_ID = "installationId"; + + private static final String PROPERTY_VERSION = "version"; + + private static final String PROPERTY_SERVICE_NAME = "serviceName"; + + private static final String PROJECT_INFO = "spring-data-gremlin/" + PropertyLoader.getProjectVersion(); + + private static final String TELEMETRY_TARGET_URL = "https://dc.services.visualstudio.com/v2/track"; + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + private static final int RETRY_LIMIT = 3; // Align the retry times with sdk + + private static final RestTemplate REST_TEMPLATE = new RestTemplate(); + + private static final HttpHeaders HEADERS = new HttpHeaders(); + + static { + HEADERS.add(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON.toString()); + } + + private ResponseEntity executeRequest(final TelemetryEventData eventData) { + try { + final HttpEntity body = new HttpEntity<>(MAPPER.writeValueAsString(eventData), HEADERS); + + return REST_TEMPLATE.exchange(TELEMETRY_TARGET_URL, HttpMethod.POST, body, String.class); + } catch (RestClientException | JsonProcessingException e) { + LOGGER.trace("Failed to exchange telemetry request, {}.", e.getMessage()); + } + + return null; + } + + private void sendTelemetryData(@NonNull TelemetryEventData eventData) { + Assert.notNull(eventData, "eventData should not be null"); + ResponseEntity response = null; + + for (int i = 0; i < RETRY_LIMIT; i++) { + response = executeRequest(eventData); + + if (response != null && response.getStatusCode() == HttpStatus.OK) { + return; + } + } + + if (response != null && response.getStatusCode() != HttpStatus.OK) { + LOGGER.trace("Failed to send telemetry data, response status code {}.", response.getStatusCode().toString()); + } + } + + public void send(String name) { + Assert.hasText(name, "Event name should contain text."); + + sendTelemetryData(new TelemetryEventData(name, getProperties())); + } + + private Map getProperties() { + final Map properties = new HashMap<>(); + + properties.put(PROPERTY_VERSION, PROJECT_INFO); + properties.put(PROPERTY_SERVICE_NAME, "gremlin"); + properties.put(PROPERTY_INSTALLATION_ID, MacAddress.getHashMac()); + + return properties; + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/resources/META-INF/project.properties b/sdk/spring/azure-spring-data-gremlin/src/main/resources/META-INF/project.properties new file mode 100644 index 000000000000..90d42083480f --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/resources/META-INF/project.properties @@ -0,0 +1 @@ +project.version=@project.version@ diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/resources/META-INF/spring.factories b/sdk/spring/azure-spring-data-gremlin/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000000..d0072b45607c --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.data.repository.core.support.RepositoryFactorySupport=com.microsoft.spring.data.gremlin.repository.support.GremlinRepositoryFactory + diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/resources/application.properties b/sdk/spring/azure-spring-data-gremlin/src/main/resources/application.properties new file mode 100644 index 000000000000..42947c4763dc --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/resources/application.properties @@ -0,0 +1,4 @@ +gremlin.endpoint=endpoint.gremlin.cosmosdb.azure.com +gremlin.port=443 +gremlin.username=/dbs/database/colls/collection +gremlin.password=password diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/resources/telemetry.config b/sdk/spring/azure-spring-data-gremlin/src/main/resources/telemetry.config new file mode 100644 index 000000000000..61b615fa0ef1 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/main/resources/telemetry.config @@ -0,0 +1 @@ +telemetry.instrumentationKey=@telemetry.instrumentationKey@ \ No newline at end of file diff --git a/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/Network.java b/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/Network.java new file mode 100644 index 000000000000..a7a7de0cc2be --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/Network.java @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.data.gremlin; + +import com.microsoft.spring.data.gremlin.annotation.EdgeSet; +import com.microsoft.spring.data.gremlin.annotation.Graph; +import com.microsoft.spring.data.gremlin.annotation.VertexSet; +import org.springframework.data.annotation.Id; + +import java.util.ArrayList; +import java.util.List; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + *

+ * Code samples for the spring-data-gremlin in README.md + */ +@Graph +public class Network { + + @Id + private String id; + + public Network() { + this.edges = new ArrayList<>(); + this.vertexes = new ArrayList<>(); + } + + @EdgeSet + private List edges; + + @VertexSet + private List vertexes; + +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/Person.java b/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/Person.java new file mode 100644 index 000000000000..da702a98e35d --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/Person.java @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.data.gremlin; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import org.springframework.data.annotation.Id; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + *

+ * Code samples for the spring-data-gremlin in README.md + */ +@Vertex +public class Person { + + @Id + private String id; + + private String name; + + private String age; + + public Person() { + + } + + public Person(String id, String name, String age) { + this.id = id; + this.name = name; + this.age = age; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/PersonRepository.java b/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/PersonRepository.java new file mode 100644 index 000000000000..05dca133db2f --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/PersonRepository.java @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.data.gremlin; + +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + *

+ * Code samples for the spring-data-gremlin in README.md + */ +@Repository +public interface PersonRepository extends GremlinRepository { + + List findByName(String name); + +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/Relation.java b/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/Relation.java new file mode 100644 index 000000000000..eddb6e5bff18 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/Relation.java @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.data.gremlin; + +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; +import org.springframework.data.annotation.Id; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + *

+ * Code samples for the spring-data-gremlin in README.md + */ +@Edge +public class Relation { + + @Id + private String id; + + private String name; + + @EdgeFrom + private Person personFrom; + + @EdgeTo + private Person personTo; + +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/SampleApplication.java b/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/SampleApplication.java new file mode 100644 index 000000000000..e5066b7966cc --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/samples/java/com/azure/spring/data/gremlin/SampleApplication.java @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.data.gremlin; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + *

+ * Code samples for the spring-data-gremlin in README.md + */ +@SpringBootApplication +public class SampleApplication implements CommandLineRunner { + + @Autowired + private PersonRepository repository; + + public static void main(String[] args) { + SpringApplication.run(SampleApplication.class, args); + } + + public void run(String... var1) { + + final Person testUser = new Person("PERSON_ID", "PERSON_NAME", "PERSON_AGE"); + repository.deleteAll(); + repository.save(testUser); + } + +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/annotation/AnnotationEdgeUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/annotation/AnnotationEdgeUnitTest.java new file mode 100644 index 000000000000..c79c7acd5d40 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/annotation/AnnotationEdgeUnitTest.java @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.annotation; + +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.common.domain.Dependency; +import com.microsoft.spring.data.gremlin.common.domain.Relationship; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import org.junit.Assert; +import org.junit.Test; + +public class AnnotationEdgeUnitTest { + + @Test + public void testAnnotationEdgeDefaultLabel() { + final GremlinSource source = GremlinUtils.toGremlinSource(Dependency.class); + + Assert.assertTrue(source instanceof GremlinSourceEdge); + Assert.assertNotNull(source.getLabel()); + Assert.assertEquals(source.getLabel(), Dependency.class.getSimpleName()); + } + + @Test + public void testAnnotationEdgeSpecifiedLabel() { + final GremlinSource source = GremlinUtils.toGremlinSource(Relationship.class); + + Assert.assertTrue(source instanceof GremlinSourceEdge); + Assert.assertNotNull(source.getLabel()); + Assert.assertEquals(source.getLabel(), TestConstants.EDGE_RELATIONSHIP_LABEL); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/annotation/AnnotationGraphUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/annotation/AnnotationGraphUnitTest.java new file mode 100644 index 000000000000..548ed1a0b474 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/annotation/AnnotationGraphUnitTest.java @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.annotation; + +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.common.domain.Network; +import com.microsoft.spring.data.gremlin.common.domain.Roadmap; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceGraph; +import org.junit.Assert; +import org.junit.Test; + +public class AnnotationGraphUnitTest { + + @Test + public void testAnnotationGraphDefaultCollection() { + final GremlinSource source = GremlinUtils.toGremlinSource(Network.class); + + Assert.assertTrue(source instanceof GremlinSourceGraph); + Assert.assertTrue(source.getLabel().isEmpty()); + } + + @Test + public void testAnnotationGraphSpecifiedCollection() { + final GremlinSource source = GremlinUtils.toGremlinSource(Roadmap.class); + + Assert.assertTrue(source instanceof GremlinSourceGraph); + Assert.assertTrue(source.getLabel().isEmpty()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/annotation/AnnotationVertexUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/annotation/AnnotationVertexUnitTest.java new file mode 100644 index 000000000000..f620f9f4b69b --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/annotation/AnnotationVertexUnitTest.java @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.annotation; + +import com.microsoft.spring.data.gremlin.common.GremlinUtils; +import com.microsoft.spring.data.gremlin.common.domain.Library; +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import org.junit.Assert; +import org.junit.Test; + +public class AnnotationVertexUnitTest { + + @Test + public void testAnnotationVertexDefaultLabel() { + final GremlinSource source = GremlinUtils.toGremlinSource(Library.class); + + Assert.assertTrue(source instanceof GremlinSourceVertex); + Assert.assertNotNull(source.getLabel()); + Assert.assertEquals(source.getLabel(), Library.class.getSimpleName()); + } + + @Test + public void testAnnotationVertexSpecifiedLabel() { + final GremlinSource source = GremlinUtils.toGremlinSource(Person.class); + + Assert.assertTrue(source instanceof GremlinSourceVertex); + Assert.assertNotNull(source.getLabel()); + Assert.assertEquals(source.getLabel(), TestConstants.VERTEX_PERSON_LABEL); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/GremlinFactoryUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/GremlinFactoryUnitTest.java new file mode 100644 index 000000000000..fe75d45ac789 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/GremlinFactoryUnitTest.java @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +import com.microsoft.spring.data.gremlin.exception.GremlinIllegalConfigurationException; +import org.apache.tinkerpop.gremlin.driver.Client; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static com.microsoft.spring.data.gremlin.common.TestConstants.EMPTY_STRING; +import static com.microsoft.spring.data.gremlin.common.TestConstants.ILLEGAL_ENDPOINT_PORT; + +@ContextConfiguration(classes = {GremlinFactoryUnitTest.TestConfiguration.class}) +@RunWith(SpringJUnit4ClassRunner.class) +public class GremlinFactoryUnitTest { + + @Autowired + private GremlinFactory factory; + + @Test(expected = GremlinIllegalConfigurationException.class) + public void testGremlinFactoryException() { + final GremlinConfig config = GremlinConfig.builder(TestConstants.FAKE_ENDPOINT, TestConstants.FAKE_USERNAME, + TestConstants.FAKE_PASSWORD).build(); + + new GremlinFactory(config).getGremlinClient(); + } + + @Test + public void testGremlinFactoryNormal() { + final Client client = factory.getGremlinClient(); + + Assert.assertEquals(client.getCluster().getPort(), TestConstants.DEFAULT_ENDPOINT_PORT); + Assert.assertFalse(client.getSettings().getSession().isPresent()); + } + + @Configuration + static class TestConfiguration { + + @Bean + public GremlinFactory getGremlinFactory() { + return new GremlinFactory(getGremlinConfig()); + } + + @Bean + public GremlinConfig getGremlinConfig() { + return GremlinConfig.builder(EMPTY_STRING, EMPTY_STRING, EMPTY_STRING) + .port(ILLEGAL_ENDPOINT_PORT) + .build(); + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/GremlinUtilsUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/GremlinUtilsUnitTest.java new file mode 100644 index 000000000000..0a799fbcb9cf --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/GremlinUtilsUnitTest.java @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +import com.microsoft.spring.data.gremlin.common.domain.Service; +import com.microsoft.spring.data.gremlin.conversion.source.AbstractGremlinSource; +import org.junit.Assert; +import org.junit.Test; + +public class GremlinUtilsUnitTest { + + @Test(expected = IllegalArgumentException.class) + public void testCreateIntegerInstance() { + GremlinUtils.createInstance(Integer.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateTestConstantsInstance() { + GremlinUtils.createInstance(TestConstants.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateAbstractInstance() { + GremlinUtils.createInstance(AbstractGremlinSource.class); + } + + @Test(expected = UnsupportedOperationException.class) + public void testTimeToMilliSecondsException() { + GremlinUtils.timeToMilliSeconds(new Service()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testToPrimitiveLongException() { + GremlinUtils.toPrimitiveLong((short) 2); + } + + @Test + public void testToPrimitiveLong() { + Assert.assertEquals(3, GremlinUtils.toPrimitiveLong(3L)); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/MacAddressUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/MacAddressUnitTest.java new file mode 100644 index 000000000000..2eb256ac2937 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/MacAddressUnitTest.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +import com.microsoft.spring.data.gremlin.telemetry.MacAddress; +import org.junit.Assert; +import org.junit.Test; + +public class MacAddressUnitTest { + + @Test + public void testGetHashMacNormal() { + Assert.assertNotNull(MacAddress.getHashMac()); + Assert.assertFalse(MacAddress.getHashMac().isEmpty()); + Assert.assertFalse(MacAddress.isValidHashMacFormat("")); + Assert.assertTrue(MacAddress.isValidHashMacFormat(MacAddress.getHashMac())); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestConstants.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestConstants.java new file mode 100644 index 000000000000..b3efdfdbe2e5 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestConstants.java @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +public final class TestConstants { + + private TestConstants() { + + } + + public static final int DEFAULT_ENDPOINT_PORT = 443; + public static final int ILLEGAL_ENDPOINT_PORT = -1; + public static final String FAKE_ENDPOINT = "XXX-xxx.XXX-xxx.cosmosdb.azure.com"; + public static final String FAKE_USERNAME = "XXX-xxx.username"; + public static final String FAKE_PASSWORD = "XXX-xxx.password"; + public static final String EMPTY_STRING = ""; + + public static final String PROPERTY_ID = "id"; + public static final String PROPERTY_NAME = "name"; + public static final String PROPERTY_LOCATION = "location"; + + public static final String VERTEX_PERSON_LABEL = "label-person"; + public static final String VERTEX_PROJECT_LABEL = "label-project"; + public static final String EDGE_RELATIONSHIP_LABEL = "label-relationship"; + public static final String GRAPH_ROADMAP_COLLECTION_NAME = "roadmap-collection"; + + public static final String VERTEX_PERSON_ID = "233333"; + public static final String VERTEX_PERSON_NAME = "incarnation-p-lee"; + + public static final String VERTEX_PERSON_0_ID = "000000"; + public static final String VERTEX_PERSON_0_NAME = "silencer"; + + public static final String VERTEX_PERSON_1_ID = "111111"; + public static final String VERTEX_PERSON_1_NAME = "templar-assassin"; + + public static final String VERTEX_PROJECT_ID = "666666"; + public static final String VERTEX_PROJECT_NAME = "spring-data-gremlin"; + public static final String VERTEX_PROJECT_URI = "https://github.com/Incarnation-p-lee/spring-data-gremlin.git"; + + public static final String VERTEX_PROJECT_0_ID = "222222"; + public static final String VERTEX_PROJECT_0_NAME = "spring-data-documentdb"; + public static final String VERTEX_PROJECT_0_URI = "https://github.com/Microsoft/spring-data-documentdb"; + + public static final String EDGE_RELATIONSHIP_ID = "999999"; + public static final String EDGE_RELATIONSHIP_NAME = "created"; + public static final String EDGE_RELATIONSHIP_LOCATION = "shanghai"; + + public static final String EDGE_RELATIONSHIP_0_ID = "333333"; + public static final String EDGE_RELATIONSHIP_0_NAME = "contributed"; + public static final String EDGE_RELATIONSHIP_0_LOCATION = "war3"; + + public static final String EDGE_RELATIONSHIP_1_ID = "444444"; + public static final String EDGE_RELATIONSHIP_1_NAME = "contributed"; + public static final String EDGE_RELATIONSHIP_1_LOCATION = "dota"; + + public static final String EDGE_RELATIONSHIP_2_ID = "555555"; + public static final String EDGE_RELATIONSHIP_2_NAME = "create"; + public static final String EDGE_RELATIONSHIP_2_LOCATION = "shanghai"; + + public static final String VERTEX_LABEL = "label-vertex"; +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestGremlinProperties.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestGremlinProperties.java new file mode 100644 index 000000000000..3ac5184feccd --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestGremlinProperties.java @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +import org.apache.tinkerpop.gremlin.driver.ser.Serializers; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("gremlin") +public class TestGremlinProperties { + private String endpoint; + + private int port; + + private String username; + + private String password; + + private boolean sslEnabled = true; + + private boolean telemetryAllowed = true; + + private String serializer = Serializers.GRAPHSON.toString(); + + public TestGremlinProperties() { + } + + public TestGremlinProperties(String endpoint, int port, String username, String password, boolean sslEnabled, boolean telemetryAllowed, String serializer) { + this.endpoint = endpoint; + this.port = port; + this.username = username; + this.password = password; + this.sslEnabled = sslEnabled; + this.telemetryAllowed = telemetryAllowed; + this.serializer = serializer; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public boolean isSslEnabled() { + return sslEnabled; + } + + public void setSslEnabled(boolean sslEnabled) { + this.sslEnabled = sslEnabled; + } + + public boolean isTelemetryAllowed() { + return telemetryAllowed; + } + + public void setTelemetryAllowed(boolean telemetryAllowed) { + this.telemetryAllowed = telemetryAllowed; + } + + public String getSerializer() { + return serializer; + } + + public void setSerializer(String serializer) { + this.serializer = serializer; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestRepositoryConfiguration.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestRepositoryConfiguration.java new file mode 100644 index 000000000000..29011a6b5632 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestRepositoryConfiguration.java @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +import com.microsoft.spring.data.gremlin.config.AbstractGremlinConfiguration; +import com.microsoft.spring.data.gremlin.repository.config.EnableGremlinRepositories; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.PropertySource; + +@EnableGremlinRepositories +@PropertySource(value = {"classpath:application.properties"}) +@EnableConfigurationProperties(TestGremlinProperties.class) +public class TestRepositoryConfiguration extends AbstractGremlinConfiguration { + + @Autowired + private TestGremlinProperties testProps; + + @Override + public GremlinConfig getGremlinConfig() { + return GremlinConfig.builder(testProps.getEndpoint(), testProps.getUsername(), testProps.getPassword()) + .port(testProps.getPort()) + .telemetryAllowed(testProps.isTelemetryAllowed()) + .sslEnabled(testProps.isSslEnabled()) + .serializer(testProps.getSerializer()) + .build(); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestUtils.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestUtils.java new file mode 100644 index 000000000000..653924a445b9 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/TestUtils.java @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common; + +import org.junit.Assert; + +import java.util.Comparator; +import java.util.List; + +public class TestUtils { + + public static void assertEntitiesEquals(List expect, List actual) { + Assert.assertEquals(actual.size(), expect.size()); + + actual.sort(Comparator.comparing(T::toString)); + expect.sort(Comparator.comparing(T::toString)); + + Assert.assertEquals(actual, expect); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/AdvancedUser.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/AdvancedUser.java new file mode 100644 index 000000000000..296307f85177 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/AdvancedUser.java @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; + +@Vertex +public class AdvancedUser extends User { + + private int level; + + public AdvancedUser(String id, String name, int level) { + super(id, name); + this.level = level; + } + + public int getLevel() { + return level; + } + +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Book.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Book.java new file mode 100644 index 000000000000..d7096edb4c60 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Book.java @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import org.springframework.data.annotation.Id; + +@Vertex +public class Book { + + @Id + private Integer serialNumber; + + private String name; + + private Double price; + + public Book() { + } + + public Book(Integer serialNumber, String name, Double price) { + this.serialNumber = serialNumber; + this.name = name; + this.price = price; + } + + public Integer getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(Integer serialNumber) { + this.serialNumber = serialNumber; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/BookReference.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/BookReference.java new file mode 100644 index 000000000000..a7408cf998f8 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/BookReference.java @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; + +@Edge +public class BookReference { + + private Integer id; + + @EdgeFrom + private Integer fromSerialNumber; + + @EdgeTo + private Integer toSerialNumber; + + public BookReference() { + } + + public BookReference(Integer id, Integer fromSerialNumber, Integer toSerialNumber) { + this.id = id; + this.fromSerialNumber = fromSerialNumber; + this.toSerialNumber = toSerialNumber; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getFromSerialNumber() { + return fromSerialNumber; + } + + public void setFromSerialNumber(Integer fromSerialNumber) { + this.fromSerialNumber = fromSerialNumber; + } + + public Integer getToSerialNumber() { + return toSerialNumber; + } + + public void setToSerialNumber(Integer toSerialNumber) { + this.toSerialNumber = toSerialNumber; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Dependency.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Dependency.java new file mode 100644 index 000000000000..bff73dabb3bc --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Dependency.java @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; + +@Edge +public class Dependency { + + private String id; + + private String type; + + @EdgeFrom + private Library source; + + @EdgeTo + private Library target; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Library getSource() { + return source; + } + + public void setSource(Library source) { + this.source = source; + } + + public Library getTarget() { + return target; + } + + public void setTarget(Library target) { + this.target = target; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Group.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Group.java new file mode 100644 index 000000000000..056a0f900f0b --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Group.java @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; +import com.microsoft.spring.data.gremlin.annotation.GeneratedValue; +import org.springframework.data.annotation.Id; + +@Edge +public class Group { + + @Id + @GeneratedValue + private Long id; + + @EdgeFrom + private Student student; + + @EdgeTo + private GroupOwner groupOwner; + + public Group(Student student, GroupOwner groupOwner) { + this.student = student; + this.groupOwner = groupOwner; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Student getStudent() { + return student; + } + + public void setStudent(Student student) { + this.student = student; + } + + public GroupOwner getGroupOwner() { + return groupOwner; + } + + public void setGroupOwner(GroupOwner groupOwner) { + this.groupOwner = groupOwner; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/GroupOwner.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/GroupOwner.java new file mode 100644 index 000000000000..24d938b2519c --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/GroupOwner.java @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import org.springframework.data.annotation.Id; + +@Vertex +public class GroupOwner { + + @Id + private String name; + + private Integer expireDays; + + public GroupOwner() { + } + + public GroupOwner(String name, Integer expireDays) { + this.name = name; + this.expireDays = expireDays; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getExpireDays() { + return expireDays; + } + + public void setExpireDays(Integer expireDays) { + this.expireDays = expireDays; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/InvalidDependency.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/InvalidDependency.java new file mode 100644 index 000000000000..e821caf608d6 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/InvalidDependency.java @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; + +@Edge +public class InvalidDependency { + + private String id; + + @EdgeFrom + private String name; + + @EdgeFrom + private String fromId; + + @EdgeTo + private String toId; + + public InvalidDependency() { + } + + public InvalidDependency(String id, String name, String fromId, String toId) { + this.id = id; + this.name = name; + this.fromId = fromId; + this.toId = toId; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFromId() { + return fromId; + } + + public void setFromId(String fromId) { + this.fromId = fromId; + } + + public String getToId() { + return toId; + } + + public void setToId(String toId) { + this.toId = toId; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Library.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Library.java new file mode 100644 index 000000000000..1170bbfb1eaf --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Library.java @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; + +@Vertex +public class Library { + + private String id; + + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Master.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Master.java new file mode 100644 index 000000000000..cf0ec179e044 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Master.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import com.microsoft.spring.data.gremlin.common.TestConstants; + +@Vertex(label = TestConstants.VERTEX_LABEL) +public class Master { + + private Long id; + + private String name; + + public Master() { + } + + public Master(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Neighbor.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Neighbor.java new file mode 100644 index 000000000000..06e0f7ae945c --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Neighbor.java @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; + +@Edge +public class Neighbor { + + private Long id; + + private Long distance; + + @EdgeFrom + private Student studentFrom; + + @EdgeTo + private Student studentTo; + + public Neighbor() { + } + + public Neighbor(Long id, Long distance, Student studentFrom, Student studentTo) { + this.id = id; + this.distance = distance; + this.studentFrom = studentFrom; + this.studentTo = studentTo; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getDistance() { + return distance; + } + + public void setDistance(Long distance) { + this.distance = distance; + } + + public Student getStudentFrom() { + return studentFrom; + } + + public void setStudentFrom(Student studentFrom) { + this.studentFrom = studentFrom; + } + + public Student getStudentTo() { + return studentTo; + } + + public void setStudentTo(Student studentTo) { + this.studentTo = studentTo; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Network.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Network.java new file mode 100644 index 000000000000..c86089b7f467 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Network.java @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.EdgeSet; +import com.microsoft.spring.data.gremlin.annotation.Graph; +import com.microsoft.spring.data.gremlin.annotation.VertexSet; + +import java.util.ArrayList; +import java.util.List; + +@Graph +public class Network { + + private String id; + + @VertexSet + private List vertexList; + + @EdgeSet + private List edgeList; + + public Network() { + this.vertexList = new ArrayList<>(); + this.edgeList = new ArrayList<>(); + } + + public void vertexAdd(Object object) { + this.vertexList.add(object); + } + + public void edgeAdd(Object object) { + this.edgeList.add(object); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public List getVertexList() { + return vertexList; + } + + public List getEdgeList() { + return edgeList; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Orange.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Orange.java new file mode 100644 index 000000000000..568d0ddf740e --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Orange.java @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.GeneratedValue; +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import org.springframework.data.annotation.Id; + +@Vertex +public class Orange { + + @Id + @GeneratedValue + private String id; + + private String location; + + private Double price; + + public Orange(String location, Double price) { + this.location = location; + this.price = price; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Person.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Person.java new file mode 100644 index 000000000000..ef37f7134819 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Person.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import com.microsoft.spring.data.gremlin.common.TestConstants; + +@Vertex(label = TestConstants.VERTEX_PERSON_LABEL) +public class Person { + + private String id; + + private String name; + + public Person() { + } + + public Person(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Project.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Project.java new file mode 100644 index 000000000000..d3aac78f38c5 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Project.java @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import com.microsoft.spring.data.gremlin.common.TestConstants; + +@Vertex(label = TestConstants.VERTEX_PROJECT_LABEL) +public class Project { + + private String id; + + private String name; + + private String uri; + + public Project() { + } + + public Project(String id, String name, String uri) { + this.id = id; + this.name = name; + this.uri = uri; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Relationship.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Relationship.java new file mode 100644 index 000000000000..6cfac92ea069 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Relationship.java @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; +import com.microsoft.spring.data.gremlin.common.TestConstants; + +@Edge(label = TestConstants.EDGE_RELATIONSHIP_LABEL) +public class Relationship { + + private String id; + + private String name; + + private String location; + + @EdgeFrom + private Person person; + + @EdgeTo + private Project project; + + public Relationship() { + } + + public Relationship(String id, String name, String location, Person person, Project project) { + this.id = id; + this.name = name; + this.location = location; + this.person = person; + this.project = project; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } + + public Project getProject() { + return project; + } + + public void setProject(Project project) { + this.project = project; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Roadmap.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Roadmap.java new file mode 100644 index 000000000000..09a8fcaf8a42 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Roadmap.java @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.EdgeSet; +import com.microsoft.spring.data.gremlin.annotation.Graph; +import com.microsoft.spring.data.gremlin.annotation.VertexSet; +import com.microsoft.spring.data.gremlin.common.TestConstants; + +import java.util.ArrayList; +import java.util.List; + +@Graph(collection = TestConstants.GRAPH_ROADMAP_COLLECTION_NAME) +public class Roadmap { + + private String id; + + @VertexSet + private List vertexList; + + @EdgeSet + private List edgeList; + + public Roadmap() { + this.vertexList = new ArrayList<>(); + this.edgeList = new ArrayList<>(); + } + + public void vertexAdd(Object object) { + this.vertexList.add(object); + } + + public void edgeAdd(Object object) { + this.edgeList.add(object); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Service.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Service.java new file mode 100644 index 000000000000..4a6659c42c93 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Service.java @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import org.springframework.data.annotation.Id; + +import java.util.Date; +import java.util.Map; + +@Vertex +public class Service { + + @Id + private String id; + + private int instanceCount; + + private boolean active; + + private String name; + + private ServiceType type; + + private Date createAt; + + private Map properties; + + public Service() { + } + + public Service(String id, int instanceCount, boolean active, String name, ServiceType type, Date createAt, Map properties) { + this.id = id; + this.instanceCount = instanceCount; + this.active = active; + this.name = name; + this.type = type; + this.createAt = createAt; + this.properties = properties; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getInstanceCount() { + return instanceCount; + } + + public void setInstanceCount(int instanceCount) { + this.instanceCount = instanceCount; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ServiceType getType() { + return type; + } + + public void setType(ServiceType type) { + this.type = type; + } + + public Date getCreateAt() { + return createAt; + } + + public void setCreateAt(Date createAt) { + this.createAt = createAt; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/ServiceType.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/ServiceType.java new file mode 100644 index 000000000000..7389c9a30ea3 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/ServiceType.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +public enum ServiceType { + FRONT_END, + BACK_END, + BOTH +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/SimpleDependency.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/SimpleDependency.java new file mode 100644 index 000000000000..6f553943a037 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/SimpleDependency.java @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; + +@Edge +public class SimpleDependency { + + private String id; + + private String name; + + @EdgeFrom + private String fromId; + + @EdgeTo + private String toId; + + public SimpleDependency() { + } + + public SimpleDependency(String id, String name, String fromId, String toId) { + this.id = id; + this.name = name; + this.fromId = fromId; + this.toId = toId; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFromId() { + return fromId; + } + + public void setFromId(String fromId) { + this.fromId = fromId; + } + + public String getToId() { + return toId; + } + + public void setToId(String toId) { + this.toId = toId; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Student.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Student.java new file mode 100644 index 000000000000..7222ff5cc4ce --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/Student.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import com.microsoft.spring.data.gremlin.common.TestConstants; + +@Vertex(label = TestConstants.VERTEX_LABEL) +public class Student { + + private Long id; + + private String name; + + public Student() { + } + + public Student(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/User.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/User.java new file mode 100644 index 000000000000..cf98a966679c --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/User.java @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +public class User { + + private String id; + + private String name; + + public User(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/UserDomain.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/UserDomain.java new file mode 100644 index 000000000000..840a60785af3 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/domain/UserDomain.java @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.domain; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import org.springframework.data.annotation.Id; + +@Vertex +public class UserDomain { + + @Id + private String name; + + private int level; + + private boolean enabled; + + public UserDomain() { + } + + public UserDomain(String name, int level, boolean enabled) { + this.name = name; + this.level = level; + this.enabled = enabled; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getLevel() { + return level; + } + + public void setLevel(int level) { + this.level = level; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/AdvancedUserRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/AdvancedUserRepository.java new file mode 100644 index 000000000000..fa965436586f --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/AdvancedUserRepository.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.AdvancedUser; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; + +public interface AdvancedUserRepository extends GremlinRepository { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/BookReferenceRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/BookReferenceRepository.java new file mode 100644 index 000000000000..3088d935d199 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/BookReferenceRepository.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.BookReference; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; + +public interface BookReferenceRepository extends GremlinRepository { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/BookRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/BookRepository.java new file mode 100644 index 000000000000..7e7b2f51282f --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/BookRepository.java @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.Book; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; + +import java.util.List; + +public interface BookRepository extends GremlinRepository { + + List findByNameOrPrice(String name, Double price); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/GroupOwnerRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/GroupOwnerRepository.java new file mode 100644 index 000000000000..1fe308fca9fa --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/GroupOwnerRepository.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.GroupOwner; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; + +public interface GroupOwnerRepository extends GremlinRepository { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/GroupRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/GroupRepository.java new file mode 100644 index 000000000000..b89a17a5df67 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/GroupRepository.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.Group; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; + +public interface GroupRepository extends GremlinRepository { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/MasterRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/MasterRepository.java new file mode 100644 index 000000000000..e63487c99398 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/MasterRepository.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.Master; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; + +public interface MasterRepository extends GremlinRepository { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/NeighborRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/NeighborRepository.java new file mode 100644 index 000000000000..dcbb3f4dd891 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/NeighborRepository.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.Neighbor; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; + +public interface NeighborRepository extends GremlinRepository { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/NetworkRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/NetworkRepository.java new file mode 100644 index 000000000000..046ecbc980fa --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/NetworkRepository.java @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.Network; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface NetworkRepository extends GremlinRepository { + + List findByEdgeList(List edgeList); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/OrangeRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/OrangeRepository.java new file mode 100644 index 000000000000..6ac9a9f509f8 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/OrangeRepository.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.Orange; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; + +public interface OrangeRepository extends GremlinRepository { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/PersonRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/PersonRepository.java new file mode 100644 index 000000000000..ccc0a56fc58e --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/PersonRepository.java @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PersonRepository extends GremlinRepository { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/ProjectRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/ProjectRepository.java new file mode 100644 index 000000000000..b5c9420a0491 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/ProjectRepository.java @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.Project; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ProjectRepository extends GremlinRepository { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/RelationshipRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/RelationshipRepository.java new file mode 100644 index 000000000000..152d05070c83 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/RelationshipRepository.java @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.Relationship; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface RelationshipRepository extends GremlinRepository { + + List findByLocation(String location); + + List findByNameAndLocation(String name, String location); + + List findByNameOrId(String name, String id); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/ServiceRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/ServiceRepository.java new file mode 100644 index 000000000000..3a02e08d22dd --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/ServiceRepository.java @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.Service; +import com.microsoft.spring.data.gremlin.common.domain.ServiceType; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import org.springframework.stereotype.Repository; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Repository +public interface ServiceRepository extends GremlinRepository { + + List findByName(String name); + + List findByInstanceCount(int instanceCount); + + List findByActive(boolean isActive); + + List findByCreateAt(Date createAt); + + List findByProperties(Map properties); + + List findByNameAndInstanceCount(String name, int instanceCount); + + List findByNameOrInstanceCount(String name, int instanceCount); + + List findByNameAndInstanceCountAndType(String name, int instanceCount, ServiceType type); + + List findByNameAndActiveOrProperties(String name, boolean isActive, Map properties); + + List findByNameOrInstanceCountAndType(String name, int instanceCount, ServiceType type); + + List findByNameAndInstanceCountOrType(String name, int instanceCount, ServiceType type); + + List findByActiveExists(); + + List findByCreateAtAfter(Date expiryDate); + + List findByNameOrTypeAndInstanceCountAndCreateAtAfter(String name, ServiceType type, int instanceCount, + Date expiryDate); + + List findByCreateAtBefore(Date expiryDate); + + List findByCreateAtAfterAndCreateAtBefore(Date startDate, Date endDate); + + List findByCreateAtBetween(Date start, Date end); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/SimpleDependencyRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/SimpleDependencyRepository.java new file mode 100644 index 000000000000..a671c7c4a7c0 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/SimpleDependencyRepository.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.SimpleDependency; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; + +public interface SimpleDependencyRepository extends GremlinRepository { +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/StudentRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/StudentRepository.java new file mode 100644 index 000000000000..7bf167b23300 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/StudentRepository.java @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.Student; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; + +import java.util.List; + +public interface StudentRepository extends GremlinRepository { + + List findByName(String name); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/UserDomainRepository.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/UserDomainRepository.java new file mode 100644 index 000000000000..25d9cd6defc2 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/common/repository/UserDomainRepository.java @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.common.repository; + +import com.microsoft.spring.data.gremlin.common.domain.UserDomain; +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; + +import java.util.List; + +public interface UserDomainRepository extends GremlinRepository { + + List findByName(String name); + + List findByEnabledExists(); + + List findByLevelBetween(int low, int high); +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/config/AbstractGremlinConfigurationIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/config/AbstractGremlinConfigurationIT.java new file mode 100644 index 000000000000..ec003dc839dc --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/config/AbstractGremlinConfigurationIT.java @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.config; + +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class AbstractGremlinConfigurationIT { + + @Autowired + private TestRepositoryConfiguration testConfig; + + @Test + public void testGremlinFactory() { + Assert.assertNotNull(this.testConfig.gremlinFactory()); + } + + @Test + public void testMappingGremlinConverter() throws ClassNotFoundException { + Assert.assertNotNull(this.testConfig.mappingGremlinConverter()); + } + + @Test + public void testGremlinTemplate() throws ClassNotFoundException { + Assert.assertNotNull(this.testConfig.gremlinTemplate(testConfig.gremlinFactory())); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/config/GremlinConfigurationSupportUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/config/GremlinConfigurationSupportUnitTest.java new file mode 100644 index 000000000000..f9c3b0b11249 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/config/GremlinConfigurationSupportUnitTest.java @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.config; + +import com.microsoft.spring.data.gremlin.common.domain.AdvancedUser; +import com.microsoft.spring.data.gremlin.common.domain.Book; +import com.microsoft.spring.data.gremlin.common.domain.BookReference; +import com.microsoft.spring.data.gremlin.common.domain.Dependency; +import com.microsoft.spring.data.gremlin.common.domain.Group; +import com.microsoft.spring.data.gremlin.common.domain.GroupOwner; +import com.microsoft.spring.data.gremlin.common.domain.InvalidDependency; +import com.microsoft.spring.data.gremlin.common.domain.Library; +import com.microsoft.spring.data.gremlin.common.domain.Master; +import com.microsoft.spring.data.gremlin.common.domain.Neighbor; +import com.microsoft.spring.data.gremlin.common.domain.Network; +import com.microsoft.spring.data.gremlin.common.domain.Orange; +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.domain.Project; +import com.microsoft.spring.data.gremlin.common.domain.Relationship; +import com.microsoft.spring.data.gremlin.common.domain.Roadmap; +import com.microsoft.spring.data.gremlin.common.domain.Service; +import com.microsoft.spring.data.gremlin.common.domain.SimpleDependency; +import com.microsoft.spring.data.gremlin.common.domain.Student; +import com.microsoft.spring.data.gremlin.common.domain.UserDomain; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +public class GremlinConfigurationSupportUnitTest { + + private static final String TEST_CONFIG_PACKAGE_NAME = "com.microsoft.spring.data.gremlin.config"; + private static final String TEST_DOMAIN_PACKAGE_NAME = "com.microsoft.spring.data.gremlin.common.domain"; + private TestConfig config; + + @Before + public void setup() { + this.config = new TestConfig(); + } + + @Test + public void testGetMappingBasePackages() { + final Collection basePackages = this.config.getMappingBasePackages(); + + Assert.assertNotNull(basePackages); + Assert.assertEquals(basePackages.size(), 1); + Assert.assertEquals(basePackages.toArray()[0], TEST_CONFIG_PACKAGE_NAME); + } + + @Test + public void testGremlinMappingContext() throws ClassNotFoundException { + Assert.assertNotNull(this.config.gremlinMappingContext()); + } + + @Test + public void testScanEntity() throws ClassNotFoundException { + final Set> entities = this.config.scanEntities(TEST_DOMAIN_PACKAGE_NAME); + final Set> references = new HashSet<>(Arrays.asList( + Dependency.class, Library.class, Network.class, Person.class, Project.class, + Relationship.class, Roadmap.class, Service.class, SimpleDependency.class, InvalidDependency.class, + UserDomain.class, AdvancedUser.class, Student.class, Book.class, BookReference.class, + Neighbor.class, Master.class, Group.class, GroupOwner.class, Orange.class) + ); + + Assert.assertNotNull(entities); + Assert.assertEquals(entities.size(), references.size()); + + references.forEach(entity -> Assert.assertTrue(entities.contains(entity))); + } + + @Test + public void testScanEntityEmpty() throws ClassNotFoundException { + final Set> entities = this.config.scanEntities(""); + + Assert.assertTrue(entities.isEmpty()); + } + + private class TestConfig extends GremlinConfigurationSupport { + + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/MappingGremlinConverterUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/MappingGremlinConverterUnitTest.java new file mode 100644 index 000000000000..d1313be4e3d4 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/MappingGremlinConverterUnitTest.java @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion; + +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.domain.Project; +import com.microsoft.spring.data.gremlin.common.domain.Relationship; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.mapping.GremlinMappingContext; +import com.microsoft.spring.data.gremlin.repository.support.GremlinEntityInformation; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.context.ApplicationContext; + +@RunWith(MockitoJUnitRunner.class) +public class MappingGremlinConverterUnitTest { + + private MappingGremlinConverter converter; + private GremlinMappingContext mappingContext; + + @Mock + private ApplicationContext applicationContext; + + @Before + public void setup() { + this.mappingContext = new GremlinMappingContext(); + + this.mappingContext.setApplicationContext(this.applicationContext); + this.mappingContext.afterPropertiesSet(); + this.mappingContext.getPersistentEntity(Person.class); + + this.converter = new MappingGremlinConverter(this.mappingContext); + } + + @Test + public void testMappingGremlinConverterGetter() { + Assert.assertEquals(this.converter.getMappingContext(), this.mappingContext); + Assert.assertNotNull(this.converter.getConversionService()); + + final Person person = new Person(TestConstants.VERTEX_PERSON_ID, TestConstants.VERTEX_PERSON_NAME); + FieldUtils.getAllFields(Person.class); + + Assert.assertNotNull(this.converter.getPropertyAccessor(person)); + Assert.assertEquals(converter.getIdFieldValue(person), TestConstants.VERTEX_PERSON_ID); + } + + @Test + public void testMappingGremlinConverterVertexRead() { + final Person person = new Person(TestConstants.VERTEX_PERSON_ID, TestConstants.VERTEX_PERSON_NAME); + final GremlinEntityInformation info = new GremlinEntityInformation<>(Person.class); + final GremlinSource source = info.createGremlinSource(); + + this.converter.write(person, source); + + Assert.assertTrue(source.getId().isPresent()); + Assert.assertEquals(source.getId().get(), person.getId()); + Assert.assertEquals(source.getProperties().get(TestConstants.PROPERTY_NAME), person.getName()); + } + + @Test + public void testMappingGremlinConverterEdgeRead() { + final Person person = new Person(TestConstants.VERTEX_PERSON_ID, TestConstants.VERTEX_PERSON_NAME); + final Project project = new Project(TestConstants.VERTEX_PROJECT_ID, TestConstants.VERTEX_PROJECT_NAME, + TestConstants.VERTEX_PROJECT_URI); + final Relationship relationship = new Relationship(TestConstants.EDGE_RELATIONSHIP_ID, + TestConstants.EDGE_RELATIONSHIP_NAME, TestConstants.EDGE_RELATIONSHIP_LOCATION, person, project); + final GremlinEntityInformation info = new GremlinEntityInformation<>(Relationship.class); + final GremlinSource source = info.createGremlinSource(); + + this.converter.write(relationship, source); + + Assert.assertTrue(source.getId().isPresent()); + Assert.assertEquals(source.getId().get(), relationship.getId()); + Assert.assertEquals(source.getProperties().get(TestConstants.PROPERTY_NAME), relationship.getName()); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultUnitTest.java new file mode 100644 index 000000000000..09857da0cd7f --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultUnitTest.java @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.result; + +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteralEdge; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteralGraph; +import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteralVertex; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import org.junit.Test; + +public class GremlinResultUnitTest { + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testVertexInsertException() { + new GremlinScriptLiteralVertex().generateInsertScript(new GremlinSourceEdge<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testVertexUpdateException() { + new GremlinScriptLiteralVertex().generateUpdateScript(new GremlinSourceEdge<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testVertexFindByIdException() { + new GremlinScriptLiteralVertex().generateFindByIdScript(new GremlinSourceEdge<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testEdgeInsertException() { + new GremlinScriptLiteralEdge().generateInsertScript(new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testEdgeUpdateException() { + new GremlinScriptLiteralEdge().generateUpdateScript(new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testEdgeFindByIdException() { + new GremlinScriptLiteralEdge().generateFindByIdScript(new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testGraphInsertException() { + new GremlinScriptLiteralGraph().generateInsertScript(new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testGraphUpdateException() { + new GremlinScriptLiteralGraph().generateUpdateScript(new GremlinSourceVertex<>()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testGraphFindByIdException() { + new GremlinScriptLiteralGraph().generateFindByIdScript(new GremlinSourceVertex<>()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/AbstractGremlinScriptLiteralUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/AbstractGremlinScriptLiteralUnitTest.java new file mode 100644 index 000000000000..06fe7d184b1f --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/AbstractGremlinScriptLiteralUnitTest.java @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.script; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException; +import org.junit.Test; + +public class AbstractGremlinScriptLiteralUnitTest { + + @Test(expected = GremlinInvalidEntityIdFieldException.class) + public void testEntityInvalidIdType() { + final Double id = 12.342; + GremlinScriptLiteralHelper.generateEntityWithRequiredId(id, GremlinEntityType.EDGE); + } + + @Test(expected = GremlinInvalidEntityIdFieldException.class) + public void testPropertyInvalidIdType() { + final Double id = 12.342; + GremlinScriptLiteralHelper.generatePropertyWithRequiredId(id); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralEdgeUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralEdgeUnitTest.java new file mode 100644 index 000000000000..c882ce73fd89 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralEdgeUnitTest.java @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.script; + +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.domain.Project; +import com.microsoft.spring.data.gremlin.common.domain.Relationship; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import com.microsoft.spring.data.gremlin.mapping.GremlinMappingContext; +import com.microsoft.spring.data.gremlin.repository.support.GremlinEntityInformation; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.context.ApplicationContext; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +@RunWith(MockitoJUnitRunner.class) +public class GremlinScriptLiteralEdgeUnitTest { + + private MappingGremlinConverter converter; + private GremlinMappingContext mappingContext; + private GremlinSource gremlinSource; + + @Mock + private ApplicationContext applicationContext; + + @Before + public void setup() { + this.mappingContext = new GremlinMappingContext(); + this.mappingContext.setApplicationContext(this.applicationContext); + this.mappingContext.afterPropertiesSet(); + this.mappingContext.getPersistentEntity(Person.class); + this.converter = new MappingGremlinConverter(this.mappingContext); + + final Relationship relationship = new Relationship("456", "rel-name", "china", + new Person("123", "bill"), // from + new Project("321", "ms-project", "http") // to + ); + final GremlinEntityInformation info = new GremlinEntityInformation<>(Relationship.class); + this.gremlinSource = info.createGremlinSource(); + this.converter.write(relationship, gremlinSource); + } + + @Test + public void testGenerateCountScript() { + final List queryList = new GremlinScriptLiteralEdge().generateCountScript(gremlinSource); + assertEquals(queryList.get(0), "g.E()"); + } + + @Test + public void testGenerateFindByIdScript() { + final List queryList = new GremlinScriptLiteralEdge().generateFindByIdScript(gremlinSource); + assertEquals(queryList.get(0), "g.E().hasId('456')"); + } + + @Test + public void testGenerateFindAllScript() { + final List queryList = new GremlinScriptLiteralEdge().generateFindAllScript(gremlinSource); + assertEquals(queryList.get(0), "g.E().has(label, 'label-relationship')" + + ".has('_classname', 'com.microsoft.spring.data.gremlin.common.domain.Relationship')"); + } + + @Test + public void testGenerateInsertScript() { + final List queryList = new GremlinScriptLiteralEdge().generateInsertScript(gremlinSource); + assertEquals(queryList.get(0), "g.V('123').as('from').V('321').as('to')" + + ".addE('label-relationship').from('from').to('to')" + + ".property(id, '456')" + + ".property('person', '{\"id\":\"123\",\"name\":\"bill\"}')" + + ".property('name', 'rel-name')" + + ".property('project', '{\"id\":\"321\",\"name\":\"ms-project\",\"uri\":\"http\"}')" + + ".property('location', 'china')" + + ".property('_classname', 'com.microsoft.spring.data.gremlin.common.domain.Relationship')"); + } + + @Test + public void testGenerateUpdateScript() { + final List queryList = new GremlinScriptLiteralEdge().generateUpdateScript(gremlinSource); + assertEquals(queryList.get(0), "g.E('456')" + + ".property('person', '{\"id\":\"123\",\"name\":\"bill\"}')" + + ".property('name', 'rel-name')" + + ".property('project', '{\"id\":\"321\",\"name\":\"ms-project\",\"uri\":\"http\"}')" + + ".property('location', 'china')" + + ".property('_classname', 'com.microsoft.spring.data.gremlin.common.domain.Relationship')"); + } + + @Test + public void testGenerateDeleteByIdScript() { + final List queryList = new GremlinScriptLiteralEdge().generateDeleteByIdScript(gremlinSource); + assertEquals(queryList.get(0), "g.E().hasId('456').drop()"); + } + + @Test + public void testGenerateDeleteAllScript() { + final List queryList = new GremlinScriptLiteralEdge().generateDeleteAllScript(); + assertEquals(queryList.get(0), "g.E().drop()"); + } + + @Test + public void testGenerateDeleteAllByClassScript() { + final List queryList = new GremlinScriptLiteralEdge().generateDeleteAllByClassScript(gremlinSource); + assertEquals(queryList.get(0), "g.E().has(label, 'label-relationship').drop()"); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testInvalidDeleteAllByClassScript() { + new GremlinScriptLiteralEdge().generateDeleteAllByClassScript(new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testInvalidFindAllScript() { + new GremlinScriptLiteralEdge().generateFindAllScript(new GremlinSourceVertex<>()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralVertexUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralVertexUnitTest.java new file mode 100644 index 000000000000..106c79682138 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralVertexUnitTest.java @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.script; + +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import com.microsoft.spring.data.gremlin.mapping.GremlinMappingContext; +import com.microsoft.spring.data.gremlin.repository.support.GremlinEntityInformation; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.context.ApplicationContext; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +@RunWith(MockitoJUnitRunner.class) +public class GremlinScriptLiteralVertexUnitTest { + + private MappingGremlinConverter converter; + private GremlinMappingContext mappingContext; + private GremlinSource gremlinSource; + private GremlinScriptLiteralVertex scriptLiteralVertex; + + @Mock + private ApplicationContext applicationContext; + + @Before + public void setup() { + this.mappingContext = new GremlinMappingContext(); + this.mappingContext.setApplicationContext(this.applicationContext); + this.mappingContext.afterPropertiesSet(); + this.mappingContext.getPersistentEntity(Person.class); + this.converter = new MappingGremlinConverter(this.mappingContext); + + final Person person = new Person("123", "bill"); + final GremlinEntityInformation info = new GremlinEntityInformation<>(Person.class); + this.gremlinSource = info.createGremlinSource(); + this.scriptLiteralVertex = new GremlinScriptLiteralVertex(); + this.converter.write(person, gremlinSource); + } + + @Test + public void testGenerateCountScript() { + final List queryList = scriptLiteralVertex.generateCountScript(gremlinSource); + assertEquals(queryList.get(0), "g.V()"); + } + + @Test + public void testGenerateFindByIdScript() { + final List queryList = scriptLiteralVertex.generateFindByIdScript(gremlinSource); + assertEquals(queryList.get(0), "g.V().hasId('123')"); + } + + @Test + public void testGenerateFindAllScript() { + final List queryList = scriptLiteralVertex.generateFindAllScript(gremlinSource); + assertEquals(queryList.get(0), "g.V().has(label, 'label-person')" + + ".has('_classname', 'com.microsoft.spring.data.gremlin.common.domain.Person')"); + } + + @Test + public void testGenerateInsertScript() { + final List queryList = scriptLiteralVertex.generateInsertScript(gremlinSource); + assertEquals(queryList.get(0), "g.addV('label-person').property(id, '123').property('name', 'bill')" + + ".property('_classname', 'com.microsoft.spring.data.gremlin.common.domain.Person')"); + } + + @Test + public void testGenerateUpdateScript() { + final List queryList = scriptLiteralVertex.generateUpdateScript(gremlinSource); + assertEquals(queryList.get(0), "g.V('123').property('name', 'bill')" + + ".property('_classname', 'com.microsoft.spring.data.gremlin.common.domain.Person')"); + } + + @Test + public void testGenerateDeleteByIdScript() { + final List queryList = scriptLiteralVertex.generateDeleteByIdScript(gremlinSource); + assertEquals(queryList.get(0), "g.V().hasId('123').drop()"); + } + + @Test + public void testGenerateDeleteAllScript() { + final List queryList = scriptLiteralVertex.generateDeleteAllScript(); + assertEquals(queryList.get(0), "g.V().drop()"); + } + + @Test + public void testGenerateDeleteAllByClassScript() { + final List queryList = new GremlinScriptLiteralVertex().generateDeleteAllByClassScript(gremlinSource); + assertEquals(queryList.get(0), "g.V().has(label, 'label-person').drop()"); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testInvalidDeleteAllByClassScript() { + new GremlinScriptLiteralVertex().generateDeleteAllByClassScript(new GremlinSourceEdge<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testInvalidFindAllScript() { + new GremlinScriptLiteralVertex().generateFindAllScript(new GremlinSourceEdge<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testInvalidDeleteById() { + new GremlinScriptLiteralVertex().generateDeleteByIdScript(new GremlinSourceEdge<>()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptUnitTest.java new file mode 100644 index 000000000000..38017e503fe2 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptUnitTest.java @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.script; + +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.conversion.result.GremlinResultEdgeReader; +import com.microsoft.spring.data.gremlin.conversion.result.GremlinResultVertexReader; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import org.apache.tinkerpop.gremlin.driver.Result; +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static java.util.Collections.singletonList; + +public class GremlinScriptUnitTest { + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testVertexWriteException() { + new GremlinResultVertexReader().read(singletonList(new Result(new Object())), new GremlinSourceEdge<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testEdgeReadException() { + new GremlinResultEdgeReader().read(singletonList(new Result(new Object())), new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedEntityTypeException.class) + public void testGeneratePropertyException() { + final Map properties = new HashMap<>(); + final GremlinSource source = new GremlinSourceVertex<>(); + properties.put("person", source); + GremlinScriptLiteralHelper.generateProperties(properties); + } + + @Test(expected = GremlinUnexpectedEntityTypeException.class) + public void testGenerateHasException() { + final GremlinSource source = new GremlinSourceVertex<>(); + GremlinScriptLiteralHelper.generateHas("fake-name", source); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testEdgeDeleteByIdScriptException() { + new GremlinScriptLiteralEdge().generateDeleteByIdScript(new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testGraphDeleteByIdScriptException() { + new GremlinScriptLiteralGraph().generateDeleteByIdScript(new GremlinSourceVertex<>()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testGraphFindAllScriptException() { + new GremlinScriptLiteralGraph().generateFindAllScript(new GremlinSourceVertex<>()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testGraphCountException() { + new GremlinScriptLiteralGraph().generateCountScript(new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testEdgeCountScriptException() { + new GremlinScriptLiteralEdge().generateCountScript(new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testVertexDeleteByIdScriptException() { + new GremlinScriptLiteralGraph().generateDeleteByIdScript(new GremlinSourceEdge<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testVertexCountScriptException() { + new GremlinScriptLiteralVertex().generateCountScript(new GremlinSourceEdge<>()); + } + + @Test + public void testVertexSourceSetProperty() { + final GremlinSource source = new GremlinSourceVertex<>(Person.class); + final Map properties = source.getProperties(); + final String fakeName = "fake-name"; + + properties.put(fakeName, "fake-value"); + properties.put("fake-name-0", "fake-value-0"); + properties.put("fake-name-1", "fake-value-1"); + + source.setProperty(fakeName, null); + + Assert.assertEquals(source.getProperties().size(), 3); // one predefined property _classname + Assert.assertNull(source.getProperties().get(fakeName)); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceUnitTest.java new file mode 100644 index 000000000000..14b4103d14ff --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/conversion/source/GremlinSourceUnitTest.java @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.conversion.source; + +import com.microsoft.spring.data.gremlin.annotation.EdgeFrom; +import com.microsoft.spring.data.gremlin.annotation.EdgeTo; +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.exception.GremlinEntityInformationException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException; +import com.microsoft.spring.data.gremlin.mapping.GremlinMappingContext; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.domain.EntityScanner; +import org.springframework.context.ApplicationContext; +import org.springframework.data.annotation.Persistent; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +public class GremlinSourceUnitTest { + + private MappingGremlinConverter converter; + + @Autowired + private ApplicationContext context; + + @Before + public void setup() throws ClassNotFoundException { + final GremlinMappingContext mappingContext = new GremlinMappingContext(); + + mappingContext.setInitialEntitySet(new EntityScanner(this.context).scan(Persistent.class)); + + this.converter = new MappingGremlinConverter(mappingContext); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testVertexWriteException() { + new GremlinSourceVertexWriter<>().write(new Object(), this.converter, new GremlinSourceEdge<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testVertexReadException() { + new GremlinSourceVertexReader<>().read(Object.class, this.converter, new GremlinSourceEdge<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testEdgeWriteException() { + new GremlinSourceEdgeWriter<>().write(new Object(), this.converter, new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testEdgeReadException() { + new GremlinSourceEdgeReader<>().read(Object.class, this.converter, new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testGraphWriteException() { + new GremlinSourceGraphWriter<>().write(new Object(), this.converter, new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testGraphReadException() { + new GremlinSourceEdgeReader<>().read(Object.class, this.converter, new GremlinSourceVertex<>()); + } + + @Test(expected = GremlinUnexpectedSourceTypeException.class) + public void testGraphAddSourceException() { + new GremlinSourceGraph<>().addGremlinSource(new GremlinSourceGraph<>()); + } + + @Test(expected = GremlinEntityInformationException.class) + public void testVertexWithPredefinedProperty() { + @SuppressWarnings("unchecked") final GremlinSource source = new GremlinSourceVertex<>(TestVertex.class); + + new GremlinSourceVertexWriter().write(new TestVertex("fake-id", "fake-name"), this.converter, + source); + } + + @Test(expected = GremlinEntityInformationException.class) + public void testEdgeWithPredefinedProperty() { + @SuppressWarnings("unchecked") final GremlinSource source = new GremlinSourceEdge<>(TestEdge.class); + + new GremlinSourceEdgeWriter().write(new TestEdge("fake-id", "fake-name", "1", "2"), this.converter, source); + } + + @Vertex + private static class TestVertex { + + private String id; + + private String classname; + + TestVertex(String id, String classname) { + this.id = id; + this.classname = classname; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getClassname() { + return classname; + } + + public void setClassname(String classname) { + this.classname = classname; + } + } + + @Vertex + private static class TestEdge { + + private String id; + + private String classname; + + @EdgeFrom + private String from; + + @EdgeTo + private String to; + + TestEdge(String id, String classname, String from, String to) { + this.id = id; + this.classname = classname; + this.from = from; + this.to = to; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getClassname() { + return classname; + } + + public void setClassname(String classname) { + this.classname = classname; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentEntityUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentEntityUnitTest.java new file mode 100644 index 000000000000..8b1c847b9df5 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentEntityUnitTest.java @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.mapping; + +import com.microsoft.spring.data.gremlin.annotation.Edge; +import com.microsoft.spring.data.gremlin.common.domain.Network; +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.domain.Relationship; +import com.microsoft.spring.data.gremlin.annotation.Graph; +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.data.util.ClassTypeInformation; + +public class BasicGremlinPersistentEntityUnitTest { + + @Test + public void testVertexPersistentEntity() { + final BasicGremlinPersistentEntity entity = + new BasicGremlinPersistentEntity<>(ClassTypeInformation.from(Person.class)); + final Vertex annotation = entity.findAnnotation(Vertex.class); + + Assert.assertEquals(entity.getType(), Person.class); + Assert.assertEquals(annotation.annotationType(), Vertex.class); + Assert.assertEquals(annotation.label(), TestConstants.VERTEX_PERSON_LABEL); + } + + @Test + public void testEdgePersistentEntity() { + final BasicGremlinPersistentEntity entity = + new BasicGremlinPersistentEntity<>(ClassTypeInformation.from(Relationship.class)); + final Edge annotation = entity.findAnnotation(Edge.class); + + Assert.assertEquals(entity.getType(), Relationship.class); + Assert.assertEquals(annotation.annotationType(), Edge.class); + Assert.assertEquals(annotation.label(), TestConstants.EDGE_RELATIONSHIP_LABEL); + } + + @Test + public void testGraphPersistentEntity() { + final BasicGremlinPersistentEntity entity = + new BasicGremlinPersistentEntity<>(ClassTypeInformation.from(Network.class)); + final Graph annotation = entity.findAnnotation(Graph.class); + + Assert.assertEquals(entity.getType(), Network.class); + Assert.assertEquals(annotation.annotationType(), Graph.class); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentPropertyUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentPropertyUnitTest.java new file mode 100644 index 000000000000..e0390bf28802 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/mapping/BasicGremlinPersistentPropertyUnitTest.java @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.mapping; + +import com.microsoft.spring.data.gremlin.common.domain.Network; +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.domain.Relationship; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.data.mapping.model.Property; +import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Field; + +public class BasicGremlinPersistentPropertyUnitTest { + + private BasicGremlinPersistentProperty createBasicGremlinPersistentProperty( + BasicGremlinPersistentEntity entity, Field field) { + return new BasicGremlinPersistentProperty(Property.of(entity.getTypeInformation(), field), entity, + SimpleTypeHolder.DEFAULT); + } + + @Test + public void testVertexPersistentProperty() { + final BasicGremlinPersistentEntity entity = + new BasicGremlinPersistentEntity<>(ClassTypeInformation.from(Person.class)); + Field field = ReflectionUtils.findField(Person.class, TestConstants.PROPERTY_ID); + BasicGremlinPersistentProperty property = this.createBasicGremlinPersistentProperty(entity, field); + + Assert.assertEquals(property.getName(), TestConstants.PROPERTY_ID); + Assert.assertTrue(property.isIdProperty()); + Assert.assertNotNull(property.createAssociation()); + + field = ReflectionUtils.findField(Person.class, TestConstants.PROPERTY_NAME); + property = this.createBasicGremlinPersistentProperty(entity, field); + + Assert.assertEquals(property.getName(), TestConstants.PROPERTY_NAME); + Assert.assertFalse(property.isIdProperty()); + Assert.assertNotNull(property.createAssociation()); + } + + @Test + public void testEdgePersistentProperty() { + final BasicGremlinPersistentEntity entity = + new BasicGremlinPersistentEntity<>(ClassTypeInformation.from(Relationship.class)); + Field field = ReflectionUtils.findField(Relationship.class, TestConstants.PROPERTY_ID); + BasicGremlinPersistentProperty property = this.createBasicGremlinPersistentProperty(entity, field); + + Assert.assertEquals(property.getName(), TestConstants.PROPERTY_ID); + Assert.assertTrue(property.isIdProperty()); + Assert.assertNotNull(property.createAssociation()); + + field = ReflectionUtils.findField(Relationship.class, TestConstants.PROPERTY_LOCATION); + property = this.createBasicGremlinPersistentProperty(entity, field); + + Assert.assertEquals(property.getName(), TestConstants.PROPERTY_LOCATION); + Assert.assertFalse(property.isIdProperty()); + Assert.assertNotNull(property.createAssociation()); + } + + @Test + public void testGraphPersistentProperty() { + final BasicGremlinPersistentEntity entity = + new BasicGremlinPersistentEntity<>(ClassTypeInformation.from(Network.class)); + final Field field = ReflectionUtils.findField(Network.class, TestConstants.PROPERTY_ID); + final BasicGremlinPersistentProperty property = this.createBasicGremlinPersistentProperty(entity, field); + + Assert.assertEquals(property.getName(), TestConstants.PROPERTY_ID); + Assert.assertTrue(property.isIdProperty()); + Assert.assertNotNull(property.createAssociation()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/mapping/GremlinMappingContextUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/mapping/GremlinMappingContextUnitTest.java new file mode 100644 index 000000000000..e8d38998faee --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/mapping/GremlinMappingContextUnitTest.java @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.mapping; + +import com.microsoft.spring.data.gremlin.annotation.Vertex; +import com.microsoft.spring.data.gremlin.common.domain.Project; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.data.util.ClassTypeInformation; + +public class GremlinMappingContextUnitTest { + + @Test + public void testCreatePersistentProperty() { + final GremlinMappingContext context = new GremlinMappingContext(); + final BasicGremlinPersistentEntity entity = context.createPersistentEntity( + ClassTypeInformation.from(Project.class)); + + Assert.assertNotNull(entity); + Assert.assertNotNull(entity.findAnnotation(Vertex.class)); + Assert.assertEquals(entity.findAnnotation(Vertex.class).label(), TestConstants.VERTEX_PROJECT_LABEL); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/GremlinTemplateIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/GremlinTemplateIT.java new file mode 100644 index 000000000000..11d507c4e546 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/GremlinTemplateIT.java @@ -0,0 +1,579 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query; + +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.domain.Project; +import com.microsoft.spring.data.gremlin.common.domain.Relationship; +import com.microsoft.spring.data.gremlin.common.GremlinConfig; +import com.microsoft.spring.data.gremlin.common.GremlinFactory; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import com.microsoft.spring.data.gremlin.common.TestGremlinProperties; +import com.microsoft.spring.data.gremlin.common.domain.InvalidDependency; +import com.microsoft.spring.data.gremlin.common.domain.Network; +import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceGraph; +import com.microsoft.spring.data.gremlin.exception.GremlinQueryException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException; +import com.microsoft.spring.data.gremlin.mapping.GremlinMappingContext; +import com.microsoft.spring.data.gremlin.repository.support.GremlinEntityInformation; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.domain.EntityScanner; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.data.annotation.Persistent; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.List; + +@RunWith(SpringJUnit4ClassRunner.class) +@PropertySource(value = {"classpath:application.properties"}) +@ContextConfiguration(classes = {GremlinTemplateIT.TestConfiguration.class}) +@EnableConfigurationProperties(TestGremlinProperties.class) +public class GremlinTemplateIT { + + private final Person person = new Person(TestConstants.VERTEX_PERSON_ID, TestConstants.VERTEX_PERSON_NAME); + private final Person person0 = new Person(TestConstants.VERTEX_PERSON_0_ID, TestConstants.VERTEX_PERSON_0_NAME); + private final Person person1 = new Person(TestConstants.VERTEX_PERSON_1_ID, TestConstants.VERTEX_PERSON_1_NAME); + + private final Project project = new Project(TestConstants.VERTEX_PROJECT_ID, TestConstants.VERTEX_PROJECT_NAME, + TestConstants.VERTEX_PROJECT_URI); + private final Project project0 = new Project(TestConstants.VERTEX_PROJECT_0_ID, TestConstants.VERTEX_PROJECT_0_NAME, + TestConstants.VERTEX_PROJECT_0_URI); + + private final Relationship relationship = new Relationship(TestConstants.EDGE_RELATIONSHIP_ID, + TestConstants.EDGE_RELATIONSHIP_NAME, TestConstants.EDGE_RELATIONSHIP_LOCATION, + this.person, this.project); + private final Relationship relationship0 = new Relationship(TestConstants.EDGE_RELATIONSHIP_0_ID, + TestConstants.EDGE_RELATIONSHIP_0_NAME, TestConstants.EDGE_RELATIONSHIP_0_LOCATION, + this.person0, this.project); + private final Relationship relationship1 = new Relationship(TestConstants.EDGE_RELATIONSHIP_1_ID, + TestConstants.EDGE_RELATIONSHIP_1_NAME, TestConstants.EDGE_RELATIONSHIP_1_LOCATION, + this.person1, this.project); + private final Relationship relationship2 = new Relationship(TestConstants.EDGE_RELATIONSHIP_2_ID, + TestConstants.EDGE_RELATIONSHIP_2_NAME, TestConstants.EDGE_RELATIONSHIP_2_LOCATION, + this.person, this.project0); + + private Network network; + + private final GremlinEntityInformation personInfo = new GremlinEntityInformation<>(Person.class); + private final GremlinEntityInformation projectInfo = new GremlinEntityInformation<>(Project.class); + private final GremlinEntityInformation relationshipInfo = + new GremlinEntityInformation<>(Relationship.class); + private final GremlinEntityInformation networkInfo = new GremlinEntityInformation<>(Network.class); + + private final GremlinSource personSource = personInfo.createGremlinSource(); + private final GremlinSource projectSource = projectInfo.createGremlinSource(); + private final GremlinSource relationshipSource = relationshipInfo.createGremlinSource(); + private final GremlinSource networkSource = networkInfo.createGremlinSource(); + + @Autowired + private ApplicationContext context; + + @Autowired + private GremlinFactory gremlinFactory; + + private GremlinTemplate template; + + @Before + public void setup() throws ClassNotFoundException { + final GremlinMappingContext mappingContext = new GremlinMappingContext(); + + mappingContext.setInitialEntitySet(new EntityScanner(this.context).scan(Persistent.class)); + + final MappingGremlinConverter converter = new MappingGremlinConverter(mappingContext); + + this.template = new GremlinTemplate(gremlinFactory, converter); + this.template.deleteAll(); + this.network = new Network(); + } + + private void buildTestGraph() { + this.network.vertexAdd(this.person); + this.network.vertexAdd(this.person0); + this.network.vertexAdd(this.person1); + this.network.vertexAdd(this.project); + this.network.vertexAdd(this.project0); + + this.network.edgeAdd(this.relationship); + this.network.edgeAdd(this.relationship0); + this.network.edgeAdd(this.relationship1); + this.network.edgeAdd(this.relationship2); + + this.template.insert(this.network, this.networkSource); + } + + @After + public void cleanup() { + this.template.deleteAll(); + } + + @Test + public void testVertexDeleteAll() { + this.buildTestGraph(); + + Person personVertex = this.template.findVertexById(this.person.getId(), this.personSource); + Project projectVertex = this.template.findVertexById(this.project.getId(), this.projectSource); + Relationship relationshipEdge = this.template.findEdgeById(this.relationship.getId(), this.relationshipSource); + + Assert.assertNotNull(personVertex); + Assert.assertNotNull(projectVertex); + Assert.assertNotNull(relationshipEdge); + + this.template.deleteAll(); + + personVertex = this.template.findVertexById(this.person.getId(), this.personSource); + projectVertex = this.template.findVertexById(this.project.getId(), this.projectSource); + relationshipEdge = this.template.findEdgeById(this.relationship.getId(), this.relationshipSource); + + Assert.assertNull(personVertex); + Assert.assertNull(projectVertex); + Assert.assertNull(relationshipEdge); + + Assert.assertTrue(this.template.findAll(this.personSource).isEmpty()); + Assert.assertTrue(this.template.findAll(this.projectSource).isEmpty()); + Assert.assertTrue(this.template.findAll(this.relationshipSource).isEmpty()); + } + + @Test + public void testVertexInsertNormal() { + this.template.insert(this.person0, this.personSource); + + final Person foundPerson = this.template.findVertexById(this.person0.getId(), this.personSource); + + Assert.assertNotNull(foundPerson); + Assert.assertEquals(foundPerson.getId(), this.person0.getId()); + Assert.assertEquals(foundPerson.getName(), this.person0.getName()); + } + + @Test(expected = GremlinQueryException.class) + public void testVertexInsertException() { + this.template.insert(this.person, this.personSource); + + final Person repeated = new Person(this.person.getId(), this.person.getName()); + + this.template.insert(repeated, this.personSource); + } + + @Test + public void testEdgeInsertNormal() { + this.template.insert(this.person, this.personSource); + this.template.insert(this.project, this.projectSource); + this.template.insert(this.relationship, this.relationshipSource); + + final Relationship foundRelationship = this.template.findById(this.relationship.getId(), relationshipSource); + + Assert.assertNotNull(foundRelationship); + Assert.assertEquals(foundRelationship.getId(), this.relationship.getId()); + Assert.assertEquals(foundRelationship.getName(), this.relationship.getName()); + Assert.assertEquals(foundRelationship.getLocation(), this.relationship.getLocation()); + } + + @Test(expected = GremlinQueryException.class) + public void testEdgeInsertException() { + this.template.insert(this.person, this.personSource); + this.template.insert(this.project, this.projectSource); + this.template.insert(this.relationship, this.relationshipSource); + + final Relationship repeated = new Relationship(this.relationship.getId(), this.relationship.getName(), + this.relationship.getLocation(), this.person, this.project); + + this.template.insert(repeated, this.relationshipSource); + } + + @Test + public void testFindVertexById() { + Person foundPerson = this.template.findVertexById(this.person1.getId(), this.personSource); + Assert.assertNull(foundPerson); + + this.template.insert(this.person1, this.personSource); + + foundPerson = this.template.findVertexById(this.person1.getId(), this.personSource); + + Assert.assertNotNull(foundPerson); + Assert.assertEquals(foundPerson.getId(), this.person1.getId()); + Assert.assertEquals(foundPerson.getName(), this.person1.getName()); + } + + @Test(expected = GremlinUnexpectedEntityTypeException.class) + public void testFindVertexByIdException() { + this.template.insert(this.person, this.personSource); + this.template.insert(this.project0, this.projectSource); + this.template.insert(this.relationship2, this.relationshipSource); + + this.template.findVertexById(this.project.getId(), this.relationshipSource); + } + + @Test + public void testFindEdgeById() { + Relationship foundRelationship = this.template.findEdgeById(this.relationship2.getId(), relationshipSource); + Assert.assertNull(foundRelationship); + + this.template.insert(this.person, this.personSource); + this.template.insert(this.project0, this.projectSource); + this.template.insert(this.relationship2, this.relationshipSource); + + foundRelationship = this.template.findEdgeById(this.relationship2.getId(), this.relationshipSource); + + Assert.assertNotNull(foundRelationship); + Assert.assertEquals(foundRelationship.getId(), this.relationship2.getId()); + Assert.assertEquals(foundRelationship.getName(), this.relationship2.getName()); + Assert.assertEquals(foundRelationship.getLocation(), this.relationship2.getLocation()); + } + + @Test(expected = GremlinUnexpectedEntityTypeException.class) + public void testFindEdgeByIdException() { + this.template.insert(this.person, this.personSource); + this.template.insert(this.project0, this.projectSource); + this.template.insert(this.relationship2, this.relationshipSource); + + this.template.findEdgeById(this.relationship2.getId(), this.projectSource); + } + + @Test + public void testFindById() { + this.buildTestGraph(); + final Person foundPerson = this.template.findById(this.person1.getId(), this.personSource); + + Assert.assertNotNull(foundPerson); + Assert.assertEquals(foundPerson.getId(), this.person1.getId()); + Assert.assertEquals(foundPerson.getName(), this.person1.getName()); + + final Relationship foundRelationship = this.template.findById(this.relationship.getId(), relationshipSource); + + Assert.assertNotNull(foundRelationship); + Assert.assertEquals(foundRelationship.getId(), this.relationship.getId()); + Assert.assertEquals(foundRelationship.getName(), this.relationship.getName()); + Assert.assertEquals(foundRelationship.getLocation(), this.relationship.getLocation()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testFindByIdException() { + this.template.findById(this.network.getId(), this.networkSource); + } + + @Test(expected = GremlinQueryException.class) + public void testUpdateException() { + this.personSource.setId(this.person.getId()); + this.template.update(this.person, this.personSource); + } + + @Test + public void testUpdateVertex() { + this.template.insert(this.person, this.personSource); + + final String updatedName = "updated-person-name"; + final Person updatedPerson = new Person(this.person.getId(), updatedName); + + this.template.update(updatedPerson, this.personSource); + + final Person foundPerson = this.template.findById(updatedPerson.getId(), this.personSource); + + Assert.assertNotNull(foundPerson); + Assert.assertEquals(this.person.getId(), foundPerson.getId()); + Assert.assertEquals(updatedPerson.getId(), foundPerson.getId()); + Assert.assertEquals(updatedPerson.getName(), foundPerson.getName()); + } + + @Test + public void testUpdateEdge() { + this.template.insert(this.person, this.personSource); + this.template.insert(this.project0, this.projectSource); + this.template.insert(this.relationship2, this.relationshipSource); + + final String updatedName = "updated-relation-name"; + final String updatedLocation = "updated-location"; + final Relationship updatedRelationship = new Relationship(TestConstants.EDGE_RELATIONSHIP_2_ID, + updatedName, updatedLocation, this.person, this.project0); + + this.template.update(updatedRelationship, this.relationshipSource); + + final Relationship foundRelationship = this.template.findById(updatedRelationship.getId(), relationshipSource); + + Assert.assertNotNull(foundRelationship); + Assert.assertEquals(this.relationship2.getId(), foundRelationship.getId()); + Assert.assertEquals(updatedRelationship.getId(), foundRelationship.getId()); + Assert.assertEquals(updatedRelationship.getName(), foundRelationship.getName()); + Assert.assertEquals(updatedRelationship.getLocation(), foundRelationship.getLocation()); + } + + @Test + public void testUpdateGraph() { + this.buildTestGraph(); + + final String updatedName = "update-person-name"; + final String updatedLocation = "update-location"; + final String updatedUri = "http://localhost:2222"; + + final Person person = (Person) this.network.getVertexList().get(0); + final Project project = (Project) this.network.getVertexList().get(3); + final Relationship relationship = (Relationship) this.network.getEdgeList().get(0); + + person.setName(updatedName); + project.setUri(updatedUri); + relationship.setLocation(updatedLocation); + + this.template.update(network, this.networkInfo.createGremlinSource()); + + final Person foundPerson = this.template.findById(person.getId(), this.personSource); + final Project foundProject = this.template.findById(project.getId(), this.projectSource); + final Relationship foundRelationship = this.template.findById(relationship.getId(), this.relationshipSource); + + Assert.assertNotNull(foundPerson); + Assert.assertNotNull(foundProject); + Assert.assertNotNull(foundRelationship); + + Assert.assertEquals(foundPerson.getId(), person.getId()); + Assert.assertEquals(foundPerson.getName(), person.getName()); + + Assert.assertEquals(foundProject.getId(), project.getId()); + Assert.assertEquals(foundProject.getUri(), project.getUri()); + + Assert.assertEquals(foundRelationship.getId(), relationship.getId()); + Assert.assertEquals(foundRelationship.getLocation(), relationship.getLocation()); + } + + @Test + public void testSaveVertex() { + this.personSource.setId(this.person.getId()); + this.template.save(this.person, this.personSource); + + Person foundPerson = this.template.findById(this.person.getId(), this.personSource); + + Assert.assertNotNull(foundPerson); + Assert.assertEquals(foundPerson.getId(), this.person.getId()); + Assert.assertEquals(foundPerson.getName(), this.person.getName()); + + final String updatedName = "update-person-name"; + final Person updatedPerson = new Person(this.person.getId(), updatedName); + + this.personSource.setId(updatedPerson.getId()); + this.template.save(updatedPerson, this.personSource); + + foundPerson = this.template.findById(updatedPerson.getId(), this.personSource); + + Assert.assertNotNull(foundPerson); + Assert.assertEquals(foundPerson.getId(), updatedPerson.getId()); + Assert.assertEquals(foundPerson.getName(), updatedPerson.getName()); + } + + @Test + public void testSaveEdge() { + this.template.insert(this.person, this.personSource); + this.template.insert(this.project, this.projectSource); + this.relationshipSource.setId(this.relationship.getId()); + this.template.save(this.relationship, this.relationshipSource); + + Relationship foundRelationship = this.template.findById(this.relationship.getId(), this.relationshipSource); + + Assert.assertNotNull(foundRelationship); + Assert.assertEquals(foundRelationship.getId(), this.relationship.getId()); + Assert.assertEquals(foundRelationship.getName(), this.relationship.getName()); + Assert.assertEquals(foundRelationship.getLocation(), this.relationship.getLocation()); + + final String updatedName = "updated-relation-name"; + final String updatedLocation = "updated-location"; + final Relationship updatedRelationship = new Relationship(TestConstants.EDGE_RELATIONSHIP_2_ID, + updatedName, updatedLocation, this.person, this.project); + + this.relationshipSource.setId(updatedRelationship.getId()); + this.template.save(updatedRelationship, this.relationshipSource); + + foundRelationship = this.template.findById(updatedRelationship.getId(), this.relationshipSource); + + Assert.assertNotNull(foundRelationship); + Assert.assertEquals(this.relationship2.getId(), foundRelationship.getId()); + Assert.assertEquals(updatedRelationship.getId(), foundRelationship.getId()); + Assert.assertEquals(updatedRelationship.getName(), foundRelationship.getName()); + Assert.assertEquals(updatedRelationship.getLocation(), foundRelationship.getLocation()); + } + + @Test + public void testSaveGraph() { + this.network.vertexAdd(this.person); + this.network.vertexAdd(this.project); + this.network.edgeAdd(this.relationship); + + this.template.save(network, this.networkSource); + + final Person personFound = this.template.findById(this.person.getId(), this.personSource); + + Assert.assertNotNull(personFound); + Assert.assertEquals(personFound.getId(), this.person.getId()); + + Relationship relationshipFound = this.template.findById(this.relationship.getId(), this.relationshipSource); + + Assert.assertNotNull(relationshipFound); + Assert.assertEquals(relationshipFound.getId(), this.relationship.getId()); + + final String updatedName = "updated-name"; + this.relationship.setName(updatedName); + + this.template.save(network, this.networkInfo.createGremlinSource()); + + relationshipFound = this.template.findById(this.relationship.getId(), this.relationshipSource); + + Assert.assertNotNull(relationshipFound); + Assert.assertEquals(relationshipFound.getId(), this.relationship.getId()); + Assert.assertEquals(relationshipFound.getName(), updatedName); + } + + @Test + public void testFindAllVertex() { + List personList = this.template.findAll(this.personSource); + + Assert.assertTrue(personList.isEmpty()); + + final List personCollection = Arrays.asList(this.person, this.person0, this.person1); + personCollection.forEach(person -> this.template.insert(person, this.personSource)); + + personList = this.template.findAll(this.personSource); + + Assert.assertFalse(personList.isEmpty()); + Assert.assertEquals(personList.size(), personCollection.size()); + + personList.sort((a, b) -> (a.getId().compareTo(b.getId()))); + personCollection.sort((a, b) -> (a.getId().compareTo(b.getId()))); + + Assert.assertArrayEquals(personList.toArray(), personCollection.toArray()); + } + + @Test + public void testFindAllEdge() { + this.template.insert(this.person, this.personSource); + this.template.insert(this.person0, this.personSource); + this.template.insert(this.person1, this.personSource); + this.template.insert(this.project, this.projectSource); + this.template.insert(this.project0, this.projectSource); + + List relationshipList = this.template.findAll(this.relationshipSource); + + Assert.assertTrue(relationshipList.isEmpty()); + + final List relationshipCollection = Arrays.asList(this.relationship, this.relationship0, + this.relationship1, this.relationship2); + relationshipCollection.forEach(relationship -> this.template.insert(relationship, this.relationshipSource)); + + relationshipList = this.template.findAll(this.relationshipSource); + + Assert.assertFalse(relationshipList.isEmpty()); + Assert.assertEquals(relationshipList.size(), relationshipCollection.size()); + + relationshipList.sort((a, b) -> (a.getId().compareTo(b.getId()))); + relationshipCollection.sort((a, b) -> (a.getId().compareTo(b.getId()))); + + Assert.assertArrayEquals(relationshipList.toArray(), relationshipCollection.toArray()); + } + + @Test + public void testVertexDeleteById() { + this.template.deleteById(this.person.getId(), this.personSource); + this.template.insert(this.person, this.personSource); + this.template.deleteById(this.person0.getId(), this.personSource); + + Person foundPerson = this.template.findById(this.person.getId(), this.personSource); + Assert.assertNotNull(foundPerson); + + this.template.deleteById(this.person.getId(), this.personSource); + + foundPerson = this.template.findById(this.person.getId(), this.personSource); + Assert.assertNull(foundPerson); + } + + @Test + public void testEdgeDeleteById() { + this.template.deleteById(this.relationship.getId(), this.relationshipSource); + + this.template.insert(this.person, this.personSource); + this.template.insert(this.project, this.projectSource); + this.template.insert(this.relationship, this.relationshipSource); + + this.template.deleteById(this.relationship0.getId(), this.relationshipSource); + + Relationship foundRelationship = this.template.findById(this.relationship.getId(), this.relationshipSource); + Assert.assertNotNull(foundRelationship); + + this.template.deleteById(this.relationship.getId(), this.relationshipSource); + + foundRelationship = this.template.findById(this.relationship.getId(), this.relationshipSource); + Assert.assertNull(foundRelationship); + } + + @Test + public void testGraphDeleteById() { + this.network.setId("fake-id"); + this.template.deleteById(this.network.getId(), this.relationshipSource); + + final Relationship foundRelationship = this.template.findById(this.relationship.getId(), relationshipSource); + Assert.assertNull(foundRelationship); + + final Person foundPerson = this.template.findById(this.person.getId(), this.personSource); + Assert.assertNull(foundPerson); + } + + @Test + public void testIsEmptyGraph() { + Assert.assertTrue(this.template.isEmptyGraph(this.networkSource)); + + this.network.vertexAdd(this.person); + this.network.vertexAdd(this.project); + this.network.edgeAdd(this.relationship); + this.template.insert(this.network, this.networkSource); + + Assert.assertFalse(this.template.isEmptyGraph(this.networkSource)); + } + + @Test(expected = GremlinQueryException.class) + public void testIsEmptyGraphException() { + this.template.isEmptyGraph(this.relationshipSource); + } + + @Test(expected = UnsupportedOperationException.class) + public void testInvalidDependencySaveException() { + final InvalidDependency dependency = new InvalidDependency(this.relationship.getId(), + this.relationship.getName(), this.person.getId(), this.project.getId()); + final GremlinSource source = new GremlinSourceGraph<>(InvalidDependency.class); + + this.personSource.setId(this.person.getId()); + this.template.save(this.person, this.personSource); + this.projectSource.setId(this.project.getId()); + this.template.save(this.project, this.projectSource); + this.relationshipSource.setId(this.relationship.getId()); + this.template.save(this.relationship, this.relationshipSource); + + this.template.findById(dependency.getId(), source); + } + + @Configuration + static class TestConfiguration { + + @Autowired + private TestGremlinProperties properties; + + @Bean + public GremlinFactory getGremlinFactory() { + return new GremlinFactory(getGremlinConfig()); + } + + @Bean + public GremlinConfig getGremlinConfig() { + return GremlinConfig.builder(properties.getEndpoint(), properties.getUsername(), properties.getPassword()) + .sslEnabled(properties.isSslEnabled()) + .port(properties.getPort()) + .build(); + } + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/SimpleGremlinEntityMetadataUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/SimpleGremlinEntityMetadataUnitTest.java new file mode 100644 index 000000000000..d6a96722103a --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/SimpleGremlinEntityMetadataUnitTest.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query; + +import com.microsoft.spring.data.gremlin.common.domain.Person; +import org.junit.Assert; +import org.junit.Test; + +public class SimpleGremlinEntityMetadataUnitTest { + + @Test + public void testSimpleGremlinEntityMetadata() { + final SimpleGremlinEntityMetadata metadata = new SimpleGremlinEntityMetadata<>(Person.class); + + Assert.assertNotNull(metadata); + Assert.assertEquals(metadata.getJavaType(), Person.class); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/criteria/CriteriaUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/criteria/CriteriaUnitTest.java new file mode 100644 index 000000000000..cdb4df9f82e0 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/criteria/CriteriaUnitTest.java @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.criteria; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static com.microsoft.spring.data.gremlin.query.criteria.CriteriaType.IS_EQUAL; +import static com.microsoft.spring.data.gremlin.query.criteria.CriteriaType.OR; + +public class CriteriaUnitTest { + + @Test(expected = IllegalArgumentException.class) + public void testGetUnaryInstanceException() { + final List values = new ArrayList<>(); + + Criteria.getUnaryInstance(OR, "fake-name", values); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetBinaryInstanceException() { + final List values = new ArrayList<>(); + final Criteria left = Criteria.getUnaryInstance(IS_EQUAL, "fake-name", values); + final Criteria right = Criteria.getUnaryInstance(IS_EQUAL, "fake-name", values); + + Criteria.getBinaryInstance(IS_EQUAL, left, right); + } + + @Test(expected = UnsupportedOperationException.class) + public void testCriteriaTypeToGremlinException() { + CriteriaType.criteriaTypeToGremlin(IS_EQUAL); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/parameter/GremlinParameterUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/parameter/GremlinParameterUnitTest.java new file mode 100644 index 000000000000..a871132248f9 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/query/parameter/GremlinParameterUnitTest.java @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.query.parameter; + +import com.microsoft.spring.data.gremlin.query.paramerter.GremlinParameter; +import com.microsoft.spring.data.gremlin.query.paramerter.GremlinParameters; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.core.MethodParameter; +import org.springframework.lang.NonNull; + +import java.lang.reflect.Method; + +public class GremlinParameterUnitTest { + + private Method method; + private MethodParameter methodParameter; + + public String handle(@NonNull String name) { + return "handle: " + name; + } + + @Before + public void setup() throws NoSuchMethodException { + method = this.getClass().getMethod("handle", String.class); + methodParameter = new MethodParameter(this.getClass().getMethod("handle", String.class), 0); + } + + @Test + public void testGremlinParameter() { + final GremlinParameter parameter = new GremlinParameter(this.methodParameter); + + Assert.assertNotNull(parameter); + Assert.assertEquals(parameter.getType(), String.class); + Assert.assertEquals(parameter.getIndex(), 0); + } + + @Test + public void testGremlinParameters() { + final GremlinParameters gremlinParameters = new GremlinParameters(this.method); + + Assert.assertNotNull(gremlinParameters); + Assert.assertEquals(gremlinParameters.getNumberOfParameters(), 1); + Assert.assertNotNull(gremlinParameters.getParameter(0)); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/AdvancedUserRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/AdvancedUserRepositoryIT.java new file mode 100644 index 000000000000..b0ec0a3c9d6b --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/AdvancedUserRepositoryIT.java @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.AdvancedUser; +import com.microsoft.spring.data.gremlin.common.repository.AdvancedUserRepository; +import org.assertj.core.util.Lists; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class AdvancedUserRepositoryIT { + + private static final String ID_0 = "id-8000"; + private static final String ID_1 = "id-8001"; + + private static final String NAME_0 = "name-9000"; + private static final String NAME_1 = "name-9001"; + + private static final int LEVEL_0 = 4; + private static final int LEVEL_1 = 38; + + private static final AdvancedUser USER_0 = new AdvancedUser(ID_0, NAME_0, LEVEL_0); + private static final AdvancedUser USER_1 = new AdvancedUser(ID_1, NAME_1, LEVEL_1); + + @Autowired + private AdvancedUserRepository repository; + + @Before + public void setup() { + this.repository.deleteAll(); + } + + @Test + public void testCrudRepository() { + final List users = Arrays.asList(USER_0, USER_1); + this.repository.saveAll(users); + + final Optional optional = this.repository.findById(USER_0.getId()); + + Assert.assertTrue(optional.isPresent()); + Assert.assertEquals(USER_0.getId(), optional.get().getId()); + Assert.assertEquals(USER_0.getName(), optional.get().getName()); + Assert.assertEquals(USER_0.getLevel(), optional.get().getLevel()); + + final List foundUsers = Lists.newArrayList(this.repository.findAll(AdvancedUser.class)); + Assert.assertEquals(foundUsers.size(), users.size()); + + this.repository.deleteById(USER_0.getId()); + + Assert.assertFalse(this.repository.findById(USER_0.getId()).isPresent()); + Assert.assertTrue(this.repository.findById(USER_1.getId()).isPresent()); + + this.repository.deleteAll(); + + Assert.assertFalse(this.repository.findById(USER_1.getId()).isPresent()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/BookReferenceRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/BookReferenceRepositoryIT.java new file mode 100644 index 000000000000..842540906ed3 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/BookReferenceRepositoryIT.java @@ -0,0 +1,200 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.Book; +import com.microsoft.spring.data.gremlin.common.domain.BookReference; +import com.microsoft.spring.data.gremlin.common.repository.BookReferenceRepository; +import com.microsoft.spring.data.gremlin.common.repository.BookRepository; +import org.assertj.core.util.Lists; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.NonNull; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class BookReferenceRepositoryIT { + + private static final Integer BOOK_ID_0 = 0; + private static final Integer BOOK_ID_1 = 1; + private static final Integer BOOK_ID_2 = 2; + private static final Integer BOOK_REFERENCE_ID_0 = 3; + private static final Integer BOOK_REFERENCE_ID_1 = 4; + private static final Integer NO_EXIST_ID = -1; + + private static final String NAME_0 = "name-0"; + private static final String NAME_1 = "name-1"; + private static final String NAME_2 = "name-2"; + + private static final Double PRINCE_0 = 3.4; + private static final Double PRINCE_1 = 72.0; + private static final Double PRINCE_2 = 102.82; + + private static final Book BOOK_0 = new Book(BOOK_ID_0, NAME_0, PRINCE_0); + private static final Book BOOK_1 = new Book(BOOK_ID_1, NAME_1, PRINCE_1); + private static final Book BOOK_2 = new Book(BOOK_ID_2, NAME_2, PRINCE_2); + + private static final BookReference BOOK_REFERENCE_0 = new BookReference(BOOK_REFERENCE_ID_0, BOOK_ID_0, BOOK_ID_2); + private static final BookReference BOOK_REFERENCE_1 = new BookReference(BOOK_REFERENCE_ID_1, BOOK_ID_1, BOOK_ID_2); + + private static final List BOOKS = Arrays.asList(BOOK_0, BOOK_1, BOOK_2); + private static final List BOOK_REFERENCES = Arrays.asList(BOOK_REFERENCE_0, BOOK_REFERENCE_1); + + @Autowired + private BookReferenceRepository referenceRepository; + + @Autowired + private BookRepository bookRepository; + + @Before + public void setup() { + this.referenceRepository.deleteAll(); + this.bookRepository.deleteAll(); + } + + private void assertDomainListEquals(@NonNull List found, @NonNull List expected) { + found.sort(Comparator.comparing(BookReference::getId)); + expected.sort(Comparator.comparing(BookReference::getId)); + + Assert.assertEquals(found.size(), expected.size()); + Assert.assertEquals(found, expected); + } + + @Test + public void testDeleteAll() { + bookRepository.saveAll(BOOKS); + referenceRepository.saveAll(BOOK_REFERENCES); + + Assert.assertTrue(referenceRepository.findAll().iterator().hasNext()); + + referenceRepository.deleteAll(); + + Assert.assertFalse(referenceRepository.findAll().iterator().hasNext()); + } + + @Test + public void testDeleteAllOnType() { + bookRepository.saveAll(BOOKS); + referenceRepository.saveAll(BOOK_REFERENCES); + + Assert.assertTrue(referenceRepository.findAll().iterator().hasNext()); + + referenceRepository.deleteAll(GremlinEntityType.EDGE); + + Assert.assertFalse(referenceRepository.findAll().iterator().hasNext()); + } + + @Test + public void testDeleteAllOnDomain() { + bookRepository.saveAll(BOOKS); + referenceRepository.saveAll(BOOK_REFERENCES); + + Assert.assertTrue(referenceRepository.findAll().iterator().hasNext()); + + referenceRepository.deleteAll(BookReference.class); + + Assert.assertFalse(referenceRepository.findAll().iterator().hasNext()); + } + + @Test + public void testSave() { + bookRepository.saveAll(BOOKS); + referenceRepository.save(BOOK_REFERENCE_0); + + Assert.assertTrue(referenceRepository.findById(BOOK_REFERENCE_0.getId()).isPresent()); + Assert.assertFalse(referenceRepository.findById(BOOK_REFERENCE_1.getId()).isPresent()); + } + + @Test + public void testSaveAll() { + bookRepository.saveAll(BOOKS); + referenceRepository.saveAll(BOOK_REFERENCES); + + final List found = Lists.newArrayList(referenceRepository.findAll()); + + assertDomainListEquals(found, BOOK_REFERENCES); + } + + @Test + public void testFindById() { + bookRepository.saveAll(BOOKS); + referenceRepository.saveAll(BOOK_REFERENCES); + + Optional optional = referenceRepository.findById(BOOK_REFERENCE_0.getId()); + + Assert.assertTrue(optional.isPresent()); + Assert.assertEquals(optional.get(), BOOK_REFERENCE_0); + + optional = referenceRepository.findById(NO_EXIST_ID); + + Assert.assertFalse(optional.isPresent()); + } + + @Test + public void testExistsById() { + bookRepository.saveAll(BOOKS); + referenceRepository.saveAll(BOOK_REFERENCES); + + Assert.assertTrue(referenceRepository.existsById(BOOK_REFERENCE_0.getId())); + Assert.assertFalse(referenceRepository.existsById(NO_EXIST_ID)); + } + + @Test + public void testFindAllById() { + bookRepository.saveAll(BOOKS); + referenceRepository.saveAll(BOOK_REFERENCES); + + final List ids = Arrays.asList(BOOK_REFERENCE_0.getId(), BOOK_REFERENCE_1.getId()); + final List found = Lists.newArrayList(referenceRepository.findAllById(ids)); + + assertDomainListEquals(found, BOOK_REFERENCES); + + Assert.assertFalse(referenceRepository.findAllById(Collections.singleton(NO_EXIST_ID)).iterator().hasNext()); + } + + @Test + public void testCount() { + bookRepository.saveAll(BOOKS); + referenceRepository.saveAll(BOOK_REFERENCES); + + Assert.assertEquals(referenceRepository.count(), BOOK_REFERENCES.size() + BOOKS.size()); + + referenceRepository.deleteAll(); + + Assert.assertEquals(referenceRepository.count(), 0); + } + + @Test + public void testDeleteById() { + bookRepository.saveAll(BOOKS); + referenceRepository.saveAll(BOOK_REFERENCES); + + Assert.assertTrue(referenceRepository.findById(BOOK_REFERENCE_0.getId()).isPresent()); + + referenceRepository.deleteById(BOOK_REFERENCE_0.getId()); + + Assert.assertFalse(referenceRepository.findById(BOOK_REFERENCE_0.getId()).isPresent()); + } + + @Test + public void testEdgeCount() { + bookRepository.saveAll(BOOKS); + referenceRepository.saveAll(BOOK_REFERENCES); + + Assert.assertEquals(referenceRepository.edgeCount(), BOOK_REFERENCES.size()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/BookRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/BookRepositoryIT.java new file mode 100644 index 000000000000..823d1041fd33 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/BookRepositoryIT.java @@ -0,0 +1,224 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.Book; +import com.microsoft.spring.data.gremlin.common.repository.BookRepository; +import org.assertj.core.util.Lists; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.NonNull; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class BookRepositoryIT { + + private static final Integer ID_0 = 0; + private static final Integer ID_1 = 1; + private static final Integer ID_2 = 2; + private static final Integer NO_EXIST_ID = -1; + + private static final String NAME_0 = "name-0"; + private static final String NAME_1 = "name-1"; + private static final String NAME_2 = "name-2"; + private static final String NO_EXIST_NAME = "no-exist-name"; + + private static final Double PRINCE_0 = 3.4; + private static final Double PRINCE_1 = 72.0; + private static final Double PRINCE_2 = 102.82; + private static final Double NO_EXIST_PRICE = -144.23; + + private static final Book BOOK_0 = new Book(ID_0, NAME_0, PRINCE_0); + private static final Book BOOK_1 = new Book(ID_1, NAME_1, PRINCE_1); + private static final Book BOOK_2 = new Book(ID_2, NAME_2, PRINCE_2); + + private static final List BOOKS = Arrays.asList(BOOK_0, BOOK_1, BOOK_2); + + @Autowired + private BookRepository repository; + + @Before + public void setup() { + this.repository.deleteAll(); + } + + private void assertDomainListEquals(@NonNull List found, @NonNull List expected) { + found.sort(Comparator.comparing(Book::getSerialNumber)); + expected.sort(Comparator.comparing(Book::getSerialNumber)); + + Assert.assertEquals(found.size(), expected.size()); + Assert.assertEquals(found, expected); + } + + @Test + public void testDeleteAll() { + repository.saveAll(BOOKS); + + Assert.assertTrue(repository.findAll().iterator().hasNext()); + + repository.deleteAll(); + + Assert.assertFalse(repository.findAll().iterator().hasNext()); + } + + @Test + public void testDeleteAllOnType() { + repository.saveAll(BOOKS); + + Assert.assertTrue(repository.findAll().iterator().hasNext()); + + repository.deleteAll(GremlinEntityType.VERTEX); + + Assert.assertFalse(repository.findAll().iterator().hasNext()); + } + + @Test + public void testDeleteAllOnDomain() { + repository.saveAll(BOOKS); + + Assert.assertTrue(repository.findAll().iterator().hasNext()); + + repository.deleteAll(Book.class); + + Assert.assertFalse(repository.findAll().iterator().hasNext()); + } + + @Test + public void testSave() { + repository.save(BOOK_0); + + Assert.assertTrue(repository.findById(BOOK_0.getSerialNumber()).isPresent()); + Assert.assertFalse(repository.findById(BOOK_1.getSerialNumber()).isPresent()); + } + + @Test + public void testSaveAll() { + repository.saveAll(BOOKS); + + final List found = Lists.newArrayList(repository.findAll()); + + assertDomainListEquals(found, BOOKS); + } + + @Test + public void testFindById() { + repository.saveAll(BOOKS); + + Optional optional = repository.findById(BOOK_0.getSerialNumber()); + + Assert.assertTrue(optional.isPresent()); + Assert.assertEquals(optional.get(), BOOK_0); + + optional = repository.findById(NO_EXIST_ID); + + Assert.assertFalse(optional.isPresent()); + } + + @Test + public void testExistsById() { + repository.saveAll(BOOKS); + + Assert.assertTrue(repository.existsById(BOOK_0.getSerialNumber())); + Assert.assertFalse(repository.existsById(NO_EXIST_ID)); + } + + @Test + public void testFindAllById() { + final List expected = Arrays.asList(BOOK_0, BOOK_1); + final List ids = Arrays.asList(BOOK_0.getSerialNumber(), BOOK_1.getSerialNumber(), NO_EXIST_ID); + + repository.saveAll(BOOKS); + + final List found = Lists.newArrayList(repository.findAllById(ids)); + + assertDomainListEquals(found, expected); + + Assert.assertFalse(repository.findAllById(Collections.singleton(NO_EXIST_ID)).iterator().hasNext()); + } + + @Test + public void testCount() { + repository.saveAll(BOOKS); + + Assert.assertEquals(repository.count(), BOOKS.size()); + + repository.deleteAll(); + + Assert.assertEquals(repository.count(), 0); + } + + @Test + public void testDeleteById() { + repository.saveAll(BOOKS); + + Assert.assertTrue(repository.findById(BOOK_0.getSerialNumber()).isPresent()); + + repository.deleteById(BOOK_0.getSerialNumber()); + + Assert.assertFalse(repository.findById(BOOK_0.getSerialNumber()).isPresent()); + } + + @Test + public void testVertexCount() { + repository.saveAll(BOOKS); + + Assert.assertEquals(repository.vertexCount(), BOOKS.size()); + } + + @Test + public void testEdgeCount() { + repository.saveAll(BOOKS); + + Assert.assertEquals(repository.edgeCount(), 0); + } + + @Test + public void testFindByNameOrPrince() { + repository.saveAll(BOOKS); + + List found = repository.findByNameOrPrice(BOOK_0.getName(), BOOK_1.getPrice()); + + assertDomainListEquals(found, Arrays.asList(BOOK_0, BOOK_1)); + + found = repository.findByNameOrPrice(NO_EXIST_NAME, NO_EXIST_PRICE); + + Assert.assertTrue(found.isEmpty()); + } + + @Test + public void testFindAllIncomplete() { + final Book book = new Book(ID_0, null, 2.34); + final Book bookNullName = new Book(ID_1, "null", 243.34); + + repository.save(book); + repository.save(bookNullName); + + final Optional optional = repository.findById(book.getSerialNumber()); + + Assert.assertTrue(optional.isPresent()); + Assert.assertEquals(optional.get(), book); + + final Optional optionalNullName = repository.findById(bookNullName.getSerialNumber()); + + Assert.assertTrue(optionalNullName.isPresent()); + Assert.assertEquals(optionalNullName.get(), bookNullName); + + final List books = Lists.newArrayList(repository.findAll()); + + assertDomainListEquals(books, Arrays.asList(book, bookNullName)); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/GroupRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/GroupRepositoryIT.java new file mode 100644 index 000000000000..a2df718d94e7 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/GroupRepositoryIT.java @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.Group; +import com.microsoft.spring.data.gremlin.common.domain.GroupOwner; +import com.microsoft.spring.data.gremlin.common.domain.Student; +import com.microsoft.spring.data.gremlin.common.repository.GroupOwnerRepository; +import com.microsoft.spring.data.gremlin.common.repository.GroupRepository; +import com.microsoft.spring.data.gremlin.common.repository.StudentRepository; +import com.microsoft.spring.data.gremlin.common.TestUtils; +import org.assertj.core.util.Lists; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class GroupRepositoryIT { + + private static final Long STUDENT_ID_0 = 1111L; + private static final Long STUDENT_ID_1 = 1234L; + private static final Long STUDENT_ID_2 = 2345L; + private static final Long STUDENT_ID_3 = 4823L; + + private static final String STUDENT_NAME_0 = "student-0"; + private static final String STUDENT_NAME_1 = "student-1"; + private static final String STUDENT_NAME_2 = "student-2"; + private static final String STUDENT_NAME_3 = "student-3"; + + private static final String GROUP_OWNER_ID_0 = "owner-0"; + private static final String GROUP_OWNER_ID_1 = "owner-1"; + + private static final Integer GROUP_OWNER_EXPIRE_DAYS_0 = 90; + private static final Integer GROUP_OWNER_EXPIRE_DAYS_1 = 120; + + private static final Student STUDENT_0 = new Student(STUDENT_ID_0, STUDENT_NAME_0); + private static final Student STUDENT_1 = new Student(STUDENT_ID_1, STUDENT_NAME_1); + private static final Student STUDENT_2 = new Student(STUDENT_ID_2, STUDENT_NAME_2); + private static final Student STUDENT_3 = new Student(STUDENT_ID_3, STUDENT_NAME_3); + + private static final GroupOwner GROUP_OWNER_0 = new GroupOwner(GROUP_OWNER_ID_0, GROUP_OWNER_EXPIRE_DAYS_0); + private static final GroupOwner GROUP_OWNER_1 = new GroupOwner(GROUP_OWNER_ID_1, GROUP_OWNER_EXPIRE_DAYS_1); + + private static final Group GROUP_0 = new Group(STUDENT_0, GROUP_OWNER_0); + private static final Group GROUP_1 = new Group(STUDENT_1, GROUP_OWNER_0); + private static final Group GROUP_2 = new Group(STUDENT_2, GROUP_OWNER_0); + private static final Group GROUP_3 = new Group(STUDENT_3, GROUP_OWNER_0); + private static final Group GROUP_4 = new Group(STUDENT_0, GROUP_OWNER_1); + private static final Group GROUP_5 = new Group(STUDENT_1, GROUP_OWNER_1); + + private static final List STUDENTS = Arrays.asList(STUDENT_0, STUDENT_1, STUDENT_2, STUDENT_3); + private static final List GROUP_OWNERS = Arrays.asList(GROUP_OWNER_0, GROUP_OWNER_1); + private static final List GROUPS = Arrays.asList(GROUP_0, GROUP_1, GROUP_2, GROUP_3, GROUP_4, GROUP_5); + + @Autowired + private GroupRepository groupRepository; + + @Autowired + private GroupOwnerRepository groupOwnerRepository; + + @Autowired + private StudentRepository studentRepository; + + @Before + public void setup() { + this.groupOwnerRepository.deleteAll(); + this.studentRepository.deleteAll(); + this.groupRepository.deleteAll(); + + this.studentRepository.saveAll(STUDENTS); + this.groupOwnerRepository.saveAll(GROUP_OWNERS); + } + + @After + public void cleanup() { + this.groupOwnerRepository.deleteAll(); + this.studentRepository.deleteAll(); + this.groupRepository.deleteAll(); + } + + @Test + public void testGeneratedIdFindById() { + final Group expect = this.groupRepository.save(GROUP_0); + + Assert.assertNotNull(expect.getId()); + + final Optional optional = this.groupRepository.findById(expect.getId()); + + Assert.assertTrue(optional.isPresent()); + Assert.assertEquals(expect, optional.get()); + } + + @Test + public void testGeneratedIdFindAll() { + final List expect = Lists.newArrayList(this.groupRepository.saveAll(GROUPS)); + final List actual = Lists.newArrayList(this.groupRepository.findAll()); + + TestUtils.assertEntitiesEquals(expect, actual); + } + + @Test + public void testGeneratedIdDeleteById() { + final Group group = this.groupRepository.save(GROUP_0); + + this.groupRepository.deleteById(group.getId()); + + Assert.assertFalse(this.groupRepository.findById(group.getId()).isPresent()); + Assert.assertFalse(this.groupRepository.existsById(group.getId())); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/MasterRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/MasterRepositoryIT.java new file mode 100644 index 000000000000..270756cf165d --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/MasterRepositoryIT.java @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.Master; +import com.microsoft.spring.data.gremlin.common.domain.Student; +import com.microsoft.spring.data.gremlin.common.repository.MasterRepository; +import com.microsoft.spring.data.gremlin.common.repository.StudentRepository; +import org.assertj.core.util.Lists; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.List; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class MasterRepositoryIT { + + private static final Long ID_STUDENT = 1L; + private static final Long ID_MASTER = 2L; + + private static final String NAME = "name"; + + private static final Student STUDENT = new Student(ID_STUDENT, NAME); + + private static final Master MASTER = new Master(ID_MASTER, NAME); + + @Autowired + private MasterRepository masterRepository; + + @Autowired + private StudentRepository studentRepository; + + @Before + public void setup() { + this.masterRepository.deleteAll(); + this.studentRepository.deleteAll(); + } + + @After + public void cleanup() { + this.masterRepository.deleteAll(); + this.studentRepository.deleteAll(); + } + + @Test + public void testDuplicatedLabelFindAll() { + this.studentRepository.save(STUDENT); + this.masterRepository.save(MASTER); + + final List masters = Lists.newArrayList(this.masterRepository.findAll(Master.class)); + + Assert.assertEquals(masters.size(), 1); + Assert.assertEquals(masters.get(0), MASTER); + + final List students = Lists.newArrayList(this.studentRepository.findAll(Student.class)); + + Assert.assertEquals(students.size(), 1); + Assert.assertEquals(students.get(0), STUDENT); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/NeighborRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/NeighborRepositoryIT.java new file mode 100644 index 000000000000..2ad6ba57ea13 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/NeighborRepositoryIT.java @@ -0,0 +1,198 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.Neighbor; +import com.microsoft.spring.data.gremlin.common.domain.Student; +import com.microsoft.spring.data.gremlin.common.repository.NeighborRepository; +import com.microsoft.spring.data.gremlin.common.repository.StudentRepository; +import org.assertj.core.util.Lists; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.NonNull; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class NeighborRepositoryIT { + + private static final Long STUDENT_ID_0 = 12349274637234L; + private static final Long STUDENT_ID_1 = 1L; + private static final Long STUDENT_ID_2 = 2L; + private static final Long DISTANCE_0 = 1234L; + private static final Long DISTANCE_1 = 3422L; + private static final Long NEIGHBOR_ID_0 = 3L; + private static final Long NEIGHBOR_ID_1 = 4L; + private static final Long NO_EXIST_ID = -1L; + + private static final String NAME_0 = "name-0"; + private static final String NAME_1 = "name-1"; + private static final String NAME_2 = "name-2"; + + private static final Student STUDENT_0 = new Student(STUDENT_ID_0, NAME_0); + private static final Student STUDENT_1 = new Student(STUDENT_ID_1, NAME_1); + private static final Student STUDENT_2 = new Student(STUDENT_ID_2, NAME_2); + + private static final Neighbor NEIGHBOR_0 = new Neighbor(NEIGHBOR_ID_0, DISTANCE_0, STUDENT_0, STUDENT_1); + private static final Neighbor NEIGHBOR_1 = new Neighbor(NEIGHBOR_ID_1, DISTANCE_1, STUDENT_2, STUDENT_1); + + private static final List STUDENTS = Arrays.asList(STUDENT_0, STUDENT_1, STUDENT_2); + private static final List NEIGHBORS = Arrays.asList(NEIGHBOR_0, NEIGHBOR_1); + + @Autowired + private StudentRepository studentRepository; + + @Autowired + private NeighborRepository neighborRepository; + + @Before + public void setup() { + this.studentRepository.deleteAll(); + this.neighborRepository.deleteAll(); + } + + private void assertDomainListEquals(@NonNull List found, @NonNull List expected) { + found.sort(Comparator.comparing(Neighbor::getId)); + expected.sort(Comparator.comparing(Neighbor::getId)); + + Assert.assertEquals(found.size(), expected.size()); + Assert.assertEquals(found, expected); + } + + @Test + public void testDeleteAll() { + neighborRepository.deleteAll(); + + Assert.assertFalse(neighborRepository.findAll().iterator().hasNext()); + + studentRepository.saveAll(STUDENTS); + neighborRepository.saveAll(NEIGHBORS); + + Assert.assertTrue(neighborRepository.findAll().iterator().hasNext()); + } + + @Test + public void testDeleteAllOnType() { + neighborRepository.deleteAll(GremlinEntityType.EDGE); + + Assert.assertFalse(neighborRepository.findAll().iterator().hasNext()); + + studentRepository.saveAll(STUDENTS); + neighborRepository.saveAll(NEIGHBORS); + + Assert.assertTrue(neighborRepository.findAll().iterator().hasNext()); + } + + @Test + public void testDeleteAllOnDomain() { + studentRepository.saveAll(STUDENTS); + neighborRepository.saveAll(NEIGHBORS); + + Assert.assertTrue(neighborRepository.findAll().iterator().hasNext()); + + neighborRepository.deleteAll(Neighbor.class); + + Assert.assertFalse(neighborRepository.findAll().iterator().hasNext()); + } + + @Test + public void testSave() { + studentRepository.saveAll(STUDENTS); + neighborRepository.save(NEIGHBOR_0); + + Assert.assertTrue(neighborRepository.findById(NEIGHBOR_0.getId()).isPresent()); + Assert.assertFalse(neighborRepository.findById(NEIGHBOR_1.getId()).isPresent()); + } + + @Test + public void testSaveAll() { + studentRepository.saveAll(STUDENTS); + neighborRepository.saveAll(NEIGHBORS); + + final List found = Lists.newArrayList(neighborRepository.findAll()); + + assertDomainListEquals(found, NEIGHBORS); + } + + @Test + public void testFindById() { + studentRepository.saveAll(STUDENTS); + neighborRepository.saveAll(NEIGHBORS); + + Optional optional = neighborRepository.findById(NEIGHBOR_0.getId()); + + Assert.assertTrue(optional.isPresent()); + Assert.assertEquals(optional.get(), NEIGHBOR_0); + + optional = neighborRepository.findById(NO_EXIST_ID); + + Assert.assertFalse(optional.isPresent()); + } + + @Test + public void testExistsById() { + studentRepository.saveAll(STUDENTS); + neighborRepository.saveAll(NEIGHBORS); + + Assert.assertTrue(neighborRepository.existsById(NEIGHBOR_0.getId())); + Assert.assertFalse(neighborRepository.existsById(NO_EXIST_ID)); + } + + @Test + public void testFindAllById() { + studentRepository.saveAll(STUDENTS); + neighborRepository.saveAll(NEIGHBORS); + + final List ids = Arrays.asList(NEIGHBOR_0.getId(), NEIGHBOR_1.getId()); + final List found = Lists.newArrayList(neighborRepository.findAllById(ids)); + + assertDomainListEquals(found, NEIGHBORS); + + Assert.assertFalse(neighborRepository.findAllById(Collections.singleton(NO_EXIST_ID)).iterator().hasNext()); + } + + @Test + public void testCount() { + studentRepository.saveAll(STUDENTS); + neighborRepository.saveAll(NEIGHBORS); + + Assert.assertEquals(neighborRepository.count(), STUDENTS.size() + NEIGHBORS.size()); + + neighborRepository.deleteAll(); + + Assert.assertEquals(neighborRepository.count(), 0); + } + + @Test + public void testDeleteById() { + studentRepository.saveAll(STUDENTS); + neighborRepository.saveAll(NEIGHBORS); + + Assert.assertTrue(neighborRepository.findById(NEIGHBOR_0.getId()).isPresent()); + + neighborRepository.deleteById(NEIGHBOR_0.getId()); + + Assert.assertFalse(neighborRepository.findById(NEIGHBOR_0.getId()).isPresent()); + } + + @Test + public void testEdgeCount() { + studentRepository.saveAll(STUDENTS); + neighborRepository.saveAll(NEIGHBORS); + + Assert.assertEquals(neighborRepository.edgeCount(), NEIGHBORS.size()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/NetworkRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/NetworkRepositoryIT.java new file mode 100644 index 000000000000..a21da92c0669 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/NetworkRepositoryIT.java @@ -0,0 +1,142 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.Network; +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.domain.Project; +import com.microsoft.spring.data.gremlin.common.domain.Relationship; +import com.microsoft.spring.data.gremlin.common.repository.NetworkRepository; +import com.microsoft.spring.data.gremlin.common.repository.PersonRepository; +import com.microsoft.spring.data.gremlin.common.repository.ProjectRepository; +import com.microsoft.spring.data.gremlin.common.repository.RelationshipRepository; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Collections; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class NetworkRepositoryIT { + + private final Person person = new Person(TestConstants.VERTEX_PERSON_ID, TestConstants.VERTEX_PERSON_NAME); + private final Project project = new Project(TestConstants.VERTEX_PROJECT_ID, TestConstants.VERTEX_PROJECT_NAME, + TestConstants.VERTEX_PROJECT_URI); + private final Relationship relationship = new Relationship(TestConstants.EDGE_RELATIONSHIP_ID, + TestConstants.EDGE_RELATIONSHIP_NAME, TestConstants.EDGE_RELATIONSHIP_LOCATION, + this.person, this.project); + + @Autowired + private NetworkRepository networkRepository; + + @Autowired + private PersonRepository personRepository; + + @Autowired + private ProjectRepository projectRepository; + + @Autowired + private RelationshipRepository relationshipRepository; + + @Before + public void setup() { + this.networkRepository.deleteAll(); + } + + @After + public void cleanup() { + this.networkRepository.deleteAll(); + } + + @Test + public void testDeleteById() { + final Network network = new Network(); + + network.setId("fake-id"); + network.vertexAdd(this.person); + network.vertexAdd(this.project); + network.edgeAdd(this.relationship); + + this.networkRepository.save(network); + + Assert.assertTrue(personRepository.findById(this.person.getId()).isPresent()); + Assert.assertTrue(projectRepository.findById(this.project.getId()).isPresent()); + Assert.assertTrue(relationshipRepository.findById(this.relationship.getId()).isPresent()); + + this.networkRepository.deleteById(network.getId()); + + Assert.assertFalse(personRepository.findById(this.person.getId()).isPresent()); + Assert.assertFalse(projectRepository.findById(this.project.getId()).isPresent()); + Assert.assertFalse(relationshipRepository.findById(this.relationship.getId()).isPresent()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testFindAllException() { + final Network network = new Network(); + + network.setId("fake-id"); + network.vertexAdd(this.person); + network.vertexAdd(this.project); + network.edgeAdd(this.relationship); + + this.networkRepository.save(network); + this.networkRepository.findAll(Network.class); + } + + @Test(expected = UnsupportedOperationException.class) + public void testFindScriptGeneratorException() { + final Network network = new Network(); + + network.setId("fake-id"); + network.vertexAdd(this.person); + network.vertexAdd(this.project); + network.edgeAdd(this.relationship); + + this.networkRepository.save(network); + this.networkRepository.findByEdgeList(Collections.singletonList(this.relationship)); + } + + @Test + public void testDeleteAllByType() { + final Network network = new Network(); + + network.setId("fake-id"); + network.vertexAdd(this.person); + network.vertexAdd(this.project); + network.edgeAdd(this.relationship); + + this.networkRepository.save(network); + this.networkRepository.deleteAll(GremlinEntityType.GRAPH); + + Assert.assertFalse(this.personRepository.findById(this.person.getId()).isPresent()); + Assert.assertFalse(this.projectRepository.findById(this.project.getId()).isPresent()); + Assert.assertFalse(this.relationshipRepository.findById(this.relationship.getId()).isPresent()); + } + + @Test + public void testDeleteAllByClass() { + final Network network = new Network(); + + network.setId("fake-id"); + network.vertexAdd(this.person); + network.vertexAdd(this.project); + network.edgeAdd(this.relationship); + + this.networkRepository.save(network); + this.networkRepository.deleteAll(Network.class); + + Assert.assertFalse(this.relationshipRepository.findById(this.relationship.getId()).isPresent()); + Assert.assertFalse(this.personRepository.findById(this.person.getId()).isPresent()); + Assert.assertFalse(this.projectRepository.findById(this.project.getId()).isPresent()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/OrangeRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/OrangeRepositoryIT.java new file mode 100644 index 000000000000..19ebf3e46caa --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/OrangeRepositoryIT.java @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.Orange; +import com.microsoft.spring.data.gremlin.common.repository.OrangeRepository; +import com.microsoft.spring.data.gremlin.common.TestUtils; +import org.assertj.core.util.Lists; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class OrangeRepositoryIT { + + private static final String LOCATION_0 = "location-0"; + private static final String LOCATION_1 = "location-1"; + private static final String LOCATION_2 = "location-2"; + private static final String LOCATION_3 = "location-3"; + + private static final Double PRINCE_0 = 1.79; + private static final Double PRINCE_1 = 2.09; + private static final Double PRINCE_2 = 3.29; + private static final Double PRINCE_3 = 3.99; + + private static final Orange ORANGE_0 = new Orange(LOCATION_0, PRINCE_0); + private static final Orange ORANGE_1 = new Orange(LOCATION_1, PRINCE_1); + private static final Orange ORANGE_2 = new Orange(LOCATION_2, PRINCE_2); + private static final Orange ORANGE_3 = new Orange(LOCATION_3, PRINCE_3); + + private static final List ORANGES = Arrays.asList(ORANGE_0, ORANGE_1, ORANGE_2, ORANGE_3); + + @Autowired + private OrangeRepository repository; + + @Before + public void setup() { + this.repository.deleteAll(); + } + + @After + public void cleanup() { + this.repository.deleteAll(); + } + + @Test + public void testGeneratedIdFindById() { + final Orange orange = this.repository.save(ORANGE_0); + + Assert.assertNotNull(orange.getId()); + + final Optional optional = this.repository.findById(orange.getId()); + + Assert.assertTrue(optional.isPresent()); + Assert.assertEquals(optional.get(), orange); + } + + @Test + public void testGeneratedIdFindAll() { + final List expect = Lists.newArrayList(this.repository.saveAll(ORANGES)); + final List actual = Lists.newArrayList(this.repository.findAll()); + + TestUtils.assertEntitiesEquals(expect, actual); + } + + @Test + public void testGeneratedIdDeleteById() { + final Orange orange = this.repository.save(ORANGE_0); + + this.repository.deleteById(orange.getId()); + + Assert.assertFalse(this.repository.findById(orange.getId()).isPresent()); + Assert.assertFalse(this.repository.existsById(orange.getId())); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/PersonRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/PersonRepositoryIT.java new file mode 100644 index 000000000000..f732aa58c5dc --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/PersonRepositoryIT.java @@ -0,0 +1,233 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.domain.Project; +import com.microsoft.spring.data.gremlin.common.repository.PersonRepository; +import com.microsoft.spring.data.gremlin.common.repository.ProjectRepository; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import org.assertj.core.util.Lists; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class PersonRepositoryIT { + + private final Person person = new Person(TestConstants.VERTEX_PERSON_ID, TestConstants.VERTEX_PERSON_NAME); + private final Person person0 = new Person(TestConstants.VERTEX_PERSON_0_ID, TestConstants.VERTEX_PERSON_0_NAME); + private final Project project = new Project(TestConstants.VERTEX_PROJECT_ID, TestConstants.VERTEX_PROJECT_NAME, + TestConstants.VERTEX_PROJECT_URI); + + @Autowired + private PersonRepository repository; + + @Autowired + private ProjectRepository projectRepository; + + @Before + public void setup() { + this.repository.deleteAll(); + } + + @After + public void cleanup() { + this.repository.deleteAll(); + } + + @Test + public void testDeleteAll() { + this.repository.save(this.person); + + Assert.assertTrue(this.repository.existsById(this.person.getId())); + + this.repository.deleteAll(); + + Assert.assertFalse(this.repository.existsById(this.person.getId())); + } + + @Test + public void testDeleteById() { + this.repository.save(this.person); + this.repository.save(this.person0); + + Assert.assertTrue(this.repository.existsById(this.person.getId())); + Assert.assertTrue(this.repository.existsById(this.person0.getId())); + + this.repository.deleteById(this.person.getId()); + + Assert.assertFalse(this.repository.existsById(this.person.getId())); + Assert.assertTrue(this.repository.existsById(this.person0.getId())); + } + + @Test + public void testDelete() { + this.repository.save(this.person); + this.repository.save(this.person0); + + Assert.assertTrue(this.repository.existsById(this.person.getId())); + Assert.assertTrue(this.repository.existsById(this.person0.getId())); + + this.repository.delete(this.person); + + Assert.assertFalse(this.repository.existsById(this.person.getId())); + Assert.assertTrue(this.repository.existsById(this.person0.getId())); + } + + @Test + public void testDeleteAllIds() { + final List domains = Arrays.asList(this.person, this.person0); + + this.repository.save(this.person); + this.repository.save(this.person0); + + this.repository.deleteAll(domains); + + Assert.assertFalse(this.repository.existsById(this.person.getId())); + Assert.assertFalse(this.repository.existsById(this.person0.getId())); + } + + @Test + public void testSave() { + this.repository.save(this.person); + this.repository.save(this.person0); + + Assert.assertTrue(this.repository.existsById(this.person.getId())); + Assert.assertTrue(this.repository.existsById(this.person0.getId())); + } + + @Test + public void testSaveAll() { + final List domains = Arrays.asList(this.person, this.person0); + + this.repository.saveAll(domains); + + Assert.assertTrue(this.repository.existsById(this.person.getId())); + Assert.assertTrue(this.repository.existsById(this.person0.getId())); + } + + @Test + public void testFindById() { + this.repository.save(this.person); + + final Person foundPerson = this.repository.findById(this.person.getId()).get(); + + Assert.assertNotNull(foundPerson); + Assert.assertEquals(foundPerson.getId(), this.person.getId()); + Assert.assertEquals(foundPerson.getName(), this.person.getName()); + + Assert.assertFalse(this.repository.findById(this.person0.getId()).isPresent()); + } + + @Test + public void testExistById() { + Assert.assertFalse(this.repository.existsById(this.person.getId())); + + this.repository.save(this.person); + + Assert.assertTrue(this.repository.existsById(this.person.getId())); + } + + @Test + public void testFindAllById() { + final List domains = Arrays.asList(this.person, this.person0); + final List ids = Arrays.asList(this.person.getId(), this.person0.getId()); + + this.repository.saveAll(domains); + + final List foundDomains = (List) this.repository.findAllById(ids); + + domains.sort((a, b) -> (a.getId().compareTo(b.getId()))); + foundDomains.sort((a, b) -> (a.getId().compareTo(b.getId()))); + + Assert.assertArrayEquals(domains.toArray(), foundDomains.toArray()); + } + + @Test + public void testDomainClassFindAll() { + final List domains = Arrays.asList(this.person, this.person0); + List foundDomains = (List) this.repository.findAll(Person.class); + + Assert.assertTrue(foundDomains.isEmpty()); + + this.repository.saveAll(domains); + + foundDomains = (List) this.repository.findAll(Person.class); + + Assert.assertEquals(domains.size(), foundDomains.size()); + + domains.sort((a, b) -> (a.getId().compareTo(b.getId()))); + foundDomains.sort((a, b) -> (a.getId().compareTo(b.getId()))); + + Assert.assertArrayEquals(domains.toArray(), foundDomains.toArray()); + } + + @Test + public void testVertexCount() { + Assert.assertEquals(this.repository.count(), 0); + Assert.assertEquals(this.repository.edgeCount(), 0); + Assert.assertEquals(this.repository.vertexCount(), 0); + + this.repository.save(this.person); + this.repository.save(this.person0); + + Assert.assertEquals(this.repository.count(), 2); + Assert.assertEquals(this.repository.edgeCount(), 0); + Assert.assertEquals(this.repository.vertexCount(), this.repository.count()); + } + + @Test + public void testDeleteAllByType() { + this.repository.save(this.person); + this.repository.save(this.person0); + + this.repository.deleteAll(GremlinEntityType.VERTEX); + + Assert.assertFalse(this.repository.findById(this.person.getId()).isPresent()); + Assert.assertFalse(this.repository.findById(this.person0.getId()).isPresent()); + } + + @Test + public void testDeleteAllByClass() { + this.repository.save(this.person); + this.repository.save(this.person0); + this.projectRepository.save(this.project); + + this.repository.deleteAll(Person.class); + + Assert.assertFalse(this.repository.findById(this.person.getId()).isPresent()); + Assert.assertFalse(this.repository.findById(this.person0.getId()).isPresent()); + Assert.assertTrue(this.projectRepository.findById(this.project.getId()).isPresent()); + } + + @Test + public void testFindAll() { + final List persons = Arrays.asList(this.person, this.person0); + this.repository.saveAll(persons); + + final List foundPersons = Lists.newArrayList(this.repository.findAll()); + + foundPersons.sort(Comparator.comparing(Person::getId)); + persons.sort(Comparator.comparing(Person::getId)); + + Assert.assertEquals(persons, foundPersons); + + this.repository.deleteAll(); + Assert.assertFalse(this.repository.findAll().iterator().hasNext()); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/RelationshipRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/RelationshipRepositoryIT.java new file mode 100644 index 000000000000..fbbae2e68d9a --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/RelationshipRepositoryIT.java @@ -0,0 +1,304 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.domain.Project; +import com.microsoft.spring.data.gremlin.common.domain.Relationship; +import com.microsoft.spring.data.gremlin.common.repository.PersonRepository; +import com.microsoft.spring.data.gremlin.common.repository.ProjectRepository; +import com.microsoft.spring.data.gremlin.common.repository.RelationshipRepository; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import org.assertj.core.util.Lists; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class RelationshipRepositoryIT { + + private final Person person = new Person(TestConstants.VERTEX_PERSON_ID, TestConstants.VERTEX_PERSON_NAME); + private final Person person0 = new Person(TestConstants.VERTEX_PERSON_0_ID, TestConstants.VERTEX_PERSON_0_NAME); + private final Project project = new Project(TestConstants.VERTEX_PROJECT_ID, TestConstants.VERTEX_PROJECT_NAME, + TestConstants.VERTEX_PROJECT_URI); + private final Relationship relationship = new Relationship(TestConstants.EDGE_RELATIONSHIP_ID, + TestConstants.EDGE_RELATIONSHIP_NAME, TestConstants.EDGE_RELATIONSHIP_LOCATION, + this.person, this.project); + private final Relationship relationship0 = new Relationship(TestConstants.EDGE_RELATIONSHIP_0_ID, + TestConstants.EDGE_RELATIONSHIP_0_NAME, TestConstants.EDGE_RELATIONSHIP_0_LOCATION, + this.person0, this.project); + + @Autowired + private RelationshipRepository relationshipRepo; + + @Autowired + private PersonRepository personRepo; + + @Autowired + private ProjectRepository projectRepo; + + @Before + public void setup() { + this.relationshipRepo.deleteAll(); + } + + @After + public void cleanup() { + this.relationshipRepo.deleteAll(); + } + + @Test + public void testDeleteAll() { + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + + Assert.assertTrue(this.relationshipRepo.existsById(this.relationship.getId())); + + this.relationshipRepo.deleteAll(); + + Assert.assertFalse(this.relationshipRepo.existsById(this.person.getId())); + } + + @Test + public void testDeleteById() { + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + + Assert.assertTrue(this.relationshipRepo.existsById(this.relationship.getId())); + + this.relationshipRepo.deleteById(this.relationship.getId()); + + Assert.assertFalse(this.relationshipRepo.existsById(this.relationship.getId())); + } + + @Test + public void testDelete() { + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + + Assert.assertTrue(this.relationshipRepo.existsById(this.relationship.getId())); + + this.relationshipRepo.delete(this.relationship); + + Assert.assertFalse(this.relationshipRepo.existsById(this.relationship.getId())); + } + + @Test + public void testDeleteAllIds() { + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + + final List domains = Arrays.asList(this.relationship); + + this.relationshipRepo.deleteAll(domains); + + Assert.assertFalse(this.relationshipRepo.existsById(this.relationship.getId())); + } + + @Test + public void testSave() { + Assert.assertFalse(this.relationshipRepo.existsById(this.relationship.getId())); + + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + + Assert.assertTrue(this.relationshipRepo.existsById(this.relationship.getId())); + } + + @Test + public void testSaveAll() { + Assert.assertFalse(this.relationshipRepo.existsById(this.relationship.getId())); + + final List domains = Arrays.asList(this.relationship); + + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.saveAll(domains); + + Assert.assertTrue(this.relationshipRepo.existsById(this.relationship.getId())); + } + + @Test + public void testFindById() { + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + + final Relationship foundRelationship = this.relationshipRepo.findById(this.relationship.getId()).get(); + + Assert.assertNotNull(foundRelationship); + Assert.assertEquals(foundRelationship.getId(), this.relationship.getId()); + Assert.assertEquals(foundRelationship.getName(), this.relationship.getName()); + + Assert.assertFalse(this.relationshipRepo.findById(this.person.getId()).isPresent()); + } + + @Test + public void testExistById() { + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + + Assert.assertFalse(this.relationshipRepo.existsById(this.person.getId())); + Assert.assertTrue(this.relationshipRepo.existsById(this.relationship.getId())); + } + + + @Test + public void testFindAllById() { + final List domains = Arrays.asList(this.relationship); + final List ids = Arrays.asList(this.relationship.getId()); + + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.saveAll(domains); + + final List foundDomains = (List) this.relationshipRepo.findAllById(ids); + + domains.sort((a, b) -> (a.getId().compareTo(b.getId()))); + foundDomains.sort((a, b) -> (a.getId().compareTo(b.getId()))); + + Assert.assertArrayEquals(domains.toArray(), foundDomains.toArray()); + } + + @Test + public void testVertexCount() { + Assert.assertEquals(this.personRepo.count(), 0); + Assert.assertEquals(this.projectRepo.edgeCount(), 0); + Assert.assertEquals(this.relationshipRepo.vertexCount(), 0); + + this.personRepo.save(this.person0); + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + + Assert.assertEquals(this.personRepo.vertexCount(), 3); + Assert.assertEquals(this.projectRepo.vertexCount(), 3); + Assert.assertEquals(this.relationshipRepo.edgeCount(), 1); + Assert.assertEquals(this.relationshipRepo.count(), 4); + } + + @Test + public void testRelationshipFindByName() { + this.personRepo.save(this.person0); + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + + final List relationships = this.relationshipRepo.findByLocation(this.relationship.getLocation()); + + Assert.assertEquals(relationships.size(), 1); + Assert.assertEquals(relationships.get(0), this.relationship); + + this.relationshipRepo.deleteAll(); + + Assert.assertTrue(this.relationshipRepo.findByLocation(this.relationship.getLocation()).isEmpty()); + } + + @Test + public void testDeleteAllByType() { + this.personRepo.save(this.person0); + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + + this.relationshipRepo.deleteAll(GremlinEntityType.EDGE); + + Assert.assertFalse(this.relationshipRepo.findById(this.relationship.getId()).isPresent()); + Assert.assertTrue(this.personRepo.existsById(this.person.getId())); + Assert.assertTrue(this.personRepo.existsById(this.person0.getId())); + Assert.assertTrue(this.projectRepo.existsById(this.project.getId())); + } + + @Test + public void testDeleteAllByClass() { + this.personRepo.save(this.person0); + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.deleteAll(Relationship.class); + + Assert.assertFalse(this.relationshipRepo.findById(this.relationship.getId()).isPresent()); + Assert.assertTrue(this.personRepo.findById(this.person0.getId()).isPresent()); + Assert.assertTrue(this.projectRepo.findById(this.project.getId()).isPresent()); + } + + @Test + public void testFindByNameAndLocation() { + this.personRepo.save(this.person0); + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + + final List domains = this.relationshipRepo.findByNameAndLocation(relationship.getName(), + relationship.getLocation()); + + Assert.assertEquals(domains.size(), 1); + Assert.assertEquals(domains.get(0), this.relationship); + Assert.assertTrue(relationshipRepo.findByNameAndLocation(relationship.getName(), "faker").isEmpty()); + } + + @Test + public void testByNameOrId() { + this.personRepo.save(this.person0); + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + this.relationshipRepo.save(this.relationship); + this.relationshipRepo.save(this.relationship0); + + final List domains = Arrays.asList(this.relationship, this.relationship0); + List foundDomains = relationshipRepo.findByNameOrId(relationship.getName(), + relationship0.getId()); + + domains.sort(Comparator.comparing(Relationship::getId)); + foundDomains.sort(Comparator.comparing(Relationship::getId)); + + Assert.assertEquals(foundDomains.size(), 2); + Assert.assertEquals(foundDomains, domains); + + foundDomains = this.relationshipRepo.findByNameOrId("fake-name", relationship0.getId()); + + Assert.assertEquals(foundDomains.size(), 1); + Assert.assertEquals(foundDomains.get(0), this.relationship0); + } + + @Test + public void testFindAll() { + this.personRepo.save(this.person0); + this.personRepo.save(this.person); + this.projectRepo.save(this.project); + + final List relationships = Arrays.asList(this.relationship, this.relationship0); + + this.relationshipRepo.saveAll(relationships); + + final List foundRelationships = Lists.newArrayList(this.relationshipRepo.findAll()); + + foundRelationships.sort(Comparator.comparing(Relationship::getId)); + relationships.sort(Comparator.comparing(Relationship::getId)); + + Assert.assertEquals(foundRelationships, relationships); + + this.relationshipRepo.deleteAll(); + + Assert.assertFalse(this.relationshipRepo.findAll().iterator().hasNext()); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/ServiceRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/ServiceRepositoryIT.java new file mode 100644 index 000000000000..61cd2bad0e92 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/ServiceRepositoryIT.java @@ -0,0 +1,459 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.Service; +import com.microsoft.spring.data.gremlin.common.domain.ServiceType; +import com.microsoft.spring.data.gremlin.common.domain.SimpleDependency; +import com.microsoft.spring.data.gremlin.common.repository.ServiceRepository; +import com.microsoft.spring.data.gremlin.common.repository.SimpleDependencyRepository; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class ServiceRepositoryIT { + + private static Service serviceA; + private static Service serviceB; + private static Service serviceC; + + private static Date createDateA; + private static Date createDateB; + private static Date createDateC; + + private static final Map PROPERTIES_A = new HashMap<>(); + private static final Map PROPERTIES_B = new HashMap<>(); + private static final Map PROPERTIES_C = new HashMap<>(); + + private static final String ID_A = "1234"; + private static final String ID_B = "8731"; + private static final String ID_C = "5781"; + + private static final int COUNT_A = 2; + private static final int COUNT_B = 8; + private static final int COUNT_C = 2; + + private static final String NAME_A = "name-A"; + private static final String NAME_B = "name-B"; + private static final String NAME_C = "name-A"; + + @Autowired + private ServiceRepository repository; + + @Autowired + private SimpleDependencyRepository dependencyRepo; + + @BeforeClass + public static void initialize() throws ParseException { + PROPERTIES_B.put("serviceB-port", 8761); + PROPERTIES_B.put("priority", "high"); + PROPERTIES_B.put("enabled-hystrix", false); + + PROPERTIES_A.put("serviceA-port", 8888); + PROPERTIES_A.put("serviceB-port", 8761); + PROPERTIES_A.put("priority", "highest"); + + PROPERTIES_C.put("serviceC-port", 8090); + PROPERTIES_C.put("serviceB-port", 8761); + PROPERTIES_C.put("priority", "medium"); + + createDateA = new SimpleDateFormat("yyyyMMdd").parse("20180601"); + createDateB = new SimpleDateFormat("yyyyMMdd").parse("20180603"); + createDateC = new SimpleDateFormat("yyyyMMdd").parse("20180503"); + + serviceA = new Service(ID_A, COUNT_A, true, NAME_A, ServiceType.FRONT_END, createDateA, PROPERTIES_A); + serviceB = new Service(ID_B, COUNT_B, false, NAME_B, ServiceType.BACK_END, createDateB, PROPERTIES_B); + serviceC = new Service(ID_C, COUNT_C, false, NAME_C, ServiceType.BACK_END, createDateC, PROPERTIES_C); + } + + @Before + public void setup() { + this.repository.deleteAll(); + } + + @After + public void cleanup() { + this.repository.deleteAll(); + } + + @Test + public void testQueries() { + Assert.assertFalse(this.repository.findById(serviceA.getId()).isPresent()); + Assert.assertFalse(this.repository.findById(serviceB.getId()).isPresent()); + + this.repository.save(serviceA); + this.repository.save(serviceB); + + Optional foundOptional = this.repository.findById(serviceA.getId()); + Assert.assertTrue(foundOptional.isPresent()); + Assert.assertEquals(foundOptional.get(), serviceA); + + foundOptional = this.repository.findById(serviceB.getId()); + Assert.assertTrue(foundOptional.isPresent()); + Assert.assertEquals(foundOptional.get(), serviceB); + + this.repository.deleteById(serviceA.getId()); + this.repository.deleteById(serviceB.getId()); + + Assert.assertFalse(this.repository.findById(serviceA.getId()).isPresent()); + Assert.assertFalse(this.repository.findById(serviceB.getId()).isPresent()); + } + + @Test + public void testEdgeFromToStringId() { + final SimpleDependency depend = new SimpleDependency("fakeId", "faked", serviceA.getId(), serviceB.getId()); + + this.repository.save(serviceA); + this.repository.save(serviceB); + this.dependencyRepo.save(depend); + + final Optional foundOptional = this.dependencyRepo.findById(depend.getId()); + Assert.assertTrue(foundOptional.isPresent()); + Assert.assertEquals(foundOptional.get(), depend); + + this.dependencyRepo.delete(foundOptional.get()); + + Assert.assertTrue(this.repository.findById(serviceA.getId()).isPresent()); + Assert.assertTrue(this.repository.findById(serviceB.getId()).isPresent()); + } + + @Test + public void testFindByName() { + this.repository.save(serviceA); + this.repository.save(serviceB); + + final List services = this.repository.findByName(serviceA.getName()); + + Assert.assertEquals(services.size(), 1); + Assert.assertEquals(services.get(0), serviceA); + + this.repository.deleteAll(); + + Assert.assertTrue(this.repository.findByName(serviceA.getName()).isEmpty()); + } + + @Test + public void testFindByInstanceCount() { + this.repository.save(serviceA); + this.repository.save(serviceB); + + final List services = this.repository.findByInstanceCount(serviceB.getInstanceCount()); + + Assert.assertEquals(services.size(), 1); + Assert.assertEquals(services.get(0), serviceB); + + this.repository.deleteAll(); + + Assert.assertTrue(this.repository.findByInstanceCount(serviceB.getInstanceCount()).isEmpty()); + } + + @Test + public void testFindByIsActive() { + this.repository.save(serviceA); + this.repository.save(serviceB); + + final List services = this.repository.findByActive(serviceB.isActive()); + + Assert.assertEquals(services.size(), 1); + Assert.assertEquals(services.get(0), serviceB); + + this.repository.deleteAll(); + + Assert.assertTrue(this.repository.findByActive(serviceB.isActive()).isEmpty()); + } + + @Test + public void testFindByCreateAt() { + this.repository.save(serviceA); + this.repository.save(serviceB); + + final List services = this.repository.findByCreateAt(serviceA.getCreateAt()); + + Assert.assertEquals(services.size(), 1); + Assert.assertEquals(services.get(0), serviceA); + + this.repository.deleteAll(); + + Assert.assertTrue(this.repository.findByCreateAt(serviceB.getCreateAt()).isEmpty()); + } + + @Test + public void testFindByProperties() { + this.repository.save(serviceA); + this.repository.save(serviceB); + + final List services = this.repository.findByProperties(serviceB.getProperties()); + + Assert.assertEquals(services.size(), 1); + Assert.assertEquals(services.get(0), serviceB); + + this.repository.deleteAll(); + + Assert.assertTrue(this.repository.findByProperties(serviceB.getProperties()).isEmpty()); + } + + @Test + public void testFindById() { + this.repository.save(serviceA); + this.repository.save(serviceB); + + final Optional foundConfig = this.repository.findById(serviceA.getId()); + final Optional foundEureka = this.repository.findById(serviceB.getId()); + + Assert.assertTrue(foundConfig.isPresent()); + Assert.assertTrue(foundEureka.isPresent()); + + Assert.assertEquals(foundConfig.get(), serviceA); + Assert.assertEquals(foundEureka.get(), serviceB); + } + + @Test + public void testFindByNameAndInstanceCount() { + this.repository.save(serviceA); + this.repository.save(serviceB); + + final List services = repository.findByNameAndInstanceCount(NAME_B, COUNT_B); + + Assert.assertEquals(services.size(), 1); + Assert.assertEquals(services.get(0), serviceB); + Assert.assertTrue(repository.findByNameAndInstanceCount(NAME_B, COUNT_A).isEmpty()); + } + + @Test + public void testFindByNameAndInstanceCountAndType() { + this.repository.save(serviceA); + this.repository.save(serviceB); + + final List services = repository.findByNameAndInstanceCountAndType(NAME_B, COUNT_B, ServiceType.BACK_END); + + Assert.assertEquals(services.size(), 1); + Assert.assertEquals(services.get(0), serviceB); + Assert.assertTrue(repository.findByNameAndInstanceCountAndType(NAME_B, COUNT_A, ServiceType.BOTH).isEmpty()); + } + + @Test + public void testFindByNameOrInstanceCount() { + final List services = Arrays.asList(serviceA, serviceB); + this.repository.saveAll(services); + + List foundServices = repository.findByNameOrInstanceCount(NAME_A, COUNT_B); + + services.sort(Comparator.comparing(Service::getId)); + foundServices.sort(Comparator.comparing(Service::getId)); + + Assert.assertEquals(foundServices.size(), 2); + Assert.assertEquals(foundServices, services); + + foundServices = repository.findByNameOrInstanceCount("fake-name", COUNT_A); + + Assert.assertEquals(foundServices.size(), 1); + Assert.assertEquals(foundServices.get(0), serviceA); + } + + @Test + public void testFindByNameAndIsActiveOrProperties() { + final List services = Arrays.asList(serviceA, serviceB); + this.repository.saveAll(services); + + List foundServices = repository.findByNameAndActiveOrProperties(NAME_A, true, PROPERTIES_B); + + services.sort(Comparator.comparing(Service::getId)); + foundServices.sort(Comparator.comparing(Service::getId)); + + Assert.assertEquals(foundServices.size(), 2); + Assert.assertEquals(foundServices, services); + + foundServices = repository.findByNameAndActiveOrProperties(NAME_B, false, new HashMap<>()); + Assert.assertEquals(foundServices.size(), 1); + Assert.assertEquals(foundServices.get(0), serviceB); + } + + @Test + public void testFindByNameOrInstanceCountAndType() { + final List services = Arrays.asList(serviceA, serviceB); + this.repository.saveAll(services); + + List foundServices = repository.findByNameOrInstanceCountAndType(NAME_A, COUNT_B, ServiceType.BACK_END); + + services.sort(Comparator.comparing(Service::getId)); + foundServices.sort(Comparator.comparing(Service::getId)); + + Assert.assertEquals(foundServices.size(), 2); + Assert.assertEquals(foundServices, services); + + foundServices = repository.findByNameOrInstanceCountAndType(NAME_B, COUNT_A, ServiceType.BACK_END); + Assert.assertEquals(foundServices.size(), 1); + Assert.assertEquals(foundServices.get(0), serviceB); + } + + @Test + public void testFindByNameAndInstanceCountOrType() { + final List services = Arrays.asList(serviceA, serviceB); + this.repository.saveAll(services); + + List foundServices = repository.findByNameAndInstanceCountOrType(NAME_A, COUNT_A, ServiceType.BACK_END); + + services.sort(Comparator.comparing(Service::getId)); + foundServices.sort(Comparator.comparing(Service::getId)); + + Assert.assertEquals(foundServices.size(), 2); + Assert.assertEquals(foundServices, services); + + foundServices = repository.findByNameAndInstanceCountOrType(NAME_A, COUNT_B, ServiceType.BACK_END); + Assert.assertEquals(foundServices.size(), 1); + Assert.assertEquals(foundServices.get(0), serviceB); + } + + @Test + public void testExistsByName() { + final List services = Arrays.asList(serviceA, serviceB, serviceC); + this.repository.saveAll(services); + + final List foundServices = repository.findByActiveExists(); + + Assert.assertEquals(foundServices.size(), 1); + Assert.assertEquals(foundServices.get(0), serviceA); + + this.repository.deleteAll(); + + Assert.assertTrue(repository.findByActiveExists().isEmpty()); + } + + @Test + public void testFindByCreateAtAfter() throws ParseException { + final List services = Arrays.asList(serviceA, serviceB); + this.repository.saveAll(services); + + Date testDate = new SimpleDateFormat("yyyyMMdd").parse("20180602"); + List foundServices = repository.findByCreateAtAfter(testDate); + Assert.assertEquals(foundServices.size(), 1); + Assert.assertEquals(foundServices.get(0), serviceB); + + testDate = new SimpleDateFormat("yyyyMMdd").parse("20180502"); + foundServices = repository.findByCreateAtAfter(testDate); + services.sort(Comparator.comparing(Service::getId)); + foundServices.sort(Comparator.comparing(Service::getId)); + Assert.assertEquals(foundServices.size(), 2); + Assert.assertEquals(foundServices, services); + + testDate = new SimpleDateFormat("yyyyMMdd").parse("20180606"); + foundServices = repository.findByCreateAtAfter(testDate); + Assert.assertTrue(foundServices.isEmpty()); + } + + @Test + public void testFindByNameOrTypeAndInstanceCountAndCreateAtAfter() throws ParseException { + final List services = Arrays.asList(serviceA, serviceB); + this.repository.saveAll(services); + + Date testDate = new SimpleDateFormat("yyyyMMdd").parse("20180601"); + List foundServices = repository.findByNameOrTypeAndInstanceCountAndCreateAtAfter(NAME_A, + serviceB.getType(), COUNT_B, testDate); + + services.sort(Comparator.comparing(Service::getId)); + foundServices.sort(Comparator.comparing(Service::getId)); + Assert.assertEquals(foundServices.size(), 2); + Assert.assertEquals(foundServices, services); + + testDate = new SimpleDateFormat("yyyyMMdd").parse("20180607"); + foundServices = repository.findByNameOrTypeAndInstanceCountAndCreateAtAfter(NAME_A, serviceB.getType(), COUNT_B, + testDate); + Assert.assertEquals(foundServices.size(), 1); + Assert.assertEquals(foundServices.get(0), serviceA); + Assert.assertTrue(repository.findByNameOrTypeAndInstanceCountAndCreateAtAfter("fake-name", serviceB.getType(), + COUNT_B, testDate).isEmpty()); + } + + @Test + public void testFindByCreateAtBefore() throws ParseException { + final List services = Arrays.asList(serviceA, serviceB); + this.repository.saveAll(services); + + Date testDate = new SimpleDateFormat("yyyyMMdd").parse("20180602"); + List foundServices = repository.findByCreateAtBefore(testDate); + Assert.assertEquals(foundServices.size(), 1); + Assert.assertEquals(foundServices.get(0), serviceA); + + testDate = new SimpleDateFormat("yyyyMMdd").parse("20180606"); + foundServices = repository.findByCreateAtBefore(testDate); + services.sort(Comparator.comparing(Service::getId)); + foundServices.sort(Comparator.comparing(Service::getId)); + Assert.assertEquals(foundServices.size(), 2); + Assert.assertEquals(foundServices, services); + + testDate = new SimpleDateFormat("yyyyMMdd").parse("20180506"); + foundServices = repository.findByCreateAtBefore(testDate); + Assert.assertTrue(foundServices.isEmpty()); + } + + @Test + public void testFindByCreateAtBeforeAndCreateAtAfter() throws ParseException { + final List services = Arrays.asList(serviceA, serviceB); + this.repository.saveAll(services); + + Date startDate = new SimpleDateFormat("yyyyMMdd").parse("20180602"); + Date endDate = new SimpleDateFormat("yyyyMMdd").parse("20180606"); + List foundServices = repository.findByCreateAtAfterAndCreateAtBefore(startDate, endDate); + Assert.assertEquals(foundServices.size(), 1); + Assert.assertEquals(foundServices.get(0), serviceB); + + startDate = new SimpleDateFormat("yyyyMMdd").parse("20180506"); + endDate = new SimpleDateFormat("yyyyMMdd").parse("20180606"); + foundServices = repository.findByCreateAtAfterAndCreateAtBefore(startDate, endDate); + services.sort(Comparator.comparing(Service::getId)); + foundServices.sort(Comparator.comparing(Service::getId)); + Assert.assertEquals(foundServices.size(), 2); + Assert.assertEquals(foundServices, services); + + startDate = new SimpleDateFormat("yyyyMMdd").parse("20180606"); + endDate = new SimpleDateFormat("yyyyMMdd").parse("20180607"); + foundServices = repository.findByCreateAtAfterAndCreateAtBefore(startDate, endDate); + Assert.assertTrue(foundServices.isEmpty()); + } + + @Test + public void testFindByCreateAtBetween() throws ParseException { + final List services = Arrays.asList(serviceA, serviceB); + this.repository.saveAll(services); + + Date startDate = new SimpleDateFormat("yyyyMMdd").parse("20180602"); + Date endDate = new SimpleDateFormat("yyyyMMdd").parse("20180606"); + List foundServices = repository.findByCreateAtBetween(startDate, endDate); + Assert.assertEquals(foundServices.size(), 1); + Assert.assertEquals(foundServices.get(0), serviceB); + + startDate = new SimpleDateFormat("yyyyMMdd").parse("20180601"); + endDate = new SimpleDateFormat("yyyyMMdd").parse("20180604"); + foundServices = repository.findByCreateAtBetween(startDate, endDate); + services.sort(Comparator.comparing(Service::getId)); + foundServices.sort(Comparator.comparing(Service::getId)); + Assert.assertEquals(foundServices.size(), 2); + Assert.assertEquals(foundServices, services); + + startDate = new SimpleDateFormat("yyyyMMdd").parse("20180606"); + endDate = new SimpleDateFormat("yyyyMMdd").parse("20180607"); + foundServices = repository.findByCreateAtBetween(startDate, endDate); + Assert.assertTrue(foundServices.isEmpty()); + } +} + diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/StudentRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/StudentRepositoryIT.java new file mode 100644 index 000000000000..d2dd85a82e45 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/StudentRepositoryIT.java @@ -0,0 +1,196 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.GremlinEntityType; +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.Student; +import com.microsoft.spring.data.gremlin.common.repository.StudentRepository; +import org.assertj.core.util.Lists; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.NonNull; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class StudentRepositoryIT { + + private static final Long ID_0 = 12349274637234L; + private static final Long ID_1 = 1L; + private static final Long ID_2 = 2L; + private static final Long NO_EXIST_ID = -1L; + + private static final String NAME_0 = "name-0"; + private static final String NAME_1 = "name-1"; + private static final String NAME_2 = "name-2"; + private static final String NO_EXIST_NAME = "no-exist-name"; + + private static final Student STUDENT_0 = new Student(ID_0, NAME_0); + private static final Student STUDENT_1 = new Student(ID_1, NAME_1); + private static final Student STUDENT_2 = new Student(ID_2, NAME_2); + + private static final List STUDENTS = Arrays.asList(STUDENT_0, STUDENT_1, STUDENT_2); + + @Autowired + private StudentRepository repository; + + @Before + public void setup() { + this.repository.deleteAll(); + } + + private void assertDomainListEquals(@NonNull List found, @NonNull List expected) { + found.sort(Comparator.comparing(Student::getId)); + expected.sort(Comparator.comparing(Student::getId)); + + Assert.assertEquals(found.size(), expected.size()); + Assert.assertEquals(found, expected); + } + + @Test + public void testDeleteAll() { + repository.saveAll(STUDENTS); + + Assert.assertTrue(repository.findAll().iterator().hasNext()); + + repository.deleteAll(); + + Assert.assertFalse(repository.findAll().iterator().hasNext()); + } + + @Test + public void testDeleteAllOnType() { + repository.saveAll(STUDENTS); + + Assert.assertTrue(repository.findAll().iterator().hasNext()); + + repository.deleteAll(GremlinEntityType.EDGE); + + Assert.assertTrue(repository.findAll().iterator().hasNext()); + + repository.deleteAll(GremlinEntityType.VERTEX); + + Assert.assertFalse(repository.findAll().iterator().hasNext()); + } + + @Test + public void testDeleteAllOnDomain() { + repository.saveAll(STUDENTS); + + Assert.assertTrue(repository.findAll().iterator().hasNext()); + + repository.deleteAll(Student.class); + + Assert.assertFalse(repository.findAll().iterator().hasNext()); + } + + @Test + public void testSave() { + repository.save(STUDENT_0); + + Assert.assertTrue(repository.findById(STUDENT_0.getId()).isPresent()); + Assert.assertFalse(repository.findById(STUDENT_1.getId()).isPresent()); + } + + @Test + public void testSaveAll() { + repository.saveAll(STUDENTS); + + final List found = Lists.newArrayList(repository.findAll()); + + assertDomainListEquals(found, STUDENTS); + } + + @Test + public void testFindById() { + repository.saveAll(STUDENTS); + + Optional optional = repository.findById(STUDENT_0.getId()); + + Assert.assertTrue(optional.isPresent()); + Assert.assertEquals(optional.get(), STUDENT_0); + + optional = repository.findById(NO_EXIST_ID); + + Assert.assertFalse(optional.isPresent()); + } + + @Test + public void testExistsById() { + repository.saveAll(STUDENTS); + + Assert.assertTrue(repository.existsById(STUDENT_0.getId())); + Assert.assertFalse(repository.existsById(NO_EXIST_ID)); + } + + @Test + public void testFindAllById() { + final List expected = Arrays.asList(STUDENT_0, STUDENT_1); + final List ids = Arrays.asList(STUDENT_0.getId(), STUDENT_1.getId(), NO_EXIST_ID); + + repository.saveAll(STUDENTS); + + final List found = Lists.newArrayList(repository.findAllById(ids)); + + assertDomainListEquals(found, expected); + + Assert.assertFalse(repository.findAllById(Collections.singleton(NO_EXIST_ID)).iterator().hasNext()); + } + + @Test + public void testCount() { + repository.saveAll(STUDENTS); + + Assert.assertEquals(repository.count(), STUDENTS.size()); + + repository.deleteAll(); + + Assert.assertEquals(repository.count(), 0); + } + + @Test + public void testDeleteById() { + repository.saveAll(STUDENTS); + + Assert.assertTrue(repository.findById(STUDENT_0.getId()).isPresent()); + + repository.deleteById(STUDENT_0.getId()); + + Assert.assertFalse(repository.findById(STUDENT_0.getId()).isPresent()); + } + + @Test + public void testVertexCount() { + repository.saveAll(STUDENTS); + + Assert.assertEquals(repository.vertexCount(), STUDENTS.size()); + } + + @Test + public void testEdgeCount() { + repository.saveAll(STUDENTS); + + Assert.assertEquals(repository.edgeCount(), 0); + } + + @Test + public void testFindByName() { + repository.saveAll(STUDENTS); + + Assert.assertTrue(repository.findByName(NO_EXIST_NAME).isEmpty()); + Assert.assertEquals(repository.findByName(STUDENT_0.getName()).size(), 1); + Assert.assertEquals(repository.findByName(STUDENT_0.getName()).get(0), STUDENT_0); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/UserDomainRepositoryIT.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/UserDomainRepositoryIT.java new file mode 100644 index 000000000000..4204edaed5b0 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/UserDomainRepositoryIT.java @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository; + +import com.microsoft.spring.data.gremlin.common.TestRepositoryConfiguration; +import com.microsoft.spring.data.gremlin.common.domain.UserDomain; +import com.microsoft.spring.data.gremlin.common.repository.UserDomainRepository; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestRepositoryConfiguration.class) +public class UserDomainRepositoryIT { + + private static final String NAME_0 = "incarnation"; + private static final String NAME_1 = "absolute"; + private static final String NAME_2 = "test_name"; + private static final int LEVEL_0 = 4; + private static final int LEVEL_1 = 7; + private static final int LEVEL_2 = 7; + private static final UserDomain DOMAIN_0 = new UserDomain(NAME_0, LEVEL_0, true); + private static final UserDomain DOMAIN_1 = new UserDomain(NAME_1, LEVEL_1, true); + private static final UserDomain DOMAIN_2 = new UserDomain(NAME_2, LEVEL_2, false); + + @Autowired + private UserDomainRepository repository; + + @Before + public void setup() { + this.repository.deleteAll(); + } + + @Test + public void testWithNoIdName() { + Assert.assertEquals(0, this.repository.vertexCount()); + Assert.assertFalse(this.repository.findById(NAME_0).isPresent()); + + this.repository.save(DOMAIN_0); + final Optional optional = this.repository.findById(NAME_0); + + Assert.assertTrue(optional.isPresent()); + Assert.assertEquals(DOMAIN_0.getName(), optional.get().getName()); + Assert.assertEquals(DOMAIN_0.getLevel(), optional.get().getLevel()); + + this.repository.deleteById(NAME_0); + Assert.assertFalse(this.repository.findById(NAME_0).isPresent()); + } + + @Test + public void testFindByName() { + this.repository.save(DOMAIN_0); + + final List domains = this.repository.findByName(DOMAIN_0.getName()); + + Assert.assertEquals(domains.size(), 1); + Assert.assertEquals(domains.get(0), DOMAIN_0); + + this.repository.deleteAll(); + + Assert.assertTrue(this.repository.findByName(DOMAIN_0.getName()).isEmpty()); + } + + @Test + public void testFindByEnabledExists() { + final List domains = Arrays.asList(DOMAIN_0, DOMAIN_1); + + this.repository.saveAll(domains); + this.repository.save(DOMAIN_2); + + final List foundDomains = this.repository.findByEnabledExists(); + + domains.sort(Comparator.comparing(UserDomain::getName)); + foundDomains.sort(Comparator.comparing(UserDomain::getName)); + + Assert.assertEquals(foundDomains.size(), domains.size()); + Assert.assertEquals(foundDomains, domains); + + this.repository.deleteAll(domains); + + Assert.assertTrue(this.repository.findByEnabledExists().isEmpty()); + } + + @Test + public void testFindByLevelBetween() { + final List domains = Arrays.asList(DOMAIN_0, DOMAIN_1); + + this.repository.saveAll(domains); + + List foundDomains = this.repository.findByLevelBetween(8, 9); + Assert.assertTrue(foundDomains.isEmpty()); + + foundDomains = this.repository.findByLevelBetween(7, 8); + Assert.assertEquals(foundDomains.size(), 1); + Assert.assertEquals(foundDomains.get(0), DOMAIN_1); + + foundDomains = this.repository.findByLevelBetween(0, 8); + domains.sort(Comparator.comparing(UserDomain::getName)); + foundDomains.sort(Comparator.comparing(UserDomain::getName)); + + Assert.assertEquals(foundDomains.size(), domains.size()); + Assert.assertEquals(foundDomains, domains); + + this.repository.deleteAll(domains); + + Assert.assertTrue(this.repository.findByLevelBetween(0, 8).isEmpty()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryConfigurationExtensionUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryConfigurationExtensionUnitTest.java new file mode 100644 index 000000000000..736b7f636593 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryConfigurationExtensionUnitTest.java @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.config; + +import com.microsoft.spring.data.gremlin.repository.GremlinRepository; +import org.apache.commons.lang3.NotImplementedException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.type.StandardAnnotationMetadata; +import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; +import org.springframework.data.repository.config.RepositoryConfigurationSource; + +public class GremlinRepositoryConfigurationExtensionUnitTest { + + private static final String GREMLIN_MODULE_NAME = "Gremlin"; + private static final String GREMLIN_MODULE_PREFIX = "gremlin"; + private static final String GREMLIN_MAPPING_CONTEXT = "gremlinMappingContext"; + + private GremlinRepositoryConfigurationExtension extension; + + @Before + public void setup() { + this.extension = new GremlinRepositoryConfigurationExtension(); + } + + @Test + public void testGremlinRepositoryConfigurationExtensionGetters() { + Assert.assertEquals(this.extension.getModuleName(), GREMLIN_MODULE_NAME); + Assert.assertEquals(this.extension.getModulePrefix(), GREMLIN_MODULE_PREFIX); + Assert.assertEquals(this.extension.getIdentifyingTypes().size(), 1); + + Assert.assertSame(this.extension.getIdentifyingTypes().toArray()[0], GremlinRepository.class); + Assert.assertTrue(this.extension.getIdentifyingAnnotations().isEmpty()); + } + + @Test + public void testGremlinRepositoryConfigurationExtensionRegisterBeansForRoot() { + final ResourceLoader loader = new PathMatchingResourcePatternResolver(); + final Environment environment = new StandardEnvironment(); + final BeanDefinitionRegistry registry = new DefaultListableBeanFactory(); + final StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(GremlinConfig.class, true); + final RepositoryConfigurationSource config = new AnnotationRepositoryConfigurationSource(metadata, + EnableGremlinRepositories.class, loader, environment, registry); + + Assert.assertFalse(registry.containsBeanDefinition(GREMLIN_MAPPING_CONTEXT)); + + this.extension.registerBeansForRoot(registry, config); + + Assert.assertTrue(registry.containsBeanDefinition(GREMLIN_MAPPING_CONTEXT)); + } + + @Test(expected = NotImplementedException.class) + public void testGetRepositoryFactoryBeanClassNameException() { + this.extension.getRepositoryFactoryBeanClassName(); + } + + @EnableGremlinRepositories + private static class GremlinConfig { + + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryRegistrarUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryRegistrarUnitTest.java new file mode 100644 index 000000000000..8c9fffc15deb --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/config/GremlinRepositoryRegistrarUnitTest.java @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.config; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class GremlinRepositoryRegistrarUnitTest { + + private GremlinRepositoryRegistrar registrar; + + @Before + public void setup() { + this.registrar = new GremlinRepositoryRegistrar(); + } + + @Test + public void testGremlinRepositoryRegistrarGetters() { + Assert.assertSame(this.registrar.getAnnotation(), EnableGremlinRepositories.class); + Assert.assertTrue(this.registrar.getExtension() instanceof GremlinRepositoryConfigurationExtension); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/support/GremlinEntityInformationUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/support/GremlinEntityInformationUnitTest.java new file mode 100644 index 000000000000..f6a2405b1c48 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/support/GremlinEntityInformationUnitTest.java @@ -0,0 +1,160 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.support; + +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.domain.Relationship; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import com.microsoft.spring.data.gremlin.common.domain.Network; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceGraph; +import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex; +import com.microsoft.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException; +import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.data.annotation.Id; + +import java.util.Date; + +public class GremlinEntityInformationUnitTest { + + @Test + public void testVertexEntityInformation() { + final Person person = new Person(TestConstants.VERTEX_PERSON_ID, TestConstants.VERTEX_PERSON_NAME); + final GremlinEntityInformation personInfo = new GremlinEntityInformation<>(Person.class); + + Assert.assertNotNull(personInfo.getIdField()); + Assert.assertEquals(personInfo.getId(person), TestConstants.VERTEX_PERSON_ID); + Assert.assertEquals(personInfo.getIdType(), String.class); + Assert.assertTrue(personInfo.createGremlinSource() instanceof GremlinSourceVertex); + } + + @Test + public void testEdgeEntityInformation() { + final GremlinEntityInformation relationshipInfo = + new GremlinEntityInformation<>(Relationship.class); + + Assert.assertNotNull(relationshipInfo.getIdField()); + Assert.assertTrue(relationshipInfo.createGremlinSource() instanceof GremlinSourceEdge); + } + + @Test + public void testGraphEntityInformation() { + final GremlinEntityInformation networkInfo = new GremlinEntityInformation<>(Network.class); + + Assert.assertNotNull(networkInfo.getIdField()); + Assert.assertTrue(networkInfo.createGremlinSource() instanceof GremlinSourceGraph); + } + + @Test(expected = GremlinUnexpectedEntityTypeException.class) + public void testEntityInformationException() { + new GremlinEntityInformation(TestDomain.class).createGremlinSource(); + } + + @Test(expected = GremlinInvalidEntityIdFieldException.class) + public void testEntityInformationNoIdException() { + new GremlinEntityInformation(TestNoIdDomain.class); + } + + @Test(expected = GremlinInvalidEntityIdFieldException.class) + public void testEntityInformationMultipleIdException() { + new GremlinEntityInformation(TestMultipleIdDomain.class); + } + + @Test(expected = GremlinInvalidEntityIdFieldException.class) + public void testEntityInformationNoStringIdException() { + new GremlinEntityInformation(TestNoStringIdDomain.class); + } + + @Test(expected = GremlinInvalidEntityIdFieldException.class) + public void testEntityInformationIdFieldAndIdAnnotation() { + new GremlinEntityInformation(TestIdFieldAndIdAnnotation.class); + } + + private class TestDomain { + private String id; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + } + + private class TestNoIdDomain { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + private class TestMultipleIdDomain { + @Id + private String name; + + @Id + private String location; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + } + + private class TestIdFieldAndIdAnnotation { + @Id + private String name; + + @Id + private String where; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getWhere() { + return where; + } + + public void setWhere(String where) { + this.where = where; + } + } + + private class TestNoStringIdDomain { + @Id + private Date date; + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactoryBeanUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactoryBeanUnitTest.java new file mode 100644 index 000000000000..54437f1ba6f0 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactoryBeanUnitTest.java @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.support; + +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.common.TestConstants; +import com.microsoft.spring.data.gremlin.common.repository.PersonRepository; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.data.repository.core.support.RepositoryFactorySupport; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +public class GremlinRepositoryFactoryBeanUnitTest { + + @Autowired + private ApplicationContext context; + + private GremlinRepositoryFactoryBean factoryBean; + + @Before + public void setup() { + this.factoryBean = new GremlinRepositoryFactoryBean<>(PersonRepository.class); + } + + @Test + public void testGetFactoryInstance() { + final Person person = new Person(TestConstants.VERTEX_PERSON_ID, TestConstants.VERTEX_PERSON_NAME); + final RepositoryFactorySupport factorySupport = this.factoryBean.getFactoryInstance(this.context); + + Assert.assertNotNull(factorySupport); + Assert.assertEquals(factorySupport.getEntityInformation(Person.class).getIdType(), String.class); + Assert.assertEquals(factorySupport.getEntityInformation(Person.class).getId(person), person.getId()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactoryUnitTest.java b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactoryUnitTest.java new file mode 100644 index 000000000000..fd61433f2a31 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/java/com/microsoft/spring/data/gremlin/repository/support/GremlinRepositoryFactoryUnitTest.java @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.spring.data.gremlin.repository.support; + +import com.microsoft.spring.data.gremlin.common.domain.Person; +import com.microsoft.spring.data.gremlin.query.GremlinOperations; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.data.repository.core.EntityInformation; +import org.springframework.data.repository.query.QueryLookupStrategy; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Optional; + +@RunWith(SpringJUnit4ClassRunner.class) +public class GremlinRepositoryFactoryUnitTest { + + @Mock + private GremlinOperations operations; + + @Autowired + private ApplicationContext context; + + private GremlinRepositoryFactory factory; + + @Before + public void setup() { + this.factory = new GremlinRepositoryFactory(this.operations, this.context); + } + + @Test + public void testGetRepositoryBaseClass() { + Assert.assertEquals(SimpleGremlinRepository.class, this.factory.getRepositoryBaseClass(null)); + } + + @Test + public void testGetEntityInformation() { + final EntityInformation information = this.factory.getEntityInformation(Person.class); + + Assert.assertNotNull(information); + Assert.assertEquals(information.getIdType(), String.class); + } + + @Test + public void testGetQueryLookupStrategy() { + final Optional strategyOptional = this.factory. + getQueryLookupStrategy(QueryLookupStrategy.Key.CREATE, null); + + Assert.assertTrue(strategyOptional.isPresent()); + } +} diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/resources/application.properties b/sdk/spring/azure-spring-data-gremlin/src/test/resources/application.properties new file mode 100644 index 000000000000..57bb8d4fdfb3 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/resources/application.properties @@ -0,0 +1,8 @@ +gremlin.endpoint=localhost +gremlin.port=8889 +gremlin.username=${your-username} +gremlin.password=${your-password} +gremlin.sslEnabled=false + +## Valid serializer(case sensitive): GRAPHSON(default), GRAPHSON_V1D0, GRAPHSON_V2D0, GRYO_V1D0, GRYO_LITE_V1D0 +# gremlin.serializer=GRAPHSON diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/resources/application.yml b/sdk/spring/azure-spring-data-gremlin/src/test/resources/application.yml new file mode 100644 index 000000000000..8fbe6f976e00 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/resources/application.yml @@ -0,0 +1,6 @@ +gremlin: + endpoint: endpoint.gremlin.cosmosdb.azure.com + port: 443 + username: /dbs/database/colls/collection + password: password + telemetryAllowed: true diff --git a/sdk/spring/azure-spring-data-gremlin/src/test/resources/telemetry.config b/sdk/spring/azure-spring-data-gremlin/src/test/resources/telemetry.config new file mode 100644 index 000000000000..61b615fa0ef1 --- /dev/null +++ b/sdk/spring/azure-spring-data-gremlin/src/test/resources/telemetry.config @@ -0,0 +1 @@ +telemetry.instrumentationKey=@telemetry.instrumentationKey@ \ No newline at end of file diff --git a/sdk/spring/ci.yml b/sdk/spring/ci.yml index a2cfbd7ba4e1..3781f9ed85e4 100644 --- a/sdk/spring/ci.yml +++ b/sdk/spring/ci.yml @@ -28,6 +28,9 @@ extends: parameters: ServiceDirectory: spring Artifacts: + - name: azure-spring-data-gremlin + groupId: com.azure + safeName: azurespringdatagremlin - name: azure-spring-boot groupId: com.microsoft.azure safeName: azurespringboot @@ -82,3 +85,7 @@ extends: groupId: com.azure - name: azure-spring-boot-sample-storage-blob groupId: com.azure + - name: azure-spring-data-sample-gremlin + groupId: com.azure + - name: azure-spring-data-sample-gremlin-web-service + groupId: com.azure diff --git a/sdk/spring/pom.xml b/sdk/spring/pom.xml index 3ff7a064ef50..58bb41d3344f 100644 --- a/sdk/spring/pom.xml +++ b/sdk/spring/pom.xml @@ -36,7 +36,10 @@ azure-spring-boot-samples/azure-spring-boot-sample-servicebus-jms-queue azure-spring-boot-samples/azure-spring-boot-sample-servicebus-jms-topic azure-spring-boot-samples/azure-spring-boot-sample-storage-blob + azure-spring-boot-samples/azure-spring-data-sample-gremlin + azure-spring-boot-samples/azure-spring-data-sample-gremlin-web-service azure-spring-boot-samples/azure-cloud-foundry-service-sample + azure-spring-data-gremlin