@feathersjs/knex 5.0.0-pre.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ # [5.0.0-pre.24](https://github.com/feathersjs/feathers/compare/v5.0.0-pre.23...v5.0.0-pre.24) (2022-06-21)
7
+
8
+
9
+ ### Features
10
+
11
+ * **knex:** Add KnexJS SQL database adapter to core ([#2671](https://github.com/feathersjs/feathers/issues/2671)) ([9380fff](https://github.com/feathersjs/feathers/commit/9380fff58596e8bb90b8bb098d2795b7eadfec20))
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Feathers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # @feathersjs/knex
2
+
3
+ [![CI](https://github.com/feathersjs/feathers/workflows/CI/badge.svg)](https://github.com/feathersjs/feathers/actions?query=workflow%3ACI)
4
+ [![Download Status](https://img.shields.io/npm/dm/@feathersjs/mongodb.svg?style=flat-square)](https://www.npmjs.com/package/@feathersjs/mongodb)
5
+
6
+ > Feathers SQL service adapter using KnexJS
7
+
8
+ ## Installation
9
+
10
+ ```
11
+ npm install @feathersjs/knex --save
12
+ ```
13
+
14
+ ## Documentation
15
+
16
+ Refer to the [Feathers documentation](https://docs.feathersjs.com) for more details.
17
+
18
+ ## License
19
+
20
+ Copyright (c) 2022 [Feathers contributors](https://github.com/feathersjs/feathers/graphs/contributors)
21
+
22
+ Licensed under the [MIT license](LICENSE).
@@ -0,0 +1,64 @@
1
+ import { Id, NullableId, Paginated, Query } from '@feathersjs/feathers';
2
+ import { AdapterBase, AdapterServiceOptions, AdapterParams, AdapterQuery, PaginationOptions } from '@feathersjs/adapter-commons';
3
+ import { Knex } from 'knex';
4
+ export interface KnexAdapterOptions extends AdapterServiceOptions {
5
+ Model: Knex;
6
+ name: string;
7
+ schema?: string;
8
+ }
9
+ export interface KnexAdapterParams<Q = AdapterQuery> extends AdapterParams<Q, Partial<KnexAdapterOptions>> {
10
+ knex?: Knex.QueryBuilder;
11
+ transaction?: Knex.Transaction;
12
+ }
13
+ export declare class KnexAdapter<T, D = Partial<T>, P extends KnexAdapterParams<any> = KnexAdapterParams> extends AdapterBase<T, D, P, KnexAdapterOptions> {
14
+ table: string;
15
+ schema?: string;
16
+ constructor(options: KnexAdapterOptions);
17
+ get Model(): Knex<any, any[]>;
18
+ get fullName(): string;
19
+ db(_params?: P): Knex.QueryBuilder<any, {
20
+ _base: any;
21
+ _hasSelection: false;
22
+ _keys: never;
23
+ _aliases: {};
24
+ _single: false;
25
+ _intersectProps: {};
26
+ _unionProps: never;
27
+ }[]>;
28
+ knexify(knexQuery: Knex.QueryBuilder, query?: Query, parentKey?: string): Knex.QueryBuilder;
29
+ createQuery(params: P): Knex.QueryBuilder<any, {
30
+ _base: any;
31
+ _hasSelection: false;
32
+ _keys: never;
33
+ _aliases: {};
34
+ _single: false;
35
+ _intersectProps: {};
36
+ _unionProps: never;
37
+ }[]>;
38
+ filterQuery(params: P): {
39
+ filters: {
40
+ [key: string]: any;
41
+ };
42
+ query: Query;
43
+ paginate: import("@feathersjs/adapter-commons").PaginationParams;
44
+ };
45
+ $find(params?: P & {
46
+ paginate?: PaginationOptions;
47
+ }): Promise<Paginated<T>>;
48
+ $find(params?: P & {
49
+ paginate: false;
50
+ }): Promise<T[]>;
51
+ $find(params?: P): Promise<Paginated<T> | T[]>;
52
+ _findOrGet(id: NullableId, params?: P): Promise<T[]>;
53
+ $get(id: Id, params?: P): Promise<T>;
54
+ $create(data: Partial<D>, params?: P): Promise<T>;
55
+ $create(data: Partial<D>[], params?: P): Promise<T[]>;
56
+ $create(data: Partial<D> | Partial<D>[], _params?: P): Promise<T | T[]>;
57
+ $patch(id: null, data: Partial<D>, params?: P): Promise<T[]>;
58
+ $patch(id: Id, data: Partial<D>, params?: P): Promise<T>;
59
+ $patch(id: NullableId, data: Partial<D>, _params?: P): Promise<T | T[]>;
60
+ $update(id: Id, _data: D, params?: P): Promise<T>;
61
+ $remove(id: null, params?: P): Promise<T[]>;
62
+ $remove(id: Id, params?: P): Promise<T>;
63
+ $remove(id: NullableId, _params?: P): Promise<T | T[]>;
64
+ }
package/lib/adapter.js ADDED
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KnexAdapter = void 0;
4
+ const commons_1 = require("@feathersjs/commons");
5
+ const adapter_commons_1 = require("@feathersjs/adapter-commons");
6
+ const errors_1 = require("@feathersjs/errors");
7
+ const error_handler_1 = require("./error-handler");
8
+ const METHODS = {
9
+ $ne: 'whereNot',
10
+ $in: 'whereIn',
11
+ $nin: 'whereNotIn',
12
+ $or: 'orWhere',
13
+ $and: 'andWhere'
14
+ };
15
+ const OPERATORS = {
16
+ $lt: '<',
17
+ $lte: '<=',
18
+ $gt: '>',
19
+ $gte: '>=',
20
+ $like: 'like',
21
+ $notlike: 'not like',
22
+ $ilike: 'ilike'
23
+ };
24
+ class KnexAdapter extends adapter_commons_1.AdapterBase {
25
+ constructor(options) {
26
+ if (!options || !options.Model) {
27
+ throw new Error('You must provide a Model (the initialized knex object)');
28
+ }
29
+ if (typeof options.name !== 'string') {
30
+ throw new Error('No table name specified.');
31
+ }
32
+ super({
33
+ id: 'id',
34
+ ...options,
35
+ filters: {
36
+ ...options.filters,
37
+ $and: (value) => value
38
+ },
39
+ operators: [...(options.operators || []), '$like', '$notlike', '$ilike', '$and', '$or']
40
+ });
41
+ this.table = options.name;
42
+ this.schema = options.schema;
43
+ }
44
+ get Model() {
45
+ return this.options.Model;
46
+ }
47
+ get fullName() {
48
+ return this.schema ? `${this.schema}.${this.table}` : this.table;
49
+ }
50
+ db(_params) {
51
+ // const { Model, table, schema, fullName } = this
52
+ // if (params && params.transaction) {
53
+ // const { trx, id } = params.transaction
54
+ // debug('ran %s with transaction %s', fullName, id)
55
+ // return schema ? trx.withSchema(schema).table(table) : trx(table)
56
+ // }
57
+ // return schema ? Model.withSchema(schema).table(table) : Model(table)
58
+ return this.Model(this.table);
59
+ }
60
+ knexify(knexQuery, query = {}, parentKey) {
61
+ const knexify = this.knexify.bind(this);
62
+ return Object.keys(query || {}).reduce((currentQuery, key) => {
63
+ const value = query[key];
64
+ if (commons_1._.isObject(value)) {
65
+ return knexify(currentQuery, value, key);
66
+ }
67
+ const column = parentKey || key;
68
+ const method = METHODS[key];
69
+ if (method) {
70
+ if (key === '$or' || key === '$and') {
71
+ // This will create a nested query
72
+ currentQuery.where(function () {
73
+ for (const condition of value) {
74
+ this[method](function () {
75
+ knexify(this, condition);
76
+ });
77
+ }
78
+ });
79
+ return currentQuery;
80
+ }
81
+ return currentQuery[method](column, value);
82
+ }
83
+ const operator = OPERATORS[key] || '=';
84
+ return operator === '='
85
+ ? currentQuery.where(column, value)
86
+ : currentQuery.where(column, operator, value);
87
+ }, knexQuery);
88
+ }
89
+ createQuery(params) {
90
+ const { table, id } = this;
91
+ const { filters, query } = this.filterQuery(params);
92
+ const builder = this.db(params);
93
+ // $select uses a specific find syntax, so it has to come first.
94
+ if (filters.$select) {
95
+ // always select the id field, but make sure we only select it once
96
+ builder.select(...new Set([...filters.$select, `${table}.${id}`]));
97
+ }
98
+ else {
99
+ builder.select(`${table}.*`);
100
+ }
101
+ // build up the knex query out of the query params, include $and and $or filters
102
+ this.knexify(builder, {
103
+ ...query,
104
+ ...commons_1._.pick(filters, '$and', '$or')
105
+ });
106
+ // Handle $sort
107
+ if (filters.$sort) {
108
+ return Object.keys(filters.$sort).reduce((currentQuery, key) => currentQuery.orderBy(key, filters.$sort[key] === 1 ? 'asc' : 'desc'), builder);
109
+ }
110
+ return builder;
111
+ }
112
+ filterQuery(params) {
113
+ const options = this.getOptions(params);
114
+ const { filters, query } = (0, adapter_commons_1.filterQuery)((params === null || params === void 0 ? void 0 : params.query) || {}, options);
115
+ return { filters, query, paginate: options.paginate };
116
+ }
117
+ async $find(params = {}) {
118
+ const { filters, paginate } = this.filterQuery(params);
119
+ const builder = params.knex ? params.knex.clone() : this.createQuery(params);
120
+ const countBuilder = builder.clone().count(`${this.table}.${this.id} as total`);
121
+ // Handle $limit
122
+ if (filters.$limit) {
123
+ builder.limit(filters.$limit);
124
+ }
125
+ // Handle $skip
126
+ if (filters.$skip) {
127
+ builder.offset(filters.$skip);
128
+ }
129
+ // provide default sorting if its not set
130
+ if (!filters.$sort) {
131
+ builder.orderBy(`${this.table}.${this.id}`, 'asc');
132
+ }
133
+ const data = filters.$limit === 0 ? [] : await builder;
134
+ if (paginate && paginate.default) {
135
+ return {
136
+ total: await countBuilder.then((count) => (count[0] ? count[0].total : 0)),
137
+ limit: filters.$limit,
138
+ skip: filters.$skip || 0,
139
+ data
140
+ };
141
+ }
142
+ return data;
143
+ }
144
+ async _findOrGet(id, params) {
145
+ const findParams = {
146
+ ...params,
147
+ paginate: false,
148
+ query: {
149
+ ...params === null || params === void 0 ? void 0 : params.query,
150
+ ...(id !== null ? { [`${this.table}.${this.id}`]: id } : {})
151
+ }
152
+ };
153
+ return this.$find(findParams);
154
+ }
155
+ async $get(id, params = {}) {
156
+ const data = await this._findOrGet(id, params);
157
+ if (data.length !== 1) {
158
+ throw new errors_1.NotFound(`No record found for id '${id}'`);
159
+ }
160
+ return data[0];
161
+ }
162
+ async $create(_data, params = {}) {
163
+ const data = _data;
164
+ if (Array.isArray(data)) {
165
+ return Promise.all(data.map((current) => this.$create(current, params)));
166
+ }
167
+ const client = this.db(params).client.config.client;
168
+ const returning = client === 'pg' || client === 'oracledb' || client === 'mssql' ? [this.id] : [];
169
+ const rows = await this.db(params).insert(data, returning).catch(error_handler_1.errorHandler);
170
+ const id = data[this.id] || rows[0][this.id] || rows[0];
171
+ if (!id) {
172
+ return rows;
173
+ }
174
+ return this.$get(id, params);
175
+ }
176
+ async $patch(id, raw, params = {}) {
177
+ var _a, _b;
178
+ const data = commons_1._.omit(raw, this.id);
179
+ const results = await this._findOrGet(id, {
180
+ ...params,
181
+ query: {
182
+ ...params === null || params === void 0 ? void 0 : params.query,
183
+ $select: [`${this.table}.${this.id}`]
184
+ }
185
+ });
186
+ const idList = results.map((current) => current[this.id]);
187
+ const updateParams = {
188
+ ...params,
189
+ query: {
190
+ [`${this.table}.${this.id}`]: { $in: idList },
191
+ ...(((_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 } : {})
192
+ }
193
+ };
194
+ const builder = this.createQuery(updateParams);
195
+ await builder.update(data);
196
+ const items = await this._findOrGet(null, updateParams);
197
+ if (id !== null) {
198
+ if (items.length === 1) {
199
+ return items[0];
200
+ }
201
+ else {
202
+ throw new errors_1.NotFound(`No record found for id '${id}'`);
203
+ }
204
+ }
205
+ return items;
206
+ }
207
+ async $update(id, _data, params = {}) {
208
+ const data = commons_1._.omit(_data, this.id);
209
+ const oldData = await this.$get(id, params);
210
+ const newObject = Object.keys(oldData).reduce((result, key) => {
211
+ if (key !== this.id) {
212
+ // We don't want the id field to be changed
213
+ result[key] = data[key] === undefined ? null : data[key];
214
+ }
215
+ return result;
216
+ }, {});
217
+ await this.db(params).update(newObject, '*').where(this.id, id);
218
+ return this.$get(id, params);
219
+ }
220
+ async $remove(id, params = {}) {
221
+ const items = await this._findOrGet(id, params);
222
+ const { query } = this.filterQuery(params);
223
+ const q = this.db(params);
224
+ const idList = items.map((current) => current[this.id]);
225
+ query[this.id] = { $in: idList };
226
+ // build up the knex query out of the query params
227
+ this.knexify(q, query);
228
+ await q.del().catch(error_handler_1.errorHandler);
229
+ if (id !== null) {
230
+ if (items.length === 1) {
231
+ return items[0];
232
+ }
233
+ throw new errors_1.NotFound(`No record found for id '${id}'`);
234
+ }
235
+ return items;
236
+ }
237
+ }
238
+ exports.KnexAdapter = KnexAdapter;
239
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const ERROR: unique symbol;
2
+ export declare function errorHandler(error: any): void;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.errorHandler = exports.ERROR = void 0;
4
+ const errors_1 = require("@feathersjs/errors");
5
+ exports.ERROR = Symbol('@feathersjs/knex/error');
6
+ function errorHandler(error) {
7
+ const { message } = error;
8
+ let feathersError = error;
9
+ if (error.sqlState && error.sqlState.length) {
10
+ // remove SQLSTATE marker (#) and pad/truncate SQLSTATE to 5 chars
11
+ const sqlState = ('00000' + error.sqlState.replace('#', '')).slice(-5);
12
+ switch (sqlState.slice(0, 2)) {
13
+ case '02':
14
+ feathersError = new errors_1.errors.NotFound(message);
15
+ break;
16
+ case '28':
17
+ feathersError = new errors_1.errors.Forbidden(message);
18
+ break;
19
+ case '08':
20
+ case '0A':
21
+ case '0K':
22
+ feathersError = new errors_1.errors.Unavailable(message);
23
+ break;
24
+ case '20':
25
+ case '21':
26
+ case '22':
27
+ case '23':
28
+ case '24':
29
+ case '25':
30
+ case '40':
31
+ case '42':
32
+ case '70':
33
+ feathersError = new errors_1.errors.BadRequest(message);
34
+ break;
35
+ default:
36
+ feathersError = new errors_1.errors.GeneralError(message);
37
+ }
38
+ }
39
+ else if (error.code === 'SQLITE_ERROR') {
40
+ // NOTE (EK): Error codes taken from
41
+ // https://www.sqlite.org/c3ref/c_abort.html
42
+ switch (error.errno) {
43
+ case 1:
44
+ case 8:
45
+ case 18:
46
+ case 19:
47
+ case 20:
48
+ feathersError = new errors_1.errors.BadRequest(message);
49
+ break;
50
+ case 2:
51
+ feathersError = new errors_1.errors.Unavailable(message);
52
+ break;
53
+ case 3:
54
+ case 23:
55
+ feathersError = new errors_1.errors.Forbidden(message);
56
+ break;
57
+ case 12:
58
+ feathersError = new errors_1.errors.NotFound(message);
59
+ break;
60
+ default:
61
+ feathersError = new errors_1.errors.GeneralError(message);
62
+ break;
63
+ }
64
+ }
65
+ else if (typeof error.code === 'string' && error.severity && error.routine) {
66
+ // NOTE: Error codes taken from
67
+ // https://www.postgresql.org/docs/9.6/static/errcodes-appendix.html
68
+ // Omit query information
69
+ const messages = error.message.split('-');
70
+ error.message = messages[messages.length - 1];
71
+ switch (error.code.slice(0, 2)) {
72
+ case '22':
73
+ case '23':
74
+ feathersError = new errors_1.errors.BadRequest(message);
75
+ break;
76
+ case '28':
77
+ feathersError = new errors_1.errors.Forbidden(message);
78
+ break;
79
+ case '3D':
80
+ case '3F':
81
+ case '42':
82
+ feathersError = new errors_1.errors.Unprocessable(message);
83
+ break;
84
+ default:
85
+ feathersError = new errors_1.errors.GeneralError(message);
86
+ break;
87
+ }
88
+ }
89
+ else if (!(error instanceof errors_1.errors.FeathersError)) {
90
+ feathersError = new errors_1.errors.GeneralError(message);
91
+ }
92
+ feathersError[exports.ERROR] = error;
93
+ throw feathersError;
94
+ }
95
+ exports.errorHandler = errorHandler;
96
+ //# sourceMappingURL=error-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../src/error-handler.ts"],"names":[],"mappings":"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"}
package/lib/hooks.d.ts ADDED
File without changes
package/lib/hooks.js ADDED
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ // import { createDebug } from '@feathersjs/commons'
3
+ // import { HookContext } from '@feathersjs/feathers'
4
+ // const debug = createDebug('feathers-knex-transaction')
5
+ // const ROLLBACK = { rollback: true }
6
+ // export const getKnex = (context: HookContext) => {
7
+ // const knex = context.service.Model
8
+ // return knex && typeof knex.transaction === 'function' ? knex : undefined
9
+ // }
10
+ // export const start = (options = {}) => {
11
+ // options = Object.assign({ getKnex }, options)
12
+ // return (context: HookContext) => {
13
+ // const { transaction } = context.params
14
+ // const parent = transaction
15
+ // const knex = transaction ? transaction.trx : options.getKnex(context)
16
+ // if (!knex) {
17
+ // return
18
+ // }
19
+ // return new Promise((resolve, reject) => {
20
+ // const transaction = {}
21
+ // if (parent) {
22
+ // transaction.parent = parent
23
+ // transaction.committed = parent.committed
24
+ // } else {
25
+ // transaction.committed = new Promise((resolve) => {
26
+ // transaction.resolve = resolve
27
+ // })
28
+ // }
29
+ // transaction.starting = true
30
+ // transaction.promise = knex
31
+ // .transaction((trx) => {
32
+ // transaction.trx = trx
33
+ // transaction.id = Date.now()
34
+ // context.params = { ...context.params, transaction }
35
+ // debug('started a new transaction %s', transaction.id)
36
+ // resolve()
37
+ // })
38
+ // .catch((error) => {
39
+ // if (transaction.starting) {
40
+ // reject(error)
41
+ // } else if (error !== ROLLBACK) {
42
+ // throw error
43
+ // }
44
+ // })
45
+ // })
46
+ // }
47
+ // }
48
+ // export const end = () => {
49
+ // return (hook) => {
50
+ // const { transaction } = hook.params
51
+ // if (!transaction) {
52
+ // return
53
+ // }
54
+ // const { trx, id, promise, parent } = transaction
55
+ // hook.params = { ...hook.params, transaction: parent }
56
+ // transaction.starting = false
57
+ // return trx
58
+ // .commit()
59
+ // .then(() => promise)
60
+ // .then(() => transaction.resolve && transaction.resolve(true))
61
+ // .then(() => debug('ended transaction %s', id))
62
+ // .then(() => hook)
63
+ // }
64
+ // }
65
+ // export const rollback = () => {
66
+ // return (hook) => {
67
+ // const { transaction } = hook.params
68
+ // if (!transaction) {
69
+ // return
70
+ // }
71
+ // const { trx, id, promise, parent } = transaction
72
+ // hook.params = { ...hook.params, transaction: parent }
73
+ // transaction.starting = false
74
+ // return trx
75
+ // .rollback(ROLLBACK)
76
+ // .then(() => promise)
77
+ // .then(() => transaction.resolve && transaction.resolve(false))
78
+ // .then(() => debug('rolled back transaction %s', id))
79
+ // .then(() => hook)
80
+ // }
81
+ // }
82
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":";AAAA,oDAAoD;AACpD,qDAAqD;AAErD,yDAAyD;AAEzD,sCAAsC;AAEtC,qDAAqD;AACrD,uCAAuC;AAEvC,6EAA6E;AAC7E,IAAI;AAEJ,2CAA2C;AAC3C,kDAAkD;AAElD,uCAAuC;AACvC,6CAA6C;AAC7C,iCAAiC;AACjC,4EAA4E;AAE5E,mBAAmB;AACnB,eAAe;AACf,QAAQ;AAER,gDAAgD;AAChD,+BAA+B;AAE/B,sBAAsB;AACtB,sCAAsC;AACtC,mDAAmD;AACnD,iBAAiB;AACjB,6DAA6D;AAC7D,0CAA0C;AAC1C,aAAa;AACb,UAAU;AAEV,oCAAoC;AACpC,mCAAmC;AACnC,kCAAkC;AAClC,kCAAkC;AAClC,wCAAwC;AAExC,gEAAgE;AAEhE,kEAAkE;AAElE,sBAAsB;AACtB,aAAa;AACb,8BAA8B;AAC9B,wCAAwC;AACxC,4BAA4B;AAC5B,6CAA6C;AAC7C,0BAA0B;AAC1B,cAAc;AACd,aAAa;AACb,SAAS;AACT,MAAM;AACN,IAAI;AAEJ,6BAA6B;AAC7B,uBAAuB;AACvB,0CAA0C;AAE1C,0BAA0B;AAC1B,eAAe;AACf,QAAQ;AAER,uDAAuD;AAEvD,4DAA4D;AAC5D,mCAAmC;AAEnC,iBAAiB;AACjB,kBAAkB;AAClB,6BAA6B;AAC7B,sEAAsE;AACtE,uDAAuD;AACvD,0BAA0B;AAC1B,MAAM;AACN,IAAI;AAEJ,kCAAkC;AAClC,uBAAuB;AACvB,0CAA0C;AAE1C,0BAA0B;AAC1B,eAAe;AACf,QAAQ;AAER,uDAAuD;AAEvD,4DAA4D;AAC5D,mCAAmC;AAEnC,iBAAiB;AACjB,4BAA4B;AAC5B,6BAA6B;AAC7B,uEAAuE;AACvE,6DAA6D;AAC7D,0BAA0B;AAC1B,MAAM;AACN,IAAI"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { PaginationOptions } from '@feathersjs/adapter-commons';
2
+ import { Paginated, ServiceMethods, Id } from '@feathersjs/feathers';
3
+ import { KnexAdapter, KnexAdapterParams } from './adapter';
4
+ export * from './adapter';
5
+ export * from './error-handler';
6
+ export declare class KnexService<T = any, D = Partial<T>, P extends KnexAdapterParams<any> = KnexAdapterParams> extends KnexAdapter<T, D, P> implements ServiceMethods<T | Paginated<T>, D, P> {
7
+ find(params?: P & {
8
+ paginate?: PaginationOptions;
9
+ }): Promise<Paginated<T>>;
10
+ find(params?: P & {
11
+ paginate: false;
12
+ }): Promise<T[]>;
13
+ find(params?: P): Promise<Paginated<T> | T[]>;
14
+ get(id: Id, params?: P): Promise<T>;
15
+ create(data: Partial<D>, params?: P): Promise<T>;
16
+ create(data: Partial<D>[], params?: P): Promise<T[]>;
17
+ update(id: Id, data: D, params?: P): Promise<T>;
18
+ patch(id: Id, data: Partial<D>, params?: P): Promise<T>;
19
+ patch(id: null, data: Partial<D>, params?: P): Promise<T[]>;
20
+ remove(id: Id, params?: P): Promise<T>;
21
+ remove(id: null, params?: P): Promise<T[]>;
22
+ }
package/lib/index.js ADDED
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.KnexService = void 0;
18
+ const adapter_1 = require("./adapter");
19
+ __exportStar(require("./adapter"), exports);
20
+ __exportStar(require("./error-handler"), exports);
21
+ // export * from './hooks'
22
+ class KnexService extends adapter_1.KnexAdapter {
23
+ async find(params) {
24
+ return this._find(params);
25
+ }
26
+ async get(id, params) {
27
+ return this._get(id, params);
28
+ }
29
+ async create(data, params) {
30
+ return this._create(data, params);
31
+ }
32
+ async update(id, data, params) {
33
+ return this._update(id, data, params);
34
+ }
35
+ async patch(id, data, params) {
36
+ return this._patch(id, data, params);
37
+ }
38
+ async remove(id, params) {
39
+ return this._remove(id, params);
40
+ }
41
+ }
42
+ exports.KnexService = KnexService;
43
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAEA,uCAA0D;AAE1D,4CAAyB;AACzB,kDAA+B;AAC/B,0BAA0B;AAE1B,MAAa,WACX,SAAQ,qBAAoB;IAM5B,KAAK,CAAC,IAAI,CAAC,MAAU;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAQ,CAAA;IAClC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAAM,EAAE,MAAU;QAC1B,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IAC9B,CAAC;IAID,KAAK,CAAC,MAAM,CAAC,IAA+B,EAAE,MAAU;QACtD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACnC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAM,EAAE,IAAO,EAAE,MAAU;QACtC,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;IACvC,CAAC;IAID,KAAK,CAAC,KAAK,CAAC,EAAc,EAAE,IAAgB,EAAE,MAAU;QACtD,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;IACtC,CAAC;IAID,KAAK,CAAC,MAAM,CAAC,EAAc,EAAE,MAAU;QACrC,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IACjC,CAAC;CACF;AApCD,kCAoCC"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@feathersjs/knex",
3
+ "description": "Feathers SQL service adapter using KnexJS",
4
+ "version": "5.0.0-pre.24",
5
+ "homepage": "https://feathersjs.com",
6
+ "main": "lib/",
7
+ "keywords": [
8
+ "feathers",
9
+ "feathers-plugin"
10
+ ],
11
+ "license": "MIT",
12
+ "funding": {
13
+ "type": "github",
14
+ "url": "https://github.com/sponsors/daffl"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git://github.com/feathersjs/feathers.git"
19
+ },
20
+ "author": {
21
+ "name": "Feathers contributors",
22
+ "email": "hello@feathersjs.com",
23
+ "url": "https://feathersjs.com"
24
+ },
25
+ "contributors": [],
26
+ "bugs": {
27
+ "url": "https://github.com/feathersjs/feathers/issues"
28
+ },
29
+ "engines": {
30
+ "node": ">= 14"
31
+ },
32
+ "files": [
33
+ "CHANGELOG.md",
34
+ "LICENSE",
35
+ "README.md",
36
+ "src/**",
37
+ "lib/**",
38
+ "*.d.ts",
39
+ "*.js"
40
+ ],
41
+ "scripts": {
42
+ "compile": "shx rm -rf lib/ && tsc",
43
+ "test": "mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts"
44
+ },
45
+ "directories": {
46
+ "lib": "lib"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "dependencies": {
52
+ "@feathersjs/adapter-commons": "^5.0.0-pre.24",
53
+ "@feathersjs/commons": "^5.0.0-pre.24",
54
+ "@feathersjs/errors": "^5.0.0-pre.24",
55
+ "@feathersjs/feathers": "^5.0.0-pre.24"
56
+ },
57
+ "peerDependencies": {
58
+ "knex": "^2.1.0"
59
+ },
60
+ "devDependencies": {
61
+ "@feathersjs/adapter-tests": "^5.0.0-pre.24",
62
+ "@types/mocha": "^9.1.1",
63
+ "@types/node": "^17.0.40",
64
+ "knex": "^2.1.0",
65
+ "mocha": "^10.0.0",
66
+ "shx": "^0.3.4",
67
+ "sqlite3": "^5.0.8",
68
+ "typescript": "^4.7.3"
69
+ },
70
+ "gitHead": "72779fa2938f43d7b345b6861ff0035481a07de3"
71
+ }
package/src/adapter.ts ADDED
@@ -0,0 +1,314 @@
1
+ import { Id, NullableId, Paginated, Query } from '@feathersjs/feathers'
2
+ import { _ } from '@feathersjs/commons'
3
+ import { AdapterBase, PaginationOptions, filterQuery } from '@feathersjs/adapter-commons'
4
+ import { NotFound } from '@feathersjs/errors'
5
+ import { Knex } from 'knex'
6
+
7
+ import { errorHandler } from './error-handler'
8
+ import { KnexAdapterOptions, KnexAdapterParams } from './declarations'
9
+ const METHODS = {
10
+ $ne: 'whereNot',
11
+ $in: 'whereIn',
12
+ $nin: 'whereNotIn',
13
+ $or: 'orWhere',
14
+ $and: 'andWhere'
15
+ }
16
+
17
+ const OPERATORS = {
18
+ $lt: '<',
19
+ $lte: '<=',
20
+ $gt: '>',
21
+ $gte: '>=',
22
+ $like: 'like',
23
+ $notlike: 'not like',
24
+ $ilike: 'ilike'
25
+ }
26
+
27
+ export class KnexAdapter<
28
+ T,
29
+ D = Partial<T>,
30
+ P extends KnexAdapterParams<any> = KnexAdapterParams
31
+ > extends AdapterBase<T, D, P, KnexAdapterOptions> {
32
+ table: string
33
+ schema?: string
34
+
35
+ constructor(options: KnexAdapterOptions) {
36
+ if (!options || !options.Model) {
37
+ throw new Error('You must provide a Model (the initialized knex object)')
38
+ }
39
+
40
+ if (typeof options.name !== 'string') {
41
+ throw new Error('No table name specified.')
42
+ }
43
+
44
+ super({
45
+ id: 'id',
46
+ ...options,
47
+ filters: {
48
+ ...options.filters,
49
+ $and: (value: any) => value
50
+ },
51
+ operators: [...(options.operators || []), '$like', '$notlike', '$ilike', '$and', '$or']
52
+ })
53
+
54
+ this.table = options.name
55
+ this.schema = options.schema
56
+ }
57
+
58
+ get Model() {
59
+ return this.options.Model
60
+ }
61
+
62
+ get fullName() {
63
+ return this.schema ? `${this.schema}.${this.table}` : this.table
64
+ }
65
+
66
+ db(params?: P) {
67
+ const { Model, table, schema } = this
68
+
69
+ if (params && params.transaction && params.transaction.trx) {
70
+ const { trx } = params.transaction
71
+ // debug('ran %s with transaction %s', fullName, id)
72
+ return schema ? (trx.withSchema(schema).table(table) as Knex.QueryBuilder) : trx(table)
73
+ }
74
+ return schema ? (Model.withSchema(schema).table(table) as Knex.QueryBuilder) : Model(table)
75
+ }
76
+
77
+ knexify(knexQuery: Knex.QueryBuilder, query: Query = {}, parentKey?: string): Knex.QueryBuilder {
78
+ const knexify = this.knexify.bind(this)
79
+
80
+ return Object.keys(query || {}).reduce((currentQuery, key) => {
81
+ const value = query[key]
82
+
83
+ if (_.isObject(value)) {
84
+ return knexify(currentQuery, value, key)
85
+ }
86
+
87
+ const column = parentKey || key
88
+ const method = METHODS[key as keyof typeof METHODS]
89
+
90
+ if (method) {
91
+ if (key === '$or' || key === '$and') {
92
+ // This will create a nested query
93
+ currentQuery.where(function (this: any) {
94
+ for (const condition of value) {
95
+ this[method](function (this: Knex.QueryBuilder) {
96
+ knexify(this, condition)
97
+ })
98
+ }
99
+ })
100
+
101
+ return currentQuery
102
+ }
103
+
104
+ return (currentQuery as any)[method](column, value)
105
+ }
106
+
107
+ const operator = OPERATORS[key as keyof typeof OPERATORS] || '='
108
+
109
+ return operator === '='
110
+ ? currentQuery.where(column, value)
111
+ : currentQuery.where(column, operator, value)
112
+ }, knexQuery)
113
+ }
114
+
115
+ createQuery(params: P) {
116
+ const { table, id } = this
117
+ const { filters, query } = this.filterQuery(params)
118
+ const builder = this.db(params)
119
+
120
+ // $select uses a specific find syntax, so it has to come first.
121
+ if (filters.$select) {
122
+ // always select the id field, but make sure we only select it once
123
+ builder.select(...new Set([...filters.$select, `${table}.${id}`]))
124
+ } else {
125
+ builder.select(`${table}.*`)
126
+ }
127
+
128
+ // build up the knex query out of the query params, include $and and $or filters
129
+ this.knexify(builder, {
130
+ ...query,
131
+ ..._.pick(filters, '$and', '$or')
132
+ })
133
+
134
+ // Handle $sort
135
+ if (filters.$sort) {
136
+ return Object.keys(filters.$sort).reduce(
137
+ (currentQuery, key) => currentQuery.orderBy(key, filters.$sort[key] === 1 ? 'asc' : 'desc'),
138
+ builder
139
+ )
140
+ }
141
+
142
+ return builder
143
+ }
144
+
145
+ filterQuery(params: P) {
146
+ const options = this.getOptions(params)
147
+ const { filters, query } = filterQuery(params?.query || {}, options)
148
+
149
+ return { filters, query, paginate: options.paginate }
150
+ }
151
+
152
+ async $find(params?: P & { paginate?: PaginationOptions }): Promise<Paginated<T>>
153
+ async $find(params?: P & { paginate: false }): Promise<T[]>
154
+ async $find(params?: P): Promise<Paginated<T> | T[]>
155
+ async $find(params: P = {} as P): Promise<Paginated<T> | T[]> {
156
+ const { filters, paginate } = this.filterQuery(params)
157
+ const builder = params.knex ? params.knex.clone() : this.createQuery(params)
158
+ const countBuilder = builder.clone().count(`${this.table}.${this.id} as total`)
159
+
160
+ // Handle $limit
161
+ if (filters.$limit) {
162
+ builder.limit(filters.$limit)
163
+ }
164
+
165
+ // Handle $skip
166
+ if (filters.$skip) {
167
+ builder.offset(filters.$skip)
168
+ }
169
+
170
+ // provide default sorting if its not set
171
+ if (!filters.$sort) {
172
+ builder.orderBy(`${this.table}.${this.id}`, 'asc')
173
+ }
174
+
175
+ const data = filters.$limit === 0 ? [] : await builder
176
+
177
+ if (paginate && paginate.default) {
178
+ return {
179
+ total: await countBuilder.then((count) => (count[0] ? count[0].total : 0)),
180
+ limit: filters.$limit,
181
+ skip: filters.$skip || 0,
182
+ data
183
+ }
184
+ }
185
+
186
+ return data
187
+ }
188
+
189
+ async _findOrGet(id: NullableId, params?: P) {
190
+ const findParams = {
191
+ ...params,
192
+ paginate: false,
193
+ query: {
194
+ ...params?.query,
195
+ ...(id !== null ? { [`${this.table}.${this.id}`]: id } : {})
196
+ }
197
+ }
198
+
199
+ return this.$find(findParams as any) as any as Promise<T[]>
200
+ }
201
+
202
+ async $get(id: Id, params: P = {} as P): Promise<T> {
203
+ const data = await this._findOrGet(id, params)
204
+
205
+ if (data.length !== 1) {
206
+ throw new NotFound(`No record found for id '${id}'`)
207
+ }
208
+
209
+ return data[0]
210
+ }
211
+
212
+ async $create(data: Partial<D>, params?: P): Promise<T>
213
+ async $create(data: Partial<D>[], params?: P): Promise<T[]>
214
+ async $create(data: Partial<D> | Partial<D>[], _params?: P): Promise<T | T[]>
215
+ async $create(_data: Partial<D> | Partial<D>[], params: P = {} as P): Promise<T | T[]> {
216
+ const data = _data as any
217
+
218
+ if (Array.isArray(data)) {
219
+ return Promise.all(data.map((current) => this.$create(current, params)))
220
+ }
221
+
222
+ const client = this.db(params).client.config.client
223
+ const returning = client === 'pg' || client === 'oracledb' || client === 'mssql' ? [this.id] : []
224
+ const rows: any = await this.db(params).insert(data, returning).catch(errorHandler)
225
+ const id = data[this.id] || rows[0][this.id] || rows[0]
226
+
227
+ if (!id) {
228
+ return rows as T[]
229
+ }
230
+
231
+ return this.$get(id, params)
232
+ }
233
+
234
+ async $patch(id: null, data: Partial<D>, params?: P): Promise<T[]>
235
+ async $patch(id: Id, data: Partial<D>, params?: P): Promise<T>
236
+ async $patch(id: NullableId, data: Partial<D>, _params?: P): Promise<T | T[]>
237
+ async $patch(id: NullableId, raw: Partial<D>, params: P = {} as P): Promise<T | T[]> {
238
+ const data = _.omit(raw, this.id)
239
+ const results = await this._findOrGet(id, {
240
+ ...params,
241
+ query: {
242
+ ...params?.query,
243
+ $select: [`${this.table}.${this.id}`]
244
+ }
245
+ })
246
+ const idList = results.map((current: any) => current[this.id])
247
+ const updateParams = {
248
+ ...params,
249
+ query: {
250
+ [`${this.table}.${this.id}`]: { $in: idList },
251
+ ...(params?.query?.$select ? { $select: params?.query?.$select } : {})
252
+ }
253
+ }
254
+ const builder = this.createQuery(updateParams)
255
+
256
+ await builder.update(data)
257
+
258
+ const items = await this._findOrGet(null, updateParams)
259
+
260
+ if (id !== null) {
261
+ if (items.length === 1) {
262
+ return items[0]
263
+ } else {
264
+ throw new NotFound(`No record found for id '${id}'`)
265
+ }
266
+ }
267
+
268
+ return items
269
+ }
270
+
271
+ async $update(id: Id, _data: D, params: P = {} as P): Promise<T> {
272
+ const data = _.omit(_data, this.id)
273
+ const oldData = await this.$get(id, params)
274
+ const newObject = Object.keys(oldData).reduce((result: any, key) => {
275
+ if (key !== this.id) {
276
+ // We don't want the id field to be changed
277
+ result[key] = data[key] === undefined ? null : data[key]
278
+ }
279
+
280
+ return result
281
+ }, {})
282
+
283
+ await this.db(params).update(newObject, '*').where(this.id, id)
284
+
285
+ return this.$get(id, params)
286
+ }
287
+
288
+ async $remove(id: null, params?: P): Promise<T[]>
289
+ async $remove(id: Id, params?: P): Promise<T>
290
+ async $remove(id: NullableId, _params?: P): Promise<T | T[]>
291
+ async $remove(id: NullableId, params: P = {} as P): Promise<T | T[]> {
292
+ const items = await this._findOrGet(id, params)
293
+ const { query } = this.filterQuery(params)
294
+ const q = this.db(params)
295
+ const idList = items.map((current: any) => current[this.id])
296
+
297
+ query[this.id] = { $in: idList }
298
+
299
+ // build up the knex query out of the query params
300
+ this.knexify(q, query)
301
+
302
+ await q.del().catch(errorHandler)
303
+
304
+ if (id !== null) {
305
+ if (items.length === 1) {
306
+ return items[0]
307
+ }
308
+
309
+ throw new NotFound(`No record found for id '${id}'`)
310
+ }
311
+
312
+ return items
313
+ }
314
+ }
@@ -0,0 +1,23 @@
1
+ import { Knex } from 'knex'
2
+ import { AdapterServiceOptions, AdapterParams, AdapterQuery } from '@feathersjs/adapter-commons'
3
+
4
+ export interface KnexAdapterOptions extends AdapterServiceOptions {
5
+ Model: Knex
6
+ name: string
7
+ schema?: string
8
+ }
9
+
10
+ export interface KnexAdapterTransaction {
11
+ starting: boolean
12
+ parent?: KnexAdapterTransaction
13
+ committed?: any
14
+ resolve?: any
15
+ trx?: Knex.Transaction
16
+ id?: number
17
+ promise?: Promise<any>
18
+ }
19
+
20
+ export interface KnexAdapterParams<Q = AdapterQuery> extends AdapterParams<Q, Partial<KnexAdapterOptions>> {
21
+ knex?: Knex.QueryBuilder
22
+ transaction?: KnexAdapterTransaction
23
+ }
@@ -0,0 +1,95 @@
1
+ import { errors } from '@feathersjs/errors'
2
+
3
+ export const ERROR = Symbol('@feathersjs/knex/error')
4
+
5
+ export function errorHandler(error: any) {
6
+ const { message } = error
7
+ let feathersError = error
8
+
9
+ if (error.sqlState && error.sqlState.length) {
10
+ // remove SQLSTATE marker (#) and pad/truncate SQLSTATE to 5 chars
11
+ const sqlState = ('00000' + error.sqlState.replace('#', '')).slice(-5)
12
+
13
+ switch (sqlState.slice(0, 2)) {
14
+ case '02':
15
+ feathersError = new errors.NotFound(message)
16
+ break
17
+ case '28':
18
+ feathersError = new errors.Forbidden(message)
19
+ break
20
+ case '08':
21
+ case '0A':
22
+ case '0K':
23
+ feathersError = new errors.Unavailable(message)
24
+ break
25
+ case '20':
26
+ case '21':
27
+ case '22':
28
+ case '23':
29
+ case '24':
30
+ case '25':
31
+ case '40':
32
+ case '42':
33
+ case '70':
34
+ feathersError = new errors.BadRequest(message)
35
+ break
36
+ default:
37
+ feathersError = new errors.GeneralError(message)
38
+ }
39
+ } else if (error.code === 'SQLITE_ERROR') {
40
+ // NOTE (EK): Error codes taken from
41
+ // https://www.sqlite.org/c3ref/c_abort.html
42
+ switch (error.errno) {
43
+ case 1:
44
+ case 8:
45
+ case 18:
46
+ case 19:
47
+ case 20:
48
+ feathersError = new errors.BadRequest(message)
49
+ break
50
+ case 2:
51
+ feathersError = new errors.Unavailable(message)
52
+ break
53
+ case 3:
54
+ case 23:
55
+ feathersError = new errors.Forbidden(message)
56
+ break
57
+ case 12:
58
+ feathersError = new errors.NotFound(message)
59
+ break
60
+ default:
61
+ feathersError = new errors.GeneralError(message)
62
+ break
63
+ }
64
+ } else if (typeof error.code === 'string' && error.severity && error.routine) {
65
+ // NOTE: Error codes taken from
66
+ // https://www.postgresql.org/docs/9.6/static/errcodes-appendix.html
67
+ // Omit query information
68
+ const messages = error.message.split('-')
69
+ error.message = messages[messages.length - 1]
70
+
71
+ switch (error.code.slice(0, 2)) {
72
+ case '22':
73
+ case '23':
74
+ feathersError = new errors.BadRequest(message)
75
+ break
76
+ case '28':
77
+ feathersError = new errors.Forbidden(message)
78
+ break
79
+ case '3D':
80
+ case '3F':
81
+ case '42':
82
+ feathersError = new errors.Unprocessable(message)
83
+ break
84
+ default:
85
+ feathersError = new errors.GeneralError(message)
86
+ break
87
+ }
88
+ } else if (!(error instanceof errors.FeathersError)) {
89
+ feathersError = new errors.GeneralError(message)
90
+ }
91
+
92
+ feathersError[ERROR] = error
93
+
94
+ throw feathersError
95
+ }
package/src/hooks.ts ADDED
@@ -0,0 +1,101 @@
1
+ import { createDebug } from '@feathersjs/commons'
2
+ import { HookContext } from '@feathersjs/feathers'
3
+ import { Knex } from 'knex'
4
+ import { KnexAdapterTransaction } from './declarations'
5
+
6
+ const debug = createDebug('feathers-knex-transaction')
7
+
8
+ const ROLLBACK = { rollback: true }
9
+
10
+ export const getKnex = (context: HookContext): Knex => {
11
+ const knex = context.service.Model
12
+
13
+ return knex && typeof knex.transaction === 'function' ? knex : undefined
14
+ }
15
+
16
+ export const start =
17
+ () =>
18
+ async (context: HookContext): Promise<void> => {
19
+ const { transaction } = context.params
20
+ const parent = transaction
21
+ const knex: Knex = transaction ? transaction.trx : getKnex(context)
22
+
23
+ if (!knex) {
24
+ return
25
+ }
26
+
27
+ return new Promise<void>((resolve, reject) => {
28
+ const transaction: KnexAdapterTransaction = {
29
+ starting: true
30
+ }
31
+
32
+ if (parent) {
33
+ transaction.parent = parent
34
+ transaction.committed = parent.committed
35
+ } else {
36
+ transaction.committed = new Promise((resolve) => {
37
+ transaction.resolve = resolve
38
+ })
39
+ }
40
+
41
+ transaction.starting = true
42
+ transaction.promise = knex
43
+ .transaction((trx) => {
44
+ transaction.trx = trx
45
+ transaction.id = Date.now()
46
+
47
+ context.params = { ...context.params, transaction }
48
+
49
+ debug('started a new transaction %s', transaction.id)
50
+
51
+ resolve()
52
+ })
53
+ .catch((error) => {
54
+ if (transaction.starting) {
55
+ reject(error)
56
+ } else if (error !== ROLLBACK) {
57
+ throw error
58
+ }
59
+ })
60
+ })
61
+ }
62
+
63
+ export const end = () => (context: HookContext) => {
64
+ const { transaction } = context.params
65
+
66
+ if (!transaction) {
67
+ return
68
+ }
69
+
70
+ const { trx, id, promise, parent } = transaction
71
+
72
+ context.params = { ...context.params, transaction: parent }
73
+ transaction.starting = false
74
+
75
+ return trx
76
+ .commit()
77
+ .then(() => promise)
78
+ .then(() => transaction.resolve && transaction.resolve(true))
79
+ .then(() => debug('ended transaction %s', id))
80
+ .then(() => context)
81
+ }
82
+
83
+ export const rollback = () => (context: HookContext) => {
84
+ const { transaction } = context.params
85
+
86
+ if (!transaction) {
87
+ return
88
+ }
89
+
90
+ const { trx, id, promise, parent } = transaction
91
+
92
+ context.params = { ...context.params, transaction: parent }
93
+ transaction.starting = false
94
+
95
+ return trx
96
+ .rollback(ROLLBACK)
97
+ .then(() => promise)
98
+ .then(() => transaction.resolve && transaction.resolve(false))
99
+ .then(() => debug('rolled back transaction %s', id))
100
+ .then(() => context)
101
+ }
package/src/index.ts ADDED
@@ -0,0 +1,47 @@
1
+ import { PaginationOptions } from '@feathersjs/adapter-commons'
2
+ import { Paginated, ServiceMethods, Id, NullableId } from '@feathersjs/feathers'
3
+ import { KnexAdapter } from './adapter'
4
+ import { KnexAdapterParams } from './declarations'
5
+
6
+ export * from './declarations'
7
+ export * from './adapter'
8
+ export * from './error-handler'
9
+ export * as transaction from './hooks'
10
+
11
+ export class KnexService<T = any, D = Partial<T>, P extends KnexAdapterParams<any> = KnexAdapterParams>
12
+ extends KnexAdapter<T, D, P>
13
+ implements ServiceMethods<T | Paginated<T>, D, P>
14
+ {
15
+ async find(params?: P & { paginate?: PaginationOptions }): Promise<Paginated<T>>
16
+ async find(params?: P & { paginate: false }): Promise<T[]>
17
+ async find(params?: P): Promise<Paginated<T> | T[]>
18
+ async find(params?: P): Promise<Paginated<T> | T[]> {
19
+ return this._find(params) as any
20
+ }
21
+
22
+ async get(id: Id, params?: P): Promise<T> {
23
+ return this._get(id, params)
24
+ }
25
+
26
+ async create(data: Partial<D>, params?: P): Promise<T>
27
+ async create(data: Partial<D>[], params?: P): Promise<T[]>
28
+ async create(data: Partial<D> | Partial<D>[], params?: P): Promise<T | T[]> {
29
+ return this._create(data, params)
30
+ }
31
+
32
+ async update(id: Id, data: D, params?: P): Promise<T> {
33
+ return this._update(id, data, params)
34
+ }
35
+
36
+ async patch(id: Id, data: Partial<D>, params?: P): Promise<T>
37
+ async patch(id: null, data: Partial<D>, params?: P): Promise<T[]>
38
+ async patch(id: NullableId, data: Partial<D>, params?: P): Promise<T | T[]> {
39
+ return this._patch(id, data, params)
40
+ }
41
+
42
+ async remove(id: Id, params?: P): Promise<T>
43
+ async remove(id: null, params?: P): Promise<T[]>
44
+ async remove(id: NullableId, params?: P): Promise<T | T[]> {
45
+ return this._remove(id, params)
46
+ }
47
+ }