Skip to content

Commit

Permalink
Merge pull request #796 from contentful/feat/support-tags
Browse files Browse the repository at this point in the history
feat: add tags support
  • Loading branch information
Tim Beyer authored Jun 11, 2021
2 parents b9b0eb8 + cd0b504 commit 47d9d03
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 13 deletions.
82 changes: 71 additions & 11 deletions lib/create-contentful-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
* @typedef {Object} ClientAPI
* @prop {function} getSpace
* @prop {function} getContentType
* @prop {function} getTag
* @prop {function} getTags
* @prop {function} getContentTypes
* @prop {function} getEntry
* @prop {function} getEntries
Expand Down Expand Up @@ -69,6 +71,7 @@ export default function createContentfulApi ({ http, getGlobalOptions }) {
const { wrapContentType, wrapContentTypeCollection } = entities.contentType
const { wrapEntry, wrapEntryCollection } = entities.entry
const { wrapAsset, wrapAssetCollection } = entities.asset
const { wrapTag, wrapTagCollection } = entities.tag
const { wrapAssetKey } = entities.assetKey
const { wrapLocaleCollection } = entities.locale
const notFoundError = (id) => {
Expand Down Expand Up @@ -295,6 +298,61 @@ export default function createContentfulApi ({ http, getGlobalOptions }) {
}
}

/**
* Gets a Tag
* @memberof ContentfulClientAPI
* @param {string} id
* @return {Promise<Entities.Tag>} Promise for a Tag
* @example
* const contentful = require('contentful')
*
* const client = contentful.createClient({
* space: '<space_id>',
* accessToken: '<content_delivery_api_key>'
* })
*
* const tag = await client.getTag('<asset_id>')
* console.log(tag)
*/
async function getTag (id) {
switchToEnvironment(http)

try {
const response = await http.get(`tags/${id}`)
return wrapTag(response.data)
} catch (error) {
errorHandler(error)
}
}

/**
* Gets a collection of Tags
* @memberof ContentfulClientAPI
* @param {Object=} query - Object with search parameters.
* @return {Promise<Entities.TagCollection>} Promise for a collection of Tags
* @example
* const contentful = require('contentful')
*
* const client = contentful.createClient({
* space: '<space_id>',
* accessToken: '<content_delivery_api_key>'
* })
*
* const response = await client.getTags()
* console.log(response.items)
*/
async function getTags (query = {}) {
switchToEnvironment(http)
query = normalizeSelect(query)

try {
const response = await http.get('tags', createRequestConfig({ query: query }))
return wrapTagCollection(response.data)
} catch (error) {
errorHandler(error)
}
}

/**
* Creates an asset key for signing asset URLs (Embargoed Assets)
* @memberof ContentfulClientAPI
Expand Down Expand Up @@ -443,16 +501,18 @@ export default function createContentfulApi ({ http, getGlobalOptions }) {
}

return {
getSpace: getSpace,
getContentType: getContentType,
getContentTypes: getContentTypes,
getEntry: getEntry,
getEntries: getEntries,
getAsset: getAsset,
getAssets: getAssets,
createAssetKey: createAssetKey,
getLocales: getLocales,
parseEntries: parseEntries,
sync: sync
getSpace,
getContentType,
getContentTypes,
getEntry,
getEntries,
getAsset,
getAssets,
getTag,
getTags,
createAssetKey,
getLocales,
parseEntries,
sync
}
}
4 changes: 3 additions & 1 deletion lib/entities/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import * as asset from './asset'
import * as assetKey from './asset-key'
import * as contentType from './content-type'
import * as locale from './locale'
import * as tag from './tag'

export default {
space,
entry,
asset,
assetKey,
contentType,
locale
locale,
tag
}
38 changes: 38 additions & 0 deletions lib/entities/tag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import copy from 'fast-copy'
import { toPlainObject, freezeSys } from 'contentful-sdk-core'

/**
* @memberof Entities
* @typedef Tag
* @prop {Entities.Sys} sys - Standard system metadata with additional entry specific properties
* @prop {string} name - Tag name
* @prop {function(): Object} toPlainObject() - Returns this tag as a plain JS object
*/

/**
* @private
* @param {Object} data - Raw tag data
* @return {Tag} Wrapped tag data
*/
export function wrapTag (data) {
return freezeSys(toPlainObject(copy(data)))
}

/**
* @memberof Entities
* @typedef TagCollection
* @prop {number} total
* @prop {number} skip
* @prop {number} limit
* @prop {Array<Entities.Tag>} items
* @prop {function(): Object} toPlainObject() - Returns this Tag collection as a plain JS object
*/

/**
* @private
* @param {Object} data - Raw tag collection data
* @return {TagCollection} Wrapped tag collection data
*/
export function wrapTagCollection (data) {
return freezeSys(toPlainObject(copy(data)))
}
20 changes: 20 additions & 0 deletions test/integration/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,12 +404,32 @@ test('Gets assets', async (t) => {
const response = await client.getAssets()
t.ok(response.items, 'items')
})

test('Gets Locales', async (t) => {
t.plan(2)
const response = await client.getLocales()
t.ok(response.items, 'items')
t.equals(response.items[0].code, 'en-US', 'first locale is en-US')
})

test('Gets tag', async (t) => {
t.plan(3)
const response = await client.getTag('publicTag1')
t.ok(response.sys, 'sys')
t.ok(response.name, 'name')
t.equal(response.name, 'public tag 1')
})

test('Gets tags', async (t) => {
t.plan(3)
const response = await client.getTags()
t.ok(response.items, 'items')

const publicTag = response.items.find((tag) => tag.sys.id === 'publicTag1')
t.ok(publicTag)
t.equal(publicTag.name, 'public tag 1')
})

test('Sync space', async (t) => {
t.plan(6)
const response = await client.sync({ initial: true })
Expand Down
4 changes: 4 additions & 0 deletions test/unit/create-contentful-api-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ function setupWithData ({
locale: {
wrapLocale: sinon.stub(),
wrapLocaleCollection: sinon.stub()
},
tag: {
wrapTag: sinon.stub(),
wrapTagCollection: sinon.stub()
}
}
createContentfulApiRewireApi.__Rewire__('entities', entitiesMock)
Expand Down
23 changes: 23 additions & 0 deletions test/unit/entities/tag-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import test from 'blue-tape'
import { tagMock } from '../mocks'
import { wrapTag, wrapTagCollection } from '../../../lib/entities/tag'

test('Tag is wrapped', (t) => {
const wrappedTag = wrapTag(tagMock)
t.looseEqual(wrappedTag.toPlainObject(), tagMock)
t.end()
})

test('Tag collection is wrapped', (t) => {
const tagCollection = {
total: 1,
skip: 0,
limit: 100,
items: [
tagMock
]
}
const wrappedTag = wrapTagCollection(tagCollection)
t.looseEqual(wrappedTag.toPlainObject(), tagCollection)
t.end()
})
43 changes: 42 additions & 1 deletion test/unit/mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,53 @@ const localeMock = {
code: 'en-US'
}

const tagMock = {
sys: {
space: {
sys: {
type: 'Link',
linkType: 'Space',
id: 'ezs1swce23xe'
}
},
id: 'publicTag1',
type: 'Tag',
createdAt: '2021-02-11T14:44:48.594Z',
updatedAt: '2021-02-11T14:44:48.594Z',
environment: {
sys: {
id: 'master',
type: 'Link',
linkType: 'Environment'
}
},
createdBy: {
sys: {
type: 'Link',
linkType: 'User',
id: '1a9rUrb3AjDqTxUGOYxBDe'
}
},
updatedBy: {
sys: {
type: 'Link',
linkType: 'User',
id: '1a9rUrb3AjDqTxUGOYxBDe'
}
},
version: 1,
visibility: 'public'
},
name: 'mock tag'
}

export {
linkMock,
sysMock,
contentTypeMock,
entryMock,
assetMock,
assetKeyMock,
localeMock
localeMock,
tagMock
}

0 comments on commit 47d9d03

Please sign in to comment.