debian-mirror-gitlab/app/assets/javascripts/ide/lib/mirror.js

155 lines
3.5 KiB
JavaScript
Raw Normal View History

2020-06-23 00:09:42 +05:30
import { getWebSocketUrl, mergeUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
2021-03-11 19:13:27 +05:30
import createDiff from './create_diff';
2020-06-23 00:09:42 +05:30
export const SERVICE_NAME = 'webide-file-sync';
export const PROTOCOL = 'webfilesync.gitlab.com';
export const MSG_CONNECTION_ERROR = __('Could not connect to Web IDE file mirror service.');
// Before actually connecting to the service, we must delay a bit
// so that the service has sufficiently started.
const noop = () => {};
export const SERVICE_DELAY = 8000;
2021-03-08 18:12:59 +05:30
const cancellableWait = (time) => {
2020-06-23 00:09:42 +05:30
let timeoutId = 0;
const cancel = () => clearTimeout(timeoutId);
2021-03-08 18:12:59 +05:30
const promise = new Promise((resolve) => {
2020-06-23 00:09:42 +05:30
timeoutId = setTimeout(resolve, time);
});
return [promise, cancel];
};
2021-03-08 18:12:59 +05:30
const isErrorResponse = (error) => error && error.code !== 0;
2020-06-23 00:09:42 +05:30
2021-03-08 18:12:59 +05:30
const isErrorPayload = (payload) => payload && payload.status_code !== 200;
2020-06-23 00:09:42 +05:30
2021-03-08 18:12:59 +05:30
const getErrorFromResponse = (data) => {
2020-06-23 00:09:42 +05:30
if (isErrorResponse(data.error)) {
return { message: data.error.Message };
} else if (isErrorPayload(data.payload)) {
return { message: data.payload.error_message };
}
return null;
};
2021-03-08 18:12:59 +05:30
const getFullPath = (path) => mergeUrlParams({ service: SERVICE_NAME }, getWebSocketUrl(path));
2020-06-23 00:09:42 +05:30
2021-03-08 18:12:59 +05:30
const createWebSocket = (fullPath) =>
2020-06-23 00:09:42 +05:30
new Promise((resolve, reject) => {
const socket = new WebSocket(fullPath, [PROTOCOL]);
const resetCallbacks = () => {
socket.onopen = null;
socket.onerror = null;
};
socket.onopen = () => {
resetCallbacks();
resolve(socket);
};
socket.onerror = () => {
resetCallbacks();
reject(new Error(MSG_CONNECTION_ERROR));
};
});
2021-03-08 18:12:59 +05:30
export const canConnect = ({ services = [] }) => services.some((name) => name === SERVICE_NAME);
2020-06-23 00:09:42 +05:30
export const createMirror = () => {
let socket = null;
let cancelHandler = noop;
let nextMessageHandler = noop;
const cancelConnect = () => {
cancelHandler();
cancelHandler = noop;
};
2021-03-08 18:12:59 +05:30
const onCancelConnect = (fn) => {
2020-06-23 00:09:42 +05:30
cancelHandler = fn;
};
2021-03-08 18:12:59 +05:30
const receiveMessage = (ev) => {
2020-06-23 00:09:42 +05:30
const handle = nextMessageHandler;
nextMessageHandler = noop;
handle(JSON.parse(ev.data));
};
2021-03-08 18:12:59 +05:30
const onNextMessage = (fn) => {
2020-06-23 00:09:42 +05:30
nextMessageHandler = fn;
};
const waitForNextMessage = () =>
new Promise((resolve, reject) => {
2021-03-08 18:12:59 +05:30
onNextMessage((data) => {
2020-06-23 00:09:42 +05:30
const err = getErrorFromResponse(data);
if (err) {
reject(err);
} else {
resolve();
}
});
});
const uploadDiff = ({ toDelete, patch }) => {
if (!socket) {
return Promise.resolve();
}
const response = waitForNextMessage();
const msg = {
code: 'EVENT',
namespace: '/files',
event: 'PATCH',
payload: { diff: patch, delete_files: toDelete },
};
socket.send(JSON.stringify(msg));
return response;
};
return {
upload(state) {
return uploadDiff(createDiff(state));
},
connect(path) {
if (socket) {
this.disconnect();
}
const fullPath = getFullPath(path);
const [wait, cancelWait] = cancellableWait(SERVICE_DELAY);
onCancelConnect(cancelWait);
return wait
.then(() => createWebSocket(fullPath))
2021-03-08 18:12:59 +05:30
.then((newSocket) => {
2020-06-23 00:09:42 +05:30
socket = newSocket;
socket.onmessage = receiveMessage;
});
},
disconnect() {
cancelConnect();
if (!socket) {
return;
}
socket.close();
socket = null;
},
};
};
export default createMirror();