diff --git a/ably.d.ts b/ably.d.ts index d51d650bd9..5558e392fb 100644 --- a/ably.d.ts +++ b/ably.d.ts @@ -1641,6 +1641,7 @@ declare namespace Types { * * @param method - The request method to use, such as `GET`, `POST`. * @param path - The request path. + * @param version - TODO need canonical docs * @param params - The parameters to include in the URL query of the request. The parameters depend on the endpoint being queried. See the [REST API reference](https://ably.com/docs/api/rest-api) for the available parameters of each endpoint. * @param body - The JSON body of the request. * @param headers - Additional HTTP headers to include in the request. @@ -1649,6 +1650,7 @@ declare namespace Types { request( method: string, path: string, + version: number, params?: any, body?: any[] | any, headers?: any, @@ -1704,6 +1706,7 @@ declare namespace Types { * * @param method - The request method to use, such as `GET`, `POST`. * @param path - The request path. + * @param version - TODO need canonical docs * @param params - The parameters to include in the URL query of the request. The parameters depend on the endpoint being queried. See the [REST API reference](https://ably.com/docs/api/rest-api) for the available parameters of each endpoint. * @param body - The JSON body of the request. * @param headers - Additional HTTP headers to include in the request. @@ -1712,6 +1715,7 @@ declare namespace Types { request( method: string, path: string, + version: number, params?: any, body?: any[] | any, headers?: any @@ -1782,6 +1786,7 @@ declare namespace Types { * * @param method - The request method to use, such as `GET`, `POST`. * @param path - The request path. + * @param version - TODO need canonical docs * @param params - The parameters to include in the URL query of the request. The parameters depend on the endpoint being queried. See the [REST API reference](https://ably.com/docs/api/rest-api) for the available parameters of each endpoint. * @param body - The JSON body of the request. * @param headers - Additional HTTP headers to include in the request. @@ -1790,6 +1795,7 @@ declare namespace Types { request( method: string, path: string, + version: number, params?: any, body?: any[] | any, headers?: any, @@ -1841,6 +1847,7 @@ declare namespace Types { * * @param method - The request method to use, such as `GET`, `POST`. * @param path - The request path. + * @param version - TODO need canonical docs * @param params - The parameters to include in the URL query of the request. The parameters depend on the endpoint being queried. See the [REST API reference](https://ably.com/docs/api/rest-api) for the available parameters of each endpoint. * @param body - The JSON body of the request. * @param headers - Additional HTTP headers to include in the request. @@ -1849,6 +1856,7 @@ declare namespace Types { request( method: string, path: string, + version: number, params?: any, body?: any[] | any, headers?: any diff --git a/src/common/lib/client/rest.ts b/src/common/lib/client/rest.ts index ef24e727f0..fcc89f84cc 100644 --- a/src/common/lib/client/rest.ts +++ b/src/common/lib/client/rest.ts @@ -168,6 +168,7 @@ class Rest { request( method: string, path: string, + version: number, params: RequestParams, body: unknown, customHeaders: Record, @@ -182,12 +183,12 @@ class Rest { const _method = method.toLowerCase() as HttpMethods; const headers = _method == 'get' - ? Utils.defaultGetHeaders(this.options, { format }) - : Utils.defaultPostHeaders(this.options, { format }); + ? Utils.defaultGetHeaders(this.options, { format, protocolVersion: version }) + : Utils.defaultPostHeaders(this.options, { format, protocolVersion: version }); if (callback === undefined) { if (this.options.promises) { - return Utils.promisify(this, 'request', [method, path, params, body, customHeaders]) as Promise< + return Utils.promisify(this, 'request', [method, path, version, params, body, customHeaders]) as Promise< HttpPaginatedResponse >; } diff --git a/src/common/lib/util/utils.ts b/src/common/lib/util/utils.ts index beb32aaf48..28cd4888cf 100644 --- a/src/common/lib/util/utils.ts +++ b/src/common/lib/util/utils.ts @@ -353,27 +353,35 @@ export enum Format { export interface HeadersOptions { format?: Format; + protocolVersion?: number; } const defaultHeadersOptions: Required = { format: Format.json, + protocolVersion: Defaults.protocolVersion, }; export function defaultGetHeaders( options: NormalisedClientOptions, - { format = defaultHeadersOptions.format }: HeadersOptions = {} + { + format = defaultHeadersOptions.format, + protocolVersion = defaultHeadersOptions.protocolVersion, + }: HeadersOptions = {} ): Record { const accept = contentTypes[format]; return { accept: accept, - 'X-Ably-Version': Defaults.protocolVersion.toString(), + 'X-Ably-Version': protocolVersion.toString(), 'Ably-Agent': getAgentString(options), }; } export function defaultPostHeaders( options: NormalisedClientOptions, - { format = defaultHeadersOptions.format }: HeadersOptions = {} + { + format = defaultHeadersOptions.format, + protocolVersion = defaultHeadersOptions.protocolVersion, + }: HeadersOptions = {} ): Record { let contentType; const accept = (contentType = contentTypes[format]); @@ -381,7 +389,7 @@ export function defaultPostHeaders( return { accept: accept, 'content-type': contentType, - 'X-Ably-Version': Defaults.protocolVersion.toString(), + 'X-Ably-Version': protocolVersion.toString(), 'Ably-Agent': getAgentString(options), }; } diff --git a/test/realtime/encoding.test.js b/test/realtime/encoding.test.js index 7c50152acb..aeef0b94f7 100644 --- a/test/realtime/encoding.test.js +++ b/test/realtime/encoding.test.js @@ -7,6 +7,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async var displayError = helper.displayError; var encodingFixturesPath = helper.testResourcesPath + 'messages-encoding.json'; var closeAndFinish = helper.closeAndFinish; + var Defaults = Ably.Rest.Platform.Defaults; describe('realtime/encoding', function () { this.timeout(60 * 1000); @@ -98,6 +99,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async realtime.request( 'post', channelPath, + Defaults.protocolVersion, null, { name: name, data: encodingSpec.data, encoding: encodingSpec.encoding }, null, @@ -174,39 +176,47 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async eachOfCb(err); return; } - realtime.request('get', channelPath, null, null, null, function (err, resultPage) { - if (err) { - eachOfCb(err); - return; - } - try { - var msgs = helper.arrFilter(resultPage.items, function (m) { - return m.name === name; - }); - expect(msgs.length).to.equal( - 2, - 'Check expected number of results (one from json rt, one from binary rt)' - ); - expect(msgs[0].encoding == encodingSpec.encoding, 'Check encodings match').to.be.ok; - expect(msgs[1].encoding == encodingSpec.encoding, 'Check encodings match').to.be.ok; - if (msgs[0].encoding === 'json') { - expect(JSON.parse(encodingSpec.data)).to.deep.equal( - JSON.parse(msgs[0].data), - 'Check data matches' - ); - expect(JSON.parse(encodingSpec.data)).to.deep.equal( - JSON.parse(msgs[1].data), - 'Check data matches' + realtime.request( + 'get', + channelPath, + Defaults.protocolVersion, + null, + null, + null, + function (err, resultPage) { + if (err) { + eachOfCb(err); + return; + } + try { + var msgs = helper.arrFilter(resultPage.items, function (m) { + return m.name === name; + }); + expect(msgs.length).to.equal( + 2, + 'Check expected number of results (one from json rt, one from binary rt)' ); - } else { - expect(encodingSpec.data).to.equal(msgs[0].data, 'Check data matches'); - expect(encodingSpec.data).to.equal(msgs[1].data, 'Check data matches'); + expect(msgs[0].encoding == encodingSpec.encoding, 'Check encodings match').to.be.ok; + expect(msgs[1].encoding == encodingSpec.encoding, 'Check encodings match').to.be.ok; + if (msgs[0].encoding === 'json') { + expect(JSON.parse(encodingSpec.data)).to.deep.equal( + JSON.parse(msgs[0].data), + 'Check data matches' + ); + expect(JSON.parse(encodingSpec.data)).to.deep.equal( + JSON.parse(msgs[1].data), + 'Check data matches' + ); + } else { + expect(encodingSpec.data).to.equal(msgs[0].data, 'Check data matches'); + expect(encodingSpec.data).to.equal(msgs[1].data, 'Check data matches'); + } + eachOfCb(); + } catch (err) { + eachOfCb(err); } - eachOfCb(); - } catch (err) { - eachOfCb(err); } - }); + ); } ); }, diff --git a/test/rest/request.test.js b/test/rest/request.test.js index 573468e023..b3a4b5b694 100644 --- a/test/rest/request.test.js +++ b/test/rest/request.test.js @@ -1,11 +1,12 @@ 'use strict'; -define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { +define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async, chai) { var rest; var expect = chai.expect; var utils = helper.Utils; var echoServerHost = 'echo.ably.io'; var restTestOnJsonMsgpack = helper.restTestOnJsonMsgpack; + var Defaults = Ably.Rest.Platform.Defaults; describe('rest/request', function () { this.timeout(60 * 1000); @@ -21,8 +22,28 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }); }); + restTestOnJsonMsgpack('request_version', function (done, rest) { + const version = 150; // arbitrarily chosen + + function testRequestHandler(_, __, ___, headers) { + // TODO why try-catch + // TODO document where taken from + try { + expect('X-Ably-Version' in headers, 'Verify version header exists').to.be.ok; + expect(headers['X-Ably-Version']).to.equal(version.toString(), 'Verify version number sent in request'); + done(); + } catch (err) { + done(err); + } + } + + rest.http.do = testRequestHandler; + + rest.request('get', '/time' /* arbitrarily chosen */, version, null, null, null, function (err, res) {}); + }); + restTestOnJsonMsgpack('request_time', function (done, rest) { - rest.request('get', '/time', null, null, null, function (err, res) { + rest.request('get', '/time', Defaults.protocolVersion, null, null, null, function (err, res) { try { expect(!err, err && helper.displayError(err)).to.be.ok; expect(res.statusCode).to.equal(200, 'Check statusCode'); @@ -40,27 +61,35 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { /* NB: can't just use /invalid or something as the CORS preflight will * fail. Need something superficially a valid path but where the actual * request fails */ - rest.request('get', '/keys/ablyjs.test/requestToken', null, null, null, function (err, res) { - try { - expect(err).to.equal( - null, - 'Check that we do not get an error response for a failure that returns an actual ably error code' - ); - expect(res.success).to.equal(false, 'Check res.success is false for a failure'); - expect(res.statusCode).to.equal(404, 'Check HPR.statusCode is 404'); - expect(res.errorCode).to.equal(40400, 'Check HPR.errorCode is 40400'); - expect(res.errorMessage, 'Check have an HPR.errorMessage').to.be.ok; - done(); - } catch (err) { - done(err); + rest.request( + 'get', + '/keys/ablyjs.test/requestToken', + Defaults.protocolVersion, + null, + null, + null, + function (err, res) { + try { + expect(err).to.equal( + null, + 'Check that we do not get an error response for a failure that returns an actual ably error code' + ); + expect(res.success).to.equal(false, 'Check res.success is false for a failure'); + expect(res.statusCode).to.equal(404, 'Check HPR.statusCode is 404'); + expect(res.errorCode).to.equal(40400, 'Check HPR.errorCode is 40400'); + expect(res.errorMessage, 'Check have an HPR.errorMessage').to.be.ok; + done(); + } catch (err) { + done(err); + } } - }); + ); }); /* With a network issue, should get an actual err, not an HttpPaginatedResponse with error members */ it('request_network_error', function (done) { rest = helper.AblyRest({ restHost: helper.unroutableAddress }); - rest.request('get', '/time', null, null, null, function (err, res) { + rest.request('get', '/time', Defaults.protocolVersion, null, null, null, function (err, res) { try { expect(err, 'Check get an err').to.be.ok; expect(!res, 'Check do not get a res').to.be.ok; @@ -80,7 +109,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { async.waterfall( [ function (cb) { - rest.request('post', channelPath, null, msgone, null, function (err, res) { + rest.request('post', channelPath, Defaults.protocolVersion, null, msgone, null, function (err, res) { try { expect(!err, err && helper.displayError(err)).to.be.ok; expect(res.statusCode).to.equal(201, 'Check statusCode is 201'); @@ -93,7 +122,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }); }, function (cb) { - rest.request('post', channelPath, null, msgtwo, null, function (err, res) { + rest.request('post', channelPath, Defaults.protocolVersion, null, msgtwo, null, function (err, res) { try { expect(!err, err && helper.displayError(err)).to.be.ok; expect(res.statusCode).to.equal(201, 'Check statusCode is 201'); @@ -105,19 +134,27 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { }); }, function (cb) { - rest.request('get', channelPath, { limit: 1, direction: 'forwards' }, null, null, function (err, res) { - try { - expect(!err, err && helper.displayError(err)).to.be.ok; - expect(res.statusCode).to.equal(200, 'Check statusCode is 200'); - expect(res.items.length).to.equal(1, 'Check only one msg returned'); - expect(res.items[0].name).to.equal(msgone.name, 'Check name is as expected'); - expect(res.items[0].data).to.equal(msgone.data, 'Check data is as expected'); - expect(res.hasNext, 'Check hasNext is true').to.be.ok; - cb(null, res.next); - } catch (err) { - cb(err); + rest.request( + 'get', + channelPath, + Defaults.protocolVersion, + { limit: 1, direction: 'forwards' }, + null, + null, + function (err, res) { + try { + expect(!err, err && helper.displayError(err)).to.be.ok; + expect(res.statusCode).to.equal(200, 'Check statusCode is 200'); + expect(res.items.length).to.equal(1, 'Check only one msg returned'); + expect(res.items[0].name).to.equal(msgone.name, 'Check name is as expected'); + expect(res.items[0].data).to.equal(msgone.data, 'Check data is as expected'); + expect(res.hasNext, 'Check hasNext is true').to.be.ok; + cb(null, res.next); + } catch (err) { + cb(err); + } } - }); + ); }, function (next, cb) { next(function (err, res) { @@ -162,7 +199,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { restTestOnJsonMsgpack('request_batch_api_success', function (done, rest, name) { var body = { channels: [name + '1', name + '2'], messages: { data: 'foo' } }; - rest.request('POST', '/messages', {}, body, {}, function (err, res) { + rest.request('POST', '/messages', Defaults.protocolVersion, {}, body, {}, function (err, res) { try { expect(err).to.equal(null, 'Check that we do not get an error response for a success'); expect(res.success).to.equal(true, 'Check res.success is true for a success'); @@ -189,7 +226,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { restTestOnJsonMsgpack.skip('request_batch_api_partial_success', function (done, rest, name) { var body = { channels: [name, '[invalid', ''], messages: { data: 'foo' } }; - rest.request('POST', '/messages', {}, body, {}, function (err, res) { + rest.request('POST', '/messages', Defaults.protocolVersion, {}, body, {}, function (err, res) { try { expect(err).to.equal(null, 'Check that we do not get an error response for a partial success'); expect(res.success).to.equal(false, 'Check res.success is false for a partial failure'); @@ -223,7 +260,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { utils.arrForEach(['put', 'patch', 'delete'], function (method) { it('check' + method, function (done) { var restEcho = helper.AblyRest({ useBinaryProtocol: false, restHost: echoServerHost, tls: true }); - restEcho.request(method, '/methods', {}, {}, {}, function (err, res) { + restEcho.request(method, '/methods', Defaults.protocolVersion, {}, {}, {}, function (err, res) { if (err) { done(err); } else { @@ -243,7 +280,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { var client = helper.AblyRest({ promises: true }); client - .request('get', '/time', null, null, null) + .request('get', '/time', Defaults.protocolVersion, null, null, null) .then(function (res) { expect(res.statusCode).to.equal(200, 'Check statusCode'); expect(res.success).to.equal(true, 'Check success');