# Specification

ObjectAPI is an interface specification to describe the boundaries of your software modules in an object format.

ObjectAPI sees the world as a set of interface modules, which together form an interface layer. Each module is encapsulated in one ObjectAPI document.

The specification describes the details how to write such a ObjectAPI document.

Version 0.1.0 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

The ObjectAPI Specification is copyrighted by ApiGear UG, all rights reserved.

# Introduction

The ObjectAPI specification is a project used to describe and document object oriented APIs across languages and technologies.

The ObjectAPI specification defines a set of files required to describe such an API. These files can then be used to create utilities, such as documentation, integration and/or testing tools.

The ObjectAPI Specification is often used to describe the interface between software modules or inter-process communication (IPC) in distributed systems built using a object oriented programming API. The ObjectAPI recommends to split APIs into smaller modules with loose coupling.

The documents describe an API module and its interfaces and data structures.

# Revision History

  • Version 0.1
    • Initial ObjectAPI specification

# Definitions

  • A system: A system is a collection of modules, which describe a coherent set of APIs on the same layer.
  • A module: A module describes a name spaced collection of API symbols, such as interfaces, structures, enumerations. There exists one module per file.
  • An interface is a named interface description of an object with properties, operations and signals.
  • A structure is a data type with fields describing the structure
  • A enumeration is a enumerated integer or string value

# Basics

InterfaceAPI is defined in terms of modules inside a layer. Each module is captured inside a InterfaceAPI module document. InterfaceAPI is a YAML based specification with a linked schema document for validation.

# Format

The files describing the InterfaceAPI in accordance to APIGear InterfaceAPI specification are described in JSON syntax and must conform to the JSON standard.

While the the API is described in JSON, other formats (YAML) can be used as input formats.

Unless otherwise noted all file names in this specification are case sensitive.

# File Structure

There exists a set of files where each file represents an API module. Ideally, the file name matches the module name. Other files can be added which contain meta information for the API modules. They allow to inject additional information which is not relevant or available during API definitions.

  • *.module.yaml | *.module.json - InterfaceAPI document
  • *.module.meta.yaml | *.module.meta.json - InterfaceAPI meta information injected into the relevant APIs.

So if a module is name org.example the InterfaceAPI document should be called org.example.module.yaml

# Data Types

In the InterfaceAPI specification data types are used in many locations. State, Method return types and parameters, signal parameters or structures.

Data fields are added at the same level to describe the data name and type. For example for the interface properties, these are:

properties:
  - name: count
    type: int

The general types available to InterfaceAPI are:

  • Primitives (bool, int, float, string)
  • Containers (arrays)
  • Complex (structures, enumerations)

# Primitives

Data types can be re-presented in different forms in different programming languages. They all need to be convertible to JSON data types on request.

  • bool
  • int
  • float
  • string

# Arrays

An array is an index based list of primitive or complex data types. Further nesting of containers are not supported but can be achieved using structs as array items. A data type is converted into a container by setting the type to array and specifying the containing type in the items key.

For example an integer array can be noted like this:

properties:
  - name: names
    type: array
    items: string

If an array does contain a symbol as containing type, then the symbol name can be used in the items key.

properties:
  - name: messages
    type: array
    items: { ref: Message }

Primitive types are always lowercase and symbols are always uppercase. The cases might change in the target language.

# Complex Types

A symbol is a named element inside a module. This can be either an interface, struct or enum/flag symbol.

structs:
  - name: Message

Inside the same module the type can be referenced by the name of the symbol using a ref. This holds true for all symbols.

properties:
  - name: msg1
    type: { ref: Message }
  - name: msg2
    type: array
    items: struct
    symbol: { ref: Message }

Outside the module, the module itself needs to be imported and the type needs to be used with its fully qualified name

imports:
  - org.example

interfaces:
  - name: Interface1
    properties:
      - name: msg1
        type: { ref: org.example.Message }
  • org.example.Message - external symbol

Note: Not every language profile does support importing.

# Rich Text Formatting

Throughout the specification description support the markdown syntax.

# API Modules

# Module Namespaces

A module is a namespace for InterfaceAPI symbols like interfaces, structures and enumerations. The module bundles these symbols together in one namespace.

