Merge pull request #932 from theseyi/master

depends on platforms api to provide supportedPurgePolicies for each p…
This commit is contained in:
Seyi Adebajo 2018-01-10 14:21:20 -08:00 committed by GitHub
commit 23e449983e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 254 additions and 84 deletions

View File

@ -1,69 +1,111 @@
import Component from '@ember/component';
import { get, set } from '@ember/object';
import { run, next } from '@ember/runloop';
import { task } from 'ember-concurrency';
import {
baseCommentEditorOptions,
DatasetPlatform,
exemptPolicy,
getSupportedPurgePolicies,
isExempt,
missingPolicyText,
PurgePolicy,
purgePolicyProps
} from 'wherehows-web/constants';
import noop from 'wherehows-web/utils/noop';
import { IComplianceInfo } from 'wherehows-web/typings/api/datasets/compliance';
import { IDataPlatform } from 'wherehows-web/typings/api/list/platforms';
import { readPlatforms } from 'wherehows-web/utils/api/list/platforms';
export default Component.extend({
export default class PurgePolicyComponent extends Component {
/**
* Reference to the purge exempt policy
* @type {string}
* @type {PurgePolicy}
*/
exemptPolicy,
exemptPolicy = exemptPolicy;
/**
* Reference to the informational text if the dataset does not have a saved purge policy
* @type {string}
*/
missingPolicyText,
missingPolicyText = missingPolicyText;
/**
* Reference to client options for each purge policy
* @type {PurgePolicyProperties}
*/
purgePolicyProps,
purgePolicyProps = purgePolicyProps;
/**
* The list of supported purge policies for the related platform
* @type {Array<PurgePolicy>}
* @memberof PurgePolicyComponent
*/
supportedPurgePolicies: Array<PurgePolicy> = [];
/**
* The dataset's platform
* @type {DatasetPlatform}
* @memberof PurgePolicyComponent
*/
platform: null,
platform: DatasetPlatform;
/**
* The currently save policy for the dataset purge
* @type {PurgePolicy}
* @memberof PurgePolicyComponent
*/
purgePolicy: <PurgePolicy>'',
purgePolicy: PurgePolicy;
requestExemptionReason: false,
/**
* Flag indication that policy has a request exemption reason
* @type {boolean}
*/
requestExemptionReason = false;
/**
* An options hash for the purge exempt reason text editor
* @type {}
* @memberof PurgePolicyComponent
*/
editorOptions: {
editorOptions = {
...baseCommentEditorOptions,
placeholder: {
text: 'Please provide an explanation for why this dataset is marked "Purge Exempt" status',
hideOnClick: false
}
},
};
/**
* Action to handle policy change, by default a no-op function
* @type {Function}
* @type {(purgePolicy: PurgePolicy) => IComplianceInfo['complianceType'] | null}
* @memberof PurgePolicyComponent
*/
onPolicyChange: noop,
onPolicyChange: (purgePolicy: PurgePolicy) => IComplianceInfo['complianceType'] | null;
didReceiveAttrs() {
this._super(...arguments);
this.checkExemption(get(this, 'purgePolicy'));
},
}
didInsertElement() {
get(this, 'getPlatformPolicies').perform();
}
/**
* Task to retrieve platform policies for and set supported policies for the current platform
* @memberof PurgePolicyComponent
*/
getPlatformPolicies = task(function*(
this: PurgePolicyComponent
): IterableIterator<Array<PurgePolicy> | Promise<Array<IDataPlatform>>> {
const platform = get(this, 'platform');
if (platform) {
const supportedPurgePolicies = getSupportedPurgePolicies(platform, yield readPlatforms());
yield set(this, 'supportedPurgePolicies', supportedPurgePolicies);
}
}).restartable();
/**
* Checks that the selected purge policy is exempt, if so, set the
@ -79,7 +121,7 @@ export default Component.extend({
// this allows us to ensure that editor it visible after the set above has been performed
run(() => next(this, 'focusEditor'));
}
},
}
/**
* Applies cursor / document focus to the purge note text editor
@ -90,16 +132,16 @@ export default Component.extend({
if (exemptionReasonElement) {
exemptionReasonElement.focus();
}
},
}
actions: {
actions = {
/**
* Handles the change to the currently selected purge policy
* @param {string} _name unused name for the radio group
* @param {PurgePolicy} purgePolicy the selected purge policy
*/
onChange(_name: string, purgePolicy: PurgePolicy) {
onChange(this: PurgePolicyComponent, _name: string, purgePolicy: PurgePolicy) {
return get(this, 'onPolicyChange')(purgePolicy);
}
}
});
};
}

View File

