mirror of
				https://github.com/datahub-project/datahub.git
				synced 2025-11-03 20:27:50 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1536 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			1536 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import Component from '@ember/component';
 | 
						|
import { computed, set, get, setProperties, getProperties, getWithDefault } from '@ember/object';
 | 
						|
import ComputedProperty, { not, or, alias } from '@ember/object/computed';
 | 
						|
import { run, schedule, next } from '@ember/runloop';
 | 
						|
import { classify } from '@ember/string';
 | 
						|
import { assert } from '@ember/debug';
 | 
						|
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 { task, waitForProperty, TaskInstance } from 'ember-concurrency';
 | 
						|
import {
 | 
						|
  getSecurityClassificationDropDownOptions,
 | 
						|
  DatasetClassifiers,
 | 
						|
  getFieldIdentifierOptions,
 | 
						|
  getDefaultSecurityClassification,
 | 
						|
  compliancePolicyStrings,
 | 
						|
  getComplianceSteps,
 | 
						|
  isExempt,
 | 
						|
  ComplianceFieldIdValue,
 | 
						|
  IDatasetClassificationOption,
 | 
						|
  DatasetClassification,
 | 
						|
  SuggestionIntent,
 | 
						|
  PurgePolicy,
 | 
						|
  getSupportedPurgePolicies,
 | 
						|
  mergeComplianceEntitiesWithSuggestions,
 | 
						|
  tagsRequiringReview,
 | 
						|
  isTagIdType,
 | 
						|
  idTypeTagsHaveLogicalType,
 | 
						|
  changeSetReviewableAttributeTriggers,
 | 
						|
  asyncMapSchemaColumnPropsToCurrentPrivacyPolicy,
 | 
						|
  foldComplianceChangeSets,
 | 
						|
  sortFoldedChangeSetTuples,
 | 
						|
  tagsWithoutIdentifierType,
 | 
						|
  singleTagsInChangeSet,
 | 
						|
  tagsForIdentifierField,
 | 
						|
  overrideTagReadonly,
 | 
						|
  editableTags,
 | 
						|
  lowQualitySuggestionConfidenceThreshold,
 | 
						|
  TagFilter,
 | 
						|
  tagSuggestionNeedsReview
 | 
						|
} from 'wherehows-web/constants';
 | 
						|
import { getTagsSuggestions } from 'wherehows-web/utils/datasets/compliance-suggestions';
 | 
						|
import { arrayFilter, arrayMap, compact, isListUnique, iterateArrayAsync } from 'wherehows-web/utils/array';
 | 
						|
import { identity, noop } from 'wherehows-web/utils/helpers/functions';
 | 
						|
import { IComplianceDataType } from 'wherehows-web/typings/api/list/compliance-datatypes';
 | 
						|
import Notifications, { NotificationEvent } from 'wherehows-web/services/notifications';
 | 
						|
import { IDatasetColumn } from 'wherehows-web/typings/api/datasets/columns';
 | 
						|
import {
 | 
						|
  IComplianceInfo,
 | 
						|
  IComplianceEntity,
 | 
						|
  ISuggestedFieldClassification,
 | 
						|
  IComplianceSuggestion
 | 
						|
} from 'wherehows-web/typings/api/datasets/compliance';
 | 
						|
import {
 | 
						|
  IComplianceChangeSet,
 | 
						|
  IComplianceEntityWithMetadata,
 | 
						|
  IComplianceFieldIdentifierOption,
 | 
						|
  IDatasetComplianceActions,
 | 
						|
  IdentifierFieldWithFieldChangeSetTuple,
 | 
						|
  IDropDownOption,
 | 
						|
  ISchemaFieldsToPolicy,
 | 
						|
  ISchemaFieldsToSuggested,
 | 
						|
  ISecurityClassificationOption
 | 
						|
} from 'wherehows-web/typings/app/dataset-compliance';
 | 
						|
import { uniqBy } from 'lodash';
 | 
						|
import { IColumnFieldProps } from 'wherehows-web/typings/app/dataset-columns';
 | 
						|
import { NonIdLogicalType } from 'wherehows-web/constants/datasets/compliance';
 | 
						|
import { trackableEvent, TrackableEventCategory } from 'wherehows-web/constants/analytics/event-tracking';
 | 
						|
import { notificationDialogActionFactory } from 'wherehows-web/utils/notifications/notifications';
 | 
						|
import { isMetadataObject, jsonValuesMatch } from 'wherehows-web/utils/datasets/compliance/metadata-schema';
 | 
						|
import { typeOf } from '@ember/utils';
 | 
						|
import { pick } from 'wherehows-web/utils/object';
 | 
						|
import { pluralize } from 'ember-inflector';
 | 
						|
import { service } from '@ember-decorators/service';
 | 
						|
 | 
						|
const {
 | 
						|
  complianceDataException,
 | 
						|
  complianceFieldNotUnique,
 | 
						|
  missingTypes,
 | 
						|
  missingPurgePolicy,
 | 
						|
  missingDatasetSecurityClassification
 | 
						|
} = compliancePolicyStrings;
 | 
						|
 | 
						|
/**
 | 
						|
 * String constant referencing the datasetClassification on the privacy policy
 | 
						|
 * @type {string}
 | 
						|
 */
 | 
						|
const datasetClassificationKey = 'complianceInfo.datasetClassification';
 | 
						|
/**
 | 
						|
 * A list of available keys for the datasetClassification map on the security specification
 | 
						|
 * @type {Array<keyof typeof DatasetClassifiers>}
 | 
						|
 */
 | 
						|
const datasetClassifiersKeys = <Array<keyof typeof DatasetClassifiers>>Object.keys(DatasetClassifiers);
 | 
						|
 | 
						|
/**
 | 
						|
 * The initial state of the compliance step for a zero based array
 | 
						|
 * @type {number}
 | 
						|
 */
 | 
						|
const initialStepIndex = -1;
 | 
						|
 | 
						|
