@apollo/gateway 0.43.1 → 2.0.0-alpha.1

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +2 -4
  2. package/LICENSE +95 -0
  3. package/dist/executeQueryPlan.d.ts +2 -3
  4. package/dist/executeQueryPlan.d.ts.map +1 -1
  5. package/dist/executeQueryPlan.js +2 -27
  6. package/dist/executeQueryPlan.js.map +1 -1
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +22 -41
  10. package/dist/index.js.map +1 -1
  11. package/package.json +7 -5
  12. package/src/__tests__/build-query-plan.feature +34 -24
  13. package/src/__tests__/buildQueryPlan.test.ts +76 -840
  14. package/src/__tests__/executeQueryPlan.test.ts +543 -537
  15. package/src/__tests__/execution-utils.ts +20 -26
  16. package/src/__tests__/gateway/lifecycle-hooks.test.ts +26 -4
  17. package/src/__tests__/gateway/reporting.test.ts +0 -14
  18. package/src/__tests__/integration/abstract-types.test.ts +40 -52
  19. package/src/__tests__/integration/boolean.test.ts +3 -1
  20. package/src/__tests__/integration/complex-key.test.ts +40 -55
  21. package/src/__tests__/integration/configuration.test.ts +5 -5
  22. package/src/__tests__/integration/custom-directives.test.ts +8 -2
  23. package/src/__tests__/integration/multiple-key.test.ts +10 -11
  24. package/src/__tests__/integration/requires.test.ts +2 -2
  25. package/src/__tests__/integration/scope.test.ts +19 -30
  26. package/src/__tests__/integration/value-types.test.ts +31 -31
  27. package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +217 -142
  28. package/src/__tests__/queryPlanCucumber.test.ts +4 -18
  29. package/src/core/__tests__/core.test.ts +1 -0
  30. package/src/executeQueryPlan.ts +1 -32
  31. package/src/index.ts +39 -80
  32. package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +4 -4
  33. package/LICENSE.md +0 -21
  34. package/dist/core/index.d.ts +0 -13
  35. package/dist/core/index.d.ts.map +0 -1
  36. package/dist/core/index.js +0 -43
  37. package/dist/core/index.js.map +0 -1
  38. package/src/__tests__/build-query-plan-fragmentization.feature +0 -282
  39. package/src/core/index.ts +0 -51
package/src/index.ts CHANGED
@@ -11,14 +11,8 @@ import {
11
11
  isIntrospectionType,
12
12
  GraphQLSchema,
13
13
  VariableDefinitionNode,
14
- DocumentNode,
15
- print,
16
- parse,
17
- Source,
18
14
  } from 'graphql';
19
15
  import {
20
- composeAndValidate,
21
- compositionHasErrors,
22
16
  ServiceDefinition,
23
17
  } from '@apollo/federation';
24
18
  import loglevel from 'loglevel';
@@ -41,7 +35,6 @@ import {
41
35
  QueryPlanner,
42
36
  QueryPlan,
43
37
  prettyFormatQueryPlan,
44
- toAPISchema,
45
38
  } from '@apollo/query-planner';