@ -1,4 +1,4 @@
import { DatasetPlatform } from 'wherehows-web/constants/datasets/platform';
import { IDataPlatform } from 'wherehows-web/typings/api/list/platforms';
/**
* Available values for the purge policy
@ -17,7 +17,6 @@ enum PurgePolicy {
*/
type PurgePolicyProperties = {
[K in PurgePolicy]: {
platforms: Array<DatasetPlatform>;
desc: string;
displayAs: string;
}
@ -29,40 +28,43 @@ type PurgePolicyProperties = {
*/
const purgePolicyProps: PurgePolicyProperties = {
AUTO_PURGE: {
platforms: [DatasetPlatform.Teradata, DatasetPlatform.Espresso, DatasetPlatform.HDFS],
desc:
'Choose this option only if its acceptable to have the centralized system purge this dataset based on the provided metadata (e.g. member ID, seat ID etc).',
displayAs: 'Auto Purge'
},
MANUAL_PURGE: {
platforms: [
DatasetPlatform.MySql,
DatasetPlatform.Espresso,
DatasetPlatform.Teradata,
DatasetPlatform.Oracle,
DatasetPlatform.HDFS
],
desc: 'Choose this option only if you or your team have implemented a custom mechanism to purge this dataset.',
displayAs: 'Manual Purge'
},
LIMITED_RETENTION: {
platforms: [DatasetPlatform.Kafka, DatasetPlatform.Teradata, DatasetPlatform.HDFS],
desc:
'Choose this option only if you rely on the data platforms default limited retention mechanism to purge your data.',
displayAs: 'Auto Limited Retention'
},
MANUAL_LIMITED_RETENTION: {
platforms: [DatasetPlatform.Espresso, DatasetPlatform.Oracle, DatasetPlatform.MySql],
desc: 'Choose this option only if you have a well established process to ensure limited data retention.',
displayAs: 'Manual Limited Retention'
},
PURGE_EXEMPTED: {
platforms: Object.keys(DatasetPlatform).map((k: keyof typeof DatasetPlatform) => DatasetPlatform[k]),
desc: 'Choose this option only if the dataset is explicitly exempted from purging',
displayAs: 'Purge Exempt'
}
};
/**
* Extracts the purge policy for a given platform from the list of DatasetPlatforms
* @param {IDataPlatform.name} platformName the name of the dataset platform
* @param {Array<IDataPlatform>} [platforms=[]] the list of objects with IDataPlatform interface
* @returns {Array<PurgePolicy>}
*/
const getSupportedPurgePolicies = (
platformName: IDataPlatform['name'],
platforms: Array<IDataPlatform> = []
): Array<PurgePolicy> => {
const platform = platforms.findBy('name', platformName);
return platform ? platform.supportedPurgePolicies : [];
};
/**
* A cache for the exempt policy
* @type {PurgePolicy}
@ -81,4 +83,4 @@ const isExempt = (policy: PurgePolicy) => policy === PurgePolicy.PurgeExempt;
*/
const missingPolicyText = 'This dataset does not have a current compliance purge policy.';
export { PurgePolicy, purgePolicyProps, isExempt, exemptPolicy, missingPolicyText };
export { PurgePolicy, purgePolicyProps, isExempt, exemptPolicy, missingPolicyText, getSupportedPurgePolicies };

View File

@ -3,17 +3,17 @@
* @enum {string}
*/
enum DatasetPlatform {
Kafka = 'KAFKA',
Espresso = 'ESPRESSO',
Oracle = 'ORACLE',
MySql = 'MYSQL',
Teradata = 'TERADATA',
HDFS = 'HDFS',
Ambry = 'AMBRY',
Couchbase = 'COUCHBASE',
Voldemort = 'VOLDEMORT',
Venice = 'VENICE',
Hive = 'HIVE'
Kafka = 'kafka',
Espresso = 'espresso',
Oracle = 'oracle',
MySql = 'mysql',
Teradata = 'teradata',
HDFS = 'hdfs',
Ambry = 'ambry',
Couchbase = 'couchbase',
Voldemort = 'voldemort',
Venice = 'venice',
Hive = 'hive'
}
export { DatasetPlatform };

View File

@ -18,4 +18,8 @@
@include pill(set-color(red, maroonflush), set-color(white, base));
}
}
&__retry-platforms {
@include flex-column-center;
}
}

View File

