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,
|
complianceDataException,
|
||||||
complianceFieldNotUnique,
|
complianceFieldNotUnique,
|
||||||
missingTypes,
|
missingTypes,
|
||||||
successUpdating,
|
|
||||||
failedUpdating,
|
|
||||||
helpText,
|
helpText,
|
||||||
successUploading,
|
successUploading,
|
||||||
invalidPolicyData,
|
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
|
* Sets the default classification for the given identifier field
|
||||||
* Using the identifierType, determine the field's default security classification based on a values
|
* 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');
|
const onSave = get(this, 'onSave');
|
||||||
setSaveFlag(isSaving);
|
setSaveFlag(isSaving);
|
||||||
|
|
||||||
await this.whenRequestCompletes(onSave(), { isSaving });
|
await onSave();
|
||||||
return this.updateStep(-1);
|
return this.updateStep(-1);
|
||||||
} finally {
|
} finally {
|
||||||
setSaveFlag();
|
setSaveFlag();
|
||||||
@ -1419,10 +1380,7 @@ export default class DatasetCompliance extends ObservableDecorator {
|
|||||||
// Rolls back changes made to the compliance spec to current
|
// Rolls back changes made to the compliance spec to current
|
||||||
// server state
|
// server state
|
||||||
resetCompliance(this: DatasetCompliance) {
|
resetCompliance(this: DatasetCompliance) {
|
||||||
const options = {
|
get(this, 'onReset')();
|
||||||
successMessage: 'Field classification has been reset to the previously saved state.'
|
|
||||||
};
|
|
||||||
this.whenRequestCompletes(get(this, 'onReset')(), options);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
import { get, set, setProperties, getProperties } from '@ember/object';
|
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 { task, TaskInstance } from 'ember-concurrency';
|
||||||
import { action } from 'ember-decorators/object';
|
import { action } from 'ember-decorators/object';
|
||||||
|
import Notifications, { NotificationEvent } from 'wherehows-web/services/notifications';
|
||||||
import { IDatasetColumn } from 'wherehows-web/typings/api/datasets/columns';
|
import { IDatasetColumn } from 'wherehows-web/typings/api/datasets/columns';
|
||||||
import { IComplianceInfo, IComplianceSuggestion } from 'wherehows-web/typings/api/datasets/compliance';
|
import { IComplianceInfo, IComplianceSuggestion } from 'wherehows-web/typings/api/datasets/compliance';
|
||||||
import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset';
|
import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset';
|
||||||
import { IDatasetSchema } from 'wherehows-web/typings/api/datasets/schema';
|
import { IDatasetSchema } from 'wherehows-web/typings/api/datasets/schema';
|
||||||
import { IComplianceDataType } from 'wherehows-web/typings/api/list/compliance-datatypes';
|
import { IComplianceDataType } from 'wherehows-web/typings/api/list/compliance-datatypes';
|
||||||
import {
|
import {
|
||||||
|
ApiResponseStatus,
|
||||||
IReadComplianceResult,
|
IReadComplianceResult,
|
||||||
readDatasetComplianceByUrn,
|
readDatasetComplianceByUrn,
|
||||||
readDatasetComplianceSuggestionByUrn,
|
readDatasetComplianceSuggestionByUrn,
|
||||||
@ -15,7 +19,11 @@ import {
|
|||||||
} from 'wherehows-web/utils/api';
|
} from 'wherehows-web/utils/api';
|
||||||
import { columnDataTypesAndFieldNames } from 'wherehows-web/utils/api/datasets/columns';
|
import { columnDataTypesAndFieldNames } from 'wherehows-web/utils/api/datasets/columns';
|
||||||
import { readDatasetSchemaByUrn } from 'wherehows-web/utils/api/datasets/schema';
|
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 { 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 {
|
export default class DatasetComplianceContainer extends Component {
|
||||||
/**
|
/**
|
||||||
@ -42,6 +50,12 @@ export default class DatasetComplianceContainer extends Component {
|
|||||||
*/
|
*/
|
||||||
complianceSuggestion: IComplianceSuggestion | void;
|
complianceSuggestion: IComplianceSuggestion | void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to the application notifications Service
|
||||||
|
* @type {ComputedProperty<Notifications>}
|
||||||
|
*/
|
||||||
|
notifications: ComputedProperty<Notifications> = inject();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object containing the compliance information for the dataset
|
* Object containing the compliance information for the dataset
|
||||||
* @type {IComplianceInfo | void}
|
* @type {IComplianceInfo | void}
|
||||||
@ -140,12 +154,38 @@ export default class DatasetComplianceContainer extends Component {
|
|||||||
* @type {Task<Promise<IDatasetSchema>, (a?: any) => TaskInstance<Promise<IDatasetSchema>>>}
|
* @type {Task<Promise<IDatasetSchema>, (a?: any) => TaskInstance<Promise<IDatasetSchema>>>}
|
||||||
*/
|
*/
|
||||||
getDatasetSchemaTask = task(function*(this: DatasetComplianceContainer): IterableIterator<Promise<IDatasetSchema>> {
|
getDatasetSchemaTask = task(function*(this: DatasetComplianceContainer): IterableIterator<Promise<IDatasetSchema>> {
|
||||||
const { columns, schemaless } = yield readDatasetSchemaByUrn(get(this, 'urn'));
|
try {
|
||||||
const schemaFieldNamesMappedToDataTypes = columnDataTypesAndFieldNames(columns);
|
const { columns, schemaless } = yield readDatasetSchemaByUrn(get(this, 'urn'));
|
||||||
|
const schemaFieldNamesMappedToDataTypes = columnDataTypesAndFieldNames(columns);
|
||||||
setProperties(this, { schemaFieldNamesMappedToDataTypes, schemaless });
|
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
|
* Persists the updates to the compliance policy on the remote host
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
@ -154,7 +194,7 @@ export default class DatasetComplianceContainer extends Component {
|
|||||||
async savePrivacyCompliancePolicy(this: DatasetComplianceContainer): Promise<void> {
|
async savePrivacyCompliancePolicy(this: DatasetComplianceContainer): Promise<void> {
|
||||||
const complianceInfo = get(this, 'complianceInfo');
|
const complianceInfo = get(this, 'complianceInfo');
|
||||||
if (complianceInfo) {
|
if (complianceInfo) {
|
||||||
return saveDatasetComplianceByUrn(get(this, 'urn'), complianceInfo);
|
return this.notifyOnSave<void>(saveDatasetComplianceByUrn(get(this, 'urn'), complianceInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
{{dataset-compliance
|
{{#if getContainerDataTask.last.isError}}
|
||||||
datasetName=datasetName
|
|
||||||
schemaless=schemaless
|
{{empty-state heading="An error occurred getting this dataset's compliance policy"}}
|
||||||
platform=platform
|
|
||||||
complianceInfo=complianceInfo
|
{{else}}
|
||||||
complianceSuggestion=complianceSuggestion
|
|
||||||
isNewComplianceInfo=isNewComplianceInfo
|
{{dataset-compliance
|
||||||
schemaFieldNamesMappedToDataTypes=schemaFieldNamesMappedToDataTypes
|
datasetName=datasetName
|
||||||
complianceDataTypes=complianceDataTypes
|
schemaless=schemaless
|
||||||
onSave=(action "savePrivacyCompliancePolicy")
|
platform=platform
|
||||||
onReset=(action "resetPrivacyCompliancePolicy")
|
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) {
|
if (e instanceof ApiError && e.status === ApiResponseStatus.NotFound) {
|
||||||
complianceInfo = createInitialComplianceInfo(urn);
|
complianceInfo = createInitialComplianceInfo(urn);
|
||||||
isNewComplianceInfo = true;
|
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 { module, test } from 'qunit';
|
||||||
|
import { ApiResponseStatus } from 'wherehows-web/utils/api/shared';
|
||||||
|
|
||||||
module('Unit | Utility | api/errors/errors');
|
module('Unit | Utility | api/errors/errors');
|
||||||
|
|
||||||
@ -13,3 +14,12 @@ test('throwIfApiError returns a Promise / thennable', function(assert) {
|
|||||||
'invocation returns a Promise object / thennable'
|
'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