feat: 🎸 Add mirage data for ascii casts and use fetch (#129)

* feat: 🎸 Add mirage data for ascii casts and use fetch

✅ Closes: ICU-8189
This commit is contained in:
Zhihe Li authored and GitHub committed 2023-03-21 15:52:58 -04:00
1 parent 06af1a9fc4
commit 1e7268ff54
7 files changed
+272 -23

No files matched your search

@@ -0,0 +1,55 @@
import ApplicationAdapter from './application';
export default class SessionRecordingAdapter extends ApplicationAdapter {
/**
* Overrides the default `buildURL` such that for custom download methods,
* URLs will get the query parameters. All other request types behave as normal.
*
* @param modelName {string}
* @param id {string}
* @param snapshot {object}
* @param requestType {string}
* @param query {object}
* @returns {string}
*/
buildURL(modelName, id, snapshot, requestType, query) {
let url = super.buildURL(...arguments);
const method = snapshot?.adapterOptions?.method;
if (method === 'download') {
const queryParams = new URLSearchParams(query).toString();
if (queryParams) url = `${url}?${queryParams}`;
}
return url;
}
/**
* Handles the download recording for recordings. Takes in a mime type for the
* type or recording to return.
* The ID can be the session ID, session recording ID,
* connection recording ID, or the channel recording ID.
* @param id {string}
* @param mimeType {string}
* @returns {Promise<string>}
*/
async getRecordingDownload({ id, mimeType }) {
const options = { adapterOptions: { method: 'download' } };
const url = this.buildURL('session-recording', id, options, 'findRecord', {
mime_type: mimeType,
});
const response = await fetch(url);
return response.text();
}
/**
* Returns an ascii cast recording.
* @param id
* @returns {Promise<string>}
*/
getAsciicast(id) {
return this.getRecordingDownload({
id,
mimeType: 'application/x-asciicast',
});
}
}
@@ -24,4 +24,13 @@ export default class ChannelRecordingModel extends GeneratedChannelRecordingMode
get isUnknown() {
return !TYPES_CHANNEL_RECORDING.includes(this.type);
}
/**
* Download the ascii cast recording for this channel.
* @returns {Promise<string>}
*/
getAsciicast() {
const adapter = this.store.adapterFor('session-recording');
return adapter.getAsciicast(this.id);
}
}
@@ -27,26 +27,4 @@ export default class SessionRecordingModel extends GeneratedSessionRecordingMode
get isUnknown() {
return !TYPES_SESSION_RECORDING.includes(this.type);
}
/**
* Download the recording for either session, connection, or channels.
* id can be the session ID, session recording ID, connection recording ID,
* or the channel recording ID.
* @param {string} id
* @param {string} mimeType
* @returns {Promise}
*/
getAsciiCast(id) {
const adapter = this.store.adapterFor('application');
const options = { adapterOptions: { method: 'download' } };
const url = adapter.buildURL(
'session-recording',
id,
options,
'findRecord'
);
return adapter.ajax(url, 'GET', {
data: { mimeType: 'application/x-asciicast' },
});
}
}
@@ -0,0 +1 @@
export { default } from 'api/adapters/session-recording';
+14 -1
View File
@@ -15,6 +15,7 @@ import { pickRandomStatusString } from './factories/session';
import initializeMockIPC from './scenarios/ipc';
import makeBooleanFilter from './helpers/bexpr-filter';
import { faker } from '@faker-js/faker';
import { asciiCasts } from './data/asciicasts';
const isTesting = environmentConfig.environment === 'test';
@@ -688,7 +689,19 @@ function routes() {
return sessionRecordings.where({ scopeId });
}
);
this.get('/session-recordings/:id');
this.get(
'/session-recordings/:idMethod',
async ({ sessionRecordings }, { params: { idMethod } }) => {
const id = idMethod.split(':')[0];
const method = idMethod.split(':')[1];
if (method === 'download') {
return faker.helpers.arrayElement(asciiCasts);
} else {
return sessionRecordings.find(id);
}
}
);
/* Uncomment the following line and the Response import above
* Then change the response code to simulate error responses.
+151
View File
@@ -0,0 +1,151 @@
export const asciiCast1 = `{"version": 2, "width": 124, "height": 36, "timestamp": 1670352594, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}}
[2.138652, "o", "\\u001b[1m\\u001b[7m%\\u001b[27m\\u001b[1m\\u001b[0m \\r \\r\\u001b]2;ember-asciinema-player\\u0007\\u001b]1;..iinema-player\\u0007"]
[2.220877, "o", "\\r\\u001b[0m\\u001b[27m\\u001b[24m\\u001b[J\\u001b[33member-asciinema-player\\u001b[00m \\u001b[32mgit\\u001b[00m:\\u001b[36m(main*)\\u001b[00m $ \\u001b[K"]
[2.221019, "o", "\\u001b[?1h\\u001b="]
[2.221076, "o", "\\u001b[?2004h"]
[2.676444, "o", "y"]
[2.788653, "o", "\\bya"]
[2.868652, "o", "r"]
[3.001764, "o", "n"]
[3.167636, "o", " "]
[3.313668, "o", "t"]
[3.460527, "o", "e"]
[3.561983, "o", "s"]
[3.63678, "o", "t"]
[3.853437, "o", "\\u001b[?1l\\u001b>"]
[3.853473, "o", "\\u001b[?2004l\\r\\r\\n"]
[3.854604, "o", "\\u001b]2;yarn test\\u0007\\u001b]1;yarn\\u0007"]
[4.489178, "o", "\\u001b[2K"]
[4.48982, "o", "\\u001b[1G\\u001b[1myarn run v1.22.19\\u001b[22m\\r\\n"]
[4.555618, "o", "\\u001b[2K"]
[4.555649, "o", "\\u001b[1G\\u001b[2m$ npm-run-all lint\\u001b[22m\\r\\n"]
[5.345253, "o", "\\u001b[2K"]
[5.345509, "o", "\\u001b[1G\\u001b[2m$ npm-run-all --print-name --aggregate-output --continue-on-error --parallel \\"lint:!(fix)\\"\\u001b[22m\\r\\n"]
[7.225926, "o", "\\r\\n> ember-asciinema-player@0.0.0 lint:js ember-asciinema-player/package.json\\r\\n> eslint . --cache \\r\\n\\r\\n$ eslint . --cache\\r\\n"]
[8.398641, "o", "\\r\\n> ember-asciinema-player@0.0.0 lint:hbs ember-asciinema-player/package.json\\r\\n> ember-template-lint . \\r\\n\\r\\n$ ember-template-lint .\\r\\n\\r\\n"]
[8.418537, "o", "\\u001b[2K\\u001b[1G\\u001b[2m$ ember test\\u001b[22m\\r\\n"]
[12.256156, "o", "\\u001b[?25l"]
[12.256464, "o", "\\u001b[32m⠋\\u001b[39m \\u001b[32mBuilding\\u001b[39m"]
[12.2573, "o", "\\u001b[2K"]
[12.257386, "o", "\\u001b[1G\\u001b[?25h"]
[12.257689, "o", "Environment: test\\r\\n"]
[12.257857, "o", "\\u001b[?25l\\u001b[32m⠋\\u001b[39m \\u001b[32mBuilding\\u001b[39m"]
[12.539035, "o", "\\u001b[2K"]
[12.539144, "o", "\\u001b[1G"]
[12.539219, "o", "\\u001b[32m⠙\\u001b[39m \\u001b[32mbuilding... \\u001b[39m"]
[12.745202, "o", "\\u001b[2K\\u001b[1G"]
[12.745343, "o", "\\u001b[32m⠹\\u001b[39m \\u001b[32mbuilding... \\u001b[39m[Babel: @ember/test-helpers > applyPatches]"]
[12.825004, "o", "\\u001b[2K"]
[12.825031, "o", "\\u001b[1G"]
[12.825122, "o", "\\u001b[32m⠸\\u001b[39m \\u001b[32mbuilding... \\u001b[39m[Babel: ember-qunit > applyPatches]"]
[12.933588, "o", "\\u001b[2K\\u001b[1G"]
[12.933624, "o", "\\u001b[32m⠼\\u001b[39m \\u001b[32mbuilding... \\u001b[39m[Babel: ember-load-initializers > applyPatches]"]
[13.194738, "o", "\\u001b[2K\\u001b[1G"]
[13.195009, "o", "\\u001b[32m⠴\\u001b[39m \\u001b[32mbuilding... \\u001b[39m[Babel: ember-asciinema-player > applyPatches]"]
[13.307878, "o", "\\u001b[2K\\u001b[1G\\u001b[32m⠦\\u001b[39m \\u001b[32mbuilding... \\u001b[39m[Package /assets/vendor.js]"]
[13.750717, "o", "\\u001b[2K\\u001b[1G"]
[13.750755, "o", "\\u001b[32m⠧\\u001b[39m \\u001b[32mbuilding... \\u001b[39m[Concat: Test Support JS]"]
[13.869411, "o", "\\u001b[2K\\u001b[1G"]
[13.869438, "o", "\\u001b[32m⠇\\u001b[39m \\u001b[32mbuilding... \\u001b[39m[ember-auto-import-webpack]"]
[14.655374, "o", "\\u001b[2K\\u001b[1G"]
[14.655415, "o", "\\u001b[32m⠏\\u001b[39m \\u001b[32mbuilding... \\u001b[39m[ember-auto-import-webpack]"]
[14.73686, "o", "\\u001b[2K\\u001b[1G\\u001b[?25h"]
[14.738533, "o", "\\u001b[?25h"]
[14.738818, "o", "\\u001b[?25l\\u001b[32m⠋\\u001b[39m cleaning up"]
[14.739061, "o", "\\u001b[2K\\u001b[1G\\u001b[?25hcleaning up...\\r\\n"]
[14.739197, "o", "\\u001b[?25l\\u001b[32m⠋\\u001b[39m cleaning up"]
[14.907985, "o", "\\u001b[2K"]
[14.90808, "o", "\\u001b[1G\\u001b[?25h"]
[14.908128, "o", "\\u001b[32mBuilt project successfully.\\u001b[39m\\r\\n"]
[16.224632, "o", "ok 1 Chrome 108.0 - [2 ms] - ember-qunit: Ember.onerror validation: Ember.onerror is functioning properly\\r\\n"]
[16.346568, "o", "\\r\\n1..1\\r\\n# tests 1\\r\\n# pass 1\\r\\n# skip 0\\r\\n# todo 0\\r\\n# fail 0\\r\\n\\r\\n# ok\\r\\n"]
[16.403167, "o", "\\u001b[2K"]
[16.403388, "o", "\\u001b[1G✨ Done in 11.92s.\\r\\n"]
[16.413967, "o", "\\u001b[1m\\u001b[7m%\\u001b[27m\\u001b[1m\\u001b[0m \\r \\r"]
[16.414147, "o", "\\u001b]2;ember-asciinema-player\\u0007\\u001b]1;..iinema-player\\u0007"]
[16.528341, "o", "\\r\\u001b[0m\\u001b[27m\\u001b[24m\\u001b[J\\u001b[33member-asciinema-player\\u001b[00m \\u001b[32mgit\\u001b[00m:\\u001b[36m(main*)\\u001b[00m $ \\u001b[K"]
[16.528501, "o", "\\u001b[?1h\\u001b="]
[16.52863, "o", "\\u001b[?2004h"]
[19.078794, "o", "\\u001b[?2004l\\r\\r\\n"]`;
export const asciiCast2 = `{"version": 2, "width": 112, "height": 30, "timestamp": 1672793720, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[0.036928, "o", "\\u001b[?2004h\\u001b[1;32m[\\u001b]0;johan@nixos: ~/src/hashicorp/boundary-enterprise\\u0007johan@nixos:~/src/hashicorp/boundary-enterprise]$\\u001b[0m "]
[2.827983, "o", "b"]
[2.9386, "o", "o"]
[2.97036, "o", "u"]
[3.032542, "o", "n"]
[3.108687, "o", "d"]
[3.246045, "o", "a"]
[3.340117, "o", "r"]
[3.841216, "o", "y"]
[4.314958, "o", " "]
[6.052102, "o", "u"]
[6.146486, "o", "s"]
[6.207341, "o", "e"]
[6.273087, "o", "r"]
[6.378873, "o", "s"]
[6.799687, "o", " "]
[7.08347, "o", "l"]
[7.159196, "o", "i"]
[7.249656, "o", "s"]
[7.352251, "o", "t"]
[7.450038, "o", " "]
[7.688195, "o", "-"]
[7.931818, "o", "r"]
[8.001542, "o", "e"]
[8.387771, "o", "c"]
[8.526339, "o", "u"]
[8.631438, "o", "r"]
[8.81899, "o", "s"]
[8.958184, "o", "i"]
[9.085688, "o", "v"]
[9.134885, "o", "e"]
[9.786868, "o", "\\r\\n"]
[9.788828, "o", "\\u001b[?2004l\\r"]
[10.086691, "o", "\\u001b[0m\\r\\nUser information:\\r\\n ID: u_anon\\r\\n Scope ID: global\\r\\n Version: 1\\r\\n Name: anonymous\\r\\n Description: The anonymous user matches any request, whether authenticated or not\\r\\n Authorized Actions:\\r\\n no-op\\r\\n read\\r\\n update\\r\\n delete\\r\\n add-accounts\\r\\n set-accounts\\r\\n remove-accounts\\r\\n\\r\\n ID: u_auth\\r\\n Scope ID: global\\r\\n Version: 1\\r\\n Name: authenticated\\r\\n Description: The authenticated user matches any user that has a valid token\\r\\n Authorized Actions:\\r\\n no-op\\r\\n read\\r\\n update\\r\\n delete\\r\\n add-accounts\\r\\n set-accounts\\r\\n remove-accounts\\r\\n\\r\\n ID: u_recovery\\r\\n Scope ID: global\\r\\n Version: 1\\r\\n Name: recovery\\r\\n Description: The recovery user is used for any request that was performed with the recovery KMS\\r\\n workflow\\r\\n Autho"]
[10.086736, "o", "rized Actions:\\r\\n no-op\\r\\n read\\r\\n update\\r\\n delete\\r\\n add-accounts\\r\\n set-accounts\\r\\n remove-accounts\\r\\n\\r\\n ID: u_1234567890\\r\\n Scope ID: global\\r\\n Version: 3\\r\\n Name: admin\\r\\n Description: Initial admin user within the \\"global\\" scope\\r\\n Primary Account ID: acctpw_1234567890\\r\\n Login Name: admin\\r\\n Authorized Actions:\\r\\n no-op\\r\\n read\\r\\n update\\r\\n delete\\r\\n add-accounts\\r\\n set-accounts\\r\\n remove-accounts\\r\\n\\r\\n ID: u_0987654321\\r\\n Scope ID: global\\r\\n Version: 3\\r\\n Name: user\\r\\n Description: Initial unprivileged user\\r\\n Primary Account ID: acctpw_0987654321\\r\\n Login Name: user\\r\\n Authorized Actions:\\r\\n no-op\\r\\n read\\r\\n update\\r\\n delete\\r\\n add-accounts\\r\\n set-accounts\\r\\n remove-accounts\\u001b[0m\\r\\n"]
[10.106453, "o", "\\u001b[?2004h\\u001b[1;32m[\\u001b]0;johan@nixos: ~/src/hashicorp/boundary-enterprise\\u0007johan@nixos:~/src/hashicorp/boundary-enterprise]$\\u001b[0m "]
[12.501496, "o", "b"]
[12.703134, "o", "o"]
[12.752198, "o", "u"]
[12.775329, "o", "n"]
[12.905261, "o", "d"]
[13.002373, "o", "a"]
[13.100111, "o", "r"]
[13.354779, "o", "y"]
[13.699827, "o", " "]
[13.998849, "o", "t"]
[14.139192, "o", "a"]
[14.19663, "o", "r"]
[14.392075, "o", "g"]
[14.489938, "o", "e"]
[14.579533, "o", "t"]
[14.674628, "o", "s"]
[14.830951, "o", " "]
[15.015311, "o", "l"]
[15.077147, "o", "i"]
[15.262221, "o", "s"]
[15.355571, "o", "t"]
[15.456597, "o", " "]
[15.91612, "o", "-"]
[16.264491, "o", "r"]
[16.351595, "o", "e"]
[16.798456, "o", "c"]
[16.891689, "o", "u"]
[16.997975, "o", "r"]
[17.176378, "o", "s"]
[17.290104, "o", "i"]
[17.434691, "o", "v"]
[17.482502, "o", "e"]
[18.393541, "o", "\\r\\n"]
[18.39558, "o", "\\u001b[?2004l\\r"]
[18.673452, "o", "\\u001b[0m\\r\\nTarget information:\\r\\n ID: ttcp_1234567890\\r\\n Scope ID: p_1234567890\\r\\n Version: 2\\r\\n Type: tcp\\r\\n Name: Generated target\\r\\n Description: Provides an initial target in Boundary\\r\\n Authorized Actions:\\r\\n no-op\\r\\n read\\r\\n update\\r\\n delete\\r\\n add-host-sources\\r\\n set-host-sources\\r\\n remove-host-sources\\r\\n add-credential-sources\\r\\n set-credential-sources\\r\\n remove-credential-sources\\r\\n authorize-session\\u001b[0m\\r\\n"]
[18.692528, "o", "\\u001b[?2004h\\u001b[1;32m[\\u001b]0;johan@nixos: ~/src/hashicorp/boundary-enterprise\\u0007johan@nixos:~/src/hashicorp/boundary-enterprise]$\\u001b[0m "]
[20.083966, "o", "e"]
[20.37301, "o", "x"]
[20.51445, "o", "i"]
[20.612885, "o", "t"]
[21.303896, "o", "\\r\\n"]
[21.307902, "o", "\\u001b[?2004l\\rexit\\r\\n"]`;
export const asciiCasts = [asciiCast1, asciiCast2];
@@ -0,0 +1,42 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Adapter | session recording', function (hooks) {
setupTest(hooks);
test('it generates correct URLs for downloading asciicast', function (assert) {
assert.expect(1);
const mockSnapshot = {
adapterOptions: {
method: 'download',
},
};
const queryObject = {
mime_type: 'mimeType',
};
const adapter = this.owner.lookup('adapter:session-recording');
const createRecordURL = adapter.buildURL(
'session-recording',
'sr_id',
mockSnapshot,
'findRecord',
queryObject
);
assert.strictEqual(
createRecordURL,
'/v1/session-recordings/sr_id:download?mime_type=mimeType'
);
});
test('it generates correct URLs for retrieving session-recordings', function (assert) {
assert.expect(1);
const adapter = this.owner.lookup('adapter:session-recording');
const createRecordURL = adapter.buildURL(
'session-recording',
'sr_id',
{},
'findRecord'
);
assert.strictEqual(createRecordURL, '/v1/session-recordings/sr_id');
});
});