graphql-advanced-projection
Advanced tools
Comparing version 1.0.0 to 1.0.1
{ | ||
"name": "graphql-advanced-projection", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "Fully customizable Mongoose/MongoDB projection generator.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
180
src/core.js
const _ = require('lodash'); | ||
const logger = require('../logger'); | ||
@@ -11,127 +10,68 @@ const stripType = (typeRef) => { | ||
const makePrefix = (prev, cur, subs) => { | ||
let curr = cur; | ||
if (curr === undefined) { | ||
curr = subs; | ||
} | ||
if (!curr) { | ||
return prev; | ||
} | ||
if (curr.startsWith('.')) { | ||
return curr.substr(1); | ||
} | ||
return prev + curr; | ||
}; | ||
function makeProjection( | ||
root, | ||
context, | ||
prefix, | ||
type, | ||
) { | ||
const { pick, info } = root; | ||
logger.debug('Projecting type', type); | ||
const cfg = (pick[type] || _.constant({}))(info); | ||
const result = {}; | ||
const pf = makePrefix(prefix, cfg.prefix); | ||
const proj = (reason, k) => { | ||
if (_.isArray(k)) { | ||
k.forEach((v) => { | ||
logger.trace(`>${reason}`, pf + v); | ||
result[pf + v] = 1; | ||
}); | ||
return; | ||
} | ||
/* istanbul ignore else */ | ||
if (_.isString(k)) { | ||
logger.trace(`>${reason}`, pf + k); | ||
result[pf + k] = 1; | ||
return; | ||
} | ||
/* istanbul ignore next */ | ||
throw new Error(`Proj not supported: ${k}`); | ||
}; | ||
if (cfg.typeProj) { | ||
proj('TypeProj', cfg.typeProj); | ||
} | ||
context.selectionSet.selections.forEach((sel) => { | ||
const fieldName = _.get(sel, 'name.value'); | ||
switch (sel.kind) { | ||
case 'Field': { | ||
logger.debug('Projecting field', fieldName); | ||
const def = _.get(cfg.proj, fieldName) || {}; | ||
if (def.query === undefined) { | ||
proj('Default', fieldName); | ||
} else if (def.query === null) { | ||
logger.trace('>Ignored'); | ||
} else { | ||
proj('Simple', def.query); | ||
} | ||
if (def.recursive && sel.selectionSet) { | ||
const makeTraverser = ({ typeFunc, fieldFunc, stepFunc, reduceFunc }, seed) => (configs) => { | ||
const { pick } = configs; | ||
const func = (root, context, type) => (args) => { | ||
const { info } = root; | ||
const config = (pick[type] || _.constant({}))(info); | ||
const cfgs = { configs, config, type }; | ||
const fieldResults = []; | ||
const typeResult = typeFunc(cfgs, args); | ||
context.selectionSet.selections.forEach((sel) => { | ||
const field = _.get(sel, 'name.value'); | ||
switch (sel.kind) { | ||
case 'Field': { | ||
const typeRef = info.schema.getType(type); | ||
/* istanbul ignore if */ | ||
if (!typeRef) { | ||
/* istanbul ignore next */ | ||
throw new Error('Type not found', type); | ||
} | ||
logger.trace('typeRef', typeRef.toString()); | ||
const field = typeRef.getFields()[fieldName]; | ||
/* istanbul ignore if */ | ||
if (!field) { | ||
/* istanbul ignore next */ | ||
throw new Error('Field not found', fieldName); | ||
} | ||
const nextTypeRef = field.type; | ||
logger.trace('nextTypeRef', nextTypeRef.toString()); | ||
const core = stripType(nextTypeRef); | ||
logger.trace('Recursive', core); | ||
_.assign(result, makeProjection( | ||
root, | ||
sel, | ||
makePrefix(pf, def.prefix, `${fieldName}.`), | ||
core, | ||
)); | ||
const next = stripType(typeRef.getFields()[field].type); | ||
const recursion = sel.selectionSet ? func(root, sel, next) : undefined; | ||
fieldResults.push(fieldFunc({ | ||
...cfgs, | ||
field, | ||
next, | ||
}, args, recursion)); | ||
return; | ||
} | ||
return; | ||
case 'InlineFragment': { | ||
const newType = _.get(sel, 'typeCondition.name.value'); | ||
const next = newType || type; | ||
const recursion = func(root, sel, next); | ||
fieldResults.push(stepFunc({ | ||
...cfgs, | ||
field, | ||
next, | ||
}, args, recursion)); | ||
return; | ||
} | ||
case 'FragmentSpread': { | ||
const frag = info.fragments[field]; | ||
const next = _.get(frag, 'typeCondition.name.value'); | ||
const recursion = func(root, frag, next); | ||
fieldResults.push(stepFunc({ | ||
...cfgs, | ||
field, | ||
next, | ||
}, args, recursion)); | ||
return; | ||
} | ||
/* istanbul ignore next */ | ||
default: | ||
/* istanbul ignore next */ | ||
throw new Error(`sel.kind not supported: ${sel.kind}`); | ||
} | ||
case 'InlineFragment': { | ||
logger.debug('Projecting inline fragment'); | ||
const newType = _.get(sel, 'typeCondition.name.value'); | ||
const newPrefix = newType ? pf : prefix; | ||
const core = newType || type; | ||
logger.trace('Recursive', { type: core, prefix: newPrefix }); | ||
_.assign(result, makeProjection( | ||
root, | ||
sel, | ||
newPrefix, | ||
core, | ||
)); | ||
return; | ||
} | ||
case 'FragmentSpread': { | ||
logger.debug('Projecting fragment', fieldName); | ||
const frag = info.fragments[fieldName]; | ||
const newType = _.get(frag, 'typeCondition.name.value'); | ||
const newPrefix = newType !== type ? pf : prefix; | ||
logger.trace('Recursive', { type: newType, prefix: newPrefix }); | ||
_.assign(result, makeProjection( | ||
root, | ||
frag, | ||
newPrefix, | ||
newType, | ||
)); | ||
return; | ||
} | ||
/* istanbul ignore next */ | ||
default: | ||
/* istanbul ignore next */ | ||
throw new Error(`sel.kind not supported: ${sel.kind}`); | ||
} | ||
}); | ||
return result; | ||
} | ||
}); | ||
return reduceFunc(cfgs, typeResult, fieldResults); | ||
}; | ||
return (info) => { | ||
const context = info.fieldNodes[0]; | ||
const type = stripType(info.returnType); | ||
return func( | ||
{ info }, | ||
context, | ||
type, | ||
)(seed); | ||
}; | ||
}; | ||
module.exports = { | ||
stripType, | ||
makeProjection, | ||
makeTraverser, | ||
}; |
@@ -5,5 +5,5 @@ const _ = require('lodash/fp'); | ||
function prepareProjectionConfig(def) { | ||
function prepareProjectionConfig(def, fieldName) { | ||
if (def === undefined) { | ||
return {}; | ||
return { query: fieldName }; | ||
} | ||
@@ -36,5 +36,5 @@ if (def === null) { | ||
return { | ||
query: def.query, | ||
query: def.query === undefined ? fieldName : def.query, | ||
select: def.select, | ||
recursive: !!def.recursive, | ||
recursive: def.recursive ? true : undefined, | ||
prefix: def.prefix, | ||
@@ -72,3 +72,3 @@ }; | ||
const ncfgs = _.compose( | ||
_.mapValues( | ||
_.mapValues.convert({ cap: false })( | ||
(v) => (_.isArray(v) | ||
@@ -78,7 +78,7 @@ ? _.compose( | ||
_.update('[1].proj'), | ||
_.mapValues, | ||
_.mapValues.convert({ cap: false }), | ||
) | ||
: _.compose( | ||
_.update('proj'), | ||
_.mapValues, | ||
_.mapValues.convert({ cap: false }), | ||
) | ||
@@ -85,0 +85,0 @@ )(prepareProjectionConfig)(v), |
@@ -1,23 +0,93 @@ | ||
const _ = require('lodash/fp'); | ||
const { stripType, makeProjection } = require('./core'); | ||
const _ = require('lodash'); | ||
const { makeTraverser } = require('./core'); | ||
const logger = require('../logger'); | ||
module.exports.genProjection = ({ root, pick }) => (info) => { | ||
try { | ||
const context = info.fieldNodes[0]; | ||
const type = stripType(info.returnType); | ||
const result = _.reduce(_.assign, {})([root, makeProjection( | ||
{ pick, info }, | ||
context, | ||
'', | ||
type, | ||
)]); | ||
logger.debug('Project result', result); | ||
const makePrefix = (prev, cur, subs) => { | ||
let curr = cur; | ||
if (curr === undefined) { | ||
curr = subs; | ||
} | ||
if (!curr) { | ||
return prev; | ||
} | ||
if (curr.startsWith('.')) { | ||
return curr.substr(1); | ||
} | ||
return prev + curr; | ||
}; | ||
const proj = (reason, pf, k) => { | ||
const result = {}; | ||
if (_.isArray(k)) { | ||
k.forEach((v) => { | ||
logger.trace(`>${reason}`, pf + v); | ||
result[pf + v] = 1; | ||
}); | ||
return result; | ||
} catch (e) { | ||
/* istanbul ignore next */ | ||
logger.error('Projecting', e); | ||
/* istanbul ignore next */ | ||
return undefined; | ||
} | ||
/* istanbul ignore else */ | ||
if (_.isString(k)) { | ||
logger.trace(`>${reason}`, pf + k); | ||
result[pf + k] = 1; | ||
return result; | ||
} | ||
/* istanbul ignore next */ | ||
throw new Error(`Proj not supported: ${k}`); | ||
}; | ||
const makeProjection = makeTraverser({ | ||
typeFunc({ config }, [prefix]) { | ||
if (config.typeProj) { | ||
const pf = makePrefix(prefix, config.prefix); | ||
return proj('TypeProj', pf, config.typeProj); | ||
} | ||
return {}; | ||
}, | ||
fieldFunc({ config, field }, [prefix], recursion) { | ||
let result; | ||
logger.debug('Projecting field', field); | ||
const def = _.get(config.proj, field) || { query: field }; | ||
const pf = makePrefix(prefix, config.prefix); | ||
if (def.query === null) { | ||
logger.trace('>Ignored'); | ||
result = {}; | ||
} else { | ||
result = proj('Simple', pf, def.query); | ||
} | ||
if (def.recursive && recursion) { | ||
result = _.assign(result, recursion([makePrefix(pf, def.prefix, `${field}.`)])); | ||
} | ||
return result; | ||
}, | ||
stepFunc({ config, field, type, next }, [prefix], recursion) { | ||
logger.debug('Projecting (inline) fragment', field); | ||
const newPrefix = type === next | ||
? prefix | ||
: makePrefix(prefix, config.prefix); | ||
return recursion([newPrefix]); | ||
}, | ||
reduceFunc(configs, typeResult, fieldResults) { | ||
return _.assign({}, typeResult, ...fieldResults); | ||
}, | ||
}, ['']); | ||
const genProjection = ({ root, pick }) => { | ||
const projector = makeProjection({ pick }); | ||
return (info) => { | ||
try { | ||
const result = _.assign({}, root, projector(info)); | ||
logger.debug('Project result', result); | ||
return result; | ||
} catch (e) { | ||
/* istanbul ignore next */ | ||
logger.error('Projecting', e); | ||
/* istanbul ignore next */ | ||
return undefined; | ||
} | ||
}; | ||
}; | ||
module.exports = { | ||
makeProjection, | ||
genProjection, | ||
}; |
@@ -9,4 +9,4 @@ const { | ||
it('should accept undefined', () => { | ||
const result = prepareProjectionConfig(undefined); | ||
expect(result.query).toEqual(undefined); | ||
const result = prepareProjectionConfig(undefined, 'fn'); | ||
expect(result.query).toEqual('fn'); | ||
expect(result.select).toEqual(undefined); | ||
@@ -18,3 +18,3 @@ expect(result.recursive).toBeFalsy(); | ||
it('should accept null', () => { | ||
const result = prepareProjectionConfig(null); | ||
const result = prepareProjectionConfig(null, 'fn'); | ||
expect(result.query).toEqual(null); | ||
@@ -27,3 +27,3 @@ expect(result.select).toEqual(undefined); | ||
it('should accept true', () => { | ||
const result = prepareProjectionConfig(true); | ||
const result = prepareProjectionConfig(true, 'fn'); | ||
expect(result.query).toEqual(null); | ||
@@ -36,3 +36,3 @@ expect(result.select).toEqual(undefined); | ||
it('should accept string', () => { | ||
const result = prepareProjectionConfig('str'); | ||
const result = prepareProjectionConfig('str', 'fn'); | ||
expect(result.query).toEqual('str'); | ||
@@ -45,3 +45,3 @@ expect(result.select).toEqual('str'); | ||
it('should accept recursive string', () => { | ||
const result = prepareProjectionConfig('str.'); | ||
const result = prepareProjectionConfig('str.', 'fn'); | ||
expect(result.query).toEqual(null); | ||
@@ -54,3 +54,3 @@ expect(result.select).toEqual('str'); | ||
it('should accept recursive string', () => { | ||
const result = prepareProjectionConfig('str'); | ||
const result = prepareProjectionConfig('str', 'fn'); | ||
expect(result.query).toEqual('str'); | ||
@@ -63,3 +63,3 @@ expect(result.select).toEqual('str'); | ||
it('should accept array', () => { | ||
const result = prepareProjectionConfig(['a', 'b']); | ||
const result = prepareProjectionConfig(['a', 'b'], 'fn'); | ||
expect(result.query).toEqual(['a', 'b']); | ||
@@ -76,3 +76,3 @@ expect(result.select).toEqual(undefined); | ||
recursive: 0, | ||
}); | ||
}, 'fn'); | ||
expect(result.query).toEqual('q'); | ||
@@ -89,3 +89,3 @@ expect(result.select).toEqual('s'); | ||
prefix: 'xxx', | ||
}); | ||
}, 'fn'); | ||
expect(result.query).toEqual(['a', 'b']); | ||
@@ -96,2 +96,12 @@ expect(result.select).toEqual(undefined); | ||
}); | ||
it('should accept object 3', () => { | ||
const result = prepareProjectionConfig({ | ||
recursive: true, | ||
}, 'fn'); | ||
expect(result.query).toEqual('fn'); | ||
expect(result.select).toEqual(undefined); | ||
expect(result.recursive).toBeTruthy(); | ||
expect(result.prefix).toBeUndefined(); | ||
}); | ||
}); | ||
@@ -161,2 +171,3 @@ | ||
a: 'b', | ||
c: {}, | ||
}, | ||
@@ -174,2 +185,5 @@ }, | ||
}, | ||
c: { | ||
query: 'c', | ||
}, | ||
}, | ||
@@ -182,2 +196,3 @@ }, | ||
a: { query: 'b', select: 'b' }, | ||
c: { query: 'c' }, | ||
}, | ||
@@ -184,0 +199,0 @@ }); |
@@ -0,1 +1,2 @@ | ||
const _ = require('lodash/fp'); | ||
const fs = require('fs'); | ||
@@ -6,4 +7,801 @@ const path = require('path'); | ||
const { prepareConfig } = require('../src/prepareConfig'); | ||
const { genProjection } = require('../src/projection'); | ||
const { makeProjection, genProjection } = require('../src/projection'); | ||
describe('makeProjection', () => { | ||
const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.graphql'), 'utf-8'); | ||
const run = (config, query) => new Promise((resolve, reject) => { | ||
const pick = _.mapValues(_.constant)(config); | ||
const go = (info) => { | ||
try { | ||
const proj = makeProjection({ pick })(info); | ||
resolve(proj); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
}; | ||
graphql(makeExecutableSchema({ | ||
typeDefs, | ||
resolvers: { | ||
Query: { | ||
obj: (parent, args, context, info) => { | ||
go(info); | ||
}, | ||
evil: (parent, args, context, info) => { | ||
go(info); | ||
}, | ||
}, | ||
}, | ||
}), query).then((res) => { | ||
if (res.errors) { | ||
throw res.errors; | ||
} | ||
}); | ||
}); | ||
it('should project default when not configured', () => { | ||
expect.hasAssertions(); | ||
return expect(run({}, '{ obj { field1 } }')).resolves.toEqual({ | ||
field1: 1, | ||
}); | ||
}); | ||
it('should project query null', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
proj: { | ||
field1: { | ||
query: null, | ||
}, | ||
}, | ||
}, | ||
}, '{ obj { field1 } }')).resolves.toEqual({ | ||
}); | ||
}); | ||
it('should project query simple', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field1: { | ||
query: 'value', | ||
}, | ||
}, | ||
}, | ||
}, '{ obj { field1 } }')).resolves.toEqual({ | ||
'wrap.value': 1, | ||
}); | ||
}); | ||
it('should project query multiple', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field1: { | ||
query: ['value', 'value2'], | ||
}, | ||
}, | ||
}, | ||
}, '{ obj { field1 } }')).resolves.toEqual({ | ||
'wrap.value': 1, | ||
'wrap.value2': 1, | ||
}); | ||
}); | ||
it('should not project recursive if false', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { }, | ||
}, | ||
Foo: { | ||
prefix: 'wrap2.', | ||
proj: { | ||
f1: { query: 'foo' }, | ||
}, | ||
}, | ||
}, '{ obj { field2 { f1 } } }')).resolves.toEqual({ | ||
'wrap.field2': 1, | ||
}); | ||
}); | ||
it('should project recursive', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field2: { | ||
query: ['evil', 'evils'], | ||
recursive: true, | ||
}, | ||
}, | ||
}, | ||
}, '{ obj { field2 { f1 } } }')).resolves.toEqual({ | ||
'wrap.evil': 1, | ||
'wrap.evils': 1, | ||
'wrap.field2.f1': 1, | ||
}); | ||
}); | ||
it('should project recursive prefix null', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field2: { | ||
query: ['evil', 'evils'], | ||
recursive: true, | ||
prefix: null, | ||
}, | ||
}, | ||
}, | ||
}, '{ obj { field2 { f1 } } }')).resolves.toEqual({ | ||
'wrap.evil': 1, | ||
'wrap.evils': 1, | ||
'wrap.f1': 1, | ||
}); | ||
}); | ||
it('should project recursive prefix', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field2: { | ||
query: ['evil', 'evils'], | ||
recursive: true, | ||
prefix: 'xxx.', | ||
}, | ||
}, | ||
}, | ||
}, '{ obj { field2 { f1 } } }')).resolves.toEqual({ | ||
'wrap.evil': 1, | ||
'wrap.evils': 1, | ||
'wrap.xxx.f1': 1, | ||
}); | ||
}); | ||
it('should project recursive prefix absolute', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field2: { | ||
query: null, | ||
recursive: true, | ||
prefix: '.xxx.', | ||
}, | ||
}, | ||
}, | ||
}, '{ obj { field2 { f1 } } }')).resolves.toEqual({ | ||
'xxx.f1': 1, | ||
}); | ||
}); | ||
it('should project recursive relative', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field2: { | ||
query: null, | ||
recursive: true, | ||
prefix: 'xxx.', | ||
}, | ||
}, | ||
}, | ||
Foo: { | ||
prefix: 'wrap2.', | ||
proj: { | ||
f1: { query: 'foo' }, | ||
}, | ||
}, | ||
}, '{ obj { field2 { f1 } } }')).resolves.toEqual({ | ||
'wrap.xxx.wrap2.foo': 1, | ||
}); | ||
}); | ||
it('should project recursive absolute', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field2: { | ||
query: null, | ||
recursive: true, | ||
prefix: 'xxx.', | ||
}, | ||
}, | ||
}, | ||
Foo: { | ||
prefix: '.wrap2.', | ||
proj: { | ||
f1: { query: 'foo' }, | ||
}, | ||
}, | ||
}, '{ obj { field2 { f1 } } }')).resolves.toEqual({ | ||
'wrap2.foo': 1, | ||
}); | ||
}); | ||
it('should project inline fragment', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field1: { query: 'value' }, | ||
}, | ||
}, | ||
}, `{ | ||
obj { | ||
... { | ||
field1 | ||
} | ||
} | ||
}`)).resolves.toEqual({ | ||
'wrap.value': 1, | ||
}); | ||
}); | ||
it('should project deep inline fragment', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field1: { query: 'value' }, | ||
}, | ||
}, | ||
}, `{ | ||
obj { | ||
... { | ||
... { | ||
... { | ||
field1 | ||
} | ||
} | ||
} | ||
} | ||
}`)).resolves.toEqual({ | ||
'wrap.value': 1, | ||
}); | ||
}); | ||
it('should project fragment', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field1: { query: 'value' }, | ||
}, | ||
}, | ||
}, `{ | ||
obj { | ||
...f | ||
} | ||
} | ||
fragment f on Obj { | ||
field1 | ||
} | ||
`)).resolves.toEqual({ | ||
'wrap.value': 1, | ||
}); | ||
}); | ||
it('should project deep fragment', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
prefix: 'wrap.', | ||
proj: { | ||
field1: { query: 'value' }, | ||
}, | ||
}, | ||
}, `{ | ||
obj { | ||
...f | ||
} | ||
} | ||
fragment f on Obj { | ||
...g | ||
} | ||
fragment g on Obj { | ||
...h | ||
} | ||
fragment h on Obj { | ||
field1 | ||
} | ||
`)).resolves.toEqual({ | ||
'wrap.value': 1, | ||
}); | ||
}); | ||
it('should project typeProj', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
proj: { | ||
field3: { | ||
query: null, | ||
recursive: true, | ||
prefix: 'evil.', | ||
}, | ||
}, | ||
}, | ||
Father: { | ||
prefix: 'wrap.', | ||
typeProj: 'type', | ||
proj: { | ||
g0: { query: 'value' }, | ||
}, | ||
}, | ||
Child: { | ||
prefix: 'wrap2.', | ||
proj: { | ||
g1: { query: 'value2' }, | ||
}, | ||
}, | ||
}, `{ | ||
obj { | ||
field3 { | ||
g0 | ||
} | ||
} | ||
}`)).resolves.toEqual({ | ||
'evil.wrap.type': 1, | ||
'evil.wrap.value': 1, | ||
}); | ||
}); | ||
it('should project inline fragment with typeCondition', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
proj: { | ||
field3: { | ||
query: null, | ||
recursive: true, | ||
prefix: 'evil.', | ||
}, | ||
}, | ||
}, | ||
Father: { | ||
prefix: 'wrap.', | ||
typeProj: 'type', | ||
proj: { | ||
g0: { query: 'value' }, | ||
}, | ||
}, | ||
Child: { | ||
prefix: 'wrap2.', | ||
proj: { | ||
g1: { query: 'value2' }, | ||
}, | ||
}, | ||
}, `{ | ||
obj { | ||
field3 { | ||
g0 | ||
... on Child { | ||
g1 | ||
} | ||
} | ||
} | ||
}`)).resolves.toEqual({ | ||
'evil.wrap.type': 1, | ||
'evil.wrap.value': 1, | ||
'evil.wrap.wrap2.value2': 1, | ||
}); | ||
}); | ||
it('should project deep inline fragment with typeCondition', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
proj: { | ||
field3: { | ||
query: null, | ||
recursive: true, | ||
prefix: 'evil.', | ||
}, | ||
}, | ||
}, | ||
Father: { | ||
prefix: 'wrap.', | ||
typeProj: 'type', | ||
proj: { | ||
g0: { query: 'value' }, | ||
}, | ||
}, | ||
Child: { | ||
prefix: 'wrap2.', | ||
proj: { | ||
g1: { query: 'value2' }, | ||
}, | ||
}, | ||
}, `{ | ||
obj { | ||
field3 { | ||
g0 | ||
... on Child { | ||
... { | ||
... on Child { | ||
g1 | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}`)).resolves.toEqual({ | ||
'evil.wrap.type': 1, | ||
'evil.wrap.value': 1, | ||
'evil.wrap.wrap2.value2': 1, | ||
}); | ||
}); | ||
it('should project inline fragment with typeCondition partial', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
proj: { | ||
field3: { | ||
query: null, | ||
recursive: true, | ||
prefix: 'evil.', | ||
}, | ||
}, | ||
}, | ||
Father: { | ||
prefix: 'wrap.', | ||
typeProj: 'type', | ||
proj: { | ||
g0: { query: 'value' }, | ||
}, | ||
}, | ||
Child: { | ||
prefix: 'wrap2.', | ||
proj: { | ||
g1: { query: 'value2' }, | ||
}, | ||
}, | ||
}, `{ | ||
obj { | ||
field3 { | ||
... on Child { | ||
g0 | ||
g1 | ||
} | ||
} | ||
} | ||
}`)).resolves.toEqual({ | ||
'evil.wrap.type': 1, | ||
'evil.wrap.wrap2.g0': 1, | ||
'evil.wrap.wrap2.value2': 1, | ||
}); | ||
}); | ||
it('should project fragment with typeCondition', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Obj: { | ||
proj: { | ||
field3: { | ||
query: null, | ||
recursive: true, | ||
prefix: null, | ||
}, | ||
}, | ||
}, | ||
Father: { | ||
prefix: 'wrap.', | ||
typeProj: 'type', | ||
proj: { | ||
g0: { query: 'value' }, | ||
}, | ||
}, | ||
Child: { | ||
prefix: 'wrap2.', | ||
proj: { | ||
g1: { query: 'value2' }, | ||
}, | ||
}, | ||
}, `{ | ||
obj { | ||
field3 { | ||
g0 | ||
...f | ||
} | ||
} | ||
} | ||
fragment f on Child { | ||
g1 | ||
} | ||
`)).resolves.toEqual({ | ||
'wrap.type': 1, | ||
'wrap.value': 1, | ||
'wrap.wrap2.value2': 1, | ||
}); | ||
}); | ||
it('should handle deep nested', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Evil: { | ||
proj: { | ||
self: { | ||
query: null, | ||
recursive: true, | ||
prefix: null, | ||
}, | ||
}, | ||
}, | ||
}, `{ | ||
evil { | ||
field | ||
self { | ||
field | ||
self { | ||
field | ||
} | ||
} | ||
} | ||
} | ||
`)).resolves.toEqual({ | ||
field: 1, | ||
}); | ||
}); | ||
it('should handle deep nested prefix', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Evil: { | ||
prefix: '.x.', | ||
proj: { | ||
self: { | ||
query: null, | ||
recursive: true, | ||
prefix: null, | ||
}, | ||
}, | ||
}, | ||
}, `{ | ||
evil { | ||
field | ||
self { | ||
field | ||
self { | ||
field | ||
} | ||
} | ||
} | ||
} | ||
`)).resolves.toEqual({ | ||
'x.field': 1, | ||
}); | ||
}); | ||
it('should handle deep nested prefix relative', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Evil: { | ||
prefix: 'x.', | ||
proj: { | ||
self: { | ||
query: null, | ||
recursive: true, | ||
prefix: null, | ||
}, | ||
}, | ||
}, | ||
}, `{ | ||
evil { | ||
field | ||
self { | ||
field | ||
self { | ||
field | ||
} | ||
} | ||
} | ||
} | ||
`)).resolves.toEqual({ | ||
'x.field': 1, | ||
'x.x.field': 1, | ||
'x.x.x.field': 1, | ||
}); | ||
}); | ||
it('should handle deep nested proj prefix', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Evil: { | ||
proj: { | ||
self: { | ||
query: null, | ||
recursive: true, | ||
prefix: 'y.', | ||
}, | ||
}, | ||
}, | ||
}, `{ | ||
evil { | ||
field | ||
self { | ||
field | ||
self { | ||
field | ||
} | ||
} | ||
} | ||
} | ||
`)).resolves.toEqual({ | ||
field: 1, | ||
'y.field': 1, | ||
'y.y.field': 1, | ||
}); | ||
}); | ||
it('should handle deep nested proj prefix prefix', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Evil: { | ||
prefix: '.x.', | ||
proj: { | ||
self: { | ||
query: null, | ||
recursive: true, | ||
prefix: 'y.', | ||
}, | ||
}, | ||
}, | ||
}, `{ | ||
evil { | ||
field | ||
self { | ||
field | ||
self { | ||
field | ||
} | ||
} | ||
} | ||
} | ||
`)).resolves.toEqual({ | ||
'x.field': 1, | ||
}); | ||
}); | ||
it('should handle deep nested proj prefix prefix relative', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Evil: { | ||
prefix: 'x.', | ||
proj: { | ||
self: { | ||
query: null, | ||
recursive: true, | ||
prefix: 'y.', | ||
}, | ||
}, | ||
}, | ||
}, `{ | ||
evil { | ||
field | ||
self { | ||
field | ||
self { | ||
field | ||
} | ||
} | ||
} | ||
} | ||
`)).resolves.toEqual({ | ||
'x.field': 1, | ||
'x.y.x.field': 1, | ||
'x.y.x.y.x.field': 1, | ||
}); | ||
}); | ||
it('should handle deep nested proj prefix abs', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Evil: { | ||
proj: { | ||
self: { | ||
query: null, | ||
recursive: true, | ||
prefix: '.y.', | ||
}, | ||
}, | ||
}, | ||
}, `{ | ||
evil { | ||
field | ||
self { | ||
field | ||
self { | ||
field | ||
} | ||
} | ||
} | ||
} | ||
`)).resolves.toEqual({ | ||
field: 1, | ||
'y.field': 1, | ||
}); | ||
}); | ||
it('should handle deep nested proj prefix abs prefix', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Evil: { | ||
prefix: '.x.', | ||
proj: { | ||
self: { | ||
query: null, | ||
recursive: true, | ||
prefix: '.y.', | ||
}, | ||
}, | ||
}, | ||
}, `{ | ||
evil { | ||
field | ||
self { | ||
field | ||
self { | ||
field | ||
} | ||
} | ||
} | ||
} | ||
`)).resolves.toEqual({ | ||
'x.field': 1, | ||
}); | ||
}); | ||
it('should handle deep nested proj prefix abs prefix relative', () => { | ||
expect.hasAssertions(); | ||
return expect(run({ | ||
Evil: { | ||
prefix: 'x.', | ||
proj: { | ||
self: { | ||
query: null, | ||
recursive: true, | ||
prefix: '.y.', | ||
}, | ||
}, | ||
}, | ||
}, `{ | ||
evil { | ||
field | ||
self { | ||
field | ||
self { | ||
field | ||
} | ||
} | ||
} | ||
} | ||
`)).resolves.toEqual({ | ||
'x.field': 1, | ||
'y.x.field': 1, | ||
}); | ||
}); | ||
}); | ||
describe('genProjection', () => { | ||
@@ -10,0 +808,0 @@ const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.graphql'), 'utf-8'); |
216861
9
46
2702