UNPKG

ra-data-graphql-simple

Version:

A GraphQL simple data provider for react-admin

1,026 lines (975 loc) 31.1 kB
import { TypeKind, print } from 'graphql'; import { gql } from '@apollo/client'; import { GET_LIST, GET_ONE, GET_MANY, GET_MANY_REFERENCE, UPDATE, CREATE, DELETE, DELETE_MANY, UPDATE_MANY, } from 'ra-core'; import buildGqlQuery, { buildApolloArgs, buildArgs, buildFields, } from './buildGqlQuery'; describe('buildArgs', () => { it('returns an empty array when query does not have any arguments', () => { expect(buildArgs({ args: [] }, {})).toEqual([]); }); it('returns an array of args correctly filtered when query has arguments', () => { expect( print( buildArgs( { args: [{ name: 'foo' }, { name: 'bar' }] }, { foo: 'foo_value' } ) ) ).toEqual(['foo: $foo']); }); }); describe('buildApolloArgs', () => { it('returns an empty array when query does not have any arguments', () => { expect(print(buildApolloArgs({ args: [] }, {}))).toEqual([]); }); it('returns an array of args correctly filtered when query has arguments', () => { expect( print( buildApolloArgs( { args: [ { name: 'foo', type: { kind: TypeKind.NON_NULL, ofType: { kind: TypeKind.SCALAR, name: 'Int', }, }, }, { name: 'barId', type: { kind: TypeKind.SCALAR, name: 'ID' }, }, { name: 'barIds', type: { kind: TypeKind.LIST, ofType: { kind: TypeKind.NON_NULL, ofType: { kind: TypeKind.SCALAR, name: 'ID', }, }, }, }, { name: 'bar' }, ], }, { foo: 'foo_value', barId: 100, barIds: [101, 102] } ) ) ).toEqual(['$foo: Int!', '$barId: ID', '$barIds: [ID!]']); }); }); function buildGQLParamsWithSparseFieldsFactory() { const introspectionResults = { resources: [ { type: { name: 'resourceType', fields: [ { name: 'id', type: { kind: TypeKind.SCALAR, name: 'ID' }, }, { name: 'name', type: { kind: TypeKind.SCALAR, name: 'String' }, }, { name: 'foo', type: { kind: TypeKind.SCALAR, name: 'String' }, }, ], }, }, ], types: [ { name: 'linkedType', fields: [ { name: 'id', type: { kind: TypeKind.SCALAR, name: 'ID' }, }, { name: 'title', type: { kind: TypeKind.SCALAR, name: 'String' }, }, { name: 'nestedLink', type: { kind: TypeKind.OBJECT, name: 'nestedLinkedType', }, }, ], }, { name: 'nestedLinkedType', fields: [ { name: 'id', type: { kind: TypeKind.SCALAR, name: 'ID' }, }, { name: 'bar', type: { kind: TypeKind.SCALAR, name: 'String' }, }, ], }, ], }; const resource = { type: { fields: [ { type: { kind: TypeKind.SCALAR, name: 'ID' }, name: 'id' }, { type: { kind: TypeKind.SCALAR, name: 'String' }, name: 'address', }, { type: { kind: TypeKind.SCALAR, name: '_internalField' }, name: 'foo1', }, { type: { kind: TypeKind.OBJECT, name: 'linkedType' }, name: 'linked', }, { type: { kind: TypeKind.OBJECT, name: 'resourceType' }, name: 'resource', }, ], }, }; const queryType = { name: 'allCommand', args: [ { name: 'foo', type: { kind: TypeKind.NON_NULL, ofType: { kind: TypeKind.SCALAR, name: 'Int' }, }, }, ], }; const params = { foo: 'foo_value', meta: { sparseFields: [ 'address', { linked: ['title'] }, { resource: ['foo', 'name'] }, ], }, }; return { introspectionResults, queryType, params, resource, }; } describe('buildFields', () => { it('returns an object with the fields to retrieve', () => { const introspectionResults = { resources: [ { type: { name: 'resourceType', fields: [ { name: 'id', type: { kind: TypeKind.SCALAR, name: 'ID' }, }, ], }, }, ], types: [ { name: 'linkedType', fields: [ { name: 'id', type: { kind: TypeKind.SCALAR, name: 'ID' }, }, ], }, ], }; const fields = [ { type: { kind: TypeKind.SCALAR, name: 'ID' }, name: 'id' }, { type: { kind: TypeKind.SCALAR, name: '_internalField' }, name: 'foo1', }, { type: { kind: TypeKind.OBJECT, name: 'linkedType' }, name: 'linked', }, { type: { kind: TypeKind.OBJECT, name: 'resourceType' }, name: 'resource', }, ]; expect(print(buildFields(introspectionResults)(fields))).toEqual([ 'id', `linked { id }`, `resource { id }`, ]); }); describe('with sparse fields', () => { it('returns an object with the fields to retrieve', () => { const { introspectionResults, resource, params } = buildGQLParamsWithSparseFieldsFactory(); // nested sparse params params.meta.sparseFields[1].linked.push({ nestedLink: ['bar'] }); expect( print( buildFields(introspectionResults)( resource.type.fields, params.meta.sparseFields ) ) ).toEqual([ 'address', `linked { title nestedLink { bar } }`, `resource { foo name }`, ]); }); it('throws an error when sparse fields is requested but empty', () => { const { introspectionResults, resource } = buildGQLParamsWithSparseFieldsFactory(); expect(() => buildFields(introspectionResults)(resource.type.fields, []) ).toThrowError( "Empty sparse fields. Specify at least one field or remove the 'sparseFields' param" ); }); it('throws an error when requested sparse fields are not available', () => { const { introspectionResults, resource } = buildGQLParamsWithSparseFieldsFactory(); expect(() => buildFields(introspectionResults)(resource.type.fields, [ 'unavailbleField', ]) ).toThrowError( "Requested sparse fields not found. Ensure sparse fields are available in the resource's type" ); }); }); }); describe('buildFieldsWithCircularDependency', () => { it('returns an object with the fields to retrieve', () => { const introspectionResults = { resources: [ { type: { name: 'resourceType', fields: [ { name: 'id', type: { kind: TypeKind.SCALAR, name: 'ID' }, }, ], }, }, ], types: [ { name: 'linkedType', fields: [ { name: 'id', type: { kind: TypeKind.SCALAR, name: 'ID' }, }, { name: 'child', type: { kind: TypeKind.OBJECT, name: 'linkedType' }, }, ], }, ], }; const fields = [ { type: { kind: TypeKind.SCALAR, name: 'ID' }, name: 'id' }, { type: { kind: TypeKind.SCALAR, name: '_internalField' }, name: 'foo1', }, { type: { kind: TypeKind.OBJECT, name: 'linkedType' }, name: 'linked', }, { type: { kind: TypeKind.OBJECT, name: 'resourceType' }, name: 'resource', }, ]; expect(print(buildFields(introspectionResults)(fields))).toEqual([ 'id', `linked { id }`, `resource { id }`, ]); }); }); describe('buildFieldsWithSameType', () => { it('returns an object with the fields to retrieve', () => { const introspectionResults = { resources: [ { type: { name: 'resourceType', fields: [ { name: 'id', type: { kind: TypeKind.SCALAR, name: 'ID' }, }, ], }, }, ], types: [ { name: 'linkedType', fields: [ { name: 'id', type: { kind: TypeKind.SCALAR, name: 'ID' }, }, ], }, ], }; const fields = [ { type: { kind: TypeKind.SCALAR, name: 'ID' }, name: 'id' }, { type: { kind: TypeKind.SCALAR, name: '_internalField' }, name: 'foo1', }, { type: { kind: TypeKind.OBJECT, name: 'linkedType' }, name: 'linked', }, { type: { kind: TypeKind.OBJECT, name: 'linkedType' }, name: 'anotherLinked', }, { type: { kind: TypeKind.OBJECT, name: 'resourceType' }, name: 'resource', }, ]; expect(print(buildFields(introspectionResults)(fields))).toEqual([ 'id', `linked { id }`, `anotherLinked { id }`, `resource { id }`, ]); }); }); describe('buildGqlQuery', () => { const introspectionResults = { resources: [ { type: { name: 'resourceType', fields: [ { name: 'id', type: { kind: TypeKind.SCALAR, name: 'ID' }, }, ], }, }, ], types: [ { name: 'linkedType', fields: [ { name: 'foo', type: { kind: TypeKind.SCALAR, name: 'bar' }, }, ], }, ], }; const resource = { type: { fields: [ { type: { kind: TypeKind.SCALAR, name: '' }, name: 'foo' }, { type: { kind: TypeKind.SCALAR, name: '_foo' }, name: 'foo1' }, { type: { kind: TypeKind.OBJECT, name: 'linkedType' }, name: 'linked', }, { type: { kind: TypeKind.OBJECT, name: 'resourceType' }, name: 'resource', }, ], }, }; const queryType = { name: 'allCommand', args: [ { name: 'foo', type: { kind: TypeKind.NON_NULL, ofType: { kind: TypeKind.SCALAR, name: 'Int' }, }, }, { name: 'barId', type: { kind: TypeKind.SCALAR }, }, { name: 'barIds', type: { kind: TypeKind.SCALAR }, }, { name: 'bar' }, ], }; const queryTypeDeleteMany = { name: 'deleteCommands', args: [ { name: 'ids', type: { kind: TypeKind.LIST, ofType: { kind: TypeKind.NON_NULL, ofType: { kind: TypeKind.SCALAR, name: 'ID', }, }, }, }, ], }; const queryTypeUpdateMany = { name: 'updateCommands', args: [ { name: 'ids', type: { kind: TypeKind.LIST, ofType: { kind: TypeKind.NON_NULL, ofType: { kind: TypeKind.SCALAR, name: 'ID', }, }, }, }, { name: 'data', type: { kind: TypeKind.OBJECT, name: 'CommandType' }, }, ], }; const params = { foo: 'foo_value' }; describe('GET_LIST', () => { it('returns the correct query', () => { expect( print( buildGqlQuery(introspectionResults)( resource, GET_LIST, queryType, params ) ) ).toEqual( print(gql` query allCommand($foo: Int!) { items: allCommand(foo: $foo) { foo linked { foo } resource { id } } total: _allCommandMeta(foo: $foo) { count } } `) ); }); it('returns the correct query with sparse fields', () => { const { introspectionResults, params, queryType, resource } = buildGQLParamsWithSparseFieldsFactory(); expect( print( buildGqlQuery(introspectionResults)( resource, GET_LIST, queryType, params ) ) ).toEqual( print(gql` query allCommand($foo: Int!) { items: allCommand(foo: $foo) { address linked { title } resource { foo name } } total: _allCommandMeta(foo: $foo) { count } } `) ); }); }); describe('GET_MANY', () => { it('returns the correct query', () => { expect( print( buildGqlQuery(introspectionResults)( resource, GET_MANY, queryType, params ) ) ).toEqual( print(gql` query allCommand($foo: Int!) { items: allCommand(foo: $foo) { foo linked { foo } resource { id } } total: _allCommandMeta(foo: $foo) { count } } `) ); }); it('returns the correct query with sparse fields', () => { const { introspectionResults, params, queryType, resource } = buildGQLParamsWithSparseFieldsFactory(); expect( print( buildGqlQuery(introspectionResults)( resource, GET_MANY, queryType, params ) ) ).toEqual( print(gql` query allCommand($foo: Int!) { items: allCommand(foo: $foo) { address linked { title } resource { foo name } } total: _allCommandMeta(foo: $foo) { count } } `) ); }); }); describe('GET_MANY_REFERENCE', () => { it('returns the correct query', () => { expect( print( buildGqlQuery(introspectionResults)( resource, GET_MANY_REFERENCE, queryType, params ) ) ).toEqual( print(gql` query allCommand($foo: Int!) { items: allCommand(foo: $foo) { foo linked { foo } resource { id } } total: _allCommandMeta(foo: $foo) { count } } `) ); }); it('returns the correct query with sparse fields', () => { const { introspectionResults, params, queryType, resource } = buildGQLParamsWithSparseFieldsFactory(); expect( print( buildGqlQuery(introspectionResults)( resource, GET_MANY_REFERENCE, queryType, params ) ) ).toEqual( print(gql` query allCommand($foo: Int!) { items: allCommand(foo: $foo) { address linked { title } resource { foo name } } total: _allCommandMeta(foo: $foo) { count } } `) ); }); }); describe('GET_ONE', () => { it('returns the correct query', () => { expect( print( buildGqlQuery(introspectionResults)( resource, GET_ONE, { ...queryType, name: 'getCommand' }, params ) ) ).toEqual( print(gql` query getCommand($foo: Int!) { data: getCommand(foo: $foo) { foo linked { foo } resource { id } } } `) ); }); it('returns the correct query with sparse fields', () => { const { introspectionResults, params, queryType, resource } = buildGQLParamsWithSparseFieldsFactory(); expect( print( buildGqlQuery(introspectionResults)( resource, GET_ONE, { ...queryType, name: 'getCommand' }, params ) ) ).toEqual( print(gql` query getCommand($foo: Int!) { data: getCommand(foo: $foo) { address linked { title } resource { foo name } } } `) ); }); }); describe('UPDATE', () => { it('returns the correct query', () => { expect( print( buildGqlQuery(introspectionResults)( resource, UPDATE, { ...queryType, name: 'updateCommand' }, params ) ) ).toEqual( print(gql` mutation updateCommand($foo: Int!) { data: updateCommand(foo: $foo) { foo linked { foo } resource { id } } } `) ); }); it('returns the correct query with sparse fields', () => { const { introspectionResults, params, queryType, resource } = buildGQLParamsWithSparseFieldsFactory(); expect( print( buildGqlQuery(introspectionResults)( resource, UPDATE, { ...queryType, name: 'updateCommand' }, params ) ) ).toEqual( print(gql` mutation updateCommand($foo: Int!) { data: updateCommand(foo: $foo) { address linked { title } resource { foo name } } } `) ); }); }); describe('CREATE', () => { it('returns the correct query', () => { expect( print( buildGqlQuery(introspectionResults)( resource, CREATE, { ...queryType, name: 'createCommand' }, params ) ) ).toEqual( print(gql` mutation createCommand($foo: Int!) { data: createCommand(foo: $foo) { foo linked { foo } resource { id } } } `) ); }); it('returns the correct query with sparse fields', () => { const { introspectionResults, params, queryType, resource } = buildGQLParamsWithSparseFieldsFactory(); expect( print( buildGqlQuery(introspectionResults)( resource, CREATE, { ...queryType, name: 'createCommand' }, params ) ) ).toEqual( print(gql` mutation createCommand($foo: Int!) { data: createCommand(foo: $foo) { address linked { title } resource { foo name } } } `) ); }); }); describe('DELETE', () => { it('returns the correct query', () => { expect( print( buildGqlQuery(introspectionResults)( resource, DELETE, { ...queryType, name: 'deleteCommand' }, params ) ) ).toEqual( print(gql` mutation deleteCommand($foo: Int!) { data: deleteCommand(foo: $foo) { foo linked { foo } resource { id } } } `) ); }); it('returns the correct query with sparse fields', () => { const { introspectionResults, params, queryType, resource } = buildGQLParamsWithSparseFieldsFactory(); expect( print( buildGqlQuery(introspectionResults)( resource, DELETE, { ...queryType, name: 'deleteCommand' }, params ) ) ).toEqual( print(gql` mutation deleteCommand($foo: Int!) { data: deleteCommand(foo: $foo) { address linked { title } resource { foo name } } } `) ); }); }); it('returns the correct query for DELETE_MANY', () => { expect( print( buildGqlQuery(introspectionResults)( resource, DELETE_MANY, queryTypeDeleteMany, { ids: [1, 2, 3] } ) ) ).toEqual( `mutation deleteCommands($ids: [ID!]) { data: deleteCommands(ids: $ids) { ids } } ` ); }); it('returns the correct query for UPDATE_MANY', () => { expect( print( buildGqlQuery(introspectionResults)( resource, UPDATE_MANY, queryTypeUpdateMany, { ids: [1, 2, 3], data: params, } ) ) ).toEqual( `mutation updateCommands($ids: [ID!], $data: CommandType) { data: updateCommands(ids: $ids, data: $data) { ids } } ` ); }); });