mirror of
https://gitlab.com/fabinfra/fabaccess/fabaccess-api.git
synced 2025-03-12 14:51:42 +01:00
Clean up structure a bit
This commit is contained in:
parent
f713df2221
commit
c10dc43f77
218
resource.capnp
218
resource.capnp
@ -9,6 +9,7 @@ using Value = import "/capnp/schema.capnp".Value;
|
||||
using User = import "user.capnp".User;
|
||||
using L10NString = import "utils.capnp".L10NString;
|
||||
using UUID = import "utils.capnp".UUID;
|
||||
using OID = import "utils.capnp".OID;
|
||||
|
||||
struct Resource {
|
||||
# BFFH's smallest unit of a physical or abstract "thing".
|
||||
@ -45,7 +46,7 @@ struct Resource {
|
||||
# in a free-form format.
|
||||
# Similar to the human-meaningful name this description can be translated.
|
||||
|
||||
notify @4 :Notify;
|
||||
notifiable @4 :Notifiable;
|
||||
# Readonly access to the state of a resource.
|
||||
# A resource can have "state". State are values attached to a resource that describe a specific
|
||||
# state that users or administrators want this resource to be in. Usually this state consists of
|
||||
@ -68,121 +69,17 @@ struct Resource {
|
||||
# least one `Claim` to remain.
|
||||
}
|
||||
|
||||
struct Map(Key, Value) {
|
||||
# A serialized key-value map represented as a list of (k,v) tuples.
|
||||
interface Notifiable {
|
||||
|
||||
entries @0 :List(Entry);
|
||||
|
||||
struct Entry {
|
||||
key @0 :Key;
|
||||
val @1 :Value;
|
||||
}
|
||||
}
|
||||
using State = Map(Text, Value);
|
||||
# Update state provided to a resource via a claim is represented as a Map of human-readable
|
||||
# identifiers to Cap'n Proto Values. These Values can be either primitive types such as Uint8,
|
||||
# Float64 or more complex types such as structs, lists, or enums.
|
||||
# The resulting state of a resource, which is the output of whatever internal logic the resource
|
||||
# implements, is also represented in this form, but the keys and also values may be different.
|
||||
#
|
||||
# Later on very common cases (use, register, return, etc.) can get shortcut functions in the Claim
|
||||
# interface that pre-emptively check permissions and ability (so you get the respective cap iff the
|
||||
# resource supports that update and if you're allowed to do that) but these functions only serve to
|
||||
# make the update more efficient than calling `update` with the string identifier and dynamic typed
|
||||
# value but do the exact same serverside as an `update` call would. This way we can make future
|
||||
# versions of the API more efficient and easier to use while not breaking compatibility with old
|
||||
# clients.
|
||||
#
|
||||
# TODO: This has the potential problem that a newer client can not distinguish between a server
|
||||
# using an old version of the API and a client simply not being allowed to call a specific shortcut
|
||||
# method because in both cases that cap will be a nullptr. Could be solved by making `Claim` a
|
||||
# struct and indicating which shortcut methods it knows of.
|
||||
# Not sure if this is a big problem, we optimize for old clients and up-to-date servers.
|
||||
#
|
||||
# TODO: We should provide a number of sensible implementations for common complex `Value` types such
|
||||
# as "colour", "temperature", etc. and define identifiers for common values.
|
||||
|
||||
interface Access {
|
||||
# Allow syncronous read access to a resource's output state. You're not given this capability
|
||||
# directly but instead Notify, Interest and Claim all extend it, allowing you to call these
|
||||
# methods on any of those.
|
||||
|
||||
readOutput @0 State;
|
||||
|
||||
# TODO: There should probably be a more efficient approach for reading state than "read *all*
|
||||
# state".
|
||||
}
|
||||
|
||||
interface Notify extends(Access) {
|
||||
# The Notify interface allows clients to be informed about state changes asyncronously. It is
|
||||
# mainly designed around the `register` function which allows a client to register a callback
|
||||
# that is called every time state changes happen to the resource in question.
|
||||
#
|
||||
# Notify are ephermal. If the connection to the server is lost all `Notify` from that client are
|
||||
# unregistered.
|
||||
|
||||
register @0 ( cb :Callback );
|
||||
# Register a given callback to be called on every state update. If this client already has a
|
||||
# callback registered for this resource the old callback is replaced.
|
||||
|
||||
unregister @1 ();
|
||||
# Unregister this callback
|
||||
|
||||
interface Callback {
|
||||
# This callback interface needs to be implemented on the client
|
||||
|
||||
newState @0 State;
|
||||
# A server will call newState() with the updated output state. However a server will only
|
||||
# allow one in-flight call, so as long as the previous call to newState() hasn't completed
|
||||
# the server will drop intermediary updates as to not overload a client.
|
||||
# Specifically, example timeline:
|
||||
# 1. Update A
|
||||
# 2. Server calls newState(A)
|
||||
# 3. Update B
|
||||
# 4. Update C
|
||||
# 5. Call to newState(A) completes
|
||||
# 6. Server calls newState(C)
|
||||
# So Update B was never sent to the client but the client will eventually always end up with
|
||||
# the latest state.
|
||||
|
||||
# TODO: There should probably be a more efficient approach here too, something along the
|
||||
# lines of server-side filtering.
|
||||
}
|
||||
}
|
||||
|
||||
interface Interestable {
|
||||
# "Interest" right now it tells BFFH that the client wants at least one `Claim` to remain.
|
||||
|
||||
register @0 ( cb :Callback ) -> ( handle :Handle );
|
||||
# Register a callback that BFFH will use to send notifications back to the client asyncronously.
|
||||
# This creates an "Interest" on this resource. Setting the callback to a `nullptr` will still
|
||||
# register an interest but the server will not be able to inform a client about an impeding
|
||||
# claim drop.
|
||||
|
||||
blocking @1 ();
|
||||
# As an alternative to the `register`/`Callback` system you can also call `blocking` which will
|
||||
# — as the name suggests — block until the last claim is being dropped. This will register an
|
||||
# ephermal Interest that can not survive a disconnect.
|
||||
|
||||
interface Callback {
|
||||
drop @0 ();
|
||||
# The last claim on the resource this Interest is registered is being dropped, invalidating
|
||||
# the Interest.
|
||||
}
|
||||
|
||||
interface Handle {
|
||||
# A Handle back to the server side Interest registered. Destroying this capability will also
|
||||
# inform the server and remove the Interest again.
|
||||
# TODO: `extends (Persistance)` so that clients can `save` this capability and thus make the
|
||||
# Interest survive disconnects.
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface Claimable {
|
||||
# Having this capability set (i.e. not be a `nullptr`) means the user has at least writeable
|
||||
# access to a resource and the resource is claimable (n > 0).
|
||||
# access to a resource
|
||||
|
||||
claim @0 () -> ClaimResponse;
|
||||
# Assert a claim on a resource.
|
||||
@ -214,6 +111,113 @@ interface Claimable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Map(Key, Value) {
|
||||
# A serialized key-value map represented as a list of (k,v) tuples.
|
||||
|
||||
entries @0 :List(Entry);
|
||||
|
||||
struct Entry {
|
||||
key @0 :Key;
|
||||
val @1 :Value;
|
||||
}
|
||||
}
|
||||
|
||||
using State = Map(Oid, Value);
|
||||
# Update state provided to a resource via a claim is represented as a Map of human-readable
|
||||
# identifiers to Cap'n Proto Values. These Values can be either primitive types such as Uint8,
|
||||
# Float64 or more complex types such as structs, lists, or enums.
|
||||
# The resulting state of a resource, which is the output of whatever internal logic the resource
|
||||
# implements, is also represented in this form, but the keys and also values may be different.
|
||||
#
|
||||
# Later on very common cases (use, register, return, etc.) can get shortcut functions in the Claim
|
||||
# interface that pre-emptively check permissions and ability (so you get the respective cap iff the
|
||||
# resource supports that update and if you're allowed to do that) but these functions only serve to
|
||||
# make the update more efficient than calling `update` with the string identifier and dynamic typed
|
||||
# value but do the exact same serverside as an `update` call would. This way we can make future
|
||||
# versions of the API more efficient and easier to use while not breaking compatibility with old
|
||||
# clients.
|
||||
#
|
||||
# TODO: This has the potential problem that a newer client can not distinguish between a server
|
||||
# using an old version of the API and a client simply not being allowed to call a specific shortcut
|
||||
# method because in both cases that cap will be a nullptr. Could be solved by making `Claim` a
|
||||
# struct and indicating which shortcut methods it knows of.
|
||||
# Not sure if this is a big problem, we optimize for old clients and up-to-date servers.
|
||||
#
|
||||
# TODO: We should provide a number of sensible implementations for common complex `Value` types such
|
||||
# as "colour", "temperature", etc. and define identifiers for common values.
|
||||
|
||||
|
||||
interface Notify {
|
||||
# The Notify interface allows clients to be informed about state changes asyncronously. It is
|
||||
# mainly designed around the `register` function which allows a client to register a callback
|
||||
# that is called every time state changes happen to the resource in question. It also allows
|
||||
# syncronous read access to a resource's output state.
|
||||
# Notify are ephermal. If the connection to the server is lost all `Notify` from that client are
|
||||
# unregistered.
|
||||
|
||||
readOutput @0 State;
|
||||
# TODO: There should probably be a more efficient approach for reading state than "read *all*
|
||||
# state".
|
||||
|
||||
setNotify @1 ( cb :Callback );
|
||||
# Register a given callback to be called on every state update. If this client already has a
|
||||
# callback registered for this resource the old callback is replaced.
|
||||
|
||||
delNotify @2 () -> :Bool;
|
||||
# Unregister a registered callback
|
||||
|
||||
interface Callback {
|
||||
# This callback interface needs to be implemented on the client
|
||||
|
||||
newState @0 State;
|
||||
# A server will call newState() with the updated output state. However a server will only
|
||||
# allow one in-flight call, so as long as the previous call to newState() hasn't completed
|
||||
# the server will drop intermediary updates as to not overload a client.
|
||||
# Specifically, example timeline:
|
||||
# 1. Update A
|
||||
# 2. Server calls newState(A)
|
||||
# 3. Update B
|
||||
# 4. Update C
|
||||
# 5. Call to newState(A) completes
|
||||
# 6. Server calls newState(C)
|
||||
# So Update B was never sent to the client but the client will eventually always end up with
|
||||
# the latest state.
|
||||
|
||||
# TODO: There should probably be a more efficient approach here too, something along the
|
||||
# lines of server-side filtering.
|
||||
}
|
||||
}
|
||||
|
||||
interface Interest extends(Notify) {
|
||||
# "Interest" right now it tells BFFH that the client wants at least one `Claim` to remain.
|
||||
|
||||
register @0 ( cb :Callback ) -> ( handle :Handle );
|
||||
# Register a callback that BFFH will use to send notifications back to the client asyncronously.
|
||||
# This creates an "Interest" on this resource. Setting the callback to a `nullptr` will still
|
||||
# register an interest but the server will not be able to inform a client about an impeding
|
||||
# claim drop.
|
||||
|
||||
blocking @1 ();
|
||||
# As an alternative to the `register`/`Callback` system you can also call `blocking` which will
|
||||
# — as the name suggests — block until the last claim is being dropped. This will register an
|
||||
# ephermal Interest that can not survive a disconnect.
|
||||
|
||||
interface Callback {
|
||||
drop @0 ();
|
||||
# The last claim on the resource this Interest is registered is being dropped, invalidating
|
||||
# the Interest.
|
||||
}
|
||||
|
||||
interface Handle {
|
||||
# A Handle back to the server side Interest registered. Destroying this capability will also
|
||||
# inform the server and remove the Interest again.
|
||||
# TODO: `extends (Persistance)` so that clients can `save` this capability and thus make the
|
||||
# Interest survive disconnects.
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
interface Claim extends(Access) {
|
||||
# TODO: extend Persistance. Claims and Interests need to be able to survive a connection loss,
|
||||
# which is exactly what `SturdyRef`/Persistance are designed to provide. The Persistance
|
||||
|
13
utils.capnp
13
utils.capnp
@ -52,3 +52,16 @@ struct UUID {
|
||||
# upper 8 bytes of the uuid, containing the MSB.
|
||||
}
|
||||
|
||||
struct OID {
|
||||
bytes @0 :Data
|
||||
# The OID, encoded as a sequence of varints. In this encoding the lower 7 bits of each octet
|
||||
# contain data bits while the MSB indicates if the *following* octet is still part of this edge.
|
||||
# It is the same encoding UTF-8 uses. To decode you simply collect octets until you find an
|
||||
# octet <128 and then concat the data bits of all the octets you've accumulated, including the
|
||||
# current one. This gives you the value of one node. Continue until you've exhausted the
|
||||
# available data.
|
||||
# This is a rather efficient encoding since almost all edges of the OID tree are smaller than
|
||||
# 128 and thus encode into one byte.
|
||||
# X.208 does *not* limit the size of nodes! However, a reasonable size limit is 128 bit per
|
||||
# node, which is the size of the UUID nodes in the `2.25` subtree.
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user