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 Component from '@ember/component';
import { get, set } from '@ember/object'; import { get, set } from '@ember/object';
import { run, next } from '@ember/runloop'; import { run, next } from '@ember/runloop';
import { task } from 'ember-concurrency';
import { import {
baseCommentEditorOptions, baseCommentEditorOptions,
DatasetPlatform,
exemptPolicy, exemptPolicy,
getSupportedPurgePolicies,
isExempt, isExempt,
missingPolicyText, missingPolicyText,
PurgePolicy, PurgePolicy,
purgePolicyProps purgePolicyProps
} from 'wherehows-web/constants'; } 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 * 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 * Reference to the informational text if the dataset does not have a saved purge policy
* @type {string} * @type {string}
*/ */
missingPolicyText, missingPolicyText = missingPolicyText;
/** /**
* Reference to client options for each purge policy * Reference to client options for each purge policy
* @type {PurgePolicyProperties} * @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 * The dataset's platform
* @type {DatasetPlatform}
* @memberof PurgePolicyComponent
*/ */
platform: null, platform: DatasetPlatform;
/** /**
* The currently save policy for the dataset purge * 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 * An options hash for the purge exempt reason text editor
* @type {} * @type {}
* @memberof PurgePolicyComponent
*/ */
editorOptions: { editorOptions = {
...baseCommentEditorOptions, ...baseCommentEditorOptions,
placeholder: { placeholder: {
text: 'Please provide an explanation for why this dataset is marked "Purge Exempt" status', text: 'Please provide an explanation for why this dataset is marked "Purge Exempt" status',
hideOnClick: false hideOnClick: false
} }
}, };
/** /**
* Action to handle policy change, by default a no-op function * 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() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
this.checkExemption(get(this, 'purgePolicy')); 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 * 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 // this allows us to ensure that editor it visible after the set above has been performed
run(() => next(this, 'focusEditor')); run(() => next(this, 'focusEditor'));
} }
}, }
/** /**
* Applies cursor / document focus to the purge note text editor * Applies cursor / document focus to the purge note text editor
@ -90,16 +132,16 @@ export default Component.extend({
if (exemptionReasonElement) { if (exemptionReasonElement) {
exemptionReasonElement.focus(); exemptionReasonElement.focus();
} }
}, }
actions: { actions = {
/** /**
* Handles the change to the currently selected purge policy * Handles the change to the currently selected purge policy
* @param {string} _name unused name for the radio group * @param {string} _name unused name for the radio group
* @param {PurgePolicy} purgePolicy the selected purge policy * @param {PurgePolicy} purgePolicy the selected purge policy
*/ */
onChange(_name: string, purgePolicy: PurgePolicy) { onChange(this: PurgePolicyComponent, _name: string, purgePolicy: PurgePolicy) {
return get(this, 'onPolicyChange')(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 * Available values for the purge policy
@ -17,7 +17,6 @@ enum PurgePolicy {
*/ */
type PurgePolicyProperties = { type PurgePolicyProperties = {
[K in PurgePolicy]: { [K in PurgePolicy]: {
platforms: Array<DatasetPlatform>;
desc: string; desc: string;
displayAs: string; displayAs: string;
} }
@ -29,40 +28,43 @@ type PurgePolicyProperties = {
*/ */
const purgePolicyProps: PurgePolicyProperties = { const purgePolicyProps: PurgePolicyProperties = {
AUTO_PURGE: { AUTO_PURGE: {
platforms: [DatasetPlatform.Teradata, DatasetPlatform.Espresso, DatasetPlatform.HDFS],
desc: 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).', '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' displayAs: 'Auto Purge'
}, },
MANUAL_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.', desc: 'Choose this option only if you or your team have implemented a custom mechanism to purge this dataset.',
displayAs: 'Manual Purge' displayAs: 'Manual Purge'
}, },
LIMITED_RETENTION: { LIMITED_RETENTION: {
platforms: [DatasetPlatform.Kafka, DatasetPlatform.Teradata, DatasetPlatform.HDFS],
desc: desc:
'Choose this option only if you rely on the data platforms default limited retention mechanism to purge your data.', 'Choose this option only if you rely on the data platforms default limited retention mechanism to purge your data.',
displayAs: 'Auto Limited Retention' displayAs: 'Auto Limited Retention'
}, },
MANUAL_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.', desc: 'Choose this option only if you have a well established process to ensure limited data retention.',
displayAs: 'Manual Limited Retention' displayAs: 'Manual Limited Retention'
}, },
PURGE_EXEMPTED: { 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', desc: 'Choose this option only if the dataset is explicitly exempted from purging',
displayAs: 'Purge Exempt' 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 * A cache for the exempt policy
* @type {PurgePolicy} * @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.'; 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 {string}
*/ */
enum DatasetPlatform { enum DatasetPlatform {
Kafka = 'KAFKA', Kafka = 'kafka',
Espresso = 'ESPRESSO', Espresso = 'espresso',
Oracle = 'ORACLE', Oracle = 'oracle',
MySql = 'MYSQL', MySql = 'mysql',
Teradata = 'TERADATA', Teradata = 'teradata',
HDFS = 'HDFS', HDFS = 'hdfs',
Ambry = 'AMBRY', Ambry = 'ambry',
Couchbase = 'COUCHBASE', Couchbase = 'couchbase',
Voldemort = 'VOLDEMORT', Voldemort = 'voldemort',
Venice = 'VENICE', Venice = 'venice',
Hive = 'HIVE' Hive = 'hive'
} }
export { DatasetPlatform }; export { DatasetPlatform };

View File

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

View File

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

View File

@ -67,7 +67,7 @@ declare module 'ember-concurrency' {
cancelAll(): void; 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>( export function task<T, A1, A2>(
generatorFn: (a1: A1, a2: A2) => Iterator<T> 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) { export default function(server) {
const fixtures = ['dataset-nodes', 'metric-metrics', 'user-entities', 'compliance-data-types'];
server.loadFixtures(...fixtures); server.loadFixtures(...fixtures);
server.create('config'); server.create('config');
server.createList('owner', 6); server.createList('owner', 6);

View File

@ -1,10 +1,23 @@
import { moduleForComponent, test } from 'ember-qunit'; import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile'; import hbs from 'htmlbars-inline-precompile';
import { triggerEvent } from 'ember-native-dom-helpers'; import { triggerEvent, waitUntil, find } from 'ember-native-dom-helpers';
import { missingPolicyText, purgePolicyProps, exemptPolicy } from 'wherehows-web/constants'; 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', { 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'; 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); assert.expect(1);
const selectedPolicy = policyTypes[1]; const selectedPolicy = PurgePolicy.ManualPurge;
const platform = purgePolicyProps[selectedPolicy].platforms[0]; const platform = DatasetPlatform.MySql;
this.set('isEditable', true); this.set('isEditable', true);
this.set('platform', platform); this.set('platform', platform);
this.set('purgePolicy', selectedPolicy); 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( assert.ok(
document.querySelector(`${policyList} [type=radio][value=${selectedPolicy}]`).checked, document.querySelector(`${policyList} [type=radio][value=${selectedPolicy}]`).checked,
`${selectedPolicy} radio is 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'); assert.equal(++focusMethodCount, 1, 'focusEditor action is invoked');
}; };
let selectedPolicy = exemptPolicy; let selectedPolicy = exemptPolicy;
let platform = purgePolicyProps[selectedPolicy].platforms[0]; let platform = DatasetPlatform.MySql;
let focusMethodCount = 0; let focusMethodCount = 0;
this.setProperties({ this.setProperties({