├── .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