46
39
  import {
47
40
  ServiceEndpointDefinition,
@@ -71,11 +64,15 @@ import {
71
64
  } from './config';
72
65
  import { loadSupergraphSdlFromStorage } from './loadSupergraphSdlFromStorage';
73
66
  import { getServiceDefinitionsFromStorage } from './legacyLoadServicesFromStorage';
74
- import { buildComposedSchema } from '@apollo/query-planner';
75
67
  import { SpanStatusCode } from '@opentelemetry/api';
76
68
  import { OpenTelemetrySpanNames, tracer } from './utilities/opentelemetry';
77
- import { CoreSchema } from '@apollo/core-schema';
78
- import { featureSupport } from './core';
69
+
70
+ import {
71
+ buildSupergraphSchema,
72
+ operationFromDocument,
73
+ Schema,
74
+ } from '@apollo/federation-internals';
75
+ import { composeServices } from '@apollo/composition'
79
76
 
80
77
  type DataSourceMap = {
81
78
  [serviceName: string]: { url?: string; dataSource: GraphQLDataSource };
@@ -176,6 +173,13 @@ interface GraphQLServiceEngineConfig {
176
173
 
177
174
  export class ApolloGateway implements GraphQLService {
178
175
  public schema?: GraphQLSchema;
176
+ // Same as a `schema` but as a `Schema` to avoid reconverting when we need it.
177
+ // TODO(sylvain): if we add caching in `Schema.toGraphQLJSSchema`, we could maybe only keep `apiSchema`
178
+ // and make `schema` a getter (though `schema` does rely on `wrapSchemaWithAliasResolver` so this should
179
+ // be accounted for and this may look ugly). Unsure if moving from a member to a getter could break anyone externally however
180
+ // (also unclear why we expose a mutable member public in the first place; don't everything break if the
181
+ // use manually assigns `schema`?).
182
+ private apiSchema?: Schema;
179
183
  private serviceMap: DataSourceMap = Object.create(null);
180
184
  private config: GatewayConfig;
181
185
  private logger: Logger;
@@ -193,7 +197,7 @@ export class ApolloGateway implements GraphQLService {
193
197
  private serviceSdlCache = new Map<string, string>();
194
198
  private warnedStates: WarnedStates = Object.create(null);
195
199
  private queryPlanner?: QueryPlanner;
196
- private parsedSupergraphSdl?: DocumentNode;
200
+ private supergraphSdl?: string;
197
201
  private fetcher: typeof fetch;
198
202
  private compositionId?: string;
199
203
  private state: GatewayState;
@@ -439,14 +443,13 @@ export class ApolloGateway implements GraphQLService {
439
443
  // Synchronously load a statically configured schema, update class instance's
440
444
  // schema and query planner.
441
445
  private loadStatic(config: StaticGatewayConfig) {
442
- let schema: GraphQLSchema;
446
+ let schema: Schema;
443
447
  let supergraphSdl: string;
444
448
  try {
445
449
  ({ schema, supergraphSdl } = isLocalConfig(config)
446
450
  ? this.createSchemaFromServiceList(config.localServiceList)
447
451
  : this.createSchemaFromSupergraphSdl(config.supergraphSdl));
448
- // TODO(trevor): #580 redundant parse
449
- this.parsedSupergraphSdl = parse(supergraphSdl);
452
+ this.supergraphSdl = supergraphSdl;
450
453
  this.updateWithSchemaAndNotify(schema, supergraphSdl, true);
451
454
  } catch (e) {
452
455
  this.state = { phase: 'failed to load' };
@@ -532,7 +535,7 @@ export class ApolloGateway implements GraphQLService {
532
535
  this.experimental_didUpdateComposition(
533
536
  {
534
537
  serviceDefinitions: result.serviceDefinitions,
535
- schema,
538
+ schema: schema.toGraphQLJSSchema(),
536
539
  ...(this.compositionMetadata && {
537
540
  compositionMetadata: this.compositionMetadata,
538
541
  }),
@@ -558,15 +561,14 @@ export class ApolloGateway implements GraphQLService {
558
561
  return;
559
562
  }
560
563
 
561
- // TODO(trevor): #580 redundant parse
562
564
  // This may throw, so we'll calculate early (specifically before making any updates)
563
565
  // In the case that it throws, the gateway will:
564
566
  // * on initial load, throw the error
565
567
  // * on update, log the error and don't update
566
- const parsedSupergraphSdl = parse(result.supergraphSdl);
568
+ const { schema, supergraphSdl } = this.createSchemaFromSupergraphSdl(result.supergraphSdl);
567
569
 
568
570
  const previousSchema = this.schema;
569
- const previousSupergraphSdl = this.parsedSupergraphSdl;
571
+ const previousSupergraphSdl = this.supergraphSdl;
570
572
  const previousCompositionId = this.compositionId;
571
573
 
572
574
  if (previousSchema) {
@@ -576,11 +578,8 @@ export class ApolloGateway implements GraphQLService {
576
578
  await this.maybePerformServiceHealthCheck(result);
577
579
 
578
580
  this.compositionId = result.id;
579
- this.parsedSupergraphSdl = parsedSupergraphSdl;
581
+ this.supergraphSdl = result.supergraphSdl;
580
582
 
581
- const { schema, supergraphSdl } = this.createSchemaFromSupergraphSdl(
582
- result.supergraphSdl,
583
- );
584
583
 
585
584
  if (!supergraphSdl) {
586
585
  this.logger.error(
@@ -594,12 +593,12 @@ export class ApolloGateway implements GraphQLService {
594
593
  {
595
594
  compositionId: result.id,
596
595
  supergraphSdl: result.supergraphSdl,
597
- schema,
596
+ schema: schema.toGraphQLJSSchema(),
598
597
  },
599
598
  previousCompositionId && previousSupergraphSdl && previousSchema
600
599
  ? {
601
600
  compositionId: previousCompositionId,
602
- supergraphSdl: print(previousSupergraphSdl),
601
+ supergraphSdl: previousSupergraphSdl,
603
602
  schema: previousSchema,
604
603
  }
605
604
  : undefined,
@@ -612,13 +611,14 @@ export class ApolloGateway implements GraphQLService {
612
611
  // ensure we do not forget to update some of that state, and to avoid scenarios where
613
612
  // concurrently executing code sees partially-updated state.
614
613
  private updateWithSchemaAndNotify(
615
- coreSchema: GraphQLSchema,
614
+ coreSchema: Schema,
616
615
  coreSupergraphSdl: string,
617
616
  // Once we remove the deprecated onSchemaChange() method, we can remove this.
618
617
  legacyDontNotifyOnSchemaChangeListeners: boolean = false,
619
618
  ): void {
620
619
  if (this.queryPlanStore) this.queryPlanStore.flush();
621
- this.schema = toAPISchema(coreSchema);
620
+ this.apiSchema = coreSchema.toAPISchema();
621
+ this.schema = wrapSchemaWithAliasResolver(this.apiSchema.toGraphQLJSSchema());
622
622
  this.queryPlanner = new QueryPlanner(coreSchema);
623
623
 
624
624
  // Notify onSchemaChange listeners of the updated schema
@@ -658,10 +658,9 @@ export class ApolloGateway implements GraphQLService {
658
658
  // This is the last chance to bail out of a schema update.
659
659
  if (this.config.serviceHealthCheck) {
660
660
  const serviceList = isSupergraphSdlUpdate(update)
661
- ? // TODO(trevor): #580 redundant parse
662
- // Parsing could technically fail and throw here, but parseability has
661
+ ? // Parsing of the supergraph SDL could technically fail and throw here, but parseability has
663
662
  // already been confirmed slightly earlier in the code path
664
- this.serviceListFromSupergraphSdl(parse(update.supergraphSdl))
663
+ this.serviceListFromSupergraphSdl(update.supergraphSdl)
665
664
  : // Existence of this is determined in advance with an early return otherwise
666
665
  update.serviceDefinitions!;
667
666
  // Here we need to construct new datasources based on the new schema info
@@ -726,10 +725,9 @@ export class ApolloGateway implements GraphQLService {
726
725
  .join('\n')}`,
727
726
  );
728
727
 
729
- const compositionResult = composeAndValidate(serviceList);
730
-
731
- if (compositionHasErrors(compositionResult)) {
732
- const { errors } = compositionResult;
728
+ const compositionResult = composeServices(serviceList);
729
+ const errors = compositionResult.errors;
730
+ if (errors) {
733
731
  if (this.experimental_didFailComposition) {
734
732
  this.experimental_didFailComposition({
735
733
  errors,
@@ -744,60 +742,25 @@ export class ApolloGateway implements GraphQLService {
744
742
  errors.map((e) => '\t' + e.message).join('\n'),
745
743
  );
746
744
  } else {
747
- const { supergraphSdl } = compositionResult;
748
745
  this.createServices(serviceList);
749
-
750
- const schema = buildComposedSchema(parse(supergraphSdl));
751
-
752
746
  this.logger.debug('Schema loaded and ready for execution');
753
-
754
- // This is a workaround for automatic wrapping of all fields, which Apollo
755
- // Server does in the case of implementing resolver wrapping for plugins.
756
- // Here we wrap all fields with support for resolving aliases as part of the
757
- // root value which happens because aliases are resolved by sub services and
758
- // the shape of the root value already contains the aliased fields as
759
- // responseNames
760
747
  return {
761
- schema: wrapSchemaWithAliasResolver(schema),
762
- supergraphSdl,
748
+ schema: compositionResult.schema,
749
+ supergraphSdl: compositionResult.supergraphSdl,
763
750
  };
764
751
  }
765
752
  }
766
753
 
767
- private serviceListFromSupergraphSdl(
768
- supergraphSdl: DocumentNode,
769
- ): Omit<ServiceDefinition, 'typeDefs'>[] {
770
- const schema = buildComposedSchema(supergraphSdl);
771
- return this.serviceListFromComposedSchema(schema);
772
- }
773
-
774
- private serviceListFromComposedSchema(schema: GraphQLSchema) {
775
- const graphMap = schema.extensions?.federation?.graphs;
776
- if (!graphMap) {
777
- throw Error(`Couldn't find graph map in composed schema`);
778
- }
779
-
780
- return Array.from(graphMap.values());
754
+ private serviceListFromSupergraphSdl(supergraphSdl: string): Omit<ServiceDefinition, 'typeDefs'>[] {
755
+ return buildSupergraphSchema(supergraphSdl)[1];
781
756
  }
782
757
 
783
758
  private createSchemaFromSupergraphSdl(supergraphSdl: string) {
784
- const core = CoreSchema.fromSource(
785
- new Source(supergraphSdl, 'supergraphSdl'),
786
- )
787
- .check() // run basic core schema compliance checks
788
- .check(featureSupport); // run supported feature check
789
-
790
- // TODO(trevor): #580 redundant parse
791
- this.parsedSupergraphSdl = core.document;
792
-
793
- const schema = buildComposedSchema(this.parsedSupergraphSdl);
794
-
795
- const serviceList = this.serviceListFromComposedSchema(schema);
796
-
759
+ const [schema, serviceList] = buildSupergraphSchema(supergraphSdl);
797
760
  this.createServices(serviceList);
798
761
 
799
762
  return {
800
- schema: wrapSchemaWithAliasResolver(schema),
763
+ schema,
801
764
  supergraphSdl,
802
765
  };
803
766
  }
@@ -1059,7 +1022,6 @@ export class ApolloGateway implements GraphQLService {
1059
1022
  span.setStatus({ code: SpanStatusCode.ERROR });
1060
1023
  return { errors: validationErrors };
1061
1024
  }
1062
-
1063
1025
  let queryPlan: QueryPlan | undefined;
1064
1026
  if (this.queryPlanStore) {
1065
1027
  queryPlan = await this.queryPlanStore.get(queryPlanStoreKey);
@@ -1070,12 +1032,9 @@ export class ApolloGateway implements GraphQLService {
1070
1032
  OpenTelemetrySpanNames.PLAN,
1071
1033
  (span) => {
1072
1034
  try {
1035
+ const operation = operationFromDocument(this.apiSchema!, document, request.operationName);
1073
1036
  // TODO(#631): Can we be sure the query planner has been initialized here?
1074
- return this.queryPlanner!.buildQueryPlan(operationContext, {
1075
- autoFragmentization: Boolean(
1076
- this.config.experimental_autoFragmentization,
1077
- ),
1078
- });
1037
+ return this.queryPlanner!.buildQueryPlan(operation);
1079
1038
  } catch (err) {
1080
1039
  span.setStatus({ code: SpanStatusCode.ERROR });
1081
1040
  throw err;
@@ -1,9 +1,9 @@
1
- import { buildSchema, execute, GraphQLError, parse } from "graphql";
2
- import { toAPISchema } from "@apollo/query-planner";
1
+ import { execute, GraphQLError, parse } from "graphql";
3
2
  import { cleanErrorOfInaccessibleNames } from "../cleanErrorOfInaccessibleNames";
3
+ import { buildSchema } from "@apollo/federation-internals";
4
4
 
5
5
  describe('cleanErrorOfInaccessibleNames', () => {
6
- let schema = buildSchema(`
6
+ const coreSchema = buildSchema(`
7
7
  directive @core(
8
8
  feature: String!,
9
9
  as: String,
@@ -46,7 +46,7 @@ describe('cleanErrorOfInaccessibleNames', () => {
46
46
  inaccessibleField: String @inaccessible
47
47
  }
48
48
  `);
49
- schema = toAPISchema(schema);
49
+ const schema = coreSchema.toAPISchema().toGraphQLJSSchema();
50
50
 
51
51
  it('removes inaccessible type names from error messages', async () => {
52
52
  const result = await execute(schema, parse('{fooField{someField}}'), {
package/LICENSE.md DELETED
@@ -1,21 +0,0 @@
1
- The MIT License
2
-
3
- Copyright (c) 2020- Apollo Graph, Inc.
4
- Copyright (c) 2019-2020 Meteor Development Group, Inc.
5
-
6
- Permission is hereby granted, free of charge, to any person obtaining a copy of
7
- this software and associated documentation files (the "Software"), to deal in
8
- the Software without restriction, including without limitation the rights to
9
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
- the Software, and to permit persons to whom the Software is furnished to do so,
11
- subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all
14
- copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,13 +0,0 @@
1
- import { CoreSchemaContext, Feature } from '@apollo/core-schema';
2
- export declare const ErrUnsupportedFeature: (feature: Feature) => import("@apollo/core-schema/dist/error").GraphQLErrorExt<"UnsupportedFeature"> & {
3
- message: string;
4
- feature: Feature;
5
- nodes: import("graphql").DirectiveNode[];
6
- };
7
- export declare const ErrForUnsupported: (core: Feature, ...features: readonly Feature[]) => import("@apollo/core-schema/dist/error").GraphQLErrorExt<"ForUnsupported"> & {
8
- message: string;
9
- features: readonly Feature[];
10
- nodes: import("graphql").DirectiveNode[];
11
- };
12
- export declare function featureSupport(this: CoreSchemaContext): void;
13
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAO,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAUtE,eAAO,MAAM,qBAAqB,YAAa,OAAO;;;;CAKlD,CAAC;AAEL,eAAO,MAAM,iBAAiB,SAAU,OAAO,eAAe,SAAS,OAAO,EAAE;;;;CAO5E,CAAC;AAEL,wBAAgB,cAAc,CAAC,IAAI,EAAE,iBAAiB,QAwBrD"}
@@ -1,43 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.featureSupport = exports.ErrForUnsupported = exports.ErrUnsupportedFeature = void 0;
4
- const core_schema_1 = require("@apollo/core-schema");
5
- const SUPPORTED_FEATURES = new Set([
6
- 'https://specs.apollo.dev/core/v0.1',
7
- 'https://specs.apollo.dev/core/v0.2',
8
- 'https://specs.apollo.dev/join/v0.1',
9
- 'https://specs.apollo.dev/tag/v0.1',
10
- 'https://specs.apollo.dev/inaccessible/v0.1',
11
- ]);
12
- const ErrUnsupportedFeature = (feature) => (0, core_schema_1.err)('UnsupportedFeature', {
13
- message: `feature ${feature.url} is for: ${feature.purpose} but is unsupported`,
14
- feature,
15
- nodes: [feature.directive],
16
- });
17
- exports.ErrUnsupportedFeature = ErrUnsupportedFeature;
18
- const ErrForUnsupported = (core, ...features) => (0, core_schema_1.err)('ForUnsupported', {
19
- message: `the \`for:\` argument is unsupported by version ${core.url.version} ` +
20
- `of the core spec. Please upgrade to at least @core v0.2 (https://specs.apollo.dev/core/v0.2).`,
21
- features,
22
- nodes: [core.directive, ...features.map(f => f.directive)]
23
- });
24
- exports.ErrForUnsupported = ErrForUnsupported;
25
- function featureSupport() {
26
- const coreVersionZeroDotOne = this.features.find('https://specs.apollo.dev/core/v0.1', true);
27
- if (coreVersionZeroDotOne) {
28
- const purposefulFeatures = [...this.features].filter(f => f.purpose);
29
- if (purposefulFeatures.length > 0)
30
- this.report((0, exports.ErrForUnsupported)(coreVersionZeroDotOne, ...purposefulFeatures));
31
- }
32
- for (const feature of this.features) {
33
- if (!feature.purpose)
34
- continue;
35
- if (feature.purpose === 'EXECUTION' || feature.purpose === 'SECURITY') {
36
- if (!SUPPORTED_FEATURES.has(feature.url.base.toString())) {
37
- this.report((0, exports.ErrUnsupportedFeature)(feature));
38
- }
39
- }
40
- }
41
- }
42
- exports.featureSupport = featureSupport;
43
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";;;AAAA,qDAAsE;AAEtE,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjC,oCAAoC;IACpC,oCAAoC;IACpC,oCAAoC;IACpC,mCAAmC;IACnC,4CAA4C;CAC7C,CAAC,CAAC;AAEI,MAAM,qBAAqB,GAAG,CAAC,OAAgB,EAAE,EAAE,CACxD,IAAA,iBAAG,EAAC,oBAAoB,EAAE;IACxB,OAAO,EAAE,WAAW,OAAO,CAAC,GAAG,YAAY,OAAO,CAAC,OAAO,qBAAqB;IAC/E,OAAO;IACP,KAAK,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;CAC3B,CAAC,CAAC;AALQ,QAAA,qBAAqB,yBAK7B;AAEE,MAAM,iBAAiB,GAAG,CAAC,IAAa,EAAE,GAAG,QAA4B,EAAE,EAAE,CAClF,IAAA,iBAAG,EAAC,gBAAgB,EAAE;IACpB,OAAO,EACL,mDAAmD,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG;QACtE,+FAA+F;IACjG,QAAQ;IACR,KAAK,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;CAC3D,CAAC,CAAC;AAPQ,QAAA,iBAAiB,qBAOzB;AAEL,SAAgB,cAAc;IAC5B,MAAM,qBAAqB,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAC9C,oCAAoC,EACpC,IAAI,CACL,CAAC;IAIF,IAAI,qBAAqB,EAAE;QACzB,MAAM,kBAAkB,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;QACpE,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC;YAC/B,IAAI,CAAC,MAAM,CAAC,IAAA,yBAAiB,EAAC,qBAAqB,EAAE,GAAG,kBAAkB,CAAC,CAAC,CAAA;KAG/E;IAED,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE;QACnC,IAAI,CAAC,OAAO,CAAC,OAAO;YAAE,SAAS;QAC/B,IAAI,OAAO,CAAC,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,OAAO,KAAK,UAAU,EAAE;YACrE,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE;gBACxD,IAAI,CAAC,MAAM,CAAC,IAAA,6BAAqB,EAAC,OAAO,CAAC,CAAC,CAAC;aAC7C;SACF;KACF;AACH,CAAC;AAxBD,wCAwBC"}
@@ -1,282 +0,0 @@
1
- Feature: Build Query Plan > Auto-fragmentization
2
-
3
- Scenario: experimental compression to downstream services should generate fragments internally to downstream requests
4
- Given query
5
- """
6
- query {
7
- topReviews {
8
- body
9
- author
10
- product {
11
- name
12
- price
13
- details {
14
- country
15
- }
16
- }
17
- }
18
- }
19
- """
20
- When using autofragmentization
21
- Then query plan
22
- """
23
- {
24
- "kind": "QueryPlan",
25
- "node": {
26
- "kind": "Sequence",
27
- "nodes": [
28
- {
29
- "kind": "Fetch",
30
- "serviceName": "reviews",
31
- "variableUsages": [],
32
- "operation": "{topReviews{...__QueryPlanFragment_1__}}fragment __QueryPlanFragment_1__ on Review{body author product{...__QueryPlanFragment_0__}}fragment __QueryPlanFragment_0__ on Product{__typename ...on Book{__typename isbn}...on Furniture{__typename upc}}"
33
- },
34
- {
35
- "kind": "Parallel",
36
- "nodes": [
37
- {
38
- "kind": "Sequence",
39
- "nodes": [
40
- {
41
- "kind": "Flatten",
42
- "path": ["topReviews", "@", "product"],
43
- "node": {
44
- "kind": "Fetch",
45
- "serviceName": "books",
46
- "requires": [
47
- {
48
- "kind": "InlineFragment",
49
- "typeCondition": "Book",
50
- "selections": [
51
- { "kind": "Field", "name": "__typename" },
52
- { "kind": "Field", "name": "isbn" }
53
- ]
54
- }
55
- ],
56
- "variableUsages": [],
57
- "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{__typename isbn title year}}}"
58
- }
59
- },
60
- {
61
- "kind": "Flatten",
62
- "path": ["topReviews", "@", "product"],
63
- "node": {
64
- "kind": "Fetch",
65
- "serviceName": "product",
66
- "requires": [
67
- {
68
- "kind": "InlineFragment",
69
- "typeCondition": "Book",
70
- "selections": [
71
- { "kind": "Field", "name": "__typename" },
72
- { "kind": "Field", "name": "isbn" },
73
- { "kind": "Field", "name": "title" },
74
- { "kind": "Field", "name": "year" }
75
- ]
76
- }
77
- ],
78
- "variableUsages": [],
79
- "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}"
80
- }
81
- }
82
- ]
83
- },
84
- {
85
- "kind": "Flatten",
86
- "path": ["topReviews", "@", "product"],
87
- "node": {
88
- "kind": "Fetch",
89
- "serviceName": "product",
90
- "requires": [
91
- {
92
- "kind": "InlineFragment",
93
- "typeCondition": "Furniture",
94
- "selections": [
95
- { "kind": "Field", "name": "__typename" },
96
- { "kind": "Field", "name": "upc" }
97
- ]
98
- },
99
- {
100
- "kind": "InlineFragment",
101
- "typeCondition": "Book",
102
- "selections": [
103
- { "kind": "Field", "name": "__typename" },
104
- { "kind": "Field", "name": "isbn" }
105
- ]
106
- }
107
- ],
108
- "variableUsages": [],
109
- "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Furniture{name price details{country}}...on Book{price details{country}}}}"
110
- }
111
- }
112
- ]
113
- }
114
- ]
115
- }
116
- }
117
- """
118
-
119
- Scenario: experimental compression to downstream services shouldn't generate fragments for selection sets of length 2 or less
120
- Given query
121
- """
122
- query {
123
- topReviews {
124
- body
125
- author
126
- }
127
- }
128
- """
129
- When using autofragmentization
130
- Then query plan
131
- """
132
- {
133
- "kind": "QueryPlan",
134
- "node": {
135
- "kind": "Fetch",
136
- "serviceName": "reviews",
137
- "variableUsages": [],
138
- "operation": "{topReviews{body author}}"
139
- }
140
- }
141
- """
142
-
143
- Scenario: experimental compression to downstream services should generate fragments for selection sets of length 3 or greater
144
- Given query
145
- """
146
- query {
147
- topReviews {
148
- id
149
- body
150
- author
151
- }
152
- }
153
- """
154
- When using autofragmentization
155
- Then query plan
156
- """
157
- {
158
- "kind": "QueryPlan",
159
- "node": {
160
- "kind": "Fetch",
161
- "serviceName": "reviews",
162
- "variableUsages": [],
163
- "operation": "{topReviews{...__QueryPlanFragment_0__}}fragment __QueryPlanFragment_0__ on Review{id body author}"
164
- }
165
- }
166
- """
167
-
168
- Scenario: experimental compression to downstream services should generate fragments correctly when aliases are used
169
- Given query
170
- """
171
- query {
172
- reviews: topReviews {
173
- content: body
174
- author
175
- product {
176
- name
177
- cost: price
178
- details {
179
- origin: country
180
- }
181
- }
182
- }
183
- }
184
- """
185
- When using autofragmentization
186
- Then query plan
187
- """
188
- {
189
- "kind": "QueryPlan",
190
- "node": {
191
- "kind": "Sequence",
192
- "nodes": [
193
- {
194
- "kind": "Fetch",
195
- "serviceName": "reviews",
196
- "variableUsages": [],
197
- "operation": "{reviews:topReviews{...__QueryPlanFragment_1__}}fragment __QueryPlanFragment_1__ on Review{content:body author product{...__QueryPlanFragment_0__}}fragment __QueryPlanFragment_0__ on Product{__typename ...on Book{__typename isbn}...on Furniture{__typename upc}}"
198
- },
199
- {
200
- "kind": "Parallel",
201
- "nodes": [
202
- {
203
- "kind": "Sequence",
204
- "nodes": [
205
- {
206
- "kind": "Flatten",
207
- "path": ["reviews", "@", "product"],
208
- "node": {
209
- "kind": "Fetch",
210
- "serviceName": "books",
211
- "requires": [
212
- {
213
- "kind": "InlineFragment",
214
- "typeCondition": "Book",
215
- "selections": [
216
- { "kind": "Field", "name": "__typename" },
217
- { "kind": "Field", "name": "isbn" }
218
- ]
219
- }
220
- ],
221
- "variableUsages": [],
222
- "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{__typename isbn title year}}}"
223
- }
224
- },
225
- {
226
- "kind": "Flatten",
227
- "path": ["reviews", "@", "product"],
228
- "node": {
229
- "kind": "Fetch",
230
- "serviceName": "product",
231
- "requires": [
232
- {
233
- "kind": "InlineFragment",
234
- "typeCondition": "Book",
235
- "selections": [
236
- { "kind": "Field", "name": "__typename" },
237
- { "kind": "Field", "name": "isbn" },
238
- { "kind": "Field", "name": "title" },
239
- { "kind": "Field", "name": "year" }
240
- ]
241
- }
242
- ],
243
- "variableUsages": [],
244
- "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}"
245
- }
246
- }
247
- ]
248
- },
249
- {
250
- "kind": "Flatten",
251
- "path": ["reviews", "@", "product"],
252
- "node": {
253
- "kind": "Fetch",
254
- "serviceName": "product",
255
- "requires": [
256
- {
257
- "kind": "InlineFragment",
258
- "typeCondition": "Furniture",
259
- "selections": [
260
- { "kind": "Field", "name": "__typename" },
261
- { "kind": "Field", "name": "upc" }
262
- ]
263
- },
264
- {
265
- "kind": "InlineFragment",
266
- "typeCondition": "Book",
267
- "selections": [
268
- { "kind": "Field", "name": "__typename" },
269
- { "kind": "Field", "name": "isbn" }
270
- ]
271
- }
272
- ],
273
- "variableUsages": [],
274
- "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Furniture{name cost:price details{origin:country}}...on Book{cost:price details{origin:country}}}}"
275
- }
276
- }
277
- ]
278
- }
279
- ]
280
- }
281
- }
282
- """