@graphoria/server 0.1.5 → 0.2.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.
- package/dist/src/analyzeQuery/resolveVariables.d.ts.map +1 -1
- package/dist/src/authentication/jwt.d.ts.map +1 -1
- package/dist/src/authentication/paseto.d.ts.map +1 -1
- package/dist/src/authentication/tokenRepository.d.ts +2 -1
- package/dist/src/authentication/tokenRepository.d.ts.map +1 -1
- package/dist/src/config/helpers/operationHelper.d.ts +60 -6
- package/dist/src/config/helpers/operationHelper.d.ts.map +1 -1
- package/dist/src/config/helpers/operationHelper.test.d.ts +2 -0
- package/dist/src/config/helpers/operationHelper.test.d.ts.map +1 -0
- package/dist/src/config/index.d.ts +65 -60
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/types/ai.d.ts +9 -3
- package/dist/src/config/types/ai.d.ts.map +1 -1
- package/dist/src/config/types/auth.d.ts +55 -15
- package/dist/src/config/types/auth.d.ts.map +1 -1
- package/dist/src/config/types/cron.d.ts +96 -2
- package/dist/src/config/types/cron.d.ts.map +1 -1
- package/dist/src/config/types/db.d.ts +102 -29
- package/dist/src/config/types/db.d.ts.map +1 -1
- package/dist/src/config/types/index.d.ts +1 -1
- package/dist/src/config/types/index.d.ts.map +1 -1
- package/dist/src/config/types/operation.d.ts +49 -17
- package/dist/src/config/types/operation.d.ts.map +1 -1
- package/dist/src/config/types/queue.d.ts +61 -14
- package/dist/src/config/types/queue.d.ts.map +1 -1
- package/dist/src/config/types/remote-rest.d.ts +10 -1
- package/dist/src/config/types/remote-rest.d.ts.map +1 -1
- package/dist/src/config/types/remote-schema.d.ts +13 -3
- package/dist/src/config/types/remote-schema.d.ts.map +1 -1
- package/dist/src/config/types/virtual-columns.d.ts +1 -1
- package/dist/src/config/types/virtual-columns.d.ts.map +1 -1
- package/dist/src/configuration/getSchemas/index.d.ts +6 -4
- package/dist/src/configuration/getSchemas/index.d.ts.map +1 -1
- package/dist/src/configuration/getSchemas/mergeEntities/index.d.ts +9 -2
- package/dist/src/configuration/getSchemas/mergeEntities/index.d.ts.map +1 -1
- package/dist/src/configuration/getSchemas/mergeEntities/index.test.d.ts +2 -0
- package/dist/src/configuration/getSchemas/mergeEntities/index.test.d.ts.map +1 -0
- package/dist/src/configuration/getSchemas/type-definition-generator/index.d.ts.map +1 -1
- package/dist/src/configuration/index.d.ts +5 -4
- package/dist/src/configuration/index.d.ts.map +1 -1
- package/dist/src/configuration/rest/generateOpenAPI.d.ts.map +1 -1
- package/dist/src/configuration/rest/handleRESTRequestFactory.d.ts.map +1 -1
- package/dist/src/configuration/rest/parseStringParams.d.ts +15 -0
- package/dist/src/configuration/rest/parseStringParams.d.ts.map +1 -0
- package/dist/src/configuration/rest/parseStringParams.test.d.ts +2 -0
- package/dist/src/configuration/rest/parseStringParams.test.d.ts.map +1 -0
- package/dist/src/databases/common.d.ts +9 -1
- package/dist/src/databases/common.d.ts.map +1 -1
- package/dist/src/databases/directives.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/connection.d.ts +1 -1
- package/dist/src/databases/engines/mssql/connection.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/getStructure.d.ts +1 -0
- package/dist/src/databases/engines/mssql/getStructure.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/query/index.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/connection.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/getStructure.d.ts +1 -0
- package/dist/src/databases/engines/mysql/getStructure.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/placeholders.d.ts +22 -0
- package/dist/src/databases/engines/mysql/placeholders.d.ts.map +1 -0
- package/dist/src/databases/engines/mysql/placeholders.test.d.ts +2 -0
- package/dist/src/databases/engines/mysql/placeholders.test.d.ts.map +1 -0
- package/dist/src/databases/engines/mysql/query/index.d.ts.map +1 -1
- package/dist/src/databases/engines/postgresql/getStructure.d.ts +1 -0
- package/dist/src/databases/engines/postgresql/getStructure.d.ts.map +1 -1
- package/dist/src/databases/engines/postgresql/query/index.d.ts.map +1 -1
- package/dist/src/databases/high-level-operations.d.ts +1 -0
- package/dist/src/databases/high-level-operations.d.ts.map +1 -1
- package/dist/src/databases/identifierMapping.test.d.ts +2 -0
- package/dist/src/databases/identifierMapping.test.d.ts.map +1 -0
- package/dist/src/databases/metadata/structure.d.ts +1 -0
- package/dist/src/databases/metadata/structure.d.ts.map +1 -1
- package/dist/src/databases/sqlTypeUtils.d.ts.map +1 -1
- package/dist/src/databases/transformers/data-transformers.d.ts +1 -0
- package/dist/src/databases/transformers/data-transformers.d.ts.map +1 -1
- package/dist/src/databases/transformers/genResolverName.d.ts.map +1 -1
- package/dist/src/databases/transformers/graphqlName.d.ts +23 -0
- package/dist/src/databases/transformers/graphqlName.d.ts.map +1 -0
- package/dist/src/databases/transformers/graphqlName.test.d.ts +2 -0
- package/dist/src/databases/transformers/graphqlName.test.d.ts.map +1 -0
- package/dist/src/singletons/env.d.ts +2 -2
- package/dist/src/types/env.d.ts +56 -56
- package/dist/src/types/resolver.d.ts +12 -2
- package/dist/src/types/resolver.d.ts.map +1 -1
- package/dist/src/types/zod/auth.d.ts +6 -6
- package/dist/src/types/zod/configuration.d.ts +52 -52
- package/dist/src/types/zod/configuration.d.ts.map +1 -1
- package/dist/src/types/zod/cron.d.ts +1 -1
- package/dist/src/types/zod/cron.d.ts.map +1 -1
- package/dist/src/types/zod/db.d.ts +21 -12
- package/dist/src/types/zod/db.d.ts.map +1 -1
- package/dist/src/types/zod/queue.d.ts +38 -38
- package/dist/src/types/zod/strict-keys.test.d.ts +2 -0
- package/dist/src/types/zod/strict-keys.test.d.ts.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/analyzeQuery/resolveVariables.ts +37 -7
- package/src/authentication/jwt.ts +3 -1
- package/src/authentication/paseto.ts +3 -1
- package/src/authentication/tokenRepository.ts +12 -4
- package/src/config/helpers/operationHelper.ts +146 -5
- package/src/config/index.ts +139 -62
- package/src/config/types/ai.ts +2 -2
- package/src/config/types/auth.ts +37 -6
- package/src/config/types/cron.ts +1 -1
- package/src/config/types/db.ts +10 -10
- package/src/config/types/index.ts +2 -0
- package/src/config/types/operation.ts +49 -9
- package/src/config/types/queue.ts +8 -8
- package/src/config/types/remote-rest.ts +1 -1
- package/src/config/types/remote-schema.ts +2 -2
- package/src/config/types/virtual-columns.ts +1 -1
- package/src/configuration/getSchemas/mergeEntities/index.ts +32 -1
- package/src/configuration/getSchemas/type-definition-generator/index.ts +23 -9
- package/src/configuration/rest/generateOpenAPI.ts +5 -9
- package/src/configuration/rest/handleRESTRequestFactory.ts +38 -14
- package/src/configuration/rest/parseStringParams.ts +92 -0
- package/src/databases/common.ts +163 -66
- package/src/databases/directives.ts +26 -8
- package/src/databases/engines/mssql/connection.ts +1 -1
- package/src/databases/engines/mssql/query/index.ts +47 -16
- package/src/databases/engines/mysql/auth.ts +2 -2
- package/src/databases/engines/mysql/connection.ts +5 -4
- package/src/databases/engines/mysql/getStructure.ts +61 -30
- package/src/databases/engines/mysql/placeholders.ts +84 -0
- package/src/databases/engines/mysql/query/index.ts +68 -10
- package/src/databases/engines/postgresql/query/index.ts +59 -7
- package/src/databases/high-level-operations.ts +2 -2
- package/src/databases/sqlTypeUtils.ts +3 -1
- package/src/databases/transformers/genResolverName.ts +3 -3
- package/src/databases/transformers/graphqlName.ts +28 -0
- package/src/subscriptions/strategies/index.ts +1 -1
- package/src/subscriptions/strategies/queue.ts +1 -1
- package/src/types/resolver.ts +17 -1
- package/src/types/zod/configuration.ts +1 -1
- package/src/types/zod/db.ts +4 -0
|
@@ -16,22 +16,28 @@ import {
|
|
|
16
16
|
isAggregationField,
|
|
17
17
|
isSingleQuery,
|
|
18
18
|
processFieldSelectionsPG,
|
|
19
|
+
sqlColumnName,
|
|
19
20
|
} from "../../../common";
|
|
20
21
|
|
|
22
|
+
import { applyDirectives } from "../../../directives";
|
|
23
|
+
|
|
21
24
|
// Generate CTE for aggregations
|
|
22
25
|
const buildAggregationCTE = (
|
|
26
|
+
entities: MergedEntities,
|
|
27
|
+
tableName: string,
|
|
23
28
|
groupByInfo: GroupByInfo,
|
|
24
29
|
dottedQuotedName: string,
|
|
25
30
|
tableAlias: string,
|
|
26
31
|
whereClause: string,
|
|
27
32
|
): string => {
|
|
28
33
|
const { groupByFields, aggregations, cteAlias } = groupByInfo;
|
|
34
|
+
const sqlName = (fieldName: string) => sqlColumnName(entities, tableName, fieldName);
|
|
29
35
|
|
|
30
36
|
const selectClauses: string[] = [];
|
|
31
37
|
|
|
32
38
|
// Add group by fields
|
|
33
39
|
groupByFields.forEach((field) => {
|
|
34
|
-
selectClauses.push(`${tableAlias}."${field}"`);
|
|
40
|
+
selectClauses.push(`${tableAlias}."${sqlName(field)}"`);
|
|
35
41
|
});
|
|
36
42
|
|
|
37
43
|
// Add aggregations
|
|
@@ -40,11 +46,11 @@ const buildAggregationCTE = (
|
|
|
40
46
|
selectClauses.push(`COUNT(*) AS ${agg.alias}`);
|
|
41
47
|
} else {
|
|
42
48
|
const func = agg.name.toUpperCase();
|
|
43
|
-
selectClauses.push(`${func}(${tableAlias}."${agg.fieldName}") AS ${agg.alias}`);
|
|
49
|
+
selectClauses.push(`${func}(${tableAlias}."${sqlName(agg.fieldName)}") AS ${agg.alias}`);
|
|
44
50
|
}
|
|
45
51
|
});
|
|
46
52
|
|
|
47
|
-
const groupByClause = `GROUP BY ${groupByFields.map((field) => `${tableAlias}."${field}"`).join(", ")}`;
|
|
53
|
+
const groupByClause = `GROUP BY ${groupByFields.map((field) => `${tableAlias}."${sqlName(field)}"`).join(", ")}`;
|
|
48
54
|
|
|
49
55
|
return `${cteAlias} AS (
|
|
50
56
|
SELECT
|
|
@@ -58,6 +64,7 @@ const buildAggregationCTE = (
|
|
|
58
64
|
// Build the main query for grouped results
|
|
59
65
|
const buildGroupedQuery = (
|
|
60
66
|
entities: MergedEntities,
|
|
67
|
+
variablesDefinition: VariableDefinition[],
|
|
61
68
|
field: SelectionAnalysis,
|
|
62
69
|
groupByInfo: GroupByInfo,
|
|
63
70
|
dottedQuotedName: string,
|
|
@@ -67,12 +74,22 @@ const buildGroupedQuery = (
|
|
|
67
74
|
const { groupByFields, aggregations, hasItems, keyResolved, hasKey, keys, cteAlias } =
|
|
68
75
|
groupByInfo;
|
|
69
76
|
|
|
77
|
+
const sqlName = (fieldName: string) => sqlColumnName(entities, field.name, fieldName);
|
|
78
|
+
|
|
70
79
|
const selectClauses: string[] = [];
|
|
71
80
|
|
|
72
81
|
if (hasKey) {
|
|
73
82
|
// Add key object with group by fields
|
|
74
83
|
const keyFields = keys
|
|
75
|
-
.map((key) =>
|
|
84
|
+
.map((key) => {
|
|
85
|
+
const selector = applyDirectives(
|
|
86
|
+
`${cteAlias}."${sqlName(key.name)}"`,
|
|
87
|
+
key.directives,
|
|
88
|
+
"pg",
|
|
89
|
+
variablesDefinition,
|
|
90
|
+
);
|
|
91
|
+
return `'${key.alias || key.name}', ${selector}`;
|
|
92
|
+
})
|
|
76
93
|
.join(", ");
|
|
77
94
|
|
|
78
95
|
selectClauses.push(`'${keyResolved}', json_build_object(${keyFields})`);
|
|
@@ -96,12 +113,21 @@ const buildGroupedQuery = (
|
|
|
96
113
|
if (itemsSelection?.selections) {
|
|
97
114
|
const itemFields = itemsSelection.selections
|
|
98
115
|
.filter((sel) => !isAggregationField(sel.name) && sel.name !== "items")
|
|
99
|
-
.map((sel) =>
|
|
116
|
+
.map((sel) => {
|
|
117
|
+
const selector = applyDirectives(
|
|
118
|
+
`${tableAlias}."${sqlName(sel.name)}"`,
|
|
119
|
+
sel.directives,
|
|
120
|
+
"pg",
|
|
121
|
+
variablesDefinition,
|
|
122
|
+
);
|
|
123
|
+
return `'${sel.alias || sel.name}', ${selector}`;
|
|
124
|
+
})
|
|
100
125
|
.join(", ");
|
|
101
126
|
|
|
102
127
|
if (itemFields) {
|
|
103
128
|
const joinConditions = groupByFields.map(
|
|
104
|
-
(
|
|
129
|
+
(groupByField) =>
|
|
130
|
+
`${tableAlias}."${sqlName(groupByField)}" = ${cteAlias}."${sqlName(groupByField)}"`,
|
|
105
131
|
);
|
|
106
132
|
|
|
107
133
|
const whereConditions = whereClause
|
|
@@ -171,7 +197,14 @@ export const generateSQL = (
|
|
|
171
197
|
{},
|
|
172
198
|
);
|
|
173
199
|
|
|
174
|
-
const cte = buildAggregationCTE(
|
|
200
|
+
const cte = buildAggregationCTE(
|
|
201
|
+
entities,
|
|
202
|
+
field.name,
|
|
203
|
+
groupByInfo,
|
|
204
|
+
dottedQuotedName,
|
|
205
|
+
tableAlias,
|
|
206
|
+
whereClause,
|
|
207
|
+
);
|
|
175
208
|
ctes.push(cte);
|
|
176
209
|
}
|
|
177
210
|
|
|
@@ -240,6 +273,7 @@ export const buildSQLForField = (
|
|
|
240
273
|
// Handle GROUP BY aggregation query
|
|
241
274
|
const mainQuery = buildGroupedQuery(
|
|
242
275
|
entities,
|
|
276
|
+
variablesDefinition,
|
|
243
277
|
field,
|
|
244
278
|
groupByInfo,
|
|
245
279
|
dottedQuotedName,
|
|
@@ -280,6 +314,24 @@ export const buildSQLForField = (
|
|
|
280
314
|
|
|
281
315
|
const isArraySelection = !!field.isArray && !withoutArrayWrapper;
|
|
282
316
|
|
|
317
|
+
// json_agg collapses the rows into one, so LIMIT/OFFSET applied next to it
|
|
318
|
+
// trims aggregate rows rather than table rows: the limit does nothing and any
|
|
319
|
+
// non-zero offset skips the only row there is. Paginate inside a derived table
|
|
320
|
+
// and aggregate that. ROW_NUMBER over the same ordering carries the order out
|
|
321
|
+
// to the aggregate, which PostgreSQL does not otherwise guarantee.
|
|
322
|
+
if (isArraySelection && paginationClause) {
|
|
323
|
+
const pageAlias = `${tableAlias}_page`;
|
|
324
|
+
|
|
325
|
+
return `
|
|
326
|
+
COALESCE((
|
|
327
|
+
SELECT json_agg(${pageAlias}.obj ORDER BY ${pageAlias}.__ord)
|
|
328
|
+
FROM (
|
|
329
|
+
SELECT json_build_object(${selectList}) AS obj, ROW_NUMBER() OVER (${orderByClause}) AS __ord ${fromClause} ${whereClause} ${orderByClause} ${paginationClause}
|
|
330
|
+
) ${pageAlias}
|
|
331
|
+
), '[]'::json)
|
|
332
|
+
`;
|
|
333
|
+
}
|
|
334
|
+
|
|
283
335
|
return `
|
|
284
336
|
COALESCE((
|
|
285
337
|
SELECT ${isArraySelection ? `json_agg(json_build_object(${selectList}) ${orderByClause})` : `json_build_object(${selectList})`} ${fromClause} ${whereClause} ${withoutArrayWrapper ? " LIMIT 1" : ""} ${paginationClause ? ` ${paginationClause}` : ""}
|
|
@@ -11,7 +11,7 @@ import type {
|
|
|
11
11
|
TableFilter,
|
|
12
12
|
VirtualColumns,
|
|
13
13
|
} from "../types/configuration";
|
|
14
|
-
import type {
|
|
14
|
+
import type { ProcedureResolver, TableColumn, TableResolver, Tables } from "../types/db";
|
|
15
15
|
|
|
16
16
|
import { getDatabaseStructure } from "./metadata/structure";
|
|
17
17
|
import { buildProcedureResolver, buildTableResolver } from "./transformers/data-transformers";
|
|
@@ -63,7 +63,7 @@ export const getDatabasesStructure = async (
|
|
|
63
63
|
|
|
64
64
|
if (tableOverride.columns.length) {
|
|
65
65
|
// Mix database columns with virtual columns from configuration
|
|
66
|
-
t.columns = [...t.columns, ...tableOverride.columns] as
|
|
66
|
+
t.columns = [...t.columns, ...tableOverride.columns] as TableColumn[];
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
if (tableOverride.relationships.length) {
|
|
@@ -27,7 +27,6 @@ export const categorizeSqlType = (dataType: string): SqlTypeCategory => {
|
|
|
27
27
|
lowerType === "bigint" ||
|
|
28
28
|
lowerType === "tinyint" ||
|
|
29
29
|
lowerType === "mediumint" ||
|
|
30
|
-
lowerType === "numeric" ||
|
|
31
30
|
lowerType === "number"
|
|
32
31
|
) {
|
|
33
32
|
return SqlTypeCategory.INTEGER;
|
|
@@ -40,6 +39,9 @@ export const categorizeSqlType = (dataType: string): SqlTypeCategory => {
|
|
|
40
39
|
lowerType === "double" ||
|
|
41
40
|
lowerType === "double precision" ||
|
|
42
41
|
lowerType === "decimal" ||
|
|
42
|
+
// PostgreSQL's information_schema reports DECIMAL(p, s) as "numeric", so
|
|
43
|
+
// classifying it as an integer typed every PG decimal column as GraphQL Int.
|
|
44
|
+
lowerType === "numeric" ||
|
|
43
45
|
lowerType === "money" ||
|
|
44
46
|
lowerType === "smallmoney"
|
|
45
47
|
) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { sanitizeGraphQLName } from "./graphqlName";
|
|
2
|
+
|
|
1
3
|
export const genResolverName = (
|
|
2
4
|
schema: string,
|
|
3
5
|
name: string,
|
|
@@ -12,7 +14,5 @@ export const genResolverName = (
|
|
|
12
14
|
.replace("{schema}", schema)
|
|
13
15
|
.replace("{name}", name);
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return baseName;
|
|
17
|
+
return sanitizeGraphQLName(suffix ? `${baseName}_${suffix}` : baseName);
|
|
18
18
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps a SQL identifier onto a legal GraphQL name (`/^[_A-Za-z][_0-9A-Za-z]*$/`).
|
|
3
|
+
*
|
|
4
|
+
* PostgreSQL, MySQL and SQL Server all accept identifiers GraphQL cannot spell —
|
|
5
|
+
* spaces, accents, punctuation — and an unmapped one produces an SDL document
|
|
6
|
+
* that fails to parse, taking the whole server down at boot.
|
|
7
|
+
*
|
|
8
|
+
* A name that is already legal is returned unchanged, so this cannot rename a
|
|
9
|
+
* field in any schema that boots today. Runs of illegal characters are not
|
|
10
|
+
* collapsed, and diacritics fold to their base letter rather than being dropped,
|
|
11
|
+
* both so that distinct identifiers stay distinct as often as possible.
|
|
12
|
+
*/
|
|
13
|
+
export const sanitizeGraphQLName = (name: string): string => {
|
|
14
|
+
const folded = name.normalize("NFD").replace(/[̀-ͯ]/g, "");
|
|
15
|
+
const replaced = folded.replace(/[^_0-9A-Za-z]/g, "_");
|
|
16
|
+
|
|
17
|
+
if (replaced.length === 0) return "_";
|
|
18
|
+
|
|
19
|
+
return /^[0-9]/.test(replaced) ? `_${replaced}` : replaced;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The GraphQL field a column is exposed as. Introspected columns carry a
|
|
24
|
+
* `fieldName` computed once at parse time; virtual columns come from
|
|
25
|
+
* configuration and carry only a name, so they are sanitised on the spot.
|
|
26
|
+
*/
|
|
27
|
+
export const columnFieldName = (column: { name: string; fieldName?: string }): string =>
|
|
28
|
+
column.fieldName ?? sanitizeGraphQLName(column.name);
|
|
@@ -14,7 +14,7 @@ export const createSubscriptionStrategyRegistry = (): SubscriptionStrategyRegist
|
|
|
14
14
|
const queueStrategy = createQueueSubscriptionStrategy();
|
|
15
15
|
|
|
16
16
|
registry.set(EntitySource.TABLE, databaseStrategy);
|
|
17
|
-
registry.set(EntitySource.
|
|
17
|
+
registry.set(EntitySource.QUEUE_SUBSCRIBER, queueStrategy);
|
|
18
18
|
|
|
19
19
|
return registry;
|
|
20
20
|
};
|
|
@@ -23,7 +23,7 @@ const buildCompositeKey = (context: SubscriptionContext): string => {
|
|
|
23
23
|
* - This strategy manages connection registration with the event emitter
|
|
24
24
|
*/
|
|
25
25
|
export const createQueueSubscriptionStrategy = (): SubscriptionStrategy => ({
|
|
26
|
-
source: EntitySource.
|
|
26
|
+
source: EntitySource.QUEUE_SUBSCRIBER,
|
|
27
27
|
|
|
28
28
|
getSubscriptionKey(context) {
|
|
29
29
|
return buildCompositeKey(context);
|
package/src/types/resolver.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RemoteRESTResolved, RemoteRESTRoute } from "../remoteREST/types";
|
|
2
2
|
import type { RemoteSchemaResolved } from "../remoteSchemas/types";
|
|
3
|
-
import type { Publisher } from "./configuration";
|
|
3
|
+
import type { Publisher, Subscriber } from "./configuration";
|
|
4
4
|
import type { ProcedureResolver, TableResolver } from "./db";
|
|
5
5
|
import type { TypedOperation } from "./zod/operation";
|
|
6
6
|
|
|
@@ -14,6 +14,8 @@ export enum EntitySource {
|
|
|
14
14
|
STORED_PROCEDURE = "stored_procedure",
|
|
15
15
|
/** Message queue (RabbitMQ, Kafka, etc.) */
|
|
16
16
|
QUEUE_PUBLISHER = "queue_publisher",
|
|
17
|
+
/** Message queue subscriber (exposed as a GraphQL subscription) */
|
|
18
|
+
QUEUE_SUBSCRIBER = "queue_subscriber",
|
|
17
19
|
/** Authentication mutations (login, refresh, etc.) */
|
|
18
20
|
AUTH = "auth",
|
|
19
21
|
/** Custom operation handlers */
|
|
@@ -57,6 +59,14 @@ export type QueueResolverEntry = ResolverEntryBase & {
|
|
|
57
59
|
resolver: Publisher;
|
|
58
60
|
};
|
|
59
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Resolver entry for message queue subscribers
|
|
64
|
+
*/
|
|
65
|
+
export type QueueSubscriberResolverEntry = ResolverEntryBase & {
|
|
66
|
+
source: EntitySource.QUEUE_SUBSCRIBER;
|
|
67
|
+
resolver: Subscriber;
|
|
68
|
+
};
|
|
69
|
+
|
|
60
70
|
/**
|
|
61
71
|
* Resolver entry for authentication operations
|
|
62
72
|
*/
|
|
@@ -114,6 +124,7 @@ export type ResolverEntry =
|
|
|
114
124
|
| TableResolverEntry
|
|
115
125
|
| StoredProcedureResolverEntry
|
|
116
126
|
| QueueResolverEntry
|
|
127
|
+
| QueueSubscriberResolverEntry
|
|
117
128
|
| AuthResolverEntry
|
|
118
129
|
| OperationResolverEntry
|
|
119
130
|
| RemoteSchemaResolverEntry
|
|
@@ -144,6 +155,11 @@ export const createResolverEntry = {
|
|
|
144
155
|
resolver,
|
|
145
156
|
}),
|
|
146
157
|
|
|
158
|
+
queueSubscriber: (resolver: Subscriber): QueueSubscriberResolverEntry => ({
|
|
159
|
+
source: EntitySource.QUEUE_SUBSCRIBER,
|
|
160
|
+
resolver,
|
|
161
|
+
}),
|
|
162
|
+
|
|
147
163
|
auth: (
|
|
148
164
|
name: string,
|
|
149
165
|
operation: AuthResolverEntry["resolver"]["operation"],
|
|
@@ -11,7 +11,7 @@ import { RemoteRESTConfigZod } from "./remoteREST";
|
|
|
11
11
|
import { RemoteSchemaConfigZod } from "./remoteSchema";
|
|
12
12
|
|
|
13
13
|
export const ConfigurationZod = z
|
|
14
|
-
.
|
|
14
|
+
.strictObject({
|
|
15
15
|
name: z.string(),
|
|
16
16
|
version: z.string(),
|
|
17
17
|
tokenStrategy: TokenStrategyZod.optional().default("jwt"),
|
package/src/types/zod/db.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
TableRelationshipZod as TableRelationshipBaseZod,
|
|
9
9
|
} from "../../config";
|
|
10
10
|
import { genResolverName } from "../../databases/transformers/genResolverName";
|
|
11
|
+
import { sanitizeGraphQLName } from "../../databases/transformers/graphqlName";
|
|
11
12
|
|
|
12
13
|
// Re-export base types and schemas from the config module
|
|
13
14
|
export type {
|
|
@@ -43,6 +44,9 @@ export const TableColumnZod = z
|
|
|
43
44
|
})
|
|
44
45
|
.transform((val) => ({
|
|
45
46
|
name: val.name,
|
|
47
|
+
// The SQL identifier stays in `name`; `fieldName` is what GraphQL exposes it as.
|
|
48
|
+
// Nothing emitting SQL may read `fieldName`, nothing emitting SDL may read `name`.
|
|
49
|
+
fieldName: sanitizeGraphQLName(val.name),
|
|
46
50
|
dataType: val.dataType,
|
|
47
51
|
isNullable: typeof val.isNullable === "boolean" ? val.isNullable : val.isNullable === 1,
|
|
48
52
|
description: val.description ?? null,
|