@eclipse-ditto/ditto-javascript-client-dom
Version:
DOM implementation of Eclipse Ditto JavaScript API to be used in browsers.
205 lines • 8.37 kB
JavaScript
/*!
* Copyright (c) 2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
import { RequestSender } from './request-sender';
import { StandardSubscription } from './websocket-request-handler';
import { ContentType } from '../constants/content-type';
import { Header } from '../constants/header';
import { HttpVerb } from '../constants/http-verb';
import { DittoAction } from '../constants/ditto-actions';
/**
* Handle to send web socket requests.
*/
export class WebSocketRequestSender extends RequestSender {
constructor(requester, group, channel, apiVersion) {
super();
this.requester = requester;
this.group = group;
this.channel = channel;
this.apiVersion = apiVersion;
}
static buildPath(path) {
if (path !== undefined) {
return path.startsWith('/') ? path : `/${path}`;
}
return '/';
}
/**
* Builds the path to use for a Message web socket request.
*
* @param path - The path to the entity within the basic object.
* @param mailbox - The direction the message is sent in (eg. inbox).
* @param subject - The subject of the message.
* @returns The path for a Message request
*/
static buildMessagePath(path, mailbox, subject) {
return `${path !== undefined ? path : ''}/${mailbox}/messages/${subject}`;
}
/**
* Splits an ID into name and namespace. Element 0 is the namespace and element 1 is the name.
*
* @param idWithNamespace - The id to separate.
* @returns The Array containing namespace and name
*/
static separateNamespace(idWithNamespace) {
if (idWithNamespace !== undefined) {
const splitName = idWithNamespace.split(':');
const namespace = splitName[0];
splitName.shift();
const name = splitName.join('');
return [namespace, name];
}
return ['', ''];
}
/**
* Translate a verb from HTTP (eg. GET) into web socket form (eg. retrieve).
*
* @param verb - The verb to translate.
* @returns The translated verb
*/
static translateVerb(verb) {
switch (verb) {
case HttpVerb.POST:
return DittoAction.CREATE;
case HttpVerb.GET:
return DittoAction.RETRIEVE;
case HttpVerb.PUT:
return DittoAction.MODIFY;
case HttpVerb.PATCH:
return DittoAction.MERGE;
case HttpVerb.DELETE:
return DittoAction.DELETE;
default:
return verb.toLowerCase();
}
}
/**
* The basic headers needed for every web socket request.
*
* @returns The headers
*/
get baseHeaders() {
return { version: this.apiVersion };
}
fetchRequest(options) {
const topic = this.buildTopic(options.id, this.group, 'commands', WebSocketRequestSender.translateVerb(options.verb));
const path = WebSocketRequestSender.buildPath(options.path);
const requestOptions = options === null || options === void 0 ? void 0 : options.requestOptions;
const headers = this.buildHeaders(requestOptions, (requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.getHeaders().has(Header.CONTENT_TYPE)) === true ? undefined : { [Header.CONTENT_TYPE]: ContentType.JSON });
return this.requester.sendRequest(topic, path, options.payload, headers)
.then(response => {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response);
}
return Promise.reject(response.body);
});
}
/**
* Fetches the specified request and checks if the request was successful.
*
* @param options - The options to use for the request.
* @returns A Promise for the response
*/
fetchGenericJsonRequest(options) {
return this.fetchRequest(options);
}
/**
* Sends a Message and returns the response.
*
* @param options - The options to use for the Message.
* @returns A Promise for the response
*/
sendMessageWithResponse(options) {
const topic = this.buildMessageTopic(options.id, options.messageSubject);
const path = WebSocketRequestSender.buildMessagePath(options.path, options.direction, options.messageSubject);
const headers = this.buildHeaders(options.options, { [Header.CONTENT_TYPE]: options.contentType });
return this.requester.sendRequest(topic, path, options.message, headers);
}
/**
* Sends a Message.
*
* @param options - The options to use for the Message.
* @returns A Promise that resolves once the Message was sent
*/
sendMessage(options) {
const topic = this.buildMessageTopic(options.id, options.messageSubject);
const path = WebSocketRequestSender.buildMessagePath(options.path, options.direction, options.messageSubject);
const headers = this.buildHeaders(options.options, { [Header.CONTENT_TYPE]: options.contentType });
return this.requester.sendMessage(topic, path, options.message, headers);
}
/**
* Registers a subscription.
*
* @param options - The options to use for the subscription.
* @returns The id of the subscription. It can be used to delete the subscription.
*/
subscribe(options) {
const topic = this.buildTopic(options.id, 'things', options.type, options.action !== undefined ? options.action : '');
const path = options.path !== undefined ? options.path : '';
const subResources = typeof options.subResources === 'boolean' ? options.subResources : true;
return this.requester.subscribe(new StandardSubscription(options.callback, topic, path, subResources));
}
/**
* Builds headers to use for a web socket request. It combines base headers, options headers and then additional headers
*
* @param options - The options to provided in the request.
* @param additionalHeaders - Additional headers to add.
* @returns The combined headers
*/
buildHeaders(options, additionalHeaders) {
let headers = this.baseHeaders;
if (options !== undefined) {
const headersMap = options.getHeaders();
headersMap.forEach((v, k) => headers[k] = v);
headers = Object.assign(headers, additionalHeaders);
return headers;
}
return additionalHeaders === undefined ? this.baseHeaders : Object.assign(headers, additionalHeaders);
}
/**
* Builds the topic to use for a Message web socket request.
*
* @param id - The id of the basic entity the request is for.
* @param messageSubject - The subject of the message.
* @returns The topic for a Message request
*/
buildMessageTopic(id, messageSubject) {
return this.buildTopic(id, 'things', 'messages', messageSubject);
}
/**
* Builds the topic to use for a web socket request.
*
* @param id - The id of the basic entity the request is for.
* @param group - The group of the request (eg. things).
* @param criterion - The area of the request (eg. commands).
* @param action - The action to perform.
* @returns The topic for a request
*/
buildTopic(id, group, criterion, action) {
const splitName = WebSocketRequestSender.separateNamespace(id);
return `${splitName[0]}/${splitName[1]}/${group}/${this.channel}/${criterion}/${action}`;
}
}
/**
* A Factory for a WebSocketRequestSender.
*/
export class WebSocketRequestSenderFactory {
constructor(apiVersion, channel, requester) {
this.apiVersion = apiVersion;
this.channel = channel;
this.requester = requester;
}
buildInstance(group) {
return new WebSocketRequestSender(this.requester, group, this.channel, this.apiVersion);
}
}
//# sourceMappingURL=websocket-request-sender.js.map