github.com/emate/nomad@v0.8.2-wo-binpacking/ui/app/adapters/watchable.js (about) 1 import { get, computed } from '@ember/object'; 2 import { assign } from '@ember/polyfills'; 3 import { inject as service } from '@ember/service'; 4 import queryString from 'npm:query-string'; 5 import ApplicationAdapter from './application'; 6 import { AbortError } from 'ember-data/adapters/errors'; 7 8 export default ApplicationAdapter.extend({ 9 watchList: service(), 10 store: service(), 11 12 xhrs: computed(function() { 13 return { 14 list: {}, 15 track(key, xhr) { 16 if (this.list[key]) { 17 this.list[key].push(xhr); 18 } else { 19 this.list[key] = [xhr]; 20 } 21 }, 22 cancel(key) { 23 while (this.list[key] && this.list[key].length) { 24 this.remove(key, this.list[key][0]); 25 } 26 }, 27 remove(key, xhr) { 28 if (this.list[key]) { 29 xhr.abort(); 30 this.list[key].removeObject(xhr); 31 } 32 }, 33 }; 34 }), 35 36 ajaxOptions() { 37 const ajaxOptions = this._super(...arguments); 38 const key = this.xhrKey(...arguments); 39 40 const previousBeforeSend = ajaxOptions.beforeSend; 41 ajaxOptions.beforeSend = function(jqXHR) { 42 if (previousBeforeSend) { 43 previousBeforeSend(...arguments); 44 } 45 this.get('xhrs').track(key, jqXHR); 46 jqXHR.always(() => { 47 this.get('xhrs').remove(key, jqXHR); 48 }); 49 }; 50 51 return ajaxOptions; 52 }, 53 54 xhrKey(url /* method, options */) { 55 return url; 56 }, 57 58 findAll(store, type, sinceToken, snapshotRecordArray, additionalParams = {}) { 59 const params = assign(this.buildQuery(), additionalParams); 60 const url = this.urlForFindAll(type.modelName); 61 62 if (get(snapshotRecordArray || {}, 'adapterOptions.watch')) { 63 params.index = this.get('watchList').getIndexFor(url); 64 } 65 66 return this.ajax(url, 'GET', { 67 data: params, 68 }); 69 }, 70 71 findRecord(store, type, id, snapshot, additionalParams = {}) { 72 let [url, params] = this.buildURL(type.modelName, id, snapshot, 'findRecord').split('?'); 73 params = assign(queryString.parse(params) || {}, this.buildQuery(), additionalParams); 74 75 if (get(snapshot || {}, 'adapterOptions.watch')) { 76 params.index = this.get('watchList').getIndexFor(url); 77 } 78 79 return this.ajax(url, 'GET', { 80 data: params, 81 }).catch(error => { 82 if (error instanceof AbortError) { 83 return; 84 } 85 throw error; 86 }); 87 }, 88 89 reloadRelationship(model, relationshipName, watch = false) { 90 const relationship = model.relationshipFor(relationshipName); 91 if (relationship.kind !== 'belongsTo' && relationship.kind !== 'hasMany') { 92 throw new Error( 93 `${relationship.key} must be a belongsTo or hasMany, instead it was ${relationship.kind}` 94 ); 95 } else { 96 const url = model[relationship.kind](relationship.key).link(); 97 let params = {}; 98 99 if (watch) { 100 params.index = this.get('watchList').getIndexFor(url); 101 } 102 103 // Avoid duplicating existing query params by passing them to ajax 104 // in the URL and in options.data 105 if (url.includes('?')) { 106 const paramsInUrl = queryString.parse(url.split('?')[1]); 107 Object.keys(paramsInUrl).forEach(key => { 108 delete params[key]; 109 }); 110 } 111 112 return this.ajax(url, 'GET', { 113 data: params, 114 }).then( 115 json => { 116 const store = this.get('store'); 117 const normalizeMethod = 118 relationship.kind === 'belongsTo' 119 ? 'normalizeFindBelongsToResponse' 120 : 'normalizeFindHasManyResponse'; 121 const serializer = store.serializerFor(relationship.type); 122 const modelClass = store.modelFor(relationship.type); 123 const normalizedData = serializer[normalizeMethod](store, modelClass, json); 124 store.push(normalizedData); 125 }, 126 error => { 127 if (error instanceof AbortError) { 128 return relationship.kind === 'belongsTo' ? {} : []; 129 } 130 throw error; 131 } 132 ); 133 } 134 }, 135 136 handleResponse(status, headers, payload, requestData) { 137 // Some browsers lowercase all headers. Others keep them 138 // case sensitive. 139 const newIndex = headers['x-nomad-index'] || headers['X-Nomad-Index']; 140 if (newIndex) { 141 this.get('watchList').setIndexFor(requestData.url, newIndex); 142 } 143 144 return this._super(...arguments); 145 }, 146 147 cancelFindRecord(modelName, id) { 148 if (!modelName || id == null) { 149 return; 150 } 151 const url = this.urlForFindRecord(id, modelName); 152 this.get('xhrs').cancel(url); 153 }, 154 155 cancelFindAll(modelName) { 156 if (!modelName) { 157 return; 158 } 159 let url = this.urlForFindAll(modelName); 160 const params = queryString.stringify(this.buildQuery()); 161 if (params) { 162 url = `${url}?${params}`; 163 } 164 this.get('xhrs').cancel(url); 165 }, 166 167 cancelReloadRelationship(model, relationshipName) { 168 if (!model || !relationshipName) { 169 return; 170 } 171 const relationship = model.relationshipFor(relationshipName); 172 if (relationship.kind !== 'belongsTo' && relationship.kind !== 'hasMany') { 173 throw new Error( 174 `${relationship.key} must be a belongsTo or hasMany, instead it was ${relationship.kind}` 175 ); 176 } else { 177 const url = model[relationship.kind](relationship.key).link(); 178 this.get('xhrs').cancel(url); 179 } 180 }, 181 });