Skip to content

A fluent api wrapper for the Powerchord Forte REST API

License

Notifications You must be signed in to change notification settings

powerchordlabs/forte-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ForteAPI Travis npm package

Easily communicate with the Powerchord Forte REST API!

ForteAPI is a wrapper around the Powerchord Forte REST API that allows you to focus on the data you want, rather than urls and verbs used to get or update the data you want.

Install

$ npm i -S forte-api

Features

  • Simple API Simplifies REST API access so you can focus on data and not HTTP
  • Semi-Automatic token auth You simply provide secret keys, or a pre-existing token and ForteApi does the rest
  • Client Fingerprinting By default ForteApi will perform a browser fingerprint on the client via a non-blocking background process, i.e. WebWorker. This can be disabled via options if legal requirements dictate.

Documentation

Quick Start

An Isomorphic usage example:

server.js
import createApi from 'forte-api'

// a global to be injected in to your html markup
CLIENT_GLOBALS = {
    scope: {
        trunk: 'TRUNKID',
        branch: 'BRANCHID'
    }   
}

let creds = {
  privateKey: 'PRIVATEKEY',
  publicKey: 'PUBLICKEY'
}

// create a new api instance using secret keys
let api = createApi(creds, CLIENT_GLOBALS.scope)

// listen for authentication events to capture the token
api.on('auth', (err, token) => {
    if(err) {
        console.log('An api auth error occurred:', err)
        return
    }

    console.log('Api auth success:', token)
    creds.bearerToken = token
})

app.get('*', function(req, res, next) => {
  res.send(ReactDOM.renderToString(<App api={api} />))
})
client.js
import createApi from 'forte-api'

// presuming CLIENT_GLOBALS was injected in your markup by the server
let creds = { bearerToken: CLIENT_GLOBALS.BearerToken }

let api = createApi(creds, CLIENT_GLOBALS.scope)

ReactDOM.render(<App api={api} />, document.getElementById('app'));
app.js
let results = api.composite
    .query({...})
    .then(composed =>
        return api.location.get(id)
          .then(location => return {
            composed.data,
            location.data
          })
    )

API

Constructor

createApi(credentials, scope, [options])

Creates an instance of the Forte Api.

import createApi from 'forte-api'

let scope = {
  trunk: 'TRUNKID',
  branch: 'BRANCHID'
}

// defaults
let api = createApi(credentials, scope);

// override api options
let api = createApi(credentials, scope, options);
args
  • credentials: {Object} Used to manage Authentication for api requests.
    • bearerToken: {string} Used by default if not null. The token will be added as an Authorization header for all endpoint requests.
    • privateKey: {string}, publicKey: {string} server-side only If bearerToken is null, an attempt will be made to use the publicKey and privateKey fields to generate a bearerToken for you. You can use the on('auth', cb) handler to subscribe to the 'auth' event and capture the bearerToken for later use, e.g. injecting the token in a client-side global.
  • scope: {Object}
    • hostname: {string} Sets the hostname scope for all requests, this is typically the TLD for your XPerience.
    • trunk: {string} Sets the trunk scope for all requests. See Organization Scopes
    • branch: {string} Optional: sets the branch scope for all requests. Note, that an error will be thrown if branch is null when accessing endpoints that require it. See Organization Scopes
  • options: {Object}
    • url: {string} default: https://api.powerchord.io The base Api url.
    • fingerPrintingEnabled: {boolean} client-side only default: true If true, performs a browser fingerprint, once per session, via a non-blocking background process, i.e. WebWorker.

Organization Scopes

All api requests require at least a trunk scope and most also require a branch scope to be able to access your data.

The constructor requires a scope.trunk param, but for requests requiring branch scope you can also use api.withBranch(). This is particularly useful on the server side, where you may have non-branch api calls during bootstrapping, as well as branch scoped calls during individual page requests.

var creds = {...}
var scope = { trunk: 'TRUNKID' } // note the lack of a branch

var api = createApi(creds, scope, opts)

// use lifecycle middleware to resolve scope
app.use(lifecycle(api))

// do some non-branch calls via lifecycle middleware...
...

app.get('*', (req, res, next) => {
  // create a branch-scoped api instance for the app to use
  branchApi = api.withBranch(req.lifecycle.scope.branch)

  render(<App api={branchApi} />)
})

api.withBranch(ID)

A convenience method that creates a new api scoped to the specified ID. All configuration is replicated from the original api instance.

args:
  • ID: {string} The identifier of the branch that future request should be scoped to.
var scope = { trunk: 'TRUNKID' }
var api = createApi(creds, scope).withBranch('BRANCHID')

Or the equivalent:

var scope = { trunk: 'TRUNKID', branch: 'BRANCHID' }
var api = createApi(creds, scope)

api.getScope(): {object}

Returns the scope of the api.

result:
  • trunk: {string} The trunk ID of the scope.
  • branch: {string|null} The branch ID of the scope. Since only trunk is required, it is possible for the branch to be null if the api has not been given a branch scope.

Events

api.on('auth', callback(err, token))

