Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2976b5e
feat: send subgraphs who requested fields
ysmolski Aug 22, 2025
a8b0596
rename extension
ysmolski Aug 26, 2025
3e83436
feat: implement openfed__protected
Aenimus Aug 26, 2025
10b8202
draft reading of protected fields
ysmolski Aug 27, 2025
d2a1701
Merge branch 'david/eng-7769-implement-oopenfed__protected' into yury…
ysmolski Aug 27, 2025
60d634d
rename fields after merge
ysmolski Aug 27, 2025
819d1c9
update based on the composition change
ysmolski Aug 27, 2025
8438d39
bump the engine
ysmolski Aug 27, 2025
c1a2456
Merge branch 'main' into yury/eng-7769-fields-authorization-for-subgr…
ysmolski Aug 27, 2025
3c222b2
Merge branch 'main' into david/eng-7769-implement-oopenfed__protected
ysmolski Aug 27, 2025
1d33752
chore: avoid potential duplicate definitions
Aenimus Aug 27, 2025
ee482e3
chore: fix typo in comment
Aenimus Aug 27, 2025
9495e2d
Merge branch 'main' into yury/eng-7769-fields-authorization-for-subgr…
ysmolski Aug 28, 2025
3ba193f
start renaming things
ysmolski Sep 1, 2025
4315610
bump engine to 20250827132225-23526ad02aa8
ysmolski Sep 1, 2025
410c6e8
chore: rename directive
Aenimus Sep 1, 2025
69005f3
Merge branch 'david/eng-7769-implement-oopenfed__protected' into yury…
ysmolski Sep 1, 2025
4d10430
Merge branch 'main' into yury/eng-7769-fields-authorization-for-subgr…
ysmolski Sep 1, 2025
03bb114
rename things for the new directive name
ysmolski Sep 1, 2025
edd54c9
generate stuff for connect
ysmolski Sep 1, 2025
65f1326
rename the config option
ysmolski Sep 1, 2025
8ce278b
renamed leftovers
ysmolski Sep 1, 2025
afafa6e
put back newlines in the golden jsons
ysmolski Sep 1, 2025
e4a62fe
remove newlines
ysmolski Sep 1, 2025
dfb43c9
remove newlines #2
ysmolski Sep 1, 2025
e83349a
use the fixed engine
ysmolski Sep 9, 2025
137706d
preallocate all fields of FederationMetaData
ysmolski Sep 9, 2025
1040bd9
Merge branch 'main' into yury/eng-7769-fields-authorization-for-subgr…
ysmolski Sep 9, 2025
ba008a5
Merge branch 'main' into yury/eng-7769-fields-authorization-for-subgr…
ysmolski Sep 10, 2025
10e8178
Merge branch 'main' into yury/eng-7769-fields-authorization-for-subgr…
ysmolski Sep 10, 2025
a8e6b50
Merge branch 'main' into yury/eng-7769-fields-authorization-for-subgr…
ysmolski Sep 11, 2025
55590fc
Update router/pkg/config/config.schema.json
ysmolski Sep 11, 2025
3a78c20
Merge branch 'main' into yury/eng-7769-fields-authorization-for-subgr…
ysmolski Sep 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 38 additions & 38 deletions composition-go/index.global.js

Large diffs are not rendered by default.

13 changes: 8 additions & 5 deletions composition/src/router-configuration/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FieldName, TypeName } from '../types/types';

export type NatsEventType = 'subscribe' | 'publish' | 'request';

export type KafkaEventType = 'subscribe' | 'publish';
Expand Down Expand Up @@ -72,20 +74,21 @@ export type FieldSetConditionDataParams = {
};

export type RequiredFieldConfiguration = {
fieldName: string;
fieldName: FieldName;
selectionSet: string;
conditions?: Array<FieldSetConditionData>;
disableEntityResolver?: boolean;
};

