@axinom/mosaic-graphql-common 0.1.0-rc.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 (95) hide show
  1. package/README.md +16 -0
  2. package/dist/common/checks.d.ts +9 -0
  3. package/dist/common/checks.d.ts.map +1 -0
  4. package/dist/common/checks.js +21 -0
  5. package/dist/common/checks.js.map +1 -0
  6. package/dist/common/index.d.ts +3 -0
  7. package/dist/common/index.d.ts.map +1 -0
  8. package/dist/common/index.js +19 -0
  9. package/dist/common/index.js.map +1 -0
  10. package/dist/common/types.d.ts +13 -0
  11. package/dist/common/types.d.ts.map +1 -0
  12. package/dist/common/types.js +15 -0
  13. package/dist/common/types.js.map +1 -0
  14. package/dist/index.d.ts +4 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +20 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/plugins/add-error-codes-enum-plugin.d.ts +27 -0
  19. package/dist/plugins/add-error-codes-enum-plugin.d.ts.map +1 -0
  20. package/dist/plugins/add-error-codes-enum-plugin.js +78 -0
  21. package/dist/plugins/add-error-codes-enum-plugin.js.map +1 -0
  22. package/dist/plugins/annotate-types-with-permissions-plugin.d.ts +22 -0
  23. package/dist/plugins/annotate-types-with-permissions-plugin.d.ts.map +1 -0
  24. package/dist/plugins/annotate-types-with-permissions-plugin.js +145 -0
  25. package/dist/plugins/annotate-types-with-permissions-plugin.js.map +1 -0
  26. package/dist/plugins/deprecate-stray-node-id-fields-plugin.d.ts +14 -0
  27. package/dist/plugins/deprecate-stray-node-id-fields-plugin.d.ts.map +1 -0
  28. package/dist/plugins/deprecate-stray-node-id-fields-plugin.js +37 -0
  29. package/dist/plugins/deprecate-stray-node-id-fields-plugin.js.map +1 -0
  30. package/dist/plugins/generic-bulk-plugin-factory.d.ts +49 -0
  31. package/dist/plugins/generic-bulk-plugin-factory.d.ts.map +1 -0
  32. package/dist/plugins/generic-bulk-plugin-factory.js +181 -0
  33. package/dist/plugins/generic-bulk-plugin-factory.js.map +1 -0
  34. package/dist/plugins/graphiql-management-mode-plugin-hook.d.ts +13 -0
  35. package/dist/plugins/graphiql-management-mode-plugin-hook.d.ts.map +1 -0
  36. package/dist/plugins/graphiql-management-mode-plugin-hook.js +44 -0
  37. package/dist/plugins/graphiql-management-mode-plugin-hook.js.map +1 -0
  38. package/dist/plugins/index.d.ts +10 -0
  39. package/dist/plugins/index.d.ts.map +1 -0
  40. package/dist/plugins/index.js +26 -0
  41. package/dist/plugins/index.js.map +1 -0
  42. package/dist/plugins/omit-from-query-root-plugin.d.ts +17 -0
  43. package/dist/plugins/omit-from-query-root-plugin.d.ts.map +1 -0
  44. package/dist/plugins/omit-from-query-root-plugin.js +43 -0
  45. package/dist/plugins/omit-from-query-root-plugin.js.map +1 -0
  46. package/dist/plugins/operations-enum-generator-plugin-factory.d.ts +15 -0
  47. package/dist/plugins/operations-enum-generator-plugin-factory.d.ts.map +1 -0
  48. package/dist/plugins/operations-enum-generator-plugin-factory.js +108 -0
  49. package/dist/plugins/operations-enum-generator-plugin-factory.js.map +1 -0
  50. package/dist/plugins/subscriptions-plugin-factory.d.ts +9 -0
  51. package/dist/plugins/subscriptions-plugin-factory.d.ts.map +1 -0
  52. package/dist/plugins/subscriptions-plugin-factory.js +67 -0
  53. package/dist/plugins/subscriptions-plugin-factory.js.map +1 -0
  54. package/dist/plugins/validation-directives-plugin.d.ts +6 -0
  55. package/dist/plugins/validation-directives-plugin.d.ts.map +1 -0
  56. package/dist/plugins/validation-directives-plugin.js +117 -0
  57. package/dist/plugins/validation-directives-plugin.js.map +1 -0
  58. package/dist/postgraphile/enhance-graphql-errors.d.ts +48 -0
  59. package/dist/postgraphile/enhance-graphql-errors.d.ts.map +1 -0
  60. package/dist/postgraphile/enhance-graphql-errors.js +67 -0
  61. package/dist/postgraphile/enhance-graphql-errors.js.map +1 -0
  62. package/dist/postgraphile/index.d.ts +4 -0
  63. package/dist/postgraphile/index.d.ts.map +1 -0
  64. package/dist/postgraphile/index.js +20 -0
  65. package/dist/postgraphile/index.js.map +1 -0
  66. package/dist/postgraphile/postgraphile-options-builder.d.ts +273 -0
  67. package/dist/postgraphile/postgraphile-options-builder.d.ts.map +1 -0
  68. package/dist/postgraphile/postgraphile-options-builder.js +419 -0
  69. package/dist/postgraphile/postgraphile-options-builder.js.map +1 -0
  70. package/dist/postgraphile/websocket-utils.d.ts +11 -0
  71. package/dist/postgraphile/websocket-utils.d.ts.map +1 -0
  72. package/dist/postgraphile/websocket-utils.js +17 -0
  73. package/dist/postgraphile/websocket-utils.js.map +1 -0
  74. package/package.json +61 -0
  75. package/src/common/checks.ts +23 -0
  76. package/src/common/index.ts +2 -0
  77. package/src/common/types.ts +15 -0
  78. package/src/index.ts +3 -0
  79. package/src/plugins/add-error-codes-enum-plugin.ts +102 -0
  80. package/src/plugins/annotate-types-with-permissions-plugin.spec.ts +158 -0
  81. package/src/plugins/annotate-types-with-permissions-plugin.ts +205 -0
  82. package/src/plugins/deprecate-stray-node-id-fields-plugin.ts +41 -0
  83. package/src/plugins/generic-bulk-plugin-factory.ts +313 -0
  84. package/src/plugins/graphiql-management-mode-plugin-hook.ts +46 -0
  85. package/src/plugins/index.ts +9 -0
  86. package/src/plugins/omit-from-query-root-plugin.ts +69 -0
  87. package/src/plugins/operations-enum-generator-plugin-factory.ts +130 -0
  88. package/src/plugins/subscriptions-plugin-factory.ts +114 -0
  89. package/src/plugins/validation-directives-plugin.ts +141 -0
  90. package/src/postgraphile/enhance-graphql-errors.spec.ts +241 -0
  91. package/src/postgraphile/enhance-graphql-errors.ts +138 -0
  92. package/src/postgraphile/index.ts +3 -0
  93. package/src/postgraphile/postgraphile-options-builder.spec.ts +744 -0
  94. package/src/postgraphile/postgraphile-options-builder.ts +510 -0
  95. package/src/postgraphile/websocket-utils.ts +19 -0
