@commitspark/graphql-api 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/.eslintrc.js +24 -0
  2. package/.prettierrc +6 -0
  3. package/CHANGELOG.md +83 -0
  4. package/LICENSE +15 -0
  5. package/README.md +223 -0
  6. package/dist/app/api.service.d.ts +31 -0
  7. package/dist/app/api.service.js +60 -0
  8. package/dist/app/api.service.js.map +1 -0
  9. package/dist/container.d.ts +2 -0
  10. package/dist/container.js +44 -0
  11. package/dist/container.js.map +1 -0
  12. package/dist/graphql/apollo-config-factory.service.d.ts +8 -0
  13. package/dist/graphql/apollo-config-factory.service.js +20 -0
  14. package/dist/graphql/apollo-config-factory.service.js.map +1 -0
  15. package/dist/graphql/input-type-generator.service.d.ts +12 -0
  16. package/dist/graphql/input-type-generator.service.js +86 -0
  17. package/dist/graphql/input-type-generator.service.js.map +1 -0
  18. package/dist/graphql/queries-mutations-generator.service.d.ts +39 -0
  19. package/dist/graphql/queries-mutations-generator.service.js +82 -0
  20. package/dist/graphql/queries-mutations-generator.service.js.map +1 -0
  21. package/dist/graphql/resolver-generators/entry-reference-resolver.generator.d.ts +12 -0
  22. package/dist/graphql/resolver-generators/entry-reference-resolver.generator.js +29 -0
  23. package/dist/graphql/resolver-generators/entry-reference-resolver.generator.js.map +1 -0
  24. package/dist/graphql/resolver-generators/mutation-create-resolver-generator.d.ts +8 -0
  25. package/dist/graphql/resolver-generators/mutation-create-resolver-generator.js +17 -0
  26. package/dist/graphql/resolver-generators/mutation-create-resolver-generator.js.map +1 -0
  27. package/dist/graphql/resolver-generators/mutation-delete-resolver-generator.d.ts +8 -0
  28. package/dist/graphql/resolver-generators/mutation-delete-resolver-generator.js +19 -0
  29. package/dist/graphql/resolver-generators/mutation-delete-resolver-generator.js.map +1 -0
  30. package/dist/graphql/resolver-generators/mutation-update.resolver-generator.d.ts +8 -0
  31. package/dist/graphql/resolver-generators/mutation-update.resolver-generator.js +17 -0
  32. package/dist/graphql/resolver-generators/mutation-update.resolver-generator.js.map +1 -0
  33. package/dist/graphql/resolver-generators/query-all-resolver-generator.d.ts +8 -0
  34. package/dist/graphql/resolver-generators/query-all-resolver-generator.js +15 -0
  35. package/dist/graphql/resolver-generators/query-all-resolver-generator.js.map +1 -0
  36. package/dist/graphql/resolver-generators/query-by-id-resolver-generator.d.ts +8 -0
  37. package/dist/graphql/resolver-generators/query-by-id-resolver-generator.js +15 -0
  38. package/dist/graphql/resolver-generators/query-by-id-resolver-generator.js.map +1 -0
  39. package/dist/graphql/resolver-generators/query-count-all-resolver-generator.d.ts +9 -0
  40. package/dist/graphql/resolver-generators/query-count-all-resolver-generator.js +17 -0
  41. package/dist/graphql/resolver-generators/query-count-all-resolver-generator.js.map +1 -0
  42. package/dist/graphql/resolver-generators/query-type-by-id-resolver-generator.d.ts +8 -0
  43. package/dist/graphql/resolver-generators/query-type-by-id-resolver-generator.js +15 -0
  44. package/dist/graphql/resolver-generators/query-type-by-id-resolver-generator.js.map +1 -0
  45. package/dist/graphql/resolver-generators/union-type-resolver-generator.d.ts +10 -0
  46. package/dist/graphql/resolver-generators/union-type-resolver-generator.js +21 -0
  47. package/dist/graphql/resolver-generators/union-type-resolver-generator.js.map +1 -0
  48. package/dist/graphql/resolver-generators/union-value-resolver-generator.d.ts +13 -0
  49. package/dist/graphql/resolver-generators/union-value-resolver-generator.js +80 -0
  50. package/dist/graphql/resolver-generators/union-value-resolver-generator.js.map +1 -0
  51. package/dist/graphql/schema-analyzer.service.d.ts +19 -0
  52. package/dist/graphql/schema-analyzer.service.js +52 -0
  53. package/dist/graphql/schema-analyzer.service.js.map +1 -0
  54. package/dist/graphql/schema-generator.service.d.ts +22 -0
  55. package/dist/graphql/schema-generator.service.js +99 -0
  56. package/dist/graphql/schema-generator.service.js.map +1 -0
  57. package/dist/graphql/schema-root-type-generator.service.d.ts +4 -0
  58. package/dist/graphql/schema-root-type-generator.service.js +37 -0
  59. package/dist/graphql/schema-root-type-generator.service.js.map +1 -0
  60. package/dist/graphql/schema-utils/entry-reference-util.d.ts +7 -0
  61. package/dist/graphql/schema-utils/entry-reference-util.js +42 -0
  62. package/dist/graphql/schema-utils/entry-reference-util.js.map +1 -0
  63. package/dist/graphql/schema-validator.d.ts +5 -0
  64. package/dist/graphql/schema-validator.js +33 -0
  65. package/dist/graphql/schema-validator.js.map +1 -0
  66. package/dist/index.d.ts +3 -0
  67. package/dist/index.js +11 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/persistence/persistence.service.d.ts +17 -0
  70. package/dist/persistence/persistence.service.js +110 -0
  71. package/dist/persistence/persistence.service.js.map +1 -0
  72. package/dist/tsconfig.build.tsbuildinfo +1 -0
  73. package/package.json +34 -0
  74. package/src/app/api.service.ts +113 -0
  75. package/src/container.ts +92 -0
  76. package/src/graphql/apollo-config-factory.service.ts +21 -0
  77. package/src/graphql/input-type-generator.service.ts +114 -0
  78. package/src/graphql/queries-mutations-generator.service.ts +155 -0
  79. package/src/graphql/resolver-generators/entry-reference-resolver.generator.ts +71 -0
  80. package/src/graphql/resolver-generators/mutation-create-resolver-generator.ts +38 -0
  81. package/src/graphql/resolver-generators/mutation-delete-resolver-generator.ts +35 -0
  82. package/src/graphql/resolver-generators/mutation-update.resolver-generator.ts +39 -0
  83. package/src/graphql/resolver-generators/query-all-resolver-generator.ts +27 -0
  84. package/src/graphql/resolver-generators/query-by-id-resolver-generator.ts +23 -0
  85. package/src/graphql/resolver-generators/query-count-all-resolver-generator.ts +29 -0
  86. package/src/graphql/resolver-generators/query-type-by-id-resolver-generator.ts +27 -0
  87. package/src/graphql/resolver-generators/union-type-resolver-generator.ts +40 -0
  88. package/src/graphql/resolver-generators/union-value-resolver-generator.ts +116 -0
  89. package/src/graphql/schema-analyzer.service.ts +70 -0
  90. package/src/graphql/schema-generator.service.ts +190 -0
  91. package/src/graphql/schema-root-type-generator.service.ts +42 -0
  92. package/src/graphql/schema-utils/entry-reference-util.ts +49 -0
  93. package/src/graphql/schema-validator.ts +41 -0
  94. package/src/index.ts +8 -0
  95. package/src/persistence/persistence.service.ts +183 -0
  96. package/tsconfig.build.json +10 -0
  97. package/tsconfig.json +18 -0
  98. package/webpack.config.js +25 -0
