mirror of
https://github.com/datahub-project/datahub.git
synced 2025-10-15 02:47:19 +00:00
Merge pull request #935 from theseyi/default-sec-class
adds types for dataset security class. defaults dataset security class to internal with user confirmation
This commit is contained in:
commit
f48ed9b0cb
@ -5,6 +5,10 @@ import { run, schedule } from '@ember/runloop';
|
|||||||
import { inject } from '@ember/service';
|
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 { 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 {
|
||||||
@ -22,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,
|
||||||
@ -43,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
|
||||||
@ -52,6 +57,7 @@ import { task } from 'ember-concurrency';
|
|||||||
interface IDatasetComplianceActions {
|
interface IDatasetComplianceActions {
|
||||||
didEditCompliancePolicy: () => Promise<boolean>;
|
didEditCompliancePolicy: () => Promise<boolean>;
|
||||||
didEditPurgePolicy: () => Promise<{} | void>;
|
didEditPurgePolicy: () => Promise<{} | void>;
|
||||||
|
didEditDatasetLevelCompliancePolicy: () => Promise<void>;
|
||||||
[K: string]: (...args: Array<any>) => any;
|
[K: string]: (...args: Array<any>) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +102,8 @@ const {
|
|||||||
helpText,
|
helpText,
|
||||||
successUploading,
|
successUploading,
|
||||||
invalidPolicyData,
|
invalidPolicyData,
|
||||||
missingPurgePolicy
|
missingPurgePolicy,
|
||||||
|
defaultDatasetClassificationMsg
|
||||||
} = compliancePolicyStrings;
|
} = compliancePolicyStrings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,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();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -158,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;
|
||||||
@ -231,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);
|
||||||
|
|
||||||
@ -350,6 +379,10 @@ export default class DatasetCompliance extends ObservableDecorator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
get(this, 'complianceAvailabilityTask').perform();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@ -360,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.
|
||||||
@ -369,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
|
||||||
*/
|
*/
|
||||||
@ -1047,6 +1108,39 @@ export default class DatasetCompliance extends ObservableDecorator {
|
|||||||
return isConfirmed;
|
return isConfirmed;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles tasks to be processed after the wizard step to edit a datasets pii and security classification is
|
||||||
|
* completed
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async didEditDatasetLevelCompliancePolicy(this: DatasetCompliance): Promise<void> {
|
||||||
|
const complianceInfo = get(this, 'complianceInfo');
|
||||||
|
|
||||||
|
if (complianceInfo) {
|
||||||
|
const { confidentiality, containingPersonalData } = complianceInfo;
|
||||||
|
const dialogActions: { [prop: string]: () => void } = {};
|
||||||
|
const confirmConfidentialityPromise = new Promise((resolve, reject) => {
|
||||||
|
dialogActions['didConfirm'] = () => resolve();
|
||||||
|
dialogActions['didDismiss'] = () => reject();
|
||||||
|
});
|
||||||
|
|
||||||
|
// defaults the containing personal data flag to false if undefined
|
||||||
|
if (typeof containingPersonalData === 'undefined') {
|
||||||
|
set(complianceInfo, 'containingPersonalData', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!confidentiality) {
|
||||||
|
get(this, 'notifications').notify(NotificationEvent.confirm, {
|
||||||
|
dialogActions,
|
||||||
|
header: 'Confirm dataset classification',
|
||||||
|
content: defaultDatasetClassificationMsg
|
||||||
|
});
|
||||||
|
await confirmConfidentialityPromise;
|
||||||
|
set(complianceInfo, 'confidentiality', Classification.Internal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles post processing tasks after the purge policy step has been completed
|
* Handles post processing tasks after the purge policy step has been completed
|
||||||
* @returns {(Promise<void | {}>)}
|
* @returns {(Promise<void | {}>)}
|
||||||
|
@ -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
|
||||||
|
@ -55,7 +55,9 @@ const compliancePolicyStrings = {
|
|||||||
'This security classification is from go/dht and should be good enough in most cases. ' +
|
'This security classification is from go/dht and should be good enough in most cases. ' +
|
||||||
'You can optionally override it if required by house security.'
|
'You can optionally override it if required by house security.'
|
||||||
},
|
},
|
||||||
missingPurgePolicy: 'Please specify a Compliance Purge Policy'
|
missingPurgePolicy: 'Please specify a Compliance Purge Policy',
|
||||||
|
defaultDatasetClassificationMsg: `You haven't set this dataset's security classification,
|
||||||
|
it's value will be set to default of "${Classification.Internal}"`
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,7 +18,9 @@ enum SuggestionIntent {
|
|||||||
enum Classification {
|
enum Classification {
|
||||||
Confidential = 'CONFIDENTIAL',
|
Confidential = 'CONFIDENTIAL',
|
||||||
LimitedDistribution = 'LIMITED_DISTRIBUTION',
|
LimitedDistribution = 'LIMITED_DISTRIBUTION',
|
||||||
HighlyConfidential = 'HIGHLY_CONFIDENTIAL'
|
HighlyConfidential = 'HIGHLY_CONFIDENTIAL',
|
||||||
|
Internal = 'INTERNAL',
|
||||||
|
Public = 'PUBLIC'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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}}
|
||||||
|
@ -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}}
|
||||||
|
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
import { moduleForComponent, test } from 'ember-qunit';
|
||||||
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
|
|
||||||
|
import notificationsStub from 'wherehows-web/tests/stubs/services/notifications';
|
||||||
|
|
||||||
|
moduleForComponent('dataset-compliance', 'Integration | Component | dataset compliance', {
|
||||||
|
integration: true,
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.register('service:notifications', notificationsStub);
|
||||||
|
|
||||||
|
this.inject.service('notifications');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders an empty state component when isCompliancePolicyAvailable is false', function(assert) {
|
||||||
|
this.render(hbs`{{dataset-compliance}}`);
|
||||||
|
|
||||||
|
assert.notOk(this.get('isCompliancePolicyAvailable'));
|
||||||
|
|
||||||
|
assert.ok(document.querySelector('empty-state'));
|
||||||
|
});
|
@ -1,9 +1,15 @@
|
|||||||
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, waitUntil, find } from 'ember-native-dom-helpers';
|
import { triggerEvent } from 'ember-native-dom-helpers';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
|
||||||
import { missingPolicyText, purgePolicyProps, exemptPolicy, PurgePolicy } from 'wherehows-web/constants';
|
import {
|
||||||
|
missingPolicyText,
|
||||||
|
purgePolicyProps,
|
||||||
|
exemptPolicy,
|
||||||
|
PurgePolicy,
|
||||||
|
getSupportedPurgePolicies
|
||||||
|
} from 'wherehows-web/constants';
|
||||||
import { DatasetPlatform } from 'wherehows-web/constants/datasets/platform';
|
import { DatasetPlatform } from 'wherehows-web/constants/datasets/platform';
|
||||||
import platforms from 'wherehows-web/mirage/fixtures/list-platforms';
|
import platforms from 'wherehows-web/mirage/fixtures/list-platforms';
|
||||||
import { ApiStatus } from 'wherehows-web/utils/api';
|
import { ApiStatus } from 'wherehows-web/utils/api';
|
||||||
@ -80,6 +86,7 @@ test('it indicates the currently selected purge policy', async function(assert)
|
|||||||
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.set('supportedPurgePolicies', getSupportedPurgePolicies(platform, platforms));
|
||||||
|
|
||||||
this.server.respondWith('GET', '/api/v1/list/platforms', [
|
this.server.respondWith('GET', '/api/v1/list/platforms', [
|
||||||
200,
|
200,
|
||||||
@ -87,10 +94,11 @@ test('it indicates the currently selected purge policy', async function(assert)
|
|||||||
JSON.stringify({ status: ApiStatus.OK, platforms })
|
JSON.stringify({ status: ApiStatus.OK, platforms })
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.render(hbs`{{purge-policy isEditable=isEditable purgePolicy=purgePolicy platform=platform}}`);
|
this.render(
|
||||||
|
hbs`{{purge-policy isEditable=isEditable purgePolicy=purgePolicy platform=platform supportedPurgePolicies=supportedPurgePolicies}}`
|
||||||
|
);
|
||||||
this.server.respond();
|
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`
|
||||||
|
5
wherehows-web/tests/stubs/services/notifications.js
Normal file
5
wherehows-web/tests/stubs/services/notifications.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import Service from '@ember/service';
|
||||||
|
|
||||||
|
export default class extends Service {
|
||||||
|
notify = () => void 0;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user