/*
* Copyright 2018-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict'
/**
* Utility:
* this module exports some utility functions.
* @module utility
*/
/**
* Get a reference to CloudEvent class definition.
* See {@link CloudEvent}.
* @private
*/
const CloudEvent = require('./cloudevent')
/**
* Get a reference to cloudevent class Validator.
* See {@link Validator}.
* @private
*/
const V = require('./validator')
/**
* Create and return a CloudEvent from the given object and options.
*
* @param {!object} obj the plain object instance to use (its properties will be used)
* @param {object} [options={}] optional attributes:
* - strict (boolean, default null so no override) to validate it in a more strict way (if null it will be used strict mode in the given event),
* - onlyValid (boolean, default false) to return it only if it's a valid instance,
* - printDebugInfo (boolean, default false) to print some debug info to the console,
* - skipExtensions (boolean, default false) to skip all non-standard (so extensions) properties,
* @throws {TypeError} if object is not a plain object instance
* @return {object} the created CloudEvent instance
* @throws {Error} if object is undefined or null, or an option is undefined/null/wrong, or is not possible to create a CloudEvent instance
*/
function createFromObject (obj = {}, {
strict = null,
onlyValid = false,
printDebugInfo = false,
skipExtensions = false
} = {}) {
if (!V.isObjectPlain(obj)) {
throw new TypeError('The given argument is not an object instance')
}
// extensions properties
let extensions = null
if (skipExtensions === false) { // get and use extension properties from the given object
extensions = CloudEvent.getExtensionsOfEvent(obj)
// note that using that method, extensions always contains at least the strict property,
// even when not set (so set as false), but do not remove from here
// to avoid problems when using this factory function
if (printDebugInfo === true) { // print some debug info
console.log(`DEBUG | extensions found: ${JSON.stringify(extensions)}`)
}
} else {
if (printDebugInfo === true) {
console.log('DEBUG | skip extensions')
}
}
// fill a new CludEvent instance with object data
const ce = new CloudEvent(obj.id,
obj.type,
obj.source,
obj.data,
{ // options
time: obj.time,
datainbase64: obj.data_base64,
datacontenttype: obj.datacontenttype,
dataschema: obj.dataschema,
subject: obj.subject,
strict: obj.strict
},
extensions
)
if (printDebugInfo === true) {
console.log(`DEBUG | cloudEvent details: ${JSON.stringify(ce)}`)
console.log(`DEBUG | ${CloudEvent.dumpValidationResults(ce, { strict }, 'ce')}`)
}
// return ce, depending on its validation option
if ((onlyValid === false) || (onlyValid === true && CloudEvent.isValidEvent(ce, { strict }) === true)) {
return ce
} else throw new Error('Unable to return a not valid CloudEvent.')
}
/**
* Clone the given CloudEvent in a plain object.
* Note that depending on the clone method chosen, a shallow clone could be done
* (usually it is, even for performance reasons),
* so pay attention using the skipExtensions option in that case.
*
* @static
* @param {!object} event the CloudEvent to clone
* @param {object} [options={}] optional attributes:
* - strict (boolean, default null so no override) to validate it in a more strict way (if null it will be used strict mode in the given event),
* - onlyValid (boolean, default false) to return it only if it's a valid instance,
* - printDebugInfo (boolean, default false) to print some debug info to the console,
* - skipExtensions (boolean, default false) to skip all non-standard (so extensions) properties,
* @return {object} the event, as a plain object
* @throws {TypeError} if event is not a CloudEvent instance or subclass
* @throws {Error} if event is undefined or null, or an option is undefined/null/wrong
*/
function cloneToObject (event = {}, {
strict = null,
onlyValid = false,
printDebugInfo = false,
skipExtensions = false
} = {}) {
if (!CloudEvent.isCloudEvent(event)) {
throw new TypeError(`The argument must be a CloudEvent (or a subclass), instead got a '${typeof event}'`)
}
let obj = null
if ((onlyValid === false) || (onlyValid === true && CloudEvent.isValidEvent(event, { strict, printDebugInfo }) === true)) {
// implement the clone via destructuring, but at the moment it's a shallow copy (like Object.assign())
// later check if support more ways, with a dedicated option (to comment in docs)
obj = { ...event }
} else throw new Error('Unable to clone a not valid CloudEvent.')
if (skipExtensions === true) { // delete extension properties from the cloned object
const extensions = CloudEvent.getExtensionsOfEvent(obj)
let count = 0
for (const [key] of Object.entries(extensions)) { // removed value from items because not used
delete obj[key]
count++
}
if (printDebugInfo === true) {
console.log(`DEBUG | filtered ${count} extensions`)
}
}
if (printDebugInfo === true) {
console.log(`DEBUG | cloned object: ${JSON.stringify(obj)}`)
}
return obj
}
module.exports = {
createFromObject,
cloneToObject
}