mirror of
https://github.com/datahub-project/datahub.git
synced 2025-09-03 06:13:14 +00:00
Merge pull request #1296 from theseyi/identifier-field-match
validates the identifierField values against the expected schema fiel…
This commit is contained in:
commit
11f2753720
@ -66,13 +66,11 @@ import {
|
|||||||
import { uniqBy } from 'lodash';
|
import { uniqBy } from 'lodash';
|
||||||
import { IColumnFieldProps } from 'wherehows-web/typings/app/dataset-columns';
|
import { IColumnFieldProps } from 'wherehows-web/typings/app/dataset-columns';
|
||||||
import { NonIdLogicalType } from 'wherehows-web/constants/datasets/compliance';
|
import { NonIdLogicalType } from 'wherehows-web/constants/datasets/compliance';
|
||||||
import { pick } from 'lodash';
|
|
||||||
import { trackableEvent, TrackableEventCategory } from 'wherehows-web/constants/analytics/event-tracking';
|
import { trackableEvent, TrackableEventCategory } from 'wherehows-web/constants/analytics/event-tracking';
|
||||||
import { notificationDialogActionFactory } from 'wherehows-web/utils/notifications/notifications';
|
import { notificationDialogActionFactory } from 'wherehows-web/utils/notifications/notifications';
|
||||||
import validateMetadataObject, {
|
import { isMetadataObject, jsonValuesMatch } from 'wherehows-web/utils/datasets/compliance/metadata-schema';
|
||||||
complianceEntitiesTaxonomy
|
|
||||||
} from 'wherehows-web/utils/datasets/compliance/metadata-schema';
|
|
||||||
import { typeOf } from '@ember/utils';
|
import { typeOf } from '@ember/utils';
|
||||||
|
import { pick } from 'wherehows-web/utils/object';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
complianceDataException,
|
complianceDataException,
|
||||||
@ -154,7 +152,14 @@ export default class DatasetCompliance extends Component {
|
|||||||
jsonComplianceEntities: ComputedProperty<string> = computed('columnIdFieldsToCurrentPrivacyPolicy', function(
|
jsonComplianceEntities: ComputedProperty<string> = computed('columnIdFieldsToCurrentPrivacyPolicy', function(
|
||||||
this: DatasetCompliance
|
this: DatasetCompliance
|
||||||
): string {
|
): 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 entityMap: ISchemaFieldsToPolicy = get(this, 'columnIdFieldsToCurrentPrivacyPolicy');
|
||||||
const entitiesWithModifiableKeys = arrayMap((tag: IComplianceEntityWithMetadata) => pick(tag, entityAttrs))(
|
const entitiesWithModifiableKeys = arrayMap((tag: IComplianceEntityWithMetadata) => pick(tag, entityAttrs))(
|
||||||
(<Array<IComplianceEntityWithMetadata>>[]).concat(...Object.values(entityMap))
|
(<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
|
// invoking the previousStep action to go back in the sequence
|
||||||
// batch previousStep invocation in a afterRender queue due to editStepIndex update
|
// batch previousStep invocation in a afterRender queue due to editStepIndex update
|
||||||
previousAction = noop;
|
previousAction = noop;
|
||||||
run((): void => {
|
run(
|
||||||
if (this.isDestroyed || this.isDestroying) {
|
(): void => {
|
||||||
return;
|
if (this.isDestroyed || this.isDestroying) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
schedule('afterRender', this, this.actions.previousStep);
|
||||||
}
|
}
|
||||||
schedule('afterRender', this, this.actions.previousStep);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).enqueue();
|
}).enqueue();
|
||||||
@ -979,7 +986,7 @@ export default class DatasetCompliance extends Component {
|
|||||||
]);
|
]);
|
||||||
const { complianceEntities } = complianceInfo!;
|
const { complianceEntities } = complianceInfo!;
|
||||||
// All changeSet attrs that can be on policy, excluding changeSet metadata
|
// All changeSet attrs that can be on policy, excluding changeSet metadata
|
||||||
const entityAttrs = [
|
const entityAttrs: Array<keyof IComplianceEntity> = [
|
||||||
'identifierField',
|
'identifierField',
|
||||||
'identifierType',
|
'identifierType',
|
||||||
'logicalType',
|
'logicalType',
|
||||||
@ -989,7 +996,7 @@ export default class DatasetCompliance extends Component {
|
|||||||
'valuePattern'
|
'valuePattern'
|
||||||
];
|
];
|
||||||
const updatingComplianceEntities = arrayMap(
|
const updatingComplianceEntities = arrayMap(
|
||||||
(tag: IComplianceChangeSet): IComplianceEntity => <IComplianceEntity>pick(tag, entityAttrs)
|
(tag: IComplianceChangeSet): IComplianceEntity => pick(tag, entityAttrs)
|
||||||
)(workingCopy);
|
)(workingCopy);
|
||||||
|
|
||||||
return complianceEntities.setObjects(updatingComplianceEntities);
|
return complianceEntities.setObjects(updatingComplianceEntities);
|
||||||
@ -1095,12 +1102,24 @@ export default class DatasetCompliance extends Component {
|
|||||||
*/
|
*/
|
||||||
onManualComplianceUpdate(this: DatasetCompliance, updatedEntities: string): void {
|
onManualComplianceUpdate(this: DatasetCompliance, updatedEntities: string): void {
|
||||||
try {
|
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 entities = JSON.parse(updatedEntities);
|
||||||
const metadataObject = {
|
const metadataObject = {
|
||||||
complianceEntities: entities
|
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: '' });
|
setProperties(this, { isManualApplyDisabled: !isValid, manualParseError: '' });
|
||||||
|
|
||||||
@ -1119,7 +1138,7 @@ export default class DatasetCompliance extends Component {
|
|||||||
async onApplyComplianceJson(this: DatasetCompliance) {
|
async onApplyComplianceJson(this: DatasetCompliance) {
|
||||||
try {
|
try {
|
||||||
await get(this, 'onComplianceJsonUpdate')(JSON.stringify(get(this, 'manuallyEnteredComplianceEntities')));
|
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);
|
this.actions.nextStep.call(this);
|
||||||
} catch {
|
} catch {
|
||||||
noop();
|
noop();
|
||||||
@ -1263,11 +1282,13 @@ export default class DatasetCompliance extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (willMarkAllAsNo) {
|
if (willMarkAllAsNo) {
|
||||||
return <DatasetClassification>setProperties(
|
return <DatasetClassification>(
|
||||||
this.getDatasetClassificationRef(),
|
setProperties(
|
||||||
datasetClassifiersKeys.reduce(
|
this.getDatasetClassificationRef(),
|
||||||
(classification, classifier): {} => ({ ...classification, ...{ [classifier]: false } }),
|
datasetClassifiersKeys.reduce(
|
||||||
{}
|
(classification, classifier): {} => ({ ...classification, ...{ [classifier]: false } }),
|
||||||
|
{}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,14 @@ type Iteratee<A, R> = (a: A) => R;
|
|||||||
*/
|
*/
|
||||||
type Many<T> = T | Array<T>;
|
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
|
* 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
|
* @template T type of elements in array
|
||||||
@ -253,5 +261,6 @@ export {
|
|||||||
arrayEvery,
|
arrayEvery,
|
||||||
arraySome,
|
arraySome,
|
||||||
iterateArrayAsync,
|
iterateArrayAsync,
|
||||||
reduceArrayAsync
|
reduceArrayAsync,
|
||||||
|
serializeStringArray
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { typeOf } from '@ember/utils';
|
import { typeOf } from '@ember/utils';
|
||||||
import { DatasetClassifiers } from 'wherehows-web/constants';
|
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 { IObject } from 'wherehows-web/typings/generic';
|
||||||
import { isObject } from 'wherehows-web/utils/object';
|
import { isObject } from 'wherehows-web/utils/object';
|
||||||
import { difference } from 'lodash';
|
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
|
* 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;
|
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
|
* Checks each key on an object matches the expected types in the typeMap
|
||||||
* @param {IObject<any>} object the object with keys to check
|
* @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 default keysEquiv;
|
||||||
|
|
||||||
export { complianceMetadataTaxonomy, complianceEntitiesTaxonomy };
|
export { complianceMetadataTaxonomy, complianceEntitiesTaxonomy, jsonValuesMatch, isMetadataObject };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user