From 8f6d202ceff7b701ad40971a91489226c593ace5 Mon Sep 17 00:00:00 2001 From: Seyi Adebajo Date: Wed, 10 Jan 2018 08:21:20 -0800 Subject: [PATCH 1/3] depends on platforms api to provide supportedPurgePolicies for each platform. uses e-c for async manangement --- wherehows-web/app/components/purge-policy.ts | 82 ++++++++++++++----- .../app/constants/dataset-purge-policy.ts | 30 +++---- .../app/constants/datasets/platform.ts | 22 ++--- .../_purge-policy-list.scss | 4 + .../app/templates/components/purge-policy.hbs | 81 +++++++++++------- .../app/typings/api/list/platforms.d.ts | 2 +- .../app/typings/ember-concurrency.d.ts | 2 +- 7 files changed, 148 insertions(+), 75 deletions(-) diff --git a/wherehows-web/app/components/purge-policy.ts b/wherehows-web/app/components/purge-policy.ts index f7283335d3..f7c786a33b 100644 --- a/wherehows-web/app/components/purge-policy.ts +++ b/wherehows-web/app/components/purge-policy.ts @@ -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} + * @memberof PurgePolicyComponent + */ + supportedPurgePolicies: Array = []; /** * 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; - 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 | Promise>> { + 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); } - } -}); + }; +} diff --git a/wherehows-web/app/constants/dataset-purge-policy.ts b/wherehows-web/app/constants/dataset-purge-policy.ts index 345818885a..647fa1fa48 100644 --- a/wherehows-web/app/constants/dataset-purge-policy.ts +++ b/wherehows-web/app/constants/dataset-purge-policy.ts @@ -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; 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 it’s 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 platform’s 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} [platforms=[]] the list of objects with IDataPlatform interface + * @returns {Array} + */ +const getSupportedPurgePolicies = ( + platformName: IDataPlatform['name'], + platforms: Array = [] +): Array => { + 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 }; diff --git a/wherehows-web/app/constants/datasets/platform.ts b/wherehows-web/app/constants/datasets/platform.ts index 5dab7df65a..7cffde4a4e 100644 --- a/wherehows-web/app/constants/datasets/platform.ts +++ b/wherehows-web/app/constants/datasets/platform.ts @@ -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 }; diff --git a/wherehows-web/app/styles/components/dataset-purge-policy/_purge-policy-list.scss b/wherehows-web/app/styles/components/dataset-purge-policy/_purge-policy-list.scss index d9f7a4cb31..f88ccd1951 100644 --- a/wherehows-web/app/styles/components/dataset-purge-policy/_purge-policy-list.scss +++ b/wherehows-web/app/styles/components/dataset-purge-policy/_purge-policy-list.scss @@ -18,4 +18,8 @@ @include pill(set-color(red, maroonflush), set-color(white, base)); } } + + &__retry-platforms { + @include flex-column-center; + } } diff --git a/wherehows-web/app/templates/components/purge-policy.hbs b/wherehows-web/app/templates/components/purge-policy.hbs index fddd78e9eb..1ec61d09ea 100644 --- a/wherehows-web/app/templates/components/purge-policy.hbs +++ b/wherehows-web/app/templates/components/purge-policy.hbs @@ -9,40 +9,65 @@
    {{#if isEditable}} - {{#each-in purgePolicyProps as |purgeType prop|}} -
  • + {{#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)}} -

    - Purge policy not available for - {{lowercase platform}} -

    - {{/unless}} +
    + +
    -

    {{prop.desc}}

    + {{/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|}} +
  • - {{/if}} -
  • - {{/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)}} +

    + Purge policy not available for + {{platform}} +

    + {{/unless}} + +

    {{prop.desc}}

    + + {{#if (and requestExemptionReason (eq purgeType exemptPolicy))}} + + {{medium-content-editable + value=purgeNote + options=editorOptions + class="comment-new__content"}} + + {{/if}} + + {{/each-in}} + + {{/if}} {{else}} diff --git a/wherehows-web/app/typings/api/list/platforms.d.ts b/wherehows-web/app/typings/api/list/platforms.d.ts index 454bd5ac2b..b3160c9b13 100644 --- a/wherehows-web/app/typings/api/list/platforms.d.ts +++ b/wherehows-web/app/typings/api/list/platforms.d.ts @@ -8,7 +8,7 @@ import { ApiStatus } from 'wherehows-web/utils/api/shared'; */ export interface IDataPlatform { // Policies supported for this dataset - supportedPolicies: Array; + supportedPurgePolicies: Array; // 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 diff --git a/wherehows-web/app/typings/ember-concurrency.d.ts b/wherehows-web/app/typings/ember-concurrency.d.ts index de0d70dfd5..be9b308df2 100644 --- a/wherehows-web/app/typings/ember-concurrency.d.ts +++ b/wherehows-web/app/typings/ember-concurrency.d.ts @@ -67,7 +67,7 @@ declare module 'ember-concurrency' { cancelAll(): void; }; - export function task(generatorFn: (a: A) => Iterator): Task TaskInstance>; + export function task(generatorFn: (a: A) => Iterator): Task TaskInstance>; export function task( generatorFn: (a1: A1, a2: A2) => Iterator From 40b287d346664e12191b652f2a5312293c411626 Mon Sep 17 00:00:00 2001 From: Seyi Adebajo Date: Wed, 10 Jan 2018 10:11:32 -0800 Subject: [PATCH 2/3] temporarily skips failing tests --- .../components/purge-policy-test.js | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/wherehows-web/tests/integration/components/purge-policy-test.js b/wherehows-web/tests/integration/components/purge-policy-test.js index b4e6001803..5ef9631ba1 100644 --- a/wherehows-web/tests/integration/components/purge-policy-test.js +++ b/wherehows-web/tests/integration/components/purge-policy-test.js @@ -1,10 +1,20 @@ -import { moduleForComponent, test } from 'ember-qunit'; +import { moduleForComponent, test, skip } 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 sinon from 'sinon'; +import { missingPolicyText, purgePolicyProps, exemptPolicy, PurgePolicy } from 'wherehows-web/constants'; +import { DatasetPlatform } from 'wherehows-web/constants/datasets/platform'; moduleForComponent('purge-policy', 'Integration | Component | purge policy', { - integration: true + integration: true, + + beforeEach() { + this.xhr = sinon.useFakeXMLHttpRequest(); + }, + + afterEach() { + this.xhr.restore(); + } }); const policyList = '.purge-policy-list'; @@ -59,10 +69,10 @@ 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) { +skip('it indicates the currently selected purge policy', 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); @@ -76,7 +86,7 @@ test('it indicates the currently selected purge policy', function(assert) { ); }); -test('it focuses the comment element for exempt policy', function(assert) { +skip('it focuses the comment element for exempt policy', function(assert) { assert.expect(1); const focusEditor = () => { From 6632de40b7cd6947bb2e0251521b2beac540bbba Mon Sep 17 00:00:00 2001 From: Seyi Adebajo Date: Wed, 10 Jan 2018 12:06:14 -0800 Subject: [PATCH 3/3] fixes skipped integration tests: mocks server requests using sinon for component level request --- .../mirage/fixtures/list-platforms.ts | 75 +++++++++++++++++++ wherehows-web/mirage/scenarios/default.js | 3 +- .../components/purge-policy-test.js | 27 +++++-- 3 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 wherehows-web/mirage/fixtures/list-platforms.ts diff --git a/wherehows-web/mirage/fixtures/list-platforms.ts b/wherehows-web/mirage/fixtures/list-platforms.ts new file mode 100644 index 0000000000..981a4e40b8 --- /dev/null +++ b/wherehows-web/mirage/fixtures/list-platforms.ts @@ -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' + } +]; diff --git a/wherehows-web/mirage/scenarios/default.js b/wherehows-web/mirage/scenarios/default.js index 9dd5be98c3..5f643518e7 100644 --- a/wherehows-web/mirage/scenarios/default.js +++ b/wherehows-web/mirage/scenarios/default.js @@ -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); diff --git a/wherehows-web/tests/integration/components/purge-policy-test.js b/wherehows-web/tests/integration/components/purge-policy-test.js index 5ef9631ba1..a1f2e3175b 100644 --- a/wherehows-web/tests/integration/components/purge-policy-test.js +++ b/wherehows-web/tests/integration/components/purge-policy-test.js @@ -1,19 +1,22 @@ -import { moduleForComponent, test, skip } from 'ember-qunit'; +import { moduleForComponent, test } from 'ember-qunit'; import hbs from 'htmlbars-inline-precompile'; -import { triggerEvent } from 'ember-native-dom-helpers'; +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, beforeEach() { - this.xhr = sinon.useFakeXMLHttpRequest(); + this.server = sinon.createFakeServer(); }, afterEach() { - this.xhr.restore(); + this.server.restore(); } }); @@ -69,7 +72,7 @@ test('it renders a user message if the purge policy is not set and is in readonl ); }); -skip('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 = PurgePolicy.ManualPurge; const platform = DatasetPlatform.MySql; @@ -78,22 +81,30 @@ skip('it indicates the currently selected purge policy', function(assert) { 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` ); }); -skip('it focuses the comment element for exempt policy', function(assert) { +test('it focuses the comment element for exempt policy', function(assert) { assert.expect(1); const focusEditor = () => { 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({