github.com/outbrain/consul@v1.4.5/ui-v2/app/adapters/kv.js (about) 1 import Adapter, { 2 REQUEST_CREATE, 3 REQUEST_UPDATE, 4 REQUEST_DELETE, 5 DATACENTER_QUERY_PARAM as API_DATACENTER_KEY, 6 } from './application'; 7 import isFolder from 'consul-ui/utils/isFolder'; 8 import injectableRequestToJQueryAjaxHash from 'consul-ui/utils/injectableRequestToJQueryAjaxHash'; 9 import { typeOf } from '@ember/utils'; 10 import { get } from '@ember/object'; 11 import { inject as service } from '@ember/service'; 12 13 import keyToArray from 'consul-ui/utils/keyToArray'; 14 import removeNull from 'consul-ui/utils/remove-null'; 15 16 import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/kv'; 17 import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc'; 18 import { PUT as HTTP_PUT, DELETE as HTTP_DELETE } from 'consul-ui/utils/http/method'; 19 import { OK as HTTP_OK } from 'consul-ui/utils/http/status'; 20 21 const API_KEYS_KEY = 'keys'; 22 const stringify = function(obj) { 23 if (typeOf(obj) === 'string') { 24 return obj; 25 } 26 return JSON.stringify(obj); 27 }; 28 export default Adapter.extend({ 29 // There is no code path that can avoid the payload of a PUT request from 30 // going via JSON.stringify. 31 // Therefore a string payload of 'foobar' will always be encoded to '"foobar"' 32 // 33 // This means we have no other choice but rewriting the entire codepath or 34 // overwriting the private `_requestToJQueryAjaxHash` method 35 // 36 // The `injectableRequestToJQueryAjaxHash` function makes the JSON object 37 // injectable, meaning we can copy letter for letter the sourcecode of 38 // `_requestToJQueryAjaxHash`, which means we can compare it with the original 39 // private method within a test (`tests/unit/utils/injectableRequestToJQueryAjaxHash.js`). 40 // This means, if `_requestToJQueryAjaxHash` changes between Ember versions 41 // we will know about it 42 43 _requestToJQueryAjaxHash: injectableRequestToJQueryAjaxHash({ 44 stringify: stringify, 45 }), 46 decoder: service('atob'), 47 urlForQuery: function(query, modelName) { 48 if (typeof query.id === 'undefined') { 49 throw new Error('You must specify an id'); 50 } 51 // append keys here otherwise query.keys will add an '=' 52 return this.appendURL('kv', keyToArray(query.id), { 53 ...{ 54 [API_KEYS_KEY]: null, 55 }, 56 ...this.cleanQuery(query), 57 }); 58 }, 59 urlForQueryRecord: function(query, modelName) { 60 if (typeof query.id === 'undefined') { 61 throw new Error('You must specify an id'); 62 } 63 return this.appendURL('kv', keyToArray(query.id), this.cleanQuery(query)); 64 }, 65 urlForCreateRecord: function(modelName, snapshot) { 66 return this.appendURL('kv', keyToArray(snapshot.attr(SLUG_KEY)), { 67 [API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY), 68 }); 69 }, 70 urlForUpdateRecord: function(id, modelName, snapshot) { 71 return this.appendURL('kv', keyToArray(snapshot.attr(SLUG_KEY)), { 72 [API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY), 73 }); 74 }, 75 urlForDeleteRecord: function(id, modelName, snapshot) { 76 const query = { 77 [API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY), 78 }; 79 if (isFolder(snapshot.attr(SLUG_KEY))) { 80 query.recurse = null; 81 } 82 return this.appendURL('kv', keyToArray(snapshot.attr(SLUG_KEY)), query); 83 }, 84 slugFromURL: function(url) { 85 // keys don't follow the 'last part of the url' rule as they contain slashes 86 return decodeURIComponent( 87 url.pathname 88 .split('/') 89 .splice(3) 90 .join('/') 91 ); 92 }, 93 isQueryRecord: function(url, method) { 94 return !url.searchParams.has(API_KEYS_KEY); 95 }, 96 handleBatchResponse: function(url, response, primary, slug) { 97 const dc = url.searchParams.get(API_DATACENTER_KEY) || ''; 98 return response.map((item, i, arr) => { 99 return { 100 [DATACENTER_KEY]: dc, 101 [PRIMARY_KEY]: this.uidForURL(url, item), 102 [SLUG_KEY]: item, 103 }; 104 }); 105 }, 106 // handleSingleResponse: function(url, response, primary, slug) { 107 // return this._super(url, removeNull(response[0]), PRIMARY_KEY, SLUG_KEY); 108 // }, 109 handleResponse: function(status, headers, payload, requestData) { 110 let response = payload; 111 const method = requestData.method; 112 if (status === HTTP_OK) { 113 const url = this.parseURL(requestData.url); 114 switch (true) { 115 case response === true: 116 response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY); 117 break; 118 case this.isQueryRecord(url, method): 119 response = this.handleSingleResponse(url, removeNull(response[0]), PRIMARY_KEY, SLUG_KEY); 120 break; 121 default: 122 response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY); 123 } 124 } 125 return this._super(status, headers, response, requestData); 126 }, 127 dataForRequest: function(params) { 128 const data = this._super(...arguments); 129 let value = ''; 130 switch (params.requestType) { 131 case REQUEST_UPDATE: 132 case REQUEST_CREATE: 133 value = data.kv.Value; 134 if (typeof value === 'string') { 135 return get(this, 'decoder').execute(value); 136 } 137 return null; 138 } 139 }, 140 methodForRequest: function(params) { 141 switch (params.requestType) { 142 case REQUEST_DELETE: 143 return HTTP_DELETE; 144 case REQUEST_CREATE: 145 return HTTP_PUT; 146 } 147 return this._super(...arguments); 148 }, 149 });