# Centralized script management for Workspace

**URL:** <https://community.fibery.io/t/centralized-script-management-for-workspace/3039>\
**Category:** Ideas & Features\
**Tags:** automations\
**Created:** [July 5, 2022, 10:49pm UTC](https://community.fibery.io/t/centralized-script-management-for-workspace/3039 "2022-07-05T22:49:01Z")\
**Posts on this page:** 1\
**Showing post:** 3

<div class="post-metadata">

**Author:** ![Matt\_Blais](https://sea2.discourse-cdn.com/flex020/user_avatar/community.fibery.io/matt_blais/32/1464_2.png) [@Matt\_Blais](https://community.fibery.io/u/Matt_Blais)\
**Post date:** [July 6, 2022, 3:40pm UTC](https://community.fibery.io/t/centralized-script-management-for-workspace/3039/3 "2022-07-06T15:40:37Z")

</div>

100% agree! A lot of my requests here are directly related to this.

Here are some things I can’t currently do with no-code:

- “Reach through” a Lookup to update the referenced entity.
- Change the Status of a related entity.
- Use References to look up a related entity:

```auto
// If a Task's "Project" field is uninitialized, and there is a "Meeting Notes"
// that references the Task, set the Task's "Project" field from the MN's "Default Project".

// Script parameters
const ENTITY_LINK_FIELD = 'Project' // Task field that needs to be set
const PARENT_TYPE = 'Project/Project' // Type we want to find a link to (value for)
const REF_ENTITY_TYPES = ['Clients/Meeting Notes'] // Types that have "Default Project" field
const REF_ENTITY_LINK_FIELD = 'Default Project' // Name of the field to copy

const fibery = context.getService('fibery')
// const log = console.log

// Iterate through the entities
for (const entity of args.currentEntities) {
    // Set the Project field if empty
    if (entity[ENTITY_LINK_FIELD].id == null) {
        // Get all references to the Task
        const entityWithRefs = await fibery.getEntityById(entity.type, entity.id, ['References'])
        // Look for a reference to to a type with "Default Project" field (i.e. Meeting Notes)
        for (const ref of entityWithRefs['References']) {
            // Get referencing doc
            const refDoc = await fibery.getEntityById('Collaboration~Documents/Reference', ref['Id'], ['FromEntityId', 'FromEntityType'])
            const refType = refDoc['FromEntityType'].name
            if (refType == PARENT_TYPE) {
                // The referring entity is the actual PARENT_TYPE (so its ID can be used directly)
                // I.e. a Task was created/referenced in a Project's Rich Text field
                const refEntity = await fibery.getEntityById(refType, refDoc['FromEntityId'], ['Name'])
                // log(` refType == PARENT_TYPE: ${refType}, ${refDoc['FromEntityId']} ${refEntity.id}`)
                await fibery.updateEntity(entity.type, entity.id, { [ENTITY_LINK_FIELD]: refEntity.id })
                break
            } else if (REF_ENTITY_TYPES.includes(refType)) {
                // Found a suitable referring entity (i.e. that has a "Default Project" field)
                const refEntity = await fibery.getEntityById(refType, refDoc['FromEntityId'], ['Name', REF_ENTITY_LINK_FIELD])
                if (refEntity != null && refEntity[REF_ENTITY_LINK_FIELD].id != null) {
                    const refId = refEntity[REF_ENTITY_LINK_FIELD].id
                    await fibery.updateEntity(entity.type, entity.id, { [ENTITY_LINK_FIELD]: refId })
                    break
                }
            }
        }
    }
}

```

- Call an external webhook
- Do something to every entity in a Collection, conditional upon the entity’s State, etc.
- Trim blanks from a field (I tried a Formula, didn’t work 🤷‍♂️)
- Access an entity’s UUID
- Treat a Formula field like a Lookup/Reference (if it returns an entity).
- Do anything with Documents (like create & link).
- Work with Rich Text:

```auto
// Create and link a new Meeting Notes entity to a Client

const log = console.log
const dump = (obj) => JSON.stringify(obj, null, 2)
const fibery = context.getService('fibery')
const MEETING_NOTES_TYPE = 'Clients/Meeting Notes'
const TEMPLATE_DOC_ID = "b62eaad0-e02b-11ec-b6d4-31b05e8bc8ae"
const template_DocName = 'Meeting Notes TEMPLATE'
const fmt = 'html'
function assert(b, msg) { if (!b) throw new Error(msg) }

// Retrieve the contents of the Meeting Notes TEMPLATE Document

const template_Secret = await getDocumentSecret(TEMPLATE_DOC_ID)
assert(template_Secret, `"${template_DocName}" Document not found - ID "${TEMPLATE_DOC_ID}"`)
const template = await fibery.getDocumentContent(template_Secret, fmt)
assert(template, `No content retrieved for "${template_DocName}" Document - ID "${TEMPLATE_DOC_ID}"`)

for (const client of args.currentEntities) {
    // Create a new Meeting Notes entity and link it to the Client
    const meetNotes = await fibery.createEntity(MEETING_NOTES_TYPE, { 'Client ID': client['Public ID'] })

    // Build the new Meeting Notes Description content via mustache macro replacement of the Template's contents
    const macros = {}
    const newDescription = template.replace(/\{\{\s*(\w+)\s*\}\}/g,
        (match, name) => (typeof macros[name] === 'string' ? macros[name] : ''))
    //log('new Description: ' + text)

    // Update the new Meeting Notes Description content
    await fibery.setDocumentContent(meetNotes.Description.Secret, newDescription, fmt)
}

async function getRichTextContent(entity, fieldName, fmt) {
    const secret = entity[fieldName].Secret
    return await fibery.getDocumentContent(secret, fmt)
}

async function getDocumentSecret(docId) {
    const view = await fibery.getEntityById('fibery/view', docId, ['fibery/meta']);
    return view['fibery/meta']['documentSecret'];
}

```

- De-duplication (looping, comparing, deleting, etc) – i.e. enforcing the uniqueness of entities by a key value (post-hoc is better than nothing!)

```auto
// Search for identical Roles (by Name) and delete all but the oldest one
const fibery = context.getService('fibery')
const entity_type = args.currentEntities[0].type
const SPACE_NAME = 'Users'

for (const entity of args.currentEntities) {
    // Find all identical Roles
    const query = `{findRoles( name:{is: "${entity.name}"} orderBy:{creationDate: ASC}) {id}}`
    const result = await fibery.graphql(SPACE_NAME, query)
    // Delete any duplicates
    if (result && result.data.findRoles.length > 1) {
        const roles = result.data.findRoles
        for (let i = 1; i < roles.length; i++) {
            // console.log(`Deleting: ${roles[i].id}`)
            await fibery.deleteEntity(entity_type, roles[i].id)
        }
    }
}

```

- Because no-code Rule conditions can only test an _entity’s own fields_ (i.e. they can’t test the fields of related entities or use arbitrary Formulas), I am forced to bring in more fields from related entities as Lookups/Formulas, and I resist doing this because it litters up my DB’s with all sorts of unwanted fields.

- The lack of a “Description” or “Comments” area for each DB’s Automations page (and for individual Rules/Buttons) is frustrating. I would really hate to inherit a complex system like this with such little documentation. Scripts at least allow me to do that.

💡 **Ideally it should be just as easy to “synthesize” a join anywhere** (instead of needing to manually construct a new DB to represent it). E.g. in a Table view, have the ability show any arbitrarily related fields (as columns) AND allow the user to edit them; or in automations, allow any related fields (and formulas) to be tested in the Rule conditions (we need formulas there!). I think this ability alone would relieve most of the architecture/code limitation issues I struggle with – big things like lack of polymorphism.

That, plus the ability to hide fields (I know it’s coming), but that’s really more of a band-aid to paper over the need to drag in so many fields with Lookups/Formulas.

---

_[View the full topic](https://community.fibery.io/t/centralized-script-management-for-workspace/3039)._
