Skip to content

Commit

Permalink
feat(endpoints): add UI Extensions endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Benedikt Rötsch committed Aug 15, 2017
1 parent 8baf9c6 commit 9d4bcf3
Show file tree
Hide file tree
Showing 7 changed files with 320 additions and 4 deletions.
89 changes: 88 additions & 1 deletion lib/create-space-api.js
Expand Up @@ -47,6 +47,9 @@ import { Promise } from 'es6-promise'
* @prop {function} getApiKeys
* @prop {function} createApiKey
* @prop {function} createApiKeyWithId
* @prop {function} getUiExtension
* @prop {function} getUiExtensions
* @prop {function} createUiExtension
*/

/**
Expand All @@ -72,6 +75,7 @@ export default function createSpaceApi ({
const {wrapApiKey, wrapApiKeyCollection} = entities.apiKey
const {wrapEditorInterface} = entities.editorInterface
const {wrapUpload} = entities.upload
const {wrapUiExtension, wrapUiExtensionCollection} = entities.uiExtension

/**
* Space instances.
Expand Down Expand Up @@ -1149,6 +1153,86 @@ export default function createSpaceApi ({
.then((response) => wrapApiKey(http, response.data), errorHandler)
}

/**
* Gets an UI Extension
* @memberof ContentfulSpaceAPI
* @param {string} id
* @return {Promise<UiExtension.UiExtension>} Promise for an UI Extension
* @example
* const contentful = require('contentful-management')
*
* const client = contentful.createClient({
* accessToken: '<content_management_api_key>'
* })
*
* client.getSpace('<space_id>')
* .then((space) => space.getUiExtension('<ui-extension-id>'))
* .then((uiExtension) => console.log(uiExtension))
* .catch(console.error)
*/
function getUiExtension (id) {
return http.get('extensions/' + id)
.then((response) => wrapUiExtension(http, response.data), errorHandler)
}

/**
* Gets a collection of UI Extension
* @memberof ContentfulSpaceAPI
* @return {Promise<UiExtension.UiExtensionCollection>} Promise for a collection of UI Extensions
* @example
* const contentful = require('contentful-management')
*
* const client = contentful.createClient({
* accessToken: '<content_management_api_key>'
* })
*
* client.getSpace('<space_id>')
* .then((space) => space.getUiExtensions()
* .then((response) => console.log(response.items))
* .catch(console.error)
*/
function getUiExtensions () {
return http.get('extensions')
.then((response) => wrapUiExtensionCollection(http, response.data), errorHandler)
}

/**
* Creates a UI Extension
* @memberof ContentfulSpaceAPI
* @see {UiExtension.UiExtension}
* @param {object} data - Object representation of the UI Extension to be created
* @return {Promise<UiExtension.UiExtension>} Promise for the newly created UI Extension
* @example
* const contentful = require('contentful-management')
*
* const client = contentful.createClient({
* accessToken: '<content_management_api_key>'
* })
*
* client.getSpace('<space_id>')
* .then((space) => space.createUiExtension({
* extension: {
* name: 'My awesome extension',
* src: 'https://example.com/my',
* fieldTypes: [
* {
* type: 'Symbol'
* },
* {
* type: 'Text'
* }
* ],
* sidebar: false
* }
* }))
* .then((uiExtension) => console.log(uiExtension))
* .catch(console.error)
*/
function createUiExtension (data) {
return http.post('extensions', data)
.then((response) => wrapUiExtension(http, response.data), errorHandler)
}

