UNPKG

@qikdev/sdk

Version:

Promise based Javascript SDK

968 lines (805 loc) 37.7 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>qik.content.js - Documentation</title> <script src="scripts/prettify/prettify.js"></script> <script src="scripts/prettify/lang-css.js"></script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc.css"> <script src="scripts/nav.js" defer></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <input type="checkbox" id="nav-trigger" class="nav-trigger" /> <label for="nav-trigger" class="navicon-button x"> <div class="navicon"></div> </label> <label for="nav-trigger" class="overlay"></label> <nav > <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="access.html">access</a><ul class='methods'><li data-type='method'><a href="access.html#.actionableScopes">actionableScopes</a></li><li data-type='method'><a href="access.html#.allPermissions">allPermissions</a></li><li data-type='method'><a href="access.html#.allPermissionTypes">allPermissionTypes</a></li><li data-type='method'><a href="access.html#.allUserScopes">allUserScopes</a></li><li data-type='method'><a href="access.html#.canCreate">canCreate</a></li><li data-type='method'><a href="access.html#.canDeleteItem">canDeleteItem</a></li><li data-type='method'><a href="access.html#.canEditItem">canEditItem</a></li><li data-type='method'><a href="access.html#.canEraseItem">canEraseItem</a></li><li data-type='method'><a href="access.html#.canKnowOf">canKnowOf</a></li><li data-type='method'><a href="access.html#.canListItem">canListItem</a></li><li data-type='method'><a href="access.html#.canRestoreItem">canRestoreItem</a></li><li data-type='method'><a href="access.html#.canViewItem">canViewItem</a></li><li data-type='method'><a href="access.html#.checkActionAccess">checkActionAccess</a></li><li data-type='method'><a href="access.html#.getAllDescendants">getAllDescendants</a></li><li data-type='method'><a href="access.html#.has">has</a></li><li data-type='method'><a href="access.html#.hasFeature">hasFeature</a></li><li data-type='method'><a href="access.html#.hashPermissions">hashPermissions</a></li><li data-type='method'><a href="access.html#.isAdministrator">isAdministrator</a></li><li data-type='method'><a href="access.html#.isAssignee">isAssignee</a></li><li data-type='method'><a href="access.html#.isOwner">isOwner</a></li></ul></li><li><a href="api.html">api</a><ul class='methods'><li data-type='method'><a href="api.html#.generateEndpointURL">generateEndpointURL</a></li></ul></li><li><a href="auth.html">auth</a><ul class='methods'><li data-type='method'><a href="auth.html#.changeOrganisation">changeOrganisation</a></li><li data-type='method'><a href="auth.html#.ensureValidToken">ensureValidToken</a></li><li data-type='method'><a href="auth.html#.getCurrentToken">getCurrentToken</a></li><li data-type='method'><a href="auth.html#.getCurrentUser">getCurrentUser</a></li><li data-type='method'><a href="auth.html#.impersonate">impersonate</a></li><li data-type='method'><a href="auth.html#.login">login</a></li><li data-type='method'><a href="auth.html#.logout">logout</a></li><li data-type='method'><a href="auth.html#.retrieveUserFromResetToken">retrieveUserFromResetToken</a></li><li data-type='method'><a href="auth.html#.sendResetPasswordRequest">sendResetPasswordRequest</a></li><li data-type='method'><a href="auth.html#.set">set</a></li><li data-type='method'><a href="auth.html#.sync">sync</a></li><li data-type='method'><a href="auth.html#.updateUserWithToken">updateUserWithToken</a></li></ul></li><li><a href="cache.html">cache</a><ul class='methods'><li data-type='method'><a href="cache.html#.get">get</a></li><li data-type='method'><a href="cache.html#.reset">reset</a></li></ul></li><li><a href="content.html">content</a><ul class='methods'><li data-type='method'><a href="content.html#.create">create</a></li><li data-type='method'><a href="content.html#.delete">delete</a></li><li data-type='method'><a href="content.html#.get">get</a></li><li data-type='method'><a href="content.html#.getFromSlug">getFromSlug</a></li><li data-type='method'><a href="content.html#.glossary">glossary</a></li><li data-type='method'><a href="content.html#.list">list</a></li><li data-type='method'><a href="content.html#.patch">patch</a></li><li data-type='method'><a href="content.html#.restore">restore</a></li><li data-type='method'><a href="content.html#.scopeGlossary">scopeGlossary</a></li><li data-type='method'><a href="content.html#.update">update</a></li><li data-type='method'><a href="content.html#.validateField">validateField</a></li><li data-type='method'><a href="content.html#.variables">variables</a></li></ul></li><li><a href="files.html">files</a><ul class='methods'><li data-type='method'><a href="files.html#.downloadUrl">downloadUrl</a></li><li data-type='method'><a href="files.html#.duration">duration</a></li><li data-type='method'><a href="files.html#.filesize">filesize</a></li><li data-type='method'><a href="files.html#.getBinaryTypeFromMime">getBinaryTypeFromMime</a></li><li data-type='method'><a href="files.html#.mediaUrl">mediaUrl</a></li></ul></li><li><a href="filter.html">filter</a><ul class='methods'><li data-type='method'><a href="filter.html#.activeFilterComparators">activeFilterComparators</a></li><li data-type='method'><a href="filter.html#.activeFilterKeys">activeFilterKeys</a></li><li data-type='method'><a href="filter.html#.activeFilterOperators">activeFilterOperators</a></li><li data-type='method'><a href="filter.html#.activeFilters">activeFilters</a></li><li data-type='method'><a href="filter.html#.activeFilterValues">activeFilterValues</a></li><li data-type='method'><a href="filter.html#.filterChangeString">filterChangeString</a></li><li data-type='method'><a href="filter.html#.isValidFilter">isValidFilter</a></li></ul></li><li><a href="geo.html">geo</a><ul class='methods'><li data-type='method'><a href="geo.html#.distance">distance</a></li><li data-type='method'><a href="geo.html#.getDefaultCountry">getDefaultCountry</a></li><li data-type='method'><a href="geo.html#.getDefaultTimezone">getDefaultTimezone</a></li><li data-type='method'><a href="geo.html#.getPosition">getPosition</a></li><li data-type='method'><a href="geo.html#.stopWatchPosition">stopWatchPosition</a></li><li data-type='method'><a href="geo.html#.watchPosition">watchPosition</a></li></ul></li><li><a href="module-QikStorage-CookieStorageAdapter.html">CookieStorageAdapter</a><ul class='methods'><li data-type='method'><a href="module-QikStorage-CookieStorageAdapter.html#getCookieOptions">getCookieOptions</a></li><li data-type='method'><a href="module-QikStorage-CookieStorageAdapter.html#isCookieMode">isCookieMode</a></li></ul></li><li><a href="module-QikStorage-LocalStorageAdapter.html">LocalStorageAdapter</a></li><li><a href="module-QikStorage-StorageAdapter.html">StorageAdapter</a><ul class='methods'><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#clearUser">clearUser</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#getAccessToken">getAccessToken</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#getRefreshToken">getRefreshToken</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#getTokenExpiry">getTokenExpiry</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#getUser">getUser</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#isAvailable">isAvailable</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#setUser">setUser</a></li></ul></li><li><a href="socket.html">socket</a><ul class='methods'><li data-type='method'><a href="socket.html#.channel">channel</a></li><li data-type='method'><a href="socket.html#.close">close</a></li><li data-type='method'><a href="socket.html#.connect">connect</a></li><li data-type='method'><a href="socket.html#.reconnect">reconnect</a></li><li data-type='method'><a href="socket.html#.subscribe">subscribe</a></li><li data-type='method'><a href="socket.html#.unsubscribe">unsubscribe</a></li></ul></li><li><a href="system.html">system</a><ul class='methods'><li data-type='method'><a href="system.html#.countries">countries</a></li></ul></li><li><a href="utils.html">utils</a><ul class='methods'><li data-type='method'><a href="utils.html#.cleanValue">cleanValue</a></li><li data-type='method'><a href="utils.html#.comma">comma</a></li><li data-type='method'><a href="utils.html#.errorMessage">errorMessage</a></li><li data-type='method'><a href="utils.html#.exists">exists</a></li><li data-type='method'><a href="utils.html#.extractFromArray">extractFromArray</a></li><li data-type='method'><a href="utils.html#.formatCurrency">formatCurrency</a></li><li data-type='method'><a href="utils.html#.getTypeFromID">getTypeFromID</a></li><li data-type='method'><a href="utils.html#.guid">guid</a></li><li data-type='method'><a href="utils.html#.hash">hash</a></li><li data-type='method'><a href="utils.html#.id">id</a></li><li data-type='method'><a href="utils.html#.ids">ids</a></li><li data-type='method'><a href="utils.html#.isValidEmailAddress">isValidEmailAddress</a></li><li data-type='method'><a href="utils.html#.loadExternalScript">loadExternalScript</a></li><li data-type='method'><a href="utils.html#.loadExternalStyle">loadExternalStyle</a></li><li data-type='method'><a href="utils.html#.machineName">machineName</a></li><li data-type='method'><a href="utils.html#.mapParameters">mapParameters</a></li><li data-type='method'><a href="utils.html#.matchInArray">matchInArray</a></li><li data-type='method'><a href="utils.html#.parseBoolean">parseBoolean</a></li><li data-type='method'><a href="utils.html#.parseDate">parseDate</a></li><li data-type='method'><a href="utils.html#.parseEmail">parseEmail</a></li><li data-type='method'><a href="utils.html#.parseInt">parseInt</a></li><li data-type='method'><a href="utils.html#.parseNumber">parseNumber</a></li><li data-type='method'><a href="utils.html#.parseURL">parseURL</a></li></ul></li></ul><h3>Modules</h3><ul><li><a href="module-QikStorage.html">QikStorage</a><ul class='methods'><li data-type='method'><a href="module-QikStorage.html#.createStorageAdapter">createStorageAdapter</a></li></ul></li></ul><h3>Global</h3><ul><li><a href="global.html#EventDispatcher">EventDispatcher</a></li></ul> </nav> <div id="main"> <h1 class="page-title">qik.content.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>/////////////////////////////////////////////////// /** * Creates a new QikContent instance. * This module provides a number of helper functions for creating and modifying content via the REST API * @alias content * @constructor * @hideconstructor * @param {QikCore} qik A reference to the parent instance of the QikCore module. This module is usually created by a QikCore instance that passes itself in as the first argument. */ export default function (qik) { if (!qik.api) { throw new Error(`Can't Instantiate QikContent before QikAPI exists`); } /////////////////////////////////////////////////// var service = { debug: false, loadingGlossary: false, }; /////////////////////////////////////////////////////////////////////////////// const variables = {}; let inflightVariablesRequest; /** * * Retrieves all global variables for the current user. This is often used when running custom code in an action. * @alias content.variables * @param {Array} keys Provide specific keys of variables you want to retrieve * @param {Object} options Additional options when making the request * @param {Boolean} options.reload Force variables to reload and not be cached. If false will retrieve any variables that are already known from the in memory cache. * @example * * const { OAUTH_CLIENT_ID, OAUTH_KEY } = await sdk.content.variables(); */ service.variables = async function (keys, options) { options = options || {}; keys = keys || []; var reload = options.refresh || options.reload || !variables.data; if (reload) { if (!inflightVariablesRequest) { inflightVariablesRequest = qik.api.post(`/variables`, { keys }); inflightVariablesRequest.then(resolveRequest, resolveRequest); function resolveRequest() { inflightVariablesRequest = null; } } const { data } = await inflightVariablesRequest; variables.data = data; qik.dispatch("variables", data); } return variables.data; }; /////////////////////////////////////////////////// const glossaryCache = qik.cache.get("user:glossary"); let inflightGlossaryRequest; /** * Yield to the event loop to keep the UI responsive * @private */ function yieldToMain() { return new Promise((resolve) => { if (typeof requestIdleCallback === "function") { requestIdleCallback(resolve, { timeout: 50 }); } else { setTimeout(resolve, 0); } }); } /** * Inflate compressed glossary data from the /glossary/compressed endpoint * Processes in chunks to avoid blocking the main thread * @private */ async function inflateGlossary({ _s: strings, _k: keyMap, types }) { function inflate(val) { if (typeof val === "string" &amp;&amp; val[0] === "~") { return strings[parseInt(val.slice(1))]; } if (Array.isArray(val)) return val.map(inflate); if (val &amp;&amp; typeof val === "object") { const result = {}; for (const [k, v] of Object.entries(val)) { result[keyMap[k] || k] = inflate(v); } return result; } return val; } const CHUNK_SIZE = 50; const results = []; for (let i = 0; i &lt; types.length; i += CHUNK_SIZE) { const chunk = types.slice(i, i + CHUNK_SIZE); results.push(...chunk.map(inflate)); // Yield to main thread between chunks if (i + CHUNK_SIZE &lt; types.length) { await yieldToMain(); } } return results; } /** * * Retrieves the glossary of all content types visible to the requesting user. By default this will include all fields, validation, expressions and other configuration * @alias content.glossary * @param {Object} options Additional options * @param {Boolean} options.hash Whether to return the data as a keyed object allowing for fast selection of specific content types, by default will return as an array * @param {Boolean} options.reload Force glossary to reload and not be cached. If false will retrieve content type data from the in memory cache * @param {Boolean} options.uncompressed Use the uncompressed endpoint * @example * * const { article, profile } = await sdk.content.glossary({hash:true}); * // Use compressed endpoint for faster loading * const glossary = await sdk.content.glossary({compressed:true}); */ service.glossary = async function (options) { options = options || {}; // Check if we already have the glossary cached let cached = glossaryCache.get("glossary"); const uncompressed = !!options.uncompressed; let compressed = !uncompressed; var reload = options.refresh || options.reload || !cached; // If we are forcing a reload if (reload) { if (!inflightGlossaryRequest) { service.loadingGlossary = true; // Use compressed endpoint if requested const endpoint = compressed ? `/glossary/compressed` : `/glossary`; inflightGlossaryRequest = qik.api.get(endpoint, { cache: false }); inflightGlossaryRequest.then(resolveRequest, resolveRequest); function resolveRequest() { inflightGlossaryRequest = null; service.loadingGlossary = false; } } // Now we have the glossary const { data } = await inflightGlossaryRequest; // Inflate if compressed format (async to avoid blocking UI) const glossaryData = data._s &amp;&amp; data._k &amp;&amp; data.types ? await inflateGlossary(data) : data; // Cache it for later glossaryCache.set("glossary", glossaryData); cached = glossaryData; qik.dispatch("glossary", glossaryData); } if (options.hash) { var result = qik.utils.hash(cached, "key"); return result; } if (options.hex) { var result = qik.utils.hash(cached, "hex"); return result; } return cached; }; /////////////////////////////////////////////////// const scopeGlossary = {}; let inflightScopeGlossaryRequest; /** * * Retrieves the scope glossary of all scopes the user can know about. This helps to convert a scope id into a human readable title. * @alias content.scopeGlossary * @param {Object} options Additional options * @param {Boolean} options.hash Whether to return the data as a keyed object with each scopes _id as the key allowing for fast selection of specific scopes, by default will return a structured tree * @param {Boolean} options.reload Force the glossary to reload and not be cached. If false will retrieve content type data from the in memory cache * @example * * const scopes = await sdk.content.scopeGlossary(); */ service.scopeGlossary = async function (options) { options = options || {}; var reload = options.refresh || options.reload || !scopeGlossary.data; if (reload) { if (!inflightScopeGlossaryRequest) { inflightScopeGlossaryRequest = qik.api.get(`/scope/glossary`, { cache: false, }); inflightScopeGlossaryRequest.then(resolveRequest, resolveRequest); function resolveRequest() { inflightScopeGlossaryRequest = null; } } const { data } = await inflightScopeGlossaryRequest; scopeGlossary.data = data; } if (options.hash) { var result = qik.utils.hash(scopeGlossary.data, "_id"); return result; } return scopeGlossary.data; }; /////////////////////////////////////////////////// /** * * Retrieves all available filter comparators for each data type * @alias content.comparators * @param {Object} options Additional options * @param {Boolean} options.reload Ignore any locally cached data * @example * const {hash, available, types} = await sdk.content.comparators(); * console.log(available) // {boolean:[{title:'Is equal to', operator:'equal'...}]} * console.log(hash) // {equal:[{title:'Is equal to', operator:'equal'...}]} */ const comparators = {}; service.comparators = async function (options) { options = options || {}; var reload = options.reload || options.refresh || !comparators.data; if (reload) { const { data } = await qik.api.get(`/system/comparators`); ///////////////////////////// data.available = {}; Object.entries(data.types).forEach(([key, value]) => { data.available[key] = value.map(function (comparatorKey) { return data.hash[comparatorKey]; }); }); ///////////////////////////// comparators.data = data; } // if (options.hash) { // var result = qik.utils.hash(comparators.data, 'key'); // return result; // } return comparators.data; }; /////////////////////////////////////////////////// function getLimits(fieldDefinition) { var { minimum, maximum } = fieldDefinition; minimum = qik.utils.parseInt(minimum); minimum = Math.max(minimum, 0); maximum = qik.utils.parseInt(maximum); maximum = Math.max(maximum, 0); // cant be less than 0 if (maximum == 0) { //Unlimited maximum } else { maximum = Math.max(maximum, minimum); // cant be less than the minimum } return { minimum, maximum }; } service.meetsValidationRequirements = function ( input, fieldType, validationCriteria, ) { if (validationCriteria.minLength) { var stringLength = String(input).length; if (stringLength &lt; validationCriteria.minLength) { return `Must be at least ${validationCriteria.minLength} characters`; } } if (validationCriteria.maxLength) { var stringLength = String(input).length; if (stringLength > validationCriteria.maxLength) { return `Can be no more than ${validationCriteria.maxLength} characters`; } } if (validationCriteria.minValue) { if (input !== undefined) { var number = Number(input); if (number &lt; validationCriteria.minValue) { return `Must be at least ${validationCriteria.minValue}`; } } } if (validationCriteria.maxValue) { if (input !== undefined) { var number = Number(input); if (number > validationCriteria.maxValue) { return `Can be no greater than ${validationCriteria.maxValue}`; } } } }; /** * Checks if a certain input validates against a field definition * @alias content.validateField * @param {Any} input The input to validate * @param {Object} fieldDefinition The field to validate against * @param {Object} options Additional options when calling the function * @example * const validationResult = await sdk.content.validateField('Johnny Bobbins', {title:'Name', key:'firstName', type:'string', minimum:1, maximum:1, ...}); * console.log(validationResult) * // Results in { valid:true } * * const validationResult = await sdk.content.validateField('Johnny Bobbins', {title:'Number', key:'number', type:'integer', minimum:1, maximum:1, ...}); * console.log(validationResult) * // Results in { valid:false, status:400, message:'Invalid number input for field' } */ service.validateField = function (input, fieldDefinition, options) { options = options || {}; var fieldType = fieldDefinition.type || "string"; var { minimum, maximum } = getLimits(fieldDefinition); var isObject = fieldDefinition.type == "group" &amp;&amp; fieldDefinition.asObject; var singleValue = isObject ? minimum === maximum &amp;&amp; maximum === 1 : maximum === 1; var multiValue = !singleValue; ////////////////// var isNumeric; switch (fieldType) { case "integer": case "decimal": case "number": case "float": isNumeric = true; break; } ////////////////// var inputWasProvided = qik.utils.exists(input); // A required boolean field means "must be true" — false counts as missing. if (fieldDefinition.type === "boolean" &amp;&amp; input !== true) { inputWasProvided = false; } //If an answer is required if (minimum) { //but none was provided if (!inputWasProvided) { //Throw an error return { valid: false, message: `${fieldDefinition.title} is a required field`, status: 400, }; } } else { //No answer is needed and none was provided if (!inputWasProvided) { return { valid: true, }; } } ////////////////// //If we are requiring multiple values if (multiValue) { //But the input is not an array if (!Array.isArray(input)) { if (minimum) { return { valid: false, message: `${fieldDefinition.title} requires at least ${minimum} values`, status: 400, }; } else { console.log("NOT PROVIDED AS ARRAY", fieldDefinition.title, input); return { valid: false, message: `${fieldDefinition.title} must be provided as an array`, status: 400, }; } } //////////////////////////// var compacted = input; compacted = compacted.filter(function (v) { //Check if we care about this value var empty = v === undefined || v === null || v === ""; return !empty; }); // console.log('COMPACTED', input.length, compacted.length); //////////////////////////// //We need an exact number of answers if (minimum == maximum) { //But we don't have the number of answers needed if (minimum &amp;&amp; compacted.length != minimum) { return { valid: false, message: `${fieldDefinition.title} requires exactly ${maximum} values`, status: 400, }; } } //We have too many answers if (maximum &amp;&amp; compacted.length > maximum) { return { valid: false, message: `${fieldDefinition.title} requires less than ${maximum + 1} values`, status: 400, }; } //We don't have enough answers if (compacted.length &lt; minimum) { // console.log(fieldDefinition.title, 'INPUT', input.length, minimum, input, compacted) return { valid: false, message: `${fieldDefinition.title} requires at least ${minimum} values`, status: 400, }; } //////////////////////////// var foundBadEntry; //Find any bad values var badEntry = compacted.find(function (val) { var valueFieldType = fieldType; if (fieldDefinition.type === "group") { valueFieldType = "object"; } if ( fieldDefinition.type === "reference" &amp;&amp; fieldDefinition.widget === "form" ) { valueFieldType = "object"; } var isValid = qik.utils.isValidValue( val, valueFieldType, options.strict, ); //////////////////////////// if (!isValid) { foundBadEntry = true; return true; } //////////////////////////// //Is there additional validation requirements if (fieldDefinition.validation) { var additionalValidationErrors = service.meetsValidationRequirements( val, fieldType, fieldDefinition.validation, ); if (additionalValidationErrors) { foundBadEntry = true; return true; } } }); if (foundBadEntry) { let badValueMessage = `Invalid input for ${fieldDefinition.title}`; return { valid: false, message: badValueMessage, status: 400, }; } } else { var dataType = fieldType; var widgetType = fieldDefinition.widget; if (dataType === "group") { dataType = "object"; } if (dataType === "reference" &amp;&amp; widgetType === "form") { dataType = "object"; } ////////////////// var cleanedValue = service.getCleanedValue(input, dataType, options); ////////////////// var isValidValue = qik.utils.isValidValue( cleanedValue, dataType, options.strict, ); ////////////////// //Invalid input if (!isValidValue) { return { valid: false, message: `Single value '${input}' is not a valid ${dataType} for ${fieldDefinition.title}`, criteria: { isValidValue, cleanedValue, fieldType, options, }, status: 400, }; } //Is there additional validation requirements let hasAdditionalValidation = false; const validation = {}; if (String(fieldDefinition.minValue)) { validation.minValue = parseInt(fieldDefinition.minValue); hasAdditionalValidation = true; } if (String(fieldDefinition.maxValue)) { validation.maxValue = parseInt(fieldDefinition.maxValue); hasAdditionalValidation = true; } if (String(fieldDefinition.minLength)) { validation.minLength = parseInt(fieldDefinition.minLength); hasAdditionalValidation = true; } if (String(fieldDefinition.maxLength)) { validation.maxLength = parseInt(fieldDefinition.maxLength); hasAdditionalValidation = true; } if (hasAdditionalValidation) { const validationCriteria = Object.assign( {}, validation, fieldDefinition.validation || {}, ); var additionalValidationError = service.meetsValidationRequirements( input, fieldType, validationCriteria, ); if (additionalValidationError) { return { valid: false, message: additionalValidationError, status: 400, }; } } } ////////////////// return { valid: true, }; }; service.getCleanedValue = function (input, dataType, options) { switch (dataType) { case "number": case "float": case "decimal": if (!qik.utils.exists(input)) { return undefined; } else { return Number(input); } break; case "integer": if (!qik.utils.exists(input)) { return undefined; } else { return parseInt(input); } break; case "boolean": if (!qik.utils.exists(input)) { return undefined; } return qik.utils.parseBoolean(input); break; case "email": return options.strict ? input : String(input).toLowerCase(); break; case "reference": return options.strict ? input : qik.utils.id(input); break; default: return options.strict ? input : qik.utils.cleanValue(input, dataType, options); break; } return input; }; /////////////////////////////////////////////////// /** * * Retrieves a list of records matching the provided criteria * @alias content.list * @param {String} type The type or definition of records we want to retrieve * @param {Object} options The options for our query * @param {String} options.search Freeform text keywords * @param {Object} options.sort How to sort the results * @param {String} options.sort.key Which key to sort on * @param {String} options.sort.direction Which direction to sort on * @param {Object} options.sort.type What type of data is being sorted * @param {Object} options.page Page configuration * @param {Number} options.page.size Page size * @param {Number} options.page.index Page index * @param {Object} options.filter How to filter the results * @example * * * sdk.content.list('profile', { * search:'Jim', * page:{ * size:50, * index:2, * }, * sort:{ * key:'age', * direction:'asc', * type:'integer', * }, * filter:{ * operator:'and', * filters:[{ * key:'age', * comparator:'>', * value:5, * }], * }, * }) */ service.list = async function (type, options, advanced) { if (!advanced) { advanced = {}; } const remoteURL = advanced.remoteURL || `/content/${type}/list`; if (advanced.cancellable) { if (!advanced.config) { advanced.config = {}; } //Create a cancel token const CancelToken = qik.api.CancelToken; const source = CancelToken.source(); advanced.config.cancelToken = source.token; const promise = qik.api.post(remoteURL, options, advanced.config); return { promise, cancel(message) { source.cancel(message || "Operation canceled by the user."); }, }; } else { const { data } = await qik.api.post(remoteURL, options, advanced.config); return data; } }; /////////////////////////////////////////////////// /** * * Create an item * @alias content.create * @param {String} type The type or definition of the record we want to create * @param {Object} input The data for our new record * @example * * * const result = await sdk.content.create('profile', { * firstName:'Mickey', * lastName:'Mouse', * gender:'male', * meta:{ * scopes:['61eca4746971e75c1fc670cf'], * } * }) */ service.create = async function (type, input) { const dataModel = { ...input }; // Sanitize down to just ids before we send if (dataModel.meta?.scopes) { dataModel.meta.scopes = qik.utils.ids(dataModel.meta.scopes); } const { data } = await qik.api.post(`/content/${type}/create`, dataModel); return data; }; /** * * Update an item, Only fields the user has permission to view will be returned * @alias content.update * @param {String} id The id of the record we want to update * @param {Object} input The data to update * @example * * const result = await sdk.content.update('61eca4746971e75c1fc670cd', { * firstName:'Minnie', * lastName:'Mouse', * gender:'female', * meta:{ * scopes:['61eca4746971e75c1fc670cd'], * } * }) */ service.update = async function (id, input) { id = qik.utils.id(id); const dataModel = { ...input }; // Sanitize down to just ids before we send if (dataModel.meta?.scopes) { dataModel.meta.scopes = qik.utils.ids(dataModel.meta.scopes); } const { data } = await qik.api.put(`/content/${id}`, dataModel); return data; }; /** * * Partially update and patch an item, Only fields the user has permission to edit will be updated * @alias content.patch * @param {String} id The id of the record we want to update * @param {Object} input The data to update, this will be merged with existing data * @example * * const result = await sdk.content.patch('61eca4746971e75c1fc670cd', { * firstName:'Mickey', * gender:'male', * }) */ service.patch = async function (id, input) { id = qik.utils.id(id); const dataModel = { ...input }; // Sanitize down to just ids before we send if (dataModel.meta?.scopes) { dataModel.meta.scopes = qik.utils.ids(dataModel.meta.scopes); } const { data } = await qik.api.patch(`/content/${id}`, dataModel); return data; }; /** * * Get an item from the database, Only fields the user has permission to view will be returned * @alias content.get * @param {String} id The id of the record we want to update * @example * * const result = await sdk.content.get('61eca4746971e75c1fc670cd') */ service.get = async function (id, params) { id = qik.utils.id(id); params = params || {}; const { data } = await qik.api.get(`/content/${id}`, params); return data; }; service.getFromID = async function (id) { return service.get(id); }; /** * * Delete an item from the database * @alias content.delete * @param {String} id The id of the record we want to delete * @example * const result = await sdk.content.delete('61eca4746971e75c1fc670cd') */ service.delete = async function (id, input) { id = qik.utils.id(id); const { data } = await qik.api.delete(`/content/${id}`, input); return data; }; /** * * Restore a deleted item from the database * @alias content.restore * @param {String} id The id of the record we want to restore * @example * const result = await sdk.content.restore('61eca4746971e75c1fc670cd') */ service.restore = async function (id, input) { id = qik.utils.id(id); const { data } = await qik.api.get(`/content/${id}/restore`, input); return data; }; /** * * Retrieve an item from the database by providing it's 'slug' * @alias content.getFromSlug * @param {String} slug The slug id of the record we want to retrieve * it must be provided as either `(type):(slug)`` or `(definition):(slug)` * (if unsure use the `meta.abs` absolute slug property of the item you are wanting to retrieve). * @example * const result = await sdk.content.getFromSlug('article:how-to-get-started') * const result = await sdk.content.getFromSlug('car:toyota-landcruiser') * const result = await sdk.content.getFromSlug('article:toyota-landcruiser') */ service.getFromSlug = async function (slug) { const { data } = await qik.api.get(`/content/slug/${slug}`); return data; }; // service.getFromSlug = async function(type, slug) { // const { data } = await qik.api.get(`/content/${type}/slug/${slug}`); // return data; // } // service.getFromExternalID = async function(type, externalID) { // const { data } = await qik.api.get(`/content/${type}/external/${externalID}`); // return data; // } return service; } </code></pre> </article> </section> </div> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.11</a> on Mon Jul 13 2026 18:49:48 GMT+1000 (Australian Eastern Standard Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme. </footer> <script>prettyPrint();</script> <script src="scripts/polyfill.js"></script> <script src="scripts/linenumber.js"></script> </body> </html>