Send Signed Requests

With Oracle Cloud Infrastructure, all REST requests must be signed per Oracle Cloud Infrastructure specification. Oracle provides the oci-common library for signing requests. This code example, which was explicitly designed for use with the Update Dynamic Entities and Export Digital Assistant Insights use cases, shows how to use the oci-common library to send signed REST calls to the Oracle Digital Assistant API.

Note:

This example sends signed requests. If you have a Digital Assistant instance that's paired with a subscription to a Fusion-based Oracle Cloud Applications service, such as HCM Cloud or Sales Cloud, or a 19.4.1 instance that was migrated to Oracle Cloud Infrastructure, then you must modify the code to instead use OAuth as described in Send IDCS OAuth Requests.

Disclaimer: This example is provided as-is, with no support provided by Oracle world-wide customer support.

Before You Begin

  • Follow the steps in Required Keys and OCIDs in Oracle Cloud Infrastructure Documentation to generate an API signing key, get the config file snippet, and create the OCI config file. The default configuration file name and location is ~/.oci/config. Here's an example of the config file's contents:

    [DEFAULT]
    user=ocid1.user.oc1..<unique_ID>
    fingerprint=<your_fingerprint>
    key_file=~/.oci/oci_api_key.pem
    tenancy=ocid1.tenancy.oc1..<unique_ID>
    region=<region such as us-ashburn-1>

    Note:

    Make sure that the region matches the region where your Oracle Digital Assistant instance is provisioned.
  • You must have already created the directory structure that's described for the use cases, as shown here:

    top folder
      config
      src
        lib
        main.js
      package.json
  • As part of implementing the use case, you created a config.js file in the config directory, which contains the instance configuration and run parameters. The section shown below provides the information that the example code uses to send the signed REST calls. If you haven't already, ensure that the OCI request signature configurations point to your config file and the profile name is correct.

    /*
     * OCI request signature configurations
     */
    
    // OCI CLI config file
    module.exports.OCI_CONFIG_FILE_PATH = '<put path to the OCI config file here: example ~/.oci/config>';
    // OCI CLI profile name
    module.exports.OCI_CONFIG_PROFILE_NAME = 'DEFAULT';
    
    /*
     * Oracle Digital Assistant configurations
     */
    
    // Digital Assistant host name without "https://"
    module.exports.ODA_HOSTNAME= '<put hostname here>';
    // API base path - do not modify
    module.exports.INSIGHTS_API_BASE_PATH = '/api/v1';

Add OCIManager.js to the lib Folder

In the lib folder, create a file named ODAManager.js, and then add this code.

const CONFIG = require('../../config/config');
const common = require('oci-common');
const log4js = require('log4js');
const logger = log4js.getLogger('OCIManager');
logger.level = CONFIG.LOGGING_LEVEL;

class OCIManager{

  constructor(){

  }

  /**
   * 
   * @param {string} uri OCI REST API Endpoint
   * @param {string} method REST API HTTP Method GET|PUT|POST|DELETE|PATCH
   * @param {object} body JSON Object to be passed as part of OCI REST API payload
   */
  async send(uri, method, body)
  {
    try {

      // Get OCI authentication provider
      const provider = await this._getAuthenticationProvider();      

      // Get OCI HTTP request signer
      const signer = new common.DefaultRequestSigner(provider);

      // Get OCI HTTP client
      const httpClient = new common.FetchHttpClient(signer);  

      // Prepare OCI HTTP client request
      const httpRequest = {
        uri: uri,
        method: method,
        // eslint-disable-next-line no-undef
        headers: new Headers(),
        body: body? JSON.stringify(body): null
      };

      // Send OCI REST API call
      logger.debug('Sending OCI REST API Call.');
      let response = await httpClient.send(httpRequest);
      logger.debug('OCI REST API Call was sent successfully, examining response content.');

      // If OCI response has issues
      if(!response.ok){
        const ociError = {
          status : response.status,
          statusText: response.statusText,
          opcRequestId: response.headers.get('opc-request-id'),
          message: (await response.json()).detail
        };
        logger.warn('OCI REST API Call returned unsuccessful response.');
        throw ociError;
      }

      // OCI response is valid, return OCI response
      logger.debug('OCI REST API call returned successful response.');
      const contentType = response.headers.get('content-type');

      const ociResult = {
        status: response.status,
        statusText: response.statusText,
        opcRequestId: response.headers.get('opc-request-id'),
        result: response.statusText == 'No Content' ? [] : (contentType === 'application/json' ? await response.json(): response)
      };

      // If no OCI result is returned
      // return empty result
      if(!ociResult.result){
        
        logger.warn('No result found, returning empty result.');
        ociResult.result = [];
        return ociResult;
      }

      // Return REST response
      if(ociResult.result.items){
        ociResult.result = ociResult.result.items;
        logger.debug(`${ociResult.result.length} objects found, returning results.`);
      }

      return ociResult;
      
    } catch (error) {
      const errorMessage = `Error calling OCI REST endpoint. Detailed error: ${error.message} Request ID: ${error.opcRequestId}.`;
      logger.error(errorMessage);
      throw error;
    }
  }

  /**
   * Creates and returns an OCI AuthenticationProvider
   * @returns OCI authentication provider
   */
  async _getAuthenticationProvider() {
    const provider = new common.ConfigFileAuthenticationDetailsProvider(
      CONFIG.OCI_CONFIG_FILE_PATH,
      CONFIG.OCI_CONFIG_PROFILE_NAME
    );
    return provider;    
  }
}

module.exports = new OCIManager();