remult 0.17.0-exp.1 → 0.17.0-exp.3
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/esm/postgres/schema-builder.js +4 -4
- package/esm/remult-express.d.ts +2 -2
- package/esm/remult-express.js +7 -9
- package/esm/remult-knex/index.js +2 -2
- package/esm/server/expressBridge.d.ts +2 -2
- package/esm/server/expressBridge.js +2 -2
- package/esm/server/initAsyncHooks.js +0 -1
- package/esm/src/context.d.ts +1 -1
- package/esm/src/context.js +1 -1
- package/esm/src/data-api.js +18 -8
- package/esm/src/data-providers/sql-database.js +1 -1
- package/esm/src/data-providers/web-sql-data-provider.js +1 -1
- package/esm/src/live-query/EventSourceLiveQueryProvider.d.ts +2 -2
- package/esm/src/live-query/EventSourceLiveQueryProvider.js +11 -4
- package/esm/src/live-query/LiveQuery.d.ts +1 -2
- package/esm/src/live-query/LiveQuery.js +22 -24
- package/esm/src/live-query/LiveQueryManager.d.ts +1 -2
- package/esm/src/live-query/LiveQueryManager.js +18 -16
- package/esm/src/live-query/LiveQueryPublisher.d.ts +33 -0
- package/esm/src/live-query/LiveQueryPublisher.js +102 -0
- package/esm/src/live-query/LiveQuerySubscriber.d.ts +67 -0
- package/esm/src/live-query/LiveQuerySubscriber.js +238 -0
- package/esm/src/remult3/RepositoryImplementation.js +6 -11
- package/esm/src/remult3/getId.d.ts +2 -0
- package/esm/src/remult3/getId.js +7 -0
- package/esm/src/server-action.d.ts +4 -0
- package/esm/src/server-action.js +1 -1
- package/esm/src/tests/testHelper.spec.d.ts +2 -0
- package/esm/src/tests/testHelper.spec.js +60 -57
- package/esm/src/valueConverters.js +1 -1
- package/package.json +1 -1
- package/postgres/schema-builder.js +4 -4
- package/remult-express.d.ts +2 -2
- package/remult-express.js +8 -10
- package/remult-knex/index.js +2 -2
- package/server/expressBridge.d.ts +2 -2
- package/server/expressBridge.js +2 -2
- package/server/initAsyncHooks.js +0 -1
- package/src/context.d.ts +1 -1
- package/src/context.js +2 -2
- package/src/data-api.js +43 -19
- package/src/data-providers/sql-database.js +1 -1
- package/src/data-providers/web-sql-data-provider.js +1 -1
- package/src/live-query/EventSourceLiveQueryProvider.d.ts +2 -2
- package/src/live-query/EventSourceLiveQueryProvider.js +12 -5
- package/src/live-query/LiveQuery.d.ts +1 -2
- package/src/live-query/LiveQuery.js +36 -38
- package/src/live-query/LiveQueryManager.d.ts +1 -2
- package/src/live-query/LiveQueryManager.js +64 -62
- package/src/live-query/LiveQueryPublisher.d.ts +33 -0
- package/src/live-query/LiveQueryPublisher.js +201 -0
- package/src/live-query/LiveQuerySubscriber.d.ts +67 -0
- package/src/live-query/LiveQuerySubscriber.js +332 -0
- package/src/remult3/RepositoryImplementation.js +6 -11
- package/src/remult3/getId.d.ts +2 -0
- package/src/remult3/getId.js +11 -0
- package/src/server-action.d.ts +4 -0
- package/src/server-action.js +2 -1
- package/src/tests/testHelper.spec.d.ts +2 -0
- package/src/tests/testHelper.spec.js +99 -95
- package/src/valueConverters.js +1 -1
|
@@ -43,7 +43,7 @@ export class PostgresSchemaBuilder {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
async verifyStructureOfAllEntities(remult) {
|
|
46
|
-
console.log("start verify structure");
|
|
46
|
+
console.log("start verify structure"); //keep me
|
|
47
47
|
for (const entityClass of allEntities) {
|
|
48
48
|
let entity = remult.repo(entityClass).metadata;
|
|
49
49
|
let e = await getDbNameProvider(entity);
|
|
@@ -56,7 +56,7 @@ export class PostgresSchemaBuilder {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
catch (err) {
|
|
59
|
-
console.log("failed verify structure of " + e.entityName + " ", err);
|
|
59
|
+
/*keep me */ console.log("failed verify structure of " + e.entityName + " ", err);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -102,7 +102,7 @@ export class PostgresSchemaBuilder {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
catch (err) {
|
|
105
|
-
console.
|
|
105
|
+
console.error(err);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
async verifyAllColumns(entity) {
|
|
@@ -122,7 +122,7 @@ export class PostgresSchemaBuilder {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
catch (err) {
|
|
125
|
-
console.
|
|
125
|
+
console.error(err);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
}
|
package/esm/remult-express.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as express from 'express';
|
|
2
2
|
import { RemultServer, RemultServerOptions } from './server/expressBridge';
|
|
3
3
|
import { Remult } from './src/context';
|
|
4
|
-
import { AMessageChannel, ChannelSubscribe } from './src/live-query/
|
|
5
|
-
import { ServerEventDispatcher, ServerEventMessage } from './src/live-query/
|
|
4
|
+
import { AMessageChannel, ChannelSubscribe } from './src/live-query/LiveQuerySubscriber';
|
|
5
|
+
import { ServerEventDispatcher, ServerEventMessage } from './src/live-query/LiveQueryPublisher';
|
|
6
6
|
export declare function remultExpress(options?: RemultServerOptions<express.Request> & {
|
|
7
7
|
bodyParser?: boolean;
|
|
8
8
|
bodySizeLimit?: string;
|
package/esm/remult-express.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as express from 'express';
|
|
2
2
|
import { createRemultServer } from './server/expressBridge';
|
|
3
|
-
import { streamUrl } from './src/live-query/
|
|
4
|
-
import {
|
|
5
|
-
import { remult } from './src/remult-proxy';
|
|
3
|
+
import { streamUrl } from './src/live-query/LiveQuerySubscriber';
|
|
4
|
+
import { LiveQueryPublisher } from './src/live-query/LiveQueryPublisher';
|
|
6
5
|
export function remultExpress(options) {
|
|
7
6
|
let app = express.Router();
|
|
8
7
|
if (!options) {
|
|
@@ -18,13 +17,15 @@ export function remultExpress(options) {
|
|
|
18
17
|
const server = createRemultServer(options);
|
|
19
18
|
server.registerRouter(app);
|
|
20
19
|
let httpServerEvents = new ServerEventsController(options.messageChannels);
|
|
21
|
-
server.liveQueryManager = new
|
|
20
|
+
server.liveQueryManager = new LiveQueryPublisher(httpServerEvents);
|
|
22
21
|
const streamPath = options.rootPath + '/' + streamUrl;
|
|
23
22
|
app.get(streamPath, (req, res) => {
|
|
24
23
|
httpServerEvents.openHttpServerStream(req, res);
|
|
25
24
|
});
|
|
26
25
|
app.post(streamPath, (r, res, next) => server.withRemult(r, res, next), (req, res) => {
|
|
27
|
-
|
|
26
|
+
server.liveQueryManager.unsubscribe(req.body);
|
|
27
|
+
res.json("ok");
|
|
28
|
+
//httpServerEvents.subscribeToChannel(remult, req.body, res)
|
|
28
29
|
});
|
|
29
30
|
app.get(streamPath + '/stats', (req, res) => {
|
|
30
31
|
httpServerEvents.consoleInfo();
|
|
@@ -50,7 +51,7 @@ export class ServerEventsController {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
consoleInfo() {
|
|
53
|
-
console.
|
|
54
|
+
console.info(this.connections.map(x => ({
|
|
54
55
|
client: x.clientId,
|
|
55
56
|
channels: x.channels
|
|
56
57
|
})));
|
|
@@ -111,7 +112,6 @@ export class ServerEventsController {
|
|
|
111
112
|
z.messages.push({ id, message: message, eventType });
|
|
112
113
|
while (z.messages.length > z.messageHistoryLength)
|
|
113
114
|
z.messages.shift();
|
|
114
|
-
//console.log({ sendingTo: z.connections.length })
|
|
115
115
|
z.connections.forEach(y => y.write(id, message, eventType));
|
|
116
116
|
}
|
|
117
117
|
}
|
|
@@ -121,12 +121,10 @@ class clientConnection {
|
|
|
121
121
|
this.clientId = clientId;
|
|
122
122
|
this.channels = {};
|
|
123
123
|
this.closed = false;
|
|
124
|
-
//console.log("open connection");
|
|
125
124
|
response.write("event:authenticate\ndata:" + "key" + "\n\n");
|
|
126
125
|
this.sendLiveMessage();
|
|
127
126
|
}
|
|
128
127
|
close() {
|
|
129
|
-
//console.log("close connection");
|
|
130
128
|
this.closed = true;
|
|
131
129
|
}
|
|
132
130
|
write(id, message, eventType) {
|
package/esm/remult-knex/index.js
CHANGED
|
@@ -256,7 +256,7 @@ export class KnexSchemaBuilder {
|
|
|
256
256
|
this.additionalWhere = '';
|
|
257
257
|
}
|
|
258
258
|
async verifyStructureOfAllEntities(remult) {
|
|
259
|
-
console.
|
|
259
|
+
console.info("start verify structure");
|
|
260
260
|
for (const entity of allEntities) {
|
|
261
261
|
let metadata = remult.repo(entity).metadata;
|
|
262
262
|
try {
|
|
@@ -319,7 +319,7 @@ export class KnexSchemaBuilder {
|
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
catch (err) {
|
|
322
|
-
console.
|
|
322
|
+
console.error(err);
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
325
|
}
|
|
@@ -5,7 +5,7 @@ import { AllowedForInstance, Remult, UserInfo } from '../src/context';
|
|
|
5
5
|
import { ClassType } from '../classType';
|
|
6
6
|
import { Repository } from '../src/remult3';
|
|
7
7
|
import { IdEntity } from '../src/id-entity';
|
|
8
|
-
import {
|
|
8
|
+
import { LiveQueryPublisher } from '../src/live-query/LiveQueryPublisher';
|
|
9
9
|
export interface RemultServerOptions<RequestType extends GenericRequest> {
|
|
10
10
|
/** Sets a database connection for Remult.
|
|
11
11
|
*
|
|
@@ -64,7 +64,7 @@ export declare class RemultServerImplementation implements RemultServer {
|
|
|
64
64
|
options: RemultServerOptions<GenericRequest>;
|
|
65
65
|
dataProvider: DataProvider | Promise<DataProvider>;
|
|
66
66
|
constructor(queue: inProcessQueueHandler, options: RemultServerOptions<GenericRequest>, dataProvider: DataProvider | Promise<DataProvider>);
|
|
67
|
-
liveQueryManager:
|
|
67
|
+
liveQueryManager: LiveQueryPublisher;
|
|
68
68
|
withRemult<T>(req: GenericRequest, res: GenericResponse, next: VoidFunction): void;
|
|
69
69
|
routeImpl: RouteImplementation;
|
|
70
70
|
handle(req: GenericRequest, gRes?: GenericResponse): Promise<ServerHandleResponse>;
|
|
@@ -162,7 +162,7 @@ export class RemultServerImplementation {
|
|
|
162
162
|
add(key, dataApiFactory, r) {
|
|
163
163
|
let myRoute = this.options.rootPath + '/' + key;
|
|
164
164
|
if (this.options.logApiEndPoints)
|
|
165
|
-
console.
|
|
165
|
+
console.info("[remult] " + myRoute);
|
|
166
166
|
r.route(myRoute)
|
|
167
167
|
.get(this.process((c, req, res) => dataApiFactory(c).httpGet(res, req))).put(this.process(async (c, req, res, orig) => dataApiFactory(c).put(res, '', orig.body)))
|
|
168
168
|
.delete(this.process(async (c, req, res, orig) => dataApiFactory(c).delete(res, '')))
|
|
@@ -225,7 +225,7 @@ export class RemultServerImplementation {
|
|
|
225
225
|
})();
|
|
226
226
|
this.backendMethodsOpenApi.push({ path: myUrl, allowed, tag });
|
|
227
227
|
if (this.options.logApiEndPoints)
|
|
228
|
-
console.
|
|
228
|
+
console.info("[remult] " + myUrl);
|
|
229
229
|
if (queue) {
|
|
230
230
|
this.hasQueue = true;
|
|
231
231
|
this.queue.mapQueuedAction(myUrl, what);
|
package/esm/src/context.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { DataProvider, RestDataProviderHttpProvider } from "./data-interfaces";
|
|
|
2
2
|
import { RestDataProviderHttpProviderUsingFetch } from './data-providers/rest-data-provider';
|
|
3
3
|
import { EntityMetadata, EntityRef, FindOptions, Repository } from "./remult3";
|
|
4
4
|
import { ClassType } from "../classType";
|
|
5
|
-
import { LiveQueryClient } from "./live-query/
|
|
5
|
+
import { LiveQueryClient } from "./live-query/LiveQuerySubscriber";
|
|
6
6
|
export interface ExternalHttpProvider {
|
|
7
7
|
post(url: string, data: any): Promise<any> | {
|
|
8
8
|
toPromise(): Promise<any>;
|
package/esm/src/context.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { actionInfo, serverActionField } from './server-action';
|
|
2
2
|
import { RestDataProvider, RestDataProviderHttpProviderUsingFetch } from './data-providers/rest-data-provider';
|
|
3
3
|
import { RepositoryImplementation } from "./remult3/RepositoryImplementation";
|
|
4
|
-
import { LiveQueryClient } from "./live-query/
|
|
4
|
+
import { LiveQueryClient } from "./live-query/LiveQuerySubscriber";
|
|
5
5
|
import { EventSourceLiveQueryProvider } from "./live-query/EventSourceLiveQueryProvider";
|
|
6
6
|
import { RemultProxy } from "./remult-proxy";
|
|
7
7
|
export class HttpProviderBridgeToRestDataProviderHttpProvider {
|
package/esm/src/data-api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { customUrlToken, buildFilterFromRequestParameters } from './filter/filter-interfaces';
|
|
2
2
|
import { Filter } from './filter/filter-interfaces';
|
|
3
|
+
import { ForbiddenError } from './server-action';
|
|
3
4
|
export class DataApi {
|
|
4
5
|
constructor(repository, remult, liveQueryProvider) {
|
|
5
6
|
this.repository = repository;
|
|
@@ -175,17 +176,26 @@ export class DataApi {
|
|
|
175
176
|
}
|
|
176
177
|
async post(response, body) {
|
|
177
178
|
try {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
179
|
+
const insert = async (what) => {
|
|
180
|
+
let newr = this.repository.create();
|
|
181
|
+
await this.repository.getEntityRef(newr)._updateEntityBasedOnApi(what);
|
|
182
|
+
if (!this.repository.getEntityRef(newr).apiInsertAllowed) {
|
|
183
|
+
throw new ForbiddenError();
|
|
184
|
+
}
|
|
185
|
+
await this.repository.getEntityRef(newr).save();
|
|
186
|
+
return this.repository.getEntityRef(newr).toApiJson();
|
|
187
|
+
};
|
|
188
|
+
if (Array.isArray(body)) {
|
|
189
|
+
response.created(await Promise.all(body.map(x => insert(x))));
|
|
183
190
|
}
|
|
184
|
-
|
|
185
|
-
|
|
191
|
+
else
|
|
192
|
+
response.created(await insert(body));
|
|
186
193
|
}
|
|
187
194
|
catch (err) {
|
|
188
|
-
|
|
195
|
+
if (err.isForbiddenError)
|
|
196
|
+
response.forbidden();
|
|
197
|
+
else
|
|
198
|
+
response.error(err);
|
|
189
199
|
}
|
|
190
200
|
}
|
|
191
201
|
}
|
|
@@ -76,7 +76,7 @@ class LogSQLCommand {
|
|
|
76
76
|
if (this.allQueries) {
|
|
77
77
|
var d = new Date().valueOf() - start.valueOf();
|
|
78
78
|
if (d > SqlDatabase.durationThreshold) {
|
|
79
|
-
console.
|
|
79
|
+
console.info({ query: sql, arguments: this.args, duration: d / 1000 });
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
return r;
|
|
@@ -18,7 +18,7 @@ export class WebSqlDataProvider {
|
|
|
18
18
|
let e = await getDbNameProvider(entity);
|
|
19
19
|
let sql = 'drop table if exists ' + e.entityName;
|
|
20
20
|
if (SqlDatabase.LogToConsole)
|
|
21
|
-
console.
|
|
21
|
+
console.info(sql);
|
|
22
22
|
await this.createCommand().execute(sql);
|
|
23
23
|
}
|
|
24
24
|
async createTable(entity) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { LiveQueryProvider, MessageHandler } from "./
|
|
1
|
+
import { LiveQueryProvider, MessageHandler } from "./LiveQuerySubscriber";
|
|
2
2
|
export declare class EventSourceLiveQueryProvider implements LiveQueryProvider {
|
|
3
3
|
private wrapMessage?;
|
|
4
4
|
constructor(wrapMessage?: (what: () => void) => void);
|
|
5
|
-
openStreamAndReturnCloseFunction(clientId: string, onMessage: MessageHandler): Promise<VoidFunction>;
|
|
5
|
+
openStreamAndReturnCloseFunction(clientId: string, onMessage: MessageHandler, onReconnect: VoidFunction): Promise<VoidFunction>;
|
|
6
6
|
}
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import { remult } from "../remult-proxy";
|
|
2
|
-
import { streamUrl } from "./
|
|
2
|
+
import { streamUrl } from "./LiveQuerySubscriber";
|
|
3
3
|
export class EventSourceLiveQueryProvider {
|
|
4
4
|
constructor(wrapMessage) {
|
|
5
5
|
this.wrapMessage = wrapMessage;
|
|
6
|
-
if (!wrapMessage)
|
|
7
|
-
wrapMessage = x => x();
|
|
6
|
+
if (!this.wrapMessage)
|
|
7
|
+
this.wrapMessage = x => x();
|
|
8
8
|
}
|
|
9
|
-
openStreamAndReturnCloseFunction(clientId, onMessage) {
|
|
9
|
+
openStreamAndReturnCloseFunction(clientId, onMessage, onReconnect) {
|
|
10
10
|
const source = new EventSource(remult.apiClient.url + '/' + streamUrl + "?id=" + clientId, {
|
|
11
11
|
withCredentials: true
|
|
12
12
|
});
|
|
13
13
|
source.onmessage = e => {
|
|
14
14
|
this.wrapMessage(() => onMessage(JSON.parse(e.data)));
|
|
15
15
|
};
|
|
16
|
+
source.onerror = e => {
|
|
17
|
+
console.error("Live Query Event Source Error", e);
|
|
18
|
+
};
|
|
16
19
|
return new Promise((res) => {
|
|
20
|
+
let connected = false;
|
|
17
21
|
source.onopen = e => {
|
|
22
|
+
if (connected)
|
|
23
|
+
onReconnect();
|
|
24
|
+
connected = true;
|
|
18
25
|
res(() => {
|
|
19
26
|
source.close();
|
|
20
27
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EntityOrderBy, FindOptions, Remult, Repository, RestDataProviderHttpProvider } from '../../index';
|
|
2
2
|
import { Allowed } from '../context';
|
|
3
|
-
import { ServerEventDispatcher } from './
|
|
3
|
+
import { ServerEventDispatcher } from './LiveQueryPublisher';
|
|
4
4
|
export declare const streamUrl = "stream1";
|
|
5
5
|
export interface LiveQueryProvider {
|
|
6
6
|
openStreamAndReturnCloseFunction(clientId: string, onMessage: MessageHandler): Promise<VoidFunction>;
|
|
@@ -23,7 +23,6 @@ export declare class LiveQueryClient {
|
|
|
23
23
|
subscribe<entityType>(repo: Repository<entityType>, options: FindOptions<entityType>, onResult: (reducer: (prevState: entityType[]) => entityType[]) => void): () => void;
|
|
24
24
|
closeListener: Promise<VoidFunction>;
|
|
25
25
|
private openIfNoOpened;
|
|
26
|
-
private openListener;
|
|
27
26
|
}
|
|
28
27
|
export declare type listener = (message: any) => void;
|
|
29
28
|
export interface SubscribeToQueryArgs<entityType = any> {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { remult as defaultRemult, Sort } from '../../index';
|
|
2
2
|
import { v4 as uuid } from 'uuid';
|
|
3
3
|
import { RestEntityDataProvider } from '../data-providers/rest-data-provider';
|
|
4
4
|
import { buildRestDataProvider } from '../context';
|
|
5
|
+
import { getId } from '../remult3/getId';
|
|
5
6
|
export const streamUrl = 'stream1';
|
|
6
|
-
class
|
|
7
|
+
class LiveQuerySubscriber {
|
|
7
8
|
constructor(repo, query) {
|
|
8
9
|
this.repo = repo;
|
|
9
10
|
this.query = query;
|
|
10
|
-
this.
|
|
11
|
+
this.defaultQueryState = [];
|
|
11
12
|
this.listeners = [];
|
|
12
13
|
}
|
|
13
14
|
async setAllItems(result) {
|
|
@@ -19,6 +20,7 @@ class LiveQueryOnFrontEnd {
|
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
22
|
forListeners(what) {
|
|
23
|
+
// what(x => this.defaultQueryState = x(this.defaultQueryState));
|
|
22
24
|
for (const l of this.listeners) {
|
|
23
25
|
what(l);
|
|
24
26
|
}
|
|
@@ -41,7 +43,7 @@ class LiveQueryOnFrontEnd {
|
|
|
41
43
|
listener(items => {
|
|
42
44
|
if (!items)
|
|
43
45
|
items = [];
|
|
44
|
-
return sort(items.map(x => this.repo.
|
|
46
|
+
return sort(items.map(x => getId(this.repo.metadata, x) === message.data.oldId ? item : x));
|
|
45
47
|
});
|
|
46
48
|
});
|
|
47
49
|
break;
|
|
@@ -52,6 +54,7 @@ class LiveQueryOnFrontEnd {
|
|
|
52
54
|
this.forListeners(listener => listener(items => {
|
|
53
55
|
if (!items)
|
|
54
56
|
items = [];
|
|
57
|
+
items = items.filter(x => getId(this.repo.metadata, x) !== getId(this.repo.metadata, item));
|
|
55
58
|
items.push(item);
|
|
56
59
|
return sort(items);
|
|
57
60
|
}));
|
|
@@ -61,7 +64,7 @@ class LiveQueryOnFrontEnd {
|
|
|
61
64
|
this.forListeners(listener => listener(items => {
|
|
62
65
|
if (!items)
|
|
63
66
|
items = [];
|
|
64
|
-
return
|
|
67
|
+
return items.filter(x => getId(this.repo.metadata, x) !== message.data.id);
|
|
65
68
|
}));
|
|
66
69
|
break;
|
|
67
70
|
}
|
|
@@ -130,7 +133,7 @@ export class LiveQueryClient {
|
|
|
130
133
|
subscribe(repo, options, onResult) {
|
|
131
134
|
let alive = true;
|
|
132
135
|
let onUnsubscribe = () => { };
|
|
133
|
-
this.runPromise(this.
|
|
136
|
+
this.runPromise(this.openIfNoOpened().then(() => repo.buildEntityDataProviderFindOptions(options)
|
|
134
137
|
.then(opts => {
|
|
135
138
|
if (!alive)
|
|
136
139
|
return;
|
|
@@ -139,7 +142,7 @@ export class LiveQueryClient {
|
|
|
139
142
|
const eventTypeKey = JSON.stringify({ url, filterObject });
|
|
140
143
|
let q = this.queries.get(eventTypeKey);
|
|
141
144
|
if (!q) {
|
|
142
|
-
this.queries.set(eventTypeKey, q = new
|
|
145
|
+
this.queries.set(eventTypeKey, q = new LiveQuerySubscriber(repo, { entityKey: repo.metadata.key, orderBy: options.orderBy }));
|
|
143
146
|
url.add("__action", 'subscribe|' + this.clientId);
|
|
144
147
|
const thenResult = (r) => {
|
|
145
148
|
this.runPromise(q.setAllItems(r.result));
|
|
@@ -152,7 +155,7 @@ export class LiveQueryClient {
|
|
|
152
155
|
this.runPromise(this.provider.get(url.url).then(thenResult));
|
|
153
156
|
}
|
|
154
157
|
else {
|
|
155
|
-
onResult(x => q.
|
|
158
|
+
onResult(x => [...q.defaultQueryState]);
|
|
156
159
|
}
|
|
157
160
|
q.listeners.push(onResult);
|
|
158
161
|
onUnsubscribe = () => {
|
|
@@ -173,23 +176,18 @@ export class LiveQueryClient {
|
|
|
173
176
|
this.provider = buildRestDataProvider(defaultRemult.apiClient.httpClient);
|
|
174
177
|
}
|
|
175
178
|
if (!this.closeListener)
|
|
176
|
-
return this.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return this.closeListener;
|
|
182
|
-
return this.runPromise(this.closeListener = this.lqp.openStreamAndReturnCloseFunction(this.clientId, message => {
|
|
183
|
-
for (const q of this.queries.values()) {
|
|
184
|
-
if (q.id === message.event) {
|
|
185
|
-
this.runPromise(q.handle(message.data));
|
|
179
|
+
return this.runPromise(this.closeListener = this.lqp.openStreamAndReturnCloseFunction(this.clientId, message => {
|
|
180
|
+
for (const q of this.queries.values()) {
|
|
181
|
+
if (q.id === message.event) {
|
|
182
|
+
this.runPromise(q.handle(message.data));
|
|
183
|
+
}
|
|
186
184
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
185
|
+
const channel = this.channels.get(message.event);
|
|
186
|
+
if (channel) {
|
|
187
|
+
channel.handle(message.data);
|
|
188
|
+
}
|
|
189
|
+
}));
|
|
190
|
+
return this.closeListener;
|
|
193
191
|
}
|
|
194
192
|
}
|
|
195
193
|
export class AMessageChannel {
|
|
@@ -2,12 +2,11 @@ import { Remult } from '../..';
|
|
|
2
2
|
import { Repository, EntityRef, FindOptions } from '../remult3';
|
|
3
3
|
import { LiveQueryProvider } from '../data-api';
|
|
4
4
|
import { liveQueryMessage } from './LiveQuery';
|
|
5
|
-
export declare class
|
|
5
|
+
export declare class LiveQueryPublisher implements LiveQueryProvider {
|
|
6
6
|
private dispatcher;
|
|
7
7
|
constructor(dispatcher: ServerEventDispatcher);
|
|
8
8
|
subscribe(repo: Repository<any>, clientId: string, findOptions: FindOptions<any>, remult: Remult, ids: any[]): string;
|
|
9
9
|
clients: clientInfo[];
|
|
10
|
-
sendMessage(key: string, message: liveQueryMessage): void;
|
|
11
10
|
hasListeners(ref: EntityRef<any>): boolean;
|
|
12
11
|
runPromise(p: Promise<any>): void;
|
|
13
12
|
saved(ref: EntityRef<any>): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { v4 as uuid } from 'uuid';
|
|
2
|
-
export class
|
|
2
|
+
export class LiveQueryPublisher {
|
|
3
3
|
constructor(dispatcher) {
|
|
4
4
|
this.dispatcher = dispatcher;
|
|
5
5
|
this.clients = [];
|
|
@@ -19,15 +19,6 @@ export class LiveQueryManager {
|
|
|
19
19
|
});
|
|
20
20
|
return id;
|
|
21
21
|
}
|
|
22
|
-
sendMessage(key, message) {
|
|
23
|
-
for (const c of this.clients) {
|
|
24
|
-
for (const q of c.queries) {
|
|
25
|
-
if (q.repo.metadata.key === key) {
|
|
26
|
-
this.dispatcher.sendQueryMessage({ clientId: c.clientId, queryId: q.id, message });
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
22
|
hasListeners(ref) {
|
|
32
23
|
for (const c of this.clients) {
|
|
33
24
|
for (const q of c.queries) {
|
|
@@ -87,11 +78,22 @@ export class LiveQueryManager {
|
|
|
87
78
|
}
|
|
88
79
|
}
|
|
89
80
|
deleted(ref) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
const id = ref.getOriginalId();
|
|
82
|
+
for (const c of this.clients) {
|
|
83
|
+
for (const q of c.queries) {
|
|
84
|
+
if (q.repo.metadata.key === ref.metadata.key) {
|
|
85
|
+
if (q.ids.includes(id)) {
|
|
86
|
+
this.dispatcher.sendQueryMessage({
|
|
87
|
+
clientId: c.clientId, queryId: q.id, message: {
|
|
88
|
+
type: "remove",
|
|
89
|
+
data: {
|
|
90
|
+
id: id
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
96
98
|
}
|
|
97
99
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Remult } from '../..';
|
|
2
|
+
import { Repository, EntityRef, FindOptions } from '../remult3';
|
|
3
|
+
import { LiveQueryProvider } from '../data-api';
|
|
4
|
+
import { ChannelSubscribe, liveQueryMessage } from './LiveQuerySubscriber';
|
|
5
|
+
export declare class LiveQueryPublisher implements LiveQueryProvider {
|
|
6
|
+
private dispatcher;
|
|
7
|
+
unsubscribe(data: ChannelSubscribe): void;
|
|
8
|
+
constructor(dispatcher: ServerEventDispatcher);
|
|
9
|
+
subscribe(repo: Repository<any>, clientId: string, findOptions: FindOptions<any>, remult: Remult, ids: any[]): string;
|
|
10
|
+
clients: clientInfo[];
|
|
11
|
+
hasListeners(ref: EntityRef<any>): boolean;
|
|
12
|
+
runPromise(p: Promise<any>): void;
|
|
13
|
+
saved(ref: EntityRef<any>): void;
|
|
14
|
+
deleted(ref: EntityRef<any>): void;
|
|
15
|
+
}
|
|
16
|
+
export interface clientInfo {
|
|
17
|
+
clientId: string;
|
|
18
|
+
queries: ({
|
|
19
|
+
id: string;
|
|
20
|
+
repo: Repository<any>;
|
|
21
|
+
findOptions: FindOptions<any>;
|
|
22
|
+
ids: any[];
|
|
23
|
+
})[];
|
|
24
|
+
}
|
|
25
|
+
export interface ServerEventDispatcher {
|
|
26
|
+
sendQueryMessage(message: ServerEventMessage): void;
|
|
27
|
+
sendChannelMessage<T>(channel: string, message: any): void;
|
|
28
|
+
}
|
|
29
|
+
export interface ServerEventMessage {
|
|
30
|
+
clientId: string;
|
|
31
|
+
queryId: string;
|
|
32
|
+
message: liveQueryMessage;
|
|
33
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { v4 as uuid } from 'uuid';
|
|
2
|
+
export class LiveQueryPublisher {
|
|
3
|
+
constructor(dispatcher) {
|
|
4
|
+
this.dispatcher = dispatcher;
|
|
5
|
+
this.clients = [];
|
|
6
|
+
}
|
|
7
|
+
unsubscribe(data) {
|
|
8
|
+
let client = this.clients.find(c => c.clientId === data.clientId);
|
|
9
|
+
if (client)
|
|
10
|
+
client.queries = client.queries.filter(q => q.id !== data.channel);
|
|
11
|
+
}
|
|
12
|
+
subscribe(repo, clientId, findOptions, remult, ids) {
|
|
13
|
+
let client = this.clients.find(c => c.clientId === clientId);
|
|
14
|
+
if (!client) {
|
|
15
|
+
this.clients.push(client = { clientId: clientId, queries: [] });
|
|
16
|
+
}
|
|
17
|
+
const id = uuid();
|
|
18
|
+
client.queries.push({
|
|
19
|
+
id,
|
|
20
|
+
findOptions: findOptions,
|
|
21
|
+
repo,
|
|
22
|
+
ids
|
|
23
|
+
});
|
|
24
|
+
return id;
|
|
25
|
+
}
|
|
26
|
+
hasListeners(ref) {
|
|
27
|
+
for (const c of this.clients) {
|
|
28
|
+
for (const q of c.queries) {
|
|
29
|
+
if (q.repo.metadata.key === ref.metadata.key)
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
runPromise(p) {
|
|
36
|
+
}
|
|
37
|
+
//TODO - reconsider usage of zone
|
|
38
|
+
saved(ref) {
|
|
39
|
+
const isNew = ref.isNew();
|
|
40
|
+
const origId = isNew ? ref.getId() : ref.getOriginalId();
|
|
41
|
+
for (const c of this.clients) {
|
|
42
|
+
for (const q of c.queries) {
|
|
43
|
+
if (q.repo.metadata.key === ref.metadata.key) {
|
|
44
|
+
this.runPromise(q.repo.find(q.findOptions).then(currentItems => {
|
|
45
|
+
const currentIds = currentItems.map(x => q.repo.getEntityRef(x).getId());
|
|
46
|
+
const sendMessage = (message) => {
|
|
47
|
+
this.dispatcher.sendQueryMessage({ clientId: c.clientId, queryId: q.id, message });
|
|
48
|
+
};
|
|
49
|
+
for (const id of q.ids.filter(y => !currentIds.includes(y))) {
|
|
50
|
+
if (id != origId || !currentIds.includes(ref.getId()))
|
|
51
|
+
sendMessage({
|
|
52
|
+
type: "remove",
|
|
53
|
+
data: {
|
|
54
|
+
id: id
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
for (const item of currentItems) {
|
|
59
|
+
const itemRef = q.repo.getEntityRef(item);
|
|
60
|
+
if (itemRef.getId() == ref.getId() && q.ids.includes(origId)) {
|
|
61
|
+
sendMessage({
|
|
62
|
+
type: "replace",
|
|
63
|
+
data: {
|
|
64
|
+
oldId: origId,
|
|
65
|
+
item: itemRef.toApiJson()
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else if (!q.ids.includes(itemRef.getId())) {
|
|
70
|
+
sendMessage({
|
|
71
|
+
type: "add",
|
|
72
|
+
data: { item: itemRef.toApiJson() }
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
q.ids = currentIds;
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
deleted(ref) {
|
|
83
|
+
const id = ref.getOriginalId();
|
|
84
|
+
for (const c of this.clients) {
|
|
85
|
+
for (const q of c.queries) {
|
|
86
|
+
if (q.repo.metadata.key === ref.metadata.key) {
|
|
87
|
+
if (q.ids.includes(id)) {
|
|
88
|
+
this.dispatcher.sendQueryMessage({
|
|
89
|
+
clientId: c.clientId, queryId: q.id, message: {
|
|
90
|
+
type: "remove",
|
|
91
|
+
data: {
|
|
92
|
+
id: id
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
q.ids = q.ids.filter(y => y != id);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|