diff --git a/wherehows-web/app/components/dataset-compliance.ts b/wherehows-web/app/components/dataset-compliance.ts index 7aaa234b15..efa57e50f1 100644 --- a/wherehows-web/app/components/dataset-compliance.ts +++ b/wherehows-web/app/components/dataset-compliance.ts @@ -171,9 +171,14 @@ export default class DatasetCompliance extends Component { * @type {ComputedProperty} * @memberof DatasetCompliance */ - initialStepNeedsReview = computed('isInitialEditStep', 'changeSetReview', function(this: DatasetCompliance): boolean { - const { isInitialEditStep, changeSetReview } = getProperties(this, ['isInitialEditStep', 'changeSetReview']); - const { length } = editableTags(changeSetReview); + initialStepNeedsReview = computed('isInitialEditStep', 'changeSetReviewWithoutSuggestionCheck', function( + this: DatasetCompliance + ): boolean { + const { isInitialEditStep, changeSetReviewWithoutSuggestionCheck } = getProperties(this, [ + 'isInitialEditStep', + 'changeSetReviewWithoutSuggestionCheck' + ]); + const { length } = editableTags(changeSetReviewWithoutSuggestionCheck); return isInitialEditStep && length > 0; }); @@ -771,6 +776,43 @@ export default class DatasetCompliance extends Component { } ); + /** + * Filters out the compliance tags requiring review excluding tags that require review, + * due to a suggestion mismatch with the current tag identifierType + * This drives the initialStep review check and fulfills the use-case, + * where the user can proceed with the compliance update, without + * being required to resolve a suggestion mismatch + * @type {ComputedProperty>} + * @memberof DatasetCompliance + */ + changeSetReviewWithoutSuggestionCheck = computed('changeSetReview', function( + this: DatasetCompliance + ): Array { + return tagsRequiringReview(get(this, 'complianceDataTypes'), { checkSuggestions: false })( + get(this, 'changeSetReview') + ); + }); + + /** + * The changeSet tags that require user attention + * @type {ComputedProperty>} + * @memberof DatasetCompliance + */ + changeSetReview = computed( + `compliancePolicyChangeSet.@each.{${changeSetReviewableAttributeTriggers}}`, + 'complianceDataTypes', + function(this: DatasetCompliance): Array { + return tagsRequiringReview(get(this, 'complianceDataTypes'))(get(this, 'compliancePolicyChangeSet')); + } + ); + + /** + * Returns a count of changeSet tags that require user attention + * @type {ComputedProperty} + * @memberof DatasetCompliance + */ + changeSetReviewCount = alias('changeSetReview.length'); + /** * Reduces the current filtered changeSet to a list of IdentifierFieldWithFieldChangeSetTuple * @type {Array} @@ -849,26 +891,6 @@ export default class DatasetCompliance extends Component { this.notifyOnChangeSetRequiresReview(hasChangeSetDrift); }; - /** - * The changeSet tags that require user attention - * @type {ComputedProperty>} - * @memberof DatasetCompliance - */ - changeSetReview = computed( - `compliancePolicyChangeSet.@each.{${changeSetReviewableAttributeTriggers}}`, - 'complianceDataTypes', - function(this: DatasetCompliance): Array { - return tagsRequiringReview(get(this, 'complianceDataTypes'))(get(this, 'compliancePolicyChangeSet')); - } - ); - - /** - * Returns a count of changeSet tags that require user attention - * @type {ComputedProperty} - * @memberof DatasetCompliance - */ - changeSetReviewCount = alias('changeSetReview.length'); - /** * 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 diff --git a/wherehows-web/app/constants/dataset-compliance.ts b/wherehows-web/app/constants/dataset-compliance.ts index ffad6077b8..47c7808c96 100644 --- a/wherehows-web/app/constants/dataset-compliance.ts +++ b/wherehows-web/app/constants/dataset-compliance.ts @@ -14,7 +14,8 @@ import { ISchemaFieldsToPolicy, ISchemaFieldsToSuggested, IComplianceEntityWithMetadata, - ISuggestedFieldTypeValues + ISuggestedFieldTypeValues, + IComplianceTagReviewOptions } from 'wherehows-web/typings/app/dataset-compliance'; import { IColumnFieldProps, @@ -189,21 +190,48 @@ const tagSuggestionNeedsReview = ({ suggestion, suggestionAuthority, identifierT ? !suggestionAuthority : false; +/** + * Checks if a compliance tag's logicalType property is missing or nonOwner flag is set + * @param {IComplianceChangeSet} tag the compliance field tag + * @return {boolean} + */ +const tagLogicalTypeNonOwnerAttributeNeedsReview = (tag: IComplianceChangeSet): boolean => + !idTypeTagHasLogicalType(tag) || !tagOwnerIsSet(tag); + +/** + * Checks if a compliance tag's valuePattern attribute is valid or otherwise + * @param {string | null} valuePattern + * @return {boolean} + */ +const tagValuePatternNeedsReview = ({ valuePattern }: IComplianceChangeSet): boolean => { + let isValid = false; + try { + ({ isValid } = validateRegExp(valuePattern, isValidCustomValuePattern)); + } catch { + isValid = false; + } + + return !isValid; +}; + /** * Checks if a compliance policy changeSet field requires user attention: if a suggestion * is available but the user has not indicated intent or a policy for the field does not currently exist remotely * and the related field changeSet has not been modified on the client and isn't readonly * @param {Array} complianceDataTypes + * @param {IComplianceTagReviewOptions} options an option bag with properties that modify the behaviour of the review + * checking steps * @return {(tag: IComplianceChangeSet) => boolean} */ -const tagNeedsReview = (complianceDataTypes: Array) => +const tagNeedsReview = (complianceDataTypes: Array, options?: IComplianceTagReviewOptions) => /** * Checks if a compliance tag needs to be reviewed against a set of rules * @param {IComplianceChangeSet} tag * @return {boolean} */ (tag: IComplianceChangeSet): boolean => { - const { isDirty, privacyPolicyExists, identifierType, logicalType, valuePattern } = tag; + const { checkSuggestions } = options || { checkSuggestions: true }; + const { isDirty, privacyPolicyExists, identifierType, logicalType } = tag; let isReviewRequired = false; // Ensure that the tag has an identifier type specified @@ -212,22 +240,18 @@ const tagNeedsReview = (complianceDataTypes: Array) => } // Check that a hi confidence suggestion exists and the identifierType does not match the change set item - isReviewRequired = isReviewRequired || tagSuggestionNeedsReview(tag); + if (checkSuggestions) { + isReviewRequired = isReviewRequired || tagSuggestionNeedsReview(tag); + } // Ensure that tag has a logical type and nonOwner flag is set when tag is of id type if (isTagIdType(complianceDataTypes)(tag)) { - isReviewRequired = isReviewRequired || !idTypeTagHasLogicalType(tag) || !tagOwnerIsSet(tag); + isReviewRequired = isReviewRequired || tagLogicalTypeNonOwnerAttributeNeedsReview(tag); } // If the tag has a IdLogicalType.Custom logicalType, check that the value pattern is truthy if (logicalType === IdLogicalType.Custom) { - let isValid = false; - try { - ({ isValid } = validateRegExp(valuePattern, isValidCustomValuePattern)); - } catch { - isValid = false; - } - isReviewRequired = isReviewRequired || !isValid; + isReviewRequired = isReviewRequired || tagValuePatternNeedsReview(tag); } // If either the privacy policy doesn't exists, or user hasn't made changes, then review is required @@ -364,10 +388,11 @@ const singleTagsInChangeSet = ( /** * Returns a list of changeSet tags that requires user attention * @param {Array} complianceDataTypes + * @param {IComplianceTagReviewOptions} options * @return {(array: Array) => Array} */ -const tagsRequiringReview = (complianceDataTypes: Array) => - arrayFilter(tagNeedsReview(complianceDataTypes)); +const tagsRequiringReview = (complianceDataTypes: Array, options?: IComplianceTagReviewOptions) => + arrayFilter(tagNeedsReview(complianceDataTypes, options)); /** * Lists the tags for a specific identifier field that need to be reviewed diff --git a/wherehows-web/app/typings/app/dataset-compliance.d.ts b/wherehows-web/app/typings/app/dataset-compliance.d.ts index bdbaacb7c4..59cf8dd49b 100644 --- a/wherehows-web/app/typings/app/dataset-compliance.d.ts +++ b/wherehows-web/app/typings/app/dataset-compliance.d.ts @@ -18,6 +18,14 @@ interface IDatasetComplianceActions { [K: string]: (...args: Array) => any; } +/** + * Describes the interface for compliance tag review check function, options argument + * @interface IComplianceTagReviewOptions + */ +interface IComplianceTagReviewOptions { + checkSuggestions: boolean; +} + /** * Alias for the properties defined on an object indicating the values for a compliance entity object in * addition to related component metadata using in processing ui interactions / rendering for the field @@ -153,5 +161,6 @@ export { ISecurityClassificationOption, IIdentifierFieldWithFieldChangeSetObject, IdentifierFieldWithFieldChangeSetTuple, - ISuggestedFieldTypeValues + ISuggestedFieldTypeValues, + IComplianceTagReviewOptions };