UNPKG

filestack-js

Version:

Official JavaScript library for Filestack

356 lines (354 loc) 44.4 kB
"use strict"; /* * Copyright (c) 2018 by Filestack. * Some rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Copyright (c) 2018 by Filestack. * Some rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var file_1 = require("./file"); var request_1 = require("./../request"); var config_1 = require("./../../config"); jest.mock('./../request'); // jest.mock('./../filelink'); var mockedSession = { apikey: 'fakeApikey', urls: Object.assign({}, config_1.config.urls, { cdnUrl: 'fakeUrl', fileApiUrl: 'fakeApiUrl', uploadApiUrl: 'fakeUploadApiUrl', cloudApiUrl: 'fakeCloudApiUrl', pickerUrl: 'fakePickerUrl', }), }; describe('FileAPI', function () { describe('Metadata', function () { it('should call correct metadata without options', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var methodMocked, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: methodMocked = jest.fn(function () { return Promise.resolve({ data: {} }); }); // @ts-ignore request_1.FsRequest.get.mockImplementation(methodMocked); return [4 /*yield*/, (0, file_1.metadata)(mockedSession, 'fakeHandle')]; case 1: resp = _a.sent(); expect(resp).toEqual({ handle: 'fakeHandle' }); expect(methodMocked).toHaveBeenLastCalledWith('fakeApiUrl/fakeHandle/metadata', { params: {}, filestackHeaders: false }); return [2 /*return*/]; } }); }); }); it('should call correct metadata with options', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var methodMocked, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: methodMocked = jest.fn(function () { return Promise.resolve({ data: {} }); }); // @ts-ignore request_1.FsRequest.get.mockImplementation(methodMocked); return [4 /*yield*/, (0, file_1.metadata)(mockedSession, 'fakeHandle', { size: true })]; case 1: resp = _a.sent(); expect(resp).toEqual({ handle: 'fakeHandle' }); expect(methodMocked).toHaveBeenLastCalledWith('fakeApiUrl/fakeHandle/metadata', { params: { size: true }, filestackHeaders: false }); return [2 /*return*/]; } }); }); }); it('should throw on wrong option', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) { // @ts-ignore return [2 /*return*/, expect(function () { return (0, file_1.metadata)(mockedSession, 'fakekey', { bla: 123 }); }).toThrowError('Invalid metadata params')]; }); }); }); it('should throw an error on empty handle', function () { return expect(function () { return (0, file_1.metadata)(mockedSession); }).toThrowError(); }); it('should respect provided security options', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var fakeSecurity, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: fakeSecurity = { signature: 'fakeS', policy: 'fakeP', }; // @ts-ignore request_1.FsRequest.get.mockImplementation(function () { return Promise.resolve({ data: {} }); }); return [4 /*yield*/, (0, file_1.metadata)(mockedSession, 'fakeHandle', {}, fakeSecurity)]; case 1: resp = _a.sent(); expect(resp).toEqual({ handle: 'fakeHandle' }); return [2 /*return*/]; } }); }); }); }); describe('Download', function () { it('should return buffer on download request', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var testResp, methodMocked, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: testResp = Buffer.from('123'); methodMocked = jest.fn(function () { return Promise.resolve(testResp); }); // @ts-ignore request_1.FsRequest.dispatch.mockImplementation(methodMocked); return [4 /*yield*/, (0, file_1.download)(mockedSession, 'gNDNCDWNTKqoGFISdd2A')]; case 1: resp = _a.sent(); expect(resp).toEqual(testResp); return [2 /*return*/]; } }); }); }); }); describe('Remove', function () { it('should call remove', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var deleteMocked, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: deleteMocked = jest.fn(function () { return Promise.resolve({ data: {} }); }); // @ts-ignore request_1.FsRequest.delete.mockImplementation(deleteMocked); return [4 /*yield*/, (0, file_1.remove)(Object.assign({}, mockedSession, { signature: 'fakeS', policy: 'fakeP', }), 'fakeHandle')]; case 1: resp = _a.sent(); expect(resp).toEqual({ data: {} }); expect(deleteMocked).toHaveBeenCalledWith('fakeApiUrl/fakeHandle', { params: { key: 'fakeApikey', policy: 'fakeP', signature: 'fakeS' }, filestackHeaders: false }); return [2 /*return*/]; } }); }); }); it('should respect skip storage option', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var methodMocked, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: methodMocked = jest.fn(function () { return Promise.resolve({ data: {} }); }); // @ts-ignore request_1.FsRequest.delete.mockImplementation(methodMocked); return [4 /*yield*/, (0, file_1.remove)(Object.assign({}, mockedSession, { signature: 'fakeS', policy: 'fakeP', }), 'fakeHandle', true)]; case 1: resp = _a.sent(); expect(resp).toEqual({ data: {} }); expect(methodMocked).toHaveBeenCalledWith('fakeApiUrl/fakeHandle', { params: { key: 'fakeApikey', policy: 'fakeP', signature: 'fakeS', skip_storage: true }, filestackHeaders: false }); return [2 /*return*/]; } }); }); }); it('should throw on empty handle', function () { expect(function () { return (0, file_1.remove)(mockedSession); }).toThrowError(); }); it('should call remove with provided session', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var fakeSecurity, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: fakeSecurity = { signature: 'fakeS', policy: 'fakeP', }; // @ts-ignore request_1.FsRequest.delete.mockImplementation(function () { return Promise.resolve({ data: {} }); }); return [4 /*yield*/, (0, file_1.remove)(mockedSession, 'fakeHandle', false, fakeSecurity)]; case 1: resp = _a.sent(); expect(resp).toEqual({ data: {} }); return [2 /*return*/]; } }); }); }); it('should throw on empty signature', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var fakeSecurity; return tslib_1.__generator(this, function (_a) { fakeSecurity = { signature: null, policy: 'fakeP', }; expect(function () { return (0, file_1.remove)(mockedSession, 'fakeHandle', false, fakeSecurity); }).toThrowError(); return [2 /*return*/]; }); }); }); it('should throw on empty policy', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var fakeSecurity; return tslib_1.__generator(this, function (_a) { fakeSecurity = { signature: 'fakeS', policy: null, }; expect(function () { return (0, file_1.remove)(mockedSession, 'fakeHandle', false, fakeSecurity); }).toThrowError(); return [2 /*return*/]; }); }); }); it('should throw on empty policy on session', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) { return [2 /*return*/, expect(function () { return (0, file_1.remove)(mockedSession, 'fakeHandle'); }).toThrowError()]; }); }); }); }); describe('Retrieve', function () { it('should make correct retrieve request (GET)', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var methodMocked, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: methodMocked = jest.fn(function () { return Promise.resolve({ data: {} }); }); // @ts-ignore request_1.FsRequest.dispatch.mockImplementation(methodMocked); return [4 /*yield*/, (0, file_1.retrieve)(mockedSession, 'fakeHandle')]; case 1: resp = _a.sent(); expect(resp).toEqual({}); expect(methodMocked).toHaveBeenCalledWith('fakeApiUrl/fakeHandle', { method: 'GET', params: { key: 'fakeApikey' }, filestackHeaders: false }); return [2 /*return*/]; } }); }); }); it('should make correct retrieve request (HEAD)', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var methodMocked, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: methodMocked = jest.fn(function () { return Promise.resolve({ data: {}, headers: { type: 'test' } }); }); // @ts-ignore request_1.FsRequest.dispatch.mockImplementation(methodMocked); return [4 /*yield*/, (0, file_1.retrieve)(mockedSession, 'fakeHandle', { head: true, })]; case 1: resp = _a.sent(); expect(resp).toEqual({ type: 'test' }); expect(methodMocked).toHaveBeenCalledWith('fakeApiUrl/fakeHandle', { method: 'HEAD', params: { key: 'fakeApikey' }, filestackHeaders: false }); return [2 /*return*/]; } }); }); }); it('should make correct retrieve request with provided security', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var methodMocked, fakeSecurity, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: methodMocked = jest.fn(function () { return Promise.resolve({ data: {} }); }); fakeSecurity = { signature: 'fakeS', policy: 'fakeP', }; // @ts-ignore request_1.FsRequest.dispatch.mockImplementation(methodMocked); return [4 /*yield*/, (0, file_1.retrieve)(mockedSession, 'fakeHandle', {}, fakeSecurity)]; case 1: resp = _a.sent(); expect(resp).toEqual({}); expect(methodMocked).toHaveBeenCalledWith('fakeApiUrl/fakeHandle', { method: 'GET', params: { key: 'fakeApikey', policy: 'fakeP', signature: 'fakeS' }, filestackHeaders: false, }); return [2 /*return*/]; } }); }); }); it('should make correct retrieve request with extension', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var methodMocked, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: methodMocked = jest.fn(function () { return Promise.resolve({ data: {} }); }); // @ts-ignore request_1.FsRequest.dispatch.mockImplementation(methodMocked); return [4 /*yield*/, (0, file_1.retrieve)(mockedSession, 'fakeHandle', { extension: 'txt', })]; case 1: resp = _a.sent(); expect(resp).toEqual({}); expect(methodMocked).toHaveBeenCalledWith('fakeApiUrl/fakeHandle+txt', { method: 'GET', params: { key: 'fakeApikey' }, filestackHeaders: false }); return [2 /*return*/]; } }); }); }); it('should make correct retrieve request with metadata', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var methodMocked, resp; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: methodMocked = jest.fn(function () { return Promise.resolve({ data: {} }); }); // @ts-ignore request_1.FsRequest.dispatch.mockImplementation(methodMocked); return [4 /*yield*/, (0, file_1.retrieve)(mockedSession, 'fakeHandle', { metadata: true, })]; case 1: resp = _a.sent(); expect(resp).toEqual({}); expect(methodMocked).toHaveBeenCalledWith('fakeApiUrl/fakeHandle/metadata', { method: 'GET', params: { key: 'fakeApikey' }, filestackHeaders: false }); return [2 /*return*/]; } }); }); }); it('should throw an error on empty handle', function () { return expect(function () { return (0, file_1.retrieve)(mockedSession, ''); }).toThrowError(); }); it('should throw an error worng options provided', function () { return expect(function () { return (0, file_1.retrieve)(mockedSession, 'fakeHandle', { // @ts-ignore test: 123, }); }).toThrowError('Invalid retrieve params'); }); it('should not throw an error worng options provided', function () { return expect(function () { return (0, file_1.retrieve)(mockedSession, 'fakeHandle', { metadata: true, }); }).not.toThrowError('Invalid retrieve params'); }); it('should throw an error when metadata and head is provided', function () { return expect(function () { return (0, file_1.retrieve)(mockedSession, 'fakeHandle', { metadata: true, head: true, }); }).toThrowError(); }); }); }); //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9saWIvYXBpL2ZpbGUuc3BlYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0g7Ozs7Ozs7Ozs7Ozs7OztHQWVHOzs7QUFFSCwrQkFBOEQ7QUFDOUQsd0NBQXlDO0FBRXpDLHlDQUF3QztBQUV4QyxJQUFJLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0FBQzFCLDhCQUE4QjtBQUU5QixJQUFNLGFBQWEsR0FBWTtJQUM3QixNQUFNLEVBQUUsWUFBWTtJQUNwQixJQUFJLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUFFLEVBQUUsZUFBTSxDQUFDLElBQUksRUFBRTtRQUNuQyxNQUFNLEVBQUUsU0FBUztRQUNqQixVQUFVLEVBQUUsWUFBWTtRQUN4QixZQUFZLEVBQUUsa0JBQWtCO1FBQ2hDLFdBQVcsRUFBRSxpQkFBaUI7UUFDOUIsU0FBUyxFQUFFLGVBQWU7S0FDM0IsQ0FBQztDQUNILENBQUM7QUFFRixRQUFRLENBQUMsU0FBUyxFQUFFO0lBQ2xCLFFBQVEsQ0FBQyxVQUFVLEVBQUU7UUFDbkIsRUFBRSxDQUFDLDhDQUE4QyxFQUFFOzs7Ozt3QkFDM0MsWUFBWSxHQUFHLElBQUksQ0FBQyxFQUFFLENBQUMsY0FBTSxPQUFBLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBN0IsQ0FBNkIsQ0FBQyxDQUFDO3dCQUNsRSxhQUFhO3dCQUNiLG1CQUFTLENBQUMsR0FBRyxDQUFDLGtCQUFrQixDQUFDLFlBQVksQ0FBQyxDQUFDO3dCQUNsQyxxQkFBTSxJQUFBLGVBQVEsRUFBQyxhQUFhLEVBQUUsWUFBWSxDQUFDLEVBQUE7O3dCQUFsRCxJQUFJLEdBQUcsU0FBMkM7d0JBRXhELE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLENBQUMsQ0FBQzt3QkFDL0MsTUFBTSxDQUFD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O2lCQUNYLENBQUM7WUFIRixDQUdFLENBQ0gsQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUNuQixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyxDQUFDLENBQUMiLCJmaWxlIjoibGliL2FwaS9maWxlLnNwZWMuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChjKSAyMDE4IGJ5IEZpbGVzdGFjay5cbiAqIFNvbWUgcmlnaHRzIHJlc2VydmVkLlxuICpcbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuLypcbiAqIENvcHlyaWdodCAoYykgMjAxOCBieSBGaWxlc3RhY2suXG4gKiBTb21lIHJpZ2h0cyByZXNlcnZlZC5cbiAqXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cblxuaW1wb3J0IHsgcmV0cmlldmUsIHJlbW92ZSwgbWV0YWRhdGEsIGRvd25sb2FkIH0gZnJvbSAnLi9maWxlJztcbmltcG9ydCB7IEZzUmVxdWVzdCB9IGZyb20gJy4vLi4vcmVxdWVzdCc7XG5pbXBvcnQgeyBTZXNzaW9uIH0gZnJvbSAnLi4vY2xpZW50JztcbmltcG9ydCB7IGNvbmZpZyB9IGZyb20gJy4vLi4vLi4vY29uZmlnJztcblxuamVzdC5tb2NrKCcuLy4uL3JlcXVlc3QnKTtcbi8vIGplc3QubW9jaygnLi8uLi9maWxlbGluaycpO1xuXG5jb25zdCBtb2NrZWRTZXNzaW9uOiBTZXNzaW9uID0ge1xuICBhcGlrZXk6ICdmYWtlQXBpa2V5JyxcbiAgdXJsczogT2JqZWN0LmFzc2lnbih7fSwgY29uZmlnLnVybHMsIHtcbiAgICBjZG5Vcmw6ICdmYWtlVXJsJyxcbiAgICBmaWxlQXBpVXJsOiAnZmFrZUFwaVVybCcsXG4gICAgdXBsb2FkQXBpVXJsOiAnZmFrZVVwbG9hZEFwaVVybCcsXG4gICAgY2xvdWRBcGlVcmw6ICdmYWtlQ2xvdWRBcGlVcmwnLFxuICAgIHBpY2tlclVybDogJ2Zha2VQaWNrZXJVcmwnLFxuICB9KSxcbn07XG5cbmRlc2NyaWJlKCdGaWxlQVBJJywgKCkgPT4ge1xuICBkZXNjcmliZSgnTWV0YWRhdGEnLCAoKSA9PiB7XG4gICAgaXQoJ3Nob3VsZCBjYWxsIGNvcnJlY3QgbWV0YWRhdGEgd2l0aG91dCBvcHRpb25zJywgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgbWV0aG9kTW9ja2VkID0gamVzdC5mbigoKSA9PiBQcm9taXNlLnJlc29sdmUoeyBkYXRhOiB7fSB9KSk7XG4gICAgICAvLyBAdHMtaWdub3JlXG4gICAgICBGc1JlcXVlc3QuZ2V0Lm1vY2tJbXBsZW1lbnRhdGlvbihtZXRob2RNb2NrZWQpO1xuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IG1ldGFkYXRhKG1vY2tlZFNlc3Npb24sICdmYWtlSGFuZGxlJyk7XG5cbiAgICAgIGV4cGVjdChyZXNwKS50b0VxdWFsKHsgaGFuZGxlOiAnZmFrZUhhbmRsZScgfSk7XG4gICAgICBleHBlY3QobWV0aG9kTW9ja2VkKS50b0hhdmVCZWVuTGFzdENhbGxlZFdpdGgoJ2Zha2VBcGlVcmwvZmFrZUhhbmRsZS9tZXRhZGF0YScsIHsgcGFyYW1zOiB7fSwgZmlsZXN0YWNrSGVhZGVyczogZmFsc2UgfSk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIGNhbGwgY29ycmVjdCBtZXRhZGF0YSB3aXRoIG9wdGlvbnMnLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBtZXRob2RNb2NrZWQgPSBqZXN0LmZuKCgpID0+IFByb21pc2UucmVzb2x2ZSh7IGRhdGE6IHt9IH0pKTtcbiAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgIEZzUmVxdWVzdC5nZXQubW9ja0ltcGxlbWVudGF0aW9uKG1ldGhvZE1vY2tlZCk7XG4gICAgICBjb25zdCByZXNwID0gYXdhaXQgbWV0YWRhdGEobW9ja2VkU2Vzc2lvbiwgJ2Zha2VIYW5kbGUnLCB7IHNpemU6IHRydWUgfSk7XG5cbiAgICAgIGV4cGVjdChyZXNwKS50b0VxdWFsKHsgaGFuZGxlOiAnZmFrZUhhbmRsZScgfSk7XG4gICAgICBleHBlY3QobWV0aG9kTW9ja2VkKS50b0hhdmVCZWVuTGFzdENhbGxlZFdpdGgoJ2Zha2VBcGlVcmwvZmFrZUhhbmRsZS9tZXRhZGF0YScsIHsgcGFyYW1zOiB7IHNpemU6IHRydWUgfSwgZmlsZXN0YWNrSGVhZGVyczogZmFsc2UgfSk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIHRocm93IG9uIHdyb25nIG9wdGlvbicsIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgIHJldHVybiBleHBlY3QoKCkgPT4gbWV0YWRhdGEobW9ja2VkU2Vzc2lvbiwgJ2Zha2VrZXknLCB7IGJsYTogMTIzIH0pKS50b1Rocm93RXJyb3IoJ0ludmFsaWQgbWV0YWRhdGEgcGFyYW1zJyk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIHRocm93IGFuIGVycm9yIG9uIGVtcHR5IGhhbmRsZScsICgpID0+IHtcbiAgICAgIHJldHVybiBleHBlY3QoKCkgPT4gbWV0YWRhdGEobW9ja2VkU2Vzc2lvbikpLnRvVGhyb3dFcnJvcigpO1xuICAgIH0pO1xuXG4gICAgaXQoJ3Nob3VsZCByZXNwZWN0IHByb3ZpZGVkIHNlY3VyaXR5IG9wdGlvbnMnLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBmYWtlU2VjdXJpdHkgPSB7XG4gICAgICAgIHNpZ25hdHVyZTogJ2Zha2VTJyxcbiAgICAgICAgcG9saWN5OiAnZmFrZVAnLFxuICAgICAgfTtcblxuICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgRnNSZXF1ZXN0LmdldC5tb2NrSW1wbGVtZW50YXRpb24oKCkgPT4gUHJvbWlzZS5yZXNvbHZlKHsgZGF0YToge30gfSkpO1xuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IG1ldGFkYXRhKG1vY2tlZFNlc3Npb24sICdmYWtlSGFuZGxlJywge30sIGZha2VTZWN1cml0eSk7XG5cbiAgICAgIGV4cGVjdChyZXNwKS50b0VxdWFsKHsgaGFuZGxlOiAnZmFrZUhhbmRsZScgfSk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCdEb3dubG9hZCcsICgpID0+IHtcbiAgICBpdCgnc2hvdWxkIHJldHVybiBidWZmZXIgb24gZG93bmxvYWQgcmVxdWVzdCcsIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IHRlc3RSZXNwID0gQnVmZmVyLmZyb20oJzEyMycpO1xuICAgICAgY29uc3QgbWV0aG9kTW9ja2VkID0gamVzdC5mbigoKSA9PiBQcm9taXNlLnJlc29sdmUodGVzdFJlc3ApKTtcblxuICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgRnNSZXF1ZXN0LmRpc3BhdGNoLm1vY2tJbXBsZW1lbnRhdGlvbihtZXRob2RNb2NrZWQpO1xuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IGRvd25sb2FkKG1vY2tlZFNlc3Npb24sICdnTkROQ0RXTlRLcW9HRklTZGQyQScpO1xuXG4gICAgICBleHBlY3QocmVzcCkudG9FcXVhbCh0ZXN0UmVzcCk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCdSZW1vdmUnLCAoKSA9PiB7XG4gICAgaXQoJ3Nob3VsZCBjYWxsIHJlbW92ZScsIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IGRlbGV0ZU1vY2tlZCA9IGplc3QuZm4oKCkgPT4gUHJvbWlzZS5yZXNvbHZlKHsgZGF0YToge30gfSkpO1xuXG4gICAgICAvLyBAdHMtaWdub3JlXG4gICAgICBGc1JlcXVlc3QuZGVsZXRlLm1vY2tJbXBsZW1lbnRhdGlvbihkZWxldGVNb2NrZWQpO1xuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IHJlbW92ZShcbiAgICAgICAgT2JqZWN0LmFzc2lnbih7fSwgbW9ja2VkU2Vzc2lvbiwge1xuICAgICAgICAgIHNpZ25hdHVyZTogJ2Zha2VTJyxcbiAgICAgICAgICBwb2xpY3k6ICdmYWtlUCcsXG4gICAgICAgIH0pLFxuICAgICAgICAnZmFrZUhhbmRsZSdcbiAgICAgICk7XG5cbiAgICAgIGV4cGVjdChyZXNwKS50b0VxdWFsKHsgZGF0YToge30gfSk7XG4gICAgICBleHBlY3QoZGVsZXRlTW9ja2VkKS50b0hhdmVCZWVuQ2FsbGVkV2l0aCgnZmFrZUFwaVVybC9mYWtlSGFuZGxlJywgeyBwYXJhbXM6IHsga2V5OiAnZmFrZUFwaWtleScsIHBvbGljeTogJ2Zha2VQJywgc2lnbmF0dXJlOiAnZmFrZVMnIH0sIGZpbGVzdGFja0hlYWRlcnM6IGZhbHNlIH0pO1xuICAgIH0pO1xuXG4gICAgaXQoJ3Nob3VsZCByZXNwZWN0IHNraXAgc3RvcmFnZSBvcHRpb24nLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBtZXRob2RNb2NrZWQgPSBqZXN0LmZuKCgpID0+IFByb21pc2UucmVzb2x2ZSh7IGRhdGE6IHt9IH0pKTtcblxuICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgRnNSZXF1ZXN0LmRlbGV0ZS5tb2NrSW1wbGVtZW50YXRpb24obWV0aG9kTW9ja2VkKTtcbiAgICAgIGNvbnN0IHJlc3AgPSBhd2FpdCByZW1vdmUoXG4gICAgICAgIE9iamVjdC5hc3NpZ24oe30sIG1vY2tlZFNlc3Npb24sIHtcbiAgICAgICAgICBzaWduYXR1cmU6ICdmYWtlUycsXG4gICAgICAgICAgcG9saWN5OiAnZmFrZVAnLFxuICAgICAgICB9KSxcbiAgICAgICAgJ2Zha2VIYW5kbGUnLFxuICAgICAgICB0cnVlXG4gICAgICApO1xuXG4gICAgICBleHBlY3QocmVzcCkudG9FcXVhbCh7IGRhdGE6IHt9IH0pO1xuICAgICAgZXhwZWN0KG1ldGhvZE1vY2tlZCkudG9IYXZlQmVlbkNhbGxlZFdpdGgoJ2Zha2VBcGlVcmwvZmFrZUhhbmRsZScsIHsgcGFyYW1zOiB7IGtleTogJ2Zha2VBcGlrZXknLCBwb2xpY3k6ICdmYWtlUCcsIHNpZ25hdHVyZTogJ2Zha2VTJywgc2tpcF9zdG9yYWdlOiB0cnVlIH0sIGZpbGVzdGFja0hlYWRlcnM6IGZhbHNlIH0pO1xuICAgIH0pO1xuXG4gICAgaXQoJ3Nob3VsZCB0aHJvdyBvbiBlbXB0eSBoYW5kbGUnLCAoKSA9PiB7XG4gICAgICBleHBlY3QoKCkgPT4gcmVtb3ZlKG1vY2tlZFNlc3Npb24pKS50b1Rocm93RXJyb3IoKTtcbiAgICB9KTtcblxuICAgIGl0KCdzaG91bGQgY2FsbCByZW1vdmUgd2l0aCBwcm92aWRlZCBzZXNzaW9uJywgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgZmFrZVNlY3VyaXR5ID0ge1xuICAgICAgICBzaWduYXR1cmU6ICdmYWtlUycsXG4gICAgICAgIHBvbGljeTogJ2Zha2VQJyxcbiAgICAgIH07XG5cbiAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgIEZzUmVxdWVzdC5kZWxldGUubW9ja0ltcGxlbWVudGF0aW9uKCgpID0+IFByb21pc2UucmVzb2x2ZSh7IGRhdGE6IHt9IH0pKTtcbiAgICAgIGNvbnN0IHJlc3AgPSBhd2FpdCByZW1vdmUobW9ja2VkU2Vzc2lvbiwgJ2Zha2VIYW5kbGUnLCBmYWxzZSwgZmFrZVNlY3VyaXR5KTtcblxuICAgICAgZXhwZWN0KHJlc3ApLnRvRXF1YWwoeyBkYXRhOiB7fSB9KTtcbiAgICB9KTtcblxuICAgIGl0KCdzaG91bGQgdGhyb3cgb24gZW1wdHkgc2lnbmF0dXJlJywgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgZmFrZVNlY3VyaXR5ID0ge1xuICAgICAgICBzaWduYXR1cmU6IG51bGwsXG4gICAgICAgIHBvbGljeTogJ2Zha2VQJyxcbiAgICAgIH07XG5cbiAgICAgIGV4cGVjdCgoKSA9PiByZW1vdmUobW9ja2VkU2Vzc2lvbiwgJ2Zha2VIYW5kbGUnLCBmYWxzZSwgZmFrZVNlY3VyaXR5KSkudG9UaHJvd0Vycm9yKCk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIHRocm93IG9uIGVtcHR5IHBvbGljeScsIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IGZha2VTZWN1cml0eSA9IHtcbiAgICAgICAgc2lnbmF0dXJlOiAnZmFrZVMnLFxuICAgICAgICBwb2xpY3k6IG51bGwsXG4gICAgICB9O1xuXG4gICAgICBleHBlY3QoKCkgPT4gcmVtb3ZlKG1vY2tlZFNlc3Npb24sICdmYWtlSGFuZGxlJywgZmFsc2UsIGZha2VTZWN1cml0eSkpLnRvVGhyb3dFcnJvcigpO1xuICAgIH0pO1xuXG4gICAgaXQoJ3Nob3VsZCB0aHJvdyBvbiBlbXB0eSBwb2xpY3kgb24gc2Vzc2lvbicsIGFzeW5jICgpID0+IHtcbiAgICAgIHJldHVybiBleHBlY3QoKCkgPT4gcmVtb3ZlKG1vY2tlZFNlc3Npb24sICdmYWtlSGFuZGxlJykpLnRvVGhyb3dFcnJvcigpO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnUmV0cmlldmUnLCAoKSA9PiB7XG4gICAgaXQoJ3Nob3VsZCBtYWtlIGNvcnJlY3QgcmV0cmlldmUgcmVxdWVzdCAoR0VUKScsIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IG1ldGhvZE1vY2tlZCA9IGplc3QuZm4oKCkgPT4gUHJvbWlzZS5yZXNvbHZlKHsgZGF0YToge30gfSkpO1xuXG4gICAgICAvLyBAdHMtaWdub3JlXG4gICAgICBGc1JlcXVlc3QuZGlzcGF0Y2gubW9ja0ltcGxlbWVudGF0aW9uKG1ldGhvZE1vY2tlZCk7XG4gICAgICBjb25zdCByZXNwID0gYXdhaXQgcmV0cmlldmUobW9ja2VkU2Vzc2lvbiwgJ2Zha2VIYW5kbGUnKTtcblxuICAgICAgZXhwZWN0KHJlc3ApLnRvRXF1YWwoe30pO1xuICAgICAgZXhwZWN0KG1ldGhvZE1vY2tlZCkudG9IYXZlQmVlbkNhbGxlZFdpdGgoJ2Zha2VBcGlVcmwvZmFrZUhhbmRsZScsIHsgbWV0aG9kOiAnR0VUJywgcGFyYW1zOiB7IGtleTogJ2Zha2VBcGlrZXknIH0sIGZpbGVzdGFja0hlYWRlcnM6IGZhbHNlIH0pO1xuICAgIH0pO1xuXG4gICAgaXQoJ3Nob3VsZCBtYWtlIGNvcnJlY3QgcmV0cmlldmUgcmVxdWVzdCAoSEVBRCknLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBtZXRob2RNb2NrZWQgPSBqZXN0LmZuKCgpID0+IFByb21pc2UucmVzb2x2ZSh7IGRhdGE6IHt9LCBoZWFkZXJzOiB7IHR5cGU6ICd0ZXN0JyB9IH0pKTtcblxuICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgRnNSZXF1ZXN0LmRpc3BhdGNoLm1vY2tJbXBsZW1lbnRhdGlvbihtZXRob2RNb2NrZWQpO1xuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IHJldHJpZXZlKG1vY2tlZFNlc3Npb24sICdmYWtlSGFuZGxlJywge1xuICAgICAgICBoZWFkOiB0cnVlLFxuICAgICAgfSk7XG5cbiAgICAgIGV4cGVjdChyZXNwKS50b0VxdWFsKHsgdHlwZTogJ3Rlc3QnIH0pO1xuICAgICAgZXhwZWN0KG1ldGhvZE1vY2tlZCkudG9IYXZlQmVlbkNhbGxlZFdpdGgoJ2Zha2VBcGlVcmwvZmFrZUhhbmRsZScsIHsgbWV0aG9kOiAnSEVBRCcsIHBhcmFtczogeyBrZXk6ICdmYWtlQXBpa2V5JyB9LCBmaWxlc3RhY2tIZWFkZXJzOiBmYWxzZSB9KTtcbiAgICB9KTtcblxuICAgIGl0KCdzaG91bGQgbWFrZSBjb3JyZWN0IHJldHJpZXZlIHJlcXVlc3Qgd2l0aCBwcm92aWRlZCBzZWN1cml0eScsIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IG1ldGhvZE1vY2tlZCA9IGplc3QuZm4oKCkgPT4gUHJvbWlzZS5yZXNvbHZlKHsgZGF0YToge30gfSkpO1xuXG4gICAgICBjb25zdCBmYWtlU2VjdXJpdHkgPSB7XG4gICAgICAgIHNpZ25hdHVyZTogJ2Zha2VTJyxcbiAgICAgICAgcG9saWN5OiAnZmFrZVAnLFxuICAgICAgfTtcblxuICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgRnNSZXF1ZXN0LmRpc3BhdGNoLm1vY2tJbXBsZW1lbnRhdGlvbihtZXRob2RNb2NrZWQpO1xuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IHJldHJpZXZlKG1vY2tlZFNlc3Npb24sICdmYWtlSGFuZGxlJywge30sIGZha2VTZWN1cml0eSk7XG5cbiAgICAgIGV4cGVjdChyZXNwKS50b0VxdWFsKHt9KTtcbiAgICAgIGV4cGVjdChtZXRob2RNb2NrZWQpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKCdmYWtlQXBpVXJsL2Zha2VIYW5kbGUnLCB7XG4gICAgICAgIG1ldGhvZDogJ0dFVCcsXG4gICAgICAgIHBhcmFtczogeyBrZXk6ICdmYWtlQXBpa2V5JywgcG9saWN5OiAnZmFrZVAnLCBzaWduYXR1cmU6ICdmYWtlUycgfSxcbiAgICAgICAgZmlsZXN0YWNrSGVhZGVyczogZmFsc2UsXG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIGl0KCdzaG91bGQgbWFrZSBjb3JyZWN0IHJldHJpZXZlIHJlcXVlc3Qgd2l0aCBleHRlbnNpb24nLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBtZXRob2RNb2NrZWQgPSBqZXN0LmZuKCgpID0+IFByb21pc2UucmVzb2x2ZSh7IGRhdGE6IHt9IH0pKTtcblxuICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgRnNSZXF1ZXN0LmRpc3BhdGNoLm1vY2tJbXBsZW1lbnRhdGlvbihtZXRob2RNb2NrZWQpO1xuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IHJldHJpZXZlKG1vY2tlZFNlc3Npb24sICdmYWtlSGFuZGxlJywge1xuICAgICAgICBleHRlbnNpb246ICd0eHQnLFxuICAgICAgfSk7XG5cbiAgICAgIGV4cGVjdChyZXNwKS50b0VxdWFsKHt9KTtcbiAgICAgIGV4cGVjdChtZXRob2RNb2NrZWQpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKCdmYWtlQXBpVXJsL2Zha2VIYW5kbGUrdHh0JywgeyBtZXRob2Q6ICdHRVQnLCBwYXJhbXM6IHsga2V5OiAnZmFrZUFwaWtleScgfSwgZmlsZXN0YWNrSGVhZGVyczogZmFsc2UgfSk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIG1ha2UgY29ycmVjdCByZXRyaWV2ZSByZXF1ZXN0IHdpdGggbWV0YWRhdGEnLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBtZXRob2RNb2NrZWQgPSBqZXN0LmZuKCgpID0+IFByb21pc2UucmVzb2x2ZSh7IGRhdGE6IHt9IH0pKTtcblxuICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgRnNSZXF1ZXN0LmRpc3BhdGNoLm1vY2tJbXBsZW1lbnRhdGlvbihtZXRob2RNb2NrZWQpO1xuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IHJldHJpZXZlKG1vY2tlZFNlc3Npb24sICdmYWtlSGFuZGxlJywge1xuICAgICAgICBtZXRhZGF0YTogdHJ1ZSxcbiAgICAgIH0pO1xuXG4gICAgICBleHBlY3QocmVzcCkudG9FcXVhbCh7fSk7XG4gICAgICBleHBlY3QobWV0aG9kTW9ja2VkKS50b0hhdmVCZWVuQ2FsbGVkV2l0aCgnZmFrZUFwaVVybC9mYWtlSGFuZGxlL21ldGFkYXRhJywgeyBtZXRob2Q6ICdHRVQnLCBwYXJhbXM6IHsga2V5OiAnZmFrZUFwaWtleScgfSwgZmlsZXN0YWNrSGVhZGVyczogZmFsc2UgfSk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIHRocm93IGFuIGVycm9yIG9uIGVtcHR5IGhhbmRsZScsICgpID0+IHtcbiAgICAgIHJldHVybiBleHBlY3QoKCkgPT4gcmV0cmlldmUobW9ja2VkU2Vzc2lvbiwgJycpKS50b1Rocm93RXJyb3IoKTtcbiAgICB9KTtcblxuICAgIGl0KCdzaG91bGQgdGhyb3cgYW4gZXJyb3Igd29ybmcgb3B0aW9ucyBwcm92aWRlZCcsICgpID0+IHtcbiAgICAgIHJldHVybiBleHBlY3QoKCkgPT5cbiAgICAgICAgcmV0cmlldmUobW9ja2VkU2Vzc2lvbiwgJ2Zha2VIYW5kbGUnLCB7XG4gICAgICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgICAgIHRlc3Q6IDEyMyxcbiAgICAgICAgfSlcbiAgICAgICkudG9UaHJvd0Vycm9yKCdJbnZhbGlkIHJldHJpZXZlIHBhcmFtcycpO1xuICAgIH0pO1xuXG4gICAgaXQoJ3Nob3VsZCBub3QgdGhyb3cgYW4gZXJyb3Igd29ybmcgb3B0aW9ucyBwcm92aWRlZCcsICgpID0+IHtcbiAgICAgIHJldHVybiBleHBlY3QoKCkgPT5cbiAgICAgICAgcmV0cmlldmUobW9ja2VkU2Vzc2lvbiwgJ2Zha2VIYW5kbGUnLCB7XG4gICAgICAgICAgbWV0YWRhdGE6IHRydWUsXG4gICAgICAgIH0pXG4gICAgICApLm5vdC50b1Rocm93RXJyb3IoJ0ludmFsaWQgcmV0cmlldmUgcGFyYW1zJyk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIHRocm93IGFuIGVycm9yIHdoZW4gbWV0YWRhdGEgYW5kIGhlYWQgaXMgcHJvdmlkZWQnLCAoKSA9PiB7XG4gICAgICByZXR1cm4gZXhwZWN0KCgpID0+XG4gICAgICAgIHJldHJpZXZlKG1vY2tlZFNlc3Npb24sICdmYWtlSGFuZGxlJywge1xuICAgICAgICAgIG1ldGFkYXRhOiB0cnVlLFxuICAgICAgICAgIGhlYWQ6IHRydWUsXG4gICAgICAgIH0pXG4gICAgICApLnRvVGhyb3dFcnJvcigpO1xuICAgIH0pO1xuICB9KTtcbn0pO1xuIl19