2017-02-13 14:51:31 -08:00
|
|
|
import Ember from 'ember';
|
2017-04-25 10:50:02 -07:00
|
|
|
import isTrackingHeaderField from 'wherehows-web/utils/validators/tracking-headers';
|
2017-05-19 08:45:05 -07:00
|
|
|
import {
|
|
|
|
classifiers,
|
|
|
|
datasetClassifiers,
|
|
|
|
fieldIdentifierTypes,
|
|
|
|
idLogicalTypes,
|
|
|
|
genericLogicalTypes,
|
|
|
|
defaultFieldDataTypeClassification
|
|
|
|
} from 'wherehows-web/constants';
|
|
|
|
|
|
|
|
const {
|
|
|
|
Component,
|
|
|
|
computed,
|
|
|
|
set,
|
|
|
|
get,
|
|
|
|
Observable: { mixins: { notifyPropertyChange } },
|
|
|
|
setProperties,
|
|
|
|
getWithDefault,
|
|
|
|
String: { htmlSafe }
|
|
|
|
} = Ember;
|
2017-03-24 20:27:43 -07:00
|
|
|
|
2017-04-02 15:40:43 -07:00
|
|
|
// TODO: DSS-6671 Extract to constants module
|
2017-05-01 09:57:17 -07:00
|
|
|
const missingTypes = 'Looks like some fields may contain privacy data but do not have a specified `Field Format`?';
|
2017-04-02 15:40:43 -07:00
|
|
|
const successUpdating = 'Your changes have been successfully saved!';
|
|
|
|
const failedUpdating = 'Oops! We are having trouble updating this dataset at the moment.';
|
2017-04-18 15:02:45 -07:00
|
|
|
const hiddenTrackingFieldsMsg = htmlSafe(
|
2017-05-19 08:45:05 -07:00
|
|
|
'<p>Some fields in this dataset have been hidden from the table(s) below. ' +
|
2017-05-01 09:57:17 -07:00
|
|
|
"These are tracking fields for which we've been able to predetermine the compliance classification.</p>" +
|
|
|
|
'<p>For example: <code>header.memberId</code>, <code>requestHeader</code>. ' +
|
|
|
|
'Hopefully, this saves you some scrolling!</p>'
|
2017-04-18 15:02:45 -07:00
|
|
|
);
|
2017-05-19 08:45:05 -07:00
|
|
|
const helpText = {
|
|
|
|
classification: 'For each of the fields below, please classify them based on the data handling table found at go/dht. ' +
|
|
|
|
'The default classification should suffice in most cases.',
|
|
|
|
subjectOwner: 'A field can contain one or multiple member IDs that do not technically "own" the entire record, ' +
|
|
|
|
'e.g. the list of recipients of a message. The field should be marked as a "Subject Owner" in this case.'
|
|
|
|
};
|
2017-04-02 15:40:43 -07:00
|
|
|
|
2017-05-19 08:45:05 -07:00
|
|
|
/**
|
|
|
|
* Quick regex to check if a string ends with urn case-insensitive, cached outside scope to avoid recreating
|
|
|
|
* on every function invocation
|
|
|
|
* @type {RegExp}
|
|
|
|
*/
|
|
|
|
const endsWithUrnRegex = /.*urn/i;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Takes a string, returns a formatted string. Niche , single use case
|
|
|
|
* for now, so no need to make into a helper
|
|
|
|
* @param {String} string
|
|
|
|
*/
|
|
|
|
const formatAsCapitalizedStringWithSpaces = string => string.replace(/[A-Z]/g, match => ` ${match}`).capitalize();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* String constant referencing the datasetClassification on the privacy policy
|
|
|
|
* @type {String}
|
|
|
|
*/
|
|
|
|
const datasetClassificationKey = 'complianceInfo.datasetClassification';
|
|
|
|
/**
|
|
|
|
* A list of available keys for the datasetClassification map on the security specification
|
|
|
|
* @type {Array}
|
|
|
|
*/
|
|
|
|
const datasetClassifiersKeys = Object.keys(datasetClassifiers);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* String constant identifying the classified fields on the security spec
|
|
|
|
* @type {String}
|
|
|
|
*/
|
|
|
|
const policyFieldClassificationKey = 'complianceInfo.fieldClassification';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A reference to the compliance policy entities on the complianceInfo map
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
const policyComplianceEntitiesKey = 'complianceInfo.complianceEntities';
|
2017-03-30 15:08:12 -07:00
|
|
|
/**
|
|
|
|
* Duplicate check using every to short-circuit iteration
|
|
|
|
* @param {Array} names = [] the list to check for dupes
|
|
|
|
* @return {Boolean} true is unique, false otherwise
|
|
|
|
*/
|
2017-05-01 09:57:17 -07:00
|
|
|
const fieldNamesAreUnique = (names = []) => names.every((name, index) => names.indexOf(name) === index);
|
2017-03-24 20:27:43 -07:00
|
|
|
|
|
|
|
/**
|
2017-05-19 08:45:05 -07:00
|
|
|
* A list of field identifier types mapped to label, value options for select display
|
|
|
|
* @type {any[]|Array.<{value: String, label: String}>}
|
2017-03-24 20:27:43 -07:00
|
|
|
*/
|
2017-05-19 08:45:05 -07:00
|
|
|
const fieldIdentifierOptions = Object.keys(fieldIdentifierTypes).map(fieldIdentifierType => ({
|
|
|
|
value: fieldIdentifierTypes[fieldIdentifierType].value,
|
|
|
|
label: fieldIdentifierTypes[fieldIdentifierType].displayAs
|
|
|
|
}));
|
2017-03-24 20:27:43 -07:00
|
|
|
|
2017-05-19 08:45:05 -07:00
|
|
|
/**
|
|
|
|
* A list of field identifier types that are Ids i.e member ID, org ID, group ID
|
|
|
|
* @type {any[]|Array.<String>}
|
|
|
|
*/
|
|
|
|
const fieldIdentifierTypeIds = Object.keys(fieldIdentifierTypes)
|
|
|
|
.map(fieldIdentifierType => fieldIdentifierTypes[fieldIdentifierType])
|
|
|
|
.filter(({ isId }) => isId)
|
|
|
|
.mapBy('value');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Caches a list of logicalType mappings for displaying its value and a label by logicalType
|
|
|
|
* @param {String} logicalType
|
|
|
|
*/
|
|
|
|
const cachedLogicalTypes = logicalType =>
|
|
|
|
computed(function() {
|
|
|
|
return {
|
|
|
|
id: idLogicalTypes,
|
|
|
|
generic: genericLogicalTypes
|
|
|
|
}[logicalType].map(value => ({
|
|
|
|
value,
|
|
|
|
label: value
|
|
|
|
? value.replace(/_/g, ' ').replace(/([A-Z]{3,})/g, f => f.toLowerCase().capitalize())
|
|
|
|
: 'Select Format'
|
|
|
|
}));
|
2017-03-24 20:27:43 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
export default Component.extend({
|
2017-03-27 20:12:09 -07:00
|
|
|
sortColumnWithName: 'identifierField',
|
2017-03-27 18:26:09 -07:00
|
|
|
filterBy: 'identifierField',
|
2017-03-24 20:27:43 -07:00
|
|
|
sortDirection: 'asc',
|
2017-02-13 14:51:31 -08:00
|
|
|
searchTerm: '',
|
2017-05-19 08:45:05 -07:00
|
|
|
helpText,
|
|
|
|
fieldIdentifierOptions,
|
2017-04-18 15:02:45 -07:00
|
|
|
hiddenTrackingFields: hiddenTrackingFieldsMsg,
|
2017-03-24 20:27:43 -07:00
|
|
|
|
2017-03-30 15:08:12 -07:00
|
|
|
didReceiveAttrs() {
|
|
|
|
this._super(...arguments);
|
|
|
|
// Perform validation step on the received component attributes
|
|
|
|
this.validateAttrs();
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensure that props received from on this component
|
|
|
|
* are valid, otherwise flag
|
|
|
|
*/
|
|
|
|
validateAttrs() {
|
2017-05-01 09:57:17 -07:00
|
|
|
const fieldNames = getWithDefault(this, 'schemaFieldNamesMappedToDataTypes', []).mapBy('fieldName');
|
2017-03-30 15:08:12 -07:00
|
|
|
|
|
|
|
if (fieldNamesAreUnique(fieldNames.sort())) {
|
2017-03-31 17:30:21 -07:00
|
|
|
return set(this, '_hasBadData', false);
|
2017-03-30 15:08:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Flag this component's data as problematic
|
|
|
|
set(this, '_hasBadData', true);
|
|
|
|
},
|
|
|
|
|
2017-05-19 08:45:05 -07:00
|
|
|
// Map logicalTypes to options consumable by DOM
|
|
|
|
idLogicalTypes: cachedLogicalTypes('id'),
|
|
|
|
|
|
|
|
// Map generic logical type to options consumable in DOM
|
|
|
|
genericLogicalTypes: cachedLogicalTypes('generic'),
|
|
|
|
|
|
|
|
// Map classifiers to options better consumed in DOM
|
|
|
|
classifiers: ['', ...classifiers.sort()].map(value => ({
|
2017-03-27 18:26:09 -07:00
|
|
|
value,
|
2017-05-19 08:45:05 -07:00
|
|
|
label: value ? formatAsCapitalizedStringWithSpaces(value) : '...'
|
2017-03-24 20:27:43 -07:00
|
|
|
})),
|
|
|
|
|
2017-04-18 15:02:45 -07:00
|
|
|
/**
|
|
|
|
* @type {Boolean} cached boolean flag indicating that fields do contain a `kafka type`
|
|
|
|
* tracking header.
|
|
|
|
* Used to indicate to viewer that these fields are hidden.
|
|
|
|
*/
|
2017-05-01 09:57:17 -07:00
|
|
|
containsHiddenTrackingFields: computed('complianceDataFieldsSansHiddenTracking.length', function() {
|
|
|
|
// If their is a diff in complianceDataFields and complianceDataFieldsSansHiddenTracking,
|
|
|
|
// then we have hidden tracking fields
|
|
|
|
return get(this, 'complianceDataFieldsSansHiddenTracking.length') !== get(this, 'complianceDataFields.length');
|
|
|
|
}),
|
2017-04-18 15:02:45 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {Array.<Object>} Filters the mapped compliance data fields without `kafka type`
|
|
|
|
* tracking headers
|
|
|
|
*/
|
2017-05-01 09:57:17 -07:00
|
|
|
complianceDataFieldsSansHiddenTracking: computed('complianceDataFields.[]', function() {
|
|
|
|
return get(this, 'complianceDataFields').filter(({ identifierField }) => !isTrackingHeaderField(identifierField));
|
2017-04-18 15:02:45 -07:00
|
|
|
}),
|
|
|
|
|
2017-05-19 08:45:05 -07:00
|
|
|
/**
|
|
|
|
* Checks that all tags/ dataset content types have a boolean value
|
|
|
|
* @type {Ember.computed}
|
|
|
|
*/
|
|
|
|
isDatasetFullyClassified: computed('datasetClassification', function() {
|
|
|
|
const datasetClassification = get(this, 'datasetClassification');
|
|
|
|
|
|
|
|
return Object.keys(datasetClassification)
|
|
|
|
.map(key => ({ value: datasetClassification[key].value }))
|
|
|
|
.every(({ value }) => [true, false].includes(value));
|
|
|
|
}),
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Computed property that is dependent on all the keys in the datasetClassification map
|
|
|
|
* Returns a new map of datasetClassificationKey: String-> Object.<Boolean|undefined,String>
|
|
|
|
* @type {Ember.computed}
|
|
|
|
*/
|
|
|
|
datasetClassification: computed(`${datasetClassificationKey}.{${datasetClassifiersKeys.join(',')}}`, function() {
|
|
|
|
const sourceDatasetClassification = get(this, datasetClassificationKey) || {};
|
|
|
|
|
|
|
|
return Object.keys(datasetClassifiers).reduce((datasetClassification, classifier) => {
|
|
|
|
return Object.assign({}, datasetClassification, {
|
|
|
|
[classifier]: {
|
|
|
|
value: sourceDatasetClassification[classifier],
|
|
|
|
label: datasetClassifiers[classifier]
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}, {});
|
|
|
|
}),
|
|
|
|
|
2017-03-27 18:26:09 -07:00
|
|
|
/**
|
|
|
|
* Lists all dataset fields found in the `columns` performs an intersection
|
|
|
|
* of fields with the currently persisted and/or updated
|
2017-05-19 08:45:05 -07:00
|
|
|
* privacyCompliancePolicy.complianceEntities.
|
2017-03-27 18:26:09 -07:00
|
|
|
* The returned list is a map of fields with current or default privacy properties
|
|
|
|
*/
|
|
|
|
complianceDataFields: computed(
|
2017-05-19 08:45:05 -07:00
|
|
|
`${policyComplianceEntitiesKey}.@each.identifierType`,
|
|
|
|
`${policyComplianceEntitiesKey}.[]`,
|
|
|
|
policyFieldClassificationKey,
|
2017-03-27 18:26:09 -07:00
|
|
|
'schemaFieldNamesMappedToDataTypes',
|
|
|
|
function() {
|
2017-05-19 08:45:05 -07:00
|
|
|
/**
|
|
|
|
* Retrieves an attribute on the `policyComplianceEntitiesKey` where the identifierField is the same as the
|
|
|
|
* provided field name
|
|
|
|
* @param attribute
|
|
|
|
* @param fieldName
|
|
|
|
* @return {null}
|
|
|
|
*/
|
2017-03-27 18:26:09 -07:00
|
|
|
const getAttributeOnField = (attribute, fieldName) => {
|
2017-05-19 10:52:58 -07:00
|
|
|
const complianceEntities = get(this, policyComplianceEntitiesKey) || [];
|
2017-05-19 11:57:46 -07:00
|
|
|
const sourceField = complianceEntities.find(({ identifierField }) => identifierField === fieldName);
|
2017-03-27 18:26:09 -07:00
|
|
|
return sourceField ? sourceField[attribute] : null;
|
|
|
|
};
|
|
|
|
|
2017-03-30 15:08:12 -07:00
|
|
|
/**
|
|
|
|
* Get value for a list of attributes
|
|
|
|
* @param {Array} attributes list of attribute keys to pull from
|
|
|
|
* sourceField
|
|
|
|
* @param {String} fieldName name of the field to lookup
|
|
|
|
* @return {Array} list of attribute values
|
|
|
|
*/
|
|
|
|
const getAttributesOnField = (attributes = [], fieldName) =>
|
2017-05-01 09:57:17 -07:00
|
|
|
attributes.map(attr => getAttributeOnField(attr, fieldName));
|
2017-03-30 15:08:12 -07:00
|
|
|
|
2017-03-27 18:26:09 -07:00
|
|
|
// Set default or if already in policy, retrieve current values from
|
2017-05-19 08:45:05 -07:00
|
|
|
// privacyCompliancePolicy.complianceEntities
|
2017-05-01 09:57:17 -07:00
|
|
|
return getWithDefault(
|
|
|
|
this,
|
|
|
|
'schemaFieldNamesMappedToDataTypes',
|
|
|
|
[]
|
|
|
|
).map(({ fieldName: identifierField, dataType }) => {
|
|
|
|
const [identifierType, isSubject, logicalType] = getAttributesOnField(
|
|
|
|
['identifierType', 'isSubject', 'logicalType'],
|
|
|
|
identifierField
|
|
|
|
);
|
2017-05-19 08:45:05 -07:00
|
|
|
// Runtime converts the identifierType to subjectMember if the isSubject flag is true
|
|
|
|
const computedIdentifierType = identifierType === fieldIdentifierTypes.member.value && isSubject
|
|
|
|
? fieldIdentifierTypes.subjectMember.value
|
|
|
|
: identifierType;
|
|
|
|
const idLogicalTypes = get(this, 'idLogicalTypes');
|
|
|
|
// Filtered list of id logical types that end with urn, or have no value
|
|
|
|
const urnFieldFormats = idLogicalTypes.filter(
|
|
|
|
type => String(type.value).match(endsWithUrnRegex) || !type.value
|
|
|
|
);
|
|
|
|
// Get the current classification list
|
2017-05-19 10:52:58 -07:00
|
|
|
const fieldClassification = get(this, policyFieldClassificationKey) || {};
|
2017-05-19 08:45:05 -07:00
|
|
|
// The field formats applicable to the current identifierType
|
|
|
|
let fieldFormats = fieldIdentifierTypeIds.includes(identifierType)
|
|
|
|
? idLogicalTypes
|
|
|
|
: get(this, 'genericLogicalTypes');
|
|
|
|
fieldFormats = identifierType === fieldIdentifierTypes.generic.value ? urnFieldFormats : fieldFormats;
|
2017-05-01 09:57:17 -07:00
|
|
|
|
|
|
|
return {
|
|
|
|
dataType,
|
|
|
|
identifierField,
|
2017-05-19 08:45:05 -07:00
|
|
|
fieldFormats,
|
|
|
|
identifierType: computedIdentifierType,
|
|
|
|
classification: fieldClassification[identifierField],
|
|
|
|
// Same object reference for equality comparision
|
|
|
|
logicalType: fieldFormats.findBy('value', logicalType)
|
2017-05-01 09:57:17 -07:00
|
|
|
};
|
|
|
|
});
|
2017-03-27 18:26:09 -07:00
|
|
|
}
|
|
|
|
),
|
|
|
|
|
2017-03-27 18:50:55 -07:00
|
|
|
/**
|
2017-05-19 08:45:05 -07:00
|
|
|
* Checks that each entity in sourceEntities has a valid logicalType
|
|
|
|
* ie. logical Type exists is the idLogicalTypes or genericLogicalTypes
|
|
|
|
* @param {Ember.Array} sourceEntities complianceEntities
|
2017-03-27 18:50:55 -07:00
|
|
|
* @return {Boolean|*} Contains or does not
|
|
|
|
*/
|
2017-05-19 08:45:05 -07:00
|
|
|
ensureIdTypesHaveLogicalType: sourceEntities => {
|
|
|
|
const logicalTypesInUppercase = [...idLogicalTypes, ...genericLogicalTypes].map(type => type.toUpperCase());
|
2017-03-27 18:50:55 -07:00
|
|
|
|
2017-05-01 09:57:17 -07:00
|
|
|
return sourceEntities.every(entity => logicalTypesInUppercase.includes(get(entity, 'logicalType')));
|
2017-03-27 18:50:55 -07:00
|
|
|
},
|
2017-03-24 20:27:43 -07:00
|
|
|
|
2017-04-02 15:40:43 -07:00
|
|
|
/**
|
|
|
|
* TODO:DSS-6719 refactor into mixin
|
|
|
|
* Clears recently shown user messages
|
|
|
|
*/
|
|
|
|
clearMessages() {
|
|
|
|
return setProperties(this, {
|
|
|
|
_message: '',
|
|
|
|
_alertType: ''
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO: DSS-6672 Extract to notifications service
|
|
|
|
* Helper method to update user when an async server update to the
|
|
|
|
* security specification is handled.
|
2017-05-19 08:45:05 -07:00
|
|
|
* @param {Promise|*} request the server request
|
2017-04-02 15:40:43 -07:00
|
|
|
* @param {String} [successMessage] optional _message for successful response
|
2017-05-19 08:45:05 -07:00
|
|
|
* @param { Boolean} [isSaving = false] optional flag indicating when the user intends to persist / save
|
2017-04-02 15:40:43 -07:00
|
|
|
*/
|
2017-05-19 08:45:05 -07:00
|
|
|
whenRequestCompletes(request, { successMessage, isSaving = false } = {}) {
|
2017-04-02 15:40:43 -07:00
|
|
|
Promise.resolve(request)
|
2017-04-11 16:02:41 -07:00
|
|
|
.then(({ status = 'error' }) => {
|
2017-05-01 09:57:17 -07:00
|
|
|
return status === 'ok'
|
|
|
|
? setProperties(this, {
|
|
|
|
_message: successMessage || successUpdating,
|
|
|
|
_alertType: 'success'
|
|
|
|
})
|
|
|
|
: Promise.reject(new Error(`Reason code for this is ${status}`));
|
2017-04-02 15:40:43 -07:00
|
|
|
})
|
2017-04-11 16:02:41 -07:00
|
|
|
.catch(err => {
|
2017-04-02 15:40:43 -07:00
|
|
|
let _message = `${failedUpdating} \n ${err}`;
|
|
|
|
let _alertType = 'danger';
|
|
|
|
|
2017-05-19 08:45:05 -07:00
|
|
|
if (get(this, 'isNewComplianceInfo') && !isSaving) {
|
2017-05-01 09:57:17 -07:00
|
|
|
_message = 'This dataset does not have any previously saved fields with a identifying information.';
|
2017-04-02 15:40:43 -07:00
|
|
|
_alertType = 'info';
|
|
|
|
}
|
|
|
|
|
|
|
|
setProperties(this, {
|
|
|
|
_message,
|
|
|
|
_alertType
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-05-19 08:45:05 -07:00
|
|
|
/**
|
|
|
|
* Sets the default classification for the given identifier field
|
|
|
|
* @param {String} identifierField
|
|
|
|
* @param {String} logicalType
|
|
|
|
*/
|
|
|
|
setDefaultClassification({ identifierField }, { value: logicalType = '' } = {}) {
|
|
|
|
const defaultTypeClassification = defaultFieldDataTypeClassification[logicalType] || null;
|
|
|
|
this.actions.onFieldClassificationChange.call(this, { identifierField }, { value: defaultTypeClassification });
|
|
|
|
},
|
|
|
|
|
2017-02-13 14:51:31 -08:00
|
|
|
actions: {
|
2017-03-27 18:26:09 -07:00
|
|
|
/**
|
2017-05-19 08:45:05 -07:00
|
|
|
* When a user updates the identifierFieldType in the DOM, update the backing store
|
|
|
|
* @param identifierField
|
|
|
|
* @param logicalType
|
|
|
|
* @param identifierType
|
2017-03-27 18:26:09 -07:00
|
|
|
*/
|
2017-05-19 08:45:05 -07:00
|
|
|
onFieldIdentifierTypeChange({ identifierField, logicalType }, { value: identifierType }) {
|
|
|
|
const complianceList = get(this, policyComplianceEntitiesKey);
|
|
|
|
// A reference to the current field in the compliance list if it exists
|
|
|
|
const currentFieldInComplianceList = complianceList.findBy('identifierField', identifierField);
|
|
|
|
const subjectId = fieldIdentifierTypes.subjectMember.value;
|
|
|
|
const updatedEntity = Object.assign({}, currentFieldInComplianceList, {
|
|
|
|
identifierField,
|
|
|
|
identifierType: subjectId === identifierType ? fieldIdentifierTypes.member.value : identifierType,
|
|
|
|
isSubject: subjectId === identifierType ? true : null,
|
|
|
|
logicalType: !currentFieldInComplianceList ? logicalType : void 0
|
|
|
|
});
|
|
|
|
let transientComplianceList = complianceList;
|
|
|
|
|
|
|
|
if (currentFieldInComplianceList) {
|
|
|
|
transientComplianceList = complianceList.filter(
|
|
|
|
({ identifierField: fieldName }) => fieldName !== identifierField
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
complianceList.setObjects([updatedEntity, ...transientComplianceList]);
|
|
|
|
this.setDefaultClassification({ identifierField });
|
2017-02-13 14:51:31 -08:00
|
|
|
},
|
|
|
|
|
2017-03-27 18:26:09 -07:00
|
|
|
/**
|
2017-05-19 08:45:05 -07:00
|
|
|
* Updates the logical type for the given identifierField
|
|
|
|
* @param {Object} field
|
|
|
|
* @prop {String} field.identifierField
|
|
|
|
* @param {Event} e the DOM change event
|
2017-03-27 18:26:09 -07:00
|
|
|
* @return {*}
|
|
|
|
*/
|
2017-05-19 08:45:05 -07:00
|
|
|
onFieldLogicalTypeChange(field, e) {
|
|
|
|
const { identifierField } = field;
|
|
|
|
const { value: logicalType } = e || {};
|
|
|
|
let sourceIdentifierField = get(this, policyComplianceEntitiesKey).findBy('identifierField', identifierField);
|
|
|
|
|
|
|
|
// If the identifierField does not current exist, invoke onFieldIdentifierChange to add it on the compliance list
|
|
|
|
if (!sourceIdentifierField) {
|
|
|
|
this.actions.onFieldIdentifierTypeChange.call(
|
|
|
|
this,
|
|
|
|
{
|
|
|
|
identifierField,
|
|
|
|
logicalType
|
|
|
|
},
|
|
|
|
{ value: fieldIdentifierTypes.none.value }
|
|
|
|
);
|
2017-05-19 11:57:46 -07:00
|
|
|
} else {
|
|
|
|
set(sourceIdentifierField, 'logicalType', logicalType);
|
2017-05-19 08:45:05 -07:00
|
|
|
}
|
2017-02-13 14:51:31 -08:00
|
|
|
|
2017-05-19 08:45:05 -07:00
|
|
|
return this.setDefaultClassification({ identifierField }, { value: logicalType });
|
2017-02-13 14:51:31 -08:00
|
|
|
},
|
|
|
|
|
2017-03-27 00:08:08 -07:00
|
|
|
/**
|
2017-05-19 08:45:05 -07:00
|
|
|
* Updates the filed classification
|
|
|
|
* @param {String} identifierField the identifier field to update the classification for
|
|
|
|
* @param {String} classification
|
|
|
|
* @return {*}
|
2017-03-27 00:08:08 -07:00
|
|
|
*/
|
2017-05-19 08:45:05 -07:00
|
|
|
onFieldClassificationChange({ identifierField }, { value: classification = null }) {
|
2017-05-19 10:52:58 -07:00
|
|
|
let fieldClassification = get(this, policyFieldClassificationKey);
|
|
|
|
// For datasets initially without a fieldClassification, the default value is null
|
|
|
|
if (fieldClassification === null) {
|
|
|
|
fieldClassification = set(this, policyFieldClassificationKey, {});
|
|
|
|
}
|
2017-03-27 18:26:09 -07:00
|
|
|
|
2017-04-02 15:40:43 -07:00
|
|
|
// TODO:DSS-6719 refactor into mixin
|
|
|
|
this.clearMessages();
|
|
|
|
|
2017-05-19 08:45:05 -07:00
|
|
|
if (!classification && identifierField in fieldClassification) {
|
|
|
|
const updatedFieldClassification = Object.assign({}, fieldClassification);
|
|
|
|
delete updatedFieldClassification[identifierField];
|
|
|
|
set(this, policyFieldClassificationKey, updatedFieldClassification);
|
|
|
|
return;
|
2017-03-27 00:08:08 -07:00
|
|
|
}
|
2017-05-19 08:45:05 -07:00
|
|
|
|
|
|
|
set(fieldClassification, identifierField, classification);
|
|
|
|
// Ember.computedProperties don't seem to have a direct way of depending on the shape of an object changing at
|
|
|
|
// runtime, notifyPropertyChange forces the computed property recompute the object reference
|
|
|
|
return notifyPropertyChange.call(this, policyFieldClassificationKey);
|
2017-03-27 00:08:08 -07:00
|
|
|
},
|
|
|
|
|
2017-03-27 18:30:44 -07:00
|
|
|
/**
|
2017-05-19 08:45:05 -07:00
|
|
|
* Updates the source object representing the current datasetClassification map
|
|
|
|
* @param {String} classifier the property on the datasetClassification to update
|
|
|
|
* @param {Boolean} value flag indicating if this dataset contains member data for the specified classifier
|
2017-03-27 18:30:44 -07:00
|
|
|
*/
|
2017-05-19 08:45:05 -07:00
|
|
|
onChangeDatasetClassification(classifier, value) {
|
|
|
|
let sourceDatasetClassification = getWithDefault(this, datasetClassificationKey, {});
|
|
|
|
|
|
|
|
// For datasets initially without a datasetClassification, the default value is null
|
|
|
|
if (sourceDatasetClassification === null) {
|
|
|
|
sourceDatasetClassification = set(this, datasetClassificationKey, {});
|
|
|
|
}
|
|
|
|
|
|
|
|
return set(sourceDatasetClassification, classifier, value);
|
2017-02-13 14:51:31 -08:00
|
|
|
},
|
|
|
|
|
2017-03-27 18:50:55 -07:00
|
|
|
/**
|
|
|
|
* If all validity checks are passed, invoke onSave action on controller
|
|
|
|
*/
|
|
|
|
saveCompliance() {
|
2017-05-19 08:45:05 -07:00
|
|
|
const complianceList = get(this, policyComplianceEntitiesKey);
|
|
|
|
const idFieldsHaveValidLogicalType = this.ensureIdTypesHaveLogicalType(
|
|
|
|
complianceList.filter(({ identifierType }) => fieldIdentifierTypeIds.includes(identifierType))
|
|
|
|
);
|
2017-03-27 18:50:55 -07:00
|
|
|
|
2017-05-19 08:45:05 -07:00
|
|
|
if (idFieldsHaveValidLogicalType) {
|
|
|
|
return this.whenRequestCompletes(get(this, 'onSave')(), { isSaving: true });
|
2017-03-27 18:50:55 -07:00
|
|
|
}
|
2017-05-19 08:45:05 -07:00
|
|
|
|
|
|
|
setProperties(this, {
|
|
|
|
_message: missingTypes,
|
|
|
|
_alertType: 'danger'
|
|
|
|
});
|
2017-03-27 18:50:55 -07:00
|
|
|
},
|
|
|
|
|
2017-02-13 14:51:31 -08:00
|
|
|
// Rolls back changes made to the compliance spec to current
|
|
|
|
// server state
|
2017-03-27 18:26:09 -07:00
|
|
|
resetCompliance() {
|
2017-04-02 15:40:43 -07:00
|
|
|
const options = {
|
|
|
|
successMessage: 'Field classification has been reset to the previously saved state.'
|
|
|
|
};
|
|
|
|
this.whenRequestCompletes(get(this, 'onReset')(), options);
|
2017-02-13 14:51:31 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|