@wundergraph/composition 0.59.0 → 0.63.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/dist/ast/utils.d.ts +2 -0
- package/dist/ast/utils.js +4 -0
- package/dist/ast/utils.js.map +1 -1
- package/dist/directive-definition-data/directive-definition-data.d.ts +3 -0
- package/dist/directive-definition-data/directive-definition-data.js +82 -1
- package/dist/directive-definition-data/directive-definition-data.js.map +1 -1
- package/dist/errors/errors.d.ts +10 -3
- package/dist/errors/errors.js +82 -10
- package/dist/errors/errors.js.map +1 -1
- package/dist/errors/types/params.d.ts +4 -0
- package/dist/router-configuration/types.d.ts +26 -0
- package/dist/router-configuration/utils.d.ts +2 -1
- package/dist/router-configuration/utils.js +11 -0
- package/dist/router-configuration/utils.js.map +1 -1
- package/dist/schema-building/types/types.d.ts +1 -0
- package/dist/schema-building/utils.js +9 -10
- package/dist/schema-building/utils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/composition-version.js +1 -1
- package/dist/utils/string-constants.d.ts +9 -0
- package/dist/utils/string-constants.js +17 -4
- package/dist/utils/string-constants.js.map +1 -1
- package/dist/v1/constants/constants.js +3 -0
- package/dist/v1/constants/constants.js.map +1 -1
- package/dist/v1/constants/directive-definitions.d.ts +3 -0
- package/dist/v1/constants/directive-definitions.js +60 -1
- package/dist/v1/constants/directive-definitions.js.map +1 -1
- package/dist/v1/constants/type-nodes.d.ts +3 -1
- package/dist/v1/constants/type-nodes.js +6 -1
- package/dist/v1/constants/type-nodes.js.map +1 -1
- package/dist/v1/federation/federation-factory.d.ts +2 -2
- package/dist/v1/federation/federation-factory.js +62 -55
- package/dist/v1/federation/federation-factory.js.map +1 -1
- package/dist/v1/normalization/normalization-factory.d.ts +11 -6
- package/dist/v1/normalization/normalization-factory.js +275 -28
- package/dist/v1/normalization/normalization-factory.js.map +1 -1
- package/dist/v1/normalization/types/params.d.ts +8 -1
- package/dist/v1/normalization/types/results.d.ts +5 -0
- package/dist/v1/normalization/types/types.d.ts +74 -6
- package/dist/v1/normalization/utils.js +9 -1
- package/dist/v1/normalization/utils.js.map +1 -1
- package/dist/v1/utils/utils.d.ts +2 -1
- package/dist/v1/utils/utils.js +7 -0
- package/dist/v1/utils/utils.js.map +1 -1
- package/dist/v1/warnings/params.d.ts +6 -0
- package/dist/v1/warnings/warnings.d.ts +2 -1
- package/dist/v1/warnings/warnings.js +12 -0
- package/dist/v1/warnings/warnings.js.map +1 -1
- package/package.json +2 -2
|
@@ -60,6 +60,11 @@ class NormalizationFactory {
|
|
|
60
60
|
directiveDefinitionDataByName = (0, utils_2.initializeDirectiveDefinitionDatas)();
|
|
61
61
|
doesParentRequireFetchReasons = false;
|
|
62
62
|
edfsDirectiveReferences = new Set();
|
|
63
|
+
/* Cached entity configs keyed by type name, populated by extractEntityCacheDirective() from
|
|
64
|
+
* @openfed__entityCache. Future caching directives (@openfed__queryCache etc.) use this as a lookup
|
|
65
|
+
* to verify a field's return type is a cached entity.
|
|
66
|
+
*/
|
|
67
|
+
entityCacheConfigByTypeName = new Map();
|
|
63
68
|
errors = new Array();
|
|
64
69
|
entityDataByTypeName = new Map();
|
|
65
70
|
entityInterfaceDataByTypeName = new Map();
|
|
@@ -888,13 +893,11 @@ class NormalizationFactory {
|
|
|
888
893
|
this.updateCompositeOutputDataByNode(node, parentData, extensionType);
|
|
889
894
|
if (!directivesByName.has(string_constants_1.INTERFACE_OBJECT)) {
|
|
890
895
|
this.addConcreteTypeNamesForImplementedInterfaces(parentData.implementedInterfaceTypeNames, typeName);
|
|
896
|
+
this.extractEntityCacheDirective(parentData);
|
|
891
897
|
}
|
|
892
898
|
return;
|
|
893
899
|
}
|
|
894
900
|
const implementedInterfaceTypeNames = this.extractImplementedInterfaceTypeNames(node, new Set());
|
|
895
|
-
if (!directivesByName.has(string_constants_1.INTERFACE_OBJECT)) {
|
|
896
|
-
this.addConcreteTypeNamesForImplementedInterfaces(implementedInterfaceTypeNames, typeName);
|
|
897
|
-
}
|
|
898
901
|
const newParentData = {
|
|
899
902
|
configureDescriptionDataBySubgraphName: new Map(),
|
|
900
903
|
directivesByName: directivesByName,
|
|
@@ -913,6 +916,10 @@ class NormalizationFactory {
|
|
|
913
916
|
subgraphNames: new Set([this.subgraphName]),
|
|
914
917
|
description: (0, utils_1.formatDescription)('description' in node ? node.description : undefined),
|
|
915
918
|
};
|
|
919
|
+
if (!directivesByName.has(string_constants_1.INTERFACE_OBJECT)) {
|
|
920
|
+
this.addConcreteTypeNamesForImplementedInterfaces(implementedInterfaceTypeNames, typeName);
|
|
921
|
+
this.extractEntityCacheDirective(newParentData);
|
|
922
|
+
}
|
|
916
923
|
this.extractConfigureDescriptionsData(newParentData);
|
|
917
924
|
this.parentDefinitionDataByTypeName.set(typeName, newParentData);
|
|
918
925
|
}
|
|
@@ -1146,40 +1153,57 @@ class NormalizationFactory {
|
|
|
1146
1153
|
}
|
|
1147
1154
|
}
|
|
1148
1155
|
}
|
|
1149
|
-
getFieldSetParent(isProvides, parentData, fieldName, parentTypeName) {
|
|
1156
|
+
getFieldSetParent({ isProvides, parentData, fieldName, fieldSet, parentTypeName, }) {
|
|
1150
1157
|
if (!isProvides) {
|
|
1151
|
-
return {
|
|
1158
|
+
return {
|
|
1159
|
+
data: parentData,
|
|
1160
|
+
success: true,
|
|
1161
|
+
};
|
|
1152
1162
|
}
|
|
1153
1163
|
const fieldData = (0, utils_5.getOrThrowError)(parentData.fieldDataByName, fieldName, `${parentTypeName}.fieldDataByFieldName`);
|
|
1154
1164
|
const fieldNamedTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
|
|
1155
1165
|
const fieldCoords = `${parentTypeName}.${fieldName}`;
|
|
1156
1166
|
if (constants_1.BASE_SCALARS.has(fieldNamedTypeName)) {
|
|
1157
1167
|
return {
|
|
1158
|
-
|
|
1168
|
+
error: (0, errors_1.incompatibleTypeWithProvidesError)({
|
|
1159
1169
|
fieldCoords,
|
|
1160
1170
|
responseType: fieldNamedTypeName,
|
|
1161
1171
|
subgraphName: this.subgraphName,
|
|
1162
1172
|
}),
|
|
1173
|
+
success: false,
|
|
1163
1174
|
};
|
|
1164
1175
|
}
|
|
1165
1176
|
const namedTypeData = this.parentDefinitionDataByTypeName.get(fieldNamedTypeName);
|
|
1166
1177
|
// This error should never happen
|
|
1167
1178
|
if (!namedTypeData) {
|
|
1168
1179
|
return {
|
|
1169
|
-
|
|
1180
|
+
error: (0, errors_1.unknownNamedTypeError)(fieldCoords, fieldNamedTypeName),
|
|
1181
|
+
success: false,
|
|
1170
1182
|
};
|
|
1171
1183
|
}
|
|
1172
1184
|
// @TODO handle abstract types and fragments
|
|
1173
|
-
if (
|
|
1174
|
-
|
|
1175
|
-
|
|
1185
|
+
if ((0, utils_1.isValidProvidesParentData)(namedTypeData)) {
|
|
1186
|
+
if ((0, utils_3.isUnionDefinitionData)(namedTypeData)) {
|
|
1187
|
+
this.warnings.push((0, warnings_1.providesOnUnionWarning)({
|
|
1176
1188
|
fieldCoords,
|
|
1177
|
-
|
|
1189
|
+
fieldSet,
|
|
1190
|
+
namedTypeName: fieldNamedTypeName,
|
|
1178
1191
|
subgraphName: this.subgraphName,
|
|
1179
|
-
})
|
|
1192
|
+
}));
|
|
1193
|
+
}
|
|
1194
|
+
return {
|
|
1195
|
+
data: namedTypeData,
|
|
1196
|
+
success: true,
|
|
1180
1197
|
};
|
|
1181
1198
|
}
|
|
1182
|
-
return {
|
|
1199
|
+
return {
|
|
1200
|
+
error: (0, errors_1.incompatibleTypeWithProvidesError)({
|
|
1201
|
+
fieldCoords,
|
|
1202
|
+
responseType: fieldNamedTypeName,
|
|
1203
|
+
subgraphName: this.subgraphName,
|
|
1204
|
+
}),
|
|
1205
|
+
success: false,
|
|
1206
|
+
};
|
|
1183
1207
|
}
|
|
1184
1208
|
#handleNonExternalConditionalField({ currentFieldCoords, directiveCoords, directiveName, fieldSet, }) {
|
|
1185
1209
|
if (this.isSubgraphVersionTwo) {
|
|
@@ -1357,10 +1381,6 @@ class NormalizationFactory {
|
|
|
1357
1381
|
shouldDefineSelectionSet = true;
|
|
1358
1382
|
return;
|
|
1359
1383
|
}
|
|
1360
|
-
if (!(0, utils_1.isKindAbstract)(parentData.kind)) {
|
|
1361
|
-
errorMessages.push((0, errors_1.invalidInlineFragmentTypeErrorMessage)(fieldSet, fieldCoordsPath, typeConditionName, parentTypeName));
|
|
1362
|
-
return graphql_1.BREAK;
|
|
1363
|
-
}
|
|
1364
1384
|
const fragmentNamedTypeData = nf.parentDefinitionDataByTypeName.get(typeConditionName);
|
|
1365
1385
|
if (!fragmentNamedTypeData) {
|
|
1366
1386
|
errorMessages.push((0, errors_1.unknownInlineFragmentTypeConditionErrorMessage)(fieldSet, fieldCoordsPath, parentTypeName, typeConditionName));
|
|
@@ -1369,13 +1389,31 @@ class NormalizationFactory {
|
|
|
1369
1389
|
shouldDefineSelectionSet = true;
|
|
1370
1390
|
switch (fragmentNamedTypeData.kind) {
|
|
1371
1391
|
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION: {
|
|
1372
|
-
|
|
1392
|
+
// The enclosing type is an Object
|
|
1393
|
+
if (!(0, utils_1.isKindAbstract)(parentData.kind)) {
|
|
1394
|
+
if (!(0, utils_3.isObjectDefinitionData)(parentData) ||
|
|
1395
|
+
!parentData.implementedInterfaceTypeNames.has(typeConditionName)) {
|
|
1396
|
+
break;
|
|
1397
|
+
}
|
|
1398
|
+
parentDatas.push(fragmentNamedTypeData);
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
// The enclosing type is an Interface or Union
|
|
1402
|
+
const concreteTypeNames = nf.concreteTypeNamesByAbstractTypeName.get(typeConditionName);
|
|
1403
|
+
const parentConcreteTypeNames = nf.concreteTypeNamesByAbstractTypeName.get(parentData.name);
|
|
1404
|
+
if (!concreteTypeNames ||
|
|
1405
|
+
!parentConcreteTypeNames ||
|
|
1406
|
+
parentConcreteTypeNames.isDisjointFrom(concreteTypeNames)) {
|
|
1373
1407
|
break;
|
|
1374
1408
|
}
|
|
1375
1409
|
parentDatas.push(fragmentNamedTypeData);
|
|
1376
1410
|
return;
|
|
1377
1411
|
}
|
|
1378
1412
|
case graphql_1.Kind.OBJECT_TYPE_DEFINITION: {
|
|
1413
|
+
if (!(0, utils_1.isKindAbstract)(parentData.kind)) {
|
|
1414
|
+
errorMessages.push((0, errors_1.invalidInlineFragmentTypeErrorMessage)(fieldSet, fieldCoordsPath, typeConditionName, parentTypeName));
|
|
1415
|
+
return graphql_1.BREAK;
|
|
1416
|
+
}
|
|
1379
1417
|
const concreteTypeNames = nf.concreteTypeNamesByAbstractTypeName.get(parentTypeName);
|
|
1380
1418
|
if (!concreteTypeNames || !concreteTypeNames.has(typeConditionName)) {
|
|
1381
1419
|
break;
|
|
@@ -1384,6 +1422,24 @@ class NormalizationFactory {
|
|
|
1384
1422
|
return;
|
|
1385
1423
|
}
|
|
1386
1424
|
case graphql_1.Kind.UNION_TYPE_DEFINITION: {
|
|
1425
|
+
const concreteTypeNames = nf.concreteTypeNamesByAbstractTypeName.get(typeConditionName);
|
|
1426
|
+
if (!concreteTypeNames) {
|
|
1427
|
+
break;
|
|
1428
|
+
}
|
|
1429
|
+
// The enclosing type is an Object
|
|
1430
|
+
if (!(0, utils_1.isKindAbstract)(parentData.kind)) {
|
|
1431
|
+
// The enclosing Object is a valid part of the Union
|
|
1432
|
+
if (concreteTypeNames.has(parentTypeName)) {
|
|
1433
|
+
parentDatas.push(fragmentNamedTypeData);
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
break;
|
|
1437
|
+
}
|
|
1438
|
+
// The enclosing type is an Interface or Union; fetch its implementations/members respectively.
|
|
1439
|
+
const parentConcreteTypeNames = nf.concreteTypeNamesByAbstractTypeName.get(parentTypeName);
|
|
1440
|
+
if (!parentConcreteTypeNames || parentConcreteTypeNames.isDisjointFrom(concreteTypeNames)) {
|
|
1441
|
+
break;
|
|
1442
|
+
}
|
|
1387
1443
|
parentDatas.push(fragmentNamedTypeData);
|
|
1388
1444
|
return;
|
|
1389
1445
|
}
|
|
@@ -1392,7 +1448,7 @@ class NormalizationFactory {
|
|
|
1392
1448
|
return graphql_1.BREAK;
|
|
1393
1449
|
}
|
|
1394
1450
|
}
|
|
1395
|
-
errorMessages.push((0, errors_1.invalidInlineFragmentTypeConditionErrorMessage)(fieldSet, fieldCoordsPath, typeConditionName, (0, utils_5.kindToNodeType)(parentData.kind), parentTypeName));
|
|
1451
|
+
errorMessages.push((0, errors_1.invalidInlineFragmentTypeConditionErrorMessage)(fieldSet, fieldCoordsPath, typeConditionName, (0, utils_5.kindToNodeType)(parentData.kind), parentTypeName, (0, utils_5.kindToNodeType)(fragmentNamedTypeData.kind)));
|
|
1396
1452
|
return graphql_1.BREAK;
|
|
1397
1453
|
},
|
|
1398
1454
|
},
|
|
@@ -1459,16 +1515,19 @@ class NormalizationFactory {
|
|
|
1459
1515
|
Consequently, at that time, it is unknown whether the named type is an entity.
|
|
1460
1516
|
If it isn't, the @provides directive does not make sense and can be ignored.
|
|
1461
1517
|
*/
|
|
1462
|
-
const
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1518
|
+
const result = this.getFieldSetParent({
|
|
1519
|
+
fieldName,
|
|
1520
|
+
fieldSet,
|
|
1521
|
+
isProvides,
|
|
1522
|
+
parentData,
|
|
1523
|
+
parentTypeName,
|
|
1524
|
+
});
|
|
1525
|
+
if (!result.success) {
|
|
1526
|
+
allErrorMessages.push(result.error.message);
|
|
1469
1527
|
continue;
|
|
1470
1528
|
}
|
|
1471
|
-
const
|
|
1529
|
+
const fieldCoords = `${parentTypeName}.${fieldName}`;
|
|
1530
|
+
const { errorMessages, configuration } = this.validateConditionalFieldSet(result.data, fieldSet, fieldName, isProvides, parentTypeName);
|
|
1472
1531
|
/*
|
|
1473
1532
|
* It is possible to return no error messages nor configuration if the @provides or @requires directive is
|
|
1474
1533
|
* considered completely redundant, i.e.,:
|
|
@@ -2956,6 +3015,179 @@ class NormalizationFactory {
|
|
|
2956
3015
|
}
|
|
2957
3016
|
}
|
|
2958
3017
|
}
|
|
3018
|
+
extractEntityCacheDirective({ directivesByName, name: typeName }) {
|
|
3019
|
+
const entityCacheDirectives = directivesByName.get(string_constants_1.OPENFED_ENTITY_CACHE);
|
|
3020
|
+
if (!entityCacheDirectives || entityCacheDirectives.length === 0) {
|
|
3021
|
+
return;
|
|
3022
|
+
}
|
|
3023
|
+
if (!this.keyFieldSetDatasByTypeName.has(typeName)) {
|
|
3024
|
+
this.errors.push((0, errors_1.invalidDirectiveError)(string_constants_1.OPENFED_ENTITY_CACHE, typeName, string_constants_1.FIRST_ORDINAL, [
|
|
3025
|
+
(0, errors_1.entityCacheWithoutKeyErrorMessage)(typeName),
|
|
3026
|
+
]));
|
|
3027
|
+
return;
|
|
3028
|
+
}
|
|
3029
|
+
/* validateDirectives() (run earlier in normalize()) has already guaranteed each argument's type —
|
|
3030
|
+
* Int for maxAge/negativeCacheTTL, Boolean for the flags — so the generic ConstDirectiveNode is
|
|
3031
|
+
* narrowed once to the precise typed node, mirroring RequestScopedDirectiveNode/ComposeDirectiveNode.
|
|
3032
|
+
* Optional arguments may be absent (definition defaults are not materialized onto the usage AST),
|
|
3033
|
+
* so the config starts at the directive's documented defaults and each present argument overrides it.
|
|
3034
|
+
*/
|
|
3035
|
+
const directive = entityCacheDirectives[0];
|
|
3036
|
+
if (this.entityCacheConfigByTypeName.has(typeName)) {
|
|
3037
|
+
// The error would be caught in directive validation as an invalid repeated directive use.
|
|
3038
|
+
return;
|
|
3039
|
+
}
|
|
3040
|
+
const config = {
|
|
3041
|
+
typeName,
|
|
3042
|
+
maxAgeSeconds: 0,
|
|
3043
|
+
notFoundCacheTtlSeconds: 0,
|
|
3044
|
+
includeHeaders: false,
|
|
3045
|
+
partialCacheLoad: false,
|
|
3046
|
+
shadowMode: false,
|
|
3047
|
+
};
|
|
3048
|
+
for (const arg of directive.arguments) {
|
|
3049
|
+
if (!arg) {
|
|
3050
|
+
continue;
|
|
3051
|
+
}
|
|
3052
|
+
const { name, value } = arg;
|
|
3053
|
+
switch (value.kind) {
|
|
3054
|
+
case graphql_1.Kind.INT: {
|
|
3055
|
+
switch (name.value) {
|
|
3056
|
+
case string_constants_1.MAX_AGE: {
|
|
3057
|
+
config.maxAgeSeconds = parseInt(value.value, 10);
|
|
3058
|
+
break;
|
|
3059
|
+
}
|
|
3060
|
+
case string_constants_1.NEGATIVE_CACHE_TTL: {
|
|
3061
|
+
config.notFoundCacheTtlSeconds = parseInt(value.value, 10);
|
|
3062
|
+
break;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
break;
|
|
3066
|
+
}
|
|
3067
|
+
default: {
|
|
3068
|
+
switch (name.value) {
|
|
3069
|
+
case string_constants_1.INCLUDE_HEADERS: {
|
|
3070
|
+
config.includeHeaders = value.value;
|
|
3071
|
+
break;
|
|
3072
|
+
}
|
|
3073
|
+
case string_constants_1.PARTIAL_CACHE_LOAD: {
|
|
3074
|
+
config.partialCacheLoad = value.value;
|
|
3075
|
+
break;
|
|
3076
|
+
}
|
|
3077
|
+
case string_constants_1.SHADOW_MODE: {
|
|
3078
|
+
config.shadowMode = value.value;
|
|
3079
|
+
break;
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
break;
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
const entityCacheErrors = [];
|
|
3087
|
+
if (config.maxAgeSeconds <= 0) {
|
|
3088
|
+
entityCacheErrors.push((0, errors_1.invalidDirectiveError)(string_constants_1.OPENFED_ENTITY_CACHE, typeName, string_constants_1.FIRST_ORDINAL, [
|
|
3089
|
+
(0, errors_1.maxAgeNotPositiveIntegerErrorMessage)(config.maxAgeSeconds),
|
|
3090
|
+
]));
|
|
3091
|
+
}
|
|
3092
|
+
if (config.notFoundCacheTtlSeconds < 0) {
|
|
3093
|
+
entityCacheErrors.push((0, errors_1.invalidDirectiveError)(string_constants_1.OPENFED_ENTITY_CACHE, typeName, string_constants_1.FIRST_ORDINAL, [
|
|
3094
|
+
(0, errors_1.negativeCacheTTLNotNonNegativeIntegerErrorMessage)(config.notFoundCacheTtlSeconds),
|
|
3095
|
+
]));
|
|
3096
|
+
}
|
|
3097
|
+
if (entityCacheErrors.length > 0) {
|
|
3098
|
+
this.errors.push(...entityCacheErrors);
|
|
3099
|
+
return;
|
|
3100
|
+
}
|
|
3101
|
+
this.entityCacheConfigByTypeName.set(typeName, config);
|
|
3102
|
+
const configurationData = (0, utils_5.getValueOrDefault)(this.configurationDataByTypeName, typeName, () => (0, utils_6.newConfigurationData)(true, typeName));
|
|
3103
|
+
(0, utils_6.getOrInitializeEntityCaching)(configurationData).entityCacheConfigurations.push(config);
|
|
3104
|
+
}
|
|
3105
|
+
// Returns true if the directive is defined and false otherwise
|
|
3106
|
+
extractCacheInvalidateConfig(fieldData) {
|
|
3107
|
+
if (!fieldData.directivesByName.has(string_constants_1.OPENFED_CACHE_INVALIDATE)) {
|
|
3108
|
+
return false;
|
|
3109
|
+
}
|
|
3110
|
+
const operationType = this.getOperationTypeNodeForRootTypeName(fieldData.originalParentTypeName);
|
|
3111
|
+
const fieldCoords = `${fieldData.originalParentTypeName}.${fieldData.name}`;
|
|
3112
|
+
if (!operationType || operationType === graphql_1.OperationTypeNode.QUERY) {
|
|
3113
|
+
this.errors.push((0, errors_1.invalidDirectiveError)(string_constants_1.OPENFED_CACHE_INVALIDATE, fieldCoords, string_constants_1.FIRST_ORDINAL, [
|
|
3114
|
+
(0, errors_1.invalidMutationOrSubscriptionFieldCoordsErrorMessage)(fieldCoords),
|
|
3115
|
+
]));
|
|
3116
|
+
return true;
|
|
3117
|
+
}
|
|
3118
|
+
const returnTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
|
|
3119
|
+
if (!this.entityCacheConfigByTypeName.has(returnTypeName)) {
|
|
3120
|
+
this.errors.push((0, errors_1.invalidDirectiveError)(string_constants_1.OPENFED_CACHE_INVALIDATE, fieldCoords, string_constants_1.FIRST_ORDINAL, [
|
|
3121
|
+
(0, errors_1.invalidEntityReturnTypeErrorMessage)({ fieldCoords, returnTypeName: returnTypeName }),
|
|
3122
|
+
]));
|
|
3123
|
+
return true;
|
|
3124
|
+
}
|
|
3125
|
+
const configurationData = (0, utils_5.getValueOrDefault)(this.configurationDataByTypeName, fieldData.renamedParentTypeName, () => (0, utils_6.newConfigurationData)(false, fieldData.renamedParentTypeName));
|
|
3126
|
+
const config = {
|
|
3127
|
+
entityTypeName: returnTypeName,
|
|
3128
|
+
fieldName: fieldData.name,
|
|
3129
|
+
operationType: operationType,
|
|
3130
|
+
};
|
|
3131
|
+
(0, utils_6.getOrInitializeEntityCaching)(configurationData).cacheInvalidateConfigurations.push(config);
|
|
3132
|
+
return true;
|
|
3133
|
+
}
|
|
3134
|
+
/* Extracts @openfed__cachePopulate from Mutation/Subscription fields.
|
|
3135
|
+
* The return type must be a cached entity (@key & @openfed__entityCache).
|
|
3136
|
+
* Argument `maxAge` is optional; if absent, the router falls back to the entity's @openfed__entityCache TTL;
|
|
3137
|
+
* if provided, must be a positive integer.
|
|
3138
|
+
*
|
|
3139
|
+
* Returns true if the directive is defined and false otherwise
|
|
3140
|
+
*/
|
|
3141
|
+
extractCachePopulateConfig(fieldData) {
|
|
3142
|
+
if (!fieldData.directivesByName.has(string_constants_1.OPENFED_CACHE_POPULATE)) {
|
|
3143
|
+
return false;
|
|
3144
|
+
}
|
|
3145
|
+
const fieldCoords = `${fieldData.originalParentTypeName}.${fieldData.name}`;
|
|
3146
|
+
const operationType = this.getOperationTypeNodeForRootTypeName(fieldData.originalParentTypeName);
|
|
3147
|
+
if (!operationType || operationType === graphql_1.OperationTypeNode.QUERY) {
|
|
3148
|
+
this.errors.push((0, errors_1.invalidDirectiveError)(string_constants_1.OPENFED_CACHE_POPULATE, fieldCoords, string_constants_1.FIRST_ORDINAL, [
|
|
3149
|
+
(0, errors_1.invalidMutationOrSubscriptionFieldCoordsErrorMessage)(fieldCoords),
|
|
3150
|
+
]));
|
|
3151
|
+
return true;
|
|
3152
|
+
}
|
|
3153
|
+
const returnTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
|
|
3154
|
+
const entityCacheConfig = this.entityCacheConfigByTypeName.get(returnTypeName);
|
|
3155
|
+
if (!entityCacheConfig) {
|
|
3156
|
+
this.errors.push((0, errors_1.invalidDirectiveError)(string_constants_1.OPENFED_CACHE_POPULATE, fieldCoords, string_constants_1.FIRST_ORDINAL, [
|
|
3157
|
+
(0, errors_1.invalidEntityReturnTypeErrorMessage)({ fieldCoords, returnTypeName }),
|
|
3158
|
+
]));
|
|
3159
|
+
return true;
|
|
3160
|
+
}
|
|
3161
|
+
/* validateDirectives() has already guaranteed maxAge is an Int when present, so the generic
|
|
3162
|
+
* ConstDirectiveNode is narrowed once to the precise typed node — mirroring the other caching
|
|
3163
|
+
* directives. maxAge is the only argument and is optional.
|
|
3164
|
+
*/
|
|
3165
|
+
const cachePopulateDirective = fieldData.directivesByName.get(string_constants_1.OPENFED_CACHE_POPULATE)[0];
|
|
3166
|
+
const config = {
|
|
3167
|
+
entityTypeName: returnTypeName,
|
|
3168
|
+
fieldName: fieldData.name,
|
|
3169
|
+
operationType,
|
|
3170
|
+
maxAgeSeconds: entityCacheConfig.maxAgeSeconds, // Set fallback immediately; overridden where appropriate.
|
|
3171
|
+
};
|
|
3172
|
+
const maxAgeArgument = cachePopulateDirective.arguments[0];
|
|
3173
|
+
if (!maxAgeArgument) {
|
|
3174
|
+
const configurationData = (0, utils_5.getValueOrDefault)(this.configurationDataByTypeName, fieldData.renamedParentTypeName, () => (0, utils_6.newConfigurationData)(false, fieldData.renamedParentTypeName));
|
|
3175
|
+
(0, utils_6.getOrInitializeEntityCaching)(configurationData).cachePopulateConfigurations.push(config);
|
|
3176
|
+
return true;
|
|
3177
|
+
}
|
|
3178
|
+
config.maxAgeSeconds = parseInt(maxAgeArgument.value.value, 10);
|
|
3179
|
+
// This syntax handles possible NaN.
|
|
3180
|
+
if (isNaN(config.maxAgeSeconds) || config.maxAgeSeconds <= 0) {
|
|
3181
|
+
this.errors.push((0, errors_1.invalidDirectiveError)(string_constants_1.OPENFED_CACHE_POPULATE, fieldCoords, string_constants_1.FIRST_ORDINAL, [
|
|
3182
|
+
// If null is explicitly provided in GraphQL the value in JS is undefined.
|
|
3183
|
+
(0, errors_1.maxAgeNotPositiveIntegerErrorMessage)(maxAgeArgument.value.value ?? null),
|
|
3184
|
+
]));
|
|
3185
|
+
return true;
|
|
3186
|
+
}
|
|
3187
|
+
const configurationData = (0, utils_5.getValueOrDefault)(this.configurationDataByTypeName, fieldData.renamedParentTypeName, () => (0, utils_6.newConfigurationData)(false, fieldData.renamedParentTypeName));
|
|
3188
|
+
(0, utils_6.getOrInitializeEntityCaching)(configurationData).cachePopulateConfigurations.push(config);
|
|
3189
|
+
return true;
|
|
3190
|
+
}
|
|
2959
3191
|
addFieldNamesToConfigurationData(fieldDataByFieldName, configurationData) {
|
|
2960
3192
|
const externalFieldNames = new Set();
|
|
2961
3193
|
for (const [fieldName, fieldData] of fieldDataByFieldName) {
|
|
@@ -3013,6 +3245,18 @@ class NormalizationFactory {
|
|
|
3013
3245
|
}
|
|
3014
3246
|
definitions.push(...dependencies);
|
|
3015
3247
|
}
|
|
3248
|
+
// Only Object fields should be passed to this method
|
|
3249
|
+
handleFieldCacheDirectives(data) {
|
|
3250
|
+
/* A field can't both evict (@openfed__cacheInvalidate) and write (@openfed__cachePopulate)
|
|
3251
|
+
* the cache for the same entity, so the two directives are mutually exclusive.
|
|
3252
|
+
*/
|
|
3253
|
+
const definesInvalidate = this.extractCacheInvalidateConfig(data);
|
|
3254
|
+
const definesPopulate = this.extractCachePopulateConfig(data);
|
|
3255
|
+
if (definesInvalidate && definesPopulate) {
|
|
3256
|
+
const fieldCoords = `${data.originalParentTypeName}.${data.name}`;
|
|
3257
|
+
this.errors.push((0, errors_1.invalidMutuallyExclusiveCacheDirectivesError)(fieldCoords));
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3016
3260
|
normalize(document) {
|
|
3017
3261
|
// Collect any renamed root types
|
|
3018
3262
|
(0, walkers_1.upsertDirectiveSchemaAndEntityDefinitions)(this, document);
|
|
@@ -3119,7 +3363,10 @@ class NormalizationFactory {
|
|
|
3119
3363
|
}
|
|
3120
3364
|
const externalInterfaceFieldNames = [];
|
|
3121
3365
|
for (const [fieldName, fieldData] of parentData.fieldDataByName) {
|
|
3122
|
-
if (
|
|
3366
|
+
if (isObject) {
|
|
3367
|
+
this.handleFieldCacheDirectives(fieldData);
|
|
3368
|
+
}
|
|
3369
|
+
else if (fieldData.externalFieldDataBySubgraphName.get(this.subgraphName)?.isDefinedExternal) {
|
|
3123
3370
|
externalInterfaceFieldNames.push(fieldName);
|
|
3124
3371
|
}
|
|
3125
3372
|
// Arguments can only be fully validated once all parents types are known
|