remult 0.17.3 → 0.18.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.
Files changed (163) hide show
  1. package/SseSubscriptionServer.d.ts +23 -0
  2. package/SseSubscriptionServer.js +143 -0
  3. package/ably.d.ts +13 -0
  4. package/ably.js +59 -0
  5. package/esm/SseSubscriptionServer.d.ts +23 -0
  6. package/esm/SseSubscriptionServer.js +102 -0
  7. package/esm/ably.d.ts +13 -0
  8. package/esm/ably.js +28 -0
  9. package/esm/index.d.ts +7 -4
  10. package/esm/index.js +4 -2
  11. package/esm/live-query/data-provider-live-query-storage.d.ts +23 -0
  12. package/esm/live-query/data-provider-live-query-storage.js +78 -0
  13. package/esm/myLog.d.ts +1 -0
  14. package/esm/myLog.js +5 -0
  15. package/esm/postgres/postgres-data-provider.d.ts +4 -2
  16. package/esm/postgres/postgres-data-provider.js +18 -6
  17. package/esm/postgres/schema-builder.d.ts +2 -1
  18. package/esm/postgres/schema-builder.js +49 -29
  19. package/esm/remult-knex/index.d.ts +10 -32
  20. package/esm/remult-knex/index.js +75 -51
  21. package/esm/remult-mongo.d.ts +9 -2
  22. package/esm/remult-mongo.js +24 -8
  23. package/esm/remult-next.d.ts +11 -0
  24. package/esm/remult-next.js +27 -0
  25. package/esm/server/core.d.ts +2 -0
  26. package/esm/server/core.js +2 -0
  27. package/esm/server/expressBridge.d.ts +19 -7
  28. package/esm/server/expressBridge.js +135 -31
  29. package/esm/server/index.d.ts +2 -1
  30. package/esm/server/index.js +2 -1
  31. package/esm/server/initDataProvider.d.ts +2 -0
  32. package/esm/server/initDataProvider.js +14 -0
  33. package/esm/src/buildRestDataProvider.d.ts +31 -0
  34. package/esm/src/buildRestDataProvider.js +110 -0
  35. package/esm/src/column-interfaces.d.ts +44 -8
  36. package/esm/src/column.d.ts +3 -3
  37. package/esm/src/context.d.ts +39 -36
  38. package/esm/src/context.js +87 -117
  39. package/esm/src/data-api.d.ts +9 -3
  40. package/esm/src/data-api.js +130 -63
  41. package/esm/src/data-interfaces.d.ts +5 -1
  42. package/esm/src/data-providers/array-entity-data-provider.d.ts +1 -1
  43. package/esm/src/data-providers/array-entity-data-provider.js +2 -17
  44. package/esm/src/data-providers/rest-data-provider.d.ts +4 -3
  45. package/esm/src/data-providers/rest-data-provider.js +64 -23
  46. package/esm/src/data-providers/sql-database.d.ts +7 -3
  47. package/esm/src/data-providers/sql-database.js +60 -17
  48. package/esm/src/data-providers/web-sql-data-provider.d.ts +3 -0
  49. package/esm/src/data-providers/web-sql-data-provider.js +23 -9
  50. package/esm/src/entity.d.ts +2 -2
  51. package/esm/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
  52. package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +29 -21
  53. package/esm/src/filter/filter-interfaces.d.ts +5 -5
  54. package/esm/src/filter/filter-interfaces.js +31 -26
  55. package/esm/src/live-query/LiveQueryClient.d.ts +1 -0
  56. package/esm/src/live-query/LiveQueryClient.js +184 -0
  57. package/esm/src/live-query/SseSubscriptionClient.d.ts +5 -0
  58. package/esm/src/live-query/SseSubscriptionClient.js +86 -0
  59. package/esm/src/live-query/SubscriptionChannel.d.ts +41 -0
  60. package/esm/src/live-query/SubscriptionChannel.js +121 -0
  61. package/esm/src/live-query/SubscriptionServer.d.ts +38 -0
  62. package/esm/src/live-query/SubscriptionServer.js +108 -0
  63. package/esm/src/remult-proxy.d.ts +1 -1
  64. package/esm/src/remult-proxy.js +52 -7
  65. package/esm/src/remult3/RepositoryImplementation.d.ts +14 -5
  66. package/esm/src/remult3/RepositoryImplementation.js +124 -44
  67. package/esm/src/remult3/getId.d.ts +2 -0
  68. package/esm/src/remult3/getId.js +7 -0
  69. package/esm/src/remult3/remult3.d.ts +67 -10
  70. package/esm/src/remult3/remult3.js +1 -0
  71. package/esm/src/server-action.d.ts +5 -1
  72. package/esm/src/server-action.js +11 -12
  73. package/esm/src/shared-tests/db-tests-setup.d.ts +12 -5
  74. package/esm/src/shared-tests/db-tests-setup.js +14 -6
  75. package/esm/src/shared-tests/db-tests.db-tests.js +71 -10
  76. package/esm/src/sort.d.ts +1 -0
  77. package/esm/src/sort.js +18 -0
  78. package/esm/src/sql-command.d.ts +5 -2
  79. package/esm/src/tests/entityForCustomFilter.d.ts +6 -6
  80. package/esm/src/tests/entityForCustomFilter.js +20 -21
  81. package/esm/src/tests/frontend-database-tests-setup.spec.js +3 -4
  82. package/esm/src/tests/testHelper.spec.d.ts +3 -1
  83. package/esm/src/tests/testHelper.spec.js +63 -60
  84. package/esm/src/valueConverters.js +15 -7
  85. package/index.d.ts +7 -4
  86. package/index.js +7 -3
  87. package/live-query/data-provider-live-query-storage.d.ts +23 -0
  88. package/live-query/data-provider-live-query-storage.js +236 -0
  89. package/myLog.d.ts +1 -0
  90. package/myLog.js +20 -0
  91. package/package.json +18 -1
  92. package/postgres/postgres-data-provider.d.ts +4 -2
  93. package/postgres/postgres-data-provider.js +54 -39
  94. package/postgres/schema-builder.d.ts +2 -1
  95. package/postgres/schema-builder.js +118 -70
  96. package/remult-express.js +1 -1
  97. package/remult-knex/index.d.ts +10 -32
  98. package/remult-knex/index.js +134 -87
  99. package/remult-mongo.d.ts +9 -2
  100. package/remult-mongo.js +39 -7
  101. package/remult-next.d.ts +11 -0
  102. package/remult-next.js +73 -0
  103. package/server/JsonEntityFileStorage.js +2 -2
  104. package/server/core.d.ts +2 -0
  105. package/server/core.js +5 -1
  106. package/server/expressBridge.d.ts +19 -7
  107. package/server/expressBridge.js +283 -69
  108. package/server/index.d.ts +2 -1
  109. package/server/index.js +3 -1
  110. package/server/initDataProvider.d.ts +2 -0
  111. package/server/initDataProvider.js +26 -0
  112. package/src/buildRestDataProvider.d.ts +31 -0
  113. package/src/buildRestDataProvider.js +162 -0
  114. package/src/column-interfaces.d.ts +44 -8
  115. package/src/column.d.ts +3 -3
  116. package/src/context.d.ts +39 -36
  117. package/src/context.js +171 -166
  118. package/src/data-api.d.ts +9 -3
  119. package/src/data-api.js +233 -88
  120. package/src/data-interfaces.d.ts +5 -1
  121. package/src/data-providers/array-entity-data-provider.d.ts +1 -1
  122. package/src/data-providers/array-entity-data-provider.js +2 -17
  123. package/src/data-providers/rest-data-provider.d.ts +4 -3
  124. package/src/data-providers/rest-data-provider.js +105 -39
  125. package/src/data-providers/sql-database.d.ts +7 -3
  126. package/src/data-providers/sql-database.js +89 -16
  127. package/src/data-providers/web-sql-data-provider.d.ts +3 -0
  128. package/src/data-providers/web-sql-data-provider.js +56 -11
  129. package/src/entity.d.ts +2 -2
  130. package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
  131. package/src/filter/filter-consumer-bridge-to-sql-request.js +59 -50
  132. package/src/filter/filter-interfaces.d.ts +5 -5
  133. package/src/filter/filter-interfaces.js +31 -26
  134. package/src/live-query/LiveQueryClient.d.ts +1 -0
  135. package/src/live-query/LiveQueryClient.js +283 -0
  136. package/src/live-query/SseSubscriptionClient.d.ts +5 -0
  137. package/src/live-query/SseSubscriptionClient.js +150 -0
  138. package/src/live-query/SubscriptionChannel.d.ts +41 -0
  139. package/src/live-query/SubscriptionChannel.js +209 -0
  140. package/src/live-query/SubscriptionServer.d.ts +38 -0
  141. package/src/live-query/SubscriptionServer.js +258 -0
  142. package/src/remult-proxy.d.ts +1 -1
  143. package/src/remult-proxy.js +153 -8
  144. package/src/remult3/RepositoryImplementation.d.ts +14 -5
  145. package/src/remult3/RepositoryImplementation.js +200 -85
  146. package/src/remult3/getId.d.ts +2 -0
  147. package/src/remult3/getId.js +11 -0
  148. package/src/remult3/remult3.d.ts +67 -10
  149. package/src/remult3/remult3.js +1 -0
  150. package/src/server-action.d.ts +5 -1
  151. package/src/server-action.js +11 -11
  152. package/src/shared-tests/db-tests-setup.d.ts +12 -5
  153. package/src/shared-tests/db-tests-setup.js +17 -8
  154. package/src/shared-tests/db-tests.db-tests.js +140 -8
  155. package/src/sort.d.ts +1 -0
  156. package/src/sort.js +18 -0
  157. package/src/sql-command.d.ts +5 -2
  158. package/src/tests/entityForCustomFilter.d.ts +6 -6
  159. package/src/tests/entityForCustomFilter.js +30 -31
  160. package/src/tests/frontend-database-tests-setup.spec.js +5 -6
  161. package/src/tests/testHelper.spec.d.ts +3 -1
  162. package/src/tests/testHelper.spec.js +104 -100
  163. package/src/valueConverters.js +15 -7
