@graphql-tools/delegate 12.0.17-alpha-cc6e877dd43c47e20e00638efc161a0bed4e2ccb → 12.0.17
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/CHANGELOG.md +3 -30
- package/dist/index.cjs +34 -334
- package/dist/index.js +34 -334
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,40 +1,13 @@
|
|
|
1
1
|
# @graphql-tools/delegate
|
|
2
2
|
|
|
3
|
-
## 12.0.17
|
|
3
|
+
## 12.0.17
|
|
4
4
|
### Patch Changes
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
- [#
|
|
8
|
+
- [#2382](https://github.com/graphql-hive/gateway/pull/2382) [`3e774e0`](https://github.com/graphql-hive/gateway/commit/3e774e050bc2d3c33e0f36a258ab6a8d94bf0750) Thanks [@gonzo-32](https://github.com/gonzo-32)! - Fix cross-subgraph delegation for unavailable fields selected through inline fragments or fragment spreads on interface fields.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
1. Forward **every** field listed in `@provides` to that subgraph, even when the client never asked for them.
|
|
13
|
-
2. After receiving the response, plan additional delegations to the owner subgraph for `@provides`-covered fields whenever the providing subgraph declared them as `@external`, even though the data was already returned.
|
|
14
|
-
|
|
15
|
-
For example with:
|
|
16
|
-
|
|
17
|
-
```graphql
|
|
18
|
-
# subgraph B (provider)
|
|
19
|
-
type Query {
|
|
20
|
-
entity: Entity @provides(fields: "name description")
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
type Entity @key(fields: "id") {
|
|
24
|
-
id: ID!
|
|
25
|
-
name: String! @external
|
|
26
|
-
description: String! @external
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
a client query of `{ entity { id name } }` would still cause the gateway to ask subgraph B for `description` *and* fetch `name` again from subgraph A (the owner of `Entity`).
|
|
31
|
-
|
|
32
|
-
After this fix:
|
|
33
|
-
|
|
34
|
-
- Only the `@provides` fields the client actually selected are forwarded to the providing subgraph (request side).
|
|
35
|
-
- The delegation planner now recognises `@provides` declarations at every nested level (e.g. `@provides(fields: "categories { id name subCategories { id name } }")`) and `@provides` declarations made via inline fragments on union/interface members (e.g. `@provides(fields: "... on Book { title }")`), so the gateway no longer round-trips to the owner subgraph for fields that the providing subgraph has already returned.
|
|
36
|
-
|
|
37
|
-
Aliases, fragments, fragment spreads, `@include`/`@skip` directives wrapping a `@provides` field, and nested `@provides` selections are preserved.
|
|
10
|
+
The fix in `extractUnavailableFieldsFromSelectionSet` within `extractUnavailableFields.ts` now preserves the fragment wrapper and type condition, so concrete-type fields do not lose their type context and get treated as missing.
|
|
38
11
|
|
|
39
12
|
## 12.0.16
|
|
40
13
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -590,7 +590,7 @@ const handleOverrideByDelegation = utils.memoize3(
|
|
|
590
590
|
}
|
|
591
591
|
);
|
|
592
592
|
|
|
593
|
-
function finalizeGatewayDocument(targetSchema,
|
|
593
|
+
function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlappingAliases, delegationContext) {
|
|
594
594
|
let usedVariables = [];
|
|
595
595
|
let usedFragments = [];
|
|
596
596
|
const newOperations = [];
|
|
@@ -687,102 +687,38 @@ function finalizeGatewayDocument(targetSchema, originalDocument, fragments, oper
|
|
|
687
687
|
definitions: [...newOperations, ...newFragments]
|
|
688
688
|
};
|
|
689
689
|
const stitchingInfo = delegationContext.info?.schema?.extensions?.["stitchingInfo"];
|
|
690
|
-
if (stitchingInfo != null
|
|
691
|
-
stitchingInfo,
|
|
692
|
-
delegationContext.subschema
|
|
693
|
-
)) {
|
|
694
|
-
const {
|
|
695
|
-
selectionSetsByPath: originalFieldSelectionSetsByPath,
|
|
696
|
-
fragments: originalFragmentsByName
|
|
697
|
-
} = collectFieldSelectionSetsByPath(originalDocument);
|
|
690
|
+
if (stitchingInfo != null) {
|
|
698
691
|
const typeInfo = getTypeInfo(targetSchema);
|
|
699
|
-
const pathStack = [];
|
|
700
|
-
const inlineFragmentCounterStack = [];
|
|
701
692
|
newDocument = graphql.visit(
|
|
702
693
|
newDocument,
|
|
703
694
|
graphql.visitWithTypeInfo(typeInfo, {
|
|
704
|
-
[graphql.Kind.
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
}
|
|
711
|
-
},
|
|
712
|
-
[graphql.Kind.OPERATION_DEFINITION]: {
|
|
713
|
-
enter(node) {
|
|
714
|
-
pathStack.push(`op:${node.operation}:${node.name?.value ?? ""}`);
|
|
715
|
-
},
|
|
716
|
-
leave() {
|
|
717
|
-
pathStack.pop();
|
|
718
|
-
}
|
|
719
|
-
},
|
|
720
|
-
[graphql.Kind.FRAGMENT_DEFINITION]: {
|
|
721
|
-
enter(node) {
|
|
722
|
-
pathStack.push(`frag:${node.name.value}`);
|
|
723
|
-
},
|
|
724
|
-
leave() {
|
|
725
|
-
pathStack.pop();
|
|
726
|
-
}
|
|
727
|
-
},
|
|
728
|
-
[graphql.Kind.INLINE_FRAGMENT]: {
|
|
729
|
-
enter(node) {
|
|
730
|
-
pathStack.push(
|
|
731
|
-
nextInlineFragmentPathSegment(
|
|
732
|
-
node,
|
|
733
|
-
inlineFragmentCounterStack[inlineFragmentCounterStack.length - 1]
|
|
734
|
-
)
|
|
735
|
-
);
|
|
736
|
-
},
|
|
737
|
-
leave() {
|
|
738
|
-
pathStack.pop();
|
|
739
|
-
}
|
|
740
|
-
},
|
|
741
|
-
[graphql.Kind.FIELD]: {
|
|
742
|
-
enter(fieldNode) {
|
|
743
|
-
pathStack.push(fieldNode.alias?.value ?? fieldNode.name.value);
|
|
744
|
-
},
|
|
745
|
-
leave(fieldNode) {
|
|
746
|
-
try {
|
|
747
|
-
const parentType = typeInfo.getParentType();
|
|
748
|
-
if (!parentType) {
|
|
749
|
-
return void 0;
|
|
750
|
-
}
|
|
751
|
-
const typeConfig = stitchingInfo?.mergedTypes?.[parentType.name];
|
|
695
|
+
[graphql.Kind.FIELD](fieldNode) {
|
|
696
|
+
const parentType = typeInfo.getParentType();
|
|
697
|
+
if (parentType) {
|
|
698
|
+
const parentTypeName = parentType.name;
|
|
699
|
+
const typeConfig = stitchingInfo?.mergedTypes?.[parentTypeName];
|
|
700
|
+
if (typeConfig) {
|
|
752
701
|
const providedSelectionsByField = typeConfig?.providedSelectionsByField?.get(
|
|
753
702
|
delegationContext.subschema
|
|
754
703
|
);
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
);
|
|
769
|
-
if (!requestedProvidedSelections.length) {
|
|
770
|
-
return void 0;
|
|
771
|
-
}
|
|
772
|
-
return {
|
|
773
|
-
...fieldNode,
|
|
774
|
-
selectionSet: {
|
|
775
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
776
|
-
selections: [
|
|
777
|
-
...requestedProvidedSelections,
|
|
778
|
-
...fieldNode.selectionSet?.selections ?? []
|
|
779
|
-
]
|
|
704
|
+
if (providedSelectionsByField) {
|
|
705
|
+
const providedSelection = providedSelectionsByField[fieldNode.name.value];
|
|
706
|
+
if (providedSelection) {
|
|
707
|
+
return {
|
|
708
|
+
...fieldNode,
|
|
709
|
+
selectionSet: {
|
|
710
|
+
kind: graphql.Kind.SELECTION_SET,
|
|
711
|
+
selections: [
|
|
712
|
+
...providedSelection.selections,
|
|
713
|
+
...fieldNode.selectionSet?.selections ?? []
|
|
714
|
+
]
|
|
715
|
+
}
|
|
716
|
+
};
|
|
780
717
|
}
|
|
781
|
-
}
|
|
782
|
-
} finally {
|
|
783
|
-
pathStack.pop();
|
|
718
|
+
}
|
|
784
719
|
}
|
|
785
720
|
}
|
|
721
|
+
return fieldNode;
|
|
786
722
|
}
|
|
787
723
|
})
|
|
788
724
|
);
|
|
@@ -797,7 +733,6 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
|
|
|
797
733
|
const { targetSchema } = delegationContext;
|
|
798
734
|
const { usedVariables, newDocument } = finalizeGatewayDocument(
|
|
799
735
|
targetSchema,
|
|
800
|
-
originalRequest.document,
|
|
801
736
|
fragments,
|
|
802
737
|
operations,
|
|
803
738
|
onOverlappingAliases,
|
|
@@ -826,251 +761,6 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
|
|
|
826
761
|
function isTypeNameField(selection) {
|
|
827
762
|
return selection.kind === graphql.Kind.FIELD && !selection.alias && selection.name.value === "__typename";
|
|
828
763
|
}
|
|
829
|
-
function collectFieldSelectionSetsByPathImpl(document) {
|
|
830
|
-
const selectionSetsByPath = /* @__PURE__ */ new Map();
|
|
831
|
-
const fragments = /* @__PURE__ */ new Map();
|
|
832
|
-
for (const def of document.definitions) {
|
|
833
|
-
if (def.kind === graphql.Kind.FRAGMENT_DEFINITION) {
|
|
834
|
-
fragments.set(def.name.value, def);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
const pathStack = [];
|
|
838
|
-
const inlineFragmentCounterStack = [];
|
|
839
|
-
graphql.visit(document, {
|
|
840
|
-
[graphql.Kind.SELECTION_SET]: {
|
|
841
|
-
enter() {
|
|
842
|
-
inlineFragmentCounterStack.push(/* @__PURE__ */ new Map());
|
|
843
|
-
},
|
|
844
|
-
leave() {
|
|
845
|
-
inlineFragmentCounterStack.pop();
|
|
846
|
-
}
|
|
847
|
-
},
|
|
848
|
-
[graphql.Kind.OPERATION_DEFINITION]: {
|
|
849
|
-
enter(node) {
|
|
850
|
-
pathStack.push(`op:${node.operation}:${node.name?.value ?? ""}`);
|
|
851
|
-
},
|
|
852
|
-
leave() {
|
|
853
|
-
pathStack.pop();
|
|
854
|
-
}
|
|
855
|
-
},
|
|
856
|
-
[graphql.Kind.FRAGMENT_DEFINITION]: {
|
|
857
|
-
enter(node) {
|
|
858
|
-
pathStack.push(`frag:${node.name.value}`);
|
|
859
|
-
},
|
|
860
|
-
leave() {
|
|
861
|
-
pathStack.pop();
|
|
862
|
-
}
|
|
863
|
-
},
|
|
864
|
-
[graphql.Kind.INLINE_FRAGMENT]: {
|
|
865
|
-
enter(node) {
|
|
866
|
-
pathStack.push(
|
|
867
|
-
nextInlineFragmentPathSegment(
|
|
868
|
-
node,
|
|
869
|
-
inlineFragmentCounterStack[inlineFragmentCounterStack.length - 1]
|
|
870
|
-
)
|
|
871
|
-
);
|
|
872
|
-
},
|
|
873
|
-
leave() {
|
|
874
|
-
pathStack.pop();
|
|
875
|
-
}
|
|
876
|
-
},
|
|
877
|
-
[graphql.Kind.FIELD]: {
|
|
878
|
-
enter(node) {
|
|
879
|
-
pathStack.push(node.alias?.value ?? node.name.value);
|
|
880
|
-
if (node.selectionSet) {
|
|
881
|
-
selectionSetsByPath.set(pathStack.join(">"), node.selectionSet);
|
|
882
|
-
if (node.alias) {
|
|
883
|
-
const namePath = [...pathStack.slice(0, -1), node.name.value].join(
|
|
884
|
-
">"
|
|
885
|
-
);
|
|
886
|
-
if (!selectionSetsByPath.has(namePath)) {
|
|
887
|
-
selectionSetsByPath.set(namePath, node.selectionSet);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
},
|
|
892
|
-
leave() {
|
|
893
|
-
pathStack.pop();
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
});
|
|
897
|
-
return { selectionSetsByPath, fragments };
|
|
898
|
-
}
|
|
899
|
-
const collectFieldSelectionSetsByPath = utils.memoize1(
|
|
900
|
-
collectFieldSelectionSetsByPathImpl
|
|
901
|
-
);
|
|
902
|
-
function nextInlineFragmentPathSegment(node, counter) {
|
|
903
|
-
const typeName = node.typeCondition?.name.value ?? "";
|
|
904
|
-
if (!counter) {
|
|
905
|
-
return `inline:${typeName}:0`;
|
|
906
|
-
}
|
|
907
|
-
const idx = counter.get(typeName) ?? 0;
|
|
908
|
-
counter.set(typeName, idx + 1);
|
|
909
|
-
return `inline:${typeName}:${idx}`;
|
|
910
|
-
}
|
|
911
|
-
function subschemaHasProvidedSelectionsImpl(stitchingInfo, subschema) {
|
|
912
|
-
const mergedTypes = stitchingInfo.mergedTypes;
|
|
913
|
-
if (!mergedTypes) {
|
|
914
|
-
return false;
|
|
915
|
-
}
|
|
916
|
-
for (const typeConfig of Object.values(mergedTypes)) {
|
|
917
|
-
if (typeConfig?.providedSelectionsByField?.has(subschema)) {
|
|
918
|
-
return true;
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
return false;
|
|
922
|
-
}
|
|
923
|
-
const subschemaHasProvidedSelections = utils.memoize2(
|
|
924
|
-
subschemaHasProvidedSelectionsImpl
|
|
925
|
-
);
|
|
926
|
-
function lookupOriginalSelectionSet(pathStack, fieldNode, selectionSetsByPath) {
|
|
927
|
-
const exact = selectionSetsByPath.get(pathStack.join(">"));
|
|
928
|
-
if (exact || !fieldNode.alias) {
|
|
929
|
-
return exact;
|
|
930
|
-
}
|
|
931
|
-
const fallback = [...pathStack.slice(0, -1), fieldNode.name.value].join(">");
|
|
932
|
-
return selectionSetsByPath.get(fallback);
|
|
933
|
-
}
|
|
934
|
-
function intersectProvidedSelections(providedSelections, originalSelectionSet, fragments) {
|
|
935
|
-
if (!originalSelectionSet) {
|
|
936
|
-
return [];
|
|
937
|
-
}
|
|
938
|
-
const providedFieldsByName = /* @__PURE__ */ new Map();
|
|
939
|
-
const otherProvided = [];
|
|
940
|
-
for (const provided of providedSelections) {
|
|
941
|
-
if (provided.kind === graphql.Kind.FIELD) {
|
|
942
|
-
providedFieldsByName.set(provided.name.value, provided);
|
|
943
|
-
} else {
|
|
944
|
-
otherProvided.push(provided);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
const result = [];
|
|
948
|
-
collectMatchingOriginalSelections(
|
|
949
|
-
originalSelectionSet,
|
|
950
|
-
providedFieldsByName,
|
|
951
|
-
fragments,
|
|
952
|
-
/* @__PURE__ */ new Set(),
|
|
953
|
-
/* @__PURE__ */ new Set(),
|
|
954
|
-
result
|
|
955
|
-
);
|
|
956
|
-
return [...result, ...otherProvided];
|
|
957
|
-
}
|
|
958
|
-
function collectMatchingOriginalSelections(selectionSet, providedFieldsByName, fragments, seenFieldNames, seenFragmentNames, result) {
|
|
959
|
-
for (const sel of selectionSet.selections) {
|
|
960
|
-
if (sel.kind === graphql.Kind.FIELD) {
|
|
961
|
-
const provided = providedFieldsByName.get(sel.name.value);
|
|
962
|
-
if (!provided) {
|
|
963
|
-
continue;
|
|
964
|
-
}
|
|
965
|
-
const dedupKey = sel.alias?.value ?? sel.name.value;
|
|
966
|
-
if (seenFieldNames.has(dedupKey)) {
|
|
967
|
-
continue;
|
|
968
|
-
}
|
|
969
|
-
seenFieldNames.add(dedupKey);
|
|
970
|
-
if (provided.selectionSet && sel.selectionSet) {
|
|
971
|
-
const nested = intersectProvidedSelections(
|
|
972
|
-
provided.selectionSet.selections,
|
|
973
|
-
sel.selectionSet,
|
|
974
|
-
fragments
|
|
975
|
-
);
|
|
976
|
-
if (!nested.length) {
|
|
977
|
-
continue;
|
|
978
|
-
}
|
|
979
|
-
result.push({
|
|
980
|
-
...sel,
|
|
981
|
-
selectionSet: {
|
|
982
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
983
|
-
selections: nested
|
|
984
|
-
}
|
|
985
|
-
});
|
|
986
|
-
} else {
|
|
987
|
-
result.push(sel);
|
|
988
|
-
}
|
|
989
|
-
} else if (sel.kind === graphql.Kind.INLINE_FRAGMENT && sel.selectionSet) {
|
|
990
|
-
const hasDirectives = (sel.directives?.length ?? 0) > 0;
|
|
991
|
-
const hasTypeCondition = sel.typeCondition != null;
|
|
992
|
-
if (hasDirectives || hasTypeCondition) {
|
|
993
|
-
const inner = [];
|
|
994
|
-
collectMatchingOriginalSelections(
|
|
995
|
-
sel.selectionSet,
|
|
996
|
-
providedFieldsByName,
|
|
997
|
-
fragments,
|
|
998
|
-
/* @__PURE__ */ new Set(),
|
|
999
|
-
/* @__PURE__ */ new Set(),
|
|
1000
|
-
inner
|
|
1001
|
-
);
|
|
1002
|
-
if (inner.length === 0) {
|
|
1003
|
-
continue;
|
|
1004
|
-
}
|
|
1005
|
-
result.push({
|
|
1006
|
-
kind: graphql.Kind.INLINE_FRAGMENT,
|
|
1007
|
-
typeCondition: sel.typeCondition,
|
|
1008
|
-
directives: sel.directives,
|
|
1009
|
-
selectionSet: {
|
|
1010
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
1011
|
-
selections: inner
|
|
1012
|
-
}
|
|
1013
|
-
});
|
|
1014
|
-
} else {
|
|
1015
|
-
collectMatchingOriginalSelections(
|
|
1016
|
-
sel.selectionSet,
|
|
1017
|
-
providedFieldsByName,
|
|
1018
|
-
fragments,
|
|
1019
|
-
seenFieldNames,
|
|
1020
|
-
seenFragmentNames,
|
|
1021
|
-
result
|
|
1022
|
-
);
|
|
1023
|
-
}
|
|
1024
|
-
} else if (sel.kind === graphql.Kind.FRAGMENT_SPREAD) {
|
|
1025
|
-
const fragmentDef = fragments.get(sel.name.value);
|
|
1026
|
-
if (!fragmentDef) {
|
|
1027
|
-
continue;
|
|
1028
|
-
}
|
|
1029
|
-
const hasSpreadDirectives = (sel.directives?.length ?? 0) > 0;
|
|
1030
|
-
const hasFragmentDirectives = (fragmentDef.directives?.length ?? 0) > 0;
|
|
1031
|
-
if (hasSpreadDirectives || hasFragmentDirectives) {
|
|
1032
|
-
const inner = [];
|
|
1033
|
-
collectMatchingOriginalSelections(
|
|
1034
|
-
fragmentDef.selectionSet,
|
|
1035
|
-
providedFieldsByName,
|
|
1036
|
-
fragments,
|
|
1037
|
-
/* @__PURE__ */ new Set(),
|
|
1038
|
-
/* @__PURE__ */ new Set(),
|
|
1039
|
-
inner
|
|
1040
|
-
);
|
|
1041
|
-
if (inner.length === 0) {
|
|
1042
|
-
continue;
|
|
1043
|
-
}
|
|
1044
|
-
const combinedDirectives = [
|
|
1045
|
-
...sel.directives ?? [],
|
|
1046
|
-
...fragmentDef.directives ?? []
|
|
1047
|
-
];
|
|
1048
|
-
result.push({
|
|
1049
|
-
kind: graphql.Kind.INLINE_FRAGMENT,
|
|
1050
|
-
typeCondition: fragmentDef.typeCondition,
|
|
1051
|
-
directives: combinedDirectives.length > 0 ? combinedDirectives : void 0,
|
|
1052
|
-
selectionSet: {
|
|
1053
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
1054
|
-
selections: inner
|
|
1055
|
-
}
|
|
1056
|
-
});
|
|
1057
|
-
} else {
|
|
1058
|
-
if (seenFragmentNames.has(sel.name.value)) {
|
|
1059
|
-
continue;
|
|
1060
|
-
}
|
|
1061
|
-
seenFragmentNames.add(sel.name.value);
|
|
1062
|
-
collectMatchingOriginalSelections(
|
|
1063
|
-
fragmentDef.selectionSet,
|
|
1064
|
-
providedFieldsByName,
|
|
1065
|
-
fragments,
|
|
1066
|
-
seenFieldNames,
|
|
1067
|
-
seenFragmentNames,
|
|
1068
|
-
result
|
|
1069
|
-
);
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
764
|
function filterTypenameFields(selections) {
|
|
1075
765
|
let hasTypeNameField = false;
|
|
1076
766
|
const filteredSelections = selections.filter((selection) => {
|
|
@@ -3205,6 +2895,7 @@ function extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldSelect
|
|
|
3205
2895
|
});
|
|
3206
2896
|
}
|
|
3207
2897
|
} else if (graphql.isObjectType(subFieldType) || graphql.isInterfaceType(subFieldType)) {
|
|
2898
|
+
const unavailableSubSelections = [];
|
|
3208
2899
|
const subFieldTypeFields = subFieldType.getFields();
|
|
3209
2900
|
for (const subSelection of selection.selectionSet.selections) {
|
|
3210
2901
|
if (subSelection.kind === graphql.Kind.FIELD) {
|
|
@@ -3215,11 +2906,20 @@ function extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldSelect
|
|
|
3215
2906
|
const subSelectionField = subFieldTypeFields[subSelection.name.value];
|
|
3216
2907
|
if (!subSelectionField) {
|
|
3217
2908
|
if (shouldAdd(subFieldType, subSelection)) {
|
|
3218
|
-
|
|
2909
|
+
unavailableSubSelections.push(subSelection);
|
|
3219
2910
|
}
|
|
3220
2911
|
}
|
|
3221
2912
|
}
|
|
3222
2913
|
}
|
|
2914
|
+
if (unavailableSubSelections.length) {
|
|
2915
|
+
unavailableSelections.push({
|
|
2916
|
+
...selection,
|
|
2917
|
+
selectionSet: {
|
|
2918
|
+
kind: graphql.Kind.SELECTION_SET,
|
|
2919
|
+
selections: unavailableSubSelections
|
|
2920
|
+
}
|
|
2921
|
+
});
|
|
2922
|
+
}
|
|
3223
2923
|
}
|
|
3224
2924
|
} else if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
|
|
3225
2925
|
const fragment = fragments[selection.name.value];
|
package/dist/index.js
CHANGED
|
@@ -590,7 +590,7 @@ const handleOverrideByDelegation = memoize3(
|
|
|
590
590
|
}
|
|
591
591
|
);
|
|
592
592
|
|
|
593
|
-
function finalizeGatewayDocument(targetSchema,
|
|
593
|
+
function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlappingAliases, delegationContext) {
|
|
594
594
|
let usedVariables = [];
|
|
595
595
|
let usedFragments = [];
|
|
596
596
|
const newOperations = [];
|
|
@@ -687,102 +687,38 @@ function finalizeGatewayDocument(targetSchema, originalDocument, fragments, oper
|
|
|
687
687
|
definitions: [...newOperations, ...newFragments]
|
|
688
688
|
};
|
|
689
689
|
const stitchingInfo = delegationContext.info?.schema?.extensions?.["stitchingInfo"];
|
|
690
|
-
if (stitchingInfo != null
|
|
691
|
-
stitchingInfo,
|
|
692
|
-
delegationContext.subschema
|
|
693
|
-
)) {
|
|
694
|
-
const {
|
|
695
|
-
selectionSetsByPath: originalFieldSelectionSetsByPath,
|
|
696
|
-
fragments: originalFragmentsByName
|
|
697
|
-
} = collectFieldSelectionSetsByPath(originalDocument);
|
|
690
|
+
if (stitchingInfo != null) {
|
|
698
691
|
const typeInfo = getTypeInfo(targetSchema);
|
|
699
|
-
const pathStack = [];
|
|
700
|
-
const inlineFragmentCounterStack = [];
|
|
701
692
|
newDocument = visit(
|
|
702
693
|
newDocument,
|
|
703
694
|
visitWithTypeInfo(typeInfo, {
|
|
704
|
-
[Kind.
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
}
|
|
711
|
-
},
|
|
712
|
-
[Kind.OPERATION_DEFINITION]: {
|
|
713
|
-
enter(node) {
|
|
714
|
-
pathStack.push(`op:${node.operation}:${node.name?.value ?? ""}`);
|
|
715
|
-
},
|
|
716
|
-
leave() {
|
|
717
|
-
pathStack.pop();
|
|
718
|
-
}
|
|
719
|
-
},
|
|
720
|
-
[Kind.FRAGMENT_DEFINITION]: {
|
|
721
|
-
enter(node) {
|
|
722
|
-
pathStack.push(`frag:${node.name.value}`);
|
|
723
|
-
},
|
|
724
|
-
leave() {
|
|
725
|
-
pathStack.pop();
|
|
726
|
-
}
|
|
727
|
-
},
|
|
728
|
-
[Kind.INLINE_FRAGMENT]: {
|
|
729
|
-
enter(node) {
|
|
730
|
-
pathStack.push(
|
|
731
|
-
nextInlineFragmentPathSegment(
|
|
732
|
-
node,
|
|
733
|
-
inlineFragmentCounterStack[inlineFragmentCounterStack.length - 1]
|
|
734
|
-
)
|
|
735
|
-
);
|
|
736
|
-
},
|
|
737
|
-
leave() {
|
|
738
|
-
pathStack.pop();
|
|
739
|
-
}
|
|
740
|
-
},
|
|
741
|
-
[Kind.FIELD]: {
|
|
742
|
-
enter(fieldNode) {
|
|
743
|
-
pathStack.push(fieldNode.alias?.value ?? fieldNode.name.value);
|
|
744
|
-
},
|
|
745
|
-
leave(fieldNode) {
|
|
746
|
-
try {
|
|
747
|
-
const parentType = typeInfo.getParentType();
|
|
748
|
-
if (!parentType) {
|
|
749
|
-
return void 0;
|
|
750
|
-
}
|
|
751
|
-
const typeConfig = stitchingInfo?.mergedTypes?.[parentType.name];
|
|
695
|
+
[Kind.FIELD](fieldNode) {
|
|
696
|
+
const parentType = typeInfo.getParentType();
|
|
697
|
+
if (parentType) {
|
|
698
|
+
const parentTypeName = parentType.name;
|
|
699
|
+
const typeConfig = stitchingInfo?.mergedTypes?.[parentTypeName];
|
|
700
|
+
if (typeConfig) {
|
|
752
701
|
const providedSelectionsByField = typeConfig?.providedSelectionsByField?.get(
|
|
753
702
|
delegationContext.subschema
|
|
754
703
|
);
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
);
|
|
769
|
-
if (!requestedProvidedSelections.length) {
|
|
770
|
-
return void 0;
|
|
771
|
-
}
|
|
772
|
-
return {
|
|
773
|
-
...fieldNode,
|
|
774
|
-
selectionSet: {
|
|
775
|
-
kind: Kind.SELECTION_SET,
|
|
776
|
-
selections: [
|
|
777
|
-
...requestedProvidedSelections,
|
|
778
|
-
...fieldNode.selectionSet?.selections ?? []
|
|
779
|
-
]
|
|
704
|
+
if (providedSelectionsByField) {
|
|
705
|
+
const providedSelection = providedSelectionsByField[fieldNode.name.value];
|
|
706
|
+
if (providedSelection) {
|
|
707
|
+
return {
|
|
708
|
+
...fieldNode,
|
|
709
|
+
selectionSet: {
|
|
710
|
+
kind: Kind.SELECTION_SET,
|
|
711
|
+
selections: [
|
|
712
|
+
...providedSelection.selections,
|
|
713
|
+
...fieldNode.selectionSet?.selections ?? []
|
|
714
|
+
]
|
|
715
|
+
}
|
|
716
|
+
};
|
|
780
717
|
}
|
|
781
|
-
}
|
|
782
|
-
} finally {
|
|
783
|
-
pathStack.pop();
|
|
718
|
+
}
|
|
784
719
|
}
|
|
785
720
|
}
|
|
721
|
+
return fieldNode;
|
|
786
722
|
}
|
|
787
723
|
})
|
|
788
724
|
);
|
|
@@ -797,7 +733,6 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
|
|
|
797
733
|
const { targetSchema } = delegationContext;
|
|
798
734
|
const { usedVariables, newDocument } = finalizeGatewayDocument(
|
|
799
735
|
targetSchema,
|
|
800
|
-
originalRequest.document,
|
|
801
736
|
fragments,
|
|
802
737
|
operations,
|
|
803
738
|
onOverlappingAliases,
|
|
@@ -826,251 +761,6 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
|
|
|
826
761
|
function isTypeNameField(selection) {
|
|
827
762
|
return selection.kind === Kind.FIELD && !selection.alias && selection.name.value === "__typename";
|
|
828
763
|
}
|
|
829
|
-
function collectFieldSelectionSetsByPathImpl(document) {
|
|
830
|
-
const selectionSetsByPath = /* @__PURE__ */ new Map();
|
|
831
|
-
const fragments = /* @__PURE__ */ new Map();
|
|
832
|
-
for (const def of document.definitions) {
|
|
833
|
-
if (def.kind === Kind.FRAGMENT_DEFINITION) {
|
|
834
|
-
fragments.set(def.name.value, def);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
const pathStack = [];
|
|
838
|
-
const inlineFragmentCounterStack = [];
|
|
839
|
-
visit(document, {
|
|
840
|
-
[Kind.SELECTION_SET]: {
|
|
841
|
-
enter() {
|
|
842
|
-
inlineFragmentCounterStack.push(/* @__PURE__ */ new Map());
|
|
843
|
-
},
|
|
844
|
-
leave() {
|
|
845
|
-
inlineFragmentCounterStack.pop();
|
|
846
|
-
}
|
|
847
|
-
},
|
|
848
|
-
[Kind.OPERATION_DEFINITION]: {
|
|
849
|
-
enter(node) {
|
|
850
|
-
pathStack.push(`op:${node.operation}:${node.name?.value ?? ""}`);
|
|
851
|
-
},
|
|
852
|
-
leave() {
|
|
853
|
-
pathStack.pop();
|
|
854
|
-
}
|
|
855
|
-
},
|
|
856
|
-
[Kind.FRAGMENT_DEFINITION]: {
|
|
857
|
-
enter(node) {
|
|
858
|
-
pathStack.push(`frag:${node.name.value}`);
|
|
859
|
-
},
|
|
860
|
-
leave() {
|
|
861
|
-
pathStack.pop();
|
|
862
|
-
}
|
|
863
|
-
},
|
|
864
|
-
[Kind.INLINE_FRAGMENT]: {
|
|
865
|
-
enter(node) {
|
|
866
|
-
pathStack.push(
|
|
867
|
-
nextInlineFragmentPathSegment(
|
|
868
|
-
node,
|
|
869
|
-
inlineFragmentCounterStack[inlineFragmentCounterStack.length - 1]
|
|
870
|
-
)
|
|
871
|
-
);
|
|
872
|
-
},
|
|
873
|
-
leave() {
|
|
874
|
-
pathStack.pop();
|
|
875
|
-
}
|
|
876
|
-
},
|
|
877
|
-
[Kind.FIELD]: {
|
|
878
|
-
enter(node) {
|
|
879
|
-
pathStack.push(node.alias?.value ?? node.name.value);
|
|
880
|
-
if (node.selectionSet) {
|
|
881
|
-
selectionSetsByPath.set(pathStack.join(">"), node.selectionSet);
|
|
882
|
-
if (node.alias) {
|
|
883
|
-
const namePath = [...pathStack.slice(0, -1), node.name.value].join(
|
|
884
|
-
">"
|
|
885
|
-
);
|
|
886
|
-
if (!selectionSetsByPath.has(namePath)) {
|
|
887
|
-
selectionSetsByPath.set(namePath, node.selectionSet);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
},
|
|
892
|
-
leave() {
|
|
893
|
-
pathStack.pop();
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
});
|
|
897
|
-
return { selectionSetsByPath, fragments };
|
|
898
|
-
}
|
|
899
|
-
const collectFieldSelectionSetsByPath = memoize1(
|
|
900
|
-
collectFieldSelectionSetsByPathImpl
|
|
901
|
-
);
|
|
902
|
-
function nextInlineFragmentPathSegment(node, counter) {
|
|
903
|
-
const typeName = node.typeCondition?.name.value ?? "";
|
|
904
|
-
if (!counter) {
|
|
905
|
-
return `inline:${typeName}:0`;
|
|
906
|
-
}
|
|
907
|
-
const idx = counter.get(typeName) ?? 0;
|
|
908
|
-
counter.set(typeName, idx + 1);
|
|
909
|
-
return `inline:${typeName}:${idx}`;
|
|
910
|
-
}
|
|
911
|
-
function subschemaHasProvidedSelectionsImpl(stitchingInfo, subschema) {
|
|
912
|
-
const mergedTypes = stitchingInfo.mergedTypes;
|
|
913
|
-
if (!mergedTypes) {
|
|
914
|
-
return false;
|
|
915
|
-
}
|
|
916
|
-
for (const typeConfig of Object.values(mergedTypes)) {
|
|
917
|
-
if (typeConfig?.providedSelectionsByField?.has(subschema)) {
|
|
918
|
-
return true;
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
return false;
|
|
922
|
-
}
|
|
923
|
-
const subschemaHasProvidedSelections = memoize2(
|
|
924
|
-
subschemaHasProvidedSelectionsImpl
|
|
925
|
-
);
|
|
926
|
-
function lookupOriginalSelectionSet(pathStack, fieldNode, selectionSetsByPath) {
|
|
927
|
-
const exact = selectionSetsByPath.get(pathStack.join(">"));
|
|
928
|
-
if (exact || !fieldNode.alias) {
|
|
929
|
-
return exact;
|
|
930
|
-
}
|
|
931
|
-
const fallback = [...pathStack.slice(0, -1), fieldNode.name.value].join(">");
|
|
932
|
-
return selectionSetsByPath.get(fallback);
|
|
933
|
-
}
|
|
934
|
-
function intersectProvidedSelections(providedSelections, originalSelectionSet, fragments) {
|
|
935
|
-
if (!originalSelectionSet) {
|
|
936
|
-
return [];
|
|
937
|
-
}
|
|
938
|
-
const providedFieldsByName = /* @__PURE__ */ new Map();
|
|
939
|
-
const otherProvided = [];
|
|
940
|
-
for (const provided of providedSelections) {
|
|
941
|
-
if (provided.kind === Kind.FIELD) {
|
|
942
|
-
providedFieldsByName.set(provided.name.value, provided);
|
|
943
|
-
} else {
|
|
944
|
-
otherProvided.push(provided);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
const result = [];
|
|
948
|
-
collectMatchingOriginalSelections(
|
|
949
|
-
originalSelectionSet,
|
|
950
|
-
providedFieldsByName,
|
|
951
|
-
fragments,
|
|
952
|
-
/* @__PURE__ */ new Set(),
|
|
953
|
-
/* @__PURE__ */ new Set(),
|
|
954
|
-
result
|
|
955
|
-
);
|
|
956
|
-
return [...result, ...otherProvided];
|
|
957
|
-
}
|
|
958
|
-
function collectMatchingOriginalSelections(selectionSet, providedFieldsByName, fragments, seenFieldNames, seenFragmentNames, result) {
|
|
959
|
-
for (const sel of selectionSet.selections) {
|
|
960
|
-
if (sel.kind === Kind.FIELD) {
|
|
961
|
-
const provided = providedFieldsByName.get(sel.name.value);
|
|
962
|
-
if (!provided) {
|
|
963
|
-
continue;
|
|
964
|
-
}
|
|
965
|
-
const dedupKey = sel.alias?.value ?? sel.name.value;
|
|
966
|
-
if (seenFieldNames.has(dedupKey)) {
|
|
967
|
-
continue;
|
|
968
|
-
}
|
|
969
|
-
seenFieldNames.add(dedupKey);
|
|
970
|
-
if (provided.selectionSet && sel.selectionSet) {
|
|
971
|
-
const nested = intersectProvidedSelections(
|
|
972
|
-
provided.selectionSet.selections,
|
|
973
|
-
sel.selectionSet,
|
|
974
|
-
fragments
|
|
975
|
-
);
|
|
976
|
-
if (!nested.length) {
|
|
977
|
-
continue;
|
|
978
|
-
}
|
|
979
|
-
result.push({
|
|
980
|
-
...sel,
|
|
981
|
-
selectionSet: {
|
|
982
|
-
kind: Kind.SELECTION_SET,
|
|
983
|
-
selections: nested
|
|
984
|
-
}
|
|
985
|
-
});
|
|
986
|
-
} else {
|
|
987
|
-
result.push(sel);
|
|
988
|
-
}
|
|
989
|
-
} else if (sel.kind === Kind.INLINE_FRAGMENT && sel.selectionSet) {
|
|
990
|
-
const hasDirectives = (sel.directives?.length ?? 0) > 0;
|
|
991
|
-
const hasTypeCondition = sel.typeCondition != null;
|
|
992
|
-
if (hasDirectives || hasTypeCondition) {
|
|
993
|
-
const inner = [];
|
|
994
|
-
collectMatchingOriginalSelections(
|
|
995
|
-
sel.selectionSet,
|
|
996
|
-
providedFieldsByName,
|
|
997
|
-
fragments,
|
|
998
|
-
/* @__PURE__ */ new Set(),
|
|
999
|
-
/* @__PURE__ */ new Set(),
|
|
1000
|
-
inner
|
|
1001
|
-
);
|
|
1002
|
-
if (inner.length === 0) {
|
|
1003
|
-
continue;
|
|
1004
|
-
}
|
|
1005
|
-
result.push({
|
|
1006
|
-
kind: Kind.INLINE_FRAGMENT,
|
|
1007
|
-
typeCondition: sel.typeCondition,
|
|
1008
|
-
directives: sel.directives,
|
|
1009
|
-
selectionSet: {
|
|
1010
|
-
kind: Kind.SELECTION_SET,
|
|
1011
|
-
selections: inner
|
|
1012
|
-
}
|
|
1013
|
-
});
|
|
1014
|
-
} else {
|
|
1015
|
-
collectMatchingOriginalSelections(
|
|
1016
|
-
sel.selectionSet,
|
|
1017
|
-
providedFieldsByName,
|
|
1018
|
-
fragments,
|
|
1019
|
-
seenFieldNames,
|
|
1020
|
-
seenFragmentNames,
|
|
1021
|
-
result
|
|
1022
|
-
);
|
|
1023
|
-
}
|
|
1024
|
-
} else if (sel.kind === Kind.FRAGMENT_SPREAD) {
|
|
1025
|
-
const fragmentDef = fragments.get(sel.name.value);
|
|
1026
|
-
if (!fragmentDef) {
|
|
1027
|
-
continue;
|
|
1028
|
-
}
|
|
1029
|
-
const hasSpreadDirectives = (sel.directives?.length ?? 0) > 0;
|
|
1030
|
-
const hasFragmentDirectives = (fragmentDef.directives?.length ?? 0) > 0;
|
|
1031
|
-
if (hasSpreadDirectives || hasFragmentDirectives) {
|
|
1032
|
-
const inner = [];
|
|
1033
|
-
collectMatchingOriginalSelections(
|
|
1034
|
-
fragmentDef.selectionSet,
|
|
1035
|
-
providedFieldsByName,
|
|
1036
|
-
fragments,
|
|
1037
|
-
/* @__PURE__ */ new Set(),
|
|
1038
|
-
/* @__PURE__ */ new Set(),
|
|
1039
|
-
inner
|
|
1040
|
-
);
|
|
1041
|
-
if (inner.length === 0) {
|
|
1042
|
-
continue;
|
|
1043
|
-
}
|
|
1044
|
-
const combinedDirectives = [
|
|
1045
|
-
...sel.directives ?? [],
|
|
1046
|
-
...fragmentDef.directives ?? []
|
|
1047
|
-
];
|
|
1048
|
-
result.push({
|
|
1049
|
-
kind: Kind.INLINE_FRAGMENT,
|
|
1050
|
-
typeCondition: fragmentDef.typeCondition,
|
|
1051
|
-
directives: combinedDirectives.length > 0 ? combinedDirectives : void 0,
|
|
1052
|
-
selectionSet: {
|
|
1053
|
-
kind: Kind.SELECTION_SET,
|
|
1054
|
-
selections: inner
|
|
1055
|
-
}
|
|
1056
|
-
});
|
|
1057
|
-
} else {
|
|
1058
|
-
if (seenFragmentNames.has(sel.name.value)) {
|
|
1059
|
-
continue;
|
|
1060
|
-
}
|
|
1061
|
-
seenFragmentNames.add(sel.name.value);
|
|
1062
|
-
collectMatchingOriginalSelections(
|
|
1063
|
-
fragmentDef.selectionSet,
|
|
1064
|
-
providedFieldsByName,
|
|
1065
|
-
fragments,
|
|
1066
|
-
seenFieldNames,
|
|
1067
|
-
seenFragmentNames,
|
|
1068
|
-
result
|
|
1069
|
-
);
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
764
|
function filterTypenameFields(selections) {
|
|
1075
765
|
let hasTypeNameField = false;
|
|
1076
766
|
const filteredSelections = selections.filter((selection) => {
|
|
@@ -3205,6 +2895,7 @@ function extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldSelect
|
|
|
3205
2895
|
});
|
|
3206
2896
|
}
|
|
3207
2897
|
} else if (isObjectType(subFieldType) || isInterfaceType(subFieldType)) {
|
|
2898
|
+
const unavailableSubSelections = [];
|
|
3208
2899
|
const subFieldTypeFields = subFieldType.getFields();
|
|
3209
2900
|
for (const subSelection of selection.selectionSet.selections) {
|
|
3210
2901
|
if (subSelection.kind === Kind.FIELD) {
|
|
@@ -3215,11 +2906,20 @@ function extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldSelect
|
|
|
3215
2906
|
const subSelectionField = subFieldTypeFields[subSelection.name.value];
|
|
3216
2907
|
if (!subSelectionField) {
|
|
3217
2908
|
if (shouldAdd(subFieldType, subSelection)) {
|
|
3218
|
-
|
|
2909
|
+
unavailableSubSelections.push(subSelection);
|
|
3219
2910
|
}
|
|
3220
2911
|
}
|
|
3221
2912
|
}
|
|
3222
2913
|
}
|
|
2914
|
+
if (unavailableSubSelections.length) {
|
|
2915
|
+
unavailableSelections.push({
|
|
2916
|
+
...selection,
|
|
2917
|
+
selectionSet: {
|
|
2918
|
+
kind: Kind.SELECTION_SET,
|
|
2919
|
+
selections: unavailableSubSelections
|
|
2920
|
+
}
|
|
2921
|
+
});
|
|
2922
|
+
}
|
|
3223
2923
|
}
|
|
3224
2924
|
} else if (selection.kind === Kind.FRAGMENT_SPREAD) {
|
|
3225
2925
|
const fragment = fragments[selection.name.value];
|
package/package.json
CHANGED