@omneo/cf
v1.2.0
Published
Omneo CF helps you develop cloud function plugins for Omneo
Downloads
10
Readme
Omneo Cloud Function Helpers
This is a small library to help boilerplate cloud functions for Omneo. It includes a bunch of handy helpers to help you get started and internal Express handlers for your routes and controllers.
Getting started
Install with yarn or npm
yarn add @omneo/cf
# OR
npm install @omneo/cf
Require @omneo/cf
then call the returned function with an object containing top level route path and controller. To make things simple, OmneoCF will register your controllers with the Express.js use()
call.
const app = require('@omneo/cf');
const profileController = (req, res) => {
res.status(200).json({
message: `You made a ${req.method} request to the profile controller`
})
}
const transactionController = () => {
const router = express.Router();
router.get('/', (req, res)=>{
res.status(200).json({
message: "Transactions"
})
})
router.post('/items', (req, res)=>{
res.status(200).json({
message: "Transaction items"
})
})
}
return app({
profiles: profileController,
transactions: transactionController()
})
Automatic environment handling
This library looks at NODE_ENV
and will either export just the routes or will start a local express server. The local server will run on NODE_PORT || 3000
This local server will also apply the same bodyParser
as GCP, to give you access to both res.body
and res.rawBody
- Useful when validating webhooks etc. It also uses dotenv()
to include your .env
file in process.env
.
Batteries-included middleware
The basics
Both local and production outputs include cors()
and compression()
Express libraries and a basic request logger. Logging can be disabled by setting the following environment variable: LOGGING=disabled
Error handling
All routes are wrapped in an error handler to ensure the service returns an error http response if possible. Note: You should still wrap your controllers in a try/catch
to ensure the best handling of this.
Tenant validation
All routes registered with the main function will sit under a /:tenant
route. This is used by the middleware/validateOmneoTenant
function to check for tenant configuration in process.env
. By default, only [tenant]_omneo_token
and [tenant]_omneo_secret
should be stored in env - All other configurations should be stored elsewhere, such as Omneo Tenant Fields or a DB.
This middleware also adds the omneo_token
and omneo_secret
to globals.tenant
, along with omneo_url
, matching the following format:
[tenant].getomneo.com
Omneo Plugin validation
All routes will be valiadated and make sure plugin has been configured in omneo. It's handled by middleware/validateOmneoPluginConfig
. Plugin handle needs to match the PluginNamespace and "Enabled"
field is required and needs to be true for successful validation. This requirement is valid for version >= 1.0.0.
Webhook validation
This library includes middleware for Omneo webhook validation at middleware/validateOmneoWebhook
This middleware accepts an array of allowed events and will automatically check against the headers Omneo uses for webhook HMAC signatures and event.
In addition, an abstracted middleware/validateWebhook
is availble for services that use similar methods for webhook validation:
validateWebhook(
events, // Array of allowed event strings eg. ['profile.created']
headerSignature, // Header key for the HMAC signature eg. 'x-omneo-hmac-sha256'
headerEvent, // Header key for the webhook event eg. 'x-omneo-event'
hmacSecret, // Secret key used to decode. For Omneo we use the OMNEO_SECRET
alg // HMAC algorithm to use when decoding
)
A handful of handy utilities
To help make your development life easier, this library includes a small set of useful utility functions.
Fetch
Our own wrapper for the popular node-fetch
library. This includes a number of helpers to construct your requsts, as well as response parsing, so you don't have to separately run response.json()
.
Note that config.global
is only available after the validateOmneoTenant
middleware has run, not at initialization. This means that any function relying on these will need to be initialized after that.
// omneo.js
const {Fetch} = require('@omneo/cf/utilities');
export.default = () => new Fetch(`https://api.${global.config.omneo_url}/api/v3`, {
headers: {
Authorization: `Bearer ${global.config.omneo_token}`
}
})
// route.js
const omneoClient = require('./omneo');
const route = async (req, res) => {
const omneo = omneoClient();
// Searching profiles. get(url, params)
const search = await omneo.get('/profiles', {
"filter[email]":"[email protected]"
})
// Creating a profile: post(url, body)
const create = await omneo.post('/profiles', {
first_name: "Gavin",
last_name: "Belson",
email: "[email protected]"
})
res.send()
}
Pluck
Dives through a js/json object, by key, to find the value. Returns the value if found, or null
where not available. Uses memoizee
to memoize the value for subsequent attempts.
const {pluck} = require('@omneo/cf/utilities');
const data = {
"some":{
"nested":{
"value":"Success value"
}
}
}
pluck(data, 'some.nested.value') // Returns "Success value"
pluck(data, 'some.other.value') // Returns null"
Pluck By
Iterates through an array of objects, to find matching key:value pair and return that object. Can take a 3rd argument to find the array in an object first, using pluck
;
const {pluckBy} = require('@omneo/cf/utilities');
const {
data: {
profile: {
"first_name":"Erlich",
"last_name":"Bachman",
"email":"[email protected]",
"attributes":{...},
"identities":[
{
"handle":"shopify",
"identifier":"SHOP12345"
},
{
"handle":"zendesk",
"identifier":"ZEN67890"
}
]
}
}
}
pluckBy(data.profile.identities, 'identifier', 'ZEN67890') // Returns {"handle":"zendesk","identifier":"ZEN67890"}
pluckBy(data.profile.identities, 'identifier', 'ZEN12345') // Returns null
pluckBy(profile, 'handle', 'shopify', 'profile.identities') // Returns {"handle":"shopify","identifier":"SHOP12345"}
ErrorHandler
Error constructor used to append additional details to the handleErrors
middleware.
Takes status
, message
and a json payload
const route = require('express').Router();
const {ErrorHandler} = require('@omneo/cf/utilities');
route.post('/', (req, res, next)=>{
const profile = req.body
try{
if(!profile.email){
throw new ErrorHandler(400, "Profile is incomplete", {
errors:{
email: "This field is required"
}
})
}
return profile
}catch(e){
next(e);
}
})
module.exports = route