From ce83346461108bea8fa0827ddf8ecb1119849dc3 Mon Sep 17 00:00:00 2001 From: Zhihe Li Date: Fri, 10 Mar 2023 14:20:58 -0500 Subject: [PATCH] Icu 8073 mirage recording connection channel (#109) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 Add mirage data for recordings, connections, channels ✅ Closes: ICU-8073 --- addons/api/addon/serializers/connection.js | 10 +++ addons/api/addon/serializers/recording.js | 9 +++ addons/api/app/serializers/connection.js | 1 + addons/api/app/serializers/recording.js | 1 + addons/api/mirage/config.js | 9 +++ addons/api/mirage/factories/channel.js | 13 ++++ addons/api/mirage/factories/connection.js | 11 ++++ addons/api/mirage/factories/recording.js | 63 +++++++++++++++++++ addons/api/mirage/factories/scope.js | 5 +- addons/api/mirage/factories/storage-bucket.js | 5 ++ .../api/mirage/generated/factories/channel.js | 20 ++++++ .../mirage/generated/factories/connection.js | 20 ++++++ .../mirage/generated/factories/recording.js | 24 +++++++ .../generated/factories/storage-bucket.js | 6 +- addons/api/mirage/models/channel.js | 10 +++ addons/api/mirage/models/connection.js | 11 ++++ addons/api/mirage/models/recording.js | 14 +++++ addons/api/mirage/models/storage-bucket.js | 5 ++ addons/api/mirage/scenarios/default.js | 20 ++++++ addons/api/mirage/serializers/channel.js | 10 +++ addons/api/mirage/serializers/connection.js | 25 ++++++++ addons/api/mirage/serializers/recording.js | 43 +++++++++++++ .../api/mirage/serializers/storage-bucket.js | 5 ++ .../tests/unit/serializers/connection-test.js | 23 +++++++ .../tests/unit/serializers/recording-test.js | 23 +++++++ 25 files changed, 384 insertions(+), 2 deletions(-) create mode 100644 addons/api/addon/serializers/connection.js create mode 100644 addons/api/addon/serializers/recording.js create mode 100644 addons/api/app/serializers/connection.js create mode 100644 addons/api/app/serializers/recording.js create mode 100644 addons/api/mirage/factories/channel.js create mode 100644 addons/api/mirage/factories/connection.js create mode 100644 addons/api/mirage/factories/recording.js create mode 100644 addons/api/mirage/generated/factories/channel.js create mode 100644 addons/api/mirage/generated/factories/connection.js create mode 100644 addons/api/mirage/generated/factories/recording.js create mode 100644 addons/api/mirage/models/channel.js create mode 100644 addons/api/mirage/models/connection.js create mode 100644 addons/api/mirage/models/recording.js create mode 100644 addons/api/mirage/serializers/channel.js create mode 100644 addons/api/mirage/serializers/connection.js create mode 100644 addons/api/mirage/serializers/recording.js create mode 100644 addons/api/tests/unit/serializers/connection-test.js create mode 100644 addons/api/tests/unit/serializers/recording-test.js diff --git a/addons/api/addon/serializers/connection.js b/addons/api/addon/serializers/connection.js new file mode 100644 index 000000000..95c01903b --- /dev/null +++ b/addons/api/addon/serializers/connection.js @@ -0,0 +1,10 @@ +import ApplicationSerializer from './application'; +import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest'; + +export default class ConnectionSerializer extends ApplicationSerializer.extend( + EmbeddedRecordsMixin +) { + attrs = { + channels: { embedded: 'always' }, + }; +} diff --git a/addons/api/addon/serializers/recording.js b/addons/api/addon/serializers/recording.js new file mode 100644 index 000000000..6759db0dd --- /dev/null +++ b/addons/api/addon/serializers/recording.js @@ -0,0 +1,9 @@ +import ApplicationSerializer from './application'; +import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest'; +export default class RecordingSerializer extends ApplicationSerializer.extend( + EmbeddedRecordsMixin +) { + attrs = { + connections: { embedded: 'always' }, + }; +} diff --git a/addons/api/app/serializers/connection.js b/addons/api/app/serializers/connection.js new file mode 100644 index 000000000..ab831ba04 --- /dev/null +++ b/addons/api/app/serializers/connection.js @@ -0,0 +1 @@ +export { default } from 'api/serializers/connection'; diff --git a/addons/api/app/serializers/recording.js b/addons/api/app/serializers/recording.js new file mode 100644 index 000000000..921180bea --- /dev/null +++ b/addons/api/app/serializers/recording.js @@ -0,0 +1 @@ +export { default } from 'api/serializers/recording'; diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js index d6b519784..62041703a 100644 --- a/addons/api/mirage/config.js +++ b/addons/api/mirage/config.js @@ -680,6 +680,15 @@ function routes() { } ); + // session recordings + this.get( + '/recordings', + ({ recordings }, { queryParams: { scope_id: scopeId } }) => { + return recordings.where({ scopeId }); + } + ); + this.get('/recordings/:id'); + /* Uncomment the following line and the Response import above * Then change the response code to simulate error responses. * this.get('/scopes', () => new Response(505)); diff --git a/addons/api/mirage/factories/channel.js b/addons/api/mirage/factories/channel.js new file mode 100644 index 000000000..812d7da44 --- /dev/null +++ b/addons/api/mirage/factories/channel.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import factory from '../generated/factories/channel'; +import generateId from '../helpers/id'; +import { TYPES_CHANNEL as types } from 'api/models/channel'; + +export default factory.extend({ + id: () => generateId('srcc_'), + type: (i) => types[i % types.length], +}); diff --git a/addons/api/mirage/factories/connection.js b/addons/api/mirage/factories/connection.js new file mode 100644 index 000000000..455c4941f --- /dev/null +++ b/addons/api/mirage/factories/connection.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import factory from '../generated/factories/connection'; +import generateId from '../helpers/id'; + +export default factory.extend({ + id: () => generateId('src_'), +}); diff --git a/addons/api/mirage/factories/recording.js b/addons/api/mirage/factories/recording.js new file mode 100644 index 000000000..1c39abf09 --- /dev/null +++ b/addons/api/mirage/factories/recording.js @@ -0,0 +1,63 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import factory from '../generated/factories/recording'; +import permissions from '../helpers/permissions'; +import generateId from '../helpers/id'; +import { faker } from '@faker-js/faker'; +import { trait } from 'miragejs'; +import { TYPES_RECORDING as types } from 'api/models/recording'; + +const pickRandomElement = (arr) => faker.helpers.arrayElement(arr); +export default factory.extend({ + id: () => generateId('sr_'), + + // Cycle through available types + type: (i) => types[i % types.length], + + authorized_actions: () => + permissions.authorizedActionsFor('recording') || ['no-op', 'read', 'list'], + + withNonExistingUserAndTarget: trait({ + afterCreate(recording, server) { + const user = pickRandomElement(server.schema.users.all().models); + user.id = 'deleted_user'; + const session = pickRandomElement(server.schema.sessions.all().models); + session.id = 'deleted_session'; + const target = session.target; + target.id = 'deleted_target'; + + recording.update({ + user, + target, + session, + }); + }, + }), + + withExistingUserAndTarget: trait({ + afterCreate(recording, server) { + const user = pickRandomElement(server.schema.users.all().models); + const session = pickRandomElement(server.schema.sessions.all().models); + const target = session.target; + + recording.update({ + user, + target, + session, + }); + }, + }), + + withConnectionAndChannels: trait({ + afterCreate(recording, server) { + server + .createList('connection', 3, { recording }) + .forEach((connection) => { + server.createList('channel', 3, { connection }); + }); + }, + }), +}); diff --git a/addons/api/mirage/factories/scope.js b/addons/api/mirage/factories/scope.js index eb8cf99bd..a61f252ee 100644 --- a/addons/api/mirage/factories/scope.js +++ b/addons/api/mirage/factories/scope.js @@ -30,7 +30,6 @@ export default factory.extend({ 'credential-stores': ['create', 'list'], 'auth-methods': ['create', 'list'], 'host-catalogs': ['create', 'list'], - 'storage-buckets': ['create', 'list'], }; // Worker permissions only available on the global scope @@ -38,6 +37,10 @@ export default factory.extend({ collectionActions.workers = ['create:worker-led', 'list']; } + if (this.type === 'global' || this.type === 'org') { + collectionActions['storage-buckets'] = ['create', 'list']; + } + return collectionActions; }, diff --git a/addons/api/mirage/factories/storage-bucket.js b/addons/api/mirage/factories/storage-bucket.js index 2145f6ed1..0946e5dae 100644 --- a/addons/api/mirage/factories/storage-bucket.js +++ b/addons/api/mirage/factories/storage-bucket.js @@ -1,3 +1,8 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + import factory from '../generated/factories/storage-bucket'; import permissions from '../helpers/permissions'; import { faker } from '@faker-js/faker'; diff --git a/addons/api/mirage/generated/factories/channel.js b/addons/api/mirage/generated/factories/channel.js new file mode 100644 index 000000000..e4e37b0f4 --- /dev/null +++ b/addons/api/mirage/generated/factories/channel.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { Factory } from 'miragejs'; +import { faker } from '@faker-js/faker'; + +/** + * GeneratedChannelModelFactory + */ +export default Factory.extend({ + bytes_up: () => faker.datatype.number(), + bytes_down: () => faker.datatype.number(), + errors: () => faker.datatype.number({ max: 10 }), + started_time() { + return faker.date.recent(3, this.finished_time); + }, + finished_time: () => faker.date.recent(), +}); diff --git a/addons/api/mirage/generated/factories/connection.js b/addons/api/mirage/generated/factories/connection.js new file mode 100644 index 000000000..c58040ded --- /dev/null +++ b/addons/api/mirage/generated/factories/connection.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { Factory } from 'miragejs'; +import { faker } from '@faker-js/faker'; + +/** + * GeneratedConnectionModelFactory + */ +export default Factory.extend({ + bytes_up: () => faker.datatype.number(), + bytes_down: () => faker.datatype.number(), + errors: () => faker.datatype.number({ max: 10 }), + started_time() { + return faker.date.recent(3, this.finished_time); + }, + finished_time: () => faker.date.recent(), +}); diff --git a/addons/api/mirage/generated/factories/recording.js b/addons/api/mirage/generated/factories/recording.js new file mode 100644 index 000000000..51824fd59 --- /dev/null +++ b/addons/api/mirage/generated/factories/recording.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { Factory } from 'miragejs'; +import { faker } from '@faker-js/faker'; + +/** + * GeneratedRecordModelFactory + */ +export default Factory.extend({ + bytes_up: () => faker.datatype.number(), + bytes_down: () => faker.datatype.number(), + errors: () => faker.datatype.number({ max: 10 }), + started_time() { + return faker.date.recent(3, this.finished_time); + }, + finished_time() { + return faker.date.recent(1, this.updated_time); + }, + updated_time: () => faker.date.recent(), + deleted_on: () => faker.date.soon(), +}); diff --git a/addons/api/mirage/generated/factories/storage-bucket.js b/addons/api/mirage/generated/factories/storage-bucket.js index f8346f8c6..82be1797b 100644 --- a/addons/api/mirage/generated/factories/storage-bucket.js +++ b/addons/api/mirage/generated/factories/storage-bucket.js @@ -1,3 +1,8 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + import { Factory } from 'miragejs'; import { faker } from '@faker-js/faker'; @@ -5,7 +10,6 @@ import { faker } from '@faker-js/faker'; * GeneratedStorageBucketModelFactory */ export default Factory.extend({ - type: 'plugin', name: () => faker.random.words(), description: () => faker.random.words(), created_time: () => faker.date.recent(), diff --git a/addons/api/mirage/models/channel.js b/addons/api/mirage/models/channel.js new file mode 100644 index 000000000..19ee5e425 --- /dev/null +++ b/addons/api/mirage/models/channel.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { belongsTo, Model } from 'miragejs'; + +export default Model.extend({ + connection: belongsTo(), +}); diff --git a/addons/api/mirage/models/connection.js b/addons/api/mirage/models/connection.js new file mode 100644 index 000000000..5706ffe4b --- /dev/null +++ b/addons/api/mirage/models/connection.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { belongsTo, hasMany, Model } from 'miragejs'; + +export default Model.extend({ + recording: belongsTo(), + channels: hasMany(), +}); diff --git a/addons/api/mirage/models/recording.js b/addons/api/mirage/models/recording.js new file mode 100644 index 000000000..ec60ca095 --- /dev/null +++ b/addons/api/mirage/models/recording.js @@ -0,0 +1,14 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { belongsTo, hasMany, Model } from 'miragejs'; + +export default Model.extend({ + scope: belongsTo(), + user: belongsTo(), + session: belongsTo(), + target: belongsTo(), + connections: hasMany(), +}); diff --git a/addons/api/mirage/models/storage-bucket.js b/addons/api/mirage/models/storage-bucket.js index 27d40334d..a68d129c9 100644 --- a/addons/api/mirage/models/storage-bucket.js +++ b/addons/api/mirage/models/storage-bucket.js @@ -1,3 +1,8 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + import { belongsTo } from 'miragejs'; import Model from './base'; export default Model.extend({ diff --git a/addons/api/mirage/scenarios/default.js b/addons/api/mirage/scenarios/default.js index eb8531c2f..e174d7d79 100644 --- a/addons/api/mirage/scenarios/default.js +++ b/addons/api/mirage/scenarios/default.js @@ -113,4 +113,24 @@ export default function (server) { // Session Recordings and Storage Buckets server.createList('storage-bucket', 3, { scope: globalScope }); server.createList('storage-bucket', 2, { scope: orgScope }); + server.createList( + 'recording', + 3, + { scope: globalScope }, + 'withConnectionAndChannels', + 'withExistingUserAndTarget' + ); + server.create( + 'recording', + { scope: globalScope }, + 'withConnectionAndChannels', + 'withNonExistingUserAndTarget' + ); + server.createList( + 'recording', + 2, + { scope: orgScope }, + 'withConnectionAndChannels', + 'withExistingUserAndTarget' + ); } diff --git a/addons/api/mirage/serializers/channel.js b/addons/api/mirage/serializers/channel.js new file mode 100644 index 000000000..839e46f34 --- /dev/null +++ b/addons/api/mirage/serializers/channel.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import ApplicationSerializer from './application'; + +export default ApplicationSerializer.extend({ + modelName: 'channel', +}); diff --git a/addons/api/mirage/serializers/connection.js b/addons/api/mirage/serializers/connection.js new file mode 100644 index 000000000..6de18bbec --- /dev/null +++ b/addons/api/mirage/serializers/connection.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import ApplicationSerializer from './application'; +import ChannelSerializer from './channel'; + +export default ApplicationSerializer.extend({ + modelName: 'connection', + _hashForModel(model) { + const json = ApplicationSerializer.prototype._hashForModel.apply( + this, + arguments + ); + const { channels } = this.schema; + if (model.channelIds) { + json.channels = model.channelIds.map((id) => { + const channel = channels.find(id); + return ChannelSerializer.prototype._hashForModel.apply(this, [channel]); + }); + } + return json; + }, +}); diff --git a/addons/api/mirage/serializers/recording.js b/addons/api/mirage/serializers/recording.js new file mode 100644 index 000000000..a4ce487d3 --- /dev/null +++ b/addons/api/mirage/serializers/recording.js @@ -0,0 +1,43 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import ApplicationSerializer from './application'; +import ConnectionSerializer from './connection'; +import UserSerializer from './user'; +import TargetSerializer from './target'; + +export default ApplicationSerializer.extend({ + modelName: 'recording', + _hashForModel(model) { + const json = ApplicationSerializer.prototype._hashForModel.apply( + this, + arguments + ); + const { connections, users, targets } = this.schema; + + // Manually map our models to the actual embedded json representation + if (model.connectionIds) { + json.connections = model.connectionIds.map((id) => { + const connection = connections.find(id); + return ConnectionSerializer.prototype._hashForModel.apply(this, [ + connection, + ]); + }); + } + if (model.userId) { + const user = users.find(model.userId); + json.user = UserSerializer.prototype._hashForModel.apply(this, [user]); + } + if (model.targetId) { + const target = targets.find(model.targetId); + json.target = TargetSerializer.prototype._hashForModel.apply(this, [ + target, + ]); + } + + json.session_id = model.sessionId; + return json; + }, +}); diff --git a/addons/api/mirage/serializers/storage-bucket.js b/addons/api/mirage/serializers/storage-bucket.js index 7a5bc48df..ca9f87933 100644 --- a/addons/api/mirage/serializers/storage-bucket.js +++ b/addons/api/mirage/serializers/storage-bucket.js @@ -1,3 +1,8 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + import ApplicationSerializer from './application'; export default ApplicationSerializer.extend({ modelName: 'storage-bucket', diff --git a/addons/api/tests/unit/serializers/connection-test.js b/addons/api/tests/unit/serializers/connection-test.js new file mode 100644 index 000000000..02a53897d --- /dev/null +++ b/addons/api/tests/unit/serializers/connection-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Serializer | connection', function (hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let serializer = store.serializerFor('connection'); + + assert.ok(serializer); + }); + + test('it serializes records', function (assert) { + let store = this.owner.lookup('service:store'); + let record = store.createRecord('connection', {}); + + let serializedRecord = record.serialize(); + + assert.ok(serializedRecord); + }); +}); diff --git a/addons/api/tests/unit/serializers/recording-test.js b/addons/api/tests/unit/serializers/recording-test.js new file mode 100644 index 000000000..458e02c16 --- /dev/null +++ b/addons/api/tests/unit/serializers/recording-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Serializer | recording', function (hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let serializer = store.serializerFor('recording'); + + assert.ok(serializer); + }); + + test('it serializes records', function (assert) { + let store = this.owner.lookup('service:store'); + let record = store.createRecord('recording', {}); + + let serializedRecord = record.serialize(); + + assert.ok(serializedRecord); + }); +});