mirror of
https://github.com/absmach/magistrala.git
synced 2026-08-07 15:25:48 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0fd0e6a13f | |||
| 8a86593dc3 | |||
| effade00aa | |||
| 8edf6907bc | |||
| 507bd2ee84 | |||
| 86f0d8e0d6 | |||
| 6461761985 | |||
| ec03d877f0 | |||
| 4e137a3214 | |||
| af281da430 | |||
| 1683d17830 | |||
| 7e83b2d612 | |||
| 312d30fd69 | |||
| 2c988c19b0 | |||
| 455fedf075 | |||
| 58cdf2cddc | |||
| 6d48446c16 | |||
| 29d146636a | |||
| f3f14e803d | |||
| b090aa3e02 | |||
| 0b954847fd | |||
| 44f5a7241f | |||
| b694f08ea2 | |||
| f1537528f9 | |||
| 344eed7976 | |||
| d8357b500c | |||
| 993398b8ab | |||
| 426f59d392 |
+1
-1
@@ -1,4 +1,4 @@
|
||||
build
|
||||
*.pb.go
|
||||
|
||||
site/
|
||||
|
||||
|
||||
Generated
+110
-7
@@ -34,6 +34,12 @@
|
||||
packages = ["quantile"]
|
||||
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/buger/jsonparser"
|
||||
packages = ["."]
|
||||
revision = "2cac668e8456b4284edb0715e17e2af02d3ec993"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cenkalti/backoff"
|
||||
packages = ["."]
|
||||
@@ -76,6 +82,12 @@
|
||||
revision = "47565b4f722fb6ceae66b95f853feed578a4a51c"
|
||||
version = "v0.3.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fatih/color"
|
||||
packages = ["."]
|
||||
revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
|
||||
version = "v1.7.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-kit/kit"
|
||||
packages = [
|
||||
@@ -108,6 +120,17 @@
|
||||
revision = "fd0aebc74e908868b09ac140fb5a53cb363884c1"
|
||||
version = "1.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/gocql/gocql"
|
||||
packages = [
|
||||
".",
|
||||
"internal/lru",
|
||||
"internal/murmur",
|
||||
"internal/streams"
|
||||
]
|
||||
revision = "651d6b1f343c6e168e45d119dcfe8f71e5bafb32"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gogo/protobuf"
|
||||
packages = ["proto"]
|
||||
@@ -126,12 +149,35 @@
|
||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/snappy"
|
||||
packages = ["."]
|
||||
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
packages = ["."]
|
||||
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/hailocab/go-hostpool"
|
||||
packages = ["."]
|
||||
revision = "e80d13ce29ede4452c43dea11e79b9bc8a15b478"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hokaccha/go-prettyjson"
|
||||
packages = ["."]
|
||||
revision = "e6b9231a2b1cc5798b336fdbf99c16cd8ad1e0c9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
packages = ["."]
|
||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/influxdata/influxdb"
|
||||
packages = [
|
||||
@@ -157,6 +203,18 @@
|
||||
]
|
||||
revision = "d34b9ff171c21ad295489235aec8b6626023cd04"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||
version = "v0.0.9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/matttproud/golang_protobuf_extensions"
|
||||
packages = ["pbutil"]
|
||||
@@ -171,28 +229,52 @@
|
||||
"bson/decimal",
|
||||
"bson/elements",
|
||||
"bson/objectid",
|
||||
"core/addr",
|
||||
"bson/parser",
|
||||
"bson/parser/ast",
|
||||
"core/address",
|
||||
"core/auth",
|
||||
"core/auth/internal/gssapi",
|
||||
"core/command",
|
||||
"core/compressor",
|
||||
"core/connection",
|
||||
"core/connstring",
|
||||
"core/description",
|
||||
"core/dispatch",
|
||||
"core/options",
|
||||
"core/event",
|
||||
"core/option",
|
||||
"core/readconcern",
|
||||
"core/readpref",
|
||||
"core/result",
|
||||
"core/session",
|
||||
"core/tag",
|
||||
"core/topology",
|
||||
"core/uuid",
|
||||
"core/version",
|
||||
"core/wiremessage",
|
||||
"core/writeconcern",
|
||||
"internal",
|
||||
"mongo"
|
||||
"mongo",
|
||||
"mongo/aggregateopt",
|
||||
"mongo/changestreamopt",
|
||||
"mongo/clientopt",
|
||||
"mongo/collectionopt",
|
||||
"mongo/countopt",
|
||||
"mongo/dbopt",
|
||||
"mongo/deleteopt",
|
||||
"mongo/distinctopt",
|
||||
"mongo/dropcollopt",
|
||||
"mongo/findopt",
|
||||
"mongo/indexopt",
|
||||
"mongo/insertopt",
|
||||
"mongo/listcollectionopt",
|
||||
"mongo/listdbopt",
|
||||
"mongo/mongoopt",
|
||||
"mongo/replaceopt",
|
||||
"mongo/runcmdopt",
|
||||
"mongo/updateopt"
|
||||
]
|
||||
revision = "29a4d7d5b648e5b0b60e03e3bd71c11703f0c203"
|
||||
version = "v0.0.5"
|
||||
revision = "8df56f107cdd485351eb2523992c3364114b4b2e"
|
||||
version = "v0.0.10"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nats-io/go-nats"
|
||||
@@ -340,9 +422,24 @@
|
||||
revision = "e9556a45379ef1da12e54847edb2fb3d7d566f36"
|
||||
version = "0.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = ["."]
|
||||
revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert"]
|
||||
packages = [
|
||||
"assert",
|
||||
"require"
|
||||
]
|
||||
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||
version = "v1.2.1"
|
||||
|
||||
@@ -456,6 +553,12 @@
|
||||
revision = "c87af80f3cc5036b55b83d77171e156791085e2e"
|
||||
version = "v1.7.1"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/inf.v0"
|
||||
packages = ["."]
|
||||
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
|
||||
version = "v0.9.1"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/ory-am/dockertest.v3"
|
||||
packages = ["."]
|
||||
@@ -465,6 +568,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "6010ae4c4f91c30b3a63789cc280466ce56d50f387c1e4d11c49df1302f083da"
|
||||
inputs-digest = "e3b07d2098253644fc2f77fede1f00fcc077a20e4f46c59466ffd61c4998bb00"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
+16
@@ -10,6 +10,10 @@
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
version = "3.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/fatih/color"
|
||||
version = "1.7.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/go-kit/kit"
|
||||
version = "0.6.0"
|
||||
@@ -22,6 +26,10 @@
|
||||
name = "github.com/golang/protobuf"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/hokaccha/go-prettyjson"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/lib/pq"
|
||||
@@ -42,6 +50,10 @@
|
||||
name = "github.com/sony/gobreaker"
|
||||
version = "0.3.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/cobra"
|
||||
version = "0.0.3"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.2.1"
|
||||
@@ -70,6 +82,10 @@
|
||||
name = "github.com/mongodb/mongo-go-driver"
|
||||
version = "0.0.5"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/gocql/gocql"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
BUILD_DIR = build
|
||||
SERVICES = users things http normalizer ws influxdb mongodb
|
||||
SERVICES = users things http normalizer ws influxdb-writer influxdb-reader mongodb-writer mongodb-reader cassandra-writer cassandra-reader cli
|
||||
DOCKERS = $(addprefix docker_,$(SERVICES))
|
||||
CGO_ENABLED ?= 0
|
||||
GOOS ?= linux
|
||||
@@ -12,20 +12,24 @@ define make_docker
|
||||
docker build --build-arg SVC_NAME=$(subst docker_,,$(1)) --tag=mainflux/$(subst docker_,,$(1)) -f docker/Dockerfile .
|
||||
endef
|
||||
|
||||
all: $(SERVICES)
|
||||
all: $(SERVICES) mqtt
|
||||
|
||||
.PHONY: all $(SERVICES) dockers latest release
|
||||
.PHONY: all $(SERVICES) dockers latest release mqtt
|
||||
|
||||
clean:
|
||||
rm -rf ${BUILD_DIR}
|
||||
rm -rf mqtt/node_modules
|
||||
|
||||
install:
|
||||
cp ${BUILD_DIR}/* $(GOBIN)
|
||||
|
||||
test:
|
||||
GOCACHE=off go test -v -race -tags test $(shell go list ./... | grep -v 'vendor\|cmd')
|
||||
|
||||
proto:
|
||||
protoc --go_out=plugins=grpc:. *.proto
|
||||
|
||||
$(SERVICES): proto
|
||||
$(SERVICES):
|
||||
$(call compile_service,$(@))
|
||||
|
||||
$(DOCKERS):
|
||||
@@ -35,6 +39,9 @@ dockers: $(DOCKERS)
|
||||
docker build --tag=mainflux/dashflux -f dashflux/docker/Dockerfile dashflux
|
||||
docker build --tag=mainflux/mqtt -f mqtt/Dockerfile .
|
||||
|
||||
mqtt:
|
||||
cd mqtt && npm install
|
||||
|
||||
latest: dockers
|
||||
for svc in $(SERVICES); do \
|
||||
docker push mainflux/$$svc; \
|
||||
@@ -54,3 +61,6 @@ release:
|
||||
docker push mainflux/dashflux:$(version)
|
||||
docker tag mainflux/mqtt mainflux/mqtt:$(version)
|
||||
docker push mainflux/mqtt:$(version)
|
||||
|
||||
run:
|
||||
cd scripts && ./run.sh
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mainflux
|
||||
|
||||
// Response contains HTTP response specific methods.
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# Mainflux CLI
|
||||
## Build
|
||||
From the project root:
|
||||
```
|
||||
make cli
|
||||
```
|
||||
|
||||
## Usage
|
||||
### Service
|
||||
#### Get the service verison
|
||||
```
|
||||
mainflux-cli version
|
||||
```
|
||||
|
||||
### User management
|
||||
#### Create User
|
||||
```
|
||||
mainflux-cli users create john.doe@email.com password
|
||||
```
|
||||
|
||||
#### Login User
|
||||
```
|
||||
mainflux-cli users token john.doe@email.com password
|
||||
```
|
||||
|
||||
### System Provisioning
|
||||
#### Provision Device
|
||||
```
|
||||
mainflux-cli things create '{"type":"device", "name":"nyDevice"}' <user_auth_token>
|
||||
```
|
||||
|
||||
#### Provision Application
|
||||
```
|
||||
mainflux-cli things create '{"type":"app", "name":"nyDevice"}' <user_auth_token>
|
||||
```
|
||||
|
||||
#### Retrieve All Things
|
||||
```
|
||||
mainflux-cli things get all --offset=1 --limit=5 <user_auth_token>
|
||||
```
|
||||
|
||||
#### Retrieve Thing By ID
|
||||
```
|
||||
mainflux-cli things get <thing_id> --offset=1 --limit=5 <user_auth_token>
|
||||
```
|
||||
|
||||
#### Remove Thing
|
||||
```
|
||||
mainflux-cli things delete <thing_id> <user_auth_token>
|
||||
```
|
||||
|
||||
#### Provision Channel
|
||||
```
|
||||
mainflux-cli channels create '{"name":"nyChannel"}' <user_auth_token>
|
||||
```
|
||||
|
||||
#### Retrieve All Channels
|
||||
```
|
||||
mainflux-cli channels get all --offset=1 --limit=5 <user_auth_token>
|
||||
```
|
||||
|
||||
#### Retrievie Channel By ID
|
||||
```
|
||||
mainflux-cli channels get <channel_id> --offset=1 --limit=5 <user_auth_token>
|
||||
```
|
||||
|
||||
#### Remove Channel
|
||||
```
|
||||
mainflux-cli channels delete <channel_id> <user_auth_token>
|
||||
```
|
||||
|
||||
### Access control
|
||||
#### Connect Thing to a Channel
|
||||
```
|
||||
mainflux-cli things connect <thing_id> <channel_id> <user_auth_token>
|
||||
```
|
||||
|
||||
#### Disconnect Things from a Channel
|
||||
```
|
||||
mainflux-cli things disconnect <thing_id> <channel_id> <user_auth_token>
|
||||
```
|
||||
|
||||
### Messaging
|
||||
#### Send a message over HTTP
|
||||
```
|
||||
mainflux-cli msg send <channel_id> '[{"bn":"some-base-name:","bt":1.276020076001e+09, "bu":"A","bver":5, "n":"voltage","u":"V","v":120.1}, {"n":"current","t":-5,"v":1.2}, {"n":"current","t":-4,"v":1.3}]' <thing_auth_token>
|
||||
```
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const channelsEP = "channels"
|
||||
|
||||
var cmdChannels = []cobra.Command{
|
||||
cobra.Command{
|
||||
Use: "create",
|
||||
Short: "create <JSON_channel> <user_auth_token>",
|
||||
Long: `Creates new channel and generates it's UUID`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
CreateChannel(args[0], args[1])
|
||||
},
|
||||
},
|
||||
cobra.Command{
|
||||
Use: "get",
|
||||
Short: "get all/<channel_id> <user_auth_token>",
|
||||
Long: `Gets list of all channels or gets channel by id`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
if args[0] == "all" {
|
||||
GetChannels(args[1])
|
||||
return
|
||||
}
|
||||
GetChannel(args[0], args[1])
|
||||
},
|
||||
},
|
||||
cobra.Command{
|
||||
Use: "update",
|
||||
Short: "update <channel_id> <JSON_string> <user_auth_token>",
|
||||
Long: `Updates channel record`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 3 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
UpdateChannel(args[0], args[1], args[2])
|
||||
},
|
||||
},
|
||||
cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "delete <channel_id> <user_auth_token>",
|
||||
Long: `Delete channel by ID`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
DeleteChannel(args[0], args[1])
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func NewChannelsCmd() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "channels",
|
||||
Short: "Manipulation with channels",
|
||||
Long: `Manipulation with channels: create, delete or update channels`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
LogUsage(cmd.Short)
|
||||
},
|
||||
}
|
||||
|
||||
for i, _ := range cmdChannels {
|
||||
cmd.AddCommand(&cmdChannels[i])
|
||||
}
|
||||
|
||||
return &cmd
|
||||
}
|
||||
|
||||
// CreateChannel - creates new channel and generates UUID
|
||||
func CreateChannel(data, token string) {
|
||||
url := fmt.Sprintf("%s/%s", serverAddr, channelsEP)
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(data))
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
|
||||
// GetChannels - gets all channels
|
||||
func GetChannels(token string) {
|
||||
url := fmt.Sprintf("%s/%s?offset=%s&limit=%s",
|
||||
serverAddr, channelsEP, strconv.Itoa(Offset), strconv.Itoa(Limit))
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
|
||||
// GetChannel - gets channel by ID
|
||||
func GetChannel(id, token string) {
|
||||
url := fmt.Sprintf("%s/%s/%s", serverAddr, channelsEP, id)
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
|
||||
// UpdateChannel - update a channel
|
||||
func UpdateChannel(id, data, token string) {
|
||||
url := fmt.Sprintf("%s/%s/%s", serverAddr, channelsEP, id)
|
||||
req, err := http.NewRequest("PUT", url, strings.NewReader(data))
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
|
||||
// DeleteChannel - removes channel
|
||||
func DeleteChannel(id, token string) {
|
||||
url := fmt.Sprintf("%s/%s/%s", serverAddr, channelsEP, id)
|
||||
req, err := http.NewRequest("DELETE", url, nil)
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/mainflux/mainflux"
|
||||
)
|
||||
|
||||
const (
|
||||
defCertsPath = "/src/github.com/mainflux/mainflux/docker/ssl/certs/"
|
||||
envCertFile = "MF_CERT_FILE"
|
||||
envKeyFile = "MF_KEY_FILE"
|
||||
envCaFile = "MF_CA_FILE"
|
||||
)
|
||||
|
||||
var (
|
||||
httpClient = &http.Client{}
|
||||
serverAddr = fmt.Sprintf("https://%s", "localhost")
|
||||
|
||||
defCertFile = fmt.Sprintf("%s%s%s", os.Getenv("GOPATH"), defCertsPath, "mainflux-server.crt")
|
||||
defKeyFile = fmt.Sprintf("%s%s%s", os.Getenv("GOPATH"), defCertsPath, "mainflux-server.key")
|
||||
defCaFile = fmt.Sprintf("%s%s%s", os.Getenv("GOPATH"), defCertsPath, "ca.crt")
|
||||
)
|
||||
|
||||
// SetServerAddr - set addr using host and port
|
||||
func SetServerAddr(proto string, host string, port int) {
|
||||
serverAddr = fmt.Sprintf("%s://%s", proto, host)
|
||||
|
||||
if port != 0 {
|
||||
serverAddr = fmt.Sprintf("%s:%s", serverAddr, strconv.Itoa(port))
|
||||
}
|
||||
}
|
||||
|
||||
func SetCerts() {
|
||||
// Set certificates paths
|
||||
certFile := mainflux.Env(envCertFile, defCertFile)
|
||||
keyFile := mainflux.Env(envKeyFile, defKeyFile)
|
||||
caFile := mainflux.Env(envCaFile, defCaFile)
|
||||
|
||||
// Load client cert
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load CA cert
|
||||
caCert, err := ioutil.ReadFile(caFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
caCertPool.AppendCertsFromPEM(caCert)
|
||||
|
||||
// Setup HTTPS client
|
||||
tlsConfig := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
RootCAs: caCertPool,
|
||||
}
|
||||
tlsConfig.BuildNameToCertificate()
|
||||
transport := &http.Transport{TLSClientConfig: tlsConfig}
|
||||
httpClient = &http.Client{Transport: transport}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const contentTypeSenml = "application/senml+json"
|
||||
|
||||
var cmdMessages = []cobra.Command{
|
||||
cobra.Command{
|
||||
Use: "send",
|
||||
Short: "send <channel_id> <JSON_string> <client_token>",
|
||||
Long: `Sends message on the channel`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 3 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
SendMsg(args[0], args[1], args[2])
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func NewMessagesCmd() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "msg",
|
||||
Short: "Send or retrieve messages",
|
||||
Long: `Send or retrieve messages: control message flow on the channel`,
|
||||
}
|
||||
|
||||
for i, _ := range cmdMessages {
|
||||
cmd.AddCommand(&cmdMessages[i])
|
||||
}
|
||||
|
||||
return &cmd
|
||||
}
|
||||
|
||||
// SendMsg - publishes SenML message on the channel
|
||||
func SendMsg(id, msg, token string) {
|
||||
url := serverAddr + "/http/channels/" + id + "/messages"
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(msg))
|
||||
LogError(err)
|
||||
|
||||
req.Header.Set("Authorization", token)
|
||||
req.Header.Add("Content-Type", contentTypeSenml)
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
FormatResLog(resp, err)
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const thingsEP = "things"
|
||||
|
||||
var cmdThings = []cobra.Command{
|
||||
cobra.Command{
|
||||
Use: "create",
|
||||
Short: "create <JSON_thing> <user_auth_token>",
|
||||
Long: `Create new thing, generate his UUID and store it`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
CreateThing(args[0], args[1])
|
||||
},
|
||||
},
|
||||
cobra.Command{
|
||||
Use: "get",
|
||||
Short: "get all/<thing_id> <user_auth_token>",
|
||||
Long: `Get all thingss or thing by id`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
if args[0] == "all" {
|
||||
GetThings(args[1])
|
||||
return
|
||||
}
|
||||
GetThing(args[0], args[1])
|
||||
},
|
||||
},
|
||||
cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "delete <thing_id> <user_auth_token>",
|
||||
Long: `Removes thing from database`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
DeleteThing(args[0], args[1])
|
||||
},
|
||||
},
|
||||
cobra.Command{
|
||||
Use: "update",
|
||||
Short: "update <thing_id> <JSON_string> <user_auth_token>",
|
||||
Long: `Update thing record`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 3 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
UpdateThing(args[0], args[1], args[2])
|
||||
},
|
||||
},
|
||||
cobra.Command{
|
||||
Use: "connect",
|
||||
Short: "connect <thing_id> <channel_id> <user_auth_token>",
|
||||
Long: `Connect thing to the channel`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 3 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
ConnectThing(args[0], args[1], args[2])
|
||||
},
|
||||
},
|
||||
cobra.Command{
|
||||
Use: "disconnect",
|
||||
Short: "disconnect <thing_id> <channel_id> <user_auth_token>",
|
||||
Long: `Disconnect thing to the channel`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 3 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
DisconnectThing(args[0], args[1], args[2])
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func NewThingsCmd() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "things",
|
||||
Short: "things <options>",
|
||||
Long: `Things handling: create, delete or update things.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
LogUsage(cmd.Short)
|
||||
},
|
||||
}
|
||||
|
||||
for i, _ := range cmdThings {
|
||||
cmd.AddCommand(&cmdThings[i])
|
||||
}
|
||||
|
||||
return &cmd
|
||||
}
|
||||
|
||||
// CreateThing - creates new thing and generates thing UUID
|
||||
func CreateThing(data, token string) {
|
||||
url := fmt.Sprintf("%s/%s", serverAddr, thingsEP)
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(data))
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
|
||||
// GetThings - gets all things
|
||||
func GetThings(token string) {
|
||||
url := fmt.Sprintf("%s/%s?offset=%s&limit=%s",
|
||||
serverAddr, thingsEP, strconv.Itoa(Offset), strconv.Itoa(Limit))
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
|
||||
// GetThing - gets thing by ID
|
||||
func GetThing(id, token string) {
|
||||
url := fmt.Sprintf("%s/%s/%s", serverAddr, thingsEP, id)
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
|
||||
// UpdateThing - updates thing by ID
|
||||
func UpdateThing(id, data, token string) {
|
||||
url := fmt.Sprintf("%s/%s/%s", serverAddr, thingsEP, id)
|
||||
req, err := http.NewRequest("PUT", url, strings.NewReader(data))
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
|
||||
// DeleteThing - removes thing
|
||||
func DeleteThing(id, token string) {
|
||||
url := fmt.Sprintf("%s/%s/%s", serverAddr, thingsEP, id)
|
||||
req, err := http.NewRequest("DELETE", url, nil)
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
|
||||
// ConnectThing - connect thing to a channel
|
||||
func ConnectThing(cliId, chanId, token string) {
|
||||
url := fmt.Sprintf("%s/%s/%s/%s/%s", serverAddr, channelsEP,
|
||||
chanId, thingsEP, cliId)
|
||||
req, err := http.NewRequest("PUT", url, nil)
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
|
||||
// DisconnectThing - connect thing to a channel
|
||||
func DisconnectThing(cliId, chanId, token string) {
|
||||
url := fmt.Sprintf("%s/%s/%s/%s/%s", serverAddr, channelsEP,
|
||||
chanId, thingsEP, cliId)
|
||||
req, err := http.NewRequest("DELETE", url, nil)
|
||||
SendRequest(req, token, err)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdUsers = []cobra.Command{
|
||||
cobra.Command{
|
||||
Use: "create",
|
||||
Short: "create <username> <password>",
|
||||
Long: `Creates new user`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
CreateUser(args[0], args[1])
|
||||
},
|
||||
},
|
||||
cobra.Command{
|
||||
Use: "token",
|
||||
Short: "token <username> <password>",
|
||||
Long: `Creates new token`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
LogUsage(cmd.Short)
|
||||
return
|
||||
}
|
||||
CreateToken(args[0], args[1])
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func NewUsersCmd() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "users",
|
||||
Short: "users create/token <email> <password>",
|
||||
Long: `Manages users in the system (create account or token)`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
LogUsage(cmd.Short)
|
||||
},
|
||||
}
|
||||
|
||||
for i, _ := range cmdUsers {
|
||||
cmd.AddCommand(&cmdUsers[i])
|
||||
}
|
||||
|
||||
return &cmd
|
||||
}
|
||||
|
||||
// CreateUser - create user
|
||||
func CreateUser(user, pwd string) {
|
||||
msg := fmt.Sprintf(`{"email": "%s", "password": "%s"}`, user, pwd)
|
||||
url := fmt.Sprintf("%s/users", serverAddr)
|
||||
resp, err := httpClient.Post(url, contentType, strings.NewReader(msg))
|
||||
FormatResLog(resp, err)
|
||||
}
|
||||
|
||||
// CreateToken - create user token
|
||||
func CreateToken(user, pwd string) {
|
||||
msg := fmt.Sprintf(`{"email": "%s", "password": "%s"}`, user, pwd)
|
||||
url := fmt.Sprintf("%s/tokens", serverAddr)
|
||||
resp, err := httpClient.Post(url, contentType, strings.NewReader(msg))
|
||||
FormatResLog(resp, err)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/hokaccha/go-prettyjson"
|
||||
)
|
||||
|
||||
const contentType = "application/json"
|
||||
|
||||
var Limit = 10
|
||||
var Offset = 0
|
||||
|
||||
func SendRequest(req *http.Request, token string, e error) {
|
||||
req.Header.Set("Authorization", token)
|
||||
req.Header.Add("Content-Type", contentType)
|
||||
if e != nil {
|
||||
LogError(e)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
FormatResLog(resp, err)
|
||||
}
|
||||
|
||||
// FormatResLog - format http response
|
||||
func FormatResLog(resp *http.Response, err error) {
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
fmt.Printf(color.CyanString("%s %s\nContent-Length: %v\n\n"),
|
||||
resp.Proto, resp.Status, resp.ContentLength)
|
||||
|
||||
if len(resp.Header.Get("Location")) != 0 {
|
||||
fmt.Printf(color.BlueString("Resource location: %s\n\n"),
|
||||
resp.Header.Get("Location"))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(body) != 0 {
|
||||
pj, err := prettyjson.Format([]byte(body))
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n\n", color.BlueString(string(body)))
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s\n\n", string(pj))
|
||||
}
|
||||
}
|
||||
|
||||
func LogUsage(u string) {
|
||||
fmt.Printf(color.YellowString("Usage: %s\n\n"), u)
|
||||
}
|
||||
|
||||
func LogError(err error) {
|
||||
fmt.Printf("%s\n\n", color.RedString(err.Error()))
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewVersionCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Get version of Mainflux Things Service",
|
||||
Long: `Mainflux server health checkt.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Version()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Version - server health check
|
||||
func Version() {
|
||||
url := fmt.Sprintf("%s/version", serverAddr)
|
||||
FormatResLog(httpClient.Get(url))
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
"github.com/gocql/gocql"
|
||||
"github.com/mainflux/mainflux"
|
||||
log "github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/readers"
|
||||
"github.com/mainflux/mainflux/readers/api"
|
||||
"github.com/mainflux/mainflux/readers/cassandra"
|
||||
thingsapi "github.com/mainflux/mainflux/things/api/grpc"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const (
|
||||
sep = ","
|
||||
|
||||
defPort = "8180"
|
||||
defCluster = "127.0.0.1"
|
||||
defKeyspace = "mainflux"
|
||||
defThingsURL = "localhost:8181"
|
||||
|
||||
envPort = "MF_CASSANDRA_READER_PORT"
|
||||
envCluster = "MF_CASSANDRA_READER_DB_CLUSTER"
|
||||
envKeyspace = "MF_CASSANDRA_READER_DB_KEYSPACE"
|
||||
envThingsURL = "MF_THINGS_URL"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
port string
|
||||
cluster string
|
||||
keyspace string
|
||||
thingsURL string
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := loadConfig()
|
||||
|
||||
logger := log.New(os.Stdout)
|
||||
|
||||
session := connectToCassandra(cfg.cluster, cfg.keyspace, logger)
|
||||
defer session.Close()
|
||||
|
||||
conn := connectToThings(cfg.thingsURL, logger)
|
||||
defer conn.Close()
|
||||
|
||||
tc := thingsapi.NewClient(conn)
|
||||
repo := newService(session, logger)
|
||||
|
||||
errs := make(chan error, 2)
|
||||
|
||||
go startHTTPServer(repo, tc, cfg.port, errs, logger)
|
||||
|
||||
go func() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, syscall.SIGINT)
|
||||
errs <- fmt.Errorf("%s", <-c)
|
||||
}()
|
||||
|
||||
err := <-errs
|
||||
logger.Error(fmt.Sprintf("Cassandra reader service terminated: %s", err))
|
||||
}
|
||||
|
||||
func loadConfig() config {
|
||||
return config{
|
||||
port: mainflux.Env(envPort, defPort),
|
||||
cluster: mainflux.Env(envCluster, defCluster),
|
||||
keyspace: mainflux.Env(envKeyspace, defKeyspace),
|
||||
thingsURL: mainflux.Env(envThingsURL, defThingsURL),
|
||||
}
|
||||
}
|
||||
|
||||
func connectToCassandra(cluster, keyspace string, logger log.Logger) *gocql.Session {
|
||||
session, err := cassandra.Connect(strings.Split(cluster, sep), keyspace)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to connect to Cassandra cluster: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
func connectToThings(url string, logger log.Logger) *grpc.ClientConn {
|
||||
conn, err := grpc.Dial(url, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func newService(session *gocql.Session, logger log.Logger) readers.MessageRepository {
|
||||
repo := cassandra.New(session)
|
||||
repo = api.LoggingMiddleware(repo, logger)
|
||||
repo = api.MetricsMiddleware(
|
||||
repo,
|
||||
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: "cassandra",
|
||||
Subsystem: "message_reader",
|
||||
Name: "request_count",
|
||||
Help: "Number of requests received.",
|
||||
}, []string{"method"}),
|
||||
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
||||
Namespace: "cassandra",
|
||||
Subsystem: "message_reader",
|
||||
Name: "request_latency_microseconds",
|
||||
Help: "Total duration of requests in microseconds.",
|
||||
}, []string{"method"}),
|
||||
)
|
||||
|
||||
return repo
|
||||
}
|
||||
|
||||
func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, port string, errs chan error, logger log.Logger) {
|
||||
p := fmt.Sprintf(":%s", port)
|
||||
logger.Info(fmt.Sprintf("Cassandra reader service started, exposed port %s", port))
|
||||
errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, "cassandra-reader"))
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
"github.com/gocql/gocql"
|
||||
"github.com/mainflux/mainflux"
|
||||
log "github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/writers"
|
||||
"github.com/mainflux/mainflux/writers/cassandra"
|
||||
nats "github.com/nats-io/go-nats"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
sep = ","
|
||||
|
||||
defNatsURL = nats.DefaultURL
|
||||
defPort = "8180"
|
||||
defCluster = "127.0.0.1"
|
||||
defKeyspace = "mainflux"
|
||||
|
||||
envNatsURL = "MF_NATS_URL"
|
||||
envPort = "MF_CASSANDRA_WRITER_PORT"
|
||||
envCluster = "MF_CASSANDRA_WRITER_DB_CLUSTER"
|
||||
envKeyspace = "MF_CASSANDRA_WRITER_DB_KEYSPACE"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
natsURL string
|
||||
port string
|
||||
cluster string
|
||||
keyspace string
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := loadConfig()
|
||||
|
||||
logger := log.New(os.Stdout)
|
||||
|
||||
nc := connectToNATS(cfg.natsURL, logger)
|
||||
defer nc.Close()
|
||||
|
||||
session := connectToCassandra(cfg.cluster, cfg.keyspace, logger)
|
||||
defer session.Close()
|
||||
|
||||
repo := newService(session, logger)
|
||||
if err := writers.Start(nc, logger, repo); err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to create Cassandra writer: %s", err))
|
||||
}
|
||||
|
||||
errs := make(chan error, 2)
|
||||
|
||||
go startHTTPServer(cfg.port, errs, logger)
|
||||
|
||||
go func() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, syscall.SIGINT)
|
||||
errs <- fmt.Errorf("%s", <-c)
|
||||
}()
|
||||
|
||||
err := <-errs
|
||||
logger.Error(fmt.Sprintf("Cassandra writer service terminated: %s", err))
|
||||
}
|
||||
|
||||
func loadConfig() config {
|
||||
return config{
|
||||
natsURL: mainflux.Env(envNatsURL, defNatsURL),
|
||||
port: mainflux.Env(envPort, defPort),
|
||||
cluster: mainflux.Env(envCluster, defCluster),
|
||||
keyspace: mainflux.Env(envKeyspace, defKeyspace),
|
||||
}
|
||||
}
|
||||
|
||||
func connectToNATS(url string, logger log.Logger) *nats.Conn {
|
||||
nc, err := nats.Connect(url)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to connect to NATS: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return nc
|
||||
}
|
||||
|
||||
func connectToCassandra(cluster, keyspace string, logger log.Logger) *gocql.Session {
|
||||
session, err := cassandra.Connect(strings.Split(cluster, sep), keyspace)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to connect to Cassandra cluster: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
func newService(session *gocql.Session, logger log.Logger) writers.MessageRepository {
|
||||
repo := cassandra.New(session)
|
||||
repo = writers.LoggingMiddleware(repo, logger)
|
||||
repo = writers.MetricsMiddleware(
|
||||
repo,
|
||||
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: "cassandra",
|
||||
Subsystem: "message_writer",
|
||||
Name: "request_count",
|
||||
Help: "Number of requests received.",
|
||||
}, []string{"method"}),
|
||||
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
||||
Namespace: "cassandra",
|
||||
Subsystem: "message_writer",
|
||||
Name: "request_latency_microseconds",
|
||||
Help: "Total duration of requests in microseconds.",
|
||||
}, []string{"method"}),
|
||||
)
|
||||
|
||||
return repo
|
||||
}
|
||||
|
||||
func startHTTPServer(port string, errs chan error, logger log.Logger) {
|
||||
p := fmt.Sprintf(":%s", port)
|
||||
logger.Info(fmt.Sprintf("Cassandra writer service started, exposed port %s", port))
|
||||
errs <- http.ListenAndServe(p, cassandra.MakeHandler())
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/mainflux/mainflux/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
conf := struct {
|
||||
host string
|
||||
port int
|
||||
insecure bool
|
||||
}{
|
||||
"localhost",
|
||||
0,
|
||||
false,
|
||||
}
|
||||
|
||||
// Root
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "mainflux-cli",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
var proto string
|
||||
|
||||
if conf.insecure {
|
||||
proto = "http"
|
||||
} else {
|
||||
proto = "https"
|
||||
cli.SetCerts()
|
||||
}
|
||||
|
||||
cli.SetServerAddr(proto, conf.host, conf.port)
|
||||
},
|
||||
}
|
||||
|
||||
// API commands
|
||||
versionCmd := cli.NewVersionCmd()
|
||||
usersCmd := cli.NewUsersCmd()
|
||||
thingsCmd := cli.NewThingsCmd()
|
||||
channelsCmd := cli.NewChannelsCmd()
|
||||
messagesCmd := cli.NewMessagesCmd()
|
||||
|
||||
// Root Commands
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
rootCmd.AddCommand(usersCmd)
|
||||
rootCmd.AddCommand(thingsCmd)
|
||||
rootCmd.AddCommand(channelsCmd)
|
||||
rootCmd.AddCommand(messagesCmd)
|
||||
|
||||
// Root Flags
|
||||
rootCmd.PersistentFlags().StringVarP(
|
||||
&conf.host, "host", "m", conf.host, "HTTP Host address")
|
||||
rootCmd.PersistentFlags().IntVarP(
|
||||
&conf.port, "port", "p", conf.port, "HTTP Host Port")
|
||||
rootCmd.PersistentFlags().BoolVarP(
|
||||
&conf.insecure, "insecure", "i", false, "do not use TLS")
|
||||
|
||||
// Client and Channels Flags
|
||||
rootCmd.PersistentFlags().IntVarP(
|
||||
&cli.Limit, "limit", "l", 100, "limit query parameter")
|
||||
rootCmd.PersistentFlags().IntVarP(
|
||||
&cli.Offset, "offset", "o", 0, "offset query parameter")
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -52,7 +59,7 @@ func main() {
|
||||
|
||||
conn, err := grpc.Dial(cfg.ThingsURL, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to connect to users service: %s", err))
|
||||
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
influxdata "github.com/influxdata/influxdb/client/v2"
|
||||
"github.com/mainflux/mainflux"
|
||||
log "github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/readers"
|
||||
"github.com/mainflux/mainflux/readers/api"
|
||||
"github.com/mainflux/mainflux/readers/influxdb"
|
||||
thingsapi "github.com/mainflux/mainflux/things/api/grpc"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const (
|
||||
defThingsURL = "localhost:8181"
|
||||
defPort = "8180"
|
||||
defDBName = "mainflux"
|
||||
defDBHost = "localhost"
|
||||
defDBPort = "8086"
|
||||
defDBUser = "mainflux"
|
||||
defDBPass = "mainflux"
|
||||
|
||||
envThingsURL = "MF_THINGS_URL"
|
||||
envPort = "MF_INFLUX_READER_PORT"
|
||||
envDBName = "MF_INFLUX_READER_DB_NAME"
|
||||
envDBHost = "MF_INFLUX_READER_DB_HOST"
|
||||
envDBPort = "MF_INFLUX_READER_DB_PORT"
|
||||
envDBUser = "MF_INFLUX_READER_DB_USER"
|
||||
envDBPass = "MF_INFLUX_READER_DB_PASS"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
ThingsURL string
|
||||
Port string
|
||||
DBName string
|
||||
DBHost string
|
||||
DBPort string
|
||||
DBUser string
|
||||
DBPass string
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg, clientCfg := loadConfigs()
|
||||
logger := log.New(os.Stdout)
|
||||
|
||||
conn := connectToThings(cfg.ThingsURL, logger)
|
||||
defer conn.Close()
|
||||
|
||||
tc := thingsapi.NewClient(conn)
|
||||
|
||||
client, err := influxdata.NewHTTPClient(clientCfg)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to create InfluxDB client: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
repo, err := influxdb.New(client, cfg.DBName)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to create InfluxDB writer: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
errs := make(chan error, 2)
|
||||
go func() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, syscall.SIGINT)
|
||||
errs <- fmt.Errorf("%s", <-c)
|
||||
}()
|
||||
|
||||
go startHTTPServer(repo, tc, cfg.Port, logger, errs)
|
||||
|
||||
err = <-errs
|
||||
logger.Error(fmt.Sprintf("InfluxDB writer service terminated: %s", err))
|
||||
}
|
||||
|
||||
func loadConfigs() (config, influxdata.HTTPConfig) {
|
||||
cfg := config{
|
||||
ThingsURL: mainflux.Env(envThingsURL, defThingsURL),
|
||||
Port: mainflux.Env(envPort, defPort),
|
||||
DBName: mainflux.Env(envDBName, defDBName),
|
||||
DBHost: mainflux.Env(envDBHost, defDBHost),
|
||||
DBPort: mainflux.Env(envDBPort, defDBPort),
|
||||
DBUser: mainflux.Env(envDBUser, defDBUser),
|
||||
DBPass: mainflux.Env(envDBPass, defDBPass),
|
||||
}
|
||||
|
||||
clientCfg := influxdata.HTTPConfig{
|
||||
Addr: fmt.Sprintf("http://%s:%s", cfg.DBHost, cfg.DBPort),
|
||||
Username: cfg.DBUser,
|
||||
Password: cfg.DBPass,
|
||||
}
|
||||
|
||||
return cfg, clientCfg
|
||||
}
|
||||
|
||||
func connectToThings(url string, logger log.Logger) *grpc.ClientConn {
|
||||
conn, err := grpc.Dial(url, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func newService(client influxdata.Client, logger log.Logger) readers.MessageRepository {
|
||||
repo, _ := influxdb.New(client, "mainflux")
|
||||
repo = api.LoggingMiddleware(repo, logger)
|
||||
repo = api.MetricsMiddleware(
|
||||
repo,
|
||||
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: "influxdb",
|
||||
Subsystem: "message_reader",
|
||||
Name: "request_count",
|
||||
Help: "Number of requests received.",
|
||||
}, []string{"method"}),
|
||||
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
||||
Namespace: "influxdb",
|
||||
Subsystem: "message_reader",
|
||||
Name: "request_latency_microseconds",
|
||||
Help: "Total duration of requests in microseconds.",
|
||||
}, []string{"method"}),
|
||||
)
|
||||
|
||||
return repo
|
||||
}
|
||||
|
||||
func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, port string, logger log.Logger, errs chan error) {
|
||||
p := fmt.Sprintf(":%s", port)
|
||||
logger.Info(fmt.Sprintf("InfluxDB reader service started, exposed port %s", port))
|
||||
errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, "influxdb-reader"))
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -18,9 +25,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
name = "influxdb-writer"
|
||||
senML = "out.senml"
|
||||
prefix = "http://"
|
||||
defNatsURL = nats.DefaultURL
|
||||
defPort = "8180"
|
||||
defPointName = "messages"
|
||||
@@ -76,7 +80,7 @@ func main() {
|
||||
counter, latency := makeMetrics()
|
||||
repo = writers.LoggingMiddleware(repo, logger)
|
||||
repo = writers.MetricsMiddleware(repo, counter, latency)
|
||||
if err := writers.Start(name, nc, logger, repo); err != nil {
|
||||
if err := writers.Start(nc, logger, repo); err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to start message writer: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -106,7 +110,7 @@ func loadConfigs() (config, influxdata.HTTPConfig) {
|
||||
}
|
||||
|
||||
clientCfg := influxdata.HTTPConfig{
|
||||
Addr: fmt.Sprintf("%s%s:%s", prefix, cfg.DBHost, cfg.DBPort),
|
||||
Addr: fmt.Sprintf("http://%s:%s", cfg.DBHost, cfg.DBPort),
|
||||
Username: cfg.DBUser,
|
||||
Password: cfg.DBPass,
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
"github.com/mainflux/mainflux"
|
||||
log "github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/readers"
|
||||
"github.com/mainflux/mainflux/readers/api"
|
||||
"github.com/mainflux/mainflux/readers/mongodb"
|
||||
thingsapi "github.com/mainflux/mainflux/things/api/grpc"
|
||||
"github.com/mongodb/mongo-go-driver/mongo"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const (
|
||||
defThingsURL = "localhost:8181"
|
||||
defPort = "8180"
|
||||
defDBName = "mainflux"
|
||||
defDBHost = "localhost"
|
||||
defDBPort = "27017"
|
||||
|
||||
envThingsURL = "MF_THINGS_URL"
|
||||
envPort = "MF_MONGO_READER_PORT"
|
||||
envDBName = "MF_MONGO_READER_DB_NAME"
|
||||
envDBHost = "MF_MONGO_READER_DB_HOST"
|
||||
envDBPort = "MF_MONGO_READER_DB_PORT"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
thingsURL string
|
||||
port string
|
||||
dbName string
|
||||
dbHost string
|
||||
dbPort string
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := loadConfigs()
|
||||
logger := log.New(os.Stdout)
|
||||
|
||||
conn := connectToThings(cfg.thingsURL, logger)
|
||||
defer conn.Close()
|
||||
|
||||
tc := thingsapi.NewClient(conn)
|
||||
|
||||
db := connectToMongoDB(cfg.dbHost, cfg.dbPort, cfg.dbName, logger)
|
||||
|
||||
repo := newService(db, logger)
|
||||
|
||||
errs := make(chan error, 2)
|
||||
go func() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, syscall.SIGINT)
|
||||
errs <- fmt.Errorf("%s", <-c)
|
||||
}()
|
||||
|
||||
go startHTTPServer(repo, tc, cfg.port, logger, errs)
|
||||
|
||||
err := <-errs
|
||||
logger.Error(fmt.Sprintf("MongoDB reader service terminated: %s", err))
|
||||
}
|
||||
|
||||
func loadConfigs() config {
|
||||
return config{
|
||||
thingsURL: mainflux.Env(envThingsURL, defThingsURL),
|
||||
port: mainflux.Env(envPort, defPort),
|
||||
dbName: mainflux.Env(envDBName, defDBName),
|
||||
dbHost: mainflux.Env(envDBHost, defDBHost),
|
||||
dbPort: mainflux.Env(envDBPort, defDBPort),
|
||||
}
|
||||
}
|
||||
|
||||
func connectToMongoDB(host, port, name string, logger log.Logger) *mongo.Database {
|
||||
client, err := mongo.Connect(context.Background(), fmt.Sprintf("mongodb://%s:%s", host, port), nil)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to connect to database: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return client.Database(name)
|
||||
}
|
||||
|
||||
func connectToThings(url string, logger log.Logger) *grpc.ClientConn {
|
||||
conn, err := grpc.Dial(url, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func newService(db *mongo.Database, logger log.Logger) readers.MessageRepository {
|
||||
repo := mongodb.New(db)
|
||||
repo = api.LoggingMiddleware(repo, logger)
|
||||
repo = api.MetricsMiddleware(
|
||||
repo,
|
||||
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: "mongodb",
|
||||
Subsystem: "message_reader",
|
||||
Name: "request_count",
|
||||
Help: "Number of requests received.",
|
||||
}, []string{"method"}),
|
||||
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
||||
Namespace: "mongodb",
|
||||
Subsystem: "message_reader",
|
||||
Name: "request_latency_microseconds",
|
||||
Help: "Total duration of requests in microseconds.",
|
||||
}, []string{"method"}),
|
||||
)
|
||||
|
||||
return repo
|
||||
}
|
||||
|
||||
func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, port string, logger log.Logger, errs chan error) {
|
||||
p := fmt.Sprintf(":%s", port)
|
||||
logger.Info(fmt.Sprintf("Mongo reader service started, exposed port %s", port))
|
||||
errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, "cassandra-reader"))
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -19,7 +26,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
name = "mongodb-writer"
|
||||
defNatsURL = nats.DefaultURL
|
||||
defPort = "8180"
|
||||
defDBName = "mainflux"
|
||||
@@ -59,16 +65,12 @@ func main() {
|
||||
}
|
||||
|
||||
db := client.Database(cfg.DBName)
|
||||
repo, err := mongodb.New(db)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to create MongoDB writer: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
repo := mongodb.New(db)
|
||||
|
||||
counter, latency := makeMetrics()
|
||||
repo = writers.LoggingMiddleware(repo, logger)
|
||||
repo = writers.MetricsMiddleware(repo, counter, latency)
|
||||
if err := writers.Start(name, nc, logger, repo); err != nil {
|
||||
if err := writers.Start(nc, logger, repo); err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to start message writer: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
+32
-19
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -10,14 +17,16 @@ import (
|
||||
"github.com/mainflux/mainflux"
|
||||
log "github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/normalizer"
|
||||
nats "github.com/nats-io/go-nats"
|
||||
"github.com/mainflux/mainflux/normalizer/api"
|
||||
"github.com/mainflux/mainflux/normalizer/nats"
|
||||
broker "github.com/nats-io/go-nats"
|
||||
|
||||
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
defNatsURL string = nats.DefaultURL
|
||||
defNatsURL string = broker.DefaultURL
|
||||
defPort string = "8180"
|
||||
envNatsURL string = "MF_NATS_URL"
|
||||
envPort string = "MF_NORMALIZER_PORT"
|
||||
@@ -36,19 +45,37 @@ func main() {
|
||||
|
||||
logger := log.New(os.Stdout)
|
||||
|
||||
nc, err := nats.Connect(cfg.NatsURL)
|
||||
nc, err := broker.Connect(cfg.NatsURL)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to connect to NATS: %s", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
defer nc.Close()
|
||||
|
||||
svc := normalizer.New()
|
||||
svc = api.LoggingMiddleware(svc, logger)
|
||||
svc = api.MetricsMiddleware(
|
||||
svc,
|
||||
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: "normalizer",
|
||||
Subsystem: "api",
|
||||
Name: "request_count",
|
||||
Help: "Number of requests received.",
|
||||
}, []string{"method"}),
|
||||
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
||||
Namespace: "normalizer",
|
||||
Subsystem: "api",
|
||||
Name: "request_latency_microseconds",
|
||||
Help: "Total duration of requests in microseconds.",
|
||||
}, []string{"method"}),
|
||||
)
|
||||
|
||||
errs := make(chan error, 2)
|
||||
|
||||
go func() {
|
||||
p := fmt.Sprintf(":%s", cfg.Port)
|
||||
logger.Info(fmt.Sprintf("Normalizer service started, exposed port %s", cfg.Port))
|
||||
errs <- http.ListenAndServe(p, normalizer.MakeHandler())
|
||||
errs <- http.ListenAndServe(p, api.MakeHandler())
|
||||
}()
|
||||
|
||||
go func() {
|
||||
@@ -57,21 +84,7 @@ func main() {
|
||||
errs <- fmt.Errorf("%s", <-c)
|
||||
}()
|
||||
|
||||
counter := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: "normalizer",
|
||||
Subsystem: "api",
|
||||
Name: "request_count",
|
||||
Help: "Number of requests received.",
|
||||
}, []string{"method"})
|
||||
|
||||
latency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
||||
Namespace: "normalizer",
|
||||
Subsystem: "api",
|
||||
Name: "request_latency_microseconds",
|
||||
Help: "Total duration of requests in microseconds.",
|
||||
}, []string{"method"})
|
||||
|
||||
normalizer.Subscribe(nc, logger, counter, latency)
|
||||
nats.Subscribe(svc, nc, logger)
|
||||
|
||||
err = <-errs
|
||||
logger.Error(fmt.Sprintf("Normalizer service terminated: %s", err))
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"outDir": "dist",
|
||||
"assets": [
|
||||
"assets",
|
||||
"favicon.ico"
|
||||
"favicon.png"
|
||||
],
|
||||
"index": "index.html",
|
||||
"main": "main.ts",
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Route, RouterModule } from '@angular/router';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
<!--
|
||||
Copyright (c) 2018
|
||||
Mainflux
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<div *mobxAutorun>
|
||||
<div class="loading" *ngIf="uiStore.loading" fxLayoutAlign="center center">
|
||||
<mat-progress-spinner [mode]="'indeterminate'"></mat-progress-spinner>
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
.loading {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { reaction } from 'mobx';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import './rxjs-extensions.ts';
|
||||
import 'hammerjs';
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
<!--
|
||||
Copyright (c) 2018
|
||||
Mainflux
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<div class="container" style="height: 80vh" fxLayoutAlign="center center">
|
||||
<form fxLayout="column" [formGroup]="loginForm" (ngSubmit)="login()" fxFlex="30%" fxFlex.sm="70%" fxFlex.xs="90%">
|
||||
<mat-card>
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
::ng-deep mat-card-content.loginCard {
|
||||
height: 200px;
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
<!--
|
||||
Copyright (c) 2018
|
||||
Mainflux
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<div class="container" style="height: 80vh" fxLayoutAlign="center center">
|
||||
<form fxLayout="column" [formGroup]="signupForm" (ngSubmit)="signup()" fxFlex="30%" fxFlex.sm="70%" fxFlex.xs="90%">
|
||||
<mat-card>
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
::ng-deep mat-card-content.loginCard {
|
||||
height: 200px;
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
<!--
|
||||
Copyright (c) 2018
|
||||
Mainflux
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<form [formGroup]="addChannelForm" (ngSubmit)="onAddChannel()">
|
||||
<h1 *ngIf="!editMode" mat-dialog-title>Add channel</h1>
|
||||
<h1 *ngIf="editMode" mat-dialog-title>Edit channel</h1>
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Inject, OnInit, Output } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
<!--
|
||||
Copyright (c) 2018
|
||||
Mainflux
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<div class="channelsContainer" fxLayout="row" fxLayoutWrap>
|
||||
<ng-container [ngSwitch]="channelsStore.channels?.length > 0">
|
||||
<ng-container *ngSwitchCase="true">
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
.addButton {
|
||||
position: fixed;
|
||||
bottom: 12px;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
+7
@@ -1,3 +1,10 @@
|
||||
<!--
|
||||
Copyright (c) 2018
|
||||
Mainflux
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<h1 mat-dialog-title>{{data.question}}</h1>
|
||||
<div mat-dialog-actions fxLayoutAlign="end end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
|
||||
+7
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material';
|
||||
|
||||
+7
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material';
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<textarea formControlName="payload" matInput placeholder="Payload" matTextareaAutosize matAutosizeMinRows="2"
|
||||
<textarea formControlName="metadata" matInput placeholder="Metadata" matTextareaAutosize matAutosizeMinRows="2"
|
||||
matAutosizeMaxRows="5"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,7 @@ export class ThingDialogComponent implements OnInit {
|
||||
id: null,
|
||||
type: ['', [Validators.required]],
|
||||
name: ['', [Validators.required, Validators.minLength(5)]],
|
||||
payload: ['']
|
||||
metadata: ['']
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
<!--
|
||||
Copyright (c) 2018
|
||||
Mainflux
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<div class="thingsContainer" fxLayout="row" fxLayoutWrap>
|
||||
<ng-container [ngSwitch]="thingsStore.things?.length > 0">
|
||||
<ng-container *ngSwitchCase="true">
|
||||
@@ -17,9 +24,9 @@
|
||||
<mat-header-cell fxFlex="10%" *matHeaderCellDef mat-sort-header>Type</mat-header-cell>
|
||||
<mat-cell fxFlex="10%" *matCellDef="let row"> {{row.type}}</mat-cell>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="payload">
|
||||
<mat-header-cell fxFlex="35%" *matHeaderCellDef mat-sort-header>Payload</mat-header-cell>
|
||||
<mat-cell fxFlex="35%" *matCellDef="let row"> {{row.payload | json}}</mat-cell>
|
||||
<ng-container matColumnDef="metadata">
|
||||
<mat-header-cell fxFlex="35%" *matHeaderCellDef mat-sort-header>Metadata</mat-header-cell>
|
||||
<mat-cell fxFlex="35%" *matCellDef="let row"> {{row.metadata | json}}</mat-cell>
|
||||
</ng-container>
|
||||
<!-- Actions -->
|
||||
<ng-container matColumnDef="actions">
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
.addButton {
|
||||
position: fixed;
|
||||
bottom: 12px;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { toJS } from 'mobx';
|
||||
@@ -16,7 +23,7 @@ import { ChannelsStore } from '../../core/store/channels.store';
|
||||
})
|
||||
export class ThingsComponent implements OnInit {
|
||||
things: Observable<Thing[]>;
|
||||
displayedColumns = ['id', 'name', 'type', 'payload', 'actions'];
|
||||
displayedColumns = ['id', 'name', 'type', 'metadata', 'actions'];
|
||||
|
||||
constructor(
|
||||
private dialog: MatDialog,
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import {
|
||||
MatButtonModule,
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import '../../../rxjs-extensions';
|
||||
|
||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { HttpClient, HttpResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export interface Thing {
|
||||
id?: '';
|
||||
type: string;
|
||||
name: string;
|
||||
payload: string;
|
||||
metadata: string;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('ThingsStore', () => {
|
||||
const newThing: Thing = {
|
||||
name: 'new thing',
|
||||
type: 'app',
|
||||
payload: '',
|
||||
metadata: '',
|
||||
};
|
||||
|
||||
thingsStore.addThing(newThing);
|
||||
@@ -89,7 +89,7 @@ describe('ThingsStore', () => {
|
||||
const newThing: Thing = {
|
||||
name: 'new thing',
|
||||
type: 'app',
|
||||
payload: '',
|
||||
metadata: '',
|
||||
};
|
||||
|
||||
thingsStore.addThing(newThing);
|
||||
@@ -104,7 +104,7 @@ describe('ThingsStore', () => {
|
||||
const newThing: Thing = {
|
||||
name: 'new thing',
|
||||
type: 'app',
|
||||
payload: '',
|
||||
metadata: '',
|
||||
};
|
||||
|
||||
thingsStore.addThing(newThing);
|
||||
@@ -120,7 +120,7 @@ describe('ThingsStore', () => {
|
||||
const newThing: Thing = {
|
||||
name: 'new thing',
|
||||
type: 'app',
|
||||
payload: '',
|
||||
metadata: '',
|
||||
};
|
||||
|
||||
thingsStore.addThing(newThing);
|
||||
@@ -136,7 +136,7 @@ describe('ThingsStore', () => {
|
||||
const editedThing: Thing = {
|
||||
name: 'edited thing',
|
||||
type: 'app',
|
||||
payload: '',
|
||||
metadata: '',
|
||||
};
|
||||
|
||||
thingsStore.editThing(editedThing);
|
||||
@@ -151,7 +151,7 @@ describe('ThingsStore', () => {
|
||||
const editedThing: Thing = {
|
||||
name: 'edited thing',
|
||||
type: 'app',
|
||||
payload: '',
|
||||
metadata: '',
|
||||
};
|
||||
|
||||
thingsStore.editThing(editedThing);
|
||||
@@ -167,7 +167,7 @@ describe('ThingsStore', () => {
|
||||
const editedThing: Thing = {
|
||||
name: 'edited thing',
|
||||
type: 'app',
|
||||
payload: '',
|
||||
metadata: '',
|
||||
};
|
||||
|
||||
thingsStore.editThing(editedThing);
|
||||
@@ -182,7 +182,7 @@ describe('ThingsStore', () => {
|
||||
const editedThing: Thing = {
|
||||
name: 'edited thing',
|
||||
type: 'app',
|
||||
payload: '',
|
||||
metadata: '',
|
||||
};
|
||||
|
||||
thingsStore.editThing(editedThing);
|
||||
@@ -198,7 +198,7 @@ describe('ThingsStore', () => {
|
||||
const thingToBeDeleted: Thing = {
|
||||
name: 'thingToBeDeleted',
|
||||
type: 'app',
|
||||
payload: ''
|
||||
metadata: ''
|
||||
};
|
||||
|
||||
thingsStore.deleteThing(thingToBeDeleted);
|
||||
@@ -213,7 +213,7 @@ describe('ThingsStore', () => {
|
||||
const thingToBeDeleted: Thing = {
|
||||
name: 'thingToBeDeleted',
|
||||
type: 'app',
|
||||
payload: ''
|
||||
metadata: ''
|
||||
};
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ describe('ThingsStore', () => {
|
||||
const thingToBeDeleted: Thing = {
|
||||
name: 'thingToBeDeleted',
|
||||
type: 'app',
|
||||
payload: ''
|
||||
metadata: ''
|
||||
};
|
||||
|
||||
thingsStore.deleteThing(thingToBeDeleted);
|
||||
@@ -245,7 +245,7 @@ describe('ThingsStore', () => {
|
||||
const thingToBeDeleted: Thing = {
|
||||
name: 'thingToBeDeleted',
|
||||
type: 'app',
|
||||
payload: ''
|
||||
metadata: ''
|
||||
};
|
||||
|
||||
thingsStore.deleteThing(thingToBeDeleted);
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import 'rxjs/add/observable/combineLatest';
|
||||
import 'rxjs/add/observable/interval';
|
||||
import 'rxjs/add/observable/of';
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -6,7 +6,7 @@
|
||||
<base href="/">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.png">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
* Mainflux
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@import '~@angular/material/prebuilt-themes/indigo-pink.css';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// Package mainflux acts as an umbrella package containing multiple different
|
||||
// microservices and defines all shared domain concepts.
|
||||
package mainflux
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
###
|
||||
# This docker-compose file contains optional Cassandra and cassandra-reader. Since these are optional, this file is
|
||||
# dependent of docker-compose file from <project_root>/docker. In order to run
|
||||
# these optional service, execute command:
|
||||
# docker-compose -f docker/docker-compose.yml -f docker/addons/cassandra-reader/docker-compose.yml up
|
||||
# from project root.
|
||||
###
|
||||
|
||||
version: "3"
|
||||
|
||||
networks:
|
||||
docker_mainflux-base-net:
|
||||
external: true
|
||||
|
||||
services:
|
||||
cassandra-reader:
|
||||
image: mainflux/cassandra-reader:latest
|
||||
container_name: mainflux-cassandra-reader
|
||||
restart: on-failure
|
||||
environment:
|
||||
MF_THINGS_URL: things:8183
|
||||
MF_CASSANDRA_READER_PORT: 8903
|
||||
MF_CASSANDRA_READER_DB_CLUSTER: mainflux-cassandra
|
||||
MF_CASSANDRA_READER_DB_KEYSPACE: mainflux
|
||||
ports:
|
||||
- 8903:8903
|
||||
networks:
|
||||
- docker_mainflux-base-net
|
||||
@@ -0,0 +1,39 @@
|
||||
###
|
||||
# This docker-compose file contains optional Cassandra and cassandra-writer. Since these are optional, this file is
|
||||
# dependent of docker-compose file from <project_root>/docker. In order to run
|
||||
# these optional service, execute command:
|
||||
# docker-compose -f docker/docker-compose.yml -f docker/addons/cassandra-writer/docker-compose.yml up
|
||||
# from project root.
|
||||
###
|
||||
|
||||
version: "3"
|
||||
|
||||
networks:
|
||||
docker_mainflux-base-net:
|
||||
external: true
|
||||
|
||||
services:
|
||||
cassandra:
|
||||
image: cassandra:3.11.2
|
||||
container_name: mainflux-cassandra
|
||||
restart: on-failure
|
||||
networks:
|
||||
- docker_mainflux-base-net
|
||||
|
||||
cassandra-writer:
|
||||
image: mainflux/cassandra-writer:latest
|
||||
container_name: mainflux-cassandra-writer
|
||||
depends_on:
|
||||
- cassandra
|
||||
expose:
|
||||
- 8902
|
||||
restart: on-failure
|
||||
environment:
|
||||
MF_NATS_URL: nats://nats:4222
|
||||
MF_CASSANDRA_WRITER_PORT: 8902
|
||||
MF_CASSANDRA_WRITER_DB_CLUSTER: mainflux-cassandra
|
||||
MF_CASSANDRA_WRITER_DB_KEYSPACE: mainflux
|
||||
ports:
|
||||
- 8902:8902
|
||||
networks:
|
||||
- docker_mainflux-base-net
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
docker-compose -f docker/addons/cassandra-writer/docker-compose.yml up -d
|
||||
sleep 20
|
||||
docker exec mainflux-cassandra cqlsh -e "CREATE KEYSPACE IF NOT EXISTS mainflux WITH replication = {'class':'SimpleStrategy','replication_factor':'1'};"
|
||||
@@ -0,0 +1,30 @@
|
||||
###
|
||||
# This docker-compose file contains optional InfluxDB-reader service for the Mainflux
|
||||
# platform. Since this service is optional, this file is dependent on the docker-compose.yml
|
||||
# file from <project_root>/docker/. In order to run InfluxDB-reader service, core services,
|
||||
# as well as the network from the core composition, should be already running.
|
||||
###
|
||||
|
||||
version: "3"
|
||||
|
||||
networks:
|
||||
docker_mainflux-base-net:
|
||||
external: true
|
||||
|
||||
services:
|
||||
influxdb-reader:
|
||||
image: mainflux/influxdb-reader:latest
|
||||
container_name: mainflux-influxdb-reader
|
||||
restart: on-failure
|
||||
environment:
|
||||
MF_THINGS_URL: things:8183
|
||||
MF_INFLUX_READER_PORT: 8905
|
||||
MF_INFLUX_READER_DB_NAME: mainflux
|
||||
MF_INFLUX_READER_DB_HOST: mainflux-influxdb
|
||||
MF_INFLUX_READER_DB_PORT: 8086
|
||||
MF_INFLUX_READER_DB_USER: mainflux
|
||||
MF_INFLUX_READER_DB_PASS: mainflux
|
||||
ports:
|
||||
- 8905:8905
|
||||
networks:
|
||||
- docker_mainflux-base-net
|
||||
+15
-7
@@ -1,13 +1,16 @@
|
||||
###
|
||||
# This docker-compose file contains optional InfluxDB, InfluxDB-writer and Grafana services
|
||||
# for Mainflux platform. Since these are optional, this file is dependent of docker-compose file
|
||||
# from <project_root>/docker. In order to run these optional service, execute command:
|
||||
# docker-compose -f docker/docker-compose.yml -f docker/addons/influxdb/docker-compose.yml up
|
||||
# from project root.
|
||||
# for the Mainflux platform. Since this services are optional, this file is dependent on the
|
||||
# docker-compose.yml file from <project_root>/docker/. In order to run these services,
|
||||
# core services, as well as the network from the core composition, should be already running.
|
||||
###
|
||||
|
||||
version: "3"
|
||||
|
||||
networks:
|
||||
docker_mainflux-base-net:
|
||||
external: true
|
||||
|
||||
services:
|
||||
|
||||
influxdb:
|
||||
@@ -18,13 +21,14 @@ services:
|
||||
INFLUXDB_DB: mainflux
|
||||
INFLUXDB_ADMIN_USER: mainflux
|
||||
INFLUXDB_ADMIN_PASSWORD: mainflux
|
||||
networks:
|
||||
- docker_mainflux-base-net
|
||||
|
||||
influxdb-writer:
|
||||
image: mainflux/influxdb:latest
|
||||
image: mainflux/influxdb-writer:latest
|
||||
container_name: mainflux-influxdb-writer
|
||||
depends_on:
|
||||
- influxdb
|
||||
- nats
|
||||
expose:
|
||||
- 8900
|
||||
restart: on-failure
|
||||
@@ -32,12 +36,14 @@ services:
|
||||
MF_NATS_URL: nats://nats:4222
|
||||
MF_INFLUX_WRITER_PORT: 8900
|
||||
MF_INFLUX_WRITER_DB_NAME: mainflux
|
||||
MF_INFLUX_WRITER_DB_HOST: influxdb
|
||||
MF_INFLUX_WRITER_DB_HOST: mainflux-influxdb
|
||||
MF_INFLUX_WRITER_DB_PORT: 8086
|
||||
MF_INFLUX_WRITER_DB_USER: mainflux
|
||||
MF_INFLUX_WRITER_DB_PASS: mainflux
|
||||
ports:
|
||||
- 8900:8900
|
||||
networks:
|
||||
- docker_mainflux-base-net
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:5.1.3
|
||||
@@ -47,3 +53,5 @@ services:
|
||||
restart: on-failure
|
||||
ports:
|
||||
- 3001:3000
|
||||
networks:
|
||||
- docker_mainflux-base-net
|
||||
@@ -0,0 +1,31 @@
|
||||
###
|
||||
# This docker-compose file contains optional MongoDB-reader service
|
||||
# for Mainflux platform. Since these are optional, this file is dependent of docker-compose file
|
||||
# from <project_root>/docker. In order to run these optional service, execute command:
|
||||
# docker-compose -f docker/docker-compose.yml -f docker/addons/mongodb-reader/docker-compose.yml up
|
||||
# from project root. MongoDB service is defined in docker/addons/mongodb-writer/docker-compose.yml.
|
||||
###
|
||||
|
||||
version: "3"
|
||||
|
||||
networks:
|
||||
docker_mainflux-base-net:
|
||||
external: true
|
||||
|
||||
services:
|
||||
mongodb-reader:
|
||||
image: mainflux/mongodb-reader:latest
|
||||
container_name: mainflux-mongodb-reader
|
||||
expose:
|
||||
- 8901
|
||||
restart: on-failure
|
||||
environment:
|
||||
MF_THINGS_URL: things:8183
|
||||
MF_MONGO_READER_PORT: 8904
|
||||
MF_MONGO_READER_DB_NAME: mainflux
|
||||
MF_MONGO_READER_DB_HOST: mongodb
|
||||
MF_MONGO_READER_DB_PORT: 27017
|
||||
ports:
|
||||
- 8904:8904
|
||||
networks:
|
||||
- docker_mainflux-base-net
|
||||
+11
-7
@@ -2,15 +2,18 @@
|
||||
# This docker-compose file contains optional MongoDB and MongoDB-writer services
|
||||
# for Mainflux platform. Since these are optional, this file is dependent of docker-compose file
|
||||
# from <project_root>/docker. In order to run these optional service, execute command:
|
||||
# docker-compose -f docker/docker-compose.yml -f docker/addons/mongodb/docker-compose.yml up
|
||||
# docker-compose -f docker/docker-compose.yml -f docker/addons/mongodb-writer/docker-compose.yml up
|
||||
# from project root. MongoDB default port (27017) is exposed, so you can use various tools for database
|
||||
# inspection and data visualization.
|
||||
###
|
||||
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
networks:
|
||||
docker_mainflux-base-net:
|
||||
external: true
|
||||
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo:3.6-jessie
|
||||
container_name: mainflux-mongodb
|
||||
@@ -21,15 +24,14 @@ services:
|
||||
MONGO_INITDB_DATABASE: mainflux
|
||||
ports:
|
||||
- 27017:27017
|
||||
networks:
|
||||
- docker_mainflux-base-net
|
||||
|
||||
mongo-writer:
|
||||
image: mainflux/mongodb:latest
|
||||
mongodb-writer:
|
||||
image: mainflux/mongodb-writer:latest
|
||||
container_name: mainflux-mongodb-writer
|
||||
depends_on:
|
||||
- mongodb
|
||||
- nats
|
||||
expose:
|
||||
- 8901
|
||||
restart: on-failure
|
||||
environment:
|
||||
MF_NATS_URL: nats://nats:4222
|
||||
@@ -39,3 +41,5 @@ services:
|
||||
MF_MONGO_WRITER_DB_PORT: 27017
|
||||
ports:
|
||||
- 8901:8901
|
||||
networks:
|
||||
- docker_mainflux-base-net
|
||||
@@ -8,6 +8,10 @@
|
||||
|
||||
version: "3"
|
||||
|
||||
networks:
|
||||
mainflux-base-net:
|
||||
driver: bridge
|
||||
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:1.13-alpine
|
||||
@@ -22,11 +26,15 @@ services:
|
||||
- 80:80
|
||||
- 443:443
|
||||
- 8883:8883
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
nats:
|
||||
image: nats:1.0.2
|
||||
image: nats:1.1.0
|
||||
container_name: mainflux-nats
|
||||
restart: on-failure
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
users-db:
|
||||
image: postgres:10.2-alpine
|
||||
@@ -36,6 +44,8 @@ services:
|
||||
POSTGRES_USER: mainflux
|
||||
POSTGRES_PASSWORD: mainflux
|
||||
POSTGRES_DB: users
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
users:
|
||||
image: mainflux/users:latest
|
||||
@@ -57,6 +67,8 @@ services:
|
||||
MF_USERS_SECRET: secret
|
||||
ports:
|
||||
- 8180:8180
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
things-db:
|
||||
image: postgres:10.2-alpine
|
||||
@@ -66,6 +78,8 @@ services:
|
||||
POSTGRES_USER: mainflux
|
||||
POSTGRES_PASSWORD: mainflux
|
||||
POSTGRES_DB: things
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
things:
|
||||
image: mainflux/things:latest
|
||||
@@ -89,16 +103,22 @@ services:
|
||||
MF_THINGS_SECRET: secret
|
||||
ports:
|
||||
- 8182:8182
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
normalizer:
|
||||
image: mainflux/normalizer:latest
|
||||
container_name: mainflux-normalizer
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- nats
|
||||
expose:
|
||||
- 8184
|
||||
environment:
|
||||
MF_NATS_URL: nats://nats:4222
|
||||
MF_NORMALIZER_PORT: 8184
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
dashflux:
|
||||
image: mainflux/dashflux:latest
|
||||
@@ -106,12 +126,15 @@ services:
|
||||
restart: on-failure
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
ws-adapter:
|
||||
image: mainflux/ws:latest
|
||||
container_name: mainflux-ws
|
||||
depends_on:
|
||||
- things
|
||||
- nats
|
||||
expose:
|
||||
- 8186
|
||||
restart: on-failure
|
||||
@@ -121,12 +144,15 @@ services:
|
||||
MF_THINGS_URL: things:8183
|
||||
ports:
|
||||
- 8186:8186
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
http-adapter:
|
||||
image: mainflux/http:latest
|
||||
container_name: mainflux-http
|
||||
depends_on:
|
||||
- things
|
||||
- nats
|
||||
restart: on-failure
|
||||
expose:
|
||||
- 8185
|
||||
@@ -136,18 +162,32 @@ services:
|
||||
MF_THINGS_URL: things:8183
|
||||
ports:
|
||||
- 8185:8185
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
redis:
|
||||
image: redis:4.0.9-alpine
|
||||
container_name: mainflux-redis
|
||||
restart: on-failure
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
mqtt-adapter:
|
||||
image: mainflux/mqtt:latest
|
||||
container_name: mainflux-mqtt
|
||||
depends_on:
|
||||
- things
|
||||
- nats
|
||||
- redis
|
||||
restart: on-failure
|
||||
environment:
|
||||
MF_MQTT_ADAPTER_PORT: 1883
|
||||
MF_MQTT_WS_PORT: 8880
|
||||
MF_MQTT_REDIS_HOST: redis
|
||||
MF_NATS_URL: nats://nats:4222
|
||||
MF_THINGS_URL: things:8183
|
||||
ports:
|
||||
- 1883:1883
|
||||
- 8880:8880
|
||||
networks:
|
||||
- mainflux-base-net
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
## Getting Mainflux
|
||||
|
||||
Mainflux can be fetched from official [Mainflux GitHub repository](https://github.com/Mainflux/mainflux):
|
||||
|
||||
```
|
||||
go get github.com/mainflux/mainflux
|
||||
cd $GOPATH/src/github.com/mainflux/mainflux
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
### Prerequisites
|
||||
Make sure that you have [Protocol Buffers](https://developers.google.com/protocol-buffers/) compiler (`protoc`) installed.
|
||||
|
||||
[Go Protobuf](https://github.com/golang/protobuf) installation instructions are [here](https://github.com/golang/protobuf#installation).
|
||||
Go Protobuf uses C bindings, so you will need to install [C++ protobuf](https://github.com/google/protobuf) as a prerequisite.
|
||||
|
||||
### Build All Services
|
||||
|
||||
Use `GNU Make` tool to build all mainflux services:
|
||||
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
Build artefacts will be put in the `build` directory.
|
||||
|
||||
> N.B. All Mainflux services are built as a statically linked binaries. This way they can be portable (transfered to any platform just by placing them there and running them) as they contain all needed libraries and do not relay on system shared libs. This helps creating [FROM scratch](https://hub.docker.com/_/scratch/) dockers.
|
||||
|
||||
### Build Individual Microservice
|
||||
Individual microservices can be built with command:
|
||||
|
||||
```
|
||||
make <microservice_name>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
make http
|
||||
```
|
||||
|
||||
will build HTTP Adapter microservice.
|
||||
|
||||
### Building Dockers
|
||||
|
||||
Dockers can be built with:
|
||||
|
||||
```
|
||||
make dockers
|
||||
```
|
||||
|
||||
or individually with
|
||||
|
||||
```
|
||||
make docker_<microservice_name>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
make docker_http
|
||||
```
|
||||
|
||||
> N.B. Mainflux creates `FROM scratch` docker containers which as compact and small in size.
|
||||
|
||||
> N.B. The `things-db` and `users-db` containers are built from a vanilla PostgreSQL Docker image downloaded from Docker Hub which does not persist the data when these containers are rebuilt. Thus, __rebuilding of all Docker containers with `make dockers` or rebuilding the `things-db` and `users-db` containers separately with `make docker_things-db` and `make docker_users-db` respectively, will cause data loss. All your users, things, channels and connections between them will be lost!__ As we use this setup only for development, we don't guarantee any permanent data persistence. If you need to retain the data between the container rebuilds you can attach volume to the `things-db` and `users-db` containers. Check the official docs on how to use volumes [here](https
|
||||
://docs.docker.com/storage/volumes/) and [here](https://docs.docker.com/compose/compose-file/#volumes).
|
||||
|
||||
### MQTT Microservice
|
||||
MQTT Microservice in Mainflux is special, as it is currently the only microservice written in NodeJS. It is not compiled,
|
||||
but node modules need to be downloaded in order to start the service:
|
||||
|
||||
```
|
||||
cd mqtt
|
||||
npm install
|
||||
```
|
||||
|
||||
Note that there is a shorthand for doing these commands with `make` tool:
|
||||
|
||||
```
|
||||
make mqtt
|
||||
```
|
||||
|
||||
After that MQTT Adapter can be started from top directory (as it needs to find `*.proto` files) with:
|
||||
```
|
||||
node mqtt/mqtt.js
|
||||
```
|
||||
|
||||
### Protobuf
|
||||
Aforementioned `make` (which is an alias for `make all` target) is calling `protoc` command prior to compiling individual microservices.
|
||||
|
||||
To do this by hand, execute:
|
||||
|
||||
```
|
||||
protoc --go_out=plugins=grpc:. *.proto
|
||||
```
|
||||
|
||||
A shorthand to do this via `make` tool is:
|
||||
|
||||
```
|
||||
make proto
|
||||
```
|
||||
|
||||
> N.B. This must be done one time in the beginning in order to generate protobuf Go structures needed for the build.
|
||||
|
||||
### Cross-compiling for ARM
|
||||
Mainflux can be compiled for ARM platform and run on Raspberry Pi or other similar IoT gateways.
|
||||
|
||||
Following the instructions [here](https://dave.cheney.net/2015/08/22/cross-compilation-with-go-1-5) or [here](https://www.alexruf.net/golang/arm/raspberrypi/2016/01/16/cross-compile-with-go-1-5-for-raspberry-pi.html) as well as information
|
||||
found [here](https://github.com/golang/go/wiki/GoArm), environment variables `GOARCH=arm` and `GOARM=7` must be set for the compilation.
|
||||
|
||||
Cross-compilation for ARM with Mainflux make:
|
||||
|
||||
```
|
||||
GOOS=linux GOARCH=arm GOARM=7 make
|
||||
```
|
||||
|
||||
## Running tests
|
||||
To run all of the test you can execute:
|
||||
```
|
||||
make test
|
||||
```
|
||||
Dockertest is used for the test, so to run the tests you will need the Docker deamon/service running.
|
||||
|
||||
## Installing
|
||||
Installing Go binaries is simple: just move them from `build` to `$GOBIN` (do not fortget to add `$GOBIN` to your `$PATH`).
|
||||
|
||||
You can execute:
|
||||
|
||||
```
|
||||
make install
|
||||
```
|
||||
|
||||
which will do this copy of binaries.
|
||||
|
||||
> N.B. Only Go binaries will be installed this way. MQTT adapter is NodeJS script and will stay in `mqtt` dir.
|
||||
|
||||
## Deployment
|
||||
|
||||
### Prerequisites
|
||||
Mainflux depends on several infrastructureal services, notably [NATS](https://www.nats.io/) broker and [PostgreSQL](https://www.postgresql.org/) database.
|
||||
|
||||
#### NATS
|
||||
Mainflux uses NATS as it's central message bus. For development purposes (when not run via Docker), it expects that NATS is installed on the local system.
|
||||
|
||||
To do this execute:
|
||||
|
||||
```
|
||||
go get github.com/nats-io/go-nats
|
||||
```
|
||||
|
||||
This will install `gnatsd` binary that can be simply run by invoking
|
||||
|
||||
```
|
||||
gnatsd
|
||||
```
|
||||
|
||||
#### PostgreSQL
|
||||
Mainflux uses PostgreSQL to store metadata (`users`, `things` and `channels` entities alongside with authorization tokens).
|
||||
It expects that PostgreSQL DB is installed, setu-up and running on the local system.
|
||||
|
||||
Inflormation how to set-up (prepare) PostgreSQL database can be found [here](https://support.rackspace.com/how-to/postgresql-creating-and-dropping-roles/),
|
||||
and it is done by executing following commands:
|
||||
|
||||
```
|
||||
# Create `users` and `things` databases
|
||||
sudo -u postgres createdb users
|
||||
sudo -u postgres createdb things
|
||||
|
||||
# Set-up Postgres roles
|
||||
sudo su - postgres
|
||||
psql -U postgres
|
||||
postgres=# CREATE ROLE mainflux WITH LOGIN ENCRYPTED PASSWORD 'mainflux';
|
||||
postgres=# ALTER USER mainflux WITH LOGIN ENCRYPTED PASSWORD 'mainflux';
|
||||
```
|
||||
|
||||
### Mainflux Services
|
||||
Running of the Mainflux microservices can be tricky, as there is a lot of them and each demand config in the form of environment variables.
|
||||
|
||||
Whole system (set of microservices) can be run with one command:
|
||||
|
||||
```
|
||||
make run
|
||||
```
|
||||
|
||||
which will properly configure and run all microservices.
|
||||
|
||||
Please assure that MQTT microservice has `node_modules` installed, as explained in _MQTT Microservice_ chapter.
|
||||
|
||||
> N.B. `make run` actually calls helper script `scripts/run.sh`, so you can inspect this script for the details.
|
||||
+28
-2
@@ -118,7 +118,7 @@ Content-Length: 1105
|
||||
"id": "cb63f852-2d48-44f0-a0cf-e450496c6c92",
|
||||
"type": "app",
|
||||
"name": "myapp",
|
||||
"key": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MjMzNTYzOTcsImlzcyI6Im1haW5mbHV4Iiwic3ViIjoiY2I2M2Y4NTItMmQ0OC00NGYwLWEwY2YtZTQ1MDQ5NmM2YzkyIn0.FE6DWB3yJmBb8uojpQJaKUEbD0Elrjx0HhJA28bVzkU"
|
||||
"key": "cbf02d60-72f2-4180-9f82-2c957db929d1"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -282,6 +282,32 @@ If you are not able to send custom headers in your handshake request, send it as
|
||||
query parameter `authorization`. Then your path should look like this
|
||||
`/channels/<channel_id>/messages?authorization=<thing_auth_key>`.
|
||||
|
||||
If you are using the docker environment prepend the url with `ws`. So for example
|
||||
`/ws/channels/<channel_id>/messages?authorization=<thing_auth_key>`
|
||||
|
||||
#### Basic nodejs example
|
||||
|
||||
```javascript
|
||||
const WebSocket = require('ws');
|
||||
|
||||
// do not verify self-signed certificates if you are using one
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
||||
|
||||
// cbf02d60-72f2-4180-9f82-2c957db929d1 is an example of a thing_auth_key
|
||||
const ws = new WebSocket('wss://localhost/ws/channels/1/messages?authorization=cbf02d60-72f2-4180-9f82-2c957db929d1')
|
||||
|
||||
ws.on('open', () => {
|
||||
ws.send('something')
|
||||
})
|
||||
|
||||
ws.on('message', (data) => {
|
||||
console.log(data)
|
||||
})
|
||||
ws.on('error', (e) => {
|
||||
console.log(e)
|
||||
})
|
||||
```
|
||||
|
||||
### MQTT
|
||||
|
||||
To send and receive messages over MQTT you could use [Mosquitto tools](https://mosquitto.org),
|
||||
@@ -290,7 +316,7 @@ or [Paho](https://www.eclipse.org/paho/) if you want to use MQTT over WebSocket.
|
||||
To publish message over channel, thing should call following command:
|
||||
|
||||
```
|
||||
mosquitto_pub -u <thing_id> -P <thing_key> -t channels/<channel_id>/messages -h localhost -m [{"bn":"some-base-name:","bt":1.276020076001e+09, "bu":"A","bver":5, "n":"voltage","u":"V","v":120.1}, {"n":"current","t":-5,"v":1.2}, {"n":"current","t":-4,"v":1.3}]
|
||||
mosquitto_pub -u <thing_id> -P <thing_key> -t channels/<channel_id>/messages -h localhost -m '[{"bn":"some-base-name:","bt":1.276020076001e+09, "bu":"A","bver":5, "n":"voltage","u":"V","v":120.1}, {"n":"current","t":-5,"v":1.2}, {"n":"current","t":-4,"v":1.3}]'
|
||||
```
|
||||
|
||||
To subscribe to channel, thing should call following command:
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mainflux
|
||||
|
||||
import "os"
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// Package http contains the domain concept definitions needed to support
|
||||
// Mainflux http adapter service functionality.
|
||||
package http
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// Package api contains API-related concerns: endpoint definitions, middlewares
|
||||
// and all resource representations.
|
||||
package api
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package api_test
|
||||
|
||||
import (
|
||||
@@ -5,6 +12,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -49,59 +57,74 @@ func (tr testRequest) make() (*http.Response, error) {
|
||||
}
|
||||
|
||||
func TestPublish(t *testing.T) {
|
||||
id := uint64(1)
|
||||
chanID := "1"
|
||||
invalidID := "wrong"
|
||||
contentType := "application/senml+json"
|
||||
token := "auth_token"
|
||||
invalidToken := "invalid_token"
|
||||
msg := `[{"n":"current","t":-1,"v":1.6}]`
|
||||
|
||||
id, _ := strconv.ParseUint(chanID, 10, 64)
|
||||
thingsClient := mocks.NewThingsClient(map[string]uint64{token: id})
|
||||
pub := newService()
|
||||
ts := newHTTPServer(pub, thingsClient)
|
||||
defer ts.Close()
|
||||
|
||||
cases := map[string]struct {
|
||||
chanID uint64
|
||||
chanID string
|
||||
msg string
|
||||
contentType string
|
||||
auth string
|
||||
status int
|
||||
}{
|
||||
"publish message": {
|
||||
chanID: id,
|
||||
chanID: chanID,
|
||||
msg: msg,
|
||||
contentType: contentType,
|
||||
auth: token,
|
||||
status: http.StatusAccepted,
|
||||
},
|
||||
"publish message without authorization token": {
|
||||
chanID: id,
|
||||
chanID: chanID,
|
||||
msg: msg,
|
||||
contentType: contentType,
|
||||
auth: "",
|
||||
status: http.StatusForbidden,
|
||||
},
|
||||
"publish message with invalid authorization token": {
|
||||
chanID: id,
|
||||
chanID: chanID,
|
||||
msg: msg,
|
||||
contentType: contentType,
|
||||
auth: invalidToken,
|
||||
status: http.StatusForbidden,
|
||||
},
|
||||
"publish message without content type": {
|
||||
chanID: id,
|
||||
chanID: chanID,
|
||||
msg: msg,
|
||||
contentType: "",
|
||||
auth: token,
|
||||
status: http.StatusAccepted,
|
||||
},
|
||||
"publish message to wrong channel": {
|
||||
chanID: invalidID,
|
||||
msg: msg,
|
||||
contentType: contentType,
|
||||
auth: token,
|
||||
status: http.StatusNotFound,
|
||||
},
|
||||
"publish message unable to authorize": {
|
||||
chanID: chanID,
|
||||
msg: msg,
|
||||
contentType: contentType,
|
||||
auth: mocks.ServiceErrToken,
|
||||
status: http.StatusServiceUnavailable,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
req := testRequest{
|
||||
client: ts.Client(),
|
||||
method: http.MethodPost,
|
||||
url: fmt.Sprintf("%s/channels/%d/messages", ts.URL, tc.chanID),
|
||||
url: fmt.Sprintf("%s/channels/%s/messages", ts.URL, tc.chanID),
|
||||
contentType: tc.contentType,
|
||||
token: tc.auth,
|
||||
body: strings.NewReader(tc.msg),
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// +build !test
|
||||
|
||||
package api
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// +build !test
|
||||
|
||||
package api
|
||||
|
||||
+10
-4
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
@@ -21,13 +28,12 @@ const protocol = "http"
|
||||
|
||||
var (
|
||||
errMalformedData = errors.New("malformed SenML data")
|
||||
errNotFound = errors.New("non-existent entity")
|
||||
auth mainflux.ThingsServiceClient
|
||||
)
|
||||
|
||||
// MakeHandler returns a HTTP handler for API endpoints.
|
||||
func MakeHandler(svc mainflux.MessagePublisher, cc mainflux.ThingsServiceClient) http.Handler {
|
||||
auth = cc
|
||||
func MakeHandler(svc mainflux.MessagePublisher, tc mainflux.ThingsServiceClient) http.Handler {
|
||||
auth = tc
|
||||
|
||||
opts := []kithttp.ServerOption{
|
||||
kithttp.ServerErrorEncoder(encodeError),
|
||||
@@ -118,7 +124,7 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
switch err {
|
||||
case errMalformedData:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
case errNotFound:
|
||||
case things.ErrNotFound:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
case things.ErrUnauthorizedAccess:
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mocks
|
||||
|
||||
import "github.com/mainflux/mainflux"
|
||||
|
||||
+21
-1
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
@@ -6,10 +13,15 @@ import (
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/mainflux/mainflux/things"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
var _ mainflux.ThingsServiceClient = (*thingsClient)(nil)
|
||||
|
||||
// ServiceErrToken is used to simulate internal server error.
|
||||
const ServiceErrToken = "unavailable"
|
||||
|
||||
type thingsClient struct {
|
||||
things map[string]uint64
|
||||
}
|
||||
@@ -21,13 +33,21 @@ func NewThingsClient(data map[string]uint64) mainflux.ThingsServiceClient {
|
||||
|
||||
func (tc thingsClient) CanAccess(ctx context.Context, req *mainflux.AccessReq, opts ...grpc.CallOption) (*mainflux.ThingID, error) {
|
||||
key := req.GetToken()
|
||||
|
||||
// Since there is no appropriate way to simulate internal server error,
|
||||
// we had to use this obscure approach. ErrorToken simulates gRPC
|
||||
// call which returns internal server error.
|
||||
if key == ServiceErrToken {
|
||||
return nil, status.Error(codes.Internal, "internal server error")
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
return nil, things.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
id, ok := tc.things[key]
|
||||
if !ok {
|
||||
return nil, things.ErrUnauthorizedAccess
|
||||
return nil, status.Error(codes.PermissionDenied, "invalid credentials provided")
|
||||
}
|
||||
|
||||
return &mainflux.ThingID{Value: id}, nil
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// Package nats contains NATS message publisher implementation.
|
||||
package nats
|
||||
|
||||
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: internal.proto
|
||||
|
||||
package mainflux
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type AccessReq struct {
|
||||
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
|
||||
ChanID uint64 `protobuf:"varint,2,opt,name=chanID,proto3" json:"chanID,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *AccessReq) Reset() { *m = AccessReq{} }
|
||||
func (m *AccessReq) String() string { return proto.CompactTextString(m) }
|
||||
func (*AccessReq) ProtoMessage() {}
|
||||
func (*AccessReq) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_internal_50ed0f35f4250621, []int{0}
|
||||
}
|
||||
func (m *AccessReq) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_AccessReq.Unmarshal(m, b)
|
||||
}
|
||||
func (m *AccessReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_AccessReq.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *AccessReq) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_AccessReq.Merge(dst, src)
|
||||
}
|
||||
func (m *AccessReq) XXX_Size() int {
|
||||
return xxx_messageInfo_AccessReq.Size(m)
|
||||
}
|
||||
func (m *AccessReq) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_AccessReq.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_AccessReq proto.InternalMessageInfo
|
||||
|
||||
func (m *AccessReq) GetToken() string {
|
||||
if m != nil {
|
||||
return m.Token
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *AccessReq) GetChanID() uint64 {
|
||||
if m != nil {
|
||||
return m.ChanID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ThingID struct {
|
||||
Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ThingID) Reset() { *m = ThingID{} }
|
||||
func (m *ThingID) String() string { return proto.CompactTextString(m) }
|
||||
func (*ThingID) ProtoMessage() {}
|
||||
func (*ThingID) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_internal_50ed0f35f4250621, []int{1}
|
||||
}
|
||||
func (m *ThingID) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ThingID.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ThingID) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ThingID.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *ThingID) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ThingID.Merge(dst, src)
|
||||
}
|
||||
func (m *ThingID) XXX_Size() int {
|
||||
return xxx_messageInfo_ThingID.Size(m)
|
||||
}
|
||||
func (m *ThingID) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ThingID.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ThingID proto.InternalMessageInfo
|
||||
|
||||
func (m *ThingID) GetValue() uint64 {
|
||||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Token) Reset() { *m = Token{} }
|
||||
func (m *Token) String() string { return proto.CompactTextString(m) }
|
||||
func (*Token) ProtoMessage() {}
|
||||
func (*Token) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_internal_50ed0f35f4250621, []int{2}
|
||||
}
|
||||
func (m *Token) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Token.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Token) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Token.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Token) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Token.Merge(dst, src)
|
||||
}
|
||||
func (m *Token) XXX_Size() int {
|
||||
return xxx_messageInfo_Token.Size(m)
|
||||
}
|
||||
func (m *Token) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Token.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Token proto.InternalMessageInfo
|
||||
|
||||
func (m *Token) GetValue() string {
|
||||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type UserID struct {
|
||||
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *UserID) Reset() { *m = UserID{} }
|
||||
func (m *UserID) String() string { return proto.CompactTextString(m) }
|
||||
func (*UserID) ProtoMessage() {}
|
||||
func (*UserID) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_internal_50ed0f35f4250621, []int{3}
|
||||
}
|
||||
func (m *UserID) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_UserID.Unmarshal(m, b)
|
||||
}
|
||||
func (m *UserID) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_UserID.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *UserID) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_UserID.Merge(dst, src)
|
||||
}
|
||||
func (m *UserID) XXX_Size() int {
|
||||
return xxx_messageInfo_UserID.Size(m)
|
||||
}
|
||||
func (m *UserID) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_UserID.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_UserID proto.InternalMessageInfo
|
||||
|
||||
func (m *UserID) GetValue() string {
|
||||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*AccessReq)(nil), "mainflux.AccessReq")
|
||||
proto.RegisterType((*ThingID)(nil), "mainflux.ThingID")
|
||||
proto.RegisterType((*Token)(nil), "mainflux.Token")
|
||||
proto.RegisterType((*UserID)(nil), "mainflux.UserID")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// ThingsServiceClient is the client API for ThingsService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type ThingsServiceClient interface {
|
||||
CanAccess(ctx context.Context, in *AccessReq, opts ...grpc.CallOption) (*ThingID, error)
|
||||
Identify(ctx context.Context, in *Token, opts ...grpc.CallOption) (*ThingID, error)
|
||||
}
|
||||
|
||||
type thingsServiceClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewThingsServiceClient(cc *grpc.ClientConn) ThingsServiceClient {
|
||||
return &thingsServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *thingsServiceClient) CanAccess(ctx context.Context, in *AccessReq, opts ...grpc.CallOption) (*ThingID, error) {
|
||||
out := new(ThingID)
|
||||
err := c.cc.Invoke(ctx, "/mainflux.ThingsService/CanAccess", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *thingsServiceClient) Identify(ctx context.Context, in *Token, opts ...grpc.CallOption) (*ThingID, error) {
|
||||
out := new(ThingID)
|
||||
err := c.cc.Invoke(ctx, "/mainflux.ThingsService/Identify", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ThingsServiceServer is the server API for ThingsService service.
|
||||
type ThingsServiceServer interface {
|
||||
CanAccess(context.Context, *AccessReq) (*ThingID, error)
|
||||
Identify(context.Context, *Token) (*ThingID, error)
|
||||
}
|
||||
|
||||
func RegisterThingsServiceServer(s *grpc.Server, srv ThingsServiceServer) {
|
||||
s.RegisterService(&_ThingsService_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _ThingsService_CanAccess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AccessReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ThingsServiceServer).CanAccess(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/mainflux.ThingsService/CanAccess",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ThingsServiceServer).CanAccess(ctx, req.(*AccessReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ThingsService_Identify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Token)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ThingsServiceServer).Identify(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/mainflux.ThingsService/Identify",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ThingsServiceServer).Identify(ctx, req.(*Token))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _ThingsService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "mainflux.ThingsService",
|
||||
HandlerType: (*ThingsServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "CanAccess",
|
||||
Handler: _ThingsService_CanAccess_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Identify",
|
||||
Handler: _ThingsService_Identify_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "internal.proto",
|
||||
}
|
||||
|
||||
// UsersServiceClient is the client API for UsersService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type UsersServiceClient interface {
|
||||
Identify(ctx context.Context, in *Token, opts ...grpc.CallOption) (*UserID, error)
|
||||
}
|
||||
|
||||
type usersServiceClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewUsersServiceClient(cc *grpc.ClientConn) UsersServiceClient {
|
||||
return &usersServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *usersServiceClient) Identify(ctx context.Context, in *Token, opts ...grpc.CallOption) (*UserID, error) {
|
||||
out := new(UserID)
|
||||
err := c.cc.Invoke(ctx, "/mainflux.UsersService/Identify", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UsersServiceServer is the server API for UsersService service.
|
||||
type UsersServiceServer interface {
|
||||
Identify(context.Context, *Token) (*UserID, error)
|
||||
}
|
||||
|
||||
func RegisterUsersServiceServer(s *grpc.Server, srv UsersServiceServer) {
|
||||
s.RegisterService(&_UsersService_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _UsersService_Identify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Token)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UsersServiceServer).Identify(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/mainflux.UsersService/Identify",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UsersServiceServer).Identify(ctx, req.(*Token))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _UsersService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "mainflux.UsersService",
|
||||
HandlerType: (*UsersServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Identify",
|
||||
Handler: _UsersService_Identify_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "internal.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("internal.proto", fileDescriptor_internal_50ed0f35f4250621) }
|
||||
|
||||
var fileDescriptor_internal_50ed0f35f4250621 = []byte{
|
||||
// 231 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcb, 0xcc, 0x2b, 0x49,
|
||||
0x2d, 0xca, 0x4b, 0xcc, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xc8, 0x4d, 0xcc, 0xcc,
|
||||
0x4b, 0xcb, 0x29, 0xad, 0x50, 0xb2, 0xe4, 0xe2, 0x74, 0x4c, 0x4e, 0x4e, 0x2d, 0x2e, 0x0e, 0x4a,
|
||||
0x2d, 0x14, 0x12, 0xe1, 0x62, 0x2d, 0xc9, 0xcf, 0x4e, 0xcd, 0x93, 0x60, 0x54, 0x60, 0xd4, 0xe0,
|
||||
0x0c, 0x82, 0x70, 0x84, 0xc4, 0xb8, 0xd8, 0x92, 0x33, 0x12, 0xf3, 0x3c, 0x5d, 0x24, 0x98, 0x14,
|
||||
0x18, 0x35, 0x58, 0x82, 0xa0, 0x3c, 0x25, 0x79, 0x2e, 0xf6, 0x90, 0x8c, 0xcc, 0xbc, 0x74, 0x4f,
|
||||
0x17, 0x90, 0xc6, 0xb2, 0xc4, 0x9c, 0xd2, 0x54, 0xb0, 0x46, 0x96, 0x20, 0x08, 0x47, 0x49, 0x96,
|
||||
0x8b, 0x35, 0x04, 0x6c, 0x02, 0x8a, 0x34, 0x27, 0x4c, 0x5a, 0x8e, 0x8b, 0x2d, 0xb4, 0x38, 0xb5,
|
||||
0x08, 0x5d, 0x3b, 0x4c, 0xde, 0xa8, 0x82, 0x8b, 0x17, 0x6c, 0x7e, 0x71, 0x70, 0x6a, 0x51, 0x59,
|
||||
0x66, 0x72, 0xaa, 0x90, 0x29, 0x17, 0xa7, 0x73, 0x62, 0x1e, 0xc4, 0xb9, 0x42, 0xc2, 0x7a, 0x30,
|
||||
0x3f, 0xe8, 0xc1, 0x3d, 0x20, 0x25, 0x88, 0x10, 0x84, 0x3a, 0x4d, 0x89, 0x41, 0xc8, 0x80, 0x8b,
|
||||
0xc3, 0x33, 0x25, 0x35, 0xaf, 0x24, 0x33, 0xad, 0x52, 0x88, 0x1f, 0x49, 0x01, 0xc8, 0x69, 0x58,
|
||||
0x75, 0x18, 0xd9, 0x73, 0xf1, 0x80, 0x5c, 0x06, 0xb7, 0x58, 0x1f, 0x9f, 0x09, 0x02, 0x08, 0x01,
|
||||
0x88, 0x77, 0x94, 0x18, 0x92, 0xd8, 0xc0, 0xc1, 0x6c, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x2f,
|
||||
0xbc, 0xec, 0x14, 0x78, 0x01, 0x00, 0x00,
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018
|
||||
// Mainflux
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package mainflux;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mainflux-grafana
|
||||
labels:
|
||||
component: mainflux-grafana
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
component: mainflux-grafana
|
||||
spec:
|
||||
containers:
|
||||
- name: mainflux-grafana
|
||||
image: grafana/grafana:5.1.3
|
||||
imagePullPolicy: Always
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mainflux-grafana
|
||||
labels:
|
||||
component: mainflux-grafana
|
||||
spec:
|
||||
selector:
|
||||
component: mainflux-grafana
|
||||
ports:
|
||||
- port: 3001
|
||||
targetPort: 3000
|
||||
type: LoadBalancer
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: mainflux-influxdb-data-disk
|
||||
labels:
|
||||
name: mainflux-influxdb-data-disk
|
||||
spec:
|
||||
capacity:
|
||||
storage: 10Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
hostPath:
|
||||
path: /tmp/data/influxdb-0
|
||||
persistentVolumeReclaimPolicy: Recycle
|
||||
---
|
||||
kind: PersistentVolumeClaim
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: mainflux-influxdb-data-claim
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: mainflux-influxdb
|
||||
labels:
|
||||
name: mainflux-influxdb
|
||||
spec:
|
||||
containers:
|
||||
- name: mainflux-influxdb
|
||||
image: influxdb:1.5.2-alpine
|
||||
env:
|
||||
- name: INFLUXDB_DB
|
||||
value: "mainflux"
|
||||
- name: INFLUXDB_ADMIN_USER
|
||||
value: "mainflux"
|
||||
- name: INFLUXDB_ADMIN_PASSWORD
|
||||
value: "mainflux"
|
||||
ports:
|
||||
- containerPort: 8086
|
||||
volumeMounts:
|
||||
- mountPath: /var/lib/influxdb
|
||||
name: mainflux-influxdb-data
|
||||
volumes:
|
||||
- name: mainflux-influxdb-data
|
||||
persistentVolumeClaim:
|
||||
claimName: mainflux-influxdb-data-claim
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mainflux-influxdb
|
||||
labels:
|
||||
name: mainflux-influxdb
|
||||
spec:
|
||||
ports:
|
||||
- port: 8086
|
||||
selector:
|
||||
name: mainflux-influxdb
|
||||
@@ -0,0 +1,47 @@
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: influxdb-writer
|
||||
labels:
|
||||
component: influxdb-writer
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
component: influxdb-writer
|
||||
spec:
|
||||
containers:
|
||||
- name: mainflux-influxdb
|
||||
image: mainflux/influxdb:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8900
|
||||
env:
|
||||
- name: MF_NATS_URL
|
||||
value: "nats://nats:4222"
|
||||
- name: MF_INFLUX_WRITER_PORT
|
||||
value: "8900"
|
||||
- name: MF_INFLUX_WRITER_DB_NAME
|
||||
value: "mainflux"
|
||||
- name: MF_INFLUX_WRITER_DB_HOST
|
||||
value: "mainflux-influxdb"
|
||||
- name: MF_INFLUX_WRITER_DB_PORT
|
||||
value: "8086"
|
||||
- name: MF_INFLUX_WRITER_DB_USER
|
||||
value: "mainflux"
|
||||
- name: MF_INFLUX_WRITER_DB_PASS
|
||||
value: "mainflux"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: influxdb-writer
|
||||
labels:
|
||||
component: influxdb-writer
|
||||
spec:
|
||||
selector:
|
||||
component: influxdb-writer
|
||||
ports:
|
||||
- port: 8900
|
||||
type: LoadBalancer
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: normalizer
|
||||
labels:
|
||||
@@ -21,16 +21,15 @@ spec:
|
||||
- name: MF_NORMALIZER_PORT
|
||||
value: "8184"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: normalizer
|
||||
labels:
|
||||
component: normalizer
|
||||
component: normalizer
|
||||
spec:
|
||||
selector:
|
||||
component: normalizer
|
||||
ports:
|
||||
- port: 8183
|
||||
targetPort: 8183
|
||||
- port: 8184
|
||||
clusterIP: None
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nats
|
||||
image: nats:1.0.4
|
||||
image: nats:1.1.0
|
||||
args: [ "--config", "/etc/nats/nats.conf"]
|
||||
volumeMounts:
|
||||
- name: config-volume
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user