@constructive-io/graphql-query 3.8.1 → 3.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.
- package/esm/introspect/infer-tables.js +54 -39
- package/introspect/infer-tables.js +54 -39
- package/package.json +3 -3
|
@@ -185,10 +185,10 @@ function buildCleanTable(entityName, entityType, typeMap, queryFields, mutationF
|
|
|
185
185
|
mutationOps.create ||
|
|
186
186
|
mutationOps.update ||
|
|
187
187
|
mutationOps.delete);
|
|
188
|
-
// Infer primary key from mutation inputs
|
|
189
|
-
const constraints = inferConstraints(entityName, typeMap);
|
|
188
|
+
// Infer primary key from mutation inputs (pass mutation ops for composite PK input type derivation)
|
|
189
|
+
const constraints = inferConstraints(entityName, typeMap, mutationOps);
|
|
190
190
|
// Infer the patch field name from UpdateXxxInput (e.g., "userPatch")
|
|
191
|
-
const patchFieldName = inferPatchFieldName(entityName, typeMap);
|
|
191
|
+
const patchFieldName = inferPatchFieldName(entityName, typeMap, mutationOps);
|
|
192
192
|
// Build inflection map from discovered types
|
|
193
193
|
const inflection = buildInflection(entityName, typeMap, entityToConnection);
|
|
194
194
|
// Combine query operations with fallbacks for UI purposes
|
|
@@ -462,21 +462,23 @@ function matchMutationOperations(entityName, mutationFields) {
|
|
|
462
462
|
if (field.name === expectedCreate) {
|
|
463
463
|
create = field.name;
|
|
464
464
|
}
|
|
465
|
-
// Match update (could be updateUser or
|
|
466
|
-
if (field.name === expectedUpdate
|
|
467
|
-
field.name
|
|
468
|
-
// Prefer non-ById version
|
|
469
|
-
if (!update || field.name === expectedUpdate) {
|
|
470
|
-
update = field.name;
|
|
471
|
-
}
|
|
465
|
+
// Match update (could be updateUser, updateUserById, or updateUserByFooAndBar for composite PKs)
|
|
466
|
+
if (field.name === expectedUpdate) {
|
|
467
|
+
update = field.name;
|
|
472
468
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
469
|
+
else if (!update &&
|
|
470
|
+
(field.name === `${expectedUpdate}ById` ||
|
|
471
|
+
field.name.startsWith(`${expectedUpdate}By`))) {
|
|
472
|
+
update = field.name;
|
|
473
|
+
}
|
|
474
|
+
// Match delete (could be deleteUser, deleteUserById, or deleteUserByFooAndBar for composite PKs)
|
|
475
|
+
if (field.name === expectedDelete) {
|
|
476
|
+
del = field.name;
|
|
477
|
+
}
|
|
478
|
+
else if (!del &&
|
|
479
|
+
(field.name === `${expectedDelete}ById` ||
|
|
480
|
+
field.name.startsWith(`${expectedDelete}By`))) {
|
|
481
|
+
del = field.name;
|
|
480
482
|
}
|
|
481
483
|
}
|
|
482
484
|
return { create, update, delete: del };
|
|
@@ -489,25 +491,29 @@ function matchMutationOperations(entityName, mutationFields) {
|
|
|
489
491
|
*
|
|
490
492
|
* Primary key can be inferred from Update/Delete mutation input types,
|
|
491
493
|
* which typically have an 'id' field or similar.
|
|
494
|
+
*
|
|
495
|
+
* For composite PK tables (e.g. junction tables), PostGraphile generates
|
|
496
|
+
* mutations like `deletePostTagByPostIdAndTagId` with input type
|
|
497
|
+
* `DeletePostTagByPostIdAndTagIdInput`. We derive input type names from
|
|
498
|
+
* the actual matched mutation names when available.
|
|
492
499
|
*/
|
|
493
|
-
function inferConstraints(entityName, typeMap) {
|
|
500
|
+
function inferConstraints(entityName, typeMap, mutations) {
|
|
494
501
|
const primaryKey = [];
|
|
495
|
-
|
|
496
|
-
const updateInputName = `Update${entityName}Input
|
|
497
|
-
const deleteInputName = `Delete${entityName}Input`;
|
|
502
|
+
const deleteInputName = inputTypeFromMutation(mutations?.delete, `Delete${entityName}Input`);
|
|
503
|
+
const updateInputName = inputTypeFromMutation(mutations?.update, `Update${entityName}Input`);
|
|
498
504
|
const updateInput = typeMap.get(updateInputName);
|
|
499
505
|
const deleteInput = typeMap.get(deleteInputName);
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
506
|
+
// Prefer Delete input (fewer non-PK fields) over Update input
|
|
507
|
+
const keyFields = inferPrimaryKeyFromInputObject(deleteInput).length > 0
|
|
508
|
+
? inferPrimaryKeyFromInputObject(deleteInput)
|
|
509
|
+
: inferPrimaryKeyFromInputObject(updateInput);
|
|
510
|
+
if (keyFields.length > 0) {
|
|
503
511
|
primaryKey.push({
|
|
504
512
|
name: 'primary',
|
|
505
|
-
fields:
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
},
|
|
510
|
-
],
|
|
513
|
+
fields: keyFields.map((f) => ({
|
|
514
|
+
name: f.name,
|
|
515
|
+
type: convertToCleanFieldType(f.type),
|
|
516
|
+
})),
|
|
511
517
|
});
|
|
512
518
|
}
|
|
513
519
|
// If no PK found from inputs, try to find 'id' field in entity type
|
|
@@ -535,22 +541,23 @@ function inferConstraints(entityName, typeMap) {
|
|
|
535
541
|
};
|
|
536
542
|
}
|
|
537
543
|
/**
|
|
538
|
-
* Infer
|
|
544
|
+
* Infer primary key fields from an Update/Delete input object.
|
|
539
545
|
*
|
|
540
546
|
* Priority:
|
|
541
547
|
* 1. Canonical keys: id, nodeId, rowId
|
|
542
|
-
* 2.
|
|
548
|
+
* 2. All non-patch/non-clientMutationId fields (supports composite keys)
|
|
543
549
|
*
|
|
544
|
-
*
|
|
550
|
+
* Returns all candidate key fields, enabling composite PK detection
|
|
551
|
+
* for junction tables like PostTag(postId, tagId).
|
|
545
552
|
*/
|
|
546
553
|
function inferPrimaryKeyFromInputObject(inputType) {
|
|
547
554
|
const inputFields = inputType?.inputFields ?? [];
|
|
548
555
|
if (inputFields.length === 0)
|
|
549
|
-
return
|
|
556
|
+
return [];
|
|
550
557
|
const canonicalKey = inputFields.find((field) => field.name === 'id' || field.name === 'nodeId' || field.name === 'rowId');
|
|
551
558
|
if (canonicalKey)
|
|
552
|
-
return canonicalKey;
|
|
553
|
-
|
|
559
|
+
return [canonicalKey];
|
|
560
|
+
return inputFields.filter((field) => {
|
|
554
561
|
if (field.name === 'clientMutationId')
|
|
555
562
|
return false;
|
|
556
563
|
const baseTypeName = getBaseTypeName(field.type);
|
|
@@ -562,7 +569,6 @@ function inferPrimaryKeyFromInputObject(inputType) {
|
|
|
562
569
|
return false;
|
|
563
570
|
return true;
|
|
564
571
|
});
|
|
565
|
-
return candidates.length === 1 ? candidates[0] : null;
|
|
566
572
|
}
|
|
567
573
|
/**
|
|
568
574
|
* Infer the patch field name from an Update input type.
|
|
@@ -573,8 +579,8 @@ function inferPrimaryKeyFromInputObject(inputType) {
|
|
|
573
579
|
*
|
|
574
580
|
* Pattern: {lcFirst(entityTypeName)}Patch
|
|
575
581
|
*/
|
|
576
|
-
function inferPatchFieldName(entityName, typeMap) {
|
|
577
|
-
const updateInputName = `Update${entityName}Input
|
|
582
|
+
function inferPatchFieldName(entityName, typeMap, mutations) {
|
|
583
|
+
const updateInputName = inputTypeFromMutation(mutations?.update, `Update${entityName}Input`);
|
|
578
584
|
const updateInput = typeMap.get(updateInputName);
|
|
579
585
|
const inputFields = updateInput?.inputFields ?? [];
|
|
580
586
|
// Find the field whose type name ends in 'Patch'
|
|
@@ -679,6 +685,15 @@ function findOrderByType(entityName, pluralName, typeMap) {
|
|
|
679
685
|
// ============================================================================
|
|
680
686
|
// Utility Functions
|
|
681
687
|
// ============================================================================
|
|
688
|
+
/**
|
|
689
|
+
* Derive the input type name for a mutation.
|
|
690
|
+
* PostGraphile always generates input types as ${PascalCaseMutationName}Input.
|
|
691
|
+
* When the actual mutation name is known (e.g. from introspection), we derive
|
|
692
|
+
* from it directly. Otherwise we fall back to the conventional ${Verb}${Entity}Input.
|
|
693
|
+
*/
|
|
694
|
+
function inputTypeFromMutation(mutationName, fallback) {
|
|
695
|
+
return mutationName ? ucFirst(mutationName) + 'Input' : fallback;
|
|
696
|
+
}
|
|
682
697
|
/**
|
|
683
698
|
* Build a map of type name → IntrospectionType for efficient lookup
|
|
684
699
|
*/
|
|
@@ -188,10 +188,10 @@ function buildCleanTable(entityName, entityType, typeMap, queryFields, mutationF
|
|
|
188
188
|
mutationOps.create ||
|
|
189
189
|
mutationOps.update ||
|
|
190
190
|
mutationOps.delete);
|
|
191
|
-
// Infer primary key from mutation inputs
|
|
192
|
-
const constraints = inferConstraints(entityName, typeMap);
|
|
191
|
+
// Infer primary key from mutation inputs (pass mutation ops for composite PK input type derivation)
|
|
192
|
+
const constraints = inferConstraints(entityName, typeMap, mutationOps);
|
|
193
193
|
// Infer the patch field name from UpdateXxxInput (e.g., "userPatch")
|
|
194
|
-
const patchFieldName = inferPatchFieldName(entityName, typeMap);
|
|
194
|
+
const patchFieldName = inferPatchFieldName(entityName, typeMap, mutationOps);
|
|
195
195
|
// Build inflection map from discovered types
|
|
196
196
|
const inflection = buildInflection(entityName, typeMap, entityToConnection);
|
|
197
197
|
// Combine query operations with fallbacks for UI purposes
|
|
@@ -465,21 +465,23 @@ function matchMutationOperations(entityName, mutationFields) {
|
|
|
465
465
|
if (field.name === expectedCreate) {
|
|
466
466
|
create = field.name;
|
|
467
467
|
}
|
|
468
|
-
// Match update (could be updateUser or
|
|
469
|
-
if (field.name === expectedUpdate
|
|
470
|
-
field.name
|
|
471
|
-
// Prefer non-ById version
|
|
472
|
-
if (!update || field.name === expectedUpdate) {
|
|
473
|
-
update = field.name;
|
|
474
|
-
}
|
|
468
|
+
// Match update (could be updateUser, updateUserById, or updateUserByFooAndBar for composite PKs)
|
|
469
|
+
if (field.name === expectedUpdate) {
|
|
470
|
+
update = field.name;
|
|
475
471
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
472
|
+
else if (!update &&
|
|
473
|
+
(field.name === `${expectedUpdate}ById` ||
|
|
474
|
+
field.name.startsWith(`${expectedUpdate}By`))) {
|
|
475
|
+
update = field.name;
|
|
476
|
+
}
|
|
477
|
+
// Match delete (could be deleteUser, deleteUserById, or deleteUserByFooAndBar for composite PKs)
|
|
478
|
+
if (field.name === expectedDelete) {
|
|
479
|
+
del = field.name;
|
|
480
|
+
}
|
|
481
|
+
else if (!del &&
|
|
482
|
+
(field.name === `${expectedDelete}ById` ||
|
|
483
|
+
field.name.startsWith(`${expectedDelete}By`))) {
|
|
484
|
+
del = field.name;
|
|
483
485
|
}
|
|
484
486
|
}
|
|
485
487
|
return { create, update, delete: del };
|
|
@@ -492,25 +494,29 @@ function matchMutationOperations(entityName, mutationFields) {
|
|
|
492
494
|
*
|
|
493
495
|
* Primary key can be inferred from Update/Delete mutation input types,
|
|
494
496
|
* which typically have an 'id' field or similar.
|
|
497
|
+
*
|
|
498
|
+
* For composite PK tables (e.g. junction tables), PostGraphile generates
|
|
499
|
+
* mutations like `deletePostTagByPostIdAndTagId` with input type
|
|
500
|
+
* `DeletePostTagByPostIdAndTagIdInput`. We derive input type names from
|
|
501
|
+
* the actual matched mutation names when available.
|
|
495
502
|
*/
|
|
496
|
-
function inferConstraints(entityName, typeMap) {
|
|
503
|
+
function inferConstraints(entityName, typeMap, mutations) {
|
|
497
504
|
const primaryKey = [];
|
|
498
|
-
|
|
499
|
-
const updateInputName = `Update${entityName}Input
|
|
500
|
-
const deleteInputName = `Delete${entityName}Input`;
|
|
505
|
+
const deleteInputName = inputTypeFromMutation(mutations?.delete, `Delete${entityName}Input`);
|
|
506
|
+
const updateInputName = inputTypeFromMutation(mutations?.update, `Update${entityName}Input`);
|
|
501
507
|
const updateInput = typeMap.get(updateInputName);
|
|
502
508
|
const deleteInput = typeMap.get(deleteInputName);
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
509
|
+
// Prefer Delete input (fewer non-PK fields) over Update input
|
|
510
|
+
const keyFields = inferPrimaryKeyFromInputObject(deleteInput).length > 0
|
|
511
|
+
? inferPrimaryKeyFromInputObject(deleteInput)
|
|
512
|
+
: inferPrimaryKeyFromInputObject(updateInput);
|
|
513
|
+
if (keyFields.length > 0) {
|
|
506
514
|
primaryKey.push({
|
|
507
515
|
name: 'primary',
|
|
508
|
-
fields:
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
},
|
|
513
|
-
],
|
|
516
|
+
fields: keyFields.map((f) => ({
|
|
517
|
+
name: f.name,
|
|
518
|
+
type: convertToCleanFieldType(f.type),
|
|
519
|
+
})),
|
|
514
520
|
});
|
|
515
521
|
}
|
|
516
522
|
// If no PK found from inputs, try to find 'id' field in entity type
|
|
@@ -538,22 +544,23 @@ function inferConstraints(entityName, typeMap) {
|
|
|
538
544
|
};
|
|
539
545
|
}
|
|
540
546
|
/**
|
|
541
|
-
* Infer
|
|
547
|
+
* Infer primary key fields from an Update/Delete input object.
|
|
542
548
|
*
|
|
543
549
|
* Priority:
|
|
544
550
|
* 1. Canonical keys: id, nodeId, rowId
|
|
545
|
-
* 2.
|
|
551
|
+
* 2. All non-patch/non-clientMutationId fields (supports composite keys)
|
|
546
552
|
*
|
|
547
|
-
*
|
|
553
|
+
* Returns all candidate key fields, enabling composite PK detection
|
|
554
|
+
* for junction tables like PostTag(postId, tagId).
|
|
548
555
|
*/
|
|
549
556
|
function inferPrimaryKeyFromInputObject(inputType) {
|
|
550
557
|
const inputFields = inputType?.inputFields ?? [];
|
|
551
558
|
if (inputFields.length === 0)
|
|
552
|
-
return
|
|
559
|
+
return [];
|
|
553
560
|
const canonicalKey = inputFields.find((field) => field.name === 'id' || field.name === 'nodeId' || field.name === 'rowId');
|
|
554
561
|
if (canonicalKey)
|
|
555
|
-
return canonicalKey;
|
|
556
|
-
|
|
562
|
+
return [canonicalKey];
|
|
563
|
+
return inputFields.filter((field) => {
|
|
557
564
|
if (field.name === 'clientMutationId')
|
|
558
565
|
return false;
|
|
559
566
|
const baseTypeName = (0, introspection_1.getBaseTypeName)(field.type);
|
|
@@ -565,7 +572,6 @@ function inferPrimaryKeyFromInputObject(inputType) {
|
|
|
565
572
|
return false;
|
|
566
573
|
return true;
|
|
567
574
|
});
|
|
568
|
-
return candidates.length === 1 ? candidates[0] : null;
|
|
569
575
|
}
|
|
570
576
|
/**
|
|
571
577
|
* Infer the patch field name from an Update input type.
|
|
@@ -576,8 +582,8 @@ function inferPrimaryKeyFromInputObject(inputType) {
|
|
|
576
582
|
*
|
|
577
583
|
* Pattern: {lcFirst(entityTypeName)}Patch
|
|
578
584
|
*/
|
|
579
|
-
function inferPatchFieldName(entityName, typeMap) {
|
|
580
|
-
const updateInputName = `Update${entityName}Input
|
|
585
|
+
function inferPatchFieldName(entityName, typeMap, mutations) {
|
|
586
|
+
const updateInputName = inputTypeFromMutation(mutations?.update, `Update${entityName}Input`);
|
|
581
587
|
const updateInput = typeMap.get(updateInputName);
|
|
582
588
|
const inputFields = updateInput?.inputFields ?? [];
|
|
583
589
|
// Find the field whose type name ends in 'Patch'
|
|
@@ -682,6 +688,15 @@ function findOrderByType(entityName, pluralName, typeMap) {
|
|
|
682
688
|
// ============================================================================
|
|
683
689
|
// Utility Functions
|
|
684
690
|
// ============================================================================
|
|
691
|
+
/**
|
|
692
|
+
* Derive the input type name for a mutation.
|
|
693
|
+
* PostGraphile always generates input types as ${PascalCaseMutationName}Input.
|
|
694
|
+
* When the actual mutation name is known (e.g. from introspection), we derive
|
|
695
|
+
* from it directly. Otherwise we fall back to the conventional ${Verb}${Entity}Input.
|
|
696
|
+
*/
|
|
697
|
+
function inputTypeFromMutation(mutationName, fallback) {
|
|
698
|
+
return mutationName ? (0, inflekt_1.ucFirst)(mutationName) + 'Input' : fallback;
|
|
699
|
+
}
|
|
685
700
|
/**
|
|
686
701
|
* Build a map of type name → IntrospectionType for efficient lookup
|
|
687
702
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructive-io/graphql-query",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.1",
|
|
4
4
|
"description": "Constructive GraphQL Query",
|
|
5
5
|
"author": "Constructive <developers@constructive.io>",
|
|
6
6
|
"main": "index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"grafast": "1.0.0-rc.9",
|
|
35
35
|
"graphile-build-pg": "5.0.0-rc.8",
|
|
36
36
|
"graphile-config": "1.0.0-rc.6",
|
|
37
|
-
"graphile-settings": "^4.12.
|
|
37
|
+
"graphile-settings": "^4.12.2",
|
|
38
38
|
"graphql": "16.13.0",
|
|
39
39
|
"inflection": "^3.0.0",
|
|
40
40
|
"inflekt": "^0.3.3",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"makage": "^0.1.10"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "bfc62d3bf8b8374dea67536cfea9d16cbe0575ac"
|
|
55
55
|
}
|