Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@apollo/query-planner

Package Overview
Dependencies
Maintainers
1
Versions
172
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@apollo/query-planner - npm Package Compare versions

Comparing version 2.0.0-alpha.6 to 2.0.0-preview.0

51

dist/buildPlan.js

@@ -270,3 +270,3 @@ "use strict";

(0, federation_internals_1.assert)(root, () => `Shouldn't have a ${operation.rootKind} operation if the subgraphs don't have a ${operation.rootKind} root`);
const processor = fetchGroupToPlanProcessor(operation.variableDefinitions, operation.selectionSet.fragments);
const processor = fetchGroupToPlanProcessor(operation.variableDefinitions, operation.selectionSet.fragments, operation.name);
if (operation.rootKind === 'mutation') {

@@ -336,5 +336,12 @@ const dependencyGraphs = computeRootSerialDependencyGraph(supergraphSchema, operation, federatedQueryGraph, root);

}
function fetchGroupToPlanProcessor(variableDefinitions, fragments) {
function toValidGraphQLName(subgraphName) {
const sanitized = subgraphName
.replace(/-/ig, '_')
.replace(/[^_0-9A-Za-z]/ig, '');
return sanitized.match(/^[0-9].*/i) ? '_' + sanitized : sanitized;
}
function fetchGroupToPlanProcessor(variableDefinitions, fragments, operationName) {
let counter = 0;
return {
onFetchGroup: (group) => group.toPlanNode(variableDefinitions, fragments),
onFetchGroup: (group) => group.toPlanNode(variableDefinitions, fragments, operationName ? `${operationName}__${toValidGraphQLName(group.subgraphName)}__${counter++}` : undefined),
reduceParallel: (values) => flatWrap('Parallel', values),

@@ -455,3 +462,3 @@ reduceSequence: (values) => flatWrap('Sequence', values),

}
toPlanNode(variableDefinitions, fragments) {
toPlanNode(variableDefinitions, fragments, operationName) {
var _a;

@@ -466,4 +473,4 @@ addTypenameFieldForAbstractTypes(this.selection);

const operation = this.isEntityFetch
? operationForEntitiesFetch(this.dependencyGraph.subgraphSchemas.get(this.subgraphName), this.selection, variableDefinitions, fragments)
: operationForQueryFetch(this.rootKind, this.selection, variableDefinitions, fragments);
? operationForEntitiesFetch(this.dependencyGraph.subgraphSchemas.get(this.subgraphName), this.selection, variableDefinitions, fragments, operationName)
: operationForQueryFetch(this.rootKind, this.selection, variableDefinitions, fragments, operationName);
const fetchNode = {

@@ -476,2 +483,3 @@ kind: 'Fetch',

operationKind: schemaRootKindToOperationKind(operation.rootKind),
operationName: operation.name,
};

@@ -528,2 +536,9 @@ return this.isTopLevel

}
federationMetadata(subgraphName) {
const schema = this.subgraphSchemas.get(subgraphName);
(0, federation_internals_1.assert)(schema, () => `Unknown schema ${subgraphName}`);
const metadata = (0, federation_internals_1.federationMetadata)(schema);
(0, federation_internals_1.assert)(metadata, () => `Schema ${subgraphName} should be a federation subgraph`);
return metadata;
}
clone() {

@@ -579,3 +594,4 @@ const cloned = new FetchDependencyGraph(this.subgraphSchemas, this.federatedQueryGraph, new federation_internals_1.MapWithCachedArrays(), new Array(this.groups.length), this.adjacencies.map(a => a.concat()), this.inEdges.map(a => a.concat()), this.pathsInParents.concat());

}
const entityType = this.subgraphSchemas.get(subgraphName).type(federation_internals_1.entityTypeName);
const entityType = this.federationMetadata(subgraphName).entityType();
(0, federation_internals_1.assert)(entityType, () => `Subgraph ${subgraphName} has not entities defined`);
return this.newFetchGroup(subgraphName, entityType, true, 'query', mergeAt, directParent, pathInParent);

@@ -598,3 +614,4 @@ }

newKeyFetchGroup(subgraphName, mergeAt) {
const entityType = this.subgraphSchemas.get(subgraphName).type(federation_internals_1.entityTypeName);
const entityType = this.federationMetadata(subgraphName).entityType();
(0, federation_internals_1.assert)(entityType, () => `Subgraph ${subgraphName} has not entities defined`);
return this.newFetchGroup(subgraphName, entityType, true, 'query', mergeAt);

@@ -1081,8 +1098,8 @@ }

function representationsVariableDefinition(schema) {
const anyType = schema.type('_Any');
(0, federation_internals_1.assert)(anyType, `Cannot find _Any type in schema`);
const representationsType = new federation_internals_1.NonNullType(new federation_internals_1.ListType(new federation_internals_1.NonNullType(anyType)));
const metadata = (0, federation_internals_1.federationMetadata)(schema);
(0, federation_internals_1.assert)(metadata, 'Expected schema to be a federation subgraph');
const representationsType = new federation_internals_1.NonNullType(new federation_internals_1.ListType(new federation_internals_1.NonNullType(metadata.anyType())));
return new federation_internals_1.VariableDefinition(schema, representationsVariable, representationsType);
}
function operationForEntitiesFetch(subgraphSchema, selectionSet, allVariableDefinitions, fragments) {
function operationForEntitiesFetch(subgraphSchema, selectionSet, allVariableDefinitions, fragments, operationName) {
const variableDefinitions = new federation_internals_1.VariableDefinitions();

@@ -1093,7 +1110,7 @@ variableDefinitions.add(representationsVariableDefinition(subgraphSchema));

(0, federation_internals_1.assert)(queryType, `Subgraphs should always have a query root (they should at least provides _entities)`);
const entities = queryType.field('_entities');
const entities = queryType.field(federation_internals_1.entitiesFieldName);
(0, federation_internals_1.assert)(entities, `Subgraphs should always have the _entities field`);
const entitiesCall = new federation_internals_1.SelectionSet(queryType);
entitiesCall.add(new federation_internals_1.FieldSelection(new federation_internals_1.Field(entities, { 'representations': representationsVariable }, variableDefinitions), selectionSet));
return new federation_internals_1.Operation('query', entitiesCall, variableDefinitions).optimize(fragments);
entitiesCall.add(new federation_internals_1.FieldSelection(new federation_internals_1.Field(entities, { representations: representationsVariable }, variableDefinitions), selectionSet));
return new federation_internals_1.Operation('query', entitiesCall, variableDefinitions, operationName).optimize(fragments);
}

@@ -1110,5 +1127,5 @@ function flatWrap(kind, nodes) {

}
function operationForQueryFetch(rootKind, selectionSet, allVariableDefinitions, fragments) {
return new federation_internals_1.Operation(rootKind, selectionSet, allVariableDefinitions.filter(selectionSet.usedVariables())).optimize(fragments);
function operationForQueryFetch(rootKind, selectionSet, allVariableDefinitions, fragments, operationName) {
return new federation_internals_1.Operation(rootKind, selectionSet, allVariableDefinitions.filter(selectionSet.usedVariables()), operationName).optimize(fragments);
}
//# sourceMappingURL=buildPlan.js.map
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

@@ -22,2 +22,3 @@ import { SelectionNode as GraphQLJSSelectionNode, OperationTypeNode } from 'graphql';

operation: string;
operationName: string | undefined;
operationKind: OperationTypeNode;

@@ -24,0 +25,0 @@ }

{
"name": "@apollo/query-planner",
"version": "2.0.0-alpha.6",
"version": "2.0.0-preview.0",
"description": "Apollo Query Planner",

@@ -28,4 +28,4 @@ "author": "Apollo <packages@apollographql.com>",

"dependencies": {
"@apollo/federation-internals": "^2.0.0-alpha.6",
"@apollo/query-graphs": "^2.0.0-alpha.6",
"@apollo/federation-internals": "^2.0.0-preview.0",
"@apollo/query-graphs": "^2.0.0-preview.0",
"chalk": "^4.1.0",

@@ -38,3 +38,3 @@ "deep-equal": "^2.0.5",

},
"gitHead": "ee84b3fd9df161c3a94ae3d70e82a14dba5794ba"
"gitHead": "e76fd3e16bf140ba9b30fe26d48657bccca180dc"
}
import { astSerializer, queryPlanSerializer, QueryPlanner } from '@apollo/query-planner';
import { composeServices } from '@apollo/composition';
import { assert, buildSchema, operationFromDocument, Schema, ServiceDefinition } from '@apollo/federation-internals';
import { asFed2SubgraphDocument, assert, buildSchema, operationFromDocument, Schema, ServiceDefinition } from '@apollo/federation-internals';
import gql from 'graphql-tag';
import { MAX_COMPUTED_PLANS } from '../buildPlan';
import { FetchNode } from '../QueryPlan';
import { FetchNode, FlattenNode, SequenceNode } from '../QueryPlan';
import { FieldNode, OperationDefinitionNode, parse } from 'graphql';

@@ -13,3 +13,5 @@

function composeAndCreatePlanner(...services: ServiceDefinition[]): [Schema, QueryPlanner] {
const compositionResults = composeServices(services);
const compositionResults = composeServices(
services.map((s) => ({ ...s, typeDefs: asFed2SubgraphDocument(s.typeDefs) }))
);
expect(compositionResults.errors).toBeUndefined();

@@ -27,3 +29,3 @@ return [

type Query {
me: User!
me: User! @shareable
}

@@ -42,3 +44,3 @@

type Query {
me: User!
me: User! @shareable
}

@@ -230,3 +232,3 @@

id: ID!
subSubResponseValue: Int
subSubResponseValue: Int @shareable
}

@@ -331,3 +333,3 @@ `

type Value {
a: Int
a: Int @shareable
}

@@ -351,3 +353,3 @@

type Value {
a: Int
a: Int @shareable
b: Int

@@ -358,3 +360,3 @@ }

id: ID!
v: Value
v: Value @shareable
}

@@ -364,3 +366,3 @@

id: ID!
v: Value
v: Value @shareable
}

@@ -554,3 +556,3 @@ `

id: ID!
a: Int
a: Int @shareable
}

@@ -560,3 +562,3 @@

id: ID!
b: Int
b: Int @shareable
}

@@ -790,4 +792,4 @@ `

id: ID!
a: Int
b: Int
a: Int @shareable
b: Int @shareable
}

@@ -1076,2 +1078,206 @@ `

describe('fetch operation names', () => {
test('handle subgraph with - in the name', () => {
const subgraph1 = {
name: 'S1',
typeDefs: gql`
type Query {
t: T
}
type T @key(fields: "id") {
id: ID!
}
`
}
const subgraph2 = {
name: 'non-graphql-name',
typeDefs: gql`
type T @key(fields: "id") {
id: ID!
x: Int
}
`
}
const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
const operation = operationFromDocument(api, gql`
query myOp {
t {
x
}
}
`);
const plan = queryPlanner.buildQueryPlan(operation);
expect(plan).toMatchInlineSnapshot(`
QueryPlan {
Sequence {
Fetch(service: "S1") {
{
t {
__typename
id
}
}
},
Flatten(path: "t") {
Fetch(service: "non-graphql-name") {
{
... on T {
__typename
id
}
} =>
{
... on T {
x
}
}
},
},
},
}
`);
const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode).node as FetchNode;
expect(fetch.operation).toMatch(/^query myOp__non_graphql_name__1.*/i);
});
test('ensures sanitization applies repeatedly', () => {
const subgraph1 = {
name: 'S1',
typeDefs: gql`
type Query {
t: T
}
type T @key(fields: "id") {
id: ID!
}
`
}
const subgraph2 = {
name: 'a-na&me-with-plen&ty-replace*ments',
typeDefs: gql`
type T @key(fields: "id") {
id: ID!
x: Int
}
`
}
const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
const operation = operationFromDocument(api, gql`
query myOp {
t {
x
}
}
`);
const plan = queryPlanner.buildQueryPlan(operation);
expect(plan).toMatchInlineSnapshot(`
QueryPlan {
Sequence {
Fetch(service: "S1") {
{
t {
__typename
id
}
}
},
Flatten(path: "t") {
Fetch(service: "a-na&me-with-plen&ty-replace*ments") {
{
... on T {
__typename
id
}
} =>
{
... on T {
x
}
}
},
},
},
}
`);
const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode).node as FetchNode;
expect(fetch.operation).toMatch(/^query myOp__a_name_with_plenty_replacements__1.*/i);
});
test('handle very non-graph subgraph name', () => {
const subgraph1 = {
name: 'S1',
typeDefs: gql`
type Query {
t: T
}
type T @key(fields: "id") {
id: ID!
}
`
}
const subgraph2 = {
name: '42!',
typeDefs: gql`
type T @key(fields: "id") {
id: ID!
x: Int
}
`
}
const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
const operation = operationFromDocument(api, gql`
query myOp {
t {
x
}
}
`);
const plan = queryPlanner.buildQueryPlan(operation);
expect(plan).toMatchInlineSnapshot(`
QueryPlan {
Sequence {
Fetch(service: "S1") {
{
t {
__typename
id
}
}
},
Flatten(path: "t") {
Fetch(service: "42!") {
{
... on T {
__typename
id
}
} =>
{
... on T {
x
}
}
},
},
},
}
`);
const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode).node as FetchNode;
expect(fetch.operation).toMatch(/^query myOp___42__1.*/i);
});
});
test('Correctly handle case where there is too many plans to consider', () => {

@@ -1090,7 +1296,7 @@ // Creating realistic examples where there is too many plan to consider is not trivial, but creating unrealistic examples

type Query {
t: T
t: T @shareable
}
type T {
${fields.map((f) => `${f}: Int\n`)}
${fields.map((f) => `${f}: Int @shareable\n`)}
}

@@ -1097,0 +1303,0 @@ `;

@@ -34,2 +34,3 @@ import {

operation: string;
operationName: string | undefined;
operationKind: OperationTypeNode;

@@ -36,0 +37,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc