@commitspark/graphql-api 0.8.0 → 0.9.1

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 (36) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/app/api.service.js +1 -0
  3. package/dist/app/api.service.js.map +1 -1
  4. package/dist/graphql/input-type-generator.service.js +1 -1
  5. package/dist/graphql/input-type-generator.service.js.map +1 -1
  6. package/dist/graphql/queries-mutations-generator.service.js +1 -1
  7. package/dist/graphql/queries-mutations-generator.service.js.map +1 -1
  8. package/dist/graphql/resolver-generators/mutation-create-resolver-generator.js +17 -3
  9. package/dist/graphql/resolver-generators/mutation-create-resolver-generator.js.map +1 -1
  10. package/dist/graphql/resolver-generators/mutation-delete-resolver-generator.js +12 -0
  11. package/dist/graphql/resolver-generators/mutation-delete-resolver-generator.js.map +1 -1
  12. package/dist/graphql/resolver-generators/mutation-update.resolver-generator.js +12 -0
  13. package/dist/graphql/resolver-generators/mutation-update.resolver-generator.js.map +1 -1
  14. package/dist/graphql/schema-generator.service.js +10 -9
  15. package/dist/graphql/schema-generator.service.js.map +1 -1
  16. package/dist/graphql/schema-root-type-generator.service.js +3 -1
  17. package/dist/graphql/schema-root-type-generator.service.js.map +1 -1
  18. package/dist/persistence/persistence.service.d.ts +6 -9
  19. package/dist/persistence/persistence.service.js +15 -11
  20. package/dist/persistence/persistence.service.js.map +1 -1
  21. package/dist/tsconfig.build.tsbuildinfo +1 -1
  22. package/package.json +16 -5
  23. package/src/app/api.service.ts +2 -0
  24. package/src/graphql/input-type-generator.service.ts +1 -1
  25. package/src/graphql/queries-mutations-generator.service.ts +1 -1
  26. package/src/graphql/resolver-generators/mutation-create-resolver-generator.ts +23 -4
  27. package/src/graphql/resolver-generators/mutation-delete-resolver-generator.ts +20 -0
  28. package/src/graphql/resolver-generators/mutation-update.resolver-generator.ts +20 -0
  29. package/src/graphql/schema-generator.service.ts +12 -9
  30. package/src/graphql/schema-root-type-generator.service.ts +3 -1
  31. package/src/persistence/persistence.service.ts +28 -25
  32. package/.eslintrc.js +0 -24
  33. package/.prettierrc +0 -6
  34. package/tsconfig.build.json +0 -10
  35. package/tsconfig.json +0 -18
  36. package/webpack.config.js +0 -25
@@ -4,6 +4,7 @@ import {
4
4
  Entry,
5
5
  PersistenceService,
6
6
  } from '../../persistence/persistence.service'
7
+ import { GraphQLError } from 'graphql/error/GraphQLError'
7
8
 
8
9
  export class MutationCreateResolverGenerator {
9
10
  constructor(private readonly persistence: PersistenceService) {}
@@ -17,21 +18,39 @@ export class MutationCreateResolverGenerator {
17
18
  context: ApolloContext,
18
19
  info,
19
20
  ): 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(
21
+ let existingEntry
22
+ try {
23
+ existingEntry = await this.persistence.findById(
24
+ context.gitAdapter,
25
+ context.getCurrentRef(),
26
+ args.id,
27
+ )
28
+ } catch (_) {}
29
+ if (existingEntry) {
30
+ throw new GraphQLError(`An entry with id "${args.id}" already exists`, {
31
+ extensions: {
32
+ code: 'BAD_USER_INPUT',
33
+ argumentName: 'id',
34
+ },
35
+ })
36
+ }
37
+
38
+ // TODO validate ID references within args.data to assert referenced IDs exist and point to correct entry type
39
+ const commitResult = await this.persistence.createType(
22
40
  context.gitAdapter,
23
41
  context.branch,
24
42
  context.getCurrentRef(),
25
43
  typeName,
44
+ args.id,
26
45
  args.data,
27
46
  args.message,
28
47
  )
29
- context.setCurrentRef(createResult.ref)
48
+ context.setCurrentRef(commitResult.ref)
30
49
  return this.persistence.findByTypeId(
31
50
  context.gitAdapter,
32
51
  context.getCurrentRef(),
33
52
  typeName,
34
- createResult.id,
53
+ args.id,
35
54
  )
36
55
  }
37
56
  }
@@ -4,6 +4,7 @@ import {
4
4
  } from '../../persistence/persistence.service'
5
5
  import { GraphQLFieldResolver } from 'graphql/type/definition'
6
6
  import { ApolloContext } from '../../app/api.service'
7
+ import { GraphQLError } from 'graphql/error/GraphQLError'
7
8
 
8
9
  export class MutationDeleteResolverGenerator {
9
10
  constructor(private readonly persistence: PersistenceService) {}
@@ -17,6 +18,25 @@ export class MutationDeleteResolverGenerator {
17
18
  context: ApolloContext,
18
19
  info,
19
20
  ): Promise<Entry> => {
21
+ try {
22
+ await this.persistence.findByTypeId(
23
+ context.gitAdapter,
24
+ context.getCurrentRef(),
25
+ typeName,
26
+ args.id,
27
+ )
28
+ } catch (_) {
29
+ throw new GraphQLError(
30
+ `No entry of type "${typeName}" with id "${args.id}" exists`,
31
+ {
32
+ extensions: {
33
+ code: 'BAD_USER_INPUT',
34
+ argumentName: 'id',
35
+ },
36
+ },
37
+ )
38
+ }
39
+
20
40
  // TODO validate ID to delete is not referenced anywhere
21
41
  const deleteResult = await this.persistence.deleteByTypeId(
22
42
  context.gitAdapter,
@@ -4,6 +4,7 @@ import {
4
4
  } from '../../persistence/persistence.service'
5
5
  import { GraphQLFieldResolver } from 'graphql/type/definition'
6
6
  import { ApolloContext } from '../../app/api.service'
7
+ import { GraphQLError } from 'graphql/error/GraphQLError'
7
8
 
8
9
  export class MutationUpdateResolverGenerator {
9
10
  constructor(private readonly persistence: PersistenceService) {}
@@ -17,6 +18,25 @@ export class MutationUpdateResolverGenerator {
17
18
  context: ApolloContext,
18
19
  info,
19
20
  ): Promise<Entry> => {
21
+ try {
22
+ await this.persistence.findByTypeId(
23
+ context.gitAdapter,
24
+ context.getCurrentRef(),
25
+ typeName,
26
+ args.id,
27
+ )
28
+ } catch (_) {
29
+ throw new GraphQLError(
30
+ `No entry of type "${typeName}" with id "${args.id}" exists`,
31
+ {
32
+ extensions: {
33
+ code: 'BAD_USER_INPUT',
34
+ argumentName: 'id',
35
+ },
36
+ },
37
+ )
38
+ }
39
+
20
40
  // TODO validate ID references in payload to assert referenced ID exists and points to correct entry type
21
41
  const updateResult = await this.persistence.updateByTypeId(
22
42
  context.gitAdapter,
@@ -49,7 +49,8 @@ export class SchemaGeneratorService {
49
49
  }
50
50
  const schemaAnalyzerResult = this.schemaAnalyzer.analyzeSchema(schema)
51
51
 
52
- const filteredOriginalSchemaString = printSchemaWithDirectives(schema)
52
+ const filteredOriginalSchemaString =
53
+ printSchemaWithDirectives(schema) + '\n'
53
54
 
54
55
  const generatedIdInputTypeStrings =
55
56
  this.inputTypeGenerator.generateIdInputTypeStrings(schemaAnalyzerResult)
@@ -61,7 +62,7 @@ export class SchemaGeneratorService {
61
62
  const generatedTypeNameQuery =
62
63
  this.queriesMutationsGenerator.generateTypeNameQuery()
63
64
 
64
- let generatedEntryReferenceResolvers: Record<
65
+ const generatedEntryReferenceResolvers: Record<
65
66
  string,
66
67
  Record<
67
68
  string,
@@ -172,14 +173,16 @@ export class SchemaGeneratorService {
172
173
  }
173
174
  }
174
175
 
176
+ const typeDefs = [
177
+ filteredOriginalSchemaString,
178
+ generatedSchemaString,
179
+ generatedIdInputTypeStrings.join('\n'),
180
+ generatedObjectInputTypeStrings.join('\n'),
181
+ generatedUnionInputTypeStrings.join('\n'),
182
+ ].filter((typeDef) => typeDef.length > 0)
183
+
175
184
  return {
176
- typeDefs: [
177
- filteredOriginalSchemaString,
178
- generatedSchemaString,
179
- generatedIdInputTypeStrings.join('\n'),
180
- generatedObjectInputTypeStrings.join('\n'),
181
- generatedUnionInputTypeStrings.join('\n'),
182
- ],
185
+ typeDefs: typeDefs,
183
186
  resolvers: allGeneratedResolvers,
184
187
  }
185
188
  }
@@ -13,16 +13,18 @@ export class SchemaRootTypeGeneratorService {
13
13
  generatedSchemas
14
14
  .map((generated) => ' ' + generated.queryAll.schemaString)
15
15
  .join('\n') +
16
+ '\n' +
16
17
  generatedSchemas
17
18
  .map((generated) => ' ' + generated.queryAllMeta.schemaString)
18
19
  .join('\n') +
20
+ '\n' +
19
21
  generatedSchemas
20
22
  .map((generated) => ' ' + generated.queryById.schemaString)
21
23
  .join('\n') +
22
24
  '\n' +
23
25
  ` ${typeQuery.schemaString}` +
24
26
  '\n' +
25
- '}\n' +
27
+ '}\n\n' +
26
28
  'type Mutation {\n' +
27
29
  generatedSchemas
28
30
  .map((generated) => ' ' + generated.createMutation.schemaString)
@@ -1,17 +1,15 @@
1
- import { v4 as uuidv4 } from 'uuid'
2
1
  import { ContentEntry, GitAdapter } from '@commitspark/git-adapter'
2
+ import { GraphQLError } from 'graphql/error/GraphQLError'
3
3
 
4
4
  export class PersistenceService {
5
- constructor() {}
6
-
7
5
  public async getTypeById(
8
6
  gitAdapter: GitAdapter,
9
7
  commitHash: string,
10
- id: ID,
8
+ id: string,
11
9
  ): Promise<string> {
12
10
  const allEntries = await gitAdapter.getContentEntries(commitHash)
13
11
  const requestedEntry = allEntries.filter(
14
- (contentEntry: ContentEntry) => contentEntry.id === (id as string),
12
+ (contentEntry: ContentEntry) => contentEntry.id === id,
15
13
  )[0]
16
14
  if (requestedEntry === undefined) {
17
15
  throw new Error(`Not found: ${id}`)
@@ -23,11 +21,11 @@ export class PersistenceService {
23
21
  public async findById(
24
22
  gitAdapter: GitAdapter,
25
23
  commitHash: string,
26
- id: ID,
24
+ id: string,
27
25
  ): Promise<Entry> {
28
26
  const allEntries = await gitAdapter.getContentEntries(commitHash)
29
27
  const requestedEntry = allEntries.filter(
30
- (contentEntry: ContentEntry) => contentEntry.id === (id as string),
28
+ (contentEntry: ContentEntry) => contentEntry.id === id,
31
29
  )[0]
32
30
  if (requestedEntry === undefined) {
33
31
  throw new Error(`Not found: ${id}`)
@@ -53,11 +51,11 @@ export class PersistenceService {
53
51
  gitAdapter: GitAdapter,
54
52
  commitHash: string,
55
53
  type: string,
56
- id: ID,
54
+ id: string,
57
55
  ): Promise<Entry> {
58
56
  const allEntries = await gitAdapter.getContentEntries(commitHash)
59
57
  const requestedEntry = allEntries.filter(
60
- (contentEntry: ContentEntry) => contentEntry.id === (id as string),
58
+ (contentEntry: ContentEntry) => contentEntry.id === id,
61
59
  )[0]
62
60
  if (requestedEntry === undefined || requestedEntry.metadata.type !== type) {
63
61
  throw new Error(`Not found: ${type}, ${id}`)
@@ -70,21 +68,32 @@ export class PersistenceService {
70
68
  gitAdapter: GitAdapter,
71
69
  branch: string,
72
70
  commitHash: string,
73
- type: string,
71
+ typeName: string,
72
+ id: string,
74
73
  data: Entry,
75
74
  message: string,
76
75
  ): Promise<CommitResult> {
77
- const id = uuidv4()
76
+ // TODO expose regex publicly to allow reuse
77
+ const regex = /^[a-zA-Z\-_]{1,250}$/
78
+ if (!id.match(regex)) {
79
+ throw new GraphQLError(`"id" does not match regex "${regex}"`, {
80
+ extensions: {
81
+ code: 'BAD_USER_INPUT',
82
+ argumentName: 'id',
83
+ },
84
+ })
85
+ }
86
+
78
87
  const commit = await gitAdapter.createCommit({
79
88
  ref: branch,
80
89
  parentSha: commitHash,
81
90
  contentEntries: [
82
91
  {
83
92
  id: id,
84
- data: data,
85
93
  metadata: {
86
- type: type,
94
+ type: typeName,
87
95
  },
96
+ data: data,
88
97
  deletion: false,
89
98
  },
90
99
  ],
@@ -92,7 +101,6 @@ export class PersistenceService {
92
101
  })
93
102
 
94
103
  return {
95
- id,
96
104
  ref: commit.ref,
97
105
  }
98
106
  }
@@ -102,13 +110,13 @@ export class PersistenceService {
102
110
  branch: string,
103
111
  commitHash: string,
104
112
  type: string,
105
- id: ID,
113
+ id: string,
106
114
  data: Entry,
107
115
  message: string,
108
116
  ): Promise<CommitResult> {
109
117
  const allEntries = await gitAdapter.getContentEntries(commitHash)
110
118
  const requestedEntry = allEntries.filter(
111
- (contentEntry: ContentEntry) => contentEntry.id === (id as string),
119
+ (contentEntry: ContentEntry) => contentEntry.id === id,
112
120
  )[0]
113
121
  if (requestedEntry === undefined || requestedEntry.metadata.type !== type) {
114
122
  throw new Error(`Not found: ${type}, ${id}`)
@@ -122,8 +130,8 @@ export class PersistenceService {
122
130
  contentEntries: [
123
131
  {
124
132
  id: id,
125
- data: newData,
126
133
  metadata: requestedEntry.metadata,
134
+ data: newData,
127
135
  deletion: false,
128
136
  },
129
137
  ],
@@ -131,7 +139,6 @@ export class PersistenceService {
131
139
  })
132
140
 
133
141
  return {
134
- id,
135
142
  ref: commit.ref,
136
143
  }
137
144
  }
@@ -141,12 +148,12 @@ export class PersistenceService {
141
148
  branch: string,
142
149
  commitHash: string,
143
150
  type: string,
144
- id: ID,
151
+ id: string,
145
152
  message: string,
146
153
  ): Promise<CommitResult> {
147
154
  const allEntries = await gitAdapter.getContentEntries(commitHash)
148
155
  const requestedEntry = allEntries.filter(
149
- (contentEntry: ContentEntry) => contentEntry.id === (id as string),
156
+ (contentEntry: ContentEntry) => contentEntry.id === id,
150
157
  )[0]
151
158
  if (requestedEntry === undefined || requestedEntry.metadata.type !== type) {
152
159
  throw new Error(`Not found: ${type}, ${id}`)
@@ -158,8 +165,8 @@ export class PersistenceService {
158
165
  contentEntries: [
159
166
  {
160
167
  id: id,
161
- data: {},
162
168
  metadata: requestedEntry.metadata,
169
+ data: {},
163
170
  deletion: true,
164
171
  },
165
172
  ],
@@ -167,17 +174,13 @@ export class PersistenceService {
167
174
  })
168
175
 
169
176
  return {
170
- id,
171
177
  ref: commit.ref,
172
178
  }
173
179
  }
174
180
  }
175
181
 
176
182
  export interface CommitResult {
177
- id: ID
178
183
  ref: string // commit sha
179
184
  }
180
185
 
181
- export type ID = string
182
-
183
186
  export type Entry = Record<string, unknown>
package/.eslintrc.js DELETED
@@ -1,24 +0,0 @@
1
- module.exports = {
2
- parser: '@typescript-eslint/parser',
3
- parserOptions: {
4
- project: 'tsconfig.json',
5
- sourceType: 'module',
6
- },
7
- plugins: ['@typescript-eslint/eslint-plugin'],
8
- extends: [
9
- 'plugin:@typescript-eslint/recommended',
10
- 'plugin:prettier/recommended',
11
- ],
12
- root: true,
13
- env: {
14
- node: true,
15
- jest: true,
16
- },
17
- ignorePatterns: ['.eslintrc.js'],
18
- rules: {
19
- '@typescript-eslint/interface-name-prefix': 'off',
20
- '@typescript-eslint/explicit-function-return-type': 'off',
21
- '@typescript-eslint/explicit-module-boundary-types': 'off',
22
- '@typescript-eslint/no-explicit-any': 'off',
23
- },
24
- };
package/.prettierrc DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "semi": false,
3
- "singleQuote": true,
4
- "tabWidth": 2,
5
- "trailingComma": "all"
6
- }
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "exclude": [
4
- "node_modules",
5
- "dist",
6
- "test",
7
- "**/*spec.ts",
8
- "doc/example"
9
- ]
10
- }
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "module": "commonjs",
4
- "declaration": true,
5
- "removeComments": true,
6
- "emitDecoratorMetadata": true,
7
- "experimentalDecorators": true,
8
- "allowSyntheticDefaultImports": true,
9
- "target": "es2017",
10
- "sourceMap": true,
11
- "outDir": "./dist",
12
- "baseUrl": "./",
13
- "incremental": true,
14
- "skipLibCheck": true,
15
- "esModuleInterop": true,
16
- "strict": true
17
- }
18
- }
package/webpack.config.js DELETED
@@ -1,25 +0,0 @@
1
- const path = require('node:path')
2
- module.exports = (options, webpack) => {
3
- return {
4
- ...options,
5
- devtool: 'source-map', // create source map for debugging
6
- externals: [],
7
- entry: {
8
- main: './src/index.ts',
9
- },
10
- output: {
11
- ...options.output,
12
- libraryTarget: 'commonjs2',
13
- },
14
- plugins: [
15
- ...options.plugins,
16
- ],
17
- resolve: {
18
- // @see https://github.com/apollographql/apollo-server/issues/4983
19
- extensions: ['.ts', '.js'],
20
- alias: {
21
- graphql$: path.resolve(__dirname, 'node_modules/graphql/index.js'),
22
- },
23
- },
24
- }
25
- }