@@ -0,0 +1,141 @@
1
+ import { camelCase, Plugin, SchemaBuilder } from 'graphile-build';
2
+ import inflection from 'inflection';
3
+ import { Dict } from '../common';
4
+
5
+ interface Constraint {
6
+ id: number;
7
+ name: string;
8
+ tableId: number;
9
+ tableIndexes: [number];
10
+ definition: string;
11
+ }
12
+
13
+ interface Constraints {
14
+ [Key: string]: Constraint[];
15
+ }
16
+
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ async function loadDbConstraints(builder: SchemaBuilder): Promise<any[]> {
19
+ const constraintQuery = `
20
+ SELECT
21
+ oid, conname, conrelid, conkey, pg_get_constraintdef(oid) AS definition
22
+ FROM
23
+ pg_constraint
24
+ WHERE
25
+ contype = 'c' AND conrelid > 0;`;
26
+
27
+ // this pgConfig is not part of the public TS builder API so it may change
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ const queryResult = await (builder as any).options.pgConfig.query(
30
+ constraintQuery,
31
+ );
32
+ return queryResult.rows;
33
+ }
34
+
35
+ async function getConstraints(builder: SchemaBuilder): Promise<Constraints> {
36
+ const constraints: Constraints = {};
37
+ const rows = await loadDbConstraints(builder);
38
+ for (const row of rows) {
39
+ const key = `${row.conrelid}`;
40
+ constraints[key] = constraints[key] || [];
41
+ constraints[key].push({
42
+ id: row.oid,
43
+ name: row.conname,
44
+ tableId: row.conrelid,
45
+ definition: row.definition,
46
+ tableIndexes: row.conkey,
47
+ });
48
+ }
49
+ return constraints;
50
+ }
51
+
52
+ function mapCheckToValidation(constraint: string): string {
53
+ const regex =
54
+ /CHECK\s*\(\s*([a-zA-Z0-9_]+\.){0,1}([a-zA-Z0-9_]+)\s*\(\s*(['a-zA-Z0-9_]+)\s*,\s*(['a-zA-Z0-9_]+)/gm;
55
+
56
+ const result = regex.exec(constraint);
57
+ if (result?.length && result.length > 4) {
58
+ switch (result[2]) {
59
+ // custom handle when parameters are used - otherwise default is fine
60
+ case 'constraint_max_length':
61
+ return `@maxLength(${result[4]})`;
62
+ case 'constraint_min_length':
63
+ return `@minLength(${result[4]})`;
64
+ default:
65
+ // eslint-disable-next-line no-case-declarations
66
+ const constraintName = inflection.camelize(
67
+ result[2].replace('constraint_', ''),
68
+ true,
69
+ );
70
+ return `@${constraintName}()`;
71
+ }
72
+ }
73
+
74
+ return '';
75
+ }
76
+ /**
77
+ * Plugin that adds validation directives to graphql schema descriptions.
78
+ */
79
+ export const ValidationDirectivesPlugin: Plugin = async (builder) => {
80
+ const constraints = await getConstraints(builder);
81
+
82
+ builder.hook('GraphQLInputObjectType:fields', (fields, _build, context) => {
83
+ const tableConstraints =
84
+ constraints[`${context.scope.pgIntrospection?.id}`];
85
+ const columnMappings: Dict<{ annotations: string[] }> = {};
86
+ // check if there are any constraints in the DB
87
+ if (tableConstraints && Object.keys(fields).length) {
88
+ for (const constraint of tableConstraints) {
89
+ for (const tableIndex of constraint.tableIndexes) {
90
+ // find the DB column by its index with 'pg_constraint.conkey'
91
+ const column = context.scope.pgIntrospection.attributes.find(
92
+ (at: { num: number }) => at.num === tableIndex,
93
+ );
94
+
95
+ const validation = mapCheckToValidation(constraint.definition);
96
+ if (validation) {
97
+ // Converting the column name to camelCase to follow the general GraphQL naming pattern when adding it to the description.
98
+ const prop = camelCase(column.name);
99
+ columnMappings[prop] = columnMappings[prop] ?? { annotations: [] };
100
+ columnMappings[prop].annotations.push(validation);
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ for (const prop in columnMappings) {
107
+ const f = fields[prop];
108
+ if (f) {
109
+ const annotations = columnMappings[prop].annotations.sort().join('\n');
110
+ f.description = f.description
111
+ ? `${f.description}\n${annotations}`
112
+ : annotations;
113
+ }
114
+ }
115
+
116
+ return fields;
117
+ });
118
+
119
+ builder.hook('GraphQLSchema', (schemaConfig /*, build*/) => {
120
+ return schemaConfig;
121
+ // The following would allow to add custom directives.
122
+ // They can only be used on the server! They are not sent back to clients.
123
+ // return {
124
+ // ...schemaConfig,
125
+ // directives: [
126
+ // ...(schemaConfig.directives || []),
127
+ // build.newWithHooks(
128
+ // build.graphql.GraphQLDirective,
129
+ // {
130
+ // name: 'upper',
131
+ // locations: ['FIELD_DEFINITION', 'FIELD'],
132
+ // args: {
133
+ // name: { type: build.graphql.GraphQLString },
134
+ // },
135
+ // },
136
+ // { isUpperDirective: true },
137
+ // ),
138
+ // ],
139
+ // };
140
+ });
141
+ };
@@ -0,0 +1,241 @@
1
+ import { GraphQLError, Source } from 'graphql';
2
+ import 'jest-extended';
3
+ import {
4
+ GeneralGraphQlErrorCode,
5
+ GraphQlOperation,
6
+ enhanceGraphqlErrors,
7
+ } from './enhance-graphql-errors';
8
+
9
+ const iso8601Regex =
10
+ /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([0-1][0-9]|2[0-3])(:[0-5][0-9]){2}(\.[0-9]{0,6})?(Z|\+00:00)$/;
11
+ const UNIT_TEST_ERROR = 'UNIT_TEST_ERROR';
12
+
13
+ describe('graphqlErrorHandler', () => {
14
+ let timestampBeforeTest: Date;
15
+ const mockLog = jest.fn();
16
+ const dateAfterTestStart = (received: string | Date): void => {
17
+ const parsedDate = new Date(received).getTime();
18
+ expect(parsedDate).toBeGreaterThanOrEqual(timestampBeforeTest.getTime());
19
+ };
20
+
21
+ beforeEach(async () => {
22
+ const date = new Date();
23
+ date.setSeconds(date.getSeconds() - 10);
24
+ timestampBeforeTest = date;
25
+ });
26
+
27
+ afterEach(async () => {
28
+ jest.restoreAllMocks();
29
+ });
30
+
31
+ it('minimal internal server error -> valid response', async () => {
32
+ // Arrange
33
+ const gqlError = new GraphQLError(
34
+ 'test',
35
+ undefined,
36
+ null,
37
+ null,
38
+ null,
39
+ new Error('original'),
40
+ {
41
+ code: UNIT_TEST_ERROR,
42
+ },
43
+ );
44
+
45
+ // Act
46
+ const enhancedErrors = enhanceGraphqlErrors([gqlError]);
47
+
48
+ // Assert
49
+ expect(enhancedErrors).toHaveLength(1);
50
+ const enhancedError = enhancedErrors[0];
51
+ expect(enhancedError.timestamp).toMatch(iso8601Regex);
52
+ dateAfterTestStart(enhancedError.timestamp);
53
+ expect(enhancedError).toEqual({
54
+ timestamp: enhancedError.timestamp,
55
+ message: 'test',
56
+ details: undefined,
57
+ code: GeneralGraphQlErrorCode,
58
+ extensions: {
59
+ exception: {
60
+ code: GeneralGraphQlErrorCode,
61
+ },
62
+ operationName: undefined,
63
+ timestamp: expect.any(String),
64
+ },
65
+ });
66
+ expect(Object.keys(enhancedError)).toHaveLength(5);
67
+ });
68
+
69
+ it('multiple minimal errors -> valid response', async () => {
70
+ // Arrange
71
+ const gqlError = new GraphQLError(
72
+ 'test',
73
+ undefined,
74
+ null,
75
+ null,
76
+ null,
77
+ new Error('original'),
78
+ null,
79
+ );
80
+
81
+ // Act
82
+ const enhancedErrors = enhanceGraphqlErrors([gqlError, gqlError]);
83
+
84
+ // Assert
85
+ expect(enhancedErrors).toHaveLength(2);
86
+ dateAfterTestStart(enhancedErrors[0].timestamp);
87
+ dateAfterTestStart(enhancedErrors[1].timestamp);
88
+ expect(enhancedErrors[0].code).toBe(GeneralGraphQlErrorCode);
89
+ expect(enhancedErrors[1].code).toBe(GeneralGraphQlErrorCode);
90
+ expect(enhancedErrors[0].message).toBe('test');
91
+ expect(enhancedErrors[1].message).toBe('test');
92
+ });
93
+
94
+ it('enhances and logs GraphQL errors', () => {
95
+ // Arrange
96
+ const gqlErrors = [
97
+ new GraphQLError(
98
+ 'test',
99
+ undefined,
100
+ null,
101
+ null,
102
+ null,
103
+ new Error('original'),
104
+ {
105
+ code: UNIT_TEST_ERROR,
106
+ },
107
+ ),
108
+ ];
109
+
110
+ // Act
111
+ const enhancedErrors = enhanceGraphqlErrors(gqlErrors, undefined, mockLog);
112
+
113
+ // Assert
114
+ expect(enhancedErrors).toHaveLength(1);
115
+ expect(mockLog).toHaveBeenCalledTimes(1);
116
+ });
117
+
118
+ it('enhances GraphQL errors with additional error mapper', () => {
119
+ // Arrange
120
+ const gqlErrors = [
121
+ new GraphQLError(
122
+ 'test',
123
+ undefined,
124
+ null,
125
+ null,
126
+ null,
127
+ new Error('original'),
128
+ {
129
+ code: UNIT_TEST_ERROR,
130
+ },
131
+ ),
132
+ ];
133
+ const errorMapper = (error: GraphQLError) => ({
134
+ ...error,
135
+ code: 'CUSTOM_ERROR_CODE',
136
+ });
137
+
138
+ // Act
139
+ const enhancedErrors = enhanceGraphqlErrors(
140
+ gqlErrors,
141
+ 'test',
142
+ errorMapper,
143
+ undefined,
144
+ );
145
+
146
+ // Assert
147
+ expect(enhancedErrors).toHaveLength(1);
148
+ expect(enhancedErrors[0].code).toBe('CUSTOM_ERROR_CODE');
149
+ });
150
+
151
+ it('contains all original data', () => {
152
+ // Arrange
153
+ const gqlError = new GraphQLError(
154
+ 'internal',
155
+ undefined,
156
+ new Source(`mutation DeleteAsset {
157
+ deleteAsset(input: {id: 1}) {
158
+ asset {
159
+ id
160
+ title
161
+ }
162
+ }
163
+ }`),
164
+ [11],
165
+ ['deleteAsset'],
166
+ null,
167
+ {
168
+ code: UNIT_TEST_ERROR,
169
+ },
170
+ );
171
+
172
+ // Act
173
+ const enhancedErrors = enhanceGraphqlErrors(
174
+ [gqlError],
175
+ undefined,
176
+ undefined,
177
+ );
178
+
179
+ // Assert
180
+ expect(enhancedErrors).toHaveLength(1);
181
+ const enhancedError = enhancedErrors[0];
182
+ expect(enhancedError).toEqual({
183
+ timestamp: enhancedError.timestamp,
184
+ locations: [
185
+ {
186
+ column: 12,
187
+ line: 1,
188
+ },
189
+ ],
190
+ message: 'internal',
191
+ path: ['deleteAsset'],
192
+ details: undefined,
193
+ code: GeneralGraphQlErrorCode,
194
+ extensions: {
195
+ exception: {
196
+ code: GeneralGraphQlErrorCode,
197
+ },
198
+ operationName: undefined,
199
+ timestamp: expect.any(String),
200
+ },
201
+ });
202
+ });
203
+
204
+ it('maps the request data for logging', () => {
205
+ // Arrange
206
+ const gqlError = new GraphQLError(
207
+ 'internal',
208
+ undefined,
209
+ new Source(`mutation MyDeleteAsset {
210
+ deleteAsset(input: {id: 1}) {
211
+ asset {
212
+ id
213
+ title
214
+ }
215
+ }
216
+ }`),
217
+ [11],
218
+ ['deleteAsset'],
219
+ null,
220
+ {
221
+ code: UNIT_TEST_ERROR,
222
+ },
223
+ );
224
+ let requestInfo: GraphQlOperation[] | undefined = undefined;
225
+
226
+ // Act
227
+ enhanceGraphqlErrors([gqlError], undefined, undefined, (_e, r) => {
228
+ requestInfo = r;
229
+ });
230
+
231
+ // Assert
232
+ expect(requestInfo).toBeDefined();
233
+ expect(requestInfo).toEqual([
234
+ {
235
+ name: 'MyDeleteAsset',
236
+ operation: 'mutation',
237
+ rootEndpoints: ['deleteAsset'],
238
+ },
239
+ ]);
240
+ });
241
+ });
@@ -0,0 +1,138 @@
1
+ import {
2
+ FieldNode,
3
+ GraphQLError,
4
+ OperationDefinitionNode,
5
+ OperationTypeNode,
6
+ Source,
7
+ parse,
8
+ } from 'graphql';
9
+ import { GraphQLErrorExtended } from 'postgraphile';
10
+ import { Dict } from '../common';
11
+
12
+ /**
13
+ * A GraphQLErrorExtended object with additional properties.
14
+ * This error is thrown by the GraphQL API and returned to the original requester.
15
+ */
16
+ export declare type GraphQLErrorEnhanced = GraphQLErrorExtended & {
17
+ extensions: {
18
+ /** The timestamp when the error was raised */
19
+ timestamp: string;
20
+ };
21
+ //TODO: remove backwards compatibility after 01-11-2023
22
+ timestamp: string;
23
+ code: string;
24
+ details?: Dict<unknown>;
25
+ };
26
+
27
+ export const GeneralGraphQlErrorCode = 'GENERAL_GRAPHQL_ERROR';
28
+
29
+ /**
30
+ * Contains data about the failed GraphQL request without exposing passed variables, since they can contain sensitive values.
31
+ */
32
+ export interface GraphQlOperation {
33
+ /**
34
+ * Type of operation, e.g. query, mutation or subscription.
35
+ */
36
+ operationType?: OperationTypeNode;
37
+ /**
38
+ * Name of operation, if defined.
39
+ */
40
+ operationName?: string;
41
+ /**
42
+ * List of root endpoints, since a single operation can perform multiple queries/mutations.
43
+ */
44
+ rootEndpoints?: string[];
45
+ }
46
+
47
+ const basicErrorMapper = (
48
+ error: GraphQLError,
49
+ operationName?: string,
50
+ ): GraphQLErrorEnhanced => {
51
+ return {
52
+ ...error,
53
+ message: error.message ?? 'No error description was provided',
54
+ extensions: {
55
+ timestamp: new Date().toISOString(),
56
+ operationName,
57
+ exception: {
58
+ code: GeneralGraphQlErrorCode,
59
+ },
60
+ },
61
+ //TODO: remove backwards compatibility after 01-11-2023
62
+ timestamp: new Date().toISOString(),
63
+ code: GeneralGraphQlErrorCode,
64
+ details: undefined,
65
+ };
66
+ };
67
+
68
+ const getGraphQlOperations = (
69
+ requestBody: string,
70
+ ): GraphQlOperation[] | undefined => {
71
+ try {
72
+ const parsedGqlRequest = parse(requestBody);
73
+ return (parsedGqlRequest.definitions as OperationDefinitionNode[]).map(
74
+ (op) => ({
75
+ name: op.name?.value ?? 'NO_OPERATION_NAME',
76
+ operation: op.operation,
77
+ rootEndpoints: op.selectionSet.selections.map(
78
+ (selection) => (selection as FieldNode).name.value,
79
+ ),
80
+ }),
81
+ );
82
+ } catch {
83
+ // Unable to parse the GraphQL request string.
84
+ return undefined;
85
+ }
86
+ };
87
+
88
+ /**
89
+ * Default graphql error handler. Extracts information to return as an error response (timestamp, message, code and path parameters) and logs error and response information using provided logger.
90
+ *
91
+ * @param errors - Array of original graphql errors
92
+ * @param operationName - The operation name that was executed in case the request contained more than one operation. Found in `req.body.operationName`.
93
+ * @param customizeFields - Override error fields message, exception code, and add extensions fields with your custom values. Extension fields are added to the extensions object and cannot use existing field names.
94
+ * @param log - allows you to log the error with parts of the GraphQL request data
95
+ */
96
+ export const enhanceGraphqlErrors = (
97
+ errors: readonly GraphQLError[],
98
+ operationName?: string,
99
+ customizeFields?: (
100
+ error: GraphQLError,
101
+ originalError?: Error | null,
102
+ ) =>
103
+ | {
104
+ message?: string;
105
+ code?: string;
106
+ extensions?: Dict<unknown>;
107
+ }
108
+ | undefined,
109
+ log?: (error: GraphQLErrorEnhanced, operations?: GraphQlOperation[]) => void,
110
+ ): GraphQLErrorEnhanced[] => {
111
+ return errors.map((error) => {
112
+ const enhancedError = basicErrorMapper(error, operationName);
113
+
114
+ if (customizeFields) {
115
+ const custom = customizeFields(error, error.originalError);
116
+ enhancedError.message = custom?.message ?? enhancedError.message;
117
+ enhancedError.extensions = {
118
+ ...custom?.extensions,
119
+ ...enhancedError.extensions,
120
+ };
121
+ enhancedError.extensions.exception.code =
122
+ custom?.code ?? enhancedError.extensions.exception.code;
123
+
124
+ //TODO: remove backwards compatibility after 01-11-2023
125
+ enhancedError.code = enhancedError.extensions.exception.code;
126
+ enhancedError.details = custom?.extensions;
127
+ }
128
+
129
+ if (log) {
130
+ const originalError = error.originalError as Error & { source: Source };
131
+ const request = error.source?.body || originalError?.source?.body;
132
+ const graphQlOperations = getGraphQlOperations(request);
133
+ log(enhancedError, graphQlOperations);
134
+ }
135
+
136
+ return enhancedError;
137
+ });
138
+ };
@@ -0,0 +1,3 @@
1
+ export * from './enhance-graphql-errors';
2
+ export * from './postgraphile-options-builder';
3
+ export * from './websocket-utils';