Skip to content

Commit 114e88f

Browse files
authored
fix(retrocompatibility): handle sequelize includables on smart field search (#850)
Co-authored-by: Thenkei <[email protected]>
1 parent 04bf19c commit 114e88f

File tree

5 files changed

+74
-27
lines changed

5 files changed

+74
-27
lines changed

src/services/query-options.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,11 @@ class QueryOptions {
189189
}
190190

191191
if (include) {
192-
this._customerIncludes.push(...include);
192+
if (Array.isArray(include)) {
193+
this._customerIncludes.push(...include);
194+
} else {
195+
this._customerIncludes.push(include);
196+
}
193197
}
194198

195199
return helper.getFieldsSearched();

src/utils/sequelize-compatibility.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import ObjectTools from './object-tools';
22
import Operators from './operators';
3-
import { isVersionLessThan } from './orm';
43
import QueryUtils from './query';
54

65
/**
@@ -86,9 +85,13 @@ function normalizeInclude(model, include) {
8685

8786
// Recurse
8887
if (include.include) {
89-
include.include = include.include.map(
90-
(childInclude) => normalizeInclude(include.model, childInclude),
91-
);
88+
if (Array.isArray(include.include)) {
89+
include.include = include.include.map(
90+
(childInclude) => normalizeInclude(include.model, childInclude),
91+
);
92+
} else {
93+
include.include = [normalizeInclude(include.model, include.include)];
94+
}
9295
}
9396

9497
return include;
@@ -143,13 +146,9 @@ exports.postProcess = (model, rawOptions) => {
143146
const options = rawOptions;
144147
const operators = Operators.getInstance({ Sequelize: model.sequelize.constructor });
145148

146-
if (isVersionLessThan(model.sequelize.constructor, '5.0.0')) {
147-
options.include = options.include.map((include) => normalizeInclude(model, include));
148-
bubbleWheresInPlace(operators, options);
149-
removeDuplicateAssociations(model, options.include);
150-
} else {
151-
bubbleWheresInPlace(operators, options);
152-
}
149+
options.include = options.include.map((include) => normalizeInclude(model, include));
150+
bubbleWheresInPlace(operators, options);
151+
removeDuplicateAssociations(model, options.include);
153152

154153
return options;
155154
};

test/services/query-options.test.js

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,27 @@ import Interface from 'forest-express';
33
import QueryOptions from '../../src/services/query-options';
44

55
describe('services > query-options', () => {
6+
const resetSchema = () => {
7+
Interface.Schemas = {
8+
schemas: {
9+
actor: {
10+
idField: 'id',
11+
fields: [{ field: 'smartField' }, { field: 'secondSmartField' }],
12+
},
13+
},
14+
};
15+
};
16+
617
const buildModelMock = (dialect) => {
718
// Sequelize is created here without connection to a database
819
const sequelize = new Sequelize({ dialect });
920

1021
const modelActor = sequelize.define('actor', {});
1122
const modelMovie = sequelize.define('movie', {});
1223

13-
modelActor.belongsTo(modelMovie);
24+
resetSchema();
1425

15-
Interface.Schemas = {
16-
schemas: {
17-
actor: {
18-
idField: 'id',
19-
fields: [{ field: 'smartField', search: (query) => { query.include = 'movie'; } }],
20-
},
21-
},
22-
};
26+
modelActor.belongsTo(modelMovie);
2327

2428
return modelActor;
2529
};
@@ -83,20 +87,27 @@ describe('services > query-options', () => {
8387
expect(loggerErrorSpy).toHaveBeenCalledWith('Cannot search properly on Smart Field smartField', errorThrown);
8488

8589
loggerErrorSpy.mockClear();
90+
resetSchema();
8691
});
8792
});
8893

89-
it('should add the search query', async () => {
94+
it('should add the search includes', async () => {
9095
expect.assertions(1);
9196

9297
Interface.Schemas.schemas.actor.fields[0].search = async (query) => {
9398
await Promise.resolve();
94-
query.include = ['movie'];
99+
query.include.push('movie');
100+
};
101+
Interface.Schemas.schemas.actor.fields[1].search = async (query) => {
102+
await Promise.resolve();
103+
query.include.push('toto');
95104
};
96105

97106
const options = new QueryOptions(model);
98107
await options.search('search string', null);
99-
expect(options._customerIncludes).toStrictEqual(['movie']);
108+
expect(options._customerIncludes).toStrictEqual(['movie', 'toto']);
109+
110+
resetSchema();
100111
});
101112
});
102113

@@ -114,6 +125,21 @@ describe('services > query-options', () => {
114125
expect(loggerErrorSpy).toHaveBeenCalledWith('Cannot search properly on Smart Field smartField', errorThrown);
115126

116127
loggerErrorSpy.mockClear();
128+
resetSchema();
129+
});
130+
});
131+
132+
describe('when smartField return none array include', () => {
133+
it('should transform include to array', async () => {
134+
expect.assertions(1);
135+
136+
Interface.Schemas.schemas.actor.fields[0].search = (query) => { query.include = 'movie'; };
137+
138+
const options = new QueryOptions(model);
139+
await options.search('search string', null);
140+
expect(options._customerIncludes).toStrictEqual(['movie']);
141+
142+
resetSchema();
117143
});
118144
});
119145
});

test/utils/sequelize-compatibility.test.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ describe('utils > sequelize-compatibility', () => {
107107
});
108108
});
109109

110-
it('shoud add attributes when both sides are defined', () => {
110+
it('should add attributes when both sides are defined', () => {
111111
expect.assertions(1);
112112

113113
const options = postProcess(Model, {
@@ -122,7 +122,7 @@ describe('utils > sequelize-compatibility', () => {
122122
});
123123
});
124124

125-
it('shoud drop attributes when either side is undefined', () => {
125+
it('should drop attributes when either side is undefined', () => {
126126
expect.assertions(1);
127127

128128
const options = postProcess(Model, {
@@ -233,5 +233,23 @@ describe('utils > sequelize-compatibility', () => {
233233
},
234234
});
235235
});
236+
237+
238+
it('should add attributes when sub include is not an array', () => {
239+
expect.assertions(1);
240+
241+
const options = postProcess(Model, {
242+
include: [{ as: 'submodelAlias', attributes: ['id'], include: 'subsubmodel1Alias' }],
243+
});
244+
245+
expect(options).toStrictEqual({
246+
include: [{
247+
as: 'submodelAlias',
248+
model: SubModel,
249+
attributes: ['id'],
250+
include: [{ as: 'subsubmodel1Alias', model: SubSubModel1 }],
251+
}],
252+
});
253+
});
236254
});
237255
});

types/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ export interface SmartFieldValueSetter<M extends Sequelize.Model = any> {
222222
}
223223

224224
export interface SmartFieldSearchQuery {
225-
include: string[],
225+
include: Sequelize.Includeable | Sequelize.Includeable[],
226226
where: Sequelize.WhereOptions,
227227
}
228228

0 commit comments

Comments
 (0)