UNPKG

@oracle/content-management-sdk

Version:
1,248 lines (1,172 loc) 116 kB
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (it) return (it = it.call(o)).next.bind(it); if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } /** * Copyright (c) 2017, 2022, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ */ // jshint ignore: start /* eslint-disable class-methods-use-this */ /* eslint-disable max-classes-per-file */ /* eslint-disable prefer-promise-reject-errors */ /* eslint-disable no-prototype-builtins */ /* eslint-disable global-require */ /* eslint-disable import/no-dynamic-require */ /* eslint-disable no-restricted-syntax */ // Detect whether we're running in a browser or in NodeJS. // Note that some other environments (e.g. React-Native) are not detected and may not // work properly. var isNodeJS = typeof window === 'undefined' && typeof process === 'object'; // // ------------------------------- Cross-browser Utility functions --------------------- // var utils = { bind: function bind(func, owner) { // eslint-disable-next-line func-names return function () { return func.apply(owner, [].slice.call(arguments)); }; }, extend: function extend(dest, orig) { for (var prop in orig) { if (orig.hasOwnProperty(prop)) { // eslint-disable-next-line no-param-reassign dest[prop] = orig[prop]; } } return dest; } }; // // ------------------------------- Internal Logger ------------------------------------- // var logger = function logger() { var theLogger = { logLevel: 'none', logLevels: ['error', 'warn', 'info', 'debug', 'log'] }; var dontLog = function dontLog( /* message */) {}; // swallow messages - default theLogger.updateLogger = function updateLogger(newLogger) { if (newLogger) { // setup loggers for each logLevel for (var i = 0; i < this.logLevels.length; i += 1) { var logLevel = theLogger.logLevels[i]; theLogger[logLevel] = typeof newLogger[logLevel] === 'function' ? utils.bind(newLogger[logLevel], newLogger) : dontLog; } } }; theLogger.updateLogger({}); // setup with no logging return theLogger; }(); // // ------------------------------- Internal Implementation ------------------------------------- // // RequireJS config support var requireConfig = { requirePaths: {}, getContentLayoutRequirePath: function getContentLayoutRequirePath(info) { var contentServer = info.contentServer; var cacheBuster = typeof info.cacheBuster === 'object' ? info.cacheBuster : { layoutKey: info.cacheBuster, systemKey: info.cacheBuster }; var layoutCacheBuster = cacheBuster.layoutKey ? "/" + cacheBuster.layoutKey : ''; var systemCacheBuster = cacheBuster.systemKey ? "/" + cacheBuster.systemKey : ''; // setup require config for this Content client's layouts if not already created if (!this.requirePaths[contentServer]) { // generate a unique require path to Content Layouts for this client var baseRequirePath = "contentLayoutPath" + Math.floor(100000000 + Math.random() * 900000000); var paths = {}; // create paths for 'published' and 'draft' paths[baseRequirePath + "published"] = contentServer + "/_compdelivery" + layoutCacheBuster; paths[baseRequirePath + "draft"] = contentServer + "/_themes/_components" + layoutCacheBuster; paths[baseRequirePath + "system"] = contentServer + "/_sitescloud" + systemCacheBuster + "/sitebuilder/contentlayouts"; // cache the base requireJS path for re-use with this content server this.requirePaths[contentServer] = baseRequirePath; // configure require to support these paths requirejs.config({ paths: paths }); } return this.requirePaths[contentServer]; }, preloadContentLayout: function preloadContentLayout(requireLayout, resolve, reject) { // require in the content layout to populate the require cache but don't render the item require([requireLayout], function /* ContentLayout */ () { // resolve the promise resolve(); }, function (err) { // note that can't find the layout and reject logger.warn('ContentClient.renderLayout: Unable to render the layout. Ensure you can access the layout: If running against published content, that the layout has been published. If draft, that you are logged onto the Sites server'); reject("Failed to get layout: " + requireLayout + " with error: " + err); }); }, renderContentLayout: function renderContentLayout(requireLayout, layoutParams, container, resolve, reject) { // require in the render.js for the layout require([requireLayout], function (ContentLayout) { var renderLayout = new ContentLayout(layoutParams); // call render to add the component to the page var renderPromise = renderLayout.render(container); if (typeof renderPromise === 'object' && typeof renderPromise.then === 'function') { renderPromise.then(function /* status */ () { // resolve the passed in Promise resolve(); }, function (errorStatus) { // failed to render, reject the passed in promise reject(errorStatus); }); } else { // simply resolve the passed in promise resolve(); } }, function (err) { // note that can't find the layout and reject logger.warn('ContentClient.renderLayout: Unable to render the layout. Ensure you can access the layout: If published, that the layout has been published. If draft, that you are logged onto the Sites server'); reject("failed to get layout: " + requireLayout + " with error: " + err); }); } }; // Node specific API var RestAPINode = /*#__PURE__*/function () { function RestAPINode(args) { Object.assign(this, args); } var _proto = RestAPINode.prototype; _proto.extractServer = function extractServer(contentServerURL) { var url = require('url'); var contentServer = contentServerURL || 'http://localhost'; var parsedURL = url.parse(contentServer); // extract the server part of the URL return parsedURL.protocol + "//" + parsedURL.hostname + (parsedURL.port ? ":" + parsedURL.port : ''); }; _proto.callRestServer = function callRestServer(targetURL, restArgs) { try { var _temp2 = function _temp2(token) { var nodePromise = new Promise(function (resolve, reject) { // parse the URL var options = url.parse(targetURL); var protocolCall = protocolCalls[options.protocol || 'https:']; var restRequest; /* jshint node: true */ var proxyType = options.protocol || 'https:'; var proxy = proxyType === 'https:' ? process.env.oce_https_proxy : process.env.oce_http_proxy; /* jshint node: false */ if (proxy) { try { logger.debug("Using proxy: " + proxy); // eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-extraneous-dependencies var HttpsProxyAgent = require('https-proxy-agent'); logger.debug('Loaded proxy agent'); var agent = new HttpsProxyAgent(proxy); logger.debug("Using proxy: " + proxy + " connecting to " + targetURL); options.agent = agent; } catch (e) { logger.warn("Could not initialize https-proxy-agent. Is the package installed in your application?.\n Making direct call to " + targetURL); } } // define function to call the consumer's "beforeSend" method if one was defined, // to add additional values to the request options var beforeSendOK = function beforeSendOK(currentOptions) { return new Promise(function (bsResolve, bsReject) { try { // if the consumer specified a "beforeSend" callback function then call it // if the result is a promise then resolve the promise if (typeof restArgs.beforeSend === 'function') { var beforeSendResult = restArgs.beforeSend(currentOptions); if (beforeSendResult && typeof beforeSendResult.then === 'function') { return beforeSendResult.then(function () { return bsResolve(beforeSendResult); })["catch"](function (e) { return bsReject({ // error in user code, reject the call status: e, statusText: 'Error in beforeSend() callback promise' }); }); } return bsResolve(beforeSendResult === undefined || beforeSendResult); } return bsResolve(true); } catch (e) { // error in user code, reject the call return bsReject({ status: e, statusText: 'Error in beforeSend() callback' }); } }); }; // function to handle request response into JSON var requestResponse = function requestResponse(response) { var chunksArray = []; var responseStatus = response.statusCode; response.on('data', function (chunk) { chunksArray.push(chunk); }); response.on('end', function () { // This is needed because the chunks may contain UTF-8 strings and // concatenating them into a string as we receive each chunk can // lead to corrupted characters. var body = "" + Buffer.concat(chunksArray); if (responseStatus >= 200 && responseStatus < 300) { try { var jsonResponse = JSON.parse(body); logger.debug(jsonResponse); resolve(jsonResponse); } catch (e) { reject({ error: body }); } } else { // return the error response object to be handled by calling function logger.debug("HTTP call failed. Response code " + responseStatus + "\n" + body); reject(response); } }); }; // store the call type in options options.method = restArgs.method.toUpperCase() || ''; options.headers = {}; if (token && token !== '') { options.headers.Authorization = token; } if (options.method === 'GET' && targetURL) { // handle 'GET' request // allow the user to update the "options" beforeSendOK(options).then(function (result) { if (result) { restRequest = protocolCall.get(options, requestResponse); } else { // aborted the call logger.debug('Call aborted by beforeSend'); reject({ error: 'call aborted by beforeSend()' }); } }); } else if (options.method === 'POST' && restArgs.noCSRFToken && restArgs.postData) { // handle 'POST' request // setup the JSON body var bodyString = JSON.stringify(restArgs.postData); options.headers['Content-Type'] = 'application/json'; options.headers['X-Requested-With'] = 'XMLHttpRequest'; options.headers['Content-Length'] = bodyString.length; // do http or https get writing the bodyString beforeSendOK(options).then(function (result) { if (result) { restRequest = protocolCall.request(options, requestResponse).write(bodyString); } else { // aborted the call logger.debug('Call aborted by beforeSend'); reject({ error: 'call aborted by beforeSend()' }); } }); } else { // unsupported method reject({ error: "unsupported REST request: " + JSON.stringify(restArgs) }); } // set up common handling if (restRequest) { // handle errors restRequest.on('error', function (error) { reject({ error: error }); }); // handle timeout restRequest.on('socket', function (socket) { socket.setTimeout(restArgs.timeout); socket.on('timeout', function () { reject({ error: "request timed out after: " + restArgs.timeout }); }); }); } else { logger.debug('no restRequest'); } }); // return the promise return Promise.resolve(nodePromise).then(function (response2) { return typeof self.coerceData === 'function' ? self.coerceData(response2) : Promise.resolve(response2); }); }; var _this = this; var self = _this; logger.debug("_rest.callRestServer: Calling " + restArgs.method + " request with:"); logger.debug(targetURL); logger.debug(restArgs); // require in the node REST call dependencies var protocolCalls = { 'http:': require('http'), 'https:': require('https') }; var url = require('url'); var _temp = restArgs.tokenManager !== null && restArgs.tokenManager !== undefined; return Promise.resolve(_temp ? Promise.resolve(restArgs.tokenManager.getAuthValue()).then(_temp2) : _temp2(restArgs.authorization)); } catch (e) { return Promise.reject(e); } }; return RestAPINode; }(); // Browser specific API var RestAPIBrowser = /*#__PURE__*/function () { function RestAPIBrowser(args) { Object.assign(this, args); } var _proto2 = RestAPIBrowser.prototype; _proto2.extractServer = function extractServer(contentServerURL) { // use the server URL if given, or default to the window URL var contentServer = contentServerURL || window.location && window.location.href; var parsedURL = document.createElement('a'); // parse the URL parsedURL.href = contentServer; // extract the server part of the URL return parsedURL.protocol + "//" + parsedURL.hostname + (parsedURL.port ? ":" + parsedURL.port : ''); }; _proto2.callRestServer = function callRestServer(targetURL, restArgs) { try { var _this2 = this; var self = _this2; logger.debug("_rest.callRestServer: Calling " + restArgs.method + " request with:"); logger.debug(restArgs); var xmlHTTPPromise = new Promise(function (resolve, reject) { // define function to call the consumer's "beforeSend" method if one was defined, // to add additional values to the request options var beforeSendOK = function beforeSendOK(currentXHR) { return new Promise(function (bsResolve, bsReject) { try { // if the consumer specified a "beforeSend" callback function then call it // if the result is a promise then resolve the promise if (typeof restArgs.beforeSend === 'function') { var beforeSendResult = restArgs.beforeSend(currentXHR); if (beforeSendResult && typeof beforeSendResult.then === 'function') { return beforeSendResult.then(function () { return bsResolve(beforeSendResult); })["catch"](function (e) { return bsReject({ // error in user code, reject the call status: e, statusText: 'Error in beforeSend() callback promise' }); }); } return bsResolve(beforeSendResult === undefined || beforeSendResult); } return bsResolve(true); } catch (e) { // error in user code, reject the call return bsReject({ status: e, statusText: 'Error in beforeSend() callback' }); } }); }; // create the XMLHttpRequest object and parameters var xhr = new XMLHttpRequest(); var xhrParams = { method: restArgs.method && restArgs.method.toUpperCase() || '', url: targetURL, timeout: restArgs.timeout, headers: {} }; var doRequest = true; // add authorization header, if provided if (restArgs.authorization) { // for /published API calls, only add header if not 'session' or // 'anonymous' (e.g. Basic Auth in non-POD environments) if (restArgs.contentType !== 'published' || ['session', 'anonymous'].indexOf(restArgs.authorization) === -1) { xhrParams.headers = { Authorization: restArgs.authorization }; } } // add the individual request parameters if (xhrParams.method === 'GET' && xhrParams.url) { // 'GET' request } else if (xhrParams.method === 'POST' && xhrParams.url && restArgs.noCSRFToken && restArgs.postData) { // 'POST' request xhrParams.headers['Content-Type'] = 'application/json; charset=UTF-8'; xhrParams.headers['X-Requested-With'] = 'XMLHttpRequest'; xhrParams.data = restArgs.postData; } else if (['POST', 'PUT'].indexOf(xhrParams.method) !== -1 && xhrParams.url && restArgs.postData) { // 'POST'/'PUT' request with X-CSRF-Token xhrParams.headers['Content-Type'] = 'application/json; charset=UTF-8'; xhrParams.headers['X-Requested-With'] = 'XMLHttpRequest'; xhrParams.headers['X-CSRF-Token'] = self.getCSRFToken(xhrParams.url); xhrParams.data = restArgs.postData; } else if (xhrParams.method === 'DELETE' && xhrParams.url) { // 'DELETE' request with X-CSRF-Token xhrParams.headers['X-CSRF-Token'] = self.getCSRFToken(xhrParams.url); } else { logger.error('_rest.callRestServer: invalid arguments:'); logger.error(restArgs); reject({ status: 400, statusText: "Expected to see arguments:\n { \"method\": \"GET/POST/PUT/DELETE\", \"url\": url } but received: " + JSON.stringify(restArgs) }); // note that no request to make doRequest = false; } // execute the request if (doRequest) { // handle the promise actions for the responses xhr.onload = function onload() { if (this.status >= 200 && this.status < 300) { resolve(JSON.parse(xhr.response ? xhr.response : xhr.responseText)); } else { reject({ status: this.status, statusText: xhr.statusText }); } }; xhr.onerror = function onerror() { reject({ status: this.status, statusText: xhr.statusText }); }; xhr.ontimeout = function ontimeout() { reject({ status: this.status, statusText: xhr.statusText }); }; xhr.open(xhrParams.method, xhrParams.url); // add in the headers for (var header in xhrParams.headers) { if (xhrParams.headers.hasOwnProperty(header)) { xhr.setRequestHeader(header, xhrParams.headers[header]); } } // VBCS adapts XMLHttpRequest to use fetch but doesn't support timeout. // This check silently ignores timeouts if they are not supported. var timeoutOverridden = Object.getOwnPropertyDescriptor(xhr, 'timeout'); if (timeoutOverridden === undefined || timeoutOverridden.writable) { xhr.timeout = xhrParams.timeout; // for IE, need to set timeout after open() } // handle the beforeSend() callback and then make the request beforeSendOK(xhr).then(function (result) { if (result) { if (xhrParams.data) { xhr.send(JSON.stringify(xhrParams.data)); } else { xhr.send(); } } }); } }); // return the promise return Promise.resolve(xmlHTTPPromise).then(function (response) { return typeof self.coerceData === 'function' ? self.coerceData(response) : Promise.resolve(response); }); } catch (e) { return Promise.reject(e); } }; return RestAPIBrowser; }(); // Content REST API handle '/content' prefix var ContentAPIConfiguration = { contextRoot: '/content', defaultVersion: 'v1', supportedVersions: [{ semanticVersion: '1.0.0', contentVersion: 'v1' }, { semanticVersion: '1.1.0', contentVersion: 'v1.1' }], state: { published: 'published', draft: 'management', preview: 'preview' } }; var ContentAPI = /*#__PURE__*/function () { function ContentAPI() { // Make these objects part of the interface of this class if (isNodeJS) { this.restAPI = new RestAPINode(ContentAPIConfiguration); } else { this.restAPI = new RestAPIBrowser(ContentAPIConfiguration); } } var _proto3 = ContentAPI.prototype; _proto3.extractServer = function extractServer(contentServerURL) { return this.restAPI.extractServer(contentServerURL); }; _proto3.callRestServer = function callRestServer(targetURL, restArgs) { try { var _this3 = this; return Promise.resolve(_this3.restAPI.callRestServer(targetURL, restArgs)); } catch (e) { return Promise.reject(e); } }; ContentAPI.getContentVersion = function getContentVersion(caller, requestedVersion) { // get semantic version var regEx = /\s*((([<>]?=?)\s*(v)?([0-9]+)(\.([0-9]+))?(\.([0-9]+))?))\s*/g; var parsedVersion = regEx.exec(requestedVersion || '0.0.0') || []; var semanticVersion = (parsedVersion[5] || '0') + "." + (parsedVersion[7] || '0') + "." + (parsedVersion[9] || '0'); // get the Supported Version based on the semantic version for (var i = 0; i < ContentAPIConfiguration.supportedVersions.length; i += 1) { if (ContentAPIConfiguration.supportedVersions[i].semanticVersion === semanticVersion) { return ContentAPIConfiguration.supportedVersions[i].contentVersion; } } // if we got to here, no version match // warn user that non-supported version requested logger.warn("Content SDK: \"" + caller + "\" has unrecognized Content Version: \"" + requestedVersion + "\".\n Defaulting to: version=\"" + this.defaultVersion + "\".\nTo avoid this message, use one of the supported versions when creating a content client: \n" + JSON.stringify(this.supportedVersions)); // return the default version return this.defaultVersion; }; _proto3.getCSRFToken = function getCSRFToken( /* requestURL */ ) { // Required for Management API return 'CSRFToken'; }; _proto3.createPrefix = function createPrefix(args) { // standard prefix is: "http://<server>:<port>/content/[management||publish]/api/[v1|v1.1]" return args.contentServer + ContentAPIConfiguration.contextRoot + "/" + ContentAPIConfiguration.state[args.contentType] + "/api/" + this.contentVersion; }; _proto3.createSuffix = function createSuffix(args) { // standard suffix is: // {search string}&[access-token|channelToken]={channelToken}&cb={cacheBuster} var search = args.search || ''; var channelToken = args.channelToken ? this.properties.tokenName + "=" + args.channelToken : ''; var cacheBusterValue = typeof args.cacheBuster === 'object' ? args.cacheBuster.contentKey : args.cacheBuster; var cacheBuster = cacheBusterValue ? "cb=" + cacheBusterValue : ''; var suffix = ''; // add in search suffix += search; // add in channelToken suffix += (suffix && channelToken ? '&' : '') + channelToken; // add in cacheBuster suffix += (suffix && cacheBuster ? '&' : '') + cacheBuster; return suffix; } // Format the fully qualified REST URL // path: section of the URL beyond the standard REST API // args: // contentServer: '<protocol>://<host>:<port>' of the content server // contentType: [management|published] // search: search string to add as query string // channelToken: 'channelToken=<channelToken>' to be added // cacheBuster: 'cb=<cacheBuster>' to be added ; _proto3.formatURL = function formatURL(path, restArgs) { var prefix = this.createPrefix(restArgs); var suffix = this.createSuffix(restArgs); var url = prefix + path + (suffix ? (path.indexOf('?') === -1 ? '?' : '&') + suffix : ''); logger.info(url); return url; }; _proto3.resolveGetTypesPath = function resolveGetTypesPath( /* args */ ) { return '/types'; } // args.typeName: restrict aggregate query to specific types ; _proto3.resolveGetTypePath = function resolveGetTypePath(args) { return "/types/" + args.typeName; }; _proto3.isDigitalAsset = function isDigitalAsset(id) { return /^DigitalAsset_/i.test(id) || id.length === 36 && (/^CONT/.test(id) || /^CORE/.test(id)); }; _proto3.getRenditionURL = function getRenditionURL(itemGUID, slug, renditionName, restArgs) { var url = ''; if (slug || itemGUID) { if (slug || this.isDigitalAsset(itemGUID)) { // Content URL var format = restArgs.format; var download = restArgs.download; var cacheBusterValue = typeof restArgs.cacheBuster === 'object' ? restArgs.cacheBuster.contentKey : restArgs.cacheBuster; var joinChar = '?'; // character to use to join query parameters // secure and non-secure assets now use the same path var digitalAssets = restArgs.secureContent ? this.properties.secureAssetURLName : this.properties.assetURLName; var rendition = renditionName || this.properties.digitalAssetDefault; var identifier = itemGUID || ".by.slug/" + slug; url = this.createPrefix(restArgs) + "/" + digitalAssets + "/" + identifier + "/" + rendition; // add in any query parameters if (cacheBusterValue) { url += joinChar + "cb=" + cacheBusterValue; joinChar = '&'; } if (format) { url += joinChar + "format=" + format; joinChar = '&'; } if (download) { url += joinChar + "download=true"; joinChar = '&'; } else if (download === false) { url += joinChar + "download=false"; joinChar = '&'; } if (restArgs.contentType === 'published' && restArgs.channelToken) { url += joinChar + this.properties.tokenName + "=" + restArgs.channelToken; joinChar = '&'; } } else { // Documents URL url = restArgs.contentServer + "/documents/file/" + itemGUID; } } logger.info(url); return url; }; _proto3.makeQueryParameters = function makeQueryParameters(args) { var queryParams = utils.extend({}, args); var searchParams = { postData: {}, getData: '', assetVersion: '' }; var parameters = ''; var search = queryParams.search; // remove Content SDK arguments and old properties we don't want to add as query parameters delete queryParams.ids; delete queryParams.IDs; delete queryParams.id; delete queryParams.ID; delete queryParams.itemGUID; delete queryParams.itemGUIDs; delete queryParams.slug; delete queryParams.timeout; delete queryParams.search; delete queryParams.types; delete queryParams.beforeSend; delete queryParams.contentType; delete queryParams.language; // define the string to separate each parameter on the URL var separator = ''; // construct the URL query string from the properties passed in for (var property in queryParams) { if (queryParams.hasOwnProperty(property)) { // if it's a valid URL property, include it if (property === encodeURI(property)) { var propVal = queryParams[property]; // convert the "orderBy" array property if required // CaaS only supports a single orderBy value, so just use the first item in the array if (property === 'orderBy' && Array.isArray(propVal) && propVal.length === 1) { var order = propVal[0].order && propVal[0].order.toLowerCase() || ''; var orderEntry = order ? ":" + (order === 'des' ? 'desc' : order) : ''; propVal = propVal[0].name + orderEntry; } if (property !== 'expand') { if (property === 'version') { if (propVal !== undefined && propVal) { searchParams.assetVersion = propVal; } } else if (typeof propVal !== 'object') { // we're only handling scalar parameters in GET requests parameters += separator + property + "=" + encodeURI(propVal); separator = '&'; } } } } } // add in any old style 'search' properties parameters += search ? separator + search : ''; // note that 'GET' call should be used and pass back the parameters searchParams.method = 'GET'; searchParams.getData = parameters; // note if should use aggregate call // aggregate calls should be used for "itemDepth" != 0 and "expand" parameters searchParams.useAggregate = queryParams.itemDepth || queryParams.expand; return searchParams; }; return ContentAPI; }(); // Content API v1: Inherit from base with v1 specific overrides var ContentApiV1Impl = /*#__PURE__*/function (_ContentAPI) { _inheritsLoose(ContentApiV1Impl, _ContentAPI); function ContentApiV1Impl() { var _this4; _this4 = _ContentAPI.call(this) || this; _this4.contentVersion = 'v1'; _this4.properties = { tokenName: 'access-token', digitalAssetDefault: 'default', assetURLName: 'digital-assets', secureAssetURLName: 'secure-digital-assets' }; return _this4; } var _proto4 = ContentApiV1Impl.prototype; _proto4.resolveGetItemListPath = function resolveGetItemListPath(args) { return "/items" + (args.useAggregate ? '/aggregate' : '') + (args.types ? "?field:type:equals=" + args.types : ''); }; _proto4.resolveGetItemPath = function resolveGetItemPath(args) { return "/items/" + args.itemGUID + (args.useAggregate ? '/aggregate' : ''); }; _proto4.resolveSearchPath = function resolveSearchPath( /* args */ ) { return '/items/queries'; }; _proto4.resolveGetBulkItemListPath = function resolveGetBulkItemListPath(args) { // args.itemGUIDs: array of IDs to add to the URL return "/items/bulk" + (args.useAggregate ? '/aggregate' : '') + "?ids=" + args.itemGUIDs.join(','); }; return ContentApiV1Impl; }(ContentAPI); // Content API v1.1: Inherit from v1 with v1.1 specific overrides var ContentApiV11Impl = /*#__PURE__*/function (_ContentApiV1Impl) { _inheritsLoose(ContentApiV11Impl, _ContentApiV1Impl); function ContentApiV11Impl(contentVersion) { var _this5; _this5 = _ContentApiV1Impl.call(this) || this; if (contentVersion) { _this5.requestedContentVersion = contentVersion; } _this5.contentVersion = 'v1.1'; _this5.properties = { tokenName: 'channelToken', digitalAssetDefault: 'native', assetURLName: 'assets', secureAssetURLName: 'assets' }; return _this5; } var _proto5 = ContentApiV11Impl.prototype; _proto5.resolveGetItemListPath = function resolveGetItemListPath(args) { var itemListURL = '/items'; var joinChar = '?'; // add in query if (args.types) { itemListURL += joinChar + "field:type:equals=" + args.types; joinChar = '&'; } // add in aggregate if (args.useAggregate) { itemListURL += joinChar + "expand=\"all\""; joinChar = '&'; } return itemListURL; }; _proto5.resolveGetItemPath = function resolveGetItemPath(args) { var language = args.language ? "/variations/language/" + args.language + "?fields=all" : ''; var nextParam = language ? '&' : '?'; var aggregate = args.useAggregate ? nextParam + "expand=" + args.useAggregate : ''; var slug = args.slug ? ".by.slug/" + args.slug : ''; // Ignored if language is given to not create an invalid URL var versionStr = ''; if (!language) { if (args.assetVersion) { versionStr = "/versions/" + args.assetVersion; } } if (args.itemGUID) { // Get Published Item by ID: // .../items/{id} // // Get Published Item by ID for specified language: // .../items/{id}/variations/language/{languageValue} return "/items/" + args.itemGUID + versionStr + language + aggregate; } // Get Published Item by slug: // .../items/.by.slug/{slug} // // Get published item by slug for specified language: // .../items/.by.slug/{slug}/variations/language/{languageValue} return "/items/" + slug + versionStr + language + aggregate; } // Get categories for a given taxonomy ; _proto5.resolveQueryTaxonomyCategoriesPath = function resolveQueryTaxonomyCategoriesPath(args) { return "/taxonomies/" + args.taxonomyGUID + "/categories"; } // get details about a given taxonomy category ; _proto5.resolveQueryTaxonomyCategoriesDetails = function resolveQueryTaxonomyCategoriesDetails(args) { return "/taxonomies/" + args.taxonomyGUID + "/categories/" + args.categoryGUID; }; _proto5.resolveGetTaxonomiesPath = function resolveGetTaxonomiesPath( /* args */ ) { return '/taxonomies'; }; _proto5.resolveGetRecommendationPath = function resolveGetRecommendationPath(args) { if (args.id) { return "/personalization/recommendationResults/.by.id/" + args.id; } return "/personalization/recommendationResults/" + args.apiName; }; _proto5.resolveSearchPath = function resolveSearchPath( /* args */ ) { return '/items'; }; _proto5.resolveGetBulkItemListPath = function resolveGetBulkItemListPath(args) { // args.itemGUIDs: array of IDs to add to the URL var idQuery = "(id eq \"" + args.itemGUIDs.join('" or id eq "') + "\")"; var languageQuery = args.language ? "(language eq \"" + args.language + "\")" : ''; return "/items?q=" + (languageQuery ? "(" + idQuery + " and " + languageQuery + ")" : idQuery); }; _proto5.coerceData = function coerceData(response) { var self = this; return new Promise(function (resolve /* , reject */) { // if the requested content version is v1, coerce data from v1.1 to v1 format if (self.requestedContentVersion === 'v1') { if (typeof response.fields === 'object') { // coerce single item if (!response.data) { response.data = response.fields; } } else if (Array.isArray(response.items)) { // coerce array of items response.items.forEach(function (item) { if (typeof item.fields === 'object' && !item.data) { // eslint-disable-next-line no-param-reassign item.data = item.fields; } }); } } // resolve with updated data return resolve(response); }); }; return ContentApiV11Impl; }(ContentApiV1Impl); // setup the REST API, content version is handled within the underlying REST call var restAPIFactory = { createRestAPI: function createRestAPI(contentVersion) { var validContentVersion = ContentAPI.getContentVersion('ContentSDK create content client', contentVersion); if (validContentVersion === 'v1') { // only support v1.1 now, so create a v1.1 API and set the requestd content version to v1 // we will coerce the data on fetch to be in the v1 format // ToDo: wait for deprecation and fix up tests that are expecting // 'v1' in the URL before making this change return new ContentApiV11Impl('v1'); } return new ContentApiV11Impl(); } }; var OptionsValidation = /*#__PURE__*/function () { function OptionsValidation() {} // A function to validate some of the options being passed into the SDK OptionsValidation.processOptions = function processOptions(options) { var validDeliveryOptions = [OptionsValidation.deliveryREST]; var validPreviewOptions = [OptionsValidation.previewREST, OptionsValidation.managementREST]; var defaultOptions = { deliveryClientAPI: OptionsValidation.deliveryREST, previewClientAPI: OptionsValidation.managementREST }; if (options && typeof options === 'object') { // Value unspecified. Use default if (!options.deliveryClientAPI) { // eslint-disable-next-line no-param-reassign options.deliveryClientAPI = defaultOptions.deliveryClientAPI; } else if (!validDeliveryOptions.includes(options.deliveryClientAPI)) { throw new Error("options.deliveryClientAPI is invalid. It may only be set to 'deliveryREST'"); } // Value unspecified. Use default if (!options.previewClientAPI) { // eslint-disable-next-line no-param-reassign options.previewClientAPI = OptionsValidation.previewREST; } else if (!validPreviewOptions.includes(options.previewClientAPI)) { throw new Error("options.previewClientAPI is invalid. It may only be set to 'previewREST' or 'managementREST'"); } return options; } return defaultOptions; }; return OptionsValidation; }(); // Supported configurations OptionsValidation.deliveryREST = 'deliveryREST'; OptionsValidation.previewREST = 'previewREST'; OptionsValidation.managementREST = 'managementREST'; /** * Copyright (c) 2017, 2022, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ */ /* eslint-disable import/named */ /* eslint-disable import/no-extraneous-dependencies */ /* * This file contains the TokenManager class, which contains methods used to create and maintain * authentication tokens to access to an instance of Oracle Content Management. * * The constructor of the class takes in OAUTHValues and a tokenString, which represent an object * containing the clientId, clientSecret, clientScopeUrl, and idpUrl used to create * access tokens and the direct access token, respectively. If both are provided, the * OAUTHValues take precedence. * * The AUTH environment variable is used to specify the Authentication header value * (including "Basic"/"Bearer") when the value does not change, corresponding to the tokenString * in the constructor. * * The AUTH_PARAMS environment variable is used to specify the Authentication object values * to get a new access token on expiry of the old one, corresponding to the OAUTHValues * in the constructor. * * A "delivery client" is used to view content which has been published to a public * channel or published to a secure channel. The "preview client" is used to view content * which has been assigned to a channel but has not yet been published. * * The minimal information which needs to be specified is the server URL, the rest API version * to use and the channel token for the channel which contains the data to display in the app. * * When previewing content or using content in a secure channel, authentication is required. * @ignore */ var TokenManager = /*#__PURE__*/function () { function TokenManager(oauthValues, tokenString) { if (oauthValues !== undefined && oauthValues !== null) { this.clientId = oauthValues.clientId; this.clientSecret = oauthValues.clientSecret; this.clientScopeUrl = oauthValues.clientScopeUrl; this.idpUrl = oauthValues.idpUrl; } else { this.clientId = null; this.clientSecret = null; this.clientScopeUrl = null; this.idpUrl = null; } this.expiry = -1; this.currentToken = tokenString; } /** * Gets the Bearer authorization needed when using preview content or * content published to a secure channel. * * This will create a NEW access_token with a new expiry * * This is only called when rendering on the server, therefore we are safe * to use node-fetch and do not have to have a client version */ var _proto = TokenManager.prototype; _proto.getBearerAuth = function getBearerAuth() { try { var _this = this; // base64 encode CLIENT_ID:CLIENT_SECRET var authString = _this.clientId + ":" + _this.clientSecret; var authValue = Buffer.from(authString).toString('base64'); // URL encode the CLIENT_SCOPE_URL var encodedScopeUrl = encodeURIComponent(_this.clientScopeUrl); // build the full REST end point URL for getting the access token var restURL = new URL('/oauth2/v1/token', _this.idpUrl); // make a request to the server to get the access token var body = "grant_type=client_credentials&scope=" + encodedScopeUrl; var options = { hostname: restURL.hostname, port: 443, path: restURL.pathname, method: 'POST', headers: { Authorization: "Basic " + authValue, 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': body.length } }; var isNodeJS = typeof window === 'undefined' && typeof process === 'object'; if (isNodeJS) { return Promise.resolve(new Promise(function (resolve, reject) { // eslint-disable-next-line global-require var https = require('https'); var req = https.request(options, function (res) { var returnData = ''; res.on('data', function (chunk) { returnData += chunk; }); res.on('end', function () { var responseJSON = JSON.parse(returnData); var accessToken = responseJSON.access_token; var expiry = responseJSON.expires_in; resolve({ authHeaderValue: "Bearer " + accessToken, expiry: expiry }); }); }); req.on('error', function (error) { reject(error); }); req.write(body); req.end(); })); } // Return an empty value if it is not running on a server return Promise.resolve({ authHeaderValue: '' }); } catch (e) { return Promise.reject(e); } } /** * Returns the auth value for any requests */ ; _proto.getAuthValue = function getAuthValue() { try { var _temp3 = function _temp3() { return _this2.currentToken; }; var _this2 = this; var _temp2 = function () { if (_this2.clientId !== null) { var _temp = function () { if (_this2.expiry - 5000 < Date.now()) { return Promise.resolve(_this2.getBearerAuth()).then(function (authDetails) { var globalAuthValue = authDetails.authHeaderValue; // Auth Expiry // calculate expiry, get the current date (in ms), add the expiry ms, then // create a new Date object, using the adjusted milliseconds time var currDateMS = Date.now(); currDateMS += authDetails.expiry; _this2.expiry = new Date(currDateMS); _this2.currentToken = globalAuthValue; }); } }(); if (_temp && _temp.then) return _temp.then(function () {}); } }(); // if no token exists, create one if OAUTH values given OR // if the auth token has expired, refresh it, otherwise existing value will be returned // add a 5 second buffer to the expiry time // This code only runs if it has the object with the OAUTH server details return Promise.resolve(_temp2 && _temp2.then ? _temp2.then(_temp3) : _temp3(_temp2)); } catch (e) { return Promise.reject(e); } }; return TokenManager; }(); // // ------------------------------- Content Client SDK ------------------------------------- // /** * Client object to interact with content published in Oracle Content Management: * <ul> * <li>Read the published content items</li> * <li>Render published content using named content layouts</li> * </ul> * @alias ContentDeliveryClient */ var ContentDeliveryClientImpl = /*#__PURE__*/function () { /** * @param {ClientParameters} args - A JavaScript object containing the parameters * to create the content delivery client instance. * @returns {ContentDeliveryClient} */ function ContentDeliveryClientImpl(args) { // create the restAPI based on the content version this.restAPI = restAPIFactory.createRestAPI(args.contentVersion); // update the logger entries logger.updateLogger(args.logger); // store the given properties this.info = { accessToken: args.channelToken || args.accessToken, channelToken: args.channelToken || args.accessToken, cacheBuster: args.cacheBuster, beforeSend: args.beforeSend, clientType: 'delivery', contentServer: this.restAPI.extractServer(args.contentServer), contentType: 'published', secureContent: args.secureContent || false, timeout: args.timeout || 0, contentVersion: this.restAPI.requestedContentVersion || this.restAPI.contentVersion, authorization: args.authorization, assetTransform: args.assetTransform, tokenManager: new TokenManager(args.authorizationParams, args.authorization) }; // store if running in compiler this.isCompiler = args.isCompiler; // note supported content types this.validContentTypes = ['published']; this.validLayoutTypes = this.validContentTypes; // define the external API this.publicSDK = { getInfo: utils.bind(this.getInfo, this), getItem: utils.bind(this.getItem, this), getItems: utils.bind(this.getItems, this), getAuthorizationHeaderValue: utils.bind(this.getAuthorizationHeaderValue, this), searchItems: utils.bind(this.queryItems, this), queryItems: utils.bind(this.queryItems, this), graphql: utils.bind(this.graphql, this), getRenditionURL: utils.bind(this.getRenditionURL, this), getLayoutInfo: utils.bind(this.getLayoutInfo, this), getRecommendationResults: utils.bind(this.getRecommendationResults, this), loadContentLayout: utils.bind(this.loadContentLayout, this), renderItem: utils.bind(this.renderItem, this), expandMacros: utils.bind(this.expandMacros, this), getTaxonomies: utils.bind(this.getTaxonomies, this), queryTaxonomyCategories: utils.bind(this.queryTaxonomyCategories, this), getTaxonomyCategory: utils.bind(this.getTaxonomyCategory, this) }; logger.debug('ContentClient.create: Content Info:'); logger.debug(this.info); } // common function for evaluating parameters to be used for the REST call var _proto = ContentDeliveryClientImpl.prototype; _proto.resolveRESTArgs = function resolveRESTArgs(method, args) { var searchParams = this.restAPI.makeQueryParameters(args); var restArgs = utils.extend({}, this.info); // start with the Client properties // add in the defaults restArgs.method = method; restArgs.contentType = this.getContentType(args.contentType); // add in authorization restArgs.authorization = this.getInfo().authorization; // add in the language locale restArgs.language = args.language; // override call specific properties restArgs.beforeSend = args.beforeSend || restArgs.beforeSend; restArgs.timeout = args.timeout || restArgs.timeout; // // ad