Skip to content

Commit

Permalink
Merge pull request #286 from social-native/feat/robust-value-transforms
Browse files Browse the repository at this point in the history
Leverage suggested value literal transforms for more kinds of filter values
  • Loading branch information
dearsaturn committed Jul 23, 2021
2 parents 4ea9709 + 6b4de75 commit 2b69225
Show file tree
Hide file tree
Showing 18 changed files with 18,557 additions and 8,302 deletions.
27 changes: 21 additions & 6 deletions __tests__/app_provider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Koa from 'koa';
import {ApolloServer, gql, IResolvers} from 'apollo-server-koa';
import {ApolloServer, gql} from 'apollo-server-koa';
import knex from 'knex';
import {ConnectionManager, IInputArgs} from '../src';
import {test as testConfig} from '../knexfile.sqlite';
Expand All @@ -19,6 +19,7 @@ const typeDefs = gql`
bio: String
age: Int
haircolor: String
isPetOwner: Boolean!
}
type QueryUserConnection implements IConnection {
Expand Down Expand Up @@ -52,6 +53,7 @@ interface IUserNode {
age: number;
haircolor: string;
bio: string;
isPetOwner: boolean;
}

type KnexQueryResult = Array<{[attributeName: string]: any}>;
Expand All @@ -60,7 +62,7 @@ type KnexQueryResult = Array<{[attributeName: string]: any}>;
const resolvers = {
...connectionResolvers,
Query: {
async users(_: any, {input: inputArgs}: {input: IInputArgs}) {
async users(_: any, input: IInputArgs) {
const queryBuilder = knexClient.from('mock');
// maps node types to sql column names
const attributeMap = {
Expand All @@ -70,10 +72,20 @@ const resolvers = {
age: 'age',
haircolor: 'haircolor',
lastname: 'lastname',
bio: 'bio'
bio: 'bio',
isPetOwner: 'is_pet_owner'
};

const nodeConnection = new ConnectionManager<IUserNode>(inputArgs, attributeMap);
const nodeConnection = new ConnectionManager<IUserNode>(input, attributeMap, {
resultOptions: {
nodeTransformer: node => {
return {
...node,
isPetOwner: node.is_pet_owner
};
}
}
});

const query = nodeConnection.createQuery(queryBuilder.clone()).select();
const result = (await query) as KnexQueryResult;
Expand All @@ -96,7 +108,7 @@ const resolvers = {
return null;
}
}
} as IResolvers;
};

const appProvider = () => {
const allTypeDefs = gql`
Expand All @@ -108,7 +120,10 @@ const appProvider = () => {
resolvers
});
const app = new Koa();
server.applyMiddleware({app});
server.start().then(() => {
server.applyMiddleware({app});
});

return app;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Input Union Type handles errors not using variables with compound filter 1`] = `
"{\\"errors\\":[{\\"message\\":\\"Expected type Filter, found {or: [{fields: \\\\\\"age\\\\\\", value: \\\\\\"41\\\\\\", operator: \\\\\\"=\\\\\\"}, {field: \\\\\\"age\\\\\\", value: \\\\\\"31\\\\\\", operator: \\\\\\"=\\\\\\"}, {and: [{field: \\\\\\"haircolor\\\\\\", value: \\\\\\"gray\\\\\\", operator: \\\\\\"=\\\\\\"}, {field: \\\\\\"age\\\\\\", value: \\\\\\"70\\\\\\", operator: \\\\\\"=\\\\\\"}, {not: [{field: \\\\\\"firstname\\\\\\", value: \\\\\\"Kenyon\\\\\\", operator: \\\\\\"=\\\\\\"}, {field: \\\\\\"firstname\\\\\\", value: \\\\\\"Nerissa\\\\\\", operator: \\\\\\"=\\\\\\"}]}]}]}; Filter should be composed of either: CompoundFilterScalar \`{and: [Filter], or: [Filter], not: [Filter]}\`, or FilterScalar \`{field: String, operator: String, value: IntString}\`\\",\\"locations\\":[{\\"line\\":4,\\"column\\":30}],\\"extensions\\":{\\"code\\":\\"GRAPHQL_VALIDATION_FAILED\\"}}]}
"{\\"errors\\":[{\\"message\\":\\"Expected type Filter, found {or: [{fields: \\\\\\"age\\\\\\", value: \\\\\\"41\\\\\\", operator: \\\\\\"=\\\\\\"}, {field: \\\\\\"age\\\\\\", value: \\\\\\"31\\\\\\", operator: \\\\\\"=\\\\\\"}, {and: [{field: \\\\\\"haircolor\\\\\\", value: \\\\\\"gray\\\\\\", operator: \\\\\\"=\\\\\\"}, {field: \\\\\\"age\\\\\\", value: \\\\\\"70\\\\\\", operator: \\\\\\"=\\\\\\"}, {not: [{field: \\\\\\"firstname\\\\\\", value: \\\\\\"Kenyon\\\\\\", operator: \\\\\\"=\\\\\\"}, {field: \\\\\\"firstname\\\\\\", value: \\\\\\"Nerissa\\\\\\", operator: \\\\\\"=\\\\\\"}]}]}]}.\\",\\"locations\\":[{\\"line\\":4,\\"column\\":30}],\\"extensions\\":{\\"code\\":\\"GRAPHQL_VALIDATION_FAILED\\"}}]}
"
`;