A module is identified by its name and version. The module name should be typically lowercase and words separated by ., like a reverse URI notation org.example.

A module can have an additional info block to describe in more detailed the module information.

Typically a module consist of the apigear.interfaces version declaration, the module name and version, the list of interfaces, structures and signals.

The version number must be written as a string, otherwise it wil be converted to a numeric value (e.g. 1 for 1.0).

Only interfaceapi, name, version are mandatory. The other identifiers are optional.

schema: apigear.module/1.0
name: org.example
version: "1.0"
interfaces:
structs:
enums:

# Interfaces

An interface is the main instance to describe your software boundary using interface terms. The interface consist of state, operation and signals. The state is typically describe a a set of properties of the interface and operations modify the interface state. Signals notify the user of changes of the interface.

The interface itself is identified by its name inside a module.

schema: apigear.module/1.0
name: "org.example"
version: "1.0"

interfaces:
  - name: MyInterface

# Object state

Each property has a name and a type as also description and additional meta data.

# ...
interfaces:
  - name: MyInterface
    properties:
      - name: value
        type: int

# Operation

A operation defines the interaction with the interface. It is a collection of operations which can either manipulate the properties or return data.

Ideally you design your operations in a way that they can be divided into commands and queries. A command is an operation which does something on the interface and a query collects data from the interface and returns it to the user.

# ...
interfaces:
  - name: MyInterface
    operations:
      - name: command
        description: A command does not have a return type
      - name: query
        type: string
        description: A query returns data

Operation can have parameter arguments

# ...
interfaces:
  - name: MyInterface
    operations:
      - name: command
        params:
          - name: step
            type: int

The arguments do parameterize the operation.

# Signals

A signal allows the interface to notify the outside world about events happening, e.g. triggered by others. A signal is like an operation, but never defines a type.

# ...
interfaces:
  - name: MyInterface
    signals:
      - name: error
        params:
          - name: code
            type: int

# Data Structures

A structure represents a data structure which can be used for communication. The structure consist of a name and a set of data fields. Each field again has a name and a type information.

# ...
types:
  - name: Message
    fields:
      - name: msg
        type: string

A data structure does not contain any operations or signals. A data structure is typically used as a type for properties, operation parameters and others.

# ...
interfaces:
  - name: MessageSender
    properties:
      - name: lastMessage
        type: { ref: Message }
    operations:
      - name: send
        params:
          - name: msg
            type: { ref: Message }

Data structure can be identified just be identified its name.

Data structures can be nested by using the type name of the nested type. In some programming languages care needs to be taken by the order of declaration.

# Enumerations

Enumerations and Flags are value types, which allow a user to use a defined number of choices to identify a value.

enums:
  - name: Status
    members:
      - name: None
        value: 0
      - name: Loading
        value: 1
      - name: Ready
        value: 2
      - name: Error
        value: 3

The values are optional and when missing the value is counted incrementally from 0 on upwards.

enums:
  - name: Status
    members:
      - name: None
      - name: Loading
      - name: Ready
      - name: Error

An enumeration is also a symbol and can be used by its name to identify its type.

# ...
interfaces:
  - name: MyInterface
    properties:
      - name: status
        type: { ref: Status } # references the Status enumeration

# Advanced

A temperature sensor tutorial using ApiGear and Raspberry Pi

# Document information

The info section allows user to add information related to the current document.

info:
  license: <license-identifier>

# Meta information

Sometimes it is required to add additional information, which is not part of the InterfaceAPI specification. For this the meta tag can be used.

interfaces:
  - name: Tuner
    meta:
      singleton: true
      config: { port: 1024 }

As the information is not part of the specification the applied code generator must have an understanding of the data. For example a C++ code generator could create a singleton type from the interface declaration.

# Compact Writing

YAML allows a compact format for single line information. This allows us to shorten API definitions considerable. For example this API

structs:
  - name: Error
    fields:
      - name: message, 
        type: string
      - name: code
        type: int

Can be written in a short format like this:

structs:
  - name: Error
    fields:
      - { name: message, type: string }
      - { name: code, type: int }