Skip to content

Commit

Permalink
[Docs] Add DR covering canonical use of managementPolicy.
Browse files Browse the repository at this point in the history
Part of #778, this aspect of the API has not been documented in any
detail.

Signed-off-by: Tom Cowland <tom@foundry.com>
  • Loading branch information
foundrytom committed Dec 20, 2022
1 parent 198ec3d commit d2ff313
Showing 1 changed file with 307 additions and 0 deletions.
307 changes: 307 additions & 0 deletions doc/decisions/DR014-Management-policy-responses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
# DR014 Management Policy responses

- **Status:** Proposed
- **Driver:** @foundrytom
- **Approver:** @feltech @ecmorris @antirotor @mattdaw
- **Outcome:** A Manager should additionally imbue a `managementPolicy`
response with the traits that it is capable of resolving/persisting
for each trait set.

## Background

The ability for a host to query a manager's capabilities and intent is a
critical part of the OpenAssetIO abstraction.

OpenAssetIO classifies entities (assets) using a Trait Set, which lists
one or more traits that describe its nature. For example, an image
file may have the trait set `{'file', 'image', 'colorManaged'}`.

The `managementPolicy` method is a high-level (i.e. not entity specific)
query that a host can use to determine a manager's behaviour in relation
to a specific trait set.

The answer to this query is commonly used by a Host to conditionally
enable user-facing functionality based on the behaviour of the specific
manager that is in use.

This mechanism is flexible, and so exactly how a manager should
implement its response is potentially ambiguous. This document outlines
the rationale behind the defined canonical behaviour.

## Relevant data

Documentation pertaining to the OpenAssetIO data model:

- [Entities, Traits and Specifications overview](../doxygen/src/EntitiesTraitsSpecifications.dox)
- [Compositional data model decision record](./DR007-Hierarchical-or-compositional-traits-for-specifications.md)
- [Unified Entity data model decision record](./DR008-Unify-the-entity-data-model.md)

Notable points:

- A Trait is how OpenAssetIO describes a behaviour or quality.
- Traits consist of a unique ID, and zero or more simple-typed
properties that further describe that trait.
- A Trait Set is an un-ordered collection of unique trait IDs.
- Trait Sets combine multiple traits to increase specificity, rather
than meaning "or".
- A `TraitsData` instance is a dict of dicts, that can be "imbued" with
zero or more traits and the values for any associated properties those
traits may have. The top-level keys of this container form a Trait
Set.
- The `managementPolicy` result is a `TraitsData` instance for each
supplied Trait Set.
- Some managers may only care about certain specific Traits (such as
those describing the assets content disposition), and handle all
'derivative entity types' the same.
- The `managementPolicy` query is not a required pre-requisite in the
use of `resolve`, et. al. but is used to adapt application behaviour
to improve user experience.

## Illustrative scenario

Consider a manager that manages the files for assets used in 2D
compositing.

It happily manages the node graphs used to define a comp, plus any
associated images read/written by the process. It does however, have
no interest in managing intermediate cache files that may be used
to speed up interactivity.

Its implementation pivots all behaviour around two (imaginary) traits:

- The `file` trait - indicating the entity's data is stored in a file.
- The `cache` trait - indicating that the data is re-creatable.

The manager has a fairly simple database and isn't capable of storing
arbitrary data for an entity, just the appropriate file path. It
fulfills the API contract, by ensuring it can also store an entity's
trait set - allowing it to filter lookups later, and properly
re-classify any given entity as required. However, it can't persist the
data for any other traits these entities may have.

A Host that manages several file-based entities (eg: its
main document and assorted data files that it reads/writes),
will query the manager's `managementPolicy` to determine for which of
these the manager should be involved with.

If a manager opts-out of managing any given trait set, then the host
will use its native UI/workflows for browsing and saving, if it opts-in,
then it will delegate UI and data locality responsibilities to the
manager.

So, how should this manager respond to the `managementPolicy` query for
any specific trait set?

Lets explore the scenario where the host is querying the manager's
policy for its native document format, and for images it generates, and
its temporary cache:

```python
traitSets =[
{ 'file', 'nodeGraph' }, # Main document
{ 'file', 'image', 'colorManaged' } # Generated images
{ 'file', 'image', 'latlong', 'colorManaged' } # Generated maps
{ 'file', 'image', 'cache' } # Temporary cache
]
```

Keep in mind, that the manager is only interested in `file` based
entities, and needs to be able to explicitly opt-out of certain trait
sets.

## Options considered

### Option 1 - Any Matching

The manager should respond on the basis of matching any of the traits in
the set, even if it doesn't understand many others, unless it explicitly
needs to opt-out:

```python
if CacheTrait.kId in traitSet:
continue
if FileTrait.kId in traitSet:
ManagedTrait.imbueTo(policy)
```

