Copy file to another entity

Use case:
Invoices come in via email.
Within the Email/Message database there is a button “Create invoice”, which prompts the user for some input (invoice date, number, amount, and so on) and then creates an entity in the Finance/Invoice database.

From my understanding of the backend, there is a separate storage for files, and each database utilizing files does so by adding an “entity collection” field, which basically references to files via file’s ID/secret.

Now as we can’t leave the server environment in automations, I guess what should be possible is to:

  • add an entity to the collection field (files)
    → with the same values for ID, Secret, name, and content-type of an already existing file.
    This would basically mean the file would show in multiple entities, but if deleted in ANY place, it will be deleted everywhere.

Maybe executeSingleCommand(command: FiberyCommand) might be needed for specific commands, but it should be possible from within Fibery automations as there is no need to leave the server environment, no?

Hi here is the sketch for a script which copies files from target Entity


// Fibery API is used to retrieve and update entities
const fibery = context.getService('fibery');
const utils = context.getService('utils');

const yourAccountHostName = "https://"; // e.g. "https://example.fibery.io"
const recipientEntityType = "" // e.g. "Copy File/File Recipient";
const recipientEntityId = "" // e.g. "bdbac3b0-c41e-11ed-b1e2-91c412ad7219";

// affected entities are stored in args.currentEntities;
// to support batch actions they always come in an array
for (const entity of args.currentEntities) {


    // to get collection fields query the API and provide the list of fields
    const entityWithExtraFields = await fibery.getEntityById(entity.type, entity.id, ['Files']);

    for (const file of entityWithExtraFields.Files) {
        const fileWithSecret = await fibery.getEntityById("fibery/file", file.id, ["Secret", "Name", "Content Type"]);
        const fileUrl = yourAccountHostName + "/api/files" + fileWithSecret["Secret"];


        await fibery.addFileFromUrl(fileUrl, fileWithSecret["Name"], recipientEntityType, recipientEntityId, {
            "Authorization": "Token YOUR_API_TOKEN" // replace YOUR_API_TOKEN with one from "You can generate, list and delete tokens on the "API Tokens" page available from the workspace menu.
        });
    }
}
4 Likes

EDIT: Found the solution to the question here, see next reply if needed

Hi @Viktar_Zhuk

Thank you very much!
I am trying to make it work with your code as a basis but I’m stuck.

The adjustment I need to make is:
RecipientEntityID variable in your code needs to be dynamic.
For this, I’m trying to query for all invoices, but filter that the single entity field “Generated through email” contains the current message.

I can now run simple queries using fibery.executeSingleCommand with the help of some posts here (since there is no documentation about that function), but I couldn’t make the filter work. This is the relevant part of the code:

const thisentityname = args.currentEntities[0].Name;

const recipentEntity = await fibery.executeSingleCommand({
    "command": "fibery.entity/query",
    "args": {
        "query": {
            "q/from": "Finance and Controlling/Invoice",
            "q/select": ['fibery/id'],
            'q/where': ['=', ['Finance and Controlling/Generated through email', "Email (invoices)/name"], '$thisentity'],
            "q/limit": "q/no-limit"
                }
            },
    "params": { '$thisentity': thisentityname }
});
console.log(recipentEntity);
return

But I’m getting this error message:

image

Could anyone point out where my mistake is?


Some technical information if relevant:
Space/database:

  • Finance and Controlling/Invoice
  • Email (invoices)/Message (this is the native Gmail sync space)

A message with attached PDFs is used to generate an Invoice entity for each PDF it contains(one-to-many).
→ each message has an entity collection field “Invoices generated from this message”
→ each invoice has a single entity field “Generated through email”

To keep it simple, this button only has to work for messages with 1 PDF → singular invoice is created by a message. Else the correct distribution of PDF to the right entity would be too complicated, these cases we can do manually.

I post below the full code, in case someone needs it.
Thanks a lot @Viktar_Zhuk!

The following code copies all attachments from the current entity (either button, or automation) to the recipient entity. See technical information in reply above for a better understanding of used spaces/fields.

// Fibery API is used to retrieve and update entities
const fibery = context.getService("fibery");
const utils = context.getService('utils');

const thisentityname = args.currentEntities[0].Name;
const yourAccountHostName = "https://your_host_name.fibery.io"; // e.g. "https://example.fibery.io"
const recipientEntityType = "Invoice"; // Database. Use "Space/Database" if same database name is used in multiple spaces.

