@graphql-tools/delegate 12.0.16 → 12.0.17-alpha-e7a313b236c7e6405179af89eea446448ee97587
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 +26 -0
- package/dist/index.cjs +333 -23
- package/dist/index.js +333 -23
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @graphql-tools/delegate
|
|
2
2
|
|
|
3
|
+
## 12.0.17-alpha-e7a313b236c7e6405179af89eea446448ee97587
|
|
4
|
+
### Patch Changes
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
- [#2351](https://github.com/graphql-hive/gateway/pull/2351) [`96a4674`](https://github.com/graphql-hive/gateway/commit/96a4674a514e93d69c299c07916e06f49feed477) Thanks [@ardatan](https://github.com/ardatan)! - Fix `@provides` so the gateway only requests the provided fields the client actually selected.
|
|
9
|
+
|
|
10
|
+
Previously, when a subgraph declared `@provides(fields: "...")` on a query field, the gateway would forward **every** field listed in the `@provides` argument to that subgraph, even when the client never asked for those fields. For example with:
|
|
11
|
+
|
|
12
|
+
```graphql
|
|
13
|
+
# subgraph B
|
|
14
|
+
type Query {
|
|
15
|
+
entity: Entity @provides(fields: "name description")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type Entity @key(fields: "id") {
|
|
19
|
+
id: ID!
|
|
20
|
+
name: String! @external
|
|
21
|
+
description: String! @external
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
a client query of `{ entity { id name } }` would still cause the gateway to ask subgraph B for `description`. After this fix the gateway only forwards `name` (because that is what the client asked for and what `@provides` allows the subgraph to resolve directly), bringing the behavior in line with Federation's `@provides` semantics where it acts as a hint that the providing subgraph **can** resolve those fields locally — not a directive to always fetch them.
|
|
26
|
+
|
|
27
|
+
Aliases, fragments, fragment spreads, and nested `@provides` selections are preserved.
|
|
28
|
+
|
|
3
29
|
## 12.0.16
|
|
4
30
|
### Patch Changes
|
|
5
31
|
|
package/dist/index.cjs
CHANGED
|
@@ -590,7 +590,7 @@ const handleOverrideByDelegation = utils.memoize3(
|
|
|
590
590
|
}
|
|
591
591
|
);
|
|
592
592
|
|
|
593
|
-
function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlappingAliases, delegationContext) {
|
|
593
|
+
function finalizeGatewayDocument(targetSchema, originalDocument, fragments, operations, onOverlappingAliases, delegationContext) {
|
|
594
594
|
let usedVariables = [];
|
|
595
595
|
let usedFragments = [];
|
|
596
596
|
const newOperations = [];
|
|
@@ -687,38 +687,102 @@ function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlapp
|
|
|
687
687
|
definitions: [...newOperations, ...newFragments]
|
|
688
688
|
};
|
|
689
689
|
const stitchingInfo = delegationContext.info?.schema?.extensions?.["stitchingInfo"];
|
|
690
|
-
if (stitchingInfo != null
|
|
690
|
+
if (stitchingInfo != null && subschemaHasProvidedSelections(
|
|
691
|
+
stitchingInfo,
|
|
692
|
+
delegationContext.subschema
|
|
693
|
+
)) {
|
|
694
|
+
const {
|
|
695
|
+
selectionSetsByPath: originalFieldSelectionSetsByPath,
|
|
696
|
+
fragments: originalFragmentsByName
|
|
697
|
+
} = collectFieldSelectionSetsByPath(originalDocument);
|
|
691
698
|
const typeInfo = getTypeInfo(targetSchema);
|
|
699
|
+
const pathStack = [];
|
|
700
|
+
const inlineFragmentCounterStack = [];
|
|
692
701
|
newDocument = graphql.visit(
|
|
693
702
|
newDocument,
|
|
694
703
|
graphql.visitWithTypeInfo(typeInfo, {
|
|
695
|
-
[graphql.Kind.
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
704
|
+
[graphql.Kind.SELECTION_SET]: {
|
|
705
|
+
enter() {
|
|
706
|
+
inlineFragmentCounterStack.push(/* @__PURE__ */ new Map());
|
|
707
|
+
},
|
|
708
|
+
leave() {
|
|
709
|
+
inlineFragmentCounterStack.pop();
|
|
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];
|
|
701
752
|
const providedSelectionsByField = typeConfig?.providedSelectionsByField?.get(
|
|
702
753
|
delegationContext.subschema
|
|
703
754
|
);
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
return {
|
|
708
|
-
...fieldNode,
|
|
709
|
-
selectionSet: {
|
|
710
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
711
|
-
selections: [
|
|
712
|
-
...providedSelection.selections,
|
|
713
|
-
...fieldNode.selectionSet?.selections ?? []
|
|
714
|
-
]
|
|
715
|
-
}
|
|
716
|
-
};
|
|
717
|
-
}
|
|
755
|
+
const providedSelection = providedSelectionsByField?.[fieldNode.name.value];
|
|
756
|
+
if (!providedSelection) {
|
|
757
|
+
return void 0;
|
|
718
758
|
}
|
|
759
|
+
const originalSelectionSet = lookupOriginalSelectionSet(
|
|
760
|
+
pathStack,
|
|
761
|
+
fieldNode,
|
|
762
|
+
originalFieldSelectionSetsByPath
|
|
763
|
+
);
|
|
764
|
+
const requestedProvidedSelections = intersectProvidedSelections(
|
|
765
|
+
providedSelection.selections,
|
|
766
|
+
originalSelectionSet,
|
|
767
|
+
originalFragmentsByName
|
|
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
|
+
]
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
} finally {
|
|
783
|
+
pathStack.pop();
|
|
719
784
|
}
|
|
720
785
|
}
|
|
721
|
-
return fieldNode;
|
|
722
786
|
}
|
|
723
787
|
})
|
|
724
788
|
);
|
|
@@ -733,6 +797,7 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
|
|
|
733
797
|
const { targetSchema } = delegationContext;
|
|
734
798
|
const { usedVariables, newDocument } = finalizeGatewayDocument(
|
|
735
799
|
targetSchema,
|
|
800
|
+
originalRequest.document,
|
|
736
801
|
fragments,
|
|
737
802
|
operations,
|
|
738
803
|
onOverlappingAliases,
|
|
@@ -761,6 +826,251 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
|
|
|
761
826
|
function isTypeNameField(selection) {
|
|
762
827
|
return selection.kind === graphql.Kind.FIELD && !selection.alias && selection.name.value === "__typename";
|
|
763
828
|
}
|
|
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
|
+
}
|
|
764
1074
|
function filterTypenameFields(selections) {
|
|
765
1075
|
let hasTypeNameField = false;
|
|
766
1076
|
const filteredSelections = selections.filter((selection) => {
|
package/dist/index.js
CHANGED
|
@@ -590,7 +590,7 @@ const handleOverrideByDelegation = memoize3(
|
|
|
590
590
|
}
|
|
591
591
|
);
|
|
592
592
|
|
|
593
|
-
function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlappingAliases, delegationContext) {
|
|
593
|
+
function finalizeGatewayDocument(targetSchema, originalDocument, fragments, operations, onOverlappingAliases, delegationContext) {
|
|
594
594
|
let usedVariables = [];
|
|
595
595
|
let usedFragments = [];
|
|
596
596
|
const newOperations = [];
|
|
@@ -687,38 +687,102 @@ function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlapp
|
|
|
687
687
|
definitions: [...newOperations, ...newFragments]
|
|
688
688
|
};
|
|
689
689
|
const stitchingInfo = delegationContext.info?.schema?.extensions?.["stitchingInfo"];
|
|
690
|
-
if (stitchingInfo != null
|
|
690
|
+
if (stitchingInfo != null && subschemaHasProvidedSelections(
|
|
691
|
+
stitchingInfo,
|
|
692
|
+
delegationContext.subschema
|
|
693
|
+
)) {
|
|
694
|
+
const {
|
|
695
|
+
selectionSetsByPath: originalFieldSelectionSetsByPath,
|
|
696
|
+
fragments: originalFragmentsByName
|
|
697
|
+
} = collectFieldSelectionSetsByPath(originalDocument);
|
|
691
698
|
const typeInfo = getTypeInfo(targetSchema);
|
|
699
|
+
const pathStack = [];
|
|
700
|
+
const inlineFragmentCounterStack = [];
|
|
692
701
|
newDocument = visit(
|
|
693
702
|
newDocument,
|
|
694
703
|
visitWithTypeInfo(typeInfo, {
|
|
695
|
-
[Kind.
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
704
|
+
[Kind.SELECTION_SET]: {
|
|
705
|
+
enter() {
|
|
706
|
+
inlineFragmentCounterStack.push(/* @__PURE__ */ new Map());
|
|
707
|
+
},
|
|
708
|
+
leave() {
|
|
709
|
+
inlineFragmentCounterStack.pop();
|
|
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];
|
|
701
752
|
const providedSelectionsByField = typeConfig?.providedSelectionsByField?.get(
|
|
702
753
|
delegationContext.subschema
|
|
703
754
|
);
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
return {
|
|
708
|
-
...fieldNode,
|
|
709
|
-
selectionSet: {
|
|
710
|
-
kind: Kind.SELECTION_SET,
|
|
711
|
-
selections: [
|
|
712
|
-
...providedSelection.selections,
|
|
713
|
-
...fieldNode.selectionSet?.selections ?? []
|
|
714
|
-
]
|
|
715
|
-
}
|
|
716
|
-
};
|
|
717
|
-
}
|
|
755
|
+
const providedSelection = providedSelectionsByField?.[fieldNode.name.value];
|
|
756
|
+
if (!providedSelection) {
|
|
757
|
+
return void 0;
|
|
718
758
|
}
|
|
759
|
+
const originalSelectionSet = lookupOriginalSelectionSet(
|
|
760
|
+
pathStack,
|
|
761
|
+
fieldNode,
|
|
762
|
+
originalFieldSelectionSetsByPath
|
|
763
|
+
);
|
|
764
|
+
const requestedProvidedSelections = intersectProvidedSelections(
|
|
765
|
+
providedSelection.selections,
|
|
766
|
+
originalSelectionSet,
|
|
767
|
+
originalFragmentsByName
|
|
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
|
+
]
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
} finally {
|
|
783
|
+
pathStack.pop();
|
|
719
784
|
}
|
|
720
785
|
}
|
|
721
|
-
return fieldNode;
|
|
722
786
|
}
|
|
723
787
|
})
|
|
724
788
|
);
|
|
@@ -733,6 +797,7 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
|
|
|
733
797
|
const { targetSchema } = delegationContext;
|
|
734
798
|
const { usedVariables, newDocument } = finalizeGatewayDocument(
|
|
735
799
|
targetSchema,
|
|
800
|
+
originalRequest.document,
|
|
736
801
|
fragments,
|
|
737
802
|
operations,
|
|
738
803
|
onOverlappingAliases,
|
|
@@ -761,6 +826,251 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
|
|
|
761
826
|
function isTypeNameField(selection) {
|
|
762
827
|
return selection.kind === Kind.FIELD && !selection.alias && selection.name.value === "__typename";
|
|
763
828
|
}
|
|
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
|
+
}
|
|
764
1074
|
function filterTypenameFields(selections) {
|
|
765
1075
|
let hasTypeNameField = false;
|
|
766
1076
|
const filteredSelections = selections.filter((selection) => {
|
package/package.json
CHANGED