Content & Experience Cloud CaaS REST API: reading

May 4, 2017 | 14 minute read
Dolf Dijkstra
Cloud Solutions Architect
Text Size 100%:

In this blog article I will introduce you to the Oracle Content & Experience Content as a Service REST API. In this blog I will discuss the most used read operations. In a following blogs I will introduce writing content items.

This blog discusses a non-public API. The API might change without warning. A Content as a Service API might be published later as part of the Content & Experience Cloud.

Recently Oracle released Content as a Service as part of Content & Experience Cloud. Content as a Service (CaaS) is a service to manage structured content. It is a service complementary to Docs and Sites Cloud Service and can be used as a corner stone for a Headless Content Management System.

CaaS has several building blocks: content items, content types, fields and data types. A content item is a piece of content, like a Blog Article. It is structured via a content type, for instance the BlogArticle content type. The content type defines what fields or attributes a piece of content consists of. It is similar to an Assettype in Oracle WebCenter Sites. A content type holds a number of fields, for instance title, heading and image. These fields are of a certain data type, like text, large text, number,  date and others.

CaaS comes with a REST API to read and write content types, content items, digital assets, fields and data types. I will provide a number of samples on how to use this API.

For this series of blog articles I will use nodejs as the client. The code makes use of two nodejs libraries: lodash and request-promise-native. Lodash is used as a helper for object manipulations and request-promise-native is used as a HTTP client. I have used the promise variant of the nodejs request library as I tend to favour Promises over callbacks because of it's chaining capabilities. If you want some more background on Promises and callbacks, please read this article.

I do expect some familiarity with nodejs/JavaScript to understand the code constructs that I have used. But even without that familiarity I do expect most developers and architects to be able to follow the code, and most importantly the API calls.

For this project to read with the CaaS REST API I have used to following package.json. This file is used by nodejs' npm tool to define the project, declare imports etc.

