Skip to content

Commit d216852

Browse files
authored
More specs & fix recursion detection (#30)
1 parent 1279957 commit d216852

File tree

7 files changed

+824
-66
lines changed

7 files changed

+824
-66
lines changed

dist/vuex-orm-graphql.esm.js

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10102,6 +10102,7 @@ var Context = /** @class */ (function () {
1010210102
return Context;
1010310103
}());
1010410104

10105+
var inflection$3 = require('inflection');
1010510106
/**
1010610107
* Contains all logic to build GraphQL queries/mutations.
1010710108
*/
@@ -10114,24 +10115,26 @@ var QueryBuilder = /** @class */ (function () {
1011410115
* @param {Model|string} model The model to use
1011510116
* @param {boolean} multiple Determines whether plural/nodes syntax or singular syntax is used.
1011610117
* @param {Arguments} args The args that will be passed to the query field ( user(role: $role) )
10117-
* @param {Array<Model>} ignoreRelations The models in this list are ignored (while traversing relations).
10118+
* @param {Array<Model>} path The relations in this list are ignored (while traversing relations).
1011810119
* Mainly for recursion
1011910120
* @param {string} name Optional name of the field. If not provided, this will be the model name
10121+
* @param filter
1012010122
* @param {boolean} allowIdFields Optional. Determines if id fields will be ignored for the argument generation.
1012110123
* See buildArguments
1012210124
* @returns {string}
1012310125
*
1012410126
* @todo Do we need the allowIdFields param?
1012510127
*/
10126-
QueryBuilder.buildField = function (model, multiple, args, ignoreRelations, name, filter, allowIdFields) {
10128+
QueryBuilder.buildField = function (model, multiple, args, path, name, filter, allowIdFields) {
1012710129
if (multiple === void 0) { multiple = true; }
10128-
if (ignoreRelations === void 0) { ignoreRelations = []; }
10130+
if (path === void 0) { path = []; }
1012910131
if (filter === void 0) { filter = false; }
1013010132
if (allowIdFields === void 0) { allowIdFields = false; }
1013110133
var context = Context.getInstance();
1013210134
model = context.getModel(model);
1013310135
var params = this.buildArguments(model, args, false, filter, allowIdFields);
10134-
var fields = "\n " + model.getQueryFields().join(' ') + "\n " + this.buildRelationsQuery(model, ignoreRelations) + "\n ";
10136+
path = path.length === 0 ? [model.singularName] : path;
10137+
var fields = "\n " + model.getQueryFields().join(' ') + "\n " + this.buildRelationsQuery(model, path) + "\n ";
1013510138
if (multiple) {
1013610139
var header = "" + (name ? name : model.pluralName) + params;
1013710140
if (context.connectionQueryMode === 'nodes') {
@@ -10297,12 +10300,12 @@ var QueryBuilder = /** @class */ (function () {
1029710300
* Generates the fields for all related models.
1029810301
*
1029910302
* @param {Model} model
10300-
* @param {Array<Model>} ignoreRelations The models in this list are ignored (while traversing relations).
10303+
* @param {Array<Model>} path
1030110304
* @returns {string}
1030210305
*/
10303-
QueryBuilder.buildRelationsQuery = function (model, ignoreRelations) {
10306+
QueryBuilder.buildRelationsQuery = function (model, path) {
1030410307
var _this = this;
10305-
if (ignoreRelations === void 0) { ignoreRelations = []; }
10308+
if (path === void 0) { path = []; }
1030610309
if (model === null)
1030710310
return '';
1030810311
var context = Context.getInstance();
@@ -10319,29 +10322,19 @@ var QueryBuilder = /** @class */ (function () {
1031910322
relatedModel = context.getModel(name);
1032010323
context.logger.log('WARNING: field has neither parent nor related property. Fallback to attribute name', field);
1032110324
}
10322-
if (model.shouldEagerLoadRelation(name, field, relatedModel) &&
10323-
!_this.shouldRelationBeIgnored(model, relatedModel, ignoreRelations)) {
10325+
var singularizedFieldName = inflection$3.singularize(name);
10326+
var ignore = path.includes(singularizedFieldName);
10327+
// console.log(`-----> Will ${ignore ? '' : 'not'} ignore ${model.singularName}.${name}, path: ${path.join('.')}`);
10328+
if (model.shouldEagerLoadRelation(name, field, relatedModel) && !ignore) {
1032410329
var multiple = !(field instanceof context.components.BelongsTo ||
1032510330
field instanceof context.components.HasOne);
10326-
ignoreRelations.push(model.singularName + "." + relatedModel.singularName);
10327-
relationQueries.push(_this.buildField(relatedModel, multiple, undefined, ignoreRelations, name, false));
10331+
var newPath = path.slice(0);
10332+
newPath.push(singularizedFieldName);
10333+
relationQueries.push(_this.buildField(relatedModel, multiple, undefined, newPath, name, false));
1032810334
}
1032910335
});
1033010336
return relationQueries.join('\n');
1033110337
};
10332-
/**
10333-
* Tells if a relation should be ignored because it's included in the ignoreRelations array.
10334-
* @param {Model} model
10335-
* @param {Model} relatedModel
10336-
* @param {Array<string>} ignoreRelations
10337-
* @returns {boolean}
10338-
*/
10339-
QueryBuilder.shouldRelationBeIgnored = function (model, relatedModel, ignoreRelations) {
10340-
var relevantRelation = model.singularName + "." + relatedModel.singularName;
10341-
return ignoreRelations.find(function (r) {
10342-
return r === relevantRelation;
10343-
}) !== undefined;
10344-
};
1034510338
return QueryBuilder;
1034610339
}());
1034710340

@@ -10495,7 +10488,7 @@ var __generator$3 = (undefined && undefined.__generator) || function (thisArg, b
1049510488
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
1049610489
}
1049710490
};
10498-
var inflection$3 = require('inflection');
10491+
var inflection$4 = require('inflection');
1049910492
/**
1050010493
* Base class for all Vuex actions. Contains some utility and convenience methods.
1050110494
*/
@@ -10592,7 +10585,7 @@ var Action = /** @class */ (function () {
1059210585
Object.keys(args).forEach(function (key) {
1059310586
var value = args[key];
1059410587
if (value instanceof context.components.Model) {
10595-
var model = context.getModel(inflection$3.singularize(value.$self().entity));
10588+
var model = context.getModel(inflection$4.singularize(value.$self().entity));
1059610589
var transformedValue = Transformer.transformOutgoingData(model, value);
1059710590
context.logger.log('A', key, 'model was found within the variables and will be transformed from', value, 'to', transformedValue);
1059810591
args[key] = transformedValue;

src/graphql/query-builder.ts

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Arguments, Field } from '../support/interfaces';
33
import { upcaseFirstLetter } from '../support/utils';
44
import gql from 'graphql-tag';
55
import Context from '../common/context';
6+
const inflection = require('inflection');
67

78
/**
89
* Contains all logic to build GraphQL queries/mutations.
@@ -14,9 +15,10 @@ export default class QueryBuilder {
1415
* @param {Model|string} model The model to use
1516
* @param {boolean} multiple Determines whether plural/nodes syntax or singular syntax is used.
1617
* @param {Arguments} args The args that will be passed to the query field ( user(role: $role) )
17-
* @param {Array<Model>} ignoreRelations The models in this list are ignored (while traversing relations).
18+
* @param {Array<Model>} path The relations in this list are ignored (while traversing relations).
1819
* Mainly for recursion
1920
* @param {string} name Optional name of the field. If not provided, this will be the model name
21+
* @param filter
2022
* @param {boolean} allowIdFields Optional. Determines if id fields will be ignored for the argument generation.
2123
* See buildArguments
2224
* @returns {string}
@@ -26,7 +28,7 @@ export default class QueryBuilder {
2628
public static buildField (model: Model | string,
2729
multiple: boolean = true,
2830
args?: Arguments,
29-
ignoreRelations: Array<string> = [],
31+
path: Array<string> = [],
3032
name?: string,
3133
filter: boolean = false,
3234
allowIdFields: boolean = false): string {
@@ -35,10 +37,11 @@ export default class QueryBuilder {
3537
model = context.getModel(model);
3638

3739
let params: string = this.buildArguments(model, args, false, filter, allowIdFields);
40+
path = path.length === 0 ? [model.singularName] : path;
3841

3942
const fields = `
4043
${model.getQueryFields().join(' ')}
41-
${this.buildRelationsQuery(model, ignoreRelations)}
44+
${this.buildRelationsQuery(model, path)}
4245
`;
4346

4447
if (multiple) {
@@ -233,10 +236,10 @@ export default class QueryBuilder {
233236
* Generates the fields for all related models.
234237
*
235238
* @param {Model} model
236-
* @param {Array<Model>} ignoreRelations The models in this list are ignored (while traversing relations).
239+
* @param {Array<Model>} path
237240
* @returns {string}
238241
*/
239-
private static buildRelationsQuery (model: (null | Model), ignoreRelations: Array<string> = []): string {
242+
private static buildRelationsQuery (model: (null | Model), path: Array<string> = []): string {
240243
if (model === null) return '';
241244

242245
const context = Context.getInstance();
@@ -254,31 +257,22 @@ export default class QueryBuilder {
254257
context.logger.log('WARNING: field has neither parent nor related property. Fallback to attribute name', field);
255258
}
256259

257-
if (model.shouldEagerLoadRelation(name, field, relatedModel) &&
258-
!this.shouldRelationBeIgnored(model, relatedModel, ignoreRelations)) {
260+
const singularizedFieldName = inflection.singularize(name);
261+
const ignore = path.includes(singularizedFieldName);
262+
263+
// console.log(`-----> Will ${ignore ? '' : 'not'} ignore ${model.singularName}.${name}, path: ${path.join('.')}`);
259264

265+
if (model.shouldEagerLoadRelation(name, field, relatedModel) && !ignore) {
260266
const multiple: boolean = !(field instanceof context.components.BelongsTo ||
261267
field instanceof context.components.HasOne);
262268

263-
ignoreRelations.push(`${model.singularName}.${relatedModel.singularName}`);
264-
relationQueries.push(this.buildField(relatedModel, multiple, undefined, ignoreRelations, name, false));
269+
const newPath = path.slice(0);
270+
newPath.push(singularizedFieldName);
271+
272+
relationQueries.push(this.buildField(relatedModel, multiple, undefined, newPath, name, false));
265273
}
266274
});
267275

268276
return relationQueries.join('\n');
269277
}
270-
271-
/**
272-
* Tells if a relation should be ignored because it's included in the ignoreRelations array.
273-
* @param {Model} model
274-
* @param {Model} relatedModel
275-
* @param {Array<string>} ignoreRelations
276-
* @returns {boolean}
277-
*/
278-
private static shouldRelationBeIgnored (model: Model, relatedModel: Model, ignoreRelations: Array<string>): boolean {
279-
const relevantRelation = `${model.singularName}.${relatedModel.singularName}`;
280-
return ignoreRelations.find((r) => {
281-
return r === relevantRelation;
282-
}) !== undefined;
283-
}
284278
}

0 commit comments

Comments
 (0)