= Extensions

== Purpose

RacksDB offers the possibility to define custom extensions of
xref:schema.adoc[vendor schema] with additional objects or additional properties
to existing objects. RacksDB ensures at load time the database content fully
respect the structure defined by the combination of the vendor schema and the
defined extensions.

The additional data is automatically available through the
xref:usage:racksdb.adoc[CLI], xref:usage:lib.adoc[Python library] and
xref:usage:rest.adoc[REST API] without further modification.

This feature can be used to enrich RacksDB database with data useful to your
applications without requiring another data source, with the advantage of being
fully integrated with the other data defined in RacksDB.

== Path

The extensions must be defined in a plain file formated in YAML language.

By default, RacksDB loads extensions defined in file
[.path]#`/etc/racksdb/extensions.yml`#. If this file does not exist, RacksDB
silently ignores it and just loads the built-in schema without modification.

NOTE: RacksDB can load extensions at the path of your choice, at the condition
of specifying this path in xref:usage:racksdb.adoc[`racksdb`
command] options and xref:usage:lib.adoc[library `load()`] arguments. For this
reason, it is highly recommended to use the default path.

== Content

The syntax of the extension file is roughly similar to the
xref:schema.adoc[schema file].

The YAML extension file can contain 2 keys at its root:

`_content`:: Mapping (_aka_: hash or associative array) to extend the
definition of the object at database root.
`_objects`:: Mapping to extend the objects definitions.

The format of `_content` is an xref:#object[object definition]. The format of
`_objects` is a mapping whose keys are objects names and values are
xref:#object[objects definitions].

include::partial$prop-types.adoc[]

== Example

This is a simple example of an extension definition file:

[source,yaml]
----
include::example$extensions.yml[]
----

With this extension:

* The `power` key is added at the root of RacksDB database structure. It can
  contain an optional list of `Ups` object.
* The new `Ups` object is defined, as it does not exist in RacksDB vendor
  structure. This object contains a property named `capacity` using the
  xref:structure.adoc#deftype-watts[~watts] defined type.
* The xref:structure.adoc#obj-racktype[RackType] object is extended with an
  optional `weight` integer attribute.

== Data Visibility

NOTE: When RacksDB is
xref:install:quickstart.adoc#_distribution_packages[installed with packages],
this example extension is installed in path
[.path]#`/usr/share/doc/racksdb/examples/extensions`# with fake values. This
example is used to illustrate data visibility of custom extensions.

The custom objects and properties data can then be explored with RacksDB
interfaces:

* xref:usage:racksdb.adoc[CLI]:

[source,console]
----
$ racksdb --db /usr/share/doc/racksdb/examples/extensions/db \
          --ext /usr/share/doc/racksdb/examples/extensions/extensions.yml \
          racks --format json | \
          jq  '.[] | { name: .name, weight: .type.weight}'
----

This reports the _weight_ of every racks:

[source,json]
----
{
  "name": "R1-A01",
  "weight": 128
}
{
  "name": "R1-A02",
  "weight": 128
}
{
  "name": "R1-A03",
  "weight": 128
}
…
----

* xref:usage:racksdb.adoc[Python library]:

Print the _capacity_ of every UPS :

[source,python]
----
>>> from racksdb import RacksDB
>>> db = RacksDB.load(
...          db="/usr/share/doc/racksdb/examples/extensions/db",
...          ext="/usr/share/doc/racksdb/examples/extensions/extensions.yml"
...      )
>>> db.power
[<racksdb.generic.db.RacksDBUps object at 0x7f7c3e7c2bf0>,
 <racksdb.generic.db.RacksDBUps object at 0x7f7c3e7c2c50>]
>>> for power in db.power:
...     print(f"capacity: {power.capacity}")
...
capacity: 250000
capacity: 1600000
----

* xref:usage:rest.adoc[REST API]:

Launch xref:usage:racksdb-web.adoc[`racksdb-web`] to get REST API server:

[source,console]
----
$ racksdb-web --db /usr/share/doc/racksdb/examples/extensions/db \
              --ext /usr/share/doc/racksdb/examples/extensions/extensions.yml
----

Request with HTTP client:

[source,console,subs=attributes]
----
$ curl http://localhost:5000/{api-version}/racks | \
  jq  '.[] | { name: .name, weight: .type.weight}'
----

This reports the _weight_ of every racks:

[source,json]
----
{
  "name": "R1-A01",
  "weight": 128
}
{
  "name": "R1-A02",
  "weight": 128
}
{
  "name": "R1-A03",
  "weight": 128
}
…
----