export type ConfigurationData = {
fieldNames: Set<string>;
fieldNames: Set<FieldName>;
isRootNode: boolean;
typeName: string;
entityInterfaceConcreteTypeNames?: Set<string>;
typeName: TypeName;
entityInterfaceConcreteTypeNames?: Set<TypeName>;
events?: EventConfiguration[];
externalFieldNames?: Set<string>;
externalFieldNames?: Set<FieldName>;
isInterfaceObject?: boolean;
protectedFieldNames?: Array<FieldName>;
provides?: RequiredFieldConfiguration[];
keys?: RequiredFieldConfiguration[];
requires?: RequiredFieldConfiguration[];
Expand Down
3 changes: 2 additions & 1 deletion composition/src/router-configuration/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ConfigurationData, FieldSetConditionData, FieldSetConditionDataParams } from './types';
import { FieldName } from '../types/types';

export function newFieldSetConditionData({
fieldCoordinatesPath,
Expand All @@ -12,7 +13,7 @@ export function newFieldSetConditionData({

export function newConfigurationData(isEntity: boolean, renamedTypeName: string): ConfigurationData {
return {
fieldNames: new Set<string>(),
fieldNames: new Set<FieldName>(),
isRootNode: isEntity,
typeName: renamedTypeName,
};
Expand Down
1 change: 1 addition & 0 deletions composition/src/schema-building/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export type ObjectDefinitionData = {
name: string;
node: MutableObjectNode;
persistedDirectivesData: PersistedDirectivesData;
protectedFieldNames: Set<FieldName>;
renamedTypeName: string;
Comment thread
ysmolski marked this conversation as resolved.
Outdated
subgraphNames: Set<string>;
description?: StringValueNode;
Expand Down
4 changes: 2 additions & 2 deletions composition/src/schema-building/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,8 @@ export function isParentDataRootType(parentData: ParentDefinitionData): boolean
return parentData.isRootType;
}

export function isParentDataInterfaceType(parentData: ParentDefinitionData): boolean {
return parentData.kind === Kind.INTERFACE_TYPE_DEFINITION;
export function isInterfaceDefinitionData(data: ParentDefinitionData): data is InterfaceDefinitionData {
return data.kind === Kind.INTERFACE_TYPE_DEFINITION;
}

export function setParentDataExtensionType(existingData: ParentDefinitionData, incomingData: ParentDefinitionData) {
Expand Down
5 changes: 3 additions & 2 deletions composition/src/subgraph/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ParentDefinitionData,
PersistedDirectiveDefinitionData,
} from '../schema-building/types';
import { TypeName } from '../types/types';

export type Subgraph = {
definitions: DocumentNode;
Expand All @@ -14,9 +15,9 @@ export type Subgraph = {
};

export type SubgraphConfig = {
configurationDataByTypeName: Map<string, ConfigurationData>;
configurationDataByTypeName: Map<TypeName, ConfigurationData>;
isVersionTwo: boolean;
parentDefinitionDataByTypeName: Map<string, ParentDefinitionData>;
parentDefinitionDataByTypeName: Map<TypeName, ParentDefinitionData>;
schema: GraphQLSchema;
};

Expand Down
3 changes: 2 additions & 1 deletion composition/src/utils/string-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export const PARENT_DEFINITION_DATA = 'parentDefinitionDataByTypeName';
export const PARENT_DEFINITION_DATA_MAP = 'parentDefinitionDataByParentTypeName';
export const PARENT_EXTENSION_DATA_MAP = 'parentExtensionDataByParentTypeName';
export const PERIOD = '.';
export const PROTECTED = 'openfed__protected';
export const PROVIDER_ID = 'providerId';
export const PROVIDES = 'provides';
export const PUBLISH = 'publish';
Expand Down Expand Up @@ -165,7 +166,7 @@ export const EXECUTABLE_DIRECTIVE_LOCATIONS = new Set<string>([
export const ROOT_TYPE_NAMES = new Set<string>([MUTATION, QUERY, SUBSCRIPTION]);
export const AUTHORIZATION_DIRECTIVES = new Set<string>([AUTHENTICATED, REQUIRES_SCOPES]);
export const PERSISTED_CLIENT_DIRECTIVES = new Set<string>([DEPRECATED]);
export const INHERITABLE_DIRECTIVE_NAMES = new Set<string>([EXTERNAL, SHAREABLE]);
export const INHERITABLE_DIRECTIVE_NAMES = new Set<string>([EXTERNAL, PROTECTED, SHAREABLE]);
export const IGNORED_FIELDS = new Set<string>([ENTITIES_FIELD, SERVICE_FIELD]);

export const INPUT_NODE_KINDS = new Set<Kind>([
Expand Down
3 changes: 2 additions & 1 deletion composition/src/v1/federation/federation-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ import {
InvalidRequiredInputValueData,
} from '../../utils/types';
import { FederateSubgraphsContractV1Params, FederateSubgraphsWithContractsV1Params, FederationParams } from './types';
import { ContractName, FieldCoords, SubgraphName, TypeName } from '../../types/types';
import { ContractName, FieldCoords, FieldName, SubgraphName, TypeName } from '../../types/types';

Comment thread
ysmolski marked this conversation as resolved.
export class FederationFactory {
authorizationDataByParentTypeName: Map<string, AuthorizationData>;
Expand Down Expand Up @@ -1132,6 +1132,7 @@ export class FederationFactory {
kind: sourceData.kind,
name: stringToNameNode(sourceData.renamedTypeName || sourceData.name),
},
protectedFieldNames: new Set<FieldName>(),
renamedTypeName: sourceData.renamedTypeName,
subgraphNames: new Set(sourceData.subgraphNames),
};
Expand Down
12 changes: 12 additions & 0 deletions composition/src/v1/normalization/directive-definition-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
KEY_DEFINITION,
LINK_DEFINITION,
OVERRIDE_DEFINITION,
PROTECTED_DEFINITION,
PROVIDES_DEFINITION,
REQUIRED_FIELDSET_TYPE_NODE,
REQUIRED_STRING_TYPE_NODE,
Expand Down Expand Up @@ -98,6 +99,7 @@ import {
CHANNELS,
EDFS_REDIS_PUBLISH,
EDFS_REDIS_SUBSCRIBE,
PROTECTED,
} from '../../utils/string-constants';

export const AUTHENTICATED_DEFINITION_DATA: DirectiveDefinitionData = {
Expand Down Expand Up @@ -439,6 +441,16 @@ export const NATS_SUBSCRIBE_DEFINITION_DATA: DirectiveDefinitionData = {
requiredArgumentNames: new Set<string>([SUBJECTS]),
};

export const PROTECTED_DEFINITION_DATA: DirectiveDefinitionData = {
argumentTypeNodeByArgumentName: new Map<string, ArgumentData>(),
isRepeatable: true,
locations: new Set<string>([FIELD_DEFINITION_UPPER, OBJECT_UPPER]),
name: PROTECTED,
node: PROTECTED_DEFINITION,
optionalArgumentNames: new Set<string>(),
requiredArgumentNames: new Set<string>(),
};

export const REDIS_PUBLISH_DEFINITION_DATA: DirectiveDefinitionData = {
argumentTypeNodeByArgumentName: new Map<string, ArgumentData>([
[
Expand Down
30 changes: 25 additions & 5 deletions composition/src/v1/normalization/normalization-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
LINK_IMPORT_DEFINITION,
LINK_PURPOSE_DEFINITION,
MAX_OR_SCOPES,
PROTECTED_DEFINITION,
SCOPE_SCALAR_DEFINITION,
SUBSCRIPTION_FIELD_CONDITION_DEFINITION,
SUBSCRIPTION_FILTER_CONDITION_DEFINITION,
Expand Down Expand Up @@ -303,6 +304,7 @@ import {
OPERATION_TO_DEFAULT,
OVERRIDE,
PROPAGATE,
PROTECTED,
PROVIDER_ID,
PROVIDER_TYPE_KAFKA,
PROVIDER_TYPE_NATS,
Expand Down Expand Up @@ -356,6 +358,7 @@ import {
} from './types';
import { newConfigurationData, newFieldSetConditionData } from '../../router-configuration/utils';
import { ImplementationErrors, InvalidFieldImplementation } from '../../utils/types';
import { FieldName } from '../../types/types';

export function normalizeSubgraphFromString(subgraphSDL: string, noLocation = true): NormalizationResult {
const { error, documentNode } = safeParse(subgraphSDL, noLocation);
Expand Down Expand Up @@ -397,6 +400,7 @@ export class NormalizationFactory {
invalidRepeatedDirectiveNameByCoords = new Map<string, Set<string>>();
isCurrentParentExtension = false;
isParentObjectExternal = false;
isParentObjectProtected = false;
isParentObjectShareable = false;
isSubgraphEventDrivenGraph = false;
isSubgraphVersionTwo = false;
Expand Down Expand Up @@ -555,14 +559,18 @@ export class NormalizationFactory {
fieldDirectivesByDirectiveName: Map<string, Array<ConstDirectiveNode>>,
inheritedDirectiveNames: Set<string>,
) {
if (this.isParentObjectShareable && !fieldDirectivesByDirectiveName.has(SHAREABLE)) {
fieldDirectivesByDirectiveName.set(SHAREABLE, [generateSimpleDirective(SHAREABLE)]);
inheritedDirectiveNames.add(SHAREABLE);
}
if (this.isParentObjectExternal && !fieldDirectivesByDirectiveName.has(EXTERNAL)) {
fieldDirectivesByDirectiveName.set(EXTERNAL, [generateSimpleDirective(EXTERNAL)]);
inheritedDirectiveNames.add(EXTERNAL);
}
if (this.isParentObjectProtected && !fieldDirectivesByDirectiveName.has(PROTECTED)) {
fieldDirectivesByDirectiveName.set(PROTECTED, [generateSimpleDirective(PROTECTED)]);
inheritedDirectiveNames.add(PROTECTED);
}
if (this.isParentObjectShareable && !fieldDirectivesByDirectiveName.has(SHAREABLE)) {
fieldDirectivesByDirectiveName.set(SHAREABLE, [generateSimpleDirective(SHAREABLE)]);
inheritedDirectiveNames.add(SHAREABLE);
}
return fieldDirectivesByDirectiveName;
}

Expand Down Expand Up @@ -591,6 +599,7 @@ export class NormalizationFactory {
continue;
}
this.isParentObjectExternal ||= directiveName === EXTERNAL;
this.isParentObjectProtected ||= directiveName === PROTECTED;
this.isParentObjectShareable ||= directiveName === SHAREABLE;
}
Comment thread
ysmolski marked this conversation as resolved.
Outdated
return directivesByDirectiveName;
Expand Down Expand Up @@ -1251,7 +1260,7 @@ export class NormalizationFactory {
const parentData = this.parentDefinitionDataByTypeName.get(typeName);
const directivesByDirectiveName = this.extractDirectives(
node,
parentData?.directivesByDirectiveName || new Map<string, ConstDirectiveNode[]>(),
parentData?.directivesByDirectiveName ?? new Map<string, ConstDirectiveNode[]>(),
);
const isRootType = this.isTypeNameRootType(typeName);
const extensionType = this.getNodeExtensionType(isRealExtension, directivesByDirectiveName, isRootType);
Expand Down Expand Up @@ -1290,6 +1299,7 @@ export class NormalizationFactory {
name: typeName,
node: getMutableObjectNode(node.name),
persistedDirectivesData: newPersistedDirectivesData(),
protectedFieldNames: new Set<FieldName>(),
renamedTypeName: this.getRenamedRootTypeName(typeName),
subgraphNames: new Set<string>([this.subgraphName]),
description: formatDescription('description' in node ? node.description : undefined),
Expand Down Expand Up @@ -2874,6 +2884,10 @@ export class NormalizationFactory {
definitions.push(LINK_PURPOSE_DEFINITION);
}

if (this.referencedDirectiveNames.has(PROTECTED)) {
definitions.push(PROTECTED_DEFINITION);
}

Comment thread
ysmolski marked this conversation as resolved.
Outdated
if (invalidEventsDirectiveDataByRootFieldPath.size > 0) {
errorMessages.push(invalidRootTypeFieldEventsDirectivesErrorMessage(invalidEventsDirectiveDataByRootFieldPath));
}
Expand Down Expand Up @@ -3327,6 +3341,9 @@ export class NormalizationFactory {
if (this.referencedDirectiveNames.has(CONFIGURE_CHILD_DESCRIPTIONS)) {
definitions.push(CONFIGURE_CHILD_DESCRIPTIONS_DEFINITION);
}
if (this.referencedDirectiveNames.has(PROTECTED)) {
definitions.push(PROTECTED_DEFINITION);
}
for (const directiveDefinition of this.customDirectiveDefinitions.values()) {
definitions.push(directiveDefinition);
}
Expand Down Expand Up @@ -3459,6 +3476,9 @@ export class NormalizationFactory {
if (parentData.fieldDataByName.size < 1 && !isNodeQuery(parentTypeName, operationTypeNode)) {
this.errors.push(noFieldDefinitionsError(kindToNodeType(parentData.kind), parentTypeName));
}
if (isObject && parentData.protectedFieldNames.size > 0) {
configurationData.protectedFieldNames = [...parentData.protectedFieldNames];
}
break;
case Kind.SCALAR_TYPE_DEFINITION:
if (parentData.extensionType === ExtensionType.REAL) {
Expand Down
3 changes: 3 additions & 0 deletions composition/src/v1/normalization/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
SPECIFIED_BY_DEFINITION_DATA,
SUBSCRIPTION_FILTER_DEFINITION_DATA,
TAG_DEFINITION_DATA,
PROTECTED_DEFINITION_DATA,
} from './directive-definition-data';
import {
AUTHENTICATED,
Expand All @@ -70,6 +71,7 @@ import {
LINK,
OVERRIDE,
PERIOD,
PROTECTED,
PROVIDES,
QUERY,
REQUIRES,
Expand Down Expand Up @@ -404,6 +406,7 @@ export function initializeDirectiveDefinitionDatas(): Map<string, DirectiveDefin
[LINK, LINK_DEFINITION_DATA],
[OVERRIDE, OVERRIDE_DEFINITION_DATA],
[PROVIDES, PROVIDES_DEFINITION_DATA],
[PROTECTED, PROTECTED_DEFINITION_DATA],
[REQUIRES, REQUIRES_DEFINITION_DATA],
[REQUIRES_SCOPES, REQUIRES_SCOPES_DEFINITION_DATA],
[SHAREABLE, SHAREABLE_DEFINITION_DATA],
Expand Down
10 changes: 8 additions & 2 deletions composition/src/v1/normalization/walkers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { EVENT_DIRECTIVE_NAMES } from '../utils/string-constants';
import {
getRenamedRootTypeName,
isParentDataCompositeOutputType,
isParentDataInterfaceType,
isInterfaceDefinitionData,
isTypeNameRootType,
newPersistedDirectivesData,
} from '../../schema-building/utils';
Expand All @@ -39,6 +39,7 @@ import {
EXTERNAL,
IGNORED_FIELDS,
PARENT_DEFINITION_DATA,
PROTECTED,
PROVIDES,
REQUIRES,
SERVICE_OBJECT,
Expand Down Expand Up @@ -362,11 +363,14 @@ export function upsertParentsAndChildren(nf: NormalizationFactory, document: Doc
const directivesByDirectiveName = nf.extractDirectives(node, new Map<string, ConstDirectiveNode[]>());
const inheritedDirectiveNames = new Set<string>();
// Add parent-level shareable and external to the field extraction and repeatable validation
if (!isParentDataInterfaceType(parentData)) {
if (!isInterfaceDefinitionData(parentData)) {
nf.addInheritedDirectivesToFieldData(directivesByDirectiveName, inheritedDirectiveNames);
if (directivesByDirectiveName.has(EXTERNAL)) {
nf.unvalidatedExternalFieldCoords.add(`${nf.originalParentTypeName}.${fieldName}`);
}
if (nf.isParentObjectProtected || directivesByDirectiveName.has(PROTECTED)) {
parentData.protectedFieldNames.add(fieldName);
}
}
const fieldData = nf.addFieldDataByNode(
parentData.fieldDataByName,
Expand Down Expand Up @@ -521,6 +525,7 @@ export function upsertParentsAndChildren(nf: NormalizationFactory, document: Doc
nf.renamedParentTypeName = '';
nf.lastParentNodeKind = Kind.NULL;
nf.isParentObjectExternal = false;
nf.isParentObjectProtected = false;
nf.isParentObjectShareable = false;
},
},
Expand All @@ -546,6 +551,7 @@ export function upsertParentsAndChildren(nf: NormalizationFactory, document: Doc
nf.renamedParentTypeName = '';
nf.lastParentNodeKind = Kind.NULL;
nf.isParentObjectExternal = false;
nf.isParentObjectProtected = false;
nf.isParentObjectShareable = false;
},
},
Expand Down
10 changes: 10 additions & 0 deletions composition/src/v1/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ import {
EDFS_REDIS_SUBSCRIBE,
CHANNEL,
CHANNELS,
PROTECTED,
} from '../../utils/string-constants';

export const REQUIRED_STRING_TYPE_NODE: TypeNode = {
Expand Down Expand Up @@ -512,6 +513,7 @@ export const ALL_IN_BUILT_DIRECTIVE_NAMES = new Set<string>([
LINK,
OVERRIDE,
PROVIDES,
PROTECTED,
REQUIRES,
REQUIRES_SCOPES,
SHAREABLE,
Expand Down Expand Up @@ -657,6 +659,14 @@ export const OVERRIDE_DEFINITION: DirectiveDefinitionNode = {
repeatable: false,
};

// directive @openfed__protected repeatable on FIELD_DEFINITION | OBJECT_DEFINITION
export const PROTECTED_DEFINITION: DirectiveDefinitionNode = {
kind: Kind.DIRECTIVE_DEFINITION,
locations: stringArrayToNameNodeArray([FIELD_DEFINITION_UPPER, OBJECT_UPPER]),
name: stringToNameNode(PROTECTED),
repeatable: true,
};

Comment thread
ysmolski marked this conversation as resolved.
// @requiresScopes(scopes: [[openfed__Scope!]!]!) on ENUM | FIELD_DEFINITION | INTERFACE | OBJECT | SCALAR
export const REQUIRES_SCOPES_DEFINITION: MutableDirectiveDefinitionNode = {
arguments: [
Expand Down
4 changes: 0 additions & 4 deletions composition/src/v1/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,6 @@ export function isNodeKindObject(kind: Kind) {
return kind === Kind.OBJECT_TYPE_DEFINITION || kind === Kind.OBJECT_TYPE_EXTENSION;
}

export function isInterfaceDefinitionData(data: ParentDefinitionData): data is InterfaceDefinitionData {
return data.kind === Kind.INTERFACE_TYPE_DEFINITION;
}

export function isObjectDefinitionData(data?: ParentDefinitionData): data is ObjectDefinitionData {
if (!data) {
return false;
Expand Down
Loading
Loading