When the api successfully creates a new Bearer Token session this method will be invoked with the new bearerToken value. If an error occured it will be returned via the err argument.

import createApi from 'forte-api'
let api = createApi(credentials, organization, options);

api.on('auth', (err, token) => {
    if(err) {
        console.log('An api auth error occurred:', err)
        return
    }
    console.log('Api auth success:', bearerToken)

    CLIENT_SIDE_GLOBALS.BearerToken = bearerToken
})

Endpoints

Endpoints are the main progamming point for api data access. They are the abstractions of the REST API endpoints. All of the endpoints return promises to allow chaining.

api.organizations.getOne('orgid').then(
  (response) => {
    console.log('success:', response.data)
  },
  (response) => {
    console.log('error:', response.statusText)
  }
)
Responses

All Endpoints return promises that have the following response signature for both success/error handlers:

  • data: {string|Object} The deserialized response body.
  • status: {number} HTTP status code of the response.
  • statusText: {string} HTTP status text of the response.
  • headers: {Object} HTTP headers of the response.

Organizations

api.organizations.getMany(filter): [{organization}, ...]

Returns an array of organization objects matching the filter option(s).

args:
  • filter: {Object} A json object that is used to filter results from the api.
// return all active items
api.organizations.getMany({status: 'active'}).then((response) => {
  console.log('organziations:', response.data)
})
api.organizations.getOne(id): {organization}

Returns one organization.

args:
  • id: {string} The id of the organization to get.
api.organizations.getOne('1').then((response) => {
  console.log('organization:', response.data)
})

Locations

api.locations.getMany(filter): [{location}, ...]

Returns an array of location objects matching the filter option(s).

args:
  • filter: {Object} A json object that is used to filter results from the api.
// return all active items
api.locations.getMany({status: 'active'}).then((response) => {
  console.log('locations:', response.data)
})
api.locations.getOne(id): {location}

Returns one location.

args:
  • id: {string} The id of the location to get.
api.locations.getOne('1').then((response) => {
  console.log('location:', response.data)
})

Content

api.content.getMany(type, filter): [{content}, ...]

Returns an array of content objects matching the type and filter option(s).

args:
  • type: {string} The type of content to get.
  • filter: {Object} A json object that is used to filter results from the api.
// return all active items
api.content.getMany('PRODUCT', {status: 'active'}).then((response) => {
  console.log('content items:', response.data)
})
api.content.getOne(type, id): {content}

Returns one content object.

args:
  • type: {string} The type of content to get.
  • id: {string} The id of the content to get.
api.locations.getOne('PRODUCT', '1').then((response) => {
  console.log('content item:', response.data)
})
api.content.getManyComplex(type, filter): [{content}, ...]

Returns an array of content objects matching the type and filter option(s).

args:
  • type: {string} The type of content to get.
  • filter: {Object} A json object that is used to filter results from the api.
// return all items that have values.showProducts = true paged with 10 per page
api.content.getManyComplex('PRODUCT', { pagination: { perPage: 10, page: 0 }, valuesFields: { showProducts: { equals: true } }}).then((response) => {
  console.log('headers:', response.header) // contains paging information

  // Available Pagination Headers
  // Powerchord-Pagination-Desc:false
  // Powerchord-Pagination-Page:0
  // Powerchord-Pagination-Pagecount:4
  // Powerchord-Pagination-Perpage:10
  // Powerchord-Pagination-Resultcount:40
  // Powerchord-Pagination-Sortby:title

  console.log('content items:', response.data)
})
api.content.aggregate(type, listFilter, aggregate): [{content}, ...]

Returns an object of results objects matching the type and filter option(s).

args:
  • type: {string} The type of content to get.
  • listFilter: {Object} A json object that is used to filter results from the api.
  • aggregate: {Object} A json object that is used to specify the value fields to perform an aggregate on.
// return aggregate values for the description value in the values.specifications collection
api.content.aggregate('PRODUCT',
{
  valuesFields: {
    showProduct: {
      equals:true
    }
  }
},
{
  parent:"specifications",
  category:"description",
  key:"value"
}).then((response) => {
  console.log('aggregate items:', response.data)
})

Analytics

api.analytics.track(events)

Writes events to the platform API.

args:
api.analytics.track({
    'pageview': {
        title: 'Hello World',
        location: 'http://my.site.com/welcome?u=me'
    }
})
Supported Analytics Events
pageview
  • title: {string} The title of the page.
  • location: {string} The full url of the page excluding the hash.
api.analytics.track({
    'pageview': {
        title: 'Hello World',
        location: 'http://my.site.com/welcome?u=me'
    }
})

Composite

advanced topic

api.composite.query(query)

Composite is an endpoint that takes a multi-entity structured query and returns all entities via one network call. While you are free to use this method directly, the mechanics of composite are complicated. Using forte-conductor is recommended.

args:
  • query: {Object}

Carts

The carts API follows a RESTful control flow pattern base on the Powerchord Platform API.

For example, given an API request of POST /carts/123/items/ the corresponding forte-api call would be carts.id(123).post({...})

For more detail regarding Paltform API see: Platform API docs