{
  "name": "cec-basics",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Dolf Dijkstra",
  "license": "ISC",
  "dependencies": {
    "lodash": "^4.17.4",
    "request": "^2.81.0",
    "request-promise-native": "^1.0.3"
  }

The code to read content from CaaS is stored in two files, both in the ./src directory: management-client.js and index.js.

First: management-client.js. A simple helper file that makes the HTTP get request to CaaS, and decorates that call with the correct headers. We'll use Basic auth in this example.


const request = require('request-promise-native')
const _ = require('lodash')
const transform = (body, response, resolveWithFullResponse) => ({
  data: body,
  headers: response.headers,
  statusCode: response.statusCode
})
function client (user, pass) {
  let auth = { user: user, pass: pass, sendImmediately: true }
  function get_http (url, args) {
    return request.get(_options('GET', url, args))
  }
  function _options (method, url, args) {
    let options = {
      method: method,
      uri: url,
      auth: auth,
      json: true,
      transform: transform
    }
    _.merge(options, args)
    return options
  }
  return { get: get_http }
}
module.exports = { client: client }

Most of the work is done in index.js


const _ = require('lodash')
const base = process.env.CEC_URL
const user = process.env.CEC_USER
const password = process.env.CEC_PASSWORD
const client = require('./management-client').client(user, password)
/*dump response headers and body to the console */ function dump (response) {
  console.log(response.headers)

  return body(response)
}
/*dump response body to the console */ function body (response) {
  let data = response.data

  if (data instanceof Buffer) {
    print(data.toString('utf8'))
  } else {
    print(json(data))
  }
  return response
}
const print = e => {
  console.log(e)
}
const extract = (data, fields) =>
  fields.reduce((acc, e) => {
    acc[e] = data[e]

    return acc
  }, {})
const json = e => JSON.stringify(e, null, 2)
const printJson = e => {
  console.log(json(e))

  return e
}
var commands = {
  dataTypes: function () {
    return client.get(base + '/content/management/api/v1/dataTypes').then(body)
  },
  types: function () {
    return client.get(base + '/content/management/api/v1/types').then(body)
  },
  'aggregates-types': function () {
    return client
      .get(base + '/content/management/api/v1/aggregates/types')
      .then(body)
  },
  fields: function () {
    return client.get(base + '/content/management/api/v1/fields').then(body)
  },
  'types-details': function () {
    return client
      .get(base + '/content/management/api/v1/types')
      .then(response => {
        let promises = response.data.items
          .filter(e => !e.isProxy)
          .map(e => client.get(e.link.href))

        Promise.all(promises).then(values => {
          console.log(json(values.map(response => response.data)))
        })
      })
  },
  items: function () {
    return client.get(base + '/content/management/api/v1/items').then(body)
  },
  dam: function () {
    return client
      .get(base + '/content/management/api/v1/items', {
        qs: {
          'field:type:equals': 'DigitalAsset'
        }
      })
      .then(body)
  },
  articles: function () {
    return client
      .get(base + '/content/management/api/v1/items', {
        qs: {
          'field:type:equals': 'Article'
        }
      })
      .then(body)
  }
}
if (process.argv.length < 3) {
  console.log('Possible commands: ' + Object.keys(commands).join(','))
} else {
  var cmd = process.argv[2]

  let fn = commands[cmd]

  if (!fn) {
    console.log(cmd + ' not found')

    return
  }
  /*if cmd not found it does not crash nicely*/
  fn().catch(err => {
    console.log(err)
  })
}

If you have there three files (package.json, src/management-client.js and src/index.js), you can install the node libraries with the command

npm install

This will will print out a list of installed libraries:

[Image with installed libraries is omitted]

if you run node src/index.js, you'll see that the application runs.

This gives you a peek into the various REST calls I'll discuss here.

I am using a small Bash script (sample.sh) to spare me typing node index.js each time:

node src/index.js $@

The last preparation step to work with the sample is to provide the URL, username and password to connect to the server. I have used environment variables is this sample, instead of hard coding or a configuration file. If you issue the following 3 commands in your Linux Bash shell, replacing the sample values with your values, you instruct index.js to use these variables.

export CEC_URL='https://your-host.documents.us2.oraclecloud.com' export CEC_USER='someone@oracle.com' export CEC_PASSWORD='mypassword'

Now you are ready to make some REST calls.

The 4 REST calls that I will be using in this blog are the 4 that show the 4 different building blocks for CaaS: data-types, fields, content-types and content-items.

  •  /content/management/api/v1/dataTypes
  •  /content/management/api/v1/fields
  •  /content/management/api/v1/types
  •  /content/management/api/v1/items

If you would execute ./sample.sh dataTypes a response like this would be printed so the screen


{
  "hasMore": false,
  "count": 9,
  "items": [
    {
      "datatype": "text",
      "description": "Supports upto 2000 characters"
    },
    {
      "datatype": "largetext",
      "description": "Supports unlimited text"
    },
    {
      "datatype": "number",
      "description": "Supports Supports as signed Integer value with the range of -2,147,483,648 to 2,147,483,647"
    },
    {
      "datatype": "decimal",
      "description": "Supports float or decimal values with the range of approximately ±3.40282347E+38F (6-7 significant decimal digits)"
    },
    {
      "datatype": "boolean",
      "description": "Supports Boolean values of TRUE or FALSE, default value is FALSE for empty"
    },
    {
      "datatype": "reference",
      "description": "Supports the reference relationship to other Item(s)"
    },
    {
      "datatype": "datetime",
      "description": "Supports date and time values"
    },
    {
      "datatype": "location",
      "description": "Supports location data about geo location using longitude and latitude values"
    },
    {
      "datatype": "json",
      "description": "Supports the json type data"
    }
  ],
  "links": [
    {
      "href": "http://yourhost.us.oracle.com:19200/content/management/api/v1/dataTypes",
      "rel": "self",
      "templated": false,
      "method": "",
      "profile": "",
      "mediaType": ""
    },
    {
      "href": "http://yourhost.us.oracle.com:19200/content/management/api/v1/dataTypes",
      "rel": "canonical",
      "templated": false,
      "method": "",
      "profile": "",
      "mediaType": ""
    },
    {
      "href": "http://yourhost.us.oracle.com:19200/content/management/api/v1/metadata-catalog/dataTypes",
      "rel": "describedby",
      "templated": false,
      "method": "GET",
      "profile": "",
      "mediaType": "application/schema+json"
    },
    {
      "href": "http://yourhost.us.oracle.com:19200/content/management/api/v1/dataTypes?offset=0",
      "rel": "first",
      "templated": false,
      "method": "",
      "profile": "",
      "mediaType": ""
    },
    {
      "href": "http://yourhost.us.oracle.com:19200/content/management/api/v1/dataTypes?offset=0",
      "rel": "last",
      "templated": false,
      "method": "",
      "profile": "",
      "mediaType": ""
    }
  ],
  "offset": 0
}
 

As you can see, the response includes the various data-types defined, like text and date, as well as a list of links to further navigate other resources.

If you would issue a call for the other REST API services like fields, content types or items, it might respond with no data because you have not created any content types or content items. Let's do that first so we can use the REST API some more.

We could use the REST API to create the fields etcetera, but I'll show first how to create them with the UI. So log on to your CEC instance, and navigate to the Experience tab. Install the Experience artefacts if the UI asks to do so.

The navigate to the Content Tab, and click on Digital Assets.

 

Empty Digital Assets

As you can see, no Digital Assets are found. First let's create a directory that can hold some images that we want to upload.

Navigate to Documents, and create  a new Folder called Images.

Documents folder

We could have used the Digital Assets folder to upload images to, but this folder is a personal folder and cannot be shared with others. It is better, if you want to share content (images) with co-workers, to create a 'normal' folder that can be shared with others, and then mark this folder as a Digital Assets folder. To do this, navigate to Digital Assets on the left panel, and click Manage Folders.

Add Folder to Digital Assets

You can now select Images and click done. Any image file uploaded to the Images folder, automatically become a Digital Asset.

Now upload some images; go back to the Documents > Images folder and drop some images in the folder.

Images on documents folder

If you now go back to the left panel Digital Assets panel, you can see that some digital assets are created.

 

Digital Assets

Now, we can use the REST API to list those items. The end-point to call to list all the content items is /content/management/api/v1/items. In the index.js sample that end-point is this called when issuing 'items'.

./sample items

{
  "hasMore": false,
  "limit": 6,
  "count": 6,
  "items": [
    {
      "id": "DigitalAsset_proxy_d8e97c92-65c8-4d8a-88ab-ee7339f4751c",
      "description": "",
      "link": {
        "href": "http://yourhost.us.oracle.com:19200/content/management/api/v1/items/DigitalAsset_proxy_d8e97c92-65c8-4d8a-88ab-ee7339f4751c",
        "rel": "Reference",
        "templated": false,
        "method": "GET",
        "profile": "",
        "mediaType": ""
      },
      "name": "PIC2-original.jpeg",
      "type": "DigitalAsset"
    },
    {
      "id": "DigitalAsset_proxy_75ce7990-71f9-4c59-b66e-a68fd186e0f7",
      "description": "",
      "link": {
        "href": "http://yourhost.us.oracle.com:19200/content/management/api/v1/items/DigitalAsset_proxy_75ce7990-71f9-4c59-b66e-a68fd186e0f7",
        "rel": "Reference",
        "templated": false,
        "method": "GET",
        "profile": "",
        "mediaType": ""
      },
      "name": "PIC3-original.jpeg",
      "type": "DigitalAsset"
    },
    {
      "id": "DigitalAsset_proxy_dfc1cdfb-df48-48b1-878f-32562aa48dbc",
      "description": "",
      "link": {
        "href": "http://yourhost.us.oracle.com:19200/content/management/api/v1/items/DigitalAsset_proxy_dfc1cdfb-df48-48b1-878f-32562aa48dbc",
        "rel": "Reference",
        "templated": false,
        "method": "GET",
        "profile": "",
        "mediaType": ""
      },
      "name": "Cafe_Supremo_small_light_brown.png",
      "type": "DigitalAsset"
    },
    {
      "id": "DigitalAsset_proxy_b87c2c1d-95e9-4527-a2c7-f03e0f13d374",
      "description": "",
      "link": {
        "href": "http://yourhost.us.oracle.com:19200/content/management/api/v1/items/DigitalAsset_proxy_b87c2c1d-95e9-4527-a2c7-f03e0f13d374",
        "rel": "Reference",
        "templated": false,
        "method": "GET",
        "profile": "",
        "mediaType": ""
      },
      "name": "PIC5-original.jpeg",
      "type": "DigitalAsset"
    },
    {
      "id": "DigitalAsset_proxy_669bac93-8ef0-4dbd-a4e0-31cdab4624de",
      "description": "",
      "link": {
        "href": "http://yourhost.us.oracle.com:19200/content/management/api/v1/items/DigitalAsset_proxy_669bac93-8ef0-4dbd-a4e0-31cdab4624de",
        "rel": "Reference",
        "templated": false,
        "method": "GET",
        "profile": "",
        "mediaType": ""
      },
      "name": "PIC1-original@2x.jpeg",
      "type": "DigitalAsset"
    },
    {
      "id": "DigitalAsset_proxy_8cf305ad-2d33-4dfd-96fb-cf20a4176866",
      "description": "",
      "link": {
        "href": "http://yourhost.us.oracle.com:19200/content/management/api/v1/items/DigitalAsset_proxy_8cf305ad-2d33-4dfd-96fb-cf20a4176866",
        "rel": "Reference",
        "templated": false,
        "method": "GET",
        "profile": "",
        "mediaType": ""
      },
      "name": "PIC4-original.jpeg",
      "type": "DigitalAsset"
    }
  ],
  "links": [],
  "offset": 0
}



As you can see, there is a list with our Digital Assets, If you have already other content items in your system, these will also be listed here. There is pagination support, so no need to worry that you will get a response with millions of content items if you have a lot of content items in your system. The links sections of the JSON document, holds the pagination links. I have removed that output here for brevity.

Working with Digital Assets is easy, they come prepackaged. If you want to work with other types of content, you will have to create those types in the system first. This is a simple process too. I will discuss that in a next blog article.

 

 

Dolf Dijkstra

Cloud Solutions Architect


Previous Post

Creating an instance in Oracle Public Cloud via REST API&#039;s - a usable example

Michael Shanley | 5 min read

Next Post


Content and Experience Cloud and Oracle Commerce Cloud Integration - Part 1

Derek Kam | 3 min read