restrict compliance policy avaialability to dataset platforms with supported list of purge policies

This commit is contained in:
Seyi Adebajo 2018-01-17 09:08:27 -08:00
parent 680e28045e
commit 28c4fda984
4 changed files with 250 additions and 204 deletions

View File

@ -6,6 +6,9 @@ import { inject } from '@ember/service';
import { classify } from '@ember/string'; import { classify } from '@ember/string';
import { IFieldIdentifierOption } from 'wherehows-web/constants/dataset-compliance'; import { IFieldIdentifierOption } from 'wherehows-web/constants/dataset-compliance';
import { Classification } from 'wherehows-web/constants/datasets/compliance'; import { Classification } from 'wherehows-web/constants/datasets/compliance';
import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset';
import { IDataPlatform } from 'wherehows-web/typings/api/list/platforms';
import { readPlatforms } from 'wherehows-web/utils/api/list/platforms';
import isTrackingHeaderField from 'wherehows-web/utils/validators/tracking-headers'; import isTrackingHeaderField from 'wherehows-web/utils/validators/tracking-headers';
import { import {
@ -23,7 +26,8 @@ import {
IComplianceField, IComplianceField,
DatasetClassification, DatasetClassification,
SuggestionIntent, SuggestionIntent,
PurgePolicy PurgePolicy,
getSupportedPurgePolicies
} from 'wherehows-web/constants'; } from 'wherehows-web/constants';
import { import {
isPolicyExpectedShape, isPolicyExpectedShape,
@ -44,7 +48,7 @@ import {
IComplianceSuggestion IComplianceSuggestion
} from 'wherehows-web/typings/api/datasets/compliance'; } from 'wherehows-web/typings/api/datasets/compliance';
import { ApiStatus } from 'wherehows-web/utils/api'; import { ApiStatus } from 'wherehows-web/utils/api';
import { task } from 'ember-concurrency'; import { task, TaskInstance } from 'ember-concurrency';
/** /**
* Describes the DatasetCompliance actions index signature to allow * Describes the DatasetCompliance actions index signature to allow
@ -140,11 +144,24 @@ const changeSetFieldsRequiringReview = arrayFilter<IComplianceChangeSet>(fieldCh
const initialStepIndex = -1; const initialStepIndex = -1;
/** /**
* Applies the observer to the editStepIndex to trigger the update edit step task * Defines observers for the DatasetCompliance Component
* @type {Component}
*/ */
const ObservableDecorator = Component.extend({ const ObservableDecorator = Component.extend({
/**
* Observes changes editStepIndex to trigger the update edit step task
* @type {() => void}
*/
editStepIndexChanged: observer('editStepIndex', function(this: DatasetCompliance) { editStepIndexChanged: observer('editStepIndex', function(this: DatasetCompliance) {
get(this, 'updateEditStepTask').perform(); get(this, 'updateEditStepTask').perform();
}),
/**
* Observes changes to the platform property and invokes the task to update the supportedPurgePolicies prop
* @type {() => void}
*/
platformChanged: observer('platform', function(this: DatasetCompliance) {
get(this, 'complianceAvailabilityTask').perform();
}) })
}); });
@ -161,6 +178,8 @@ export default class DatasetCompliance extends ObservableDecorator {
_hasBadData: boolean; _hasBadData: boolean;
_message: string; _message: string;
_alertType: string; _alertType: string;
platform: IDatasetView['platform'];
isCompliancePolicyAvailable: boolean = false;
showAllDatasetMemberData: boolean; showAllDatasetMemberData: boolean;
complianceInfo: void | IComplianceInfo; complianceInfo: void | IComplianceInfo;
complianceSuggestion: IComplianceSuggestion; complianceSuggestion: IComplianceSuggestion;
@ -234,6 +253,13 @@ export default class DatasetCompliance extends ObservableDecorator {
*/ */
isSaving = false; isSaving = false;
/**
* The list of supported purge policies for the related platform
* @type {Array<PurgePolicy>}
* @memberof DatasetCompliance
*/
supportedPurgePolicies: Array<PurgePolicy> = [];
constructor() { constructor() {
super(...arguments); super(...arguments);
@ -353,6 +379,10 @@ export default class DatasetCompliance extends ObservableDecorator {
} }
} }
didInsertElement() {
get(this, 'complianceAvailabilityTask').perform();
}
/** /**
* @override * @override
*/ */
@ -363,6 +393,33 @@ export default class DatasetCompliance extends ObservableDecorator {
this.enableDomCloaking(); this.enableDomCloaking();
} }
/**
* Parent task to determine if a compliance policy can be created or updated for the dataset
* @type {Task<TaskInstance<Promise<Array<IDataPlatform>>>, () => TaskInstance<TaskInstance<Promise<Array<IDataPlatform>>>>>}
* @memberof DatasetCompliance
*/
complianceAvailabilityTask = task(function*(
this: DatasetCompliance
): IterableIterator<TaskInstance<Promise<Array<IDataPlatform>>>> {
yield get(this, 'getPlatformPoliciesTask').perform();
const supportedPurgePolicies = get(this, 'supportedPurgePolicies');
set(this, 'isCompliancePolicyAvailable', !!supportedPurgePolicies.length);
}).restartable();
/**
* Task to retrieve platform policies and set supported policies for the current platform
* @type {Task<Promise<Array<IDataPlatform>>, () => TaskInstance<Promise<Array<IDataPlatform>>>>}
* @memberof DatasetCompliance
*/
getPlatformPoliciesTask = task(function*(this: DatasetCompliance): IterableIterator<Promise<Array<IDataPlatform>>> {
const platform = get(this, 'platform');
if (platform) {
set(this, 'supportedPurgePolicies', getSupportedPurgePolicies(platform, yield readPlatforms()));
}
}).restartable();
/** /**
* A `lite` / intermediary step to occlusion culling, this helps to improve the rendering of * A `lite` / intermediary step to occlusion culling, this helps to improve the rendering of
* elements that are currently rendered in the viewport by hiding that aren't. * elements that are currently rendered in the viewport by hiding that aren't.
@ -372,13 +429,14 @@ export default class DatasetCompliance extends ObservableDecorator {
*/ */
enableDomCloaking() { enableDomCloaking() {
const dom = this.element.querySelector('.dataset-compliance-fields'); const dom = this.element.querySelector('.dataset-compliance-fields');
const triggerCount = 100; const triggerThreshold = 100;
if (dom) { if (dom) {
const rows = dom.querySelectorAll('tbody tr'); const rows = dom.querySelectorAll('tbody tr');
// if we already have watchers for elements, or if the elements previously cached are no longer valid, // if we already have watchers for elements, or if the elements previously cached are no longer valid,
// e.g. those elements were destroyed when new data was received, pagination etc // e.g. those elements were destroyed when new data was received, pagination etc
if (rows.length > triggerCount && (!this.complianceWatchers || !this.complianceWatchers.has(rows[0]))) { if (rows.length > triggerThreshold && (!this.complianceWatchers || !this.complianceWatchers.has(rows[0]))) {
/** /**
* If an item is not in the viewport add a class to occlude it * If an item is not in the viewport add a class to occlude it
*/ */
@ -1067,7 +1125,7 @@ export default class DatasetCompliance extends ObservableDecorator {
}); });
// defaults the containing personal data flag to false if undefined // defaults the containing personal data flag to false if undefined
if (typeof containingPersonalData !== 'undefined') { if (typeof containingPersonalData === 'undefined') {
set(complianceInfo, 'containingPersonalData', false); set(complianceInfo, 'containingPersonalData', false);
} }

View File

@ -1,20 +1,17 @@
import Component from '@ember/component'; import Component from '@ember/component';
import { get, set, observer } 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 DatasetCompliance from 'wherehows-web/components/dataset-compliance';
import { import {
baseCommentEditorOptions, baseCommentEditorOptions,
DatasetPlatform, DatasetPlatform,
exemptPolicy, exemptPolicy,
getSupportedPurgePolicies,
isExempt, isExempt,
missingPolicyText, missingPolicyText,
PurgePolicy, PurgePolicy,
purgePolicyProps purgePolicyProps
} from 'wherehows-web/constants'; } from 'wherehows-web/constants';
import { IComplianceInfo } from 'wherehows-web/typings/api/datasets/compliance'; 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 class PurgePolicyComponent extends Component { export default class PurgePolicyComponent extends Component {
/** /**
@ -40,7 +37,7 @@ export default class PurgePolicyComponent extends Component {
* @type {Array<PurgePolicy>} * @type {Array<PurgePolicy>}
* @memberof PurgePolicyComponent * @memberof PurgePolicyComponent
*/ */
supportedPurgePolicies: Array<PurgePolicy> = []; supportedPurgePolicies: DatasetCompliance['supportedPurgePolicies'];
/** /**
* The dataset's platform * The dataset's platform
@ -87,31 +84,6 @@ export default class PurgePolicyComponent extends Component {
this.checkExemption(get(this, 'purgePolicy')); this.checkExemption(get(this, 'purgePolicy'));
} }
didInsertElement() {
get(this, 'getPlatformPolicies').perform();
}
/**
* Observes changes to the platform property and invokes the task to update the supportedPurgePolicies prop
* @type {void}
* @memberof PurgePolicyComponent
*/
platformChanged = observer('platform', function(this: PurgePolicyComponent) {
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<Array<IDataPlatform>>> {
const platform = get(this, 'platform');
if (platform) {
set(this, 'supportedPurgePolicies', getSupportedPurgePolicies(platform, yield readPlatforms()));
}
}).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
* flag to request the exemption to true * flag to request the exemption to true

View File

@ -1,134 +1,175 @@
<div id="compliance" class="tab-body"> {{#if complianceAvailabilityTask.isRunning}}
{{#if _hasBadData}} {{pendulum-ellipsis-animation}}
<div class="alert alert-danger post-action-notification" role="alert"> {{else}}
<p> {{#if isCompliancePolicyAvailable}}
Oops! We have discovered some issues with this Dataset's fields that
may put any updates in an invalid state. <br/>
Unfortunately updates are <strong>disabled</strong> at this time. <br/>
Please shoot us an email at
<a href="mailto:ask_metadata@linkedin.com">ask_metadata@linkedin.com</a>
and we will get on this asap! <br/>
Our apologies.
</p>
</div>
{{else}}
<section class="action-bar">
{{#if isEditing}}
{{#if _message}} <div id="compliance" class="tab-body">
<div class="alert alert-{{_alertType}} post-action-notification action-footer-notification" role="alert"> {{#if _hasBadData}}
{{_message}} <div class="alert alert-danger post-action-notification" role="alert">
</div> <p>
{{/if}} Oops! We have discovered some issues with this Dataset's fields that
may put any updates in an invalid state. <br/>
Unfortunately updates are <strong>disabled</strong> at this time. <br/>
Please shoot us an email at
<a href="mailto:ask_metadata@linkedin.com">ask_metadata@linkedin.com</a>
and we will get on this asap! <br/>
Our apologies.
</p>
</div>
{{else}}
<section class="action-bar">
{{#if isEditing}}
<div class="container action-bar__content"> {{#if _message}}
{{#if (has-next editStep editSteps)}} <div class="alert alert-{{_alertType}} post-action-notification action-footer-notification" role="alert">
<button {{_message}}
class="nacho-button nacho-button--large-inverse action-bar__item" </div>
title="Next" {{/if}}
{{action "nextStep"}}>
Next
</button>
{{else}}
<button
class="nacho-button nacho-button--large-inverse action-bar__item"
title={{unless isDatasetFullyClassified
"Ensure you have provided a yes/no value for all dataset tags"
"Save"}}
disabled={{isSavingDisabled}}
{{action "saveCompliance"}}>
Save
</button>
{{/if}}
{{#if (has-previous editStep editSteps)}} <div class="container action-bar__content">
<button {{#if (has-next editStep editSteps)}}
class="nacho-button--large nacho-button--secondary action-bar__item" <button
title="Back" class="nacho-button nacho-button--large-inverse action-bar__item"
{{action "previousStep"}}> title="Next"
Back {{action "nextStep"}}>
</button> Next
</button>
{{else}}
<button
class="nacho-button nacho-button--large-inverse action-bar__item"
title={{unless isDatasetFullyClassified
"Ensure you have provided a yes/no value for all dataset tags"
"Save"}}
disabled={{isSavingDisabled}}
{{action "saveCompliance"}}>
Save
</button>
{{/if}}
{{#if (has-previous editStep editSteps)}}
<button
class="nacho-button--large nacho-button--secondary action-bar__item"
title="Back"
{{action "previousStep"}}>
Back
</button>
{{/if}}
</div>
{{/if}} {{/if}}
</section>
{{/if}}
<div class="secondary-actions">
<div class="policy-last-saved">
Last saved:
<span class="policy-last-saved__saved">
{{if isNewComplianceInfo 'Never'
(moment-from-now complianceInfo.modifiedTime)}}
</span>
</div> </div>
{{#unless isEditing}}
<button
{{action "nextStep"}}
class="nacho-button--large nacho-button--secondary secondary-actions__action">
Edit
</button>
{{#unless isNewComplianceInfo}}
<button
{{action "onComplianceDownloadJson"}}
class="nacho-button nacho-button--large-inverse secondary-actions__action">
Download compliance metadata
</button>
{{/unless}}
{{/unless}}
</div>
{{#if isEditing}}
<div class="dataset-compliance-step-container">
{{partial "datasets/dataset-compliance/-dataset-compliance-step-indicator"}}
</div>
{{/if}} {{/if}}
</section>
{{/if}}
<div class="secondary-actions"> {{#unless schemaless}}
<div class="policy-last-saved"> {{#if (and (eq editStep.name editSteps.0.name) (not _hasBadData))}}
Last saved: {{json-upload receiveJsonFile=(action "onComplianceJsonUpload") class="secondary-actions__action"}}
<span class="policy-last-saved__saved"> {{/if}}
{{if isNewComplianceInfo 'Never'
(moment-from-now complianceInfo.modifiedTime)}}
</span>
</div>
{{#unless isEditing}}
<button
{{action "nextStep"}}
class="nacho-button--large nacho-button--secondary secondary-actions__action">
Edit
</button>
{{#unless isNewComplianceInfo}}
<button
{{action "onComplianceDownloadJson"}}
class="nacho-button nacho-button--large-inverse secondary-actions__action">
Download compliance metadata
</button>
{{/unless}} {{/unless}}
{{/unless}} {{#if (or isReadOnly (eq editStep.name editSteps.2.name))}}
</div> {{partial "datasets/dataset-compliance/dataset-classification"}}
{{/if}}
{{#if isEditing}} {{#if (or isReadOnly (eq editStep.name editSteps.1.name))}}
<div class="dataset-compliance-step-container"> {{#if getPlatformPoliciesTask.isRunning}}
{{partial "datasets/dataset-compliance/-dataset-compliance-step-indicator"}} {{pendulum-ellipsis-animation}}
{{/if}}
{{#if getPlatformPoliciesTask.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"
}}
<div class="purge-policy-list__retry-platforms">
<button
class="nacho-button nacho-button--large-inverse"
onclick={{perform getPlatformPoliciesTask}}>
Retry
</button>
</div>
{{/if}}
{{#if getPlatformPoliciesTask.last.isSuccessful}}
{{purge-policy
isEditable=(not isReadOnly)
platform=platform
supportedPurgePolicies=supportedPurgePolicies
purgeNote=complianceInfo.compliancePurgeNote
purgePolicy=(readonly complianceInfo.complianceType)
onPolicyChange=(action "onDatasetPurgePolicyChange")
}}
{{/if}}
{{/if}}
{{#if schemaless}}
{{#if (or isReadOnly (eq editStep.name editSteps.0.name))}}
{{datasets/schemaless-tagging
isEditable=(not isReadOnly)
classification=(readonly complianceInfo.confidentiality)
containsPersonalData=(readonly complianceInfo.containingPersonalData)
onClassificationChange=(action "onDatasetSecurityClassificationChange")
onPersonalDataChange=(action "onDatasetLevelPolicyChange")
}}
{{/if}}
{{else}}
{{#if (or isReadOnly (eq editStep.name editSteps.0.name))}}
{{partial "datasets/dataset-compliance/dataset-compliance-entities"}}
{{/if}}
{{/if}}
</div> </div>
{{/if}}
{{#unless schemaless}}
{{#if (and (eq editStep.name editSteps.0.name) (not _hasBadData))}}
{{json-upload receiveJsonFile=(action "onComplianceJsonUpload") class="secondary-actions__action"}}
{{/if}}
{{/unless}}
{{#if (or isReadOnly (eq editStep.name editSteps.2.name))}}
{{partial "datasets/dataset-compliance/dataset-classification"}}
{{/if}}
{{#if (or isReadOnly (eq editStep.name editSteps.1.name))}}
{{purge-policy
isEditable=(not isReadOnly)
platform=platform
purgeNote=complianceInfo.compliancePurgeNote
purgePolicy=(readonly complianceInfo.complianceType)
onPolicyChange=(action "onDatasetPurgePolicyChange")
}}
{{/if}}
{{#if schemaless}}
{{#if (or isReadOnly (eq editStep.name editSteps.0.name))}}
{{datasets/schemaless-tagging
isEditable=(not isReadOnly)
classification=(readonly complianceInfo.confidentiality)
containsPersonalData=(readonly complianceInfo.containingPersonalData)
onClassificationChange=(action "onDatasetSecurityClassificationChange")
onPersonalDataChange=(action "onDatasetLevelPolicyChange")
}}
{{/if}}
{{else}} {{else}}
{{#if (or isReadOnly (eq editStep.name editSteps.0.name))}} {{empty-state
{{partial "datasets/dataset-compliance/dataset-compliance-entities"}} heading="Compliance Policy not available"
{{/if}} subHead="Compliance Policy is currently not available for this platform"
}}
{{/if}} {{/if}}
</div> {{/if}}
{{yield}} {{yield}}

View File

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