Skip to content

Commit 1034374

Browse files
committed
Merge branch 'release/v0.2.0'
2 parents e966b1d + 2e86fbd commit 1034374

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+8818
-1645
lines changed

.github/FUNDING.yml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# These are supported funding model platforms
2+
github: [jirenius]
3+
custom: ["https://www.paypal.me/jirenius"]

.travis.yml

+8-10
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
language: go
22
go:
3-
- 1.10.x
4-
- 1.11.x
3+
- 1.13.x
54
- 1.12.x
5+
- 1.11.x
6+
before_install:
7+
- if [[ "$TRAVIS_GO_VERSION" =~ ^1\.11\. ]]; then export GO111MODULE=off; fi
68
install:
79
- go get -t ./...
8-
- go get github.com/mattn/goveralls
9-
- go get golang.org/x/lint/golint
10-
- go get github.com/client9/misspell/cmd/misspell
10+
- if [[ "$TRAVIS_GO_VERSION" =~ ^1\.13\. ]]; then ./scripts/install-checks.sh; fi
1111
before_script:
12-
- $(exit $(go fmt ./... | wc -l))
13-
- go vet ./...
14-
- golint ./...
15-
- misspell -error -locale US ./...
12+
- if [[ "$TRAVIS_GO_VERSION" =~ ^1\.13\. ]]; then ./scripts/check.sh; fi
13+
- go build
1614
- go build $(go list ./examples/...)
1715
script:
18-
- if [[ "$TRAVIS_GO_VERSION" =~ ^1\.12\. ]]; then ./scripts/cover.sh TRAVIS; else go test -i -race ./...; fi
16+
- if [[ "$TRAVIS_GO_VERSION" =~ ^1\.13\. ]]; then ./scripts/cover.sh TRAVIS; else go test -i -race ./...; fi

README.md

+24-13
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<p align="center"><a href="https://resgate.io" target="_blank" rel="noopener noreferrer"><img width="100" src="https://resgate.io/img/resgate-logo.png" alt="Resgate logo"></a></p>
22
<h2 align="center"><b>RES Service for Go</b><br/>Synchronize Your Clients</h2>
33
<p align="center">
4-
<a href="http://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License"></a>
4+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License"></a>
55
<a href="http://goreportcard.com/report/jirenius/go-res"><img src="http://goreportcard.com/badge/github.com/jirenius/go-res" alt="Report Card"></a>
66
<a href="https://travis-ci.com/jirenius/go-res"><img src="https://travis-ci.com/jirenius/go-res.svg?branch=master" alt="Build Status"></a>
77
<a href="https://coveralls.io/github/jirenius/go-res?branch=master"><img src="https://coveralls.io/repos/github/jirenius/go-res/badge.svg?branch=master" alt="Coverage"></a>
8-
<a href="http://godoc.org/github.com/jirenius/go-res"><img src="https://godoc.org/github.com/jirenius/go-res?status.svg" alt="GoDoc"></a>
8+
<a href="https://pkg.go.dev/github.com/jirenius/go-res"><img src="https://img.shields.io/static/v1?label=reference&message=go.dev&color=5673ae" alt="Reference"></a>
99
</p>
1010

