Skip to content

Commit 13017b0

Browse files
committed
fix: request gets JSON as body
When an object is created or edited the payload can be either an object or an object with `updates` property
1 parent 2c00ede commit 13017b0

8 files changed

Lines changed: 141 additions & 43 deletions

File tree

src/Api.ts

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,10 @@ export class Api {
176176
* @returns {Promise} A promise which will resolved with the ID and any other specified fields of newly created object
177177
*/
178178
create(objCode: string, params: any, fields?: TFields) {
179-
if (params.hasOwnProperty('updates') && typeof params.updates === 'string') {
180-
return this.request(objCode, params.updates, fields, Api.Methods.POST, true)
179+
if (params.hasOwnProperty('updates') && !(params.updates instanceof Array)) {
180+
return this.request(objCode, params, fields, Api.Methods.POST)
181181
}
182-
return this.request(objCode, params, fields, Api.Methods.POST, true)
182+
return this.request(objCode, {updates: params}, fields, Api.Methods.POST)
183183
}
184184

185185
/**
@@ -192,10 +192,10 @@ export class Api {
192192
* @return {Promise} A promise which will resolved with results if everything went ok and rejected otherwise
193193
*/
194194
edit(objCode: string, objID: string, updates: any, fields?: TFields) {
195-
if (updates.hasOwnProperty('updates') && typeof updates.updates === 'string') {
196-
return this.request(objCode + '/' + objID, updates.updates, fields, Api.Methods.PUT, true)
195+
if (updates.hasOwnProperty('updates') && !(updates.updates instanceof Array)) {
196+
return this.request(objCode + '/' + objID, updates, fields, Api.Methods.PUT)
197197
}
198-
return this.request(objCode + '/' + objID, updates, fields, Api.Methods.PUT, false)
198+
return this.request(objCode + '/' + objID, {updates: updates}, fields, Api.Methods.PUT)
199199
}
200200

201201
/**
@@ -209,15 +209,12 @@ export class Api {
209209
*/
210210
execute(objCode: string, objID: string | null, action: string, actionArgs?: object) {
211211
let endPoint = objCode
212-
let params = {}
212+
let params: any = { method: Api.Methods.PUT }
213213
if (objID) {
214214
endPoint += '/' + objID + '/' + action
215215
}
216216
else {
217-
params = {
218-
method: Api.Methods.PUT,
219-
action: action
220-
}
217+
params.action = action
221218
}
222219
if (actionArgs) {
223220
params = objectAssign(params, actionArgs)
@@ -351,17 +348,16 @@ export class Api {
351348
* @param {Object} params An object with params
352349
* @param {Object} [fields] Fields to query for the request
353350
* @param {String} [method=GET] The method which the request will do (GET|POST|PUT|DELETE)
354-
* @param {String} [sendJSONBody=false] Whether the params payload is sent as JSON.stringify in the request body
355351
* @return {Promise} A promise which will resolved with results if everything went ok and rejected otherwise
356352
*/
357-
request(path: string, params: THttpParams, fields?: TFields, method: string = Api.Methods.GET, sendJSONBody = false): Promise<any> {
358-
params = objectAssign(params || {}, this._httpParams)
353+
request(path: string, params: THttpParams, fields?: TFields, method: string = Api.Methods.GET): Promise<any> {
354+
const clonedParams = objectAssign({}, params || {}, this._httpParams)
359355

360356
const alwaysUseGet = this._httpOptions.alwaysUseGet
361357

362358
const options = objectAssign({}, this._httpOptions)
363-
if (alwaysUseGet) {
364-
params.method = method
359+
if (alwaysUseGet && path !== 'login') {
360+
clonedParams.method = method
365361
options.method = Api.Methods.GET
366362
} else {
367363
options.method = method
@@ -379,7 +375,7 @@ export class Api {
379375
fields = [fields]
380376
}
381377
if (fields.length !== 0) {
382-
params.fields = fields.join()
378+
clonedParams.fields = fields.join()
383379
}
384380

385381
const headers = new Headers()
@@ -392,23 +388,33 @@ export class Api {
392388
}
393389

394390
let bodyParams = null, queryString = ''
395-
if (NodeFormData && params instanceof NodeFormData) {
396-
bodyParams = params
391+
if (NodeFormData && clonedParams instanceof NodeFormData) {
392+
bodyParams = clonedParams
397393
}
398-
else if (GlobalScope.FormData && params instanceof GlobalScope.FormData) {
399-
bodyParams = params
394+
else if (GlobalScope.FormData && clonedParams instanceof GlobalScope.FormData) {
395+
bodyParams = clonedParams
400396
}
401397
else {
402-
if (sendJSONBody && (options.method === Api.Methods.POST || options.method === Api.Methods.PUT)) {
398+
if (clonedParams.hasOwnProperty('updates') && (options.method === Api.Methods.POST || options.method === Api.Methods.PUT)) {
403399
headers.append('Content-Type', 'application/json')
404-
bodyParams = JSON.stringify(params)
400+
if (typeof clonedParams.updates === 'string') {
401+
bodyParams = clonedParams.updates
402+
} else {
403+
bodyParams = JSON.stringify(clonedParams.updates)
404+
}
405+
406+
delete clonedParams.updates
407+
const qs = queryStringify(clonedParams)
408+
if (qs) {
409+
queryString = '?' + qs
410+
}
405411
} else {
406412
headers.append('Content-Type', 'application/x-www-form-urlencoded')
407-
bodyParams = Object.keys(params).reduce(function(a, k) {
408-
a.push(k + '=' + encodeURIComponent(params[k]))
409-
return a
410-
}, []).join('&')
411-
if (options.method === Api.Methods.GET) {
413+
if (clonedParams.hasOwnProperty('updates') && typeof clonedParams.updates !== 'string') {
414+
clonedParams.updates = JSON.stringify(clonedParams.updates)
415+
}
416+
bodyParams = queryStringify(clonedParams)
417+
if (options.method === Api.Methods.GET || options.method === Api.Methods.DELETE) {
412418
if (bodyParams) {
413419
queryString = '?' + bodyParams
414420
}
@@ -487,6 +493,13 @@ export class Api {
487493
}
488494
}
489495

496+
const queryStringify = function(params) {
497+
return Object.keys(params).reduce(function(a, k) {
498+
a.push(k + '=' + encodeURIComponent(params[k]))
499+
return a
500+
}, []).join('&')
501+
}
502+
490503
const ResponseHandler = {
491504
success: (response) => {
492505
if (response.ok) {

test/integration/copy.spec.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,15 @@ describe('Copy', function() {
5050
const [url, opts] = fetchMock.lastCall('copy')
5151
should(url).endWith('foo')
5252
should(opts.method).equal('POST')
53-
should(opts.body).containEql('{"copySourceID":"bar"}')
53+
should(opts.body).equal('copySourceID=bar')
54+
})
55+
})
56+
it('makes request to objCode with copySourceID in the params with some edits to the object', function() {
57+
return this.api.copy('foo', 'bar', {name: 'Copy of bar'}).then(function() {
58+
const [url, opts] = fetchMock.lastCall('copy')
59+
should(url).endWith('foo?copySourceID=bar')
60+
should(opts.method).equal('POST')
61+
should(opts.body).equal('{"name":"Copy of bar"}')
5462
})
5563
})
5664
it('returns data with a new ID', function() {

test/integration/create.spec.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,50 @@ describe('Create', function() {
5454

5555
return this.api.create(objCode, params, fields).then(function() {
5656
const [url, opts] = fetchMock.lastCall('create')
57-
should(url).endWith(objCode)
57+
should(url).endWith(objCode + '?fields=' + encodeURIComponent('*,zzz:*'))
5858
should(opts.method).equal('POST')
59-
should(opts.body).containEql('{"foo":"bar","fields":"*,zzz:*"}')
59+
should(opts.body).equal('{"foo":"bar"}')
6060
})
6161
})
6262
it('should return the new created object', function() {
6363
const params = {
6464
name: 'test'
6565
}
66+
const fields = 'customerID'
6667
const objCode = 'TASK'
6768

68-
return this.api.create(objCode, params).then(function(data) {
69+
return this.api.create(objCode, params, fields).then(function(data) {
6970
should(data).have.properties(['ID', 'name', 'objCode'])
7071
should(data.objCode).equal(objCode)
7172
should(data.name).equal(params.name)
7273
})
7374
})
75+
it('should create an object using the old api (passing updates property)', function() {
76+
const params = {
77+
updates: JSON.stringify({name: 'test'})
78+
}
79+
const objCode = 'TASK'
80+
81+
return this.api.create(objCode, params).then(function(data) {
82+
const [url, opts] = fetchMock.lastCall('create')
83+
should(url).endWith(objCode)
84+
should(opts.method).equal('POST')
85+
should(opts.body).equal(params.updates)
86+
87+
should(data).have.properties(['ID', 'name', 'objCode'])
88+
should(data.objCode).equal(objCode)
89+
should(data.name).equal('test')
90+
})
91+
})
92+
it('should not do changes on the given parameter object', function() {
93+
const params = {
94+
updates: JSON.stringify({name: 'test'})
95+
}
96+
const objCode = 'TASK'
97+
98+
Object.freeze(params)
99+
should(() => {
100+
this.api.create(objCode, params)
101+
}).not.throw()
102+
})
74103
})

test/integration/edit.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,23 @@ describe('Edit', function() {
6565
should(data.name).equal(params.name)
6666
})
6767
})
68+
it('should edit an object using the old api (passing updates property)', function() {
69+
const params = {
70+
updates: JSON.stringify({name: 'api test 2'})
71+
}
72+
const objCode = 'PROJ',
73+
objID = 'foobar'
74+
75+
return this.api.edit(objCode, objID, params).then(function(data) {
76+
const [url, opts] = fetchMock.lastCall('edit')
77+
should(opts.method).equal('PUT')
78+
should(url).endWith(objCode + '/' + objID)
79+
should(opts.headers.get('apiKey')).equal('testapikey')
80+
should(opts.body).containEql(params.updates)
81+
82+
should(data.name).equal('api test 2')
83+
should(data).have.properties(['ID', 'name', 'objCode'])
84+
should(data.objCode).equal(objCode)
85+
})
86+
})
6887
})

test/integration/editUsingGet.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ describe('Edit', function() {
6060
should(url).containEql(objCode + '/' + objID)
6161
should(opts.headers.get('apiKey')).equal('testapikey')
6262
should(opts.body).equal(null)
63-
should(url).containEql('name=' + encodeURIComponent('api test 2') + '&method=PUT')
63+
should(url).containEql('updates=' + encodeURIComponent(JSON.stringify(params)))
64+
should(url).containEql('method=PUT')
6465

6566
should(data).have.properties(['ID', 'name', 'objCode'])
6667
should(data.objCode).equal(objCode)

test/integration/execute.spec.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe('Execute', function() {
4949
return this.api.execute('foo', 'bar', 'baz').then(function() {
5050
const [url, opts] = fetchMock.lastCall('execute')
5151
should(url).endWith('foo/bar/baz')
52-
should(opts.body).equal('{}')
52+
should(opts.body).equal('method=PUT')
5353
should(opts.method).equal('POST')
5454
})
5555
})
@@ -58,7 +58,8 @@ describe('Execute', function() {
5858
return this.api.execute('foo', 'bar', 'baz', {foo: 'barbaz'}).then(function() {
5959
const [url, opts] = fetchMock.lastCall('execute')
6060
should(url).endWith('foo/bar/baz')
61-
should(opts.body).containEql('{"foo":"barbaz"}')
61+
should(opts.body).containEql('foo=barbaz')
62+
should(opts.body).containEql('method=PUT')
6263
should(opts.method).equal('POST')
6364
})
6465
})
@@ -67,9 +68,9 @@ describe('Execute', function() {
6768
return this.api.execute('foo', null, 'baz', {foo: 'barbaz'}).then(function() {
6869
const [url, opts] = fetchMock.lastCall('execute')
6970
should(url).endWith('foo')
70-
should(opts.body).containEql('"action":"baz"')
71-
should(opts.body).containEql('"foo":"barbaz"')
72-
should(opts.body).containEql('"method":"PUT"')
71+
should(opts.body).containEql('action=baz')
72+
should(opts.body).containEql('foo=barbaz')
73+
should(opts.body).containEql('method=PUT')
7374
should(opts.method).equal('POST')
7475
})
7576
})
@@ -78,9 +79,9 @@ describe('Execute', function() {
7879
return this.api.execute('foo', null, 'baz').then(function() {
7980
const [url, opts] = fetchMock.lastCall('execute')
8081
should(url).endWith('foo')
81-
should(opts.body).containEql('"action":"baz"')
82+
should(opts.body).containEql('action=baz')
83+
should(opts.body).containEql('method=PUT')
8284
should(opts.method).equal('POST')
83-
should(opts.body).containEql('"method":"PUT"')
8485
})
8586
})
8687
})

test/integration/getApiKey.spec.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,37 @@ describe('getApiKey', function() {
6666
{name: 'generateApiKey'}
6767
)
6868

69-
this.api.getApiKey('foo', 'bar').then(function(apiKey) {
69+
this.api.getApiKey('foo', 'bar').then((apiKey) => {
7070
should(apiKey).equal('baz')
7171
should(fetchMock.called('getApiKey')).be.true()
7272
should(fetchMock.called('generateApiKey')).be.false()
73+
should(fetchMock.calls('getApiKey')).length(1)
74+
this.api.getApiKey('foo', 'bar').then(function() {
75+
should(fetchMock.calls('getApiKey')).length(1)
76+
done()
77+
})
78+
})
79+
})
80+
it('should get and then clear the apiKey', function(done) {
81+
fetchMock.mock(
82+
(url, opts) => opts.body.indexOf('getApiKey') !== -1,
83+
'{"data": {"result": "baz"}}',
84+
{name: 'getApiKey'}
85+
)
86+
fetchMock.mock(
87+
(url, opts) => opts.body.indexOf('clearApiKey') !== -1,
88+
'{"data": {"success": true}}',
89+
{name: 'clearApiKey'}
90+
)
91+
92+
this.api.getApiKey('foo', 'bar').then((apiKey) => {
93+
should(apiKey).equal('baz')
94+
should(fetchMock.calls('getApiKey')).length(1)
95+
should(this.api._httpOptions.headers).have.property('apiKey').eql('baz')
96+
this.api.clearApiKey().then(() => {
97+
should(this.api._httpOptions.headers).not.have.property('apiKey')
98+
done()
99+
})
73100
done()
74101
})
75102
})

test/integration/login.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ describe('Login', function() {
6666
this.api.login('foo', 'bar')
6767
const [url, opts] = fetchMock.lastCall('login')
6868
should(url).endWith('login')
69-
should(opts.body).containEql('"username":"foo"')
70-
should(opts.body).containEql('"password":"bar"')
69+
should(opts.body).containEql('username=foo')
70+
should(opts.body).containEql('password=bar')
7171
})
7272
})
7373
describe('authentication exception', function() {

0 commit comments

Comments
 (0)