Skip to content

CodeYellowBV/spine-high-templar

Repository files navigation

spine-high-templar

Build Status codecov npm version

A frontend package which adds websocket and pubSub logic from high-templar to mobx-spine.

The functionality of this package includes:

  • Websocket creation
  • Keepalive
  • Automatic reconnection
  • Subscriptions
  • Handlers for the publishes happening for a subscription
  • Subscription refreshing after a socket disconnect => reconnect

Usage

Socket opening

spine-high-templar offers a Socket model. It is recommended to create this socket in the viewStore and save it in the api instance.

In the store/View constructor:

this.fetchBootstrap().then(() => {
    if (this.bootstrapCode === 200) {
        api.socket = new Socket({
            url: process.env.CY_FRONTEND_WEBSOCKET_URL,
        });
    }
});

It is important that the bootstrap has actually succeeded, the high-templar instance will use the headers provided in the socket-open-request to authenticate the user against the binder instance.

Subscribing & unsubscribing

The frontend subscribes to a room (in the form of an object). onPublish will be called with every message published in that room.

Don't forget to unsubscribe when a view will unmount.

View example:

componentDidMount() {
    this.subscription = this.props.store.api.socket.subscribe({
        onPublish: this.handlePublish.bind(this),
        room: {
            tenant: 16,
            driver: this.props.screenProps.viewStore.currentUser.id,
        },
    });
}

handlePublish(msg) {
    this.props.store.add(msg.data);
}

componentWillUnmount() {
    this.props.store.api.socket.unsubscribe(this.subscription);
}

Reconnect

The websocket may disconnect. This can be a client side reason (loss of internet) or a server side reason (deployment). When the websocket has been disconnected, the frontend might be outdated, as publishes may have been missed.

For this reason the subscribe action has a onReconnect callback, where the frontend can refetch/do whatever to make sure it's up to date.

componentDidMount() {
    this.subscription = this.props.store.api.socket.subscribe({
        onPublish: this.handlePublish.bind(this),
        onReconnect: this.handleReconnect.bind(this),
        room: {
            tenant: 16,
            driver: this.props.screenProps.viewStore.currentUser.id,
        },
    });
}

handlePublish(msg) {
    this.props.store.add(msg.data);
}

handleReconnect() {
    this.props.store.fetch();
}

componentWillUnmount() {
    this.props.store.api.socket.unsubscribe(this.subscription);
}

Authorization: Token

If the app doesn't use session auth but an Authorization token, one can pass the token under the token key in the Socket constructor options. Due to a limitation of the WebSocket available in browsers, it's not possible to add custom headers to a websocket open request, so we handle this in high-templar.

The high-templar instance will add a header Authorization: Token ${token} when authenticating against the binder instance.

api.socket = new Socket({
    url: WEBSOCKET_URL,
    token: this.authToken.value,
});