refactor: 💡 Add channel ability for asciicasts (#174)
This commit is contained in:
1 parent
3bf2620b2c
commit
62ec3fe914
14 files changed
+1783
-860
No files matched your search
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import ModelAbility from './model';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
/**
|
||||
* Provides abilities for channel recordings.
|
||||
*/
|
||||
export default class ChannelRecordingAbility extends ModelAbility {
|
||||
// =services
|
||||
@service abilities;
|
||||
|
||||
// =permissions
|
||||
get canDownloadAsciicast() {
|
||||
const sessionRecording = this.model.connection_recording.session_recording;
|
||||
const sessionRecordingAbility = this.abilities.abilityFor(
|
||||
'session-recording',
|
||||
sessionRecording
|
||||
);
|
||||
|
||||
return this.model.isAsciicast && sessionRecordingAbility.canDownload;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ export default class SessionRecordingAbility extends ModelAbility {
|
||||
);
|
||||
}
|
||||
|
||||
get canDownloadAsciiCast() {
|
||||
get canDownload() {
|
||||
return this.model.isSSH && this.hasAuthorizedAction('download');
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import ApplicationAdapter from './application';
|
||||
import { MIME_TYPE_ASCIICAST } from 'api/models/channel-recording';
|
||||
|
||||
export default class SessionRecordingAdapter extends ApplicationAdapter {
|
||||
/**
|
||||
@@ -49,7 +50,7 @@ export default class SessionRecordingAdapter extends ApplicationAdapter {
|
||||
getAsciicast(id) {
|
||||
return this.getRecordingDownload({
|
||||
id,
|
||||
mimeType: 'application/x-asciicast',
|
||||
mimeType: MIME_TYPE_ASCIICAST,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,18 @@
|
||||
import GeneratedChannelRecordingModel from '../generated/models/channel-recording';
|
||||
import { belongsTo } from '@ember-data/model';
|
||||
|
||||
export const MIME_TYPE_ASCIICAST = 'application/x-asciicast';
|
||||
|
||||
export default class ChannelRecordingModel extends GeneratedChannelRecordingModel {
|
||||
@belongsTo('connection-recording') connection_recording;
|
||||
@belongsTo('connection-recording', { async: false }) connection_recording;
|
||||
|
||||
/**
|
||||
* True if the channel supports asciicast.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isAsciicast() {
|
||||
return this.mime_types.includes(MIME_TYPE_ASCIICAST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the ascii cast recording for this channel.
|
||||
|
||||
@@ -2,6 +2,6 @@ import GeneratedConnectionRecordingModel from '../generated/models/connection-re
|
||||
import { belongsTo, hasMany } from '@ember-data/model';
|
||||
|
||||
export default class ConnectionRecordingModel extends GeneratedConnectionRecordingModel {
|
||||
@belongsTo('session-recording') session_recording;
|
||||
@hasMany('channel-recording') channel_recordings;
|
||||
@belongsTo('session-recording', { async: false }) session_recording;
|
||||
@hasMany('channel-recording', { async: false }) channel_recordings;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ export const TYPES_SESSION_RECORDING = Object.freeze([
|
||||
]);
|
||||
|
||||
export default class SessionRecordingModel extends GeneratedSessionRecordingModel {
|
||||
@hasMany('connection-recording') connection_recordings;
|
||||
@hasMany('connection-recording', { async: false }) connection_recordings;
|
||||
|
||||
@service store;
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
export { default } from 'api/abilities/channel-recording';
|
||||
@@ -1,3 +1,6 @@
|
||||
import { Ability } from 'ember-can';
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
export default class extends Ability {}
|
||||
export { default } from 'api/abilities/session-recording';
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { Factory } from 'miragejs';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { MIME_TYPE_ASCIICAST } from 'api/models/channel-recording';
|
||||
|
||||
/**
|
||||
* GeneratedChannelRecordingModelFactory
|
||||
@@ -20,5 +21,5 @@ export default Factory.extend({
|
||||
},
|
||||
end_time: () => faker.date.recent(),
|
||||
duration: () => `${faker.datatype.number()}s`,
|
||||
mime_types: () => ['application/x-asciicast'],
|
||||
mime_types: () => [MIME_TYPE_ASCIICAST],
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { MIME_TYPE_ASCIICAST } from 'api/models/channel-recording';
|
||||
import { TYPE_SESSION_RECORDING_SSH } from 'api/models/session-recording';
|
||||
|
||||
module('Unit | Ability | channel-recording', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
let canService;
|
||||
let store;
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
canService = this.owner.lookup('service:can');
|
||||
store = this.owner.lookup('service:store');
|
||||
});
|
||||
|
||||
test('it exists', function (assert) {
|
||||
const ability = this.owner.lookup('ability:channel-recording');
|
||||
assert.ok(ability);
|
||||
});
|
||||
|
||||
test('it can download asciicast', function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const sessionRecordingModel = store.createRecord('session-recording', {
|
||||
authorized_actions: ['download'],
|
||||
type: TYPE_SESSION_RECORDING_SSH,
|
||||
});
|
||||
const connectionRecordingModel = store.createRecord(
|
||||
'connection-recording',
|
||||
{
|
||||
session_recording: sessionRecordingModel,
|
||||
}
|
||||
);
|
||||
const channelRecordingModel = store.createRecord('channel-recording', {
|
||||
mime_types: [MIME_TYPE_ASCIICAST],
|
||||
connection_recording: connectionRecordingModel,
|
||||
});
|
||||
|
||||
assert.true(
|
||||
canService.can(
|
||||
'downloadAsciicast channel-recording',
|
||||
channelRecordingModel
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('it cannot download asciicast without the correct mime type', function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const sessionRecordingModel = store.createRecord('session-recording', {
|
||||
authorized_actions: ['download'],
|
||||
type: TYPE_SESSION_RECORDING_SSH,
|
||||
});
|
||||
const connectionRecordingModel = store.createRecord(
|
||||
'connection-recording',
|
||||
{
|
||||
session_recording: sessionRecordingModel,
|
||||
}
|
||||
);
|
||||
const channelRecordingModel = store.createRecord('channel-recording', {
|
||||
mime_types: ['unknown'],
|
||||
connection_recording: connectionRecordingModel,
|
||||
});
|
||||
|
||||
assert.false(
|
||||
canService.can(
|
||||
'downloadAsciicast channel-recording',
|
||||
channelRecordingModel
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { TYPE_SESSION_RECORDING_SSH } from 'api/models/session-recording';
|
||||
|
||||
module('Unit | Ability | session-recording', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
let canService;
|
||||
let store;
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
canService = this.owner.lookup('service:can');
|
||||
store = this.owner.lookup('service:store');
|
||||
});
|
||||
|
||||
test('it exists', function (assert) {
|
||||
const ability = this.owner.lookup('ability:session-recording');
|
||||
assert.ok(ability);
|
||||
});
|
||||
|
||||
test('can download when an ssh recording has download authorization', function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const recordingWithAuthorizedAction = store.createRecord(
|
||||
'session-recording',
|
||||
{
|
||||
authorized_actions: ['download'],
|
||||
type: TYPE_SESSION_RECORDING_SSH,
|
||||
}
|
||||
);
|
||||
const recordingWithoutAuthorizedAction = store.createRecord(
|
||||
'session-recording',
|
||||
{
|
||||
authorized_actions: [],
|
||||
type: TYPE_SESSION_RECORDING_SSH,
|
||||
}
|
||||
);
|
||||
|
||||
assert.true(
|
||||
canService.can(
|
||||
'download session-recording',
|
||||
recordingWithAuthorizedAction
|
||||
)
|
||||
);
|
||||
assert.false(
|
||||
canService.can(
|
||||
'download session-recording',
|
||||
recordingWithoutAuthorizedAction
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('cannot download when a recording is not ssh type', function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const recordingWithAuthorizedAction = store.createRecord(
|
||||
'session-recording',
|
||||
{
|
||||
authorized_actions: ['download'],
|
||||
type: 'unknown',
|
||||
}
|
||||
);
|
||||
const recordingWithoutAuthorizedAction = store.createRecord(
|
||||
'session-recording',
|
||||
{
|
||||
authorized_actions: [],
|
||||
type: 'unknown',
|
||||
}
|
||||
);
|
||||
|
||||
assert.false(
|
||||
canService.can(
|
||||
'download session-recording',
|
||||
recordingWithAuthorizedAction
|
||||
)
|
||||
);
|
||||
assert.false(
|
||||
canService.can(
|
||||
'download session-recording',
|
||||
recordingWithoutAuthorizedAction
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { MIME_TYPE_ASCIICAST } from 'api/models/channel-recording';
|
||||
|
||||
module('Unit | Model | channel-recording', function (hooks) {
|
||||
setupTest(hooks);
|
||||
@@ -9,4 +10,17 @@ module('Unit | Model | channel-recording', function (hooks) {
|
||||
let model = store.createRecord('channel-recording', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
|
||||
test('it has isAsciicast property and returns the expected values', async function (assert) {
|
||||
assert.expect(2);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const modelA = store.createRecord('channel-recording', {
|
||||
mime_types: [MIME_TYPE_ASCIICAST],
|
||||
});
|
||||
const modelB = store.createRecord('channel-recording', {
|
||||
mime_types: ['random'],
|
||||
});
|
||||
assert.true(modelA.isAsciicast);
|
||||
assert.false(modelB.isAsciicast);
|
||||
});
|
||||
});
|
||||
@@ -202,68 +202,4 @@ module('Unit | Abilities | session-recording', function (hooks) {
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('can download asciicast when an ssh recording has download authorization', function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const recordingWithAuthorizedAction = store.createRecord(
|
||||
'session-recording',
|
||||
{
|
||||
authorized_actions: ['download'],
|
||||
type: TYPE_SESSION_RECORDING_SSH,
|
||||
}
|
||||
);
|
||||
const recordingWithoutAuthorizedAction = store.createRecord(
|
||||
'session-recording',
|
||||
{
|
||||
authorized_actions: [],
|
||||
type: TYPE_SESSION_RECORDING_SSH,
|
||||
}
|
||||
);
|
||||
|
||||
assert.true(
|
||||
canService.can(
|
||||
'downloadAsciiCast session-recording',
|
||||
recordingWithAuthorizedAction
|
||||
)
|
||||
);
|
||||
assert.false(
|
||||
canService.can(
|
||||
'downloadAsciiCast session-recording',
|
||||
recordingWithoutAuthorizedAction
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('cannot download asciicast when a recording is not ssh type', function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const recordingWithAuthorizedAction = store.createRecord(
|
||||
'session-recording',
|
||||
{
|
||||
authorized_actions: ['download'],
|
||||
type: 'unknown',
|
||||
}
|
||||
);
|
||||
const recordingWithoutAuthorizedAction = store.createRecord(
|
||||
'session-recording',
|
||||
{
|
||||
authorized_actions: [],
|
||||
type: 'unknown',
|
||||
}
|
||||
);
|
||||
|
||||
assert.false(
|
||||
canService.can(
|
||||
'downloadAsciiCast session-recording',
|
||||
recordingWithAuthorizedAction
|
||||
)
|
||||
);
|
||||
assert.false(
|
||||
canService.can(
|
||||
'downloadAsciiCast session-recording',
|
||||
recordingWithoutAuthorizedAction
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user