Change-Id: I72a497658cfdaea076e1a40330b113ae35381a5e Partial-Bug: #1603950
10 KiB
MuranoPL Metadata
MuranoPL metadata is a way to attach additional information to various MuranoPL entities such as classes, packages, properties, methods, and method arguments. That information can be used by both applications (to implement dynamic programming techniques) or by the external callers (API consumers like UI or even by the Murano Engine itself to impose some runtime behavior based on well known meta values). Thus, metadata is a flexible alternative to adding new keyword for every new feature.
Work with metadata includes the following cases:
- Defining your own meta-classes
- Attaching metadata to various parts of MuranoPL code
- Obtaining metadata and its usage
Define meta-classes
Define meta-class with the description of arbitrary metadata.
Meta-class and regular class have two differences. The
Usage
attribute of meta-class equals to Meta
,
while the Usage
attribute of regular class equals to
Class
. The default value of the Usage
attribute is Class
. Meta-class also has additional
attributes (Cardinality
, Applies
and
Inherited
) to control how and where instances of that
meta-class can be attached.
Cardinality
The Cardinality
attribute can be set to either
One
or Many
and indicates the possibility to
attach two or more instances of meta-class to a single language entity.
The default value is One
.
Applies
The Applies
attribute can be set to one of
Package
, Type
, Method
,
Property
, Argument
or All
and
controls the possible language entities which instances of meta-class
can be attached to. It is possible to specify several values using YAML
list notation. The default value is All
.
Inherited
The Inherited
attribute can be set to true
or false
and specifies if there is metadata retained for
child classes, overridden methods and properties. The default value is
false
.
Using of Inherited: true
has the following
consequences.
If some class inherits from two classes with the same metadata
attached and this metadata has Cardinality: One
, it will
lead to emerging of two metadata objects with
Cardinality: One
within a single entity and will throw an
exception. However, if the child class has this metadata attached
explicitly, it will override the inherited metas and there is no
conflict.
If the child class has the same meta as its parent (attached
explicitly), then in case of Cardinatity: One
the meta of
the child overrides the meta of the parent as it is mentioned above. And
in case of Cardinatity: Many
meta of the parent is added to
the list of the child's metas.
Example
The following example shows a simple meta-class implementation:
Name: MetaClassOne
Usage: Meta
Cardinality: One
Applies: All
Properties:
description:
Contract: $.string()
Default: null
count:
Contract: $.int().check($ >= 0)
Default: 0
MetaClassOne
is defined as a meta-class by setting the
Usage
attribute to Meta
. The
Cardinality
and Applies
attributes determine
that only one instance of MetaClassOne
can be attached to
object of any type. The Inherited
attribute is omitted so
there is no metadata retained for child classes, overridden methods and
properties. In the example above, Cardinality
and
Applies
can be omitted as well, as their values are set to
default but in this case the author wants to be explicit.
The following example shows meta-class with different values of attributes:
Name: MetaClassMany
Usage: Meta
Cardinality: Many
Applies: [Property, Method]
Inherited: true
Properties:
description:
Contract: $.string()
Default: null
count:
Contract: $.int().check($ >= 0)
Default: 0
An instance (or several instances) of MetaClassMany
can
be attached to either property or method. Overridden methods and
properties inherit metadata from its parents.
Attach metadata to a MuranoPL entity
To attach metadata to MuranoPL class, package, property, method or
method argument, add the Meta
keyword to its description.
Under the description, specify a list of meta-class instances which you
want to attach to the entity. To attach only one meta-class instance,
use a single scalar instead of a list.
Consider the example of attaching previously defined metadata to different entities in a class definition:
Namespaces:
=: io.murano.bar
std: io.murano
res: io.murano.resources
sys: io.murano.system
Name: Bar
Extends: std:Application
Meta:
MetaClassOne:
description: "Just an empty application class with some metadata"
count: 1
Properties:
name:
Contract: $.string().notNull()
Meta:
- MetaClassOne:
description: "Name of the app"
count: 1
- MetaClassMany:
count: 2
- MetaClassMany:
count: 3
Methods:
initialize:
Body:
- $._environment: $.find(std:Environment).require()
Meta:
MetaClassOne:
description: "Method for initializing app"
count: 1
deploy:
Body:
- If: not $.getAttr(deployed, false)
Then:
- $._environment.reporter.report($this, 'Deploy started')
- $._environment.reporter.report($this, 'Deploy finished')
- $.setAttr(deployed, true)
The Bar
class has an instance of meta-class
MetaClassOne
attached. For this, the Meta
keyword is added to the Bar
class description and the
instance of the MetaClassOne
class is specified under it.
This instance's properties are description
and
count
.
There are three meta-objects attached to the name
property of the Bar
class. One of it is a
MetaclassOne
object and the other two are
MetaClassMany
objects. There can be more than one instance
of MetaClassMany
attached to a single entity since the
Cardinality
attribute of MetaClassMany
is set
to Many
.
The initialize
method of Bar
also has its
metadata.
To attach metadata to the package, add the Meta
keyword
to manifest.yaml
file.
Example:
Format: 1.0
Type: Application
FullName: io.murano.bar.Bar
Name: Bar
Description: |
Empty DescriptionAuthor: author
Tags: [bar]
Classes:
io.murano.bar.Bar: Bar.yaml
io.murano.bar.MetaClassOne: MetaClassOne.yaml
io.murano.bar.MetaClassMany: MetaClassMany.yaml
Supplier:
Name: Name
Description: Description
Summary: Summary
Meta:
io.murano.bar.MetaClassOne:
description: "Just an empty application with some metadata"
count: 1
Obtain metadata in runtime
Metadata can be accessed from MuranoPL using reflection capabilities and from Python code using existing YAQL mechanism.
The following example shows how applications can access attached metadata:
Namespaces:
=: io.murano.bar
std: io.murano
res: io.murano.resources
sys: io.murano.system
Name: Bar
Extends: std:Application
Meta:
MetaClassOne:
description: "Just an empty application class with some metadata"
Methods:
sampleAction:
Scope: Public
Body:
- $._environment.reporter.report($this, typeinfo($).meta.
where($ is MetaClassOne).single().description)
The sampleAction
method is added to the Bar
class definition. This makes use of metadata attached to the
Bar
class.
The information about the Bar
class is received by
calling the typeinfo
function. Then metadata is accessed
through the meta
property which returns the collection of
all meta attached to the property. Then it is checked that the meta is a
MetaClassOne
object to ensure that it has
description
. While executing the action, the phrase "Just
an empty application class with some metadata" is reported to a log.
Some advanced usages of MuranoPL reflection capabilities can be found in
the corresponding section of this reference.
By using metadata, an application can get information of any type attached to any object and use this information to change its own behavior. The most valuable use-cases of metadata can be:
- Providing information about capabilities of application and its parts
- Setting application requirements
Capabilities can include version of software, information for use in UI or CLI, permissions, and any other. Metadata can also be used in requirements as a part of the contract.
The following example demonstrates the possible use cases for the meta-classes:
Name: BlogApp
Meta:
m:SomeFeatureSupport:
support: true
Properties:
volumeName:
Contract: $.string().notNull()
Meta:
m:Deprecated:
text: "volumeName property is deprecated"
server:
Contract: $.class(srv:CoolServer).notNull().check(typeinfo($).meta.
where($ is m:SomeFeatureSupport and $.support = true).any())
Methods:
importantAction:
Scope: Public
Meta:
m:CallerMustBeAdmin
Note, that the classes in the example do not exist as of Murano Mitaka, and therefore the example is not a real working code.
The SomeFeatureSupport
meta-class with
support: true
says that the BlogApp
application supports some feature. The Deprecated
meta-class attached to the volumeName
property informs that
this property has a better alternative and it will not be used in the
future versions anymore. The CallerMustBeAdmin
meta-class
attached to the importantAction
method sets permission to
execute this method to the admin users only.
In the contract of the server
property it is specified
that the server application must be of the srv:CoolServer
class and must have the attached meta-object of the
m:SomeFeatureSupport
meta-class with the
support
property set to true
.