@@ -1,23 +1,38 @@
1
1
  import { customUrlToken, buildFilterFromRequestParameters } from './filter/filter-interfaces';
2
+ import { doTransaction } from './context';
2
3
  import { Filter } from './filter/filter-interfaces';
4
+ import { ForbiddenError } from './server-action';
5
+ import { getId } from './remult3/getId';
6
+ import { findOptionsToJson, liveQueryAction } from './data-providers/rest-data-provider';
3
7
  export class DataApi {
4
8
  constructor(repository, remult) {
5
9
  this.repository = repository;
6
10
  this.remult = remult;
7
11
  }
8
- httpGet(res, req) {
9
- if (req.get("__action") == "count") {
10
- return this.count(res, req);
12
+ httpGet(res, req, serializeRequest) {
13
+ const action = req?.get("__action");
14
+ if (action?.startsWith(liveQueryAction))
15
+ return this.liveQuery(res, req, undefined, serializeRequest, getLiveQueryChannel(action.substring(liveQueryAction.length), this.remult.user?.id));
16
+ switch (action) {
17
+ case "get":
18
+ case "count":
19
+ return this.count(res, req, undefined);
11
20
  }
12
- else
13
- return this.getArray(res, req);
21
+ return this.getArray(res, req, undefined);
14
22
  }
15
- httpPost(res, req, body) {
16
- switch (req.get("__action")) {
23
+ async httpPost(res, req, body, serializeRequest) {
24
+ const action = req?.get("__action");
25
+ if (action?.startsWith(liveQueryAction))
26
+ return this.liveQuery(res, req, undefined, serializeRequest, getLiveQueryChannel(action.substring(liveQueryAction.length), this.remult.user?.id));
27
+ switch (action) {
17
28
  case "get":
18
29
  return this.getArray(res, req, body);
19
30
  case "count":
20
31
  return this.count(res, req, body);
32
+ case "endLiveQuery":
33
+ await this.remult.liveQueryStorage.remove(getLiveQueryChannel(body.id, this.remult.user?.id));
34
+ res.success("ok");
35
+ return;
21
36
  default:
22
37
  return this.post(res, body);
23
38
  }
@@ -41,61 +56,95 @@ export class DataApi {
41
56
  response.error(err);
42
57
  }
43
58
  }
59
+ async getArrayImpl(response, request, filterBody) {
60
+ let findOptions = { load: () => [] };
61
+ findOptions.where = await this.buildWhere(request, filterBody);
62
+ if (request) {
63
+ let sort = request.get("_sort");
64
+ if (sort != undefined) {
65
+ let dir = request.get('_order');
66
+ findOptions.orderBy = determineSort(sort, dir);
67
+ }
68
+ let limit = +request.get("_limit");
69
+ if (!limit && DataApi.defaultGetLimit)
70
+ limit = DataApi.defaultGetLimit;
71
+ findOptions.limit = limit;
72
+ findOptions.page = +request.get("_page");
73
+ }
74
+ if (this.remult.isAllowed(this.repository.metadata.options.apiRequireId)) {
75
+ let hasId = false;
76
+ let w = await Filter.fromEntityFilter(this.repository.metadata, findOptions.where);
77
+ if (w) {
78
+ w.__applyToConsumer({
79
+ containsCaseInsensitive: () => { },
80
+ isDifferentFrom: () => { },
81
+ isEqualTo: (col, val) => {
82
+ if (this.repository.metadata.idMetadata.isIdField(col))
83
+ hasId = true;
84
+ },
85
+ custom: () => { },
86
+ databaseCustom: () => { },
87
+ isGreaterOrEqualTo: () => { },
88
+ isGreaterThan: () => { },
89
+ isIn: () => { },
90
+ isLessOrEqualTo: () => { },
91
+ isLessThan: () => { },
92
+ isNotNull: () => { },
93
+ isNull: () => { },
94
+ or: () => { }
95
+ });
96
+ }
97
+ if (!hasId) {
98
+ response.forbidden();
99
+ throw new ForbiddenError();
100
+ }
101
+ }
102
+ const r = await this.repository.find(findOptions)
103
+ .then(async (r) => {
104
+ return await Promise.all(r.map(async (y) => this.repository.getEntityRef(y).toApiJson()));
105
+ });
106
+ return { r, findOptions };
107
+ }
44
108
  async getArray(response, request, filterBody) {
45
109
  if (!this.repository.metadata.apiReadAllowed) {
46
110
  response.forbidden();
47
111
  return;
48
112
  }
49
113
  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())));
114
+ const { r } = await this.getArrayImpl(response, request, filterBody);
115
+ response.success(r);
116
+ }
117
+ catch (err) {
118
+ if (err.isForbiddenError)
119
+ response.forbidden();
120
+ else
121
+ response.error(err);
122
+ }
123
+ }
124
+ async liveQuery(response, request, filterBody, serializeRequest, queryChannel) {
125
+ if (!this.repository.metadata.apiReadAllowed) {
126
+ response.forbidden();
127
+ return;
128
+ }
129
+ try {
130
+ const r = await this.getArrayImpl(response, request, filterBody);
131
+ const data = {
132
+ requestJson: serializeRequest(),
133
+ findOptionsJson: findOptionsToJson(r.findOptions, this.repository.metadata),
134
+ lastIds: r.r.map(y => getId(this.repository.metadata, y))
135
+ };
136
+ await this.remult.liveQueryStorage.add({
137
+ entityKey: this.repository.metadata.key,
138
+ id: queryChannel,
139
+ data
95
140
  });
141
+ response.success(r.r);
96
142
  }
97
143
  catch (err) {
98
- response.error(err);
144
+ if (err.isForbiddenError)
145
+ response.forbidden();
146
+ else
147
+ response.error(err);
99
148
  }
100
149
  }
101
150
  async buildWhere(request, filterBody) {
@@ -146,8 +195,8 @@ export class DataApi {
146
195
  response.forbidden();
147
196
  return;
148
197
  }
149
- await this.repository.getEntityRef(row).save();
150
- response.success(this.repository.getEntityRef(row).toApiJson());
198
+ await ref.save();
199
+ response.success(ref.toApiJson());
151
200
  });
