@apollo/federation-internals 2.12.0-preview.3 → 2.12.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/argumentCompositionStrategies.d.ts +6 -0
- package/dist/argumentCompositionStrategies.d.ts.map +1 -1
- package/dist/argumentCompositionStrategies.js +77 -0
- package/dist/argumentCompositionStrategies.js.map +1 -1
- package/dist/buildSchema.d.ts.map +1 -1
- package/dist/buildSchema.js +42 -2
- package/dist/buildSchema.js.map +1 -1
- package/dist/directiveAndTypeSpecification.d.ts +8 -3
- package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
- package/dist/directiveAndTypeSpecification.js +2 -1
- package/dist/directiveAndTypeSpecification.js.map +1 -1
- package/dist/error.d.ts +2 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +4 -0
- package/dist/error.js.map +1 -1
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +37 -8
- package/dist/federation.js.map +1 -1
- package/dist/specs/authenticatedSpec.d.ts +2 -0
- package/dist/specs/authenticatedSpec.d.ts.map +1 -1
- package/dist/specs/authenticatedSpec.js +3 -0
- package/dist/specs/authenticatedSpec.js.map +1 -1
- package/dist/specs/connectSpec.d.ts +0 -3
- package/dist/specs/connectSpec.d.ts.map +1 -1
- package/dist/specs/connectSpec.js +240 -64
- package/dist/specs/connectSpec.js.map +1 -1
- package/dist/specs/policySpec.d.ts +4 -0
- package/dist/specs/policySpec.d.ts.map +1 -1
- package/dist/specs/policySpec.js +4 -1
- package/dist/specs/policySpec.js.map +1 -1
- package/dist/specs/requiresScopesSpec.d.ts +4 -0
- package/dist/specs/requiresScopesSpec.d.ts.map +1 -1
- package/dist/specs/requiresScopesSpec.js +4 -1
- package/dist/specs/requiresScopesSpec.js.map +1 -1
- package/package.json +1 -1
- package/src/argumentCompositionStrategies.ts +114 -2
- package/src/buildSchema.ts +51 -0
- package/src/directiveAndTypeSpecification.ts +8 -2
- package/src/error.ts +14 -0
- package/src/federation.ts +45 -8
- package/src/specs/authenticatedSpec.ts +5 -0
- package/src/specs/connectSpec.ts +380 -126
- package/src/specs/policySpec.ts +6 -2
- package/src/specs/requiresScopesSpec.ts +6 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {InputType, NonNullType, Schema, isListType, isNonNullType} from "./definitions"
|
|
2
2
|
import { sameType } from "./types";
|
|
3
3
|
import { valueEquals } from "./values";
|
|
4
4
|
|
|
@@ -19,6 +19,14 @@ function supportFixedTypes(types: (schema: Schema) => InputType[]): TypeSupportV
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function supportAnyNonNullNestedArray(): TypeSupportValidator {
|
|
23
|
+
return (_, type) =>
|
|
24
|
+
isNonNullType(type) && isListType(type.ofType)
|
|
25
|
+
&& isNonNullType(type.ofType.ofType) && isListType(type.ofType.ofType.ofType)
|
|
26
|
+
? { valid: true }
|
|
27
|
+
: { valid: false, supportedMsg: 'non nullable nested list types of any type' }
|
|
28
|
+
}
|
|
29
|
+
|
|
22
30
|
function supportAnyNonNullArray(): TypeSupportValidator {
|
|
23
31
|
return (_, type) => isNonNullType(type) && isListType(type.ofType)
|
|
24
32
|
? { valid: true }
|
|
@@ -54,6 +62,104 @@ function unionValues(values: any[]): any {
|
|
|
54
62
|
}, []);
|
|
55
63
|
}
|
|
56
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Performs conjunction of 2d arrays that represent conditions in Disjunctive Normal Form.
|
|
67
|
+
*
|
|
68
|
+
* * Each inner array is interpreted as the conjunction of the conditions in the array.
|
|
69
|
+
* * The top-level array is interpreted as the disjunction of the inner arrays
|
|
70
|
+
*
|
|
71
|
+
* Algorithm
|
|
72
|
+
* * filter out duplicate entries to limit the amount of necessary computations
|
|
73
|
+
* * calculate cartesian product of the arrays to find all possible combinations
|
|
74
|
+
* * simplify combinations by dropping duplicate conditions (i.e. p ^ p = p, p ^ q = q ^ p)
|
|
75
|
+
* * eliminate entries that are subsumed by others (i.e. (p ^ q) subsumes (p ^ q ^ r))
|
|
76
|
+
*/
|
|
77
|
+
function dnfConjunction<T>(values: T[][][]): T[][] {
|
|
78
|
+
// should never be the case
|
|
79
|
+
if (values.length == 0) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// we first filter out duplicate values from candidates
|
|
84
|
+
// this avoids exponential computation of exactly the same conditions
|
|
85
|
+
const filtered = filterNestedArrayDuplicates(values);
|
|
86
|
+
|
|
87
|
+
// initialize with first entry
|
|
88
|
+
let result: T[][] = filtered[0];
|
|
89
|
+
// perform cartesian product to find all possible entries
|
|
90
|
+
for (let i = 1; i < filtered.length; i++) {
|
|
91
|
+
const current = filtered[i];
|
|
92
|
+
const accumulator: T[][] = [];
|
|
93
|
+
const seen = new Set<string>;
|
|
94
|
+
|
|
95
|
+
for (const accElement of result) {
|
|
96
|
+
for (const currentElement of current) {
|
|
97
|
+
// filter out elements that are already present in accElement
|
|
98
|
+
const filteredElement = currentElement.filter((e) => !accElement.includes(e));
|
|
99
|
+
const candidate = [...accElement, ...filteredElement].sort();
|
|
100
|
+
const key = JSON.stringify(candidate);
|
|
101
|
+
// only add entries which has not been seen yet
|
|
102
|
+
if (!seen.has(key)) {
|
|
103
|
+
seen.add(key);
|
|
104
|
+
accumulator.push(candidate);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Now we need to deduplicate the results. Given that
|
|
109
|
+
// - outer array implies OR requirements
|
|
110
|
+
// - inner array implies AND requirements
|
|
111
|
+
// We can filter out any inner arrays that fully contain other inner arrays, i.e.
|
|
112
|
+
// A OR B OR (A AND B) OR (A AND B AND C) => A OR B
|
|
113
|
+
result = deduplicateSubsumedValues(accumulator);
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function filterNestedArrayDuplicates<T>(values: T[][][]): T[][][] {
|
|
119
|
+
const filtered: T[][][] = [];
|
|
120
|
+
const seen = new Set<string>;
|
|
121
|
+
values.forEach((value) => {
|
|
122
|
+
value.sort();
|
|
123
|
+
const key = JSON.stringify(value);
|
|
124
|
+
if (!seen.has(key)) {
|
|
125
|
+
seen.add(key);
|
|
126
|
+
filtered.push(value);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return filtered;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function deduplicateSubsumedValues<T>(values: T[][]): T[][] {
|
|
133
|
+
const result: T[][] = [];
|
|
134
|
+
// we first sort by length as the longer ones might be dropped
|
|
135
|
+
values.sort((first, second) => {
|
|
136
|
+
if (first.length < second.length) {
|
|
137
|
+
return -1;
|
|
138
|
+
} else if (first.length > second.length) {
|
|
139
|
+
return 1;
|
|
140
|
+
} else {
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
for (const candidate of values) {
|
|
146
|
+
const entry = new Set(candidate);
|
|
147
|
+
let redundant = false;
|
|
148
|
+
for (const r of result) {
|
|
149
|
+
if (r.every(e => entry.has(e))) {
|
|
150
|
+
// if `r` is a subset of a `candidate` then it means `candidate` is redundant
|
|
151
|
+
redundant = true;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!redundant) {
|
|
157
|
+
result.push(candidate);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
|
|
57
163
|
export const ARGUMENT_COMPOSITION_STRATEGIES = {
|
|
58
164
|
MAX: {
|
|
59
165
|
name: 'MAX',
|
|
@@ -95,7 +201,8 @@ export const ARGUMENT_COMPOSITION_STRATEGIES = {
|
|
|
95
201
|
schema.booleanType(),
|
|
96
202
|
new NonNullType(schema.booleanType())
|
|
97
203
|
]),
|
|
98
|
-
mergeValues:
|
|
204
|
+
mergeValues:
|
|
205
|
+
mergeNullableValues(
|
|
99
206
|
(values: boolean[]) => values.every((v) => v)
|
|
100
207
|
),
|
|
101
208
|
},
|
|
@@ -113,5 +220,10 @@ export const ARGUMENT_COMPOSITION_STRATEGIES = {
|
|
|
113
220
|
name: 'NULLABLE_UNION',
|
|
114
221
|
isTypeSupported: supportAnyArray(),
|
|
115
222
|
mergeValues: mergeNullableValues(unionValues),
|
|
223
|
+
},
|
|
224
|
+
DNF_CONJUNCTION: {
|
|
225
|
+
name: 'DNF_CONJUNCTION',
|
|
226
|
+
isTypeSupported: supportAnyNonNullNestedArray(),
|
|
227
|
+
mergeValues: dnfConjunction
|
|
116
228
|
}
|
|
117
229
|
}
|
package/src/buildSchema.ts
CHANGED
|
@@ -56,6 +56,9 @@ import {
|
|
|
56
56
|
} from "./definitions";
|
|
57
57
|
import { ERRORS, errorCauses, withModifiedErrorNodes } from "./error";
|
|
58
58
|
import { introspectionTypeNames } from "./introspection";
|
|
59
|
+
import { coreFeatureDefinitionIfKnown } from "./knownCoreFeatures";
|
|
60
|
+
import { connectIdentity } from "./specs/connectSpec";
|
|
61
|
+
|
|
59
62
|
|
|
60
63
|
function buildValue(value?: ValueNode): any {
|
|
61
64
|
return value ? valueFromASTUntyped(value) : undefined;
|
|
@@ -143,6 +146,48 @@ export function buildSchemaFromAST(
|
|
|
143
146
|
buildSchemaDefinitionInner(schemaExtension, schema.schemaDefinition, errors, schema.schemaDefinition.newExtension());
|
|
144
147
|
}
|
|
145
148
|
|
|
149
|
+
// The following block of code is a one-off to support input objects in the
|
|
150
|
+
// connect spec. It will be non-maintainable/bug-prone to do this again, and
|
|
151
|
+
// has various limitations/unsupported edge cases already.
|
|
152
|
+
//
|
|
153
|
+
// There's work to be done to support input objects more generally; please see
|
|
154
|
+
// https://github.com/apollographql/federation/pull/3311 for more information.
|
|
155
|
+
const connectFeature = schema.coreFeatures?.getByIdentity(connectIdentity);
|
|
156
|
+
const handledConnectTypeNames = new Set<string>();
|
|
157
|
+
if (connectFeature) {
|
|
158
|
+
const connectFeatureDefinition =
|
|
159
|
+
coreFeatureDefinitionIfKnown(connectFeature.url);
|
|
160
|
+
if (connectFeatureDefinition) {
|
|
161
|
+
const connectTypeNamesInSchema = new Set(
|
|
162
|
+
connectFeatureDefinition.typeSpecs()
|
|
163
|
+
.map(({ name }) => connectFeature.typeNameInSchema(name))
|
|
164
|
+
);
|
|
165
|
+
for (const typeNode of typeDefinitions) {
|
|
166
|
+
if (connectTypeNamesInSchema.has(typeNode.name.value)
|
|
167
|
+
&& typeNode.kind === 'InputObjectTypeDefinition'
|
|
168
|
+
) {
|
|
169
|
+
handledConnectTypeNames.add(typeNode.name.value)
|
|
170
|
+
} else {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
buildNamedTypeInner(typeNode, schema.type(typeNode.name.value)!, schema.blueprint, errors);
|
|
174
|
+
}
|
|
175
|
+
for (const typeExtensionNode of typeExtensions) {
|
|
176
|
+
if (connectTypeNamesInSchema.has(typeExtensionNode.name.value)
|
|
177
|
+
&& typeExtensionNode.kind === 'InputObjectTypeExtension'
|
|
178
|
+
) {
|
|
179
|
+
handledConnectTypeNames.add(typeExtensionNode.name.value)
|
|
180
|
+
} else {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const toExtend = schema.type(typeExtensionNode.name.value)!;
|
|
184
|
+
const extension = toExtend.newExtension();
|
|
185
|
+
extension.sourceAST = typeExtensionNode;
|
|
186
|
+
buildNamedTypeInner(typeExtensionNode, toExtend, schema.blueprint, errors, extension);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
146
191
|
// The following is a no-op for "standard" schema, but for federation subgraphs, this is where we handle the auto-addition
|
|
147
192
|
// of imported federation directive definitions. That is why we have avoid looking at directive applications within
|
|
148
193
|
// directive definition earlier: if one of those application was of an imported federation directive, the definition
|
|
@@ -155,9 +200,15 @@ export function buildSchemaFromAST(
|
|
|
155
200
|
}
|
|
156
201
|
|
|
157
202
|
for (const typeNode of typeDefinitions) {
|
|
203
|
+
if (handledConnectTypeNames.has(typeNode.name.value)) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
158
206
|
buildNamedTypeInner(typeNode, schema.type(typeNode.name.value)!, schema.blueprint, errors);
|
|
159
207
|
}
|
|
160
208
|
for (const typeExtensionNode of typeExtensions) {
|
|
209
|
+
if (handledConnectTypeNames.has(typeExtensionNode.name.value)) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
161
212
|
const toExtend = schema.type(typeExtensionNode.name.value)!;
|
|
162
213
|
const extension = toExtend.newExtension();
|
|
163
214
|
extension.sourceAST = typeExtensionNode;
|
|
@@ -67,7 +67,13 @@ export type FieldSpecification = {
|
|
|
67
67
|
args?: ResolvedArgumentSpecification[],
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
type ResolvedArgumentSpecification = {
|
|
70
|
+
export type ResolvedArgumentSpecification = {
|
|
71
|
+
name: string,
|
|
72
|
+
type: InputType,
|
|
73
|
+
defaultValue?: any,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type InputFieldSpecification = {
|
|
71
77
|
name: string,
|
|
72
78
|
type: InputType,
|
|
73
79
|
defaultValue?: any,
|
|
@@ -342,7 +348,7 @@ export function createEnumTypeSpecification({
|
|
|
342
348
|
}
|
|
343
349
|
}
|
|
344
350
|
|
|
345
|
-
function ensureSameTypeKind(expected: NamedType['kind'], actual: NamedType): GraphQLError[] {
|
|
351
|
+
export function ensureSameTypeKind(expected: NamedType['kind'], actual: NamedType): GraphQLError[] {
|
|
346
352
|
return expected === actual.kind
|
|
347
353
|
? []
|
|
348
354
|
: [
|
package/src/error.ts
CHANGED
|
@@ -633,6 +633,18 @@ const MAX_VALIDATION_SUBGRAPH_PATHS_EXCEEDED = makeCodeDefinition(
|
|
|
633
633
|
{ addedIn: '2.8.0' },
|
|
634
634
|
);
|
|
635
635
|
|
|
636
|
+
const AUTHENTICATION_APPLIED_ON_INTERFACE = makeCodeDefinition(
|
|
637
|
+
'AUTHENTICATION_APPLIED_ON_INTERFACE',
|
|
638
|
+
'The @authenticated, @requiresScopes and @policy directive cannot be applied on interface, interface object or their fields.',
|
|
639
|
+
{ addedIn: '2.9.4' },
|
|
640
|
+
);
|
|
641
|
+
|
|
642
|
+
const MISSING_TRANSITIVE_AUTH_REQUIREMENTS = makeCodeDefinition(
|
|
643
|
+
'MISSING_TRANSITIVE_AUTH_REQUIREMENTS',
|
|
644
|
+
'Field missing transitive @authenticated, @requiresScopes and/or @policy auth requirements needed to access dependent data.',
|
|
645
|
+
{ addedIn: '2.9.4' },
|
|
646
|
+
)
|
|
647
|
+
|
|
636
648
|
export const ERROR_CATEGORIES = {
|
|
637
649
|
DIRECTIVE_FIELDS_MISSING_EXTERNAL,
|
|
638
650
|
DIRECTIVE_UNSUPPORTED_ON_INTERFACE,
|
|
@@ -734,6 +746,8 @@ export const ERRORS = {
|
|
|
734
746
|
LIST_SIZE_INVALID_SIZED_FIELD,
|
|
735
747
|
LIST_SIZE_INVALID_SLICING_ARGUMENT,
|
|
736
748
|
MAX_VALIDATION_SUBGRAPH_PATHS_EXCEEDED,
|
|
749
|
+
AUTHENTICATION_APPLIED_ON_INTERFACE,
|
|
750
|
+
MISSING_TRANSITIVE_AUTH_REQUIREMENTS,
|
|
737
751
|
};
|
|
738
752
|
|
|
739
753
|
const codeDefByCode = Object.values(ERRORS).reduce((obj: {[code: string]: ErrorCodeDefinition}, codeDef: ErrorCodeDefinition) => { obj[codeDef.code] = codeDef; return obj; }, {});
|
package/src/federation.ts
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
isWrapperType,
|
|
38
38
|
possibleRuntimeTypes,
|
|
39
39
|
isIntType,
|
|
40
|
-
Type,
|
|
40
|
+
Type, isFieldDefinition,
|
|
41
41
|
} from "./definitions";
|
|
42
42
|
import { assert, MultiMap, printHumanReadableList, OrderedMap, mapValues, assertUnreachable } from "./utils";
|
|
43
43
|
import { SDLValidationRule } from "graphql/validation/ValidationContext";
|
|
@@ -97,7 +97,7 @@ import { createObjectTypeSpecification, createScalarTypeSpecification, createUni
|
|
|
97
97
|
import { didYouMean, suggestionList } from "./suggestions";
|
|
98
98
|
import { coreFeatureDefinitionIfKnown } from "./knownCoreFeatures";
|
|
99
99
|
import { joinIdentity } from "./specs/joinSpec";
|
|
100
|
-
import {
|
|
100
|
+
import { CostDirectiveArguments, ListSizeDirectiveArguments } from "./specs/costSpec";
|
|
101
101
|
|
|
102
102
|
const linkSpec = LINK_VERSIONS.latest();
|
|
103
103
|
const tagSpec = TAG_VERSIONS.latest();
|
|
@@ -1828,18 +1828,16 @@ export class FederationBlueprint extends SchemaBlueprint {
|
|
|
1828
1828
|
}
|
|
1829
1829
|
}
|
|
1830
1830
|
|
|
1831
|
-
const
|
|
1832
|
-
const
|
|
1833
|
-
const costDirective = costSpec?.costDirective(schema);
|
|
1834
|
-
const listSizeDirective = costSpec?.listSizeDirective(schema);
|
|
1831
|
+
const costDirective = metadata.costDirective();
|
|
1832
|
+
const listSizeDirective = metadata.listSizeDirective();
|
|
1835
1833
|
|
|
1836
1834
|
// Validate @cost
|
|
1837
|
-
for (const application of costDirective
|
|
1835
|
+
for (const application of costDirective.applications()) {
|
|
1838
1836
|
validateCostNotAppliedToInterface(application, errorCollector);
|
|
1839
1837
|
}
|
|
1840
1838
|
|
|
1841
1839
|
// Validate @listSize
|
|
1842
|
-
for (const application of listSizeDirective
|
|
1840
|
+
for (const application of listSizeDirective.applications()) {
|
|
1843
1841
|
const parent = application.parent;
|
|
1844
1842
|
assert(parent instanceof FieldDefinition, "@listSize can only be applied to FIELD_DEFINITION");
|
|
1845
1843
|
validateListSizeAppliedToList(application, parent, errorCollector);
|
|
@@ -1848,6 +1846,9 @@ export class FederationBlueprint extends SchemaBlueprint {
|
|
|
1848
1846
|
validateSizedFieldsAreValidLists(application, parent, errorCollector);
|
|
1849
1847
|
}
|
|
1850
1848
|
|
|
1849
|
+
// Validate @authenticated, @requireScopes and @policy
|
|
1850
|
+
validateNoAuthenticationOnInterfaces(metadata, errorCollector);
|
|
1851
|
+
|
|
1851
1852
|
return errorCollector;
|
|
1852
1853
|
}
|
|
1853
1854
|
|
|
@@ -2899,3 +2900,39 @@ function withoutNonExternalLeafFields(selectionSet: SelectionSet): SelectionSet
|
|
|
2899
2900
|
return undefined;
|
|
2900
2901
|
});
|
|
2901
2902
|
}
|
|
2903
|
+
|
|
2904
|
+
function validateNoAuthenticationOnInterfaces(metadata: FederationMetadata, errorCollector: GraphQLError[]) {
|
|
2905
|
+
const authenticatedDirective = metadata.authenticatedDirective();
|
|
2906
|
+
const requiresScopesDirective = metadata.requiresScopesDirective();
|
|
2907
|
+
const policyDirective = metadata.policyDirective();
|
|
2908
|
+
[authenticatedDirective, requiresScopesDirective, policyDirective].forEach((directive) => {
|
|
2909
|
+
for (const application of directive.applications()) {
|
|
2910
|
+
const element = application.parent;
|
|
2911
|
+
function isAppliedOnInterface(type: Type) {
|
|
2912
|
+
return isInterfaceType(type) || isInterfaceObjectType(baseType(type));
|
|
2913
|
+
}
|
|
2914
|
+
function isAppliedOnInterfaceField(elem: SchemaElement<any, any>) {
|
|
2915
|
+
return isFieldDefinition(elem) && isAppliedOnInterface(elem.parent);
|
|
2916
|
+
}
|
|
2917
|
+
|
|
2918
|
+
if (isAppliedOnInterface(element) || isAppliedOnInterfaceField(element)) {
|
|
2919
|
+
let kind = '';
|
|
2920
|
+
switch (element.kind) {
|
|
2921
|
+
case 'FieldDefinition':
|
|
2922
|
+
kind = 'field';
|
|
2923
|
+
break;
|
|
2924
|
+
case 'InterfaceType':
|
|
2925
|
+
kind = 'interface';
|
|
2926
|
+
break;
|
|
2927
|
+
case 'ObjectType':
|
|
2928
|
+
kind = 'interface object';
|
|
2929
|
+
break;
|
|
2930
|
+
}
|
|
2931
|
+
errorCollector.push(ERRORS.AUTHENTICATION_APPLIED_ON_INTERFACE.err(
|
|
2932
|
+
`Invalid use of @${directive.name} on ${kind} "${element.coordinate}": @${directive.name} cannot be applied on interfaces, interface objects or their fields`,
|
|
2933
|
+
{nodes: sourceASTs(application, element.parent)},
|
|
2934
|
+
));
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
});
|
|
2938
|
+
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from "./coreSpec";
|
|
9
9
|
import { createDirectiveSpecification } from "../directiveAndTypeSpecification";
|
|
10
10
|
import { registerKnownFeature } from "../knownCoreFeatures";
|
|
11
|
+
import {DirectiveDefinition, Schema} from "../definitions";
|
|
11
12
|
|
|
12
13
|
export class AuthenticatedSpecDefinition extends FeatureDefinition {
|
|
13
14
|
public static readonly directiveName = "authenticated";
|
|
@@ -37,6 +38,10 @@ export class AuthenticatedSpecDefinition extends FeatureDefinition {
|
|
|
37
38
|
}));
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
authenticatedDirective(schema: Schema): DirectiveDefinition | undefined {
|
|
42
|
+
return this.directive(schema, AuthenticatedSpecDefinition.directiveName);
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
get defaultCorePurpose(): CorePurpose {
|
|
41
46
|
return 'SECURITY';
|
|
42
47
|
}
|