const recipientEntity = await fibery.executeSingleCommand({
    "command": "fibery.entity/query",
    "args": {
        "query": {
            "q/from": "Finance and Controlling/Invoice",
            "q/select": ['fibery/id'],
            "q/where": ["q/contains", ["Finance and Controlling/Generated through email", "Email (invoices)/name"], "$nameofthisentity"],
            "q/limit": "q/no-limit"
        },
        "params": {
            "$nameofthisentity": thisentityname
        }
    }
});
const recipientEntityId = recipientEntity[0]['fibery/id'];


// affected entities are stored in args.currentEntities;
// to support batch actions they always come in an array
for (const entity of args.currentEntities) {


    // to get collection fields query the API and provide the list of fields
    const entityWithExtraFields = await fibery.getEntityById(entity.type, entity.id, ['Files']);

    for (const file of entityWithExtraFields.Files) {
        const fileWithSecret = await fibery.getEntityById("fibery/file", file.id, ["Secret", "Name", "Content Type"]);
        const fileUrl = yourAccountHostName + "/api/files" + fileWithSecret["Secret"];


        await fibery.addFileFromUrl(fileUrl, fileWithSecret["Name"], recipientEntityType, recipientEntityId, {
            "Authorization": "Token YOUR_API_TOKEN" // replace YOUR_API_TOKEN with one from "You can generate, list and delete tokens on the "API Tokens" page available from the workspace menu.
        });
    }
}

If you’re creating the Invoice in an earlier step of the automation, you can just refer to it in the script, using args.steps
Does that help?

For anyone who reads this thread.
We’ve made some simplifications to allow to write reusable scripts that copy files between fibery entities.

Here is the code. Assuming Database on what you create this Automation has a relation named “Story” to Database of name “SoftDev/Story”. This code copies files from selected entities to their related Stories.

const fibery = context.getService('fibery');
const utils = context.getService('utils');
const entitiesWithExtraFields = await fibery.getEntitiesByIds(args.currentEntities[0].type, args.currentEntities.map(x => x.id), ['Files', 'Story']);
for (const entity of entitiesWithExtraFields) {
    if (entity['Story'].id !== null) {
        for (const file of entity.Files) {
            const fileWithSecret = await fibery.getEntityById("fibery/file", file.id, ["Secret", "Name", "Content Type"]);
            const fileUrl = utils.getFileUrl(fileWithSecret["Secret"]);
            await fibery.addFileFromUrl(fileUrl, fileWithSecret["Name"], 'SoftDev/Story', entity['Story'].id, {});
        }
    }
}
3 Likes

That’s awesome!

Is there also a script to copy an entities relations?

So example: we have a task with the following linked databases

  • project
  • priority
  • notes

When you duplicate the task, the relations get lost.

But if you have simple relations like this, it would be so awesome if we can have a way to duplicate a task including the relations of that task.

@YvetteLans To clone a task including relations, see this button script (that only copies the Project relation) as example. Be sure to replace CCE Root/Task with your spacename/Task

const fibery = context.getService('fibery');

for (const entity of args.currentEntities) {
    // Specify the fully qualified name of the "Task" type
    const taskType = 'CCE Root/Task';

    // Fetch the content of the 'Project' field from the current entity
    const entityWithExtraFields = await fibery.getEntityById(taskType, entity.id, ['Project']);
    
    // Extract the value of the 'Project' field
    const projectId = entityWithExtraFields['Project']['Id'];

    // Clone the current entity with only the title
    const clonedEntity = await fibery.createEntity(taskType, {
        'Name': entity['Name'] + ' (Clone)',
    });

    // Set the 'Project' relation for the cloned entity
    await fibery.updateEntity(taskType, clonedEntity['id'], {'Project': projectId});
}

Although there is a confirmation message after cloning, there is not link appearing to navigate to the newly created Task…something to ponder about.

1 Like

Hm. You don’t actually need a script to duplicate entity and copy some relations.
Just use “Create” action and setup fields using formula:

Still you will have same problem as Yuri mentioned: no link to created entity will be in a “success” message. In scripts you can customize “success” message just by writing return "some success message" statement in the script. But I think it does not support inserting links at the moment. Hepefully we will improve here at some point, allowing links.

I think I missed my coffee this morning :see_no_evil: Thanks!

And indeed no success message. But with a bit of fantasy you can

  • Notify user
  • Create comment and put the link there

Not very fancy but in the meantime it’s a workaround.