Such that it opts-in to managing all entities with the file trait in
their set, aside from caches:

```python
policies = [
{ 'managed': {...} }, # Main document
{ 'managed': {...} }, # Generated images
{ 'managed': {...} }, # Generated maps
{}, # Temporary cache
]
```

Any other queries for trait sets without the file trait would be empty
`{}` indicating un-managed status.

#### Pros

- Hosts can query specific and precise trait sets, but the manager does
not need to worry about all possible permutations.

#### Cons

- There is no way for a host to determine what traits can be resolved
(or persisted) for any given entity, and so must tolerate missing data
at a later date (e.g. the color space for the `colorManaged` trait, if
the manager can't ever provide this).

### Option 2 - Exact Matching

The manager should respond only to sets where it is capable of resolving
or persisting all of the requested trait set:

```python
if traitSet == { FileTrait.kId }:
ManagedTrait.imbueTo(policy)
```

Such that it _only_ opts into managing the specifically limited trait
set that it can support:

```python
policies = [
{}, # Main document
{}, # Generated Images
{}, # Generated maps
{} # Temporary caches
]
```

The Host would receive empty responses (`{}`) for all of the original
trait sets, and so must potentially decompose and re-try its queried
trait sets to reveal some supported sub-set. For example, if ultimately
it just needed the file path, it could try these combinations:

```python
traitSets = [
{ 'file', 'nodeGraph' },
{ 'file', 'image', 'colorManaged' },
{ 'file', 'image' },
{ 'file', 'image', 'latLong', 'colorManaged' },
{ 'file', 'image', 'latLong' },
{ 'file', 'cache' },
{ 'file' }
]
```

Resulting in:

```python
policies = [
{},
{},
{},
{},
{},
{},
{ 'managed': {...} }
]
```

#### Pros

- Conveys only what traits are supported to the host, allowing
behaviour to be properly adapted to the manager's capabilities.

#### Cons

- No way to know that actually the manager was interested in managing
all but one of the requested trait sets.
- Host implementation is significantly more complicated.
- No way to hint at any additional traits that may be resolvable
outside the typing trait set.

### Option 3 - Supported Traits Response

The host should also include any additional traits that it may attempt
to resolve in the future, e.g. if it supported the ability to customise
the file format used for writing images, or cache lifetime by resolving
a custom trait:

```python
traitSets =[
{ 'file', 'nodeGraph' },
{ 'file', 'image', 'colorManaged', 'fileFormatOptions' }
{ 'file', 'image', 'latLong', 'colorManaged', 'fileFormatOptions' }
{ 'file', 'cache', 'retention' }
]
```

The manager should respond as per Option 1, but in addition, imbue any
of the queried traits that the manager is capable of resolving:

```python
if CacheTrait.kId in traitSet:
continue
if FileTrait.kId in traitSet:
ManagerTrait.imbueTo(policy)
FileTrait.imbueTo(policy)
```

This would then look like the following:

```python
policies = [
{ 'managed': {...}, 'file': {} }, # Main document
{ 'managed': {...}, 'file': {} }, # Generated images
{ 'managed': {...}, 'file': {} }, # Generated maps
{} # Temporary caches
]
```

This indicates that the manager would like to handle interactions for
entities with all except the cache traits set, but it can only resolve
the `file` trait for these.

> Note:
> We keep the concept of additionally imbuing the `managed` trait
> (though it may seem superfluous, as an empty trait set is equivalent to
> un-managed) as in practical use cases it may well have other properties
> that cover topics such as exclusivity/etc.
> There are also additional traits that may be imbued by the manager to
> determine how it handles various aspects of the publishing process,
> e.g. thumbnails.
#### Pros

- Hosts can query specific and precise trait sets, but the manager does
not need to worry about all possible permutations.
- Explicit communication of exact capabilities, so hosts and managers
can suitably adapt behaviour.
- Facilitates functional workflows where the result of `managementPolicy`
can be passed as the requested trait set for `resolve`.

#### Cons

- Implementation in the manager and host may be slightly more involved
than other options in some scenarios in order to parse the additional
data in the response.

## Outcome

The `managementPolicy` mechanism should always be queried with the full
trait set for any given entity specification, and any additional traits
a host may attempt to resolve/publish for that type (if known).

The response populated by a manager should include all
resolvable/persisted traits from that set, along with any additional
traits that describe the manager's behaviour.

## Rationale

Option 3 (chosen) provides the highest quality information to both hosts
and managers, whilst keeping the additional programming overhead to a
minimum. It allows both parties to adapt their business logic in an
explicit and coordinated fashion.

Option 1 conveys which trait sets may be managed, but not that some
traits are unresolvable.

Option 2 precludes the ability to differentiate between unmanaged trait
sets and unresolvable component traits and so is not a viable option.

0 comments on commit d2ff313

Please sign in to comment.