Skip to content

Latest commit

 

History

History
255 lines (184 loc) · 10.3 KB

buf.md

File metadata and controls

255 lines (184 loc) · 10.3 KB

Hiber API

This is the protobuf specification of the Hiber GRPC API.

Hiber also offers a limited, read-only rest API for convenience. This is based on the protobuf object definitions, but is (for now) mainly intended as a compatibility option to fetch messages and message events.

Technical tools

Protocol Buffers

Protobuf is Google's replacement for JSON. Due to its use of binary buffers, it's a more efficient way to encode data than text based protocols (SOAP, JSON), especially in encoding integers. Fields are encoded through a 'tag', which are integers, which improves the density even more.

There are protobuf libraries in most used languages, such as Java, JavaScript, Golang, C#, C, and C++. This makes adoption of the protocol really easy. Because server and client model are generated from the same source, there can be no confusion about the model.

For more information, take a look at:

GRPC

GRPC is a way of defining rpc services in the protobuf format. GRPC is based on HTTP/2 and formalized the networking, status responses and streaming implementation for API calls. Because the specification generates both the client and server code, the specification is valid by default and API documentation is in one place.

For more information, take a look at:

GRPC Web

Since GRPC is based on HTTP/2, it is not (yet) compatible with web browsers. In order to support web-based applications, we are running the grpc-web proxy. This allows GRPC calls from the browser, using the typescript code generated by the GRPC Web generator.

For more information, take a look at:

(Note that Google has recently release a beta version of their implementation of GRPC Web. We are looking into switching to that implementation in the future.)

Usage

First of all, have a look through the .proto files in this repository. You will find that they are very easy to read, and we have tried to name everything as clearly as possible, so as to avoid unnecessary extra documentation.

To use the proto files, clone this repository and generate code in your preferred language:

Simple example

For example:

service CurrentUserService {
  rpc CurrentUser (CurrentUserRequest) returns (CurrentUser);
}

message CurrentUser {
  string id = 1;
  string email = 2;
  string name = 3;
  repeated string organizations = 4;
}

message CurrentUserRequest {
}

(Note that this is a reduced, and possibly outdated, example and you should consult the actual proto file for the actual, up to date definition.)

This defines a service that has one rpc call, which expects an empty request object and returns a CurrentUser object.

For API client purposes, you can mostly ignore the numbers and see this as a data class. Note the repeated keyword for the organizations field. This indicates that the field is of an array type (like string[] organizations).

List requests, selection objects and pagination

For many of our list calls, which can list items like modems, users, etc., we use a selection object to simplify the filtering of the data.

Since the same selection criteria can be used in different calls, selection objects are a simple way to extract this duplication out of the definitions.

For example:

service WebhookService {
  rpc List (ListRequest) returns (ListRequest.Response);
}

message Webhook {
  int64 id = 1;
  string organization = 2;
  string description = 3;
  repeated api.tag.Tag tags = 4;
}

message WebhookSelection {
  string description = 1;
  string url = 2;
  Filter.Webhooks webhooks = 3;
  api.tag.TagSelection tags = 4;
}

message ListRequest {
  message Response {
    repeated Webhook webhooks = 1;
    ListRequest request = 2;
    Pagination.Result pagination = 3;
  }

  string organization = 1;
  WebhookSelection selection = 2;
  Pagination pagination = 3;
}

message Pagination {
  message Result {
    int32 size = 1;
    int32 page = 2;
    int32 total = 3;
    int32 totalPages = 4;
    Pagination previous = 6;
    Pagination next = 7;
  }
  int32 size = 1;
  int32 page = 2;
}

(Note, again, that this is a reduced, and possibly outdated, example.)

This code actually contains a few interesting things. First, nested messages. These are used to denoted linked function, i.e. the Response message inside the ListRequest.

The WebhookSelection message contains the filters that can be applied to webhooks. Note that the WebhookSelection message contains a TagSelection as well. This is another selection object, used to filter webhooks by tags.

The Pagination message is used to paginate the result. It's a simple page size and page number combination (where 0 is the first page). The Pagination.Result message also contains size and page, but has corrected the size field to a maximum value (or default value if it was 0). For convenience, it also contains a previous and next Pagination object, for easy pagination.

Note that ListRequest.Response contains a ListRequest, which is a corrected version of the request. Selection object may have certain defaults or value limits, which will be visible in the corrected request.

Default values

Everything in protobuf has a default value, which has a few interesting complications.

For example:

enum ContentType {
  DEFAULT = 0;
  JSON = 1;
  PROTO = 2;
}

Since protobuf has a default value for all objects, enum takes the first value as default value. To avoid the situation where omitting the field would still set a value, we've introduced the DEFAULT value.

message UpdateBoolean {
  bool updated = 1;
  bool value = 2;
}

Since false is the default value for a boolean, we need to distinguish between an omitted value and setting the value to false in an update object. For this, we've added an UpdateBoolean message (and UpdateClearableString and UpdateZeroableInt for string that can be set to empty, and ints that can be set to 0, respectively)

Usage is pretty simple, set a value in the object and set updated to true.

Versioning

Protobuf/GRPC has a built-in versioning mechanism by using numbered fields. This essentially means that a field would change in a non-backwards compatible way, it would either be removed and added it with a new number, or deprecated and a new field with a new name would be added. In case of removal, the server is able to detect whether an old version is used and convert the field.

For more information and a small number of examples, see the examples of versioning methods in the examples repo: Versioning example

Developing on our API

For Hiber customer development, we have set up endpoints under acc.env.hiber.cloud:

Notes:

  • This can be considered a stable production-like environment.
  • Technically, you might be able to connect a normal GRPC client to the GRPC Web API, but this is not recommended.

API Tokens

The Hiber API requires a token for all GRPC calls, using a metadata field called authorization with value bearer $YOUR_TOKEN_HERE.

To get a token for our API, go to the web application at acc.env.hiber.cloud and log in. Once you are linked to an organization (this process is defined in the api, but not implemented in the web interface yet) you can create a token (using another token) on the API.

(Note: for pilot customers, a number of tokens can be generated in advance, so this process is not necessary.)

Using our API in production

For actual modems and modem messages, use hiber.cloud:

  • hiber.cloud: The Hiber web application
  • api.hiber.cloud (or grpc.hiber.cloud): The GRPC API, accessible over https on port 443. (Note that opening this url in your browser will not work, since it requires HTTP2.)

Examples

We've provided a collection of minimal examples in different languages in our examples repository.

For initial development, to simplify client experimentation with our GRPC API, and as a server used by our examples, we've created a simple mock server that implements our API. It will return example data, and does not do any error handling aside from requiring that a token is present.

For simple usage, you can simply pull the docker image at hiberglobal/mock-server (most of the examples do this automatically when you run them). You can see the source code get some more information at the mock-server folder in the examples repository.