| 
									
										
										
										
											2017-02-13 14:52:14 -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-01 22:58:16 -07:00
										 |  |  | import { defaultFieldDataTypeClassification, classifiers, datasetClassifiers } from 'wherehows-web/constants'; | 
					
						
							| 
									
										
										
										
											2017-02-13 14:52:14 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  | const { | 
					
						
							|  |  |  |   get, | 
					
						
							|  |  |  |   set, | 
					
						
							|  |  |  |   isBlank, | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |   isPresent, | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |   computed, | 
					
						
							|  |  |  |   getWithDefault, | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |   setProperties, | 
					
						
							| 
									
										
										
										
											2017-04-18 15:59:28 -07:00
										 |  |  |   Component, | 
					
						
							|  |  |  |   String: { htmlSafe } | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  | } = Ember; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 22:58:16 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * String constant identifying the classified fields on the security spec | 
					
						
							|  |  |  |  * @type {String} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  | const sourceClassificationKey = 'securitySpecification.classification'; | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 22:58:16 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * String constant identifying the datasetClassification on the security spec | 
					
						
							|  |  |  |  * @type {String} | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const datasetClassificationKey = 'securitySpecification.datasetClassification'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * List of logical types  / field level data types | 
					
						
							|  |  |  |  * https://iwww.corp.linkedin.com/wiki/cf/display/DWH/List+of+Metadata+for+Data+Sets
 | 
					
						
							|  |  |  |  * @type {Array} | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const logicalTypes = Object.keys(defaultFieldDataTypeClassification); | 
					
						
							| 
									
										
										
										
											2017-05-01 22:58:16 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * A list of available keys for the datasetClassification map on the security specification | 
					
						
							|  |  |  |  * @type {Array} | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const datasetClassifiersKeys = Object.keys(datasetClassifiers); | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TODO: DSS-6671 Extract to constants module
 | 
					
						
							|  |  |  | const successUpdating = 'Your changes have been successfully saved!'; | 
					
						
							|  |  |  | const failedUpdating = 'Oops! We are having trouble updating this dataset at the moment.'; | 
					
						
							| 
									
										
										
										
											2017-04-02 15:40:43 -07:00
										 |  |  | const missingTypes = 'Looks like some fields are marked as `Confidential` or ' + | 
					
						
							|  |  |  |   '`Highly Confidential` but do not have a specified `Field Format`?'; | 
					
						
							| 
									
										
										
										
											2017-04-18 15:59:28 -07:00
										 |  |  | const hiddenTrackingFieldsMsg = htmlSafe( | 
					
						
							|  |  |  |   '<p>Hey! Just a heads up that some fields in this dataset have been hidden from the table(s) below. ' + | 
					
						
							|  |  |  |   '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-02 15:40:43 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * 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(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default Component.extend({ | 
					
						
							|  |  |  |   sortColumnWithName: 'identifierField', | 
					
						
							|  |  |  |   filterBy: 'identifierField', | 
					
						
							|  |  |  |   sortDirection: 'asc', | 
					
						
							| 
									
										
										
										
											2017-02-13 14:52:14 -08:00
										 |  |  |   searchTerm: '', | 
					
						
							| 
									
										
										
										
											2017-04-18 15:59:28 -07:00
										 |  |  |   hiddenTrackingFields: hiddenTrackingFieldsMsg, | 
					
						
							| 
									
										
										
										
											2017-02-13 14:52:14 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |   // Map classifiers to options better consumed by  drop down
 | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |   classifiers: classifiers.map(value => ({ | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |     value, | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |     label: formatAsCapitalizedStringWithSpaces(value) | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |   })), | 
					
						
							| 
									
										
										
										
											2017-03-31 08:41:50 -07:00
										 |  |  |   // Map logicalTypes to options better consumed by  drop down
 | 
					
						
							|  |  |  |   logicalTypes: ['', ...logicalTypes].map(value => { | 
					
						
							|  |  |  |     const label = value ? | 
					
						
							| 
									
										
										
										
											2017-04-11 10:34:20 -07:00
										 |  |  |       value.replace(/_/g, ' ') | 
					
						
							| 
									
										
										
										
											2017-03-31 08:41:50 -07:00
										 |  |  |         .replace(/([A-Z]{3,})/g, f => f.toLowerCase().capitalize()) : | 
					
						
							| 
									
										
										
										
											2017-05-02 10:18:09 -07:00
										 |  |  |       'Not Specified'; | 
					
						
							| 
									
										
										
										
											2017-03-31 08:41:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       value, | 
					
						
							|  |  |  |       label | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-03 10:55:19 -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)); | 
					
						
							|  |  |  |   }), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 22:58:16 -07:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * 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() { | 
					
						
							| 
									
										
										
										
											2017-05-02 08:50:36 -07:00
										 |  |  |     const sourceDatasetClassification = get(this, datasetClassificationKey) || {}; | 
					
						
							| 
									
										
										
										
											2017-05-01 22:58:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return Object.keys(datasetClassifiers).reduce((datasetClassification, classifier) => { | 
					
						
							|  |  |  |       datasetClassification[classifier] = { | 
					
						
							|  |  |  |         value: sourceDatasetClassification[classifier], | 
					
						
							|  |  |  |         label: datasetClassifiers[classifier] | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return datasetClassification; | 
					
						
							|  |  |  |     }, {}); | 
					
						
							|  |  |  |   }), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Creates a lookup table of fieldNames to classification | 
					
						
							|  |  |  |    *   Also, the expectation is that the association from fieldName -> classification | 
					
						
							|  |  |  |    *   is one-to-one hence no check to ensure a fieldName gets clobbered | 
					
						
							|  |  |  |    *   in the lookup assignment | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |   fieldNameToClass: computed( | 
					
						
							| 
									
										
										
										
											2017-05-01 22:58:16 -07:00
										 |  |  |     `${sourceClassificationKey}.{confidential,limitedDistribution,highlyConfidential}.[]`, | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |     function () { | 
					
						
							|  |  |  |       const sourceClasses = getWithDefault(this, sourceClassificationKey, []); | 
					
						
							|  |  |  |       // Creates a lookup table of fieldNames to classification
 | 
					
						
							|  |  |  |       //   Also, the expectation is that the association from fieldName -> classification
 | 
					
						
							|  |  |  |       //   is one-to-one hence no check to ensure a fieldName gets clobbered
 | 
					
						
							|  |  |  |       //   in the lookup assignment
 | 
					
						
							|  |  |  |       return Object.keys(sourceClasses) | 
					
						
							|  |  |  |         .reduce((lookup, classificationKey) => | 
					
						
							|  |  |  |             // For the provided classificationKey, iterate over it's fieldNames,
 | 
					
						
							|  |  |  |             //   and assign the classificationKey to the fieldName in the table
 | 
					
						
							|  |  |  |             (sourceClasses[classificationKey] || []).reduce((lookup, field) => { | 
					
						
							|  |  |  |               const { identifierField } = field; | 
					
						
							|  |  |  |               // cKey -> 1...fieldNameList => fieldName -> cKey
 | 
					
						
							|  |  |  |               lookup[identifierField] = classificationKey; | 
					
						
							|  |  |  |               return lookup; | 
					
						
							|  |  |  |             }, lookup), | 
					
						
							|  |  |  |           {} | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |     }), | 
					
						
							| 
									
										
										
										
											2017-02-13 14:52:14 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Lists all the dataset fields found in the `columns` api, and intersects | 
					
						
							|  |  |  |    *   each with the currently classified field names in | 
					
						
							|  |  |  |    *   securitySpecification.classification or null if not found | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   classificationDataFields: computed( | 
					
						
							| 
									
										
										
										
											2017-05-01 22:58:16 -07:00
										 |  |  |     `${sourceClassificationKey}.{confidential,limitedDistribution,highlyConfidential}.[]`, | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |     'schemaFieldNamesMappedToDataTypes', | 
					
						
							|  |  |  |     function () { | 
					
						
							|  |  |  |       // Set default or if already in policy, retrieve current values from
 | 
					
						
							|  |  |  |       //   privacyCompliancePolicy.compliancePurgeEntities
 | 
					
						
							|  |  |  |       return getWithDefault( | 
					
						
							|  |  |  |         this, 'schemaFieldNamesMappedToDataTypes', [] | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |       ).map(({ fieldName: identifierField, dataType }) => { | 
					
						
							|  |  |  |         // Get the current classification list
 | 
					
						
							| 
									
										
										
										
											2017-04-02 14:05:57 -07:00
										 |  |  |         const currentClassLookup = get(this, 'fieldNameToClass'); | 
					
						
							|  |  |  |         const classification = currentClassLookup[identifierField]; | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // If the classification type exists, then find the identifierField, and
 | 
					
						
							|  |  |  |         //   assign to field, otherwise null
 | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |         //   Rather than assigning the default classification here, nulling gives the benefit of allowing
 | 
					
						
							|  |  |  |         //   subsequent consumer know that this field did not have a previous classification
 | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |         const field = classification ? | 
					
						
							|  |  |  |           get(this, `${sourceClassificationKey}.${classification}`) | 
					
						
							|  |  |  |             .findBy('identifierField', identifierField) : | 
					
						
							|  |  |  |           null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Extract the logicalType from the field
 | 
					
						
							|  |  |  |         const logicalType = isPresent(field) ? field.logicalType : null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Map to a new literal containing these props
 | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |           dataType, | 
					
						
							|  |  |  |           identifierField, | 
					
						
							|  |  |  |           classification, | 
					
						
							|  |  |  |           logicalType | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-02-13 14:52:14 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |   ), | 
					
						
							| 
									
										
										
										
											2017-02-13 14:52:14 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-18 15:59:28 -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. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   containsHiddenTrackingFields: computed( | 
					
						
							|  |  |  |     'classificationDataFieldsSansHiddenTracking.length', | 
					
						
							|  |  |  |     function () { | 
					
						
							|  |  |  |       // If their is a diff in complianceDataFields and complianceDataFieldsSansHiddenTracking,
 | 
					
						
							|  |  |  |       //   then we have hidden tracking fields
 | 
					
						
							|  |  |  |       return get(this, 'classificationDataFieldsSansHiddenTracking.length') !== get(this, 'classificationDataFields.length'); | 
					
						
							|  |  |  |     }), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @type {Array.<Object>} Filters the mapped confidential data fields without `kafka type` | 
					
						
							|  |  |  |    *   tracking headers | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   classificationDataFieldsSansHiddenTracking: computed('classificationDataFields.[]', function () { | 
					
						
							|  |  |  |     return get(this, 'classificationDataFields') | 
					
						
							| 
									
										
										
										
											2017-04-25 10:50:02 -07:00
										 |  |  |       .filter(({ identifierField }) => !isTrackingHeaderField(identifierField)); | 
					
						
							| 
									
										
										
										
											2017-04-18 15:59:28 -07:00
										 |  |  |   }), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * TODO: DSS-6672 Extract to notifications service | 
					
						
							|  |  |  |    * Helper method to update user when an async server update to the | 
					
						
							|  |  |  |    * security specification is handled. | 
					
						
							|  |  |  |    * @param {XMLHttpRequest|Promise|jqXHR|*} request the server request | 
					
						
							| 
									
										
										
										
											2017-04-01 14:03:13 -07:00
										 |  |  |    * @param {String} [successMessage] optional _message for successful response | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |    */ | 
					
						
							| 
									
										
										
										
											2017-03-31 17:48:28 -07:00
										 |  |  |   whenRequestCompletes(request, { successMessage } = {}) { | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |     Promise.resolve(request) | 
					
						
							| 
									
										
										
										
											2017-04-11 16:02:41 -07:00
										 |  |  |       .then(({ status = 'error' }) => { | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |         // The server api currently responds with an object containing
 | 
					
						
							| 
									
										
										
										
											2017-04-11 16:02:41 -07:00
										 |  |  |         //   a status when complete
 | 
					
						
							|  |  |  |         return status === 'ok' ? | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |           setProperties(this, { | 
					
						
							| 
									
										
										
										
											2017-04-01 14:03:13 -07:00
										 |  |  |             _message: successMessage || successUpdating, | 
					
						
							|  |  |  |             _alertType: 'success' | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |           }) : | 
					
						
							| 
									
										
										
										
											2017-04-11 16:02:41 -07:00
										 |  |  |           Promise.reject(new Error(`Reason code for this is ${status}`)); | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |       }) | 
					
						
							| 
									
										
										
										
											2017-04-11 16:02:41 -07:00
										 |  |  |       .catch(err => { | 
					
						
							| 
									
										
										
										
											2017-04-01 14:03:13 -07:00
										 |  |  |         let _message = `${failedUpdating} \n ${err}`; | 
					
						
							|  |  |  |         let _alertType = 'danger'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-11 16:02:41 -07:00
										 |  |  |         if (get(this, 'isNewSecuritySpecification')) { | 
					
						
							| 
									
										
										
										
											2017-04-01 14:16:28 -07:00
										 |  |  |           _message = 'This dataset does not have any ' + | 
					
						
							|  |  |  |             'previously saved fields with a Security Classification.'; | 
					
						
							|  |  |  |           _alertType = 'info'; | 
					
						
							| 
									
										
										
										
											2017-04-01 14:03:13 -07:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |         setProperties(this, { | 
					
						
							| 
									
										
										
										
											2017-04-01 14:03:13 -07:00
										 |  |  |           _message, | 
					
						
							|  |  |  |           _alertType | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -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: '' | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Takes an identifierField and a logicalType and updates the field on the | 
					
						
							|  |  |  |    * classification if it exists. Otherwise this is a no-op | 
					
						
							|  |  |  |    * @param {String} identifierField | 
					
						
							|  |  |  |    * @param {String} logicalType the type to be updated | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   changeFieldLogicalType(identifierField, logicalType) { | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |     const nextProps = { identifierField, logicalType }; | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |     // The current classification name for the candidate identifier
 | 
					
						
							| 
									
										
										
										
											2017-04-02 14:05:57 -07:00
										 |  |  |     const currentClassLookup = get(this, 'fieldNameToClass'); | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |     const defaultClassification = getWithDefault(this, `${sourceClassificationKey}.${defaultFieldDataTypeClassification[logicalType]}`, []); | 
					
						
							|  |  |  |     let currentClassificationName = currentClassLookup[identifierField]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * If the field does not already have a classification, we set it's default | 
					
						
							|  |  |  |      * @link classificationDataFields | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     if (isBlank(currentClassificationName)) { | 
					
						
							|  |  |  |       currentClassificationName = defaultFieldDataTypeClassification[logicalType]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |     // The current classification list
 | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |     const currentClassification = get(this, `${sourceClassificationKey}.${currentClassificationName}`); | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |     if (!Array.isArray(currentClassification)) { | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |       throw new Error(`
 | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |       You have specified a classification object that is not a list ${currentClassification}. | 
					
						
							| 
									
										
										
										
											2017-04-02 14:05:57 -07:00
										 |  |  |       Ensure that the classification for this identifierField (${identifierField}) is | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |       set before attempting to change the logicalType. | 
					
						
							|  |  |  |       `);
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |     const field = currentClassification.findBy('identifierField', identifierField); | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |     // Since this is change on the logical type, if the field already exists in a classification
 | 
					
						
							|  |  |  |     //   list, we remove it to allow applying the default for the new field type, below
 | 
					
						
							|  |  |  |     if (isPresent(field)) { | 
					
						
							|  |  |  |       // Remove identifierField from list
 | 
					
						
							|  |  |  |       currentClassification.setObjects( | 
					
						
							|  |  |  |         currentClassification.filter( | 
					
						
							|  |  |  |           ({ identifierField: fieldName }) => fieldName !== identifierField) | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |     // Update the default classification list
 | 
					
						
							|  |  |  |     return defaultClassification.setObjects([nextProps, ...defaultClassification]); | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-02 14:35:51 -07:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Checks that each field in a given source classification has a value | 
					
						
							|  |  |  |    *   for the logicalType | 
					
						
							|  |  |  |    * @param {Array} sourceClassification | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   ensureFieldsContainLogicalType: (sourceClassification = []) => | 
					
						
							|  |  |  |     sourceClassification.every(({ logicalType }) => logicalType), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-13 14:52:14 -08:00
										 |  |  |   actions: { | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Updates the logical type for a field with the provided identifierField | 
					
						
							|  |  |  |      * @param {String} identifierField the name of the field to update | 
					
						
							|  |  |  |      * @param {String} logicalType the updated logical type | 
					
						
							|  |  |  |      * @return {*|String|void} | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     updateLogicalType({ identifierField }, { value: logicalType }) { | 
					
						
							| 
									
										
										
										
											2017-04-02 15:40:43 -07:00
										 |  |  |       // TODO:DSS-6719 refactor into mixin
 | 
					
						
							|  |  |  |       this.clearMessages(); | 
					
						
							| 
									
										
										
										
											2017-03-31 12:12:31 -07:00
										 |  |  |       return this.changeFieldLogicalType(identifierField, logicalType); | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Toggles the provided identifierField onto a classification list | 
					
						
							|  |  |  |      *   on securitySpecification.classification, identified by the provided | 
					
						
							|  |  |  |      *   classKey. | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |      * @param {Object} props field attributes for the field to update on the classification list | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |      * @param {String} classKey the name of the class to add, or potentially | 
					
						
							|  |  |  |      *   remove the identifierField from | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |     updateClassification(props, { value: classKey }) { | 
					
						
							|  |  |  |       const { identifierField } = props; | 
					
						
							| 
									
										
										
										
											2017-04-02 14:05:57 -07:00
										 |  |  |       // fieldNames can be paths i.e. identifierField.identifierPath.subPath
 | 
					
						
							|  |  |  |       //   therefore, using Ember's `path lookup` syntax will not work
 | 
					
						
							|  |  |  |       const currentClassLookup = get(this, 'fieldNameToClass'); | 
					
						
							|  |  |  |       const currentClass = currentClassLookup[identifierField]; | 
					
						
							| 
									
										
										
										
											2017-04-02 15:40:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // TODO:DSS-6719 refactor into mixin
 | 
					
						
							|  |  |  |       this.clearMessages(); | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |       // Since the association from identifierField -> classification is 1-to-1
 | 
					
						
							|  |  |  |       //  ensure that we do not currently have this identifierField
 | 
					
						
							|  |  |  |       // in any other classification lists by checking that the lookup is void
 | 
					
						
							|  |  |  |       if (!isBlank(currentClass)) { | 
					
						
							|  |  |  |         // Get the current classification list
 | 
					
						
							|  |  |  |         const currentClassification = get( | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |           this, | 
					
						
							|  |  |  |           `${sourceClassificationKey}.${currentClass}` | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2017-04-02 14:05:57 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |         // Remove identifierField from list
 | 
					
						
							| 
									
										
										
										
											2017-04-02 14:05:57 -07:00
										 |  |  |         currentClassification.setObjects( | 
					
						
							|  |  |  |           currentClassification.filter( | 
					
						
							|  |  |  |             ({ identifierField: fieldName }) => fieldName !== identifierField) | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-13 14:52:14 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |       if (classKey) { | 
					
						
							|  |  |  |         // Get the candidate list
 | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |         let classification = get( | 
					
						
							|  |  |  |           this, | 
					
						
							|  |  |  |           `${sourceClassificationKey}.${classKey}` | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |         // In the case that the list is not pre-populated,
 | 
					
						
							|  |  |  |         //  the value will be the default null, array ops won't work here
 | 
					
						
							|  |  |  |         //  ...so make array
 | 
					
						
							|  |  |  |         if (!classification) { | 
					
						
							|  |  |  |           classification = set(this, `${sourceClassificationKey}.${classKey}`, []); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Finally perform operation
 | 
					
						
							| 
									
										
										
										
											2017-04-28 22:02:46 -07:00
										 |  |  |         classification.addObject(Object.assign({}, props)); | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-02-13 14:52:14 -08:00
										 |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 22:58:16 -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 | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     didChangeDatasetClassification(classifier, value) { | 
					
						
							| 
									
										
										
										
											2017-05-02 08:50:36 -07:00
										 |  |  |       let sourceDatasetClassification = getWithDefault(this, datasetClassificationKey, {}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // For datasets initially without a datasetClassification, the default value is null
 | 
					
						
							|  |  |  |       if (sourceDatasetClassification === null) { | 
					
						
							|  |  |  |         sourceDatasetClassification = set(this, datasetClassificationKey, {}); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 22:58:16 -07:00
										 |  |  |       return set(sourceDatasetClassification, classifier, value); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Notify controller to propagate changes | 
					
						
							|  |  |  |      * @return {Boolean} | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |     saveSecuritySpecification() { | 
					
						
							| 
									
										
										
										
											2017-04-02 14:35:51 -07:00
										 |  |  |       /** | 
					
						
							|  |  |  |        * For Each classifier ensure that the fields on the securitySpec | 
					
						
							|  |  |  |        *   contain a valid `logicalType` attribute | 
					
						
							|  |  |  |        * @type {Boolean} | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       const classedFieldsHaveLogicalType = classifiers.every(classifier => | 
					
						
							|  |  |  |         this.ensureFieldsContainLogicalType( | 
					
						
							|  |  |  |           getWithDefault(this, `${sourceClassificationKey}.${classifier}`, []) | 
					
						
							|  |  |  |         )); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (classedFieldsHaveLogicalType) { | 
					
						
							|  |  |  |         this.whenRequestCompletes(get(this, 'onSave')()); | 
					
						
							| 
									
										
										
										
											2017-04-02 15:40:43 -07:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         setProperties(this, { | 
					
						
							|  |  |  |           _message: missingTypes, | 
					
						
							|  |  |  |           _alertType: 'danger' | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-04-02 14:35:51 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-13 14:52:14 -08:00
										 |  |  |       return false; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-29 14:50:16 -07:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Rolls back changes made to the compliance spec to current | 
					
						
							|  |  |  |      * server state | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2017-03-28 00:45:34 -07:00
										 |  |  |     resetSecuritySpecification() { | 
					
						
							| 
									
										
										
										
											2017-03-31 17:48:28 -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:52:14 -08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }); |