From 9e7246730329234a51e94cc64f56e6873bf05da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Franciszek=20Cies=CC=81lak?= Date: Thu, 7 Nov 2019 20:06:20 +0100 Subject: [PATCH] fix(resolvers): rename values to resolvers --- README.md | 27 ++++++++------- src/class.ts | 20 ++++++------ src/index.ts | 80 ++++++++++++++++++++++----------------------- tests/class.test.ts | 6 ++-- tests/dist.test.ts | 6 ++-- tests/index.test.ts | 6 ++-- 6 files changed, 74 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 8abc674..b551f06 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ By default, this parser assumes a simple structural convention for writing your 2. Use nested objects for embedded queries, like so: - `{ nested: { level1: { level2: { _NE: 10 } } } }` will parse to: `{ 'nested.level1.level2': { $ne: 10 } }` + `{ nested: { level1: { level2: { _NE: 10 } } } }` will parse to: + `{ 'nested.level1.level2': { $ne: 10 } }` ## Usage: @@ -44,7 +45,7 @@ import GQLMongoQuery from 'graphql-mongo-query' // Example arguments: const query = { _OR: [{ num: 10 }, { nested: {property: 'X'} }] } -const parser = GQLMongoQuery(, , ) +const parser = GQLMongoQuery(, , ) const MongoFilters = parser(query) // MongoFilters will equal to: @@ -58,7 +59,7 @@ import GQLMongoQuery from 'graphql-mongo-query/class' // Example arguments: const query = { _OR: [{ num: 10 }, { nested: {property: 'X'} }] } -const parser = new GQLMongoQuery(, , ) +const parser = new GQLMongoQuery(, , ) const MongoFilters = parser.buildFiltersquerys) // MongoFilters will equal to: @@ -73,7 +74,7 @@ const MongoFilters = parser.buildFiltersquerys) #### `keywords` (optional) -Maps the query keywords to mongo keywords. +Maps the query keywords to mongo keywords. Every key in this object will be replaced by corresponding value. ```javascript // Defaults: @@ -107,17 +108,17 @@ Maps the query keywords to mongo keywords. } ``` -#### `values` (optional) +#### `resolvers` (optional) -An object mapping specified query keys to functions that will resolve their value. +An object mapping specified query keys to custom resolver functions that will return a new key and value. These resolver functions take `parent` object as the only parameter, and should return a value that will replace that parent. Parent object is a single single `{key: value}` pair. -The parser will iterate through a query, and when finding a key that matches, it will replace the entire `{key: value}` pair with the result of the function. +The parser will iterate through a query, and when finding a key that matches, it will replace the entire `{key: value}` pair with the result of the resolver function. ```typescript // Examples: -const values = { +const resolvers = { test1(parent) { return {test1: !!parent.test1} }, @@ -140,7 +141,7 @@ const values = { ``` #### `merge` (optional, default: `true`) -If set to true, `keywords` and `values` from options will be merged with defaults. Otherwise they will overwrite the defaults. +If set to true, `keywords` and `resolvers` from options will be merged with defaults. Otherwise they will overwrite the defaults. ## Examples: @@ -149,13 +150,15 @@ For examples checkout the [tests](https://github.com/jfcieslak/graphql-mongo-que An example of a complex Input filter and it’s parsed value: ```javascript -// arg received from graphQL input: -const values = { +// resolvers option object +const resolvers = { dateField(parent) { parent.dateField = new Date(parent.dateField) return parent } } + +// query received from graphQL input: const query = { _OR: [ { field1: { _NE: 'not me' } }, @@ -166,7 +169,7 @@ const query = { } // Parsed filter: -const filter = GQLMongoQuery(null, values)querys) +const filter = GQLMongoQuery(null, resolvers)querys) expect(filter).toEqual({ $or: [ { field1: { $ne: 'not me' } }, diff --git a/src/class.ts b/src/class.ts index d4e08e5..5fdf490 100644 --- a/src/class.ts +++ b/src/class.ts @@ -1,9 +1,9 @@ export type ArgType = 'OPERATOR' | 'COMPUTED' | 'PRIMITIVE' | 'ARRAY' | 'NESTED' | 'FLAT' -export type ValueParser = (parent: any) => any +export type Resolver = (parent: any) => any -export interface Values { - [key: string]: ValueParser +export interface Resolvers { + [key: string]: Resolver } export interface Keywords { @@ -37,7 +37,7 @@ const defaultKeywords: Keywords = { _MAX_DISTANCE: '$maxDistance', _MIN_DISTANCE: '$minDistance' } -const defaultValues: Values = {} +const defaultValues: Resolvers = {} const primitives = [ 'string', @@ -51,15 +51,15 @@ const primitives = [ export default class GQLMongoQuery { keywords: Keywords - values: Values + resolvers: Resolvers constructor( keywords: Keywords = defaultKeywords, - values: Values = defaultValues, + resolvers: Resolvers = defaultValues, merge: boolean = true ) { this.keywords = merge ? { ...defaultKeywords, ...keywords } : keywords - this.values = merge ? { ...defaultValues, ...values } : values + this.resolvers = merge ? { ...defaultValues, ...resolvers } : resolvers } private isOperator(key): boolean { @@ -72,7 +72,7 @@ export default class GQLMongoQuery { } private isComputable(key): boolean { - return Object.keys(this.values).includes(key) + return Object.keys(this.resolvers).includes(key) } private isNested(obj): boolean { @@ -88,9 +88,9 @@ export default class GQLMongoQuery { } private computedValue(args) { - for (const valueKey in this.values) { + for (const valueKey in this.resolvers) { if (args[valueKey] !== undefined) { - return this.values[valueKey](args) + return this.resolvers[valueKey](args) } } } diff --git a/src/index.ts b/src/index.ts index aec9e67..92a3b9e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,9 @@ export type ArgType = 'OPERATOR' | 'COMPUTED' | 'PRIMITIVE' | 'ARRAY' | 'NESTED' | 'FLAT' -export type ValueParser = (parent: any) => any +export type Resolver = (parent: any) => any -export interface Values { - [key: string]: ValueParser +export interface Resolvers { + [key: string]: Resolver } export interface Keywords { @@ -47,7 +47,7 @@ export const defaultKeywords: Keywords = { _MAX_DISTANCE: '$maxDistance', _MIN_DISTANCE: '$minDistance' } -const defaultValues: Values = {} +const defaultValues: Resolvers = {} export const isOperator = (key: string, keywords: object): boolean => { return Object.keys(keywords).includes(key) @@ -58,18 +58,18 @@ export const isPrimitive = (val): boolean => { else return false } -export const isComputable = (key: string, values: object): boolean => { - return Object.keys(values).includes(key) +export const isComputable = (key: string, resolvers: object): boolean => { + return Object.keys(resolvers).includes(key) } -export const isNested = (value, keywords: object, values: object): boolean => { +export const isNested = (value, keywords: object, resolvers: object): boolean => { if (typeof value !== 'object') return false let nested = false for (const key in value) { if ( !isOperator(key, keywords) && !isPrimitive(value[key]) && - !isComputable(key, values) + !isComputable(key, resolvers) ) { nested = true break @@ -78,38 +78,38 @@ export const isNested = (value, keywords: object, values: object): boolean => { return nested } -export const computedValue = (parent: object, values: object) => { - for (const valueKey in values) { +export const computedValue = (parent: object, resolvers: object) => { + for (const valueKey in resolvers) { if (parent[valueKey] !== undefined) { - return values[valueKey](parent) + return resolvers[valueKey](parent) } } } export const argType = ( keywords: object, - values: object, + resolvers: object, key?: string, val?: string ): ArgType => { if (isOperator(key, keywords)) return 'OPERATOR' - else if (isComputable(key, values)) return 'COMPUTED' + else if (isComputable(key, resolvers)) return 'COMPUTED' else if (isPrimitive(val)) return 'PRIMITIVE' else if (Array.isArray(val)) return 'ARRAY' - else if (isNested(val, keywords, values)) return 'NESTED' + else if (isNested(val, keywords, resolvers)) return 'NESTED' else if (typeof val === 'object') return 'FLAT' else return null } const parseNested = ( keywords: Keywords, - values: Values, + resolvers: Resolvers, key: string, val, lastResult = {} ) => { - if (isComputable(key, values)) { - return buildFilters(val, key, keywords, values) + if (isComputable(key, resolvers)) { + return buildFilters(val, key, keywords, resolvers) } let result = lastResult @@ -117,14 +117,14 @@ const parseNested = ( let isFinal = false // COMPUTABLE VALUE - if (isComputable(k, values)) { - result = { ...result, ...buildFilters(val[k], k, keywords, values) } + if (isComputable(k, resolvers)) { + result = { ...result, ...buildFilters(val[k], k, keywords, resolvers) } return result } // OPERATOR if (isOperator(k, keywords)) { - result = { ...result, [key]: buildFilters(val, key, keywords, values) } + result = { ...result, [key]: buildFilters(val, key, keywords, resolvers) } return result } @@ -132,29 +132,29 @@ const parseNested = ( const subval = val[k] // subval is COMPUTABLE VALUE - if (isComputable(subkey, values)) { - result = { ...result, ...buildFilters(subval, subkey, keywords, values) } + if (isComputable(subkey, resolvers)) { + result = { ...result, ...buildFilters(subval, subkey, keywords, resolvers) } isFinal = true } // subval is a PRIMITIVE else if (isPrimitive(subval)) { - result[subkey] = buildFilters(subval, null, keywords, values) + result[subkey] = buildFilters(subval, null, keywords, resolvers) isFinal = true } // subval is NESTED else { for (const sk in subval) { - const t = argType(keywords, values, sk, subval) + const t = argType(keywords, resolvers, sk, subval) if (t !== 'NESTED' && t !== 'FLAT') { - result[subkey] = buildFilters(subval, null, keywords, values) + result[subkey] = buildFilters(subval, null, keywords, resolvers) isFinal = true break } } } - if (!isFinal) parseNested(keywords, values, subkey, subval, result) + if (!isFinal) parseNested(keywords, resolvers, subkey, subval, result) } return result } @@ -163,11 +163,11 @@ export const buildFilters = ( args, parentKey?: string, keywords: Keywords = {}, - values: Values = {} + resolvers: Resolvers = {} ) => { // PARENT IS A COMPUTABLE VALUE - if (isComputable(parentKey, values)) { - return computedValue({ [parentKey]: args }, values) + if (isComputable(parentKey, resolvers)) { + return computedValue({ [parentKey]: args }, resolvers) } // NO PARENT AND ARGS IS A DIRECT VALUE @@ -181,11 +181,11 @@ export const buildFilters = ( for (const key in args) { if (!filters) filters = {} const val = args[key] - const t = argType(keywords, values, key, val) + const t = argType(keywords, resolvers, key, val) // COMPUTED VALUE - if (isComputable(key, values)) { - const computed = buildFilters(val, key, keywords, values) + if (isComputable(key, resolvers)) { + const computed = buildFilters(val, key, keywords, resolvers) filters = { ...filters, ...computed @@ -195,22 +195,22 @@ export const buildFilters = ( else if (t === 'OPERATOR') { const keyword = keywords[key] if (Array.isArray(val)) { - filters[keyword] = val.map(v => buildFilters(v, null, keywords, values)) + filters[keyword] = val.map(v => buildFilters(v, null, keywords, resolvers)) } else { - filters[keyword] = buildFilters(val, null, keywords, values) + filters[keyword] = buildFilters(val, null, keywords, resolvers) } } // NESTED QUERY else if (t === 'NESTED' || t === 'FLAT') { - filters = { ...filters, ...parseNested(keywords, values, key, val) } + filters = { ...filters, ...parseNested(keywords, resolvers, key, val) } } // ARRAY else if (t === 'ARRAY') { - filters[key] = val.map(v => buildFilters(v, null, keywords, values)) + filters[key] = val.map(v => buildFilters(v, null, keywords, resolvers)) } // ELSE else { - filters[key] = buildFilters(val, null, keywords, values) + filters[key] = buildFilters(val, null, keywords, resolvers) } } return filters @@ -218,12 +218,12 @@ export const buildFilters = ( export default ( customKeywords: Keywords = {}, - customValues: Values = {}, + customResolvers: Resolvers = {}, merge: boolean = true ) => { const keywords: Keywords = merge ? { ...defaultKeywords, ...customKeywords } : customKeywords - const values: Values = merge ? { ...defaultValues, ...customValues } : customValues - return (args: object): object => buildFilters(args, null, keywords, values) + const resolvers: Resolvers = merge ? { ...defaultValues, ...customResolvers } : customResolvers + return (args: object): object => buildFilters(args, null, keywords, resolvers) } diff --git a/tests/class.test.ts b/tests/class.test.ts index 887e357..bffc780 100644 --- a/tests/class.test.ts +++ b/tests/class.test.ts @@ -1,7 +1,7 @@ import GMQ from '../src/class' -test('Flat computed values', () => { - const values = { +test('Resolvers', () => { + const resolvers = { test1(parent) { parent.test1 = true return parent @@ -43,7 +43,7 @@ test('Flat computed values', () => { _AND: [{ nested: { a: 'aaa' } }, { b: true }] } - const filter = new GMQ(null, values).buildFilters(arg) + const filter = new GMQ(null, resolvers).buildFilters(arg) expect(filter).toEqual({ test1: true, logical: { $or: [1, 2] }, diff --git a/tests/dist.test.ts b/tests/dist.test.ts index a699003..f796f3c 100644 --- a/tests/dist.test.ts +++ b/tests/dist.test.ts @@ -1,6 +1,6 @@ -import parseQuery, { Values } from '../dist' +import parseQuery, { Resolvers } from '../dist' -const values: Values = { +const resolvers: Resolvers = { test1(parent) { parent.test1 = true return parent @@ -52,7 +52,7 @@ const args = { } test('functional', () => { - const parser = parseQuery(null, values) + const parser = parseQuery(null, resolvers) const filter = parser(args) expect(filter).toEqual({ test1: true, diff --git a/tests/index.test.ts b/tests/index.test.ts index 2a99ad8..8f70fdb 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -1,6 +1,6 @@ -import parseQuery, { Values } from '../src/' +import parseQuery, { Resolvers } from '../src/' -const values: Values = { +const resolvers: Resolvers = { test1(parent) { return {test1: !!parent.test1} }, @@ -49,7 +49,7 @@ const args = { } test('functional', () => { - const parser = parseQuery(null, values) + const parser = parseQuery(null, resolvers) const filter = parser(args) expect(filter).toEqual({ test1: true,