1111
---
@@ -40,31 +40,42 @@ func main() {
4040
s.ListenAndServe("nats://localhost:4222")
4141
}
4242
```
43+
> **Prerequisite**
44+
>
45+
> [Install](https://resgate.io/docs/get-started/installation/) *NATS Server* and *Resgate*. Can be done with 3 docker commands:
46+
> ```text
47+
> docker network create res
48+
> docker run -d --name nats -p 4222:4222 --net res nats
49+
> docker run --name resgate -p 8080:8080 --net res resgateio/resgate --nats nats://nats:4222
50+
> ```
51+
4352
4453
## Examples
4554
4655
| Example | Description
4756
| --- | ---
48-
| [Edit Text](examples/edit-text/) | Text field that can be edited by multiple clients concurrently.
49-
| [Edit Text BadgerDB](examples/edit-text-badgerdb/) | Edit Text example using BadgerDB middleware to persist all changes.
50-
| [Book Collection](examples/book-collection/) | List of book titles & authors that can be edited by many.
51-
| [Book Collection BadgerDB](examples/book-collection-badgerdb/) | Book Collection example using BadgerBD middleware to persist all changes.
57+
| [Hello World](examples/01-hello-world/) | Smallest of services serving a static message.
58+
| [Edit Text](examples/02-edit-text/) | Single text field that is updated in real time.
59+
| [Edit Text Persisted](examples/03-edit-text-persisted/) | Edit Text example persisting changes using BadgerDB middleware.
60+
| [Book Collection](examples/04-book-collection/) | List of book titles & authors that can be edited by many.
61+
| [Book Collection Persisted](examples/05-book-collection-persisted/) | Book Collection example persisting changes using BadgerBD middleware.
62+
| [Search Query](examples/06-search-query/) | Make live queries against a large customer database.
5263
5364
> **Note**
5465
>
5566
> Above examples are complete with both service and client.
5667
57-
## Middleware <a href="http://godoc.org/github.com/jirenius/go-res/middleware"><img src="https://godoc.org/github.com/jirenius/go-res/middleware?status.svg" alt="GoDoc"></a>
68+
## Middleware
5869
59-
The `middleware` sub-package contains middleware that adds handler functions to a `res.Handler`, to perform tasks such as:
70+
The *middleware* subfolder contains packages that adds handler functions to a `res.Handler`, to perform tasks such as:
6071
61-
* storing, loading and updating persisted data
72+
* store, load and update persisted data
6273
* synchronize changes between multiple service instances
63-
* provide helpers for complex live queries
74+
* perform live queries with indexed searches
6475
65-
| Name | Description
66-
| --- | ---
67-
| `middleware.BadgerDB` | Stores and updates resources in a BadgerDB using the events.
76+
| Name | Description | Documentation
77+
| --- | --- | ---
78+
| [resbadger](middleware/resbadger) | BadgerDB storage middleware. | <a href="https://pkg.go.dev/github.com/jirenius/go-res/middleware/resbadger"><img src="https://img.shields.io/static/v1?label=reference&message=go.dev&color=5673ae" alt="Reference"></a>
6879
6980
## Usage
7081

codec.go

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ type successResponse struct {
1717
Result interface{} `json:"result"`
1818
}
1919

20+
type resourceResponse struct {
21+
Resource Ref `json:"resource"`
22+
}
23+
2024
type errorResponse struct {
2125
Error *Error `json:"error"`
2226
}

errors.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,21 @@ func InternalError(err error) *Error {
2727

2828
// Predefined error codes
2929
const (
30-
CodeAccessDenied = "system.accessDenied"
31-
CodeInternalError = "system.internalError"
32-
CodeInvalidParams = "system.invalidParams"
33-
CodeMethodNotFound = "system.methodNotFound"
34-
CodeNotFound = "system.notFound"
35-
CodeTimeout = "system.timeout"
36-
CodeBadRequest = "system.badRequest"
37-
CodeMethodNotAllowed = "system.methodNotAllowed"
30+
CodeAccessDenied = "system.accessDenied"
31+
CodeInternalError = "system.internalError"
32+
CodeInvalidParams = "system.invalidParams"
33+
CodeInvalidQuery = "system.invalidQuery"
34+
CodeMethodNotFound = "system.methodNotFound"
35+
CodeNotFound = "system.notFound"
36+
CodeTimeout = "system.timeout"
3837
)
3938

4039
// Predefined errors
4140
var (
4241
ErrAccessDenied = &Error{Code: CodeAccessDenied, Message: "Access denied"}
4342
ErrInternalError = &Error{Code: CodeInternalError, Message: "Internal error"}
4443
ErrInvalidParams = &Error{Code: CodeInvalidParams, Message: "Invalid parameters"}
44+
ErrInvalidQuery = &Error{Code: CodeInvalidQuery, Message: "Invalid query"}
4545
ErrMethodNotFound = &Error{Code: CodeMethodNotFound, Message: "Method not found"}
4646
ErrNotFound = &Error{Code: CodeNotFound, Message: "Not found"}
4747
ErrTimeout = &Error{Code: CodeTimeout, Message: "Request timeout"}

examples/01-hello-world/README.md

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Hello World Example
2+
3+
**Tags:** *Models*
4+
5+
## Description
6+
Simple service serving a message to the world.
7+
8+
## Prerequisite
9+
10+
* [Download](https://golang.org/dl/) and install Go
11+
* [Install](https://resgate.io/docs/get-started/installation/) *NATS Server* and *Resgate* (done with 3 docker commands).
12+
13+
## Install and run
14+
15+
```text
16+
git clone https://github.com/jirenius/go-res
17+
cd go-res/examples/01-hello-world
18+
go run main.go
19+
```
20+
21+
## Things to try out
22+
23+
### Access API through HTTP
24+
* Open the browser and go to:
25+
```text
26+
http://localhost:8080/api/example/model
27+
```
28+
29+
### Access API through WebSocket
30+
* Open *Chrome* and go to [resgate.io - resource viewer](https://resgate.io/viewer).
31+
* Type in the resource ID below, and click *View*:
32+
```text
33+
example.model
34+
```
35+
> **Note**
36+
>
37+
> Chrome allows websites to connect to *localhost*, while other browsers may give a security error.
38+
39+
### Real time update on static resource
40+
* Stop the project, and change the `"Hello, World!"` message in *main.go*.
41+
* Restart the project and observe how the message is updated in the viewer (see [above](#access-api-through-websocket)).
42+
43+
### Get resource with ResClient
44+
* In the [resgate.io - resource viewer](https://resgate.io/viewer), open the DevTools console (*Ctrl+Shift+J*).
45+
* Type the following command, and press *Enter*:
46+
```javascript
47+
client.get("example.model").then(m => console.log(m.message));
48+
```
49+
> **Note**
50+
>
51+
> The resource viewer app stores the *ResClient* instance in the global `client` variable, for easy access.
52+

examples/01-hello-world/main.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package main
2+
3+
import res "github.com/jirenius/go-res"
4+
5+
func main() {
6+
s := res.NewService("example")
7+
s.Handle("model",
8+
res.Access(res.AccessGranted),
9+
res.GetModel(func(r res.ModelRequest) {
10+
r.Model(map[string]string{
11+
"message": "Hello, World!",
12+
})
13+
}),
14+
)
15+
s.ListenAndServe("nats://localhost:4222")
16+
}
File renamed without changes.

examples/02-edit-text/README.md

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Edit Text Example
2+
3+
**Tags:** *Models*, *Call methods*, *Client subscriptions*
4+
5+
## Description
6+
A simple text field that can be edited by multiple clients simultaneously.
7+
8+
## Prerequisite
9+
10+
* [Download](https://golang.org/dl/) and install Go
11+
* [Install](https://resgate.io/docs/get-started/installation/) *NATS Server* and *Resgate* (done with 3 docker commands).
12+
13+
## Install and run
14+
15+
```text
16+
git clone https://github.com/jirenius/go-res
17+
cd go-res/examples/02-edit-text
18+
go run main.go
19+
```
20+
21+
Open the client
22+
```
23+
http://localhost:8082
24+
```
25+
26+
## Things to try out
27+
28+
### Realtime updates
29+
* Open the client in two separate tabs.
30+
* Edit the message in one tab, and observe realtime updates in both.
31+
32+
### System reset
33+
* Stop the service.
34+
* Edit the default text (`"Hello, Go World!"`) in *main.go*.
35+
* Restart the service to observe resetting of the message in all clients.
36+
37+
## API
38+
39+
Request | Resource | Description
40+
--- | --- | ---
41+
*get* | `text.shared` | Simple model.
42+
*call* | `text.shared.set` | Sets the model's *message* property.
43+
44+
## REST API
45+
46+
Resources can be retrieved using ordinary HTTP GET requests, and methods can be called using HTTP POST requests.
47+
48+
### Get model
49+
```
50+
GET http://localhost:8080/api/text/shared
51+
```
52+
53+
### Update model
54+
```
55+
POST http://localhost:8080/api/text/shared/set
56+
```
57+
*Body*
58+
```
59+
{ "message": "Updated through HTTP" }
60+
```

examples/02-edit-text/index.html

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Edit Text Example</title>
6+
<link rel="icon" href="data:,">
7+
<script src="https://cdn.jsdelivr.net/npm/resclient@latest/dist/resclient.min.js"></script>
8+
</head>
9+
<body>
10+
<p>Try running the client in two separate tabs!</p>
11+
<p>Web resource: <a href="http://localhost:8080/api/text/shared" target="_blank">http://localhost:8080/api/text/shared</a></p>
12+
<hr />
13+
<div id="root"><input id="input" /></div>
14+
<script>
15+
const ResClient = resclient.default;
16+
17+
// Creating the client instance.
18+
let client = new ResClient('ws://localhost:8080');
19+
20+
// Get the model from the service.
21+
client.get('text.shared').then(model => {
22+
// Set value of the input element
23+
input.value = model.message;
24+
25+
// Listen for user input and call set to update the remote model.
26+
input.addEventListener('input', () => {
27+
model.set({ message: input.value });
28+
});
29+
30+
// Listen for model change events.
31+
// The model will be unsubscribed after calling model.off
32+
model.on('change', () => {
33+
input.value = model.message;
34+
});
35+
}).catch(err => {
36+
root.textContent = err.code == 'system.connectionError'
37+
? "Connection error. Are NATS Server and Resgate running?"
38+
: err.message;
39+
});
40+
</script>
41+
</body>
42+
</html>

examples/edit-text/main.go examples/02-edit-text/main.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
This is an example of a simple text field that can be edited by multiple clients.
3-
* It exposes a single resource: "example.shared".
3+
* It exposes a single resource: "text.shared".
44
* It allows setting the resource's Message property through the "set" method.
55
* It resets the model on server restart.
66
* It serves a web client at http://localhost:8082
@@ -24,9 +24,9 @@ var shared = &Model{Message: "Hello, Go World!"}
2424

2525
func main() {
2626
// Create a new RES Service
27-
s := res.NewService("example")
27+
s := res.NewService("text")
2828

29-
// Add handlers for "example.shared" resource
29+
// Add handlers for "text.shared" resource
3030
s.Handle("shared",
3131
// Allow everone to access this resource
3232
res.Access(res.AccessGranted),
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Edit Text Persisted Example
2+
3+
**Tags:** *Models*, *Call methods*, *Client subscriptions*, *Persistence*
4+
5+
## Description
6+
This is the Edit Text example where all changes are persisted using the BadgerDB middleware. With a database middleware, both clients and database can be updated with a single event.
7+
8+
## Prerequisite
9+
10+
* [Download](https://golang.org/dl/) and install Go
11+
* [Install](https://resgate.io/docs/get-started/installation/) *NATS Server* and *Resgate* (done with 3 docker commands).
12+
13+
## Install and run
14+
15+
```text
16+
git clone https://github.com/jirenius/go-res
17+
cd go-res/examples/03-edit-text-persisted
18+
go run main.go
19+
```
20+
21+
Open the client
22+
```
23+
http://localhost:8083
24+
```
25+
26+
## Things to try out
27+
28+
### BadgerDB persistence
29+
Run the client and make edits to the text. Restart the service and observe all changes are persisted.
30+
31+
## API
32+
33+
Request | Resource | Description
34+
--- | --- | ---
35+
*get* | `text.shared` | Simple model.
36+
*call* | `text.shared.set` | Sets the model's *message* property.
37+
38+
## REST API
39+
40+
Resources can be retrieved using ordinary HTTP GET requests, and methods can be called using HTTP POST requests.
41+
42+
### Get model
43+
```
44+
GET http://localhost:8080/api/text/shared
45+
```
46+
47+
### Update model
48+
```
49+
POST http://localhost:8080/api/text/shared/set
50+
```
51+
*Body*
52+
```
53+
{ "message": "Updated through HTTP" }
54+
```

0 commit comments

Comments
 (0)