Last active
August 31, 2022 16:05
-
-
Save symbioquine/bd2b9762286089f10ff46e133dba9450 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { h } from 'vue'; | |
import { QBtn } from 'quasar'; | |
import { formatRFC3339 } from "assetlink-plugin-api"; | |
/** | |
* Defines an example plugin format that allows logs | |
* to be created based on based on a basic JSON file. | |
*/ | |
export default class BasicAssetLogPluginFormat { | |
static onLoad(handle, assetLink) { | |
handle.definePluginIngestor(pluginIngestor => { | |
pluginIngestor.onEveryPlugin(plugin => { | |
// Ignore everything except the file type we're prepared to handle | |
if (!plugin.pluginUrl.pathname.endsWith('alink.act.json')) { | |
return; | |
} | |
// Tell asset link that we're going to define things on behalf of the JSON file | |
// that we're ingesting | |
handle.onBehalfOf(plugin, attributedHandle => { | |
let pluginData = undefined; | |
try { | |
pluginData = JSON.parse(plugin.rawSource); | |
} catch (error) { | |
// Errors recorded this way will show up on the manage plugins page | |
// associated with the plugin we've specified | |
attributedHandle.recordError(error); | |
return; | |
} | |
// Ignore everything except the plugin format we're trying to implement | |
if (pluginData.AssetLinkPluginFormat !== "com.example.farmos_asset_link.plugin_format.v0.basic_asset_log") { | |
return; | |
} | |
// A better implementation would probably use something like JSONPath/JSONata/JMESPath | |
// but for the sake of this example, we'll implement a super simple expression language that | |
// only supports escaped dollar signs and a single "command" `$null` which can be used to | |
// match a missing/undefined value. | |
const validatedPredicatesPath = {}; | |
for (const [predicateKey, predicateExpr] of Object.entries(pluginData.appliesTo || {})) { | |
if (predicateExpr === '$null') { | |
validatedPredicatesPath[predicateKey] = v => v === null || v === undefined; | |
continue; | |
} | |
// Remove escaped dollar signs by splitting the string on them | |
const exprParts = predicateExpr.split('$$'); | |
// Check there aren't any other dollar signs | |
const firstInvalidExprPart = exprParts.find(exprPart => exprPart.indexOf('$') !== -1); | |
if (firstInvalidExprPart) { | |
attributedHandle.recordError(`Invalid expression command: '${firstInvalidExprPart}' ` | |
+ "Only escaped dollar signs '$$' or the '$null' command are currently supported."); | |
return; | |
} | |
const unescapedExpectedValue = exprParts.join('$'); | |
// TODO: Consider handling numeric values/comparisons | |
validatedPredicatesPath[predicateKey] = v => v === unescapedExpectedValue; | |
} | |
// Super simple value lookup (TODO: handle list indexes) | |
const valueLookup = (source, path) => { | |
const pathParts = path.split('.'); | |
let currentSource = source; | |
let value = undefined; | |
while(currentSource && pathParts.length > 0) { | |
const pathPart = pathParts.shift(); | |
currentSource = currentSource[pathPart]; | |
if (!pathParts.length) { | |
value = currentSource; | |
} | |
} | |
return value; | |
}; | |
attributedHandle.defineSlot(`com.example.farmos_asset_link.basic_asset_log.v0.${pluginData.id}`, action => { | |
action.type('asset-action'); | |
action.showIf(({ asset }) => { | |
for (const [predicateKey, predicate] of Object.entries(validatedPredicatesPath)) { | |
const value = valueLookup(asset, predicateKey); | |
if (!predicate(value)) { | |
return false; | |
} | |
} | |
return true; | |
}); | |
// This gets called when the user clicks the action button | |
const doActionWorkflow = async (asset) => { | |
let log = {}; | |
for (const [attrPath, attrValue] of Object.entries(pluginData.createLog)) { | |
const pathParts = attrPath.split('.'); | |
// Dereference the destination within the log object where the current | |
// attribute will be written (TODO: consider handling list indexes) | |
let dest = log; | |
while(pathParts.length > 1) { | |
const pathPart = pathParts.shift(); | |
if (!dest[pathPart]) { | |
dest[pathPart] = {}; | |
} | |
dest = dest[pathPart]; | |
} | |
const destKey = pathParts[0]; | |
// Replace handle-bars style patterns like `{{ asset.attributes.name }}` | |
// TODO: Consider using a real templating language... | |
dest[destKey] = attrValue.replace(/\{\{\s*([^\s]*)\s*\}\}/, (match, patternKey) => { | |
return valueLookup({ asset }, patternKey); | |
}); | |
} | |
log.timestamp = formatRFC3339(new Date()); | |
log.relationships = { | |
asset: { | |
data: [ | |
{ | |
type: asset.type, | |
id: asset.id, | |
} | |
] | |
}, | |
}; | |
assetLink.entitySource.update( | |
(t) => t.addRecord(log), | |
{label: log.attributes.name}); | |
}; | |
action.component(({ asset }) => | |
h(QBtn, { block: true, color: 'secondary', onClick: () => doActionWorkflow(asset), 'no-caps': true }, () => pluginData.name )); | |
}); | |
}); | |
}); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment