423 lines
14 KiB
TypeScript
423 lines
14 KiB
TypeScript
import IframeReference from './IframeReference';
|
|
import EventBus from './EventBus';
|
|
import UserStore from './UserStrore';
|
|
import type { ProfileData } from './models/ProfileData';
|
|
import type { FolderData } from './models/FolderData';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
|
|
export default class MessageBus {
|
|
private static instance: MessageBus;
|
|
private readonly origin: string;
|
|
private messageListener: ((event: MessageEvent) => void) | null = null;
|
|
private errors: { [key: string]: string } = {};
|
|
|
|
private constructor(origin: string) {
|
|
this.origin = origin;
|
|
}
|
|
|
|
public static getInstance(origin: string): MessageBus {
|
|
if (!MessageBus.instance) {
|
|
MessageBus.instance = new MessageBus(origin);
|
|
}
|
|
return MessageBus.instance;
|
|
}
|
|
|
|
public isReady(): Promise<void> {
|
|
return new Promise<void>((resolve: () => void) => {
|
|
const correlationId = uuidv4();
|
|
this.initMessageListener(correlationId);
|
|
|
|
const unsubscribe = EventBus.getInstance().on('IS_READY', (responseId: string) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribe();
|
|
this.destroyMessageListener();
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
public requestLink(): Promise<void> {
|
|
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
|
|
const correlationId = uuidv4();
|
|
this.initMessageListener(correlationId);
|
|
|
|
const unsubscribe = EventBus.getInstance().on('LINK_ACCEPTED', (responseId: string, accessToken: string, refreshToken: string) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribe();
|
|
this.destroyMessageListener();
|
|
UserStore.getInstance().connect(accessToken, refreshToken);
|
|
resolve();
|
|
});
|
|
|
|
const unsubscribeError = EventBus.getInstance().on('ERROR_LINK_ACCEPTED', (responseId: string, error: string) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribeError();
|
|
this.destroyMessageListener();
|
|
reject(error);
|
|
});
|
|
|
|
this.sendMessage({
|
|
type: 'REQUEST_LINK'
|
|
});
|
|
});
|
|
}
|
|
|
|
public validateToken(): Promise<boolean> {
|
|
return new Promise<boolean>((resolve: (isValid: boolean) => void, reject: (error: string) => void) => {
|
|
const userStore = UserStore.getInstance();
|
|
if (!userStore.isConnected()) {
|
|
reject('User is not connected');
|
|
return;
|
|
}
|
|
const accessToken = userStore.getAccessToken()!;
|
|
const refreshToken = userStore.getRefreshToken()!;
|
|
|
|
const correlationId = uuidv4();
|
|
this.initMessageListener(correlationId);
|
|
|
|
const unsubscribe = EventBus.getInstance().on('TOKEN_VALIDATED', (responseId: string, isValid: boolean) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribe();
|
|
this.destroyMessageListener();
|
|
resolve(isValid);
|
|
});
|
|
|
|
const unsubscribeError = EventBus.getInstance().on('ERROR_TOKEN_VALIDATED', (responseId: string, error: string) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribeError();
|
|
this.destroyMessageListener();
|
|
reject(error);
|
|
});
|
|
|
|
this.sendMessage({
|
|
type: 'VALIDATE_TOKEN',
|
|
accessToken,
|
|
refreshToken
|
|
});
|
|
});
|
|
}
|
|
|
|
public renewToken(): Promise<void> {
|
|
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
|
|
const userStore = UserStore.getInstance();
|
|
if (!userStore.isConnected()) {
|
|
reject('User is not connected');
|
|
return;
|
|
}
|
|
const refreshToken = userStore.getRefreshToken()!;
|
|
|
|
const correlationId = uuidv4();
|
|
this.initMessageListener(correlationId);
|
|
|
|
const unsubscribe = EventBus.getInstance().on('TOKEN_RENEWED', (responseId: string, accessToken: string, refreshToken: string) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribe();
|
|
this.destroyMessageListener();
|
|
UserStore.getInstance().connect(accessToken, refreshToken);
|
|
resolve();
|
|
});
|
|
|
|
const unsubscribeError = EventBus.getInstance().on('ERROR_TOKEN_RENEWED', (responseId: string, error: string) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribeError();
|
|
this.destroyMessageListener();
|
|
reject(error);
|
|
});
|
|
|
|
this.sendMessage({
|
|
type: 'RENEW_TOKEN',
|
|
refreshToken
|
|
});
|
|
});
|
|
}
|
|
|
|
public getProcesses(): Promise<any> {
|
|
return new Promise<any>((resolve: (processes: any) => void, reject: (error: string) => void) => {
|
|
this.checkToken().then(() => {
|
|
const correlationId = uuidv4();
|
|
this.initMessageListener(correlationId);
|
|
|
|
const unsubscribe = EventBus.getInstance().on('PROCESSES_RETRIEVED', (responseId: string, processes: any) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribe();
|
|
this.destroyMessageListener();
|
|
resolve(processes);
|
|
});
|
|
|
|
const unsubscribeError = EventBus.getInstance().on('ERROR_PROCESSES_RETRIEVED', (responseId: string, error: string) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribeError();
|
|
this.destroyMessageListener();
|
|
reject(error);
|
|
});
|
|
|
|
this.sendMessage({
|
|
type: 'GET_PROCESSES'
|
|
});
|
|
}).catch(console.error);
|
|
});
|
|
}
|
|
|
|
public getData(processId: string, stateId: string): Promise<any> {
|
|
return new Promise<any>((resolve: (data: any) => void, reject: (error: string) => void) => {
|
|
this.checkToken().then(() => {
|
|
const userStore = UserStore.getInstance();
|
|
const accessToken = userStore.getAccessToken()!;
|
|
|
|
const correlationId = uuidv4();
|
|
this.initMessageListener(correlationId);
|
|
|
|
const unsubscribe = EventBus.getInstance().on('DATA_RETRIEVED', (responseId: string, data: any) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribe();
|
|
this.destroyMessageListener();
|
|
resolve(data);
|
|
});
|
|
|
|
const unsubscribeError = EventBus.getInstance().on('ERROR_DATA_RETRIEVED', (responseId: string, error: string) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribeError();
|
|
this.destroyMessageListener();
|
|
reject(error);
|
|
});
|
|
|
|
this.sendMessage({
|
|
type: 'RETRIEVE_DATA',
|
|
processId,
|
|
stateId,
|
|
token: accessToken
|
|
});
|
|
}).catch(console.error);
|
|
});
|
|
}
|
|
|
|
public createProfile(profileData: ProfileData): Promise<ProfileData> {
|
|
return new Promise<ProfileData>((resolve: (profileData: ProfileData) => void, reject: (error: string) => void) => {
|
|
this.checkToken().then(() => {
|
|
const userStore = UserStore.getInstance();
|
|
const accessToken = userStore.getAccessToken()!;
|
|
const refreshToken = userStore.getRefreshToken()!;
|
|
|
|
const correlationId = uuidv4();
|
|
this.initMessageListener(correlationId);
|
|
|
|
const unsubscribe = EventBus.getInstance().on('PROFILE_CREATED', (responseId: string, profileData: ProfileData) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribe();
|
|
this.destroyMessageListener();
|
|
resolve(profileData);
|
|
});
|
|
|
|
const unsubscribeError = EventBus.getInstance().on('ERROR_PROFILE_CREATED', (responseId: string, error: string) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribeError();
|
|
this.destroyMessageListener();
|
|
reject(error);
|
|
});
|
|
|
|
this.sendMessage({
|
|
type: 'CREATE_PROFILE',
|
|
profileData,
|
|
accessToken,
|
|
refreshToken
|
|
});
|
|
}).catch(console.error);
|
|
});
|
|
}
|
|
|
|
public createFolder(folderData: FolderData): Promise<FolderData> {
|
|
return new Promise<FolderData>((resolve: (folderData: FolderData) => void, reject: (error: string) => void) => {
|
|
this.checkToken().then(() => {
|
|
const userStore = UserStore.getInstance();
|
|
const accessToken = userStore.getAccessToken()!;
|
|
|
|
const correlationId = uuidv4();
|
|
this.initMessageListener(correlationId);
|
|
|
|
const unsubscribe = EventBus.getInstance().on('FOLDER_CREATED', (responseId: string, folderData: FolderData) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribe();
|
|
this.destroyMessageListener();
|
|
resolve(folderData);
|
|
});
|
|
|
|
const unsubscribeError = EventBus.getInstance().on('ERROR_FOLDER_CREATED', (responseId: string, error: string) => {
|
|
if (responseId !== correlationId) {
|
|
return;
|
|
}
|
|
unsubscribeError();
|
|
this.destroyMessageListener();
|
|
reject(error);
|
|
});
|
|
|
|
this.sendMessage({
|
|
type: 'CREATE_FOLDER',
|
|
folderData,
|
|
token: accessToken
|
|
});
|
|
}).catch(console.error);
|
|
});
|
|
}
|
|
|
|
private checkToken(): Promise<void> {
|
|
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
|
|
this.validateToken().then((isValid: boolean) => {
|
|
if (!isValid) {
|
|
this.renewToken().then(resolve).catch(reject);
|
|
} else {
|
|
resolve();
|
|
}
|
|
}).catch(reject);
|
|
});
|
|
}
|
|
|
|
private sendMessage(message: any): void {
|
|
const iframe = IframeReference.getIframe();
|
|
if (!iframe) {
|
|
console.error('[MessageBus] sendMessage: iframe not found');
|
|
return;
|
|
}
|
|
console.log('[MessageBus] sendMessage:', message);
|
|
iframe.contentWindow?.postMessage(message, this.origin);
|
|
}
|
|
|
|
private initMessageListener(correlationId: string): void {
|
|
this.destroyMessageListener();
|
|
|
|
this.messageListener = this.handleMessage.bind(this, correlationId);
|
|
window.addEventListener('message', this.messageListener);
|
|
}
|
|
|
|
private destroyMessageListener(): void {
|
|
if (this.messageListener) {
|
|
window.removeEventListener('message', this.messageListener);
|
|
this.messageListener = null;
|
|
}
|
|
}
|
|
|
|
private handleMessage(correlationId: string, event: MessageEvent): void {
|
|
if (event.origin !== this.origin) {
|
|
return;
|
|
}
|
|
|
|
if (!event.data || typeof event.data !== 'object') {
|
|
return;
|
|
}
|
|
|
|
const message = event.data;
|
|
switch (message.type) {
|
|
case 'LISTENING':
|
|
EventBus.getInstance().emit('IS_READY', correlationId);
|
|
break;
|
|
|
|
case 'LINK_ACCEPTED':
|
|
if (this.errors[correlationId]) {
|
|
const error = this.errors[correlationId];
|
|
delete this.errors[correlationId];
|
|
EventBus.getInstance().emit('ERROR_LINK_ACCEPTED', correlationId, error);
|
|
return;
|
|
}
|
|
EventBus.getInstance().emit('MESSAGE_RECEIVED', message);
|
|
EventBus.getInstance().emit('LINK_ACCEPTED', correlationId, message.accessToken, message.refreshToken);
|
|
break;
|
|
|
|
case 'VALIDATE_TOKEN':
|
|
if (this.errors[correlationId]) {
|
|
const error = this.errors[correlationId];
|
|
delete this.errors[correlationId];
|
|
EventBus.getInstance().emit('ERROR_TOKEN_VALIDATED', correlationId, error);
|
|
return;
|
|
}
|
|
EventBus.getInstance().emit('MESSAGE_RECEIVED', message);
|
|
EventBus.getInstance().emit('TOKEN_VALIDATED', correlationId, message.isValid);
|
|
break;
|
|
|
|
case 'RENEW_TOKEN':
|
|
if (this.errors[correlationId]) {
|
|
const error = this.errors[correlationId];
|
|
delete this.errors[correlationId];
|
|
EventBus.getInstance().emit('ERROR_TOKEN_RENEWED', correlationId, error);
|
|
return;
|
|
}
|
|
EventBus.getInstance().emit('MESSAGE_RECEIVED', message);
|
|
EventBus.getInstance().emit('TOKEN_RENEWED', correlationId, message.accessToken, message.refreshToken);
|
|
break;
|
|
|
|
case 'PROCESSES_RETRIEVED': // GET_PROCESSES
|
|
if (this.errors[correlationId]) {
|
|
const error = this.errors[correlationId];
|
|
delete this.errors[correlationId];
|
|
EventBus.getInstance().emit('ERROR_PROCESSES_RETRIEVED', correlationId, error);
|
|
return;
|
|
}
|
|
EventBus.getInstance().emit('PROCESSES_RETRIEVED', correlationId, message.processes);
|
|
break;
|
|
|
|
case 'PROFILE_CREATED': // CREATE_PROFILE
|
|
if (this.errors[correlationId]) {
|
|
const error = this.errors[correlationId];
|
|
delete this.errors[correlationId];
|
|
EventBus.getInstance().emit('ERROR_PROFILE_CREATED', correlationId, error);
|
|
return;
|
|
}
|
|
EventBus.getInstance().emit('MESSAGE_RECEIVED', message);
|
|
EventBus.getInstance().emit('PROFILE_CREATED', correlationId, message.profileData);
|
|
break;
|
|
|
|
case 'FOLDER_CREATED': // CREATE_FOLDER
|
|
if (this.errors[correlationId]) {
|
|
const error = this.errors[correlationId];
|
|
delete this.errors[correlationId];
|
|
EventBus.getInstance().emit('ERROR_FOLDER_CREATED', correlationId, error);
|
|
return;
|
|
}
|
|
EventBus.getInstance().emit('MESSAGE_RECEIVED', message);
|
|
break;
|
|
|
|
case 'DATA_RETRIEVED': // RETRIEVE_DATA
|
|
if (this.errors[correlationId]) {
|
|
const error = this.errors[correlationId];
|
|
delete this.errors[correlationId];
|
|
EventBus.getInstance().emit('ERROR_DATA_RETRIEVED', correlationId, error);
|
|
return;
|
|
}
|
|
EventBus.getInstance().emit('MESSAGE_RECEIVED', message);
|
|
EventBus.getInstance().emit('DATA_RETRIEVED', correlationId, message.data);
|
|
break;
|
|
|
|
case 'ERROR':
|
|
console.error('Error:', message);
|
|
this.errors[correlationId] = message.error;
|
|
break;
|
|
}
|
|
}
|
|
}
|