From b9cf61932b41b37c36c03da658d451aed11a1f86 Mon Sep 17 00:00:00 2001 From: Zhihe Li Date: Tue, 7 Mar 2023 16:38:41 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Add=20session=20recordin?= =?UTF-8?q?g=20models=20(#100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 Add session recording models ✅ Closes: ICU-8075 --- addons/api/addon/generated/models/channel.js | 53 ++++++++++++ .../api/addon/generated/models/connection.js | 47 +++++++++++ .../api/addon/generated/models/recording.js | 83 +++++++++++++++++++ addons/api/addon/models/channel.js | 25 ++++++ addons/api/addon/models/connection.js | 7 ++ addons/api/addon/models/recording.js | 25 ++++++ addons/api/app/models/channel.js | 1 + addons/api/app/models/connection.js | 1 + addons/api/app/models/recording.js | 1 + addons/api/tests/unit/models/channel-test.js | 39 +++++++++ .../api/tests/unit/models/connection-test.js | 23 +++++ .../api/tests/unit/models/recording-test.js | 53 ++++++++++++ 12 files changed, 358 insertions(+) create mode 100644 addons/api/addon/generated/models/channel.js create mode 100644 addons/api/addon/generated/models/connection.js create mode 100644 addons/api/addon/generated/models/recording.js create mode 100644 addons/api/addon/models/channel.js create mode 100644 addons/api/addon/models/connection.js create mode 100644 addons/api/addon/models/recording.js create mode 100644 addons/api/app/models/channel.js create mode 100644 addons/api/app/models/connection.js create mode 100644 addons/api/app/models/recording.js create mode 100644 addons/api/tests/unit/models/channel-test.js create mode 100644 addons/api/tests/unit/models/connection-test.js create mode 100644 addons/api/tests/unit/models/recording-test.js diff --git a/addons/api/addon/generated/models/channel.js b/addons/api/addon/generated/models/channel.js new file mode 100644 index 000000000..6f9a68b71 --- /dev/null +++ b/addons/api/addon/generated/models/channel.js @@ -0,0 +1,53 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import BaseModel from '../../models/base'; +import { attr } from '@ember-data/model'; + +/** + * A channel contains data for a single channel within a connection. + */ +export default class GeneratedChannelModel extends BaseModel { + // =attributes + + @attr('string', { + description: 'The type of the resource, to help differentiate schemas', + readOnly: true, + }) + type; + + @attr('number', { + description: + 'The total number of bytes uploaded from the client in this Channel.', + readOnly: true, + }) + bytes_up; + + @attr('number', { + description: + 'The total number of bytes downloaded to the client in this Channel.', + readOnly: true, + }) + bytes_down; + + @attr('number', { + description: + 'The total number of errors that occurred during the use of this Channel.', + readOnly: true, + }) + errors; + + @attr('date', { + description: 'The time the Channel started.', + readOnly: true, + }) + started_time; + + @attr('date', { + description: 'The time the Channel finished.', + readOnly: true, + }) + finished_time; +} diff --git a/addons/api/addon/generated/models/connection.js b/addons/api/addon/generated/models/connection.js new file mode 100644 index 000000000..b0d28d7f2 --- /dev/null +++ b/addons/api/addon/generated/models/connection.js @@ -0,0 +1,47 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import BaseModel from '../../models/base'; +import { attr } from '@ember-data/model'; + +/** + * A connection contains data for a boundary connection within a session. + */ +export default class GeneratedConnectionModel extends BaseModel { + // =attributes + + @attr('number', { + description: + 'The total number of bytes uploaded from the client in this Connection.', + readOnly: true, + }) + bytes_up; + + @attr('number', { + description: + 'The total number of bytes downloaded to the client in this Connection.', + readOnly: true, + }) + bytes_down; + + @attr('number', { + description: + 'The total number of errors that occurred during the use of this Connection.', + readOnly: true, + }) + errors; + + @attr('date', { + description: 'The time the Connection started.', + readOnly: true, + }) + started_time; + + @attr('date', { + description: 'The time the Connection finished.', + readOnly: true, + }) + finished_time; +} diff --git a/addons/api/addon/generated/models/recording.js b/addons/api/addon/generated/models/recording.js new file mode 100644 index 000000000..4853894e6 --- /dev/null +++ b/addons/api/addon/generated/models/recording.js @@ -0,0 +1,83 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import BaseModel from '../../models/base'; +import { attr } from '@ember-data/model'; + +/** + * A recording contains all data related to session recordings + */ +export default class GeneratedRecordingModel extends BaseModel { + // =attributes + + @attr('string', { + description: 'The type of the resource, to help differentiate schemas.', + readOnly: true, + }) + type; + + @attr({ + description: 'The user who created the session.', + readOnly: true, + }) + user; + + @attr({ + description: 'The target the session connected to.', + readOnly: true, + }) + target; + + @attr('string', { + description: 'The ID of the Session which this Session Recording recorded.', + readOnly: true, + }) + session_id; + + @attr('number', { + description: + 'The total number of bytes uploaded from the client in the Session.', + readOnly: true, + }) + bytes_up; + + @attr('number', { + description: + 'The total number of bytes downloaded to the client in the Session.', + readOnly: true, + }) + bytes_down; + + @attr('number', { + description: + 'The total number of errors that occurred during the use of the Session.', + readOnly: true, + }) + errors; + + @attr('date', { + description: 'The time the Session started.', + readOnly: true, + }) + started_time; + + @attr('date', { + description: 'The time the Session finished.', + readOnly: true, + }) + finished_time; + + @attr('date', { + description: 'The time the Session recording was last updated.', + readOnly: true, + }) + updated_time; + + @attr('date', { + description: 'The time that the Session recording will be deleted.', + readOnly: true, + }) + delete_on; +} diff --git a/addons/api/addon/models/channel.js b/addons/api/addon/models/channel.js new file mode 100644 index 000000000..f0e776d8e --- /dev/null +++ b/addons/api/addon/models/channel.js @@ -0,0 +1,25 @@ +import GeneratedChannelModel from '../generated/models/channel'; +import { belongsTo } from '@ember-data/model'; + +export const TYPE_CHANNEL_SESSION = 'session'; +export const TYPES_CHANNEL = Object.freeze([TYPE_CHANNEL_SESSION]); + +export default class ChannelModel extends GeneratedChannelModel { + @belongsTo('connection') connection; + + /** + * True if the channel type is `session`. + * @type {boolean} + */ + get isSession() { + return this.type === TYPE_CHANNEL_SESSION; + } + + /** + * True if the channel is an unknown type. + * @type {boolean} + */ + get isUnknown() { + return !TYPES_CHANNEL.includes(this.type); + } +} diff --git a/addons/api/addon/models/connection.js b/addons/api/addon/models/connection.js new file mode 100644 index 000000000..482e85b4d --- /dev/null +++ b/addons/api/addon/models/connection.js @@ -0,0 +1,7 @@ +import GeneratedConnectionModel from '../generated/models/connection'; +import { belongsTo, hasMany } from '@ember-data/model'; + +export default class ConnectionModel extends GeneratedConnectionModel { + @belongsTo('recording') recording; + @hasMany('channel') channels; +} diff --git a/addons/api/addon/models/recording.js b/addons/api/addon/models/recording.js new file mode 100644 index 000000000..bea1ee92d --- /dev/null +++ b/addons/api/addon/models/recording.js @@ -0,0 +1,25 @@ +import GeneratedRecordingModel from '../generated/models/recording'; +import { hasMany } from '@ember-data/model'; + +export const TYPE_RECORDING_SSH = 'ssh'; +export const TYPES_RECORDING = Object.freeze([TYPE_RECORDING_SSH]); + +export default class RecordingModel extends GeneratedRecordingModel { + @hasMany('connection') connections; + + /** + * True if the recording type is `ssh`. + * @type {boolean} + */ + get isSSH() { + return this.type === TYPE_RECORDING_SSH; + } + + /** + * True if the recording is an unknown type. + * @type {boolean} + */ + get isUnknown() { + return !TYPES_RECORDING.includes(this.type); + } +} diff --git a/addons/api/app/models/channel.js b/addons/api/app/models/channel.js new file mode 100644 index 000000000..83fa2e823 --- /dev/null +++ b/addons/api/app/models/channel.js @@ -0,0 +1 @@ +export { default } from 'api/models/channel'; diff --git a/addons/api/app/models/connection.js b/addons/api/app/models/connection.js new file mode 100644 index 000000000..17c9eb2ac --- /dev/null +++ b/addons/api/app/models/connection.js @@ -0,0 +1 @@ +export { default } from 'api/models/connection'; diff --git a/addons/api/app/models/recording.js b/addons/api/app/models/recording.js new file mode 100644 index 000000000..bdcbf74be --- /dev/null +++ b/addons/api/app/models/recording.js @@ -0,0 +1 @@ +export { default } from 'api/models/recording'; diff --git a/addons/api/tests/unit/models/channel-test.js b/addons/api/tests/unit/models/channel-test.js new file mode 100644 index 000000000..7447e0eb6 --- /dev/null +++ b/addons/api/tests/unit/models/channel-test.js @@ -0,0 +1,39 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import { TYPE_CHANNEL_SESSION } from 'api/models/channel'; + +module('Unit | Model | channel', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let model = store.createRecord('channel', {}); + assert.ok(model); + }); + + test('it has isUnknown property and returns the expected values', async function (assert) { + assert.expect(2); + const store = this.owner.lookup('service:store'); + const modelA = store.createRecord('channel', { + type: TYPE_CHANNEL_SESSION, + }); + const modelB = store.createRecord('channel', { + type: 'no-such-type', + }); + assert.false(modelA.isUnknown); + assert.true(modelB.isUnknown); + }); + + test('it has isSession property and returns the expected values', async function (assert) { + assert.expect(2); + const store = this.owner.lookup('service:store'); + const modelA = store.createRecord('channel', { + type: TYPE_CHANNEL_SESSION, + }); + const modelB = store.createRecord('channel', { + type: 'random', + }); + assert.true(modelA.isSession); + assert.false(modelB.isSession); + }); +}); diff --git a/addons/api/tests/unit/models/connection-test.js b/addons/api/tests/unit/models/connection-test.js new file mode 100644 index 000000000..e6d50eaa9 --- /dev/null +++ b/addons/api/tests/unit/models/connection-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Model | connection', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let model = store.createRecord('connection', {}); + assert.ok(model); + }); + + test('it has channels as a relationship', async function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const model = store.createRecord('connection', { + channels: [store.createRecord('channel')], + }); + + const channels = await model.channels; + assert.strictEqual(channels.length, 1); + }); +}); diff --git a/addons/api/tests/unit/models/recording-test.js b/addons/api/tests/unit/models/recording-test.js new file mode 100644 index 000000000..096954c8b --- /dev/null +++ b/addons/api/tests/unit/models/recording-test.js @@ -0,0 +1,53 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import { TYPE_RECORDING_SSH } from 'api/models/recording'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; + +module('Unit | Model | recording', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let model = store.createRecord('recording', {}); + assert.ok(model); + }); + + test('it has isUnknown property and returns the expected values', async function (assert) { + assert.expect(2); + const store = this.owner.lookup('service:store'); + const modelA = store.createRecord('recording', { + type: TYPE_RECORDING_SSH, + }); + const modelB = store.createRecord('recording', { + type: 'no-such-type', + }); + assert.false(modelA.isUnknown); + assert.true(modelB.isUnknown); + }); + + test('it has isSSH property and returns the expected values', async function (assert) { + assert.expect(2); + const store = this.owner.lookup('service:store'); + const modelA = store.createRecord('recording', { + type: TYPE_RECORDING_SSH, + }); + const modelB = store.createRecord('recording', { + type: 'random', + }); + assert.true(modelA.isSSH); + assert.false(modelB.isSSH); + }); + + test('it has connections as a relationship', async function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const model = store.createRecord('recording', { + type: TYPE_RECORDING_SSH, + connections: [store.createRecord('connection')], + }); + + const connections = await model.connections; + assert.strictEqual(connections.length, 1); + }); +});