Overview

An Ontology edit refers to the idea of creating, modifying, or deleting an object. Functions support returning Ontology edits for use in a Function-backed Action. These Functions are authored using the @OntologyEditFunction decorator which provides special semantics to simplify your code.

These Functions also use the @Edits decorator in order to provide Actions with provenance information, which Actions may use to enforce permissions.

You can write unit tests for Ontology edit Functions using the APIs available for verifying Ontology edits.

The rest of this document describes how Ontology Edit Functions work behind the scenes to provide you with a better understanding of the underlying infrastructure.

When edits are applied

A common misunderstanding about Ontology Edit Functions is whether or not running them will update objects in the Ontology. When you run an Ontology Edit Function in the Functions helper in Authoring, edits are not applied to the actual objects. The only way to update objects using a Function is by configuring an Action to use the Function as described in the documentation for Function-backed Actions.

This means that you can freely run Ontology Edit Functions in the Functions helper to validate results on various inputs, without concern that the objects themselves will be updated.

Results pane

How edits are captured

When an Ontology Edit Function is executed, all updates to objects are captured by the Functions infrastructure and returned at the end of the Function execution. This includes new object creations via the Objects.create() API, all property updates, and Object deletions.

Edits are collapsed intelligently so that the minimal set of edits are applied in an Action. For example, if you create a new object and then update its properties, a single Create Object edit will be returned containing the property updates. Similarly, updating multiple properties of an existing object will return a single Update Object edit containing all of the property edits. Deleting an object will erase any other property edits that were done before the deletion. The entire Function must succeed in order to generate the list of edits which is passed to the Actions service executing the atomic transaction.

The captured Ontology Edits are returned as a list from the Function execution. This is why Ontology Edit Functions must have a return type of void or Promise<void>: when they are executed, the true return type of the Function is a list of Ontology Edits, so it isn’t possible to simultaneously return another value.

Edits are captured in a single edit store over the entire lifecycle of a single Function execution. This means that it is possible to call into helper functions which create, update, or delete objects even if those helper functions aren’t published as Ontology Edit Functions. For example:

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 export class HelperEditFunctions { @Edits(ObjectA, ObjectB) @OntologyEditFunction() public createAndLink(): void { const objectA = this.createObjectA(); const objectB = this.createObjectB(); objectA.linkToB.set(objectB); } /** * Even though these helper functions aren't annotated with @OntologyEditFunction(), * they can create new objects for use in other edit functions. */ private createObjectA(): ObjectA { const objectA = Objects.create().objectA(this.generateRandomId()); objectA.prop1 = "example"; objectA.prop2 = 42; return objectA; } private createObjectB(): ObjectB { const objectB = Objects.create().objectB(this.generateRandomId()); objectB.prop1 = "another example"; return objectB; } /* Generate your primary keys as needed, for instance import { Uuid } from "@foundry/functions-utils"; private generateRandomId(){ return Uuid.random(); } */ }

Retrieving edited values

When edits are done in a Function, the Functions infrastructure will return the edited values when you read them. For example, setting a property of an object and then retrieving it will return the new value:

airplane.departureTime = newDepartureTime;
console.log(airplane.departureTime); // Will log newDepartureTime

Deleting an object will remove it from search results and prevent accessing its properties.

Changes to objects and links are propagated to the Objects.search() APIs after your function has finished executing. This means that Objects.search() APIs will use the old objects, properties and links. As a result, search, filtering, search arounds, and aggregations may not reflect the edits to the Ontology, including creation and deletion. Your function will need to handle this case manually.

For the following example, assume there is an Employee with ID 1.

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 import { OntologyEditFunction } from "@foundry/functions-api"; import { Employee, Objects } from "@foundry/ontology-api"; export class CaveatEditFunctions { @Edits(Employee) @OntologyEditFunction() public async editAndSearch(): Promise<void> { let employeeOne = Objects.search().employee().filter(e => e.id.exactMatch(1)).all()[0]; employeeOne.name = "Bob"; console.log(await Objects.search().employee().filter(e => e.name.exactMatch("Bob").count() ?? -1); // Expected: 1, Actual: 0 } }

The @Edits decorator

Actions may require provenance information to enforce its permissions. To provide Actions with this information, you can use the @Edits decorator, specifying the object types for which your Function returns edits.

Consider the following when using the @Edits decorator:

  • When editing properties, the type of the object that was edited should be declared.
  • When editing one-to-one or one-to-many links, the type of the object with the foreign key property should be declared.
  • When editing join table links, both the source and target object types should be declared.

Functions perform static analysis of your code to automatically detect referenced object types, but this should be not be relied on, since static analysis may fail to properly detect a reference. We strongly recommend that you always use the @Edits decorator to provide provenance information about referenced object types.

Example

For the following example, the two object types, Employee and Aircraft, are edited by the following function:

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 import { OntologyEditFunction } from "@foundry/functions-api"; import { Employee, Aircraft, Objects } from "@foundry/ontology-api"; export class MyOntologyEditFunction { @Edits(Aircraft, Employee) @OntologyEditFunction() public myFunction(): void { const x = Objects.search().aircraft().all()[0]; x.businessCapacity = 3; const y = Objects.search().employee().all()[0]; y.department = "HR"; } }