mirror of
https://github.com/datahub-project/datahub.git
synced 2025-09-01 05:13:15 +00:00
Merge pull request #993 from theseyi/handle-comp-exception
refactors exception handling in compliance presentational component t…
This commit is contained in:
commit
4f475c694f
@ -117,8 +117,6 @@ const {
|
||||
complianceDataException,
|
||||
complianceFieldNotUnique,
|
||||
missingTypes,
|
||||
successUpdating,
|
||||
failedUpdating,
|
||||
helpText,
|
||||
successUploading,
|
||||
invalidPolicyData,
|
||||
@ -790,43 +788,6 @@ export default class DatasetCompliance extends ObservableDecorator {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to update user when an async server update to the
|
||||
* security specification is handled.
|
||||
* @template T
|
||||
* @param {Promise<T>} request the server request
|
||||
* @param {{successMessage?: string, isSaving?: boolean}} [{ successMessage = successUpdating, isSaving = false }={}]
|
||||
* @prop {successMessage} optional message for successful response
|
||||
* @prop {isSaving} optional flag indicating when the user intends to persist / save
|
||||
* @returns {Promise<void>}
|
||||
* @memberof DatasetCompliance
|
||||
*/
|
||||
whenRequestCompletes<T extends { status: ApiStatus }>(
|
||||
this: DatasetCompliance,
|
||||
request: Promise<T>,
|
||||
{ successMessage = successUpdating, isSaving = false }: { successMessage?: string; isSaving?: boolean } = {}
|
||||
): Promise<void> {
|
||||
const { notify } = get(this, 'notifications');
|
||||
|
||||
return Promise.resolve(request)
|
||||
.then(({ status = ApiStatus.ERROR }): void | Promise<void> => {
|
||||
return status === ApiStatus.OK
|
||||
? notify(NotificationEvent.success, { content: successMessage })
|
||||
: Promise.reject(new Error(`Reason code for this is ${status}`));
|
||||
})
|
||||
.catch((err: string) => {
|
||||
let message = `${failedUpdating} \n ${err}`;
|
||||
|
||||
if (get(this, 'isNewComplianceInfo') && !isSaving) {
|
||||
return notify(NotificationEvent.info, {
|
||||
content: 'This dataset does not have any previously saved fields with a identifying information.'
|
||||
});
|
||||
}
|
||||
|
||||
notify(NotificationEvent.error, { content: message });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default classification for the given identifier field
|
||||
* Using the identifierType, determine the field's default security classification based on a values
|
||||
@ -1409,7 +1370,7 @@ export default class DatasetCompliance extends ObservableDecorator {
|
||||
const onSave = get(this, 'onSave');
|
||||
setSaveFlag(isSaving);
|
||||
|
||||
await this.whenRequestCompletes(onSave(), { isSaving });
|
||||
await onSave();
|
||||
return this.updateStep(-1);
|
||||
} finally {
|
||||
setSaveFlag();
|
||||
@ -1419,10 +1380,7 @@ export default class DatasetCompliance extends ObservableDecorator {
|
||||
// Rolls back changes made to the compliance spec to current
|
||||
// server state
|
||||
resetCompliance(this: DatasetCompliance) {
|
||||
const options = {
|
||||
successMessage: 'Field classification has been reset to the previously saved state.'
|
||||
};
|
||||
this.whenRequestCompletes(get(this, 'onReset')(), options);
|
||||
get(this, 'onReset')();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,13 +1,17 @@
|
||||
import Component from '@ember/component';
|
||||
import { get, set, setProperties, getProperties } from '@ember/object';
|
||||
import ComputedProperty from '@ember/object/computed';
|
||||
import { inject } from '@ember/service';
|
||||
import { task, TaskInstance } from 'ember-concurrency';
|
||||
import { action } from 'ember-decorators/object';
|
||||
import Notifications, { NotificationEvent } from 'wherehows-web/services/notifications';
|
||||
import { IDatasetColumn } from 'wherehows-web/typings/api/datasets/columns';
|
||||
import { IComplianceInfo, IComplianceSuggestion } from 'wherehows-web/typings/api/datasets/compliance';
|
||||
import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset';
|
||||
import { IDatasetSchema } from 'wherehows-web/typings/api/datasets/schema';
|
||||
import { IComplianceDataType } from 'wherehows-web/typings/api/list/compliance-datatypes';
|
||||
import {
|
||||
ApiResponseStatus,
|
||||
IReadComplianceResult,
|
||||
readDatasetComplianceByUrn,
|
||||
readDatasetComplianceSuggestionByUrn,
|
||||
@ -15,7 +19,11 @@ import {
|
||||
} from 'wherehows-web/utils/api';
|
||||
import { columnDataTypesAndFieldNames } from 'wherehows-web/utils/api/datasets/columns';
|
||||
import { readDatasetSchemaByUrn } from 'wherehows-web/utils/api/datasets/schema';
|
||||
import { ApiError } from 'wherehows-web/utils/api/errors/errors';
|
||||
import { readComplianceDataTypes } from 'wherehows-web/utils/api/list/compliance-datatypes';
|
||||
import { compliancePolicyStrings } from 'wherehows-web/constants';
|
||||
|
||||
const { successUpdating, failedUpdating } = compliancePolicyStrings;
|
||||
|
||||
export default class DatasetComplianceContainer extends Component {
|
||||
/**
|
||||
@ -42,6 +50,12 @@ export default class DatasetComplianceContainer extends Component {
|
||||
*/
|
||||
complianceSuggestion: IComplianceSuggestion | void;
|
||||
|
||||
/**
|
||||
* Reference to the application notifications Service
|
||||
* @type {ComputedProperty<Notifications>}
|
||||
*/
|
||||
notifications: ComputedProperty<Notifications> = inject();
|
||||
|
||||
/**
|
||||
* Object containing the compliance information for the dataset
|
||||
* @type {IComplianceInfo | void}
|
||||
@ -140,12 +154,38 @@ export default class DatasetComplianceContainer extends Component {
|
||||
* @type {Task<Promise<IDatasetSchema>, (a?: any) => TaskInstance<Promise<IDatasetSchema>>>}
|
||||
*/
|
||||
getDatasetSchemaTask = task(function*(this: DatasetComplianceContainer): IterableIterator<Promise<IDatasetSchema>> {
|
||||
const { columns, schemaless } = yield readDatasetSchemaByUrn(get(this, 'urn'));
|
||||
const schemaFieldNamesMappedToDataTypes = columnDataTypesAndFieldNames(columns);
|
||||
|
||||
setProperties(this, { schemaFieldNamesMappedToDataTypes, schemaless });
|
||||
try {
|
||||
const { columns, schemaless } = yield readDatasetSchemaByUrn(get(this, 'urn'));
|
||||
const schemaFieldNamesMappedToDataTypes = columnDataTypesAndFieldNames(columns);
|
||||
setProperties(this, { schemaFieldNamesMappedToDataTypes, schemaless });
|
||||
} catch (e) {
|
||||
// If this schema is missing, silence exception, otherwise propagate
|
||||
if (!(e instanceof ApiError && e.status === ApiResponseStatus.NotFound)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Handles user notifications when save succeeds or fails
|
||||
* @template T the return type for the save request
|
||||
* @param {Promise<T>} request async policy save request
|
||||
* @returns {Promise<T>}
|
||||
* @memberof DatasetComplianceContainer
|
||||
*/
|
||||
async notifyOnSave<T>(this: DatasetComplianceContainer, request: Promise<T>): Promise<T> {
|
||||
const { notify } = get(this, 'notifications');
|
||||
|
||||
try {
|
||||
await request;
|
||||
notify(NotificationEvent.success, { content: successUpdating });
|
||||
} catch (e) {
|
||||
notify(NotificationEvent.error, { content: failedUpdating });
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the updates to the compliance policy on the remote host
|
||||
* @return {Promise<void>}
|
||||
@ -154,7 +194,7 @@ export default class DatasetComplianceContainer extends Component {
|
||||
async savePrivacyCompliancePolicy(this: DatasetComplianceContainer): Promise<void> {
|
||||
const complianceInfo = get(this, 'complianceInfo');
|
||||
if (complianceInfo) {
|
||||
return saveDatasetComplianceByUrn(get(this, 'urn'), complianceInfo);
|
||||
return this.notifyOnSave<void>(saveDatasetComplianceByUrn(get(this, 'urn'), complianceInfo));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,20 @@
|
||||
{{dataset-compliance
|
||||
datasetName=datasetName
|
||||
schemaless=schemaless
|
||||
platform=platform
|
||||
complianceInfo=complianceInfo
|
||||
complianceSuggestion=complianceSuggestion
|
||||
isNewComplianceInfo=isNewComplianceInfo
|
||||
schemaFieldNamesMappedToDataTypes=schemaFieldNamesMappedToDataTypes
|
||||
complianceDataTypes=complianceDataTypes
|
||||
onSave=(action "savePrivacyCompliancePolicy")
|
||||
onReset=(action "resetPrivacyCompliancePolicy")
|
||||
}}
|
||||
{{#if getContainerDataTask.last.isError}}
|
||||
|
||||
{{empty-state heading="An error occurred getting this dataset's compliance policy"}}
|
||||
|
||||
{{else}}
|
||||
|
||||
{{dataset-compliance
|
||||
datasetName=datasetName
|
||||
schemaless=schemaless
|
||||
platform=platform
|
||||
complianceInfo=complianceInfo
|
||||
complianceSuggestion=complianceSuggestion
|
||||
isNewComplianceInfo=isNewComplianceInfo
|
||||
schemaFieldNamesMappedToDataTypes=schemaFieldNamesMappedToDataTypes
|
||||
complianceDataTypes=complianceDataTypes
|
||||
onSave=(action "savePrivacyCompliancePolicy")
|
||||
onReset=(action "resetPrivacyCompliancePolicy")
|
||||
}}
|
||||
|
||||
{{/if}}
|
||||
|
@ -101,6 +101,8 @@ const readDatasetComplianceByUrn = async (urn: string): Promise<IReadComplianceR
|
||||
if (e instanceof ApiError && e.status === ApiResponseStatus.NotFound) {
|
||||
complianceInfo = createInitialComplianceInfo(urn);
|
||||
isNewComplianceInfo = true;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { throwIfApiError } from 'wherehows-web/utils/api/errors/errors';
|
||||
import { throwIfApiError, ApiError } from 'wherehows-web/utils/api/errors/errors';
|
||||
import { module, test } from 'qunit';
|
||||
import { ApiResponseStatus } from 'wherehows-web/utils/api/shared';
|
||||
|
||||
module('Unit | Utility | api/errors/errors');
|
||||
|
||||
@ -13,3 +14,12 @@ test('throwIfApiError returns a Promise / thennable', function(assert) {
|
||||
'invocation returns a Promise object / thennable'
|
||||
);
|
||||
});
|
||||
|
||||
test('ApiError subclasses built-in Error and has attributes', function(assert) {
|
||||
const status = ApiResponseStatus.NotFound;
|
||||
const apiError = new ApiError(status);
|
||||
|
||||
assert.ok(apiError instanceof Error, 'is an instanceof Error');
|
||||
assert.ok(apiError.timestamp instanceof Date, 'has a valid timestamp');
|
||||
assert.ok(apiError.status === status, 'has a status attribute');
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user