Skip to content

Commit

Permalink
Merge pull request #164 from resgateio/feature/gh-163-static-resource…
Browse files Browse the repository at this point in the history
…-type

Feature/gh 163 static resource type
  • Loading branch information
jirenius committed Jun 10, 2020
2 parents 0b333c8 + 0a092cf commit a451ddc
Show file tree
Hide file tree
Showing 20 changed files with 547 additions and 90 deletions.
63 changes: 54 additions & 9 deletions docs/res-client-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,28 +56,37 @@ A resource that is referred to with a non-soft [resource reference](res-protocol
## Resource set
Any request or event resulting in new subscriptions will contain a set of resources that contains any subscribed resource previously not subscribed by the client.

The set is grouped by type, `models`, `collections`, and `errors`. Each group is represented by a key/value object where the key is the [resource ID](res-protocol.md#resource-ids), and the value is the [model](res-protocol.md#models), [collection](res-protocol.md#collections), or [error](#error-object).
The set is grouped by type, `models`, `collections`, `statics`, and `errors`. Each group is represented by a key/value object where the key is the [resource ID](res-protocol.md#resource-ids), and the value is the [model](res-protocol.md#models), [collection](res-protocol.md#collections), [static](res-protocol.md#statics), or [error](#error-object).

**Example**
```json
{
"models": {
"messageService.message.1": {
"id": 1,
"msg": "foo"
},
"messageService.message.2": {
"id": 2,
"msg": "bar"
"messageService.overview?start=0&limit=3": {
"total": 123,
"lastSent": 1136239445,
"messages": { "rid": "messageService.messages?start=0&limit=3" }
}
},
"collections": {
"messageService.messages": [
"messageService.messages?start=0&limit=3": [
{ "rid": "messageService.message.1" },
{ "rid": "messageService.message.2" },
{ "rid": "messageService.message.3" }
]
},
"statics": {
"messageService.message.1": {
"id": 1,
"msg": "Application started.",
"data": null
},
"messageService.message.2": {
"id": 2,
"msg": "Email sent to {recipient}.",
"data": { "recipient": "info@example.com" }
}
},
"errors": {
"messageService.message.3": {
"code": "system.notFound",
Expand Down Expand Up @@ -146,6 +155,7 @@ Code | Message | Meaning
`system.noSubscription` | No subscription | The resource has no direct subscription
`system.invalidRequest` | Invalid request | Invalid request
`system.unsupportedProtocol` | Unsupported protocol | RES protocol version is not supported
`system.unsupportedFeature` | Unsupported feature | Feature requires a client supporting a higher RES protocol version


# Requests
Expand Down Expand Up @@ -224,6 +234,10 @@ May be omitted if no new models were subscribed.
[Resource set](#resource-set) collections.
May be omitted if no new collections were subscribed.

**statics**
[Resource set](#resource-set) statics.
May be omitted if no new statics were subscribed.

**errors**
[Resource set](#resource-set) errors.
May be omitted if no subscribed resources encountered errors.
Expand Down Expand Up @@ -280,6 +294,10 @@ May be omitted if no new models were retrieved.
[Resource set](#resource-set) collections.
May be omitted if no new collections were retrieved.

**statics**
[Resource set](#resource-set) statics.
May be omitted if no new statics were retrieved.

**errors**
[Resource set](#resource-set) errors.
May be omitted if no retrieved resources encountered errors.
Expand Down Expand Up @@ -324,6 +342,11 @@ MUST be omitted if **payload** is set.
May be omitted if no new collections were subscribed.
MUST be omitted if **payload** is set.

**statics**
[Resource set](#resource-set) statics.
May be omitted if no new statics were subscribed.
MUST be omitted if **payload** is set.

**errors**
[Resource set](#resource-set) errors.
May be omitted if no subscribed resources encountered errors.
Expand Down Expand Up @@ -366,6 +389,16 @@ MUST be omitted if **payload** is set.
May be omitted if no new collections were subscribed.
MUST be omitted if **payload** is set.

**statics**
[Resource set](#resource-set) statics.
May be omitted if no new statics were subscribed.
MUST be omitted if **payload** is set.

**errors**
[Resource set](#resource-set) errors.
May be omitted if no subscribed resources encountered errors.
MUST be omitted if **payload** is set.

### Error
An error response will be sent if the method couldn't be called, or if the authentication failed.

Expand Down Expand Up @@ -397,6 +430,10 @@ May be omitted if no new models were subscribed.
[Resource set](#resource-set) collections.
May be omitted if no new collections were subscribed.

**statics**
[Resource set](#resource-set) statics.
May be omitted if no new statics were subscribed.

**errors**
[Resource set](#resource-set) errors.
May be omitted if no subscribed resources encountered errors.
Expand Down Expand Up @@ -451,6 +488,10 @@ May be omitted if no new models were subscribed.
[Resource set](#resource-set) collections.
May be omitted if no new collections were subscribed.

**statics**
[Resource set](#resource-set) statics.
May be omitted if no new statics were subscribed.

**errors**
[Resource set](#resource-set) errors.
May be omitted if no subscribed resources encountered errors.
Expand Down Expand Up @@ -500,6 +541,10 @@ May be omitted if no new models were subscribed.
[Resource set](#resource-set) collections.
May be omitted if no new collections were subscribed.

**statics**
[Resource set](#resource-set) statics.
May be omitted if no new statics were subscribed.

**errors**
[Resource set](#resource-set) errors.
May be omitted if no subscribed resources encountered errors.
Expand Down
16 changes: 15 additions & 1 deletion docs/res-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* [Resource IDs](#resource-ids)
* [Models](#models)
* [Collections](#collections)
* [Statics](#statics)
* [Values](#values)
* [Resource references](#resource-references)
* [Messaging system](#messaging-system)
Expand All @@ -34,7 +35,7 @@ This document gives an overview of the protocol and its features, and describes

## Resources

RES protocol is built around a concept of resources. A resource may be either be a [*model*](#models) or a [*collection*](#collections). Each resource (model or collection) is identified by a unique [*resource ID*](#resource-ids), also called *rid* for short.
RES protocol is built around a concept of resources. A resource may be either be a [*model*](#models), a [*collection*](#collections), or a [*static*](#statics). Each resource (model, collection, or static) is identified by a unique [*resource ID*](#resource-ids), also called *rid* for short.

## Resource IDs
A *resource ID* is a string that consist of a *resource name* and an optional *query*.
Expand Down Expand Up @@ -77,6 +78,19 @@ A collection is an ordered list of [values](#values) represented by a JSON array
[ "admin", "tester", "developer" ]
```

## Statics

A static resource is immutable arbitrary data represented by any JSON value.

**Example**
```json
{
"timestamp": 1136239445,
"log": "Email sent to {to}.",
"params": { "to": "info@example.com" }
}
```

## Values

A value is either a *primitive* or a [resource reference](#resource-references).
Expand Down
29 changes: 19 additions & 10 deletions docs/res-service-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,15 @@ MUST be a string.

**model**
An object containing the named properties and [values](res-protocol.md#values) of the model.
MUST be omitted if *collection* is provided.
MUST be omitted if *collection* or *static* is provided.

**collection**
An ordered array containing the [values](res-protocol.md#values) of the collection.
MUST be omitted if *model* is provided.
MUST be omitted if *model* or *static* is provided.

**static**
An arbitrary JSON value.
MUST be omitted if *model* or *collection* is provided.

**query**
Normalized query without the question mark separator.
Expand Down Expand Up @@ -328,7 +332,7 @@ A set request is used to update or delete a model's properties.
**Parameters**
The parameters SHOULD be a key/value object describing the properties to be changed. Each property should have a new [value](res-protocol.md#values) or a [delete action](#delete-action). Unchanged properties SHOULD NOT be included.
If any of the model properties are changed, a [model change event](#model-change-event) MUST be sent prior to sending the response.
MUST NOT be sent on [collections](res-protocol.md#collections).
SHOULD NOT be sent on [collections](res-protocol.md#collections) or [statics](res-protocol.md#statics).

## New call request

Expand Down Expand Up @@ -374,7 +378,7 @@ When a resource is modified, the service MUST send the defined events that descr
`event.<resourceName>.change`

Change events are sent when a [model](res-protocol.md#models)'s properties has been changed.
MUST NOT be sent on [collections](res-protocol.md#collections).
MUST NOT be sent on other resource types.
The event payload has the following parameter:

**values**
Expand Down Expand Up @@ -405,7 +409,7 @@ A delete action is a JSON object used when a property has been deleted from a mo

Add events are sent when a value is added to a [collection](res-protocol.md#collections).
Any previous value at the same index or higher will implicitly be shifted one step to a higher index.
MUST NOT be sent on [models](res-protocol.md#models).
MUST NOT be sent on other resource types.
The event payload has the following parameters:

**value**
Expand All @@ -430,7 +434,7 @@ MUST be a number that is zero or greater and less than or equal to the length of

Remove events are sent when a value is removed from a [collection](res-protocol.md#collections).
Any previous value at a higher index will implicitly be shifted one step to a lower index.
MUST NOT be sent on [models](res-protocol.md#models).
MUST NOT be sent on other resource types.
The event payload has the following parameter:

**idx**
Expand Down Expand Up @@ -551,7 +555,7 @@ Eg. `messageService.>` - Pattern that matches all resources owned by *messageSer

# Query resources

A query resource is a resource where its model properties or collection values may vary based on the query. It is used to request partial or filtered resources, such as for searches, sorting, or pagination.
A query resource is a resource where its model, collection, or static values may vary based on the query. It is used to request partial or filtered resources, such as for searches, sorting, or pagination.

## Query event

Expand Down Expand Up @@ -600,18 +604,23 @@ MUST be a string.
An array of events for the query resource.
MUST be an array of [event query objects](#event-query-object)
May be omitted if there are no events.
Must be omitted if *model* or *collection* is provided.
Must be omitted if *model*, *collection*, or *static* is provided.

**model**
An object containing the named properties and [values](res-protocol.md#values) of the model.
Must be omitted if *events* or *collection* is provided.
Must be omitted if *events*, *collection*, or *static* is provided.
Must be omitted if the query resource is not a model.

**collection**
An ordered array containing the [values](res-protocol.md#values) of the collection.
Must be omitted if *events* or *model* is provided.
Must be omitted if *events*, *model*, or *static* is provided.
Must be omitted if the query resource is not a collection.

**static**
An arbitrary JSON value.
Must be omitted if *events*, *model*, or *collection* is provided.
Must be omitted if the query resource is not a static.

**Example result payload with events**
```json
{
Expand Down
9 changes: 9 additions & 0 deletions server/apiEncoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,12 @@ func (e *encoderJSON) encodeSubscription(s *Subscription, wrap bool) error {
}
}
e.b.WriteByte('}')

case rescache.TypeStatic:
if wrap {
e.b.Write([]byte(`,"static":`))
}
e.b.Write(s.Static())
}

// Remove itself from path
Expand Down Expand Up @@ -379,6 +385,9 @@ func (e *encoderJSONFlat) encodeSubscription(s *Subscription) error {
}
}
e.b.WriteByte('}')

case rescache.TypeStatic:
e.b.Write(s.Static())
}

// Remove itself from path
Expand Down
9 changes: 7 additions & 2 deletions server/codec/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type GetResponse struct {
type GetResult struct {
Model map[string]Value `json:"model"`
Collection []Value `json:"collection"`
Static json.RawMessage `json:"static"`
Query string `json:"query"`
}

Expand Down Expand Up @@ -115,6 +116,7 @@ type EventQueryResult struct {
Events []*EventQueryEvent `json:"events"`
Model map[string]Value `json:"model"`
Collection []Value `json:"collection"`
Static json.RawMessage `json:"static"`
}

// EventQueryEvent represents an event in the response of a RES-server query request
Expand Down Expand Up @@ -329,7 +331,7 @@ func DecodeGetResponse(payload []byte) (*GetResult, error) {
// Assert we got either a model or a collection
res := r.Result
if res.Model != nil {
if res.Collection != nil {
if res.Collection != nil || res.Static != nil {
return nil, errInvalidResponse
}
// Assert model only has proper values
Expand All @@ -339,13 +341,16 @@ func DecodeGetResponse(payload []byte) (*GetResult, error) {
}
}
} else if res.Collection != nil {
if res.Static != nil {
return nil, errInvalidResponse
}
// Assert collection only has proper values
for _, v := range res.Collection {
if !v.IsProper() {
return nil, errInvalidResponse
}
}
} else {
} else if res.Static == nil {
return nil, errInvalidResponse
}

Expand Down
12 changes: 10 additions & 2 deletions server/rescache/eventSubscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type ResourceType byte
const (
TypeCollection ResourceType = ResourceType(stateCollection)
TypeModel ResourceType = ResourceType(stateModel)
TypeStatic ResourceType = ResourceType(stateStatic)
TypeError ResourceType = ResourceType(stateError)
)

Expand Down Expand Up @@ -102,7 +103,7 @@ func (e *EventSubscription) addSubscriber(sub Subscriber) {
defer e.mu.Lock()
sub.Loaded(nil, rs.err)

// stateModel or stateCollection
// stateModel, stateCollection, or stateStatic
default:
e.mu.Unlock()
defer e.mu.Lock()
Expand Down Expand Up @@ -307,10 +308,17 @@ func (e *EventSubscription) handleQueryEvent(subj string, payload []byte) {
// Handle collection response
case result.Collection != nil:
if rs.state != stateCollection {
e.cache.Errorf("Error processing query event for %s?%s: non-model payload on model %s", e.ResourceName, rs.query, data)
e.cache.Errorf("Error processing query event for %s?%s: non-collection payload on collection %s", e.ResourceName, rs.query, data)
return
}
rs.processResetCollection(result.Collection)
// Handle static response
case result.Static != nil:
if rs.state != stateStatic {
e.cache.Errorf("Error processing query event for %s?%s: non-static payload on static %s", e.ResourceName, rs.query, data)
return
}
rs.processResetStatic(result.Static)
}
})
})
Expand Down
Loading

0 comments on commit a451ddc

Please sign in to comment.