refactor(core): Rename push sessionId to pushRef (#8905)
This commit is contained in:
committed by
GitHub
parent
eaaefd76da
commit
072c3db97d
@@ -14,7 +14,7 @@ import type { OrchestrationService } from '@/services/orchestration.service';
|
||||
export abstract class AbstractPush<T> extends EventEmitter {
|
||||
protected connections: Record<string, T> = {};
|
||||
|
||||
protected userIdBySessionId: Record<string, string> = {};
|
||||
protected userIdByPushRef: Record<string, string> = {};
|
||||
|
||||
protected abstract close(connection: T): void;
|
||||
protected abstract sendToOneConnection(connection: T, data: string): void;
|
||||
@@ -26,100 +26,100 @@ export abstract class AbstractPush<T> extends EventEmitter {
|
||||
super();
|
||||
}
|
||||
|
||||
protected add(sessionId: string, userId: User['id'], connection: T) {
|
||||
const { connections, userIdBySessionId: userIdsBySessionId } = this;
|
||||
this.logger.debug('Add editor-UI session', { sessionId });
|
||||
protected add(pushRef: string, userId: User['id'], connection: T) {
|
||||
const { connections, userIdByPushRef } = this;
|
||||
this.logger.debug('Add editor-UI session', { pushRef });
|
||||
|
||||
const existingConnection = connections[sessionId];
|
||||
const existingConnection = connections[pushRef];
|
||||
|
||||
if (existingConnection) {
|
||||
// Make sure to remove existing connection with the same ID
|
||||
this.close(existingConnection);
|
||||
}
|
||||
|
||||
connections[sessionId] = connection;
|
||||
userIdsBySessionId[sessionId] = userId;
|
||||
connections[pushRef] = connection;
|
||||
userIdByPushRef[pushRef] = userId;
|
||||
}
|
||||
|
||||
protected onMessageReceived(sessionId: string, msg: unknown) {
|
||||
this.logger.debug('Received message from editor-UI', { sessionId, msg });
|
||||
protected onMessageReceived(pushRef: string, msg: unknown) {
|
||||
this.logger.debug('Received message from editor-UI', { pushRef, msg });
|
||||
|
||||
const userId = this.userIdBySessionId[sessionId];
|
||||
const userId = this.userIdByPushRef[pushRef];
|
||||
|
||||
this.emit('message', { sessionId, userId, msg });
|
||||
this.emit('message', { pushRef, userId, msg });
|
||||
}
|
||||
|
||||
protected remove(sessionId?: string) {
|
||||
if (!sessionId) return;
|
||||
protected remove(pushRef?: string) {
|
||||
if (!pushRef) return;
|
||||
|
||||
this.logger.debug('Removed editor-UI session', { sessionId });
|
||||
this.logger.debug('Removed editor-UI session', { pushRef });
|
||||
|
||||
delete this.connections[sessionId];
|
||||
delete this.userIdBySessionId[sessionId];
|
||||
delete this.connections[pushRef];
|
||||
delete this.userIdByPushRef[pushRef];
|
||||
}
|
||||
|
||||
private sendToSessions(type: IPushDataType, data: unknown, sessionIds: string[]) {
|
||||
private sendTo(type: IPushDataType, data: unknown, pushRefs: string[]) {
|
||||
this.logger.debug(`Send data of type "${type}" to editor-UI`, {
|
||||
dataType: type,
|
||||
sessionIds: sessionIds.join(', '),
|
||||
pushRefs: pushRefs.join(', '),
|
||||
});
|
||||
|
||||
const stringifiedPayload = jsonStringify({ type, data }, { replaceCircularRefs: true });
|
||||
|
||||
for (const sessionId of sessionIds) {
|
||||
const connection = this.connections[sessionId];
|
||||
for (const pushRef of pushRefs) {
|
||||
const connection = this.connections[pushRef];
|
||||
assert(connection);
|
||||
this.sendToOneConnection(connection, stringifiedPayload);
|
||||
}
|
||||
}
|
||||
|
||||
sendToAllSessions(type: IPushDataType, data?: unknown) {
|
||||
this.sendToSessions(type, data, Object.keys(this.connections));
|
||||
sendToAll(type: IPushDataType, data?: unknown) {
|
||||
this.sendTo(type, data, Object.keys(this.connections));
|
||||
}
|
||||
|
||||
sendToOneSession(type: IPushDataType, data: unknown, sessionId: string) {
|
||||
sendToOneSession(type: IPushDataType, data: unknown, pushRef: string) {
|
||||
/**
|
||||
* Multi-main setup: In a manual webhook execution, the main process that
|
||||
* handles a webhook might not be the same as the main process that created
|
||||
* the webhook. If so, the handler process commands the creator process to
|
||||
* relay the former's execution lifecyle events to the creator's frontend.
|
||||
* relay the former's execution lifecycle events to the creator's frontend.
|
||||
*/
|
||||
if (this.orchestrationService.isMultiMainSetupEnabled && !this.hasSessionId(sessionId)) {
|
||||
const payload = { type, args: data, sessionId };
|
||||
if (this.orchestrationService.isMultiMainSetupEnabled && !this.hasPushRef(pushRef)) {
|
||||
const payload = { type, args: data, pushRef };
|
||||
|
||||
void this.orchestrationService.publish('relay-execution-lifecycle-event', payload);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.connections[sessionId] === undefined) {
|
||||
this.logger.error(`The session "${sessionId}" is not registered.`, { sessionId });
|
||||
if (this.connections[pushRef] === undefined) {
|
||||
this.logger.error(`The session "${pushRef}" is not registered.`, { pushRef });
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendToSessions(type, data, [sessionId]);
|
||||
this.sendTo(type, data, [pushRef]);
|
||||
}
|
||||
|
||||
sendToUsers(type: IPushDataType, data: unknown, userIds: Array<User['id']>) {
|
||||
const { connections } = this;
|
||||
const userSessionIds = Object.keys(connections).filter((sessionId) =>
|
||||
userIds.includes(this.userIdBySessionId[sessionId]),
|
||||
const userPushRefs = Object.keys(connections).filter((pushRef) =>
|
||||
userIds.includes(this.userIdByPushRef[pushRef]),
|
||||
);
|
||||
|
||||
this.sendToSessions(type, data, userSessionIds);
|
||||
this.sendTo(type, data, userPushRefs);
|
||||
}
|
||||
|
||||
closeAllConnections() {
|
||||
for (const sessionId in this.connections) {
|
||||
for (const pushRef in this.connections) {
|
||||
// Signal the connection that we want to close it.
|
||||
// We are not removing the sessions here because it should be
|
||||
// the implementation's responsibility to do so once the connection
|
||||
// has actually closed.
|
||||
this.close(this.connections[sessionId]);
|
||||
this.close(this.connections[pushRef]);
|
||||
}
|
||||
}
|
||||
|
||||
hasSessionId(sessionId: string) {
|
||||
return this.connections[sessionId] !== undefined;
|
||||
hasPushRef(pushRef: string) {
|
||||
return this.connections[pushRef] !== undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,36 +41,36 @@ export class Push extends EventEmitter {
|
||||
const {
|
||||
user,
|
||||
ws,
|
||||
query: { sessionId },
|
||||
query: { pushRef },
|
||||
} = req;
|
||||
|
||||
if (!sessionId) {
|
||||
if (!pushRef) {
|
||||
if (ws) {
|
||||
ws.send('The query parameter "sessionId" is missing!');
|
||||
ws.send('The query parameter "pushRef" is missing!');
|
||||
ws.close(1008);
|
||||
return;
|
||||
}
|
||||
throw new BadRequestError('The query parameter "sessionId" is missing!');
|
||||
throw new BadRequestError('The query parameter "pushRef" is missing!');
|
||||
}
|
||||
|
||||
if (req.ws) {
|
||||
(this.backend as WebSocketPush).add(sessionId, user.id, req.ws);
|
||||
(this.backend as WebSocketPush).add(pushRef, user.id, req.ws);
|
||||
} else if (!useWebSockets) {
|
||||
(this.backend as SSEPush).add(sessionId, user.id, { req, res });
|
||||
(this.backend as SSEPush).add(pushRef, user.id, { req, res });
|
||||
} else {
|
||||
res.status(401).send('Unauthorized');
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit('editorUiConnected', sessionId);
|
||||
this.emit('editorUiConnected', pushRef);
|
||||
}
|
||||
|
||||
broadcast(type: IPushDataType, data?: unknown) {
|
||||
this.backend.sendToAllSessions(type, data);
|
||||
this.backend.sendToAll(type, data);
|
||||
}
|
||||
|
||||
send(type: IPushDataType, data: unknown, sessionId: string) {
|
||||
this.backend.sendToOneSession(type, data, sessionId);
|
||||
send(type: IPushDataType, data: unknown, pushRef: string) {
|
||||
this.backend.sendToOneSession(type, data, pushRef);
|
||||
}
|
||||
|
||||
getBackend() {
|
||||
|
||||
@@ -17,13 +17,13 @@ export class SSEPush extends AbstractPush<Connection> {
|
||||
constructor(logger: Logger, orchestrationService: OrchestrationService) {
|
||||
super(logger, orchestrationService);
|
||||
|
||||
this.channel.on('disconnect', (channel, { req }) => {
|
||||
this.remove(req?.query?.sessionId);
|
||||
this.channel.on('disconnect', (_, { req }) => {
|
||||
this.remove(req?.query?.pushRef);
|
||||
});
|
||||
}
|
||||
|
||||
add(sessionId: string, userId: User['id'], connection: Connection) {
|
||||
super.add(sessionId, userId, connection);
|
||||
add(pushRef: string, userId: User['id'], connection: Connection) {
|
||||
super.add(pushRef, userId, connection);
|
||||
this.channel.addClient(connection.req, connection.res);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { AuthenticatedRequest } from '@/requests';
|
||||
|
||||
// TODO: move all push related types here
|
||||
|
||||
export type PushRequest = AuthenticatedRequest<{}, {}, {}, { sessionId: string }>;
|
||||
export type PushRequest = AuthenticatedRequest<{}, {}, {}, { pushRef: string }>;
|
||||
|
||||
export type SSEPushRequest = PushRequest & { ws: undefined };
|
||||
export type WebSocketPushRequest = PushRequest & { ws: WebSocket };
|
||||
@@ -14,7 +14,7 @@ export type WebSocketPushRequest = PushRequest & { ws: WebSocket };
|
||||
export type PushResponse = Response & { req: PushRequest };
|
||||
|
||||
export type OnPushMessageEvent = {
|
||||
sessionId: string;
|
||||
pushRef: string;
|
||||
userId: User['id'];
|
||||
msg: unknown;
|
||||
};
|
||||
|
||||
@@ -18,21 +18,21 @@ export class WebSocketPush extends AbstractPush<WebSocket> {
|
||||
setInterval(() => this.pingAll(), 60 * 1000);
|
||||
}
|
||||
|
||||
add(sessionId: string, userId: User['id'], connection: WebSocket) {
|
||||
add(pushRef: string, userId: User['id'], connection: WebSocket) {
|
||||
connection.isAlive = true;
|
||||
connection.on('pong', heartbeat);
|
||||
|
||||
super.add(sessionId, userId, connection);
|
||||
super.add(pushRef, userId, connection);
|
||||
|
||||
const onMessage = (data: WebSocket.RawData) => {
|
||||
try {
|
||||
const buffer = Array.isArray(data) ? Buffer.concat(data) : Buffer.from(data);
|
||||
|
||||
this.onMessageReceived(sessionId, JSON.parse(buffer.toString('utf8')));
|
||||
this.onMessageReceived(pushRef, JSON.parse(buffer.toString('utf8')));
|
||||
} catch (error) {
|
||||
this.logger.error("Couldn't parse message from editor-UI", {
|
||||
error: error as unknown,
|
||||
sessionId,
|
||||
pushRef,
|
||||
data,
|
||||
});
|
||||
}
|
||||
@@ -42,7 +42,7 @@ export class WebSocketPush extends AbstractPush<WebSocket> {
|
||||
connection.once('close', () => {
|
||||
connection.off('pong', heartbeat);
|
||||
connection.off('message', onMessage);
|
||||
this.remove(sessionId);
|
||||
this.remove(pushRef);
|
||||
});
|
||||
|
||||
connection.on('message', onMessage);
|
||||
@@ -57,11 +57,11 @@ export class WebSocketPush extends AbstractPush<WebSocket> {
|
||||
}
|
||||
|
||||
private pingAll() {
|
||||
for (const sessionId in this.connections) {
|
||||
const connection = this.connections[sessionId];
|
||||
for (const pushRef in this.connections) {
|
||||
const connection = this.connections[pushRef];
|
||||
// If a connection did not respond with a `PONG` in the last 60 seconds, disconnect
|
||||
if (!connection.isAlive) {
|
||||
delete this.connections[sessionId];
|
||||
delete this.connections[pushRef];
|
||||
return connection.terminate();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user