export default class DatasetCompliance extends Component {
 | 
						|
  isNewComplianceInfo: boolean;
 | 
						|
  datasetName: string;
 | 
						|
  sortColumnWithName: string;
 | 
						|
  filterBy: string;
 | 
						|
  sortDirection: string;
 | 
						|
  searchTerm: string;
 | 
						|
  _hasBadData: boolean;
 | 
						|
  platform: IDatasetView['platform'];
 | 
						|
  isCompliancePolicyAvailable: boolean = false;
 | 
						|
  showAllDatasetMemberData: boolean;
 | 
						|
  complianceInfo: undefined | IComplianceInfo;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Lists the compliance entities that are entered via the advanced editing interface
 | 
						|
   * @type {Pick<IComplianceInfo, 'complianceEntities'>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  manuallyEnteredComplianceEntities: Pick<IComplianceInfo, 'complianceEntities'>;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Flag enabling or disabling the manual apply button
 | 
						|
   * @type {boolean}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  isManualApplyDisabled: boolean = false;
 | 
						|
 | 
						|
  /**
 | 
						|
   * String representation of a parse error that may have occurred when validating manually entered compliance entities
 | 
						|
   * @type {string}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  manualParseError: string = '';
 | 
						|
 | 
						|
  /**
 | 
						|
   * Flag indicating the current compliance policy edit-view mode
 | 
						|
   * @type {boolean}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  showGuidedComplianceEditMode: boolean = true;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Confidence percentage number used to filter high quality suggestions versus lower quality
 | 
						|
   * @type {number}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  suggestionConfidenceThreshold: number;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Formatted JSON string representing the compliance entities for this dataset
 | 
						|
   * @type {ComputedProperty<string>}
 | 
						|
   */
 | 
						|
  jsonComplianceEntities: ComputedProperty<string> = computed('columnIdFieldsToCurrentPrivacyPolicy', function(
 | 
						|
    this: DatasetCompliance
 | 
						|
  ): string {
 | 
						|
    const entityAttrs: Array<keyof IComplianceEntity> = [
 | 
						|
      'identifierField',
 | 
						|
      'identifierType',
 | 
						|
      'logicalType',
 | 
						|
      'nonOwner',
 | 
						|
      'valuePattern',
 | 
						|
      'readonly'
 | 
						|
    ];
 | 
						|
    const entityMap: ISchemaFieldsToPolicy = get(this, 'columnIdFieldsToCurrentPrivacyPolicy');
 | 
						|
    const entitiesWithModifiableKeys = arrayMap((tag: IComplianceEntityWithMetadata) => pick(tag, entityAttrs))(
 | 
						|
      (<Array<IComplianceEntityWithMetadata>>[]).concat(...Object.values(entityMap))
 | 
						|
    );
 | 
						|
 | 
						|
    return JSON.stringify(entitiesWithModifiableKeys, null, '\t');
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Convenience computed property flag indicates if current edit step is the first step in  the wizard flow
 | 
						|
   * @type {ComputedProperty<boolean>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  isInitialEditStep = computed('editStep', 'editSteps.0.name', function(this: DatasetCompliance): boolean {
 | 
						|
    const { editStep, editSteps } = getProperties(this, ['editStep', 'editSteps']);
 | 
						|
    const [initialStep] = editSteps;
 | 
						|
    return editStep.name === initialStep.name;
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates if the first step does not need further user review to advance
 | 
						|
   * @type {ComputedProperty<boolean>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  initialStepNeedsReview = computed('isInitialEditStep', 'changeSetReviewWithoutSuggestionCheck', function(
 | 
						|
    this: DatasetCompliance
 | 
						|
  ): boolean {
 | 
						|
    const { isInitialEditStep, changeSetReviewWithoutSuggestionCheck } = getProperties(this, [
 | 
						|
      'isInitialEditStep',
 | 
						|
      'changeSetReviewWithoutSuggestionCheck'
 | 
						|
    ]);
 | 
						|
    const { length } = editableTags(changeSetReviewWithoutSuggestionCheck);
 | 
						|
 | 
						|
    return isInitialEditStep && length > 0;
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Flag indicating if the Guided vs Advanced mode should be shown for the initial edit step
 | 
						|
   * @type {ComputedProperty<boolean>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  showAdvancedEditApplyStep = computed('isInitialEditStep', 'showGuidedComplianceEditMode', function(
 | 
						|
    this: DatasetCompliance
 | 
						|
  ): boolean {
 | 
						|
    const { isInitialEditStep, showGuidedComplianceEditMode } = getProperties(this, [
 | 
						|
      'isInitialEditStep',
 | 
						|
      'showGuidedComplianceEditMode'
 | 
						|
    ]);
 | 
						|
    return isInitialEditStep && !showGuidedComplianceEditMode;
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Flag indicating the readonly confirmation dialog should not be shown again for this compliance form
 | 
						|
   * @type {boolean}
 | 
						|
   */
 | 
						|
  doNotShowReadonlyConfirmation: boolean = false;
 | 
						|
 | 
						|
  /**
 | 
						|
   * References the ComplianceFieldIdValue enum
 | 
						|
   * @type {ComplianceFieldIdValue}
 | 
						|
   */
 | 
						|
  ComplianceFieldIdValue = ComplianceFieldIdValue;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Suggested values for compliance types e.g. identifier type and/or logical type
 | 
						|
   * @type {IComplianceSuggestion | void}
 | 
						|
   */
 | 
						|
  complianceSuggestion: IComplianceSuggestion | void;
 | 
						|
 | 
						|
  schemaFieldNamesMappedToDataTypes: Array<Pick<IDatasetColumn, 'dataType' | 'fieldName'>>;
 | 
						|
  onReset: <T>() => Promise<T>;
 | 
						|
  onSave: <T>() => Promise<T>;
 | 
						|
 | 
						|
  /**
 | 
						|
   * External action to handle manual compliance entity metadata entry
 | 
						|
   */
 | 
						|
  onComplianceJsonUpdate: (jsonString: string) => Promise<void>;
 | 
						|
 | 
						|
  notifyOnChangeSetSuggestions: (hasSuggestions: boolean) => void;
 | 
						|
  notifyOnChangeSetRequiresReview: (hasChangeSetDrift: boolean) => void;
 | 
						|
 | 
						|
  classNames = ['compliance-container'];
 | 
						|
 | 
						|
  classNameBindings = ['isEditing:compliance-container--edit-mode'];
 | 
						|
 | 
						|
  /**
 | 
						|
   * Reference to the application notifications Service
 | 
						|
   * @type {ComputedProperty<Notifications>}
 | 
						|
   */
 | 
						|
  @service
 | 
						|
  notifications: Notifications;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Flag indicating that the field names in each compliance row is truncated or rendered in full
 | 
						|
   * @type {boolean}
 | 
						|
   */
 | 
						|
  isShowingFullFieldNames = true;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Flag indicating that the related dataset is schemaless or has a schema
 | 
						|
   * @type {boolean}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  schemaless: boolean;
 | 
						|
  /**
 | 
						|
   * Tracks the current index of the compliance policy update wizard flow
 | 
						|
   * @type {number}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  editStepIndex: number;
 | 
						|
 | 
						|
  /**
 | 
						|
   * List of complianceDataType values
 | 
						|
   * @type {Array<IComplianceDataType>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  complianceDataTypes: Array<IComplianceDataType>;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Mapped list of classifiers options for drop down
 | 
						|
   * @type {Array<ISecurityClassificationOption>}
 | 
						|
   */
 | 
						|
  classifiers: Array<ISecurityClassificationOption> = getSecurityClassificationDropDownOptions();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Default to show all fields to review
 | 
						|
   * @type {string}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  fieldReviewOption: TagFilter = TagFilter.showAll;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Computes a cta string for the selected field review filter option
 | 
						|
   * @type {ComputedProperty<string>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  fieldReviewHint: ComputedProperty<string> = computed('fieldReviewOption', 'changeSetReviewCount', function(
 | 
						|
    this: DatasetCompliance
 | 
						|
  ): string {
 | 
						|
    type TagFilterHint = { [K in TagFilter]: string };
 | 
						|
 | 
						|
    const { fieldReviewOption, changeSetReviewCount } = getProperties(this, [
 | 
						|
      'fieldReviewOption',
 | 
						|
      'changeSetReviewCount'
 | 
						|
    ]);
 | 
						|
 | 
						|
    const hint = (<TagFilterHint>{
 | 
						|
      [TagFilter.showAll]: `${pluralize(changeSetReviewCount, 'field')} to be reviewed`,
 | 
						|
      [TagFilter.showReview]: 'It is required to select compliance info for all fields',
 | 
						|
      [TagFilter.showSuggested]: 'Please review suggestions and provide feedback'
 | 
						|
    })[fieldReviewOption];
 | 
						|
 | 
						|
    return changeSetReviewCount ? hint : '';
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Flag indicating that the component is in edit mode
 | 
						|
   * @type {ComputedProperty<boolean>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  isEditing = computed('editStepIndex', 'complianceInfo.fromUpstream', function(this: DatasetCompliance): boolean {
 | 
						|
    // initialStepIndex is less than the currently set step index
 | 
						|
    return get(this, 'editStepIndex') > initialStepIndex;
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Convenience flag indicating the policy is not currently being edited
 | 
						|
   * @type {ComputedProperty<boolean>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  isReadOnly = not('isEditing');
 | 
						|
 | 
						|
  /**
 | 
						|
   * Flag indicating that the component is currently saving / attempting to save the privacy policy
 | 
						|
   * @type {boolean}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  isSaving = false;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The list of supported purge policies for the related platform
 | 
						|
   * @type {Array<PurgePolicy>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  supportedPurgePolicies: Array<PurgePolicy> = [];
 | 
						|
 | 
						|
  /**
 | 
						|
   * Computed prop over the current Id fields in the Privacy Policy
 | 
						|
   * @type {ISchemaFieldsToPolicy}
 | 
						|
   */
 | 
						|
  columnIdFieldsToCurrentPrivacyPolicy: ISchemaFieldsToPolicy = {};
 | 
						|
 | 
						|
  /**
 | 
						|
   * Enum of categories that can be tracked for this component
 | 
						|
   * @type {TrackableEventCategory}
 | 
						|
   */
 | 
						|
  trackableCategory = TrackableEventCategory;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Map of events that can be tracked
 | 
						|
   * @type {ITrackableEventCategoryEvent}
 | 
						|
   */
 | 
						|
  trackableEvent = trackableEvent;
 | 
						|
 | 
						|
  constructor() {
 | 
						|
    super(...arguments);
 | 
						|
 | 
						|
    //sets default values for class fields
 | 
						|
    this.editStepIndex = initialStepIndex;
 | 
						|
    this.sortColumnWithName || set(this, 'sortColumnWithName', 'identifierField');
 | 
						|
    this.filterBy || set(this, 'filterBy', '0'); // first element in field type is identifierField
 | 
						|
    this.sortDirection || set(this, 'sortDirection', 'asc');
 | 
						|
    this.searchTerm || set(this, 'searchTerm', '');
 | 
						|
    this.schemaFieldNamesMappedToDataTypes || (this.schemaFieldNamesMappedToDataTypes = []);
 | 
						|
    this.complianceDataTypes || (this.complianceDataTypes = []);
 | 
						|
    typeOf(this.suggestionConfidenceThreshold) === 'number' ||
 | 
						|
      set(this, 'suggestionConfidenceThreshold', lowQualitySuggestionConfidenceThreshold);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Lists the compliance wizard edit steps based on the datasets schemaless property
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  editSteps = computed('schemaless', function(this: DatasetCompliance): Array<{ name: string }> {
 | 
						|
    const hasSchema = !getWithDefault(this, 'schemaless', false);
 | 
						|
    const steps = getComplianceSteps(hasSchema);
 | 
						|
 | 
						|
    // Ensure correct step ordering
 | 
						|
    return Object.keys(steps)
 | 
						|
      .sort()
 | 
						|
      .map((key: string): { name: string } => steps[+key]);
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Reads the complianceDataTypes property and transforms into a list of drop down options for the field
 | 
						|
   * identifier type
 | 
						|
   * @type {ComputedProperty<Array<IComplianceFieldIdentifierOption  | IDropDownOption<null | 'NONE'>>>}
 | 
						|
   */
 | 
						|
  complianceFieldIdDropdownOptions = computed('complianceDataTypes', function(
 | 
						|
    this: DatasetCompliance
 | 
						|
  ): Array<IComplianceFieldIdentifierOption | IDropDownOption<null | ComplianceFieldIdValue.None>> {
 | 
						|
    // object with interface IComplianceDataType and an index number indicative of position
 | 
						|
    type IndexedComplianceDataType = IComplianceDataType & { index: number };
 | 
						|
 | 
						|
    const noneDropDownOption: IDropDownOption<ComplianceFieldIdValue.None> = {
 | 
						|
      value: ComplianceFieldIdValue.None,
 | 
						|
      label: 'None'
 | 
						|
    };
 | 
						|
    // Creates a list of IComplianceDataType each with an index. The intent here is to perform a stable sort on
 | 
						|
    // the items in the list, Array#sort is not stable, so for items that equal on the primary comparator
 | 
						|
    // break the tie based on position in original list
 | 
						|
    const indexedDataTypes: Array<IndexedComplianceDataType> = (get(this, 'complianceDataTypes') || []).map(
 | 
						|
      (type, index): IndexedComplianceDataType => ({
 | 
						|
        ...type,
 | 
						|
        index
 | 
						|
      })
 | 
						|
    );
 | 
						|
 | 
						|
    /**
 | 
						|
     * Compares each compliance data type, ensure that positional order is maintained
 | 
						|
     * @param {IComplianceDataType} a the compliance type to compare
 | 
						|
     * @param {IComplianceDataType} b the other
 | 
						|
     * @returns {number} 0, 1, -1 indicating sort order
 | 
						|
     */
 | 
						|
    const dataTypeComparator = (a: IndexedComplianceDataType, b: IndexedComplianceDataType): number => {
 | 
						|
      const { idType: aIdType, index: aIndex } = a;
 | 
						|
      const { idType: bIdType, index: bIndex } = b;
 | 
						|
      // Convert boolean values to number type
 | 
						|
      const typeCompare = Number(aIdType) - Number(bIdType);
 | 
						|
 | 
						|
      // True types first, hence negation
 | 
						|
      // If types are same, then sort on original position i.e stable sort
 | 
						|
      return typeCompare ? -typeCompare : aIndex - bIndex;
 | 
						|
    };
 | 
						|
 | 
						|
    /**
 | 
						|
     * Inserts a divider in the list of compliance field identifier options
 | 
						|
     * @param {Array<IComplianceFieldIdentifierOption>} types
 | 
						|
     * @returns {Array<IComplianceFieldIdentifierOption>}
 | 
						|
     */
 | 
						|
    const insertDividers = (
 | 
						|
      types: Array<IComplianceFieldIdentifierOption>
 | 
						|
    ): Array<IComplianceFieldIdentifierOption> => {
 | 
						|
      const isId = ({ isId }: IComplianceFieldIdentifierOption): boolean => isId;
 | 
						|
      const ids = types.filter(isId);
 | 
						|
      const nonIds = types.filter((type): boolean => !isId(type));
 | 
						|
      //divider to indicate section for ids
 | 
						|
      const idsDivider = { value: '', label: 'First Party IDs', isDisabled: true };
 | 
						|
      // divider to indicate section for non ids
 | 
						|
      const nonIdsDivider = { value: '', label: 'Non First Party IDs', isDisabled: true };
 | 
						|
 | 
						|
      return [
 | 
						|
        <IComplianceFieldIdentifierOption>idsDivider,
 | 
						|
        ...ids,
 | 
						|
        <IComplianceFieldIdentifierOption>nonIdsDivider,
 | 
						|
        ...nonIds
 | 
						|
      ];
 | 
						|
    };
 | 
						|
 | 
						|
    return [
 | 
						|
      noneDropDownOption,
 | 
						|
      ...insertDividers(getFieldIdentifierOptions(indexedDataTypes.sort(dataTypeComparator)))
 | 
						|
    ];
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * e-c Task to update the current edit step in the wizard flow.
 | 
						|
   * Handles the transitions between steps, including performing each step's
 | 
						|
   * post processing action once a user has completed a step, or reverting the step
 | 
						|
   * and stepping backward if the post process fails
 | 
						|
   * @type {Task<void, (a?: void) => TaskInstance<void>>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  updateEditStepTask = (function() {
 | 
						|
    // initialize the previous action with a no-op function
 | 
						|
    let previousAction = noop;
 | 
						|
    // initialize the last seen index to the same value as editStepIndex
 | 
						|
    let lastIndex = initialStepIndex;
 | 
						|
 | 
						|
    return task(function*(this: DatasetCompliance): IterableIterator<void> {
 | 
						|
      const { editStepIndex: currentIndex, editSteps } = getProperties(this, ['editStepIndex', 'editSteps']);
 | 
						|
      // the current step in the edit sequence
 | 
						|
      const editStep = editSteps[currentIndex] || { name: '' };
 | 
						|
      const { name } = editStep;
 | 
						|
 | 
						|
      if (name) {
 | 
						|
        // using the steps name, construct a reference to the step process handler
 | 
						|
        const nextAction = this.actions[`did${classify(name)}`];
 | 
						|
        let previousActionResult: void;
 | 
						|
 | 
						|
        // if the transition is backward, then the previous action is ignored
 | 
						|
        currentIndex > lastIndex && (previousActionResult = previousAction.call(this));
 | 
						|
        lastIndex = currentIndex;
 | 
						|
 | 
						|
        try {
 | 
						|
          yield previousActionResult;
 | 
						|
          // if the previous action is resolved successfully, then replace with the next processor
 | 
						|
          previousAction = typeof nextAction === 'function' ? nextAction : noop;
 | 
						|
 | 
						|
          set(this, 'editStep', editStep);
 | 
						|
        } catch {
 | 
						|
          // if the previous action settles in a rejected state, replace with no-op before
 | 
						|
          // invoking the previousStep action to go back in the sequence
 | 
						|
          // batch previousStep invocation in a afterRender queue due to editStepIndex update
 | 
						|
          previousAction = noop;
 | 
						|
          run(
 | 
						|
            (): void => {
 | 
						|
              if (this.isDestroyed || this.isDestroying) {
 | 
						|
                return;
 | 
						|
              }
 | 
						|
              schedule('afterRender', this, this.actions.previousStep);
 | 
						|
            }
 | 
						|
          );
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }).enqueue();
 | 
						|
  })();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Holds a reference to the current step in the compliance edit wizard flow
 | 
						|
   * @type {{ name: string }}
 | 
						|
   */
 | 
						|
  editStep: { name: string } = { name: '' };
 | 
						|
 | 
						|
  /**
 | 
						|
   * A list of ui values and labels for review filter drop-down
 | 
						|
   * @type {Array<{value: string, label:string}>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  fieldReviewOptions: Array<{ value: DatasetCompliance['fieldReviewOption']; label: string }> = [
 | 
						|
    { value: TagFilter.showAll, label: 'Show all fields' },
 | 
						|
    { value: TagFilter.showReview, label: 'Show required fields' },
 | 
						|
    { value: TagFilter.showSuggested, label: 'Show suggested fields' }
 | 
						|
  ];
 | 
						|
 | 
						|
  didReceiveAttrs(): void {
 | 
						|
    // Perform validation step on the received component attributes
 | 
						|
    this.validateAttrs();
 | 
						|
 | 
						|
    // Set the current step to first edit step if compliance policy is new / doesn't exist
 | 
						|
    if (get(this, 'isNewComplianceInfo')) {
 | 
						|
      this.updateStep(0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  didInsertElement(): void {
 | 
						|
    get(this, 'complianceAvailabilityTask').perform();
 | 
						|
    get(this, 'columnFieldsToCompliancePolicyTask').perform();
 | 
						|
    get(this, 'foldChangeSetTask').perform();
 | 
						|
  }
 | 
						|
 | 
						|
  didUpdateAttrs(): void {
 | 
						|
    get(this, 'columnFieldsToCompliancePolicyTask').perform();
 | 
						|
    get(this, 'foldChangeSetTask').perform();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Ensure that props received from on this component
 | 
						|
   * are valid, otherwise flag
 | 
						|
   * @returns {boolean | void}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  validateAttrs(this: DatasetCompliance): boolean | void {
 | 
						|
    const fieldNames: Array<string> = getWithDefault(this, 'schemaFieldNamesMappedToDataTypes', []).mapBy('fieldName');
 | 
						|
 | 
						|
    // identifier field names from the column api should be unique
 | 
						|
    if (isListUnique(fieldNames.sort())) {
 | 
						|
      return set(this, '_hasBadData', false);
 | 
						|
    }
 | 
						|
 | 
						|
    // Flag this component's data as problematic
 | 
						|
    set(this, '_hasBadData', true);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Checks that dataset content types have a boolean value
 | 
						|
   * @type {ComputedProperty<boolean>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  isDatasetFullyClassified = computed('datasetClassification', function(this: DatasetCompliance): boolean {
 | 
						|
    const datasetClassification = get(this, 'datasetClassification');
 | 
						|
 | 
						|
    return datasetClassification
 | 
						|
      .map(({ value }) => ({ value: value }))
 | 
						|
      .every(({ value }) => typeof value === 'boolean');
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Checks if any of the attributes on the dataset classification is false
 | 
						|
   * @type {ComputedProperty<boolean>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  excludesSomeMemberData = computed(datasetClassificationKey, function(this: DatasetCompliance): boolean {
 | 
						|
    const { datasetClassification } = get(this, 'complianceInfo') || { datasetClassification: {} };
 | 
						|
 | 
						|
    // `datasetClassification` is nullable hence default
 | 
						|
    return Object.values(datasetClassification || {}).some(hasMemberData => !hasMemberData);
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Determines if all types of data fields should be shown in the table i.e. show only fields contained in
 | 
						|
   * this dataset or otherwise
 | 
						|
   * @type {ComputedProperty<boolean>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  shouldShowAllMemberData = or('showAllDatasetMemberData', 'isEditing');
 | 
						|
 | 
						|
  /**
 | 
						|
   * Determines if the save feature is allowed for the current dataset, otherwise e.g. interface should be disabled
 | 
						|
   * @type {ComputedProperty<boolean>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  isSavingDisabled = computed('isDatasetFullyClassified', 'isSaving', function(this: DatasetCompliance): boolean {
 | 
						|
    const { isDatasetFullyClassified, isSaving } = getProperties(this, ['isDatasetFullyClassified', 'isSaving']);
 | 
						|
 | 
						|
    return !isDatasetFullyClassified || isSaving;
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Checks to ensure the the number of fields added to compliance entities is less than or equal
 | 
						|
   * to what is available on the dataset schema
 | 
						|
   * @return {boolean}
 | 
						|
   */
 | 
						|
  isSchemaFieldLengthGreaterThanUniqComplianceEntities(this: DatasetCompliance): boolean {
 | 
						|
    const complianceInfo = get(this, 'complianceInfo');
 | 
						|
    if (complianceInfo) {
 | 
						|
      const { length: columnFieldsLength } = getWithDefault(this, 'schemaFieldNamesMappedToDataTypes', []);
 | 
						|
      const { length: complianceListLength } = uniqBy(
 | 
						|
        get(complianceInfo, 'complianceEntities') || [],
 | 
						|
        'identifierField'
 | 
						|
      );
 | 
						|
 | 
						|
      return columnFieldsLength >= complianceListLength;
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Computed property that is dependent on all the keys in the datasetClassification map
 | 
						|
   * @type {ComputedProperty<Array<IDatasetClassificationOption>>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  datasetClassification = computed(`${datasetClassificationKey}.{${datasetClassifiersKeys.join(',')}}`, function(
 | 
						|
    this: DatasetCompliance
 | 
						|
  ): Array<IDatasetClassificationOption> {
 | 
						|
    const complianceInfo = get(this, 'complianceInfo');
 | 
						|
    if (complianceInfo) {
 | 
						|
      const { datasetClassification } = complianceInfo;
 | 
						|
 | 
						|
      return datasetClassifiersKeys.sort().reduce((datasetClassifiers, classifier) => {
 | 
						|
        return [
 | 
						|
          ...datasetClassifiers,
 | 
						|
          {
 | 
						|
            classifier,
 | 
						|
            value: datasetClassification ? datasetClassification[classifier] : void 0, // undefined !== false, tri-state
 | 
						|
            label: DatasetClassifiers[classifier]
 | 
						|
          }
 | 
						|
        ];
 | 
						|
      }, []);
 | 
						|
    }
 | 
						|
 | 
						|
    return [];
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Task to retrieve column fields async and set values on Component
 | 
						|
   * @type {Task<Promise<any>, () => TaskInstance<Promise<any>>>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  columnFieldsToCompliancePolicyTask = task(function*(this: DatasetCompliance): IterableIterator<any> {
 | 
						|
    // Truncated list of Dataset field names and data types currently returned from the column endpoint
 | 
						|
    const schemaFieldNamesMappedToDataTypes: DatasetCompliance['schemaFieldNamesMappedToDataTypes'] = yield waitForProperty(
 | 
						|
      this,
 | 
						|
      'schemaFieldNamesMappedToDataTypes',
 | 
						|
      ({ length }) => !!length
 | 
						|
    );
 | 
						|
 | 
						|
    const { complianceEntities = [], modifiedTime }: Pick<IComplianceInfo, 'complianceEntities' | 'modifiedTime'> = get(
 | 
						|
      this,
 | 
						|
      'complianceInfo'
 | 
						|
    )!;
 | 
						|
    const renameFieldNameAttr = ({
 | 
						|
      fieldName,
 | 
						|
      dataType
 | 
						|
    }: Pick<IDatasetColumn, 'dataType' | 'fieldName'>): {
 | 
						|
      identifierField: IDatasetColumn['fieldName'];
 | 
						|
      dataType: IDatasetColumn['dataType'];
 | 
						|
    } => ({
 | 
						|
      identifierField: fieldName,
 | 
						|
      dataType
 | 
						|
    });
 | 
						|
    const columnProps: Array<IColumnFieldProps> = yield iterateArrayAsync(arrayMap(renameFieldNameAttr))(
 | 
						|
      schemaFieldNamesMappedToDataTypes
 | 
						|
    );
 | 
						|
 | 
						|
    const columnIdFieldsToCurrentPrivacyPolicy: ISchemaFieldsToPolicy = yield asyncMapSchemaColumnPropsToCurrentPrivacyPolicy(
 | 
						|
      {
 | 
						|
        columnProps,
 | 
						|
        complianceEntities,
 | 
						|
        policyModificationTime: modifiedTime
 | 
						|
      }
 | 
						|
    );
 | 
						|
 | 
						|
    set(this, 'columnIdFieldsToCurrentPrivacyPolicy', columnIdFieldsToCurrentPrivacyPolicy);
 | 
						|
  }).enqueue();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a mapping of compliance suggestions to identifierField
 | 
						|
   * This improves performance in a subsequent merge op since this loop
 | 
						|
   * happens only once and is cached
 | 
						|
   * @type {ComputedProperty<ISchemaFieldsToSuggested>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  identifierFieldToSuggestion = computed('complianceSuggestion', function(
 | 
						|
    this: DatasetCompliance
 | 
						|
  ): ISchemaFieldsToSuggested {
 | 
						|
    const fieldSuggestions: ISchemaFieldsToSuggested = {};
 | 
						|
    const complianceSuggestion = get(this, 'complianceSuggestion') || {
 | 
						|
      lastModified: 0,
 | 
						|
      suggestedFieldClassification: <Array<ISuggestedFieldClassification>>[]
 | 
						|
    };
 | 
						|
    const { lastModified: suggestionsModificationTime, suggestedFieldClassification = [] } = complianceSuggestion;
 | 
						|
 | 
						|
    // If the compliance suggestions array contains suggestions the create reduced lookup map,
 | 
						|
    // otherwise, ignore
 | 
						|
    if (suggestedFieldClassification.length) {
 | 
						|
      return suggestedFieldClassification.reduce(
 | 
						|
        (
 | 
						|
          fieldSuggestions: ISchemaFieldsToSuggested,
 | 
						|
          { suggestion: { identifierField, identifierType, logicalType, securityClassification }, confidenceLevel, uid }
 | 
						|
        ) => ({
 | 
						|
          ...fieldSuggestions,
 | 
						|
          [identifierField]: {
 | 
						|
            identifierType,
 | 
						|
            logicalType,
 | 
						|
            securityClassification,
 | 
						|
            confidenceLevel,
 | 
						|
            uid,
 | 
						|
            suggestionsModificationTime
 | 
						|
          }
 | 
						|
        }),
 | 
						|
        fieldSuggestions
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    return fieldSuggestions;
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Caches a reference to the generated list of merged data between the column api and the current compliance entities list
 | 
						|
   * @type {ComputedProperty<IComplianceChangeSet>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  compliancePolicyChangeSet = computed(
 | 
						|
    'columnIdFieldsToCurrentPrivacyPolicy',
 | 
						|
    'complianceDataTypes',
 | 
						|
    'identifierFieldToSuggestion',
 | 
						|
    'suggestionConfidenceThreshold',
 | 
						|
    function(this: DatasetCompliance): Array<IComplianceChangeSet> {
 | 
						|
      // schemaFieldNamesMappedToDataTypes is a dependency for CP columnIdFieldsToCurrentPrivacyPolicy, so no need to dep on that directly
 | 
						|
      const changeSet = mergeComplianceEntitiesWithSuggestions(
 | 
						|
        get(this, 'columnIdFieldsToCurrentPrivacyPolicy'),
 | 
						|
        get(this, 'identifierFieldToSuggestion')
 | 
						|
      );
 | 
						|
      const suggestionThreshold = get(this, 'suggestionConfidenceThreshold');
 | 
						|
 | 
						|
      // pass current changeSet state to parent handlers
 | 
						|
      run(() => next(this, 'notifyHandlerOfSuggestions', suggestionThreshold, changeSet));
 | 
						|
      run(() =>
 | 
						|
        next(
 | 
						|
          this,
 | 
						|
          'notifyHandlerOfFieldsRequiringReview',
 | 
						|
          suggestionThreshold,
 | 
						|
          get(this, 'complianceDataTypes'),
 | 
						|
          changeSet
 | 
						|
        )
 | 
						|
      );
 | 
						|
 | 
						|
      return changeSet;
 | 
						|
    }
 | 
						|
  );
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns a list of changeSet fields that meets the user selected filter criteria
 | 
						|
   * @type {ComputedProperty<IComplianceChangeSet>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  filteredChangeSet = computed(
 | 
						|
    'changeSetReviewCount',
 | 
						|
    'fieldReviewOption',
 | 
						|
    'compliancePolicyChangeSet',
 | 
						|
    'complianceDataTypes',
 | 
						|
    'suggestionConfidenceThreshold',
 | 
						|
    function(this: DatasetCompliance): Array<IComplianceChangeSet> {
 | 
						|
      /**
 | 
						|
       * Aliases the index signature for a hash of callback functions keyed by TagFilter
 | 
						|
       * to filter out compliance changeset items
 | 
						|
       * @alias
 | 
						|
       */
 | 
						|
      type TagFilterCallback<T = Array<IComplianceChangeSet>> = { [K in TagFilter]: (x: T) => T };
 | 
						|
 | 
						|
      const {
 | 
						|
        compliancePolicyChangeSet: changeSet,
 | 
						|
        complianceDataTypes,
 | 
						|
        suggestionConfidenceThreshold
 | 
						|
      } = getProperties(this, ['compliancePolicyChangeSet', 'complianceDataTypes', 'suggestionConfidenceThreshold']);
 | 
						|
 | 
						|
      // references the filter predicate for changeset items based on the currently set tag filter
 | 
						|
      const changeSetFilter = (<TagFilterCallback>{
 | 
						|
        [TagFilter.showAll]: identity,
 | 
						|
        [TagFilter.showReview]: tagsRequiringReview(complianceDataTypes, {
 | 
						|
          checkSuggestions: false,
 | 
						|
          suggestionConfidenceThreshold
 | 
						|
        }),
 | 
						|
        [TagFilter.showSuggested]: arrayFilter((tag: IComplianceChangeSet) =>
 | 
						|
          tagSuggestionNeedsReview({ ...tag, suggestionConfidenceThreshold })
 | 
						|
        )
 | 
						|
      })[get(this, 'fieldReviewOption')];
 | 
						|
 | 
						|
      return changeSetFilter(changeSet);
 | 
						|
    }
 | 
						|
  );
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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<Array<IComplianceChangeSet>>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  changeSetReviewWithoutSuggestionCheck = computed('changeSetReview', function(
 | 
						|
    this: DatasetCompliance
 | 
						|
  ): Array<IComplianceChangeSet> {
 | 
						|
    return tagsRequiringReview(get(this, 'complianceDataTypes'), {
 | 
						|
      checkSuggestions: false,
 | 
						|
      suggestionConfidenceThreshold: 0 // irrelevant value set to 0 since checkSuggestions flag is false above
 | 
						|
    })(get(this, 'changeSetReview'));
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * The changeSet tags that require user attention
 | 
						|
   * @type {ComputedProperty<Array<IComplianceChangeSet>>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  changeSetReview = computed(
 | 
						|
    `compliancePolicyChangeSet.@each.{${changeSetReviewableAttributeTriggers}}`,
 | 
						|
    'complianceDataTypes',
 | 
						|
    'suggestionConfidenceThreshold',
 | 
						|
    function(this: DatasetCompliance): Array<IComplianceChangeSet> {
 | 
						|
      const { suggestionConfidenceThreshold, compliancePolicyChangeSet } = getProperties(this, [
 | 
						|
        'suggestionConfidenceThreshold',
 | 
						|
        'compliancePolicyChangeSet'
 | 
						|
      ]);
 | 
						|
 | 
						|
      return tagsRequiringReview(get(this, 'complianceDataTypes'), {
 | 
						|
        checkSuggestions: true,
 | 
						|
        suggestionConfidenceThreshold
 | 
						|
      })(compliancePolicyChangeSet);
 | 
						|
    }
 | 
						|
  );
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns a count of changeSet tags that require user attention
 | 
						|
   * @type {ComputedProperty<number>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  changeSetReviewCount = alias('changeSetReview.length');
 | 
						|
 | 
						|
  /**
 | 
						|
   * Reduces the current filtered changeSet to a list of IdentifierFieldWithFieldChangeSetTuple
 | 
						|
   * @type {Array<IdentifierFieldWithFieldChangeSetTuple>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  foldedChangeSet: Array<IdentifierFieldWithFieldChangeSetTuple>;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Task to retrieve platform policies and set supported policies for the current platform
 | 
						|
   * @type {Task<Promise<any>, () => TaskInstance<Promise<any>>>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  foldChangeSetTask = task(function*(this: DatasetCompliance): IterableIterator<any> {
 | 
						|
    //@ts-ignore dot notation for property access
 | 
						|
    yield waitForProperty(this, 'columnFieldsToCompliancePolicyTask.isIdle');
 | 
						|
    const filteredChangeSet = get(this, 'filteredChangeSet');
 | 
						|
    const foldedChangeSet: Array<IdentifierFieldWithFieldChangeSetTuple> = yield foldComplianceChangeSets(
 | 
						|
      filteredChangeSet
 | 
						|
    );
 | 
						|
 | 
						|
    set(this, 'foldedChangeSet', sortFoldedChangeSetTuples(foldedChangeSet));
 | 
						|
  }).enqueue();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Lists the IComplianceChangeSet / tags without an identifierType value
 | 
						|
   * @type {ComputedProperty<Array<IComplianceChangeSet>>}
 | 
						|
   * @memberof DatasetCompliance
 | 
						|
   */
 | 
						|
  unspecifiedTags = computed(`compliancePolicyChangeSet.@each.{${changeSetReviewableAttributeTriggers}}`, function(
 | 
						|
    this: DatasetCompliance
 | 
						|
  ): Array<IComplianceChangeSet> {
 | 
						|
    const tags = get(this, 'compliancePolicyChangeSet');
 | 
						|
    const singleTags = singleTagsInChangeSet(tags, tagsForIdentifierField);
 | 
						|
 | 
						|
    return tagsWithoutIdentifierType(singleTags);
 | 
						|
  });
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the identifierType attribute on IComplianceChangeSetFields without an identifierType to ComplianceFieldIdValue.None
 | 
						|
   * @returns {Promise<Array<IComplianceChangeSet>>}
 | 
						|
   */
 | 
						|
  setUnspecifiedTagsAsNoneTask = task(function*(
 | 
						|
    this: DatasetCompliance
 | 
						|
  ): IterableIterator<Promise<Array<ComplianceFieldIdValue | NonIdLogicalType>>> {
 | 
						|
    const unspecifiedTags = get(this, 'unspecifiedTags');
 | 
						|
    // const setTagIdentifier = (value: ComplianceFieldIdValue | NonIdLogicalType) => (tag: IComplianceChangeSet) =>
 | 
						|
    //   set(tag, 'identifierType', value);
 | 
						|
 | 
						|
    // yield iterateArrayAsync(arrayMap(setTagIdentifier(ComplianceFieldIdValue.None)))(unspecifiedTags);
 | 
						|
    unspecifiedTags.forEach(tag => {
 | 
						|
      set(tag, 'identifierType', ComplianceFieldIdValue.None);
 | 
						|
    });
 | 
						|
  }).drop();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Invokes external action with flag indicating that at least 1 suggestion exists for a field in the changeSet
 | 
						|
   * @param {number} suggestionConfidenceThreshold confidence threshold for filtering out higher quality suggestions
 | 
						|
   * @param {Array<IComplianceChangeSet>} changeSet
 | 
						|
   */
 | 
						|
  notifyHandlerOfSuggestions = (
 | 
						|
    suggestionConfidenceThreshold: number,
 | 
						|
    changeSet: Array<IComplianceChangeSet>
 | 
						|
  ): void => {
 | 
						|
    const hasChangeSetSuggestions = !!compact(getTagsSuggestions({ suggestionConfidenceThreshold })(changeSet)).length;
 | 
						|
    this.notifyOnChangeSetSuggestions(hasChangeSetSuggestions);
 | 
						|
  };
 | 
						|
 | 
						|
  /**
 | 
						|
   * Invokes external action with flag indicating that a field in the tags requires user review
 | 
						|
   * @param {number} suggestionConfidenceThreshold confidence threshold for filtering out higher quality suggestions
 | 
						|
   * @param {Array<IComplianceDataType>} complianceDataTypes
 | 
						|
   * @param {Array<IComplianceChangeSet>} tags
 | 
						|
   */
 | 
						|
  notifyHandlerOfFieldsRequiringReview = (
 | 
						|
    suggestionConfidenceThreshold: number,
 | 
						|
    complianceDataTypes: Array<IComplianceDataType>,
 | 
						|
    tags: Array<IComplianceChangeSet>
 | 
						|
  ): void => {
 | 
						|
    // adding assertions for run-loop callback invocation, because static type checks are bypassed
 | 
						|
    assert('expected complianceDataTypes to be of type `array`', Array.isArray(complianceDataTypes));
 | 
						|
    assert('expected tags to be of type `array`', Array.isArray(tags));
 | 
						|
 | 
						|
    const hasChangeSetDrift = !!tagsRequiringReview(complianceDataTypes, {
 | 
						|
      checkSuggestions: true,
 | 
						|
      suggestionConfidenceThreshold
 | 
						|
    })(tags).length;
 | 
						|
 | 
						|
    this.notifyOnChangeSetRequiresReview(hasChangeSetDrift);
 | 
						|
  };
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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
 | 
						|
   */
 | 
						|
  setDefaultClassification(
 | 
						|
    this: DatasetCompliance,
 | 
						|
    { identifierField, identifierType }: Pick<IComplianceEntity, 'identifierField' | 'identifierType'>
 | 
						|
  ): void {
 | 
						|
    const complianceDataTypes = get(this, 'complianceDataTypes');
 | 
						|
    const defaultSecurityClassification = getDefaultSecurityClassification(complianceDataTypes, identifierType);
 | 
						|
 | 
						|
    this.actions.tagClassificationChanged.call(this, { identifierField }, { value: defaultSecurityClassification });
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Maps attributes from the working copy to the compliance entities to be persisted remotely
 | 
						|
   * @returns {Promise<Array<IComplianceEntity>>}
 | 
						|
   */
 | 
						|
  async applyWorkingCopy(this: DatasetCompliance): Promise<Array<IComplianceEntity>> {
 | 
						|
    // Current list of compliance entities on policy
 | 
						|
    const { complianceInfo, compliancePolicyChangeSet: workingCopy } = getProperties(this, [
 | 
						|
      'complianceInfo',
 | 
						|
      'compliancePolicyChangeSet'
 | 
						|
    ]);
 | 
						|
    const { complianceEntities } = complianceInfo!;
 | 
						|
    // All changeSet attrs that can be on policy, excluding changeSet metadata
 | 
						|
    const entityAttrs: Array<keyof IComplianceEntity> = [
 | 
						|
      'identifierField',
 | 
						|
      'identifierType',
 | 
						|
      'logicalType',
 | 
						|
      'nonOwner',
 | 
						|
      'securityClassification',
 | 
						|
      'readonly',
 | 
						|
      'valuePattern'
 | 
						|
    ];
 | 
						|
    const updatingComplianceEntities = arrayMap(
 | 
						|
      (tag: IComplianceChangeSet): IComplianceEntity => pick(tag, entityAttrs)
 | 
						|
    )(workingCopy);
 | 
						|
 | 
						|
    return complianceEntities.setObjects(updatingComplianceEntities);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Ensures the fields in the updated list of compliance entities meet the criteria
 | 
						|
   * checked in the function. If criteria is not met, an the returned promise is settled
 | 
						|
   * in a rejected state, otherwise fulfilled
 | 
						|
   * @method
 | 
						|
   * @return {Promise<never | void>}
 | 
						|
   */
 | 
						|
  async validateFields(this: DatasetCompliance): Promise<never | void> {
 | 
						|
    const { notify } = get(this, 'notifications');
 | 
						|
    const { complianceEntities = [] } = get(this, 'complianceInfo') || {};
 | 
						|
    const idTypeComplianceEntities = complianceEntities.filter(isTagIdType(get(this, 'complianceDataTypes')));
 | 
						|
 | 
						|
    // Validation operations
 | 
						|
    const idFieldsHaveValidLogicalType: boolean = idTypeTagsHaveLogicalType(idTypeComplianceEntities);
 | 
						|
    const isSchemaFieldLengthGreaterThanUniqComplianceEntities: boolean = this.isSchemaFieldLengthGreaterThanUniqComplianceEntities();
 | 
						|
 | 
						|
    if (!isSchemaFieldLengthGreaterThanUniqComplianceEntities) {
 | 
						|
      notify(NotificationEvent.error, { content: complianceDataException });
 | 
						|
      return Promise.reject(new Error(complianceFieldNotUnique));
 | 
						|
    }
 | 
						|
 | 
						|
    if (!idFieldsHaveValidLogicalType) {
 | 
						|
      return Promise.reject(notify(NotificationEvent.error, { content: missingTypes }));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Gets a reference to the current dataset classification object
 | 
						|
   */
 | 
						|
  getDatasetClassificationRef(this: DatasetCompliance): DatasetClassification {
 | 
						|
    const complianceInfo = get(this, 'complianceInfo');
 | 
						|
 | 
						|
    if (!complianceInfo) {
 | 
						|
      return <DatasetClassification>{};
 | 
						|
    }
 | 
						|
 | 
						|
    let { datasetClassification } = complianceInfo;
 | 
						|
 | 
						|
    // For datasets initially without a datasetClassification, the default value is null
 | 
						|
    if (datasetClassification === null) {
 | 
						|
      datasetClassification = set(complianceInfo, 'datasetClassification', <DatasetClassification>{});
 | 
						|
    }
 | 
						|
 | 
						|
    return datasetClassification;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Display a modal dialog requesting that the user check affirm that the purge type is exempt
 | 
						|
   * @return {Promise<void>}
 | 
						|
   */
 | 
						|
  showPurgeExemptionWarning(this: DatasetCompliance): Promise<void> {
 | 
						|
    const { dialogActions, dismissedOrConfirmed } = notificationDialogActionFactory();
 | 
						|
 | 
						|
    get(this, 'notifications').notify(NotificationEvent.confirm, {
 | 
						|
      header: 'Confirm purge exemption',
 | 
						|
      content:
 | 
						|
        'By choosing this option you understand that either Legal or HSEC may contact you to verify the purge exemption',
 | 
						|
      dialogActions
 | 
						|
    });
 | 
						|
 | 
						|
    return dismissedOrConfirmed;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Notifies the user to provide a missing purge policy
 | 
						|
   * @return {Promise<never>}
 | 
						|
   */
 | 
						|
  needsPurgePolicyType(this: DatasetCompliance): Promise<never> {
 | 
						|
    return Promise.reject(get(this, 'notifications').notify(NotificationEvent.error, { content: missingPurgePolicy }));
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Updates the currently active step in the edit sequence
 | 
						|
   * @param {number} step
 | 
						|
   */
 | 
						|
  updateStep(this: DatasetCompliance, step: number): void {
 | 
						|
    set(this, 'editStepIndex', step);
 | 
						|
    get(this, 'updateEditStepTask').perform();
 | 
						|
  }
 | 
						|
 | 
						|
  actions: IDatasetComplianceActions = {
 | 
						|
    /**
 | 
						|
     * Toggle the visibility of the guided compliance edit view vs the advanced edit view modes
 | 
						|
     * @param {boolean} toggle flag ,if true, show guided edit mode, otherwise, advanced
 | 
						|
     */
 | 
						|
    onShowGuidedEditMode(this: DatasetCompliance, toggle: boolean): void {
 | 
						|
      const isShowingGuidedEditMode = set(this, 'showGuidedComplianceEditMode', toggle);
 | 
						|
 | 
						|
      if (!isShowingGuidedEditMode) {
 | 
						|
        this.actions.onManualComplianceUpdate.call(this, get(this, 'jsonComplianceEntities'));
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Handles updating the list of compliance entities when a user manually enters values
 | 
						|
     * for the compliance entity metadata
 | 
						|
     * @param {string} updatedEntities json string of entities
 | 
						|
     */
 | 
						|
    onManualComplianceUpdate(this: DatasetCompliance, updatedEntities: string): void {
 | 
						|
      try {
 | 
						|
        // check if the string is parse-able as a JSON object
 | 
						|
        const entities = JSON.parse(updatedEntities);
 | 
						|
        const metadataObject = {
 | 
						|
          complianceEntities: entities
 | 
						|
        };
 | 
						|
        // Check that metadataObject has a valid property matching complianceEntitiesTaxonomy
 | 
						|
        let isValid = isMetadataObject(metadataObject);
 | 
						|
 | 
						|
        // Lists the fieldNames / identifierField property values on the edit compliance policy
 | 
						|
        const updatedIdentifierFieldValues = new Set(
 | 
						|
          arrayMap(({ identifierField }: IComplianceEntity) => identifierField)(entities)
 | 
						|
        );
 | 
						|
        // Lists the expected fieldNames / identifierField property values from the schemaFieldNamesMappedToDataTypes
 | 
						|
        const expectedIdentifierFieldValues = arrayMap(
 | 
						|
          ({ fieldName }: Pick<IDatasetColumn, 'dataType' | 'fieldName'>) => fieldName
 | 
						|
        )(get(this, 'schemaFieldNamesMappedToDataTypes'));
 | 
						|
 | 
						|
        isValid = isValid && jsonValuesMatch([...updatedIdentifierFieldValues], expectedIdentifierFieldValues);
 | 
						|
 | 
						|
        setProperties(this, { isManualApplyDisabled: !isValid, manualParseError: '' });
 | 
						|
 | 
						|
        if (isValid) {
 | 
						|
          set(this, 'manuallyEnteredComplianceEntities', metadataObject);
 | 
						|
        }
 | 
						|
      } catch (e) {
 | 
						|
        setProperties(this, { isManualApplyDisabled: true, manualParseError: e.message });
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Handler to apply manually entered compliance entities to the actual list of
 | 
						|
     * compliance metadata entities to be saved
 | 
						|
     */
 | 
						|
    async onApplyComplianceJson(this: DatasetCompliance) {
 | 
						|
      try {
 | 
						|
        await get(this, 'onComplianceJsonUpdate')(JSON.stringify(get(this, 'manuallyEnteredComplianceEntities')));
 | 
						|
        // Proceed to next step if application of entities is successful
 | 
						|
        this.actions.nextStep.call(this);
 | 
						|
      } catch {
 | 
						|
        noop();
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Action handles wizard step cancellation
 | 
						|
     */
 | 
						|
    onCancel(this: DatasetCompliance): void {
 | 
						|
      this.updateStep(initialStepIndex);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Toggles the flag isShowingFullFieldNames when invoked
 | 
						|
     */
 | 
						|
    onFieldDblClick(): void {
 | 
						|
      this.toggleProperty('isShowingFullFieldNames');
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * 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): void {
 | 
						|
      get(this, 'compliancePolicyChangeSet').addObject(tag);
 | 
						|
      get(this, 'foldChangeSetTask').perform();
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Removes a field tag from the list of compliance change set items
 | 
						|
     * @param {IComplianceChangeSet} tag
 | 
						|
     * @return {IComplianceChangeSet}
 | 
						|
     */
 | 
						|
    onFieldTagRemoved(this: DatasetCompliance, tag: IComplianceChangeSet): void {
 | 
						|
      get(this, 'compliancePolicyChangeSet').removeObject(tag);
 | 
						|
      get(this, 'foldChangeSetTask').perform();
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Disables the readonly attribute of a compliance policy changeSet tag,
 | 
						|
     * allowing the user to override properties on the tag
 | 
						|
     * @param {IComplianceChangeSet} tag the IComplianceChangeSet instance
 | 
						|
     */
 | 
						|
    async onTagReadOnlyDisable(this: DatasetCompliance, tag: IComplianceChangeSet): Promise<void> {
 | 
						|
      const { dialogActions, dismissedOrConfirmed } = notificationDialogActionFactory();
 | 
						|
      const {
 | 
						|
        doNotShowReadonlyConfirmation,
 | 
						|
        notifications: { notify }
 | 
						|
      } = getProperties(this, ['doNotShowReadonlyConfirmation', 'notifications']);
 | 
						|
 | 
						|
      if (doNotShowReadonlyConfirmation) {
 | 
						|
        overrideTagReadonly(tag);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      notify(NotificationEvent.confirm, {
 | 
						|
        header: 'Are you sure you would like to modify this field?',
 | 
						|
        content:
 | 
						|
          "This field's compliance information is currently readonly, please confirm if you would like to override this value",
 | 
						|
        dialogActions,
 | 
						|
        toggleText: 'Do not show this again for this dataset',
 | 
						|
        onDialogToggle: (doNotShow: boolean): boolean => set(this, 'doNotShowReadonlyConfirmation', doNotShow)
 | 
						|
      });
 | 
						|
 | 
						|
      try {
 | 
						|
        await dismissedOrConfirmed;
 | 
						|
        overrideTagReadonly(tag);
 | 
						|
      } catch (e) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Applies wholesale user changes to a field tag's properties
 | 
						|
     * @param {IComplianceChangeSet} tag a reference to the current tag object
 | 
						|
     * @param {IComplianceChangeSet} tagUpdates updated properties to be applied to the current tag
 | 
						|
     */
 | 
						|
    tagPropertiesUpdated(tag: IComplianceChangeSet, tagUpdates: IComplianceChangeSet) {
 | 
						|
      setProperties(tag, tagUpdates);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * When a user updates the identifierFieldType, update working copy
 | 
						|
     * @param {IComplianceChangeSet} tag
 | 
						|
     * @param {ComplianceFieldIdValue} identifierType
 | 
						|
     */
 | 
						|
    tagIdentifierChanged(
 | 
						|
      this: DatasetCompliance,
 | 
						|
      tag: IComplianceChangeSet,
 | 
						|
      { value: identifierType }: { value: ComplianceFieldIdValue }
 | 
						|
    ): void {
 | 
						|
      const { identifierField } = tag;
 | 
						|
      if (tag) {
 | 
						|
        setProperties(tag, {
 | 
						|
          identifierType,
 | 
						|
          logicalType: null,
 | 
						|
          nonOwner: null,
 | 
						|
          isDirty: true,
 | 
						|
          valuePattern: null
 | 
						|
        });
 | 
						|
      }
 | 
						|
 | 
						|
      this.setDefaultClassification({ identifierField, identifierType });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * 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(
 | 
						|
      tag: IComplianceChangeSet,
 | 
						|
      { value: securityClassification = null }: { value: IComplianceChangeSet['securityClassification'] }
 | 
						|
    ): void {
 | 
						|
      setProperties(tag, {
 | 
						|
        securityClassification,
 | 
						|
        isDirty: true
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Sets each datasetClassification value as false
 | 
						|
     * @returns {Promise<DatasetClassification>}
 | 
						|
     */
 | 
						|
    async markDatasetAsNotContainingMemberData(this: DatasetCompliance): Promise<DatasetClassification | void> {
 | 
						|
      const { dialogActions, dismissedOrConfirmed: confirmMarkAllSettler } = notificationDialogActionFactory();
 | 
						|
      let willMarkAllAsNo = true;
 | 
						|
 | 
						|
      get(this, 'notifications').notify(NotificationEvent.confirm, {
 | 
						|
        content: 'Are you sure this dataset does not contain any of the listed types of data?',
 | 
						|
        header: 'Dataset contains no listed types of data',
 | 
						|
        dialogActions
 | 
						|
      });
 | 
						|
 | 
						|
      try {
 | 
						|
        await confirmMarkAllSettler;
 | 
						|
      } catch (e) {
 | 
						|
        willMarkAllAsNo = false;
 | 
						|
      }
 | 
						|
 | 
						|
      if (willMarkAllAsNo) {
 | 
						|
        return <DatasetClassification>(
 | 
						|
          setProperties(
 | 
						|
            this.getDatasetClassificationRef(),
 | 
						|
            datasetClassifiersKeys.reduce(
 | 
						|
              (classification, classifier): {} => ({ ...classification, ...{ [classifier]: false } }),
 | 
						|
              {}
 | 
						|
            )
 | 
						|
          )
 | 
						|
        );
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Toggles the flag to show all member potential member data fields that may be contained in this dataset
 | 
						|
     * @returns {boolean}
 | 
						|
     */
 | 
						|
    onShowAllDatasetMemberData(this: DatasetCompliance): boolean {
 | 
						|
      return this.toggleProperty('showAllDatasetMemberData');
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Updates the fieldReviewOption with the user selected value
 | 
						|
     * @param {{value: TagFilter}} { value }
 | 
						|
     * @returns {TagFilter}
 | 
						|
     */
 | 
						|
    onFieldReviewChange(this: DatasetCompliance, { value }: { value: TagFilter }): TagFilter {
 | 
						|
      const option = set(this, 'fieldReviewOption', value);
 | 
						|
      get(this, 'foldChangeSetTask').perform();
 | 
						|
 | 
						|
      return option;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Progresses 1 step backward in the edit sequence
 | 
						|
     */
 | 
						|
    previousStep(this: DatasetCompliance): void {
 | 
						|
      const editStepIndex = get(this, 'editStepIndex');
 | 
						|
      const previousIndex = editStepIndex > 0 ? editStepIndex - 1 : editStepIndex;
 | 
						|
      this.updateStep(previousIndex);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Progresses 1 step forward in the edit sequence
 | 
						|
     */
 | 
						|
    nextStep(this: DatasetCompliance): void {
 | 
						|
      const { editStepIndex, editSteps } = getProperties(this, ['editStepIndex', 'editSteps']);
 | 
						|
      const nextIndex = editStepIndex < editSteps.length - 1 ? editStepIndex + 1 : editStepIndex;
 | 
						|
      this.updateStep(nextIndex);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Handler applies fields changeSet working copy to compliance policy to be persisted amd validates fields
 | 
						|
     * @returns {Promise<void>}
 | 
						|
     */
 | 
						|
    async didEditCompliancePolicy(this: DatasetCompliance): Promise<void> {
 | 
						|
      // Ensure that the fields on the policy meet the validation criteria before proceeding
 | 
						|
      // Otherwise exit early
 | 
						|
      try {
 | 
						|
        await this.applyWorkingCopy();
 | 
						|
        await this.validateFields();
 | 
						|
      } catch (e) {
 | 
						|
        // Flag this dataset's data as problematic
 | 
						|
        if (e instanceof Error && [complianceDataException, complianceFieldNotUnique].includes(e.message)) {
 | 
						|
          set(this, '_hasBadData', true);
 | 
						|
          window.scrollTo(0, 0);
 | 
						|
        }
 | 
						|
 | 
						|
        throw e;
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * 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;
 | 
						|
 | 
						|
        // defaults the containing personal data flag to false if undefined
 | 
						|
        if (typeof containingPersonalData !== 'boolean') {
 | 
						|
          set(complianceInfo, 'containingPersonalData', false);
 | 
						|
        }
 | 
						|
 | 
						|
        if (!confidentiality) {
 | 
						|
          get(this, 'notifications').notify(NotificationEvent.error, {
 | 
						|
            content: missingDatasetSecurityClassification
 | 
						|
          });
 | 
						|
 | 
						|
          return Promise.reject(new Error(missingDatasetSecurityClassification));
 | 
						|
        }
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Handles post processing tasks after the purge policy step has been completed
 | 
						|
     * @returns {(Promise<void>)}
 | 
						|
     */
 | 
						|
    didEditPurgePolicy(this: DatasetCompliance): Promise<void> {
 | 
						|
      const { complianceType = null } = get(this, 'complianceInfo') || {};
 | 
						|
 | 
						|
      if (!complianceType) {
 | 
						|
        return this.needsPurgePolicyType();
 | 
						|
      }
 | 
						|
 | 
						|
      if (isExempt(complianceType)) {
 | 
						|
        return this.showPurgeExemptionWarning();
 | 
						|
      }
 | 
						|
 | 
						|
      return Promise.resolve();
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Augments the tag props with a suggestionAuthority indicating that the tag
 | 
						|
     * suggestion has either been accepted or ignored, and assigns the value of that change to the prop
 | 
						|
     * @param {IComplianceChangeSet} tag tag for which this suggestion intent should apply
 | 
						|
     * @param {SuggestionIntent} [intent=SuggestionIntent.ignore] user's intended action for suggestion, Defaults to `ignore`
 | 
						|
     */
 | 
						|
    onFieldSuggestionIntentChange(
 | 
						|
      this: DatasetCompliance,
 | 
						|
      tag: IComplianceChangeSet,
 | 
						|
      intent: SuggestionIntent = SuggestionIntent.ignore
 | 
						|
    ): void {
 | 
						|
      set(tag, 'suggestionAuthority', intent);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Receives the json representation for compliance and applies each key to the policy
 | 
						|
     * @param {string} jsonString string representation for the JSON file
 | 
						|
     */
 | 
						|
    onComplianceJsonUpload(this: DatasetCompliance, jsonString: string): void {
 | 
						|
      get(this, 'onComplianceJsonUpdate')(jsonString);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Updates the source object representing the current datasetClassification map
 | 
						|
     * @param {keyof typeof DatasetClassifiers} classifier the property on the datasetClassification to update
 | 
						|
     * @param {boolean} value
 | 
						|
     * @returns
 | 
						|
     */
 | 
						|
    onChangeDatasetClassification<K extends keyof typeof DatasetClassifiers>(
 | 
						|
      this: DatasetCompliance,
 | 
						|
      classifier: K,
 | 
						|
      value: DatasetClassification[K]
 | 
						|
    ): DatasetClassification[K] {
 | 
						|
      return set(this.getDatasetClassificationRef(), classifier, value);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Updates the complianceType on the compliance policy
 | 
						|
     * @param {PurgePolicy} purgePolicy
 | 
						|
     * @returns {IComplianceInfo.complianceType}
 | 
						|
     */
 | 
						|
    onDatasetPurgePolicyChange(
 | 
						|
      this: DatasetCompliance,
 | 
						|
      purgePolicy: PurgePolicy
 | 
						|
    ): IComplianceInfo['complianceType'] | null {
 | 
						|
      const complianceInfo = get(this, 'complianceInfo');
 | 
						|
 | 
						|
      if (!complianceInfo) {
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      // directly set the complianceType to the updated value
 | 
						|
      return set(complianceInfo, 'complianceType', purgePolicy);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Updates the policy flag indicating that this dataset contains personal data
 | 
						|
     * @param {boolean} containsPersonalData
 | 
						|
     * @returns {boolean}
 | 
						|
     */
 | 
						|
    onDatasetLevelPolicyChange(this: DatasetCompliance, containsPersonalData: boolean): boolean | null {
 | 
						|
      const complianceInfo = get(this, 'complianceInfo');
 | 
						|
      // directly mutate the attribute on the complianceInfo object
 | 
						|
      return complianceInfo ? set(complianceInfo, 'containingPersonalData', containsPersonalData) : null;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Updates the confidentiality flag on the dataset compliance
 | 
						|
     * @param {IComplianceInfo.confidentiality} [securityClassification=null]
 | 
						|
     * @returns {IComplianceInfo.confidentiality}
 | 
						|
     */
 | 
						|
    onDatasetSecurityClassificationChange(
 | 
						|
      this: DatasetCompliance,
 | 
						|
      securityClassification: IComplianceInfo['confidentiality'] = null
 | 
						|
    ): IComplianceInfo['confidentiality'] {
 | 
						|
      const complianceInfo = get(this, 'complianceInfo');
 | 
						|
 | 
						|
      return complianceInfo ? set(complianceInfo, 'confidentiality', securityClassification) : null;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * If all validity checks are passed, invoke onSave action on controller
 | 
						|
     */
 | 
						|
    async saveCompliance(this: DatasetCompliance): Promise<void> {
 | 
						|
      const setSaveFlag = (flag = false): boolean => set(this, 'isSaving', flag);
 | 
						|
 | 
						|
      try {
 | 
						|
        const isSaving = true;
 | 
						|
        const onSave = get(this, 'onSave');
 | 
						|
        setSaveFlag(isSaving);
 | 
						|
 | 
						|
        await onSave();
 | 
						|
        return this.updateStep(-1);
 | 
						|
      } finally {
 | 
						|
        setSaveFlag();
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    // Rolls back changes made to the compliance spec to current
 | 
						|
    // server state
 | 
						|
    resetCompliance(): void {
 | 
						|
      get(this, 'onReset')();
 | 
						|
    }
 | 
						|
  };
 | 
						|
}
 |