From 8e09815d57dbe54613d1d30d2d8e84b1949c4593 Mon Sep 17 00:00:00 2001 From: Matt Hinchliffe Date: Wed, 5 Apr 2017 08:44:08 +0100 Subject: [PATCH 1/2] Add tag method to fetch a single tag --- index.js | 3 +- lib/tag.js | 29 ++++++++++++ test/fixtures/tag-found.json | 77 ++++++++++++++++++++++++++++++++ test/fixtures/tag-not-found.json | 14 ++++++ test/spec/search-spec.js | 16 +++---- test/spec/tag-spec.js | 72 +++++++++++++++++++++++++++++ 6 files changed, 202 insertions(+), 9 deletions(-) create mode 100644 lib/tag.js create mode 100644 test/fixtures/tag-found.json create mode 100644 test/fixtures/tag-not-found.json create mode 100644 test/spec/tag-spec.js diff --git a/index.js b/index.js index 66e3526..4a90687 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ module.exports = { search: require('./lib/search'), mget: require('./lib/mget'), - get: require('./lib/get') + get: require('./lib/get'), + tag: require('./lib/tag') }; diff --git a/lib/tag.js b/lib/tag.js new file mode 100644 index 0000000..c22f6a0 --- /dev/null +++ b/lib/tag.js @@ -0,0 +1,29 @@ +const search = require('./search'); + +function handleData (data, uuid) { + if (data.length) { + return data[0].metadata.find((tag) => tag.idV1 === uuid); + } +} + +function tag (uuid, timeout = 3000) { + const params = { + query: { + // avoid scoring to increase caching + constant_score: { + filter: { + term: { + 'metadata.idV1': uuid + } + } + } + }, + size: 1, + _source: [ 'metadata' ] + }; + + return search(params, timeout) + .then((data) => handleData(data, uuid)); +} + +module.exports = tag; diff --git a/test/fixtures/tag-found.json b/test/fixtures/tag-found.json new file mode 100644 index 0000000..32ae554 --- /dev/null +++ b/test/fixtures/tag-found.json @@ -0,0 +1,77 @@ +{ + "took": 4, + "timed_out": false, + "_shards": { + "total": 1, + "successful": 1, + "failed": 0 + }, + "hits": { + "total": 26082, + "max_score": 1, + "hits": [ + { + "_index": "v3_api_v2-2016-12-20", + "_type": "item", + "_id": "07004678-4661-3417-bd5f-3b5f1e673773", + "_score": 1, + "_source": { + "metadata": [ + { + "idV1": "NTM=-U2VjdGlvbnM=", + "prefLabel": "Technology", + "attributes": [], + "primaryTag": true, + "taxonomy": "sections", + "teaserTag": true, + "url": "https://www.ft.com/companies/technology", + "primary": "section" + }, + { + "idV1": "OA==-R2VucmVz", + "prefLabel": "Comment", + "attributes": [], + "taxonomy": "genre", + "url": "https://www.ft.com/stream/genreId/OA==-R2VucmVz" + }, + { + "idV1": "MDEwMzFmMmUtYThkNy00ZWJmLWEzOTYtOWM2OWQ3YzA0ZGE1-T04=", + "prefLabel": "Financial Times", + "attributes": [ + { + "value": "true", + "key": "is_company" + } + ], + "taxonomy": "organisations", + "url": "https://www.ft.com/stream/organisationsId/MDEwMzFmMmUtYThkNy00ZWJmLWEzOTYtOWM2OWQ3YzA0ZGE1-T04=" + }, + { + "idV1": "TnN0ZWluX09OX0FGVE1fT05fMzI5M19NU0w=-T04=", + "prefLabel": "Lehman Brothers Holdings", + "attributes": [ + { + "value": "us:LEHMQ", + "key": "wsod_key" + }, + { + "value": "true", + "key": "is_company" + } + ], + "taxonomy": "organisations", + "url": "https://www.ft.com/topics/organisations/Lehman_Brothers_Holdings_Inc" + }, + { + "idV1": "Mjk=-U2VjdGlvbnM=", + "prefLabel": "Companies", + "attributes": [], + "taxonomy": "sections", + "url": "https://www.ft.com/companies" + } + ] + } + } + ] + } +} diff --git a/test/fixtures/tag-not-found.json b/test/fixtures/tag-not-found.json new file mode 100644 index 0000000..07359cf --- /dev/null +++ b/test/fixtures/tag-not-found.json @@ -0,0 +1,14 @@ +{ + "took": 1, + "timed_out": false, + "_shards": { + "total": 1, + "successful": 1, + "failed": 0 + }, + "hits": { + "total": 0, + "max_score": null, + "hits": [] + } +} diff --git a/test/spec/search-spec.js b/test/spec/search-spec.js index 8795476..ac30968 100644 --- a/test/spec/search-spec.js +++ b/test/spec/search-spec.js @@ -59,6 +59,14 @@ describe('Search', () => { expect(result.total).to.equal(fixtureWithResults.hits.total); }) )); + + it('returns the document source', () => ( + subject().then((result) => { + result.forEach((doc) => { + expect(doc).to.include.keys('id', 'title'); + }); + }) + )); }); context('Response - no results', () => { @@ -79,14 +87,6 @@ describe('Search', () => { expect(result.total).to.equal(0); }) )); - - it('returns the document source', () => ( - subject().then((result) => { - result.forEach((doc) => { - expect(doc).to.include.keys('id', 'title'); - }); - }) - )); }); context('Response - error', () => { diff --git a/test/spec/tag-spec.js b/test/spec/tag-spec.js new file mode 100644 index 0000000..c5bf859 --- /dev/null +++ b/test/spec/tag-spec.js @@ -0,0 +1,72 @@ +const { expect } = require('chai'); +const nock = require('nock'); + +const subject = require('../../lib/tag'); + +const fixtureFound = require('../fixtures/tag-found.json'); +const fixtureNotFound = require('../fixtures/tag-not-found.json'); + +describe('Tag', () => { + afterEach(() => { + nock.isDone(); + nock.cleanAll(); + }); + + context('Response - found', () => { + const id = 'NTM=-U2VjdGlvbnM='; + + beforeEach(() => { + nock('https://next-elastic.ft.com') + .post('/v3_api_v2/item/_search') + .reply(200, fixtureFound); + }); + + it('returns an object', () => ( + subject(id).then((result) => { + expect(result).to.be.an('object'); + expect(result).to.include.keys('idV1', 'prefLabel', 'taxonomy'); + }) + )); + + it('plucks out the requested tag', () => { + subject(id).then((result) => { + expect(result.idV1).to.equal(id); + }) + }); + }); + + context('Response - not found', () => { + const id = 'No=-Exist='; + + beforeEach(() => { + nock('https://next-elastic.ft.com') + .post('/v3_api_v2/item/_search') + .reply(200, fixtureNotFound); + }); + + it('returns nothing', () => ( + subject(id).then((result) => { + expect(result).to.be.undefined; + }) + )); + }); + + context('Response - error', () => { + beforeEach(() => { + nock('https://next-elastic.ft.com') + .post('/v3_api_v2/item/_search') + .reply(500); + }); + + it('throws an HTTP error', () => ( + subject() + .then((result) => { + expect(result).to.equal('This should never run'); + }) + .catch((error) => { + expect(error).to.be.an('error'); + expect(error.name).to.equal('InternalServerError'); + }) + )); + }); +}); From a32d032bc03127d7881e90109e6cfc63efb9aca9 Mon Sep 17 00:00:00 2001 From: Matt Hinchliffe Date: Wed, 5 Apr 2017 08:51:36 +0100 Subject: [PATCH 2/2] Pluck only the necessary tag properties --- README.md | 4 ++++ lib/tag.js | 12 +++++++++++- test/spec/tag-spec.js | 10 ++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5067330..290b076 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,10 @@ es.search({ }); ``` +### `.tag(uuid[, timeout])` + +Get a single TME tag by UUID. Returns an object or `undefined` if no matches were found. + [1]: https://github.com/matthew-andrews/signed-aws-es-fetch [2]: https://www.npmjs.com/package/http-errors [3]: https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-source-filtering.html diff --git a/lib/tag.js b/lib/tag.js index c22f6a0..34c5e1b 100644 --- a/lib/tag.js +++ b/lib/tag.js @@ -1,8 +1,18 @@ const search = require('./search'); +const KEYS = [ 'idV1', 'prefLabel', 'taxonomy', 'attributes', 'url' ]; + +function pluck (rawTag) { + return KEYS.reduce((newTag, key) => { + newTag[key] = rawTag[key]; + return newTag; + }, {}); +} + function handleData (data, uuid) { if (data.length) { - return data[0].metadata.find((tag) => tag.idV1 === uuid); + const tag = data[0].metadata.find((tag) => tag.idV1 === uuid); + return pluck(tag); } } diff --git a/test/spec/tag-spec.js b/test/spec/tag-spec.js index c5bf859..dfe5abd 100644 --- a/test/spec/tag-spec.js +++ b/test/spec/tag-spec.js @@ -24,15 +24,21 @@ describe('Tag', () => { it('returns an object', () => ( subject(id).then((result) => { expect(result).to.be.an('object'); - expect(result).to.include.keys('idV1', 'prefLabel', 'taxonomy'); }) )); - it('plucks out the requested tag', () => { + it('finds the requested tag', () => { subject(id).then((result) => { expect(result.idV1).to.equal(id); }) }); + + it('plucks out the necessary keys', () => ( + subject(id).then((result) => { + expect(result).to.include.keys('idV1', 'prefLabel', 'taxonomy', 'attributes', 'url'); + expect(result).to.not.include.keys('primaryTag', 'teaserTag', 'primary'); + }) + )); }); context('Response - not found', () => {