mirror of
https://github.com/absmach/supermq.git
synced 2026-06-23 07:10:19 +00:00
🐭 GO GO GO!
Signed-off-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
This commit is contained in:
@@ -40,3 +40,11 @@ Session.vim
|
||||
# auto-generated tag files
|
||||
tags
|
||||
|
||||
# Go sources
|
||||
./src
|
||||
|
||||
# Go tests
|
||||
*.test
|
||||
|
||||
# Binary
|
||||
mainflux
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
###
|
||||
# Mainflux Dockerfile
|
||||
###
|
||||
|
||||
FROM golang:alpine
|
||||
MAINTAINER Mainflux
|
||||
|
||||
###
|
||||
# Install
|
||||
###
|
||||
|
||||
RUN apk update && apk add git && rm -rf /var/cache/apk/*
|
||||
|
||||
# Copy the local package files to the container's workspace.
|
||||
ADD . /go/src/github.com/mainflux/mainflux-lite
|
||||
|
||||
RUN mkdir -p /config/lite
|
||||
COPY config/config-docker.yml /config/lite/config.yml
|
||||
|
||||
# Get and install the dependencies
|
||||
RUN go get github.com/mainflux/mainflux-lite
|
||||
|
||||
###
|
||||
# Run main command from entrypoint and parameters in CMD[]
|
||||
###
|
||||
CMD ["/config/lite/config.yml"]
|
||||
|
||||
# Run mainflux command by default when the container starts.
|
||||
ENTRYPOINT ["/go/bin/mainflux-lite"]
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
# Mainflux follows the timeless, highly efficient and totally unfair system
|
||||
# known as [Benevolent dictator for
|
||||
# life](https://en.wikipedia.org/wiki/Benevolent_Dictator_for_Life), with
|
||||
# Drasko DRASKOVIC in the role of BDFL.
|
||||
|
||||
[bdfl]
|
||||
|
||||
[[drasko]]
|
||||
Name = "Drasko DRASKOVIC"
|
||||
Email = "drasko.draskovic@mainflux.com"
|
||||
GitHub = "drasko"
|
||||
|
||||
# However, this role serves only in dead-lock events, or in a special and very rare cases
|
||||
# when BDFL completely disagrees with the decisions made.
|
||||
# In the normal flow of events, decisions on the project design are made through discussions,
|
||||
# most often on the Pull Requests.
|
||||
#
|
||||
# Maintainers have the special role in the project in managing and accepting PRs,
|
||||
# overall leading the project and making design decisions on the maintained subsystems.
|
||||
#
|
||||
# A reference list of all maintainers of the Mainflux project.
|
||||
|
||||
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
|
||||
|
||||
[maintainers]
|
||||
|
||||
[[janko]]
|
||||
Name = "Janko ISIDOROVIC"
|
||||
Email = "janko.isidorovic@mainflux.com"
|
||||
GitHub = "janko-isidorovic"
|
||||
|
||||
[[nikola]]
|
||||
Name = "Nikola MARCETIC"
|
||||
Email = "nikola.marcetic@mainflux.com"
|
||||
GitHub = "nmarcetic"
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package clients
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"log"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/mainflux/mainflux-lite/db"
|
||||
|
||||
"github.com/krylovsk/gosenml"
|
||||
"github.com/kataras/iris"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
)
|
||||
|
||||
type (
|
||||
ChannelWriteStatus struct {
|
||||
Nb int
|
||||
Str string
|
||||
}
|
||||
|
||||
MqttConn struct {
|
||||
Opts *mqtt.ClientOptions
|
||||
Client mqtt.Client
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
MqttClient mqtt.Client
|
||||
WriteStatusChannel chan ChannelWriteStatus
|
||||
)
|
||||
|
||||
//define a function for the default message handler
|
||||
var msgHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
|
||||
fmt.Printf("TOPIC: %s\n", msg.Topic())
|
||||
fmt.Printf("MSG: %s\n", msg.Payload())
|
||||
|
||||
s := strings.Split(msg.Topic(), "/")
|
||||
chanId := s[len(s)-1]
|
||||
status := WriteChannel(chanId, msg.Payload())
|
||||
|
||||
// Send status to HTTP publisher
|
||||
WriteStatusChannel <- status
|
||||
|
||||
fmt.Println(status)
|
||||
}
|
||||
|
||||
func (mqc *MqttConn) MqttSub() {
|
||||
// Create a ClientOptions struct setting the broker address, clientid, turn
|
||||
// off trace output and set the default message handler
|
||||
mqc.Opts = mqtt.NewClientOptions().AddBroker("tcp://localhost:1883")
|
||||
mqc.Opts.SetClientID("mainflux")
|
||||
mqc.Opts.SetDefaultPublishHandler(msgHandler)
|
||||
|
||||
//create and start a client using the above ClientOptions
|
||||
mqc.Client = mqtt.NewClient(mqc.Opts)
|
||||
if token := mqc.Client.Connect(); token.Wait() && token.Error() != nil {
|
||||
panic(token.Error())
|
||||
}
|
||||
|
||||
// Subscribe to all channels of all the devices and request messages to be delivered
|
||||
// at a maximum qos of zero, wait for the receipt to confirm the subscription
|
||||
// Topic is in the form:
|
||||
// mainflux/<channel_id>
|
||||
if token := mqc.Client.Subscribe("mainflux/+", 0, nil); token.Wait() && token.Error() != nil {
|
||||
fmt.Println(token.Error())
|
||||
}
|
||||
|
||||
MqttClient = mqc.Client
|
||||
WriteStatusChannel = make(chan ChannelWriteStatus)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WriteChannel()
|
||||
* Generic function that updates the channel value.
|
||||
* Can be called via various protocols
|
||||
*/
|
||||
func WriteChannel(id string, bodyBytes []byte) ChannelWriteStatus {
|
||||
var body map[string]interface{}
|
||||
if err := json.Unmarshal(bodyBytes, &body); err != nil {
|
||||
fmt.Println("Error unmarshaling body")
|
||||
}
|
||||
|
||||
Db := db.MgoDb{}
|
||||
Db.Init()
|
||||
defer Db.Close()
|
||||
|
||||
// Check if someone is trying to change "id" key
|
||||
// and protect us from this
|
||||
s := ChannelWriteStatus{}
|
||||
if _, ok := body["id"]; ok {
|
||||
s.Nb = iris.StatusBadRequest
|
||||
s.Str = "Invalid request: 'id' is read-only"
|
||||
return s
|
||||
}
|
||||
if _, ok := body["device"]; ok {
|
||||
println("Error: can not change device")
|
||||
s.Nb = iris.StatusBadRequest
|
||||
s.Str = "Invalid request: 'device' is read-only"
|
||||
return s
|
||||
}
|
||||
if _, ok := body["created"]; ok {
|
||||
println("Error: can not change device")
|
||||
s.Nb = iris.StatusBadRequest
|
||||
s.Str = "Invalid request: 'created' is read-only"
|
||||
return s
|
||||
}
|
||||
|
||||
senmlDecoder := gosenml.NewJSONDecoder()
|
||||
m, _ := senmlDecoder.DecodeMessage(bodyBytes)
|
||||
for _, e := range m.Entries {
|
||||
// BaseName
|
||||
e.Name = m.BaseName + e.Name
|
||||
|
||||
// BaseTime
|
||||
e.Time = m.BaseTime + e.Time
|
||||
if e.Time <= 0 {
|
||||
e.Time += time.Now().Unix()
|
||||
}
|
||||
|
||||
// BaseUnits
|
||||
if e.Units == "" {
|
||||
e.Units = m.BaseUnits
|
||||
}
|
||||
|
||||
/** Insert entry in DB */
|
||||
colQuerier := bson.M{"id": id}
|
||||
change := bson.M{"$push": bson.M{"values": e}}
|
||||
err := Db.C("channels").Update(colQuerier, change)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
s.Nb = iris.StatusNotFound
|
||||
s.Str = "Not inserted"
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// Timestamp
|
||||
t := time.Now().UTC().Format(time.RFC3339)
|
||||
body["updated"] = t
|
||||
|
||||
/** Then update channel timestamp */
|
||||
colQuerier := bson.M{"id": id}
|
||||
change := bson.M{"$set": bson.M{"updated": body["updated"]}}
|
||||
err := Db.C("channels").Update(colQuerier, change)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
s.Nb = iris.StatusNotFound
|
||||
s.Str = "Not updated"
|
||||
return s
|
||||
}
|
||||
|
||||
s.Nb = iris.StatusOK
|
||||
s.Str = "Updated"
|
||||
return s
|
||||
}
|
||||
@@ -6,6 +6,13 @@
|
||||
# See the included LICENSE file for more details.
|
||||
###
|
||||
|
||||
###
|
||||
# HTPP Server
|
||||
###
|
||||
http:
|
||||
host: "0.0.0.0"
|
||||
port: 7070
|
||||
|
||||
###
|
||||
# MongoDB
|
||||
###
|
||||
@@ -14,17 +21,3 @@ mongo:
|
||||
port: 27017
|
||||
db: "mainflux"
|
||||
|
||||
###
|
||||
# InfluxDB
|
||||
###
|
||||
influx:
|
||||
host: "influx"
|
||||
port: 8086
|
||||
db: "mainflux"
|
||||
|
||||
###
|
||||
# NATS
|
||||
###
|
||||
nats:
|
||||
host: "nats"
|
||||
port: 4222
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/viper"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// HTTP
|
||||
HttpHost string
|
||||
HttpPort int
|
||||
|
||||
// Mongo
|
||||
MongoHost string
|
||||
MongoPort int
|
||||
MongoDatabase string
|
||||
|
||||
// Influx
|
||||
InfluxHost string
|
||||
InfluxPort int
|
||||
InfluxDatabase string
|
||||
}
|
||||
|
||||
|
||||
func (this *Config) Parse() {
|
||||
/**
|
||||
* Config
|
||||
*/
|
||||
/** Viper setup */
|
||||
viper.SetConfigType("yaml") // or viper.SetConfigType("YAML")
|
||||
|
||||
testEnv := os.Getenv("TEST_ENV")
|
||||
if testEnv == "" && len(os.Args) > 1 {
|
||||
// We are not in the TEST_ENV (where different args are provided)
|
||||
// and provided config file as an argument
|
||||
viper.SetConfigFile(os.Args[1])
|
||||
} else {
|
||||
// default cfg path to source dir, as we keep cfg.yml there
|
||||
cfgDir := os.Getenv("GOPATH") + "/src/github.com/mainflux/mainflux-lite/config"
|
||||
viper.SetConfigName("config") // name of config file (without extension)
|
||||
viper.AddConfigPath(cfgDir) // path to look for the config file in
|
||||
}
|
||||
|
||||
err := viper.ReadInConfig() // Find and read the config file
|
||||
if err != nil { // Handle errors reading the config file
|
||||
panic(fmt.Errorf("Fatal error config file: %s \n", err))
|
||||
}
|
||||
|
||||
this.MongoHost = viper.GetString("mongo.host")
|
||||
this.MongoPort = viper.GetInt("mongo.port")
|
||||
this.MongoDatabase = viper.GetString("mongo.db")
|
||||
|
||||
this.InfluxHost = viper.GetString("influx.host")
|
||||
this.InfluxPort = viper.GetInt("influx.port")
|
||||
this.InfluxDatabase = viper.GetString("influx.db")
|
||||
|
||||
this.HttpHost = viper.GetString("http.host")
|
||||
this.HttpPort = viper.GetInt("http.port")
|
||||
}
|
||||
@@ -7,15 +7,18 @@
|
||||
###
|
||||
|
||||
###
|
||||
# HTTP Server
|
||||
# HTPP Server
|
||||
###
|
||||
server:
|
||||
host: "0.0.0.0"
|
||||
http:
|
||||
host: "0.0.0.0"
|
||||
port: 7070
|
||||
|
||||
###
|
||||
# NATS
|
||||
# MongoDB
|
||||
###
|
||||
nats:
|
||||
host: "nats"
|
||||
port: 4222
|
||||
mongo:
|
||||
host: "localhost"
|
||||
port: 27017
|
||||
db: "mainflux"
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0 license.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
var config = {};
|
||||
|
||||
/**
|
||||
* WS Server
|
||||
*/
|
||||
config.ws = {
|
||||
host: '0.0.0.0',
|
||||
port: 9090,
|
||||
}
|
||||
|
||||
/**
|
||||
* NATS
|
||||
*/
|
||||
config.nats = {
|
||||
host : 'nats',
|
||||
port : 4222
|
||||
}
|
||||
|
||||
|
||||
module.exports = config;
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* FluxMQ is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// VERSION is the current version for the server.
|
||||
VERSION = "0.0.1"
|
||||
|
||||
// DEFAULT_PORT is the default port for client connections.
|
||||
DEFAULT_PORT = 1833
|
||||
|
||||
// DEFAULT_HOST defaults to all interfaces.
|
||||
DEFAULT_HOST = "0.0.0.0"
|
||||
|
||||
// MAX_PAYLOAD_SIZE is the maximum allowed payload size. Should be using
|
||||
// something different if > 1MB payloads are needed.
|
||||
MAX_PAYLOAD_SIZE = (1024 * 1024)
|
||||
|
||||
// MAX_PENDING_SIZE is the maximum outbound size (in bytes) per client.
|
||||
MAX_PENDING_SIZE = (10 * 1024 * 1024)
|
||||
|
||||
// DEFAULT_MAX_CONNECTIONS is the default maximum connections allowed.
|
||||
DEFAULT_MAX_CONNECTIONS = (64 * 1024)
|
||||
|
||||
// ACCEPT_MIN_SLEEP is the minimum acceptable sleep times on temporary errors.
|
||||
ACCEPT_MIN_SLEEP = 10 * time.Millisecond
|
||||
|
||||
// ACCEPT_MAX_SLEEP is the maximum acceptable sleep times on temporary errors
|
||||
ACCEPT_MAX_SLEEP = 1 * time.Second
|
||||
|
||||
// _EMPTY_ is empty string
|
||||
_EMPTY_ = ""
|
||||
)
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux-lite/db"
|
||||
"github.com/mainflux/mainflux-lite/models"
|
||||
"github.com/mainflux/mainflux-lite/clients"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
/** == Functions == */
|
||||
|
||||
/**
|
||||
* CreateChannel ()
|
||||
*/
|
||||
func CreateChannel(ctx *iris.Context) {
|
||||
var body map[string]interface{}
|
||||
ctx.ReadJSON(&body)
|
||||
/*
|
||||
if validateJsonSchema("channel", body) != true {
|
||||
println("Invalid schema")
|
||||
ctx.JSON(iris.StatusBadRequest, iris.Map{"response": "invalid json schema in request"})
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// Init new Mongo session
|
||||
// and get the "channels" collection
|
||||
// from this new session
|
||||
Db := db.MgoDb{}
|
||||
Db.Init()
|
||||
defer Db.Close()
|
||||
|
||||
|
||||
c := models.Channel{}
|
||||
json.Unmarshal(ctx.RequestCtx.Request.Body(), &c)
|
||||
|
||||
// Creating UUID Version 4
|
||||
uuid := uuid.NewV4()
|
||||
fmt.Println(uuid.String())
|
||||
|
||||
c.Id = uuid.String()
|
||||
|
||||
// Insert reference to DeviceId
|
||||
did := ctx.Param("device_id")
|
||||
c.Device = did
|
||||
|
||||
// Timestamp
|
||||
t := time.Now().UTC().Format(time.RFC3339)
|
||||
c.Created, c.Updated = t, t
|
||||
|
||||
// Insert Channel
|
||||
err := Db.C("channels").Insert(c)
|
||||
if err != nil {
|
||||
ctx.JSON(iris.StatusInternalServerError, iris.Map{"response": "cannot create device"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(iris.StatusCreated, iris.Map{"response": "created", "id": c.Id})
|
||||
}
|
||||
|
||||
/**
|
||||
* GetChannels()
|
||||
*/
|
||||
func GetChannels(ctx *iris.Context) {
|
||||
Db := db.MgoDb{}
|
||||
Db.Init()
|
||||
defer Db.Close()
|
||||
|
||||
results := []models.Channel{}
|
||||
err := Db.C("channels").Find(nil).All(&results)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
ctx.JSON(iris.StatusOK, &results)
|
||||
}
|
||||
|
||||
/**
|
||||
* GetChannel()
|
||||
*/
|
||||
func GetChannel(ctx *iris.Context) {
|
||||
Db := db.MgoDb{}
|
||||
Db.Init()
|
||||
defer Db.Close()
|
||||
|
||||
id := ctx.Param("channel_id")
|
||||
|
||||
result := models.Channel{}
|
||||
err := Db.C("channels").Find(bson.M{"id": id}).One(&result)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
ctx.JSON(iris.StatusNotFound, iris.Map{"response": "not found", "id": id})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(iris.StatusOK, &result)
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdateChannel()
|
||||
*/
|
||||
func UpdateChannel(ctx *iris.Context) {
|
||||
var body map[string]interface{}
|
||||
ctx.ReadJSON(&body)
|
||||
// Validate JSON schema user provided
|
||||
/*
|
||||
if validateJsonSchema("channel", body) != true {
|
||||
println("Invalid schema")
|
||||
ctx.JSON(iris.StatusBadRequest, iris.Map{"response": "invalid json schema in request"})
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
id := ctx.Param("channel_id")
|
||||
|
||||
|
||||
// Publish the channel update.
|
||||
// This will be catched by the MQTT main client (subscribed to all channel topics)
|
||||
// and then written in the DB in the MQTT handler
|
||||
token := clients.MqttClient.Publish("mainflux/" + id, 0, false, string(ctx.RequestCtx.Request.Body()))
|
||||
token.Wait()
|
||||
|
||||
// Wait on status from MQTT handler (which executes DB write)
|
||||
status := <-clients.WriteStatusChannel
|
||||
ctx.JSON(status.Nb, iris.Map{"response": status.Str})
|
||||
}
|
||||
|
||||
/**
|
||||
* DeleteChannel()
|
||||
*/
|
||||
func DeleteChannel(ctx *iris.Context) {
|
||||
Db := db.MgoDb{}
|
||||
Db.Init()
|
||||
defer Db.Close()
|
||||
|
||||
id := ctx.Param("channel_id")
|
||||
|
||||
err := Db.C("channels").Remove(bson.M{"id": id})
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
ctx.JSON(iris.StatusNotFound, iris.Map{"response": "not deleted", "id": id})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(iris.StatusOK, iris.Map{"response": "deleted", "id": id})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux-lite/db"
|
||||
"github.com/mainflux/mainflux-lite/models"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
/** == Functions == */
|
||||
/**
|
||||
* CreateDevice ()
|
||||
*/
|
||||
func CreateDevice(ctx *iris.Context) {
|
||||
var body map[string]interface{}
|
||||
ctx.ReadJSON(&body)
|
||||
if validateJsonSchema("device", body) != true {
|
||||
println("Invalid schema")
|
||||
ctx.JSON(iris.StatusBadRequest, iris.Map{"response": "invalid json schema in request"})
|
||||
return
|
||||
}
|
||||
|
||||
// Init new Mongo session
|
||||
// and get the "devices" collection
|
||||
// from this new session
|
||||
Db := db.MgoDb{}
|
||||
Db.Init()
|
||||
defer Db.Close()
|
||||
|
||||
// Set up defaults and pick up new values from user-provided JSON
|
||||
d := models.Device{Name: "Some Name"}
|
||||
json.Unmarshal(ctx.RequestCtx.Request.Body(), &d)
|
||||
|
||||
// Creating UUID Version 4
|
||||
uuid := uuid.NewV4()
|
||||
fmt.Println(uuid.String())
|
||||
|
||||
d.Id = uuid.String()
|
||||
|
||||
// Timestamp
|
||||
t := time.Now().UTC().Format(time.RFC3339)
|
||||
d.Created, d.Updated = t, t
|
||||
|
||||
// Insert Device
|
||||
erri := Db.C("devices").Insert(d)
|
||||
if erri != nil {
|
||||
ctx.JSON(iris.StatusInternalServerError, iris.Map{"response": "cannot create device"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(iris.StatusCreated, iris.Map{"response": "created", "id": d.Id})
|
||||
}
|
||||
|
||||
/**
|
||||
* GetDevices()
|
||||
*/
|
||||
func GetDevices(ctx *iris.Context) {
|
||||
Db := db.MgoDb{}
|
||||
Db.Init()
|
||||
defer Db.Close()
|
||||
|
||||
results := []models.Device{}
|
||||
err := Db.C("devices").Find(nil).All(&results)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
ctx.JSON(iris.StatusOK, &results)
|
||||
}
|
||||
|
||||
/**
|
||||
* GetDevice()
|
||||
*/
|
||||
func GetDevice(ctx *iris.Context) {
|
||||
Db := db.MgoDb{}
|
||||
Db.Init()
|
||||
defer Db.Close()
|
||||
|
||||
id := ctx.Param("device_id")
|
||||
|
||||
result := models.Device{}
|
||||
err := Db.C("devices").Find(bson.M{"id": id}).One(&result)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
ctx.JSON(iris.StatusNotFound, iris.Map{"response": "not found", "id": id})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(iris.StatusOK, &result)
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdateDevice()
|
||||
*/
|
||||
func UpdateDevice(ctx *iris.Context) {
|
||||
var body map[string]interface{}
|
||||
ctx.ReadJSON(&body)
|
||||
|
||||
Db := db.MgoDb{}
|
||||
Db.Init()
|
||||
defer Db.Close()
|
||||
|
||||
id := ctx.Param("device_id")
|
||||
|
||||
// Validate JSON schema user provided
|
||||
if validateJsonSchema("device", body) != true {
|
||||
println("Invalid schema")
|
||||
ctx.JSON(iris.StatusBadRequest, iris.Map{"response": "invalid json schema in request"})
|
||||
return
|
||||
}
|
||||
|
||||
// Check if someone is trying to change "id" key
|
||||
// and protect us from this
|
||||
if _, ok := body["id"]; ok {
|
||||
ctx.JSON(iris.StatusBadRequest, iris.Map{"response": "invalid request: device id is read-only"})
|
||||
return
|
||||
}
|
||||
if _, ok := body["created"]; ok {
|
||||
println("Error: can not change device")
|
||||
ctx.JSON(iris.StatusBadRequest, iris.Map{"response": "invalid request: 'created' is read-only"})
|
||||
return
|
||||
}
|
||||
|
||||
// Timestamp
|
||||
t := time.Now().UTC().Format(time.RFC3339)
|
||||
body["updated"] = t
|
||||
|
||||
colQuerier := bson.M{"id": id}
|
||||
change := bson.M{"$set": body}
|
||||
err := Db.C("devices").Update(colQuerier, change)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
ctx.JSON(iris.StatusNotFound, iris.Map{"response": "not updated", "id": id})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(iris.StatusOK, iris.Map{"response": "updated", "id": id})
|
||||
}
|
||||
|
||||
/**
|
||||
* DeleteDevice()
|
||||
*/
|
||||
func DeleteDevice(ctx *iris.Context) {
|
||||
Db := db.MgoDb{}
|
||||
Db.Init()
|
||||
defer Db.Close()
|
||||
|
||||
id := ctx.Param("device_id")
|
||||
|
||||
err := Db.C("devices").Remove(bson.M{"id": id})
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
ctx.JSON(iris.StatusNotFound, iris.Map{"response": "not deleted", "id": id})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(iris.StatusOK, iris.Map{"response": "deleted", "id": id})
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0 license.
|
||||
* Mainflux server is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
var mosca = require('mosca');
|
||||
package controllers
|
||||
|
||||
var config = {};
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
/** == Functions == */
|
||||
|
||||
/**
|
||||
* Mosca
|
||||
* getStatus()
|
||||
*/
|
||||
config.mosca = {
|
||||
port: 1883,
|
||||
func GetStatus(ctx *iris.Context) {
|
||||
ctx.JSON(iris.StatusOK, iris.Map{"status": "OK"})
|
||||
}
|
||||
|
||||
|
||||
module.exports = config;
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
)
|
||||
|
||||
/**
|
||||
* Function validates JSON schema for `device` od `channel` models
|
||||
* By convention, Schema files must be kept as:
|
||||
* - ./models/deviceSchema.json
|
||||
* - ./models/channelSchema.json
|
||||
*/
|
||||
func validateJsonSchema(model string, body map[string]interface{}) bool {
|
||||
pwd, _ := os.Getwd()
|
||||
schemaLoader := gojsonschema.NewReferenceLoader("file://" + pwd +
|
||||
"/models/" + model + "Schema.json")
|
||||
bodyLoader := gojsonschema.NewGoLoader(body)
|
||||
result, err := gojsonschema.Validate(schemaLoader, bodyLoader)
|
||||
if err != nil {
|
||||
log.Print(err.Error())
|
||||
}
|
||||
|
||||
if result.Valid() {
|
||||
fmt.Printf("The document is valid\n")
|
||||
return true
|
||||
} else {
|
||||
fmt.Printf("The document is not valid. See errors :\n")
|
||||
for _, desc := range result.Errors() {
|
||||
fmt.Printf("- %s\n", desc)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"gopkg.in/mgo.v2"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
mainSession *mgo.Session
|
||||
mainDb *mgo.Database
|
||||
DbName string
|
||||
)
|
||||
|
||||
type MgoDb struct {
|
||||
Session *mgo.Session
|
||||
Db *mgo.Database
|
||||
Col *mgo.Collection
|
||||
}
|
||||
|
||||
func InitMongo(host string, port int, db string) error {
|
||||
var err error
|
||||
if mainSession == nil {
|
||||
mainSession, err = mgo.Dial("mongodb://" + host + ":" + strconv.Itoa(port))
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mainSession.SetMode(mgo.Monotonic, true)
|
||||
mainDb = mainSession.DB(db)
|
||||
DbName = db
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func SetMainSession(s *mgo.Session) {
|
||||
mainSession = s
|
||||
mainSession.SetMode(mgo.Monotonic, true)
|
||||
}
|
||||
|
||||
func SetMainDb(db string) {
|
||||
mainDb = mainSession.DB(db)
|
||||
DbName = db
|
||||
}
|
||||
|
||||
func (this *MgoDb) Init() *mgo.Session {
|
||||
this.Session = mainSession.Copy()
|
||||
this.Db = this.Session.DB(DbName)
|
||||
|
||||
return this.Session
|
||||
}
|
||||
|
||||
func (this *MgoDb) C(collection string) *mgo.Collection {
|
||||
this.Col = this.Session.DB(DbName).C(collection)
|
||||
return this.Col
|
||||
}
|
||||
|
||||
func (this *MgoDb) Close() bool {
|
||||
defer this.Session.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *MgoDb) DropoDb() {
|
||||
err := this.Session.DB(DbName).DropDatabase()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *MgoDb) RemoveAll(collection string) bool {
|
||||
this.Session.DB(DbName).C(collection).RemoveAll(nil)
|
||||
|
||||
this.Col = this.Session.DB(DbName).C(collection)
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *MgoDb) Index(collection string, keys []string) bool {
|
||||
index := mgo.Index{
|
||||
Key: keys,
|
||||
Unique: true,
|
||||
DropDups: true,
|
||||
Background: true,
|
||||
Sparse: true,
|
||||
}
|
||||
err := this.Db.C(collection).EnsureIndex(index)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *MgoDb) IsDup(err error) bool {
|
||||
if mgo.IsDup(err) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
###
|
||||
# Copyright (c) Mainflux
|
||||
#
|
||||
# Mainflux server is licensed under an Apache license, version 2.0 license.
|
||||
# All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
# See the included LICENSE file for more details.
|
||||
###
|
||||
|
||||
###
|
||||
# InfluxDB
|
||||
###
|
||||
influx:
|
||||
image: influxdb:latest
|
||||
container_name: mainflux-influxdb
|
||||
ports:
|
||||
- "8083:8083"
|
||||
- "8086:8086"
|
||||
|
||||
###
|
||||
# MongoDB
|
||||
###
|
||||
mongo:
|
||||
image: mongo:3.2
|
||||
container_name: mainflux-mongodb
|
||||
command: --smallfiles --nojournal
|
||||
|
||||
###
|
||||
# NATS
|
||||
###
|
||||
nats:
|
||||
image: apcera/gnatsd:latest
|
||||
container_name: mainflux-nats
|
||||
ports:
|
||||
- "4222:4222"
|
||||
- "8333:8333"
|
||||
|
||||
###
|
||||
# Mainflux Core Server
|
||||
###
|
||||
mainflux-core:
|
||||
image: mainflux/mainflux-core-server:latest
|
||||
container_name: mainflux-core
|
||||
volumes:
|
||||
- ./config/core/config.yml:/go/src/github.com/mainflux/mainflux-core-server/config.yml
|
||||
links:
|
||||
- influx
|
||||
- mongo
|
||||
- nats
|
||||
|
||||
###
|
||||
# Mainflux HTTP Server
|
||||
###
|
||||
mainflux-http:
|
||||
image: mainflux/mainflux-http-server:latest
|
||||
container_name: mainflux-http
|
||||
volumes:
|
||||
- ./config/http/config.yml:/go/src/github.com/mainflux/mainflux-http-server/config.yml
|
||||
links:
|
||||
- nats
|
||||
ports:
|
||||
- "7070:7070"
|
||||
|
||||
###
|
||||
# Mainflux MQTT Server
|
||||
###
|
||||
mainflux-mqtt:
|
||||
image: mainflux/mainflux-mqtt-server:latest
|
||||
container_name: mainflux-mqtt
|
||||
volumes:
|
||||
- ./config/mqtt/config.js:/mainflux-mqtt/config.js
|
||||
links:
|
||||
- nats
|
||||
ports:
|
||||
- "1883:1883"
|
||||
|
||||
###
|
||||
# Mainflux WS Server
|
||||
###
|
||||
mainflux-ws:
|
||||
image: mainflux/mainflux-ws-server:latest
|
||||
container_name: mainflux-ws
|
||||
volumes:
|
||||
- ./config/ws/config.js:/mainflux-ws/config.js
|
||||
links:
|
||||
- nats
|
||||
ports:
|
||||
- "9090:9090"
|
||||
|
||||
+4
-57
@@ -6,16 +6,6 @@
|
||||
# See the included LICENSE file for more details.
|
||||
###
|
||||
|
||||
###
|
||||
# InfluxDB
|
||||
###
|
||||
influx:
|
||||
image: influxdb:latest
|
||||
container_name: mainflux-influxdb
|
||||
ports:
|
||||
- "8083:8083"
|
||||
- "8086:8086"
|
||||
|
||||
###
|
||||
# MongoDB
|
||||
###
|
||||
@@ -25,56 +15,13 @@ mongo:
|
||||
command: --smallfiles --nojournal
|
||||
|
||||
###
|
||||
# NATS
|
||||
# Mainflux Lite
|
||||
###
|
||||
nats:
|
||||
image: apcera/gnatsd:latest
|
||||
container_name: mainflux-nats
|
||||
ports:
|
||||
- "4222:4222"
|
||||
- "8333:8333"
|
||||
|
||||
###
|
||||
# Mainflux Core Server
|
||||
###
|
||||
mainflux-core:
|
||||
image: mainflux/mainflux-core-server:latest
|
||||
container_name: mainflux-core
|
||||
mainflux-lite:
|
||||
image: mainflux/mainflux-lite:latest
|
||||
container_name: mainflux-lite
|
||||
links:
|
||||
- influx
|
||||
- mongo
|
||||
- nats
|
||||
|
||||
###
|
||||
# Mainflux HTTP Server
|
||||
###
|
||||
mainflux-http:
|
||||
image: mainflux/mainflux-http-server:latest
|
||||
container_name: mainflux-http
|
||||
links:
|
||||
- nats
|
||||
ports:
|
||||
- "7070:7070"
|
||||
|
||||
###
|
||||
# Mainflux MQTT Server
|
||||
###
|
||||
mainflux-mqtt:
|
||||
image: mainflux/mainflux-mqtt-server:latest
|
||||
container_name: mainflux-mqtt
|
||||
links:
|
||||
- nats
|
||||
ports:
|
||||
- "1883:1883"
|
||||
|
||||
###
|
||||
# Mainflux WS Server
|
||||
###
|
||||
mainflux-ws:
|
||||
image: mainflux/mainflux-ws-server:latest
|
||||
container_name: mainflux-ws
|
||||
links:
|
||||
- nats
|
||||
ports:
|
||||
- "9090:9090"
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"strconv"
|
||||
"github.com/mainflux/mainflux-lite/config"
|
||||
"github.com/mainflux/mainflux-lite/db"
|
||||
"github.com/mainflux/mainflux-lite/servers"
|
||||
"github.com/mainflux/mainflux-lite/clients"
|
||||
"github.com/fatih/color"
|
||||
"runtime"
|
||||
"flag"
|
||||
)
|
||||
|
||||
type MainfluxLite struct {
|
||||
}
|
||||
|
||||
var usageStr = `
|
||||
Usage: mainflux [options]
|
||||
Server Options:
|
||||
-a, --addr <host> Bind to host address (default: 0.0.0.0)
|
||||
-p, --port <port> Use port for clients (default: 4222)
|
||||
-P, --pid <file> File to store PID
|
||||
-c, --config <file> Configuration file
|
||||
Logging Options:
|
||||
-l, --log <file> File to redirect log output
|
||||
-T, --logtime Timestamp log entries (default: true)
|
||||
-D, --debug Enable debugging output
|
||||
-V, --trace Trace the raw protocol
|
||||
-DV Debug and trace
|
||||
Common Options:
|
||||
-h, --help Show this message
|
||||
-v, --version Show version
|
||||
`
|
||||
|
||||
// usage will print out the flag options for the server.
|
||||
func usage() {
|
||||
fmt.Printf("%s\n", usageStr)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// PrintServerAndExit will print our version and exit.
|
||||
func PrintServerAndExit() {
|
||||
fmt.Printf("Mainflux version %s\n", VERSION)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Server Options
|
||||
opts := Options{}
|
||||
|
||||
var showVersion bool
|
||||
var debugAndTrace bool
|
||||
var configFile string
|
||||
|
||||
// Parse flags
|
||||
flag.IntVar(&opts.Port, "port", 0, "Port to listen on.")
|
||||
flag.IntVar(&opts.Port, "p", 0, "Port to listen on.")
|
||||
flag.StringVar(&opts.Host, "host", "", "Network host to listen on.")
|
||||
flag.StringVar(&opts.Host, "h", "", "Network host to listen on.")
|
||||
flag.StringVar(&opts.Host, "net", "", "Network host to listen on.")
|
||||
flag.BoolVar(&opts.Debug, "D", false, "Enable Debug logging.")
|
||||
flag.BoolVar(&opts.Debug, "debug", false, "Enable Debug logging.")
|
||||
flag.BoolVar(&opts.Trace, "V", false, "Enable Trace logging.")
|
||||
flag.BoolVar(&opts.Trace, "trace", false, "Enable Trace logging.")
|
||||
flag.BoolVar(&debugAndTrace, "DV", false, "Enable Debug and Trace logging.")
|
||||
flag.BoolVar(&opts.Logtime, "T", true, "Timestamp log entries.")
|
||||
flag.BoolVar(&opts.Logtime, "logtime", true, "Timestamp log entries.")
|
||||
flag.StringVar(&opts.Username, "user", "", "Username required for connection.")
|
||||
flag.StringVar(&opts.Password, "pass", "", "Password required for connection.")
|
||||
flag.StringVar(&opts.Authorization, "auth", "", "Authorization token required for connection.")
|
||||
flag.StringVar(&configFile, "c", "", "Configuration file.")
|
||||
flag.StringVar(&configFile, "config", "", "Configuration file.")
|
||||
flag.StringVar(&opts.PidFile, "P", "", "File to store process pid.")
|
||||
flag.StringVar(&opts.PidFile, "pid", "", "File to store process pid.")
|
||||
flag.StringVar(&opts.LogFile, "l", "", "File to store logging output.")
|
||||
flag.StringVar(&opts.LogFile, "log", "", "File to store logging output.")
|
||||
flag.BoolVar(&showVersion, "version", false, "Print version information.")
|
||||
flag.BoolVar(&showVersion, "v", false, "Print version information.")
|
||||
|
||||
flag.Usage = usage
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// Show version and exit
|
||||
if showVersion {
|
||||
PrintServerAndExit()
|
||||
}
|
||||
|
||||
// One flag can set multiple options.
|
||||
if debugAndTrace {
|
||||
opts.Trace, opts.Debug = true, true
|
||||
}
|
||||
|
||||
// Process args looking for non-flag options,
|
||||
// 'version' and 'help' only for now
|
||||
for _, arg := range flag.Args() {
|
||||
switch strings.ToLower(arg) {
|
||||
case "version":
|
||||
PrintServerAndExit()
|
||||
case "help":
|
||||
usage()
|
||||
}
|
||||
}
|
||||
|
||||
// Parse config
|
||||
var cfg config.Config
|
||||
cfg.Parse()
|
||||
|
||||
// MongoDb
|
||||
db.InitMongo(cfg.MongoHost, cfg.MongoPort, cfg.MongoDatabase)
|
||||
|
||||
// MQTT
|
||||
mqc := new(clients.MqttConn)
|
||||
//Sub to everything comming on all channels of all devices
|
||||
mqc.MqttSub()
|
||||
|
||||
// Serve HTTP
|
||||
go servers.HttpServer(cfg)
|
||||
|
||||
// Print banner
|
||||
color.Cyan(banner)
|
||||
color.Cyan("Magic happens on port " + strconv.Itoa(cfg.HttpPort))
|
||||
|
||||
/** Keep main() runnig */
|
||||
runtime.Goexit()
|
||||
}
|
||||
|
||||
var banner = `
|
||||
_| _| _| _|_| _|
|
||||
_|_| _|_| _|_|_| _|_|_| _| _| _| _| _| _|
|
||||
_| _| _| _| _| _| _| _| _|_|_|_| _| _| _| _|_|
|
||||
_| _| _| _| _| _| _| _| _| _| _| _| _|
|
||||
_| _| _|_|_| _| _| _| _| _| _|_|_| _| _|
|
||||
|
||||
|
||||
== Industrial IoT System ==
|
||||
|
||||
Made with <3 by Mainflux Team
|
||||
[w] http://mainflux.io
|
||||
[t] @mainflux
|
||||
|
||||
** LITE **
|
||||
|
||||
`
|
||||
@@ -0,0 +1,20 @@
|
||||
## JSON Modles
|
||||
Mainflux uses 2 entities in the system to represent devices and their properties:
|
||||
- device
|
||||
- channel
|
||||
|
||||
**Device** represents device itself - model, type, serial number, location...
|
||||
|
||||
**Channel** represents an observable property of device that we measure - temperature, pressure, light, velocity...
|
||||
|
||||
### Creating JSON Schema From the Model Templates
|
||||
We use `deviceTemplate.json` and `channelTemplate.json` to describe our entities. Then based on these files
|
||||
we can create more decriptive documents - [JSON Schemas](http://json-schema.org/).
|
||||
|
||||
To do this we can use on-line tool [http://jsonschema.net/](http://jsonschema.net/) or `npm` package `json-schema-generator`:
|
||||
```bash
|
||||
sudo npm install -g json-schema-generator
|
||||
json-schema-generator ./deviceTemplate.json -o deviceSchema.json
|
||||
```
|
||||
|
||||
Schemas will be used to perform JSON schema validation during API calls, as described in [this article](http://www.litixsoft.de/english/mms-json-schema/)
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/krylovsk/gosenml"
|
||||
)
|
||||
|
||||
type (
|
||||
Channel struct {
|
||||
Id string `json:"id"`
|
||||
Device string `json:"device"`
|
||||
Created string `json:"created"`
|
||||
Updated string `json:"updated"`
|
||||
|
||||
Values []gosenml.Entry `json:"values"`
|
||||
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"e": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"n": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"u": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"v": {
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"sv": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"bv": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"s": {
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"t": {
|
||||
"type": "integer",
|
||||
"default": 0
|
||||
},
|
||||
"ut": {
|
||||
"type": "integer",
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
"default": {"n": "", "u": "", "v": 0, "sv": "", "bv": false, "s": 0, "t": 0, "ut": 0},
|
||||
"required": [
|
||||
"n"
|
||||
]
|
||||
}
|
||||
},
|
||||
"bn": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"bt": {
|
||||
"type": "integer",
|
||||
"default": 0
|
||||
},
|
||||
"ver": {
|
||||
"type": "integer",
|
||||
"default": 0
|
||||
},
|
||||
"bu": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"device": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"created": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"updated": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"ts" : {
|
||||
"e":[
|
||||
{
|
||||
"n": "voltage",
|
||||
"u": "V",
|
||||
"v": 120.1,
|
||||
"sv": "one-two",
|
||||
"bv": true,
|
||||
"s": 321.12,
|
||||
"t": -5,
|
||||
"ut": -1
|
||||
},
|
||||
{ "n": "current", "t": -5, "v": 1.2 },
|
||||
{ "n": "current", "t": -4, "v": 1.30 },
|
||||
{ "n": "current", "t": -3, "v": 0.14e1 },
|
||||
{ "n": "current", "t": -2, "v": 1.5 },
|
||||
{ "n": "current", "t": -1, "v": 1.6 },
|
||||
{ "n": "current", "t": 0, "v": 1.7 }
|
||||
],
|
||||
"bn": "",
|
||||
"bt": 1276020076,
|
||||
"ver": 1,
|
||||
"bu": "A"
|
||||
},
|
||||
|
||||
"id": "88fce370-1c68-11e6-8610-27f9510f1a02",
|
||||
"device": "9ef8de30-20dd-11e6-9e63-43ae8893b86e",
|
||||
|
||||
"created": "2014-09-01T10:00:00.764Z",
|
||||
"updated": "2014-09-02T10:00:00.245Z",
|
||||
|
||||
"metadata": {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package models
|
||||
|
||||
type (
|
||||
DeviceLocation struct {
|
||||
Name string `json:"name"`
|
||||
Latitude int `json:"latitude"`
|
||||
Longitude int `json:"longitude"`
|
||||
Elevation int `json:"elevation"`
|
||||
}
|
||||
|
||||
Device struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
Description string `json:"description"`
|
||||
Visibility string `json:"visibility"`
|
||||
Status string `json:"status"`
|
||||
Tags []string `json:"tags"`
|
||||
Location DeviceLocation `json:"location"`
|
||||
|
||||
Created string `json:"created"`
|
||||
Updated string `json:"updated"`
|
||||
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"readOnly": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": "Unnamed Device"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"visibility": {
|
||||
"type": "string",
|
||||
"default": "private"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"default": "disabled"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"latitude": {
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"longitude": {
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"elevation": {
|
||||
"type": "integer",
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
"default": {"name": "", "latitude": 0, "longitude": 0, "elevation": 0}
|
||||
},
|
||||
"created": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"reaOnly": true
|
||||
},
|
||||
"updated": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"readOnly": true
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"id": "88fce370-1c68-11e6-8610-27f9510f1a02",
|
||||
"name": "Some Device",
|
||||
"description": "Longer description for Some Device",
|
||||
"visibility": "private",
|
||||
"status": "enabled",
|
||||
"tags": [ "lorem", "ipsum" ],
|
||||
"location": {
|
||||
"name": "Storage Room",
|
||||
"latitude": -37.9788423562422,
|
||||
"longitude": -57.5478776916862,
|
||||
"elevation": 5
|
||||
},
|
||||
"created": "2014-09-01T10:00:00.764Z",
|
||||
"updated": "2014-09-02T10:00:00.245Z",
|
||||
"metadata": {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* FluxMQ is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
// Options block for gnatsd server.
|
||||
type Options struct {
|
||||
Host string
|
||||
Port int
|
||||
Trace bool
|
||||
Debug bool
|
||||
MaxConn int
|
||||
Logtime bool
|
||||
Authorization string
|
||||
Username string
|
||||
Password string
|
||||
PidFile string
|
||||
LogFile string
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package servers
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/mainflux/mainflux-lite/controllers"
|
||||
"github.com/mainflux/mainflux-lite/config"
|
||||
|
||||
"github.com/iris-contrib/middleware/logger"
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
|
||||
func HttpServer(cfg config.Config) {
|
||||
// Iris config
|
||||
iris.Config.DisableBanner = true
|
||||
|
||||
// set the global middlewares
|
||||
iris.Use(logger.New())
|
||||
|
||||
// set the custom errors
|
||||
iris.OnError(iris.StatusNotFound, func(ctx *iris.Context) {
|
||||
ctx.Render("errors/404.html", iris.Map{"Title": iris.StatusText(iris.StatusNotFound)})
|
||||
})
|
||||
|
||||
iris.OnError(iris.StatusInternalServerError, func(ctx *iris.Context) {
|
||||
ctx.Render("errors/500.html", nil, iris.RenderOptions{"layout": iris.NoLayout})
|
||||
})
|
||||
|
||||
// register public API
|
||||
registerRoutes()
|
||||
|
||||
// start the server
|
||||
iris.Listen(cfg.HttpHost + ":" + strconv.Itoa(cfg.HttpPort))
|
||||
|
||||
// Use following to start HTTPS server on the same port
|
||||
//iris.ListenTLS(cfg.HttpHost + ":" + strconv.Itoa(cfg.HttpPort), "tls/mainflux.crt", "tls/mainflux.key")
|
||||
}
|
||||
|
||||
func registerRoutes() {
|
||||
// STATUS
|
||||
iris.Get("/status", controllers.GetStatus)
|
||||
|
||||
// DEVICES
|
||||
iris.Post("/devices", controllers.CreateDevice)
|
||||
iris.Get("/devices", controllers.GetDevices)
|
||||
|
||||
iris.Get("/devices/:device_id", controllers.GetDevice)
|
||||
iris.Put("/devices/:device_id", controllers.UpdateDevice)
|
||||
|
||||
iris.Delete("/devices/:device_id", controllers.DeleteDevice)
|
||||
|
||||
// CHANNELS
|
||||
iris.Post("/devices/:device_id/channels", controllers.CreateChannel)
|
||||
iris.Get("/devices/:device_id/channels", controllers.GetChannels)
|
||||
|
||||
iris.Get("/devices/:device_id/channels/:channel_id", controllers.GetChannel)
|
||||
iris.Put("/devices/:device_id/channels/:channel_id", controllers.UpdateChannel)
|
||||
|
||||
iris.Delete("/devices/:device_id/channels/:channel_id", controllers.DeleteChannel)
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) Mainflux
|
||||
*
|
||||
* Mainflux server is licensed under an Apache license, version 2.0.
|
||||
* All rights not explicitly granted in the Apache license, version 2.0 are reserved.
|
||||
* See the included LICENSE file for more details.
|
||||
*/
|
||||
|
||||
package servers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/mainflux/mainflux-lite/config"
|
||||
mfdb "github.com/mainflux/mainflux-lite/db"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/ory-am/dockertest"
|
||||
"gopkg.in/mgo.v2"
|
||||
)
|
||||
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// We are in testing - notify the program
|
||||
// so that it is not confused if some other commad line
|
||||
// arguments come in - for example when test is started with `go test -v ./...`
|
||||
// which is what Travis does
|
||||
os.Setenv("TEST_ENV", "1")
|
||||
|
||||
var db *mgo.Session
|
||||
c, err := dockertest.ConnectToMongoDB(15, time.Millisecond*500, func(url string) bool {
|
||||
// This callback function checks if the image's process is responsive.
|
||||
// Sometimes, docker images are booted but the process (in this case MongoDB) is still doing maintenance
|
||||
// before being fully responsive which might cause issues like "TCP Connection reset by peer".
|
||||
var err error
|
||||
db, err = mgo.Dial(url)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Sometimes, dialing the database is not enough because the port is already open but the process is not responsive.
|
||||
// Most database conenctors implement a ping function which can be used to test if the process is responsive.
|
||||
// Alternatively, you could execute a query to see if an error occurs or not.
|
||||
return db.Ping() == nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Could not connect to database: %s", err)
|
||||
}
|
||||
|
||||
// Set-up DB
|
||||
mfdb.SetMainSession(db)
|
||||
mfdb.SetMainDb("mainflux_test")
|
||||
|
||||
// Run tests
|
||||
result := m.Run()
|
||||
|
||||
// Close database connection.
|
||||
db.Close()
|
||||
|
||||
// Clean up image.
|
||||
c.KillRemove()
|
||||
|
||||
// Exit tests.
|
||||
os.Exit(result)
|
||||
}
|
||||
|
||||
func TestServer(t *testing.T) {
|
||||
|
||||
// Config
|
||||
var cfg config.Config
|
||||
cfg.Parse()
|
||||
|
||||
go HttpServer(cfg)
|
||||
|
||||
// prepare test framework
|
||||
if ok := <-iris.Available; !ok {
|
||||
t.Fatal("Unexpected error: server cannot start, please report this as bug!!")
|
||||
}
|
||||
|
||||
|
||||
e := iris.Tester(t)
|
||||
r := e.Request("GET", "/status").Expect().Status(iris.StatusOK).JSON()
|
||||
fmt.Println("%v", r)
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
## Locust Performance Testing
|
||||
Locust website: http://locust.io/
|
||||
|
||||
Locust is an easy-to-use, distributed, user load testing tool.
|
||||
Intended for load testing web sites (or other systems) and figuring out how many
|
||||
concurrent users a system can handle.
|
||||
|
||||
## Usage
|
||||
```bash
|
||||
locust -f locustfile.py --host=http://localhost:1026
|
||||
```
|
||||
|
||||
Open the browser at localhost:8089 and start the swarm.
|
||||
|
||||
## Documentation
|
||||
http://docs.locust.io/en/latest/index.html
|
||||
@@ -1,19 +0,0 @@
|
||||
from locust import HttpLocust, TaskSet, task, ResponseError
|
||||
|
||||
class CheckStatus(TaskSet):
|
||||
def on_start(self):
|
||||
""" on_start is called when a Locust start before any task is scheduled """
|
||||
print("Locus test started")
|
||||
|
||||
@task(1)
|
||||
def getVersion(self):
|
||||
headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
|
||||
response = self.client.get("/version", headers=headers)
|
||||
|
||||
print "Response status code:", response.status_code
|
||||
print "Response content:", response.content
|
||||
|
||||
class HelloLocust(HttpLocust):
|
||||
task_set = CheckStatus
|
||||
min_wait=5000
|
||||
max_wait=9000
|
||||
@@ -0,0 +1,3 @@
|
||||
# TLS in Mainflux
|
||||
|
||||
Documentation on TLS in Mainflux can be found in official [mainflux-doc](https://github.com/Mainflux/mainflux-doc) repo.
|
||||
@@ -0,0 +1,32 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
crIHY4DycNHKqah++V/w4m5ASqFIZT4=
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"id": "d9799076-e11c-8926-da5e-9092763cf5bb",
|
||||
"name": "mainfluxHttp",
|
||||
"description": "",
|
||||
"order": [
|
||||
"b27812bf-0e9b-7a20-102c-b901c535e35f",
|
||||
"2609fa8b-e39b-5cd9-407e-81ff4d1124b7",
|
||||
"c28e0d5b-b271-960b-6b41-f3c2b52670b1",
|
||||
"887ad31c-2e30-f600-1035-68c38c11c3f7"
|
||||
],
|
||||
"folders": [],
|
||||
"timestamp": 1463596373226,
|
||||
"owner": "",
|
||||
"remoteLink": "",
|
||||
"public": false,
|
||||
"requests": [
|
||||
{
|
||||
"id": "2609fa8b-e39b-5cd9-407e-81ff4d1124b7",
|
||||
"url": "http://localhost:7070/devices/3686f6d0-1c68-11e6-8610-27f9510f1a02",
|
||||
"method": "DELETE",
|
||||
"headers": "Content-Type: application/json\n",
|
||||
"data": null,
|
||||
"dataMode": "params",
|
||||
"tests": null,
|
||||
"preRequestScript": null,
|
||||
"currentHelper": "normal",
|
||||
"pathVariables": {},
|
||||
"version": 2,
|
||||
"name": "http://localhost:7070/devices/3686f6d0-1c68-11e6-8610-27f9510f1a02",
|
||||
"description": "",
|
||||
"descriptionFormat": "html",
|
||||
"collectionId": "d9799076-e11c-8926-da5e-9092763cf5bb"
|
||||
},
|
||||
{
|
||||
"id": "887ad31c-2e30-f600-1035-68c38c11c3f7",
|
||||
"url": "http://localhost:7070/devices",
|
||||
"method": "POST",
|
||||
"headers": "Content-Type: application/json\n",
|
||||
"data": [],
|
||||
"dataMode": "raw",
|
||||
"tests": null,
|
||||
"preRequestScript": null,
|
||||
"currentHelper": "normal",
|
||||
"pathVariables": {},
|
||||
"version": 2,
|
||||
"name": "http://localhost:7070/devices",
|
||||
"description": "",
|
||||
"descriptionFormat": "html",
|
||||
"collectionId": "d9799076-e11c-8926-da5e-9092763cf5bb",
|
||||
"rawModeData": "{\n \"deviceName\" : \"board01\",\n \"deviceType\" : \"weio\"\n}"
|
||||
},
|
||||
{
|
||||
"id": "b27812bf-0e9b-7a20-102c-b901c535e35f",
|
||||
"url": "http://localhost:7070/devices",
|
||||
"method": "GET",
|
||||
"headers": "Content-Type: application/json\n",
|
||||
"data": null,
|
||||
"dataMode": "params",
|
||||
"tests": null,
|
||||
"preRequestScript": null,
|
||||
"currentHelper": "normal",
|
||||
"pathVariables": {},
|
||||
"version": 2,
|
||||
"name": "http://localhost:7070/devices",
|
||||
"description": "",
|
||||
"descriptionFormat": "html",
|
||||
"collectionId": "d9799076-e11c-8926-da5e-9092763cf5bb"
|
||||
},
|
||||
{
|
||||
"id": "c28e0d5b-b271-960b-6b41-f3c2b52670b1",
|
||||
"url": "http://localhost:7070/devices/3686f6d0-1c68-11e6-8610-27f9510f1a02",
|
||||
"method": "PUT",
|
||||
"headers": "Content-Type: application/json\n",
|
||||
"data": [],
|
||||
"dataMode": "raw",
|
||||
"tests": null,
|
||||
"preRequestScript": null,
|
||||
"currentHelper": "normal",
|
||||
"pathVariables": {},
|
||||
"version": 2,
|
||||
"name": "http://localhost:7070/devices/3686f6d0-1c68-11e6-8610-27f9510f1a02",
|
||||
"description": "",
|
||||
"descriptionFormat": "html",
|
||||
"collectionId": "d9799076-e11c-8926-da5e-9092763cf5bb",
|
||||
"rawModeData": "{\n \"deviceName\" : \"board05\",\n \"deviceType\" : \"ESP7766\",\n \"attributes\": [\n {\n \"temperature\": 15\n },\n {\n \"pressure\": 10\n \n }\n ]\n}\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user