@iotize/tap
Version:
IoTize Device client for Javascript
608 lines (597 loc) • 21.6 kB
JavaScript
import { QueueComProtocol } from '@iotize/tap/protocol/core';
import { Subject, of, throwError, from } from 'rxjs';
import { delay, first, timeout } from 'rxjs/operators';
import { createDebugger } from '@iotize/common/debug';
import { hexStringToBuffer } from '@iotize/common/byte-converter';
import { TapRequestFrame, ResultCode } from '@iotize/tap/client/api';
import { TapRequestFrameBuilder, TapRequestHelper, TapClient, TapApduRequestConverter, TapApduResponseConverter, TapClientError, TapResponseFrameBuilder } from '@iotize/tap/client/impl';
import { promiseDelay } from '@iotize/common/promise';
import { TapResponse, ServiceCallRunner, Tap } from '@iotize/tap';
import '@iotize/tap/service/core';
const prefix = '@iotize/tap/testing';
const debug = createDebugger(prefix);
class MockRouter {
constructor() {
this._routes = [];
}
// constructor() {
// this._routes = [];
// }
get routes() {
return this._routes;
}
findRoute(request) {
const candidates = this._routes.filter((r) => r.predicate(request));
if (candidates.length === 0) {
return undefined;
}
else {
return candidates[0].adapter(request);
}
}
// addRoutes(map: RouteMapping) {
// // throw new Error('Method not implemented.');
// // TODO
// }
addRoute(predicate, adapter) {
// var filter: Predicate<Command>;
// if (typeof left === 'string') {
// left = Util.parseApiRequestString(left);
// }
// if (left instanceof ApiRequest) {
// filter = new EqualRequestFilter(left);
// } else if (left instanceof RegExp) {
// filter = new RegExpFilter(left);
// } else if (typeof left === 'function') {
// filter = {
// test: left
// };
// } else {
// filter = left as Predicate<Command>;
// }
// var adapter: ResponseAdapter<T>;
// if (right instanceof ResponseImpl) {
// adapter = new (class Tmp implements ResponseAdapter<T> {
// public adapt(command: Command) {
// return right;
// }
// })();
// } else if (right instanceof Uint8Array) {
// adapter = new (class Tmp implements ResponseAdapter<T> {
// public adapt(command: Command) {
// return new ResponseImpl<T>(right);
// }
// })();
// } else if (typeof right === 'function') {
// adapter = {
// adapt: right
// };
// } else {
// adapter = right as ResponseAdapter<T>;
// }
this._routes.push({
predicate,
adapter,
});
return this;
}
// /**
// * Mapping a command (represented as a string with format "<request-type> <path> [data]"
// * ie: GET /3//3 0xABCD or POST /1024//0 0xtest
// *
// * TapResponse must either be:
// * - an hexadecimal data represented as a string (0xABCD)
// * - An object with a coreRet and a body
// * - A Uint8Array
// */
// addRoutes(map: { [key: string]: RouteType }): this {
// for (const pathString in map) {
// const info = map[pathString];
// const command: Command = Util.parseApiRequestString(pathString);
// let response;
// if (info instanceof Uint8Array) {
// response = new ResponseImpl(info);
// } else if (info instanceof ResponseImpl) {
// response = info;
// } else if (typeof info === 'number') {
// response = ResponseImpl.create(info);
// } else if (typeof info === 'string') {
// response = new ResponseImpl(
// hexStringToBuffer(info)
// );
// } else if (typeof info === 'object') {
// const codeRet: number = (info as RouteTypeObject).codeRet;
// response = new ResponseImpl(
// KaitaiStreamWriter.mergeArrays(
// Uint8Array.from([codeRet]),
// (info as RouteTypeObject).body || new Uint8Array(0)
// )
// );
// } else {
// throw new Error(
// `Invalid response type ${typeof info} with value: ${JSON.stringify(
// info
// )}`
// );
// }
// debug(TAG, `\t- Add map ${command} -> ${response}`);
// this.addRoute(command, response);
// }
// return this;
// }
clearRoutes() {
this._routes = [];
return this;
}
}
// /**
// * @deprecated in favor of {@link #addRoute}
// */
// public mapResponse<T>(
// left:
// | FilterFunction<TapRequestFrame>
// | Predicate<TapRequestFrame>
// | TapRequestFrame,
// right:
// | ResponseAdapterFunction<T>
// | ResponseAdapter<T>
// | TapResponse<T>
// | Uint8Array
// ): this {
// return this.addRoute(left, right);
// }
// public addRoute<T>(
// left:
// | FilterFunction<TapRequestFrame>
// | Predicate<TapRequestFrame>
// | TapRequestFrame,
// right:
// | ResponseAdapterFunction<T>
// | ResponseAdapter<T>
// | TapResponse<T>
// | Uint8Array
// ): this {
// this.router.addRoute(left, right);
// return this;
// }
// // public Configuration getConfiguration() {
// // return configuration;
// // }
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
const TAG$1 = 'MockProtocol';
function createMockProtocol() {
return new MockProtocol();
}
/**
*
* <p>
* This is a mock com protocol for testing only
* <p>
* We can simulate iotize response according to the request message
*/
class MockProtocol extends QueueComProtocol {
constructor() {
super();
this.mockOptions = {
connect: {
delay: 100,
},
disconnect: {
delay: 100,
},
write: {
delay: 1,
},
};
this.input = new Subject();
this.output = new Subject();
this._router = new MockRouter();
// this.connectDelay = 2000;
// this.disconnectDelay = 2000;
// this.callback = callback;
// this.lastReceivedData = new Uint8Array(0);
// this.configuration = new Configuration();
// this.configuration.connectionTimeoutMillis = 3000;
// this.configuration.sendTimeoutMillis = 3000;
}
static MIRROR() {
const mockProtocol = new MockProtocol();
mockProtocol.input.subscribe((input) => {
mockProtocol.setOutputData(input);
});
// mockProtocol.router.addRoute(
// (command: Uint8Array) => {
// return true;
// },
// (request: Uint8Array) => {
// return request;
// }
// );
return mockProtocol;
}
// /**
// * Create a new instance from roote mapping
// */
// public static createFromRoutes(map: RouteMapping): MockProtocol {
// const mockProtocol = new MockProtocol();
// mockProtocol.router.addRoutes(map);
// return mockProtocol;
// }
// public get router(): MockRouter {
// return this._router;
// }
setConnectionState(connectionState) {
debug(TAG$1, `setConnectionState: ${connectionState}`);
this.connectionState = connectionState;
return this;
}
setConnectDelay(connectDelay) {
this.mockOptions.connect.delay = connectDelay;
return this;
}
getDisconnectDelay() {
return this.mockOptions.disconnect.delay;
}
setDisconnectDelay(disconnectDelay) {
this.mockOptions.disconnect.delay = disconnectDelay;
return this;
}
setOutputData(inputData) {
this.lastTranseiveData = inputData;
this.output.next(inputData);
}
setInputData(outputData) {
this.lastReceivedData = outputData;
this.input.next(outputData);
}
setCallback(callback) {
this.callback = callback;
}
_connect(options) {
return of(undefined).pipe(delay(this.mockOptions.connect.delay));
}
_disconnect(options) {
return of(undefined).pipe(delay(this.mockOptions.disconnect.delay));
}
write(request) {
return __awaiter(this, void 0, void 0, function* () {
try {
if (this.callback) {
yield this.callback.beforeWrite(request);
}
// this.lastReceivedData = request;
// const response = this.router.findRoute(request);
// if (!response) {
// throw new Error(`Not route for request ${bufferToHexString(request)}`)
// }
// debug(
// TAG,
// 'MockProtocol',
// 'Mocking response for ' + request + ' => ' + response
// );
// var result = this.responseEncoder.encode(response);
this.setInputData(request);
if (this.callback) {
yield this.callback.afterWrite(request);
}
return new Promise((resolve) => setTimeout(resolve, this.mockOptions.write.delay));
}
catch (err) {
return new Promise((resolve, reject) => setTimeout(() => reject(err), this.mockOptions.write.delay));
}
});
}
read() {
try {
if (this.callback) {
this.callback.beforeRead();
}
return this.output
.pipe(first())
.toPromise()
.then((v) => (v ? v : new Uint8Array()));
// while (!this.hasNewData()) {
// // TODO wait ??
// }
// if (this.lastTranseiveData) {
// const result = of(this.lastTranseiveData);
// if (this.callback) {
// this.callback.afterRead(result);
// }
// return result.toPromise();
// } else {
// return Promise.reject(new Error('Read timeout'));
// }
}
catch (err) {
return Promise.reject(err);
}
}
}
// public addOnRequestListener() {
// this.mOnRequestListener =
// }
// public interface onRequestListener{
// onRequest(RequestMessage request);
// }
const TAP_REQUEST_STRING_REGEX = /^(GET|PUT|POST)\s+(\/[0-9]+\/[0-9]*\/[0-9]+)\s?(\s+(0x[a-zA-Z0-9]+))?$/i;
function createTapRequestFromString(request) {
const match = TAP_REQUEST_STRING_REGEX.exec(request);
if (match) {
const methodType = match[1];
const path = match[2];
const body = match[4] ? hexStringToBuffer(match[4]) : undefined;
return TapRequestFrameBuilder.create(TapRequestFrame.MethodType[methodType], path, body);
}
else {
throw new Error(`Invalid tap request string format: "${request}".`);
}
}
const tapRequestEqualFilter = (expectedRequest) => {
return (request) => {
return (TapRequestHelper.toString(expectedRequest) ===
TapRequestHelper.toString(request));
};
};
const ɵ0 = tapRequestEqualFilter;
const TAG = 'MockClient';
function createMockClient(options) {
return new MockClient({
isConnected: !!(options === null || options === void 0 ? void 0 : options.connected),
});
}
class MockClient extends TapClient {
constructor(options = {}, router = new MockRouter()) {
super(new TapApduRequestConverter(), new TapApduResponseConverter());
this.router = router;
this.input = new Subject();
this.output = new Subject();
this.options = Object.assign(Object.assign({}, MockClient.DEFAULT_OPTIONS), options);
}
isConnected() {
return this.options.isConnected;
}
// /**
// *
// * TOPO add support for parameters ?
// * TODO use Shell Grammar instead ?
// *
// * @param map
// */
// public static createFromMap(map: MappingType): MockClient {
// const client = new MockClient();
// client.addRoutes(map);
// return client;
// }
static createWithMockProtocol() {
const client = new MockClient({
isConnected: false,
});
client.addComProtocol(createMockProtocol());
return client;
}
connect() {
const observable = of(undefined).pipe(delay(this.options.connect.delay));
observable.subscribe(() => {
this.options.isConnected = true;
});
return observable;
}
disconnect() {
const observable = of(undefined).pipe(delay(this.options.disconnect.delay));
observable.subscribe(() => {
this.options.isConnected = false;
});
return observable;
}
mockResponse(tapResponse) {
this.output.next(tapResponse);
}
request(request) {
if (!this.isConnected()) {
return throwError(TapClientError.notConnectedError());
}
const responseP = this.output
.pipe(
// tap(event => console.log('EVENT', event)),
first(), delay(this.options.command.delay), timeout(this.options.command.timeout))
.toPromise();
this.input.next(request);
return from(responseP);
}
// /**
// * @deprecated in favor of addRoute
// * @param cmd
// * @param response
// */
// public mapCommandToResponse(cmd: Command, response: TapResponse<any>) {
// return this.addRoute(cmd, response);
// }
// public addRoute<T>(
// command: Command | string | RegExp | TapRequestFilter,
// response: ResponseMapType
// ): this {
// debug(
// TAG,
// `\t- Mapping command ${command.toString()} to response ${response.toString()}`
// );
// if (command instanceof RegExp) {
// throw new Error(
// `Route with regular expressions are not implemented yet`
// );
// } else if (typeof command === 'string') {
// command = Util.parseApiRequestString(command);
// }
// this.router.addRoute(
// command,
// this.createResponse(command, response)
// );
// return this;
// }
// public addRoutes(map: MappingType): this {
// for (const pathString in map) {
// const info = map[pathString];
// const command: Command = Util.parseApiRequestString(pathString);
// const response = this.createResponse(command, info);
// this.addRoute(command, response);
// }
// return this;
// }
// public hasRoute(command: Command | string): boolean {
// if (typeof command === 'string') {
// command = Util.parseApiRequestString(command);
// }
// return this.router.findRoute(command) != undefined;
// }
// createResponse<T>(
// command: Command | TapRequestFilter,
// info: ResponseMapType
// ): TapResponse<T> | ResponseAdapterFunction<any> {
// let response: TapResponse<T>;
// if (typeof info === 'number') {
// response = TapResponse.create(info);
// } else if (typeof info === 'string') {
// response = new TapResponse(hexStringToBuffer(info));
// } else if (typeof info === 'object') {
// if (info instanceof TapResponse) {
// response = info;
// } else {
// response = TapResponse.create(typeof info.codeRet === 'number' ? info.codeRet : info.status);
// (response as TapResponse<T>).setBody(info.body);
// }
// } else if (typeof info === 'function') {
// if (command instanceof ApiRequest) {
// response = info(command);
// } else {
// return (command: TapRequestFrame) => {
// const response = info(command);
// if (response instanceof ResponseImpl) {
// response.setRequest(command);
// }
// return response;
// };
// }
// } else {
// throw new Error(
// `Invalid payload type from key ${command.toString()}`
// );
// }
// if (command instanceof ApiRequest && response instanceof ResponseImpl) {
// response.setRequest(command);
// }
// return response;
// }
computeResponse(cmd) {
const response = this.router.findRoute(cmd);
if (response) {
return response;
}
else {
debug(TAG, `MockClient`, `command ${cmd} has not been mapped. Returning 404 not found`);
return TapResponseFrameBuilder.ERROR(ResultCode.NOT_FOUND);
}
}
}
MockClient.DEFAULT_OPTIONS = {
isConnected: false,
connect: {
delay: 100,
},
disconnect: {
delay: 100,
},
command: {
delay: 100,
timeout: 500,
},
encryption: true,
};
function createMockServiceFunctionCustom(responseBuilder, options = {
responseDelay: 10,
}) {
return (...args) => {
return promiseDelay(responseBuilder(...args), options.responseDelay);
};
}
function createMockServiceFunction(responseStatus = ResultCode.OK, options = {
responseDelay: 10,
}) {
return () => {
const response = createMockServiceResponse(responseStatus);
return promiseDelay(response, options.responseDelay);
};
}
function createMockServiceFunctionWithBody(responseStatus = ResultCode.OK, body, options = {
responseDelay: 10,
}) {
return () => {
const response = createMockServiceResponseWithBody(responseStatus, body);
return promiseDelay(response, options.responseDelay);
};
}
function createMockServiceResponse(responseStatus = ResultCode.OK) {
const response = new TapResponse(TapResponseFrameBuilder.create(responseStatus));
return response;
}
function createMockServiceResponseWithBody(responseStatus = ResultCode.OK, body) {
const response = new TapResponse(TapResponseFrameBuilder.create(responseStatus));
if (body instanceof Uint8Array) {
response['_tapResponse'].data = body;
}
if (body) {
response.setBody(body);
}
return response;
}
function createMockServiceResponseError(code) {
const response = new TapResponse(TapResponseFrameBuilder.ERROR(code));
return response;
}
function createMockService(constructorFct) {
const serviceCallRunnner = new MockServiceCallRunner();
return new constructorFct(serviceCallRunnner);
}
class MockServiceCallRunner extends ServiceCallRunner {
// TODO
constructor() {
super(new MockClient());
}
}
function createMockTap(options) {
return new MockTap(options);
}
/**
* @experimental
*/
class MockTap extends Tap {
get client() {
return this._client;
}
get lwm2m() {
return this._lwm2m;
}
constructor(options) {
super(createMockClient(options));
}
}
"use strict";
Array.prototype.findSafe = function (predicate) {
const val = this.find(predicate);
if (val === undefined) {
throw new Error(`Cannot find element with predicate: ${predicate}`);
}
return val;
};
/**
* Generated bundle index. Do not edit.
*/
export { MockClient, MockProtocol, MockServiceCallRunner, MockTap, createMockClient, createMockProtocol, createMockService, createMockServiceFunction, createMockServiceFunctionCustom, createMockServiceFunctionWithBody, createMockServiceResponse, createMockServiceResponseError, createMockServiceResponseWithBody, createMockTap, createTapRequestFromString, ɵ0, MockRouter as ɵa };
//# sourceMappingURL=iotize-tap-testing.js.map