Merge pull request #1296 from theseyi/identifier-field-match

validates the identifierField values against the expected schema fiel…
This commit is contained in:
Seyi Adebajo 2018-08-06 11:21:57 -07:00 committed by GitHub
commit 11f2753720
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 23 deletions

View File

@ -66,13 +66,11 @@ import {
import { uniqBy } from 'lodash';
import { IColumnFieldProps } from 'wherehows-web/typings/app/dataset-columns';
import { NonIdLogicalType } from 'wherehows-web/constants/datasets/compliance';
import { pick } from 'lodash';
import { trackableEvent, TrackableEventCategory } from 'wherehows-web/constants/analytics/event-tracking';
import { notificationDialogActionFactory } from 'wherehows-web/utils/notifications/notifications';
import validateMetadataObject, {
complianceEntitiesTaxonomy
} from 'wherehows-web/utils/datasets/compliance/metadata-schema';
import { isMetadataObject, jsonValuesMatch } from 'wherehows-web/utils/datasets/compliance/metadata-schema';
import { typeOf } from '@ember/utils';
import { pick } from 'wherehows-web/utils/object';
const {
complianceDataException,
@ -154,7 +152,14 @@ export default class DatasetCompliance extends Component {
jsonComplianceEntities: ComputedProperty<string> = computed('columnIdFieldsToCurrentPrivacyPolicy', function(
this: DatasetCompliance
): string {
const entityAttrs = ['identifierField', 'identifierType', 'logicalType', 'nonOwner', 'valuePattern', 'readonly'];
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))
@ -472,12 +477,14 @@ export default class DatasetCompliance extends Component {
// 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;
run(
(): void => {
if (this.isDestroyed || this.isDestroying) {
return;
}
schedule('afterRender', this, this.actions.previousStep);
}
schedule('afterRender', this, this.actions.previousStep);
});
);
}
}
}).enqueue();
@ -979,7 +986,7 @@ export default class DatasetCompliance extends Component {
]);
const { complianceEntities } = complianceInfo!;
// All changeSet attrs that can be on policy, excluding changeSet metadata
const entityAttrs = [
const entityAttrs: Array<keyof IComplianceEntity> = [
'identifierField',
'identifierType',
'logicalType',
@ -989,7 +996,7 @@ export default class DatasetCompliance extends Component {
'valuePattern'
];
const updatingComplianceEntities = arrayMap(
(tag: IComplianceChangeSet): IComplianceEntity => <IComplianceEntity>pick(tag, entityAttrs)
(tag: IComplianceChangeSet): IComplianceEntity => pick(tag, entityAttrs)
)(workingCopy);
return complianceEntities.setObjects(updatingComplianceEntities);
@ -1095,12 +1102,24 @@ export default class DatasetCompliance extends Component {
*/
onManualComplianceUpdate(this: DatasetCompliance, updatedEntities: string): void {
try {
// check if the string is parseable as a JSON object
// check if the string is parse-able as a JSON object
const entities = JSON.parse(updatedEntities);
const metadataObject = {
complianceEntities: entities
};
const isValid = validateMetadataObject(metadataObject, complianceEntitiesTaxonomy);
// 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 = 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: '' });
@ -1119,7 +1138,7 @@ export default class DatasetCompliance extends Component {
async onApplyComplianceJson(this: DatasetCompliance) {
try {
await get(this, 'onComplianceJsonUpdate')(JSON.stringify(get(this, 'manuallyEnteredComplianceEntities')));
// Proceed to next step if application of entites is successful
// Proceed to next step if application of entities is successful
this.actions.nextStep.call(this);
} catch {
noop();
@ -1263,11 +1282,13 @@ export default class DatasetCompliance extends Component {
}
if (willMarkAllAsNo) {
return <DatasetClassification>setProperties(
this.getDatasetClassificationRef(),
datasetClassifiersKeys.reduce(
(classification, classifier): {} => ({ ...classification, ...{ [classifier]: false } }),
{}
return <DatasetClassification>(
setProperties(
this.getDatasetClassificationRef(),
datasetClassifiersKeys.reduce(
(classification, classifier): {} => ({ ...classification, ...{ [classifier]: false } }),
{}
)
)
);
}

View File

@ -13,6 +13,14 @@ type Iteratee<A, R> = (a: A) => R;
*/
type Many<T> = T | Array<T>;
/**
* Serializes the values in an array as a sorted string
* @template S the type of values in the array that are assignable to type string
* @param {Array<S>} stringArray array of values assignable to S
* @returns {string}
*/
const serializeStringArray = <S extends string>(stringArray: Array<S>): string => String([...stringArray].sort());
/**
* Takes a number of elements in the list from the start up to the length of the list
* @template T type of elements in array
@ -253,5 +261,6 @@ export {
arrayEvery,
arraySome,
iterateArrayAsync,
reduceArrayAsync
reduceArrayAsync,
serializeStringArray
};

View File

@ -1,9 +1,10 @@
import { typeOf } from '@ember/utils';
import { DatasetClassifiers } from 'wherehows-web/constants';
import { arrayEvery, arrayMap, arrayReduce } from 'wherehows-web/utils/array';
import { arrayEvery, arrayMap, arrayReduce, serializeStringArray } from 'wherehows-web/utils/array';
import { IObject } from 'wherehows-web/typings/generic';
import { isObject } from 'wherehows-web/utils/object';
import { difference } from 'lodash';
import { IComplianceEntity } from 'wherehows-web/typings/api/datasets/compliance';
/**
* Defines the interface for an IDL that specifies the data types for properties on
@ -185,6 +186,34 @@ const keysMatchNames = (object: IObject<any>, typeMaps: Array<IMetadataType>): t
return match;
};
/**
* Validates that an array of json object string values match
* @param {Array<string>} values the received list of strings
* @param {Array<string>} expectedValues the expected list of strings
* @returns {true}
*/
const jsonValuesMatch = (values: Array<string>, expectedValues: Array<string>): true => {
const sValues = serializeStringArray(values);
const sExpectedValues = serializeStringArray(expectedValues);
const match = sValues === sExpectedValues;
if (!match) {
throw new Error(
` Found ${difference(values, expectedValues).join(', ')}. Expected only ${expectedValues.join(', ')}`
);
}
return match;
};
/**
* Type guard asserts that object is assignable to { complianceEntities: Array<IComplianceEntity> }
* @param {*} object object to be tested against complianceEntitiesTaxonomy
* @returns {(object is { complianceEntities: Array<IComplianceEntity> })}
*/
const isMetadataObject = (object: any): object is { complianceEntities: Array<IComplianceEntity> } =>
keysEquiv(object, complianceEntitiesTaxonomy);
/**
* Checks each key on an object matches the expected types in the typeMap
* @param {IObject<any>} object the object with keys to check
@ -196,4 +225,4 @@ const keysEquiv = (object: IObject<any>, typeMaps: Array<IMetadataType>): boolea
export default keysEquiv;
export { complianceMetadataTaxonomy, complianceEntitiesTaxonomy };
export { complianceMetadataTaxonomy, complianceEntitiesTaxonomy, jsonValuesMatch, isMetadataObject };