diff --git a/README.md b/README.md index 3da3c24..d6f57d3 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,88 @@ client .catch(error => console.log(error.errorMessages)) ``` +##### Listing Curations + +```javascript +const engineName = 'favorite-videos' + +client + .listCurations(engineName) + .then(response => console.log(response)) + .catch(error => console.log(error.errorMessages)) + +// Pagination details are optional +const paginationDetails = { + page: { + current: 2, + size: 10 + } + } + +client + .listCurations(engineName, paginationDetails) + .then(response => console.log(response)) + .catch(error => console.log(error.errorMessages)) +``` + +##### Retrieving Curations + +```javascript +const engineName = 'favorite-videos' +const curationId = 'cur-7438290' + +client + .getCuration(engineName, curationId) + .then(response => console.log(response)) + .catch(error => console.log(error.errorMessages)) +``` + +##### Creating Curations + +```javascript +const engineName = 'favorite-videos' +const newCuration = { + queries: ['cat blop'], + promoted: ['Jdas78932'], + hidden: ['INscMGmhmX4', 'JNDFojsd02'] +} + +client + .createCuration(engineName, newCuration) + .then(response => console.log(response)) + .catch(error => console.log(error.errorMessages)) +``` + +##### Updating Curations + +```javascript +const engineName = 'favorite-videos' +const curationId = 'cur-7438290' +// "queries" is required, either "promoted" or "hidden" is required. +// Values sent for all fields will overwrite existing values. +const newDetails = { + queries: ['cat blop'], + promoted: ['Jdas78932', 'JFayf782'] +} + +client + .updateCuration(engineName, curationId, newDetails) + .then(response => console.log(response)) + .catch(error => console.log(error.errorMessages)) +``` + +##### Deleting Curations + +```javascript +const engineName = 'favorite-videos' +const curationId = 'cur-7438290' + +client + .destroyCuration(engineName, curationId) + .then(response => console.log(response)) + .catch(error => console.log(error.errorMessages)) +``` + ## Running tests The specs in this project use [node-replay](https://github.com/assaf/node-replay) to capture responses. diff --git a/fixtures/host-c5s2mj.api.swiftype.com-443/154775139235186519 b/fixtures/host-c5s2mj.api.swiftype.com-443/154775139235186519 new file mode 100644 index 0000000..0eb198a --- /dev/null +++ b/fixtures/host-c5s2mj.api.swiftype.com-443/154775139235186519 @@ -0,0 +1,29 @@ +GET /api/as/v1/engines/swiftype-api-example/curations +x-swiftype-client: swiftype-app-search-node +x-swiftype-client-version: 0.5.0 +content-type: application/json +host: host-c5s2mj.api.swiftype.com +authorization: Bearer api-mu75psc5egt9ppzuycnc2mc3 +accept: application/json +body: {} + +HTTP/1.1 200 OK +date: Thu, 17 Jan 2019 18:56:32 GMT +content-type: application/json; charset=utf-8 +transfer-encoding: chunked +connection: close +status: 401 Unauthorized +x-frame-options: SAMEORIGIN +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +www-authenticate: Basic realm="Swiftype" +vary: Origin +cache-control: no-cache +x-request-id: c3f4f569d971ad431d1287b3caa52c31 +x-runtime: 0.035655 +x-swiftype-frontend-datacenter: dal05 +x-swiftype-frontend-node: web02.dal05 +x-swiftype-edge-datacenter: dal05 +x-swiftype-edge-node: web02.dal05 + +{"meta":{"page":{"current":1,"total_pages":1,"total_results":2,"size":25}},"results":[{"id":"cur-9382","queries":["mew hiss"],"promoted":["INscMGmhmX4"],"hidden":["JNDFojsd02"]},{"id":"cur-378291","queries":["grrr scratch"],"promoted":["JNDFojsd02"],"hidden":["INscMGmhmX4"]}]} \ No newline at end of file diff --git a/fixtures/host-c5s2mj.api.swiftype.com-443/154775139267473451 b/fixtures/host-c5s2mj.api.swiftype.com-443/154775139267473451 new file mode 100644 index 0000000..400ce14 --- /dev/null +++ b/fixtures/host-c5s2mj.api.swiftype.com-443/154775139267473451 @@ -0,0 +1,29 @@ +GET /api/as/v1/engines/swiftype-api-example/curations?page[current]=2&page[size]=1 +x-swiftype-client: swiftype-app-search-node +x-swiftype-client-version: 0.5.0 +content-type: application/json +host: host-c5s2mj.api.swiftype.com +authorization: Bearer api-mu75psc5egt9ppzuycnc2mc3 +accept: application/json +body: {} + +HTTP/1.1 200 OK +date: Thu, 17 Jan 2019 18:56:32 GMT +content-type: application/json; charset=utf-8 +transfer-encoding: chunked +connection: close +vary: Accept-Encoding, Accept-Encoding, Origin +status: 200 OK +x-frame-options: SAMEORIGIN +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +www-authenticate: Basic realm="Swiftype" +cache-control: no-cache +x-request-id: 03632a579705aa38573c9fcbd29c0be2 +x-runtime: 0.021305 +x-swiftype-frontend-datacenter: dal05 +x-swiftype-frontend-node: web02.dal05 +x-swiftype-edge-datacenter: dal05 +x-swiftype-edge-node: web02.dal05 + +{"meta":{"page":{"current":2,"total_pages":2,"total_results":2,"size":1}},"results":[{"id":"cur-378291","queries":["grrr scratch"],"promoted":["JNDFojsd02"],"hidden":["INscMGmhmX4"]}]} \ No newline at end of file diff --git a/fixtures/host-c5s2mj.api.swiftype.com-443/154775207819050958 b/fixtures/host-c5s2mj.api.swiftype.com-443/154775207819050958 new file mode 100644 index 0000000..4a796cd --- /dev/null +++ b/fixtures/host-c5s2mj.api.swiftype.com-443/154775207819050958 @@ -0,0 +1,29 @@ +GET /api/as/v1/engines/swiftype-api-example/curations/cur-9382 +x-swiftype-client: swiftype-app-search-node +x-swiftype-client-version: 0.5.0 +content-type: application/json +host: host-c5s2mj.api.swiftype.com +authorization: Bearer api-mu75psc5egt9ppzuycnc2mc3 +accept: application/json +body: {} + +HTTP/1.1 200 OK +date: Thu, 17 Jan 2019 19:07:58 GMT +content-type: application/json; charset=utf-8 +transfer-encoding: chunked +connection: close +status: 200 OK +x-frame-options: SAMEORIGIN +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +www-authenticate: Basic realm="Swiftype" +vary: Origin +cache-control: no-cache +x-request-id: 401c83bae4b9e9be82acf7366a8a15bd +x-runtime: 0.017829 +x-swiftype-frontend-datacenter: dal05 +x-swiftype-frontend-node: web02.dal05 +x-swiftype-edge-datacenter: dal05 +x-swiftype-edge-node: web02.dal05 + +{"meta":{"page":{"current":1,"total_pages":1,"total_results":1,"size":25}},"results":[{"id":"cur-9382","queries":["mew hiss"],"promoted":["INscMGmhmX4"],"hidden":["JNDFojsd02"]}]} \ No newline at end of file diff --git a/fixtures/host-c5s2mj.api.swiftype.com-443/154775369860896529 b/fixtures/host-c5s2mj.api.swiftype.com-443/154775369860896529 new file mode 100644 index 0000000..391fe6b --- /dev/null +++ b/fixtures/host-c5s2mj.api.swiftype.com-443/154775369860896529 @@ -0,0 +1,29 @@ +POST /api/as/v1/engines/swiftype-api-example/curations +x-swiftype-client: swiftype-app-search-node +x-swiftype-client-version: 0.5.0 +content-type: application/json +host: host-c5s2mj.api.swiftype.com +authorization: Bearer api-mu75psc5egt9ppzuycnc2mc3 +accept: application/json +body: {\"queries\":[\"arf arf\"],\"promoted\":[],\"hidden\":[\"INscMGmhmX4\",\"JNDFojsd02\"]} + +HTTP/1.1 200 OK +date: Thu, 17 Jan 2019 19:34:58 GMT +content-type: application/json; charset=utf-8 +transfer-encoding: chunked +connection: close +status: 200 OK +x-frame-options: SAMEORIGIN +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +www-authenticate: Basic realm="Swiftype" +vary: Origin +cache-control: no-cache +x-request-id: ffc905295c0530ef21f71cf5c322ba91 +x-runtime: 0.021898 +x-swiftype-frontend-datacenter: dal05 +x-swiftype-frontend-node: web02.dal05 +x-swiftype-edge-datacenter: dal05 +x-swiftype-edge-node: web02.dal05 + +{"id": "cur-9043829"} \ No newline at end of file diff --git a/fixtures/host-c5s2mj.api.swiftype.com-443/154775388696063661 b/fixtures/host-c5s2mj.api.swiftype.com-443/154775388696063661 new file mode 100644 index 0000000..22846cb --- /dev/null +++ b/fixtures/host-c5s2mj.api.swiftype.com-443/154775388696063661 @@ -0,0 +1,29 @@ +DELETE /api/as/v1/engines/swiftype-api-example/curations/cur-378291 +x-swiftype-client: swiftype-app-search-node +x-swiftype-client-version: 0.5.0 +content-type: application/json +host: host-c5s2mj.api.swiftype.com +authorization: Bearer api-mu75psc5egt9ppzuycnc2mc3 +accept: application/json +body: {} + +HTTP/1.1 200 OK +date: Thu, 17 Jan 2019 19:38:06 GMT +content-type: application/json; charset=utf-8 +transfer-encoding: chunked +connection: close +status: 200 OK +x-frame-options: SAMEORIGIN +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +www-authenticate: Basic realm="Swiftype" +vary: Origin +cache-control: no-cache +x-request-id: 83fb305ee12bfe40282325524789753f +x-runtime: 0.024097 +x-swiftype-frontend-datacenter: dal05 +x-swiftype-frontend-node: web01.dal05 +x-swiftype-edge-datacenter: dal05 +x-swiftype-edge-node: web01.dal05 + +{"deleted": true} \ No newline at end of file diff --git a/fixtures/host-c5s2mj.api.swiftype.com-443/154775411564681581 b/fixtures/host-c5s2mj.api.swiftype.com-443/154775411564681581 new file mode 100644 index 0000000..9232379 --- /dev/null +++ b/fixtures/host-c5s2mj.api.swiftype.com-443/154775411564681581 @@ -0,0 +1,29 @@ +PUT /api/as/v1/engines/swiftype-api-example/curations/cur-9382 +x-swiftype-client: swiftype-app-search-node +x-swiftype-client-version: 0.5.0 +content-type: application/json +host: host-c5s2mj.api.swiftype.com +authorization: Bearer api-mu75psc5egt9ppzuycnc2mc3 +accept: application/json +body: {\"queries\":[\"mew hiss\",\"sideways hop\"],\"promoted\":[\"INscMGmhmX4\"],\"hidden\":[\"JNDFojsd02\"]} + +HTTP/1.1 200 OK +date: Thu, 17 Jan 2019 19:41:55 GMT +content-type: application/json; charset=utf-8 +transfer-encoding: chunked +connection: close +status: 200 OK +x-frame-options: SAMEORIGIN +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +www-authenticate: Basic realm="Swiftype" +vary: Origin +cache-control: no-cache +x-request-id: c7f73cec2a3c5ac9177e93cf6ac32032 +x-runtime: 0.020304 +x-swiftype-frontend-datacenter: dal05 +x-swiftype-frontend-node: web02.dal05 +x-swiftype-edge-datacenter: dal05 +x-swiftype-edge-node: web02.dal05 + +{"id": "cur-9382"} \ No newline at end of file diff --git a/fixtures/host-c5s2mj.api.swiftype.com-443/154775710380210466 b/fixtures/host-c5s2mj.api.swiftype.com-443/154775710380210466 new file mode 100644 index 0000000..8cb7eea --- /dev/null +++ b/fixtures/host-c5s2mj.api.swiftype.com-443/154775710380210466 @@ -0,0 +1,29 @@ +GET /api/as/v1/engines/swiftype-api-example/curations/cur-0000 +x-swiftype-client: swiftype-app-search-node +x-swiftype-client-version: 0.5.0 +content-type: application/json +host: host-c5s2mj.api.swiftype.com +authorization: Bearer api-mu75psc5egt9ppzuycnc2mc3 +accept: application/json +body: {} + +HTTP/1.1 404 Not Found +date: Thu, 17 Jan 2019 20:31:43 GMT +content-type: application/json; charset=utf-8 +transfer-encoding: chunked +connection: close +status: 404 Not Found +x-frame-options: SAMEORIGIN +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +www-authenticate: Basic realm="Swiftype" +vary: Origin +cache-control: no-cache +x-request-id: fc44266bdab7628ed1cdef8e6af334b0 +x-runtime: 0.030466 +x-swiftype-frontend-datacenter: dal05 +x-swiftype-frontend-node: web02.dal05 +x-swiftype-edge-datacenter: dal05 +x-swiftype-edge-node: web02.dal05 + +{"errors":["Curation not found with id: cur-0000"]} \ No newline at end of file diff --git a/lib/client.js b/lib/client.js index 5e3086f..fb19d36 100644 --- a/lib/client.js +++ b/lib/client.js @@ -38,6 +38,10 @@ class Client { post(path, params) { return this._jsonRequest('POST', path, params) } + + put(path, params) { + return this._jsonRequest('PUT', path, params) + } delete(path, params) { return this._jsonRequest('DELETE', path, params) diff --git a/lib/swiftypeAppSearch.js b/lib/swiftypeAppSearch.js index d038f91..e5dad74 100644 --- a/lib/swiftypeAppSearch.js +++ b/lib/swiftypeAppSearch.js @@ -175,6 +175,67 @@ class SwiftypeAppSearchClient { return this.client.delete(`engines/${encodeURIComponent(engineName)}`, {}) } + + /** + * List all Curations + * + * @param {String} engineName unique Engine name + * @returns {Promise} a Promise that returns a result {Object} when resolved, otherwise throws an Error. + */ + listCurations(engineName, options = {}) { + const pagingParams = buildPagingParams(options.page || {}); + const prefix = pagingParams.length ? '?' : ''; + + return this.client.get(`engines/${encodeURIComponent(engineName)}/curations${prefix}${pagingParams.join('&')}`, {}) + } + + /** + * Retrieve a Curation by id + * + * @param {String} engineName unique Engine name + * @param {String} curationId unique Curation id + * @returns {Promise} a Promise that returns a result {Object} when resolved, otherwise throws an Error. + */ + getCuration(engineName, curationId) { + + return this.client.get(`engines/${encodeURIComponent(engineName)}/curations/${encodeURIComponent(curationId)}`, {}) + } + + /** + * Create a new Curation + * + * @param {String} engineName unique Engine name + * @param {Object} newCuration body of the Curation object + * @returns {Promise} a Promise that returns a result {Object} when resolved, otherwise throws an Error. + */ + createCuration(engineName, newCuration) { + return this.client.post(`engines/${encodeURIComponent(engineName)}/curations`, newCuration) + } + + /** + * Update an existing curation + * + * @param {String} engineName unique Engine name + * @param {String} curationId unique Curation id + * @param {Object} newCuration body of the Curation object + * @returns {Promise} a Promise that returns a result {Object} when resolved, otherwise throws an Error. + */ + updateCuration(engineName, curationId, newCuration) { + return this.client.put(`engines/${encodeURIComponent(engineName)}/curations/${encodeURIComponent(curationId)}`, newCuration) + } + + /** + * Delete a curation + * + * @param {String} engineName unique Engine name + * @param {String} curationId unique Curation name + * @returns {Promise} a Promise that returns a result {Object} when resolved, otherwise throws an Error. + */ + destroyCuration(engineName, curationId) { + return this.client.delete(`engines/${encodeURIComponent(engineName)}/curations/${encodeURIComponent(curationId)}`, {}) + } + + /** * Creates a jwt search key that can be used for authentication to enforce a set of required search options. * diff --git a/test/test.js b/test/test.js index b2c5f1f..7d76758 100644 --- a/test/test.js +++ b/test/test.js @@ -20,6 +20,32 @@ describe('SwiftypeAppSearchClient', () => { body: 'this is also a test' } ] + const curations = [ + { + "id": "cur-9382", + "queries": [ + "mew hiss" + ], + "promoted": [ + "INscMGmhmX4" + ], + "hidden": [ + "JNDFojsd02" + ] + }, + { + "id": "cur-378291", + "queries": [ + "grrr scratch" + ], + "promoted": [ + "JNDFojsd02" + ], + "hidden": [ + "INscMGmhmX4" + ] + } + ] const swiftype = new SwiftypeAppSearchClient(hostIdentifier, apiKey) @@ -299,7 +325,134 @@ describe('SwiftypeAppSearchClient', () => { done() }) }) + + describe('#listCurations', () => { + it('should list curations successfully', (done) => { + swiftype.listCurations(engineName) + .then((results) => { + assert.deepEqual({ + "meta": { + "page": { + "current": 1, + "total_pages": 1, + "total_results": 2, + "size": 25 + } + }, + "results": curations + }, results) + done() + }) + .catch((error) => { + done(error) + }) + }) + + it('should support paging', (done) => { + swiftype.listCurations(engineName, { + page: { + current: 2, + size: 1 + } + }) + .then((results) => { + assert.deepEqual({ + "meta": { + "page": { + "current": 2, + "total_pages": 2, + "total_results": 2, + "size": 1 + } + }, + "results": [ + curations[1] + ] + }, results) + done() + }) + .catch((error) => { + done(error) + }) + }) + }) + + describe('#getCuration', () => { + it('should get a curation successfully', (done) => { + const expected = { + meta: { + page: { current: 1, total_pages: 1, total_results: 1, size: 25 } + }, + results: + [ curations[0] ] + } + swiftype.getCuration(engineName, curations[0].id) + .then((results) => { + assert.deepEqual(expected, results) + done() + }) + .catch((error) => { + done(error) + }) + }) + it('passes along errors', (done) => { + swiftype.getCuration(engineName, "cur-0000") + .then((results) => { + done(new Error('This should have resulted in an error')) + }) + .catch((error) => { + assert(error.errorMessages.join().toString() === "Curation not found with id: cur-0000") + done() + }) + }) + }) + + describe('#createCuration', () => { + it('should create a curation successfully', (done) => { + swiftype.createCuration(engineName, {queries: ["arf arf"], promoted: [], hidden: ["INscMGmhmX4", "JNDFojsd02"]}) + .then((results) => { + assert.deepEqual({ + "id": "cur-9043829" + }, results) + done() + }) + .catch((error) => { + done(error) + }) + }) + }) + + describe('#destroyCuration', () => { + it('should delete a curation successfully', (done) => { + swiftype.destroyCuration(engineName, curations[1].id) + .then((results) => { + assert.deepEqual({ + "deleted": true + }, results) + done() + }) + .catch((error) => { + done(error) + }) + }) + }) + + describe('#updateCuration', () => { + it('should update a curation successfully', (done) => { + swiftype.updateCuration(engineName, "cur-9382", {queries: ["mew hiss", "sideways hop"], promoted: ["INscMGmhmX4"], hidden: ["JNDFojsd02"]}) + .then((results) => { + assert.deepEqual({ + "id": "cur-9382" + }, results) + done() + }) + .catch((error) => { + done(error) + }) + }) + }) + describe('error handling', () => { it('should handle 404', (done) => { swiftype.search('invalid-engine-name', 'cat') @@ -375,4 +528,5 @@ describe('SwiftypeAppSearchClient', () => { }) }) }) + })