Skip to content

Commit

Permalink
Merge pull request #50 from social-native/feat/add-is-null
Browse files Browse the repository at this point in the history
Handle "is null" filter
  • Loading branch information
dearsaturn committed May 18, 2020
2 parents 711d25e + dd6dc55 commit 2a0d7ed
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 93 deletions.
15 changes: 15 additions & 0 deletions __tests__/integration/input_args.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,21 @@ describe('Input args with', () => {
});
});

describe('Filters using suggested value literal transforms', () => {
it.skip('Transforms "null" value to real null', async () => {
const filter = {field: 'lastname', operator: '=', value: 'null'};
const {pageInfo, edges} = await createConnection({
first: 30,
filter,
orderBy: 'lastname',
orderDir: 'desc'
});
expect(pageInfo.hasNextPage).toBe(true); // there are 32 people with no last name
expect(pageInfo.hasPreviousPage).toBe(false);
expect(edges.length).toBe(30); // we selected 30 people
});
});

describe('Filters that return no results', () => {
it('Can be handled properly', async () => {
const filter = {
Expand Down
7 changes: 2 additions & 5 deletions db/seeds/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,6 @@ const remainingRows = Array(usernames.length)
const rows = [...finalCaseA, ...finalCaseB, ...finalCaseC, ...remainingRows];

export async function seed(knex: Knex) {
return knex('mock')
.del()
.then(() => {
return knex.batchInsert('mock', rows, 100);
});
await knex('mock').del();
await knex.batchInsert('mock', rows, 100);
}
33 changes: 30 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "graphql-connections",
"version": "6.0.0",
"version": "7.0.0",
"description": "Build and handle Relay-like GraphQL connections using a Knex query builder",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
Expand All @@ -14,7 +14,7 @@
"dev:mysql": "nodemon -e ts -w ./dev -x npm run ts-node-start:mysql",
"ts-node-start:mysql": "node ./node_modules/.bin/ts-node --project tsconfig.dev.json -r tsconfig-paths/register dev/dev.mysql.ts",
"clean": "rm -rf dist",
"test": "NODE_ENV=test npm run migrate:sqlite:latest && NODE_ENV=test npm run seed:sqlite:run && jest",
"test": "rimraf ./db/test.sqlite3 && NODE_ENV=test npm run migrate:sqlite:latest && NODE_ENV=test npm run seed:sqlite:run && jest",
"test:watch": "npm run test -- --watchAll --runInBand",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -- --watch",
Expand Down Expand Up @@ -75,6 +75,7 @@
"mysql2": "^1.6.5",
"nodemon": "^1.18.10",
"prettier": "^1.16.4",
"rimraf": "^3.0.2",
"rollup": "^1.2.2",
"rollup-plugin-typescript2": "^0.21.1",
"sqlite3": "^4.1.1",
Expand Down
2 changes: 1 addition & 1 deletion src/connection_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import QueryResult from './query_result';

type KnexQueryResult = Array<{[attributeName: string]: any}>;

interface IConnectionManagerOptions<CursorObj, Node> {
export interface IConnectionManagerOptions<CursorObj, Node> {
contextOptions?: IQueryContextOptions<CursorObj>;
resultOptions?: IQueryResultOptions<CursorObj, Node>;
builderOptions?: QueryBuilderOptions;
Expand Down
2 changes: 1 addition & 1 deletion src/graphql_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
GraphQLScalarType,
GraphQLInt
} from 'graphql';
import InputUnionType from 'input_union_type';
import InputUnionType from './input_union_type';

const compoundFilterScalar = new GraphQLInputObjectType({
name: 'CompoundFilterScalar',
Expand Down
54 changes: 45 additions & 9 deletions src/query_builder/knex_base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import QueryContext from '../query_context';
import {QueryBuilder as Knex} from 'knex';
import {QueryBuilder as Knex, Where, QueryBuilder} from 'knex';
import {
IFilterMap,
IInAttributeMap,
Expand Down Expand Up @@ -31,6 +31,7 @@ export default class KnexQueryBuilder implements IQueryBuilder<Knex> {
protected queryContext: QueryContext;
protected attributeMap: IInAttributeMap;
protected filterMap: IFilterMap;
protected useSuggestedValueLiteralTransforms: boolean;
protected filterTransformer: NonNullable<IKnexQueryBuilderOptions['filterTransformer']>;

constructor(
Expand All @@ -40,6 +41,10 @@ export default class KnexQueryBuilder implements IQueryBuilder<Knex> {
) {
this.queryContext = queryContext;
this.attributeMap = attributeMap;
/** Default to true */
this.useSuggestedValueLiteralTransforms = !(
options.useSuggestedValueLiteralTransforms === false
);
this.filterMap = options.filterMap || defaultFilterMap;
this.filterTransformer = options.filterTransformer || defaultFilterTransformer;

Expand Down Expand Up @@ -98,7 +103,7 @@ export default class KnexQueryBuilder implements IQueryBuilder<Knex> {
}

private computeFilterOperator(operator: string) {
const mappedField = this.filterMap[operator];
const mappedField = this.filterMap[operator.toLowerCase()];
if (mappedField) {
return mappedField;
}
Expand All @@ -108,22 +113,53 @@ export default class KnexQueryBuilder implements IQueryBuilder<Knex> {
);
}

private filterArgs(filter: IFilter): [string, string, string] {
const f = this.filterTransformer(filter);
return [this.computeFilterField(f.field), this.computeFilterOperator(f.operator), f.value];
// [string, string, string | number | null]
// tslint:disable-next-line: cyclomatic-complexity
private filterArgs(filter: IFilter) {
const transformedFilter = this.filterTransformer(filter);

if (
this.useSuggestedValueLiteralTransforms &&
transformedFilter.operator.toLowerCase() === '=' &&
transformedFilter.value.toLowerCase() === 'null'
) {
return [
(builder: QueryBuilder) => {
builder.whereNull(transformedFilter.field);
}
];
}

if (
this.useSuggestedValueLiteralTransforms &&
transformedFilter.operator.toLowerCase() === '<>' &&
transformedFilter.value.toLowerCase() === 'null'
) {
return [
(builder: QueryBuilder) => {
builder.whereNotNull(transformedFilter.field);
}
];
}

return [
this.computeFilterField(transformedFilter.field),
this.computeFilterOperator(transformedFilter.operator),
transformedFilter.value
];
}

private addFilterRecursively(filter: IInputFilter, queryBuilder: Knex) {
if (isFilter(filter)) {
queryBuilder.where(...this.filterArgs(filter as IFilter));
queryBuilder.where(...(this.filterArgs(filter) as Parameters<Where>));
return queryBuilder;
}

// tslint:disable-next-line
if (filter.and && filter.and.length > 0) {
filter.and.forEach(f => {
if (isFilter(f)) {
queryBuilder.andWhere(...this.filterArgs(f));
queryBuilder.andWhere(...(this.filterArgs(f) as Parameters<Where>));
} else {
queryBuilder.andWhere(k => this.addFilterRecursively(f, k));
}
Expand All @@ -133,7 +169,7 @@ export default class KnexQueryBuilder implements IQueryBuilder<Knex> {
if (filter.or && filter.or.length > 0) {
filter.or.forEach(f => {
if (isFilter(f)) {
queryBuilder.orWhere(...this.filterArgs(f));
queryBuilder.orWhere(...(this.filterArgs(f) as Parameters<Where>));
} else {
queryBuilder.orWhere(k => this.addFilterRecursively(f, k));
}
Expand All @@ -143,7 +179,7 @@ export default class KnexQueryBuilder implements IQueryBuilder<Knex> {
if (filter.not && filter.not.length > 0) {
filter.not.forEach(f => {
if (isFilter(f)) {
queryBuilder.andWhereNot(...this.filterArgs(f));
queryBuilder.andWhereNot(...(this.filterArgs(f) as Parameters<Where>));
} else {
queryBuilder.andWhereNot(k => this.addFilterRecursively(f, k));
}
Expand Down
2 changes: 1 addition & 1 deletion src/query_result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
NodeTransformer,
IQueryResultOptions
} from './types';
import {CursorEncoder} from 'index';
import {CursorEncoder} from './index';

/**
* QueryResult
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,13 @@ export type QueryBuilderOptions = IKnexQueryBuilderOptions | IKnexMySQLQueryBuil
export interface IKnexQueryBuilderOptions {
filterMap?: {[operator: string]: string};
filterTransformer?: (filter: IFilter) => IFilter;
useSuggestedValueLiteralTransforms?: boolean;
}

export interface IKnexMySQLQueryBuilderOptions extends IKnexQueryBuilderOptions {
filterMap?: {[operator: string]: string};
filterTransformer?: (filter: IFilter) => IFilter;
useSuggestedValueLiteralTransforms?: boolean;
searchColumns?: string[];
searchModifier?:
| 'IN NATURAL LANGUAGE MODE'
Expand Down
Loading

0 comments on commit 2a0d7ed

Please sign in to comment.