@ -9,40 +9,65 @@
<ul class="purge-policy-list">
{{#if isEditable}}
{{#each-in purgePolicyProps as |purgeType prop|}}
<li
class="purge-policy-list__item {{unless (contains (uppercase platform) prop.platforms)
'purge-policy-list__item--disabled'}}">
{{#if getPlatformPolicies.isRunning}}
{{pendulum-ellipsis-animation}}
{{/if}}
{{#radio-button-composer
name="dataset-purge-policy"
value=(if (contains (uppercase platform) prop.platforms) purgeType null)
disabled=(if (contains (uppercase platform) prop.platforms) false true)
groupValue=(readonly purgePolicy)
changed=(action "onChange")
}}
{{prop.displayAs}}
{{/radio-button-composer}}
{{#if getPlatformPolicies.last.isError}}
{{!--todo: swap out with error-state component--}}
{{empty-state
heading="Purge Policies not available"
subHead="Could not find supported purge policies for this platform"
}}
{{#unless (contains (uppercase platform) prop.platforms)}}
<p class="purge-policy-list__availability">
Purge policy not available for <span class="purge-policy-list__platform--unavailable">
{{lowercase platform}}</span>
</p>
{{/unless}}
<div class="purge-policy-list__retry-platforms">
<button
class="nacho-button nacho-button--large-inverse"
onclick={{perform getPlatformPolicies}}>
Retry
</button>
</div>
<p>{{prop.desc}}</p>
{{/if}}
{{#if (and requestExemptionReason (eq purgeType exemptPolicy))}}
{{#if getPlatformPolicies.last.isSuccessful}}
{{medium-content-editable
value=purgeNote
options=editorOptions
class="comment-new__content"}}
{{#each-in purgePolicyProps as |purgeType prop|}}
<li
class="purge-policy-list__item {{unless (contains purgeType supportedPurgePolicies)
'purge-policy-list__item--disabled'}}">
{{/if}}
</li>
{{/each-in}}
{{#radio-button-composer
name="dataset-purge-policy"
value=(if (contains purgeType supportedPurgePolicies) purgeType null)
disabled=(if (contains purgeType supportedPurgePolicies) false true)
groupValue=(readonly purgePolicy)
changed=(action "onChange")
}}
{{prop.displayAs}}
{{/radio-button-composer}}
{{#unless (contains purgeType supportedPurgePolicies)}}
<p class="purge-policy-list__availability">
Purge policy not available for <span class="purge-policy-list__platform--unavailable">
{{platform}}</span>
</p>
{{/unless}}
<p>{{prop.desc}}</p>
{{#if (and requestExemptionReason (eq purgeType exemptPolicy))}}
{{medium-content-editable
value=purgeNote
options=editorOptions
class="comment-new__content"}}
{{/if}}
</li>
{{/each-in}}
{{/if}}
{{else}}

View File

@ -8,7 +8,7 @@ import { ApiStatus } from 'wherehows-web/utils/api/shared';
*/
export interface IDataPlatform {
// Policies supported for this dataset
supportedPolicies: Array<PurgePolicy>;
supportedPurgePolicies: Array<PurgePolicy>;
// the type of the dataset platform with the given name e.g. DISTRIBUTED_FILE_SYSTEM, RELATIONAL_DB
type: string;
// the name of the dataset platform

View File

@ -67,7 +67,7 @@ declare module 'ember-concurrency' {
cancelAll(): void;
};
export function task<T, A>(generatorFn: (a: A) => Iterator<T>): Task<T, (a: A) => TaskInstance<T>>;
export function task<T, A>(generatorFn: (a: A) => Iterator<T>): Task<T, (a?: A) => TaskInstance<T>>;
export function task<T, A1, A2>(
generatorFn: (a1: A1, a2: A2) => Iterator<T>

View File

@ -0,0 +1,75 @@
export default [
{
supportedPurgePolicies: ['AUTO_PURGE', 'MANUAL_PURGE', 'MANUAL_LIMITED_RETENTION', 'PURGE_EXEMPTED'],
type: 'DISTRIBUTED_KEY_VALUE_STORE',
name: 'espresso'
},
{
supportedPurgePolicies: [
'AUTO_PURGE',
'MANUAL_PURGE',
'LIMITED_RETENTION',
'MANUAL_LIMITED_RETENTION',
'PURGE_EXEMPTED'
],
type: 'DISTRIBUTED_FILE_SYSTEM',
name: 'hdfs'
},
{
supportedPurgePolicies: [
'AUTO_PURGE',
'MANUAL_PURGE',
'LIMITED_RETENTION',
'MANUAL_LIMITED_RETENTION',
'PURGE_EXEMPTED'
],
type: 'DISTRIBUTED_FILE_SYSTEM',
name: 'hive'
},
{
supportedPurgePolicies: ['LIMITED_RETENTION'],
type: 'DISTRIBUTED_MESSAGE_BROKER',
name: 'kafka'
},
{
supportedPurgePolicies: ['MANUAL_PURGE', 'MANUAL_LIMITED_RETENTION', 'PURGE_EXEMPTED'],
type: 'RELATIONAL_DB',
name: 'mysql'
},
{
supportedPurgePolicies: ['MANUAL_PURGE', 'MANUAL_LIMITED_RETENTION', 'PURGE_EXEMPTED'],
type: 'RELATIONAL_DB',
name: 'oracle'
},
{
supportedPurgePolicies: [
'AUTO_PURGE',
'MANUAL_PURGE',
'LIMITED_RETENTION',
'MANUAL_LIMITED_RETENTION',
'PURGE_EXEMPTED'
],
type: 'RELATIONAL_DB',
name: 'teradata'
},
{
supportedPurgePolicies: ['MANUAL_LIMITED_RETENTION', 'PURGE_EXEMPTED'],
type: 'DISTRIBUTED_KEY_VALUE_STORE',
name: 'venice'
},
{
supportedPurgePolicies: ['MANUAL_LIMITED_RETENTION', 'PURGE_EXEMPTED'],
type: 'DISTRIBUTED_KEY_VALUE_STORE',
name: 'voldemort'
},
{
supportedPurgePolicies: ['MANUAL_PURGE', 'MANUAL_LIMITED_RETENTION', 'PURGE_EXEMPTED'],
type: 'DISTRIBUTED_KEY_VALUE_STORE',
name: 'couchbase'
},
{
supportedPurgePolicies: ['MANUAL_PURGE', 'MANUAL_LIMITED_RETENTION', 'PURGE_EXEMPTED'],
type: 'DISTRIBUTED_OBJECT_STORE',
name: 'ambry'
}
];

View File

@ -1,5 +1,6 @@
const fixtures = ['dataset-nodes', 'metric-metrics', 'user-entities', 'compliance-data-types', 'list-platforms'];
export default function(server) {
const fixtures = ['dataset-nodes', 'metric-metrics', 'user-entities', 'compliance-data-types'];
server.loadFixtures(...fixtures);
server.create('config');
server.createList('owner', 6);

View File

@ -1,10 +1,23 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import { triggerEvent } from 'ember-native-dom-helpers';
import { missingPolicyText, purgePolicyProps, exemptPolicy } from 'wherehows-web/constants';
import { triggerEvent, waitUntil, find } from 'ember-native-dom-helpers';
import sinon from 'sinon';
import { missingPolicyText, purgePolicyProps, exemptPolicy, PurgePolicy } from 'wherehows-web/constants';
import { DatasetPlatform } from 'wherehows-web/constants/datasets/platform';
import platforms from 'wherehows-web/mirage/fixtures/list-platforms';
import { ApiStatus } from 'wherehows-web/utils/api';
moduleForComponent('purge-policy', 'Integration | Component | purge policy', {
integration: true
integration: true,
beforeEach() {
this.server = sinon.createFakeServer();
},
afterEach() {
this.server.restore();
}
});
const policyList = '.purge-policy-list';
@ -59,17 +72,25 @@ test('it renders a user message if the purge policy is not set and is in readonl
);
});
test('it indicates the currently selected purge policy', function(assert) {
test('it indicates the currently selected purge policy', async function(assert) {
assert.expect(1);
const selectedPolicy = policyTypes[1];
const platform = purgePolicyProps[selectedPolicy].platforms[0];
const selectedPolicy = PurgePolicy.ManualPurge;
const platform = DatasetPlatform.MySql;
this.set('isEditable', true);
this.set('platform', platform);
this.set('purgePolicy', selectedPolicy);
this.render(hbs`{{purge-policy isEditable=isEditable purgePolicy=purgePolicy platform=platform}}`);
this.server.respondWith('GET', '/api/v1/list/platforms', [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({ status: ApiStatus.OK, platforms })
]);
this.render(hbs`{{purge-policy isEditable=isEditable purgePolicy=purgePolicy platform=platform}}`);
this.server.respond();
await waitUntil(() => find(`${policyList} [type=radio][value=${selectedPolicy}]`));
assert.ok(
document.querySelector(`${policyList} [type=radio][value=${selectedPolicy}]`).checked,
`${selectedPolicy} radio is checked`
@ -83,7 +104,7 @@ test('it focuses the comment element for exempt policy', function(assert) {
assert.equal(++focusMethodCount, 1, 'focusEditor action is invoked');
};
let selectedPolicy = exemptPolicy;
let platform = purgePolicyProps[selectedPolicy].platforms[0];
let platform = DatasetPlatform.MySql;
let focusMethodCount = 0;
this.setProperties({