vcf-services

Object Metadata 2.0

Metadata is available in the /cloudapi REST api.

The following object types have metadata(expanding list):

Object metadata gives cloud operators and tenants a flexible way to associate user-defined properties (key=value pairs) with objects. New in this version:

Basic structure

There currently are five entry kinds with different value types: String, Number, Boolean, Json and File. Here is an example of the core structure. For more details, please refer to the openapi specification. Example:

Entries are unique on the fields: domain, namespace, key, within the context of the main object that they are attached to. Once created, the entry will have a unique URN id. The id is then used to access individual entries for view/update/delete operations. You can update only the value and the persistent fields of the entry. If you want to change any other fields, you must re-create it.

```http request GET /1.0.0/entities/{id}/metadata/{entry-urn}


```http request
PUT /1.0.0/entities/{id}/metadata/{entry-urn}

```http request DELETE /1.0.0/entities/{id}/metadata/{entry-urn}


There can be up to `50` entries per entity across namespaces and domains.

## Json metadata

`JsonEntry` allows attaching an arbitrary JSON object to an entity. The value is stored and returned verbatim. VCFA validates JSON well-formedness on write but does not interpret the contents.

`JsonEntry` is available starting with API version `9.1.2`.

Constraints:

* The `value` must be a JSON **object** (not a scalar or array).
* The serialized value size is capped at **64 KiB** per entry by default (cell-config tunable).
* `JsonEntry` values are **not searchable** and **not sortable**. They cannot be used in a `FIQL` metadata filter expression. Retrieve by key instead.
* `JsonEntry` metadata is visible to any caller who can read the parent object. Do **not** store secrets in a `JsonEntry`.

## File metadata

A new kind of metadata has been introduced which is intended for storage of small bits of data – up to `32 MB` per entry. The `GET` address of such a resource is stable (you do not have to first request а download transfer) and the binary response payload will include a Content-Type header with the value of the mimeType of the entry as created/updated. In this way you can store for example an image, whose location can be embedded in a html `<img>` tag as a source for a UI extension.

The lifecycle of the `File` entries is slightly different from the other kinds, because there is an asynchronous binary content upload step. Therefore, only for this kind the creation operation returns a task, and the uniqueness validation is performed upon task(upload) completion.
The binary content upload itself is done through a link header returned to the creation request to track the task and upload the content. You must match the `Content-Length` header of this request, to the `size` field in the `FileDescriptor` of the metadata entry.

In relation to this kind of metadata a new quota type has been added – `Total metadata file size`, which enables providers to configure the amount of storage a tenant can consume through this feature.

## Access control model

The access control model is based on `domain`. `File` entries have an additional layer of authorization through a right (`Metadata File Entry: Create/Modify`) which enables only specific users to work with this kind of resource(create/update/delete).

Each user has an access level to the metadata, which is defined based on the formula: `min(main object access level, entry access level)`. The entry access level is defined based on the `domain` and `readOnly` fields.\
The `domain` field allows providers to share entries created by them to their tenants. The `readOnly` field specifies whether they should additionally be able to modify the entries.

Currently, there are only two valid values for the domain in the following order: `TENANT` < `PROVIDER`. Users cannot place an entry in a domain which is greater than the one they operate in or define a `readOnly` entry in their own domain.\
For example, a provider can place an entry in the `TENANT` domain with `readOnly=true` to allow a tenant user to view it (but not modify it). However, a tenant user cannot create an entry in the `PROVIDER` domain, or a `readOnly` entry in the `TENANT` domain.

In summary:

* In order to be able to view the metadata of the object, you must at least have read access level to it and the entries must be in a domain up to- or below the one you are operating in
* In order to be able to create/update the metadata of the object, you must at least have `write` access level to it.
* In order to be able to update metadata, it must be in a domain up to- or below the one you are operating in and not `readOnly`. Additionally, for file entries you must have the right `Metadata File Entry: Create/Modify`

## Namespace

Think of the `namespace` as an optional secondary key by which entries are unique. It provides a more obvious mechanism to group entries by vendor.

## Configurable lifecycle on cloned objects

You can specify if the metadata of the object should be copied over on certain clone type of operations on the object. The list of operations depends on the type of the object. Entries with their `persistent` field set to `true` will be copied on such operations automatically.

## Searching by metadata

In the query service of the `/api` space, searching by metadata has two aspects:

* a filter for entry existence
* a projection to include specific entries in the representation of the main object

These have equivalent representations in the new version – _filter_ and _summary_.

### Metadata Filter

Various api endpoints which allow searching for instances of an object type having metadata now have an additional query parameter – `metadata`. For example:

```http request
GET /1.0.0/entities/types/{vendor}/{nss}/{version}?metadata=

Users can specify a FIQL expression selecting by metadata as if the keys and values are regular properties of the object. The structure of a single entry selection is the following: [namespace|]key operator value. The namespace is optional and must be separated from the key by the pipe symbol. Therefore, the pipe symbol is a restricted character and cannot be used in the namespace or key of the entries. You can search by wildcard and key-only expressions. Examples:

Example of filtering both at a core object property and metadata:

```http request GET /1.0.0/entities/types/{vendor}/{nss}/{version}?metadata=test.key.3==42&filter=name==testEntity


This functionality should not be confused with the individual entries search on a particular object. The api `GET …/{object-id}/metadata` supports a regular filter query parameter, which enables you to search for particular entries on the object by key (note the syntax which is a consequence of the resource structure: `?filter=keyValue.key==test.key`).

### Metadata Summary

Instead of specifying a particular projection of which entries to include in the payload, users will now receive additional link headers in the response of the previous search operation. The response of such a link will contain a portion of a simplified representation of the metadata of the previously retrieved main objects. Consumers of this api must fetch all the links and merge the result in order to retrieve the total metadata summary. 

Example:

* Link: `/1.0.0/metadataSummaries/rHLWh2aJRxazDCACHOaRqlqaXw7JdE0avCSxVYnG_IZBFhs1UTxP_6j7amOI8hHU>;rel=”related";title=1;type="application/json";model="MetadataSummaries"`
* response payload is a `map` of object urn to array of `KeyValue`:

* ```json
    {
    "urn:…ac72d687-6689-4716-b30c-20021ce691aa": [
      {
        domain:,
        namespace:,
        key:,
        value:
      }, …
    ],…
    }

The title property of the link contains an index for the links returned. Each link can (optionally) be ordered on the title.
The response content will include only metadata to which the user has access to at the time of request. If the configuration of the objects/entries related to the access control model changes between requests, you will potentially receive different contents.

Optimistic concurrency control

Users can optionally enable safe concurrent modifications of entries via the standard If-Match and ETag headers. When an entry is retrieved, an ETag header is returned, representing the current state id. When an If-Match header with that id is added to an update/delete request, the system will only perform the operation if the current state matches the requested reference state.