mirror of
https://github.com/datahub-project/datahub.git
synced 2025-07-16 13:50:14 +00:00
Merge pull request #1092 from theseyi/compliance-multi-tagging
create tag row components. create compliance row rollup component. adds compliance field tag factory. makes the policymodificationtime on the schemafieldtopolicyvalue type optional. adds styling for row components. enables some actions for newly create components. adds actions for removing a field tag. actions for field tag operatio...
This commit is contained in:
commit
b4dcad5ce5
253
wherehows-web/app/components/dataset-compliance-field-tag.ts
Normal file
253
wherehows-web/app/components/dataset-compliance-field-tag.ts
Normal file
@ -0,0 +1,253 @@
|
||||
import Component from '@ember/component';
|
||||
import ComputedProperty from '@ember/object/computed';
|
||||
import { get, getProperties, computed, getWithDefault } from '@ember/object';
|
||||
import {
|
||||
Classification,
|
||||
ComplianceFieldIdValue,
|
||||
getDefaultSecurityClassification,
|
||||
idTypeFieldHasLogicalType,
|
||||
isFieldIdType,
|
||||
SuggestionIntent
|
||||
} from 'wherehows-web/constants';
|
||||
import {
|
||||
IComplianceChangeSet,
|
||||
IComplianceFieldFormatOption,
|
||||
IDropDownOption
|
||||
} from 'wherehows-web/typings/app/dataset-compliance';
|
||||
import { IComplianceDataType } from 'wherehows-web/typings/api/list/compliance-datatypes';
|
||||
import { action } from 'ember-decorators/object';
|
||||
import { getFieldSuggestions } from 'wherehows-web/utils/datasets/compliance-suggestions';
|
||||
|
||||
/**
|
||||
* Constant definition for an unselected field format
|
||||
* @type {IDropDownOption<null>}
|
||||
*/
|
||||
const unSelectedFieldFormatValue: IDropDownOption<null> = {
|
||||
value: null,
|
||||
label: 'Select Field Format...',
|
||||
isDisabled: true
|
||||
};
|
||||
|
||||
export default class DatasetComplianceFieldTag extends Component {
|
||||
tagName = 'tr';
|
||||
|
||||
/**
|
||||
* Describes action interface for `onSuggestionIntent` action
|
||||
* @memberof DatasetComplianceFieldTag
|
||||
*/
|
||||
onSuggestionIntent: (tag: IComplianceChangeSet, intent?: SuggestionIntent) => void;
|
||||
|
||||
/**
|
||||
* Describes action interface for `onTagIdentifierTypeChange` action
|
||||
* @memberof DatasetComplianceFieldTag
|
||||
*/
|
||||
onTagIdentifierTypeChange: (tag: IComplianceChangeSet, option: { value: ComplianceFieldIdValue | null }) => void;
|
||||
|
||||
/**
|
||||
* Describes the parent action interface for `onTagLogicalTypeChange`
|
||||
*/
|
||||
onTagLogicalTypeChange: (tag: IComplianceChangeSet, value: IComplianceChangeSet['logicalType']) => void;
|
||||
|
||||
/**
|
||||
* Describes the parent action interface for `onTagClassificationChange`
|
||||
*/
|
||||
onTagClassificationChange: (tag: IComplianceChangeSet, option: { value: '' | Classification }) => void;
|
||||
|
||||
/**
|
||||
* Describes the parent action interface for `onTagOwnerChange`
|
||||
*/
|
||||
onTagOwnerChange: (tag: IComplianceChangeSet, nonOwner: boolean) => void;
|
||||
|
||||
/**
|
||||
* References the change set item / tag to be added to the parent field
|
||||
* @type {IComplianceChangeSet}
|
||||
* @memberof DatasetComplianceFieldTag
|
||||
*/
|
||||
tag: IComplianceChangeSet;
|
||||
|
||||
/**
|
||||
* Reference to the compliance data types
|
||||
* @type {Array<IComplianceDataType>}
|
||||
*/
|
||||
complianceDataTypes: Array<IComplianceDataType>;
|
||||
|
||||
/**
|
||||
* Flag indicating that this tag has an identifier type of idType that is true
|
||||
* @type {ComputedProperty<boolean>}
|
||||
* @memberof DatasetComplianceFieldTag
|
||||
*/
|
||||
isIdType: ComputedProperty<boolean> = computed('tag.identifierType', 'complianceDataTypes', function(
|
||||
this: DatasetComplianceFieldTag
|
||||
): boolean {
|
||||
const { tag, complianceDataTypes } = getProperties(this, ['tag', 'complianceDataTypes']);
|
||||
return isFieldIdType(complianceDataTypes)(tag);
|
||||
});
|
||||
|
||||
/**
|
||||
* A list of field formats that are determined based on the tag identifierType
|
||||
* @type ComputedProperty<Array<IComplianceFieldFormatOption>>
|
||||
* @memberof DatasetComplianceFieldTag
|
||||
*/
|
||||
fieldFormats: ComputedProperty<Array<IComplianceFieldFormatOption>> = computed(
|
||||
'isIdType',
|
||||
'complianceDataTypes',
|
||||
function(this: DatasetComplianceFieldTag): Array<IComplianceFieldFormatOption> {
|
||||
const identifierType = get(this, 'tag')['identifierType'] || '';
|
||||
const { isIdType, complianceDataTypes } = getProperties(this, ['isIdType', 'complianceDataTypes']);
|
||||
const complianceDataType = complianceDataTypes.findBy('id', identifierType);
|
||||
let fieldFormatOptions: Array<IComplianceFieldFormatOption> = [];
|
||||
|
||||
if (complianceDataType && isIdType) {
|
||||
const supportedFieldFormats = complianceDataType.supportedFieldFormats || [];
|
||||
const supportedFormatOptions = supportedFieldFormats.map(format => ({ value: format, label: format }));
|
||||
|
||||
return supportedFormatOptions.length
|
||||
? [unSelectedFieldFormatValue, ...supportedFormatOptions]
|
||||
: supportedFormatOptions;
|
||||
}
|
||||
|
||||
return fieldFormatOptions;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Checks if the field format / logical type for this tag is missing, if the field is of ID type
|
||||
* @type {ComputedProperty<boolean>}
|
||||
* @memberof DatasetComplianceFieldTag
|
||||
*/
|
||||
isTagFormatMissing = computed('isIdType', 'tag.logicalType', function(this: DatasetComplianceFieldTag): boolean {
|
||||
return get(this, 'isIdType') && !idTypeFieldHasLogicalType(get(this, 'tag'));
|
||||
});
|
||||
|
||||
/**
|
||||
* The tag's security classification
|
||||
* Retrieves the tag security classification from the compliance tag if it exists, otherwise
|
||||
* defaults to the default security classification for the identifier type
|
||||
* in other words, the field must have a security classification if it has an identifier type
|
||||
* @type {ComputedProperty<Classification | null>}
|
||||
* @memberof DatasetComplianceFieldTag
|
||||
*/
|
||||
tagClassification = computed('tag.classification', 'tag.identifierType', 'complianceDataTypes', function(
|
||||
this: DatasetComplianceFieldTag
|
||||
): IComplianceChangeSet['securityClassification'] {
|
||||
const { tag: { identifierType, securityClassification }, complianceDataTypes } = getProperties(this, [
|
||||
'tag',
|
||||
'complianceDataTypes'
|
||||
]);
|
||||
|
||||
return securityClassification || getDefaultSecurityClassification(complianceDataTypes, identifierType);
|
||||
});
|
||||
|
||||
/**
|
||||
* Flag indicating that this tag has an identifier type that is of pii type
|
||||
* @type {ComputedProperty<boolean>}
|
||||
* @memberof DatasetComplianceFieldTag
|
||||
*/
|
||||
isPiiType = computed('tag.identifierType', function(this: DatasetComplianceFieldTag): boolean {
|
||||
const { identifierType } = get(this, 'tag');
|
||||
const isDefinedIdentifierType = identifierType !== null || identifierType !== ComplianceFieldIdValue.None;
|
||||
|
||||
// If identifierType exists, and tag is not idType or None or null
|
||||
return !!identifierType && !get(this, 'isIdType') && isDefinedIdentifierType;
|
||||
});
|
||||
|
||||
/**
|
||||
* Extracts the tag suggestions into a cached computed property, if a suggestion exists
|
||||
* @type {(ComputedProperty<{ identifierType: ComplianceFieldIdValue; logicalType: string; confidence: number } | void>)}
|
||||
* @memberof DatasetComplianceFieldTag
|
||||
*/
|
||||
prediction = computed('tag.suggestion', 'tag.suggestionAuthority', function(
|
||||
this: DatasetComplianceFieldTag
|
||||
): {
|
||||
identifierType: IComplianceChangeSet['identifierType'];
|
||||
logicalType: IComplianceChangeSet['logicalType'];
|
||||
confidence: number;
|
||||
} | void {
|
||||
return getFieldSuggestions(getWithDefault(this, 'tag', <IComplianceChangeSet>{}));
|
||||
});
|
||||
|
||||
/**
|
||||
* Handles UI changes to the tag identifierType
|
||||
* @param {{ value: ComplianceFieldIdValue }} { value }
|
||||
*/
|
||||
@action
|
||||
tagIdentifierTypeDidChange(this: DatasetComplianceFieldTag, { value }: { value: ComplianceFieldIdValue | null }) {
|
||||
const onTagIdentifierTypeChange = get(this, 'onTagIdentifierTypeChange');
|
||||
|
||||
if (typeof onTagIdentifierTypeChange === 'function') {
|
||||
// if the field has a predicted value, but the user changes the identifier type,
|
||||
// ignore the suggestion
|
||||
if (get(this, 'prediction')) {
|
||||
this.onSuggestionAction(SuggestionIntent.ignore);
|
||||
}
|
||||
|
||||
onTagIdentifierTypeChange(get(this, 'tag'), { value });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the updates when the tag's logical type changes on this tag
|
||||
* @param {(IComplianceChangeSet['logicalType'])} value contains the selected drop-down value
|
||||
*/
|
||||
@action
|
||||
tagLogicalTypeDidChange(this: DatasetComplianceFieldTag, { value }: { value: IComplianceChangeSet['logicalType'] }) {
|
||||
const onTagLogicalTypeChange = get(this, 'onTagLogicalTypeChange');
|
||||
|
||||
if (typeof onTagLogicalTypeChange === 'function') {
|
||||
onTagLogicalTypeChange(get(this, 'tag'), value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles UI change to field security classification
|
||||
* @param {({ value: '' | Classification })} { value } contains the changed classification value
|
||||
*/
|
||||
@action
|
||||
tagClassificationDidChange(this: DatasetComplianceFieldTag, { value }: { value: '' | Classification }) {
|
||||
const onTagClassificationChange = get(this, 'onTagClassificationChange');
|
||||
if (typeof onTagClassificationChange === 'function') {
|
||||
onTagClassificationChange(get(this, 'tag'), { value });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the nonOwner flag update on the tag
|
||||
* @param {boolean} nonOwner
|
||||
*/
|
||||
@action
|
||||
tagOwnerDidChange(this: DatasetComplianceFieldTag, nonOwner: boolean) {
|
||||
// inverts the value of nonOwner, toggle is shown in the UI as `Owner` i.e. not nonOwner
|
||||
get(this, 'onTagOwnerChange')(get(this, 'tag'), !nonOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for user interactions with a suggested value. Applies / ignores the suggestion
|
||||
* Then invokes the parent supplied suggestion handler
|
||||
* @param {SuggestionIntent} intent a binary indicator to accept or ignore suggestion
|
||||
*/
|
||||
@action
|
||||
onSuggestionAction(this: DatasetComplianceFieldTag, intent: SuggestionIntent = SuggestionIntent.ignore) {
|
||||
const onSuggestionIntent = get(this, 'onSuggestionIntent');
|
||||
|
||||
// Accept the suggestion for either identifierType and/or logicalType
|
||||
if (intent === SuggestionIntent.accept) {
|
||||
const { identifierType, logicalType } = get(this, 'prediction') || {
|
||||
identifierType: void 0,
|
||||
logicalType: void 0
|
||||
};
|
||||
|
||||
if (identifierType) {
|
||||
this.actions.tagIdentifierTypeDidChange.call(this, { value: identifierType });
|
||||
}
|
||||
|
||||
if (logicalType) {
|
||||
this.actions.tagLogicalTypeDidChange.call(this, logicalType);
|
||||
}
|
||||
}
|
||||
|
||||
// Invokes parent handle to runtime ignore future suggesting this suggestion
|
||||
if (typeof onSuggestionIntent === 'function') {
|
||||
onSuggestionIntent(get(this, 'tag'), intent);
|
||||
}
|
||||
}
|
||||
}
|
122
wherehows-web/app/components/dataset-compliance-rollup-row.ts
Normal file
122
wherehows-web/app/components/dataset-compliance-rollup-row.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import Component from '@ember/component';
|
||||
import ComputedProperty, { alias } from '@ember/object/computed';
|
||||
import { get, getProperties, computed } from '@ember/object';
|
||||
import { action } from 'ember-decorators/object';
|
||||
import {
|
||||
IComplianceChangeSet,
|
||||
IdentifierFieldWithFieldChangeSetTuple
|
||||
} from 'wherehows-web/typings/app/dataset-compliance';
|
||||
import { complianceFieldTagFactory } from 'wherehows-web/constants';
|
||||
|
||||
export default class DatasetComplianceRollupRow extends Component.extend({
|
||||
tagName: ''
|
||||
}) {
|
||||
/**
|
||||
* References the parent external action to add a tag to the list of change sets
|
||||
*/
|
||||
onFieldTagAdded: (tag: IComplianceChangeSet) => IComplianceChangeSet;
|
||||
|
||||
/**
|
||||
* References the parent external action to add a tag to the list of change sets
|
||||
*/
|
||||
onFieldTagRemoved: (tag: IComplianceChangeSet) => IComplianceChangeSet;
|
||||
|
||||
/**
|
||||
* Flag indicating if the row is expanded or collapsed
|
||||
* @type {boolean}
|
||||
* @memberof DatasetComplianceRollupRow
|
||||
*/
|
||||
isRowExpanded: boolean;
|
||||
|
||||
/**
|
||||
* References the compliance field tuple containing the field name and the field change set properties
|
||||
* @type {IdentifierFieldWithFieldChangeSetTuple}
|
||||
* @memberof DatasetComplianceRollupRow
|
||||
*/
|
||||
field: IdentifierFieldWithFieldChangeSetTuple;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
const isDirty: boolean = !!get(this, 'isRowDirty');
|
||||
|
||||
// if any tag is dirty, then expand the parent row on instantiation
|
||||
this.isRowExpanded || (this.isRowExpanded = isDirty);
|
||||
}
|
||||
|
||||
/**
|
||||
* References the first item in the IdentifierFieldWithFieldChangeSetTuple tuple, which is the field name
|
||||
* @type {ComputedProperty<string>}
|
||||
* @memberof DatasetComplianceRollupRow
|
||||
*/
|
||||
identifierField: ComputedProperty<string> = alias('field.firstObject');
|
||||
|
||||
/**
|
||||
* References the second item in the IdentifierFieldWithFieldChangeSetTuple type, this is the list of tags
|
||||
* for this field
|
||||
* @type {ComputedProperty<Array<IComplianceChangeSet>>}
|
||||
* @memberof DatasetComplianceRollupRow
|
||||
*/
|
||||
fieldChangeSet: ComputedProperty<Array<IComplianceChangeSet>> = alias('field.1');
|
||||
|
||||
/**
|
||||
* Aliases the dataType property on the first item in the field change set, this should available
|
||||
* regardless of if the field already exists on the compliance policy or otherwise
|
||||
* @type {ComputedProperty<string>}
|
||||
* @memberof DatasetComplianceRollupRow
|
||||
*/
|
||||
dataType: ComputedProperty<string> = alias('fieldChangeSet.firstObject.dataType');
|
||||
|
||||
/**
|
||||
* Checks if any of the field tags for this row are dirty
|
||||
* @type {ComputedProperty<boolean>}
|
||||
* @memberof DatasetComplianceRollupRow
|
||||
*/
|
||||
isRowDirty: ComputedProperty<boolean> = computed('fieldChangeSet', function(
|
||||
this: DatasetComplianceRollupRow
|
||||
): boolean {
|
||||
return get(this, 'fieldChangeSet').some(tag => tag.isDirty);
|
||||
});
|
||||
|
||||
/**
|
||||
* Toggles the expansion / collapse of the row expansion flag
|
||||
* @memberof DatasetComplianceRollupRow
|
||||
*/
|
||||
@action
|
||||
onToggleRowExpansion() {
|
||||
this.toggleProperty('isRowExpanded');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles adding a field tag when the user indicates the action through the UI
|
||||
* @memberof DatasetComplianceRollupRow
|
||||
*/
|
||||
@action
|
||||
onAddFieldTag(this: DatasetComplianceRollupRow) {
|
||||
const { identifierField, dataType, onFieldTagAdded } = getProperties(this, [
|
||||
'identifierField',
|
||||
'dataType',
|
||||
'onFieldTagAdded'
|
||||
]);
|
||||
|
||||
if (typeof onFieldTagAdded === 'function') {
|
||||
onFieldTagAdded(complianceFieldTagFactory({ identifierField, dataType }));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the removal of a field tag from the list of change set items
|
||||
* @param {IComplianceChangeSet} tag
|
||||
* @memberof DatasetComplianceRollupRow
|
||||
*/
|
||||
@action
|
||||
onRemoveFieldTag(this: DatasetComplianceRollupRow, tag: IComplianceChangeSet) {
|
||||
const onFieldTagRemoved = get(this, 'onFieldTagRemoved');
|
||||
//@ts-ignore dot notation access is ts limitation with ember object model
|
||||
const isSoleTag = get(this, 'fieldChangeSet.length') === 1;
|
||||
``;
|
||||
|
||||
if (typeof onFieldTagRemoved === 'function' && !isSoleTag) {
|
||||
onFieldTagRemoved(tag);
|
||||
}
|
||||
}
|
||||
}
|
@ -57,6 +57,7 @@ import {
|
||||
ISecurityClassificationOption,
|
||||
ShowAllShowReview
|
||||
} from 'wherehows-web/typings/app/dataset-compliance';
|
||||
import { uniqBy } from 'lodash';
|
||||
|
||||
const {
|
||||
complianceDataException,
|
||||
@ -535,11 +536,14 @@ export default class DatasetCompliance extends Component {
|
||||
* to what is available on the dataset schema
|
||||
* @return {boolean}
|
||||
*/
|
||||
isSchemaFieldLengthGreaterThanComplianceEntities(this: DatasetCompliance): boolean {
|
||||
isSchemaFieldLengthGreaterThanUniqComplianceEntities(this: DatasetCompliance): boolean {
|
||||
const complianceInfo = get(this, 'complianceInfo');
|
||||
if (complianceInfo) {
|
||||
const { length: columnFieldsLength } = getWithDefault(this, 'schemaFieldNamesMappedToDataTypes', []);
|
||||
const { length: complianceListLength } = get(complianceInfo, 'complianceEntities') || [];
|
||||
const { length: complianceListLength } = uniqBy(
|
||||
get(complianceInfo, 'complianceEntities') || [],
|
||||
'identifierField'
|
||||
);
|
||||
|
||||
return columnFieldsLength >= complianceListLength;
|
||||
}
|
||||
@ -746,8 +750,8 @@ export default class DatasetCompliance extends Component {
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the default classification for the given identifier field
|
||||
* Using the identifierType, determine the field's default security classification based on a values
|
||||
* Sets the default classification for the given identifier field's tag
|
||||
* Using the identifierType, determine the tag's default security classification based on a values
|
||||
* supplied by complianceDataTypes endpoint
|
||||
* @param {string} identifierField the field for which the default classification should apply
|
||||
* @param {ComplianceFieldIdValue} identifierType the value of the field's identifier type
|
||||
@ -759,7 +763,7 @@ export default class DatasetCompliance extends Component {
|
||||
const complianceDataTypes = get(this, 'complianceDataTypes');
|
||||
const defaultSecurityClassification = getDefaultSecurityClassification(complianceDataTypes, identifierType);
|
||||
|
||||
this.actions.onFieldClassificationChange.call(this, { identifierField }, { value: defaultSecurityClassification });
|
||||
this.actions.tagClassificationChanged.call(this, { identifierField }, { value: defaultSecurityClassification });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -825,7 +829,7 @@ export default class DatasetCompliance extends Component {
|
||||
|
||||
// Create confirmation dialog
|
||||
get(this, 'notifications').notify(NotificationEvent.confirm, {
|
||||
header: 'Confirm fields marked as `none`',
|
||||
header: 'Confirm fields to tagged as `none` field type',
|
||||
content: `There are ${unformatted.length} non-ID fields. `,
|
||||
dialogActions: dialogActions
|
||||
});
|
||||
@ -856,15 +860,9 @@ export default class DatasetCompliance extends Component {
|
||||
|
||||
// Validation operations
|
||||
const idFieldsHaveValidLogicalType: boolean = idTypeFieldsHaveLogicalType(idTypeComplianceEntities);
|
||||
const fieldIdentifiersAreUnique: boolean = isListUnique(complianceEntities.mapBy('identifierField'));
|
||||
const schemaFieldLengthGreaterThanComplianceEntities: boolean = this.isSchemaFieldLengthGreaterThanComplianceEntities();
|
||||
const isSchemaFieldLengthGreaterThanUniqComplianceEntities: boolean = this.isSchemaFieldLengthGreaterThanUniqComplianceEntities();
|
||||
|
||||
if (!fieldIdentifiersAreUnique) {
|
||||
notify(NotificationEvent.error, { content: complianceFieldNotUnique });
|
||||
return Promise.reject(new Error(complianceFieldNotUnique));
|
||||
}
|
||||
|
||||
if (!schemaFieldLengthGreaterThanComplianceEntities) {
|
||||
if (!isSchemaFieldLengthGreaterThanUniqComplianceEntities) {
|
||||
notify(NotificationEvent.error, { content: complianceDataException });
|
||||
return Promise.reject(new Error(complianceFieldNotUnique));
|
||||
}
|
||||
@ -932,6 +930,88 @@ export default class DatasetCompliance extends Component {
|
||||
}
|
||||
|
||||
actions: IDatasetComplianceActions = {
|
||||
/**
|
||||
* Adds a new field tag to the list of compliance change set items
|
||||
* @param {IComplianceChangeSet} tag properties for new field tag
|
||||
* @return {IComplianceChangeSet}
|
||||
*/
|
||||
onFieldTagAdded(this: DatasetCompliance, tag: IComplianceChangeSet): IComplianceChangeSet {
|
||||
return get(this, 'compliancePolicyChangeSet').addObject(tag);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a field tag from the list of compliance change set items
|
||||
* @param {IComplianceChangeSet} tag
|
||||
* @return {IComplianceChangeSet}
|
||||
*/
|
||||
onFieldTagRemoved(this: DatasetCompliance, tag: IComplianceChangeSet): IComplianceChangeSet {
|
||||
return get(this, 'compliancePolicyChangeSet').removeObject(tag);
|
||||
},
|
||||
|
||||
/**
|
||||
* When a user updates the identifierFieldType, update working copy
|
||||
* @param {IComplianceChangeSet} tag
|
||||
* @param {ComplianceFieldIdValue} identifierType
|
||||
*/
|
||||
tagIdentifierChanged(
|
||||
this: DatasetCompliance,
|
||||
tag: IComplianceChangeSet,
|
||||
{ value: identifierType }: { value: ComplianceFieldIdValue }
|
||||
) {
|
||||
const { identifierField } = tag;
|
||||
if (tag) {
|
||||
setProperties(tag, {
|
||||
identifierType,
|
||||
logicalType: null,
|
||||
nonOwner: null,
|
||||
isDirty: true
|
||||
});
|
||||
}
|
||||
|
||||
this.setDefaultClassification({ identifierField, identifierType });
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the logical type for a tag
|
||||
* @param {IComplianceChangeSet} tag the tag to be updated
|
||||
* @param {IComplianceChangeSet.logicalType} logicalType the updated logical type
|
||||
*/
|
||||
tagLogicalTypeChanged(
|
||||
this: DatasetCompliance,
|
||||
tag: IComplianceChangeSet,
|
||||
logicalType: IComplianceChangeSet['logicalType']
|
||||
) {
|
||||
setProperties(tag, { logicalType, isDirty: true });
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the security classification on a field tag
|
||||
* @param {IComplianceChangeSet} tag the tag to be updated
|
||||
* @param {IComplianceChangeSet.securityClassification} securityClassification the updated security classification value
|
||||
*/
|
||||
tagClassificationChanged(
|
||||
this: DatasetCompliance,
|
||||
tag: IComplianceChangeSet,
|
||||
{ value: securityClassification = null }: { value: IComplianceChangeSet['securityClassification'] }
|
||||
) {
|
||||
setProperties(tag, {
|
||||
securityClassification,
|
||||
isDirty: true
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the nonOwner property on the tag
|
||||
* @param {IComplianceChangeSet} tag the field tag to be updated
|
||||
* @param {IComplianceChangeSet.nonOwner} nonOwner flag indicating the field property is a nonOwner
|
||||
*/
|
||||
tagOwnerChanged(this: DatasetCompliance, tag: IComplianceChangeSet, nonOwner: IComplianceChangeSet['nonOwner']) {
|
||||
setProperties(tag, {
|
||||
nonOwner,
|
||||
isDirty: true
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets each datasetClassification value as false
|
||||
* @returns {Promise<DatasetClassification>}
|
||||
@ -1170,92 +1250,6 @@ export default class DatasetCompliance extends Component {
|
||||
anchor.click();
|
||||
},
|
||||
|
||||
/**
|
||||
* When a user updates the identifierFieldType in the DOM, update the backing store
|
||||
* @param {String} identifierField
|
||||
* @param {String} logicalType
|
||||
* @param {String} identifierType
|
||||
*/
|
||||
onFieldIdentifierTypeChange(
|
||||
this: DatasetCompliance,
|
||||
{ identifierField }: IComplianceChangeSet,
|
||||
{ value: identifierType }: { value: ComplianceFieldIdValue }
|
||||
) {
|
||||
const complianceEntitiesChangeSet = get(this, 'compliancePolicyChangeSet');
|
||||
// A reference to the current field in the compliance list, it should exist even for empty complianceEntities
|
||||
// since this is a reference created in the working copy: compliancePolicyChangeSet
|
||||
const changeSetComplianceField = complianceEntitiesChangeSet.findBy('identifierField', identifierField);
|
||||
|
||||
// Reset field attributes on change to field in change set
|
||||
if (changeSetComplianceField) {
|
||||
setProperties(changeSetComplianceField, <IComplianceChangeSet>{
|
||||
identifierType,
|
||||
logicalType: null,
|
||||
nonOwner: false,
|
||||
isDirty: true
|
||||
});
|
||||
}
|
||||
|
||||
// Set the defaultClassification for the identifierField,
|
||||
this.setDefaultClassification({ identifierField, identifierType });
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the logical type for the given identifierField
|
||||
* @param {IComplianceChangeSet} field
|
||||
* @param {IComplianceChangeSet.logicalType} logicalType
|
||||
*/
|
||||
onFieldLogicalTypeChange(
|
||||
this: DatasetCompliance,
|
||||
field: IComplianceChangeSet,
|
||||
logicalType: IComplianceChangeSet['logicalType']
|
||||
) {
|
||||
setProperties(field, <IComplianceChangeSet>{ logicalType, isDirty: true });
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the field security classification
|
||||
* @param {IComplianceChangeSet} { identifierField } the identifier field to update the classification for
|
||||
* @param {{value: IComplianceChangeSet.classification}} { value: classification = null }
|
||||
*/
|
||||
onFieldClassificationChange(
|
||||
this: DatasetCompliance,
|
||||
{ identifierField }: IComplianceChangeSet,
|
||||
{ value: securityClassification = null }: { value: IComplianceChangeSet['securityClassification'] }
|
||||
) {
|
||||
const currentFieldInComplianceList = get(this, 'compliancePolicyChangeSet').findBy(
|
||||
'identifierField',
|
||||
identifierField
|
||||
);
|
||||
|
||||
// Apply the updated classification value to the current instance of the field in working copy
|
||||
if (currentFieldInComplianceList) {
|
||||
setProperties(currentFieldInComplianceList, <IComplianceChangeSet>{
|
||||
securityClassification,
|
||||
isDirty: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the field non owner flag
|
||||
* @param {IComplianceChangeSet} { identifierField }
|
||||
* @param {IComplianceChangeSet.nonOwner} nonOwner
|
||||
*/
|
||||
onFieldOwnerChange(
|
||||
this: DatasetCompliance,
|
||||
{ identifierField }: IComplianceChangeSet,
|
||||
nonOwner: IComplianceChangeSet['nonOwner']
|
||||
) {
|
||||
const currentFieldInComplianceList = get(this, 'compliancePolicyChangeSet').findBy(
|
||||
'identifierField',
|
||||
identifierField
|
||||
);
|
||||
if (currentFieldInComplianceList) {
|
||||
setProperties(currentFieldInComplianceList, <IComplianceChangeSet>{ nonOwner, isDirty: true });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the source object representing the current datasetClassification map
|
||||
* @param {keyof typeof DatasetClassifiers} classifier the property on the datasetClassification to update
|
||||
|
@ -12,7 +12,8 @@ import {
|
||||
IdentifierFieldWithFieldChangeSetTuple,
|
||||
IIdentifierFieldWithFieldChangeSetObject,
|
||||
ISchemaFieldsToPolicy,
|
||||
ISchemaFieldsToSuggested
|
||||
ISchemaFieldsToSuggested,
|
||||
SchemaFieldToPolicyValue
|
||||
} from 'wherehows-web/typings/app/dataset-compliance';
|
||||
import {
|
||||
IColumnFieldProps,
|
||||
@ -228,7 +229,7 @@ const mergeMappedColumnFieldsWithSuggestions = (
|
||||
fieldSuggestionMap: ISchemaFieldsToSuggested = {}
|
||||
): Array<IComplianceChangeSet> =>
|
||||
Object.keys(mappedColumnFields).map(fieldName => {
|
||||
const field = pick(mappedColumnFields[fieldName], [
|
||||
const field: IComplianceChangeSet = pick(mappedColumnFields[fieldName], [
|
||||
'identifierField',
|
||||
'dataType',
|
||||
'identifierType',
|
||||
@ -263,7 +264,7 @@ const foldComplianceChangeSetToField = (
|
||||
changeSet: IComplianceChangeSet
|
||||
): IIdentifierFieldWithFieldChangeSetObject => ({
|
||||
...identifierFieldMap,
|
||||
[changeSet.identifierField]: [...identifierFieldMap[changeSet.identifierField], changeSet]
|
||||
[changeSet.identifierField]: [...(identifierFieldMap[changeSet.identifierField] || []), changeSet]
|
||||
});
|
||||
|
||||
/**
|
||||
@ -339,6 +340,23 @@ const mapSchemaColumnPropsToCurrentPrivacyPolicy = ({
|
||||
}: ISchemaColumnMappingProps): ISchemaFieldsToPolicy =>
|
||||
arrayReduce(columnToPolicyReducingFn(complianceEntities, policyModificationTime), {})(columnProps);
|
||||
|
||||
/**
|
||||
* Creates a new tag / change set item for a compliance entity / field with default properties
|
||||
* @param {IColumnFieldProps} { identifierField, dataType } the runtime properties to apply to the created instance
|
||||
* @returns {SchemaFieldToPolicyValue}
|
||||
*/
|
||||
const complianceFieldTagFactory = ({ identifierField, dataType }: IColumnFieldProps): SchemaFieldToPolicyValue => ({
|
||||
identifierField,
|
||||
dataType,
|
||||
identifierType: null,
|
||||
logicalType: null,
|
||||
securityClassification: null,
|
||||
nonOwner: null,
|
||||
readonly: false,
|
||||
privacyPolicyExists: false,
|
||||
isDirty: true
|
||||
});
|
||||
|
||||
/**
|
||||
* Takes the current compliance entities, and mod time and returns a reducer that consumes a list of IColumnFieldProps
|
||||
* instances and maps each entry to a compliance entity on the current compliance policy
|
||||
@ -392,5 +410,6 @@ export {
|
||||
changeSetFieldsRequiringReview,
|
||||
changeSetReviewableAttributeTriggers,
|
||||
mapSchemaColumnPropsToCurrentPrivacyPolicy,
|
||||
foldComplianceChangeSets
|
||||
foldComplianceChangeSets,
|
||||
complianceFieldTagFactory
|
||||
};
|
||||
|
@ -3,6 +3,10 @@
|
||||
$compliance-readonly-color: get-color(red7);
|
||||
$compliance-review-required-color: get-color(blue5);
|
||||
$compliance-ok-color: get-color(green5);
|
||||
@mixin select-wrapper {
|
||||
max-width: item-spacing(9) * 2;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
&__has-suggestions {
|
||||
color: $compliance-suggestion-color;
|
||||
@ -17,6 +21,14 @@
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
&__identifier-column {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
&__identifier-cell {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&__classification-column {
|
||||
width: 20%;
|
||||
}
|
||||
@ -77,6 +89,77 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__add-field {
|
||||
&#{&} {
|
||||
font-weight: fw(normal, 4);
|
||||
}
|
||||
}
|
||||
|
||||
&__rollup-toggle {
|
||||
&#{&} {
|
||||
color: get-color(gray7);
|
||||
}
|
||||
}
|
||||
|
||||
&__remove-tag {
|
||||
&#{&} {
|
||||
font-weight: fw(normal, 2);
|
||||
font-size: item-spacing(6);
|
||||
background-color: transparent;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
color: get-color(black, 0.7);
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__select-wrapper {
|
||||
@include select-wrapper;
|
||||
}
|
||||
|
||||
&__identifier-select {
|
||||
$border: 1px solid get-color(black, 0.7);
|
||||
|
||||
@include select-wrapper;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: item-spacing(2);
|
||||
height: 50%;
|
||||
border-bottom: $border;
|
||||
border-left: $border;
|
||||
top: 2px;
|
||||
left: -(item-spacing(3));
|
||||
}
|
||||
}
|
||||
|
||||
&__tag-options {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__tag-label {
|
||||
margin: item-spacing(0 2);
|
||||
font-weight: fw(normal, 2);
|
||||
}
|
||||
|
||||
&__tag-row {
|
||||
background-color: get-color(gray0);
|
||||
|
||||
&#{&} {
|
||||
td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.compliance-depends {
|
||||
|
@ -0,0 +1,12 @@
|
||||
{{yield (hash
|
||||
rowId=elementId
|
||||
isIdType=isIdType
|
||||
isPiiType=isPiiType
|
||||
fieldFormats=fieldFormats
|
||||
isTagFormatMissing=isTagFormatMissing
|
||||
tagClassification=tagClassification
|
||||
tagIdentifierTypeDidChange=(action "tagIdentifierTypeDidChange")
|
||||
tagLogicalTypeDidChange=(action "tagLogicalTypeDidChange")
|
||||
tagOwnerDidChange=(action "tagOwnerDidChange")
|
||||
tagClassificationDidChange=(action "tagClassificationDidChange")
|
||||
)}}
|
@ -0,0 +1,12 @@
|
||||
{{yield (hash
|
||||
cell=(component 'dataset-table-cell')
|
||||
isRowExpanded=isRowExpanded
|
||||
fieldChangeSet=fieldChangeSet
|
||||
identifierField=identifierField
|
||||
dataType=dataType
|
||||
isIdType=isIdType
|
||||
fieldFormats=fieldFormats
|
||||
onToggleRowExpansion=(action "onToggleRowExpansion")
|
||||
onAddFieldTag=(action "onAddFieldTag")
|
||||
onRemoveFieldTag=(action "onRemoveFieldTag")
|
||||
)}}
|
@ -30,7 +30,7 @@ type SchemaFieldToPolicyValue = Pick<
|
||||
privacyPolicyExists: boolean;
|
||||
// flag indicating the field changeSet has been modified on the client
|
||||
isDirty: boolean;
|
||||
policyModificationTime: IComplianceInfo['modifiedTime'];
|
||||
policyModificationTime?: IComplianceInfo['modifiedTime'];
|
||||
dataType: string;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user