Socket
Socket
Sign inDemoInstall

graphql-advanced-projection

Package Overview
Dependencies
3
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.0-beta.1 to 1.0.0

9

docs/API.md

@@ -11,2 +11,5 @@ # Terms

# Exported functions
- `gqlProjection (default): (config) => { project, resolvers }`
- Shorthand for calling the the following functions.
- `prepareConfig: (config) => config`
- `genProjection: (config) => (info) => result`

@@ -16,3 +19,2 @@ - `info` is the 4th argument of a resolver function.

- `result` is an object with Path as keys and `1` or `0` as value.
- `result._id` always exists.
- `genResolvers: (config) => resolvers`

@@ -53,3 +55,6 @@ - `resolvers` is of valid GraphQL resolver format. SHOULD be used with [`graphql-tools/makeExecutableSchema`](https://github.com/apollographql/graphql-tools).

- `null` can match zero, one, or more path items.
- A string can match one path item (exactly match its key) and following numeric keys.
- `''` can match one path item (not numeric) and following numeric keys.
- `'?'` can match nothing or (one path item (not numeric) and following numeric keys).
- A string with suffix `'?'` can match nothing or (one path item (exactly match its key) and following numeric keys).
- Otherwise, a string can match one path item (exactly match its key) and following numeric keys.

@@ -56,0 +61,0 @@ # Type config object

@@ -6,9 +6,5 @@ const _ = require('lodash');

const { User, Item } = require('./models');
const {
prepareConfig,
genProjection,
genResolvers,
} = require('../../');
const gqlProjection = require('../../');
const config = prepareConfig({
const { project, resolvers } = gqlProjection({
User: {

@@ -34,4 +30,2 @@ proj: {

});
const project = genProjection(config);
const resolvers = genResolvers(config);

@@ -38,0 +32,0 @@ module.exports = makeExecutableSchema({

@@ -6,9 +6,5 @@ const _ = require('lodash');

const { User } = require('./models');
const {
prepareConfig,
genProjection,
genResolvers,
} = require('../../');
const gqlProjection = require('../../');
const config = prepareConfig({
const { project, resolvers } = gqlProjection({
User: {

@@ -33,4 +29,2 @@ typeProj: 'type',

});
const project = genProjection(config);
const resolvers = genResolvers(config);

@@ -37,0 +31,0 @@ module.exports = makeExecutableSchema({

@@ -6,9 +6,5 @@ const _ = require('lodash');

const { User } = require('./models');
const {
prepareConfig,
genProjection,
genResolvers,
} = require('../../');
const gqlProjection = require('../../');
const config = prepareConfig({
const { project, resolvers } = gqlProjection({
User: {

@@ -25,4 +21,2 @@ proj: {

});
const project = genProjection(config);
const resolvers = genResolvers(config);

@@ -29,0 +23,0 @@ module.exports = makeExecutableSchema({

@@ -6,9 +6,5 @@ const _ = require('lodash');

const { User } = require('./models');
const {
prepareConfig,
genProjection,
genResolvers,
} = require('../../');
const gqlProjection = require('../../');
const config = prepareConfig({
const { project, resolvers } = gqlProjection({
User: {

@@ -26,4 +22,2 @@ proj: {

});
const project = genProjection(config);
const resolvers = genResolvers(config);

@@ -30,0 +24,0 @@ module.exports = makeExecutableSchema({

@@ -6,9 +6,5 @@ const _ = require('lodash');

const { User, Item } = require('./models');
const {
prepareConfig,
genProjection,
genResolvers,
} = require('../../');
const gqlProjection = require('../../');
const config = prepareConfig({
const { project, resolvers } = gqlProjection({
User: {

@@ -26,4 +22,2 @@ proj: {

});
const project = genProjection(config);
const resolvers = genResolvers(config);

@@ -30,0 +24,0 @@ module.exports = makeExecutableSchema({

@@ -6,9 +6,5 @@ const _ = require('lodash');

const { User } = require('./models');
const {
prepareConfig,
genProjection,
genResolvers,
} = require('../../');
const gqlProjection = require('../../');
const config = prepareConfig({
const { project, resolvers } = gqlProjection({
User: {

@@ -22,4 +18,2 @@ proj: {

});
const project = genProjection(config);
const resolvers = genResolvers(config);

@@ -26,0 +20,0 @@ module.exports = makeExecutableSchema({

@@ -5,7 +5,15 @@ const { prepareConfig } = require('./src/prepareConfig');

module.exports = {
default: prepareConfig,
prepareConfig,
genProjection,
genResolvers,
const gqlProjection = (config) => {
const ncfgs = prepareConfig(config);
return {
project: genProjection(ncfgs),
resolvers: genResolvers(ncfgs),
};
};
module.exports = gqlProjection;
module.exports.default = gqlProjection;
module.exports.gqlProjection = gqlProjection;
module.exports.prepareConfig = prepareConfig;
module.exports.genProjection = genProjection;
module.exports.genResolvers = genResolvers;
{
"name": "graphql-advanced-projection",
"version": "1.0.0-beta.1",
"version": "1.0.0",
"description": "Fully customizable Mongoose/MongoDB projection generator.",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -36,2 +36,3 @@ # graphql-advanced-projection

### Setup `mongoose`
```js

@@ -46,2 +47,3 @@ const UserSchema = new mongoose.Schema({

### Setup `graphql`
```graphql

@@ -59,11 +61,5 @@ type Query {

### Setup `graphql-advanced-projection`
```js
const {
prepareConfig,
genProjection,
genResolvers,
} = require('graphql-advanced-projection');
// Projection config
const config = prepareConfig({
const { project, resolvers } = gqlProjection({
User: {

@@ -77,4 +73,2 @@ proj: {

});
const project = genProjection(config);
const resolvers = genResolvers(config);
```

@@ -85,5 +79,2 @@

```js
const _ = require('lodash');
const { makeExecutableSchema } = require('graphql-tools');
module.exports = makeExecutableSchema({

@@ -90,0 +81,0 @@ typeDefs,

@@ -62,5 +62,5 @@ const _ = require('lodash/fp');

if (_.isArray(config)) {
return config.map(([m, t]) => [norm(m), t]);
return config.map(([m, t]) => [norm(m), _.cloneDeep(t)]);
}
return [[[[null]], config]];
return _.cloneDeep(config);
}

@@ -72,13 +72,18 @@

_.mapValues(
_.compose(
_.map,
_.update('[1].proj'),
_.mapValues,
)(prepareProjectionConfig),
(v) => (_.isArray(v)
? _.compose(
_.map,
_.update('[1].proj'),
_.mapValues,
)
: _.compose(
_.update('proj'),
_.mapValues,
)
)(prepareProjectionConfig)(v),
),
_.cloneDeep,
_.mapValues(prepareSchemaConfig),
_.pickBy((v, k) => /^[A-Z]/.test(k)),
)(configs);
logger.info('Total config', ncfgs);
logger.info(`Total config: ${JSON.stringify(ncfgs, null, 2)}`);
return {

@@ -85,0 +90,0 @@ root,

@@ -5,23 +5,20 @@ const _ = require('lodash/fp');

module.exports.genProjection = ({ root, pick }) => {
logger.trace('genProjection');
return (info) => {
module.exports.genProjection = ({ root, pick }) => (info) => {
try {
const context = info.fieldNodes[0];
logger.trace('returnType', info.returnType);
const type = stripType(info.returnType);
logger.trace('Stripped returnType', type);
try {
return _.reduce(_.assign, {})([root, makeProjection(
{ pick, info },
context,
'',
type,
)]);
} catch (e) {
/* istanbul ignore next */
logger.error('Projecting', e);
/* istanbul ignore next */
return undefined;
}
};
const result = _.reduce(_.assign, {})([root, makeProjection(
{ pick, info },
context,
'',
type,
)]);
logger.debug('Project result', result);
return result;
} catch (e) {
/* istanbul ignore next */
logger.error('Projecting', e);
/* istanbul ignore next */
return undefined;
}
};

@@ -5,19 +5,32 @@ const _ = require('lodash/fp');

function makeResolver(configs, pick) {
logger.trace('makeResolver', configs);
const res = _.compose(
_.fromPairs,
_.map((k) => [k, (parent, args, context, info) => {
const cfg = pick(info);
const select = _.get(['proj', k, 'select'])(cfg);
return _.get(select || k)(parent);
}]),
_.uniq,
_.flatMap(_.keys),
_.map('[1].proj'),
)(configs);
logger.trace('Generated resolver', _.keys(res));
let fn;
if (!_.isArray(configs)) {
fn = _.compose(
_.mapValues(_.get),
_.pickBy(_.identity),
_.mapValues(_.get('select')),
_.get('proj'),
);
} else {
fn = _.compose(
_.fromPairs,
_.map((k) => [k, (parent, args, context, info) => {
const cfg = pick(info);
const select = _.get(['proj', k, 'select'])(cfg);
return _.get(select || k)(parent);
}]),
_.uniq,
_.flatMap(_.keys),
_.map('[1].proj'),
);
}
const res = fn(configs);
return res;
}
const genResolvers = ({ config, pick }) => _.mergeWith(makeResolver)(config, pick);
const genResolvers = ({ config, pick }) => {
const result = _.mergeWith(makeResolver)(config, pick);
logger.info('Resolvers', result);
return result;
};

@@ -24,0 +37,0 @@ module.exports = {

@@ -1,2 +0,3 @@

const _ = require('lodash');
const _ = require('lodash/fp');
const logger = require('../logger');

@@ -22,2 +23,3 @@ function unwindPath(path) {

* [ANY]: [Number],
* [NOTNUMBER]: [Number],
* [NUMBER]: [Number],

@@ -33,6 +35,7 @@ * key: [Number],

const ANY = Symbol('any');
const NOTNUMBER = Symbol('not-number');
const NUMBER = Symbol('number');
const ACCEPT = Symbol('accept');
const extend = (NFA, states) => {
const extend = (NFA) => (states) => {
const extended = new Set();

@@ -48,31 +51,30 @@ const queue = [...states];

}
return extended;
return [...extended];
};
const run = (NFA, input) => {
let states = extend(NFA, new Set([0]));
for (let id = 0; id < input.length; id += 1) {
const char = input[id];
const next = new Set();
states.forEach((state) => {
const cfg = NFA[state];
if (cfg[ANY]) {
next.add(...cfg[ANY]);
}
if (_.isNumber(char) && cfg[NUMBER]) {
next.add(...cfg[NUMBER]);
}
if (cfg[char]) {
next.add(...cfg[char]);
}
});
if (!next.size) {
return false;
}
states = extend(NFA, next);
}
let accept = false;
states.forEach((state) => { accept = accept || NFA[state][ACCEPT]; });
return !!accept;
};
const run = (NFA) => _.compose(
_.some((state) => NFA[state][ACCEPT]),
_.compose(
_.reduce((states, char) => _.compose(
extend(NFA),
_.reduce((next, state) => {
const cfg = NFA[state];
const mer = (k) => {
if (cfg[k]) {
next.add(...cfg[k]);
}
};
mer(ANY);
if (_.isNumber(char)) {
mer(NUMBER);
} else {
mer(NOTNUMBER);
}
mer(char);
return next;
})(new Set()),
)(states)),
extend(NFA),
)(new Set([0])),
);

@@ -100,25 +102,53 @@ const append = (obj, key, val) => {

const appendExactOrNothing = (NFA, str) => {
const len = NFA.length;
append(NFA[len - 1], str, len);
append(NFA[len - 1], EPSILON, len + 1);
NFA.push({ [NUMBER]: [len], [EPSILON]: [len + 1] });
NFA.push({});
};
const matchSchema = (cfg) => {
const NFA = [{}];
cfg.forEach((c) => {
const NFA = _.reduce((n, c) => {
if (c === null) {
appendAny(NFA);
appendAny(n);
} else if (c === '') {
appendExact(n, NOTNUMBER);
} else if (c === '?') {
appendExactOrNothing(n, NOTNUMBER);
} else if (c.endsWith('?')) {
appendExactOrNothing(n, c.substr(0, c.length - 1));
} else {
appendExact(NFA, c);
appendExact(n, c);
}
});
return n;
})([{}])(cfg);
NFA[NFA.length - 1][ACCEPT] = true;
return (path) => run(NFA, path);
logger.info('NFA', NFA);
return run(NFA);
};
const matchSchemas = (cfgs) => {
const ms = cfgs.map(matchSchema);
return (path) => ms.some((m) => m(path));
};
const matchSchemas = _.compose(
_.overSome,
_.map(matchSchema),
);
const pickType = (config) => {
const matchers = config.map(([cfgs]) => matchSchemas(cfgs));
if (!_.isArray(config)) {
return _.constant(config);
}
const matchers = _.compose(
_.over,
_.map(_.compose(
matchSchemas,
_.get('0'),
)),
)(config);
return (info) => {
const path = unwindPath(info.path);
const id = matchers.findIndex((m) => m(path));
const id = _.compose(
_.findIndex(_.identity),
matchers,
unwindPath,
_.get('path'),
)(info);
if (id === -1) {

@@ -125,0 +155,0 @@ return {};

@@ -91,3 +91,3 @@ const {

it('should accept object', () => {
expect(prepareSchemaConfig({ obj: true })).toEqual([[[[null]], { obj: true }]]);
expect(prepareSchemaConfig({ obj: true })).toEqual({ obj: true });
});

@@ -157,13 +157,11 @@

expect(config).toEqual({
Obj: [
[[[null]], {
prefix: 'x',
proj: {
a: {
query: 'b',
select: 'b',
},
Obj: {
prefix: 'x',
proj: {
a: {
query: 'b',
select: 'b',
},
}],
],
},
},
});

@@ -170,0 +168,0 @@ expect(pick.Obj({})).toEqual({

@@ -77,2 +77,23 @@ const _ = require('lodash');

});
it('should accept complex 3', async (done) => {
const result = await run({
Evil: [
['obj', {
proj: {
field: 'x',
},
}],
['evil', {
proj: {
field: null,
},
}],
],
}, '{ evil { field } }', {
evil: { field: 'xxx' },
});
expect(result).toEqual({ data: { evil: { field: 'xxx' } } });
done();
});
});

@@ -45,2 +45,4 @@ const { prepareSchemaConfig } = require('../src/prepareConfig');

expect(func('a', 0, 'a', 1)).toEqual(false);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});

@@ -57,2 +59,4 @@

expect(func('a', 0, 'a', 1)).toEqual(false);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});

@@ -69,2 +73,4 @@

expect(func('a', 0, 'a', 1)).toEqual(true);
expect(func(2)).toEqual(true);
expect(func(2, 'a')).toEqual(true);
});

@@ -81,2 +87,4 @@

expect(func('a', 0, 'a', 1)).toEqual(false);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});

@@ -93,2 +101,4 @@

expect(func('a', 0, 'a', 1)).toEqual(true);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});

@@ -105,2 +115,4 @@

expect(func('a', 0, 'a', 1)).toEqual(true);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});

@@ -117,2 +129,4 @@

expect(func('a', 0, 'a', 1)).toEqual(false);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});

@@ -129,2 +143,4 @@

expect(func('a', 0, 'a', 1)).toEqual(false);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});

@@ -141,2 +157,4 @@

expect(func('a', 0, 'a', 1)).toEqual(false);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});

@@ -153,2 +171,4 @@

expect(func('a', 0, 'a', 1)).toEqual(true);
expect(func(2)).toEqual(true);
expect(func(2, 'a')).toEqual(true);
});

@@ -165,6 +185,65 @@

expect(func('a', 0, 'a', 1)).toEqual(false);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});
it('should match empty', () => {
const func = (...args) => matchSchema([''])(args);
expect(func()).toEqual(false);
expect(func('a')).toEqual(true);
expect(func('a', 'b')).toEqual(false);
expect(func('a', 0, 1)).toEqual(true);
expect(func('a', 'b', 0, 1)).toEqual(false);
expect(func('a', 'a')).toEqual(false);
expect(func('a', 0, 'a', 1)).toEqual(false);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});
it('should match empty empty', () => {
const func = (...args) => matchSchema(['', ''])(args);
expect(func()).toEqual(false);
expect(func('a')).toEqual(false);
expect(func('a', 'b')).toEqual(true);
expect(func('a', 0, 1)).toEqual(false);
expect(func('a', 'b', 0, 1)).toEqual(true);
expect(func('a', 'a')).toEqual(true);
expect(func('a', 0, 'a', 1)).toEqual(true);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});
it('should match opt empty', () => {
const func = (...args) => matchSchema(['?'])(args);
expect(func()).toEqual(true);
expect(func('a')).toEqual(true);
expect(func('a', 'b')).toEqual(false);
expect(func('a', 0, 1)).toEqual(true);
expect(func('a', 'b', 0, 1)).toEqual(false);
expect(func('a', 'a')).toEqual(false);
expect(func('a', 0, 'a', 1)).toEqual(false);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});
it('should match opt', () => {
const func = (...args) => matchSchema(['a?', 'a'])(args);
expect(func()).toEqual(false);
expect(func('a')).toEqual(true);
expect(func('a', 'b')).toEqual(false);
expect(func('a', 0, 1)).toEqual(true);
expect(func('a', 'b', 0, 1)).toEqual(false);
expect(func('a', 'a')).toEqual(true);
expect(func('a', 0, 'a', 1)).toEqual(true);
expect(func(2)).toEqual(false);
expect(func(2, 'a')).toEqual(false);
});
});
describe('pickType', () => {
it('should pick object', () => {
const config = prepareSchemaConfig({ k: 1 });
expect(pickType(config)()).toEqual(config);
});
it('should pick simple', () => {

@@ -175,3 +254,3 @@ const config = prepareSchemaConfig([

]);
expect(pickType(config)({ path: { key: 'a' } })).toBe(config[0][1]);
expect(pickType(config)({ path: { key: 'a' } })).toEqual(config[0][1]);
});

@@ -184,3 +263,3 @@

]);
expect(pickType(config)({ path: { key: 'a' } })).toBe(config[0][1]);
expect(pickType(config)({ path: { key: 'a' } })).toEqual(config[0][1]);
});

@@ -201,3 +280,3 @@

]);
expect(pickType(config)({ path: { key: 'a' } })).toBe(config[0][1]);
expect(pickType(config)({ path: { key: 'a' } })).toEqual(config[0][1]);
});

@@ -204,0 +283,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc