UNPKG

@iotize/tap

Version:

IoTize Device client for Javascript

608 lines (597 loc) 21.6 kB
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