remult 0.14.7 → 0.14.8
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/LICENSE +21 -0
- package/esm/assign.d.ts +1 -0
- package/esm/assign.js +5 -0
- package/esm/classType.d.ts +3 -0
- package/esm/classType.js +1 -0
- package/esm/graphql.d.ts +5 -0
- package/esm/graphql.js +102 -0
- package/esm/index.d.ts +36 -0
- package/esm/index.js +30 -0
- package/esm/inputTypes.d.ts +9 -0
- package/esm/inputTypes.js +9 -0
- package/esm/postgres/index.d.ts +2 -0
- package/esm/postgres/index.js +2 -0
- package/esm/postgres/postgres-data-provider.d.ts +28 -0
- package/esm/postgres/postgres-data-provider.js +97 -0
- package/esm/postgres/schema-builder.d.ts +15 -0
- package/esm/postgres/schema-builder.js +128 -0
- package/esm/remult-express.d.ts +5 -0
- package/esm/remult-express.js +16 -0
- package/esm/remult-knex/index.d.ts +51 -0
- package/esm/remult-knex/index.js +381 -0
- package/esm/remult-middleware.d.ts +2 -0
- package/esm/remult-middleware.js +63 -0
- package/esm/remult-mongo.d.ts +9 -0
- package/esm/remult-mongo.js +220 -0
- package/esm/server/JsonEntityFileStorage.d.ts +7 -0
- package/esm/server/JsonEntityFileStorage.js +20 -0
- package/esm/server/expressBridge.d.ts +140 -0
- package/esm/server/expressBridge.js +668 -0
- package/esm/server/index.d.ts +4 -0
- package/esm/server/index.js +4 -0
- package/esm/server/register-actions-on-server.d.ts +3 -0
- package/esm/server/register-actions-on-server.js +24 -0
- package/esm/server/register-entities-on-server.d.ts +3 -0
- package/esm/server/register-entities-on-server.js +15 -0
- package/esm/src/__EntityValueProvider.d.ts +10 -0
- package/esm/src/__EntityValueProvider.js +1 -0
- package/esm/src/__RowsOfDataForTesting.d.ts +3 -0
- package/esm/src/__RowsOfDataForTesting.js +1 -0
- package/esm/src/backend-tests/backend-database-test-setup.backend-spec.d.ts +5 -0
- package/esm/src/backend-tests/backend-database-test-setup.backend-spec.js +113 -0
- package/esm/src/backend-tests/my-test.backend-spec.d.ts +1 -0
- package/esm/src/backend-tests/my-test.backend-spec.js +49 -0
- package/esm/src/column-interfaces.d.ts +95 -0
- package/esm/src/column-interfaces.js +7 -0
- package/esm/src/column.d.ts +49 -0
- package/esm/src/column.js +133 -0
- package/esm/src/context.d.ts +124 -0
- package/esm/src/context.js +285 -0
- package/esm/src/data-api.d.ts +33 -0
- package/esm/src/data-api.js +204 -0
- package/esm/src/data-interfaces.d.ts +37 -0
- package/esm/src/data-interfaces.js +12 -0
- package/esm/src/data-providers/array-entity-data-provider.d.ts +22 -0
- package/esm/src/data-providers/array-entity-data-provider.js +264 -0
- package/esm/src/data-providers/in-memory-database.d.ts +9 -0
- package/esm/src/data-providers/in-memory-database.js +16 -0
- package/esm/src/data-providers/json-data-provider.d.ts +12 -0
- package/esm/src/data-providers/json-data-provider.js +52 -0
- package/esm/src/data-providers/rest-data-provider.d.ts +35 -0
- package/esm/src/data-providers/rest-data-provider.js +188 -0
- package/esm/src/data-providers/sql-database.d.ts +16 -0
- package/esm/src/data-providers/sql-database.js +261 -0
- package/esm/src/data-providers/web-sql-data-provider.d.ts +18 -0
- package/esm/src/data-providers/web-sql-data-provider.js +121 -0
- package/esm/src/entity.d.ts +82 -0
- package/esm/src/entity.js +1 -0
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.d.ts +50 -0
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +139 -0
- package/esm/src/filter/filter-consumer-bridge-to-url-builder.d.ts +1 -0
- package/esm/src/filter/filter-consumer-bridge-to-url-builder.js +1 -0
- package/esm/src/filter/filter-interfaces.d.ts +85 -0
- package/esm/src/filter/filter-interfaces.js +467 -0
- package/esm/src/id-entity.d.ts +4 -0
- package/esm/src/id-entity.js +8 -0
- package/esm/src/remult3/RepositoryImplementation.d.ts +326 -0
- package/esm/src/remult3/RepositoryImplementation.js +1806 -0
- package/esm/src/remult3/index.d.ts +2 -0
- package/esm/src/remult3/index.js +2 -0
- package/esm/src/remult3/remult3.d.ts +312 -0
- package/esm/src/remult3/remult3.js +1 -0
- package/esm/src/server-action.d.ts +70 -0
- package/esm/src/server-action.js +365 -0
- package/esm/src/shared-tests/db-tests-setup.d.ts +23 -0
- package/esm/src/shared-tests/db-tests-setup.js +66 -0
- package/esm/src/shared-tests/db-tests.db-tests.d.ts +26 -0
- package/esm/src/shared-tests/db-tests.db-tests.js +723 -0
- package/esm/src/shared-tests/deleteAll.d.ts +2 -0
- package/esm/src/shared-tests/deleteAll.js +4 -0
- package/esm/src/shared-tests/entityWithValidations.d.ts +14 -0
- package/esm/src/shared-tests/entityWithValidations.js +53 -0
- package/esm/src/shared-tests/index.d.ts +4 -0
- package/esm/src/shared-tests/index.js +4 -0
- package/esm/src/shared-tests/test-custom-id-column.spec.d.ts +1 -0
- package/esm/src/shared-tests/test-custom-id-column.spec.js +62 -0
- package/esm/src/shared-tests/test-special-value.spec.d.ts +10 -0
- package/esm/src/shared-tests/test-special-value.spec.js +88 -0
- package/esm/src/shared-tests/test-update-with-null.spec.d.ts +1 -0
- package/esm/src/shared-tests/test-update-with-null.spec.js +47 -0
- package/esm/src/sort.d.ts +15 -0
- package/esm/src/sort.js +82 -0
- package/esm/src/sql-command.d.ts +15 -0
- package/esm/src/sql-command.js +1 -0
- package/esm/src/tests/Done.d.ts +5 -0
- package/esm/src/tests/Done.js +11 -0
- package/esm/src/tests/TestDataApiResponse.d.ts +10 -0
- package/esm/src/tests/TestDataApiResponse.js +22 -0
- package/esm/src/tests/c.d.ts +8 -0
- package/esm/src/tests/c.js +22 -0
- package/esm/src/tests/createData.d.ts +7 -0
- package/esm/src/tests/createData.js +22 -0
- package/esm/src/tests/d.d.ts +6 -0
- package/esm/src/tests/d.js +19 -0
- package/esm/src/tests/dWithPrefilter.d.ts +7 -0
- package/esm/src/tests/dWithPrefilter.js +32 -0
- package/esm/src/tests/entityForCustomFilter.d.ts +16 -0
- package/esm/src/tests/entityForCustomFilter.js +44 -0
- package/esm/src/tests/entityWithValidationsOnColumn.d.ts +5 -0
- package/esm/src/tests/entityWithValidationsOnColumn.js +21 -0
- package/esm/src/tests/frontend-database-tests-setup.spec.d.ts +4 -0
- package/esm/src/tests/frontend-database-tests-setup.spec.js +38 -0
- package/esm/src/tests/h.d.ts +6 -0
- package/esm/src/tests/h.js +35 -0
- package/esm/src/tests/remult-3-entities.d.ts +23 -0
- package/esm/src/tests/remult-3-entities.js +72 -0
- package/esm/src/tests/tasks.d.ts +6 -0
- package/esm/src/tests/tasks.js +24 -0
- package/esm/src/tests/testHelper.spec.d.ts +34 -0
- package/esm/src/tests/testHelper.spec.js +217 -0
- package/esm/src/tests/testModel/models.d.ts +16 -0
- package/esm/src/tests/testModel/models.js +31 -0
- package/esm/src/validators.d.ts +14 -0
- package/esm/src/validators.js +33 -0
- package/esm/urlBuilder.d.ts +6 -0
- package/esm/urlBuilder.js +19 -0
- package/esm/valueConverters.d.ts +12 -0
- package/esm/valueConverters.js +169 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +22 -1
- package/postgres/index.d.ts +1 -0
- package/postgres/index.js +1 -0
- package/postgres/postgres-data-provider.d.ts +1 -13
- package/postgres/postgres-data-provider.js +28 -262
- package/postgres/schema-builder.d.ts +15 -0
- package/postgres/schema-builder.js +270 -0
- package/readme.md +68 -30
- package/src/data-providers/web-sql-data-provider.js +4 -3
- package/src/entity.d.ts +9 -2
- package/src/remult3/RepositoryImplementation.js +11 -3
- package/src/shared-tests/test-update-with-null.spec.js +38 -0
- package/valueConverters.js +4 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { Action, actionInfo } from './server-action';
|
|
2
|
+
import { RestDataProvider, RestDataProviderHttpProviderUsingFetch } from './data-providers/rest-data-provider';
|
|
3
|
+
import { RepositoryImplementation } from "./remult3/RepositoryImplementation";
|
|
4
|
+
export class HttpProviderBridgeToRestDataProviderHttpProvider {
|
|
5
|
+
constructor(http) {
|
|
6
|
+
this.http = http;
|
|
7
|
+
}
|
|
8
|
+
async post(url, data) {
|
|
9
|
+
return await retry(() => toPromise(this.http.post(url, data)));
|
|
10
|
+
}
|
|
11
|
+
delete(url) {
|
|
12
|
+
return toPromise(this.http.delete(url));
|
|
13
|
+
}
|
|
14
|
+
put(url, data) {
|
|
15
|
+
return toPromise(this.http.put(url, data));
|
|
16
|
+
}
|
|
17
|
+
async get(url) {
|
|
18
|
+
return await retry(() => toPromise(this.http.get(url)));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export async function retry(what) {
|
|
22
|
+
while (true) {
|
|
23
|
+
try {
|
|
24
|
+
return await what();
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
if (err.message?.startsWith("Error occurred while trying to proxy") ||
|
|
28
|
+
err.message?.startsWith("Error occured while trying to proxy") ||
|
|
29
|
+
err.message?.includes("http proxy error") ||
|
|
30
|
+
err.message?.startsWith("Gateway Timeout")) {
|
|
31
|
+
await new Promise((res, req) => {
|
|
32
|
+
setTimeout(() => {
|
|
33
|
+
res({});
|
|
34
|
+
}, 250);
|
|
35
|
+
});
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function toPromise(p) {
|
|
43
|
+
let r;
|
|
44
|
+
if (p["toPromise"] !== undefined) {
|
|
45
|
+
r = p["toPromise"]();
|
|
46
|
+
}
|
|
47
|
+
//@ts-ignore
|
|
48
|
+
else
|
|
49
|
+
r = p;
|
|
50
|
+
return r.then((x) => {
|
|
51
|
+
if (x && (x.status == 200 || x.status == 201) && x.headers && x.request && x.data) //for axios
|
|
52
|
+
return x.data;
|
|
53
|
+
return x;
|
|
54
|
+
}).catch(async (ex) => {
|
|
55
|
+
throw await processHttpException(ex);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
export async function processHttpException(ex) {
|
|
59
|
+
let z = await ex;
|
|
60
|
+
var error;
|
|
61
|
+
if (z.error)
|
|
62
|
+
error = z.error;
|
|
63
|
+
else if (z.isAxiosError) {
|
|
64
|
+
if (typeof z.response?.data === "string")
|
|
65
|
+
error = z.response.data;
|
|
66
|
+
else
|
|
67
|
+
error = z?.response?.data;
|
|
68
|
+
}
|
|
69
|
+
if (!error)
|
|
70
|
+
error = z.message;
|
|
71
|
+
if (z.status == 0 && z.error.isTrusted)
|
|
72
|
+
error = "Network Error";
|
|
73
|
+
if (typeof error === 'string') {
|
|
74
|
+
error = {
|
|
75
|
+
message: error,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
let httpStatusCode = z.status;
|
|
79
|
+
if (httpStatusCode === undefined)
|
|
80
|
+
httpStatusCode = z.response?.status;
|
|
81
|
+
if (httpStatusCode !== undefined && httpStatusCode !== null) {
|
|
82
|
+
error.httpStatusCode = httpStatusCode;
|
|
83
|
+
}
|
|
84
|
+
var result = Object.assign(error, {
|
|
85
|
+
// exception: ex disabled for now because JSON.stringify crashed with this
|
|
86
|
+
});
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
export function isBackend() {
|
|
90
|
+
return actionInfo.runningOnServer;
|
|
91
|
+
}
|
|
92
|
+
export class Remult {
|
|
93
|
+
/** Creates a new instance of the `remult` object.
|
|
94
|
+
*
|
|
95
|
+
* Can receive either an HttpProvider or a DataProvider as a parameter - which will be used to fetch data from.
|
|
96
|
+
*
|
|
97
|
+
* If no provider is specified, `fetch` will be used as an http provider
|
|
98
|
+
*/
|
|
99
|
+
constructor(provider) {
|
|
100
|
+
this._userChangeEvent = new EventSource();
|
|
101
|
+
this.repCache = new Map();
|
|
102
|
+
if (provider && provider.getEntityDataProvider) {
|
|
103
|
+
this._dataSource = provider;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
let http = provider;
|
|
107
|
+
let dataProvider;
|
|
108
|
+
if (http) {
|
|
109
|
+
dataProvider = new HttpProviderBridgeToRestDataProviderHttpProvider(http);
|
|
110
|
+
}
|
|
111
|
+
if (!dataProvider) {
|
|
112
|
+
dataProvider = new RestDataProviderHttpProviderUsingFetch();
|
|
113
|
+
}
|
|
114
|
+
this._dataSource = new RestDataProvider(Remult.apiBaseUrl, dataProvider);
|
|
115
|
+
if (!Action.provider)
|
|
116
|
+
Action.provider = dataProvider;
|
|
117
|
+
}
|
|
118
|
+
/**Return's a `Repository` of the specific entity type
|
|
119
|
+
* @example
|
|
120
|
+
* const taskRepo = remult.repo(Task);
|
|
121
|
+
* @see [Repository](https://remult.dev/docs/ref_repository.html)
|
|
122
|
+
*
|
|
123
|
+
*/
|
|
124
|
+
repo(entity, dataProvider) {
|
|
125
|
+
if (dataProvider === undefined)
|
|
126
|
+
dataProvider = this._dataSource;
|
|
127
|
+
let dpCache = this.repCache.get(dataProvider);
|
|
128
|
+
if (!dpCache)
|
|
129
|
+
this.repCache.set(dataProvider, dpCache = new Map());
|
|
130
|
+
let r = dpCache.get(entity);
|
|
131
|
+
if (!r) {
|
|
132
|
+
dpCache.set(entity, r = new RepositoryImplementation(entity, this, dataProvider));
|
|
133
|
+
}
|
|
134
|
+
return r;
|
|
135
|
+
}
|
|
136
|
+
/** Returns the current user's info */
|
|
137
|
+
get user() {
|
|
138
|
+
if (this._user === undefined) {
|
|
139
|
+
return {
|
|
140
|
+
id: undefined,
|
|
141
|
+
name: '',
|
|
142
|
+
roles: []
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return this._user;
|
|
146
|
+
}
|
|
147
|
+
/** Set's the current user info */
|
|
148
|
+
async setUser(info) {
|
|
149
|
+
this._user = info;
|
|
150
|
+
let auth = info;
|
|
151
|
+
if (auth) {
|
|
152
|
+
if (!this._user.id && auth.sub)
|
|
153
|
+
this._user.id = auth.sub;
|
|
154
|
+
if (!this._user.roles && auth.permissions) {
|
|
155
|
+
this._user.roles = auth.permissions;
|
|
156
|
+
}
|
|
157
|
+
if (!this._user.name)
|
|
158
|
+
this._user.name = auth.username;
|
|
159
|
+
}
|
|
160
|
+
if (this._user && !this._user.roles)
|
|
161
|
+
this._user.roles = [];
|
|
162
|
+
await this._userChangeEvent.fire();
|
|
163
|
+
}
|
|
164
|
+
/** Checks if a user was authenticated */
|
|
165
|
+
authenticated() {
|
|
166
|
+
return this.user.id !== undefined;
|
|
167
|
+
}
|
|
168
|
+
/** checks if the user has any of the roles specified in the parameters
|
|
169
|
+
* @example
|
|
170
|
+
* remult.isAllowed("admin")
|
|
171
|
+
* @see
|
|
172
|
+
* [Allowed](https://remult.dev/docs/allowed.html)
|
|
173
|
+
*/
|
|
174
|
+
isAllowed(roles) {
|
|
175
|
+
if (roles == undefined)
|
|
176
|
+
return undefined;
|
|
177
|
+
if (roles instanceof Array) {
|
|
178
|
+
for (const role of roles) {
|
|
179
|
+
if (this.isAllowed(role) === true) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
if (typeof roles === 'function') {
|
|
186
|
+
return roles(this);
|
|
187
|
+
}
|
|
188
|
+
if (typeof roles === 'boolean')
|
|
189
|
+
return roles;
|
|
190
|
+
if (typeof roles === 'string')
|
|
191
|
+
if (this.user.roles.indexOf(roles.toString()) >= 0)
|
|
192
|
+
return true;
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
/** checks if the user matches the allowedForInstance callback
|
|
196
|
+
* @see
|
|
197
|
+
* [Allowed](https://remult.dev/docs/allowed.html)
|
|
198
|
+
*/
|
|
199
|
+
isAllowedForInstance(instance, allowed) {
|
|
200
|
+
if (Array.isArray(allowed)) {
|
|
201
|
+
{
|
|
202
|
+
for (const item of allowed) {
|
|
203
|
+
if (this.isAllowedForInstance(instance, item))
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else if (typeof (allowed) === "function") {
|
|
209
|
+
return allowed(this, instance);
|
|
210
|
+
}
|
|
211
|
+
else
|
|
212
|
+
return this.isAllowed(allowed);
|
|
213
|
+
}
|
|
214
|
+
/** returns a dispatcher object that fires once a user has changed*/
|
|
215
|
+
get userChange() {
|
|
216
|
+
return this._userChangeEvent.dispatcher;
|
|
217
|
+
}
|
|
218
|
+
/** sets the current data provider */
|
|
219
|
+
setDataProvider(dataProvider) {
|
|
220
|
+
this._dataSource = dataProvider;
|
|
221
|
+
}
|
|
222
|
+
clearAllCache() {
|
|
223
|
+
this.repCache.clear();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/** The api Base Url to be used in all remult calls. by default it's set to `/api`.
|
|
227
|
+
*
|
|
228
|
+
* Set this property in case you want to determine a non relative api url
|
|
229
|
+
*/
|
|
230
|
+
Remult.apiBaseUrl = '/api';
|
|
231
|
+
/** A helper callback that can be used to debug and trace all find operations. Useful in debugging scenarios */
|
|
232
|
+
Remult.onFind = (metadata, options) => { };
|
|
233
|
+
export const allEntities = [];
|
|
234
|
+
export const classHelpers = new Map();
|
|
235
|
+
export class ClassHelper {
|
|
236
|
+
constructor() {
|
|
237
|
+
this.methods = [];
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export class MethodHelper {
|
|
241
|
+
constructor() {
|
|
242
|
+
this.classes = new Map();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
export function setControllerSettings(target, options) {
|
|
246
|
+
let r = target;
|
|
247
|
+
while (true) {
|
|
248
|
+
let helper = classHelpers.get(r);
|
|
249
|
+
if (helper) {
|
|
250
|
+
for (const m of helper.methods) {
|
|
251
|
+
m.classes.set(target, options);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
let p = Object.getPrototypeOf(r.prototype);
|
|
255
|
+
if (p == null)
|
|
256
|
+
break;
|
|
257
|
+
r = p.constructor;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
export class Allow {
|
|
261
|
+
}
|
|
262
|
+
Allow.everyone = () => true;
|
|
263
|
+
Allow.authenticated = (remult) => remult.authenticated();
|
|
264
|
+
export const queryConfig = {
|
|
265
|
+
defaultPageSize: 200
|
|
266
|
+
};
|
|
267
|
+
export class EventSource {
|
|
268
|
+
constructor() {
|
|
269
|
+
this.listeners = [];
|
|
270
|
+
this.dispatcher = {
|
|
271
|
+
observe: async (what) => {
|
|
272
|
+
this.listeners.push(what);
|
|
273
|
+
await what();
|
|
274
|
+
return () => {
|
|
275
|
+
this.listeners = this.listeners.filter(x => x != what);
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
async fire() {
|
|
281
|
+
for (const l of this.listeners) {
|
|
282
|
+
await l();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Remult } from './context';
|
|
2
|
+
import { Repository } from './remult3';
|
|
3
|
+
import { ErrorInfo } from './data-interfaces';
|
|
4
|
+
export declare class DataApi<T = any> {
|
|
5
|
+
private repository;
|
|
6
|
+
private remult;
|
|
7
|
+
constructor(repository: Repository<T>, remult: Remult);
|
|
8
|
+
httpGet(res: DataApiResponse, req: DataApiRequest): Promise<void>;
|
|
9
|
+
httpPost(res: DataApiResponse, req: DataApiRequest, body: any): Promise<void>;
|
|
10
|
+
static defaultGetLimit: number;
|
|
11
|
+
get(response: DataApiResponse, id: any): Promise<void>;
|
|
12
|
+
count(response: DataApiResponse, request: DataApiRequest, filterBody?: any): Promise<void>;
|
|
13
|
+
getArray(response: DataApiResponse, request: DataApiRequest, filterBody?: any): Promise<void>;
|
|
14
|
+
private buildWhere;
|
|
15
|
+
private doOnId;
|
|
16
|
+
put(response: DataApiResponse, id: any, body: any): Promise<void>;
|
|
17
|
+
delete(response: DataApiResponse, id: any): Promise<void>;
|
|
18
|
+
post(response: DataApiResponse, body: any): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export interface DataApiResponse {
|
|
21
|
+
success(data: any): void;
|
|
22
|
+
deleted(): void;
|
|
23
|
+
created(data: any): void;
|
|
24
|
+
notFound(): void;
|
|
25
|
+
error(data: ErrorInfo): void;
|
|
26
|
+
forbidden(): void;
|
|
27
|
+
progress(progress: number): void;
|
|
28
|
+
}
|
|
29
|
+
export interface DataApiRequest {
|
|
30
|
+
get(key: string): any;
|
|
31
|
+
}
|
|
32
|
+
export declare function determineSort(sortUrlParm: string, dirUrlParam: string): any;
|
|
33
|
+
export declare function serializeError(data: ErrorInfo): ErrorInfo<any>;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { customUrlToken, buildFilterFromRequestParameters } from './filter/filter-interfaces';
|
|
2
|
+
import { Filter } from './filter/filter-interfaces';
|
|
3
|
+
export class DataApi {
|
|
4
|
+
constructor(repository, remult) {
|
|
5
|
+
this.repository = repository;
|
|
6
|
+
this.remult = remult;
|
|
7
|
+
}
|
|
8
|
+
httpGet(res, req) {
|
|
9
|
+
if (req.get("__action") == "count") {
|
|
10
|
+
return this.count(res, req);
|
|
11
|
+
}
|
|
12
|
+
else
|
|
13
|
+
return this.getArray(res, req);
|
|
14
|
+
}
|
|
15
|
+
httpPost(res, req, body) {
|
|
16
|
+
switch (req.get("__action")) {
|
|
17
|
+
case "get":
|
|
18
|
+
return this.getArray(res, req, body);
|
|
19
|
+
case "count":
|
|
20
|
+
return this.count(res, req, body);
|
|
21
|
+
default:
|
|
22
|
+
return this.post(res, body);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async get(response, id) {
|
|
26
|
+
if (!this.repository.metadata.apiReadAllowed) {
|
|
27
|
+
response.forbidden();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
await this.doOnId(response, id, async (row) => response.success(this.repository.getEntityRef(row).toApiJson()));
|
|
31
|
+
}
|
|
32
|
+
async count(response, request, filterBody) {
|
|
33
|
+
if (!this.repository.metadata.apiReadAllowed) {
|
|
34
|
+
response.forbidden();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
response.success({ count: +await this.repository.count(await this.buildWhere(request, filterBody)) });
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
response.error(err);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async getArray(response, request, filterBody) {
|
|
45
|
+
if (!this.repository.metadata.apiReadAllowed) {
|
|
46
|
+
response.forbidden();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
let findOptions = { load: () => [] };
|
|
51
|
+
findOptions.where = await this.buildWhere(request, filterBody);
|
|
52
|
+
if (this.remult.isAllowed(this.repository.metadata.options.apiRequireId)) {
|
|
53
|
+
let hasId = false;
|
|
54
|
+
let w = await Filter.fromEntityFilter(this.repository.metadata, findOptions.where);
|
|
55
|
+
if (w) {
|
|
56
|
+
w.__applyToConsumer({
|
|
57
|
+
containsCaseInsensitive: () => { },
|
|
58
|
+
isDifferentFrom: () => { },
|
|
59
|
+
isEqualTo: (col, val) => {
|
|
60
|
+
if (this.repository.metadata.idMetadata.isIdField(col))
|
|
61
|
+
hasId = true;
|
|
62
|
+
},
|
|
63
|
+
custom: () => { },
|
|
64
|
+
databaseCustom: () => { },
|
|
65
|
+
isGreaterOrEqualTo: () => { },
|
|
66
|
+
isGreaterThan: () => { },
|
|
67
|
+
isIn: () => { },
|
|
68
|
+
isLessOrEqualTo: () => { },
|
|
69
|
+
isLessThan: () => { },
|
|
70
|
+
isNotNull: () => { },
|
|
71
|
+
isNull: () => { },
|
|
72
|
+
or: () => { }
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (!hasId) {
|
|
76
|
+
response.forbidden();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (request) {
|
|
81
|
+
let sort = request.get("_sort");
|
|
82
|
+
if (sort != undefined) {
|
|
83
|
+
let dir = request.get('_order');
|
|
84
|
+
findOptions.orderBy = determineSort(sort, dir);
|
|
85
|
+
}
|
|
86
|
+
let limit = +request.get("_limit");
|
|
87
|
+
if (!limit && DataApi.defaultGetLimit)
|
|
88
|
+
limit = DataApi.defaultGetLimit;
|
|
89
|
+
findOptions.limit = limit;
|
|
90
|
+
findOptions.page = +request.get("_page");
|
|
91
|
+
}
|
|
92
|
+
await this.repository.find(findOptions)
|
|
93
|
+
.then(async (r) => {
|
|
94
|
+
response.success(await Promise.all(r.map(async (y) => this.repository.getEntityRef(y).toApiJson())));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
response.error(err);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async buildWhere(request, filterBody) {
|
|
102
|
+
var where = [];
|
|
103
|
+
if (this.repository.metadata.options.apiPrefilter) {
|
|
104
|
+
if (typeof this.repository.metadata.options.apiPrefilter === "function")
|
|
105
|
+
where.push(await this.repository.metadata.options.apiPrefilter());
|
|
106
|
+
else
|
|
107
|
+
where.push(this.repository.metadata.options.apiPrefilter);
|
|
108
|
+
}
|
|
109
|
+
if (request) {
|
|
110
|
+
where.push(buildFilterFromRequestParameters(this.repository.metadata, {
|
|
111
|
+
get: key => {
|
|
112
|
+
let result = request.get(key);
|
|
113
|
+
if (key.startsWith(customUrlToken) && result)
|
|
114
|
+
return JSON.parse(result);
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
if (filterBody)
|
|
120
|
+
where.push(Filter.entityFilterFromJson(this.repository.metadata, filterBody));
|
|
121
|
+
return { $and: where };
|
|
122
|
+
}
|
|
123
|
+
async doOnId(response, id, what) {
|
|
124
|
+
try {
|
|
125
|
+
await this.repository.find({
|
|
126
|
+
where: { $and: [this.repository.metadata.options.apiPrefilter, this.repository.metadata.idMetadata.getIdFilter(id)] }
|
|
127
|
+
})
|
|
128
|
+
.then(async (r) => {
|
|
129
|
+
if (r.length == 0)
|
|
130
|
+
response.notFound();
|
|
131
|
+
else if (r.length > 1)
|
|
132
|
+
response.error({ message: "id is not unique" });
|
|
133
|
+
else
|
|
134
|
+
await what(r[0]);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
response.error(err);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async put(response, id, body) {
|
|
142
|
+
await this.doOnId(response, id, async (row) => {
|
|
143
|
+
let ref = this.repository.getEntityRef(row);
|
|
144
|
+
await ref._updateEntityBasedOnApi(body);
|
|
145
|
+
if (!ref.apiUpdateAllowed) {
|
|
146
|
+
response.forbidden();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
await this.repository.getEntityRef(row).save();
|
|
150
|
+
response.success(this.repository.getEntityRef(row).toApiJson());
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
async delete(response, id) {
|
|
154
|
+
await this.doOnId(response, id, async (row) => {
|
|
155
|
+
if (!this.repository.getEntityRef(row).apiDeleteAllowed) {
|
|
156
|
+
response.forbidden();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
await this.repository.getEntityRef(row).delete();
|
|
160
|
+
response.deleted();
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
async post(response, body) {
|
|
164
|
+
try {
|
|
165
|
+
let newr = this.repository.create();
|
|
166
|
+
await this.repository.getEntityRef(newr)._updateEntityBasedOnApi(body);
|
|
167
|
+
if (!this.repository.getEntityRef(newr).apiInsertAllowed) {
|
|
168
|
+
response.forbidden();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
await this.repository.getEntityRef(newr).save();
|
|
172
|
+
response.created(this.repository.getEntityRef(newr).toApiJson());
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
response.error(err);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
DataApi.defaultGetLimit = 0;
|
|
180
|
+
export function determineSort(sortUrlParm, dirUrlParam) {
|
|
181
|
+
let dirItems = [];
|
|
182
|
+
if (dirUrlParam)
|
|
183
|
+
dirItems = dirUrlParam.split(',');
|
|
184
|
+
let result = {};
|
|
185
|
+
sortUrlParm.split(',').map((name, i) => {
|
|
186
|
+
let key = name.trim();
|
|
187
|
+
if (i < dirItems.length && dirItems[i].toLowerCase().trim().startsWith("d"))
|
|
188
|
+
return result[key] = "desc";
|
|
189
|
+
else
|
|
190
|
+
return result[key] = "asc";
|
|
191
|
+
});
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
export function serializeError(data) {
|
|
195
|
+
if (data instanceof TypeError) {
|
|
196
|
+
data = { message: data.message, stack: data.stack };
|
|
197
|
+
}
|
|
198
|
+
let x = JSON.parse(JSON.stringify(data));
|
|
199
|
+
if (!x.message && !x.modelState)
|
|
200
|
+
data = { message: data.message, stack: data.stack };
|
|
201
|
+
if (typeof x === 'string')
|
|
202
|
+
data = { message: x };
|
|
203
|
+
return data;
|
|
204
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Sort } from './sort';
|
|
2
|
+
import { Filter } from './filter/filter-interfaces';
|
|
3
|
+
import { EntityMetadata, OmitEB } from './remult3';
|
|
4
|
+
export interface DataProvider {
|
|
5
|
+
getEntityDataProvider(entity: EntityMetadata): EntityDataProvider;
|
|
6
|
+
transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
|
|
7
|
+
supportsCustomFilter?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface EntityDataProvider {
|
|
10
|
+
count(where: Filter): Promise<number>;
|
|
11
|
+
find(options?: EntityDataProviderFindOptions): Promise<Array<any>>;
|
|
12
|
+
update(id: any, data: any): Promise<any>;
|
|
13
|
+
delete(id: any): Promise<void>;
|
|
14
|
+
insert(data: any): Promise<any>;
|
|
15
|
+
}
|
|
16
|
+
export interface EntityDataProviderFindOptions {
|
|
17
|
+
where?: Filter;
|
|
18
|
+
limit?: number;
|
|
19
|
+
page?: number;
|
|
20
|
+
orderBy?: Sort;
|
|
21
|
+
}
|
|
22
|
+
export interface RestDataProviderHttpProvider {
|
|
23
|
+
post(url: string, data: any): Promise<any>;
|
|
24
|
+
delete(url: string): Promise<void>;
|
|
25
|
+
put(url: string, data: any): Promise<any>;
|
|
26
|
+
get(url: string): Promise<any>;
|
|
27
|
+
}
|
|
28
|
+
export declare function extractSort(sort: any): Sort;
|
|
29
|
+
export interface ErrorInfo<entityType = any> {
|
|
30
|
+
message?: string;
|
|
31
|
+
modelState?: {
|
|
32
|
+
[Properties in keyof Partial<OmitEB<entityType>>]?: string;
|
|
33
|
+
};
|
|
34
|
+
stack?: string;
|
|
35
|
+
exception?: any;
|
|
36
|
+
httpStatusCode?: number;
|
|
37
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { EntityDataProvider, EntityDataProviderFindOptions } from '../data-interfaces';
|
|
2
|
+
import { Filter } from '../filter/filter-interfaces';
|
|
3
|
+
import { EntityMetadata, EntityFilter } from '../remult3';
|
|
4
|
+
export declare class ArrayEntityDataProvider implements EntityDataProvider {
|
|
5
|
+
private entity;
|
|
6
|
+
private rows?;
|
|
7
|
+
static customFilter(filter: CustomArrayFilter): EntityFilter<any>;
|
|
8
|
+
constructor(entity: EntityMetadata, rows?: any[]);
|
|
9
|
+
private verifyThatRowHasAllNotNullColumns;
|
|
10
|
+
count(where?: Filter): Promise<number>;
|
|
11
|
+
find(options?: EntityDataProviderFindOptions): Promise<any[]>;
|
|
12
|
+
translateFromJson(row: any): {};
|
|
13
|
+
translateToJson(row: any): {};
|
|
14
|
+
private idMatches;
|
|
15
|
+
update(id: any, data: any): Promise<any>;
|
|
16
|
+
delete(id: any): Promise<void>;
|
|
17
|
+
insert(data: any): Promise<any>;
|
|
18
|
+
}
|
|
19
|
+
export declare type CustomArrayFilter = (item: any) => boolean;
|
|
20
|
+
export interface CustomArrayFilterObject {
|
|
21
|
+
arrayFilter: CustomArrayFilter;
|
|
22
|
+
}
|