@@ -13,7 +13,6 @@ import {
13
13
printDirectiveDefinition ,
14
14
printSchema ,
15
15
printType ,
16
- RequiresScopesSpecDefinition ,
17
16
} from '@apollo/federation-internals' ;
18
17
import { CompositionOptions , CompositionResult , composeServices } from '../compose' ;
19
18
import gql from 'graphql-tag' ;
@@ -4243,15 +4242,20 @@ describe('composition', () => {
4243
4242
} ) ;
4244
4243
} ) ;
4245
4244
4246
- describe ( '@requiresScopes' , ( ) => {
4247
- it ( 'comprehensive locations' , ( ) => {
4245
+ // @requiresScopes and @policy behave exactly the same way, and so all tests should be equally applicable to both directives
4246
+ describe ( '@requiresScopes and @policy' , ( ) => {
4247
+ const testsToRun = [
4248
+ { directiveName : '@requiresScopes' , argName : 'scopes' , argType : 'requiresScopes__Scope' , fedType : 'federation__Scope' , identity : 'https://specs.apollo.dev/requiresScopes' } ,
4249
+ { directiveName : '@policy' , argName : 'policies' , argType : 'policy__Policy' , fedType : 'federation__Policy' , identity : 'https://specs.apollo.dev/policy' } ,
4250
+ ]
4251
+ it . each ( testsToRun ) ( 'comprehensive locations' , ( { directiveName, argName } ) => {
4248
4252
const onObject = {
4249
4253
typeDefs : gql `
4250
4254
type Query {
4251
4255
object: ScopedObject!
4252
4256
}
4253
4257
4254
- type ScopedObject @requiresScopes(scopes : ["object"]) {
4258
+ type ScopedObject ${ directiveName } ( ${ argName } : ["object"]) {
4255
4259
field: Int!
4256
4260
}
4257
4261
` ,
@@ -4264,7 +4268,7 @@ describe('composition', () => {
4264
4268
interface: ScopedInterface!
4265
4269
}
4266
4270
4267
- interface ScopedInterface @requiresScopes(scopes : ["interface"]) {
4271
+ interface ScopedInterface ${ directiveName } ( ${ argName } : ["interface"]) {
4268
4272
field: Int!
4269
4273
}
4270
4274
` ,
@@ -4276,7 +4280,7 @@ describe('composition', () => {
4276
4280
type ScopedInterfaceObject
4277
4281
@interfaceObject
4278
4282
@key(fields: "id")
4279
- @requiresScopes(scopes : ["interfaceObject"])
4283
+ ${ directiveName } ( ${ argName } : ["interfaceObject"])
4280
4284
{
4281
4285
id: String!
4282
4286
}
@@ -4286,11 +4290,11 @@ describe('composition', () => {
4286
4290
4287
4291
const onScalar = {
4288
4292
typeDefs : gql `
4289
- scalar ScopedScalar @requiresScopes(scopes : ["scalar"])
4293
+ scalar ScopedScalar ${ directiveName } ( ${ argName } : ["scalar"])
4290
4294
4291
4295
# This needs to exist in at least one other subgraph from where it's defined
4292
4296
# as an @interfaceObject (so arbitrarily adding it here). We don't actually
4293
- # apply @requiresScopes to this one since we want to see it propagate even
4297
+ # apply ${ directiveName } to this one since we want to see it propagate even
4294
4298
# when it's not applied in all locations.
4295
4299
interface ScopedInterfaceObject @key(fields: "id") {
4296
4300
id: String!
@@ -4301,7 +4305,7 @@ describe('composition', () => {
4301
4305
4302
4306
const onEnum = {
4303
4307
typeDefs : gql `
4304
- enum ScopedEnum @requiresScopes(scopes : ["enum"]) {
4308
+ enum ScopedEnum ${ directiveName } ( ${ argName } : ["enum"]) {
4305
4309
A
4306
4310
B
4307
4311
}
@@ -4312,7 +4316,7 @@ describe('composition', () => {
4312
4316
const onRootField = {
4313
4317
typeDefs : gql `
4314
4318
type Query {
4315
- scopedRootField: Int! @requiresScopes(scopes : ["rootField"])
4319
+ scopedRootField: Int! ${ directiveName } ( ${ argName } : ["rootField"])
4316
4320
}
4317
4321
` ,
4318
4322
name : 'on-root-field' ,
@@ -4325,7 +4329,7 @@ describe('composition', () => {
4325
4329
}
4326
4330
4327
4331
type ObjectWithScopedField {
4328
- field: Int! @requiresScopes(scopes : ["objectField"])
4332
+ field: Int! ${ directiveName } ( ${ argName } : ["objectField"])
4329
4333
}
4330
4334
` ,
4331
4335
name : 'on-object-field' ,
@@ -4339,7 +4343,7 @@ describe('composition', () => {
4339
4343
4340
4344
type EntityWithScopedField @key(fields: "id") {
4341
4345
id: ID!
4342
- field: Int! @requiresScopes(scopes : ["entityField"])
4346
+ field: Int! ${ directiveName } ( ${ argName } : ["entityField"])
4343
4347
}
4344
4348
` ,
4345
4349
name : 'on-entity-field' ,
@@ -4372,18 +4376,18 @@ describe('composition', () => {
4372
4376
expect (
4373
4377
result . schema
4374
4378
. elementByCoordinate ( element )
4375
- ?. hasAppliedDirective ( "requiresScopes" )
4379
+ ?. hasAppliedDirective ( directiveName . slice ( 1 ) )
4376
4380
) . toBeTruthy ( ) ;
4377
4381
}
4378
4382
} ) ;
4379
4383
4380
- it ( 'applies @requiresScopes on types as long as it is used once' , ( ) => {
4384
+ it . each ( testsToRun ) ( 'applies directive on types as long as it is used once' , ( { directiveName , argName } ) => {
4381
4385
const a1 = {
4382
4386
typeDefs : gql `
4383
4387
type Query {
4384
4388
a: A
4385
4389
}
4386
- type A @key(fields: "id") @requiresScopes(scopes : ["a"]) {
4390
+ type A @key(fields: "id") ${ directiveName } ( ${ argName } : ["a"]) {
4387
4391
id: String!
4388
4392
a1: String
4389
4393
}
@@ -4407,18 +4411,18 @@ describe('composition', () => {
4407
4411
assertCompositionSuccess ( result1 ) ;
4408
4412
assertCompositionSuccess ( result2 ) ;
4409
4413
4410
- expect ( result1 . schema . type ( 'A' ) ?. hasAppliedDirective ( 'requiresScopes' ) ) . toBeTruthy ( ) ;
4411
- expect ( result2 . schema . type ( 'A' ) ?. hasAppliedDirective ( 'requiresScopes' ) ) . toBeTruthy ( ) ;
4414
+ expect ( result1 . schema . type ( 'A' ) ?. hasAppliedDirective ( directiveName . slice ( 1 ) ) ) . toBeTruthy ( ) ;
4415
+ expect ( result2 . schema . type ( 'A' ) ?. hasAppliedDirective ( directiveName . slice ( 1 ) ) ) . toBeTruthy ( ) ;
4412
4416
} ) ;
4413
4417
4414
- it ( 'merges @requiresScopes lists (simple union)' , ( ) => {
4418
+ it . each ( testsToRun ) ( 'merges ${directiveName} lists (simple union)' , ( { directiveName , argName } ) => {
4415
4419
const a1 = {
4416
4420
typeDefs : gql `
4417
4421
type Query {
4418
4422
a: A
4419
4423
}
4420
4424
4421
- type A @requiresScopes(scopes : ["a"]) @key(fields: "id") {
4425
+ type A ${ directiveName } ( ${ argName } : ["a"]) @key(fields: "id") {
4422
4426
id: String!
4423
4427
a1: String
4424
4428
}
@@ -4427,7 +4431,7 @@ describe('composition', () => {
4427
4431
} ;
4428
4432
const a2 = {
4429
4433
typeDefs : gql `
4430
- type A @requiresScopes(scopes : ["b"]) @key(fields: "id") {
4434
+ type A ${ directiveName } ( ${ argName } : ["b"]) @key(fields: "id") {
4431
4435
id: String!
4432
4436
a2: String
4433
4437
}
@@ -4439,19 +4443,19 @@ describe('composition', () => {
4439
4443
assertCompositionSuccess ( result ) ;
4440
4444
expect (
4441
4445
result . schema . type ( 'A' )
4442
- ?. appliedDirectivesOf ( 'requiresScopes' )
4443
- ?. [ 0 ] ?. arguments ( ) ?. scopes ) . toStrictEqual ( [ 'a' , 'b' ]
4446
+ ?. appliedDirectivesOf ( directiveName . slice ( 1 ) )
4447
+ ?. [ 0 ] ?. arguments ( ) ?. [ argName ] ) . toStrictEqual ( [ 'a' , 'b' ]
4444
4448
) ;
4445
4449
} ) ;
4446
4450
4447
- it ( 'merges @requiresScopes lists (deduplicates intersecting scopes)' , ( ) => {
4451
+ it . each ( testsToRun ) ( 'merges ${directiveName} lists (deduplicates intersecting scopes)' , ( { directiveName , argName } ) => {
4448
4452
const a1 = {
4449
4453
typeDefs : gql `
4450
4454
type Query {
4451
4455
a: A
4452
4456
}
4453
4457
4454
- type A @requiresScopes(scopes : ["a", "b"]) @key(fields: "id") {
4458
+ type A ${ directiveName } ( ${ argName } : ["a", "b"]) @key(fields: "id") {
4455
4459
id: String!
4456
4460
a1: String
4457
4461
}
@@ -4460,7 +4464,7 @@ describe('composition', () => {
4460
4464
} ;
4461
4465
const a2 = {
4462
4466
typeDefs : gql `
4463
- type A @requiresScopes(scopes : ["b", "c"]) @key(fields: "id") {
4467
+ type A ${ directiveName } ( ${ argName } : ["b", "c"]) @key(fields: "id") {
4464
4468
id: String!
4465
4469
a2: String
4466
4470
}
@@ -4472,37 +4476,37 @@ describe('composition', () => {
4472
4476
assertCompositionSuccess ( result ) ;
4473
4477
expect (
4474
4478
result . schema . type ( 'A' )
4475
- ?. appliedDirectivesOf ( 'requiresScopes' )
4476
- ?. [ 0 ] ?. arguments ( ) ?. scopes ) . toStrictEqual ( [ 'a' , 'b' , 'c' ]
4479
+ ?. appliedDirectivesOf ( directiveName . slice ( 1 ) )
4480
+ ?. [ 0 ] ?. arguments ( ) ?. [ argName ] ) . toStrictEqual ( [ 'a' , 'b' , 'c' ]
4477
4481
) ;
4478
4482
} ) ;
4479
4483
4480
- it ( '@requiresScopes has correct definition in the supergraph', ( ) => {
4484
+ it . each ( testsToRun ) ( '${directiveName} has correct definition in the supergraph', ( { directiveName , argName , argType , identity } ) => {
4481
4485
const a = {
4482
4486
typeDefs : gql `
4483
4487
type Query {
4484
- x: Int @requiresScopes(scopes : ["a", "b"])
4488
+ x: Int ${ directiveName } ( ${ argName } : ["a", "b"])
4485
4489
}
4486
4490
` ,
4487
4491
name : 'a' ,
4488
4492
} ;
4489
4493
4490
4494
const result = composeAsFed2Subgraphs ( [ a ] ) ;
4491
4495
assertCompositionSuccess ( result ) ;
4492
- expect ( result . schema . coreFeatures ?. getByIdentity ( RequiresScopesSpecDefinition . identity ) ?. url . toString ( ) ) . toBe (
4493
- " https://specs.apollo.dev/requiresScopes /v0.1"
4496
+ expect ( result . schema . coreFeatures ?. getByIdentity ( identity ) ?. url . toString ( ) ) . toBe (
4497
+ ` https://specs.apollo.dev/${ directiveName . slice ( 1 ) } /v0.1`
4494
4498
) ;
4495
- expect ( printDirectiveDefinition ( result . schema . directive ( 'requiresScopes' ) ! ) ) . toMatchString ( `
4496
- directive @requiresScopes(scopes : [[requiresScopes__Scope !]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
4499
+ expect ( printDirectiveDefinition ( result . schema . directive ( directiveName . slice ( 1 ) ) ! ) ) . toMatchString ( `
4500
+ directive ${ directiveName } ( ${ argName } : [[${ argType } !]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
4497
4501
` ) ;
4498
4502
} ) ;
4499
4503
4500
- it ( 'composes with existing `Scope` scalar definitions in subgraphs' , ( ) => {
4504
+ it . each ( testsToRun ) ( 'composes with existing `Scope` scalar definitions in subgraphs' , ( { directiveName , argName } ) => {
4501
4505
const a = {
4502
4506
typeDefs : gql `
4503
4507
scalar Scope
4504
4508
type Query {
4505
- x: Int @requiresScopes(scopes : ["a", "b"])
4509
+ x: Int ${ directiveName } ( ${ argName } : ["a", "b"])
4506
4510
}
4507
4511
` ,
4508
4512
name : 'a' ,
@@ -4512,7 +4516,7 @@ describe('composition', () => {
4512
4516
typeDefs : gql `
4513
4517
scalar Scope @specifiedBy(url: "not-the-apollo-spec")
4514
4518
type Query {
4515
- y: Int @requiresScopes(scopes : ["a", "b"])
4519
+ y: Int ${ directiveName } ( ${ argName } : ["a", "b"])
4516
4520
}
4517
4521
` ,
4518
4522
name : 'b' ,
@@ -4523,69 +4527,69 @@ describe('composition', () => {
4523
4527
} ) ;
4524
4528
4525
4529
describe ( 'validation errors' , ( ) => {
4526
- it ( 'on incompatible directive location' , ( ) => {
4530
+ it . each ( testsToRun ) ( 'on incompatible directive location' , ( { directiveName , argName , fedType } ) => {
4527
4531
const invalidDefinition = {
4528
4532
typeDefs : gql `
4529
- scalar federation__Scope
4530
- directive @requiresScopes(scopes : [[federation__Scope !]!]!) on ENUM_VALUE
4533
+ scalar ${ fedType }
4534
+ directive ${ directiveName } ( ${ argName } : [[${ fedType } !]!]!) on ENUM_VALUE
4531
4535
4532
4536
type Query {
4533
4537
a: Int
4534
4538
}
4535
4539
4536
4540
enum E {
4537
- A @requiresScopes(scopes : [])
4541
+ A ${ directiveName } ( ${ argName } : [])
4538
4542
}
4539
4543
` ,
4540
4544
name : 'invalidDefinition' ,
4541
4545
} ;
4542
4546
const result = composeAsFed2Subgraphs ( [ invalidDefinition ] ) ;
4543
4547
expect ( errors ( result ) [ 0 ] ) . toEqual ( [
4544
4548
"DIRECTIVE_DEFINITION_INVALID" ,
4545
- " [invalidDefinition] Invalid definition for directive \"@requiresScopes \": \"@requiresScopes \" should have locations FIELD_DEFINITION, OBJECT, INTERFACE, SCALAR, ENUM, but found (non-subset) ENUM_VALUE" ,
4549
+ ` [invalidDefinition] Invalid definition for directive \"${ directiveName } \": \"${ directiveName } \" should have locations FIELD_DEFINITION, OBJECT, INTERFACE, SCALAR, ENUM, but found (non-subset) ENUM_VALUE` ,
4546
4550
] ) ;
4547
4551
} ) ;
4548
4552
4549
- it ( 'on incompatible args' , ( ) => {
4553
+ it . each ( testsToRun ) ( 'on incompatible args' , ( { directiveName , argName , fedType } ) => {
4550
4554
const invalidDefinition = {
4551
4555
typeDefs : gql `
4552
- scalar federation__Scope
4553
- directive @requiresScopes(scopes : [federation__Scope ]!) on FIELD_DEFINITION
4556
+ scalar ${ fedType }
4557
+ directive ${ directiveName } ( ${ argName } : [${ fedType } ]!) on FIELD_DEFINITION
4554
4558
4555
4559
type Query {
4556
4560
a: Int
4557
4561
}
4558
4562
4559
4563
enum E {
4560
- A @requiresScopes(scopes : [])
4564
+ A ${ directiveName } ( ${ argName } : [])
4561
4565
}
4562
4566
` ,
4563
4567
name : 'invalidDefinition' ,
4564
4568
} ;
4565
4569
const result = composeAsFed2Subgraphs ( [ invalidDefinition ] ) ;
4566
4570
expect ( errors ( result ) [ 0 ] ) . toEqual ( [
4567
4571
"DIRECTIVE_DEFINITION_INVALID" ,
4568
- " [invalidDefinition] Invalid definition for directive \"@requiresScopes \": argument \"scopes \" should have type \"[[federation__Scope !]!]!\" but found type \"[federation__Scope ]!\"" ,
4572
+ ` [invalidDefinition] Invalid definition for directive \"${ directiveName } \": argument \"${ argName } \" should have type \"[[${ fedType } !]!]!\" but found type \"[${ fedType } ]!\"` ,
4569
4573
] ) ;
4570
4574
} ) ;
4571
4575
4572
- it ( 'on invalid application' , ( ) => {
4576
+ it . each ( testsToRun ) ( 'on invalid application' , ( { directiveName , argName } ) => {
4573
4577
const invalidApplication = {
4574
4578
typeDefs : gql `
4575
4579
type Query {
4576
4580
a: Int
4577
4581
}
4578
4582
4579
4583
enum E {
4580
- A @requiresScopes(scopes : [])
4584
+ A ${ directiveName } ( ${ argName } : [])
4581
4585
}
4582
4586
` ,
4583
4587
name : 'invalidApplication' ,
4584
4588
} ;
4585
4589
const result = composeAsFed2Subgraphs ( [ invalidApplication ] ) ;
4586
4590
expect ( errors ( result ) [ 0 ] ) . toEqual ( [
4587
4591
"INVALID_GRAPHQL" ,
4588
- " [invalidApplication] Directive \"@requiresScopes \" may not be used on ENUM_VALUE." ,
4592
+ ` [invalidApplication] Directive \"${ directiveName } \" may not be used on ENUM_VALUE.` ,
4589
4593
] ) ;
4590
4594
} ) ;
4591
4595
} ) ;
0 commit comments