Skip to content

Commit

Permalink
GH-34: Improved middleware documentation and READMEs
Browse files Browse the repository at this point in the history
  • Loading branch information
jirenius committed May 29, 2019
1 parent 99b511c commit c18b14d
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 48 deletions.
14 changes: 13 additions & 1 deletion README.md
Expand Up @@ -45,8 +45,10 @@ func main() {

| Example | Description
| --- | ---
| [Hello World](examples/hello-world/) | Single text field that is updated in real time.
| [Edit Text](examples/edit-text/) | Text field that can be edited by multiple clients concurrently.
| [Edit Text BadgerDB](examples/edit-text-badgerdb/) | Edit Text example using BadgerDB middleware to persist all changes.
| [Book Collection](examples/book-collection/) | List of book titles & authors that can be edited by many.
| [Book Collection BadgerDB](examples/book-collection-badgerdb/) | Book Collection example using BadgerBD middleware to persist all changes.

> **Note**
>
Expand Down Expand Up @@ -176,6 +178,16 @@ s.Handle("mymodel",
)
```

#### Using routes

```go
s.Route("v2", func(m *res.Mux) {
m.Handle("mymodel",
/* ... */
)
})
```

#### Start service

```go
Expand Down
31 changes: 14 additions & 17 deletions examples/book-collection-badgerdb/README.md
@@ -1,13 +1,17 @@
# Book Collection BadgerDB example
# Book Collection BadgerDB Example

This is the Book Collection example where all changes are persisted using the BadgerDB middleware.
By using the BadgerDB middleware, both clients and database can be updated with a single event.

This is an example RES service with an editable list of book authors and titles, persisted in BadgerDB.
* It exposes a collection, `library.books`, containing book model references.
* It exposes book models, `library.book.<BOOK_ID>`, of each book.
* It allows setting the books' *title* and *author* property through the `set` method.
* It allows creating new books that are added to the collection with the `new` method.
* It allows deleting existing books from the collection with the `delete` method.
* It verifies that a *title* and *author* is always set.
* It persists all changes to BadgerDB.
* The middleware adds a GetResource handler that loads the resources from the database.
* The middleware adds a ApplyChange handler that updates the books on change events.
* The middleware adds a ApplyAdd handler that updates the list on add events.
* The middleware adds a ApplyRemove handler that updates the list on remove events.
* The middleware adds a ApplyCreate handler that stores new books on create events.
* The middleware adds a ApplyDelete handler that deletes books on delete events.
* It persists all changes to a local BadgerDB database under `./db`.

## Prerequisite

Expand All @@ -18,7 +22,7 @@ This is an example RES service with an editable list of book authors and titles,
Clone go-res repository and run example:
```bash
git clone https://github.com/jirenius/go-res
cd go-res/examples/book-collection
cd go-res/examples/book-collection-badgerdb
go run main.go
```

Expand All @@ -29,15 +33,8 @@ http://localhost:8083

## Things to try out

**Realtime updates**
Run the client in two separate tabs to observe realtime updates.

**System reset**
Run the client and make some changes. Restart the node.js server to observe resetting of resources in clients.

**Resynchronization**
Run the client on two separate devices. Disconnect one device, then make changes with the other. Reconnect the first device to observe resynchronization.

**BadgerDB persistance**
Run the client and make changes to the list of books. Restart the service and observe that all changes are persisted.

## Web resources

Expand Down
6 changes: 3 additions & 3 deletions examples/book-collection-badgerdb/main.go
Expand Up @@ -47,7 +47,7 @@ func main() {
defer db.Close()

// Create badgerDB middleware for res.Service
badgerDB := middleware.BadgerDB(db)
badgerDB := middleware.BadgerDB{DB: db}

// Create a new RES Service
s := res.NewService("library")
Expand All @@ -57,7 +57,7 @@ func main() {
"book.$id",
res.Model,
res.Access(res.AccessGranted),
badgerDB.SetType(Book{}),
badgerDB.WithType(Book{}),
res.Set(setBookHandler),
)

Expand All @@ -66,7 +66,7 @@ func main() {
"books",
res.Collection,
res.Access(res.AccessGranted),
badgerDB.SetType([]res.Ref(nil)),
badgerDB.WithType([]res.Ref(nil)),
res.New(newBookHandler),
res.Call("delete", deleteBookHandler),
)
Expand Down
3 changes: 2 additions & 1 deletion examples/book-collection/README.md
@@ -1,7 +1,8 @@
# Book Collection example
# Book Collection Example

This is an example RES service that shows a lists of books, where book titles can be added,
edited and deleted by multiple users simultaneously.

* It exposes a collection, `library.books`, containing book model references.
* It exposes book models, `library.book.<BOOK_ID>`, of each book.
* It allows setting the books' *title* and *author* property through the `set` method.
Expand Down
52 changes: 52 additions & 0 deletions examples/edit-text-badgerdb/README.md
@@ -0,0 +1,52 @@
# Edit Text BadgerDB Example

This is the Edit Text example where all changes are persisted using the BadgerDB middleware.
By using the BadgerDB middleware, both clients and database can be updated with a single event.

* It exposes a single resource: `example.shared`.
* It allows setting the resource's `message` property through the `set` method.
* The middleware adds a GetResource handler that loads the resource from the database.
* The middleware adds a ApplyChange handler that updates the database on change events.
* It persists all changes to a local BadgerDB database under `./db`.
* It serves a web client at http://localhost:8082

## Prerequisite

* Have [NATS Server](https://nats.io/download/nats-io/gnatsd/) and [Resgate](https://github.com/resgateio/resgate) running

## Install and run

Clone go-res repository and run example:
```bash
git clone https://github.com/jirenius/go-res
cd go-res/examples/edit-text-badgerdb
go run main.go
```

Open the client
```
http://localhost:8082
```

## Things to try out

**BadgerDB persistance**
Run the client and make edits to the text. Restart the service and observe all changes are persisted.

## Web resources

Resources can be retrieved using ordinary HTTP GET requests, and methods can be called using HTTP POST requests.

### Get model
```
GET http://localhost:8080/api/example/shared
```

### Update model
```
POST http://localhost:8080/api/example/shared/set
```
*Body*
```
{ "message": "Updated through HTTP" }
```
4 changes: 2 additions & 2 deletions examples/edit-text-badgerdb/main.go
Expand Up @@ -34,8 +34,8 @@ func main() {
res.Model,
// Allow everone to access this resource
res.Access(res.AccessGranted),
// BadgerDB middleware adds a get handler
middleware.BadgerDB(db).SetDefault(map[string]interface{}{"message": "Resgate loves BadgerDB"}),
// BadgerDB middleware adds a GetResource and ApplyChange handler
middleware.BadgerDB{DB: db, Default: map[string]interface{}{"message": "Resgate loves BadgerDB"}},

// Handle setting of the message
res.Set(func(r res.CallRequest) {
Expand Down
17 changes: 9 additions & 8 deletions examples/edit-text/README.md
@@ -1,10 +1,11 @@
# Hello World example
# Edit Text Example

This is an example of a simple Hello World RES service written in Go.
* It exposes a single resource: `example.mymodel`.
This is an example of a simple text field that can be edited by multiple clients.

* It exposes a single resource: `example.shared`.
* It allows setting the resource's `message` property through the `set` method.
* It resets the model on server restart.
* It serves a web client at http://localhost:8081
* It serves a web client at http://localhost:8082

## Prerequisite

Expand All @@ -15,13 +16,13 @@ This is an example of a simple Hello World RES service written in Go.
Clone go-res repository and run example:
```bash
git clone https://github.com/jirenius/go-res
cd go-res/examples/hello-world
cd go-res/examples/edit-text
go run main.go
```

Open the client
```
http://localhost:8081
http://localhost:8082
```

## Things to try out
Expand All @@ -35,12 +36,12 @@ Resources can be retrieved using ordinary HTTP GET requests, and methods can be

### Get model
```
GET http://localhost:8080/api/exampleService/myModel
GET http://localhost:8080/api/example/shared
```

### Update model
```
POST http://localhost:8080/api/exampleService/myModel/set
POST http://localhost:8080/api/example/shared/set
```
*Body*
```
Expand Down
43 changes: 27 additions & 16 deletions middleware/badgerdb.go
Expand Up @@ -10,16 +10,17 @@ import (
res "github.com/jirenius/go-res"
)

type badgerDB struct {
rawDefault json.RawMessage
t reflect.Type
BadgerDBOption
}

type BadgerDBOption struct {
DB *badger.DB
// BadgerDB provides persistance to BadgerDB for the res Handlers.
//
// It will set the GetResource and Apply* handlers to load, store, and update the resources
// in the database, using the resource ID as key value.
type BadgerDB struct {
// BadgerDB database
DB *badger.DB
// Default resource value if not found in database. Will return res.ErrNotFound if not set.
Default interface{}
Type interface{}
// Type used to marshal into when calling r.Value() or r.RequireValue().
Type interface{}
}

var (
Expand All @@ -29,34 +30,44 @@ var (
errResourceAlreadyExists = res.InternalError(errors.New("resource already exists"))
)

func BadgerDB(db *badger.DB) BadgerDBOption {
return BadgerDBOption{DB: db}
type badgerDB struct {
rawDefault json.RawMessage
t reflect.Type
BadgerDB
}

func (o BadgerDBOption) SetDefault(i interface{}) BadgerDBOption {
// WithDefault returns a new BadgerDB value with the Default resource value set to i.
func (o BadgerDB) WithDefault(i interface{}) BadgerDB {
o.Default = i
return o
}

func (o BadgerDBOption) SetType(v interface{}) BadgerDBOption {
// WithType returns a new BadgerDB value with the Type value set to v.
func (o BadgerDB) WithType(v interface{}) BadgerDB {
o.Type = v
return o
}

func (o BadgerDBOption) SetDB(db *badger.DB) BadgerDBOption {
// WithDB returns a new BadgerDB value with the DB set to db.
func (o BadgerDB) WithDB(db *badger.DB) BadgerDB {
o.DB = db
return o
}

func (o BadgerDBOption) SetOption(hs *res.Handler) {
// SetOption is to implement the res.Option interface
func (o BadgerDB) SetOption(hs *res.Handler) {
var err error

if o.DB == nil {
panic("middleware: no badger DB set")
}

if hs.Type == res.TypeUnset {
panic("middleware: no resource Type set for handler prior to setting BadgerDB middleware")
}

b := badgerDB{
BadgerDBOption: o,
BadgerDB: o,
}

if o.Type != nil {
Expand Down
26 changes: 26 additions & 0 deletions middleware/doc.go
@@ -0,0 +1,26 @@
/*
Package middleware provides middleware for the res package:
https://github.com/jirenius/go-res
Middleware can be used for adding handler functions to a res.Handler,
to perform tasks such as:
* storing, loading and updating persisted data
* synchronize changes between multiple service instances
* add additional logging
* provide helpers for complex live queries
Currently, only the BadgerDB middleware is created, to demonstrate
database persistance.
Usage
Add middleware to a resource:
s.Handle("user.$id",
middlware.BadgerDB{DB: db},
)
*/
package middleware
52 changes: 52 additions & 0 deletions middleware/example_test.go
@@ -0,0 +1,52 @@
package middleware_test

import (
"github.com/dgraph-io/badger"
res "github.com/jirenius/go-res"
"github.com/jirenius/go-res/middleware"
)

func ExampleBadgerDB() {
db := &badger.DB{} // Dummy. Use badger.Open

s := res.NewService("directory")
s.Handle("user.$id",
res.Model,
middleware.BadgerDB{DB: db},
/* ... */
)
}

func ExampleBadgerDB_WithType() {
db := &badger.DB{} // Dummy. Use badger.Open

type User struct {
ID int
Name string
}

s := res.NewService("directory")
badgerDB := middleware.BadgerDB{DB: db}
s.Handle("user.$id",
res.Collection,
badgerDB.WithType(User{}),
res.Set(func(r res.CallRequest) {
_ = r.RequireValue().(User)
/* ... */
r.OK(nil)
}),
)
}

func ExampleBadgerDB_WithDefault() {
db := &badger.DB{} // Dummy. Use badger.Open

s := res.NewService("directory")
badgerDB := middleware.BadgerDB{DB: db}
s.Handle("users",
res.Collection,
// Default to an empty slice of references
badgerDB.WithType([]res.Ref{}).WithDefault([]res.Ref{}),
/* ... */
)
}

0 comments on commit c18b14d

Please sign in to comment.