mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Converting server-side Comm.js to typescript
Summary: - Add app/common/CommTypes.ts to define types shared by client and server. - Include @types/ws npm package Test Plan: Intended to have no changes in behavior Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D3467
This commit is contained in:
@@ -16,118 +16,26 @@
|
||||
*
|
||||
* Implementation
|
||||
* --------------
|
||||
* Messages are serialized as follows. Note that this is a matter between the client's and the
|
||||
* server's communication libraries, and code outside of them should not rely on these details.
|
||||
* Requests: {
|
||||
* reqId: Number,
|
||||
* method: String,
|
||||
* args: Array
|
||||
* }
|
||||
* Responses: {
|
||||
* reqId: Number, // distinguishes responses from async messages
|
||||
* error: String // if the request failed
|
||||
* data: Object // if the request succeeded, may be undefined if nothing to return
|
||||
* }
|
||||
* Async messages from server: {
|
||||
* type: String, // 'docListAction' or 'docUserAction' or 'clientConnect'
|
||||
* docFD: Number, // For 'docUserAction', the file descriptor of the open document.
|
||||
* data: Object // The message data.
|
||||
* // other keys may exist depending on message type.
|
||||
* }
|
||||
* Messages are serialized as JSON using types CommRequest, CommResponse, CommResponseError for
|
||||
* method calls, and CommMessage for async messages from the server. These are all defined in
|
||||
* app/common/CommTypes. Note that this is a matter between the client's and the server's
|
||||
* communication libraries, and code outside of them should not rely on these details.
|
||||
*/
|
||||
|
||||
import {GristWSConnection} from 'app/client/components/GristWSConnection';
|
||||
import * as dispose from 'app/client/lib/dispose';
|
||||
import {CommMessage, CommRequest, CommResponse, CommResponseError, ValidEvent} from 'app/common/CommTypes';
|
||||
import {UserAction} from 'app/common/DocActions';
|
||||
import {DocListAPI, OpenLocalDocResult} from 'app/common/DocListAPI';
|
||||
import {GristServerAPI} from 'app/common/GristServerAPI';
|
||||
import {StringUnion} from 'app/common/StringUnion';
|
||||
import {getInitialDocAssignment} from 'app/common/urlUtils';
|
||||
import {Events as BackboneEvents} from 'backbone';
|
||||
|
||||
// tslint:disable:no-console
|
||||
|
||||
/**
|
||||
* Event for a change to the document list.
|
||||
* These are sent to all connected clients, regardless of which documents they have open.
|
||||
* TODO: implement and document.
|
||||
* @event docListAction
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event for a user action on a document, or part of one. Sent to all clients that have this
|
||||
* document open.
|
||||
* @event docUserAction
|
||||
* @property {Number} docFD - The file descriptor of the open document, specific to each client.
|
||||
* @property {Array} data.actionGroup - ActionGroup object containing user action, and doc actions.
|
||||
* @property {Boolean} fromSelf - Flag to indicate whether the action originated from this client.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event for a change to document usage. Sent to all clients that have this document open.
|
||||
* @event docUsage
|
||||
* @property {Number} docFD - The file descriptor of the open document, specific to each client.
|
||||
* @property {FilteredDocUsageSummary} data.docUsage - Document usage summary.
|
||||
* @property {Product} data.product - Product that was used to compute `data.docUsage`
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event for when a document is forcibly shutdown, and requires the client to re-open it.
|
||||
* @event docShutdown
|
||||
* @property {Number} docFD - The file descriptor of the open document, specific to each client.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event sent by server received when a client first connects.
|
||||
* @event clientConnect
|
||||
* @property {Number} clientId - The ID for the client, which may be reused if a client reconnects
|
||||
* to reattach to its state on the server.
|
||||
* @property {Number} missedMessages - Array of messages missed from the server.
|
||||
* @property {Object} settings - Object containing server settings and features which
|
||||
* should be used to initialize the client.
|
||||
* @property {Object} profile - Object containing session profile information if the user
|
||||
* is signed in, or null otherwise. See "clientLogin" message below for fields.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event sent by server to all clients in the session when the updated profile is retrieved.
|
||||
* Does not necessarily contain all properties, may only include updated properties.
|
||||
* Gets sent on login with all properties.
|
||||
* @event profileFetch
|
||||
* @property {String} email User email.
|
||||
* @property {String} name User name,
|
||||
* @property {String} imageUrl The url of the user's profile image.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event sent by server to all clients in the session when the user settings are updated.
|
||||
* @event userSettings
|
||||
* @property {Object} features - Object containing feature flags such as login, indicating
|
||||
* which features are activated.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event sent by server to all clients in the session when a client logs out.
|
||||
* @event clientLogout
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event sent by server to all clients when an invite is received or for all invites received
|
||||
* while away when a user logs in.
|
||||
* @event receiveInvites
|
||||
* @property {Number} data - An array of unread invites (see app/common/sharing).
|
||||
*/
|
||||
|
||||
const ValidEvent = StringUnion('docListAction', 'docUserAction', 'docShutdown', 'docError',
|
||||
'docUsage', 'clientConnect', 'clientLogout',
|
||||
'profileFetch', 'userSettings', 'receiveInvites');
|
||||
type ValidEvent = typeof ValidEvent.type;
|
||||
|
||||
/**
|
||||
* A request that is currently being processed.
|
||||
*/
|
||||
export interface CommRequestInFlight {
|
||||
resolve: (result: any) => void;
|
||||
resolve: (result: unknown) => void;
|
||||
reject: (err: Error) => void;
|
||||
// clientId is non-null for those requests which should not be re-sent on reconnect if
|
||||
// the clientId has changed; it is null when it's safe to re-send.
|
||||
@@ -138,50 +46,10 @@ export interface CommRequestInFlight {
|
||||
sent: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A request in the appropriate form for sending to the server.
|
||||
*/
|
||||
export interface CommRequest {
|
||||
reqId: number;
|
||||
method: string;
|
||||
args: any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A regular, successful response from the server.
|
||||
*/
|
||||
export interface CommResponse {
|
||||
reqId: number;
|
||||
data: any;
|
||||
error?: null; // TODO: keep until sure server never sets this on regular responses.
|
||||
}
|
||||
|
||||
/**
|
||||
* An exceptional response from the server when there is an error.
|
||||
*/
|
||||
export interface CommResponseError {
|
||||
reqId: number;
|
||||
error: string;
|
||||
errorCode: string;
|
||||
shouldFork?: boolean; // if set, the server suggests forking the document.
|
||||
details?: any; // if set, error has extra details available. TODO - the treatment of
|
||||
// details could do with some harmonisation between rest API and ws API,
|
||||
// and between front-end and back-end types.
|
||||
}
|
||||
|
||||
function isCommResponseError(msg: CommResponse | CommResponseError): msg is CommResponseError {
|
||||
return Boolean(msg.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* A message pushed from the server, not in response to a request.
|
||||
*/
|
||||
export interface CommMessage {
|
||||
type: ValidEvent;
|
||||
docFD: number;
|
||||
data: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comm object provides the interfaces to communicate with the server.
|
||||
* Each method that calls to the server returns a promise for the response.
|
||||
|
||||
@@ -1,35 +1,14 @@
|
||||
import {Comm, CommMessage} from 'app/client/components/Comm';
|
||||
import {Comm} from 'app/client/components/Comm';
|
||||
import {reportError, reportMessage} from 'app/client/models/errors';
|
||||
import {Notifier} from 'app/client/models/NotifyModel';
|
||||
import {ActionGroup} from 'app/common/ActionGroup';
|
||||
import {ActiveDocAPI, ApplyUAOptions, ApplyUAResult} from 'app/common/ActiveDocAPI';
|
||||
import {DocAction, UserAction} from 'app/common/DocActions';
|
||||
import {CommMessage} from 'app/common/CommTypes';
|
||||
import {UserAction} from 'app/common/DocActions';
|
||||
import {OpenLocalDocResult} from 'app/common/DocListAPI';
|
||||
import {FilteredDocUsageSummary} from 'app/common/DocUsage';
|
||||
import {Product} from 'app/common/Features';
|
||||
import {docUrl} from 'app/common/urlUtils';
|
||||
import {Events as BackboneEvents} from 'backbone';
|
||||
import {Disposable, Emitter} from 'grainjs';
|
||||
|
||||
// tslint:disable:no-console
|
||||
|
||||
export interface DocUserAction extends CommMessage {
|
||||
fromSelf?: boolean;
|
||||
data: {
|
||||
docActions: DocAction[];
|
||||
actionGroup: ActionGroup;
|
||||
docUsage: FilteredDocUsageSummary;
|
||||
error?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DocUsageMessage extends CommMessage {
|
||||
data: {
|
||||
docUsage: FilteredDocUsageSummary;
|
||||
product?: Product;
|
||||
};
|
||||
}
|
||||
|
||||
const SLOW_NOTIFICATION_TIMEOUT_MS = 1000; // applies to user actions only
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ import {CodeEditorPanel} from 'app/client/components/CodeEditorPanel';
|
||||
import * as commands from 'app/client/components/commands';
|
||||
import {CursorPos} from 'app/client/components/Cursor';
|
||||
import {CursorMonitor, ViewCursorPos} from "app/client/components/CursorMonitor";
|
||||
import {DocComm, DocUsageMessage, DocUserAction} from 'app/client/components/DocComm';
|
||||
import {DocComm} from 'app/client/components/DocComm';
|
||||
import * as DocConfigTab from 'app/client/components/DocConfigTab';
|
||||
import {Drafts} from "app/client/components/Drafts";
|
||||
import {EditorMonitor} from "app/client/components/EditorMonitor";
|
||||
@@ -51,6 +51,7 @@ import {FieldEditor} from "app/client/widgets/FieldEditor";
|
||||
import {MinimalActionGroup} from 'app/common/ActionGroup';
|
||||
import {ClientQuery} from "app/common/ActiveDocAPI";
|
||||
import {delay} from 'app/common/delay';
|
||||
import {CommDocUsage, CommDocUserAction} from 'app/common/CommTypes';
|
||||
import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
|
||||
import {isSchemaAction, UserAction} from 'app/common/DocActions';
|
||||
import {OpenLocalDocResult} from 'app/common/DocListAPI';
|
||||
@@ -441,7 +442,7 @@ export class GristDoc extends DisposableWithEvents {
|
||||
* Process actions received from the server by forwarding them to `docData.receiveAction()` and
|
||||
* pushing them to actionLog.
|
||||
*/
|
||||
public onDocUserAction(message: DocUserAction) {
|
||||
public onDocUserAction(message: CommDocUserAction) {
|
||||
console.log("GristDoc.onDocUserAction", message);
|
||||
let schemaUpdated = false;
|
||||
/**
|
||||
@@ -489,7 +490,7 @@ export class GristDoc extends DisposableWithEvents {
|
||||
* Process usage and product received from the server by updating their respective
|
||||
* observables.
|
||||
*/
|
||||
public onDocUsageMessage(message: DocUsageMessage) {
|
||||
public onDocUsageMessage(message: CommDocUsage) {
|
||||
if (!this.docComm.isActionFromThisDoc(message)) { return; }
|
||||
|
||||
bundleChanges(() => {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {createAppUI} from 'app/client/ui/AppUI';
|
||||
import {addViewportTag} from 'app/client/ui/viewport';
|
||||
import {attachCssRootVars} from 'app/client/ui2018/cssVars';
|
||||
import {BaseAPI} from 'app/common/BaseAPI';
|
||||
import {CommDocError} from 'app/common/CommTypes';
|
||||
import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
|
||||
import {fetchFromHome} from 'app/common/urlUtils';
|
||||
import {ISupportedFeatures} from 'app/common/UserConfig';
|
||||
@@ -158,7 +159,7 @@ export class App extends DisposableWithEvents {
|
||||
setTimeout(() => this.reloadPane(), 0);
|
||||
});
|
||||
|
||||
this.listenTo(this.comm, 'docError', (msg) => {
|
||||
this.listenTo(this.comm, 'docError', (msg: CommDocError) => {
|
||||
this._checkError(new Error(msg.data.message));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user