Skip to main content
Glama
template.ts9.27 kB
// deno-lint-ignore-file no-explicit-any import _ from "npm:lodash"; export default { variables, updateVarsInViews, converge, checkUniqueNamePrefix, getComponentName, sourceOrValue, }; function sourceOrValue(path: string, thisComponent: any) { if (thisComponent.sources[path]) { return { "$source": thisComponent.sources[path] }; } else { const lodashPathArray = path.split("/"); lodashPathArray.shift(); const value = _.get(thisComponent.properties, lodashPathArray); if (value) { return value; } else { throw new Error(`You must provide a value or a subscription for "${path}" in the template components attributes`); } } } interface ComponentPartialName { properties: { si: { name: string, } } } export function checkUniqueNamePrefix(namePrefix: string, components: Record<string, ComponentPartialName>): boolean { for (const component of Object.values(components)) { const componentName = component.properties.si.name; if (componentName && componentName.startsWith(namePrefix)) { throw new Error(`Found a component named '${componentName}' that starts with the prefix '${namePrefix}'. Change the 'Name Prefix' so they do not overlap and try again.`); } } return true; } type ComponentWithSiNameAttributeValue = string | { '$source': any } | boolean | number; interface ComponentScaffold { attributes: { [path: string]: ComponentWithSiNameAttributeValue; } } export function getComponentName(component: ComponentScaffold): string { const siName = component.attributes["/si/name"]; if (_.isString(siName)) { return siName as string; } else if (_.isBoolean(siName)) { return `${siName}`; } else if (_.isNumber(siName)) { return `${siName}`; } else { throw new Error(`the /si/name of the component is not a string; received value:\n\n${JSON.stringify(component, null, 2)}`); } } export function updateVarsInViews( mgmtComponentKind: string, currentView: any, thisComponent: any, components: any, ): any { const templateObjectName = thisComponent.properties.si.name; const synced: string[] = []; const views: Record<string, any[]> = { create: [], }; const update: Record<string, any> = {}; const create: Record<string, any> = {}; const templateVarsToSync = _.merge( _.get(thisComponent, "properties.domain.Template.Default", {}), _.get(thisComponent, "properties.domain.Template.Override", {}), ); console.log("template vars to sync", { templateVarsToSync, }); for (const view of _.get(thisComponent, "properties.domain.Views", [])) { if (!view.Sync) { continue; } synced.push(view.Name); const viewName = `${templateObjectName} ${view.Name}`; views.create.push(viewName); let exists = false; // foo for ( const [componentId, componentObject] of Object.entries( components as object, ) ) { if (componentObject.properties.si.name.startsWith(viewName)) { exists = true; update[componentId] = { properties: { domain: { Template: { Default: templateVarsToSync, }, }, }, geometry: componentObject.geometry, }; } } if (!exists) { const viewName = `${templateObjectName} ${view.Name}`; const geometry: Record<string, any> = {}; geometry[viewName] = thisComponent.geometry[currentView]; create[viewName] = { kind: mgmtComponentKind, properties: { si: { name: viewName, }, domain: { Template: { Default: templateVarsToSync, }, }, }, geometry, }; } } // something else const ops: Record<string, any> = {}; const result: Record<string, any> = { status: "ok", message: `Updated Values for Views: ${synced.join(", ")}`, }; if (Object.keys(update).length) { ops.update = update; } if (Object.keys(create).length) { ops.create = create; } if (views.create.length) { ops.views = views; } result.ops = ops; console.log("results", { result, }); return result; } export function converge( _currentView: string, thisComponent: unknown, components: Record<string, unknown>, specs: { properties: Record<string, any>; connect: Record<string, any>; geometry: Record<string, any>; }[], updateFuncName?: string, ): any { const templateObjectName = _.get( thisComponent, ["properties", "si", "name"], "unknown", ); const status = "ok"; const message = "Updated Components"; const ops: Record<string, unknown> = {}; const update: Record<string, unknown> = {}; const create: Record<string, unknown> = {}; const deletes: Array<unknown> = []; const actions: Record<string, { add: string[] }> = {}; // that gets created, or deleted, by a management function. It needs // to be stored as metadata that then gets sent in to the // function as an optional argument. That will allow us to let // names change without things going very wrong // For now, the idempotency key is the name. Bewware! // Iterate over all the connected components, and check for any that // are no longer in our specification, or that represent environment // objects, and schedule them for deletion. for (const component of Object.values(components)) { const idempotencyKey = _.get(component, "properties.si.name"); const hasSpec = _.find(specs, { properties: { si: { name: idempotencyKey, }, }, }); if (hasSpec) { continue; } else { let isEnvironmentComponent = false; for ( const environment of _.get( thisComponent, "properties.domain.Views", [], ) ) { const viewName = `${templateObjectName} ${environment.Name}`; if (viewName === idempotencyKey) { isEnvironmentComponent = true; } } if (isEnvironmentComponent) { continue; } } deletes.push(idempotencyKey); } // Itereate over all desired specs, and check to see if we need to create them // or update them. for (const desired of specs) { const idempotencyKey = _.get(desired, "properties.si.name"); let currentId = ""; let current = {}; for (const [id, ob] of Object.entries(components)) { const match = _.isMatch(ob, { properties: { si: { name: idempotencyKey, }, }, }); if (match) { currentId = id; current = ob as object; } } if (currentId) { const updateOp: Record<string, any> = {}; // Compute the update operation const currentProperties = _.get(current, "properties", {}); if (!_.isMatch(currentProperties, desired.properties)) { updateOp.properties = desired.properties; } // All connections will be overriden const currentConnections = _.get(current, "connect", []); if (!_.isEqual(currentConnections, desired.connect)) { const toRemove = _.difference(currentConnections, desired.connect); const toAdd = _.difference(desired.connect, currentConnections); if (toAdd.length) _.set(updateOp, ["connect", "add"], toAdd); if (toRemove.length) _.set(updateOp, ["connect", "remove"], toRemove); } // Never update Geometry - right now, we switch from absolute on create // to "relative" on update. For now, you have to manually position // updates. //const currentGeometry = _.get(current, "geometry", {}); //if (!_.isMatch(currentGeometry, desired.geometry)) { // if (currentView === "DEFAULT") { // _.set(updateOp, ["geometry"], desired.geometry); // } else { // _.set(updateOp, ["geometry", currentView], desired.geometry); // } //} if (Object.keys(updateOp).length !== 0) { update[currentId] = updateOp; if (updateFuncName) { if (_.get(actions, [idempotencyKey, "add"], []).length === 0) { _.set(actions, [idempotencyKey, "add"], [updateFuncName]); } else { actions[idempotencyKey]["add"].push(updateFuncName); } } } } else { // Create the component from the desired spec const name = _.get(desired, "properties.si.name"); create[name] = desired; } } if (Object.keys(create).length) { ops.create = create; } if (Object.keys(update).length) { ops.update = update; } if (Object.keys(deletes).length) { ops.delete = deletes; } if (Object.keys(actions).length) { ops.actions = actions; } return { status, message, ops, }; } export function variables( thisComponent: Record<string, any>, ): Record<string, any> { const something = _.merge( {}, _.get(thisComponent, [ "properties", "domain", "Template", "Default", "Values", ], {}), _.get(thisComponent, [ "properties", "domain", "Template", "Override", "Values", ], {}), ); return something; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server