mirror of
				https://github.com/datahub-project/datahub.git
				synced 2025-10-31 02:37:05 +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( | ||||||
|  |             (): void => { | ||||||
|               if (this.isDestroyed || this.isDestroying) { |               if (this.isDestroyed || this.isDestroying) { | ||||||
|                 return; |                 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,12 +1282,14 @@ export default class DatasetCompliance extends Component { | |||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (willMarkAllAsNo) { |       if (willMarkAllAsNo) { | ||||||
|         return <DatasetClassification>setProperties( |         return <DatasetClassification>( | ||||||
|  |           setProperties( | ||||||
|             this.getDatasetClassificationRef(), |             this.getDatasetClassificationRef(), | ||||||
|             datasetClassifiersKeys.reduce( |             datasetClassifiersKeys.reduce( | ||||||
|               (classification, classifier): {} => ({ ...classification, ...{ [classifier]: false } }), |               (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
	 Seyi Adebajo
						Seyi Adebajo