/*
* @private
* sdk relies heavily on sys metadata
Expand Down Expand Up @@ -1198,6 +1282,9 @@ export default function createSpaceApi ({
getApiKey: getApiKey,
getApiKeys: getApiKeys,
createApiKey: createApiKey,
createApiKeyWithId: createApiKeyWithId
createApiKeyWithId: createApiKeyWithId,
getUiExtension: getUiExtension,
getUiExtensions: getUiExtensions,
createUiExtension: createUiExtension
}
}
4 changes: 3 additions & 1 deletion lib/entities/index.js
Expand Up @@ -10,6 +10,7 @@ import * as role from './role'
import * as apiKey from './api-key'
import * as upload from './upload'
import * as organization from './organization'
import * as uiExtension from './ui-extension'

export default {
space,
Expand All @@ -23,5 +24,6 @@ export default {
role,
apiKey,
upload,
organization
organization,
uiExtension
}
102 changes: 102 additions & 0 deletions lib/entities/ui-extension.js
@@ -0,0 +1,102 @@
/**
* UI Extension instances
* @namespace UiExtension
*/
import cloneDeep from 'lodash/cloneDeep'
import { freezeSys, toPlainObject } from 'contentful-sdk-core'
import enhanceWithMethods from '../enhance-with-methods'
import {
createUpdateEntity,
createDeleteEntity
} from '../instance-actions'

/**
* @memberof UiExtension
* @typedef UiExtension
* @prop {Meta.Sys} sys - System metadata
* @prop {object} extension - UI Extension config
* @prop {string} extension.name - Extension name
* @prop {array} extension.fieldTypes - Field types where an extension can be used
* @prop {array} extension.src - URL where the root HTML document of the extension can be found
* @prop {array} extension.srcdoc - String representation of the extension (e.g. inline HTML code)
* @prop {boolean} extension.sidebar - Controls the location of the extension. If true it will be rendered on the sidebar instead of replacing the field's editing control
* @prop {function(): Object} toPlainObject() - Returns this UI Extension as a plain JS object
*/

function createUiExtensionApi (http) {
return {
/**
* Sends an update to the server with any changes made to the object's properties
* @memberof UiExtension
* @func update
* @return {Promise<UiExtension>} Object returned from the server with updated changes.
* @example
* const contentful = require('contentful-management')
*
* const client = contentful.createClient({
* accessToken: '<content_management_api_key>'
* })
*
* client.getSpace('<space_id>')
* .then((space) => space.getUiExtension('<ui_extension_id>'))
* .then((uiExtension) => {
* uiExtension.extension.name = 'New UI Extension name'
* return uiExtension.update()
* })
* .then((uiExtension) => console.log(`UI Extension ${uiExtension.sys.id} updated.`))
* .catch(console.error)
*/
update: createUpdateEntity({
http: http,
entityPath: 'extensions',
wrapperMethod: wrapUiExtension
}),

/**
* Deletes this object on the server.
* @memberof UiExtension
* @func delete
* @return {Promise} Promise for the deletion. It contains no data, but the Promise error case should be handled.
* @example
* const contentful = require('contentful-management')
*
* const client = contentful.createClient({
* accessToken: '<content_management_api_key>'
* })
*
* client.getSpace('<space_id>')
* .then((space) => space.getUiExtension('<ui_extension_id>'))
* .then((uiExtension) => uiExtension.delete())
* .then(() => console.log(`UI Extension deleted.`))
* .catch(console.error)
*/
delete: createDeleteEntity({
http: http,
entityPath: 'extensions'
})
}
}

/**
* @private
* @param {Object} http - HTTP client instance
* @param {Object} data - Raw UI Extension data
* @return {UiExtension} Wrapped UI Extension data
*/
export function wrapUiExtension (http, data) {
const uiExtension = toPlainObject(cloneDeep(data))
enhanceWithMethods(uiExtension, createUiExtensionApi(http))
return freezeSys(uiExtension)
}

/**
* @private
* @param {Object} http - HTTP client instance
* @param {Object} data - Raw UI Extension collection data
* @return {UiExtensionCollection} Wrapped UI Extension collection data
*/
export function wrapUiExtensionCollection (http, data) {
const uiExtensions = toPlainObject(cloneDeep(data))
uiExtensions.items = uiExtensions.items.map((entity) => wrapUiExtension(http, entity))
return freezeSys(uiExtensions)
}
2 changes: 2 additions & 0 deletions test/integration/integration-tests.js
Expand Up @@ -7,6 +7,7 @@ import webhookTests from './webhook-integration'
import spaceMembershipTests from './space-membership-integration'
import roleTests from './role-integration'
import apiKeyTests from './api-key-integration'
import uiExtensionTests from './ui-extension-integration'
import generateRandomId from './generate-random-id'
import { createClient } from '../../'

Expand Down Expand Up @@ -114,6 +115,7 @@ test('Create space for tests which create, change and delete data', (t) => {
spaceMembershipTests(t, space)
roleTests(t, space)
apiKeyTests(t, space)
uiExtensionTests(t, space)
test.onFinish(() => space.delete())
})
})
54 changes: 54 additions & 0 deletions test/integration/ui-extension-integration.js
@@ -0,0 +1,54 @@
export default function uiExtensionTests (t, space) {
t.test('Create, update, get, get all and delete UI Extension', (t) => {
return space.createUiExtension({
extension: {
name: 'My awesome extension',
src: 'https://extensions.example.com/your-extension.html',
fieldTypes: [
{ type: 'Text' }
]
}
})
.then((uiExtension) => {
t.equals(uiExtension.sys.type, 'Extension', 'type')
t.equals(uiExtension.extension.name, 'My awesome extension', 'name')

uiExtension.extension.name = 'New name'
return uiExtension.update()
})
.then((uiExtension) => {
t.equals(uiExtension.extension.name, 'New name', 'name')

return space.getUiExtension(uiExtension.sys.id)
.then((uiExtension) => {
t.equals(uiExtension.sys.id, uiExtension.sys.id, 'id')
t.equals(uiExtension.extension.name, 'New name', 'name')

return space.getUiExtensions()
.then((result) => {
t.equals(result.items.length, result.total, 'returns the just created ui extensions')
})
.then(() => uiExtension.delete())
})
})
})

t.test('Create and delete UI Extension hosted by Contentful', (t) => {
return space.createUiExtension({
extension: {
name: 'My awesome extension hosted at Contentful',
srcdoc: '<html><head><title>MyAwesomeUiExtension</title></head><body><h1>Awesome</h1></body></html>',
fieldTypes: [
{ type: 'Text' }
]
}
})
.then((uiExtension) => {
t.equals(uiExtension.sys.type, 'Extension', 'type')
t.equals(uiExtension.extension.name, 'My awesome extension hosted at Contentful', 'name')
t.equals(uiExtension.extension.srcdoc, '<html><head><title>MyAwesomeUiExtension</title></head><body><h1>Awesome</h1></body></html>', 'name')

return uiExtension.delete()
})
})
}
57 changes: 57 additions & 0 deletions test/unit/entities/ui-extension-test.js
@@ -0,0 +1,57 @@
import test from 'tape'
import {cloneMock} from '../mocks/entities'
import setupHttpMock from '../mocks/http'
import {wrapUiExtension, wrapUiExtensionCollection} from '../../../lib/entities/ui-extension'
import {
entityWrappedTest,
entityCollectionWrappedTest,
entityUpdateTest,
entityDeleteTest,
failingActionTest,
failingVersionActionTest
} from '../test-creators/instance-entity-methods'

function setup (promise) {
return {
httpMock: setupHttpMock(promise),
entityMock: cloneMock('uiExtension')
}
}

test('UiExtension is wrapped', (t) => {
entityWrappedTest(t, setup, {
wrapperMethod: wrapUiExtension
})
})

test('UiExtension collection is wrapped', (t) => {
return entityCollectionWrappedTest(t, setup, {
wrapperMethod: wrapUiExtensionCollection
})
})

test('UiExtension update', (t) => {
return entityUpdateTest(t, setup, {
wrapperMethod: wrapUiExtension
})
})

test('UiExtension update fails', (t) => {
return failingVersionActionTest(t, setup, {
wrapperMethod: wrapUiExtension,
actionMethod: 'update'
})
})

test('UiExtension delete', (t) => {
return entityDeleteTest(t, setup, {
wrapperMethod: wrapUiExtension
})
})

test('UiExtension delete fails', (t) => {
return failingActionTest(t, setup, {
wrapperMethod: wrapUiExtension,
actionMethod: 'delete'
})
})

0 comments on commit 9d4bcf3

Please sign in to comment.