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.
- package/SseSubscriptionServer.d.ts +23 -0
- package/SseSubscriptionServer.js +143 -0
- package/ably.d.ts +13 -0
- package/ably.js +59 -0
- package/esm/SseSubscriptionServer.d.ts +23 -0
- package/esm/SseSubscriptionServer.js +102 -0
- package/esm/ably.d.ts +13 -0
- package/esm/ably.js +28 -0
- package/esm/index.d.ts +7 -4
- package/esm/index.js +4 -2
- package/esm/live-query/data-provider-live-query-storage.d.ts +23 -0
- package/esm/live-query/data-provider-live-query-storage.js +78 -0
- package/esm/myLog.d.ts +1 -0
- package/esm/myLog.js +5 -0
- package/esm/postgres/postgres-data-provider.d.ts +4 -2
- package/esm/postgres/postgres-data-provider.js +18 -6
- package/esm/postgres/schema-builder.d.ts +2 -1
- package/esm/postgres/schema-builder.js +49 -29
- package/esm/remult-knex/index.d.ts +10 -32
- package/esm/remult-knex/index.js +75 -51
- package/esm/remult-mongo.d.ts +9 -2
- package/esm/remult-mongo.js +24 -8
- package/esm/remult-next.d.ts +11 -0
- package/esm/remult-next.js +27 -0
- package/esm/server/core.d.ts +2 -0
- package/esm/server/core.js +2 -0
- package/esm/server/expressBridge.d.ts +19 -7
- package/esm/server/expressBridge.js +135 -31
- package/esm/server/index.d.ts +2 -1
- package/esm/server/index.js +2 -1
- package/esm/server/initDataProvider.d.ts +2 -0
- package/esm/server/initDataProvider.js +14 -0
- package/esm/src/buildRestDataProvider.d.ts +31 -0
- package/esm/src/buildRestDataProvider.js +110 -0
- package/esm/src/column-interfaces.d.ts +44 -8
- package/esm/src/column.d.ts +3 -3
- package/esm/src/context.d.ts +39 -36
- package/esm/src/context.js +87 -117
- package/esm/src/data-api.d.ts +9 -3
- package/esm/src/data-api.js +130 -63
- package/esm/src/data-interfaces.d.ts +5 -1
- package/esm/src/data-providers/array-entity-data-provider.d.ts +1 -1
- package/esm/src/data-providers/array-entity-data-provider.js +2 -17
- package/esm/src/data-providers/rest-data-provider.d.ts +4 -3
- package/esm/src/data-providers/rest-data-provider.js +64 -23
- package/esm/src/data-providers/sql-database.d.ts +7 -3
- package/esm/src/data-providers/sql-database.js +60 -17
- package/esm/src/data-providers/web-sql-data-provider.d.ts +3 -0
- package/esm/src/data-providers/web-sql-data-provider.js +23 -9
- package/esm/src/entity.d.ts +2 -2
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +29 -21
- package/esm/src/filter/filter-interfaces.d.ts +5 -5
- package/esm/src/filter/filter-interfaces.js +31 -26
- package/esm/src/live-query/LiveQueryClient.d.ts +1 -0
- package/esm/src/live-query/LiveQueryClient.js +184 -0
- package/esm/src/live-query/SseSubscriptionClient.d.ts +5 -0
- package/esm/src/live-query/SseSubscriptionClient.js +86 -0
- package/esm/src/live-query/SubscriptionChannel.d.ts +41 -0
- package/esm/src/live-query/SubscriptionChannel.js +121 -0
- package/esm/src/live-query/SubscriptionServer.d.ts +38 -0
- package/esm/src/live-query/SubscriptionServer.js +108 -0
- package/esm/src/remult-proxy.d.ts +1 -1
- package/esm/src/remult-proxy.js +52 -7
- package/esm/src/remult3/RepositoryImplementation.d.ts +14 -5
- package/esm/src/remult3/RepositoryImplementation.js +124 -44
- package/esm/src/remult3/getId.d.ts +2 -0
- package/esm/src/remult3/getId.js +7 -0
- package/esm/src/remult3/remult3.d.ts +67 -10
- package/esm/src/remult3/remult3.js +1 -0
- package/esm/src/server-action.d.ts +5 -1
- package/esm/src/server-action.js +11 -12
- package/esm/src/shared-tests/db-tests-setup.d.ts +12 -5
- package/esm/src/shared-tests/db-tests-setup.js +14 -6
- package/esm/src/shared-tests/db-tests.db-tests.js +71 -10
- package/esm/src/sort.d.ts +1 -0
- package/esm/src/sort.js +18 -0
- package/esm/src/sql-command.d.ts +5 -2
- package/esm/src/tests/entityForCustomFilter.d.ts +6 -6
- package/esm/src/tests/entityForCustomFilter.js +20 -21
- package/esm/src/tests/frontend-database-tests-setup.spec.js +3 -4
- package/esm/src/tests/testHelper.spec.d.ts +3 -1
- package/esm/src/tests/testHelper.spec.js +63 -60
- package/esm/src/valueConverters.js +15 -7
- package/index.d.ts +7 -4
- package/index.js +7 -3
- package/live-query/data-provider-live-query-storage.d.ts +23 -0
- package/live-query/data-provider-live-query-storage.js +236 -0
- package/myLog.d.ts +1 -0
- package/myLog.js +20 -0
- package/package.json +18 -1
- package/postgres/postgres-data-provider.d.ts +4 -2
- package/postgres/postgres-data-provider.js +54 -39
- package/postgres/schema-builder.d.ts +2 -1
- package/postgres/schema-builder.js +118 -70
- package/remult-express.js +1 -1
- package/remult-knex/index.d.ts +10 -32
- package/remult-knex/index.js +134 -87
- package/remult-mongo.d.ts +9 -2
- package/remult-mongo.js +39 -7
- package/remult-next.d.ts +11 -0
- package/remult-next.js +73 -0
- package/server/JsonEntityFileStorage.js +2 -2
- package/server/core.d.ts +2 -0
- package/server/core.js +5 -1
- package/server/expressBridge.d.ts +19 -7
- package/server/expressBridge.js +283 -69
- package/server/index.d.ts +2 -1
- package/server/index.js +3 -1
- package/server/initDataProvider.d.ts +2 -0
- package/server/initDataProvider.js +26 -0
- package/src/buildRestDataProvider.d.ts +31 -0
- package/src/buildRestDataProvider.js +162 -0
- package/src/column-interfaces.d.ts +44 -8
- package/src/column.d.ts +3 -3
- package/src/context.d.ts +39 -36
- package/src/context.js +171 -166
- package/src/data-api.d.ts +9 -3
- package/src/data-api.js +233 -88
- package/src/data-interfaces.d.ts +5 -1
- package/src/data-providers/array-entity-data-provider.d.ts +1 -1
- package/src/data-providers/array-entity-data-provider.js +2 -17
- package/src/data-providers/rest-data-provider.d.ts +4 -3
- package/src/data-providers/rest-data-provider.js +105 -39
- package/src/data-providers/sql-database.d.ts +7 -3
- package/src/data-providers/sql-database.js +89 -16
- package/src/data-providers/web-sql-data-provider.d.ts +3 -0
- package/src/data-providers/web-sql-data-provider.js +56 -11
- package/src/entity.d.ts +2 -2
- package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
- package/src/filter/filter-consumer-bridge-to-sql-request.js +59 -50
- package/src/filter/filter-interfaces.d.ts +5 -5
- package/src/filter/filter-interfaces.js +31 -26
- package/src/live-query/LiveQueryClient.d.ts +1 -0
- package/src/live-query/LiveQueryClient.js +283 -0
- package/src/live-query/SseSubscriptionClient.d.ts +5 -0
- package/src/live-query/SseSubscriptionClient.js +150 -0
- package/src/live-query/SubscriptionChannel.d.ts +41 -0
- package/src/live-query/SubscriptionChannel.js +209 -0
- package/src/live-query/SubscriptionServer.d.ts +38 -0
- package/src/live-query/SubscriptionServer.js +258 -0
- package/src/remult-proxy.d.ts +1 -1
- package/src/remult-proxy.js +153 -8
- package/src/remult3/RepositoryImplementation.d.ts +14 -5
- package/src/remult3/RepositoryImplementation.js +200 -85
- package/src/remult3/getId.d.ts +2 -0
- package/src/remult3/getId.js +11 -0
- package/src/remult3/remult3.d.ts +67 -10
- package/src/remult3/remult3.js +1 -0
- package/src/server-action.d.ts +5 -1
- package/src/server-action.js +11 -11
- package/src/shared-tests/db-tests-setup.d.ts +12 -5
- package/src/shared-tests/db-tests-setup.js +17 -8
- package/src/shared-tests/db-tests.db-tests.js +140 -8
- package/src/sort.d.ts +1 -0
- package/src/sort.js +18 -0
- package/src/sql-command.d.ts +5 -2
- package/src/tests/entityForCustomFilter.d.ts +6 -6
- package/src/tests/entityForCustomFilter.js +30 -31
- package/src/tests/frontend-database-tests-setup.spec.js +5 -6
- package/src/tests/testHelper.spec.d.ts +3 -1
- package/src/tests/testHelper.spec.js +104 -100
- package/src/valueConverters.js +15 -7
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { __RowsOfDataForTesting } from "../__RowsOfDataForTesting";
|
|
2
2
|
import { SqlCommand, SqlImplementation } from "../sql-command";
|
|
3
3
|
import { EntityMetadata } from "../remult3";
|
|
4
|
+
import { Remult } from "../context";
|
|
4
5
|
export declare class WebSqlDataProvider implements SqlImplementation, __RowsOfDataForTesting {
|
|
5
6
|
private databaseName;
|
|
6
7
|
rows: {
|
|
7
8
|
[tableName: string]: any;
|
|
8
9
|
};
|
|
9
10
|
constructor(databaseName: string, databaseSize?: number);
|
|
11
|
+
static getDb(remult?: Remult): any;
|
|
10
12
|
getLimitSqlSyntax(limit: number, offset: number): string;
|
|
11
13
|
entityIsUsedForTheFirstTime(entity: EntityMetadata): Promise<void>;
|
|
14
|
+
ensureSchema(entities: EntityMetadata<any>[]): Promise<void>;
|
|
12
15
|
dropTable(entity: EntityMetadata): Promise<void>;
|
|
13
16
|
createTable(entity: EntityMetadata<any>): Promise<void>;
|
|
14
17
|
createCommand(): SqlCommand;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isAutoIncrement } from "../remult3";
|
|
2
2
|
import { SqlDatabase } from "./sql-database";
|
|
3
|
-
import {
|
|
3
|
+
import { dbNamesOf, isDbReadonly } from "../filter/filter-consumer-bridge-to-sql-request";
|
|
4
4
|
//SqlDatabase.LogToConsole = true;
|
|
5
5
|
export class WebSqlDataProvider {
|
|
6
6
|
constructor(databaseName, databaseSize = 2 * 1024 * 1024) {
|
|
@@ -8,41 +8,55 @@ export class WebSqlDataProvider {
|
|
|
8
8
|
//@ts-ignore
|
|
9
9
|
this.db = window.openDatabase(databaseName, '1.0', databaseName, databaseSize);
|
|
10
10
|
}
|
|
11
|
+
// return instance of websql.Database class - see @types/websql
|
|
12
|
+
static getDb(remult) {
|
|
13
|
+
const sql = SqlDatabase.getDb(remult);
|
|
14
|
+
const me = sql._getSourceSql();
|
|
15
|
+
if (!me.db) {
|
|
16
|
+
throw "the data provider is not a WebSqlDataProvider";
|
|
17
|
+
}
|
|
18
|
+
return me.db;
|
|
19
|
+
}
|
|
11
20
|
getLimitSqlSyntax(limit, offset) {
|
|
12
21
|
return ' limit ' + limit + ' offset ' + offset;
|
|
13
22
|
}
|
|
14
23
|
async entityIsUsedForTheFirstTime(entity) {
|
|
15
24
|
await this.createTable(entity);
|
|
16
25
|
}
|
|
26
|
+
async ensureSchema(entities) {
|
|
27
|
+
for (const entity of entities) {
|
|
28
|
+
await this.createTable(entity);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
17
31
|
async dropTable(entity) {
|
|
18
|
-
let e = await
|
|
19
|
-
let sql = 'drop table if exists ' + e
|
|
32
|
+
let e = await dbNamesOf(entity);
|
|
33
|
+
let sql = 'drop table if exists ' + e.$entityName;
|
|
20
34
|
if (SqlDatabase.LogToConsole)
|
|
21
|
-
console.
|
|
35
|
+
console.info(sql);
|
|
22
36
|
await this.createCommand().execute(sql);
|
|
23
37
|
}
|
|
24
38
|
async createTable(entity) {
|
|
25
39
|
let result = '';
|
|
26
|
-
let e = await
|
|
40
|
+
let e = await dbNamesOf(entity);
|
|
27
41
|
for (const x of entity.fields) {
|
|
28
|
-
if (!
|
|
42
|
+
if (!isDbReadonly(x, e) || isAutoIncrement(x)) {
|
|
29
43
|
if (result.length != 0)
|
|
30
44
|
result += ',';
|
|
31
45
|
result += '\r\n ';
|
|
32
46
|
if (isAutoIncrement(x)) {
|
|
33
47
|
if (x.key != entity.idMetadata.field.key)
|
|
34
48
|
throw "in web sql, autoincrement is only allowed for primary key";
|
|
35
|
-
result += e
|
|
49
|
+
result += e.$dbNameOf(x) + ' integer primary key autoincrement';
|
|
36
50
|
}
|
|
37
51
|
else {
|
|
38
|
-
result += this.addColumnSqlSyntax(x, e
|
|
52
|
+
result += this.addColumnSqlSyntax(x, e.$dbNameOf(x));
|
|
39
53
|
if (x.key == entity.idMetadata.field.key) {
|
|
40
54
|
result += ' primary key';
|
|
41
55
|
}
|
|
42
56
|
}
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
|
-
let sql = 'create table if not exists ' + e
|
|
59
|
+
let sql = 'create table if not exists ' + e.$entityName + ' (' + result + '\r\n)';
|
|
46
60
|
if (SqlDatabase.LogToConsole)
|
|
47
61
|
console.log(sql);
|
|
48
62
|
await this.createCommand().execute(sql);
|
package/esm/src/entity.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Allowed, AllowedForInstance } from "./context";
|
|
2
2
|
import { FieldMetadata as FieldMetadata } from './column-interfaces';
|
|
3
|
-
import { EntityOrderBy, FieldsMetadata, EntityFilter, EntityRef } from "./remult3";
|
|
3
|
+
import { EntityOrderBy, FieldsMetadata, EntityFilter, EntityRef, EntityMetadata } from "./remult3";
|
|
4
4
|
export interface EntityOptions<entityType = any> {
|
|
5
5
|
/**A human readable name for the entity */
|
|
6
6
|
caption?: string;
|
|
@@ -67,7 +67,7 @@ export interface EntityOptions<entityType = any> {
|
|
|
67
67
|
*/
|
|
68
68
|
dbName?: string;
|
|
69
69
|
/** For entities that are based on SQL expressions instead of a physical table or view*/
|
|
70
|
-
sqlExpression?: string | ((entity:
|
|
70
|
+
sqlExpression?: string | ((entity: EntityMetadata<entityType>) => string | Promise<string>);
|
|
71
71
|
/** An arrow function that identifies the `id` column to use for this entity
|
|
72
72
|
* @example
|
|
73
73
|
* //Single column id
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SqlCommandWithParameters } from "../sql-command";
|
|
2
2
|
import { Filter, FilterConsumer } from './filter-interfaces';
|
|
3
3
|
import { FieldMetadata } from "../column-interfaces";
|
|
4
|
-
import {
|
|
4
|
+
import { EntityFilter, OmitEB } from "../remult3/remult3";
|
|
5
|
+
import { EntityMetadataOverloads, RepositoryOverloads } from "../remult3";
|
|
5
6
|
export declare class FilterConsumerBridgeToSqlRequest implements FilterConsumer {
|
|
6
7
|
private r;
|
|
7
8
|
private nameProvider;
|
|
@@ -9,7 +10,7 @@ export declare class FilterConsumerBridgeToSqlRequest implements FilterConsumer
|
|
|
9
10
|
_addWhere: boolean;
|
|
10
11
|
promises: Promise<void>[];
|
|
11
12
|
resolveWhere(): Promise<string>;
|
|
12
|
-
constructor(r:
|
|
13
|
+
constructor(r: SqlCommandWithParameters, nameProvider: EntityDbNamesBase);
|
|
13
14
|
custom(key: string, customItem: any): void;
|
|
14
15
|
or(orElements: Filter[]): void;
|
|
15
16
|
isNull(col: FieldMetadata): void;
|
|
@@ -32,19 +33,18 @@ export interface CustomSqlFilterObject {
|
|
|
32
33
|
}
|
|
33
34
|
export declare class CustomSqlFilterBuilder {
|
|
34
35
|
private r;
|
|
35
|
-
constructor(r:
|
|
36
|
+
constructor(r: SqlCommandWithParameters);
|
|
36
37
|
sql: string;
|
|
37
38
|
addParameterAndReturnSqlToken<valueType>(val: valueType, field?: FieldMetadata<valueType>): string;
|
|
39
|
+
filterToRaw<entityType>(repo: RepositoryOverloads<entityType>, condition: EntityFilter<entityType>): Promise<string>;
|
|
38
40
|
}
|
|
39
|
-
export declare function
|
|
40
|
-
export declare
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
isDbReadonly(x: FieldMetadata): boolean;
|
|
50
|
-
}
|
|
41
|
+
export declare function isDbReadonly<entityType>(field: FieldMetadata, dbNames: EntityDbNames<entityType>): boolean;
|
|
42
|
+
export declare type EntityDbNamesBase = {
|
|
43
|
+
$entityName: string;
|
|
44
|
+
$dbNameOf(field: FieldMetadata<any> | string): string;
|
|
45
|
+
toString(): string;
|
|
46
|
+
};
|
|
47
|
+
export declare type EntityDbNames<entityType> = {
|
|
48
|
+
[Properties in keyof Required<OmitEB<entityType>>]: string;
|
|
49
|
+
} & EntityDbNamesBase;
|
|
50
|
+
export declare function dbNamesOf<entityType>(repo: EntityMetadataOverloads<entityType>): Promise<EntityDbNames<entityType>>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getEntityMetadata } from "../remult3";
|
|
2
|
+
import { SqlDatabase } from "../data-providers/sql-database";
|
|
1
3
|
export class FilterConsumerBridgeToSqlRequest {
|
|
2
4
|
constructor(r, nameProvider) {
|
|
3
5
|
this.r = r;
|
|
@@ -44,15 +46,15 @@ export class FilterConsumerBridgeToSqlRequest {
|
|
|
44
46
|
})());
|
|
45
47
|
}
|
|
46
48
|
isNull(col) {
|
|
47
|
-
this.promises.push((async () => this.addToWhere(this.nameProvider
|
|
49
|
+
this.promises.push((async () => this.addToWhere(this.nameProvider.$dbNameOf(col) + ' is null'))());
|
|
48
50
|
}
|
|
49
51
|
isNotNull(col) {
|
|
50
|
-
this.promises.push((async () => this.addToWhere(this.nameProvider
|
|
52
|
+
this.promises.push((async () => this.addToWhere(this.nameProvider.$dbNameOf(col) + ' is not null'))());
|
|
51
53
|
}
|
|
52
54
|
isIn(col, val) {
|
|
53
55
|
this.promises.push((async () => {
|
|
54
56
|
if (val && val.length > 0)
|
|
55
|
-
this.addToWhere(this.nameProvider
|
|
57
|
+
this.addToWhere(this.nameProvider.$dbNameOf(col) + " in (" + val.map(x => this.r.addParameterAndReturnSqlToken(col.valueConverter.toDb(x))).join(",") + ")");
|
|
56
58
|
else
|
|
57
59
|
this.addToWhere('1 = 0 /*isIn with no values*/');
|
|
58
60
|
})());
|
|
@@ -77,12 +79,12 @@ export class FilterConsumerBridgeToSqlRequest {
|
|
|
77
79
|
}
|
|
78
80
|
containsCaseInsensitive(col, val) {
|
|
79
81
|
this.promises.push((async () => {
|
|
80
|
-
this.addToWhere('lower (' + this.nameProvider
|
|
82
|
+
this.addToWhere('lower (' + this.nameProvider.$dbNameOf(col) + ") like lower ('%" + val.replace(/'/g, '\'\'') + "%')");
|
|
81
83
|
})());
|
|
82
84
|
}
|
|
83
85
|
add(col, val, operator) {
|
|
84
86
|
this.promises.push((async () => {
|
|
85
|
-
let x = this.nameProvider
|
|
87
|
+
let x = this.nameProvider.$dbNameOf(col) + ' ' + operator + ' ' + this.r.addParameterAndReturnSqlToken(col.valueConverter.toDb(val));
|
|
86
88
|
this.addToWhere(x);
|
|
87
89
|
})());
|
|
88
90
|
}
|
|
@@ -117,23 +119,29 @@ export class CustomSqlFilterBuilder {
|
|
|
117
119
|
val = field.valueConverter.toDb(val);
|
|
118
120
|
return this.r.addParameterAndReturnSqlToken(val);
|
|
119
121
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
var result = new dbNameProviderImpl();
|
|
123
|
-
for (const f of meta.fields) {
|
|
124
|
-
result.map.set(f.key, await f.getDbName());
|
|
122
|
+
async filterToRaw(repo, condition) {
|
|
123
|
+
return SqlDatabase.filterToRaw(repo, condition, this);
|
|
125
124
|
}
|
|
126
|
-
result.entityName = await meta.getDbName();
|
|
127
|
-
return result;
|
|
128
125
|
}
|
|
129
|
-
export
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
126
|
+
export function isDbReadonly(field, dbNames) {
|
|
127
|
+
return (field.dbReadOnly || field.isServerExpression || dbNames.$dbNameOf(field) != field.options.dbName);
|
|
128
|
+
}
|
|
129
|
+
export async function dbNamesOf(repo) {
|
|
130
|
+
var meta = getEntityMetadata(repo);
|
|
131
|
+
const result = {
|
|
132
|
+
$entityName: await meta.getDbName(),
|
|
133
|
+
toString: () => result.$entityName,
|
|
134
|
+
$dbNameOf: (field) => {
|
|
135
|
+
var key;
|
|
136
|
+
if (typeof field === "string")
|
|
137
|
+
key = field;
|
|
138
|
+
else
|
|
139
|
+
key = field.key;
|
|
140
|
+
return result[key];
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
for (const field of meta.fields) {
|
|
144
|
+
result[field.key] = await field.getDbName();
|
|
138
145
|
}
|
|
146
|
+
return result;
|
|
139
147
|
}
|
|
@@ -6,8 +6,8 @@ export declare class Filter {
|
|
|
6
6
|
constructor(apply?: (add: FilterConsumer) => void);
|
|
7
7
|
__applyToConsumer(add: FilterConsumer): void;
|
|
8
8
|
static resolve<entityType>(filter: EntityFilter<entityType> | (() => EntityFilter<entityType> | Promise<EntityFilter<entityType>>)): Promise<EntityFilter<entityType>>;
|
|
9
|
-
static createCustom<entityType>(
|
|
10
|
-
static createCustom<entityType, argsType>(
|
|
9
|
+
static createCustom<entityType>(rawFilterTranslator: (unused: never, r: Remult) => EntityFilter<entityType> | Promise<EntityFilter<entityType>>, key?: string): (() => EntityFilter<entityType>) & rawFilterInfo<entityType>;
|
|
10
|
+
static createCustom<entityType, argsType>(rawFilterTranslator: (args: argsType, r: Remult) => EntityFilter<entityType> | Promise<EntityFilter<entityType>>, key?: string): ((y: argsType) => EntityFilter<entityType>) & rawFilterInfo<entityType>;
|
|
11
11
|
static fromEntityFilter<T>(entity: EntityMetadata<T>, whereItem: EntityFilter<T>): Filter;
|
|
12
12
|
toJson(): any;
|
|
13
13
|
static entityFilterToJson<T>(entityDefs: EntityMetadata<T>, where: EntityFilter<T>): any;
|
|
@@ -77,9 +77,9 @@ export declare function entityFilterToJson<T>(entityDefs: EntityMetadata<T>, whe
|
|
|
77
77
|
export declare function buildFilterFromRequestParameters(entity: EntityMetadata, filterInfo: {
|
|
78
78
|
get: (key: string) => any;
|
|
79
79
|
}): EntityFilter<any>;
|
|
80
|
-
export interface
|
|
81
|
-
|
|
80
|
+
export interface rawFilterInfo<entityType> {
|
|
81
|
+
rawFilterInfo: {
|
|
82
82
|
key: string;
|
|
83
|
-
|
|
83
|
+
rawFilterTranslator: (args: any, r: Remult) => (EntityFilter<entityType> | Promise<EntityFilter<entityType>>);
|
|
84
84
|
};
|
|
85
85
|
}
|
|
@@ -14,18 +14,18 @@ export class Filter {
|
|
|
14
14
|
return await filter();
|
|
15
15
|
return filter;
|
|
16
16
|
}
|
|
17
|
-
static createCustom(
|
|
18
|
-
let
|
|
17
|
+
static createCustom(rawFilterTranslator, key = '') {
|
|
18
|
+
let rawFilterInfo = { key: key, rawFilterTranslator };
|
|
19
19
|
return Object.assign((x) => {
|
|
20
20
|
let z = {};
|
|
21
21
|
if (x == undefined)
|
|
22
22
|
x = {};
|
|
23
|
-
if (!
|
|
23
|
+
if (!rawFilterInfo.key)
|
|
24
24
|
throw "Usage of custom filter before a key was assigned to it";
|
|
25
25
|
return {
|
|
26
|
-
[customUrlToken +
|
|
26
|
+
[customUrlToken + rawFilterInfo.key]: x
|
|
27
27
|
};
|
|
28
|
-
}, {
|
|
28
|
+
}, { rawFilterInfo });
|
|
29
29
|
}
|
|
30
30
|
static fromEntityFilter(entity, whereItem) {
|
|
31
31
|
let result = [];
|
|
@@ -79,6 +79,7 @@ export class Filter {
|
|
|
79
79
|
break;
|
|
80
80
|
case "$ne":
|
|
81
81
|
case "!=":
|
|
82
|
+
case "$nin":
|
|
82
83
|
found = true;
|
|
83
84
|
if (Array.isArray(element)) {
|
|
84
85
|
result.push(fh.isNotIn(element));
|
|
@@ -86,6 +87,10 @@ export class Filter {
|
|
|
86
87
|
else
|
|
87
88
|
result.push(fh.isDifferentFrom(element));
|
|
88
89
|
break;
|
|
90
|
+
case "$in":
|
|
91
|
+
found = true;
|
|
92
|
+
result.push(fh.isIn(element));
|
|
93
|
+
break;
|
|
89
94
|
case "$contains":
|
|
90
95
|
found = true;
|
|
91
96
|
result.push(fh.contains(element));
|
|
@@ -123,9 +128,9 @@ export class Filter {
|
|
|
123
128
|
let r = [];
|
|
124
129
|
for (const key in entity.entityType) {
|
|
125
130
|
const element = entity.entityType[key];
|
|
126
|
-
if (element && element.
|
|
127
|
-
if (element.
|
|
128
|
-
r.push(await Filter.fromEntityFilter(entity, await element.
|
|
131
|
+
if (element && element.rawFilterInfo && element.rawFilterInfo.rawFilterTranslator) {
|
|
132
|
+
if (element.rawFilterInfo.key == filterKey) {
|
|
133
|
+
r.push(await Filter.fromEntityFilter(entity, await element.rawFilterInfo.rawFilterTranslator(custom, remult)));
|
|
129
134
|
}
|
|
130
135
|
}
|
|
131
136
|
}
|
|
@@ -259,34 +264,34 @@ export class FilterSerializer {
|
|
|
259
264
|
}));
|
|
260
265
|
}
|
|
261
266
|
isNull(col) {
|
|
262
|
-
this.add(col.key + "
|
|
267
|
+
this.add(col.key + ".null", true);
|
|
263
268
|
}
|
|
264
269
|
isNotNull(col) {
|
|
265
|
-
this.add(col.key + "
|
|
270
|
+
this.add(col.key + ".null", false);
|
|
266
271
|
}
|
|
267
272
|
isIn(col, val) {
|
|
268
|
-
this.add(col.key + "
|
|
273
|
+
this.add(col.key + ".in", val.map(x => col.valueConverter.toJson(x)));
|
|
269
274
|
}
|
|
270
275
|
isEqualTo(col, val) {
|
|
271
276
|
this.add(col.key, col.valueConverter.toJson(val));
|
|
272
277
|
}
|
|
273
278
|
isDifferentFrom(col, val) {
|
|
274
|
-
this.add(col.key + '
|
|
279
|
+
this.add(col.key + '.ne', col.valueConverter.toJson(val));
|
|
275
280
|
}
|
|
276
281
|
isGreaterOrEqualTo(col, val) {
|
|
277
|
-
this.add(col.key + '
|
|
282
|
+
this.add(col.key + '.gte', col.valueConverter.toJson(val));
|
|
278
283
|
}
|
|
279
284
|
isGreaterThan(col, val) {
|
|
280
|
-
this.add(col.key + '
|
|
285
|
+
this.add(col.key + '.gt', col.valueConverter.toJson(val));
|
|
281
286
|
}
|
|
282
287
|
isLessOrEqualTo(col, val) {
|
|
283
|
-
this.add(col.key + '
|
|
288
|
+
this.add(col.key + '.lte', col.valueConverter.toJson(val));
|
|
284
289
|
}
|
|
285
290
|
isLessThan(col, val) {
|
|
286
|
-
this.add(col.key + '
|
|
291
|
+
this.add(col.key + '.lt', col.valueConverter.toJson(val));
|
|
287
292
|
}
|
|
288
293
|
containsCaseInsensitive(col, val) {
|
|
289
|
-
this.add(col.key + "
|
|
294
|
+
this.add(col.key + ".contains", val);
|
|
290
295
|
}
|
|
291
296
|
}
|
|
292
297
|
export function entityFilterToJson(entityDefs, where) {
|
|
@@ -337,13 +342,13 @@ export function buildFilterFromRequestParameters(entity, filterInfo) {
|
|
|
337
342
|
}
|
|
338
343
|
let c = new filterHelper(col);
|
|
339
344
|
addFilter('', val => val);
|
|
340
|
-
addFilter('
|
|
341
|
-
addFilter('
|
|
342
|
-
addFilter('
|
|
343
|
-
addFilter('
|
|
344
|
-
addFilter('
|
|
345
|
-
addFilter('
|
|
346
|
-
var nullFilter = filterInfo.get(col.key + "
|
|
345
|
+
addFilter('.gt', val => ({ "$gt": val }));
|
|
346
|
+
addFilter('.gte', val => ({ "$gte": val }));
|
|
347
|
+
addFilter('.lt', val => ({ "$lt": val }));
|
|
348
|
+
addFilter('.lte', val => ({ "$lte": val }));
|
|
349
|
+
addFilter('.ne', val => ({ "$ne": val }));
|
|
350
|
+
addFilter('.in', val => val, true);
|
|
351
|
+
var nullFilter = filterInfo.get(col.key + ".null");
|
|
347
352
|
if (nullFilter) {
|
|
348
353
|
nullFilter = nullFilter.toString().trim().toLowerCase();
|
|
349
354
|
switch (nullFilter) {
|
|
@@ -357,7 +362,7 @@ export function buildFilterFromRequestParameters(entity, filterInfo) {
|
|
|
357
362
|
break;
|
|
358
363
|
}
|
|
359
364
|
}
|
|
360
|
-
addFilter('
|
|
365
|
+
addFilter('.contains', val => ({ $contains: val }), false, true);
|
|
361
366
|
});
|
|
362
367
|
let val = filterInfo.get('OR');
|
|
363
368
|
if (val) {
|
|
@@ -370,7 +375,7 @@ export function buildFilterFromRequestParameters(entity, filterInfo) {
|
|
|
370
375
|
}
|
|
371
376
|
for (const key in entity.entityType) {
|
|
372
377
|
const element = entity.entityType[key];
|
|
373
|
-
if (element && element.
|
|
378
|
+
if (element && element.rawFilterInfo && element.rawFilterInfo.rawFilterTranslator) {
|
|
374
379
|
let custom = filterInfo.get(customUrlToken + key);
|
|
375
380
|
if (custom !== undefined) {
|
|
376
381
|
where.push({ [customUrlToken + key]: custom });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { RestDataProvider } from '../data-providers/rest-data-provider';
|
|
2
|
+
import { buildRestDataProvider } from "../buildRestDataProvider";
|
|
3
|
+
import { LiveQuerySubscriber, liveQueryKeepAliveRoute } from './SubscriptionChannel';
|
|
4
|
+
/* @internal*/
|
|
5
|
+
export class LiveQueryClient {
|
|
6
|
+
constructor(apiProvider, getUserId) {
|
|
7
|
+
this.apiProvider = apiProvider;
|
|
8
|
+
this.getUserId = getUserId;
|
|
9
|
+
this.queries = new Map();
|
|
10
|
+
this.channels = new Map();
|
|
11
|
+
}
|
|
12
|
+
wrapMessageHandling(handleMessage) {
|
|
13
|
+
var x = this.apiProvider().wrapMessageHandling;
|
|
14
|
+
if (x)
|
|
15
|
+
x(handleMessage);
|
|
16
|
+
else
|
|
17
|
+
handleMessage();
|
|
18
|
+
}
|
|
19
|
+
;
|
|
20
|
+
runPromise(p) {
|
|
21
|
+
return p;
|
|
22
|
+
}
|
|
23
|
+
close() {
|
|
24
|
+
this.queries.clear();
|
|
25
|
+
this.channels.clear();
|
|
26
|
+
this.closeIfNoListeners();
|
|
27
|
+
}
|
|
28
|
+
async subscribeChannel(key, onResult) {
|
|
29
|
+
let onUnsubscribe = () => { };
|
|
30
|
+
const client = await this.openIfNoOpened();
|
|
31
|
+
try {
|
|
32
|
+
let q = this.channels.get(key);
|
|
33
|
+
if (!q) {
|
|
34
|
+
this.channels.set(key, q = new MessageChannel());
|
|
35
|
+
try {
|
|
36
|
+
q.unsubscribe = await client.subscribe(key, value => this.wrapMessageHandling(() => q.handle(value)), err => {
|
|
37
|
+
onResult.error(err);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
onResult.error(err);
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
q.listeners.push(onResult);
|
|
46
|
+
onUnsubscribe = () => {
|
|
47
|
+
q.listeners.splice(q.listeners.indexOf(onResult), 1);
|
|
48
|
+
if (q.listeners.length == 0) {
|
|
49
|
+
this.channels.delete(key);
|
|
50
|
+
q.unsubscribe();
|
|
51
|
+
}
|
|
52
|
+
this.closeIfNoListeners();
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
onResult.error(err);
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
return () => {
|
|
60
|
+
onUnsubscribe();
|
|
61
|
+
onUnsubscribe = () => { };
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
closeIfNoListeners() {
|
|
65
|
+
if (this.client)
|
|
66
|
+
if (this.queries.size === 0 && this.channels.size === 0) {
|
|
67
|
+
this.runPromise(this.client.then(x => x.close()));
|
|
68
|
+
this.client = undefined;
|
|
69
|
+
clearInterval(this.interval);
|
|
70
|
+
this.interval = undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
subscribe(repo, options, listener) {
|
|
74
|
+
let alive = true;
|
|
75
|
+
let onUnsubscribe = () => { alive = false; };
|
|
76
|
+
this.runPromise(repo.buildEntityDataProviderFindOptions(options)
|
|
77
|
+
.then(opts => {
|
|
78
|
+
if (!alive)
|
|
79
|
+
return;
|
|
80
|
+
const { createKey, subscribe } = new RestDataProvider(this.apiProvider).getEntityDataProvider(repo.metadata).buildFindRequest(opts);
|
|
81
|
+
const eventTypeKey = createKey();
|
|
82
|
+
let q = this.queries.get(eventTypeKey);
|
|
83
|
+
if (!q) {
|
|
84
|
+
this.queries.set(eventTypeKey, q = new LiveQuerySubscriber(repo, { entityKey: repo.metadata.key, orderBy: options.orderBy }, this.getUserId()));
|
|
85
|
+
q.subscribeCode = () => {
|
|
86
|
+
if (q.unsubscribe) {
|
|
87
|
+
q.unsubscribe();
|
|
88
|
+
q.unsubscribe = () => { };
|
|
89
|
+
}
|
|
90
|
+
this.runPromise(this.subscribeChannel(q.queryChannel, {
|
|
91
|
+
next: (value) => this.runPromise(q.handle(value)),
|
|
92
|
+
complete: () => { },
|
|
93
|
+
error: er => {
|
|
94
|
+
q.listeners.forEach(l => l.error(er));
|
|
95
|
+
}
|
|
96
|
+
}).then(unsubscribeToChannel => {
|
|
97
|
+
if (q.listeners.length == 0) {
|
|
98
|
+
unsubscribeToChannel();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
this.runPromise(subscribe(q.id)
|
|
102
|
+
.then(r => {
|
|
103
|
+
if (q.listeners.length === 0) {
|
|
104
|
+
r.unsubscribe();
|
|
105
|
+
unsubscribeToChannel();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this.runPromise(q.setAllItems(r.result));
|
|
109
|
+
q.unsubscribe = () => {
|
|
110
|
+
q.unsubscribe = () => { };
|
|
111
|
+
unsubscribeToChannel();
|
|
112
|
+
this.runPromise(r.unsubscribe());
|
|
113
|
+
};
|
|
114
|
+
})
|
|
115
|
+
.catch(err => {
|
|
116
|
+
listener.error(err);
|
|
117
|
+
}));
|
|
118
|
+
})).catch(err => {
|
|
119
|
+
q.listeners.forEach(l => l.error(err));
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
q.subscribeCode();
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
q.sendDefaultState(listener.next);
|
|
126
|
+
}
|
|
127
|
+
q.listeners.push(listener);
|
|
128
|
+
onUnsubscribe = () => {
|
|
129
|
+
q.listeners.splice(q.listeners.indexOf(listener), 1);
|
|
130
|
+
listener.complete();
|
|
131
|
+
if (q.listeners.length == 0) {
|
|
132
|
+
this.queries.delete(eventTypeKey);
|
|
133
|
+
q.unsubscribe();
|
|
134
|
+
}
|
|
135
|
+
this.closeIfNoListeners();
|
|
136
|
+
};
|
|
137
|
+
})
|
|
138
|
+
.catch(err => {
|
|
139
|
+
listener.error(err);
|
|
140
|
+
}));
|
|
141
|
+
return () => {
|
|
142
|
+
onUnsubscribe();
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
openIfNoOpened() {
|
|
146
|
+
if (!this.client) {
|
|
147
|
+
this.interval = setInterval(async () => {
|
|
148
|
+
const ids = [];
|
|
149
|
+
for (const q of this.queries.values()) {
|
|
150
|
+
ids.push(q.queryChannel);
|
|
151
|
+
}
|
|
152
|
+
if (ids.length > 0) {
|
|
153
|
+
let p = this.apiProvider();
|
|
154
|
+
let { actionInfo } = await import('../server-action');
|
|
155
|
+
const invalidIds = await this.runPromise(await actionInfo.runActionWithoutBlockingUI(() => buildRestDataProvider(p.httpClient).post(p.url + '/' + liveQueryKeepAliveRoute, ids)));
|
|
156
|
+
for (const id of invalidIds) {
|
|
157
|
+
for (const q of this.queries.values()) {
|
|
158
|
+
if (q.queryChannel === id)
|
|
159
|
+
q.subscribeCode();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}, 30000);
|
|
164
|
+
return this.runPromise(this.client =
|
|
165
|
+
this.apiProvider().subscriptionClient.openConnection(() => {
|
|
166
|
+
for (const q of this.queries.values()) {
|
|
167
|
+
q.subscribeCode();
|
|
168
|
+
}
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
return this.client;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
class MessageChannel {
|
|
175
|
+
constructor() {
|
|
176
|
+
this.unsubscribe = () => { };
|
|
177
|
+
this.listeners = [];
|
|
178
|
+
}
|
|
179
|
+
async handle(message) {
|
|
180
|
+
for (const l of this.listeners) {
|
|
181
|
+
l.next(message);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { SubscriptionClient, SubscriptionClientConnection } from "./SubscriptionChannel";
|
|
2
|
+
export declare class SseSubscriptionClient implements SubscriptionClient {
|
|
3
|
+
openConnection(onReconnect: VoidFunction): Promise<SubscriptionClientConnection>;
|
|
4
|
+
}
|
|
5
|
+
export declare const ConnectionNotFoundError = "client connection not found";
|