mirror of
				https://github.com/datahub-project/datahub.git
				synced 2025-10-31 10:49:00 +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 { 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 } }), | ||||
|               {} | ||||
|             ) | ||||
|           ) | ||||
|         ); | ||||
|       } | ||||
|  | ||||
| @ -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 | ||||
| }; | ||||
|  | ||||
| @ -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 }; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Seyi Adebajo
						Seyi Adebajo