152
201
  }
153
202
  async delete(response, id) {
@@ -162,17 +211,32 @@ export class DataApi {
162
211
  }
163
212
  async post(response, body) {
164
213
  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;
214
+ const insert = async (what) => {
215
+ let newr = this.repository.create();
216
+ await this.repository.getEntityRef(newr)._updateEntityBasedOnApi(what);
217
+ if (!this.repository.getEntityRef(newr).apiInsertAllowed) {
218
+ throw new ForbiddenError();
219
+ }
220
+ await this.repository.getEntityRef(newr).save();
221
+ return this.repository.getEntityRef(newr).toApiJson();
222
+ };
223
+ if (Array.isArray(body)) {
224
+ const result = [];
225
+ await doTransaction(this.remult, async () => {
226
+ for (const item of body) {
227
+ result.push(await insert(item));
228
+ }
229
+ });
230
+ response.created(result);
170
231
  }
171
- await this.repository.getEntityRef(newr).save();
172
- response.created(this.repository.getEntityRef(newr).toApiJson());
232
+ else
233
+ response.created(await insert(body));
173
234
  }
174
235
  catch (err) {
175
- response.error(err);
236
+ if (err.isForbiddenError)
237
+ response.forbidden();
238
+ else
239
+ response.error(err);
176
240
  }
177
241
  }
178
242
  }
@@ -202,3 +266,6 @@ export function serializeError(data) {
202
266
  data = { message: x };
203
267
  return data;
204
268
  }
269
+ export function getLiveQueryChannel(queryId, userId) {
270
+ return `users:${userId}:queries:${queryId}`;
271
+ }
@@ -4,7 +4,11 @@ import { EntityMetadata, OmitEB } from './remult3';
4
4
  export interface DataProvider {
5
5
  getEntityDataProvider(entity: EntityMetadata): EntityDataProvider;
6
6
  transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
7
- supportsCustomFilter?: boolean;
7
+ supportsRawFilter?: boolean;
8
+ ensureSchema?(entities: EntityMetadata[]): Promise<void>;
9
+ }
10
+ export interface Storage {
11
+ ensureSchema(): Promise<void>;
8
12
  }
9
13
  export interface EntityDataProvider {
10
14
  count(where: Filter): Promise<number>;
@@ -4,7 +4,7 @@ import { EntityMetadata, EntityFilter } from '../remult3';
4
4
  export declare class ArrayEntityDataProvider implements EntityDataProvider {
5
5
  private entity;
6
6
  private rows?;
7
- static customFilter(filter: CustomArrayFilter): EntityFilter<any>;
7
+ static rawFilter(filter: CustomArrayFilter): EntityFilter<any>;
8
8
  constructor(entity: EntityMetadata, rows?: any[]);
9
9
  private verifyThatRowHasAllNotNullColumns;
10
10
  count(where?: Filter): Promise<number>;
@@ -12,7 +12,7 @@ export class ArrayEntityDataProvider {
12
12
  }
13
13
  }
14
14
  }
15
- static customFilter(filter) {
15
+ static rawFilter(filter) {
16
16
  return {
17
17
  [customDatabaseFilterToken]: {
18
18
  arrayFilter: filter
@@ -66,22 +66,7 @@ export class ArrayEntityDataProvider {
66
66
  }
67
67
  if (options.orderBy) {
68
68
  rows = rows.sort((a, b) => {
69
- let r = 0;
70
- for (let i = 0; i < options.orderBy.Segments.length; i++) {
71
- let seg = options.orderBy.Segments[i];
72
- let left = a[seg.field.key];
73
- let right = b[seg.field.key];
74
- if (left > right)
75
- r = 1;
76
- else if (left < right)
77
- r = -1;
78
- if (r != 0) {
79
- if (seg.isDescending)
80
- r *= -1;
81
- return r;
82
- }
83
- }
84
- return r;
69
+ return options.orderBy.compare(a, b);
85
70
  });
86
71
  }
87
72
  rows = pageArray(rows, options);
@@ -6,9 +6,9 @@ import { ApiClient } from '../context';
6
6
  export declare class RestDataProvider implements DataProvider {
7
7
  private apiProvider;
8
8
  constructor(apiProvider: () => ApiClient);
9
- getEntityDataProvider(entity: EntityMetadata): EntityDataProvider;
9
+ getEntityDataProvider(entity: EntityMetadata): RestEntityDataProvider;
10
10
  transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
11
- supportsCustomFilter: boolean;
11
+ supportsRawFilter: boolean;
12
12
  }
13
13
  export declare class RestEntityDataProvider implements EntityDataProvider {
14
14
  private url;
@@ -18,7 +18,7 @@ export declare class RestEntityDataProvider implements EntityDataProvider {
18
18
  translateFromJson(row: any): {};
19
19
  translateToJson(row: any): {};
20
20
  count(where: Filter): Promise<number>;
21
- find(options: EntityDataProviderFindOptions): Promise<Array<any>>;
21
+ find(options?: EntityDataProviderFindOptions): Promise<Array<any>>;
22
22
  update(id: any, data: any): Promise<any>;
23
23
  delete(id: any): Promise<void>;
24
24
  insert(data: any): Promise<any>;
@@ -36,3 +36,4 @@ export declare class RestDataProviderHttpProviderUsingFetch implements RestDataP
36
36
  }): Promise<any>;
37
37
  }
38
38
  export declare function addFilterToUrlAndReturnTrueIfSuccessful(filter: any, url: UrlBuilder): boolean;
39
+ export declare const liveQueryAction = "liveQuery-";
@@ -1,10 +1,11 @@
1
1
  import { UrlBuilder } from '../../urlBuilder';
2
- import { customUrlToken } from '../filter/filter-interfaces';
3
- import { buildRestDataProvider, retry } from '../context';
2
+ import { customUrlToken, Filter } from '../filter/filter-interfaces';
3
+ import { buildRestDataProvider, retry } from "../buildRestDataProvider";
4
+ import { actionInfo } from '../server-action';
4
5
  export class RestDataProvider {
5
6
  constructor(apiProvider) {
6
7
  this.apiProvider = apiProvider;
7
- this.supportsCustomFilter = true;
8
+ this.supportsRawFilter = true;
8
9
  }
9
10
  getEntityDataProvider(entity) {
10
11
  return new RestEntityDataProvider(() => {
@@ -20,6 +21,29 @@ export class RestDataProvider {
20
21
  throw new Error("Method not implemented.");
21
22
  }
22
23
  }
24
+ //@internal
25
+ export function findOptionsToJson(options, meta) {
26
+ return {
27
+ limit: options.limit,
28
+ page: options.page,
29
+ where: Filter.entityFilterToJson(meta, options.where),
30
+ orderBy: options.orderBy
31
+ };
32
+ }
33
+ //@internal
34
+ export function findOptionsFromJson(json, meta) {
35
+ let r = {};
36
+ for (const key of ["limit", "page", "where", "orderBy"]) {
37
+ if (json[key] !== undefined) {
38
+ if (key === "where") {
39
+ r[key] = Filter.entityFilterFromJson(meta, json.where);
40
+ }
41
+ else
42
+ r[key] = json[key];
43
+ }
44
+ }
45
+ return r;
46
+ }
23
47
  export class RestEntityDataProvider {
24
48
  constructor(url, http, entity) {
25
49
  this.url = url;
@@ -41,20 +65,15 @@ export class RestEntityDataProvider {
41
65
  return result;
42
66
  }
43
67
  async count(where) {
44
- let url = new UrlBuilder(this.url());
45
- url.add("__action", "count");
46
- let filterObject;
47
- if (where) {
48
- filterObject = where.toJson();
49
- if (addFilterToUrlAndReturnTrueIfSuccessful(filterObject, url))
50
- filterObject = undefined;
51
- }
52
- if (filterObject)
53
- return this.http().post(url.url, filterObject).then(r => +(r.count));
54
- else
55
- return this.http().get(url.url).then(r => +(r.count));
68
+ const { run } = this.buildFindRequest({ where });
69
+ return run("count").then(r => +r.count);
56
70
  }
57
71
  find(options) {
72
+ let { run } = this.buildFindRequest(options);
73
+ return run().then(x => x.map(y => this.translateFromJson(y)));
74
+ }
75
+ //@internal
76
+ buildFindRequest(options) {
58
77
  let url = new UrlBuilder(this.url());
59
78
  let filterObject;
60
79
  if (options) {
@@ -82,13 +101,34 @@ export class RestEntityDataProvider {
82
101
  if (options.page)
83
102
  url.add('_page', options.page);
84
103
  }
85
- if (filterObject) {
86
- url.add("__action", "get");
87
- return this.http().post(url.url, filterObject).then(x => x.map(y => this.translateFromJson(y)));
88
- }
89
- else
90
- return this.http().get(url.url).then(x => x.map(y => this.translateFromJson(y)));
91
- ;
104
+ const run = (action) => {
105
+ let u = new UrlBuilder(url.url);
106
+ if (!action && filterObject) {
107
+ action = "get";
108
+ }
109
+ if (action)
110
+ u.add("__action", action);
111
+ if (filterObject) {
112
+ return this.http().post(u.url, filterObject);
113
+ }
114
+ else
115
+ return this.http().get(u.url);
116
+ };
117
+ return {
118
+ createKey: () => JSON.stringify({ url, filterObject }),
119
+ run,
120
+ subscribe: async (queryId) => {
121
+ const result = await run(liveQueryAction + queryId);
122
+ return {
123
+ result,
124
+ unsubscribe: async () => {
125
+ return actionInfo.runActionWithoutBlockingUI(() => this.http().post(this.url() + "?__action=endLiveQuery", {
126
+ id: queryId
127
+ }));
128
+ }
129
+ };
130
+ }
131
+ };
92
132
  }
93
133
  update(id, data) {
94
134
  let result = {};
@@ -186,7 +226,7 @@ export function addFilterToUrlAndReturnTrueIfSuccessful(filter, url) {
186
226
  if (Object.prototype.hasOwnProperty.call(filter, key)) {
187
227
  const element = filter[key];
188
228
  if (Array.isArray(element)) {
189
- if (key.endsWith("_in"))
229
+ if (key.endsWith(".in"))
190
230
  url.add(key, JSON.stringify(element));
191
231
  else
192
232
  element.forEach(e => url.add(key, e));
@@ -199,3 +239,4 @@ export function addFilterToUrlAndReturnTrueIfSuccessful(filter, url) {
199
239
  }
200
240
  return true;
201
241
  }
242
+ export const liveQueryAction = "liveQuery-";
@@ -1,14 +1,18 @@
1
1
  import { EntityDataProvider, DataProvider } from "../data-interfaces";
2
- import { SqlCommand, SqlImplementation, SqlResult } from "../sql-command";
2
+ import { SqlCommand, SqlCommandWithParameters, SqlImplementation, SqlResult } from "../sql-command";
3
3
  import { CustomSqlFilterBuilderFunction } from "../filter/filter-consumer-bridge-to-sql-request";
4
- import { EntityMetadata, EntityFilter } from "../remult3";
4
+ import { EntityMetadata, EntityFilter, RepositoryOverloads } from "../remult3";
5
+ import { Remult } from "../context";
5
6
  export declare class SqlDatabase implements DataProvider {
6
7
  private sql;
8
+ static getDb(remult?: Remult): SqlDatabase;
7
9
  createCommand(): SqlCommand;
8
10
  execute(sql: string): Promise<SqlResult>;
11
+ ensureSchema(entities: EntityMetadata<any>[]): Promise<void>;
9
12
  getEntityDataProvider(entity: EntityMetadata): EntityDataProvider;
10
13
  transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
11
- static customFilter(build: CustomSqlFilterBuilderFunction): EntityFilter<any>;
14
+ static rawFilter(build: CustomSqlFilterBuilderFunction): EntityFilter<any>;
15
+ static filterToRaw<entityType>(repo: RepositoryOverloads<entityType>, condition: EntityFilter<entityType>, sqlCommand?: SqlCommandWithParameters): Promise<string>;
12
16
  static LogToConsole: boolean;
13
17
  static durationThreshold: number;
14
18
  constructor(sql: SqlImplementation);
@@ -1,23 +1,39 @@
1
1
  import { CompoundIdField } from "../column";
2
- import { FilterConsumerBridgeToSqlRequest, getDbNameProvider } from "../filter/filter-consumer-bridge-to-sql-request";
2
+ import { dbNamesOf, FilterConsumerBridgeToSqlRequest, isDbReadonly } from "../filter/filter-consumer-bridge-to-sql-request";
3
3
  import { customDatabaseFilterToken, Filter } from '../filter/filter-interfaces';
4
4
  import { Sort } from '../sort';
5
+ import { getRepository } from "../remult3";
6
+ import { RemultProxy } from "../remult-proxy";
5
7
  // @dynamic
6
8
  export class SqlDatabase {
7
9
  constructor(sql) {
8
10
  this.sql = sql;
9
11
  this.createdEntities = [];
10
12
  }
13
+ static getDb(remult) {
14
+ const r = (remult || RemultProxy.defaultRemult).dataProvider;
15
+ if (!r.createCommand)
16
+ throw "the data provider is not an SqlDatabase";
17
+ return r;
18
+ }
11
19
  createCommand() {
12
20
  return new LogSQLCommand(this.sql.createCommand(), SqlDatabase.LogToConsole);
13
21
  }
14
22
  async execute(sql) {
15
23
  return await this.createCommand().execute(sql);
16
24
  }
25
+ /* @internal*/
26
+ _getSourceSql() {
27
+ return this.sql;
28
+ }
29
+ async ensureSchema(entities) {
30
+ if (this.sql.ensureSchema)
31
+ await this.sql.ensureSchema(entities);
32
+ }
17
33
  getEntityDataProvider(entity) {
18
34
  return new ActualSQLServerDataProvider(entity, this, async (dbName) => {
19
- if (this.createdEntities.indexOf(dbName.entityName) < 0) {
20
- this.createdEntities.push(dbName.entityName);
35
+ if (this.createdEntities.indexOf(dbName.$entityName) < 0) {
36
+ this.createdEntities.push(dbName.$entityName);
21
37
  await this.sql.entityIsUsedForTheFirstTime(entity);
22
38
  }
23
39
  }, this.sql);
@@ -48,13 +64,23 @@ export class SqlDatabase {
48
64
  }
49
65
  });
50
66
  }
51
- static customFilter(build) {
67
+ static rawFilter(build) {
52
68
  return {
53
69
  [customDatabaseFilterToken]: {
54
70
  buildSql: build
55
71
  }
56
72
  };
57
73
  }
74
+ static async filterToRaw(repo, condition, sqlCommand) {
75
+ if (!sqlCommand) {
76
+ sqlCommand = new myDummySQLCommand();
77
+ }
78
+ const r = getRepository(repo);
79
+ var b = new FilterConsumerBridgeToSqlRequest(sqlCommand, await dbNamesOf(r.metadata));
80
+ b._addWhere = false;
81
+ await (await (r.translateWhereToFilter(condition))).__applyToConsumer(b);
82
+ return await b.resolveWhere();
83
+ }
58
84
  }
59
85
  SqlDatabase.LogToConsole = false;
60
86
  SqlDatabase.durationThreshold = 0;
@@ -76,7 +102,7 @@ class LogSQLCommand {
76
102
  if (this.allQueries) {
77
103
  var d = new Date().valueOf() - start.valueOf();
78
104
  if (d > SqlDatabase.durationThreshold) {
79
- console.log({ query: sql, arguments: this.args, duration: d / 1000 });
105
+ console.info({ query: sql, arguments: this.args, duration: d / 1000 });
80
106
  }
81
107
  }
82
108
  return r;
@@ -95,13 +121,13 @@ class ActualSQLServerDataProvider {
95
121
  this.strategy = strategy;
96
122
  }
97
123
  async init() {
98
- let dbNameProvider = await getDbNameProvider(this.entity);
124
+ let dbNameProvider = await dbNamesOf(this.entity);
99
125
  await this.iAmUsed(dbNameProvider);
100
126
  return dbNameProvider;
101
127
  }
102
128
  async count(where) {
103
129
  let e = await this.init();
104
- let select = 'select count(*) count from ' + e.entityName;
130
+ let select = 'select count(*) count from ' + e.$entityName;
105
131
  let r = this.sql.createCommand();
106
132
  if (where) {
107
133
  let wc = new FilterConsumerBridgeToSqlRequest(r, e);
@@ -116,7 +142,7 @@ class ActualSQLServerDataProvider {
116
142
  let e = await this.init();
117
143
  let { colKeys, select } = this.buildSelect(e);
118
144
  select = 'select ' + select;
119
- select += '\n from ' + e.entityName;
145
+ select += '\n from ' + e.$entityName;
120
146
  let r = this.sql.createCommand();
121
147
  if (options) {
122
148
  if (options.where) {
@@ -147,7 +173,7 @@ class ActualSQLServerDataProvider {
147
173
  }
148
174
  else
149
175
  select += ', ';
150
- select += e.nameOf(c.field);
176
+ select += e.$dbNameOf(c.field);
151
177
  if (c.isDescending)
152
178
  select += ' desc';
153
179
  }
@@ -189,7 +215,7 @@ class ActualSQLServerDataProvider {
189
215
  else {
190
216
  if (colKeys.length > 0)
191
217
  select += ', ';
192
- select += e.nameOf(x);
218
+ select += e.$dbNameOf(x);
193
219
  colKeys.push(x);
194
220
  }
195
221
  }
@@ -200,12 +226,12 @@ class ActualSQLServerDataProvider {
200
226
  let r = this.sql.createCommand();
201
227
  let f = new FilterConsumerBridgeToSqlRequest(r, e);
202
228
  Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
203
- let statement = 'update ' + e.entityName + ' set ';
229
+ let statement = 'update ' + e.$entityName + ' set ';
204
230
  let added = false;
205
231
  for (const x of this.entity.fields) {
206
232
  if (x instanceof CompoundIdField) {
207
233
  }
208
- if (e.isDbReadonly(x)) { }
234
+ if (isDbReadonly(x, e)) { }
209
235
  else if (data[x.key] !== undefined) {
210
236
  let v = x.valueConverter.toDb(data[x.key]);
211
237
  if (v !== undefined) {
@@ -213,7 +239,7 @@ class ActualSQLServerDataProvider {
213
239
  added = true;
214
240
  else
215
241
  statement += ', ';
216
- statement += e.nameOf(x) + ' = ' + r.addParameterAndReturnSqlToken(v);
242
+ statement += e.$dbNameOf(x) + ' = ' + r.addParameterAndReturnSqlToken(v);
217
243
  }
218
244
  }
219
245
  }
@@ -229,7 +255,7 @@ class ActualSQLServerDataProvider {
229
255
  let r = this.sql.createCommand();
230
256
  let f = new FilterConsumerBridgeToSqlRequest(r, e);
231
257
  Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
232
- let statement = 'delete from ' + e.entityName;
258
+ let statement = 'delete from ' + e.$entityName;
233
259
  statement += await f.resolveWhere();
234
260
  return r.execute(statement).then(() => { });
235
261
  }
@@ -240,7 +266,7 @@ class ActualSQLServerDataProvider {
240
266
  let vals = '';
241
267
  let added = false;
242
268
  for (const x of this.entity.fields) {
243
- if (e.isDbReadonly(x)) { }
269
+ if (isDbReadonly(x, e)) { }
244
270
  else {
245
271
  let v = x.valueConverter.toDb(data[x.key]);
246
272
  if (v != undefined) {
@@ -250,15 +276,32 @@ class ActualSQLServerDataProvider {
250
276
  cols += ', ';
251
277
  vals += ', ';
252
278
  }
253
- cols += e.nameOf(x);
279
+ cols += e.$dbNameOf(x);
254
280
  vals += r.addParameterAndReturnSqlToken(v);
255
281
  }
256
282
  }
257
283
  }
258
- let statement = `insert into ${e.entityName} (${cols}) values (${vals})`;
284
+ let statement = `insert into ${e.$entityName} (${cols}) values (${vals})`;
259
285
  let { colKeys, select } = this.buildSelect(e);
260
286
  statement += ' returning ' + select;
261
287
  return await r.execute(statement).then(sql => this.buildResultRow(colKeys, sql.rows[0], sql));
262
288
  }
263
289
  }
264
290
  ActualSQLServerDataProvider.LogToConsole = false;
291
+ class myDummySQLCommand {
292
+ execute(sql) {
293
+ throw new Error("Method not implemented.");
294
+ }
295
+ addParameterAndReturnSqlToken(val) {
296
+ if (val === null)
297
+ return "null";
298
+ if (val instanceof Date)
299
+ val = val.toISOString();
300
+ if (typeof (val) == "string") {
301
+ if (val == undefined)
302
+ val = '';
303
+ return '\'' + val.replace(/'/g, '\'\'') + '\'';
304
+ }
305
+ return val.toString();
306
+ }
307
+ }