Socket
Socket
Sign inDemoInstall

feathers-sequelize

Package Overview
Dependencies
Maintainers
2
Versions
84
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

feathers-sequelize - npm Package Compare versions

Comparing version 7.0.3 to 8.0.0-pre.0

20

lib/adapter.d.ts

@@ -5,13 +5,13 @@ import { AdapterBase } from '@feathersjs/adapter-commons';

import type { Id, NullableId, Paginated, Query } from '@feathersjs/feathers';
import type { FindOptions } from 'sequelize';
import { Op } from 'sequelize';
export declare class SequelizeAdapter<Result, Data = Partial<Result>, ServiceParams extends SequelizeAdapterParams = SequelizeAdapterParams, PatchData = Partial<Data>> extends AdapterBase<Result, Data, PatchData, ServiceParams, SequelizeAdapterOptions> {
constructor(options: SequelizeAdapterOptions);
get raw(): boolean;
get Op(): any;
/**
*
* @deprecated Use `Op` from `sequelize` directly
*/
get Op(): typeof Op;
get Model(): import("sequelize").ModelStatic<any>;
getModel(_params?: ServiceParams): import("sequelize").ModelStatic<any>;
/**
* @deprecated use 'service.ModelWithScope' instead. 'applyScope' will be removed in a future release.
*/
applyScope(params?: ServiceParams): import("sequelize").ModelStatic<any>;
ModelWithScope(params: ServiceParams): import("sequelize").ModelStatic<any>;

@@ -26,9 +26,3 @@ convertOperators(q: any): Query;

};
paramsToAdapter(id: NullableId, _params?: ServiceParams): FindOptions;
/**
* returns either the model instance / jsonified object for an id or all unpaginated
* items for `params` if id is null
*/
_getOrFind(id: Id, _params?: ServiceParams): Promise<Result>;
_getOrFind(id: null, _params?: ServiceParams): Promise<Result[]>;
paramsToAdapter(id: NullableId, _params?: ServiceParams): any;
_find(params?: ServiceParams & {

@@ -35,0 +29,0 @@ paginate?: PaginationOptions;

@@ -9,30 +9,20 @@ "use strict";

const sequelize_1 = require("sequelize");
const defaultOpMap = () => {
return {
$eq: sequelize_1.Op.eq,
$ne: sequelize_1.Op.ne,
$gte: sequelize_1.Op.gte,
$gt: sequelize_1.Op.gt,
$lte: sequelize_1.Op.lte,
$lt: sequelize_1.Op.lt,
$in: sequelize_1.Op.in,
$nin: sequelize_1.Op.notIn,
$like: sequelize_1.Op.like,
$notLike: sequelize_1.Op.notLike,
$iLike: sequelize_1.Op.iLike,
$notILike: sequelize_1.Op.notILike,
$or: sequelize_1.Op.or,
$and: sequelize_1.Op.and
};
const defaultOperatorMap = {
$eq: sequelize_1.Op.eq,
$ne: sequelize_1.Op.ne,
$gte: sequelize_1.Op.gte,
$gt: sequelize_1.Op.gt,
$lte: sequelize_1.Op.lte,
$lt: sequelize_1.Op.lt,
$in: sequelize_1.Op.in,
$nin: sequelize_1.Op.notIn,
$like: sequelize_1.Op.like,
$notLike: sequelize_1.Op.notLike,
$iLike: sequelize_1.Op.iLike,
$notILike: sequelize_1.Op.notILike,
$or: sequelize_1.Op.or,
$and: sequelize_1.Op.and
};
const defaultFilters = () => {
return {
$returning: (value) => {
if (value === true || value === false || value === undefined) {
return value;
}
throw new errors_1.BadRequest('Invalid $returning filter value');
},
$and: true
};
const defaultFilters = {
$and: true
};

@@ -47,3 +37,6 @@ class SequelizeAdapter extends adapter_commons_1.AdapterBase {

}
const operatorMap = Object.assign(defaultOpMap(), options.operatorMap);
const operatorMap = {
...defaultOperatorMap,
...options.operatorMap
};
const operators = Object.keys(operatorMap);

@@ -61,4 +54,13 @@ if (options.operators) {

: 'id';
const filters = Object.assign(defaultFilters(), options.filters);
super(Object.assign({ id }, options, { operatorMap, filters, operators }));
const filters = {
...defaultFilters,
...options.filters
};
super({
id,
...options,
operatorMap,
filters,
operators
});
}

@@ -68,5 +70,8 @@ get raw() {

}
/**
*
* @deprecated Use `Op` from `sequelize` directly
*/
get Op() {
// @ts-ignore
return this.options.Model.sequelize.Sequelize.Op;
return sequelize_1.Op;
}

@@ -86,6 +91,3 @@ get Model() {

}
/**
* @deprecated use 'service.ModelWithScope' instead. 'applyScope' will be removed in a future release.
*/
applyScope(params) {
ModelWithScope(params) {
var _a;

@@ -98,5 +100,2 @@ const Model = this.getModel(params);

}
ModelWithScope(params) {
return this.applyScope(params);
}
convertOperators(q) {

@@ -128,2 +127,8 @@ if (Array.isArray(q)) {

});
if (filters.$select) {
if (!filters.$select.includes(this.id)) {
filters.$select.push(this.id);
}
filters.$select = filters.$select.map((select) => `${select}`);
}
return {

@@ -135,122 +140,126 @@ filters,

}
// paramsToAdapter (id: NullableId, _params?: ServiceParams): FindOptions {
paramsToAdapter(id, _params) {
const params = _params || {};
if (id) {
const { query: where } = this.filterQuery(params);
const { and } = this.Op;
// Attach 'where' constraints, if any were used.
const q = Object.assign({
raw: this.raw,
where: Object.assign(where, {
[and]: where[and] ? [...where[and], { [this.id]: id }] : { [this.id]: id }
})
}, params.sequelize);
return q;
}
else {
const { filters, query: where } = this.filterQuery(params);
const order = (0, utils_1.getOrder)(filters.$sort);
const q = Object.assign({
where,
order,
const { filters, query: where } = this.filterQuery(params);
// Until Sequelize fix all the findAndCount issues, a few 'hacks' are needed to get the total count correct
// Adding an empty include changes the way the count is done
// See: https://github.com/sequelize/sequelize/blob/7e441a6a5ca44749acd3567b59b1d6ceb06ae64b/lib/model.js#L1780-L1782
// sequelize.include = sequelize.include || [];
const defaults = {
where,
attributes: filters.$select,
distinct: true,
returning: true,
raw: this.raw,
...params.sequelize
};
if (id === null) {
return {
order: (0, utils_1.getOrder)(filters.$sort),
limit: filters.$limit,
offset: filters.$skip,
raw: this.raw,
distinct: true
}, params.sequelize);
if (filters.$select) {
// Add the id to the select if it is not already there
if (!filters.$select.includes(this.id)) {
filters.$select.push(this.id);
}
q.attributes = filters.$select.map((select) => `${select}`);
}
// Until Sequelize fix all the findAndCount issues, a few 'hacks' are needed to get the total count correct
// Adding an empty include changes the way the count is done
// See: https://github.com/sequelize/sequelize/blob/7e441a6a5ca44749acd3567b59b1d6ceb06ae64b/lib/model.js#L1780-L1782
q.include = q.include || [];
return q;
...defaults
};
}
}
async _getOrFind(id, _params) {
const params = _params || {};
if (id === null) {
return await this._find({
...params,
paginate: false
});
const sequelize = {
limit: 1,
...defaults
};
if (where[this.id] === id) {
return sequelize;
}
return await this._get(id, params);
if (this.id in where) {
const { and } = sequelize_1.Op;
where[and] = where[and]
? [...where[and], { [this.id]: id }]
: { [this.id]: id };
}
else {
where[this.id] = id;
}
return sequelize;
}
async _find(params = {}) {
const Model = this.ModelWithScope(params);
const { paginate } = this.filterQuery(params);
const Model = this.ModelWithScope(params);
const q = this.paramsToAdapter(null, params);
try {
if (paginate && paginate.default) {
const result = await Model.findAndCountAll(q);
return {
total: result.count,
limit: q.limit,
skip: q.offset || 0,
data: result.rows
};
}
return await Model.findAll(q);
const sequelizeOptions = this.paramsToAdapter(null, params);
if (!paginate || !paginate.default) {
const result = await Model.findAll(sequelizeOptions).catch(utils_1.errorHandler);
return result;
}
catch (err) {
return (0, utils_1.errorHandler)(err);
if (sequelizeOptions.limit === 0) {
const total = (await Model
.count({ ...sequelizeOptions, attributes: undefined })
.catch(utils_1.errorHandler));
return {
total,
limit: sequelizeOptions.limit,
skip: sequelizeOptions.offset || 0,
data: []
};
}
const result = await Model.findAndCountAll(sequelizeOptions).catch(utils_1.errorHandler);
return {
total: result.count,
limit: sequelizeOptions.limit,
skip: sequelizeOptions.offset || 0,
data: result.rows
};
}
async _get(id, params = {}) {
const Model = this.ModelWithScope(params);
const q = this.paramsToAdapter(id, params);
// findById calls findAll under the hood. We use findAll so that
// eager loading can be used without a separate code path.
try {
const result = await Model.findAll(q);
if (result.length === 0) {
throw new errors_1.NotFound(`No record found for id '${id}'`);
}
const item = result[0];
return (0, adapter_commons_1.select)(params, this.id)(item);
const sequelizeOptions = this.paramsToAdapter(id, params);
const result = await Model.findAll(sequelizeOptions).catch(utils_1.errorHandler);
if (result.length === 0) {
throw new errors_1.NotFound(`No record found for id '${id}'`);
}
catch (err) {
return (0, utils_1.errorHandler)(err);
}
return result[0];
}
async _create(data, params = {}) {
if (Array.isArray(data) && !this.allowsMulti('create', params)) {
const isArray = Array.isArray(data);
const select = (0, adapter_commons_1.select)(params, this.id);
if (isArray && !this.allowsMulti('create', params)) {
throw new errors_1.MethodNotAllowed('Can not create multiple entries');
}
const options = Object.assign({ raw: this.raw }, params.sequelize);
// Model.create's `raw` option is different from other methods.
// In order to use `raw` consistently to serialize the result,
// we need to shadow the Model.create use of raw, which we provide
// access to by specifying `ignoreSetters`.
const ignoreSetters = !!options.ignoreSetters;
const createOptions = Object.assign({
returning: true
}, options, { raw: ignoreSetters });
const isArray = Array.isArray(data);
if (isArray && data.length === 0) {
return [];
}
const Model = this.ModelWithScope(params);
try {
const result = isArray
? await Model.bulkCreate(data, createOptions)
: await Model.create(data, createOptions);
const sel = (0, adapter_commons_1.select)(params, this.id);
if (options.raw === false) {
const sequelizeOptions = this.paramsToAdapter(null, params);
if (isArray) {
const instances = await Model
.bulkCreate(data, sequelizeOptions)
.catch(utils_1.errorHandler);
if (sequelizeOptions.returning === false) {
return [];
}
if (sequelizeOptions.raw) {
const result = instances.map((instance) => {
if ((0, utils_1.isPresent)(sequelizeOptions.attributes)) {
return select(instance.toJSON());
}
return instance.toJSON();
});
return result;
}
if (isArray) {
return result.map(item => sel(item.toJSON()));
if ((0, utils_1.isPresent)(sequelizeOptions.attributes)) {
const result = instances.map((instance) => {
const result = select(instance.toJSON());
return Model.build(result, { isNewRecord: false });
});
return result;
}
return sel(result.toJSON());
return instances;
}
catch (err) {
return (0, utils_1.errorHandler)(err);
const result = await Model
.create(data, sequelizeOptions)
.catch(utils_1.errorHandler);
if (sequelizeOptions.raw) {
return select(result.toJSON());
}
return result;
}
async _patch(id, data, params = {}) {
var _a, _b;
var _a, _b, _c, _d;
if (id === null && !this.allowsMulti('patch', params)) {

@@ -260,60 +269,146 @@ throw new errors_1.MethodNotAllowed('Can not patch multiple entries');

const Model = this.ModelWithScope(params);
// Get a list of ids that match the id/query. Overwrite the
// $select because only the id is needed for this idList
const itemOrItems = await this._getOrFind(id, {
...params,
query: {
...params === null || params === void 0 ? void 0 : params.query,
$select: [this.id]
const sequelizeOptions = this.paramsToAdapter(id, params);
const select = (0, adapter_commons_1.select)(params, this.id);
const values = commons_1._.omit(data, this.id);
if (id === null) {
const current = await this._find({
...params,
paginate: false,
query: {
...params === null || params === void 0 ? void 0 : params.query,
$select: [this.id]
}
});
if (!current.length) {
return [];
}
});
const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
const ids = items.map(item => item[this.id]);
try {
const seqOptions = Object.assign({ raw: this.raw }, params.sequelize, { where: { [this.id]: { [this.Op.in]: ids } } });
await Model.update(commons_1._.omit(data, this.id), seqOptions);
if (params.$returning === false) {
return Promise.resolve([]);
const ids = current.map((item) => item[this.id]);
let [, instances] = await Model
.update(values, {
...sequelizeOptions,
where: { [this.id]: ids.length === 1 ? ids[0] : { [sequelize_1.Op.in]: ids } }
})
.catch(utils_1.errorHandler);
if (sequelizeOptions.returning === false) {
return [];
}
// Create a new query that re-queries all ids that
// were originally changed
const findParams = {
// Returning is only supported in postgres and mssql, and
// its a little goofy array order how Sequelize handles it.
// https://github.com/sequelize/sequelize/blob/abca55ee52d959f95c98dc7ae8b8162005536d05/packages/core/src/model.js#L3110
if (!instances || typeof instances === 'number') {
instances = null;
}
const hasAttributes = (0, utils_1.isPresent)(sequelizeOptions.attributes);
if (instances) {
if ((0, utils_1.isPresent)((_a = params.query) === null || _a === void 0 ? void 0 : _a.$sort)) {
const sortedInstances = [];
const unsortedInstances = [];
current.forEach((item) => {
const id = item[this.id];
const instance = instances.find(instance => instance[this.id] === id);
if (instance) {
sortedInstances.push(instance);
}
else {
unsortedInstances.push(item);
}
});
instances = [...sortedInstances, ...unsortedInstances];
}
if (sequelizeOptions.raw) {
const result = instances.map((instance) => {
if (hasAttributes) {
return select(instance.toJSON());
}
return instance.toJSON();
});
return result;
}
if (hasAttributes) {
const result = instances.map((instance) => {
const result = select(instance.toJSON());
return Model.build(result, { isNewRecord: false });
});
return result;
}
return instances;
}
const result = await this._find({
...params,
paginate: false,
query: {
[this.id]: { $in: ids },
...(((_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 } : {})
[this.id]: ids.length === 1 ? ids[0] : { $in: ids },
$select: (_b = params === null || params === void 0 ? void 0 : params.query) === null || _b === void 0 ? void 0 : _b.$select,
$sort: (_c = params === null || params === void 0 ? void 0 : params.query) === null || _c === void 0 ? void 0 : _c.$sort
}
};
const result = await this._getOrFind(id, findParams);
return (0, adapter_commons_1.select)(params, this.id)(result);
});
return result;
}
catch (err) {
return (0, utils_1.errorHandler)(err);
const instance = await this._get(id, {
...params,
sequelize: { ...params.sequelize, raw: false }
});
await instance
.set(values)
.update(values, sequelizeOptions)
.catch(utils_1.errorHandler);
if ((0, utils_1.isPresent)(sequelizeOptions.include)) {
return this._get(id, {
...params,
query: { $select: (_d = params.query) === null || _d === void 0 ? void 0 : _d.$select }
});
}
if (sequelizeOptions.raw) {
const result = instance.toJSON();
if ((0, utils_1.isPresent)(sequelizeOptions.attributes)) {
return select(result);
}
return result;
}
if ((0, utils_1.isPresent)(sequelizeOptions.attributes)) {
const result = select(instance.toJSON());
return Model.build(result, { isNewRecord: false });
}
return instance;
}
async _update(id, data, params = {}) {
var _a;
const query = Object.assign({}, this.filterQuery(params).query);
// Force the {raw: false} option as the instance is needed to properly update
const seqOptions = Object.assign({}, params.sequelize, { raw: false });
const instance = await this._get(id, { sequelize: seqOptions, query });
const itemToUpdate = Object.keys(instance.toJSON()).reduce((result, key) => {
// @ts-ignore
result[key] = key in data ? data[key] : null;
return result;
const Model = this.ModelWithScope(params);
const sequelizeOptions = this.paramsToAdapter(id, params);
const select = (0, adapter_commons_1.select)(params, this.id);
const instance = await this._get(id, {
...params,
sequelize: { ...params.sequelize, raw: false }
});
const values = Object.values(Model.getAttributes())
.reduce((values, attribute) => {
const key = attribute.fieldName;
if (key === this.id) {
return values;
}
values[key] = key in data ? data[key] : null;
return values;
}, {});
try {
await instance.update(itemToUpdate, seqOptions);
const item = await this._get(id, {
sequelize: Object.assign({}, seqOptions, {
raw: typeof ((_a = params === null || params === void 0 ? void 0 : params.sequelize) === null || _a === void 0 ? void 0 : _a.raw) === 'boolean'
? params.sequelize.raw
: this.raw
})
await instance
.set(values)
.update(values, sequelizeOptions)
.catch(utils_1.errorHandler);
if ((0, utils_1.isPresent)(sequelizeOptions.include)) {
return this._get(id, {
...params,
query: { $select: (_a = params.query) === null || _a === void 0 ? void 0 : _a.$select }
});
return (0, adapter_commons_1.select)(params, this.id)(item);
}
catch (err) {
return (0, utils_1.errorHandler)(err);
if (sequelizeOptions.raw) {
const result = instance.toJSON();
if ((0, utils_1.isPresent)(sequelizeOptions.attributes)) {
return select(result);
}
return result;
}
if ((0, utils_1.isPresent)(sequelizeOptions.attributes)) {
const result = select(instance.toJSON());
return Model.build(result, { isNewRecord: false });
}
return instance;
}

@@ -326,29 +421,29 @@ async _remove(id, params = {}) {

const Model = this.ModelWithScope(params);
const findParams = { ...params };
if (params.$returning === false) {
findParams.query = {
...findParams.query,
$select: [this.id]
};
}
else if ((_a = params.query) === null || _a === void 0 ? void 0 : _a.$select) {
findParams.query = {
...findParams.query,
$select: [...params.query.$select, this.id]
};
}
const itemOrItems = await this._getOrFind(id, findParams);
const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
const ids = items.map(item => item[this.id]);
const seqOptions = Object.assign({ raw: this.raw }, params.sequelize, { where: { [this.id]: { [this.Op.in]: ids } } });
try {
await Model.destroy(seqOptions);
if (params.$returning === false) {
const sequelizeOptions = this.paramsToAdapter(id, params);
if (id === null) {
const $select = sequelizeOptions.returning === false
? [this.id]
: (_a = params === null || params === void 0 ? void 0 : params.query) === null || _a === void 0 ? void 0 : _a.$select;
const current = await this._find({
...params,
paginate: false,
query: { ...params.query, $select }
});
if (!current.length) {
return [];
}
return (0, adapter_commons_1.select)(params, this.id)(itemOrItems);
const ids = current.map((item) => item[this.id]);
await Model.destroy({
...params.sequelize,
where: { [this.id]: ids.length === 1 ? ids[0] : { [sequelize_1.Op.in]: ids } }
}).catch(utils_1.errorHandler);
if (sequelizeOptions.returning === false) {
return [];
}
return current;
}
catch (err) {
return (0, utils_1.errorHandler)(err);
}
const result = await this._get(id, params);
const instance = result instanceof Model ? result : Model.build(result, { isNewRecord: false });
await instance.destroy(sequelizeOptions);
return result;
}

@@ -355,0 +450,0 @@ }

@@ -10,3 +10,2 @@ import type { AdapterParams, AdapterQuery, AdapterServiceOptions } from '@feathersjs/adapter-commons';

sequelize?: any;
$returning?: boolean;
}

@@ -13,0 +12,0 @@ export type HydrateOptions = {

@@ -5,1 +5,2 @@ export declare const ERROR: unique symbol;

export declare const isPlainObject: (obj: any) => boolean;
export declare const isPresent: (obj: any) => boolean;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isPlainObject = exports.getOrder = exports.errorHandler = exports.ERROR = void 0;
exports.isPresent = exports.isPlainObject = exports.getOrder = exports.errorHandler = exports.ERROR = void 0;
const errors_1 = require("@feathersjs/errors");

@@ -49,5 +49,15 @@ exports.ERROR = Symbol('feathers-sequelize/error');

const isPlainObject = (obj) => {
return obj && obj.constructor === {}.constructor;
return !!obj && obj.constructor === {}.constructor;
};
exports.isPlainObject = isPlainObject;
const isPresent = (obj) => {
if (Array.isArray(obj)) {
return obj.length > 0;
}
if ((0, exports.isPlainObject)(obj)) {
return Object.keys(obj).length > 0;
}
return !!obj;
};
exports.isPresent = isPresent;
//# sourceMappingURL=utils.js.map
{
"name": "feathers-sequelize",
"description": "A service adapter for Sequelize an SQL ORM",
"version": "7.0.3",
"version": "8.0.0-pre.0",
"homepage": "https://github.com/feathersjs-ecosystem/feathers-sequelize",

@@ -64,14 +64,14 @@ "main": "lib/",

"dependencies": {
"@feathersjs/adapter-commons": "^5.0.11",
"@feathersjs/commons": "^5.0.11",
"@feathersjs/errors": "^5.0.11"
"@feathersjs/adapter-commons": "^5.0.25",
"@feathersjs/commons": "^5.0.25",
"@feathersjs/errors": "^5.0.25"
},
"devDependencies": {
"@feathersjs/adapter-tests": "^5.0.11",
"@feathersjs/express": "^5.0.11",
"@feathersjs/feathers": "^5.0.11",
"@types/chai": "^4.3.9",
"@types/mocha": "^10.0.3",
"@types/node": "^20.8.10",
"@types/pg": "^8.10.7",
"@feathersjs/adapter-tests": "^5.0.25",
"@feathersjs/express": "^5.0.25",
"@feathersjs/feathers": "^5.0.25",
"@types/chai": "^4.3.16",
"@types/mocha": "^10.0.6",
"@types/node": "^20.12.11",
"@types/pg": "^8.11.6",
"@typescript-eslint/eslint-plugin": "^6.9.1",

@@ -83,13 +83,13 @@ "@typescript-eslint/parser": "^6.9.1",

"eslint": "^8.52.0",
"mocha": "^10.2.0",
"mysql2": "^3.6.2",
"npm-check-updates": "^16.14.6",
"pg": "^8.11.3",
"mocha": "^10.4.0",
"mysql2": "^3.9.7",
"npm-check-updates": "^16.14.20",
"pg": "^8.11.5",
"pg-hstore": "^2.3.4",
"sequelize": "^6.33.0",
"sequelize": "^6.37.3",
"shx": "^0.3.4",
"sqlite3": "^5.1.6",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
"sqlite3": "^5.1.7",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
}
}

@@ -30,2 +30,3 @@ # feathers-sequelize

- [Validation](#validation)
- [Errors](#errors)
- [Testing sequelize queries in isolation](#testing-sequelize-queries-in-isolation)

@@ -91,3 +92,4 @@ - [1. Build a test file](#1-build-a-test-file)

- `operatorMap` (*optional*) - A mapping from query syntax property names to to [Sequelize secure operators](http://docs.sequelizejs.com/manual/tutorial/querying.html)
- `operators` (*optional*) - A list of additional query parameters to allow (e..g `[ '$regex', '$geoNear' ]`). Default is the supported `operators`
- `operators` (*optional*) - An array of additional query operators to allow (e..g `[ '$regex', '$geoNear' ]`). Default is the supported `operators`
- `filters` (*optional*) - An object of additional query parameters to allow (e..g `{ '$post.id$': true }`).`

@@ -119,3 +121,7 @@ ### params.sequelize

This library offers some additional functionality when using `sequelize.returning` in services that support `multi`. The `multi` option allows you to create, patch, and remove multiple records at once. When using `sequelize.returning` with `multi`, the `sequelize.returning` is used to indicate if the method should return any results. This is helpful when updating large numbers of records and you do not need the API (or events) to be bogged down with results.
```js
### operatorMap

@@ -142,3 +148,3 @@

```
```js
// Find all users with name similar to Dav

@@ -169,3 +175,3 @@ app.service('users').find({

Don't worry! The solution is easy. Please read the guides about [working with model instances](#working-with-sequelize-model-instances).
Don't worry! The solution is easy. Please read the guides about [working with model instances](#working-with-sequelize-model-instances). You can also pass `{ raw: true/false}` in `params.sequelize` to change the behavior per service call.

@@ -183,3 +189,3 @@ ### Working with MSSQL

```js
module.exports = function (options = {}) {
export default function (options = {}) {
return async context => {

@@ -298,3 +304,3 @@ const { query = {} } = context.params;

context.params.sequelize = {
include: [{ model: AssociatedModel }]
include: [{ model: AssociatedModel }]
};

@@ -323,7 +329,5 @@ // Update the query to not include `include`

> **Note**: This adapter supports an additional `$returning` parameter for patch and remove queries. By setting `params.$returning = false` it will disable feathers and sequelize from returning what was changed, so mass updates can be done without overwhelming node and/or clients.
### Querying a nested column
To query based on a column in an associated model, you can use Sequelize's [nested column syntax](https://sequelize.org/master/manual/eager-loading.html#complex-where-clauses-at-the-top-level) in a query. The nested column syntax is considered an operator by Feathers, and so each such usage has to be [whitelisted](#options-whitelist).
To query based on a column in an associated model, you can use Sequelize's [nested column syntax](https://sequelize.org/master/manual/eager-loading.html#complex-where-clauses-at-the-top-level) in a query. The nested column syntax is considered a `filter` by Feathers, and so each such usage has to be [whitelisted](#whitelist).

@@ -343,3 +347,3 @@ Example:

For this case to work, you'll need to add '$post.id$' to the service options' ['whitelist' property](#options-whitelist).
For this case to work, you'll need to add '$post.id$' to the service options' ['filters' property](#whitelist).

@@ -352,24 +356,47 @@ ## Working with Sequelize Model instances

```js
function rawFalse(context) {
if (!context.params.sequelize) context.params.sequelize = {};
Object.assign(context.params.sequelize, { raw: false });
return context;
const rawFalse = () => (context) => {
if (!context.params.sequelize) context.params.sequelize = {};
Object.assign(context.params.sequelize, { raw: false });
return context;
}
hooks.before.find = [rawFalse];
export default {
after: {
// ...
find: [rawFalse()]
// ...
},
// ...
};
```
1. Use the new `hydrate` hook in the "after" phase:
1. Use the `hydrate` hook in the "after" phase:
```js
const hydrate = require('feathers-sequelize/hooks/hydrate');
hooks.after.find = [hydrate()];
import { hydrate } from 'feathers-sequelize';
export default {
after: {
// ...
find: [hydrate()]
// ...
},
// ...
};
// Or, if you need to include associated models, you can do the following:
function includeAssociated (context) {
return hydrate({
include: [{ model: context.app.services.fooservice.Model }]
}).call(this, context);
}
hooks.after.find = [includeAssociated];
```
const includeAssociated = () => (context) => hydrate({
include: [{ model: context.app.services.fooservice.Model }]
});
export default {
after: {
// ...
find: [includeAssociated()]
// ...
},
// ...
};
```
For a more complete example see this [gist](https://gist.github.com/sicruse/bfaa17008990bab2fd1d76a670c3923f).

@@ -379,6 +406,13 @@

> ```js
> const { dehydrate, hydrate } = require('feathers-sequelize');
> const { populate } = require('feathers-hooks-common');
> import { dehydrate, hydrate } from 'feathers-sequelize';
> import { populate } = from 'feathers-hooks-common';
>
> hooks.after.find = [hydrate(), doSomethingCustom(), dehydrate(), populate()];
> export default {
> after: {
> // ...
> find: [hydrate(), doSomethingCustom(), dehydrate(), populate()]
> // ...
> },
> // ...
> };
> ```

@@ -390,2 +424,18 @@

## Errors
Errors do not contain Sequelize specific information. The original Sequelize error can be retrieved on the server via:
```js
import { ERROR } = from 'feathers-sequelize';
try {
await sequelizeService.doSomething();
} catch(error) {
// error is a FeathersError
// Safely retrieve the Sequelize error
const sequelizeError = error[ERROR];
}
```
## Testing sequelize queries in isolation

@@ -397,9 +447,10 @@

Creat a temporary file in your project root like this:
Create a temporary file in your project root like this:
```js
// test.js
const app = require('./src/app');
import app from from './src/app';
// run setup to initialize relations
app.setup();
const seqClient = app.get('sequelizeClient');

@@ -410,5 +461,5 @@ const SomeModel = seqClient.models['some-model'];

SomeModel.findAll({
/*
* Build your custom query here. We will use this object later.
*/
/*
* Build your custom query here. We will use this object later.
*/
}).then(log).catch(log);

@@ -633,34 +684,14 @@ ```

### Migrating
## License
`feathers-sequelize` 4.0.0 comes with important security and usability updates.
Copyright (c) 2024
> __Important:__ For general migration information to the new database adapter functionality see [crow.docs.feathersjs.com/migrating.html#database-adapters](https://crow.docs.feathersjs.com/migrating.html#database-adapters).
Licensed under the [MIT license](LICENSE).
The following breaking changes have been introduced:
### whitelist
- All methods now take `params.sequelize` into account
- All methods allow additional query parameters
- Multiple updates are disabled by default (see the `multi` option)
- Upgraded to secure Sequelize operators (see the [operators](#operators) option)
- Errors no longer contain Sequelize specific information. The original Sequelize error can be retrieved on the server via:
The `whitelist` property is no longer, you should use `filters` instead. Checkout the migration guide below.
```js
const { ERROR } = require('feathers-sequelize');
> Feathers v5 introduces a convention for `options.operators` and `options.filters`. The way feathers-sequelize worked in previous version is not compatible with these conventions. Please read https://dove.feathersjs.com/guides/migrating.html#custom-filters-operators.
try {
await sequelizeService.doSomethign();
} catch(error) {
// error is a FeathersError
// Safely retrieve the Sequelize error
const sequelizeError = error[ERROR];
}
```
## License
Copyright (c) 2022
Licensed under the [MIT license](LICENSE).
## Migrate to Feathers v5 (dove)

@@ -674,3 +705,3 @@

```js
const { SequelizeService } = require('feathers-sequelize');
import { SequelizeService } from 'feathers-sequelize';

@@ -688,4 +719,4 @@ app.use('/messages', new SequelizeService({ ... }));

```js
const { SequelizeService } = require('feathers-sequelize');
const { Op } = require('sequelize');
import { SequelizeService } from 'feathers-sequelize';
import { Op } from 'sequelize';

@@ -701,4 +732,2 @@ app.use('/messages', new SequelizeService({

The new `options.operators` option is an array of allowed operators.
### filters

@@ -711,3 +740,3 @@

```js
const { SequelizeService } = require('feathers-sequelize');
import { SequelizeService } from 'feathers-sequelize';

@@ -722,5 +751,1 @@ app.use('/messages', new SequelizeService({

```
### whitelist
> Feathers v5 introduces a convention for `options.operators` and `options.filters`. The way feathers-sequelize worked in previous version is not compatible with these conventions. Please read https://dove.feathersjs.com/guides/migrating.html#custom-filters-operators.

@@ -1,41 +0,39 @@

import { BadRequest, MethodNotAllowed, NotFound } from '@feathersjs/errors';
import { MethodNotAllowed, NotFound } from '@feathersjs/errors';
import { _ } from '@feathersjs/commons';
import { select, AdapterBase, filterQuery } from '@feathersjs/adapter-commons';
import {
select as selector,
AdapterBase,
filterQuery
} from '@feathersjs/adapter-commons';
import type { PaginationOptions } from '@feathersjs/adapter-commons';
import type { SequelizeAdapterOptions, SequelizeAdapterParams } from './declarations';
import type { Id, NullableId, Paginated, Query } from '@feathersjs/feathers';
import { errorHandler, getOrder, isPlainObject } from './utils';
import { errorHandler, getOrder, isPlainObject, isPresent } from './utils';
import { Op } from 'sequelize';
import type { CreateOptions, FindOptions, Model } from 'sequelize';
import type {
CreateOptions,
UpdateOptions,
FindOptions,
Model
} from 'sequelize';
const defaultOpMap = () => {
return {
$eq: Op.eq,
$ne: Op.ne,
$gte: Op.gte,
$gt: Op.gt,
$lte: Op.lte,
$lt: Op.lt,
$in: Op.in,
$nin: Op.notIn,
$like: Op.like,
$notLike: Op.notLike,
$iLike: Op.iLike,
$notILike: Op.notILike,
$or: Op.or,
$and: Op.and
};
const defaultOperatorMap = {
$eq: Op.eq,
$ne: Op.ne,
$gte: Op.gte,
$gt: Op.gt,
$lte: Op.lte,
$lt: Op.lt,
$in: Op.in,
$nin: Op.notIn,
$like: Op.like,
$notLike: Op.notLike,
$iLike: Op.iLike,
$notILike: Op.notILike,
$or: Op.or,
$and: Op.and
};
const defaultFilters = () => {
return {
$returning: (value: any) => {
if (value === true || value === false || value === undefined) {
return value;
}
throw new BadRequest('Invalid $returning filter value');
},
$and: true
}
const defaultFilters = {
$and: true as const
}

@@ -58,3 +56,6 @@

const operatorMap = Object.assign(defaultOpMap(), options.operatorMap);
const operatorMap = {
...defaultOperatorMap,
...options.operatorMap
};
const operators = Object.keys(operatorMap);

@@ -74,14 +75,26 @@ if (options.operators) {

const filters = Object.assign(defaultFilters(), options.filters);
const filters = {
...defaultFilters,
...options.filters
};
super(Object.assign({ id }, options, { operatorMap, filters, operators }));
super({
id,
...options,
operatorMap,
filters,
operators
});
}
get raw () {
get raw (): boolean {
return this.options.raw !== false;
}
get Op () {
// @ts-ignore
return this.options.Model.sequelize.Sequelize.Op;
/**
*
* @deprecated Use `Op` from `sequelize` directly
*/
get Op (): typeof Op {
return Op;
}

@@ -106,6 +119,3 @@

/**
* @deprecated use 'service.ModelWithScope' instead. 'applyScope' will be removed in a future release.
*/
applyScope (params?: ServiceParams) {
ModelWithScope (params: ServiceParams) {
const Model = this.getModel(params);

@@ -118,6 +128,2 @@ if (params?.sequelize?.scope) {

ModelWithScope (params: ServiceParams) {
return this.applyScope(params);
}
convertOperators (q: any): Query {

@@ -159,2 +165,9 @@ if (Array.isArray(q)) {

if (filters.$select) {
if (!filters.$select.includes(this.id)) {
filters.$select.push(this.id);
}
filters.$select = filters.$select.map((select: any) => `${select}`);
}
return {

@@ -167,68 +180,52 @@ filters,

paramsToAdapter (id: NullableId, _params?: ServiceParams): FindOptions {
// paramsToAdapter (id: NullableId, _params?: ServiceParams): FindOptions {
paramsToAdapter (id: NullableId, _params?: ServiceParams): any {
const params = _params || {} as ServiceParams;
if (id) {
const { query: where } = this.filterQuery(params);
const { filters, query: where } = this.filterQuery(params);
const { and } = this.Op;
// Attach 'where' constraints, if any were used.
const q: FindOptions = Object.assign({
raw: this.raw,
where: Object.assign(where, {
[and]: where[and] ? [...where[and], { [this.id]: id }] : { [this.id]: id }
})
}, params.sequelize);
// Until Sequelize fix all the findAndCount issues, a few 'hacks' are needed to get the total count correct
return q;
} else {
const { filters, query: where } = this.filterQuery(params);
const order = getOrder(filters.$sort);
// Adding an empty include changes the way the count is done
// See: https://github.com/sequelize/sequelize/blob/7e441a6a5ca44749acd3567b59b1d6ceb06ae64b/lib/model.js#L1780-L1782
// sequelize.include = sequelize.include || [];
const q: FindOptions = Object.assign({
where,
order,
const defaults = {
where,
attributes: filters.$select,
distinct: true,
returning: true,
raw: this.raw,
...params.sequelize
}
if (id === null) {
return {
order: getOrder(filters.$sort),
limit: filters.$limit,
offset: filters.$skip,
raw: this.raw,
distinct: true
}, params.sequelize);
...defaults
} as FindOptions
}
if (filters.$select) {
// Add the id to the select if it is not already there
if (!filters.$select.includes(this.id)) {
filters.$select.push(this.id);
}
const sequelize: FindOptions = {
limit: 1,
...defaults
};
q.attributes = filters.$select.map((select: any) => `${select}`);
}
// Until Sequelize fix all the findAndCount issues, a few 'hacks' are needed to get the total count correct
// Adding an empty include changes the way the count is done
// See: https://github.com/sequelize/sequelize/blob/7e441a6a5ca44749acd3567b59b1d6ceb06ae64b/lib/model.js#L1780-L1782
q.include = q.include || [];
return q;
if (where[this.id] === id) {
return sequelize;
}
}
/**
* returns either the model instance / jsonified object for an id or all unpaginated
* items for `params` if id is null
*/
async _getOrFind (id: Id, _params?: ServiceParams): Promise<Result>
async _getOrFind (id: null, _params?: ServiceParams): Promise<Result[]>
async _getOrFind (id: NullableId, _params: ServiceParams) {
const params = _params || {} as ServiceParams;
if (id === null) {
return await this._find({
...params,
paginate: false
});
if (this.id in where) {
const { and } = Op;
where[and as any] = where[and as any]
? [...where[and as any], { [this.id]: id }]
: { [this.id]: id };
} else {
where[this.id] = id;
}
return await this._get(id, params);
return sequelize;
}
async _find (params?: ServiceParams & { paginate?: PaginationOptions }): Promise<Paginated<Result>>

@@ -238,22 +235,31 @@ async _find (params?: ServiceParams & { paginate: false }): Promise<Result[]>

async _find (params: ServiceParams = {} as ServiceParams): Promise<Paginated<Result> | Result[]> {
const Model = this.ModelWithScope(params);
const { paginate } = this.filterQuery(params);
const sequelizeOptions = this.paramsToAdapter(null, params);
const Model = this.ModelWithScope(params);
const q = this.paramsToAdapter(null, params);
if (!paginate || !paginate.default) {
const result = await Model.findAll(sequelizeOptions).catch(errorHandler);
return result;
}
try {
if (paginate && paginate.default) {
const result = await Model.findAndCountAll(q);
if (sequelizeOptions.limit === 0) {
const total = (await Model
.count({ ...sequelizeOptions, attributes: undefined })
.catch(errorHandler)) as any as number;
return {
total: result.count,
limit: q.limit,
skip: q.offset || 0,
data: result.rows as Result[]
}
return {
total,
limit: sequelizeOptions.limit,
skip: sequelizeOptions.offset || 0,
data: []
}
}
return await Model.findAll(q) as Result[];
} catch (err: any) {
return errorHandler(err);
const result = await Model.findAndCountAll(sequelizeOptions).catch(errorHandler);
return {
total: result.count,
limit: sequelizeOptions.limit,
skip: sequelizeOptions.offset || 0,
data: result.rows
}

@@ -264,19 +270,8 @@ }

const Model = this.ModelWithScope(params);
const q = this.paramsToAdapter(id, params);
// findById calls findAll under the hood. We use findAll so that
// eager loading can be used without a separate code path.
try {
const result = await Model.findAll(q);
if (result.length === 0) {
throw new NotFound(`No record found for id '${id}'`);
}
const item = result[0];
return select(params, this.id)(item);
} catch (err: any) {
return errorHandler(err);
const sequelizeOptions = this.paramsToAdapter(id, params);
const result = await Model.findAll(sequelizeOptions).catch(errorHandler);
if (result.length === 0) {
throw new NotFound(`No record found for id '${id}'`);
}
return result[0];
}

@@ -288,34 +283,55 @@

async _create (data: Data | Data[], params: ServiceParams = {} as ServiceParams): Promise<Result | Result[]> {
if (Array.isArray(data) && !this.allowsMulti('create', params)) {
const isArray = Array.isArray(data);
const select = selector(params, this.id);
if (isArray && !this.allowsMulti('create', params)) {
throw new MethodNotAllowed('Can not create multiple entries')
}
const options = Object.assign({ raw: this.raw }, params.sequelize);
// Model.create's `raw` option is different from other methods.
// In order to use `raw` consistently to serialize the result,
// we need to shadow the Model.create use of raw, which we provide
// access to by specifying `ignoreSetters`.
const ignoreSetters = !!options.ignoreSetters;
const createOptions = Object.assign({
returning: true
}, options, { raw: ignoreSetters });
const isArray = Array.isArray(data);
if (isArray && data.length === 0) {
return []
}
const Model = this.ModelWithScope(params);
const sequelizeOptions = this.paramsToAdapter(null, params);
try {
const result = isArray
? await Model.bulkCreate(data as any[], createOptions)
: await Model.create(data as any, createOptions as CreateOptions);
if (isArray) {
const instances = await Model
.bulkCreate(data as any[], sequelizeOptions)
.catch(errorHandler);
const sel = select(params, this.id);
if (options.raw === false) {
return result as Result | Result[];
if (sequelizeOptions.returning === false) {
return [];
}
if (isArray) {
return (result as Model[]).map(item => sel(item.toJSON()));
if (sequelizeOptions.raw) {
const result = instances.map((instance) => {
if (isPresent(sequelizeOptions.attributes)) {
return select(instance.toJSON());
}
return instance.toJSON();
})
return result;
}
return sel((result as Model).toJSON());
} catch (err: any) {
return errorHandler(err);
if (isPresent(sequelizeOptions.attributes)) {
const result = instances.map((instance) => {
const result = select(instance.toJSON())
return Model.build(result, { isNewRecord: false });
})
return result;
}
return instances;
}
const result = await Model
.create(data as any, sequelizeOptions as CreateOptions)
.catch(errorHandler);
if (sequelizeOptions.raw) {
return select((result as Model).toJSON())
}
return result;
}

@@ -331,78 +347,173 @@

const Model = this.ModelWithScope(params);
const sequelizeOptions = this.paramsToAdapter(id, params);
const select = selector(params, this.id);
const values = _.omit(data, this.id);
// Get a list of ids that match the id/query. Overwrite the
// $select because only the id is needed for this idList
const itemOrItems = await this._getOrFind(id, {
...params,
query: {
...params?.query,
$select: [this.id]
if (id === null) {
const current = await this._find({
...params,
paginate: false,
query: {
...params?.query,
$select: [this.id]
}
});
if (!current.length) {
return []
}
})
const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
const ids: Id[] = items.map(item => item[this.id]);
const ids = current.map((item: any) => item[this.id]);
try {
const seqOptions = Object.assign(
{ raw: this.raw },
params.sequelize,
{ where: { [this.id]: { [this.Op.in]: ids } } }
);
let [, instances] = await Model
.update(values, {
...sequelizeOptions,
where: { [this.id]: ids.length === 1 ? ids[0] : { [Op.in]: ids } }
} as UpdateOptions)
.catch(errorHandler) as [number, Model[]?];
await Model.update(_.omit(data, this.id), seqOptions);
if (sequelizeOptions.returning === false) {
return []
}
if (params.$returning === false) {
return Promise.resolve([]);
// Returning is only supported in postgres and mssql, and
// its a little goofy array order how Sequelize handles it.
// https://github.com/sequelize/sequelize/blob/abca55ee52d959f95c98dc7ae8b8162005536d05/packages/core/src/model.js#L3110
if (!instances || typeof instances === 'number') {
instances = null;
}
// Create a new query that re-queries all ids that
// were originally changed
const findParams = {
const hasAttributes = isPresent(sequelizeOptions.attributes);
if (instances) {
if (isPresent(params.query?.$sort)) {
const sortedInstances: Model[] = [];
const unsortedInstances: Model[] = [];
current.forEach((item: any) => {
const id = item[this.id];
const instance = instances.find(instance => (instance as any)[this.id] === id);
if (instance) {
sortedInstances.push(instance);
} else {
unsortedInstances.push(item);
}
});
instances = [...sortedInstances, ...unsortedInstances];
}
if (sequelizeOptions.raw) {
const result = instances.map((instance) => {
if (hasAttributes) {
return select(instance.toJSON());
}
return instance.toJSON();
})
return result;
}
if (hasAttributes) {
const result = instances.map((instance) => {
const result = select(instance.toJSON())
return Model.build(result, { isNewRecord: false });
})
return result;
}
return instances as unknown as Result[];
}
const result = await this._find({
...params,
paginate: false,
query: {
[this.id]: { $in: ids },
...(params?.query?.$select ? { $select: params?.query?.$select } : {})
[this.id]: ids.length === 1 ? ids[0] : { $in: ids },
$select: params?.query?.$select,
$sort: params?.query?.$sort
}
}
});
const result = await this._getOrFind(id, findParams);
return result;
}
return select(params, this.id)(result);
const instance = await this._get(id, {
...params,
sequelize: { ...params.sequelize, raw: false }
}) as unknown as Model;
} catch (err: any) {
return errorHandler(err);
await instance
.set(values)
.update(values, sequelizeOptions)
.catch(errorHandler);
if (isPresent(sequelizeOptions.include)) {
return this._get(id, {
...params,
query: { $select: params.query?.$select }
});
}
if (sequelizeOptions.raw) {
const result = instance.toJSON();
if (isPresent(sequelizeOptions.attributes)) {
return select(result);
}
return result;
}
if (isPresent(sequelizeOptions.attributes)) {
const result = select(instance.toJSON())
return Model.build(result, { isNewRecord: false });
}
return instance as unknown as Result;
}
async _update (id: Id, data: Data, params: ServiceParams = {} as ServiceParams): Promise<Result> {
const query = Object.assign({}, this.filterQuery(params).query);
const Model = this.ModelWithScope(params);
const sequelizeOptions = this.paramsToAdapter(id, params);
const select = selector(params, this.id);
// Force the {raw: false} option as the instance is needed to properly update
const seqOptions = Object.assign({}, params.sequelize, { raw: false });
const instance = await this._get(id, {
...params,
sequelize: { ...params.sequelize, raw: false }
}) as unknown as Model;
const instance = await this._get(id, { sequelize: seqOptions, query } as ServiceParams) as Model
const values = Object.values(Model.getAttributes())
.reduce((values, attribute: any) => {
const key = attribute.fieldName as string;
if (key === this.id) {
return values
}
values[key] = key in (data as any) ? (data as any)[key] : null;
return values;
}, {} as Record<string, any>);
const itemToUpdate = Object.keys(instance.toJSON()).reduce((result: Record<string, any>, key) => {
// @ts-ignore
result[key] = key in data ? data[key] : null;
await instance
.set(values)
.update(values, sequelizeOptions)
.catch(errorHandler);
if (isPresent(sequelizeOptions.include)) {
return this._get(id, {
...params,
query: { $select: params.query?.$select }
});
}
if (sequelizeOptions.raw) {
const result = instance.toJSON();
if (isPresent(sequelizeOptions.attributes)) {
return select(result);
}
return result;
}, {});
}
try {
await instance.update(itemToUpdate, seqOptions);
if (isPresent(sequelizeOptions.attributes)) {
const result = select(instance.toJSON())
return Model.build(result, { isNewRecord: false });
}
const item = await this._get(id, {
sequelize: Object.assign({}, seqOptions, {
raw: typeof params?.sequelize?.raw === 'boolean'
? params.sequelize.raw
: this.raw
})
} as ServiceParams);
return select(params, this.id)(item);
} catch (err: any) {
return errorHandler(err);
}
return instance as unknown as Result;
}

@@ -418,35 +529,41 @@

const Model = this.ModelWithScope(params);
const sequelizeOptions = this.paramsToAdapter(id, params);
const findParams = { ...params };
if (params.$returning === false) {
findParams.query = {
...findParams.query,
$select: [this.id]
if (id === null) {
const $select = sequelizeOptions.returning === false
? [this.id]
: params?.query?.$select
const current = await this._find({
...params,
paginate: false,
query: { ...params.query, $select }
});
if (!current.length) {
return [];
}
} else if (params.query?.$select) {
findParams.query = {
...findParams.query,
$select: [...params.query.$select, this.id]
const ids: Id[] = current.map((item: any) => item[this.id]);
await Model.destroy({
...params.sequelize,
where: { [this.id]: ids.length === 1 ? ids[0] : { [Op.in]: ids } }
}).catch(errorHandler);
if (sequelizeOptions.returning === false) {
return [];
}
return current;
}
const itemOrItems = await this._getOrFind(id, findParams);
const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
const ids: Id[] = items.map(item => item[this.id]);
const seqOptions = Object.assign(
{ raw: this.raw },
params.sequelize,
{ where: { [this.id]: { [this.Op.in]: ids } } }
);
const result = await this._get(id, params);
try {
await Model.destroy(seqOptions);
if (params.$returning === false) {
return []
}
return select(params, this.id)(itemOrItems);
} catch (err: any) {
return errorHandler(err);
}
const instance = result instanceof Model ? result : Model.build(result as any, { isNewRecord: false });
await instance.destroy(sequelizeOptions);
return result;
}
}

@@ -12,3 +12,2 @@ import type { AdapterParams, AdapterQuery, AdapterServiceOptions } from '@feathersjs/adapter-commons';

sequelize?: any // FindOptions | CreateOptions | BulkCreateOptions
$returning?: boolean
}

@@ -15,0 +14,0 @@

@@ -11,2 +11,3 @@ import { SequelizeAdapter } from './adapter';

export { ERROR, errorHandler } from './utils';
export class SequelizeService<Result = any, Data = Partial<Result>, ServiceParams extends Params<any> = SequelizeAdapterParams, PatchData = Partial<Data>>

@@ -13,0 +14,0 @@ extends SequelizeAdapter<Result, Data, ServiceParams, PatchData>

@@ -5,2 +5,3 @@ import type { FeathersError } from '@feathersjs/errors';

export const ERROR = Symbol('feathers-sequelize/error');
const wrap = (error: FeathersError, original: BaseError) => Object.assign(error, { [ERROR]: original });

@@ -51,4 +52,14 @@

export const isPlainObject = (obj: any) => {
return obj && obj.constructor === {}.constructor;
export const isPlainObject = (obj: any): boolean => {
return !!obj && obj.constructor === {}.constructor;
};
export const isPresent = (obj: any): boolean => {
if (Array.isArray(obj)) {
return obj.length > 0;
}
if (isPlainObject(obj)) {
return Object.keys(obj).length > 0;
}
return !!obj;
};

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc