├── .gitignore ├── .travis.yml ├── KotlinInspectionProfile.xml ├── LICENSE ├── README.md ├── example ├── .gitignore ├── README.md ├── build.gradle ├── frontend │ ├── .gitignore │ ├── README.md │ ├── __generated__ │ │ └── globalTypes.ts │ ├── config │ │ ├── env.js │ │ ├── jest │ │ │ ├── cssTransform.js │ │ │ ├── fileTransform.js │ │ │ └── typescriptTransform.js │ │ ├── paths.js │ │ ├── polyfills.js │ │ ├── webpack.config.dev.js │ │ ├── webpack.config.prod.js │ │ └── webpackDevServer.config.js │ ├── extract-fragment-types.js │ ├── generate-gql-types.sh │ ├── graphql.config.json │ ├── images.d.ts │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ ├── schema.json │ ├── scripts │ │ ├── build.js │ │ ├── start.js │ │ └── test.js │ ├── src │ │ ├── App.css │ │ ├── App.test.tsx │ │ ├── App.tsx │ │ ├── Character │ │ │ ├── __generated__ │ │ │ │ └── Character.ts │ │ │ ├── index.tsx │ │ │ └── query.graphql │ │ ├── Home │ │ │ └── index.tsx │ │ ├── fragmentTypes.json │ │ ├── gql_query_type.ts │ │ ├── index.css │ │ ├── index.tsx │ │ ├── json_type.ts │ │ ├── registerServiceWorker.ts │ │ └── scalars.d.ts │ ├── tsconfig.json │ ├── tsconfig.prod.json │ ├── tsconfig.test.json │ ├── tslint.json │ └── yarn.lock ├── locustfile.py └── src │ └── main │ ├── Resources │ ├── application.properties │ ├── graphqls │ │ ├── Character.graphqls │ │ ├── Droid.graphqls │ │ ├── Episode.graphqls │ │ ├── Human.graphqls │ │ ├── Mutation.graphqls │ │ ├── Name.graphqls │ │ ├── Node.graphqls │ │ ├── Query.graphqls │ │ └── Scalars.graphqls │ └── static │ │ └── graphiql.html │ └── kotlin │ ├── framework │ ├── BaseEdge.kt │ ├── BaseElement.kt │ ├── BaseVertex.kt │ └── StaticBiMapper.kt │ └── starwars │ ├── Application.kt │ ├── StarwarsGraphMapper.kt │ ├── StarwarsGraphMapperSupplier.kt │ ├── StarwarsSampleGraph.kt │ ├── graphql │ ├── Pagination.kt │ ├── StarwarsGraphQLCorsFilter.kt │ ├── StarwarsGraphQLHttpServlet.kt │ ├── StarwarsGraphQLServletRegistrationBean.kt │ ├── character │ │ ├── CharacterMutationResolver.kt │ │ ├── CharacterPage.kt │ │ ├── CharacterPageInfo.kt │ │ ├── CharacterPageOrder.kt │ │ ├── CharacterQueryResolver.kt │ │ └── CharacterTypeResolver.kt │ ├── droid │ │ ├── DroidMutationResolver.kt │ │ ├── DroidQueryResolver.kt │ │ └── DroidTypeResolver.kt │ ├── human │ │ ├── HumanPage.kt │ │ ├── HumanPageInfo.kt │ │ ├── HumanPageOrder.kt │ │ ├── HumanQueryResolver.kt │ │ └── HumanTypeResolver.kt │ └── scalars │ │ ├── GraphQLDecimal.kt │ │ ├── GraphQLPageLimit.kt │ │ ├── GraphQLTimestamp.kt │ │ └── GraphQLURL.kt │ ├── models │ ├── Character.kt │ ├── Episode.kt │ ├── Name.kt │ └── Sibling.kt │ └── traversals │ ├── character │ └── SecondDegreeFriends.kt │ └── human │ └── TwinSiblings.kt ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── kotlin-gremlin-graphql ├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties └── src │ └── main │ └── kotlin │ └── graphql │ ├── schema │ └── pagination │ │ ├── GraphQLPageCursor.kt │ │ ├── Identifiable.kt │ │ └── Page.kt │ └── servlet │ └── ogm │ ├── BatchedStepFetcher.kt │ ├── DataFetchingEnvironment.kt │ ├── GraphMapperGQLContext.kt │ ├── GraphMapperGQLContextBuilder.kt │ ├── PermissionResult.kt │ └── batchloaders │ ├── StepToManyBatchLoader.kt │ ├── StepToOptionalBatchLoader.kt │ └── StepToSingleBatchLoader.kt ├── kotlin-gremlin-ogm ├── .gitignore ├── build.gradle ├── gradle.properties └── src │ ├── main │ └── kotlin │ │ └── org │ │ └── apache │ │ └── tinkerpop │ │ └── gremlin │ │ └── ogm │ │ ├── GraphMapper.kt │ │ ├── annotations │ │ ├── Element.kt │ │ ├── FromVertex.kt │ │ ├── ID.kt │ │ ├── Mapper.kt │ │ ├── Property.kt │ │ ├── ToVertex.kt │ │ └── defaults │ │ │ ├── DefaultBoolean.kt │ │ │ ├── DefaultByte.kt │ │ │ ├── DefaultChar.kt │ │ │ ├── DefaultDouble.kt │ │ │ ├── DefaultFloat.kt │ │ │ ├── DefaultInt.kt │ │ │ ├── DefaultLong.kt │ │ │ ├── DefaultShort.kt │ │ │ ├── DefaultString.kt │ │ │ └── DefaultValue.kt │ │ ├── caching │ │ ├── CachedGraphMapper.kt │ │ └── GraphMapperCache.kt │ │ ├── elements │ │ ├── BasicEdge.kt │ │ ├── Edge.kt │ │ ├── Element.kt │ │ └── Vertex.kt │ │ ├── exceptions │ │ ├── AnnotationException.kt │ │ ├── ClassInheritanceMismatch.kt │ │ ├── ClassifierUnavailable.kt │ │ ├── ClientException.kt │ │ ├── ConflictingAnnotations.kt │ │ ├── ConflictingEdge.kt │ │ ├── DuplicateFromVertex.kt │ │ ├── DuplicateIDProperty.kt │ │ ├── DuplicatePropertyName.kt │ │ ├── DuplicateRelationshipName.kt │ │ ├── DuplicateToVertex.kt │ │ ├── ElementAnnotationMissing.kt │ │ ├── EmptyListTokenIsReserved.kt │ │ ├── EmptyMapTokenIsReserved.kt │ │ ├── FromVertexParameterMissing.kt │ │ ├── IDNotFound.kt │ │ ├── IDParameterRequired.kt │ │ ├── IDPropertyRequired.kt │ │ ├── IncompatibleIterable.kt │ │ ├── IncompatibleMap.kt │ │ ├── IterableNotSupported.kt │ │ ├── MapperUnsupported.kt │ │ ├── MissingEdge.kt │ │ ├── NonNullableID.kt │ │ ├── NonNullableNonOptionalParameter.kt │ │ ├── NullablePropertyWithDefault.kt │ │ ├── NullableVertexParam.kt │ │ ├── ObjectDescriptionMissing.kt │ │ ├── ObjectNotSaved.kt │ │ ├── ParameterPropertyNotFound.kt │ │ ├── PrimaryConstructorMissing.kt │ │ ├── PropertyMapperMissing.kt │ │ ├── PropertyUnsupported.kt │ │ ├── ReservedIDName.kt │ │ ├── ReservedNestedPropertyDelimiter.kt │ │ ├── ReservedNumberKey.kt │ │ ├── SuperclassAnnotationException.kt │ │ ├── ToVertexParameterMissing.kt │ │ ├── UnregisteredClass.kt │ │ └── UnregisteredLabel.kt │ │ ├── extensions │ │ ├── Elements.kt │ │ ├── Maps.kt │ │ └── MutableMaps.kt │ │ ├── mappers │ │ ├── BiMapper.kt │ │ ├── EdgeDeserializer.kt │ │ ├── EdgeSerializer.kt │ │ ├── EnumBiMapper.kt │ │ ├── Mapper.kt │ │ ├── ObjectDeserializer.kt │ │ ├── ObjectSerializer.kt │ │ ├── PropertyBiMapper.kt │ │ ├── PropertyDeserializer.kt │ │ ├── PropertySerializer.kt │ │ ├── SerializedProperty.kt │ │ ├── VertexDeserializer.kt │ │ ├── VertexSerializer.kt │ │ └── scalar │ │ │ ├── BigDecimalPropertyMapper.kt │ │ │ ├── InstantPropertyMapper.kt │ │ │ ├── URLPropertyMapper.kt │ │ │ ├── UUIDPropertyMapper.kt │ │ │ └── identity │ │ │ ├── BooleanPropertyMapper.kt │ │ │ ├── BytePropertyMapper.kt │ │ │ ├── DoublePropertyManager.kt │ │ │ ├── FloatPropertyMapper.kt │ │ │ ├── IdentityPropertyMapper.kt │ │ │ ├── IntegerPropertyMapper.kt │ │ │ ├── LongPropertyMapper.kt │ │ │ └── StringPropertyMapper.kt │ │ ├── reflection │ │ ├── CachedGraphDescription.kt │ │ ├── GraphDescription.kt │ │ ├── ObjectDescription.kt │ │ └── PropertyDescription.kt │ │ └── steps │ │ ├── Dedup.kt │ │ ├── Filter.kt │ │ ├── FilterMap.kt │ │ ├── FlatMap.kt │ │ ├── Map.kt │ │ ├── Slice.kt │ │ ├── Sort.kt │ │ ├── Step.kt │ │ ├── StepTraverser.kt │ │ ├── bound │ │ ├── BoundStep.kt │ │ ├── BoundStepToMany.kt │ │ ├── BoundStepToOptional.kt │ │ ├── BoundStepToSingle.kt │ │ ├── edgespec │ │ │ ├── BoundEdgeSpec.kt │ │ │ ├── BoundEdgeSpecToMany.kt │ │ │ ├── BoundEdgeSpecToOptional.kt │ │ │ └── BoundEdgeSpecToSingle.kt │ │ └── single │ │ │ ├── SingleBoundStep.kt │ │ │ ├── SingleBoundStepToMany.kt │ │ │ ├── SingleBoundStepToOptional.kt │ │ │ ├── SingleBoundStepToSingle.kt │ │ │ └── edgespec │ │ │ ├── SingleBoundEdgeSpec.kt │ │ │ ├── SingleBoundEdgeSpecToMany.kt │ │ │ ├── SingleBoundEdgeSpecToOptional.kt │ │ │ └── SingleBoundEdgeSpecToSingle.kt │ │ ├── edgestep │ │ ├── EdgeStep.kt │ │ ├── EdgeStepToMany.kt │ │ ├── EdgeStepToOptional.kt │ │ ├── EdgeStepToSingle.kt │ │ └── OutEdge.kt │ │ ├── path │ │ ├── Path.kt │ │ ├── PathToMany.kt │ │ ├── PathToOptional.kt │ │ ├── PathToSingle.kt │ │ └── relationship │ │ │ ├── ManyToManyRelationshipPath.kt │ │ │ ├── ManyToOptionalRelationshipPath.kt │ │ │ ├── ManyToSingleRelationshipPath.kt │ │ │ ├── OptionalToManyRelationshipPath.kt │ │ │ ├── OptionalToOptionalRelationshipPath.kt │ │ │ ├── OptionalToSingleRelationshipPath.kt │ │ │ ├── RelationshipPath.kt │ │ │ ├── SingleToManyRelationshipPath.kt │ │ │ ├── SingleToOptionalRelationshipPath.kt │ │ │ └── SingleToSingleRelationshipPath.kt │ │ └── relationship │ │ ├── Relationship.kt │ │ └── edgespec │ │ ├── EdgeSpec.kt │ │ ├── ManyToManyAsymmetricEdgeSpec.kt │ │ ├── ManyToManySymmetricEdgeSpec.kt │ │ ├── ManyToOptionalEdgeSpec.kt │ │ ├── ManyToSingleEdgeSpec.kt │ │ ├── OptionalToManyEdgeSpec.kt │ │ ├── OptionalToOptionalAsymmetricEdgeSpec.kt │ │ ├── OptionalToOptionalSymmetricEdgeSpec.kt │ │ ├── OptionalToSingleEdgeSpec.kt │ │ ├── SingleToManyEdgeSpec.kt │ │ ├── SingleToOptionalEdgeSpec.kt │ │ ├── SingleToSingleAsymmetricEdgeSpec.kt │ │ └── SingleToSingleSymmetricEdgeSpec.kt │ └── test │ ├── README.md │ ├── kotlin │ ├── org │ │ └── apache │ │ │ └── tinkerpop │ │ │ └── gremlin │ │ │ └── ogm │ │ │ ├── GraphMapperTest.kt │ │ │ ├── extensions │ │ │ ├── ElementsTest.kt │ │ │ └── MutableMapsTest.kt │ │ │ └── reflection │ │ │ └── ElementDescriptionTest.kt │ └── util │ │ ├── StaticBiMapper.kt │ │ └── example │ │ ├── DefaultValueSuppliers.kt │ │ ├── Edges.kt │ │ ├── Enums.kt │ │ ├── Graph.kt │ │ ├── Nested.kt │ │ ├── PropertyBiMappers.kt │ │ ├── Relationships.kt │ │ └── Vertices.kt │ └── resources │ └── log4j.properties ├── kotlin-janusgraph-ogm ├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties └── src │ ├── main │ └── kotlin │ │ └── org │ │ └── janusgraph │ │ └── ogm │ │ ├── JanusGraphIndicesBuilder.kt │ │ ├── annotations │ │ └── Indexed.kt │ │ ├── exceptions │ │ ├── IndexNotOnProperty.kt │ │ ├── IndexTypeMismatch.kt │ │ ├── IterableIndexUnsupported.kt │ │ ├── MapIndexUnsupported.kt │ │ ├── NestedIndexUnsupported.kt │ │ ├── SuperclassAnnotationException.kt │ │ ├── UnknownElementType.kt │ │ └── UnrecognizedPropertyClass.kt │ │ └── reflection │ │ └── IndexDescription.kt │ └── test │ └── kotlin │ ├── org │ └── janusgraph │ │ └── ogm │ │ └── reflection │ │ └── IndexDescriptionTest.kt │ └── util │ └── example │ ├── Graph.kt │ └── Verticies.kt ├── kotlin-rx-ogm ├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties └── src │ └── main │ └── kotlin │ └── org │ └── apache │ └── tinkerpop │ └── gremlin │ └── ogm │ └── rx │ ├── Extensions.kt │ ├── MultiBoundGraphTraversalToManyRx.kt │ ├── MultiBoundGraphTraversalToOptionalRx.kt │ ├── MultiBoundGraphTraversalToSingleRx.kt │ ├── SingleBoundGraphTraversalToManyRx.kt │ ├── SingleBoundGraphTraversalToOptionalRx.kt │ └── SingleBoundGraphTraversalToSingleRx.kt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Package Files # 2 | *.war 3 | *.ear 4 | *.zip 5 | *.tar.gz 6 | *.rar 7 | *.pyc 8 | 9 | .gradle 10 | /build/ 11 | 12 | ### IntelliJ IDEA ### 13 | /out/ 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | sudo: false # faster builds 3 | jdk: 4 | - oraclejdk8 5 | install: 6 | - ./gradlew :kotlin-gremlin-ogm:assemble -x :kotlin-gremlin-ogm:signArchives 7 | - ./gradlew :kotlin-janusgraph-ogm:assemble -x :kotlin-janusgraph-ogm:signArchives 8 | script: 9 | - ./gradlew :kotlin-gremlin-ogm:check 10 | - ./gradlew :kotlin-janusgraph-ogm:check 11 | after_success: 12 | - bash <(curl -s https://codecov.io/bash) 13 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /out/ 3 | 4 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Running the server 3 | From the repository root: 4 | 5 | ./gradlew :example:run 6 | 7 | 8 | Then load `http://localhost:5000/graphiql.html` to explore the sample Starwars data using GraphiQL 9 | 10 | ## Running the frontend 11 | From the repository root: 12 | 13 | cd example/frontend && yarn start 14 | 15 | Then load `http://localhost:3000` to explore the sample Starwars data using a basic web client 16 | -------------------------------------------------------------------------------- /example/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | kotlinVersion = '1.3.20' 4 | springBootVersion = '2.1.2.RELEASE' 5 | } 6 | repositories { 7 | mavenCentral() 8 | jcenter() 9 | } 10 | dependencies { 11 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 12 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}") 13 | classpath("com.github.ben-manes:gradle-versions-plugin:0.20.0") 14 | } 15 | } 16 | 17 | apply plugin: 'kotlin' 18 | apply plugin: 'org.springframework.boot' 19 | apply plugin: 'io.spring.dependency-management' 20 | apply plugin: 'idea' 21 | apply plugin: 'application' 22 | apply plugin: 'com.github.ben-manes.versions' 23 | 24 | mainClassName = 'starwars.ApplicationKt' 25 | group = 'starwars' 26 | version = '0.0.1-SNAPSHOT' 27 | sourceCompatibility = 1.8 28 | compileKotlin { 29 | kotlinOptions { 30 | freeCompilerArgs = ["-Xjsr305=strict"] 31 | jvmTarget = "1.8" 32 | } 33 | } 34 | compileTestKotlin { 35 | kotlinOptions { 36 | freeCompilerArgs = ["-Xjsr305=strict"] 37 | jvmTarget = "1.8" 38 | } 39 | } 40 | 41 | repositories { 42 | mavenCentral() 43 | } 44 | 45 | dependencies { 46 | 47 | // Spring-boot 48 | compile('org.springframework.boot:spring-boot-starter-actuator') 49 | compile('org.springframework.boot:spring-boot-starter-web') 50 | 51 | // GraphQL 52 | compile('com.graphql-java-kickstart:graphql-java-tools:5.4.1') 53 | 54 | // Guava 55 | compile('com.google.guava:guava:27.0.1-jre') 56 | 57 | // JanusGraph 58 | compile("org.janusgraph:janusgraph-lucene:0.3.1") 59 | 60 | // OGM 61 | compile project(':kotlin-janusgraph-ogm') 62 | compile project(':kotlin-gremlin-graphql') 63 | } 64 | -------------------------------------------------------------------------------- /example/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /example/frontend/__generated__/globalTypes.ts: -------------------------------------------------------------------------------- 1 | // @generated 2 | /* tslint:disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | //============================================================== 6 | // START Enums and Input Objects 7 | //============================================================== 8 | 9 | /** 10 | * One of the films in the Star Wars Trilogy 11 | */ 12 | export enum Episode { 13 | EMPIRE = "EMPIRE", 14 | JEDI = "JEDI", 15 | NEW_HOPE = "NEW_HOPE", 16 | } 17 | 18 | //============================================================== 19 | // END Enums and Input Objects 20 | //============================================================== 21 | -------------------------------------------------------------------------------- /example/frontend/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /example/frontend/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/en/webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /example/frontend/config/jest/typescriptTransform.js: -------------------------------------------------------------------------------- 1 | // Copyright 2004-present Facebook. All Rights Reserved. 2 | 3 | 'use strict'; 4 | 5 | const tsJestPreprocessor = require('ts-jest/preprocessor'); 6 | 7 | module.exports = tsJestPreprocessor; 8 | -------------------------------------------------------------------------------- /example/frontend/config/polyfills.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof Promise === 'undefined') { 4 | // Rejection tracking prevents a common issue where React gets into an 5 | // inconsistent state due to an error, but it gets swallowed by a Promise, 6 | // and the user has no idea what causes React's erratic future behavior. 7 | require('promise/lib/rejection-tracking').enable(); 8 | window.Promise = require('promise/lib/es6-extensions.js'); 9 | } 10 | 11 | // fetch() polyfill for making API calls. 12 | require('whatwg-fetch'); 13 | 14 | // Object.assign() is commonly used with React. 15 | // It will use the native implementation if it's present and isn't buggy. 16 | Object.assign = require('object-assign'); 17 | 18 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet. 19 | // We don't polyfill it in the browser--this is user's responsibility. 20 | if (process.env.NODE_ENV === 'test') { 21 | require('raf').polyfill(global); 22 | } 23 | -------------------------------------------------------------------------------- /example/frontend/extract-fragment-types.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const fs = require('fs'); 3 | 4 | fetch(`http://localhost:5000/graphql`, { 5 | headers: {'Content-Type': 'application/json'}, 6 | method: 'POST', 7 | body: JSON.stringify({ 8 | query: ` 9 | { 10 | __schema { 11 | types { 12 | kind 13 | name 14 | possibleTypes { 15 | name 16 | } 17 | } 18 | } 19 | } 20 | `, 21 | variables: {}, 22 | }), 23 | }).then(result => result.json()).then(result => { 24 | // here we're filtering out any type information unrelated to unions or interfaces 25 | result.data.__schema.types = result.data.__schema.types.filter( 26 | type => type.possibleTypes !== null, 27 | ); 28 | fs.writeFile( 29 | './src/fragmentTypes.json', 30 | JSON.stringify(result.data), 31 | err => { 32 | if (err) { 33 | // tslint:disable-next-line:no-console 34 | console.error('Error writing fragmentTypes file', err); 35 | } else { 36 | // tslint:disable-next-line:no-console 37 | console.log(' ✔ Fragment types successfully extracted'); 38 | } 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /example/frontend/generate-gql-types.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit on error; error on use of unassigned variable; propagate failed status codes through pipes 4 | set -euo pipefail 5 | 6 | find . -prune -type d -name '__generated__' -exec rm -rf {} +; 7 | echo " ✔ Deleted __generated__ directories" 8 | 9 | $(npm bin)/apollo schema:download \ 10 | --endpoint="http://localhost:5000/graphql" \ 11 | schema.json 12 | 13 | echo " ✔ Downloaded schema" 14 | 15 | # It would be nice to use the backend local 16 | # schema file so that the backend wouldn't need to 17 | # be running, however it seems apollo codegen doesn't 18 | # handle types that inherit multiple interfaces 19 | # --localSchemaFile=../src/main/Resources/graphqls/**.graphqls \ 20 | 21 | $(npm bin)/apollo client:codegen \ 22 | --localSchemaFile="schema.json" \ 23 | --queries="src/**/*.graphql" \ 24 | --target="typescript" \ 25 | --passthroughCustomScalars \ 26 | --mergeInFieldsFromFragmentSpreads \ 27 | --addTypename; 28 | 29 | # Replace the first line of every file with '// @gen3rated' (misspelled intentionally here) which indicates to code 30 | # review tools to skip these files in diffs not show the diff. 31 | function prepend(){ 32 | echo -e "// @""generated\n$(cat $1)" > "$1" 33 | } 34 | 35 | for f in __generated__/*; do 36 | prepend "$f"; 37 | done 38 | 39 | for f in src/**/__generated__/*; do 40 | prepend "$f"; 41 | done 42 | 43 | echo " ✔ Added // @""generated to the top of generated files" 44 | -------------------------------------------------------------------------------- /example/frontend/graphql.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "file": "schema.json" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /example/frontend/images.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' 2 | declare module '*.png' 3 | declare module '*.jpg' 4 | declare module '*.jpeg' 5 | declare module '*.gif' 6 | declare module '*.bmp' 7 | declare module '*.tiff' 8 | -------------------------------------------------------------------------------- /example/frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pm-dev/kotlin-gremlin-ogm/8197847f310bd9928ff1eccb7733b59830e3c12e/example/frontend/public/favicon.ico -------------------------------------------------------------------------------- /example/frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /example/frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Kotlin Gremlin OGM", 3 | "name": "Kotlin Gremlin OGM Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /example/frontend/scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | let argv = process.argv.slice(2); 20 | 21 | // Watch unless on CI, in coverage mode, or explicitly running all tests 22 | if ( 23 | !process.env.CI && 24 | argv.indexOf('--coverage') === -1 && 25 | argv.indexOf('--watchAll') === -1 26 | ) { 27 | argv.push('--watch'); 28 | } 29 | 30 | 31 | jest.run(argv); 32 | -------------------------------------------------------------------------------- /example/frontend/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | @keyframes App-logo-spin { 6 | from { transform: rotate(0deg); } 7 | to { transform: rotate(360deg); } 8 | } 9 | -------------------------------------------------------------------------------- /example/frontend/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /example/frontend/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { BrowserRouter, Route } from 'react-router-dom'; 3 | import './App.css'; 4 | import Character from './Character'; 5 | import Home from './Home'; 6 | 7 | 8 | class App extends React.Component { 9 | public render() { 10 | return ( 11 | 12 |
13 | 14 | 15 |
16 |
17 | ); 18 | } 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /example/frontend/src/Character/query.graphql: -------------------------------------------------------------------------------- 1 | query Character($name: String!) { 2 | character(name: $name) { 3 | id 4 | createdAt 5 | appearsIn 6 | name { 7 | full 8 | } 9 | ...on Human { 10 | homePlanet 11 | } 12 | ...on Droid { 13 | primaryFunction 14 | } 15 | friends { 16 | results { 17 | id 18 | name { 19 | full 20 | } 21 | } 22 | } 23 | secondDegreeFriends { 24 | results { 25 | id 26 | name { 27 | full 28 | } 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/frontend/src/Home/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | export default class HomeComponent extends React.Component<{}, {}> { 5 | public render() { 6 | return ( 7 |
8 |

{'Starwars Character Directory'}

9 |
10 |
Luke Skywalker
11 |
Han Solo
12 |
Leia Organa
13 |
C-3PO
14 |
R2-D2
15 |
16 |
17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/frontend/src/fragmentTypes.json: -------------------------------------------------------------------------------- 1 | {"__schema":{"types":[{"kind":"INTERFACE","name":"Node","possibleTypes":[{"name":"Human"},{"name":"Droid"}]},{"kind":"INTERFACE","name":"Character","possibleTypes":[{"name":"Human"},{"name":"Droid"}]}]}} -------------------------------------------------------------------------------- /example/frontend/src/gql_query_type.ts: -------------------------------------------------------------------------------- 1 | declare module '*.graphql' { 2 | import { DocumentNode } from 'graphql'; 3 | const content: DocumentNode; 4 | export default content; 5 | } 6 | -------------------------------------------------------------------------------- /example/frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /example/frontend/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { InMemoryCache } from 'apollo-cache-inmemory'; 2 | import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; 3 | import { ApolloClient } from 'apollo-client'; 4 | import { ApolloLink } from 'apollo-link'; 5 | import { onError } from 'apollo-link-error'; 6 | import { HttpLink } from 'apollo-link-http'; 7 | import * as React from 'react'; 8 | import { ApolloProvider } from 'react-apollo'; 9 | import * as ReactDOM from 'react-dom'; 10 | import introspectionQueryResultData from './fragmentTypes.json'; 11 | 12 | import App from './App'; 13 | import './index.css'; 14 | import registerServiceWorker from './registerServiceWorker'; 15 | 16 | const fragmentMatcher = new IntrospectionFragmentMatcher({ 17 | introspectionQueryResultData 18 | }); 19 | 20 | const client = new ApolloClient({ 21 | link: ApolloLink.from([ 22 | onError(({ graphQLErrors, networkError }) => { 23 | if (graphQLErrors) { 24 | graphQLErrors.map(({ message, locations, path }) => 25 | // tslint:disable-next-line:no-console 26 | console.log( 27 | `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`, 28 | ), 29 | ); 30 | } 31 | if (networkError) { 32 | // tslint:disable-next-line:no-console 33 | console.log(`[Network error]: ${networkError}`); 34 | } 35 | }), 36 | new HttpLink({ 37 | credentials: 'same-origin', 38 | uri: 'http://localhost:5000/graphql', 39 | }) 40 | ]), 41 | cache: new InMemoryCache({ fragmentMatcher }) 42 | }); 43 | 44 | const app = ( 45 | 46 | 47 | 48 | ); 49 | 50 | ReactDOM.render(app, document.getElementById('root') as HTMLElement); 51 | 52 | registerServiceWorker(); 53 | -------------------------------------------------------------------------------- /example/frontend/src/json_type.ts: -------------------------------------------------------------------------------- 1 | declare module "*.json" { 2 | const value: any; 3 | export default value; 4 | } 5 | -------------------------------------------------------------------------------- /example/frontend/src/scalars.d.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Custom scalar Type mappings for GraphQL. We must add these manually. 4 | */ 5 | declare global { 6 | type Timestamp = number; 7 | } 8 | 9 | export { 10 | Timestamp 11 | } 12 | -------------------------------------------------------------------------------- /example/frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "build/dist", 5 | "module": "esnext", 6 | "target": "es6", 7 | "lib": ["es6", "dom", "esnext"], 8 | "sourceMap": true, 9 | "allowJs": true, 10 | "jsx": "react", 11 | "moduleResolution": "node", 12 | "rootDir": "src", 13 | "forceConsistentCasingInFileNames": true, 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "noImplicitAny": true, 17 | "importHelpers": true, 18 | "strictNullChecks": true, 19 | "suppressImplicitAnyIndexErrors": true, 20 | "noUnusedLocals": true 21 | }, 22 | "exclude": [ 23 | "node_modules", 24 | "build", 25 | "scripts", 26 | "acceptance-tests", 27 | "webpack", 28 | "jest", 29 | "src/setupTests.ts" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /example/frontend/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json" 3 | } -------------------------------------------------------------------------------- /example/frontend/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | } -------------------------------------------------------------------------------- /example/frontend/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"], 3 | "defaultSeverity": "warning", 4 | "linterOptions": { 5 | "exclude": [ 6 | "config/**/*.js", 7 | "node_modules/**/*.ts", 8 | "coverage/lcov-report/*.js" 9 | ] 10 | }, 11 | "rules": { 12 | "interface-name": false, 13 | "no-empty-interface": false, 14 | "max-classes-per-file": [true, 1, "exclude-class-expressions"], 15 | "object-literal-sort-keys": [true, "match-declaration-order"] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /example/locustfile.py: -------------------------------------------------------------------------------- 1 | from locust import HttpLocust, TaskSet, task 2 | import json 3 | 4 | class UserBehavior(TaskSet): 5 | # def on_start(self): 6 | # """ on_start is called when a Locust start before any task is scheduled """ 7 | # self.login() 8 | 9 | # def on_stop(self): 10 | # """ on_stop is called when the TaskSet is stopping """ 11 | # self.logout() 12 | 13 | # def login(self): 14 | # self.client.post("/login", {"username":"ellen_key", "password":"education"}) 15 | 16 | # def logout(self): 17 | # self.client.post("/logout", {"username":"ellen_key", "password":"education"}) 18 | 19 | @task(1) 20 | def getR2D2(self): 21 | self.client.post("/graphql", data=json.dumps({"query":"{\n character(name: \"R2-D2\") {\n name {\n full\n }\n friends {\n createdAt\n name {\n full\n }\n friends {\n name {\n full\n }\n friends {\n name {\n full\n }\n }\n }\n }\n secondDegreeFriends(limit: 2) {\n id\n name {\n full\n }\n }\n ... on Human {\n twinSiblings {\n name {\n full\n }\n }\n }\n }\n}\n"}), headers={'content-type': 'application/json'}) 22 | 23 | class WebsiteUser(HttpLocust): 24 | task_set = UserBehavior 25 | min_wait = 1000 26 | max_wait = 5000 -------------------------------------------------------------------------------- /example/src/main/Resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=5000 2 | spring.groovy.template.check-template-location=false 3 | logging.level.starwars=debug 4 | logging.level.graphql.servlet.ogm=debug 5 | logging.level.org.apache.tinkerpop.gremlin.ogm.caching=debug 6 | -------------------------------------------------------------------------------- /example/src/main/Resources/graphqls/Character.graphqls: -------------------------------------------------------------------------------- 1 | # A character in the Star Wars Trilogy 2 | interface Character { 3 | 4 | # The id of the character 5 | id: ID! 6 | 7 | # The timestamp this character was created 8 | createdAt: Timestamp! 9 | 10 | # The name of the character 11 | name: Name! 12 | 13 | # The friends of the character, or an empty list if they have none 14 | friends( 15 | resume: CharacterPageCursor, 16 | limit: PageLimit = 100, 17 | order: CharacterPageOrder = CREATED_AT_DESCENDING 18 | ): CharacterPage! 19 | 20 | # Which movies they appear in 21 | appearsIn: [Episode!]! 22 | 23 | # The paginated friends of the droid, or an empty list if they have none 24 | secondDegreeFriends( 25 | resume: CharacterPageCursor, 26 | limit: PageLimit = 100, 27 | order: CharacterPageOrder = CREATED_AT_DESCENDING 28 | ): CharacterPage! 29 | } 30 | 31 | type CharacterPage { 32 | 33 | previous: CharacterPageCursor 34 | 35 | next: CharacterPageCursor 36 | 37 | results: [Character!]! 38 | } 39 | 40 | scalar CharacterPageCursor 41 | 42 | enum CharacterPageOrder { 43 | 44 | CREATED_AT_ASCENDING, 45 | 46 | CREATED_AT_DESCENDING, 47 | 48 | FULL_NAME_ASCENDING, 49 | 50 | FULL_NAME_DESCENDING 51 | } 52 | -------------------------------------------------------------------------------- /example/src/main/Resources/graphqls/Droid.graphqls: -------------------------------------------------------------------------------- 1 | # A mechanical creature in the Star Wars universe 2 | type Droid implements Node, Character { 3 | 4 | # The id of the droid 5 | id: ID! 6 | 7 | # The moment this droid was created 8 | createdAt: Timestamp! 9 | 10 | # The name of the droid 11 | name: Name! 12 | 13 | # The friends of the droid, or an empty list if they have none 14 | friends( 15 | resume: CharacterPageCursor, 16 | limit: PageLimit = 100, 17 | order: CharacterPageOrder = CREATED_AT_DESCENDING 18 | ): CharacterPage! 19 | 20 | # Which movies they appear in 21 | appearsIn: [Episode!]! 22 | 23 | # The primary function of the droid 24 | primaryFunction: String! 25 | 26 | # The friends of the droid's friends 27 | secondDegreeFriends( 28 | resume: CharacterPageCursor, 29 | limit: PageLimit = 100, 30 | order: CharacterPageOrder = CREATED_AT_DESCENDING 31 | ): CharacterPage! 32 | } 33 | -------------------------------------------------------------------------------- /example/src/main/Resources/graphqls/Episode.graphqls: -------------------------------------------------------------------------------- 1 | # One of the films in the Star Wars Trilogy 2 | enum Episode { 3 | 4 | NEW_HOPE, 5 | 6 | EMPIRE, 7 | 8 | JEDI 9 | } 10 | -------------------------------------------------------------------------------- /example/src/main/Resources/graphqls/Human.graphqls: -------------------------------------------------------------------------------- 1 | # A humanoid creature in the Star Wars universe 2 | type Human implements Node, Character { 3 | 4 | # The id of the human 5 | id: ID! 6 | 7 | # The name of the human 8 | name: Name! 9 | 10 | # The moment this human was created 11 | createdAt: Timestamp! 12 | 13 | # The friends of the human, or an empty list if they have none 14 | friends( 15 | resume: CharacterPageCursor, 16 | limit: PageLimit = 100, 17 | order: CharacterPageOrder = CREATED_AT_DESCENDING 18 | ): CharacterPage! 19 | 20 | # Which movies they appear in 21 | appearsIn: [Episode!]! 22 | 23 | # The home planet of the human, or null if unknown 24 | homePlanet: String 25 | 26 | # The friends of the human's friends 27 | secondDegreeFriends( 28 | resume: CharacterPageCursor, 29 | limit: PageLimit = 100, 30 | order: CharacterPageOrder = CREATED_AT_DESCENDING 31 | ): CharacterPage! 32 | 33 | # Siblings of this human who have the same birthday 34 | twinSiblings( 35 | resume: HumanPageCursor, 36 | limit: PageLimit = 100, 37 | order: HumanPageOrder = CREATED_AT_DESCENDING 38 | ): HumanPage! 39 | } 40 | 41 | type HumanPage { 42 | 43 | previous: HumanPageCursor 44 | 45 | next: HumanPageCursor 46 | 47 | results: [Human!]! 48 | } 49 | 50 | scalar HumanPageCursor 51 | 52 | enum HumanPageOrder { 53 | 54 | CREATED_AT_ASCENDING, 55 | 56 | CREATED_AT_DESCENDING, 57 | 58 | FULL_NAME_ASCENDING, 59 | 60 | FULL_NAME_DESCENDING, 61 | 62 | HOME_PLANET_ASCENDING, 63 | 64 | HOME_PLANET_DESCENDING 65 | } 66 | -------------------------------------------------------------------------------- /example/src/main/Resources/graphqls/Mutation.graphqls: -------------------------------------------------------------------------------- 1 | type Mutation { 2 | 3 | # Create a new droid 4 | createDroid(name: String!, primaryFunction: String!, friendIds: [ID!]!, appearsIn: [Episode!]!): Droid! 5 | 6 | updateCharacterName(characterID: ID!, name: String!): Character 7 | } 8 | -------------------------------------------------------------------------------- /example/src/main/Resources/graphqls/Name.graphqls: -------------------------------------------------------------------------------- 1 | type Name { 2 | 3 | given: String! 4 | 5 | surname: String 6 | 7 | full: String! 8 | } 9 | -------------------------------------------------------------------------------- /example/src/main/Resources/graphqls/Node.graphqls: -------------------------------------------------------------------------------- 1 | interface Node { 2 | 3 | # The id of the node 4 | id: ID! 5 | 6 | # The timestamp this node was created 7 | createdAt: Timestamp! 8 | } 9 | -------------------------------------------------------------------------------- /example/src/main/Resources/graphqls/Query.graphqls: -------------------------------------------------------------------------------- 1 | type Query { 2 | 3 | # Hero of the Star wars saga 4 | hero: Character! 5 | 6 | # Find character by its full name 7 | character(name: String!): Character 8 | 9 | # Find human by its full name 10 | human(name: String!): Human 11 | 12 | # Find droid by its full name 13 | droid(name: String!): Droid 14 | } 15 | -------------------------------------------------------------------------------- /example/src/main/Resources/graphqls/Scalars.graphqls: -------------------------------------------------------------------------------- 1 | # A single instantaneous point on the time-line. This timestamp counts in seconds from the unix epoch. 2 | # Accurate to the nearest millisecond. Formatted as a json number. 3 | scalar Timestamp 4 | 5 | # [0 - 100] 6 | scalar PageLimit 7 | -------------------------------------------------------------------------------- /example/src/main/kotlin/framework/BaseEdge.kt: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Edge 4 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 5 | import org.janusgraph.graphdb.relations.RelationIdentifier 6 | 7 | internal abstract class BaseEdge : BaseElement(), Edge 8 | -------------------------------------------------------------------------------- /example/src/main/kotlin/framework/BaseElement.kt: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | import graphql.schema.pagination.Identifiable 4 | 5 | 6 | internal abstract class BaseElement : Identifiable { 7 | 8 | abstract override val id: ID? 9 | 10 | override fun hashCode() = id?.hashCode() ?: super.hashCode() 11 | 12 | override fun equals(other: Any?) = id != null && other != null && other is BaseElement<*> && id == other.id 13 | } 14 | -------------------------------------------------------------------------------- /example/src/main/kotlin/framework/BaseVertex.kt: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | import java.time.Instant 4 | 5 | internal abstract class BaseVertex : BaseElement() { 6 | 7 | abstract val createdAt: Instant 8 | } 9 | -------------------------------------------------------------------------------- /example/src/main/kotlin/framework/StaticBiMapper.kt: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | import com.google.common.collect.BiMap 4 | import org.apache.tinkerpop.gremlin.ogm.mappers.BiMapper 5 | 6 | internal interface StaticBiMapper : BiMapper { 7 | 8 | val map: BiMap 9 | 10 | override fun forwardMap(from: X): Y = map[from] ?: throw MissingFromDomain(from) 11 | override fun inverseMap(from: Y): X = map.inverse()[from] ?: throw MissingFromCodomain(from) 12 | 13 | private class MissingFromDomain(obj: Any) : RuntimeException("Value missing from domain of static bi-map: $obj") 14 | private class MissingFromCodomain(obj: Any) : RuntimeException("Value missing from co-domain of static bi-map: $obj") 15 | } 16 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/Application.kt: -------------------------------------------------------------------------------- 1 | package starwars 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | import org.springframework.boot.runApplication 5 | 6 | 7 | @SpringBootApplication 8 | internal open class Application 9 | 10 | internal fun main(args: Array) { 11 | runApplication(*args) 12 | } 13 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/StarwarsGraphMapper.kt: -------------------------------------------------------------------------------- 1 | package starwars 2 | 3 | import com.google.common.cache.CacheBuilder 4 | import com.google.common.cache.CacheLoader 5 | import com.google.common.cache.LoadingCache 6 | import org.apache.tinkerpop.gremlin.ogm.GraphEdge 7 | import org.apache.tinkerpop.gremlin.ogm.GraphVertex 8 | import org.apache.tinkerpop.gremlin.ogm.caching.CachedGraphMapper 9 | import org.apache.tinkerpop.gremlin.ogm.caching.GraphMapperCache 10 | import org.apache.tinkerpop.gremlin.ogm.elements.Edge 11 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 12 | import org.apache.tinkerpop.gremlin.ogm.reflection.GraphDescription 13 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource 14 | 15 | internal class StarwarsGraphMapper( 16 | override val graphDescription: GraphDescription, 17 | override val g: GraphTraversalSource 18 | ) : CachedGraphMapper { 19 | 20 | override val cache = object : GraphMapperCache { 21 | 22 | private val vertexCache: LoadingCache = CacheBuilder.newBuilder().build( 23 | object : CacheLoader() { 24 | override fun load(key: GraphVertex): Vertex = this@StarwarsGraphMapper.load(key) 25 | }) 26 | 27 | private val edgeCache: LoadingCache> = CacheBuilder.newBuilder().build( 28 | object : CacheLoader>() { 29 | override fun load(key: GraphEdge): Edge = this@StarwarsGraphMapper.load(key) 30 | }) 31 | 32 | @Suppress("UNCHECKED_CAST") 33 | override fun get(serialized: GraphVertex): V = 34 | vertexCache.get(serialized) as V 35 | 36 | @Suppress("UNCHECKED_CAST") 37 | override fun > get(serialized: GraphEdge): E = 38 | edgeCache.get(serialized) as E 39 | 40 | override fun put(serialized: GraphVertex, deserialized: V) = 41 | vertexCache.put(serialized, deserialized) 42 | 43 | override fun > put(serialized: GraphEdge, deserialized: E) = 44 | edgeCache.put(serialized, deserialized) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/Pagination.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql 2 | 3 | import starwars.graphql.character.CharacterPage 4 | import starwars.graphql.character.CharacterPageInfo 5 | import starwars.graphql.human.HumanPage 6 | import starwars.graphql.human.HumanPageInfo 7 | import starwars.models.Character 8 | import starwars.models.Human 9 | 10 | internal fun List.paginate(info: CharacterPageInfo): CharacterPage = info.paginate(results = this) 11 | internal fun List.paginate(info: HumanPageInfo): HumanPage = info.paginate(results = this) 12 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/StarwarsGraphQLCorsFilter.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql 2 | 3 | import org.springframework.context.annotation.Configuration 4 | import org.springframework.web.cors.CorsConfiguration 5 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource 6 | import org.springframework.web.filter.CorsFilter 7 | 8 | @Configuration 9 | internal open class StarwarsGraphQLCorsFilter : CorsFilter( 10 | UrlBasedCorsConfigurationSource().let { source -> 11 | val config = CorsConfiguration() 12 | config.allowCredentials = true 13 | allowedClients.forEach(config::addAllowedOrigin) 14 | config.addAllowedHeader("*") 15 | config.addAllowedMethod("POST") 16 | source.registerCorsConfiguration("/graphql", config) 17 | source 18 | }) { 19 | companion object { 20 | val allowedClients = listOf("http://localhost:3000") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/StarwarsGraphQLServletRegistrationBean.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql 2 | 3 | import graphql.servlet.SimpleGraphQLHttpServlet 4 | import org.springframework.boot.web.servlet.ServletRegistrationBean 5 | import org.springframework.stereotype.Component 6 | 7 | @Component 8 | internal class StarwarsGraphQLServletRegistrationBean( 9 | servlet: SimpleGraphQLHttpServlet 10 | ) : ServletRegistrationBean(servlet, "/graphql") 11 | 12 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/character/CharacterMutationResolver.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package starwars.graphql.character 4 | 5 | import com.coxautodev.graphql.tools.GraphQLMutationResolver 6 | import graphql.schema.DataFetchingEnvironment 7 | import graphql.servlet.ogm.mutate 8 | import org.springframework.stereotype.Component 9 | import starwars.models.Character 10 | import starwars.models.Droid 11 | import starwars.models.Human 12 | import starwars.models.Name 13 | 14 | @Component 15 | internal class CharacterMutationResolver : GraphQLMutationResolver { 16 | 17 | fun updateCharacterName( 18 | characterID: Long, 19 | name: String, 20 | env: DataFetchingEnvironment 21 | ): Character = env.mutate { 22 | val character = V(id = characterID)!! 23 | val newName = Name.parse(raw = name) 24 | val updated = when (character) { 25 | is Droid -> character.copy(name = newName) 26 | is Human -> character.copy(name = newName) 27 | } 28 | saveV(vertex = updated) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/character/CharacterPage.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql.character 2 | 3 | import graphql.schema.pagination.Page 4 | import starwars.models.Character 5 | 6 | internal data class CharacterPage( 7 | override val previous: CharacterPageInfo?, 8 | override val next: CharacterPageInfo?, 9 | override val results: List 10 | ) : Page 11 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/character/CharacterPageInfo.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql.character 2 | 3 | import graphql.schema.pagination.Page 4 | import starwars.models.Character 5 | 6 | internal data class CharacterPageInfo( 7 | override val order: CharacterPageOrder, 8 | override val fromID: Long? = null, 9 | override val direction: Page.Info.Direction? = null, 10 | @Transient override val limit: Int? = null 11 | ) : Page.Info { 12 | 13 | fun paginate(results: Collection): CharacterPage { 14 | val parts = parts(results = results) 15 | val previousResult = parts.previous 16 | val nextResult = parts.next 17 | return CharacterPage( 18 | previous = when (previousResult) { 19 | null -> null 20 | else -> CharacterPageInfo( 21 | order = order, 22 | fromID = previousResult.id, 23 | direction = Page.Info.Direction.BACKWARD) 24 | }, 25 | next = when (nextResult) { 26 | null -> null 27 | else -> CharacterPageInfo( 28 | order = order, 29 | fromID = nextResult.id, 30 | direction = 31 | Page.Info.Direction.FORWARD) 32 | }, 33 | results = parts.page) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/character/CharacterPageOrder.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql.character 2 | 3 | import starwars.models.Character 4 | 5 | 6 | internal enum class CharacterPageOrder( 7 | private val comparator: Comparator 8 | ) : Comparator { 9 | 10 | CREATED_AT_ASCENDING(Comparator.comparing(Character::createdAt)), 11 | 12 | CREATED_AT_DESCENDING(CREATED_AT_ASCENDING.comparator.reversed()), 13 | 14 | FULL_NAME_ASCENDING(Comparator.comparing { it.name.full }), 15 | 16 | FULL_NAME_DESCENDING(FULL_NAME_ASCENDING.comparator.reversed()), 17 | ; 18 | 19 | override fun compare(o1: Character?, o2: Character?): Int = comparator.compare(o1, o2) 20 | } 21 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/character/CharacterQueryResolver.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package starwars.graphql.character 4 | 5 | import com.coxautodev.graphql.tools.GraphQLQueryResolver 6 | import graphql.schema.DataFetchingEnvironment 7 | import graphql.servlet.ogm.fetch 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi 9 | import kotlinx.coroutines.GlobalScope 10 | import kotlinx.coroutines.future.await 11 | import kotlinx.coroutines.future.future 12 | import org.apache.tinkerpop.gremlin.ogm.allV 13 | import org.springframework.stereotype.Component 14 | import starwars.models.Character 15 | import starwars.models.Name 16 | import java.util.concurrent.CompletableFuture 17 | 18 | @ExperimentalCoroutinesApi 19 | @Component 20 | internal class CharacterQueryResolver : GraphQLQueryResolver { 21 | 22 | fun hero(env: DataFetchingEnvironment): CompletableFuture = GlobalScope.future { 23 | character(rawName = "Luke Skywalker", env = env).await()!! 24 | } 25 | 26 | fun character(rawName: String, env: DataFetchingEnvironment): CompletableFuture = env.fetch { 27 | // JanusGraph will warn that this query requires iterating over all vertices. 28 | // This is because abstract Vertex classes are queried by union-ing queries of their base classes 29 | // Need to find a fix for this. 30 | val name = Name.parse(raw = rawName) 31 | graphMapper.allV { 32 | has("name.given", name.given).apply { 33 | if (name.surname != null) has("name.surname", name.surname) 34 | } 35 | }.singleOrNull() 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/character/CharacterTypeResolver.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql.character 2 | 3 | import graphql.schema.DataFetchingEnvironment 4 | import graphql.servlet.ogm.fetch 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import starwars.graphql.paginate 7 | import starwars.models.Character 8 | import java.util.* 9 | import java.util.concurrent.CompletableFuture 10 | 11 | @ExperimentalCoroutinesApi 12 | internal interface CharacterTypeResolver { 13 | 14 | fun getId(character: Character): Any = character.id ?: UUID.randomUUID().toString() 15 | 16 | fun getFriends( 17 | character: Character, 18 | resume: CharacterPageInfo?, 19 | limit: Int, 20 | order: CharacterPageOrder, 21 | env: DataFetchingEnvironment 22 | ): CompletableFuture = env.fetch { 23 | character.friends 24 | .load() 25 | .paginate(info = resume?.copy(limit = limit) ?: CharacterPageInfo(order = order, limit = limit)) 26 | } 27 | 28 | fun getSecondDegreeFriends( 29 | character: Character, 30 | resume: CharacterPageInfo?, 31 | limit: Int, 32 | order: CharacterPageOrder, 33 | env: DataFetchingEnvironment 34 | ): CompletableFuture = env.fetch { 35 | character.secondDegreeFriends 36 | .load() 37 | .paginate(info = resume?.copy(limit = limit) ?: CharacterPageInfo(order = order, limit = limit)) 38 | } 39 | } 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/droid/DroidMutationResolver.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package starwars.graphql.droid 4 | 5 | import com.coxautodev.graphql.tools.GraphQLMutationResolver 6 | import graphql.schema.DataFetchingEnvironment 7 | import graphql.servlet.ogm.mutate 8 | import org.springframework.stereotype.Component 9 | import starwars.models.Character 10 | import starwars.models.Droid 11 | import starwars.models.Episode 12 | import starwars.models.Name 13 | import java.time.Instant 14 | 15 | @Component 16 | internal class DroidMutationResolver : GraphQLMutationResolver { 17 | 18 | fun createDroid( 19 | name: String, 20 | primaryFunction: String, 21 | friendIds: Set, 22 | appearsIn: Set, 23 | env: DataFetchingEnvironment 24 | ): Droid = env.mutate { 25 | val friends = V(ids = friendIds) 26 | val droid = saveV(vertex = Droid( 27 | name = Name.parse(raw = name), 28 | appearsIn = appearsIn, 29 | createdAt = Instant.now(), 30 | primaryFunction = primaryFunction)) 31 | saveE(edges = Character.friends from droid to friends) 32 | droid 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/droid/DroidQueryResolver.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package starwars.graphql.droid 4 | 5 | import com.coxautodev.graphql.tools.GraphQLQueryResolver 6 | import graphql.schema.DataFetchingEnvironment 7 | import graphql.servlet.ogm.fetch 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi 9 | import org.apache.tinkerpop.gremlin.ogm.allV 10 | import org.springframework.stereotype.Component 11 | import starwars.models.Droid 12 | import starwars.models.Name 13 | import java.util.concurrent.CompletableFuture 14 | 15 | @ExperimentalCoroutinesApi 16 | @Component 17 | internal class DroidQueryResolver : GraphQLQueryResolver { 18 | 19 | fun droid(rawName: String, env: DataFetchingEnvironment): CompletableFuture = env.fetch { 20 | val name = Name.parse(raw = rawName) 21 | graphMapper.allV { 22 | has("name.given", name.given).apply { 23 | if (name.surname != null) has("name.surname", name.surname) 24 | } 25 | }.singleOrNull() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/droid/DroidTypeResolver.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package starwars.graphql.droid 4 | 5 | import com.coxautodev.graphql.tools.GraphQLResolver 6 | import graphql.schema.DataFetchingEnvironment 7 | import kotlinx.coroutines.ExperimentalCoroutinesApi 8 | import org.springframework.stereotype.Component 9 | import starwars.graphql.character.CharacterPage 10 | import starwars.graphql.character.CharacterPageInfo 11 | import starwars.graphql.character.CharacterPageOrder 12 | import starwars.graphql.character.CharacterTypeResolver 13 | import starwars.models.Droid 14 | import java.util.concurrent.CompletableFuture 15 | 16 | @ExperimentalCoroutinesApi 17 | @Component 18 | internal class DroidTypeResolver : CharacterTypeResolver, GraphQLResolver { 19 | 20 | // These redundant overrides are necessary for graphql.tools 21 | 22 | fun getId(droid: Droid): Any = super.getId(character = droid) 23 | 24 | fun getFriends( 25 | droid: Droid, 26 | resume: CharacterPageInfo?, 27 | limit: Int, 28 | order: CharacterPageOrder, 29 | env: DataFetchingEnvironment 30 | ): CompletableFuture = super.getFriends( 31 | character = droid, 32 | resume = resume, 33 | limit = limit, 34 | order = order, 35 | env = env) 36 | 37 | fun getSecondDegreeFriends( 38 | droid: Droid, 39 | resume: CharacterPageInfo?, 40 | limit: Int, 41 | order: CharacterPageOrder, 42 | env: DataFetchingEnvironment 43 | ): CompletableFuture = super.getSecondDegreeFriends( 44 | character = droid, 45 | resume = resume, 46 | limit = limit, 47 | order = order, 48 | env = env) 49 | } 50 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/human/HumanPage.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql.human 2 | 3 | import graphql.schema.pagination.Page 4 | import starwars.models.Human 5 | 6 | internal data class HumanPage( 7 | override val previous: HumanPageInfo?, 8 | override val next: HumanPageInfo?, 9 | override val results: List 10 | ) : Page 11 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/human/HumanPageInfo.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql.human 2 | 3 | import graphql.schema.pagination.Page 4 | import starwars.models.Human 5 | 6 | internal data class HumanPageInfo( 7 | override val order: HumanPageOrder, 8 | override val fromID: Long? = null, 9 | override val direction: Page.Info.Direction? = null, 10 | @Transient override val limit: Int? = null 11 | ) : Page.Info { 12 | 13 | fun paginate(results: List): HumanPage { 14 | val parts = parts(results = results) 15 | val previousResult = parts.previous 16 | val nextResult = parts.next 17 | return HumanPage( 18 | previous = when (previousResult) { 19 | null -> null 20 | else -> HumanPageInfo( 21 | order = order, 22 | fromID = previousResult.id, 23 | direction = Page.Info.Direction.BACKWARD) 24 | }, 25 | next = when (nextResult) { 26 | null -> null 27 | else -> HumanPageInfo( 28 | order = order, 29 | fromID = nextResult.id, 30 | direction = 31 | Page.Info.Direction.FORWARD) 32 | }, 33 | results = parts.page) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/human/HumanPageOrder.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql.human 2 | 3 | import starwars.models.Human 4 | 5 | internal enum class HumanPageOrder( 6 | private val comparator: Comparator 7 | ) : Comparator { 8 | 9 | CREATED_AT_ASCENDING(Comparator.comparing(Human::createdAt)), 10 | 11 | CREATED_AT_DESCENDING(CREATED_AT_ASCENDING.comparator.reversed()), 12 | 13 | FULL_NAME_ASCENDING(Comparator.comparing { it.name.full }), 14 | 15 | FULL_NAME_DESCENDING(FULL_NAME_ASCENDING.comparator.reversed()), 16 | 17 | HOME_PLANET_ASCENDING(Comparator.comparing(Human::homePlanet)), 18 | 19 | HOME_PLANET_DESCENDING(HOME_PLANET_ASCENDING.comparator.reversed()) 20 | ; 21 | 22 | override fun compare(o1: Human?, o2: Human?): Int = comparator.compare(o1, o2) 23 | } 24 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/human/HumanQueryResolver.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package starwars.graphql.human 4 | 5 | import com.coxautodev.graphql.tools.GraphQLQueryResolver 6 | import graphql.schema.DataFetchingEnvironment 7 | import graphql.servlet.ogm.fetch 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi 9 | import org.apache.tinkerpop.gremlin.ogm.allV 10 | import org.springframework.stereotype.Component 11 | import starwars.models.Human 12 | import starwars.models.Name 13 | import java.util.concurrent.CompletableFuture 14 | 15 | @ExperimentalCoroutinesApi 16 | @Component 17 | internal class HumanQueryResolver : GraphQLQueryResolver { 18 | 19 | fun human(rawName: String, env: DataFetchingEnvironment): CompletableFuture = env.fetch { 20 | val name = Name.parse(raw = rawName) 21 | graphMapper.allV { 22 | has("name.given", name.given).apply { 23 | if (name.surname != null) has("name.surname", name.surname) 24 | } 25 | }.singleOrNull() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/scalars/GraphQLDecimal.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql.scalars 2 | 3 | import graphql.language.FloatValue 4 | import graphql.language.IntValue 5 | import graphql.language.StringValue 6 | import graphql.schema.Coercing 7 | import graphql.schema.CoercingSerializeException 8 | import graphql.schema.GraphQLScalarType 9 | import java.math.BigDecimal 10 | 11 | internal object GraphQLDecimal : GraphQLScalarType( 12 | "Decimal", 13 | "A number in base 10. Formatted as a json string (because floating point decimal numbers may not have " + 14 | "an exact binary representation when stored as a float or double)", 15 | object : Coercing { 16 | 17 | override fun serialize(dataFetcherResult: Any?): String = when (dataFetcherResult) { 18 | is BigDecimal -> dataFetcherResult.toString() 19 | else -> throw CoercingSerializeException("Decimal field cannot serialize '" + dataFetcherResult?.javaClass?.simpleName + "'.") 20 | } 21 | 22 | override fun parseLiteral(input: Any?): BigDecimal = when (input) { 23 | is String -> BigDecimal(input) 24 | is Long -> BigDecimal.valueOf(input) 25 | is Int -> BigDecimal.valueOf(input.toLong()) 26 | is Double -> BigDecimal.valueOf(input) 27 | is Float -> BigDecimal.valueOf(input.toDouble()) 28 | is Short -> BigDecimal.valueOf(input.toLong()) 29 | else -> throw CoercingSerializeException("Decimal field cannot deserialize '" + input?.javaClass?.simpleName + "'.") 30 | } 31 | 32 | override fun parseValue(input: Any?): BigDecimal = when (input) { 33 | is StringValue -> BigDecimal(input.value) 34 | is FloatValue -> input.value 35 | is IntValue -> input.value.toBigDecimal() 36 | else -> throw CoercingSerializeException("Decimal field cannot deserialize '" + input?.javaClass?.simpleName + "'.") 37 | } 38 | } 39 | ) 40 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/scalars/GraphQLPageLimit.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql.scalars 2 | 3 | import graphql.Scalars 4 | import graphql.schema.Coercing 5 | import graphql.schema.CoercingSerializeException 6 | import graphql.schema.GraphQLScalarType 7 | 8 | internal object GraphQLPageLimit : GraphQLScalarType( 9 | "PageLimit", 10 | "[0 - 100]", 11 | object : Coercing { 12 | 13 | @Suppress("UNCHECKED_CAST") 14 | val integerCoercing = Scalars.GraphQLInt.coercing as Coercing 15 | 16 | override fun serialize(dataFetcherResult: Any?) = integerCoercing.serialize(dataFetcherResult).also { 17 | if (it < 0 || it > 100) { 18 | throw CoercingSerializeException("GQL scalar type 'PageLimit' must be 0-100. Was $dataFetcherResult") 19 | } 20 | } 21 | 22 | override fun parseLiteral(input: Any?) = integerCoercing.parseLiteral(input).also { 23 | if (it < 0 || it > 100) { 24 | throw CoercingSerializeException("GQL scalar type 'PageLimit' must be 0-100. Was $input") 25 | } 26 | } 27 | 28 | override fun parseValue(input: Any?) = integerCoercing.parseValue(input).also { 29 | if (it < 0 || it > 100) { 30 | throw CoercingSerializeException("GQL scalar type 'PageLimit' must be 0-100. Was $input") 31 | } 32 | } 33 | } 34 | ) 35 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/scalars/GraphQLTimestamp.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql.scalars 2 | 3 | import graphql.language.FloatValue 4 | import graphql.language.IntValue 5 | import graphql.schema.Coercing 6 | import graphql.schema.CoercingSerializeException 7 | import graphql.schema.GraphQLScalarType 8 | import starwars.graphql.scalars.GraphQLTimestamp.MILLIS_IN_SECOND 9 | import java.time.Instant 10 | 11 | internal object GraphQLTimestamp : GraphQLScalarType( 12 | "Timestamp", 13 | "A single instantaneous point on the time-line. This timestamp counts in seconds from the unix epoch. " + 14 | "Accurate to the nearest millisecond. Formatted as a json number.", 15 | object : Coercing { 16 | 17 | override fun serialize(dataFetcherResult: Any?): Double = when (dataFetcherResult) { 18 | is Instant -> dataFetcherResult.toEpochMilli() / MILLIS_IN_SECOND 19 | else -> throw CoercingSerializeException("Timestamp field cannot serialize '" + dataFetcherResult?.javaClass?.simpleName + "'.") 20 | } 21 | 22 | override fun parseLiteral(input: Any?): Instant = when (input) { 23 | is Number -> Instant.ofEpochMilli((input.toDouble() * MILLIS_IN_SECOND).toLong()) 24 | else -> throw CoercingSerializeException("Timestamp field cannot deserialize '" + input?.javaClass?.simpleName + "'.") 25 | } 26 | 27 | override fun parseValue(input: Any?): Instant = when (input) { 28 | is IntValue -> Instant.ofEpochMilli((input.value.toDouble() * MILLIS_IN_SECOND).toLong()) 29 | is FloatValue -> Instant.ofEpochMilli((input.value.toDouble() * MILLIS_IN_SECOND).toLong()) 30 | else -> throw CoercingSerializeException("Timestamp field cannot deserialize '" + input?.javaClass?.simpleName + "'.") 31 | } 32 | } 33 | ) { 34 | private const val MILLIS_IN_SECOND: Double = 1000.0 35 | } 36 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/graphql/scalars/GraphQLURL.kt: -------------------------------------------------------------------------------- 1 | package starwars.graphql.scalars 2 | 3 | import graphql.language.StringValue 4 | import graphql.schema.Coercing 5 | import graphql.schema.CoercingSerializeException 6 | import graphql.schema.GraphQLScalarType 7 | import java.net.URL 8 | 9 | internal object GraphQLURL : GraphQLScalarType( 10 | "URL", 11 | "A url to a resource on the web. Formatted as a json string.", 12 | object : Coercing { 13 | 14 | override fun serialize(dataFetcherResult: Any?): String = when (dataFetcherResult) { 15 | is URL -> dataFetcherResult.toString() 16 | else -> throw CoercingSerializeException("URL field cannot serialize '" + dataFetcherResult?.javaClass?.simpleName + "'.") 17 | } 18 | 19 | override fun parseLiteral(input: Any?): URL = when (input) { 20 | is String -> URL(input) 21 | else -> throw CoercingSerializeException("URL field cannot deserialize '" + input?.javaClass?.simpleName + "'.") 22 | } 23 | 24 | override fun parseValue(input: Any?): URL = when (input) { 25 | is StringValue -> URL(input.value) 26 | else -> throw CoercingSerializeException("URL field cannot deserialize '" + input?.javaClass?.simpleName + "'.") 27 | } 28 | } 29 | ) 30 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/models/Episode.kt: -------------------------------------------------------------------------------- 1 | package starwars.models 2 | 3 | import com.google.common.collect.ImmutableBiMap 4 | import framework.StaticBiMapper 5 | import org.apache.tinkerpop.gremlin.ogm.mappers.EnumBiMapper 6 | 7 | internal enum class Episode { 8 | NEW_HOPE, 9 | EMPIRE, 10 | JEDI, 11 | ; 12 | 13 | companion object : EnumBiMapper, StaticBiMapper { 14 | override val map: ImmutableBiMap = ImmutableBiMap.of( 15 | NEW_HOPE, "NEW_HOPE", 16 | EMPIRE, "EMPIRE", 17 | JEDI, "JEDI") 18 | 19 | override val serializedClass get() = String::class 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/models/Name.kt: -------------------------------------------------------------------------------- 1 | package starwars.models 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.annotations.Property 4 | 5 | internal data class Name( 6 | 7 | @Property(key = "given") 8 | val given: String, 9 | 10 | @Property(key = "surname") 11 | val surname: String? = null 12 | ) { 13 | val full get() = if (surname == null) given else "$given $surname" 14 | 15 | override fun toString() = full 16 | 17 | companion object { 18 | fun parse(raw: String): Name { 19 | val nameParts = raw.split(" ") 20 | val lastName = nameParts.subList(1, nameParts.size).joinToString(" ") 21 | return Name(given = nameParts.first(), surname = if (lastName.isEmpty()) null else lastName) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/models/Sibling.kt: -------------------------------------------------------------------------------- 1 | package starwars.models 2 | 3 | import framework.BaseEdge 4 | import org.apache.tinkerpop.gremlin.ogm.annotations.* 5 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.ManyToManySymmetricEdgeSpec 6 | import org.janusgraph.graphdb.relations.RelationIdentifier 7 | 8 | @Element(label = "siblings") 9 | internal data class Sibling( 10 | 11 | @ID 12 | override val id: RelationIdentifier? = null, 13 | 14 | @FromVertex 15 | override val from: Human, 16 | 17 | @ToVertex 18 | override val to: Human, 19 | 20 | @Property("twins") 21 | val twins: Boolean 22 | 23 | ) : BaseEdge() { 24 | 25 | companion object { 26 | val siblings = ManyToManySymmetricEdgeSpec(name = "siblings") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example/src/main/kotlin/starwars/traversals/human/TwinSiblings.kt: -------------------------------------------------------------------------------- 1 | package starwars.traversals.human 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.edgestep.EdgeStepToMany 4 | import starwars.models.Human 5 | import starwars.models.Sibling 6 | import starwars.models.Sibling.Companion.siblings 7 | 8 | internal val Human.Companion.twinSiblings 9 | get() = EdgeStepToMany(siblings) 10 | .filter(Sibling::twins) 11 | .map(Sibling::to) 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pm-dev/kotlin-gremlin-ogm/8197847f310bd9928ff1eccb7733b59830e3c12e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Jan 27 22:33:10 EST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip 7 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /out/ 3 | *.gpg 4 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/README.md: -------------------------------------------------------------------------------- 1 | # A library for easily deploying a GraphQL server backed by a graph database using kotlin-gremlin-ogm 2 | 3 | 4 | #### Basic Usage: 5 | 6 | Fetching relationships with coroutines in batched data loaders is as easy as 7 | 8 | fun getFriends(person: Person, env: DataFetchingEnvironment): CompletableFuture> = env.fetch { 9 | (friends from person).load() // returns List 10 | } 11 | 12 | Just build your GraphQLConfiguration with a `GraphMapperGQLContextBuilder` from this library 13 | 14 | 15 | An example of a working implementation can be seen in the [starwars example project](https://github.com/pm-dev/kotlin-gremlin-ogm/tree/master/example/src/main/kotlin/starwars) 16 | 17 | #### Installation: 18 | 19 | - Gradle 20 | 21 | compile 'com.github.pm-dev:kotlin-gremlin-graphql:0.21.0' 22 | 23 | - Maven 24 | 25 | 26 | com.github.pm-dev 27 | kotlin-gremlin-graphql 28 | 0.21.0 29 | 30 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/gradle.properties: -------------------------------------------------------------------------------- 1 | signing.keyId=16201A38 2 | signing.password= 3 | signing.secretKeyRingFile=secring.gpg 4 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/src/main/kotlin/graphql/schema/pagination/GraphQLPageCursor.kt: -------------------------------------------------------------------------------- 1 | package graphql.schema.pagination 2 | 3 | import com.google.gson.Gson 4 | import graphql.language.StringValue 5 | import graphql.schema.Coercing 6 | import graphql.schema.CoercingSerializeException 7 | import graphql.schema.GraphQLScalarType 8 | import org.slf4j.LoggerFactory 9 | import java.util.* 10 | 11 | class GraphQLPageCursor>( 12 | name: String, 13 | clazz: Class 14 | ) : GraphQLScalarType( 15 | name, 16 | "A string allowing clients to resume pagination", 17 | object : Coercing { 18 | 19 | private val logger = LoggerFactory.getLogger(GraphQLPageCursor::class.java) 20 | 21 | private val gson = Gson() 22 | 23 | override fun serialize(dataFetcherResult: Any?): String = when (dataFetcherResult) { 24 | is Page.Info<*> -> { 25 | val jsonString = gson.toJson(dataFetcherResult) 26 | logger.debug("Serializing $name as $jsonString") 27 | Base64.getEncoder().encodeToString(jsonString.toByteArray()) 28 | } 29 | else -> throw CoercingSerializeException("$name field cannot serialize '" + dataFetcherResult?.javaClass?.simpleName + "'.") 30 | } 31 | 32 | override fun parseValue(input: Any?): I = when (input) { 33 | is String -> { 34 | val jsonString = String(Base64.getDecoder().decode(input.toByteArray())) 35 | logger.debug("Parsing (deserializing) $name from $jsonString") 36 | gson.fromJson(jsonString, clazz) 37 | } 38 | else -> throw CoercingSerializeException("$name field cannot deserialize '" + input?.javaClass?.simpleName + "'.") 39 | } 40 | 41 | override fun parseLiteral(input: Any?): I = when (input) { 42 | is StringValue -> parseValue(input.value) 43 | else -> throw CoercingSerializeException("$name field cannot deserialize '" + input?.javaClass?.simpleName + "'.") 44 | } 45 | } 46 | ) 47 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/src/main/kotlin/graphql/schema/pagination/Identifiable.kt: -------------------------------------------------------------------------------- 1 | package graphql.schema.pagination 2 | 3 | 4 | typealias ID = Any 5 | 6 | interface Identifiable { 7 | val id: ID? 8 | } 9 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/src/main/kotlin/graphql/schema/pagination/Page.kt: -------------------------------------------------------------------------------- 1 | package graphql.schema.pagination 2 | 3 | import kotlin.math.max 4 | import kotlin.math.min 5 | 6 | 7 | interface Page { 8 | 9 | val previous: Info? 10 | 11 | val next: Info? 12 | 13 | val results: List 14 | 15 | data class Parts(val previous: T?, val page: List, val next: T?) 16 | 17 | interface Info { 18 | 19 | val order: Comparator 20 | 21 | val fromID: ID? 22 | 23 | val direction: Direction? 24 | 25 | val limit: Int? 26 | 27 | enum class Direction { 28 | FORWARD, 29 | BACKWARD 30 | } 31 | 32 | fun parts(results: Iterable): Page.Parts { 33 | val sorted = results.sortedWith(order) 34 | val fromIndex = when (fromID) { 35 | null -> 0 36 | else -> { 37 | val fromIndex = sorted.indexOfFirst { it.id == fromID } 38 | when (fromIndex) { 39 | -1 -> 0 40 | else -> fromIndex 41 | } 42 | } 43 | } 44 | val limit = limit 45 | val (startIndexInclusive, endIndexExclusive) = when (direction) { 46 | Direction.BACKWARD -> when (limit) { 47 | null -> 0 to fromIndex + 1 48 | else -> max(fromIndex + 1 - limit, 0) to fromIndex + 1 49 | } 50 | else -> when (limit) { 51 | null -> fromIndex to sorted.size 52 | else -> fromIndex to min(fromIndex + limit, sorted.size) 53 | } 54 | } 55 | val previousResult = when (startIndexInclusive) { 56 | 0 -> null 57 | else -> sorted[startIndexInclusive - 1] 58 | } 59 | val nextResult = when (endIndexExclusive) { 60 | sorted.size -> null 61 | else -> sorted[endIndexExclusive] 62 | } 63 | return Page.Parts( 64 | previousResult, 65 | sorted.subList(startIndexInclusive, endIndexExclusive), 66 | nextResult) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/src/main/kotlin/graphql/servlet/ogm/BatchedStepFetcher.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package graphql.servlet.ogm 4 | 5 | import graphql.schema.DataFetchingEnvironment 6 | import graphql.servlet.GraphQLContext 7 | import kotlinx.coroutines.future.await 8 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 9 | import org.apache.tinkerpop.gremlin.ogm.steps.bound.single.SingleBoundStep 10 | import org.dataloader.DataLoader 11 | 12 | data class BatchedStepFetcher(private val env: DataFetchingEnvironment) { 13 | 14 | val graphMapper get() = env.graphMapper 15 | 16 | suspend fun SingleBoundStep.ToOptional.load(): TO? = 17 | dataLoader(step).load(from).await() 18 | 19 | suspend fun SingleBoundStep.ToSingle.load(): TO = 20 | dataLoader(step).load(from).await() 21 | 22 | suspend fun SingleBoundStep.ToMany.load(): List = 23 | dataLoader>(step).load(from).await() 24 | 25 | private fun dataLoader(step: Step<*, *>): DataLoader { 26 | val context = env.getContext() as? GraphMapperGQLContext 27 | ?: throw IncorrectGraphQLContext(env.getContext()) 28 | val dataLoaderRegistry = context.dataLoaderRegistry.get() 29 | val dataLoaderKey = context.dataLoaderKeyLookup[step] ?: throw DataLoaderKeyNotFound(step) 30 | return dataLoaderRegistry.getDataLoader(dataLoaderKey) 31 | } 32 | 33 | private class DataLoaderKeyNotFound(step: Step<*, *>) : 34 | RuntimeException("GraphMapperGQLContext.dataLoaderKey does not have a key for step $step") 35 | } 36 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/src/main/kotlin/graphql/servlet/ogm/GraphMapperGQLContext.kt: -------------------------------------------------------------------------------- 1 | package graphql.servlet.ogm 2 | 3 | import graphql.servlet.GraphQLContext 4 | import org.apache.tinkerpop.gremlin.ogm.GraphMapper 5 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 6 | import javax.security.auth.Subject 7 | import javax.servlet.http.HttpServletRequest 8 | import javax.servlet.http.HttpServletResponse 9 | import javax.websocket.Session 10 | import javax.websocket.server.HandshakeRequest 11 | 12 | open class GraphMapperGQLContext( 13 | internal val graphMapper: GraphMapper, 14 | internal val dataLoaderKeyLookup: Map, String>, 15 | httpServletRequest: HttpServletRequest? = null, 16 | httpServletResponse: HttpServletResponse? = null, 17 | session: Session? = null, 18 | handshakeRequest: HandshakeRequest? = null, 19 | subject: Subject? = null 20 | ) : GraphQLContext(httpServletRequest, httpServletResponse, session, handshakeRequest, subject) 21 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/src/main/kotlin/graphql/servlet/ogm/PermissionResult.kt: -------------------------------------------------------------------------------- 1 | package graphql.servlet.ogm 2 | 3 | sealed class PermissionResult { 4 | object Allow : PermissionResult() 5 | data class Deny(val reason: String) : PermissionResult() 6 | } 7 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/src/main/kotlin/graphql/servlet/ogm/batchloaders/StepToManyBatchLoader.kt: -------------------------------------------------------------------------------- 1 | package graphql.servlet.ogm.batchloaders 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.future.future 5 | import org.apache.tinkerpop.gremlin.ogm.GraphMapper 6 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 7 | import org.dataloader.BatchLoader 8 | import org.slf4j.LoggerFactory 9 | 10 | 11 | data class StepToManyBatchLoader( 12 | private val step: Step.ToMany, 13 | private val graphMapper: GraphMapper 14 | ) : BatchLoader> { 15 | 16 | override fun load(froms: List) = GlobalScope.future { 17 | logger.debug("Loading to-many step $step from $froms") 18 | val result = graphMapper.traverse(step from froms) 19 | froms.map { from -> result[from] } 20 | } 21 | 22 | companion object { 23 | private val logger = LoggerFactory.getLogger(StepToSingleBatchLoader::class.java) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/src/main/kotlin/graphql/servlet/ogm/batchloaders/StepToOptionalBatchLoader.kt: -------------------------------------------------------------------------------- 1 | package graphql.servlet.ogm.batchloaders 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.future.future 5 | import org.apache.tinkerpop.gremlin.ogm.GraphMapper 6 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 7 | import org.dataloader.BatchLoader 8 | import org.slf4j.LoggerFactory 9 | 10 | 11 | data class StepToOptionalBatchLoader( 12 | private val step: Step.ToOptional, 13 | private val graphMapper: GraphMapper 14 | ) : BatchLoader { 15 | 16 | override fun load(froms: List) = GlobalScope.future { 17 | logger.debug("Loading to-optional step $step from $froms") 18 | val result = graphMapper.traverse(step from froms) 19 | froms.map { from -> result[from] } 20 | } 21 | 22 | companion object { 23 | private val logger = LoggerFactory.getLogger(StepToOptionalBatchLoader::class.java) 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /kotlin-gremlin-graphql/src/main/kotlin/graphql/servlet/ogm/batchloaders/StepToSingleBatchLoader.kt: -------------------------------------------------------------------------------- 1 | package graphql.servlet.ogm.batchloaders 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.future.future 5 | import org.apache.tinkerpop.gremlin.ogm.GraphMapper 6 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 7 | import org.dataloader.BatchLoader 8 | import org.slf4j.LoggerFactory 9 | 10 | 11 | data class StepToSingleBatchLoader( 12 | private val step: Step.ToSingle, 13 | private val graphMapper: GraphMapper 14 | ) : BatchLoader { 15 | 16 | override fun load(froms: List) = GlobalScope.future { 17 | logger.debug("Loading to-many path $step from $froms") 18 | val result = graphMapper.traverse(step from froms) 19 | froms.map { from -> result[from] } 20 | } 21 | 22 | companion object { 23 | private val logger = LoggerFactory.getLogger(StepToSingleBatchLoader::class.java) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /out/ 3 | *.gpg 4 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/gradle.properties: -------------------------------------------------------------------------------- 1 | signing.keyId=16201A38 2 | signing.password= 3 | signing.secretKeyRingFile=secring.gpg 4 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/Element.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | 6 | /** 7 | * Marks a class as capable of being mapped to an element of the graph (edge or vertex). 8 | * Clients should register classes marked with @Element with a GraphMapper. 9 | */ 10 | @Retention(value = AnnotationRetention.RUNTIME) 11 | @Target(allowedTargets = [(AnnotationTarget.CLASS)]) 12 | @Inherited 13 | annotation class Element( 14 | 15 | /** 16 | * The label of the vertex or edge when stored to the graph. We require clients to specify an explicit 17 | * label (instead of using the class name) to guard against refactoring situations 18 | * where the class name is changed and this annotation is not updated to keep the original label. 19 | */ 20 | val label: String) 21 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/FromVertex.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * An annotation used to mark the property and constructor parameter for an edge's out-vertex. 7 | * For relationships that are symmetric, it doesn't matter which property/param is marked as the to-vertex and 8 | * and which one is marked as the out-vertex. 9 | */ 10 | @Retention(value = AnnotationRetention.RUNTIME) 11 | @Target(allowedTargets = [AnnotationTarget.VALUE_PARAMETER]) 12 | @Inherited 13 | annotation class FromVertex 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/ID.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Represents the primary unique constraint used to reference the id of a graph element (Vertex or Edge). 7 | * This annotation must be present on a nullable constructor parameter and property for classes annotated with @Element 8 | * or @Edge. We require annotation on @Element classes because the library needs to 9 | * do indexed look-ups to prevent duplicate or conflicting edges. 10 | * The property annotated with @ID may not be set manually, as it is generated automatically by the graph 11 | * implementation. 12 | */ 13 | @Retention(value = AnnotationRetention.RUNTIME) 14 | @Target(allowedTargets = [AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER]) 15 | @Inherited 16 | annotation class ID 17 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/Mapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.mappers.PropertyBiMapper 4 | import org.apache.tinkerpop.gremlin.ogm.mappers.SerializedProperty 5 | import java.lang.annotation.Inherited 6 | import kotlin.reflect.KClass 7 | 8 | /** 9 | * Annotation to override the default mapping behavior for a vertex property. Using @Mapper allows 10 | * clients to specify a custom vertexMapper for a vertex property annotated with @param:Property. 11 | */ 12 | @Retention(value = AnnotationRetention.RUNTIME) 13 | @Target(AnnotationTarget.VALUE_PARAMETER) 14 | @Inherited 15 | annotation class Mapper( 16 | 17 | /** 18 | * The class to use for mapping a property to/from the graph. Must have a constructor with 19 | * no arguments or where V arguments are optional. 20 | */ 21 | val kClass: KClass> 22 | ) 23 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/Property.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Registers a parameter or property as a value that should be mapped to/from a property of a vertex. 7 | * The class must be annotated with @Element and registered as a vertex or edge with a GraphMapper. 8 | * Without this annotation, a property will be transient (not persisted to the graph) and must be nullable. 9 | * 10 | * If the annotated property is declared separate from its constructor param, its constructor param must 11 | * have the same name, or also have this annotation with the same key parameter. 12 | */ 13 | @Retention(value = AnnotationRetention.RUNTIME) 14 | @Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER) 15 | @Inherited 16 | annotation class Property( 17 | 18 | /** 19 | * The key of the property as stored to the graph. We require clients to specify an explicit 20 | * key (instead of using the property name) to guard against refactoring situations 21 | * where the property name is changed and this annotation is not updated to keep the original key. 22 | */ 23 | val key: String 24 | ) 25 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/ToVertex.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * An annotation used to mark the constructor parameter for an edge's to-vertex 7 | */ 8 | @Retention(value = AnnotationRetention.RUNTIME) 9 | @Target(allowedTargets = [AnnotationTarget.VALUE_PARAMETER]) 10 | @Inherited 11 | annotation class ToVertex 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/defaults/DefaultBoolean.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations.defaults 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Annotation that, when used with the @Property annotation, specifies a value to use when 7 | * the graph does not have a value for the given parameter. This can be useful as an alternative to a migration 8 | * when adding a non-nullable property to an Element. 9 | */ 10 | @Retention(value = AnnotationRetention.RUNTIME) 11 | @Target(AnnotationTarget.VALUE_PARAMETER) 12 | @Inherited 13 | annotation class DefaultBoolean( 14 | 15 | /** 16 | * The value to use when deserializing this property from the graph, but the graph has no value for this 17 | * property. 18 | */ 19 | val value: Boolean) 20 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/defaults/DefaultByte.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations.defaults 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Annotation that, when used with the @Property annotation, specifies a value to use when 7 | * the graph does not have a value for the given parameter. This can be useful as an alternative to a migration 8 | * when adding a non-nullable property to an Element. 9 | */ 10 | @Retention(value = AnnotationRetention.RUNTIME) 11 | @Target(AnnotationTarget.VALUE_PARAMETER) 12 | @Inherited 13 | annotation class DefaultByte( 14 | 15 | /** 16 | * The value to use when deserializing this property from the graph, but the graph has no value for this 17 | * property. 18 | */ 19 | val value: Byte) 20 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/defaults/DefaultChar.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations.defaults 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Annotation that, when used with the @Property annotation, specifies a value to use when 7 | * the graph does not have a value for the given parameter. This can be useful as an alternative to a migration 8 | * when adding a non-nullable property to an Element. 9 | */ 10 | @Retention(value = AnnotationRetention.RUNTIME) 11 | @Target(AnnotationTarget.VALUE_PARAMETER) 12 | @Inherited 13 | annotation class DefaultChar( 14 | 15 | /** 16 | * The value to use when deserializing this property from the graph, but the graph has no value for this 17 | * property. 18 | */ 19 | val value: Char) 20 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/defaults/DefaultDouble.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations.defaults 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Annotation that, when used with the @Property annotation, specifies a value to use when 7 | * the graph does not have a value for the given parameter. This can be useful as an alternative to a migration 8 | * when adding a non-nullable property to an Element. 9 | */ 10 | @Retention(value = AnnotationRetention.RUNTIME) 11 | @Target(AnnotationTarget.VALUE_PARAMETER) 12 | @Inherited 13 | annotation class DefaultDouble( 14 | 15 | /** 16 | * The value to use when deserializing this property from the graph, but the graph has no value for this 17 | * property. 18 | */ 19 | val value: Double) 20 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/defaults/DefaultFloat.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations.defaults 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Annotation that, when used with the @Property annotation, specifies a value to use when 7 | * the graph does not have a value for the given parameter. This can be useful as an alternative to a migration 8 | * when adding a non-nullable property to an Element. 9 | */ 10 | @Retention(value = AnnotationRetention.RUNTIME) 11 | @Target(AnnotationTarget.VALUE_PARAMETER) 12 | @Inherited 13 | annotation class DefaultFloat( 14 | 15 | /** 16 | * The value to use when deserializing this property from the graph, but the graph has no value for this 17 | * property. 18 | */ 19 | val value: Float) 20 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/defaults/DefaultInt.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations.defaults 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Annotation that, when used with the @Property annotation, specifies a value to use when 7 | * the graph does not have a value for the given parameter. This can be useful as an alternative to a migration 8 | * when adding a non-nullable property to an Element. 9 | */ 10 | @Retention(value = AnnotationRetention.RUNTIME) 11 | @Target(AnnotationTarget.VALUE_PARAMETER) 12 | @Inherited 13 | annotation class DefaultInt( 14 | 15 | /** 16 | * The value to use when deserializing this property from the graph, but the graph has no value for this 17 | * property. 18 | */ 19 | val value: Int) 20 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/defaults/DefaultLong.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations.defaults 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Annotation that, when used with the @Property annotation, specifies a value to use when 7 | * the graph does not have a value for the given parameter. This can be useful as an alternative to a migration 8 | * when adding a non-nullable property to an Element. 9 | */ 10 | @Retention(value = AnnotationRetention.RUNTIME) 11 | @Target(AnnotationTarget.VALUE_PARAMETER) 12 | @Inherited 13 | annotation class DefaultLong( 14 | 15 | /** 16 | * The value to use when deserializing this property from the graph, but the graph has no value for this 17 | * property. 18 | */ 19 | val value: Long) 20 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/defaults/DefaultShort.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations.defaults 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Annotation that, when used with the @Property annotation, specifies a value to use when 7 | * the graph does not have a value for the given parameter. This can be useful as an alternative to a migration 8 | * when adding a non-nullable property to an Element. 9 | */ 10 | @Retention(value = AnnotationRetention.RUNTIME) 11 | @Target(AnnotationTarget.VALUE_PARAMETER) 12 | @Inherited 13 | annotation class DefaultShort( 14 | 15 | /** 16 | * The value to use when deserializing this property from the graph, but the graph has no value for this 17 | * property. 18 | */ 19 | val value: Short) 20 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/defaults/DefaultString.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations.defaults 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Annotation that, when used with the @Property annotation, specifies a value to use when 7 | * the graph does not have a value for the given parameter. This can be useful as an alternative to a migration 8 | * when adding a non-nullable property to an Element. 9 | */ 10 | @Retention(value = AnnotationRetention.RUNTIME) 11 | @Target(AnnotationTarget.VALUE_PARAMETER) 12 | @Inherited 13 | annotation class DefaultString( 14 | 15 | /** 16 | * The value to use when deserializing this property from the graph, but the graph has no value for this 17 | * property. 18 | */ 19 | val value: String) 20 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/annotations/defaults/DefaultValue.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.annotations.defaults 2 | 3 | import java.lang.annotation.Inherited 4 | import java.util.function.Supplier 5 | import kotlin.reflect.KClass 6 | 7 | /** 8 | * Annotation that, when used with the @Property annotation, specifies a value to use when 9 | * the graph does not have a value for the given parameter. This can be useful as an alternative to a migration 10 | * when adding a non-nullable property to an Element. 11 | */ 12 | @Retention(value = AnnotationRetention.RUNTIME) 13 | @Target(AnnotationTarget.VALUE_PARAMETER) 14 | @Inherited 15 | annotation class DefaultValue( 16 | 17 | /** 18 | * A class that can supply the default value for the parameter this annotation is placed on. 19 | * The supplied value must never be null. 20 | */ 21 | val supplier: KClass>) 22 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/caching/GraphMapperCache.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.caching 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.GraphEdge 4 | import org.apache.tinkerpop.gremlin.ogm.GraphVertex 5 | import org.apache.tinkerpop.gremlin.ogm.elements.Edge 6 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 7 | 8 | /** 9 | * The cache used by a CachedGraphMapper. The implementation should be thread-safe if a 10 | * CachedGraphMapper using this cache is shared across threads. 11 | */ 12 | interface GraphMapperCache { 13 | 14 | /** 15 | * Requests that the vertex be explicitly cached using its serialized graph form as the key. 16 | */ 17 | fun put(serialized: GraphVertex, deserialized: V) 18 | 19 | /** 20 | * Requests a vertex from the cache for a given serialized vertex. If there is a cache miss, implementors 21 | * should call CachedGraphMapper#load 22 | */ 23 | fun get(serialized: GraphVertex): V 24 | 25 | /** 26 | * Requests that the edge be explicitly cached using its serialized graph form as the key. 27 | */ 28 | fun > put(serialized: GraphEdge, deserialized: E) 29 | 30 | /** 31 | * Requests an edge from the cache for a given serialized edge. If there is a cache miss, implementors 32 | * should call CachedGraphMapper#load 33 | */ 34 | fun > get(serialized: GraphEdge): E 35 | } 36 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/elements/BasicEdge.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.elements 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 4 | 5 | 6 | internal class BasicEdge( 7 | override val from: FROM, 8 | override val to: TO, 9 | val spec: EdgeSpec 10 | ) : Edge 11 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/elements/Edge.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.elements 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 4 | 5 | 6 | /** 7 | * An [Edge] represents a [FROM] and [TO] vertex that are connected through a [Relationship]. 8 | * [Edge] may be implemented for clients wishing to add properties to edges. In this case, the 9 | * [Edge] subclass must be registered with a GraphMapper alongside its [Relationship] 10 | */ 11 | interface Edge { 12 | 13 | /** 14 | * The out-vertex for the edge. Final subclasses must annotate the parameter 15 | * that sets this with @FromVertex. 16 | */ 17 | val from: FROM 18 | 19 | /** 20 | * The to-vertex for the edge. Final subclasses must annotate the parameter 21 | * that sets this with @ToVertex. 22 | */ 23 | val to: TO 24 | } 25 | 26 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/elements/Element.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.elements 2 | 3 | typealias Element = Any 4 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/elements/Vertex.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.elements 2 | 3 | typealias Vertex = Any 4 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/AnnotationException.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | 4 | internal open class AnnotationException( 5 | description: String 6 | ) : ClientException( 7 | description = "Annotation Exception - There was a problem with how you annotated your objects:\n$description" 8 | ) 9 | 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ClassInheritanceMismatch.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class ClassInheritanceMismatch( 6 | lowerBound: KClass<*>, 7 | upperBound: KClass<*> 8 | ) : AnnotationException( 9 | description = "$lowerBound must be a subclass of $upperBound" 10 | ) 11 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ClassifierUnavailable.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | internal class ClassifierUnavailable : AnnotationException( 4 | description = "Classifier cannot be null" 5 | ) 6 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ClientException.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | internal open class ClientException( 4 | description: String 5 | ) : RuntimeException("ClientException: $description") 6 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ConflictingAnnotations.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | import kotlin.reflect.KParameter 5 | 6 | internal class ConflictingAnnotations( 7 | kClass: KClass<*>, 8 | param: KParameter 9 | ) : AnnotationException( 10 | "Param '${param.name}'can be annotated with only one of @ID, @Property, @ToVertex, FromVertex. Class: $kClass" 11 | ) 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ConflictingEdge.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | internal class ConflictingEdge( 4 | from: Any, 5 | to: Any, 6 | relationshipName: String 7 | ) : ClientException( 8 | description = "Unable to create edge $relationshipName from $from to $to. " + 9 | "This means at least one of the following conditions was true:\n" + 10 | "\t1) The spec is a 'FromOne' and the 'to' object already has an edge with this name." + 11 | "\t2) The spec is a 'ToOne' and the 'from' object already has an edge with this name." + 12 | "\t3) The spec name is being used on a different spec." 13 | ) 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/DuplicateFromVertex.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class DuplicateFromVertex( 6 | kClass: KClass<*> 7 | ) : AnnotationException( 8 | "Only one param may be annotated with @FromVertex. Class: $kClass." 9 | ) 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/DuplicateIDProperty.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class DuplicateIDProperty( 6 | kClass: KClass<*> 7 | ) : AnnotationException( 8 | "At most one member property of $kClass may be annotated with @ID" 9 | ) 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/DuplicatePropertyName.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class DuplicatePropertyName(kClass: KClass<*>) : AnnotationException( 6 | "Cannot have multiple @Property annotations on member properties that have the same key. Class: $kClass" 7 | ) 8 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/DuplicateRelationshipName.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | internal class DuplicateRelationshipName(name: String) : AnnotationException( 4 | "Names of relationships must be unique. Duplicated name: $name" 5 | ) 6 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/DuplicateToVertex.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class DuplicateToVertex( 6 | kClass: KClass<*> 7 | ) : AnnotationException( 8 | "Only one param may be annotated with @ToVertex. Class: $kClass." 9 | ) 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ElementAnnotationMissing.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class ElementAnnotationMissing( 6 | kClass: KClass<*> 7 | ) : AnnotationException( 8 | "Could not find @Element annotation on class that was registered as an Edge or Vertex. Class: $kClass" 9 | ) 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/EmptyListTokenIsReserved.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | internal class EmptyListTokenIsReserved( 4 | emptyListToken: String 5 | ) : ClientException( 6 | description = "Encountered a property value that is equal to a value that is reserved by the " + 7 | "library: $emptyListToken. This should never happen since the empty list token is a UUID." 8 | ) 9 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/EmptyMapTokenIsReserved.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | internal class EmptyMapTokenIsReserved( 4 | emptyMapToken: String 5 | ) : ClientException( 6 | description = "Encountered a property value that is equal to a value that is reserved by the " + 7 | "library: $emptyMapToken. This should never happen since the empty map token is a UUID." 8 | ) 9 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/FromVertexParameterMissing.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | 6 | internal class FromVertexParameterMissing( 7 | kClass: KClass<*> 8 | ) : AnnotationException( 9 | "Classes registered as an edge must have a parameter annotated with @FromVertex. Class: $kClass" 10 | ) 11 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/IDNotFound.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | internal class IDNotFound( 4 | obj: Any, 5 | id: Any? 6 | ) : ClientException( 7 | "Attempting to save element '$obj' whose @ID property is non-null: '$id', " + 8 | "however that id was not found in the graph." 9 | ) 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/IDParameterRequired.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class IDParameterRequired( 6 | kClass: KClass<*> 7 | ) : AnnotationException( 8 | "Graph elements must have exactly one parameter annotated with @ID. Class: $kClass" 9 | ) 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/IDPropertyRequired.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class IDPropertyRequired( 6 | kClass: KClass<*> 7 | ) : AnnotationException( 8 | "Could not find ID property of graph element. This library will first look for member properties " + 9 | "annotated with @ID, then it will look for a member property with the same name as the parameter " + 10 | "annotated with @ID. Class: $kClass" 11 | ) 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/IncompatibleIterable.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.reflection.PropertyDescription 4 | 5 | internal class IncompatibleIterable( 6 | propertyDescription: PropertyDescription<*, *> 7 | ) : ClientException( 8 | description = "Unsupported iterable of type ${propertyDescription.property}. " + 9 | "This library knows how to serialize/deserialize the iterable based on the iterable's type " + 10 | "parameter's first upper bound constraint, which must be a class. For example when " + 11 | "deserializing List, T's first upper bound constraint must be a class. That class determines how to" + 12 | "map the iterable's elements to/from the graph. Alternatively, you can define a custom vertexMapper for this iterable property." 13 | ) 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/IncompatibleMap.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.reflection.PropertyDescription 4 | 5 | internal class IncompatibleMap( 6 | propertyDescription: PropertyDescription<*, *> 7 | ) : ClientException( 8 | description = "Unsupported map of type ${propertyDescription.kClass}. " + 9 | "This library knows how to serialize/deserialize the map based on the map's type " + 10 | "parameters' first upper bound constraint, which must be a class. For example when " + 11 | "deserializing Map, K and V must both have their first upper bound constraint be a class." + 12 | "That class determines how to map the keys/values to and from the graph." + 13 | "Alternatively, you can define a custom vertexMapper for this map property." 14 | ) 15 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/IterableNotSupported.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class IterableNotSupported( 6 | iterableClass: KClass<*> 7 | ) : AnnotationException( 8 | description = "List and Set are the only Iterable property types supported. " + 9 | "Attempting to serialize $iterableClass." 10 | ) 11 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/MapperUnsupported.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KParameter 4 | 5 | internal class MapperUnsupported( 6 | param: KParameter 7 | ) : ClientException( 8 | description = "The @Mapper annotation is not supported on the field annotated with @ID, @ToVertex or @FromVertex" + 9 | " For @ID, the type of the field must match the id type of your Gremlin implementation. " + 10 | "Parameter name: ${param.name}" 11 | ) 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/MissingEdge.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import org.apache.tinkerpop.gremlin.structure.Vertex 4 | 5 | internal class MissingEdge( 6 | fromVertex: Vertex, 7 | relationshipName: String 8 | ) : ClientException( 9 | description = "Unable to find required (aka ToSingle) edge " + 10 | "from vertex ${fromVertex.label()} with id ${fromVertex.id()} to $relationshipName." 11 | ) 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/NonNullableID.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | import kotlin.reflect.KParameter 5 | 6 | internal class NonNullableID( 7 | kClass: KClass<*>, 8 | param: KParameter 9 | ) : AnnotationException( 10 | "Param '${param.name}' annotated with @ID must be nullable for when the " + 11 | "object has not yet been persisted. " + 12 | "Clients may choose to have another param used for identification that is non-null. " + 13 | "Class $kClass" 14 | ) 15 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/NonNullableNonOptionalParameter.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KParameter 4 | 5 | internal class NonNullableNonOptionalParameter( 6 | parameter: KParameter 7 | ) : AnnotationException( 8 | "Non-nullable, non-optional, primary constructor parameter must be annotated " + 9 | "with @Property or @ID (for elements) or @ToVertex (for edges) or @FromVertex (for edges). " + 10 | "Parameter: ${parameter.name}." 11 | ) 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/NullablePropertyWithDefault.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KParameter 4 | 5 | internal class NullablePropertyWithDefault( 6 | parameter: KParameter 7 | ) : AnnotationException( 8 | "Parameter with @DefaultVaule annotation marked nullable. It's pretty odd to have an @Property that allows null " + 9 | "when the Element is created and serialized to the graph, but is given a default value when there's " + 10 | "no value for the @Property when deserializing from the graph. Most likely this parameter should be " + 11 | "non-nullable." + 12 | "Parameter: ${parameter.name}." 13 | ) 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/NullableVertexParam.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | import kotlin.reflect.KParameter 5 | 6 | internal class NullableVertexParam( 7 | kClass: KClass<*>, 8 | param: KParameter 9 | ) : AnnotationException( 10 | "Param '${param.name}' annotated with @ToVertex or @FromVertex must be non-nullable. Class $kClass" 11 | ) 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ObjectDescriptionMissing.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class ObjectDescriptionMissing( 6 | kClass: KClass<*> 7 | ) : ClientException( 8 | description = "You need to tell the library how to serialize/deserialize an object to the graph. " + 9 | "Where you create your GraphMapper instance, please register $kClass as a nested " + 10 | "object (if it is annotated) or register a scalarMapper for $kClass." 11 | ) 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ObjectNotSaved.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | 4 | internal class ObjectNotSaved( 5 | obj: Any 6 | ) : ClientException( 7 | description = "Object $obj must be saved before creating edges with it. This library doesn't implicitly save vertices " + 8 | "when saving edges. This is done to prevent saving objects multiple times and creating duplicates." 9 | ) 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ParameterPropertyNotFound.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.annotations.Property 4 | import kotlin.reflect.KClass 5 | import kotlin.reflect.KParameter 6 | 7 | 8 | internal class ParameterPropertyNotFound( 9 | kClass: KClass<*>, 10 | annotation: Property, 11 | param: KParameter 12 | ) : AnnotationException( 13 | "Could not find a member property for parameter $param. This library will first look " + 14 | "for a @Property annotation on a member property with key: ${annotation.key}, " + 15 | "then it will look for a member property with the same name as the parameter. Class $kClass" 16 | ) 17 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/PrimaryConstructorMissing.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class PrimaryConstructorMissing( 6 | kClass: KClass<*> 7 | ) : AnnotationException( 8 | description = "Encountered a vertex object without a primary constructor $kClass" 9 | ) 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/PropertyMapperMissing.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class PropertyMapperMissing( 6 | kClass: KClass<*> 7 | ) : ClientException( 8 | description = "You need to tell the library how to serialize/deserialize property of class: $kClass. " + 9 | "Where you create your GraphMapper instance, please register a PropertyBiMapper as a scalar mapper." 10 | ) 11 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/PropertyUnsupported.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KParameter 4 | 5 | internal class PropertyUnsupported( 6 | parameter: KParameter 7 | ) : ClientException( 8 | description = "Element parameter '${parameter.name}' must be a KClass type" 9 | ) 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ReservedIDName.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.mappers.EdgeDeserializer.Companion.idTag 4 | import kotlin.reflect.KClass 5 | 6 | internal class ReservedIDName( 7 | kClass: KClass<*> 8 | ) : AnnotationException( 9 | description = "@Property.name may not equal $idTag. " + 10 | "This name is reserved by the library. Class: $kClass" 11 | ) 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ReservedNestedPropertyDelimiter.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.extensions.nestedPropertyDelimiter 4 | import kotlin.reflect.KClass 5 | 6 | internal class ReservedNestedPropertyDelimiter( 7 | kClass: KClass<*>, 8 | key: String 9 | ) : AnnotationException( 10 | description = "@Property.name may not contain '$nestedPropertyDelimiter' as this string is used " + 11 | "as a delimiter for nested properties. '$key' on class $kClass" 12 | ) 13 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ReservedNumberKey.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class ReservedNumberKey( 6 | kClass: KClass<*>, 7 | key: String 8 | ) : AnnotationException( 9 | description = "@Property.name may not be a number, since number indexes are used " + 10 | "as index keys for collection properties. '$key' on class $kClass" 11 | ) 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/SuperclassAnnotationException.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | import kotlin.reflect.KProperty1 5 | 6 | internal class SuperclassAnnotationException( 7 | baseClass: KClass<*>, 8 | superClass: KClass<*>, 9 | property: KProperty1<*, *>, 10 | annotations: List<*> 11 | ) : AnnotationException("All annotations must happen in the base class. Baseclass: $baseClass. " + 12 | "Superclass $superClass. Property: $property. Annotations: $annotations" 13 | ) 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/ToVertexParameterMissing.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class ToVertexParameterMissing( 6 | kClass: KClass<*> 7 | ) : AnnotationException( 8 | "Classes registered as an edge must have a parameter annotated with @ToVertex. Class: $kClass" 9 | ) 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/UnregisteredClass.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class UnregisteredClass( 6 | kClass: KClass<*> 7 | ) : ClientException( 8 | description = "Attempting to serialize object to the graph whose class: $kClass " + 9 | "has not been registered with your GraphMapper instance." 10 | ) 11 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/exceptions/UnregisteredLabel.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.exceptions 2 | 3 | internal class UnregisteredLabel( 4 | label: String 5 | ) : ClientException( 6 | description = "Attempting to deserialize an element with label $label, but no " + 7 | "@Element class or Relationship has been registered with GraphMapper with the label $label." 8 | ) 9 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/extensions/Maps.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.extensions 2 | 3 | internal fun Map.filterNulls(): Map = 4 | entries 5 | .asSequence() 6 | .filter { it.key != null && it.value != null } 7 | .map { it.key!! to it.value!! } 8 | .associate { it } 9 | 10 | internal fun Map.filterNullValues(): Map = 11 | filter { it.value != null } 12 | .mapValues { it.value!! } 13 | 14 | 15 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/extensions/MutableMaps.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.extensions 2 | 3 | internal fun MutableMap.mapValuesInPlace(transform: (Map.Entry) -> V) = 4 | entries.forEach { entry -> 5 | entry.setValue(transform(entry)) 6 | } 7 | 8 | internal fun Iterator>.toMultiMap(requireKeys: Collection = emptyList()): Map> { 9 | val remainingRequiredKeys = requireKeys.toMutableSet() 10 | val map = mutableMapOf>() 11 | forEach { 12 | remainingRequiredKeys.remove(it.first) 13 | map[it.first]?.add(it.second) ?: kotlin.run { 14 | map[it.first] = mutableListOf(it.second) 15 | } 16 | } 17 | remainingRequiredKeys.forEach { 18 | map[it] = mutableListOf() 19 | } 20 | return map 21 | } 22 | 23 | 24 | internal fun Iterator>.toOptionalMap(requireKeys: Collection = emptyList()): Map { 25 | val remainingRequiredKeys = requireKeys.toMutableSet() 26 | val finalMap = mutableMapOf() 27 | forEach { pair -> 28 | if (finalMap[pair.first] != null) { 29 | throw IllegalArgumentException("Two pairs have the same key which shouldn't be possible in an optional map") 30 | } 31 | remainingRequiredKeys.remove(pair.first) 32 | finalMap[pair.first] = pair.second 33 | } 34 | remainingRequiredKeys.forEach { 35 | finalMap[it] = null 36 | } 37 | return finalMap 38 | } 39 | 40 | internal fun Iterator>.toSingleMap(requireKeys: Collection = emptyList()): Map { 41 | val remainingRequiredKeys = requireKeys.toMutableSet() 42 | val finalMap = mutableMapOf() 43 | forEach { pair -> 44 | if (finalMap[pair.first] != null) { 45 | throw IllegalArgumentException("Two pairs have the same key which shouldn't be possible in a single map") 46 | } 47 | remainingRequiredKeys.remove(pair.first) 48 | finalMap[pair.first] = pair.second 49 | } 50 | if (remainingRequiredKeys.isNotEmpty()) { 51 | throw NoSuchElementException("Sequence is missing pair for keys $remainingRequiredKeys") 52 | } 53 | return finalMap 54 | } 55 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/BiMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers 2 | 3 | /** 4 | * A function that is also able to map elements to its co-domain to its domain 5 | */ 6 | interface BiMapper { 7 | 8 | /** 9 | * Maps an object of type X to an object of type Y. It is expected that the value 10 | * returned by this function could be passed to [inverseMap] and an object equal to 11 | * 'from' would be returned. 12 | */ 13 | fun forwardMap(from: X): Y 14 | 15 | /** 16 | * Maps an object of type Y to an object of type X. It is expected that the value 17 | * returned by this function could be passed to [forwardMap] and an object equal to 18 | * 'from' would be returned. 19 | */ 20 | fun inverseMap(from: Y): X 21 | } 22 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/EnumBiMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers 2 | 3 | /** 4 | * The convenience interface to use when creating the mapping between enums and their graph serialized string form. 5 | * A convenient place to implement this is on the enum's companion object. 6 | */ 7 | interface EnumBiMapper> : PropertyBiMapper 8 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/Mapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers 2 | 3 | /** 4 | * An interface marking an object that can be invoked to map an object X from 5 | * its domain to an object Y from its co-domain 6 | */ 7 | interface Mapper { 8 | 9 | /** 10 | * A function that can be invoked on a [Mapper] to apply the mapping. 11 | */ 12 | operator fun invoke(from: X): Y 13 | } 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/ObjectDeserializer.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.reflection.GraphDescription 4 | import org.apache.tinkerpop.gremlin.ogm.reflection.ObjectDescription 5 | import org.apache.tinkerpop.gremlin.ogm.reflection.PropertyDescription 6 | import kotlin.reflect.KParameter 7 | 8 | internal class ObjectDeserializer( 9 | private val graphDescription: GraphDescription, 10 | private val objectDescription: ObjectDescription, 11 | private val idProperty: Pair>? = null, 12 | private val fromVertexParameter: Pair? = null, 13 | private val toVertexParameter: Pair? = null 14 | ) : Mapper, T> { 15 | 16 | override fun invoke(from: Map<*, *>): T { 17 | val constructorParameters = mutableMapOf() 18 | constructorParameters.putAll(objectDescription.properties.entries.associate { keyValue -> 19 | val propertyKey = keyValue.key 20 | val propertyDescription = keyValue.value 21 | val serializedPropertyValue = from[propertyKey] 22 | val deserializer = PropertyDeserializer(graphDescription, propertyDescription) 23 | val deserializedPropertyValue = deserializer(serializedPropertyValue) 24 | propertyDescription.parameter to deserializedPropertyValue 25 | }) 26 | constructorParameters.putAll(objectDescription.nullConstructorParameters.associate { it to null }) 27 | if (idProperty != null) { 28 | val id = from[idProperty.first] 29 | constructorParameters[idProperty.second.parameter] = id 30 | } 31 | if (fromVertexParameter != null) { 32 | val fromVertex = from[fromVertexParameter.first] 33 | constructorParameters[fromVertexParameter.second] = fromVertex 34 | } 35 | if (toVertexParameter != null) { 36 | val toVertex = from[toVertexParameter.first] 37 | constructorParameters[toVertexParameter.second] = toVertex 38 | } 39 | return objectDescription.constructor.callBy(constructorParameters) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/ObjectSerializer.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.reflection.GraphDescription 4 | import org.apache.tinkerpop.gremlin.ogm.reflection.ObjectDescription 5 | 6 | internal class ObjectSerializer( 7 | private val graphDescription: GraphDescription, 8 | private val objectDescription: ObjectDescription 9 | ) : Mapper> { 10 | 11 | override fun invoke(from: T): Map = 12 | objectDescription.properties.mapValues { 13 | val propertyDescription = it.value 14 | val unserializedPropertyValue = propertyDescription.property.get(from) 15 | val serializer = PropertySerializer(graphDescription, propertyDescription) 16 | serializer(unserializedPropertyValue) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/PropertyBiMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers 2 | 3 | import kotlin.reflect.KClass 4 | 5 | /** 6 | * A type that maps an object to/from its serialized graph representation. 7 | */ 8 | interface PropertyBiMapper : BiMapper { 9 | val serializedClass: KClass 10 | } 11 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/SerializedProperty.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers 2 | 3 | /** 4 | * This typealias is used to represent any types that are accepted by the client's graph implementation 5 | * as a vertex property. 6 | * 7 | * In addition to the types accepted by the underlying graph implementation, a SerializedProperty 8 | * may also be one of these supported collection types: 9 | * 10 | * Map 11 | * List 12 | */ 13 | typealias SerializedProperty = Any 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/VertexDeserializer.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.GraphVertex 4 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 5 | import org.apache.tinkerpop.gremlin.ogm.extensions.getProperties 6 | import org.apache.tinkerpop.gremlin.ogm.mappers.EdgeDeserializer.Companion.idTag 7 | import org.apache.tinkerpop.gremlin.ogm.reflection.GraphDescription 8 | 9 | internal class VertexDeserializer( 10 | private val graphDescription: GraphDescription 11 | ) { 12 | 13 | operator fun invoke(from: GraphVertex): T { 14 | val vertexDescription = graphDescription.getVertexDescription(from.label()) 15 | val objectDeserializer = ObjectDeserializer(graphDescription, vertexDescription, Pair(idTag, vertexDescription.id)) 16 | return objectDeserializer(from.getProperties() + Pair(idTag, from.id())) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/VertexSerializer.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.exceptions.IDNotFound 5 | import org.apache.tinkerpop.gremlin.ogm.extensions.setProperties 6 | import org.apache.tinkerpop.gremlin.ogm.reflection.GraphDescription 7 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource 8 | 9 | internal class VertexSerializer( 10 | private val graphDescription: GraphDescription, 11 | private val g: GraphTraversalSource 12 | ) { 13 | 14 | operator fun invoke(from: T): org.apache.tinkerpop.gremlin.structure.Vertex { 15 | val vertexDescription = graphDescription.getVertexDescription(from::class) 16 | val id = vertexDescription.id.property.get(from) 17 | val traversal = when (id) { 18 | null -> g.addV(vertexDescription.label) 19 | else -> g.V(id) 20 | } 21 | val objectSerializer = ObjectSerializer(graphDescription, vertexDescription) 22 | return traversal.map { vertex -> 23 | val serializedProperties = objectSerializer(from) 24 | vertex.get().setProperties(serializedProperties) 25 | }.toList().singleOrNull() ?: throw IDNotFound(from, id) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/BigDecimalPropertyMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.mappers.PropertyBiMapper 4 | import java.math.BigDecimal 5 | 6 | internal object BigDecimalPropertyMapper : PropertyBiMapper { 7 | 8 | override fun forwardMap(from: BigDecimal) = from.toString() 9 | 10 | override fun inverseMap(from: String): BigDecimal = BigDecimal(from) 11 | 12 | override val serializedClass get() = String::class 13 | } 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/InstantPropertyMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.mappers.PropertyBiMapper 4 | import java.time.Instant 5 | 6 | internal object InstantPropertyMapper : PropertyBiMapper { 7 | 8 | override fun forwardMap(from: Instant) = "${from.epochSecond}:${from.nano}" 9 | 10 | override fun inverseMap(from: String): Instant { 11 | val parts = from.split(':') 12 | val epochSecond = parts[0].toLong() 13 | val nano = parts[1].toLong() 14 | return Instant.ofEpochSecond(epochSecond, nano) 15 | } 16 | 17 | override val serializedClass get() = String::class 18 | } 19 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/URLPropertyMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.mappers.PropertyBiMapper 4 | import java.net.URL 5 | 6 | internal object URLPropertyMapper : PropertyBiMapper { 7 | 8 | override fun forwardMap(from: URL) = from.toString() 9 | 10 | override fun inverseMap(from: String): URL = URL(from) 11 | 12 | override val serializedClass get() = String::class 13 | } 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/UUIDPropertyMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.mappers.PropertyBiMapper 4 | import java.util.* 5 | 6 | internal object UUIDPropertyMapper : PropertyBiMapper { 7 | 8 | override fun forwardMap(from: UUID) = from.toString() 9 | 10 | override fun inverseMap(from: String): UUID = UUID.fromString(from) 11 | 12 | override val serializedClass get() = String::class 13 | } 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/identity/BooleanPropertyMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar.identity 2 | 3 | internal object BooleanPropertyMapper : IdentityPropertyMapper { 4 | override val serializedClass get() = Boolean::class 5 | } 6 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/identity/BytePropertyMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar.identity 2 | 3 | internal object BytePropertyMapper : IdentityPropertyMapper { 4 | override val serializedClass get() = Byte::class 5 | } 6 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/identity/DoublePropertyManager.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar.identity 2 | 3 | internal object DoublePropertyManager : IdentityPropertyMapper { 4 | override val serializedClass get() = Double::class 5 | } 6 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/identity/FloatPropertyMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar.identity 2 | 3 | internal object FloatPropertyMapper : IdentityPropertyMapper { 4 | override val serializedClass get() = Float::class 5 | } 6 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/identity/IdentityPropertyMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar.identity 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.mappers.PropertyBiMapper 4 | 5 | internal interface IdentityPropertyMapper : PropertyBiMapper { 6 | override fun forwardMap(from: TYPE): TYPE = from 7 | override fun inverseMap(from: TYPE): TYPE = from 8 | } 9 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/identity/IntegerPropertyMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar.identity 2 | 3 | internal object IntegerPropertyMapper : IdentityPropertyMapper { 4 | override val serializedClass get() = Int::class 5 | } 6 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/identity/LongPropertyMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar.identity 2 | 3 | internal object LongPropertyMapper : IdentityPropertyMapper { 4 | override val serializedClass get() = Long::class 5 | } 6 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/mappers/scalar/identity/StringPropertyMapper.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.mappers.scalar.identity 2 | 3 | internal object StringPropertyMapper : IdentityPropertyMapper { 4 | override val serializedClass get() = String::class 5 | } 6 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/reflection/GraphDescription.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.reflection 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Edge 4 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 5 | import org.apache.tinkerpop.gremlin.ogm.mappers.PropertyBiMapper 6 | import org.apache.tinkerpop.gremlin.ogm.mappers.SerializedProperty 7 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 8 | import kotlin.reflect.KClass 9 | 10 | /** 11 | * An interface that describes a graph's vertices, edges, object properties, scalar properties and is 12 | * used by a GraphMapper to serialize/deserialize these instances to/from the graph, respectively. 13 | */ 14 | interface GraphDescription { 15 | 16 | /** 17 | * Vertices 18 | */ 19 | 20 | val vertexClasses: Set> 21 | 22 | fun getVertexDescription(vertexClass: KClass): VertexDescription 23 | 24 | val vertexLabels: Set 25 | 26 | fun getVertexDescription(vertexLabel: String): VertexDescription 27 | 28 | /** 29 | * Edges 30 | */ 31 | 32 | val edgeClasses: Set>> 33 | 34 | fun > getEdgeDescription(edgeClass: KClass): EdgeDescription 35 | 36 | val edgeLabels: Set 37 | 38 | fun getEdgeSpec(edgeLabel: String): EdgeSpec 39 | 40 | fun > getEdgeDescription(edgeLabel: String): EdgeDescription? 41 | 42 | /** 43 | * Nested Objects 44 | */ 45 | 46 | val objectPropertyClasses: Set> 47 | 48 | fun getObjectPropertyDescription(objectPropertyClass: KClass): ObjectDescription 49 | 50 | /** 51 | * Scalars 52 | */ 53 | 54 | val scalarPropertyClasses: Set> 55 | 56 | fun getScalarPropertyMapper(scalarClass: KClass): PropertyBiMapper 57 | } 58 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/reflection/PropertyDescription.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.reflection 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.exceptions.PropertyUnsupported 4 | import org.apache.tinkerpop.gremlin.ogm.mappers.PropertyBiMapper 5 | import org.apache.tinkerpop.gremlin.ogm.mappers.SerializedProperty 6 | import java.util.function.Supplier 7 | import kotlin.reflect.KClass 8 | import kotlin.reflect.KParameter 9 | import kotlin.reflect.KProperty1 10 | 11 | /** 12 | * Describes a property on an object representing a vertex to a graph. 13 | */ 14 | data class PropertyDescription( 15 | 16 | /** 17 | * The primary constructor parameter on RECEIVER that sets this property. 18 | */ 19 | val parameter: KParameter, 20 | 21 | /** 22 | * The property which can be found on RECEIVER. 23 | */ 24 | val property: KProperty1, 25 | 26 | /** 27 | * The a custom serializer to map this property to/from its SerializedProperty form 28 | */ 29 | val mapper: PropertyBiMapper?, 30 | 31 | /** 32 | * An object that supplies a value to be used as the default for when the graph does not have a 33 | * value for this property. 34 | */ 35 | val default: Supplier? 36 | ) { 37 | /** 38 | * The concrete KClass type for this property if this property represents such a type. If this property description 39 | * does not have a KClass, it must have a non-null vertexMapper. 40 | */ 41 | val kClass = property.returnType.classifier as? KClass<*> ?: throw PropertyUnsupported(parameter) 42 | } 43 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/Dedup.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps 2 | 3 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal 4 | 5 | /** 6 | * A step that filters out duplicate objects at the current location to the g. 7 | */ 8 | internal class Dedup : Step.ToSingle { 9 | 10 | override fun invoke(from: StepTraverser): GraphTraversal<*, TYPE> = 11 | from.traversal.dedup() 12 | 13 | override fun hashCode() = Dedup::class.hashCode() 14 | 15 | override fun equals(other: Any?) = other is Dedup<*> 16 | } 17 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/Filter.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps 2 | 3 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal 4 | 5 | /** 6 | * A step that doesn't advance to a new type but simply removes a subset of objects from the traversal based on 7 | * a predicate. 8 | */ 9 | internal data class Filter(val predicate: (TYPE) -> Boolean) : Step.ToOptional { 10 | override fun invoke(from: StepTraverser): GraphTraversal<*, TYPE> = 11 | from.traversal.filter { traverser -> predicate(traverser.get()) } 12 | } 13 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/FilterMap.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps 2 | 3 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal 4 | 5 | /** 6 | * A step that maps the current object to a new object, or removes the current object if the map function returns null. 7 | */ 8 | internal data class FilterMap(private val map: (FROM) -> TO?) : Step.ToOptional { 9 | override fun invoke(from: StepTraverser): GraphTraversal<*, TO> = 10 | from.traversal 11 | .map { map(it.get()) } 12 | .filter { it.get() != null } 13 | .map { it.get()!! } 14 | } 15 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/FlatMap.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps 2 | 3 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal 4 | 5 | /** 6 | * A step that maps the current object to zero or more new objects. 7 | */ 8 | internal data class FlatMap(private val map: (FROM) -> Iterable) : Step.ToMany { 9 | override fun invoke(from: StepTraverser): GraphTraversal<*, TO> = 10 | from.traversal.flatMap { map(it.get()).iterator() } 11 | } 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/Map.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps 2 | 3 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal 4 | 5 | /** 6 | * A step that maps the current object to a new object. 7 | */ 8 | internal data class Map(private val map: (FROM) -> TO) : Step.ToSingle { 9 | override fun invoke(from: StepTraverser): GraphTraversal<*, TO> = 10 | from.traversal.map { map(it.get()) } 11 | } 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/Slice.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps 2 | 3 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal 4 | 5 | /** 6 | * A path that filters out objects outside a given range. 7 | */ 8 | internal data class Slice(private val range: LongRange) : Step.ToOptional { 9 | override fun invoke(from: StepTraverser): GraphTraversal<*, TYPE> = 10 | from.traversal.range(range.start, range.endInclusive + 1) 11 | } 12 | 13 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/Sort.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps 2 | 3 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal 4 | 5 | /** 6 | * A path that sorts the traversal's objects by a given comparators. The 2nd comparator is the secondary ordering, 7 | * 3rd comparator is the tertiary ordering, etc. 8 | */ 9 | internal data class Sort(private val comparators: Collection>) : Step.ToSingle { 10 | 11 | constructor(comparator: Comparator) : this(listOf(comparator)) 12 | 13 | override fun invoke(from: StepTraverser): GraphTraversal<*, TYPE> = 14 | comparators.fold(initial = from.traversal.order(), operation = GraphTraversal::by) 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/StepTraverser.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.GraphMapper 4 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal 5 | 6 | 7 | /** 8 | * This object is passed to the 'invoke' function of objects implementing the Step interface. 9 | */ 10 | class StepTraverser( 11 | 12 | /** 13 | * The g that an implementor of a Path should use to advance the g. 14 | */ 15 | val traversal: GraphTraversal<*, FROM>, 16 | 17 | /** 18 | * The graph mapper that is managing the g. 19 | * Use this mapper to serialize/deserialize objects to/from their graph form 20 | */ 21 | val graphMapper: GraphMapper 22 | ) 23 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/BoundStep.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | import org.apache.tinkerpop.gremlin.ogm.steps.StepTraverser 5 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal 6 | 7 | 8 | /** 9 | * A [BoundStep] composes a [Step] with multiple [FROM] objects to start the step's traversal with 10 | */ 11 | interface BoundStep { 12 | 13 | /** 14 | * The objects to start traversing with. 15 | */ 16 | val froms: List 17 | 18 | /** 19 | * The step that performs the traversal 20 | */ 21 | val step: Step 22 | 23 | /** 24 | * A [BoundStep] that results to a optional or non-optional (aka single) object 25 | * for each [froms] object that the path is traversed with. 26 | */ 27 | interface ToOne : BoundStep { 28 | 29 | override val step: Step.ToOne 30 | } 31 | 32 | /** 33 | * A [BoundStep] that results to 0 or more objects for each [froms] object that 34 | * the traversed path starts with. 35 | */ 36 | interface ToMany : BoundStep { 37 | 38 | override val step: Step.ToMany 39 | } 40 | 41 | /** 42 | * A [BoundStep] that results to a non-optional object 43 | * for each [froms] object that the path is traversed with. 44 | */ 45 | interface ToSingle : ToOne { 46 | 47 | override val step: Step.ToSingle 48 | } 49 | 50 | /** 51 | * A [BoundStep] that results to an optional object 52 | * for each [froms] object that the path is traversed with. 53 | */ 54 | interface ToOptional : ToOne { 55 | 56 | override val step: Step.ToOptional 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/BoundStepToMany.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | 5 | /** 6 | * A [BoundStep] that results to 0 or more objects for each [froms] object that 7 | * the traversed path starts with. 8 | */ 9 | internal data class BoundStepToMany( 10 | override val froms: List, 11 | override val step: Step.ToMany 12 | ) : BoundStep.ToMany 13 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/BoundStepToOptional.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | 5 | /** 6 | * A [BoundStep] that results to 0 or 1 object for each [froms] object that 7 | * the traversed path starts with. 8 | */ 9 | internal data class BoundStepToOptional( 10 | override val froms: List, 11 | override val step: Step.ToOptional 12 | ) : BoundStep.ToOptional 13 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/BoundStepToSingle.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | 5 | /** 6 | * A [BoundStep] that results to exactly 1 object for each [froms] object that 7 | * the traversed path starts with. 8 | */ 9 | internal data class BoundStepToSingle( 10 | override val froms: List, 11 | override val step: Step.ToSingle 12 | ) : BoundStep.ToSingle 13 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/edgespec/BoundEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.BasicEdge 4 | import org.apache.tinkerpop.gremlin.ogm.elements.Edge 5 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 6 | import org.apache.tinkerpop.gremlin.ogm.steps.bound.BoundStep 7 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 8 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 9 | 10 | interface BoundEdgeSpec : BoundStep { 11 | 12 | override val step: EdgeSpec 13 | 14 | infix fun to(to: TO): List> = froms.map { BasicEdge(from = it, to = to, spec = step) } 15 | 16 | /** 17 | * A [BoundEdgeSpec] where the spec is a [Relationship.ToOne] 18 | */ 19 | interface ToOne : BoundEdgeSpec, BoundStep.ToOne { 20 | 21 | override val step: EdgeSpec.ToOne 22 | } 23 | 24 | /** 25 | * A [BoundEdgeSpec] where the spec is a [Relationship.ToMany] 26 | */ 27 | interface ToMany : BoundEdgeSpec, BoundStep.ToMany { 28 | 29 | override val step: EdgeSpec.ToMany 30 | } 31 | 32 | /** 33 | * A [BoundEdgeSpec] where the spec is a [Relationship.ToSingle] 34 | */ 35 | interface ToSingle : ToOne, BoundStep.ToSingle { 36 | 37 | override val step: EdgeSpec.ToSingle 38 | } 39 | 40 | /** 41 | * A [BoundEdgeSpec] where the spec is a [Relationship.ToOptional] 42 | */ 43 | interface ToOptional : ToOne, BoundStep.ToOptional { 44 | 45 | override val step: EdgeSpec.ToOptional 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/edgespec/BoundEdgeSpecToMany.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 5 | 6 | /** 7 | * A [BoundEdgeSpec] that results to 0 or more objects for each [froms] object that 8 | * the traversed path starts with. 9 | */ 10 | internal data class BoundEdgeSpecToMany( 11 | override val froms: List, 12 | override val step: EdgeSpec.ToMany 13 | ) : BoundEdgeSpec.ToMany 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/edgespec/BoundEdgeSpecToOptional.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 5 | 6 | /** 7 | * A [BoundEdgeSpec] that results to 0 or 1 object for each [froms] object that 8 | * the traversed path starts with. 9 | */ 10 | internal data class BoundEdgeSpecToOptional( 11 | override val froms: List, 12 | override val step: EdgeSpec.ToOptional 13 | ) : BoundEdgeSpec.ToOptional 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/edgespec/BoundEdgeSpecToSingle.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 5 | 6 | /** 7 | * A [BoundEdgeSpec] that results to exactly 1 object for each to object that 8 | * the traversed path starts with. 9 | */ 10 | internal data class BoundEdgeSpecToSingle( 11 | override val froms: List, 12 | override val step: EdgeSpec.ToSingle 13 | ) : BoundEdgeSpec.ToSingle 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/single/SingleBoundStep.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.single 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | import org.apache.tinkerpop.gremlin.ogm.steps.bound.BoundStep 5 | 6 | /** 7 | * A [SingleBoundStep] composes a [Step] and a single [FROM] object to start the traversal from. 8 | */ 9 | interface SingleBoundStep : BoundStep { 10 | 11 | /** 12 | * The object to start the [SingleBoundStep]'s g with 13 | */ 14 | val from: FROM 15 | 16 | override val froms: List get() = listOf(from) 17 | 18 | override val step: Step 19 | 20 | /** 21 | * A [SingleBoundStep] that traverses to a optional or non-optional (aka single) object. 22 | */ 23 | interface ToOne : SingleBoundStep, BoundStep.ToOne { 24 | 25 | override val step: Step.ToOne 26 | } 27 | 28 | /** 29 | * A [SingleBoundStep] that results to 0 or more 'TO' objects. 30 | */ 31 | interface ToMany : SingleBoundStep, BoundStep.ToMany { 32 | 33 | override val step: Step.ToMany 34 | } 35 | 36 | /** 37 | * A [SingleBoundStep] that results to exactly 1 'TO' objects. 38 | */ 39 | interface ToSingle : ToOne, BoundStep.ToSingle { 40 | 41 | override val step: Step.ToSingle 42 | } 43 | 44 | /** 45 | * A [SingleBoundStep] that results to 0 or 1 'TO' objects. 46 | */ 47 | interface ToOptional : ToOne, BoundStep.ToOptional { 48 | 49 | override val step: Step.ToOptional 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/single/SingleBoundStepToMany.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.single 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | 5 | /** 6 | * A [SingleBoundStep] that results to 0 or more 'TO' objects. 7 | */ 8 | internal data class SingleBoundStepToMany( 9 | override val from: FROM, 10 | override val step: Step.ToMany 11 | ) : SingleBoundStep.ToMany 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/single/SingleBoundStepToOptional.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.single 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | 5 | /** 6 | * A [SingleBoundStep] that results to 0 or 1 'TO' objects. 7 | */ 8 | internal data class SingleBoundStepToOptional( 9 | override val from: FROM, 10 | override val step: Step.ToOptional 11 | ) : SingleBoundStep.ToOptional 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/single/SingleBoundStepToSingle.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.single 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | 5 | /** 6 | * A [SingleBoundStep] that results to exactly 1 'TO' object. 7 | */ 8 | internal data class SingleBoundStepToSingle( 9 | override val from: FROM, 10 | override val step: Step.ToSingle 11 | ) : SingleBoundStep.ToSingle 12 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/single/edgespec/SingleBoundEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.single.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.BasicEdge 4 | import org.apache.tinkerpop.gremlin.ogm.elements.Edge 5 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 6 | import org.apache.tinkerpop.gremlin.ogm.steps.bound.single.SingleBoundStep 7 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 8 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 9 | 10 | /** 11 | * A [SingleBoundStep] whose step is a [EdgeSpec] 12 | */ 13 | interface SingleBoundEdgeSpec : SingleBoundStep { 14 | 15 | override val step: EdgeSpec 16 | 17 | infix fun to(to: TO): Edge = BasicEdge(from = from, to = to, spec = step) 18 | 19 | interface ToOne : SingleBoundEdgeSpec, SingleBoundStep.ToOne { 20 | 21 | override val step: EdgeSpec.ToOne 22 | } 23 | 24 | /** 25 | * A [SingleBoundEdgeSpec] whose spec is a [Relationship.ToSingle] 26 | */ 27 | interface ToSingle : ToOne, SingleBoundStep.ToSingle { 28 | 29 | override val step: EdgeSpec.ToSingle 30 | } 31 | 32 | /** 33 | * A [SingleBoundEdgeSpec] whose spec is a [Relationship.ToOptional] 34 | */ 35 | interface ToOptional : ToOne, SingleBoundStep.ToOptional { 36 | 37 | override val step: EdgeSpec.ToOptional 38 | } 39 | 40 | /** 41 | * A [SingleBoundEdgeSpec] whose spec is a [Relationship.ToMany] 42 | */ 43 | interface ToMany : SingleBoundEdgeSpec, SingleBoundStep.ToMany { 44 | 45 | override val step: EdgeSpec.ToMany 46 | 47 | infix fun to(tos: Collection): List> = tos.map { BasicEdge(from = from, to = it, spec = step) } 48 | 49 | fun to(vararg tos: TO): List> = tos.map { BasicEdge(from = from, to = it, spec = step) } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/single/edgespec/SingleBoundEdgeSpecToMany.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.single.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 6 | 7 | /** 8 | * A [SingleBoundEdgeSpec] whose spec is a [Relationship.ToMany] 9 | */ 10 | internal data class SingleBoundEdgeSpecToMany( 11 | override val from: FROM, 12 | override val step: EdgeSpec.ToMany 13 | ) : SingleBoundEdgeSpec.ToMany 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/single/edgespec/SingleBoundEdgeSpecToOptional.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.single.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 6 | 7 | /** 8 | * A [SingleBoundEdgeSpec] whose spec is a [Relationship.ToOptional] 9 | */ 10 | internal data class SingleBoundEdgeSpecToOptional( 11 | override val from: FROM, 12 | override val step: EdgeSpec.ToOptional 13 | ) : SingleBoundEdgeSpec.ToOptional 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/bound/single/edgespec/SingleBoundEdgeSpecToSingle.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.bound.single.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 6 | 7 | /** 8 | * A [SingleBoundEdgeSpec] whose spec is a [Relationship.ToSingle] 9 | */ 10 | internal data class SingleBoundEdgeSpecToSingle( 11 | override val from: FROM, 12 | override val step: EdgeSpec.ToSingle 13 | ) : SingleBoundEdgeSpec.ToSingle 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/edgestep/EdgeStepToMany.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.edgestep 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Edge 4 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 5 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 6 | 7 | data class EdgeStepToMany>( 8 | override val edgeSpec: EdgeSpec.ToMany 9 | ) : EdgeStep.ToMany 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/edgestep/EdgeStepToOptional.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.edgestep 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Edge 4 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 5 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 6 | 7 | data class EdgeStepToOptional>( 8 | override val edgeSpec: EdgeSpec.ToOptional 9 | ) : EdgeStep.ToOptional 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/edgestep/EdgeStepToSingle.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.edgestep 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Edge 4 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 5 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.EdgeSpec 6 | 7 | data class EdgeStepToSingle>( 8 | override val edgeSpec: EdgeSpec.ToSingle 9 | ) : EdgeStep.ToSingle 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/Path.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | import org.apache.tinkerpop.gremlin.ogm.steps.StepTraverser 5 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal 6 | 7 | /** 8 | * A path represents a [GraphTraversal] transformation of 2 [Step]s 9 | */ 10 | internal interface Path : Step { 11 | 12 | /** 13 | * The first step. 14 | */ 15 | val first: Step 16 | 17 | /** 18 | * The second step. 19 | */ 20 | val last: Step 21 | 22 | override fun invoke(from: StepTraverser): GraphTraversal<*, TO> = 23 | last.invoke(StepTraverser(first(from), from.graphMapper)) 24 | 25 | /** 26 | * A path that is either a [ToOptional] or [ToSingle] 27 | */ 28 | interface ToOne : Path, Step.ToOne { 29 | 30 | override val first: Step.ToOne 31 | override val last: Step.ToOne 32 | } 33 | 34 | /** 35 | * A path that does not change the number of objects that would result from the current g 36 | */ 37 | interface ToSingle : ToOne, Step.ToSingle { 38 | 39 | override val first: Step.ToSingle 40 | override val last: Step.ToSingle 41 | } 42 | 43 | /** 44 | * A path that may reduce the number of objects that would result from the current g 45 | */ 46 | interface ToOptional : ToOne, Step.ToOptional 47 | 48 | /** 49 | * A path that may increase the number of objects that would result from the 50 | */ 51 | interface ToMany : Path, Step.ToMany 52 | } 53 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/PathToMany.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | 5 | internal data class PathToMany( 6 | override val first: Step, 7 | override val last: Step 8 | ) : Path.ToMany 9 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/PathToOptional.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | 5 | internal data class PathToOptional( 6 | override val first: Step.ToOne, 7 | override val last: Step.ToOne 8 | ) : Path.ToOptional 9 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/PathToSingle.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.Step 4 | 5 | internal data class PathToSingle( 6 | override val first: Step.ToSingle, 7 | override val last: Step.ToSingle 8 | ) : Path.ToSingle 9 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/relationship/ManyToManyRelationshipPath.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path.relationship 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | internal data class ManyToManyRelationshipPath( 7 | override val first: Relationship, 8 | override val last: Relationship 9 | ) : RelationshipPath.ManyToMany 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/relationship/ManyToOptionalRelationshipPath.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path.relationship 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | internal data class ManyToOptionalRelationshipPath( 7 | override val first: Relationship.ToOne, 8 | override val last: Relationship.ToOne 9 | ) : RelationshipPath.ManyToOptional 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/relationship/ManyToSingleRelationshipPath.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path.relationship 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | internal data class ManyToSingleRelationshipPath( 7 | override val first: Relationship.ToSingle, 8 | override val last: Relationship.ToSingle 9 | ) : RelationshipPath.ManyToSingle 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/relationship/OptionalToManyRelationshipPath.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path.relationship 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | internal data class OptionalToManyRelationshipPath( 7 | override val first: Relationship.FromOne, 8 | override val last: Relationship.FromOne 9 | ) : RelationshipPath.OptionalToMany 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/relationship/OptionalToOptionalRelationshipPath.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path.relationship 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | internal data class OptionalToOptionalRelationshipPath( 7 | override val first: Relationship.OneToOne, 8 | override val last: Relationship.OneToOne 9 | ) : RelationshipPath.OptionalToOptional 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/relationship/OptionalToSingleRelationshipPath.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path.relationship 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | internal data class OptionalToSingleRelationshipPath( 7 | override val first: Relationship.OneToSingle, 8 | override val last: Relationship.OneToSingle 9 | ) : RelationshipPath.OptionalToSingle 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/relationship/SingleToManyRelationshipPath.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path.relationship 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | internal data class SingleToManyRelationshipPath( 7 | override val first: Relationship.FromSingle, 8 | override val last: Relationship.FromSingle 9 | ) : RelationshipPath.SingleToMany 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/relationship/SingleToOptionalRelationshipPath.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path.relationship 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | internal data class SingleToOptionalRelationshipPath( 7 | override val first: Relationship.SingleToOne, 8 | override val last: Relationship.SingleToOne 9 | ) : RelationshipPath.SingleToOptional 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/path/relationship/SingleToSingleRelationshipPath.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.path.relationship 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | internal data class SingleToSingleRelationshipPath( 7 | override val first: Relationship.SingleToSingle, 8 | override val last: Relationship.SingleToSingle 9 | ) : RelationshipPath.SingleToSingle 10 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/ManyToManyAsymmetricEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | /** 7 | * Creates a [Relationship] that is uni-directional. When traversed from a 'FROM' object, 8 | * there will be 0 or more 'TO' objects. When the [inverse] is traversed from a 'TO' object, 9 | * there will be 0 or more 'FROM' objects. 10 | */ 11 | data class ManyToManyAsymmetricEdgeSpec( 12 | override val name: String, 13 | override val direction: EdgeSpec.Direction = EdgeSpec.Direction.FORWARD 14 | ) : EdgeSpec.ManyToMany.Asymmetric 15 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/ManyToManySymmetricEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | /** 7 | * Creates a [Relationship] that is bi-directional. When traversed from a 'FROM' object, 8 | * there will be 0 or more 'TO' objects. When the [inverse] is traversed from a 'TO' object, 9 | * there will be 0 or more 'FROM' objects. 10 | */ 11 | data class ManyToManySymmetricEdgeSpec( 12 | override val name: String 13 | ) : EdgeSpec.ManyToMany.Symmetric 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/ManyToOptionalEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | 5 | /** 6 | * We restrict creating ManyToOne relationships by clients to prevent creation of a 7 | * ManyToOne spec that is equivalent to meaning to an already defined OneToMany 8 | * spec, but using a different name. To get a ManyToOne spec, define it 9 | * as its OneToMany equivalent then get its inverse. 10 | */ 11 | internal data class ManyToOptionalEdgeSpec( 12 | override val name: String 13 | ) : EdgeSpec.ManyToOptional 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/ManyToSingleEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | 5 | /** 6 | * We restrict creating ManyToOne relationships by clients to prevent creation of a 7 | * ManyToOne spec that is equivalent to meaning to an already defined OneToMany 8 | * spec, but using a different name. To get a ManyToOne spec, define it 9 | * as its OneToMany equivalent then get its inverse. 10 | */ 11 | internal data class ManyToSingleEdgeSpec( 12 | override val name: String 13 | ) : EdgeSpec.ManyToSingle 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/OptionalToManyEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | 7 | /** 8 | * Creates a [Relationship] that is uni-directional. When traversed from a 'FROM' object, 9 | * there will be 0 or more 'TO' objects. When the [inverse] is traversed from a 'TO' object, 10 | * there will be 0 or 1 'FROM' object. 11 | */ 12 | data class OptionalToManyEdgeSpec( 13 | override val name: String 14 | ) : EdgeSpec.OptionalToMany 15 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/OptionalToOptionalAsymmetricEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | /** 7 | * Creates a [Relationship] that is uni-directional. When traversed from a 'FROM' object, 8 | * there will be 0 or 1 'TO' objects. When the [inverse] is traversed from a 'TO' object, 9 | * there will be 0 or 1 'FROM' objects. 10 | */ 11 | data class OptionalToOptionalAsymmetricEdgeSpec( 12 | override val name: String, 13 | override val direction: EdgeSpec.Direction = EdgeSpec.Direction.FORWARD 14 | ) : EdgeSpec.OptionalToOptional.Asymmetric 15 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/OptionalToOptionalSymmetricEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | /** 7 | * Creates a [Relationship] that is bi-directional. When traversed from a 'FROM' object, 8 | * there will be 0 or 1 'TO' objects. When the [inverse] is traversed from a 'TO' object, 9 | * there will be 0 or 1 'FROM' objects. 10 | */ 11 | data class OptionalToOptionalSymmetricEdgeSpec( 12 | override val name: String 13 | ) : EdgeSpec.OptionalToOptional.Symmetric 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/OptionalToSingleEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | /** 7 | * Creates a [Relationship] that is uni-directional. When traversed from a 'FROM' object, 8 | * there will be exactly 1 'TO' objects. When the [inverse] is traversed from a 'TO' object, 9 | * there will be 0 or 1 'FROM' objects. 10 | */ 11 | data class OptionalToSingleEdgeSpec( 12 | override val name: String, 13 | override val direction: EdgeSpec.Direction = EdgeSpec.Direction.FORWARD 14 | ) : EdgeSpec.OptionalToSingle 15 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/SingleToManyEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | /** 7 | * Creates a [Relationship] that is uni-directional. When traversed from a 'FROM' object, 8 | * there will be 0 or more 'TO' objects. When the [inverse] is traversed from a 'TO' object, 9 | * there will be exactly 1 'FROM' object. 10 | */ 11 | data class SingleToManyEdgeSpec( 12 | override val name: String 13 | ) : EdgeSpec.SingleToMany 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/SingleToOptionalEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | /** 7 | * Creates a [Relationship] that is uni-directional. When traversed from a 'FROM' object, 8 | * there will be 0 or 1 'TO' objects. When the [inverse] is traversed from a 'TO' object, 9 | * there will be exactly 1 'FROM' object. 10 | */ 11 | data class SingleToOptionalEdgeSpec( 12 | override val name: String, 13 | override val direction: EdgeSpec.Direction = EdgeSpec.Direction.FORWARD 14 | ) : EdgeSpec.SingleToOptional 15 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/SingleToSingleAsymmetricEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | /** 7 | * Creates a [Relationship] that is uni-directional. When traversed from a 'FROM' object, 8 | * there will be exactly 1 'TO' object. When the [inverse] is traversed from a 'TO' object, 9 | * there will be exactly 1 'FROM' object. 10 | */ 11 | data class SingleToSingleAsymmetricEdgeSpec( 12 | override val name: String, 13 | override val direction: EdgeSpec.Direction = EdgeSpec.Direction.FORWARD 14 | ) : EdgeSpec.SingleToSingle.Asymmetric 15 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/steps/relationship/edgespec/SingleToSingleSymmetricEdgeSpec.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.elements.Vertex 4 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.Relationship 5 | 6 | /** 7 | * Creates a [Relationship] that is bi-directional. When traversed from a 'FROM' object, 8 | * there will be exactly 1 'TO' object. When the [inverse] is traversed from a 'TO' object, 9 | * there will be exactly 1 'FROM' object. 10 | */ 11 | data class SingleToSingleSymmetricEdgeSpec( 12 | override val name: String 13 | ) : EdgeSpec.SingleToSingle.Symmetric 14 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/test/README.md: -------------------------------------------------------------------------------- 1 | # Test Results for kotlin-gremlin-ogm 2 | 3 | [![Code Coverage](https://codecov.io/gh/pm-dev/kotlin-gremlin-ogm/branch/code-cov/graph/badge.svg)](https://codecov.io/gh/pm-dev/kotlin-gremlin-ogm/branch/code-cov) 4 | 5 | [![Sunburst](https://codecov.io/gh/pm-dev/kotlin-gremlin-ogm/branch/master/graphs/sunburst.svg)](https://codecov.io/gh/pm-dev/kotlin-gremlin-ogm/branch/code-cov) 6 | 7 | [![Grid](https://codecov.io/gh/pm-dev/kotlin-gremlin-ogm/branch/master/graphs/tree.svg)](https://codecov.io/gh/pm-dev/kotlin-gremlin-ogm/branch/code-cov) 8 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/test/kotlin/org/apache/tinkerpop/gremlin/ogm/extensions/MutableMapsTest.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.extensions 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.Test 5 | 6 | internal class MutableMapsTest { 7 | 8 | @Test 9 | fun `test mapValuesInPlace`() { 10 | val mutableMap = mutableMapOf( 11 | "first" to 1, 12 | "second" to 2, 13 | "third" to 3) 14 | mutableMap.mapValuesInPlace { it.value * 10 } 15 | assertThat(mutableMap).hasSize(3) 16 | assertThat(mutableMap["first"]).isEqualTo(10) 17 | assertThat(mutableMap["second"]).isEqualTo(20) 18 | assertThat(mutableMap["third"]).isEqualTo(30) 19 | } 20 | 21 | @Test 22 | fun `test toMultiMap`() { 23 | val multiMap = listOf(1 to "a", 1 to "b", 2 to "c", 2 to "d", 3 to "e", 4 to "f", 5 to "g").iterator().toMultiMap() 24 | assertThat(multiMap).hasSize(5) 25 | assertThat(multiMap[1]).hasSize(2) 26 | assertThat(multiMap[2]).hasSize(2) 27 | assertThat(multiMap[3]).hasSize(1) 28 | assertThat(multiMap[4]).hasSize(1) 29 | assertThat(multiMap[5]).hasSize(1) 30 | } 31 | 32 | @Test 33 | fun `test toOptionalMap`() { 34 | val optionalMap = listOf(1 to "a", 2 to null).iterator().toOptionalMap() 35 | assertThat(optionalMap).hasSize(2) 36 | assertThat(optionalMap[1]).isEqualTo("a") 37 | assertThat(optionalMap[2]).isNull() 38 | } 39 | 40 | @Test 41 | fun `test toSingleMap`() { 42 | val multiMap = listOf(1 to "a", 2 to "b").iterator().toSingleMap() 43 | assertThat(multiMap).hasSize(2) 44 | assertThat(multiMap[1]).isEqualTo("a") 45 | assertThat(multiMap[2]).isEqualTo("b") 46 | } 47 | 48 | @Test(expected = NoSuchElementException::class) 49 | fun `test toSingleMap no element`() { 50 | listOf(1 to "a").iterator().toSingleMap(requireKeys = setOf(1, 2)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/test/kotlin/util/StaticBiMapper.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import com.google.common.collect.BiMap 4 | import org.apache.tinkerpop.gremlin.ogm.mappers.BiMapper 5 | 6 | 7 | internal interface StaticBiMapper : BiMapper { 8 | 9 | val map: BiMap 10 | 11 | override fun forwardMap(from: X): Y = map[from] ?: throw MissingFromDomain(from) 12 | override fun inverseMap(from: Y): X = map.inverse()[from] ?: throw MissingFromCodomain(from) 13 | 14 | private class MissingFromDomain(obj: Any) : RuntimeException("Value missing from domain of static bi-map: $obj") 15 | private class MissingFromCodomain(obj: Any) : RuntimeException("Value missing from co-domain of static bi-map: $obj") 16 | } 17 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/test/kotlin/util/example/DefaultValueSuppliers.kt: -------------------------------------------------------------------------------- 1 | package util.example 2 | 3 | import java.util.function.Supplier 4 | 5 | class DefaultStringSupplier : Supplier { 6 | override fun get() = "DefaultString" 7 | } 8 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/test/kotlin/util/example/Edges.kt: -------------------------------------------------------------------------------- 1 | package util.example 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.annotations.* 4 | import org.apache.tinkerpop.gremlin.ogm.elements.Edge 5 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.SingleToSingleAsymmetricEdgeSpec 6 | 7 | @Element(label = "fromIntToBool") 8 | internal class IntToBoolEdge( 9 | 10 | @ID 11 | val id: Long? = null, 12 | 13 | @Property("a") 14 | val a: String, 15 | 16 | @FromVertex 17 | override val from: VertexWithInt, 18 | 19 | @ToVertex 20 | override val to: VertexWithBoolean 21 | 22 | ) : Edge { 23 | 24 | override fun hashCode(): Int = id?.hashCode() ?: super.hashCode() 25 | 26 | override fun equals(other: Any?): Boolean = id != null && other != null && other is IntToBoolEdge && id == other.id 27 | 28 | companion object { 29 | val fromIntToBool = SingleToSingleAsymmetricEdgeSpec("fromIntToBool") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/test/kotlin/util/example/Enums.kt: -------------------------------------------------------------------------------- 1 | package util.example 2 | 3 | import com.google.common.collect.ImmutableBiMap 4 | import org.apache.tinkerpop.gremlin.ogm.mappers.EnumBiMapper 5 | import util.StaticBiMapper 6 | 7 | internal enum class Sport { 8 | BASKETBALL, 9 | FOOTBALL, 10 | BASEBALL, 11 | TENNIS, 12 | ; 13 | 14 | companion object : EnumBiMapper, StaticBiMapper { 15 | override val map: ImmutableBiMap = ImmutableBiMap.of( 16 | BASKETBALL, "BASKETBALL", 17 | FOOTBALL, "FOOTBALL", 18 | BASEBALL, "BASEBALL", 19 | TENNIS, "TENNIS") 20 | 21 | override val serializedClass get() = String::class 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/test/kotlin/util/example/Nested.kt: -------------------------------------------------------------------------------- 1 | package util.example 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.annotations.Property 4 | import java.util.* 5 | 6 | internal data class ObjectWithInt( 7 | 8 | @Property(key = "intVal") 9 | val intVal: Int 10 | ) { 11 | companion object { 12 | fun sample() = ObjectWithInt(intVal = Random().nextInt()) 13 | } 14 | } 15 | 16 | internal data class Nested( 17 | 18 | @Property(key = "objWithInt") 19 | val nestedObj: ObjectWithInt 20 | ) { 21 | companion object { 22 | fun sample() = Nested(nestedObj = ObjectWithInt.sample()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/test/kotlin/util/example/PropertyBiMappers.kt: -------------------------------------------------------------------------------- 1 | package util.example 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.mappers.PropertyBiMapper 4 | import java.util.* 5 | 6 | internal class Base64Mapper : PropertyBiMapper { 7 | 8 | override fun forwardMap(from: String): String = Base64.getEncoder().encodeToString(from.toByteArray()) 9 | 10 | override fun inverseMap(from: String): String = String(Base64.getDecoder().decode(from)) 11 | 12 | override val serializedClass get() = String::class 13 | } 14 | 15 | internal class NumberToStringMapper : PropertyBiMapper { 16 | 17 | override fun forwardMap(from: Number): String { 18 | val prefix = when (from) { 19 | is Long -> "Long" 20 | is Byte -> "Byte" 21 | is Int -> "Int" 22 | is Double -> "Double" 23 | is Float -> "Float" 24 | is Short -> "Short" 25 | else -> throw Exception("Unrecognized number type: $from") 26 | } 27 | return "$prefix:$from" 28 | } 29 | 30 | override fun inverseMap(from: String): Number { 31 | val split = from.split(":") 32 | val prefix = split.first() 33 | val numberString = split.last() 34 | return when (prefix) { 35 | "Long" -> numberString.toLong() 36 | "Byte" -> numberString.toByte() 37 | "Int" -> numberString.toInt() 38 | "Double" -> numberString.toDouble() 39 | "Float" -> numberString.toFloat() 40 | "Short" -> numberString.toShort() 41 | else -> throw Exception("Unrecognized prefix: $from") 42 | } 43 | } 44 | 45 | override val serializedClass get() = String::class 46 | } 47 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/test/kotlin/util/example/Relationships.kt: -------------------------------------------------------------------------------- 1 | package util.example 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.steps.relationship.edgespec.* 4 | 5 | internal val asymmetricManyToMany = ManyToManyAsymmetricEdgeSpec(name = "asymmetric_many_to_many") 6 | internal val asymmetricOptionalToMany = OptionalToManyEdgeSpec(name = "asymmetric_optional_to_many") 7 | internal val asymmetricOptionalToOptional = OptionalToOptionalAsymmetricEdgeSpec(name = "asymmetric_optional_to_optional") 8 | internal val asymmetricOptionalToSingle = OptionalToSingleEdgeSpec(name = "asymmetric_optional_to_single") 9 | internal val asymmetricSingleToMany = SingleToManyEdgeSpec(name = "asymmetric_single_to_many") 10 | internal val asymmetricSingleToOptional = SingleToOptionalEdgeSpec(name = "asymmetric-Single_to_optional") 11 | internal val asymmetricSingleToSingle = SingleToSingleAsymmetricEdgeSpec(name = "asymmetric_single_to_single") 12 | internal val symmetricManyToMany = ManyToManySymmetricEdgeSpec(name = "symmetric_many_to_many") 13 | internal val symmetricOptionalToOptional = OptionalToOptionalSymmetricEdgeSpec(name = "symmetric_optional_to_optional") 14 | internal val symmetricSingleToSingle = SingleToSingleSymmetricEdgeSpec(name = "symmetric_single_to_single") 15 | -------------------------------------------------------------------------------- /kotlin-gremlin-ogm/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Set root logger level to DEBUG and its only appender to A1. 2 | log4j.rootLogger=DEBUG, A1 3 | 4 | # A1 is set to be a ConsoleAppender. 5 | log4j.appender.A1=org.apache.log4j.ConsoleAppender 6 | 7 | # A1 uses PatternLayout. 8 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout 9 | log4j.appender.A1.layout.ConversionPattern=%-5p [%t] %-4r %c %x: %m%n 10 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /out/ 3 | *.gpg 4 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/README.md: -------------------------------------------------------------------------------- 1 | # The Object Graph Mapping Library for Kotlin and JanusGraph 2 | 3 | [![Build Status](https://travis-ci.org/pm-dev/kotlin-gremlin-ogm.svg?branch=master)](https://travis-ci.org/pm-dev/kotlin-gremlin-ogm) 4 | [![Latest Release](https://img.shields.io/maven-metadata/v/http/central.maven.org/maven2/com/github/pm-dev/kotlin-janusgraph-ogm/maven-metadata.xml.svg)](http://central.maven.org/maven2/com/github/pm-dev/kotlin-janusgraph-ogm/) 5 | [![Kotlin Version](https://img.shields.io/badge/kotlin-1.3.10-blue.svg)](http://kotlinlang.org/) 6 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0) 7 | 8 | This library is an extension of [kotlin-gremlin-ogm](https://github.com/pm-dev/kotlin-gremlin-ogm) that provides the 9 | additional features: 10 | 11 | - Indexing via @Indexed annotation 12 | 13 | 14 | #### Basic Usage: 15 | 16 | Define a Vertex 17 | 18 | @Element("Person") 19 | data class Person( 20 | 21 | @ID 22 | val id: Long? = null, 23 | 24 | @Indexed(unique = false) @Property("name") 25 | val name: String) 26 | 27 | Save a Vertex 28 | 29 | val mighael = graphMapper.saveV(Person(name = "Michael Scott")) 30 | 31 | Lookup Vertex by a property other than its ID 32 | 33 | val michael = graphMapper.allV { has("name", "Michael Scott") }.single() 34 | 35 | More complex examples can be seen in the [starwars example project](https://github.com/pm-dev/kotlin-gremlin-ogm/tree/master/example/src/main/kotlin/starwars), 36 | which exposes a graph database through a GraphQL endpoint. 37 | 38 | 39 | #### Installation: 40 | 41 | - Gradle 42 | 43 | compile 'com.github.pm-dev:kotlin-janusgraph-ogm:0.21.0' 44 | 45 | - Maven 46 | 47 | 48 | com.github.pm-dev 49 | kotlin-janusgraph-ogm 50 | 0.21.0 51 | 52 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/gradle.properties: -------------------------------------------------------------------------------- 1 | signing.keyId=16201A38 2 | signing.password= 3 | signing.secretKeyRingFile=secring.gpg 4 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/main/kotlin/org/janusgraph/ogm/annotations/Indexed.kt: -------------------------------------------------------------------------------- 1 | package org.janusgraph.ogm.annotations 2 | 3 | import java.lang.annotation.Inherited 4 | 5 | /** 6 | * Registers a property that should be indexed in the graph. Indexing [Iterable] and [Map] properties is currently 7 | * unsupported. Indexing nested-object/non-scalar properties is allowed, however be aware that these objects are 8 | * persisted in the graph with multiple vertex properties, so each vertex property will be indexed. 9 | * For example 10 | * class Name(val first: String, val last: String) 11 | * @Element("person") class Person(@Indexed @Property("name") val name: Name) 12 | * 13 | * ... will result in both name properties being indexed: "name.first" and "name.last" 14 | * 15 | * The class must be annotated with @Element and registered as a vertex with a GraphMapper. 16 | */ 17 | @Retention(value = AnnotationRetention.RUNTIME) 18 | @Target(allowedTargets = [AnnotationTarget.PROPERTY]) 19 | @Inherited 20 | annotation class Indexed( 21 | 22 | /** 23 | * True if the value for this property may not be repeated in different instances of the same type of element. 24 | */ 25 | val unique: Boolean = false 26 | ) 27 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/main/kotlin/org/janusgraph/ogm/exceptions/IndexNotOnProperty.kt: -------------------------------------------------------------------------------- 1 | package org.janusgraph.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | import kotlin.reflect.KProperty1 5 | 6 | internal class IndexNotOnProperty( 7 | element: KClass<*>, 8 | property: KProperty1<*, *> 9 | ) : RuntimeException("@Indexed is only supported on Vertex or Edge properties that are annotated with @Property. Class: $element, property: $property") 10 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/main/kotlin/org/janusgraph/ogm/exceptions/IndexTypeMismatch.kt: -------------------------------------------------------------------------------- 1 | package org.janusgraph.ogm.exceptions 2 | 3 | internal class IndexTypeMismatch( 4 | expected: Class<*>, 5 | actual: Class<*> 6 | ) : RuntimeException("Attempting to index a property that is mapped to the graph as a $expected, " + 7 | "but the graph schema indicates this property already exists as a $actual") 8 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/main/kotlin/org/janusgraph/ogm/exceptions/IterableIndexUnsupported.kt: -------------------------------------------------------------------------------- 1 | package org.janusgraph.ogm.exceptions 2 | 3 | import kotlin.reflect.KProperty1 4 | 5 | internal class IterableIndexUnsupported( 6 | prefix: String, 7 | property: KProperty1<*, *> 8 | ) : RuntimeException("@Indexed annotation may not be placed on a property that is an Iterable, or a property that " + 9 | "has nested Iterable properties. $prefix, $property") 10 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/main/kotlin/org/janusgraph/ogm/exceptions/MapIndexUnsupported.kt: -------------------------------------------------------------------------------- 1 | package org.janusgraph.ogm.exceptions 2 | 3 | import kotlin.reflect.KProperty1 4 | 5 | internal class MapIndexUnsupported( 6 | prefix: String, 7 | property: KProperty1<*, *> 8 | ) : RuntimeException("@Indexed annotation may not be placed on a property that is a Map, or a property that " + 9 | "has nested Map properties. $prefix, $property") 10 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/main/kotlin/org/janusgraph/ogm/exceptions/NestedIndexUnsupported.kt: -------------------------------------------------------------------------------- 1 | package org.janusgraph.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class NestedIndexUnsupported( 6 | nestedObject: KClass<*>, 7 | property: String 8 | ) : RuntimeException("Attempting to create an index on property of a class that is registered as a nested object, " + 9 | "but indices may only be added to properties of Edges or Vertices. " + 10 | "Nested object: $nestedObject, property: $property") 11 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/main/kotlin/org/janusgraph/ogm/exceptions/SuperclassAnnotationException.kt: -------------------------------------------------------------------------------- 1 | package org.janusgraph.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | import kotlin.reflect.KProperty1 5 | 6 | internal class SuperclassAnnotationException( 7 | baseClass: KClass<*>, 8 | superClass: KClass<*>, 9 | property: KProperty1<*, *>, 10 | annotations: List<*> 11 | ) : RuntimeException("All annotations must happen in the base class. Baseclass: $baseClass. " + 12 | "Superclass $superClass. Property: $property. Annotations: $annotations" 13 | ) 14 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/main/kotlin/org/janusgraph/ogm/exceptions/UnknownElementType.kt: -------------------------------------------------------------------------------- 1 | package org.janusgraph.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class UnknownElementType( 6 | kClass: KClass<*> 7 | ) : RuntimeException("Attempting to create an index on class $kClass which is not an Edge or Vertex. " + 8 | "This indicates a bug in the library") 9 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/main/kotlin/org/janusgraph/ogm/exceptions/UnrecognizedPropertyClass.kt: -------------------------------------------------------------------------------- 1 | package org.janusgraph.ogm.exceptions 2 | 3 | import kotlin.reflect.KClass 4 | 5 | internal class UnrecognizedPropertyClass( 6 | propertyKey: String, 7 | kClass: KClass<*> 8 | ) : RuntimeException("Attempting to index a property, but it's unknown how to map this class type to the graph. " + 9 | "$propertyKey, $kClass") 10 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/main/kotlin/org/janusgraph/ogm/reflection/IndexDescription.kt: -------------------------------------------------------------------------------- 1 | package org.janusgraph.ogm.reflection 2 | 3 | import org.apache.tinkerpop.gremlin.structure.Element 4 | import kotlin.reflect.KClass 5 | 6 | internal data class IndexDescription( 7 | val propertyName: String, 8 | val dataType: KClass<*>, 9 | val unique: Boolean, 10 | val elementType: KClass, 11 | val elementLabel: String) { 12 | 13 | val indexName get() = "$elementLabel-$propertyName${if (unique) "-unique" else ""}-index" 14 | } 15 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/test/kotlin/util/example/Graph.kt: -------------------------------------------------------------------------------- 1 | package util.example 2 | 3 | import org.janusgraph.core.JanusGraph 4 | import org.janusgraph.core.JanusGraphFactory 5 | 6 | internal fun exampleGraph(): JanusGraph = 7 | JanusGraphFactory.build() 8 | .set("storage.backend", "inmemory") 9 | .set("index.search.backend", "lucene") 10 | .set("index.search.directory", "/tmp") 11 | .open() 12 | -------------------------------------------------------------------------------- /kotlin-janusgraph-ogm/src/test/kotlin/util/example/Verticies.kt: -------------------------------------------------------------------------------- 1 | package util.example 2 | 3 | import org.apache.tinkerpop.gremlin.ogm.annotations.Element 4 | import org.apache.tinkerpop.gremlin.ogm.annotations.ID 5 | import org.apache.tinkerpop.gremlin.ogm.annotations.Property 6 | import java.util.* 7 | 8 | @Element(label = "VertexWithInt") 9 | internal class VertexWithInt( 10 | 11 | @ID 12 | id: Long? = null, 13 | 14 | @Property(key = "a") 15 | int: Int 16 | 17 | ) : BaseVertex(id = id, a = int) { 18 | 19 | companion object { 20 | fun sample() = VertexWithInt(int = Random().nextInt()) 21 | } 22 | } 23 | 24 | internal abstract class BaseVertex( 25 | 26 | val id: Long? = null, 27 | 28 | @property:Property(key = "a") 29 | val a: T 30 | 31 | ) { 32 | 33 | override fun hashCode(): Int = id?.hashCode() ?: super.hashCode() 34 | 35 | override fun equals(other: Any?): Boolean = id != null && other != null && other is BaseVertex<*> && id == other.id 36 | } 37 | -------------------------------------------------------------------------------- /kotlin-rx-ogm/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /out/ 3 | *.gpg 4 | -------------------------------------------------------------------------------- /kotlin-rx-ogm/gradle.properties: -------------------------------------------------------------------------------- 1 | signing.keyId=16201A38 2 | signing.password= 3 | signing.secretKeyRingFile=secring.gpg 4 | -------------------------------------------------------------------------------- /kotlin-rx-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/rx/Extensions.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package org.apache.tinkerpop.gremlin.ogm.rx 4 | 5 | import io.reactivex.Maybe 6 | import io.reactivex.Observable 7 | import io.reactivex.Single 8 | import org.apache.tinkerpop.gremlin.ogm.GraphMapper 9 | import org.apache.tinkerpop.gremlin.ogm.steps.bound.BoundStep 10 | import org.apache.tinkerpop.gremlin.ogm.steps.bound.single.SingleBoundStep 11 | 12 | fun GraphMapper.traverseRx(step: SingleBoundStep.ToSingle<*, TO>): Single = SingleBoundGraphTraversalToSingleRx(this, step) 13 | fun GraphMapper.traverseRx(step: SingleBoundStep.ToOptional<*, TO>): Maybe = SingleBoundGraphTraversalToOptionalRx(this, step) 14 | fun GraphMapper.traverseRx(step: SingleBoundStep.ToMany<*, TO>): Observable = SingleBoundGraphTraversalToManyRx(this, step) 15 | fun GraphMapper.traverseRx(step: BoundStep.ToSingle): Single> = MultiBoundGraphTraversalToSingleRx(this, step) 16 | fun GraphMapper.traverseRx(step: BoundStep.ToOptional): Single> = MultiBoundGraphTraversalToOptionalRx(this, step) 17 | fun GraphMapper.traverseRx(step: BoundStep.ToMany): Single>> = MultiBoundGraphTraversalToManyRx(this, step) 18 | -------------------------------------------------------------------------------- /kotlin-rx-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/rx/MultiBoundGraphTraversalToManyRx.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.rx 2 | 3 | import io.reactivex.Single 4 | import io.reactivex.SingleObserver 5 | import io.reactivex.disposables.Disposables 6 | import io.reactivex.exceptions.Exceptions 7 | import io.reactivex.plugins.RxJavaPlugins 8 | import org.apache.tinkerpop.gremlin.ogm.GraphMapper 9 | import org.apache.tinkerpop.gremlin.ogm.steps.bound.BoundStep 10 | 11 | internal data class MultiBoundGraphTraversalToManyRx( 12 | private val mapper: GraphMapper, 13 | private val boundStep: BoundStep.ToMany 14 | ) : Single>>() { 15 | 16 | override fun subscribeActual(observer: SingleObserver>>) { 17 | val d = Disposables.empty() 18 | observer.onSubscribe(d) 19 | if (d.isDisposed) { 20 | return 21 | } 22 | val v: Map> 23 | try { 24 | v = mapper.traverse(boundStep) 25 | } catch (ex: Throwable) { 26 | Exceptions.throwIfFatal(ex) 27 | if (d.isDisposed) { 28 | RxJavaPlugins.onError(ex) 29 | } else { 30 | observer.onError(ex) 31 | } 32 | return 33 | } 34 | if (d.isDisposed) { 35 | return 36 | } 37 | observer.onSuccess(v) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /kotlin-rx-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/rx/MultiBoundGraphTraversalToOptionalRx.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.rx 2 | 3 | import io.reactivex.Single 4 | import io.reactivex.SingleObserver 5 | import io.reactivex.disposables.Disposables 6 | import io.reactivex.exceptions.Exceptions 7 | import io.reactivex.plugins.RxJavaPlugins 8 | import org.apache.tinkerpop.gremlin.ogm.GraphMapper 9 | import org.apache.tinkerpop.gremlin.ogm.steps.bound.BoundStep 10 | 11 | internal data class MultiBoundGraphTraversalToOptionalRx( 12 | private val mapper: GraphMapper, 13 | private val boundStep: BoundStep.ToOptional 14 | ) : Single>() { 15 | 16 | override fun subscribeActual(observer: SingleObserver>) { 17 | val d = Disposables.empty() 18 | observer.onSubscribe(d) 19 | if (d.isDisposed) { 20 | return 21 | } 22 | val v: Map 23 | try { 24 | v = mapper.traverse(boundStep) 25 | } catch (ex: Throwable) { 26 | Exceptions.throwIfFatal(ex) 27 | if (d.isDisposed) { 28 | RxJavaPlugins.onError(ex) 29 | } else { 30 | observer.onError(ex) 31 | } 32 | return 33 | } 34 | if (d.isDisposed) { 35 | return 36 | } 37 | observer.onSuccess(v) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /kotlin-rx-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/rx/MultiBoundGraphTraversalToSingleRx.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.rx 2 | 3 | import io.reactivex.Single 4 | import io.reactivex.SingleObserver 5 | import io.reactivex.disposables.Disposables 6 | import io.reactivex.exceptions.Exceptions 7 | import io.reactivex.plugins.RxJavaPlugins 8 | import org.apache.tinkerpop.gremlin.ogm.GraphMapper 9 | import org.apache.tinkerpop.gremlin.ogm.steps.bound.BoundStep 10 | 11 | internal data class MultiBoundGraphTraversalToSingleRx( 12 | private val mapper: GraphMapper, 13 | private val boundStep: BoundStep.ToSingle 14 | ) : Single>() { 15 | 16 | override fun subscribeActual(observer: SingleObserver>) { 17 | val d = Disposables.empty() 18 | observer.onSubscribe(d) 19 | if (d.isDisposed) { 20 | return 21 | } 22 | val v: Map 23 | try { 24 | v = mapper.traverse(boundStep) 25 | } catch (ex: Throwable) { 26 | Exceptions.throwIfFatal(ex) 27 | if (d.isDisposed) { 28 | RxJavaPlugins.onError(ex) 29 | } else { 30 | observer.onError(ex) 31 | } 32 | return 33 | } 34 | if (d.isDisposed) { 35 | return 36 | } 37 | observer.onSuccess(v) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /kotlin-rx-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/rx/SingleBoundGraphTraversalToOptionalRx.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.rx 2 | 3 | import io.reactivex.Maybe 4 | import io.reactivex.MaybeObserver 5 | import io.reactivex.disposables.Disposables 6 | import io.reactivex.exceptions.Exceptions 7 | import io.reactivex.plugins.RxJavaPlugins 8 | import org.apache.tinkerpop.gremlin.ogm.GraphMapper 9 | import org.apache.tinkerpop.gremlin.ogm.steps.bound.single.SingleBoundStep 10 | 11 | internal data class SingleBoundGraphTraversalToOptionalRx( 12 | private val mapper: GraphMapper, 13 | private val boundStep: SingleBoundStep.ToOptional<*, TO> 14 | ) : Maybe() { 15 | 16 | override fun subscribeActual(observer: MaybeObserver) { 17 | val d = Disposables.empty() 18 | observer.onSubscribe(d) 19 | if (d.isDisposed) { 20 | return 21 | } 22 | val v: TO? 23 | try { 24 | v = mapper.traverse(boundStep) 25 | } catch (ex: Throwable) { 26 | Exceptions.throwIfFatal(ex) 27 | if (d.isDisposed) { 28 | RxJavaPlugins.onError(ex) 29 | } else { 30 | observer.onError(ex) 31 | } 32 | return 33 | } 34 | if (d.isDisposed) { 35 | return 36 | } 37 | if (v == null) { 38 | observer.onComplete() 39 | } else { 40 | observer.onSuccess(v) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /kotlin-rx-ogm/src/main/kotlin/org/apache/tinkerpop/gremlin/ogm/rx/SingleBoundGraphTraversalToSingleRx.kt: -------------------------------------------------------------------------------- 1 | package org.apache.tinkerpop.gremlin.ogm.rx 2 | 3 | import io.reactivex.Single 4 | import io.reactivex.SingleObserver 5 | import io.reactivex.disposables.Disposables 6 | import io.reactivex.exceptions.Exceptions 7 | import io.reactivex.plugins.RxJavaPlugins 8 | import org.apache.tinkerpop.gremlin.ogm.GraphMapper 9 | import org.apache.tinkerpop.gremlin.ogm.steps.bound.single.SingleBoundStep 10 | 11 | internal data class SingleBoundGraphTraversalToSingleRx( 12 | private val mapper: GraphMapper, 13 | private val boundStep: SingleBoundStep.ToSingle<*, TO> 14 | ) : Single() { 15 | 16 | override fun subscribeActual(observer: SingleObserver) { 17 | val d = Disposables.empty() 18 | observer.onSubscribe(d) 19 | if (d.isDisposed) { 20 | return 21 | } 22 | val v: TO 23 | try { 24 | v = mapper.traverse(boundStep) 25 | } catch (ex: Throwable) { 26 | Exceptions.throwIfFatal(ex) 27 | if (d.isDisposed) { 28 | RxJavaPlugins.onError(ex) 29 | } else { 30 | observer.onError(ex) 31 | } 32 | return 33 | } 34 | if (d.isDisposed) { 35 | return 36 | } 37 | observer.onSuccess(v) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':kotlin-gremlin-ogm', ':example', ':kotlin-janusgraph-ogm', ':kotlin-gremlin-graphql', ':kotlin-rx-ogm' 2 | --------------------------------------------------------------------------------