@objectql/core 1.1.0 → 1.2.0
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 +4 -10
- package/dist/index.d.ts +14 -11
- package/dist/index.js +222 -34
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +23 -5
- package/dist/loader.js +201 -9
- package/dist/loader.js.map +1 -1
- package/dist/repository.d.ts +3 -5
- package/dist/repository.js +107 -112
- package/dist/repository.js.map +1 -1
- package/jest.config.js +3 -0
- package/package.json +10 -7
- package/src/index.ts +261 -41
- package/src/loader.ts +194 -10
- package/src/repository.ts +123 -127
- package/test/action.test.ts +58 -0
- package/test/hook.test.ts +60 -0
- package/test/utils.ts +54 -0
- package/tsconfig.json +4 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/README.md +0 -53
- package/dist/driver.d.ts +0 -17
- package/dist/driver.js +0 -3
- package/dist/driver.js.map +0 -1
- package/dist/metadata.d.ts +0 -104
- package/dist/metadata.js +0 -3
- package/dist/metadata.js.map +0 -1
- package/dist/query.d.ts +0 -10
- package/dist/query.js +0 -3
- package/dist/query.js.map +0 -1
- package/dist/registry.d.ts +0 -4
- package/dist/registry.js +0 -8
- package/dist/registry.js.map +0 -1
- package/dist/types.d.ts +0 -83
- package/dist/types.js +0 -6
- package/dist/types.js.map +0 -1
- package/src/driver.ts +0 -24
- package/src/metadata.ts +0 -143
- package/src/query.ts +0 -11
- package/src/registry.ts +0 -6
- package/src/types.ts +0 -115
- package/test/dynamic.test.ts +0 -34
- package/test/fixtures/project.action.ts +0 -6
- package/test/fixtures/project.object.yml +0 -41
- package/test/loader.test.ts +0 -22
- package/test/metadata.test.ts +0 -49
- package/test/mock-driver.ts +0 -86
- package/test/repository.test.ts +0 -150
package/src/repository.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import { ObjectQLContext, IObjectQL, HookContext,
|
|
2
|
-
import { ObjectConfig, FieldConfig } from './metadata';
|
|
3
|
-
import { Driver } from './driver';
|
|
4
|
-
import { UnifiedQuery, FilterCriterion } from './query';
|
|
1
|
+
import { ObjectQLContext, IObjectQL, ObjectConfig, Driver, UnifiedQuery, HookContext, ActionContext, HookAPI, RetrievalHookContext, MutationHookContext, UpdateHookContext } from '@objectql/types';
|
|
5
2
|
|
|
6
3
|
export class ObjectRepository {
|
|
7
4
|
constructor(
|
|
@@ -31,97 +28,52 @@ export class ObjectRepository {
|
|
|
31
28
|
return obj;
|
|
32
29
|
}
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const obj = this.getSchema();
|
|
43
|
-
if (!obj.listeners || !obj.listeners[hookName]) return;
|
|
44
|
-
|
|
45
|
-
const hookFn = obj.listeners[hookName] as HookFunction;
|
|
46
|
-
|
|
47
|
-
// Construct HookContext
|
|
48
|
-
const hookContext: HookContext = {
|
|
49
|
-
ctx: this.context,
|
|
50
|
-
entity: this.objectName,
|
|
51
|
-
op: op,
|
|
52
|
-
utils: {
|
|
53
|
-
restrict: (criterion: FilterCriterion) => {
|
|
54
|
-
if (op !== 'find' && op !== 'count') {
|
|
55
|
-
throw new Error('utils.restrict is only available in query operations');
|
|
56
|
-
}
|
|
57
|
-
const query = dataOrQuery as UnifiedQuery;
|
|
58
|
-
if (!query.filters) {
|
|
59
|
-
query.filters = [criterion];
|
|
60
|
-
} else {
|
|
61
|
-
// Enclose existing filters in implicit AND group by array structure logic or explicit 'and'
|
|
62
|
-
// Implementation depends on how driver parses.
|
|
63
|
-
// Safe approach: filters = [ [criterion], 'and', [existing] ] or similar.
|
|
64
|
-
// For simplicity assuming array of terms means AND:
|
|
65
|
-
query.filters.push(criterion);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
getPreviousDoc: async () => {
|
|
70
|
-
// For update/delete, we might need the ID to find the doc.
|
|
71
|
-
// If doc has ID, use it.
|
|
72
|
-
// This is simplistic; usually 'update' takes 'id', we need to capture it from arguments.
|
|
73
|
-
if (op === 'create') return undefined;
|
|
74
|
-
if (dataOrQuery._id || dataOrQuery.id) {
|
|
75
|
-
return this.findOne(dataOrQuery._id || dataOrQuery.id);
|
|
76
|
-
}
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
31
|
+
private getHookAPI(): HookAPI {
|
|
32
|
+
return {
|
|
33
|
+
find: (obj, q) => this.context.object(obj).find(q),
|
|
34
|
+
findOne: (obj, id) => this.context.object(obj).findOne(id),
|
|
35
|
+
count: (obj, q) => this.context.object(obj).count(q),
|
|
36
|
+
create: (obj, data) => this.context.object(obj).create(data),
|
|
37
|
+
update: (obj, id, data) => this.context.object(obj).update(id, data),
|
|
38
|
+
delete: (obj, id) => this.context.object(obj).delete(id)
|
|
79
39
|
};
|
|
80
|
-
|
|
81
|
-
if (op === 'find' || op === 'count' || op === 'aggregate') {
|
|
82
|
-
hookContext.query = dataOrQuery;
|
|
83
|
-
} else {
|
|
84
|
-
hookContext.doc = dataOrQuery;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Pass ID manually if needed or attach to doc?
|
|
88
|
-
// For strictness, getPreviousDoc needs the ID passed to the operation.
|
|
89
|
-
// We'll rely on "doc" having the data being processed.
|
|
90
|
-
|
|
91
|
-
await hookFn(hookContext);
|
|
92
40
|
}
|
|
93
41
|
|
|
94
42
|
async find(query: UnifiedQuery = {}): Promise<any[]> {
|
|
95
|
-
|
|
96
|
-
|
|
43
|
+
const hookCtx: RetrievalHookContext = {
|
|
44
|
+
...this.context,
|
|
45
|
+
objectName: this.objectName,
|
|
46
|
+
operation: 'find',
|
|
47
|
+
api: this.getHookAPI(),
|
|
48
|
+
state: {},
|
|
49
|
+
query
|
|
50
|
+
};
|
|
51
|
+
await this.app.triggerHook('beforeFind', this.objectName, hookCtx);
|
|
97
52
|
|
|
98
|
-
// TODO: Apply basic filters like spaceId
|
|
99
|
-
const results = await this.getDriver().find(this.objectName, query, this.getOptions());
|
|
53
|
+
// TODO: Apply basic filters like spaceId
|
|
54
|
+
const results = await this.getDriver().find(this.objectName, hookCtx.query || {}, this.getOptions());
|
|
100
55
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const hookFn = this.getSchema().listeners!.afterFind!;
|
|
106
|
-
// Executing per result or once? Spec says "HookContext" has "doc".
|
|
107
|
-
// If finding list, might not match signature.
|
|
108
|
-
// Implemented per-item for now (caution: performance).
|
|
109
|
-
/*
|
|
110
|
-
for (const item of results) {
|
|
111
|
-
await this.executeHookForDoc('afterFind', 'find', item);
|
|
112
|
-
}
|
|
113
|
-
*/
|
|
114
|
-
}
|
|
115
|
-
return results;
|
|
56
|
+
hookCtx.result = results;
|
|
57
|
+
await this.app.triggerHook('afterFind', this.objectName, hookCtx);
|
|
58
|
+
|
|
59
|
+
return hookCtx.result as any[];
|
|
116
60
|
}
|
|
117
61
|
|
|
118
62
|
async findOne(idOrQuery: string | number | UnifiedQuery): Promise<any> {
|
|
119
63
|
if (typeof idOrQuery === 'string' || typeof idOrQuery === 'number') {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
64
|
+
const hookCtx: RetrievalHookContext = {
|
|
65
|
+
...this.context,
|
|
66
|
+
objectName: this.objectName,
|
|
67
|
+
operation: 'find',
|
|
68
|
+
api: this.getHookAPI(), state: {}, query: { _id: idOrQuery }
|
|
69
|
+
};
|
|
70
|
+
await this.app.triggerHook('beforeFind', this.objectName, hookCtx);
|
|
71
|
+
|
|
72
|
+
const result = await this.getDriver().findOne(this.objectName, idOrQuery, hookCtx.query, this.getOptions());
|
|
73
|
+
|
|
74
|
+
hookCtx.result = result;
|
|
75
|
+
await this.app.triggerHook('afterFind', this.objectName, hookCtx);
|
|
76
|
+
return hookCtx.result;
|
|
125
77
|
} else {
|
|
126
78
|
const results = await this.find(idOrQuery);
|
|
127
79
|
return results[0] || null;
|
|
@@ -129,60 +81,95 @@ export class ObjectRepository {
|
|
|
129
81
|
}
|
|
130
82
|
|
|
131
83
|
async count(filters: any): Promise<number> {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
84
|
+
const hookCtx: RetrievalHookContext = {
|
|
85
|
+
...this.context,
|
|
86
|
+
objectName: this.objectName,
|
|
87
|
+
operation: 'count',
|
|
88
|
+
api: this.getHookAPI(),
|
|
89
|
+
state: {},
|
|
90
|
+
query: filters
|
|
91
|
+
};
|
|
92
|
+
await this.app.triggerHook('beforeCount', this.objectName, hookCtx);
|
|
137
93
|
|
|
138
|
-
|
|
139
|
-
const obj = this.getSchema();
|
|
140
|
-
if (this.context.userId) doc.created_by = this.context.userId;
|
|
141
|
-
if (this.context.spaceId) doc.space_id = this.context.spaceId;
|
|
94
|
+
const result = await this.getDriver().count(this.objectName, hookCtx.query, this.getOptions());
|
|
142
95
|
|
|
143
|
-
|
|
96
|
+
hookCtx.result = result;
|
|
97
|
+
await this.app.triggerHook('afterCount', this.objectName, hookCtx);
|
|
98
|
+
return hookCtx.result as number;
|
|
99
|
+
}
|
|
144
100
|
|
|
145
|
-
|
|
101
|
+
async create(doc: any): Promise<any> {
|
|
102
|
+
const hookCtx: MutationHookContext = {
|
|
103
|
+
...this.context,
|
|
104
|
+
objectName: this.objectName,
|
|
105
|
+
operation: 'create',
|
|
106
|
+
state: {},
|
|
107
|
+
api: this.getHookAPI(),
|
|
108
|
+
data: doc
|
|
109
|
+
};
|
|
110
|
+
await this.app.triggerHook('beforeCreate', this.objectName, hookCtx);
|
|
111
|
+
const finalDoc = hookCtx.data || doc;
|
|
146
112
|
|
|
147
|
-
|
|
148
|
-
|
|
113
|
+
const obj = this.getSchema();
|
|
114
|
+
if (this.context.userId) finalDoc.created_by = this.context.userId;
|
|
115
|
+
if (this.context.spaceId) finalDoc.space_id = this.context.spaceId;
|
|
116
|
+
|
|
117
|
+
const result = await this.getDriver().create(this.objectName, finalDoc, this.getOptions());
|
|
118
|
+
|
|
119
|
+
hookCtx.result = result;
|
|
120
|
+
await this.app.triggerHook('afterCreate', this.objectName, hookCtx);
|
|
121
|
+
return hookCtx.result;
|
|
149
122
|
}
|
|
150
123
|
|
|
151
124
|
async update(id: string | number, doc: any, options?: any): Promise<any> {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
125
|
+
const hookCtx: UpdateHookContext = {
|
|
126
|
+
...this.context,
|
|
127
|
+
objectName: this.objectName,
|
|
128
|
+
operation: 'update',
|
|
129
|
+
state: {},
|
|
130
|
+
api: this.getHookAPI(),
|
|
131
|
+
id,
|
|
132
|
+
data: doc,
|
|
133
|
+
isModified: (field) => hookCtx.data ? Object.prototype.hasOwnProperty.call(hookCtx.data, field) : false
|
|
134
|
+
};
|
|
135
|
+
await this.app.triggerHook('beforeUpdate', this.objectName, hookCtx);
|
|
159
136
|
|
|
160
|
-
const result = await this.getDriver().update(this.objectName, id,
|
|
137
|
+
const result = await this.getDriver().update(this.objectName, id, hookCtx.data, this.getOptions(options));
|
|
161
138
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
await this.executeHook('afterUpdate', 'update', docWithId);
|
|
166
|
-
return result;
|
|
139
|
+
hookCtx.result = result;
|
|
140
|
+
await this.app.triggerHook('afterUpdate', this.objectName, hookCtx);
|
|
141
|
+
return hookCtx.result;
|
|
167
142
|
}
|
|
168
143
|
|
|
169
144
|
async delete(id: string | number): Promise<any> {
|
|
170
|
-
const
|
|
171
|
-
|
|
145
|
+
const hookCtx: MutationHookContext = {
|
|
146
|
+
...this.context,
|
|
147
|
+
objectName: this.objectName,
|
|
148
|
+
operation: 'delete',
|
|
149
|
+
state: {},
|
|
150
|
+
api: this.getHookAPI(),
|
|
151
|
+
id
|
|
152
|
+
};
|
|
153
|
+
await this.app.triggerHook('beforeDelete', this.objectName, hookCtx);
|
|
172
154
|
|
|
173
155
|
const result = await this.getDriver().delete(this.objectName, id, this.getOptions());
|
|
174
156
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
157
|
+
hookCtx.result = result;
|
|
158
|
+
await this.app.triggerHook('afterDelete', this.objectName, hookCtx);
|
|
159
|
+
return hookCtx.result;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async aggregate(query: any): Promise<any> {
|
|
178
163
|
const driver = this.getDriver();
|
|
179
164
|
if (!driver.aggregate) throw new Error("Driver does not support aggregate");
|
|
165
|
+
|
|
180
166
|
return driver.aggregate(this.objectName, query, this.getOptions());
|
|
181
167
|
}
|
|
182
168
|
|
|
183
169
|
async distinct(field: string, filters?: any): Promise<any[]> {
|
|
184
170
|
const driver = this.getDriver();
|
|
185
171
|
if (!driver.distinct) throw new Error("Driver does not support distinct");
|
|
172
|
+
|
|
186
173
|
return driver.distinct(this.objectName, field, filters, this.getOptions());
|
|
187
174
|
}
|
|
188
175
|
|
|
@@ -193,8 +180,8 @@ export class ObjectRepository {
|
|
|
193
180
|
}
|
|
194
181
|
|
|
195
182
|
async createMany(data: any[]): Promise<any> {
|
|
196
|
-
// TODO: Triggers per record?
|
|
197
183
|
const driver = this.getDriver();
|
|
184
|
+
|
|
198
185
|
if (!driver.createMany) {
|
|
199
186
|
// Fallback
|
|
200
187
|
const results = [];
|
|
@@ -218,15 +205,24 @@ export class ObjectRepository {
|
|
|
218
205
|
return driver.deleteMany(this.objectName, filters, this.getOptions());
|
|
219
206
|
}
|
|
220
207
|
|
|
221
|
-
async
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
208
|
+
async execute(actionName: string, id: string | number | undefined, params: any): Promise<any> {
|
|
209
|
+
const api: HookAPI = {
|
|
210
|
+
find: (obj, q) => this.context.object(obj).find(q),
|
|
211
|
+
findOne: (obj, id) => this.context.object(obj).findOne(id),
|
|
212
|
+
count: (obj, q) => this.context.object(obj).count(q),
|
|
213
|
+
create: (obj, data) => this.context.object(obj).create(data),
|
|
214
|
+
update: (obj, id, data) => this.context.object(obj).update(id, data),
|
|
215
|
+
delete: (obj, id) => this.context.object(obj).delete(id)
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const ctx: ActionContext = {
|
|
219
|
+
...this.context,
|
|
220
|
+
objectName: this.objectName,
|
|
221
|
+
actionName,
|
|
222
|
+
id,
|
|
223
|
+
input: params,
|
|
224
|
+
api
|
|
225
|
+
};
|
|
226
|
+
return await this.app.executeAction(this.objectName, actionName, ctx);
|
|
231
227
|
}
|
|
232
228
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ObjectQL } from '../src';
|
|
2
|
+
import { MockDriver } from './utils';
|
|
3
|
+
|
|
4
|
+
describe('ObjectQL Actions', () => {
|
|
5
|
+
let app: ObjectQL;
|
|
6
|
+
let driver: MockDriver;
|
|
7
|
+
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
driver = new MockDriver();
|
|
10
|
+
app = new ObjectQL({
|
|
11
|
+
datasources: {
|
|
12
|
+
default: driver
|
|
13
|
+
},
|
|
14
|
+
objects: {
|
|
15
|
+
'invoice': {
|
|
16
|
+
name: 'invoice',
|
|
17
|
+
fields: {
|
|
18
|
+
amount: { type: 'number' },
|
|
19
|
+
status: { type: 'text' }
|
|
20
|
+
},
|
|
21
|
+
actions: {
|
|
22
|
+
'pay': {
|
|
23
|
+
label: 'Pay Invoice',
|
|
24
|
+
params: {
|
|
25
|
+
method: { type: 'text' }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
await app.init();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should register and execute an action', async () => {
|
|
36
|
+
const repo = app.createContext({}).object('invoice');
|
|
37
|
+
|
|
38
|
+
let actionCalled = false;
|
|
39
|
+
app.registerAction('invoice', 'pay', async (ctx) => {
|
|
40
|
+
actionCalled = true;
|
|
41
|
+
expect(ctx.objectName).toBe('invoice');
|
|
42
|
+
expect(ctx.actionName).toBe('pay');
|
|
43
|
+
expect(ctx.id).toBe('inv-123');
|
|
44
|
+
expect(ctx.params.method).toBe('credit_card');
|
|
45
|
+
return { success: true, paid: true };
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const result = await repo.execute('pay', 'inv-123', { method: 'credit_card' });
|
|
49
|
+
|
|
50
|
+
expect(actionCalled).toBe(true);
|
|
51
|
+
expect(result.success).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should throw error if action not registered', async () => {
|
|
55
|
+
const repo = app.createContext({}).object('invoice');
|
|
56
|
+
await expect(repo.execute('refund', '1', {})).rejects.toThrow("Action 'refund' not found for object 'invoice'");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ObjectQL } from '../src';
|
|
2
|
+
import { MockDriver } from './utils';
|
|
3
|
+
|
|
4
|
+
describe('ObjectQL Hooks', () => {
|
|
5
|
+
let app: ObjectQL;
|
|
6
|
+
let driver: MockDriver;
|
|
7
|
+
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
driver = new MockDriver();
|
|
10
|
+
app = new ObjectQL({
|
|
11
|
+
datasources: {
|
|
12
|
+
default: driver
|
|
13
|
+
},
|
|
14
|
+
objects: {
|
|
15
|
+
'post': {
|
|
16
|
+
name: 'post',
|
|
17
|
+
fields: {
|
|
18
|
+
title: { type: 'text' },
|
|
19
|
+
status: { type: 'text' }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
await app.init();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should trigger beforeFind and modify query', async () => {
|
|
28
|
+
const repo = app.createContext({}).object('post');
|
|
29
|
+
|
|
30
|
+
let hookTriggered = false;
|
|
31
|
+
app.on('beforeFind', 'post', async (ctx) => {
|
|
32
|
+
hookTriggered = true;
|
|
33
|
+
ctx.query = { ...ctx.query, filters: [['status', '=', 'published']] };
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Mock driver find to check query
|
|
37
|
+
const spyFind = jest.spyOn(driver, 'find');
|
|
38
|
+
|
|
39
|
+
await repo.find({});
|
|
40
|
+
|
|
41
|
+
expect(hookTriggered).toBe(true);
|
|
42
|
+
expect(spyFind).toHaveBeenCalledWith('post', { filters: [['status', '=', 'published']] }, expect.any(Object));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should trigger afterCreate and return result', async () => {
|
|
46
|
+
const repo = app.createContext({ userId: 'u1' }).object('post');
|
|
47
|
+
|
|
48
|
+
app.on('afterCreate', 'post', async (ctx) => {
|
|
49
|
+
if (ctx.result) {
|
|
50
|
+
ctx.result.augmented = true;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const created = await repo.create({ title: 'New Post' });
|
|
55
|
+
|
|
56
|
+
expect(created.id).toBeDefined();
|
|
57
|
+
expect(created.created_by).toBe('u1');
|
|
58
|
+
expect(created.augmented).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
});
|
package/test/utils.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Driver } from '@objectql/types';
|
|
2
|
+
|
|
3
|
+
export class MockDriver implements Driver {
|
|
4
|
+
private data: Record<string, any[]> = {};
|
|
5
|
+
|
|
6
|
+
constructor() {}
|
|
7
|
+
|
|
8
|
+
private getData(objectName: string) {
|
|
9
|
+
if (!this.data[objectName]) {
|
|
10
|
+
this.data[objectName] = [];
|
|
11
|
+
}
|
|
12
|
+
return this.data[objectName];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async find(objectName: string, query: any, options?: any): Promise<any[]> {
|
|
16
|
+
return this.getData(objectName);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async findOne(objectName: string, id: string | number, query?: any, options?: any): Promise<any> {
|
|
20
|
+
return this.getData(objectName).find(item => item.id == id);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async create(objectName: string, data: any, options?: any): Promise<any> {
|
|
24
|
+
const list = this.getData(objectName);
|
|
25
|
+
if (!data.id) data.id = list.length + 1;
|
|
26
|
+
list.push(data);
|
|
27
|
+
return data;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async update(objectName: string, id: string | number, data: any, options?: any): Promise<any> {
|
|
31
|
+
const list = this.getData(objectName);
|
|
32
|
+
const idx = list.findIndex(item => item.id == id);
|
|
33
|
+
if (idx >= 0) {
|
|
34
|
+
list[idx] = { ...list[idx], ...data };
|
|
35
|
+
return list[idx];
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async delete(objectName: string, id: string | number, options?: any): Promise<any> {
|
|
41
|
+
const list = this.getData(objectName);
|
|
42
|
+
const idx = list.findIndex(item => item.id == id);
|
|
43
|
+
if (idx >= 0) {
|
|
44
|
+
const deleted = list[idx];
|
|
45
|
+
list.splice(idx, 1);
|
|
46
|
+
return deleted;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async count(objectName: string, filters: any, options?: any): Promise<number> {
|
|
52
|
+
return this.getData(objectName).length;
|
|
53
|
+
}
|
|
54
|
+
}
|
package/tsconfig.json
CHANGED