import gql from 'graphql-tag'; import { execute } from '../execution-utils'; import { astSerializer, queryPlanSerializer } from '../../snapshotSerializers'; expect.addSnapshotSerializer(astSerializer); expect.addSnapshotSerializer(queryPlanSerializer); it('handles an abstract type from the base service', async () => { const query = `#graphql query GetProduct($upc: String!) { product(upc: $upc) { upc name price } } `; const upc = '1'; const { data, queryPlan } = await execute({ query, variables: { upc }, }); expect(data).toEqual({ product: { upc, name: 'Table', price: '899', }, }); expect(queryPlan).toCallService('product'); expect(queryPlan).toMatchInlineSnapshot(` QueryPlan { Sequence { Fetch(service: "product") { { product(upc: $upc) { __typename ... on Book { upc __typename isbn price } ... on Furniture { upc name price } } } }, Flatten(path: "product") { Fetch(service: "books") { { ... on Book { __typename isbn } } => { ... on Book { __typename isbn title year } } }, }, Flatten(path: "product") { Fetch(service: "product") { { ... on Book { __typename isbn title year } } => { ... on Book { name } } }, }, }, } `); }); it('can request fields on extended interfaces', async () => { const query = `#graphql query GetProduct($upc: String!) { product(upc: $upc) { inStock } } `; const upc = '1'; const { data, queryPlan } = await execute({ query, variables: { upc }, }); expect(data).toEqual({ product: { inStock: true } }); expect(queryPlan).toCallService('product'); expect(queryPlan).toCallService('inventory'); expect(queryPlan).toMatchInlineSnapshot(` QueryPlan { Sequence { Fetch(service: "product") { { product(upc: $upc) { __typename ... on Book { __typename isbn } ... on Furniture { __typename sku } } } }, Flatten(path: "product") { Fetch(service: "inventory") { { ... on Book { __typename isbn } ... on Furniture { __typename sku } } => { ... on Book { inStock } ... on Furniture { inStock } } }, }, }, } `); }); it('can request fields on extended types that implement an interface', async () => { const query = `#graphql query GetProduct($upc: String!) { product(upc: $upc) { inStock ... on Furniture { isHeavy } } } `; const upc = '1'; const { data, queryPlan } = await execute({ query, variables: { upc }, }); expect(data).toEqual({ product: { inStock: true, isHeavy: false } }); expect(queryPlan).toCallService('product'); expect(queryPlan).toCallService('inventory'); expect(queryPlan).toMatchInlineSnapshot(` QueryPlan { Sequence { Fetch(service: "product") { { product(upc: $upc) { __typename ... on Book { __typename isbn } ... on Furniture { __typename sku } } } }, Flatten(path: "product") { Fetch(service: "inventory") { { ... on Book { __typename isbn } ... on Furniture { __typename sku } } => { ... on Book { inStock } ... on Furniture { inStock isHeavy } } }, }, }, } `); }); it('prunes unfilled type conditions', async () => { const query = `#graphql query GetProduct($upc: String!) { product(upc: $upc) { inStock ... on Furniture { isHeavy } ... on Book { isCheckedOut } } } `; const upc = '1'; const { data, queryPlan } = await execute({ query, variables: { upc }, }); expect(data).toEqual({ product: { inStock: true, isHeavy: false } }); expect(queryPlan).toCallService('product'); expect(queryPlan).toCallService('inventory'); expect(queryPlan).toMatchInlineSnapshot(` QueryPlan { Sequence { Fetch(service: "product") { { product(upc: $upc) { __typename ... on Book { __typename isbn } ... on Furniture { __typename sku } } } }, Flatten(path: "product") { Fetch(service: "inventory") { { ... on Book { __typename isbn } ... on Furniture { __typename sku } } => { ... on Book { inStock isCheckedOut } ... on Furniture { inStock isHeavy } } }, }, }, } `); }); it('fetches interfaces returned from other services', async () => { const query = `#graphql query GetUserAndProducts { me { reviews { product { price ... on Book { title } } } } } `; const { data, queryPlan } = await execute({ query, }); expect(data).toEqual({ me: { reviews: [ { product: { price: '899' } }, { product: { price: '1299' } }, { product: { price: '49', title: 'Design Patterns' } }, ], }, }); expect(queryPlan).toCallService('accounts'); expect(queryPlan).toCallService('reviews'); expect(queryPlan).toCallService('product'); expect(queryPlan).toMatchInlineSnapshot(` QueryPlan { Sequence { Fetch(service: "accounts") { { me { __typename id } } }, Flatten(path: "me") { Fetch(service: "reviews") { { ... on User { __typename id } } => { ... on User { reviews { product { __typename ... on Book { __typename isbn } ... on Furniture { __typename upc } } } } } }, }, Parallel { Flatten(path: "me.reviews.@.product") { Fetch(service: "product") { { ... on Book { __typename isbn } ... on Furniture { __typename upc } } => { ... on Book { price } ... on Furniture { price } } }, }, Flatten(path: "me.reviews.@.product") { Fetch(service: "books") { { ... on Book { __typename isbn } } => { ... on Book { title } } }, }, }, }, } `); }); it('fetches composite fields from a foreign type casted to an interface [@provides field]', async () => { const query = `#graphql query GetUserAndProducts { me { reviews { product { price ... on Book { name } } } } } `; const { data, queryPlan } = await execute({ query, }); expect(data).toEqual({ me: { reviews: [ { product: { price: '899' } }, { product: { price: '1299' } }, { product: { price: '49', name: 'Design Patterns (1995)' } }, ], }, }); expect(queryPlan).toCallService('accounts'); expect(queryPlan).toCallService('reviews'); expect(queryPlan).toCallService('product'); expect(queryPlan).toMatchInlineSnapshot(` QueryPlan { Sequence { Fetch(service: "accounts") { { me { __typename id } } }, Flatten(path: "me") { Fetch(service: "reviews") { { ... on User { __typename id } } => { ... on User { reviews { product { __typename ... on Book { __typename isbn } ... on Furniture { __typename upc } } } } } }, }, Parallel { Flatten(path: "me.reviews.@.product") { Fetch(service: "product") { { ... on Book { __typename isbn } ... on Furniture { __typename upc } } => { ... on Book { price } ... on Furniture { price } } }, }, Sequence { Flatten(path: "me.reviews.@.product") { Fetch(service: "books") { { ... on Book { __typename isbn } } => { ... on Book { __typename isbn title year } } }, }, Flatten(path: "me.reviews.@.product") { Fetch(service: "product") { { ... on Book { __typename isbn title year } } => { ... on Book { name } } }, }, }, }, }, } `); }); it('allows for extending an interface from another service with fields', async () => { const query = `#graphql query GetProduct($upc: String!) { product(upc: $upc) { reviews { body } } } `; const upc = '1'; const { data, queryPlan } = await execute({ query, variables: { upc }, }); expect(data).toEqual({ product: { reviews: [{ body: 'Love it!' }, { body: 'Prefer something else.' }], }, }); expect(queryPlan).toCallService('reviews'); expect(queryPlan).toCallService('product'); expect(queryPlan).toMatchInlineSnapshot(` QueryPlan { Sequence { Fetch(service: "product") { { product(upc: $upc) { __typename ... on Book { __typename isbn } ... on Furniture { __typename upc } } } }, Flatten(path: "product") { Fetch(service: "reviews") { { ... on Book { __typename isbn } ... on Furniture { __typename upc } } => { ... on Book { reviews { body } } ... on Furniture { reviews { body } } } }, }, }, } `); }); describe('unions', () => { it('handles unions from the same service', async () => { const query = `#graphql query GetUserAndProducts { me { reviews { product { price ... on Furniture { brand { ... on Ikea { asile } ... on Amazon { referrer } } } } } } } `; const { data, queryPlan } = await execute({ query, }); expect(data).toEqual({ me: { reviews: [ { product: { price: '899', brand: { asile: 10 } } }, { product: { price: '1299', brand: { referrer: 'https://canopy.co' }, }, }, { product: { price: '49' } }, ], }, }); expect(queryPlan).toCallService('accounts'); expect(queryPlan).toCallService('reviews'); expect(queryPlan).toCallService('product'); expect(queryPlan).toMatchInlineSnapshot(` QueryPlan { Sequence { Fetch(service: "accounts") { { me { __typename id } } }, Flatten(path: "me") { Fetch(service: "reviews") { { ... on User { __typename id } } => { ... on User { reviews { product { __typename ... on Book { __typename isbn } ... on Furniture { __typename upc } } } } } }, }, Flatten(path: "me.reviews.@.product") { Fetch(service: "product") { { ... on Book { __typename isbn } ... on Furniture { __typename upc } } => { ... on Book { price } ... on Furniture { price brand { __typename ... on Ikea { asile } ... on Amazon { referrer } } } } }, }, }, } `); }); it("doesn't expand interfaces with inline type conditions if all possibilities are fufilled by one service", async () => { const query = `#graphql query GetProducts { topProducts { name } } `; const { queryPlan, errors } = await execute({ query }, [ { name: 'products', typeDefs: gql` extend type Query { topProducts: [Product] } interface Product { name: String } type Shoe implements Product { name: String } type Car implements Product { name: String } `, }, ]); expect(errors).toBeUndefined(); expect(queryPlan).toMatchInlineSnapshot(` QueryPlan { Fetch(service: "products") { { topProducts { __typename name } } }, } `); }); // FIXME: turn back on when extending unions is supported in composition it.todo('fetches unions across services'); // async () => { // const query = gql` // query GetUserAndProducts { // me { // account { // ... on LibraryAccount { // library { // name // } // } // ... on SMSAccount { // number // } // } // } // } // `; // const { data, queryPlan } = await execute( // { // query, // }, // ); // expect(data).toEqual({ // me: { // account: { // library: { // name: 'NYC Public Library', // }, // }, // }, // }); // expect(queryPlan).toCallService('accounts'); // expect(queryPlan).toCallService('books'); // }); });