github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/app/serializers/application.js (about) 1 import { copy } from 'ember-copy'; 2 import { get } from '@ember/object'; 3 import { makeArray } from '@ember/array'; 4 import JSONSerializer from '@ember-data/serializer/json'; 5 import { pluralize, singularize } from 'ember-inflector'; 6 import removeRecord from '../utils/remove-record'; 7 import { assign } from '@ember/polyfills'; 8 import classic from 'ember-classic-decorator'; 9 import { camelize, capitalize, dasherize } from '@ember/string'; 10 @classic 11 export default class Application extends JSONSerializer { 12 primaryKey = 'ID'; 13 14 /** 15 A list of keys that are converted to empty arrays if their value is null. 16 17 arrayNullOverrides = ['Array']; 18 { Array: null } => { Array: [] } 19 20 @property arrayNullOverrides 21 @type String[] 22 */ 23 arrayNullOverrides = null; 24 25 /** 26 A list of keys that are converted to empty objects if their value is null. 27 28 objectNullOverrides = ['Object']; 29 { Object: null } => { Object: {} } 30 31 @property objectNullOverrides 32 @type String[] 33 */ 34 objectNullOverrides = null; 35 36 /** 37 A list of keys or objects to convert a map into an array of maps with the original map keys as Name properties. 38 39 mapToArray = ['Map']; 40 { Map: { a: { x: 1 } } } => { Map: [ { Name: 'a', x: 1 }] } 41 42 mapToArray = [{ beforeName: 'M', afterName: 'Map' }]; 43 { M: { a: { x: 1 } } } => { Map: [ { Name: 'a', x: 1 }] } 44 45 @property mapToArray 46 @type (String|Object)[] 47 */ 48 mapToArray = null; 49 50 /** 51 A list of keys for nanosecond timestamps that will be split into two properties: `separateNanos = ['Time']` will 52 produce a `Time` property with a millisecond timestamp and `TimeNanos` with the nanoseconds alone. 53 54 separateNanos = ['Time']; 55 { Time: 1607839992000100000 } => { Time: 1607839992000, TimeNanos: 100096 } 56 57 @property separateNanos 58 @type String[] 59 */ 60 separateNanos = null; 61 62 keyForAttribute(attr) { 63 return capitalize(camelize(attr)); 64 } 65 66 keyForRelationship(attr, relationshipType) { 67 const key = `${capitalize(camelize(singularize(attr)))}ID`; 68 return relationshipType === 'hasMany' ? pluralize(key) : key; 69 } 70 71 // Modeled after the pushPayload for ember-data/serializers/rest 72 pushPayload(store, payload) { 73 const documentHash = { 74 data: [], 75 included: [], 76 }; 77 78 Object.keys(payload).forEach((key) => { 79 const modelName = this.modelNameFromPayloadKey(key); 80 const serializer = store.serializerFor(modelName); 81 const type = store.modelFor(modelName); 82 83 makeArray(payload[key]).forEach((hash) => { 84 const { data, included } = serializer.normalize(type, hash, key); 85 documentHash.data.push(data); 86 if (included) { 87 documentHash.included.push(...included); 88 } 89 }); 90 }); 91 92 store.push(documentHash); 93 } 94 95 normalize(modelClass, hash) { 96 if (hash) { 97 if (this.arrayNullOverrides) { 98 this.arrayNullOverrides.forEach((key) => { 99 if (!hash[key]) { 100 hash[key] = []; 101 } 102 }); 103 } 104 if (this.objectNullOverrides) { 105 this.objectNullOverrides.forEach((key) => { 106 if (!hash[key]) { 107 hash[key] = {}; 108 } 109 }); 110 } 111 112 if (this.mapToArray) { 113 this.mapToArray.forEach((conversion) => { 114 let apiKey, uiKey; 115 116 if (conversion.beforeName) { 117 apiKey = conversion.beforeName; 118 uiKey = conversion.afterName; 119 } else { 120 apiKey = conversion; 121 uiKey = conversion; 122 } 123 124 const map = hash[apiKey] || {}; 125 126 hash[uiKey] = Object.keys(map) 127 .sort() 128 .map((mapKey) => { 129 const propertiesForKey = map[mapKey] || {}; 130 const convertedMap = { Name: mapKey }; 131 132 assign(convertedMap, propertiesForKey); 133 134 return convertedMap; 135 }); 136 }); 137 } 138 139 if (this.separateNanos) { 140 this.separateNanos.forEach((key) => { 141 const timeWithNanos = hash[key]; 142 hash[`${key}Nanos`] = timeWithNanos % 1000000; 143 hash[key] = Math.floor(timeWithNanos / 1000000); 144 }); 145 } 146 } 147 148 return super.normalize(modelClass, hash); 149 } 150 151 normalizeFindAllResponse(store, modelClass) { 152 const result = super.normalizeFindAllResponse(...arguments); 153 this.cullStore(store, modelClass.modelName, result.data); 154 return result; 155 } 156 157 // When records are removed server-side, and therefore don't show up in requests, 158 // the local copies of those records need to be unloaded from the store. 159 cullStore(store, type, records, storeFilter = () => true) { 160 const newRecords = copy(records).filter((record) => get(record, 'id')); 161 const oldRecords = store.peekAll(type); 162 oldRecords 163 .filter((record) => get(record, 'id')) 164 .filter(storeFilter) 165 .forEach((old) => { 166 const newRecord = newRecords.find( 167 (record) => get(record, 'id') === get(old, 'id') 168 ); 169 if (!newRecord) { 170 removeRecord(store, old); 171 } else { 172 newRecords.removeObject(newRecord); 173 } 174 }); 175 } 176 177 modelNameFromPayloadKey(key) { 178 return singularize(dasherize(key)); 179 } 180 }