@feathersjs/memory 5.0.0-pre.9 → 5.0.1

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/src/index.ts CHANGED
@@ -1,156 +1,308 @@
1
- import { NotFound } from '@feathersjs/errors';
2
- import { _ } from '@feathersjs/commons';
3
- import { sorter, select, AdapterService, ServiceOptions, InternalServiceMethods, AdapterParams } from '@feathersjs/adapter-commons';
4
- import sift from 'sift';
5
- import { NullableId, Id } from '@feathersjs/feathers';
1
+ import { BadRequest, MethodNotAllowed, NotFound } from '@feathersjs/errors'
2
+ import { _ } from '@feathersjs/commons'
3
+ import {
4
+ sorter,
5
+ select,
6
+ AdapterBase,
7
+ AdapterServiceOptions,
8
+ PaginationOptions,
9
+ AdapterParams
10
+ } from '@feathersjs/adapter-commons'
11
+ import sift from 'sift'
12
+ import { NullableId, Id, Params, Paginated } from '@feathersjs/feathers'
6
13
 
7
14
  export interface MemoryServiceStore<T> {
8
- [key: string]: T;
15
+ [key: string]: T
9
16
  }
10
17
 
11
- export interface MemoryServiceOptions<T = any> extends ServiceOptions {
12
- store: MemoryServiceStore<T>;
13
- startId: number;
14
- matcher?: (query: any) => any;
15
- sorter?: (sort: any) => any;
18
+ export interface MemoryServiceOptions<T = any> extends AdapterServiceOptions {
19
+ store?: MemoryServiceStore<T>
20
+ startId?: number
21
+ matcher?: (query: any) => any
22
+ sorter?: (sort: any) => any
16
23
  }
17
24
 
18
- const _select = (data: any, params: any, ...args: any[]) => {
19
- const base = select(params, ...args);
25
+ const _select = (data: any, params: any, ...args: string[]) => {
26
+ const base = select(params, ...args)
20
27
 
21
- return base(JSON.parse(JSON.stringify(data)));
22
- };
23
-
24
- export class Service<T = any, D = Partial<any>> extends AdapterService<T, D> implements InternalServiceMethods<T> {
25
- options: MemoryServiceOptions;
26
- store: MemoryServiceStore<T>;
27
- _uId: number;
28
+ return base(JSON.parse(JSON.stringify(data)))
29
+ }
28
30
 
29
- constructor (options: Partial<MemoryServiceOptions<T>> = {}) {
30
- super(_.extend({
31
+ export class MemoryAdapter<
32
+ Result = any,
33
+ Data = Partial<Result>,
34
+ ServiceParams extends Params = Params,
35
+ PatchData = Partial<Data>
36
+ > extends AdapterBase<Result, Data, PatchData, ServiceParams, MemoryServiceOptions<Result>> {
37
+ store: MemoryServiceStore<Result>
38
+ _uId: number
39
+
40
+ constructor(options: MemoryServiceOptions<Result> = {}) {
41
+ super({
31
42
  id: 'id',
32
43
  matcher: sift,
33
- sorter
34
- }, options));
35
- this._uId = options.startId || 0;
36
- this.store = options.store || {};
44
+ sorter,
45
+ store: {},
46
+ startId: 0,
47
+ ...options
48
+ })
49
+ this._uId = this.options.startId
50
+ this.store = { ...this.options.store }
37
51
  }
38
52
 
39
- async getEntries (params = {}) {
40
- const { query } = this.filterQuery(params);
53
+ async getEntries(_params?: ServiceParams) {
54
+ const params = _params || ({} as ServiceParams)
41
55
 
42
- return this._find(Object.assign({}, params, {
43
- paginate: false,
44
- query
45
- }) as any) as Promise<T[]>;
56
+ return this._find({
57
+ ...params,
58
+ paginate: false
59
+ })
46
60
  }
47
61
 
48
- async _find (params: AdapterParams = {}) {
49
- const { query, filters, paginate } = this.filterQuery(params);
50
- let values = _.values(this.store).filter(this.options.matcher(query));
51
- const total = values.length;
62
+ getQuery(params: ServiceParams) {
63
+ const { $skip, $sort, $limit, $select, ...query } = params.query || {}
52
64
 
53
- if (filters.$sort !== undefined) {
54
- values.sort(this.options.sorter(filters.$sort));
65
+ return {
66
+ query,
67
+ filters: { $skip, $sort, $limit, $select }
55
68
  }
69
+ }
56
70
 
57
- if (filters.$skip !== undefined) {
58
- values = values.slice(filters.$skip);
71
+ async _find(_params?: ServiceParams & { paginate?: PaginationOptions }): Promise<Paginated<Result>>
72
+ async _find(_params?: ServiceParams & { paginate: false }): Promise<Result[]>
73
+ async _find(_params?: ServiceParams): Promise<Paginated<Result> | Result[]>
74
+ async _find(params: ServiceParams = {} as ServiceParams): Promise<Paginated<Result> | Result[]> {
75
+ const { paginate } = this.getOptions(params)
76
+ const { query, filters } = this.getQuery(params)
77
+
78
+ let values = _.values(this.store)
79
+ const total = values.length
80
+ const hasSkip = filters.$skip !== undefined
81
+ const hasSort = filters.$sort !== undefined
82
+ const hasLimit = filters.$limit !== undefined
83
+ const hasQuery = _.keys(query).length > 0
84
+
85
+ if (hasSort) {
86
+ values.sort(this.options.sorter(filters.$sort))
59
87
  }
60
88
 
61
- if (filters.$limit !== undefined) {
62
- values = values.slice(0, filters.$limit);
89
+ if (hasQuery || hasLimit || hasSkip) {
90
+ let skipped = 0
91
+ const matcher = this.options.matcher(query)
92
+ const matched = []
93
+
94
+ for (let index = 0, length = values.length; index < length; index++) {
95
+ const value = values[index]
96
+
97
+ if (hasQuery && !matcher(value, index, values)) {
98
+ continue
99
+ }
100
+
101
+ if (hasSkip && filters.$skip > skipped) {
102
+ skipped++
103
+ continue
104
+ }
105
+
106
+ matched.push(_select(value, params, this.id))
107
+
108
+ if (hasLimit && filters.$limit === matched.length) {
109
+ break
110
+ }
111
+ }
112
+
113
+ values = matched
114
+ } else {
115
+ values = values.map((value) => _select(value, params, this.id))
63
116
  }
64
117
 
65
- const result = {
66
- total,
118
+ const result: Paginated<Result> = {
119
+ total: hasQuery ? values.length : total,
67
120
  limit: filters.$limit,
68
121
  skip: filters.$skip || 0,
69
- data: values.map(value => _select(value, params))
70
- };
122
+ data: filters.$limit === 0 ? [] : values
123
+ }
71
124
 
72
- if (!(paginate && (paginate ).default)) {
73
- return result.data;
125
+ if (!paginate) {
126
+ return result.data
74
127
  }
75
128
 
76
- return result;
129
+ return result
77
130
  }
78
131
 
79
- async _get (id: Id, params: AdapterParams = {}) {
132
+ async _get(id: Id, params: ServiceParams = {} as ServiceParams): Promise<Result> {
133
+ const { query } = this.getQuery(params)
134
+
80
135
  if (id in this.store) {
81
- const { query } = this.filterQuery(params);
82
- const value = this.store[id];
136
+ const value = this.store[id]
83
137
 
84
138
  if (this.options.matcher(query)(value)) {
85
- return _select(value, params, this.id);
139
+ return _select(value, params, this.id)
86
140
  }
87
141
  }
88
142
 
89
- throw new NotFound(`No record found for id '${id}'`);
143
+ throw new NotFound(`No record found for id '${id}'`)
90
144
  }
91
145
 
92
- // Create without hooks and mixins that can be used internally
93
- async _create (data: Partial<T> | Partial<T>[], params: AdapterParams = {}): Promise<T | T[]> {
146
+ async _create(data: Partial<Data>, params?: ServiceParams): Promise<Result>
147
+ async _create(data: Partial<Data>[], params?: ServiceParams): Promise<Result[]>
148
+ async _create(data: Partial<Data> | Partial<Data>[], _params?: ServiceParams): Promise<Result | Result[]>
149
+ async _create(
150
+ data: Partial<Data> | Partial<Data>[],
151
+ params: ServiceParams = {} as ServiceParams
152
+ ): Promise<Result | Result[]> {
94
153
  if (Array.isArray(data)) {
95
- return Promise.all(data.map(current => this._create(current, params) as Promise<T>));
154
+ return Promise.all(data.map((current) => this._create(current, params)))
96
155
  }
97
156
 
98
- const id = (data as any)[this.id] || this._uId++;
99
- const current = _.extend({}, data, { [this.id]: id });
100
- const result = (this.store[id] = current);
157
+ const id = (data as any)[this.id] || this._uId++
158
+ const current = _.extend({}, data, { [this.id]: id })
159
+ const result = (this.store[id] = current)
101
160
 
102
- return _select(result, params, this.id);
161
+ return _select(result, params, this.id) as Result
103
162
  }
104
163
 
105
- async _update (id: NullableId, data: T, params: AdapterParams = {}) {
106
- const oldEntry = await this._get(id);
164
+ async _update(id: Id, data: Data, params: ServiceParams = {} as ServiceParams): Promise<Result> {
165
+ if (id === null || Array.isArray(data)) {
166
+ throw new BadRequest("You can not replace multiple instances. Did you mean 'patch'?")
167
+ }
168
+
169
+ const oldEntry = await this._get(id)
107
170
  // We don't want our id to change type if it can be coerced
108
- const oldId = oldEntry[this.id];
171
+ const oldId = (oldEntry as any)[this.id]
109
172
 
110
173
  // eslint-disable-next-line eqeqeq
111
- id = oldId == id ? oldId : id;
174
+ id = oldId == id ? oldId : id
112
175
 
113
- this.store[id] = _.extend({}, data, { [this.id]: id });
176
+ this.store[id] = _.extend({}, data, { [this.id]: id })
114
177
 
115
- return this._get(id, params);
178
+ return this._get(id, params)
116
179
  }
117
180
 
118
- async _patch (id: NullableId, data: Partial<T>, params: AdapterParams = {}) {
119
- const patchEntry = (entry: T) => {
120
- const currentId = (entry as any)[this.id];
181
+ async _patch(id: null, data: PatchData, params?: ServiceParams): Promise<Result[]>
182
+ async _patch(id: Id, data: PatchData, params?: ServiceParams): Promise<Result>
183
+ async _patch(id: NullableId, data: PatchData, _params?: ServiceParams): Promise<Result | Result[]>
184
+ async _patch(
185
+ id: NullableId,
186
+ data: PatchData,
187
+ params: ServiceParams = {} as ServiceParams
188
+ ): Promise<Result | Result[]> {
189
+ if (id === null && !this.allowsMulti('patch', params)) {
190
+ throw new MethodNotAllowed('Can not patch multiple entries')
191
+ }
192
+
193
+ const { query } = this.getQuery(params)
194
+ const patchEntry = (entry: Result) => {
195
+ const currentId = (entry as any)[this.id]
121
196
 
122
- this.store[currentId] = _.extend(this.store[currentId], _.omit(data, this.id));
197
+ this.store[currentId] = _.extend(this.store[currentId], _.omit(data, this.id))
123
198
 
124
- return _select(this.store[currentId], params, this.id);
125
- };
199
+ return _select(this.store[currentId], params, this.id)
200
+ }
126
201
 
127
202
  if (id === null) {
128
- const entries = await this.getEntries(params);
203
+ const entries = await this.getEntries({
204
+ ...params,
205
+ query
206
+ })
129
207
 
130
- return entries.map(patchEntry);
208
+ return entries.map(patchEntry)
131
209
  }
132
210
 
133
- return patchEntry(await this._get(id, params)); // Will throw an error if not found
211
+ return patchEntry(await this._get(id, params)) // Will throw an error if not found
134
212
  }
135
213
 
136
- // Remove without hooks and mixins that can be used internally
137
- async _remove (id: NullableId, params: AdapterParams = {}): Promise<T|T[]> {
214
+ async _remove(id: null, params?: ServiceParams): Promise<Result[]>
215
+ async _remove(id: Id, params?: ServiceParams): Promise<Result>
216
+ async _remove(id: NullableId, _params?: ServiceParams): Promise<Result | Result[]>
217
+ async _remove(id: NullableId, params: ServiceParams = {} as ServiceParams): Promise<Result | Result[]> {
218
+ if (id === null && !this.allowsMulti('remove', params)) {
219
+ throw new MethodNotAllowed('Can not remove multiple entries')
220
+ }
221
+
222
+ const { query } = this.getQuery(params)
223
+
138
224
  if (id === null) {
139
- const entries = await this.getEntries(params);
225
+ const entries = await this.getEntries({
226
+ ...params,
227
+ query
228
+ })
229
+
230
+ return Promise.all(entries.map((current: any) => this._remove(current[this.id] as Id, params)))
231
+ }
232
+
233
+ const entry = await this._get(id, params)
234
+
235
+ delete this.store[id]
236
+
237
+ return entry
238
+ }
239
+ }
240
+
241
+ export class MemoryService<
242
+ Result = any,
243
+ Data = Partial<Result>,
244
+ ServiceParams extends AdapterParams = AdapterParams,
245
+ PatchData = Partial<Data>
246
+ > extends MemoryAdapter<Result, Data, ServiceParams, PatchData> {
247
+ async find(params?: ServiceParams & { paginate?: PaginationOptions }): Promise<Paginated<Result>>
248
+ async find(params?: ServiceParams & { paginate: false }): Promise<Result[]>
249
+ async find(params?: ServiceParams): Promise<Paginated<Result> | Result[]>
250
+ async find(params?: ServiceParams): Promise<Paginated<Result> | Result[]> {
251
+ return this._find({
252
+ ...params,
253
+ query: await this.sanitizeQuery(params)
254
+ })
255
+ }
140
256
 
141
- return Promise.all(entries.map(current =>
142
- this._remove((current as any)[this.id], params) as Promise<T>
143
- ));
257
+ async get(id: Id, params?: ServiceParams): Promise<Result> {
258
+ return this._get(id, {
259
+ ...params,
260
+ query: await this.sanitizeQuery(params)
261
+ })
262
+ }
263
+
264
+ async create(data: Data, params?: ServiceParams): Promise<Result>
265
+ async create(data: Data[], params?: ServiceParams): Promise<Result[]>
266
+ async create(data: Data | Data[], params?: ServiceParams): Promise<Result | Result[]> {
267
+ if (Array.isArray(data) && !this.allowsMulti('create', params)) {
268
+ throw new MethodNotAllowed('Can not create multiple entries')
144
269
  }
145
270
 
146
- const entry = await this._get(id, params);
271
+ return this._create(data, params)
272
+ }
273
+
274
+ async update(id: Id, data: Data, params?: ServiceParams): Promise<Result> {
275
+ return this._update(id, data, {
276
+ ...params,
277
+ query: await this.sanitizeQuery(params)
278
+ })
279
+ }
280
+
281
+ async patch(id: Id, data: PatchData, params?: ServiceParams): Promise<Result>
282
+ async patch(id: null, data: PatchData, params?: ServiceParams): Promise<Result[]>
283
+ async patch(id: NullableId, data: PatchData, params?: ServiceParams): Promise<Result | Result[]> {
284
+ const { $limit, ...query } = await this.sanitizeQuery(params)
147
285
 
148
- delete this.store[id];
286
+ return this._patch(id, data, {
287
+ ...params,
288
+ query
289
+ })
290
+ }
149
291
 
150
- return entry;
292
+ async remove(id: Id, params?: ServiceParams): Promise<Result>
293
+ async remove(id: null, params?: ServiceParams): Promise<Result[]>
294
+ async remove(id: NullableId, params?: ServiceParams): Promise<Result | Result[]> {
295
+ const { $limit, ...query } = await this.sanitizeQuery(params)
296
+
297
+ return this._remove(id, {
298
+ ...params,
299
+ query
300
+ })
151
301
  }
152
302
  }
153
303
 
154
- export function memory (options: Partial<MemoryServiceOptions> = {}) {
155
- return new Service(options);
304
+ export function memory<T = any, D = Partial<T>, P extends Params = Params>(
305
+ options: Partial<MemoryServiceOptions<T>> = {}
306
+ ) {
307
+ return new MemoryService<T, D, P>(options)
156
308
  }