1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.SequelizeAdapter = void 0;
|
4 | const errors_1 = require("@feathersjs/errors");
|
5 | const commons_1 = require("@feathersjs/commons");
|
6 | const adapter_commons_1 = require("@feathersjs/adapter-commons");
|
7 | const utils_1 = require("./utils");
|
8 | const sequelize_1 = require("sequelize");
|
9 | const defaultOpMap = () => {
|
10 | return {
|
11 | $eq: sequelize_1.Op.eq,
|
12 | $ne: sequelize_1.Op.ne,
|
13 | $gte: sequelize_1.Op.gte,
|
14 | $gt: sequelize_1.Op.gt,
|
15 | $lte: sequelize_1.Op.lte,
|
16 | $lt: sequelize_1.Op.lt,
|
17 | $in: sequelize_1.Op.in,
|
18 | $nin: sequelize_1.Op.notIn,
|
19 | $like: sequelize_1.Op.like,
|
20 | $notLike: sequelize_1.Op.notLike,
|
21 | $iLike: sequelize_1.Op.iLike,
|
22 | $notILike: sequelize_1.Op.notILike,
|
23 | $or: sequelize_1.Op.or,
|
24 | $and: sequelize_1.Op.and
|
25 | };
|
26 | };
|
27 | const defaultFilters = () => {
|
28 | return {
|
29 | $returning: (value) => {
|
30 | if (value === true || value === false || value === undefined) {
|
31 | return value;
|
32 | }
|
33 | throw new errors_1.BadRequest('Invalid $returning filter value');
|
34 | },
|
35 | $and: true
|
36 | };
|
37 | };
|
38 | class SequelizeAdapter extends adapter_commons_1.AdapterBase {
|
39 | constructor(options) {
|
40 | if (!options.Model) {
|
41 | throw new Error('You must provide a Sequelize Model');
|
42 | }
|
43 | if (options.operators && !Array.isArray(options.operators)) {
|
44 | throw new Error('The \'operators\' option must be an array. For migration from feathers.js v4 see: https://github.com/feathersjs-ecosystem/feathers-sequelize/tree/dove#migrate-to-feathers-v5-dove');
|
45 | }
|
46 | const operatorMap = Object.assign(defaultOpMap(), options.operatorMap);
|
47 | const operators = Object.keys(operatorMap);
|
48 | if (options.operators) {
|
49 | options.operators.forEach(op => {
|
50 | if (!operators.includes(op)) {
|
51 | operators.push(op);
|
52 | }
|
53 | });
|
54 | }
|
55 | const { primaryKeyAttributes } = options.Model;
|
56 | const id = typeof primaryKeyAttributes === 'object' && primaryKeyAttributes[0] !== undefined
|
57 | ? primaryKeyAttributes[0]
|
58 | : 'id';
|
59 | const filters = Object.assign(defaultFilters(), options.filters);
|
60 | super(Object.assign({ id }, options, { operatorMap, filters, operators }));
|
61 | }
|
62 | get raw() {
|
63 | return this.options.raw !== false;
|
64 | }
|
65 | get Op() {
|
66 |
|
67 | return this.options.Model.sequelize.Sequelize.Op;
|
68 | }
|
69 | get Model() {
|
70 | if (!this.options.Model) {
|
71 | throw new Error('The Model getter was called with no Model provided in options!');
|
72 | }
|
73 | return this.options.Model;
|
74 | }
|
75 |
|
76 | getModel(_params) {
|
77 | if (!this.options.Model) {
|
78 | throw new Error('getModel was called without a Model present in the constructor options and without overriding getModel! Perhaps you intended to override getModel in a child class?');
|
79 | }
|
80 | return this.options.Model;
|
81 | }
|
82 | |
83 |
|
84 |
|
85 | applyScope(params) {
|
86 | var _a;
|
87 | const Model = this.getModel(params);
|
88 | if ((_a = params === null || params === void 0 ? void 0 : params.sequelize) === null || _a === void 0 ? void 0 : _a.scope) {
|
89 | return Model.scope(params.sequelize.scope);
|
90 | }
|
91 | return Model;
|
92 | }
|
93 | ModelWithScope(params) {
|
94 | return this.applyScope(params);
|
95 | }
|
96 | convertOperators(q) {
|
97 | if (Array.isArray(q)) {
|
98 | return q.map(subQuery => this.convertOperators(subQuery));
|
99 | }
|
100 | if (!(0, utils_1.isPlainObject)(q)) {
|
101 | return q;
|
102 | }
|
103 | const { operatorMap } = this.options;
|
104 | const converted = Object.keys(q).reduce((result, prop) => {
|
105 | const value = q[prop];
|
106 | const key = (operatorMap[prop] ? operatorMap[prop] : prop);
|
107 | result[key] = this.convertOperators(value);
|
108 | return result;
|
109 | }, {});
|
110 | Object.getOwnPropertySymbols(q).forEach(symbol => {
|
111 | converted[symbol] = q[symbol];
|
112 | });
|
113 | return converted;
|
114 | }
|
115 | filterQuery(params) {
|
116 | const options = this.getOptions(params);
|
117 | const { filters, query: _query } = (0, adapter_commons_1.filterQuery)(params.query || {}, options);
|
118 | const query = this.convertOperators({
|
119 | ..._query,
|
120 | ...commons_1._.omit(filters, '$select', '$skip', '$limit', '$sort')
|
121 | });
|
122 | return {
|
123 | filters,
|
124 | query,
|
125 | paginate: options.paginate
|
126 | };
|
127 | }
|
128 | paramsToAdapter(id, _params) {
|
129 | const params = _params || {};
|
130 | if (id) {
|
131 | const { query: where } = this.filterQuery(params);
|
132 | const { and } = this.Op;
|
133 |
|
134 | const q = Object.assign({
|
135 | raw: this.raw,
|
136 | where: Object.assign(where, {
|
137 | [and]: where[and] ? [...where[and], { [this.id]: id }] : { [this.id]: id }
|
138 | })
|
139 | }, params.sequelize);
|
140 | return q;
|
141 | }
|
142 | else {
|
143 | const { filters, query: where } = this.filterQuery(params);
|
144 | const order = (0, utils_1.getOrder)(filters.$sort);
|
145 | const q = Object.assign({
|
146 | where,
|
147 | order,
|
148 | limit: filters.$limit,
|
149 | offset: filters.$skip,
|
150 | raw: this.raw,
|
151 | distinct: true
|
152 | }, params.sequelize);
|
153 | if (filters.$select) {
|
154 |
|
155 | if (!filters.$select.includes(this.id)) {
|
156 | filters.$select.push(this.id);
|
157 | }
|
158 | q.attributes = filters.$select.map((select) => `${select}`);
|
159 | }
|
160 |
|
161 |
|
162 |
|
163 | q.include = q.include || [];
|
164 | return q;
|
165 | }
|
166 | }
|
167 | async _getOrFind(id, _params) {
|
168 | const params = _params || {};
|
169 | if (id === null) {
|
170 | return await this._find({
|
171 | ...params,
|
172 | paginate: false
|
173 | });
|
174 | }
|
175 | return await this._get(id, params);
|
176 | }
|
177 | async _find(params = {}) {
|
178 | const { paginate } = this.filterQuery(params);
|
179 | const Model = this.ModelWithScope(params);
|
180 | const q = this.paramsToAdapter(null, params);
|
181 | try {
|
182 | if (paginate && paginate.default) {
|
183 | const result = await Model.findAndCountAll(q);
|
184 | return {
|
185 | total: result.count,
|
186 | limit: q.limit,
|
187 | skip: q.offset || 0,
|
188 | data: result.rows
|
189 | };
|
190 | }
|
191 | return await Model.findAll(q);
|
192 | }
|
193 | catch (err) {
|
194 | return (0, utils_1.errorHandler)(err);
|
195 | }
|
196 | }
|
197 | async _get(id, params = {}) {
|
198 | const Model = this.ModelWithScope(params);
|
199 | const q = this.paramsToAdapter(id, params);
|
200 |
|
201 |
|
202 | try {
|
203 | const result = await Model.findAll(q);
|
204 | if (result.length === 0) {
|
205 | throw new errors_1.NotFound(`No record found for id '${id}'`);
|
206 | }
|
207 | const item = result[0];
|
208 | return (0, adapter_commons_1.select)(params, this.id)(item);
|
209 | }
|
210 | catch (err) {
|
211 | return (0, utils_1.errorHandler)(err);
|
212 | }
|
213 | }
|
214 | async _create(data, params = {}) {
|
215 | if (Array.isArray(data) && !this.allowsMulti('create', params)) {
|
216 | throw new errors_1.MethodNotAllowed('Can not create multiple entries');
|
217 | }
|
218 | const options = Object.assign({ raw: this.raw }, params.sequelize);
|
219 |
|
220 |
|
221 |
|
222 |
|
223 | const ignoreSetters = !!options.ignoreSetters;
|
224 | const createOptions = Object.assign({
|
225 | returning: true
|
226 | }, options, { raw: ignoreSetters });
|
227 | const isArray = Array.isArray(data);
|
228 | const Model = this.ModelWithScope(params);
|
229 | try {
|
230 | const result = isArray
|
231 | ? await Model.bulkCreate(data, createOptions)
|
232 | : await Model.create(data, createOptions);
|
233 | const sel = (0, adapter_commons_1.select)(params, this.id);
|
234 | if (options.raw === false) {
|
235 | return result;
|
236 | }
|
237 | if (isArray) {
|
238 | return result.map(item => sel(item.toJSON()));
|
239 | }
|
240 | return sel(result.toJSON());
|
241 | }
|
242 | catch (err) {
|
243 | return (0, utils_1.errorHandler)(err);
|
244 | }
|
245 | }
|
246 | async _patch(id, data, params = {}) {
|
247 | var _a, _b;
|
248 | if (id === null && !this.allowsMulti('patch', params)) {
|
249 | throw new errors_1.MethodNotAllowed('Can not patch multiple entries');
|
250 | }
|
251 | const Model = this.ModelWithScope(params);
|
252 |
|
253 |
|
254 | const itemOrItems = await this._getOrFind(id, {
|
255 | ...params,
|
256 | query: {
|
257 | ...params === null || params === void 0 ? void 0 : params.query,
|
258 | $select: [this.id]
|
259 | }
|
260 | });
|
261 | const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
|
262 | const ids = items.map(item => item[this.id]);
|
263 | try {
|
264 | const seqOptions = Object.assign({ raw: this.raw }, params.sequelize, { where: { [this.id]: { [this.Op.in]: ids } } });
|
265 | await Model.update(commons_1._.omit(data, this.id), seqOptions);
|
266 | if (params.$returning === false) {
|
267 | return Promise.resolve([]);
|
268 | }
|
269 |
|
270 |
|
271 | const findParams = {
|
272 | ...params,
|
273 | query: {
|
274 | [this.id]: { $in: ids },
|
275 | ...(((_a = params === null || params === void 0 ? void 0 : params.query) === null || _a === void 0 ? void 0 : _a.$select) ? { $select: (_b = params === null || params === void 0 ? void 0 : params.query) === null || _b === void 0 ? void 0 : _b.$select } : {})
|
276 | }
|
277 | };
|
278 | const result = await this._getOrFind(id, findParams);
|
279 | return (0, adapter_commons_1.select)(params, this.id)(result);
|
280 | }
|
281 | catch (err) {
|
282 | return (0, utils_1.errorHandler)(err);
|
283 | }
|
284 | }
|
285 | async _update(id, data, params = {}) {
|
286 | var _a;
|
287 | const query = Object.assign({}, this.filterQuery(params).query);
|
288 |
|
289 | const seqOptions = Object.assign({}, params.sequelize, { raw: false });
|
290 | const instance = await this._get(id, { sequelize: seqOptions, query });
|
291 | const itemToUpdate = Object.keys(instance.toJSON()).reduce((result, key) => {
|
292 |
|
293 | result[key] = key in data ? data[key] : null;
|
294 | return result;
|
295 | }, {});
|
296 | try {
|
297 | await instance.update(itemToUpdate, seqOptions);
|
298 | const item = await this._get(id, {
|
299 | sequelize: Object.assign({}, seqOptions, {
|
300 | raw: typeof ((_a = params === null || params === void 0 ? void 0 : params.sequelize) === null || _a === void 0 ? void 0 : _a.raw) === 'boolean'
|
301 | ? params.sequelize.raw
|
302 | : this.raw
|
303 | })
|
304 | });
|
305 | return (0, adapter_commons_1.select)(params, this.id)(item);
|
306 | }
|
307 | catch (err) {
|
308 | return (0, utils_1.errorHandler)(err);
|
309 | }
|
310 | }
|
311 | async _remove(id, params = {}) {
|
312 | var _a;
|
313 | if (id === null && !this.allowsMulti('remove', params)) {
|
314 | throw new errors_1.MethodNotAllowed('Can not remove multiple entries');
|
315 | }
|
316 | const Model = this.ModelWithScope(params);
|
317 | const findParams = { ...params };
|
318 | if (params.$returning === false) {
|
319 | findParams.query = {
|
320 | ...findParams.query,
|
321 | $select: [this.id]
|
322 | };
|
323 | }
|
324 | else if ((_a = params.query) === null || _a === void 0 ? void 0 : _a.$select) {
|
325 | findParams.query = {
|
326 | ...findParams.query,
|
327 | $select: [...params.query.$select, this.id]
|
328 | };
|
329 | }
|
330 | const itemOrItems = await this._getOrFind(id, findParams);
|
331 | const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
|
332 | const ids = items.map(item => item[this.id]);
|
333 | const seqOptions = Object.assign({ raw: this.raw }, params.sequelize, { where: { [this.id]: { [this.Op.in]: ids } } });
|
334 | try {
|
335 | await Model.destroy(seqOptions);
|
336 | if (params.$returning === false) {
|
337 | return [];
|
338 | }
|
339 | return (0, adapter_commons_1.select)(params, this.id)(itemOrItems);
|
340 | }
|
341 | catch (err) {
|
342 | return (0, utils_1.errorHandler)(err);
|
343 | }
|
344 | }
|
345 | }
|
346 | exports.SequelizeAdapter = SequelizeAdapter;
|
347 |
|
\ | No newline at end of file |