exports[`Input Union Type handles errors not using variables with single filter 1`] = `
"{\\"errors\\":[{\\"message\\":\\"Expected type Filter, found {fields: \\\\\\"haircolor\\\\\\", value: \\\\\\"gray\\\\\\", operator: \\\\\\"=\\\\\\"}; Filter should be composed of either: CompoundFilterScalar \`{and: [Filter], or: [Filter], not: [Filter]}\`, or FilterScalar \`{field: String, operator: String, value: IntString}\`\\",\\"locations\\":[{\\"line\\":4,\\"column\\":33}],\\"extensions\\":{\\"code\\":\\"GRAPHQL_VALIDATION_FAILED\\"}}]}
"{\\"errors\\":[{\\"message\\":\\"Expected type Filter, found {fields: \\\\\\"haircolor\\\\\\", value: \\\\\\"gray\\\\\\", operator: \\\\\\"=\\\\\\"}; Filter should be composed of either: CompoundFilterScalar \`{and: [Filter], or: [Filter], not: [Filter]}\`, or FilterScalar \`{field: String, operator: String, value: FilterValue}\`\\",\\"locations\\":[{\\"line\\":4,\\"column\\":33}],\\"extensions\\":{\\"code\\":\\"GRAPHQL_VALIDATION_FAILED\\"}}]}
"
`;

Expand Down
44 changes: 44 additions & 0 deletions __tests__/integration/coerce_string_value.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {coerceStringValue} from '../../src/coerce_string_value';

describe('coerceStringValue', function() {
it('coerces boolean strings', function() {
const tests: Array<[string, boolean]> = [
['true', true],
['TRUE', true],
['false', false],
['FALSE', false]
];

for (const [input, expectedOutput] of tests) {
expect(coerceStringValue(input)).toEqual(expectedOutput);
}
});

it('does not use auto trimming to coerce string to number', function() {
expect(coerceStringValue('1.24 cows')).toEqual('1.24 cows');
});

it('ignores empty strings', function() {
expect(coerceStringValue('')).toEqual('');
});

it('coerces float strings', function() {
expect(coerceStringValue('1.22334')).toEqual(1.22334);
});

it('coerces int strings', function() {
expect(coerceStringValue('5')).toEqual(5);
});

it('coerces null strings', function() {
for (const input of ['NULL', 'null', 'nUlL']) {
expect(coerceStringValue(input)).toBeNull();
}
});

it('preserves strings that are not coercible', function() {
const preserved = 'This is a really long sentence.';

expect(coerceStringValue(preserved)).toEqual(preserved);
});
});
86 changes: 80 additions & 6 deletions __tests__/integration/input_args.integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import knex from 'knex';
import {ConnectionManager, IInputArgs} from '../../src';
import {IUserNode, KnexQueryResult} from '../types';
import {ConnectionManager, IFilter, IInputArgs} from '../../src';
import {KnexQueryResult} from '../types';
import {rejectionOf, validateFieldIsOrderedAlphabetically} from '../utils';
import {test as testConfig} from '../../knexfile.sqlite';
const knexClient = knex(testConfig);
Expand All @@ -12,18 +12,38 @@ const attributeMap = {
age: 'age',
haircolor: 'haircolor',
lastname: 'lastname',
bio: 'bio'
bio: 'bio',
isPetOwner: 'is_pet_owner'
};

const createConnection = async (inputArgs: IInputArgs) => {
const queryBuilder = knexClient.queryBuilder().from('mock');
const connection = new ConnectionManager<IUserNode>(inputArgs, attributeMap);
const connection = new ConnectionManager<{
id: number;
username?: string;
firstname?: string;
age?: number;
haircolor?: string;
lastname?: string;
bio?: string;
is_pet_owner: 0 | 1;
}>(inputArgs, attributeMap);

connection.createQuery(queryBuilder);
const sql = queryBuilder.toString();
const result = ((await queryBuilder.select()) || []) as KnexQueryResult;
const result: KnexQueryResult = await queryBuilder;
connection.addResult(result);
const pageInfo = connection.pageInfo;
const edges = connection.edges;
const edges = connection.edges.map(edge => {
return {
...edge,
node: {
...edge.node,
isPetOwner: edge.node.is_pet_owner === 1
}
};
});

return {pageInfo, edges, sql};
};

Expand Down Expand Up @@ -418,4 +438,58 @@ describe('Input args with', () => {
expect(edges[0].node.id).toBe(9321);
});
});

describe('Boolean and boolean string filters', function() {
it('Can handle a stringified boolean', async () => {
const falseFilter: IFilter = {
field: 'isPetOwner',
operator: '=',
value: 'false'
};

const {edges: falseEdges} = await createConnection({filter: falseFilter});

for (const edge of falseEdges) {
expect(edge.node.isPetOwner).toBe(false);
}

const trueFilter: IFilter = {
field: 'isPetOwner',
operator: '=',
value: 'true'
};

const {edges: trueEdges} = await createConnection({filter: trueFilter});

for (const edge of trueEdges) {
expect(edge.node.isPetOwner).toBe(true);
}
});

it.only('Can handle a boolean value', async () => {
const falseFilter: IFilter = {
field: 'isPetOwner',
operator: '=',
value: false
};

const {edges: falseEdges} = await createConnection({filter: falseFilter});

for (const edge of falseEdges) {
expect(edge.node.isPetOwner).toBe(false);
}

const trueFilter: IFilter = {
field: 'isPetOwner',
operator: '=',
value: true
};

const {edges: trueEdges} = await createConnection({filter: trueFilter});

for (const edge of trueEdges) {
expect(edge.node.isPetOwner).toBe(true);
}
});
});
});
Loading

0 comments on commit 2b69225

Please sign in to comment.