Skip to content

Instantly share code, notes, and snippets.

@kevinswiber
Last active July 12, 2018 11:16
Show Gist options
  • Save kevinswiber/8cb485290a3b8f2f7efd to your computer and use it in GitHub Desktop.
Save kevinswiber/8cb485290a3b8f2f7efd to your computer and use it in GitHub Desktop.
Siren in Protobuffers v3.
{
"class": [ "order" ],
"properties": {
"orderNumber": 42,
"itemCount": 3,
"status": "pending"
},
"entities": [
{
"class": [ "items", "collection" ],
"rel": [ "http://x.io/rels/order-items" ],
"href": "http://api.x.io/orders/42/items"
},
{
"class": [ "info", "customer" ],
"rel": [ "http://x.io/rels/customer" ],
"properties": {
"customerId": "pj123",
"name": "Peter Joseph"
},
"links": [
{ "rel": [ "self" ], "href": "http://api.x.io/customers/pj123" }
]
}
],
"actions": [
{
"name": "add-item",
"title": "Add Item",
"method": "POST",
"href": "http://api.x.io/orders/42/items",
"type": "application/x-www-form-urlencoded",
"fields": [
{ "name": "orderNumber", "type": "hidden", "value": "42" },
{ "name": "productCode", "type": "text" },
{ "name": "quantity", "type": "number" }
]
}
],
"links": [
{ "rel": [ "self" ], "href": "http://api.x.io/orders/42" },
{ "rel": [ "previous" ], "href": "http://api.x.io/orders/41" },
{ "rel": [ "next" ], "href": "http://api.x.io/orders/43" }
]
}
syntax = "proto3";
package siren.example;
message Order {
message Properties {
int32 order_number = 1;
int32 order_count = 2;
string status = 3;
}
message CustomerSubEntity {
message Properties {
string customer_id = 1;
string name = 2;
}
}
}
syntax = "proto3";
package siren;
// http://schema.sirenspec.org/protobuf/siren.Entity
message Entity {
repeated string class = 1;
bytes properties = 2;
repeated AnySubEntity entities = 3;
repeated Action actions = 4;
repeated Link links = 5;
}
// http://schema.sirenspec.org/protobuf/siren.AnySubEntity
message AnySubEntity {
Type type = 1;
bytes value = 2;
// http://schema.sirenspec.org/protobuf/siren.AnySubEntity.Type
enum Type {
EMBEDDED = 0;
LINKED = 1;
}
}
// http://schema.sirenspec.org/protobuf/siren.Link
message Link {
repeated string class = 1;
repeated string rel = 2;
string href = 3;
string title = 4;
string type = 5;
}
// http://schema.sirenspec.org/protobuf/siren.LinkedEntity
message LinkedEntity {
repeated string class = 1;
repeated string rel = 2;
string href = 3;
string title = 4;
}
// http://schema.sirenspec.org/protobuf/siren.Action
message Action {
repeated string class = 1;
string name = 2;
string title = 3;
string method = 4;
string href = 5;
string type = 6;
repeated Field fields = 7;
}
// http://schema.sirenspec.org/protobuf/siren.Field
message Field {
string name = 1;
Type type = 2;
bytes value = 3;
// http://schema.sirenspec.org/protobuf/siren.Field.Type
enum Type {
TEXT = 0;
HIDDEN = 1;
SEARCH = 2;
TEL = 3;
URL = 4;
EMAIL = 5;
PASSWORD = 6;
DATETIME = 7;
DATE = 8;
MONTH = 9;
WEEK = 10;
TIME = 11;
DATETIME_LOCAL = 12;
NUMBER = 13;
RANGE = 14;
COLOR = 15;
CHECKBOX = 16;
RADIO = 17;
FILE = 18;
}
}
var zlib = require('zlib');
var ProtoBuf = require('protobufjs');
var Enum = ProtoBuf.Reflect.Enum;
var builder = ProtoBuf.newBuilder({ convertFieldsToCamelCase: true });
ProtoBuf.loadProtoFile('./order.proto', builder);
ProtoBuf.loadProtoFile('./siren.proto', builder);
var Siren = builder.build('siren');
var SirenExample = builder.build('siren.example');
var json = require('./order.json');
var Order = SirenExample.Order;
var order = new Siren.Entity({
class: ['order'],
properties: new Order.Properties({
orderNumber: 42,
orderCount: 3,
status: 'pending'
}).toBuffer(),
entities: [
new Siren.AnySubEntity({
type: 'EMBEDDED',
value: new Siren.Entity({
class: ['customer', 'info'],
properties: new Order.CustomerSubEntity.Properties({
customerId: 'pj123',
name: 'Peter Joseph'
}).toBuffer(),
links: [
new Siren.Link({
rel: ['self'],
href: 'http://api.x.io/customers/pj123'
})
]
}).toBuffer()
}),
new Siren.AnySubEntity({
type: 'LINKED',
value: new Siren.LinkedEntity({
class: ['items', 'collection'],
rel: ['http://x.io/rels/order-items'],
href: 'http://api.x.io/orders/42/items'
}).toBuffer()
})
],
actions: [
new Siren.Action({
name: 'add-item',
method: 'POST',
href: 'http://api.x.io/orders/42/items',
type: 'application/x-www-form-urlencoded',
title: 'Add Item',
fields: [
new Siren.Field({
name: 'orderNumber',
type: 'HIDDEN',
value: new Buffer('42')
}),
new Siren.Field({
name: 'productCode',
type: 'TEXT'
}),
new Siren.Field({
name: 'quantity',
type: 'NUMBER'
})
]
})
],
links: [
new Siren.Link({
rel: ['self'],
href: 'http://api.x.io/orders/42'
}),
new Siren.Link({
rel: ['previous'],
href: 'http://api.x.io/orders/41'
}),
new Siren.Link({
rel: ['next'],
href: 'http://api.x.io/orders/43'
})
]
});
var jsonBuffer = new Buffer(JSON.stringify(json));
var protoBuffer = order.toBuffer();
var stringified = JSON.stringify(json);
console.log('json:', jsonBuffer.length);
console.log('proto:', protoBuffer.length);
console.log('json-gzip:', zlib.gzipSync(jsonBuffer).length);
console.log('proto-gzip:', zlib.gzipSync(protoBuffer).length);
var decoded = Siren.Entity.decode(protoBuffer);
if (decoded.class.indexOf('order') > -1) {
var fieldValue = decoded.actions[0].fields[0].value;
console.log(fieldValue.toString('utf8'));
decoded.entities.forEach(function(subEntity) {
if (subEntity.type === Siren.AnySubEntity.Type.EMBEDDED) {
var e = Siren.Entity.decode(subEntity.value);
if (e.class.indexOf('customer') > -1 && e.class.indexOf('info') > -1) {
var props = SirenExample.Order.CustomerSubEntity.Properties.decode(e.properties);
console.log(props.name);
}
}
});
}
var fs = require('fs');
var writeStream = fs.createWriteStream('./Protobuffun/Protobuffun/bin/Debug/protobuf.dat');
writeStream.end(protoBuffer);
writeStream.on('finish', function() {
var file = fs.readFileSync('./Protobuffun/Protobuffun/bin/Debug/protobuf.dat');
var root = Siren.Entity.decode(file);
if (root.class.indexOf('order') > -1) {
var props = SirenExample.Order.Properties.decode(root.properties);
console.log(props);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment