graphile-settings 4.9.3 → 4.10.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.
- package/esm/plugins/PublicKeySignature.d.ts +11 -0
- package/esm/plugins/PublicKeySignature.js +184 -0
- package/esm/plugins/conflict-detector.d.ts +7 -0
- package/esm/plugins/conflict-detector.js +67 -0
- package/esm/plugins/custom-inflector.d.ts +9 -0
- package/esm/plugins/custom-inflector.js +394 -0
- package/esm/plugins/enable-all-filter-columns.d.ts +60 -0
- package/esm/plugins/enable-all-filter-columns.js +85 -0
- package/esm/plugins/index.d.ts +16 -4
- package/esm/plugins/index.js +25 -4
- package/esm/plugins/inflector-logger.d.ts +7 -0
- package/esm/plugins/inflector-logger.js +215 -0
- package/esm/plugins/many-to-many-preset.d.ts +62 -0
- package/esm/plugins/many-to-many-preset.js +86 -0
- package/esm/plugins/meta-schema/cache.d.ts +4 -0
- package/esm/plugins/meta-schema/cache.js +7 -0
- package/esm/plugins/meta-schema/constraint-meta-builders.d.ts +13 -0
- package/esm/plugins/meta-schema/constraint-meta-builders.js +51 -0
- package/esm/plugins/meta-schema/graphql-meta-field.d.ts +4 -0
- package/esm/plugins/meta-schema/graphql-meta-field.js +201 -0
- package/esm/plugins/meta-schema/inflection-utils.d.ts +4 -0
- package/esm/plugins/meta-schema/inflection-utils.js +20 -0
- package/esm/plugins/meta-schema/name-meta-builders.d.ts +4 -0
- package/esm/plugins/meta-schema/name-meta-builders.js +38 -0
- package/esm/plugins/meta-schema/plugin.d.ts +2 -0
- package/esm/plugins/meta-schema/plugin.js +23 -0
- package/esm/plugins/meta-schema/relation-meta-builders.d.ts +8 -0
- package/esm/plugins/meta-schema/relation-meta-builders.js +115 -0
- package/esm/plugins/meta-schema/table-meta-builder.d.ts +2 -0
- package/esm/plugins/meta-schema/table-meta-builder.js +69 -0
- package/esm/plugins/meta-schema/table-meta-context.d.ts +13 -0
- package/esm/plugins/meta-schema/table-meta-context.js +11 -0
- package/esm/plugins/meta-schema/table-resource-utils.d.ts +12 -0
- package/esm/plugins/meta-schema/table-resource-utils.js +50 -0
- package/esm/plugins/meta-schema/type-mappings.d.ts +3 -0
- package/esm/plugins/meta-schema/type-mappings.js +75 -0
- package/esm/plugins/meta-schema/types.d.ts +206 -0
- package/esm/plugins/meta-schema/types.js +1 -0
- package/esm/plugins/meta-schema.d.ts +19 -0
- package/esm/plugins/meta-schema.js +20 -0
- package/esm/plugins/minimal-preset.d.ts +7 -0
- package/esm/plugins/minimal-preset.js +42 -0
- package/esm/plugins/pg-type-mappings.d.ts +41 -0
- package/esm/plugins/pg-type-mappings.js +122 -0
- package/esm/plugins/primary-key-only.d.ts +96 -0
- package/esm/plugins/primary-key-only.js +143 -0
- package/esm/plugins/required-input-plugin.d.ts +37 -0
- package/esm/plugins/required-input-plugin.js +88 -0
- package/esm/presets/constructive-preset.js +2 -1
- package/package.json +11 -9
- package/plugins/PublicKeySignature.d.ts +11 -0
- package/plugins/PublicKeySignature.js +191 -0
- package/plugins/conflict-detector.d.ts +7 -0
- package/plugins/conflict-detector.js +70 -0
- package/plugins/custom-inflector.d.ts +9 -0
- package/plugins/custom-inflector.js +397 -0
- package/plugins/enable-all-filter-columns.d.ts +60 -0
- package/plugins/enable-all-filter-columns.js +88 -0
- package/plugins/index.d.ts +16 -4
- package/plugins/index.js +54 -31
- package/plugins/inflector-logger.d.ts +7 -0
- package/plugins/inflector-logger.js +218 -0
- package/plugins/many-to-many-preset.d.ts +62 -0
- package/plugins/many-to-many-preset.js +89 -0
- package/plugins/meta-schema/cache.d.ts +4 -0
- package/plugins/meta-schema/cache.js +12 -0
- package/plugins/meta-schema/constraint-meta-builders.d.ts +13 -0
- package/plugins/meta-schema/constraint-meta-builders.js +58 -0
- package/plugins/meta-schema/graphql-meta-field.d.ts +4 -0
- package/plugins/meta-schema/graphql-meta-field.js +204 -0
- package/plugins/meta-schema/inflection-utils.d.ts +4 -0
- package/plugins/meta-schema/inflection-utils.js +25 -0
- package/plugins/meta-schema/name-meta-builders.d.ts +4 -0
- package/plugins/meta-schema/name-meta-builders.js +43 -0
- package/plugins/meta-schema/plugin.d.ts +2 -0
- package/plugins/meta-schema/plugin.js +26 -0
- package/plugins/meta-schema/relation-meta-builders.d.ts +8 -0
- package/plugins/meta-schema/relation-meta-builders.js +120 -0
- package/plugins/meta-schema/table-meta-builder.d.ts +2 -0
- package/plugins/meta-schema/table-meta-builder.js +72 -0
- package/plugins/meta-schema/table-meta-context.d.ts +13 -0
- package/plugins/meta-schema/table-meta-context.js +15 -0
- package/plugins/meta-schema/table-resource-utils.d.ts +12 -0
- package/plugins/meta-schema/table-resource-utils.js +60 -0
- package/plugins/meta-schema/type-mappings.d.ts +3 -0
- package/plugins/meta-schema/type-mappings.js +79 -0
- package/plugins/meta-schema/types.d.ts +206 -0
- package/plugins/meta-schema/types.js +2 -0
- package/plugins/meta-schema.d.ts +19 -0
- package/plugins/meta-schema.js +20 -0
- package/plugins/minimal-preset.d.ts +7 -0
- package/plugins/minimal-preset.js +45 -0
- package/plugins/pg-type-mappings.d.ts +41 -0
- package/plugins/pg-type-mappings.js +128 -0
- package/plugins/primary-key-only.d.ts +96 -0
- package/plugins/primary-key-only.js +147 -0
- package/plugins/required-input-plugin.d.ts +37 -0
- package/plugins/required-input-plugin.js +91 -0
- package/presets/constructive-preset.js +11 -10
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { buildFieldMeta } from './type-mappings';
|
|
2
|
+
import { buildFieldList } from './table-meta-context';
|
|
3
|
+
export function buildForeignKeyConstraint(constraintName, localCodec, localAttributes, localAttributeNames, remoteCodec, remoteAttributes, remoteAttributeNames, context) {
|
|
4
|
+
const referencedTable = remoteCodec?.name || 'unknown';
|
|
5
|
+
const referencedFields = remoteAttributeNames.map((attrName) => remoteCodec ? context.inflectAttr(attrName, remoteCodec) : attrName);
|
|
6
|
+
return {
|
|
7
|
+
name: constraintName,
|
|
8
|
+
fields: buildFieldList(localAttributeNames, localCodec, localAttributes, context),
|
|
9
|
+
referencedTable,
|
|
10
|
+
referencedFields,
|
|
11
|
+
refFields: remoteAttributeNames.map((attrName) => buildFieldMeta(remoteCodec ? context.inflectAttr(attrName, remoteCodec) : attrName, remoteAttributes[attrName], context.build)),
|
|
12
|
+
refTable: { name: referencedTable },
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function buildIndexes(codec, attributes, uniques, context) {
|
|
16
|
+
return uniques.map((unique) => ({
|
|
17
|
+
name: unique.tags?.name || `${codec.name}_${unique.attributes.join('_')}_idx`,
|
|
18
|
+
isUnique: true,
|
|
19
|
+
isPrimary: !!unique.isPrimary,
|
|
20
|
+
columns: unique.attributes.map((attrName) => context.inflectAttr(attrName, codec)),
|
|
21
|
+
fields: buildFieldList(unique.attributes, codec, attributes, context),
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
export function buildPrimaryKey(codec, attributes, uniques, context) {
|
|
25
|
+
const primaryUnique = uniques.find((unique) => unique.isPrimary);
|
|
26
|
+
if (!primaryUnique)
|
|
27
|
+
return null;
|
|
28
|
+
return {
|
|
29
|
+
name: primaryUnique.tags?.name || `${codec.name}_pkey`,
|
|
30
|
+
fields: buildFieldList(primaryUnique.attributes, codec, attributes, context),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function buildUniqueConstraints(codec, attributes, uniques, context) {
|
|
34
|
+
return uniques
|
|
35
|
+
.filter((unique) => !unique.isPrimary)
|
|
36
|
+
.map((unique) => ({
|
|
37
|
+
name: unique.tags?.name || `${codec.name}_${unique.attributes.join('_')}_key`,
|
|
38
|
+
fields: buildFieldList(unique.attributes, codec, attributes, context),
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
export function buildForeignKeyConstraints(codec, attributes, relations, context) {
|
|
42
|
+
const constraints = [];
|
|
43
|
+
for (const [relationName, relation] of Object.entries(relations)) {
|
|
44
|
+
if (relation.isReferencee !== false)
|
|
45
|
+
continue;
|
|
46
|
+
const remoteCodec = relation.remoteResource?.codec;
|
|
47
|
+
const remoteAttributes = remoteCodec?.attributes || {};
|
|
48
|
+
constraints.push(buildForeignKeyConstraint(relationName, codec, attributes, relation.localAttributes || [], remoteCodec, remoteAttributes, relation.remoteAttributes || [], context));
|
|
49
|
+
}
|
|
50
|
+
return constraints;
|
|
51
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { GraphQLBoolean, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString, } from 'graphql';
|
|
2
|
+
function nn(type) {
|
|
3
|
+
return new GraphQLNonNull(type);
|
|
4
|
+
}
|
|
5
|
+
function nnList(type) {
|
|
6
|
+
return nn(new GraphQLList(nn(type)));
|
|
7
|
+
}
|
|
8
|
+
function createMetaSchemaType() {
|
|
9
|
+
const MetaTypeType = new GraphQLObjectType({
|
|
10
|
+
name: 'MetaType',
|
|
11
|
+
description: 'Information about a PostgreSQL type',
|
|
12
|
+
fields: () => ({
|
|
13
|
+
pgType: { type: nn(GraphQLString) },
|
|
14
|
+
gqlType: { type: nn(GraphQLString) },
|
|
15
|
+
isArray: { type: nn(GraphQLBoolean) },
|
|
16
|
+
isNotNull: { type: GraphQLBoolean },
|
|
17
|
+
hasDefault: { type: GraphQLBoolean },
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
const MetaFieldType = new GraphQLObjectType({
|
|
21
|
+
name: 'MetaField',
|
|
22
|
+
description: 'Information about a table field/column',
|
|
23
|
+
fields: () => ({
|
|
24
|
+
name: { type: nn(GraphQLString) },
|
|
25
|
+
type: { type: nn(MetaTypeType) },
|
|
26
|
+
isNotNull: { type: nn(GraphQLBoolean) },
|
|
27
|
+
hasDefault: { type: nn(GraphQLBoolean) },
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
const MetaIndexType = new GraphQLObjectType({
|
|
31
|
+
name: 'MetaIndex',
|
|
32
|
+
description: 'Information about a database index',
|
|
33
|
+
fields: () => ({
|
|
34
|
+
name: { type: nn(GraphQLString) },
|
|
35
|
+
isUnique: { type: nn(GraphQLBoolean) },
|
|
36
|
+
isPrimary: { type: nn(GraphQLBoolean) },
|
|
37
|
+
columns: { type: nnList(GraphQLString) },
|
|
38
|
+
fields: { type: new GraphQLList(nn(MetaFieldType)) },
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
const MetaPrimaryKeyConstraintType = new GraphQLObjectType({
|
|
42
|
+
name: 'MetaPrimaryKeyConstraint',
|
|
43
|
+
description: 'Information about a primary key constraint',
|
|
44
|
+
fields: () => ({
|
|
45
|
+
name: { type: nn(GraphQLString) },
|
|
46
|
+
fields: { type: nnList(MetaFieldType) },
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
const MetaUniqueConstraintType = new GraphQLObjectType({
|
|
50
|
+
name: 'MetaUniqueConstraint',
|
|
51
|
+
description: 'Information about a unique constraint',
|
|
52
|
+
fields: () => ({
|
|
53
|
+
name: { type: nn(GraphQLString) },
|
|
54
|
+
fields: { type: nnList(MetaFieldType) },
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
const MetaRefTableType = new GraphQLObjectType({
|
|
58
|
+
name: 'MetaRefTable',
|
|
59
|
+
description: 'Reference to a related table',
|
|
60
|
+
fields: () => ({
|
|
61
|
+
name: { type: nn(GraphQLString) },
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
const MetaForeignKeyConstraintType = new GraphQLObjectType({
|
|
65
|
+
name: 'MetaForeignKeyConstraint',
|
|
66
|
+
description: 'Information about a foreign key constraint',
|
|
67
|
+
fields: () => ({
|
|
68
|
+
name: { type: nn(GraphQLString) },
|
|
69
|
+
fields: { type: nnList(MetaFieldType) },
|
|
70
|
+
referencedTable: { type: nn(GraphQLString) },
|
|
71
|
+
referencedFields: { type: nnList(GraphQLString) },
|
|
72
|
+
refFields: { type: new GraphQLList(nn(MetaFieldType)) },
|
|
73
|
+
refTable: { type: MetaRefTableType },
|
|
74
|
+
}),
|
|
75
|
+
});
|
|
76
|
+
const MetaConstraintsType = new GraphQLObjectType({
|
|
77
|
+
name: 'MetaConstraints',
|
|
78
|
+
description: 'Table constraints',
|
|
79
|
+
fields: () => ({
|
|
80
|
+
primaryKey: { type: MetaPrimaryKeyConstraintType },
|
|
81
|
+
unique: { type: nnList(MetaUniqueConstraintType) },
|
|
82
|
+
foreignKey: { type: nnList(MetaForeignKeyConstraintType) },
|
|
83
|
+
}),
|
|
84
|
+
});
|
|
85
|
+
const MetaInflectionType = new GraphQLObjectType({
|
|
86
|
+
name: 'MetaInflection',
|
|
87
|
+
description: 'Table inflection names',
|
|
88
|
+
fields: () => ({
|
|
89
|
+
tableType: { type: nn(GraphQLString) },
|
|
90
|
+
allRows: { type: nn(GraphQLString) },
|
|
91
|
+
connection: { type: nn(GraphQLString) },
|
|
92
|
+
edge: { type: nn(GraphQLString) },
|
|
93
|
+
filterType: { type: GraphQLString },
|
|
94
|
+
orderByType: { type: nn(GraphQLString) },
|
|
95
|
+
conditionType: { type: nn(GraphQLString) },
|
|
96
|
+
patchType: { type: GraphQLString },
|
|
97
|
+
createInputType: { type: nn(GraphQLString) },
|
|
98
|
+
createPayloadType: { type: nn(GraphQLString) },
|
|
99
|
+
updatePayloadType: { type: GraphQLString },
|
|
100
|
+
deletePayloadType: { type: nn(GraphQLString) },
|
|
101
|
+
}),
|
|
102
|
+
});
|
|
103
|
+
const MetaQueryType = new GraphQLObjectType({
|
|
104
|
+
name: 'MetaQuery',
|
|
105
|
+
description: 'Table query/mutation names',
|
|
106
|
+
fields: () => ({
|
|
107
|
+
all: { type: nn(GraphQLString) },
|
|
108
|
+
one: { type: GraphQLString },
|
|
109
|
+
create: { type: GraphQLString },
|
|
110
|
+
update: { type: GraphQLString },
|
|
111
|
+
delete: { type: GraphQLString },
|
|
112
|
+
}),
|
|
113
|
+
});
|
|
114
|
+
const MetaBelongsToRelationType = new GraphQLObjectType({
|
|
115
|
+
name: 'MetaBelongsToRelation',
|
|
116
|
+
description: 'A belongs-to (forward FK) relation',
|
|
117
|
+
fields: () => ({
|
|
118
|
+
fieldName: { type: GraphQLString },
|
|
119
|
+
isUnique: { type: nn(GraphQLBoolean) },
|
|
120
|
+
type: { type: GraphQLString },
|
|
121
|
+
keys: { type: nnList(MetaFieldType) },
|
|
122
|
+
references: { type: nn(MetaRefTableType) },
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
const MetaHasRelationType = new GraphQLObjectType({
|
|
126
|
+
name: 'MetaHasRelation',
|
|
127
|
+
description: 'A has-one or has-many (reverse FK) relation',
|
|
128
|
+
fields: () => ({
|
|
129
|
+
fieldName: { type: GraphQLString },
|
|
130
|
+
isUnique: { type: nn(GraphQLBoolean) },
|
|
131
|
+
type: { type: GraphQLString },
|
|
132
|
+
keys: { type: nnList(MetaFieldType) },
|
|
133
|
+
referencedBy: { type: nn(MetaRefTableType) },
|
|
134
|
+
}),
|
|
135
|
+
});
|
|
136
|
+
const MetaManyToManyRelationType = new GraphQLObjectType({
|
|
137
|
+
name: 'MetaManyToManyRelation',
|
|
138
|
+
description: 'A many-to-many relation via junction table',
|
|
139
|
+
fields: () => ({
|
|
140
|
+
fieldName: { type: GraphQLString },
|
|
141
|
+
type: { type: GraphQLString },
|
|
142
|
+
junctionTable: { type: nn(MetaRefTableType) },
|
|
143
|
+
junctionLeftConstraint: { type: nn(MetaForeignKeyConstraintType) },
|
|
144
|
+
junctionLeftKeyAttributes: { type: nnList(MetaFieldType) },
|
|
145
|
+
junctionRightConstraint: { type: nn(MetaForeignKeyConstraintType) },
|
|
146
|
+
junctionRightKeyAttributes: { type: nnList(MetaFieldType) },
|
|
147
|
+
leftKeyAttributes: { type: nnList(MetaFieldType) },
|
|
148
|
+
rightKeyAttributes: { type: nnList(MetaFieldType) },
|
|
149
|
+
rightTable: { type: nn(MetaRefTableType) },
|
|
150
|
+
}),
|
|
151
|
+
});
|
|
152
|
+
const MetaRelationsType = new GraphQLObjectType({
|
|
153
|
+
name: 'MetaRelations',
|
|
154
|
+
description: 'Table relations',
|
|
155
|
+
fields: () => ({
|
|
156
|
+
belongsTo: { type: nnList(MetaBelongsToRelationType) },
|
|
157
|
+
has: { type: nnList(MetaHasRelationType) },
|
|
158
|
+
hasOne: { type: nnList(MetaHasRelationType) },
|
|
159
|
+
hasMany: { type: nnList(MetaHasRelationType) },
|
|
160
|
+
manyToMany: { type: nnList(MetaManyToManyRelationType) },
|
|
161
|
+
}),
|
|
162
|
+
});
|
|
163
|
+
const MetaTableType = new GraphQLObjectType({
|
|
164
|
+
name: 'MetaTable',
|
|
165
|
+
description: 'Information about a database table',
|
|
166
|
+
fields: () => ({
|
|
167
|
+
name: { type: nn(GraphQLString) },
|
|
168
|
+
schemaName: { type: nn(GraphQLString) },
|
|
169
|
+
fields: { type: nnList(MetaFieldType) },
|
|
170
|
+
indexes: { type: nnList(MetaIndexType) },
|
|
171
|
+
constraints: { type: nn(MetaConstraintsType) },
|
|
172
|
+
foreignKeyConstraints: { type: nnList(MetaForeignKeyConstraintType) },
|
|
173
|
+
primaryKeyConstraints: { type: nnList(MetaPrimaryKeyConstraintType) },
|
|
174
|
+
uniqueConstraints: { type: nnList(MetaUniqueConstraintType) },
|
|
175
|
+
relations: { type: nn(MetaRelationsType) },
|
|
176
|
+
inflection: { type: nn(MetaInflectionType) },
|
|
177
|
+
query: { type: nn(MetaQueryType) },
|
|
178
|
+
}),
|
|
179
|
+
});
|
|
180
|
+
return new GraphQLObjectType({
|
|
181
|
+
name: 'MetaSchema',
|
|
182
|
+
description: 'Root meta schema type',
|
|
183
|
+
fields: () => ({
|
|
184
|
+
tables: { type: nnList(MetaTableType) },
|
|
185
|
+
}),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
export function extendQueryWithMetaField(fields, tablesMeta) {
|
|
189
|
+
const metaSchemaType = createMetaSchemaType();
|
|
190
|
+
const metaField = {
|
|
191
|
+
type: metaSchemaType,
|
|
192
|
+
description: 'Metadata about the database schema, including tables, fields, indexes, and constraints. Useful for code generation tools.',
|
|
193
|
+
resolve() {
|
|
194
|
+
return { tables: tablesMeta };
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
return {
|
|
198
|
+
...fields,
|
|
199
|
+
_meta: metaField,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { MetaInflection, PgCodec } from './types';
|
|
2
|
+
export declare function safeInflection<T>(fn: () => T, fallback: T): T;
|
|
3
|
+
export declare function createAttributeInflector(inflection: MetaInflection): (attrName: string, codec: PgCodec) => string;
|
|
4
|
+
export declare function fallbackTableType(codecName: string): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function safeInflection(fn, fallback) {
|
|
2
|
+
try {
|
|
3
|
+
return fn() ?? fallback;
|
|
4
|
+
}
|
|
5
|
+
catch {
|
|
6
|
+
return fallback;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function createAttributeInflector(inflection) {
|
|
10
|
+
return (attrName, codec) => {
|
|
11
|
+
const attributeName = safeInflection(() => inflection._attributeName?.({ attributeName: attrName, codec }), attrName);
|
|
12
|
+
return safeInflection(() => inflection.camelCase?.(attributeName), attributeName);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function fallbackTableType(codecName) {
|
|
16
|
+
return codecName
|
|
17
|
+
.replace(/_/g, ' ')
|
|
18
|
+
.replace(/\b\w/g, (char) => char.toUpperCase())
|
|
19
|
+
.replace(/ /g, '');
|
|
20
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { InflectionMeta, MetaBuild, PgCodec, PgTableResource, PgUnique, QueryMeta } from './types';
|
|
2
|
+
export declare function resolveTableType(build: MetaBuild, codec: PgCodec): string;
|
|
3
|
+
export declare function buildInflectionMeta(resource: PgTableResource, tableType: string, build: MetaBuild): InflectionMeta;
|
|
4
|
+
export declare function buildQueryMeta(resource: PgTableResource, uniques: PgUnique[], tableType: string, build: MetaBuild): QueryMeta;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { fallbackTableType, safeInflection } from './inflection-utils';
|
|
2
|
+
export function resolveTableType(build, codec) {
|
|
3
|
+
return safeInflection(() => build.inflection.tableType(codec), fallbackTableType(codec.name));
|
|
4
|
+
}
|
|
5
|
+
export function buildInflectionMeta(resource, tableType, build) {
|
|
6
|
+
const inflection = build.inflection;
|
|
7
|
+
return {
|
|
8
|
+
tableType,
|
|
9
|
+
allRows: safeInflection(() => inflection.allRows?.(resource), `${tableType.toLowerCase()}s`),
|
|
10
|
+
connection: safeInflection(() => inflection.connectionType?.(tableType), `${tableType}Connection`),
|
|
11
|
+
edge: safeInflection(() => inflection.edgeType?.(tableType), `${tableType}Edge`),
|
|
12
|
+
filterType: safeInflection(() => inflection.filterType?.(tableType), `${tableType}Filter`),
|
|
13
|
+
orderByType: safeInflection(() => inflection.orderByType?.(tableType), `${tableType}OrderBy`),
|
|
14
|
+
conditionType: safeInflection(() => inflection.conditionType?.(tableType), `${tableType}Condition`),
|
|
15
|
+
patchType: safeInflection(() => inflection.patchType?.(tableType), `${tableType}Patch`),
|
|
16
|
+
createInputType: safeInflection(() => inflection.createInputType?.(resource), `Create${tableType}Input`),
|
|
17
|
+
createPayloadType: safeInflection(() => inflection.createPayloadType?.(resource), `Create${tableType}Payload`),
|
|
18
|
+
updatePayloadType: safeInflection(() => inflection.updatePayloadType?.(resource), `Update${tableType}Payload`),
|
|
19
|
+
deletePayloadType: safeInflection(() => inflection.deletePayloadType?.(resource), `Delete${tableType}Payload`),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function buildQueryMeta(resource, uniques, tableType, build) {
|
|
23
|
+
const inflection = build.inflection;
|
|
24
|
+
const hasPrimaryKey = uniques.some((unique) => unique.isPrimary);
|
|
25
|
+
return {
|
|
26
|
+
all: safeInflection(() => inflection.allRows?.(resource), `${tableType.toLowerCase()}s`),
|
|
27
|
+
one: hasPrimaryKey
|
|
28
|
+
? safeInflection(() => inflection.tableFieldName?.(resource), tableType.toLowerCase())
|
|
29
|
+
: null,
|
|
30
|
+
create: safeInflection(() => inflection.createField?.(resource), `create${tableType}`),
|
|
31
|
+
update: hasPrimaryKey
|
|
32
|
+
? safeInflection(() => inflection.updateByKeys?.(resource), `update${tableType}`)
|
|
33
|
+
: null,
|
|
34
|
+
delete: hasPrimaryKey
|
|
35
|
+
? safeInflection(() => inflection.deleteByKeys?.(resource), `delete${tableType}`)
|
|
36
|
+
: null,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getCachedTablesMeta, setCachedTablesMeta } from './cache';
|
|
2
|
+
import { extendQueryWithMetaField } from './graphql-meta-field';
|
|
3
|
+
import { collectTablesMeta } from './table-meta-builder';
|
|
4
|
+
export const MetaSchemaPlugin = {
|
|
5
|
+
name: 'MetaSchemaPlugin',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
description: 'Exposes _meta query for database schema introspection',
|
|
8
|
+
schema: {
|
|
9
|
+
hooks: {
|
|
10
|
+
init(input, rawBuild) {
|
|
11
|
+
const build = rawBuild;
|
|
12
|
+
setCachedTablesMeta(collectTablesMeta(build));
|
|
13
|
+
return input;
|
|
14
|
+
},
|
|
15
|
+
GraphQLObjectType_fields(rawFields, _rawBuild, rawContext) {
|
|
16
|
+
const context = rawContext;
|
|
17
|
+
if (context.Self?.name !== 'Query')
|
|
18
|
+
return rawFields;
|
|
19
|
+
return extendQueryWithMetaField(rawFields, getCachedTablesMeta());
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type BuildContext } from './table-meta-context';
|
|
2
|
+
import type { BelongsToRelation, HasRelation, ManyToManyRelation, PgAttribute, PgCodec, PgRelation, PgTableResource, PgUnique } from './types';
|
|
3
|
+
export declare function buildBelongsToRelations(codec: PgCodec, attributes: Record<string, PgAttribute>, uniques: PgUnique[], relations: Record<string, PgRelation>, context: BuildContext): BelongsToRelation[];
|
|
4
|
+
export declare function buildReverseRelations(codec: PgCodec, attributes: Record<string, PgAttribute>, relations: Record<string, PgRelation>, context: BuildContext): {
|
|
5
|
+
hasOne: HasRelation[];
|
|
6
|
+
hasMany: HasRelation[];
|
|
7
|
+
};
|
|
8
|
+
export declare function buildManyToManyRelations(resource: PgTableResource, codec: PgCodec, context: BuildContext): ManyToManyRelation[];
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { safeInflection } from './inflection-utils';
|
|
2
|
+
import { buildForeignKeyConstraint, } from './constraint-meta-builders';
|
|
3
|
+
import { buildFieldList } from './table-meta-context';
|
|
4
|
+
import { getRelation, getResourceCodec, getUniques, sameAttributes, } from './table-resource-utils';
|
|
5
|
+
function isRecord(value) {
|
|
6
|
+
return typeof value === 'object' && value !== null;
|
|
7
|
+
}
|
|
8
|
+
export function buildBelongsToRelations(codec, attributes, uniques, relations, context) {
|
|
9
|
+
const belongsTo = [];
|
|
10
|
+
for (const [relationName, relation] of Object.entries(relations)) {
|
|
11
|
+
if (relation.isReferencee !== false)
|
|
12
|
+
continue;
|
|
13
|
+
const localAttributes = relation.localAttributes || [];
|
|
14
|
+
const isUnique = uniques.some((unique) => sameAttributes(unique.attributes, localAttributes));
|
|
15
|
+
belongsTo.push({
|
|
16
|
+
fieldName: relationName,
|
|
17
|
+
isUnique,
|
|
18
|
+
type: relation.remoteResource?.codec?.name || null,
|
|
19
|
+
keys: buildFieldList(localAttributes, codec, attributes, context),
|
|
20
|
+
references: { name: relation.remoteResource?.codec?.name || 'unknown' },
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return belongsTo;
|
|
24
|
+
}
|
|
25
|
+
export function buildReverseRelations(codec, attributes, relations, context) {
|
|
26
|
+
const hasOne = [];
|
|
27
|
+
const hasMany = [];
|
|
28
|
+
for (const [relationName, relation] of Object.entries(relations)) {
|
|
29
|
+
if (relation.isReferencee !== true)
|
|
30
|
+
continue;
|
|
31
|
+
const remoteUniques = getUniques(relation.remoteResource || {});
|
|
32
|
+
const remoteAttributes = relation.remoteAttributes || [];
|
|
33
|
+
const isUnique = remoteUniques.some((unique) => sameAttributes(unique.attributes, remoteAttributes));
|
|
34
|
+
const meta = {
|
|
35
|
+
fieldName: relationName,
|
|
36
|
+
isUnique,
|
|
37
|
+
type: relation.remoteResource?.codec?.name || null,
|
|
38
|
+
keys: buildFieldList(relation.localAttributes || [], codec, attributes, context),
|
|
39
|
+
referencedBy: { name: relation.remoteResource?.codec?.name || 'unknown' },
|
|
40
|
+
};
|
|
41
|
+
if (isUnique) {
|
|
42
|
+
hasOne.push(meta);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
hasMany.push(meta);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { hasOne, hasMany };
|
|
49
|
+
}
|
|
50
|
+
function isManyToManyDetails(value) {
|
|
51
|
+
if (!isRecord(value))
|
|
52
|
+
return false;
|
|
53
|
+
return (isRecord(value.leftTable) &&
|
|
54
|
+
isRecord(value.junctionTable) &&
|
|
55
|
+
isRecord(value.rightTable) &&
|
|
56
|
+
typeof value.leftRelationName === 'string' &&
|
|
57
|
+
typeof value.rightRelationName === 'string');
|
|
58
|
+
}
|
|
59
|
+
function parseManyToManyRelationships(value) {
|
|
60
|
+
if (!Array.isArray(value))
|
|
61
|
+
return [];
|
|
62
|
+
return value.filter(isManyToManyDetails);
|
|
63
|
+
}
|
|
64
|
+
function getManyToManyRelationships(build, tableResource, codec) {
|
|
65
|
+
const relationshipsByResource = build.pgManyToManyRealtionshipsByResource;
|
|
66
|
+
if (!(relationshipsByResource instanceof Map))
|
|
67
|
+
return [];
|
|
68
|
+
const direct = parseManyToManyRelationships(relationshipsByResource.get(tableResource));
|
|
69
|
+
if (direct.length > 0)
|
|
70
|
+
return direct;
|
|
71
|
+
for (const [leftTable, relationships] of relationshipsByResource.entries()) {
|
|
72
|
+
const details = parseManyToManyRelationships(relationships);
|
|
73
|
+
if (details.length === 0 || !isRecord(leftTable))
|
|
74
|
+
continue;
|
|
75
|
+
if (leftTable.codec === codec) {
|
|
76
|
+
return details;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
function buildManyToManyRelation(details, context) {
|
|
82
|
+
const leftCodec = getResourceCodec(details.leftTable);
|
|
83
|
+
const junctionCodec = getResourceCodec(details.junctionTable);
|
|
84
|
+
const rightCodec = getResourceCodec(details.rightTable);
|
|
85
|
+
if (!leftCodec || !junctionCodec || !rightCodec)
|
|
86
|
+
return null;
|
|
87
|
+
const leftRelation = getRelation(details.leftTable, details.leftRelationName);
|
|
88
|
+
const junctionRightRelation = getRelation(details.junctionTable, details.rightRelationName);
|
|
89
|
+
if (!leftRelation || !junctionRightRelation)
|
|
90
|
+
return null;
|
|
91
|
+
const leftJunctionAttributes = leftRelation.remoteAttributes || [];
|
|
92
|
+
const leftTableAttributes = leftRelation.localAttributes || [];
|
|
93
|
+
const rightJunctionAttributes = junctionRightRelation.localAttributes || [];
|
|
94
|
+
const rightTableAttributes = junctionRightRelation.remoteAttributes || [];
|
|
95
|
+
const relationFieldName = safeInflection(() => context.build.inflection._manyToManyRelation?.(details), details.rightRelationName || rightCodec.name || null);
|
|
96
|
+
const junctionLeftConstraint = buildForeignKeyConstraint(details.leftRelationName || `${junctionCodec.name}_${leftCodec.name}_fkey`, junctionCodec, junctionCodec.attributes, leftJunctionAttributes, leftCodec, leftCodec.attributes, leftTableAttributes, context);
|
|
97
|
+
const junctionRightConstraint = buildForeignKeyConstraint(details.rightRelationName || `${junctionCodec.name}_${rightCodec.name}_fkey`, junctionCodec, junctionCodec.attributes, rightJunctionAttributes, rightCodec, rightCodec.attributes, rightTableAttributes, context);
|
|
98
|
+
return {
|
|
99
|
+
fieldName: relationFieldName,
|
|
100
|
+
type: rightCodec.name || null,
|
|
101
|
+
junctionTable: { name: junctionCodec.name || 'unknown' },
|
|
102
|
+
junctionLeftConstraint,
|
|
103
|
+
junctionLeftKeyAttributes: buildFieldList(leftJunctionAttributes, junctionCodec, junctionCodec.attributes, context),
|
|
104
|
+
junctionRightConstraint,
|
|
105
|
+
junctionRightKeyAttributes: buildFieldList(rightJunctionAttributes, junctionCodec, junctionCodec.attributes, context),
|
|
106
|
+
leftKeyAttributes: buildFieldList(leftTableAttributes, leftCodec, leftCodec.attributes, context),
|
|
107
|
+
rightKeyAttributes: buildFieldList(rightTableAttributes, rightCodec, rightCodec.attributes, context),
|
|
108
|
+
rightTable: { name: rightCodec.name || 'unknown' },
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function buildManyToManyRelations(resource, codec, context) {
|
|
112
|
+
return getManyToManyRelationships(context.build, resource, codec)
|
|
113
|
+
.map((details) => buildManyToManyRelation(details, context))
|
|
114
|
+
.filter((relation) => relation !== null);
|
|
115
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { buildForeignKeyConstraints, buildIndexes, buildPrimaryKey, buildUniqueConstraints, } from './constraint-meta-builders';
|
|
2
|
+
import { buildInflectionMeta, buildQueryMeta, resolveTableType } from './name-meta-builders';
|
|
3
|
+
import { buildBelongsToRelations, buildManyToManyRelations, buildReverseRelations, } from './relation-meta-builders';
|
|
4
|
+
import { buildFieldMeta } from './type-mappings';
|
|
5
|
+
import { createBuildContext, } from './table-meta-context';
|
|
6
|
+
import { getConfiguredSchemas, getRelations, getSchemaName, getUniques, isTableResource, } from './table-resource-utils';
|
|
7
|
+
function buildTableMeta(resource, schemaName, context) {
|
|
8
|
+
const codec = resource.codec;
|
|
9
|
+
const attributes = codec.attributes;
|
|
10
|
+
const uniques = getUniques(resource);
|
|
11
|
+
const relations = getRelations(resource);
|
|
12
|
+
const fields = Object.entries(attributes).map(([attrName, attr]) => buildFieldMeta(context.inflectAttr(attrName, codec), attr, context.build));
|
|
13
|
+
const indexes = buildIndexes(codec, attributes, uniques, context);
|
|
14
|
+
const primaryKey = buildPrimaryKey(codec, attributes, uniques, context);
|
|
15
|
+
const uniqueConstraints = buildUniqueConstraints(codec, attributes, uniques, context);
|
|
16
|
+
const foreignKeyConstraints = buildForeignKeyConstraints(codec, attributes, relations, context);
|
|
17
|
+
const constraints = {
|
|
18
|
+
primaryKey,
|
|
19
|
+
unique: uniqueConstraints,
|
|
20
|
+
foreignKey: foreignKeyConstraints,
|
|
21
|
+
};
|
|
22
|
+
const belongsTo = buildBelongsToRelations(codec, attributes, uniques, relations, context);
|
|
23
|
+
const { hasOne, hasMany } = buildReverseRelations(codec, attributes, relations, context);
|
|
24
|
+
const manyToMany = buildManyToManyRelations(resource, codec, context);
|
|
25
|
+
const relationsMeta = {
|
|
26
|
+
belongsTo,
|
|
27
|
+
has: [...hasOne, ...hasMany],
|
|
28
|
+
hasOne,
|
|
29
|
+
hasMany,
|
|
30
|
+
manyToMany,
|
|
31
|
+
};
|
|
32
|
+
const tableType = resolveTableType(context.build, codec);
|
|
33
|
+
return {
|
|
34
|
+
name: tableType,
|
|
35
|
+
schemaName,
|
|
36
|
+
fields,
|
|
37
|
+
indexes,
|
|
38
|
+
constraints,
|
|
39
|
+
foreignKeyConstraints,
|
|
40
|
+
primaryKeyConstraints: primaryKey ? [primaryKey] : [],
|
|
41
|
+
uniqueConstraints,
|
|
42
|
+
relations: relationsMeta,
|
|
43
|
+
inflection: buildInflectionMeta(resource, tableType, context.build),
|
|
44
|
+
query: buildQueryMeta(resource, uniques, tableType, context.build),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function collectTablesMeta(build) {
|
|
48
|
+
const configuredSchemas = getConfiguredSchemas(build);
|
|
49
|
+
const context = createBuildContext(build);
|
|
50
|
+
const seenCodecs = new Set();
|
|
51
|
+
const tablesMeta = [];
|
|
52
|
+
for (const resource of Object.values(build.input.pgRegistry.pgResources || {})) {
|
|
53
|
+
if (!isTableResource(resource))
|
|
54
|
+
continue;
|
|
55
|
+
const codec = resource.codec;
|
|
56
|
+
if (seenCodecs.has(codec))
|
|
57
|
+
continue;
|
|
58
|
+
seenCodecs.add(codec);
|
|
59
|
+
const schemaName = getSchemaName(resource);
|
|
60
|
+
if (!schemaName)
|
|
61
|
+
continue;
|
|
62
|
+
if (configuredSchemas.length > 0 &&
|
|
63
|
+
!configuredSchemas.includes(schemaName)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
tablesMeta.push(buildTableMeta(resource, schemaName, context));
|
|
67
|
+
}
|
|
68
|
+
return tablesMeta;
|
|
69
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FieldMeta, MetaBuild, PgAttribute, PgCodec, PgTableResource } from './types';
|
|
2
|
+
export type AttributeInflector = (attrName: string, codec: PgCodec) => string;
|
|
3
|
+
export interface BuildContext {
|
|
4
|
+
build: MetaBuild;
|
|
5
|
+
inflectAttr: AttributeInflector;
|
|
6
|
+
}
|
|
7
|
+
export type TableResourceWithCodec = PgTableResource & {
|
|
8
|
+
codec: PgCodec & {
|
|
9
|
+
attributes: Record<string, PgAttribute>;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export declare function createBuildContext(build: MetaBuild): BuildContext;
|
|
13
|
+
export declare function buildFieldList(attrNames: string[], codec: PgCodec, attributes: Record<string, PgAttribute>, context: BuildContext): FieldMeta[];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createAttributeInflector } from './inflection-utils';
|
|
2
|
+
import { buildFieldMeta } from './type-mappings';
|
|
3
|
+
export function createBuildContext(build) {
|
|
4
|
+
return {
|
|
5
|
+
build,
|
|
6
|
+
inflectAttr: createAttributeInflector(build.inflection),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function buildFieldList(attrNames, codec, attributes, context) {
|
|
10
|
+
return attrNames.map((attrName) => buildFieldMeta(context.inflectAttr(attrName, codec), attributes[attrName], context.build));
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { MetaBuild, PgAttribute, PgCodec, PgRelation, PgTableResource, PgUnique } from './types';
|
|
2
|
+
import type { TableResourceWithCodec } from './table-meta-context';
|
|
3
|
+
export declare function isTableResource(resource: PgTableResource): resource is TableResourceWithCodec;
|
|
4
|
+
export declare function getSchemaName(resource: PgTableResource): string | null;
|
|
5
|
+
export declare function getUniques(resource: PgTableResource): PgUnique[];
|
|
6
|
+
export declare function getRelations(resource: PgTableResource): Record<string, PgRelation>;
|
|
7
|
+
export declare function getRelation(resource: PgTableResource, relationName: string): PgRelation | null;
|
|
8
|
+
export declare function sameAttributes(uniqueAttributes: string[] | undefined, relationAttributes: string[] | undefined): boolean;
|
|
9
|
+
export declare function getConfiguredSchemas(build: MetaBuild): string[];
|
|
10
|
+
export declare function getResourceCodec(resource: PgTableResource): (PgCodec & {
|
|
11
|
+
attributes: Record<string, PgAttribute>;
|
|
12
|
+
}) | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
function isRecord(value) {
|
|
2
|
+
return typeof value === 'object' && value !== null;
|
|
3
|
+
}
|
|
4
|
+
export function isTableResource(resource) {
|
|
5
|
+
return (!!resource.codec &&
|
|
6
|
+
!resource.codec.isAnonymous &&
|
|
7
|
+
isRecord(resource.codec.attributes) &&
|
|
8
|
+
!resource.parameters &&
|
|
9
|
+
!resource.isVirtual &&
|
|
10
|
+
!resource.isUnique);
|
|
11
|
+
}
|
|
12
|
+
export function getSchemaName(resource) {
|
|
13
|
+
const schemaName = resource.codec?.extensions?.pg?.schemaName;
|
|
14
|
+
if (typeof schemaName !== 'string' || schemaName.length === 0)
|
|
15
|
+
return null;
|
|
16
|
+
return schemaName;
|
|
17
|
+
}
|
|
18
|
+
export function getUniques(resource) {
|
|
19
|
+
return Array.isArray(resource.uniques) ? resource.uniques : [];
|
|
20
|
+
}
|
|
21
|
+
export function getRelations(resource) {
|
|
22
|
+
return resource.relations || resource.getRelations?.() || {};
|
|
23
|
+
}
|
|
24
|
+
export function getRelation(resource, relationName) {
|
|
25
|
+
if (!relationName)
|
|
26
|
+
return null;
|
|
27
|
+
if (typeof resource.getRelation === 'function') {
|
|
28
|
+
return resource.getRelation(relationName) || null;
|
|
29
|
+
}
|
|
30
|
+
const relations = getRelations(resource);
|
|
31
|
+
return relations[relationName] || null;
|
|
32
|
+
}
|
|
33
|
+
export function sameAttributes(uniqueAttributes, relationAttributes) {
|
|
34
|
+
if (!uniqueAttributes || !relationAttributes)
|
|
35
|
+
return false;
|
|
36
|
+
return (uniqueAttributes.length === relationAttributes.length &&
|
|
37
|
+
uniqueAttributes.every((attrName) => relationAttributes.includes(attrName)));
|
|
38
|
+
}
|
|
39
|
+
export function getConfiguredSchemas(build) {
|
|
40
|
+
const pgSchemas = build.options?.pgSchemas;
|
|
41
|
+
if (!Array.isArray(pgSchemas))
|
|
42
|
+
return [];
|
|
43
|
+
return pgSchemas.filter((schemaName) => typeof schemaName === 'string');
|
|
44
|
+
}
|
|
45
|
+
export function getResourceCodec(resource) {
|
|
46
|
+
const codec = resource.codec;
|
|
47
|
+
if (!codec?.attributes)
|
|
48
|
+
return null;
|
|
49
|
+
return codec;
|
|
50
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { FieldMeta, GqlTypeResolverBuild, PgAttribute } from './types';
|
|
2
|
+
export declare function pgTypeToGqlType(pgTypeName: string): string;
|
|
3
|
+
export declare function buildFieldMeta(name: string, attr: PgAttribute | null | undefined, build?: GqlTypeResolverBuild): FieldMeta;
|