@@ -0,0 +1,155 @@
1
+ import { Entry } from '../persistence/persistence.service'
2
+ import { ApolloContext } from '../app/api.service'
3
+ import { GraphQLFieldResolver } from 'graphql/type/definition'
4
+ import { GraphQLObjectType } from 'graphql/type'
5
+ import { QueryAllResolverGenerator } from './resolver-generators/query-all-resolver-generator'
6
+ import { QueryByIdResolverGenerator } from './resolver-generators/query-by-id-resolver-generator'
7
+ import { MutationCreateResolverGenerator } from './resolver-generators/mutation-create-resolver-generator'
8
+ import { MutationUpdateResolverGenerator } from './resolver-generators/mutation-update.resolver-generator'
9
+ import { MutationDeleteResolverGenerator } from './resolver-generators/mutation-delete-resolver-generator'
10
+ import { QueryCountAllResolverGenerator } from './resolver-generators/query-count-all-resolver-generator'
11
+ import { QueryTypeByIdResolverGenerator } from './resolver-generators/query-type-by-id-resolver-generator'
12
+
13
+ export class QueriesMutationsGeneratorService {
14
+ constructor(
15
+ private readonly queryAllResolverGenerator: QueryAllResolverGenerator,
16
+ private readonly queryByIdResolverGenerator: QueryByIdResolverGenerator,
17
+ private readonly queryCountAllResolverGenerator: QueryCountAllResolverGenerator,
18
+ private readonly queryTypeByIdResolverGenerator: QueryTypeByIdResolverGenerator,
19
+ private readonly mutationCreateResolverGenerator: MutationCreateResolverGenerator,
20
+ private readonly mutationUpdateResolverGenerator: MutationUpdateResolverGenerator,
21
+ private readonly mutationDeleteResolverGenerator: MutationDeleteResolverGenerator,
22
+ ) {}
23
+
24
+ public generateFromAnalyzedSchema(
25
+ entryDirectiveTypes: GraphQLObjectType[],
26
+ ): IGeneratedSchema[] {
27
+ return entryDirectiveTypes.map((objectType): IGeneratedSchema => {
28
+ const typeName = objectType.name
29
+
30
+ const queryAllName = `all${typeName}s`
31
+ const queryAllString = `${queryAllName}: [${objectType.name}]`
32
+ const queryAllResolver: GraphQLFieldResolver<
33
+ any,
34
+ ApolloContext,
35
+ any,
36
+ Promise<Entry[]>
37
+ > = this.queryAllResolverGenerator.createResolver(typeName)
38
+
39
+ const queryAllMetaName = `_${queryAllName}Meta`
40
+ const queryAllMetaString = `${queryAllMetaName}: ListMetadata`
41
+ const queryAllMetaResolver: GraphQLFieldResolver<
42
+ any,
43
+ ApolloContext,
44
+ any,
45
+ Promise<TypeCount>
46
+ > = this.queryCountAllResolverGenerator.createResolver(typeName)
47
+
48
+ const queryByIdName = typeName
49
+ const queryByIdString = `${queryByIdName}(id: ID!): ${objectType.name}`
50
+ const queryByIdResolver: GraphQLFieldResolver<
51
+ any,
52
+ ApolloContext,
53
+ any,
54
+ Promise<Entry>
55
+ > = this.queryByIdResolverGenerator.createResolver(typeName)
56
+
57
+ const inputTypeName = `${typeName}Input`
58
+ const createMutationName = `create${typeName}`
59
+ const createMutationString = `${createMutationName}(data:${inputTypeName}, message:String): ${typeName}`
60
+ const createMutationResolver: GraphQLFieldResolver<
61
+ any,
62
+ ApolloContext,
63
+ any,
64
+ Promise<Entry>
65
+ > = this.mutationCreateResolverGenerator.createResolver(typeName)
66
+
67
+ const updateMutationName = `update${typeName}`
68
+ const updateMutationString = `${updateMutationName}(id: ID!, data:${inputTypeName}, message:String): ${typeName}`
69
+ const updateMutationResolver: GraphQLFieldResolver<
70
+ any,
71
+ ApolloContext,
72
+ any,
73
+ Promise<Entry>
74
+ > = this.mutationUpdateResolverGenerator.createResolver(typeName)
75
+
76
+ const deleteMutationName = `delete${typeName}`
77
+ const deleteMutationString = `${deleteMutationName}(id: ID!, message:String): DeletionResult`
78
+ const deleteMutationResolver: GraphQLFieldResolver<
79
+ any,
80
+ ApolloContext,
81
+ any,
82
+ Promise<Entry>
83
+ > = this.mutationDeleteResolverGenerator.createResolver(typeName)
84
+
85
+ return {
86
+ queryAll: {
87
+ name: queryAllName,
88
+ schemaString: queryAllString,
89
+ resolver: queryAllResolver,
90
+ },
91
+ queryAllMeta: {
92
+ name: queryAllMetaName,
93
+ schemaString: queryAllMetaString,
94
+ resolver: queryAllMetaResolver,
95
+ },
96
+ queryById: {
97
+ name: queryByIdName,
98
+ schemaString: queryByIdString,
99
+ resolver: queryByIdResolver,
100
+ },
101
+ createMutation: {
102
+ name: createMutationName,
103
+ schemaString: createMutationString,
104
+ resolver: createMutationResolver,
105
+ },
106
+ updateMutation: {
107
+ name: updateMutationName,
108
+ schemaString: updateMutationString,
109
+ resolver: updateMutationResolver,
110
+ },
111
+ deleteMutation: {
112
+ name: deleteMutationName,
113
+ schemaString: deleteMutationString,
114
+ resolver: deleteMutationResolver,
115
+ },
116
+ }
117
+ })
118
+ }
119
+
120
+ public generateTypeNameQuery(): IGeneratedQuery<Promise<string>> {
121
+ const contentTypeQueryName = '_typeName'
122
+ const contentTypeQueryString = `${contentTypeQueryName}(id: ID!): String`
123
+ const contentTypeQueryResolver: GraphQLFieldResolver<
124
+ any,
125
+ ApolloContext,
126
+ any,
127
+ Promise<string>
128
+ > = this.queryTypeByIdResolverGenerator.createResolver()
129
+
130
+ return {
131
+ name: contentTypeQueryName,
132
+ schemaString: contentTypeQueryString,
133
+ resolver: contentTypeQueryResolver,
134
+ }
135
+ }
136
+ }
137
+
138
+ export interface IGeneratedSchema {
139
+ queryAll: IGeneratedQuery<Promise<Entry[]>>
140
+ queryAllMeta: IGeneratedQuery<Promise<TypeCount>>
141
+ queryById: IGeneratedQuery<Promise<Entry>>
142
+ createMutation: IGeneratedQuery<Promise<Entry>>
143
+ updateMutation: IGeneratedQuery<Promise<Entry>>
144
+ deleteMutation: IGeneratedQuery<Promise<Entry>>
145
+ }
146
+
147
+ export interface IGeneratedQuery<T> {
148
+ name: string
149
+ schemaString: string
150
+ resolver: GraphQLFieldResolver<any, ApolloContext, any, T>
151
+ }
152
+
153
+ export type TypeCount = {
154
+ count: number
155
+ }
@@ -0,0 +1,71 @@
1
+ import { GraphQLField, GraphQLObjectType } from 'graphql'
2
+ import {
3
+ Entry,
4
+ PersistenceService,
5
+ } from '../../persistence/persistence.service'
6
+ import { ApolloContext } from '../../app/api.service'
7
+ import { GraphQLFieldResolver } from 'graphql/type/definition'
8
+
9
+ export class EntryReferenceResolverGenerator {
10
+ constructor(private readonly persistence: PersistenceService) {}
11
+
12
+ public createResolver(
13
+ context: ApolloContext,
14
+ obj: {
15
+ type: GraphQLObjectType
16
+ fields: GraphQLField<any, any>[]
17
+ },
18
+ ): Record<
19
+ string,
20
+ GraphQLFieldResolver<
21
+ Record<string, any>,
22
+ ApolloContext,
23
+ any,
24
+ Promise<Entry | Entry[] | undefined>
25
+ >
26
+ > {
27
+ const persistence = this.persistence
28
+
29
+ const fieldResolvers: Record<
30
+ string,
31
+ GraphQLFieldResolver<
32
+ Record<string, any>,
33
+ ApolloContext,
34
+ any,
35
+ Promise<Entry | Entry[] | undefined>
36
+ >
37
+ > = {}
38
+
39
+ for (const field of obj.fields) {
40
+ const fieldName = field.name
41
+ fieldResolvers[fieldName] = async (
42
+ source,
43
+ args,
44
+ context,
45
+ info,
46
+ ): Promise<Entry | Entry[] | undefined> => {
47
+ if (source[fieldName] === undefined) {
48
+ return undefined
49
+ }
50
+ // TODO decide this based on type definition, not data
51
+ if (Array.isArray(source[fieldName])) {
52
+ return source[fieldName].map((el: any) =>
53
+ persistence.findById(
54
+ context.gitAdapter,
55
+ context.getCurrentRef(),
56
+ el.id,
57
+ ),
58
+ )
59
+ } else {
60
+ return persistence.findById(
61
+ context.gitAdapter,
62
+ context.getCurrentRef(),
63
+ source[fieldName].id,
64
+ )
65
+ }
66
+ }
67
+ }
68
+
69
+ return fieldResolvers
70
+ }
71
+ }
@@ -0,0 +1,38 @@
1
+ import { GraphQLFieldResolver } from 'graphql/type/definition'
2
+ import { ApolloContext } from '../../app/api.service'
3
+ import {
4
+ Entry,
5
+ PersistenceService,
6
+ } from '../../persistence/persistence.service'
7
+
8
+ export class MutationCreateResolverGenerator {
9
+ constructor(private readonly persistence: PersistenceService) {}
10
+
11
+ public createResolver(
12
+ typeName: string,
13
+ ): GraphQLFieldResolver<any, ApolloContext, any, Promise<Entry>> {
14
+ return async (
15
+ source,
16
+ args,
17
+ context: ApolloContext,
18
+ info,
19
+ ): Promise<Entry> => {
20
+ // TODO validate ID references in payload to assert referenced ID exists and points to correct entry type
21
+ const createResult = await this.persistence.createType(
22
+ context.gitAdapter,
23
+ context.branch,
24
+ context.getCurrentRef(),
25
+ typeName,
26
+ args.data,
27
+ args.message,
28
+ )
29
+ context.setCurrentRef(createResult.ref)
30
+ return this.persistence.findByTypeId(
31
+ context.gitAdapter,
32
+ context.getCurrentRef(),
33
+ typeName,
34
+ createResult.id,
35
+ )
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,35 @@
1
+ import {
2
+ Entry,
3
+ PersistenceService,
4
+ } from '../../persistence/persistence.service'
5
+ import { GraphQLFieldResolver } from 'graphql/type/definition'
6
+ import { ApolloContext } from '../../app/api.service'
7
+
8
+ export class MutationDeleteResolverGenerator {
9
+ constructor(private readonly persistence: PersistenceService) {}
10
+
11
+ public createResolver(
12
+ typeName: string,
13
+ ): GraphQLFieldResolver<any, ApolloContext, any, Promise<Entry>> {
14
+ return async (
15
+ source,
16
+ args,
17
+ context: ApolloContext,
18
+ info,
19
+ ): Promise<Entry> => {
20
+ // TODO validate ID to delete is not referenced anywhere
21
+ const deleteResult = await this.persistence.deleteByTypeId(
22
+ context.gitAdapter,
23
+ context.branch,
24
+ context.getCurrentRef(),
25
+ typeName,
26
+ args.id,
27
+ args.message,
28
+ )
29
+ context.setCurrentRef(deleteResult.ref)
30
+ return {
31
+ id: args.id,
32
+ }
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,39 @@
1
+ import {
2
+ Entry,
3
+ PersistenceService,
4
+ } from '../../persistence/persistence.service'
5
+ import { GraphQLFieldResolver } from 'graphql/type/definition'
6
+ import { ApolloContext } from '../../app/api.service'
7
+
8
+ export class MutationUpdateResolverGenerator {
9
+ constructor(private readonly persistence: PersistenceService) {}
10
+
11
+ public createResolver(
12
+ typeName: string,
13
+ ): GraphQLFieldResolver<any, ApolloContext, any, Promise<Entry>> {
14
+ return async (
15
+ source,
16
+ args,
17
+ context: ApolloContext,
18
+ info,
19
+ ): Promise<Entry> => {
20
+ // TODO validate ID references in payload to assert referenced ID exists and points to correct entry type
21
+ const updateResult = await this.persistence.updateByTypeId(
22
+ context.gitAdapter,
23
+ context.branch,
24
+ context.getCurrentRef(),
25
+ typeName,
26
+ args.id,
27
+ args.data,
28
+ args.message,
29
+ )
30
+ context.setCurrentRef(updateResult.ref)
31
+ return this.persistence.findByTypeId(
32
+ context.gitAdapter,
33
+ context.getCurrentRef(),
34
+ typeName,
35
+ args.id,
36
+ )
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,27 @@
1
+ import { ApolloContext } from '../../app/api.service'
2
+ import {
3
+ Entry,
4
+ PersistenceService,
5
+ } from '../../persistence/persistence.service'
6
+ import { GraphQLFieldResolver } from 'graphql/type/definition'
7
+
8
+ export class QueryAllResolverGenerator {
9
+ constructor(private readonly persistence: PersistenceService) {}
10
+
11
+ public createResolver(
12
+ typeName: string,
13
+ ): GraphQLFieldResolver<any, ApolloContext, any, Promise<Entry[]>> {
14
+ return async (
15
+ obj,
16
+ args,
17
+ context: ApolloContext,
18
+ info,
19
+ ): Promise<Entry[]> => {
20
+ return this.persistence.findByType(
21
+ context.gitAdapter,
22
+ context.getCurrentRef(),
23
+ typeName,
24
+ )
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,23 @@
1
+ import {
2
+ Entry,
3
+ PersistenceService,
4
+ } from '../../persistence/persistence.service'
5
+ import { GraphQLFieldResolver } from 'graphql/type/definition'
6
+ import { ApolloContext } from '../../app/api.service'
7
+
8
+ export class QueryByIdResolverGenerator {
9
+ constructor(private readonly persistence: PersistenceService) {}
10
+
11
+ public createResolver(
12
+ typeName: string,
13
+ ): GraphQLFieldResolver<any, ApolloContext, any, Promise<Entry>> {
14
+ return async (obj, args, context, info): Promise<Entry> => {
15
+ return this.persistence.findByTypeId(
16
+ context.gitAdapter,
17
+ context.getCurrentRef(),
18
+ typeName,
19
+ args.id,
20
+ )
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,29 @@
1
+ import { PersistenceService } from '../../persistence/persistence.service'
2
+ import { GraphQLFieldResolver } from 'graphql/type/definition'
3
+ import { ApolloContext } from '../../app/api.service'
4
+ import { TypeCount } from '../queries-mutations-generator.service'
5
+
6
+ export class QueryCountAllResolverGenerator {
7
+ constructor(private readonly persistence: PersistenceService) {}
8
+
9
+ public createResolver(
10
+ typeName: string,
11
+ ): GraphQLFieldResolver<any, ApolloContext, any, Promise<TypeCount>> {
12
+ return async (
13
+ obj,
14
+ args,
15
+ context: ApolloContext,
16
+ info,
17
+ ): Promise<TypeCount> => {
18
+ return {
19
+ count: (
20
+ await this.persistence.findByType(
21
+ context.gitAdapter,
22
+ context.getCurrentRef(),
23
+ typeName,
24
+ )
25
+ ).length,
26
+ }
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,27 @@
1
+ import { PersistenceService } from '../../persistence/persistence.service'
2
+ import { GraphQLFieldResolver } from 'graphql/type/definition'
3
+ import { ApolloContext } from '../../app/api.service'
4
+
5
+ export class QueryTypeByIdResolverGenerator {
6
+ constructor(private readonly persistence: PersistenceService) {}
7
+
8
+ public createResolver(): GraphQLFieldResolver<
9
+ any,
10
+ ApolloContext,
11
+ any,
12
+ Promise<string>
13
+ > {
14
+ return async (
15
+ source,
16
+ args,
17
+ context: ApolloContext,
18
+ info,
19
+ ): Promise<string> => {
20
+ return this.persistence.getTypeById(
21
+ context.gitAdapter,
22
+ context.getCurrentRef(),
23
+ args.id,
24
+ )
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,40 @@
1
+ import { ApolloContext } from '../../app/api.service'
2
+ import { PersistenceService } from '../../persistence/persistence.service'
3
+ import {
4
+ GraphQLAbstractType,
5
+ GraphQLResolveInfo,
6
+ GraphQLTypeResolver,
7
+ } from 'graphql/type/definition'
8
+ import { EntryReferenceUtil } from '../schema-utils/entry-reference-util'
9
+
10
+ export class UnionTypeResolverGenerator {
11
+ constructor(
12
+ private readonly persistence: PersistenceService,
13
+ private readonly entryReferenceUtil: EntryReferenceUtil,
14
+ ) {}
15
+
16
+ public createResolver(): GraphQLTypeResolver<any, ApolloContext> {
17
+ return async (
18
+ obj: any,
19
+ context: ApolloContext,
20
+ info: GraphQLResolveInfo,
21
+ abstractType: GraphQLAbstractType,
22
+ ): Promise<string> => {
23
+ if (
24
+ this.entryReferenceUtil.buildsOnTypeWithEntryDirective(abstractType)
25
+ ) {
26
+ return this.persistence.getTypeById(
27
+ context.gitAdapter,
28
+ context.getCurrentRef(),
29
+ obj.id,
30
+ )
31
+ } else {
32
+ // We have injected an internal `__typename` field into the data of fields pointing to a non-entry union
33
+ // in UnionValueResolverGenerator. This artificial field holds the type information we need here.
34
+ return obj.__typename
35
+ }
36
+
37
+ // TODO same for interface type: https://www.apollographql.com/docs/apollo-server/data/resolvers/#resolving-unions-and-interfaces
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,116 @@
1
+ import {
2
+ GraphQLFieldResolver,
3
+ GraphQLNullableType,
4
+ GraphQLUnionType,
5
+ } from 'graphql/type/definition'
6
+ import { ApolloContext } from '../../app/api.service'
7
+ import { Entry } from '../../persistence/persistence.service'
8
+ import { GraphQLSchema, Kind } from 'graphql'
9
+ import {
10
+ GraphQLField,
11
+ GraphQLList,
12
+ GraphQLNonNull,
13
+ GraphQLObjectType,
14
+ } from 'graphql/type'
15
+ import { EntryReferenceUtil } from '../schema-utils/entry-reference-util'
16
+
17
+ export class UnionValueResolverGenerator {
18
+ constructor(private readonly entryReferenceUtil: EntryReferenceUtil) {}
19
+
20
+ public createResolver(
21
+ schema: GraphQLSchema,
22
+ ): Record<
23
+ string,
24
+ Record<string, GraphQLFieldResolver<any, ApolloContext, any, Entry>>
25
+ > {
26
+ const resolvers: Record<
27
+ string,
28
+ Record<string, GraphQLFieldResolver<any, ApolloContext, any, Entry>>
29
+ > = {}
30
+ for (const typeName of Object.keys(schema.getTypeMap())) {
31
+ const type = schema.getType(typeName)
32
+ if (!type || type.astNode?.kind !== Kind.OBJECT_TYPE_DEFINITION) {
33
+ continue
34
+ }
35
+ const objectType = type as GraphQLObjectType
36
+ const unionFieldsNotUsingEntry =
37
+ this.getFieldsWithUnionTypeNotUsingEntryTypes(objectType)
38
+
39
+ const fieldResolvers: Record<
40
+ string,
41
+ GraphQLFieldResolver<any, ApolloContext, any, Entry>
42
+ > = {}
43
+ for (const field of unionFieldsNotUsingEntry) {
44
+ fieldResolvers[field.name] = (obj, args, context, info): Entry => {
45
+ const fieldValue = obj[info.fieldName]
46
+ return this.collapseTypeField(field.type, fieldValue)
47
+ }
48
+ }
49
+
50
+ if (Object.keys(fieldResolvers).length > 0) {
51
+ resolvers[typeName] = fieldResolvers
52
+ }
53
+ }
54
+
55
+ return resolvers
56
+ }
57
+
58
+ private getFieldsWithUnionTypeNotUsingEntryTypes(
59
+ objectType: GraphQLObjectType,
60
+ ): GraphQLField<any, any>[] {
61
+ const fields = []
62
+ for (const fieldsKey in objectType.getFields()) {
63
+ const field: GraphQLField<any, any> = objectType.getFields()[fieldsKey]
64
+ if (
65
+ this.buildsOnUnionType(field.type) &&
66
+ !this.entryReferenceUtil.buildsOnTypeWithEntryDirective(field.type)
67
+ ) {
68
+ fields.push(field)
69
+ }
70
+ }
71
+ return fields
72
+ }
73
+
74
+ private buildsOnUnionType(type: GraphQLNullableType): boolean {
75
+ if (type instanceof GraphQLNonNull) {
76
+ return this.buildsOnUnionType(type.ofType)
77
+ } else if (type instanceof GraphQLList) {
78
+ return this.buildsOnUnionType(type.ofType)
79
+ } else if (type instanceof GraphQLUnionType) {
80
+ return true
81
+ } else if (type instanceof GraphQLObjectType) {
82
+ return false
83
+ }
84
+ return false
85
+ }
86
+
87
+ private collapseTypeField(
88
+ type: GraphQLNullableType,
89
+ fieldValue: Record<string, any> | Record<string, any>[],
90
+ ): Record<string, any> {
91
+ if (type instanceof GraphQLNonNull) {
92
+ return this.collapseTypeField(type.ofType, fieldValue)
93
+ } else if (type instanceof GraphQLList) {
94
+ if (!Array.isArray(fieldValue)) {
95
+ throw new Error('Expected array value')
96
+ }
97
+ return fieldValue.map((item) => this.collapseTypeField(type.ofType, item))
98
+ } else {
99
+ // Based on our @oneOf directive, we expect only one field whose name
100
+ // corresponds to the concrete type's name.
101
+ const firstKey = Object.keys(fieldValue)[0]
102
+ if (Array.isArray(fieldValue)) {
103
+ throw new Error('Did not expect array value')
104
+ }
105
+
106
+ // We replace the above name field that nests our concrete data directly
107
+ // with the data and a `__typename` field, so that our output data
108
+ // corresponds to the output schema provided by the user (i.e. there is
109
+ // no additional nesting level there).
110
+ return {
111
+ ...fieldValue[firstKey],
112
+ __typename: firstKey.slice(0, 1).toUpperCase() + firstKey.slice(1),
113
+ }
114
+ }
115
+ }
116
+ }
@@ -0,0 +1,70 @@
1
+ import { GraphQLField, GraphQLObjectType, GraphQLSchema } from 'graphql'
2
+ import { getDirective } from '@graphql-tools/utils'
3
+ import { GraphQLInterfaceType, GraphQLUnionType } from 'graphql/type/definition'
4
+ import { Kind } from 'graphql/language/kinds'
5
+ import { ApolloContext } from '../app/api.service'
6
+ import { EntryReferenceUtil } from './schema-utils/entry-reference-util'
7
+
8
+ export class SchemaAnalyzerService {
9
+ constructor(private readonly entryReferenceUtil: EntryReferenceUtil) {}
10
+
11
+ public analyzeSchema(schema: GraphQLSchema): ISchemaAnalyzerResult {
12
+ const result: ISchemaAnalyzerResult = {
13
+ entryDirectiveTypes: [],
14
+ objectTypes: [],
15
+ interfaceTypes: [],
16
+ unionTypes: [],
17
+ typesWithEntryReferences: [],
18
+ }
19
+
20
+ const typeMap = schema.getTypeMap()
21
+
22
+ for (const [key, type] of Object.entries(typeMap)) {
23
+ if (key.startsWith('__')) {
24
+ continue
25
+ }
26
+
27
+ if (type.astNode?.kind === Kind.OBJECT_TYPE_DEFINITION) {
28
+ const objectType = type as GraphQLObjectType
29
+ result.objectTypes.push(objectType)
30
+ const entityDirective = getDirective(schema, objectType, 'Entry')?.[0]
31
+ if (entityDirective) {
32
+ result.entryDirectiveTypes.push(objectType)
33
+ }
34
+ const fieldsWithEntryReference =
35
+ this.entryReferenceUtil.getFieldsWithReferenceToTypeWithEntryDirective(
36
+ objectType,
37
+ )
38
+ if (fieldsWithEntryReference.length > 0) {
39
+ result.typesWithEntryReferences.push({
40
+ type: objectType,
41
+ fields: fieldsWithEntryReference,
42
+ })
43
+ }
44
+ }
45
+
46
+ if (type.astNode?.kind === Kind.INTERFACE_TYPE_DEFINITION) {
47
+ const interfaceType = type as GraphQLInterfaceType
48
+ result.interfaceTypes.push(interfaceType)
49
+ }
50
+
51
+ if (type.astNode?.kind === Kind.UNION_TYPE_DEFINITION) {
52
+ const unionType = type as GraphQLUnionType
53
+ result.unionTypes.push(unionType)
54
+ }
55
+ }
56
+
57
+ return result
58
+ }
59
+ }
60
+
61
+ export interface ISchemaAnalyzerResult {
62
+ entryDirectiveTypes: GraphQLObjectType[]
63
+ objectTypes: GraphQLObjectType[]
64
+ interfaceTypes: GraphQLInterfaceType[]
65
+ unionTypes: GraphQLUnionType[]
66
+ typesWithEntryReferences: {
67
+ type: GraphQLObjectType<any, ApolloContext>
68
+ fields: GraphQLField<any, ApolloContext>[]
69
+ }[]
70
+ }