boilerplate code
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
package app
|
||||
|
||||
type App struct {
|
||||
}
|
||||
|
||||
func NewApp(environment string) *App {
|
||||
|
||||
startRotativeLogger(environment)
|
||||
|
||||
return &App{}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package broker
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
type NATS struct {
|
||||
*nats.Conn
|
||||
}
|
||||
|
||||
func NewNATS(url string) *NATS {
|
||||
conn, err := nats.Connect(url)
|
||||
if err != nil {
|
||||
slog.Error("cannot stablise a connection to server", "error", err)
|
||||
}
|
||||
|
||||
return &NATS{
|
||||
Conn: conn,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package sensors
|
||||
@@ -0,0 +1,110 @@
|
||||
package sensors
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"nats-app/internal/iot"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
const (
|
||||
subjectSensorsRegister = "sensors.register"
|
||||
subjectSensorsUpdate = "sensors.update"
|
||||
subjectSensorsGet = "sensors.get"
|
||||
subjectSensorsValuesGet = "sensors.values.get"
|
||||
subjectSensorsList = "sensors.list"
|
||||
)
|
||||
|
||||
type Handlers struct {
|
||||
service *Service
|
||||
*iot.IoTDevice
|
||||
}
|
||||
|
||||
func NewHandlers(service *Service, iot *iot.IoTDevice) *Handlers {
|
||||
return &Handlers{
|
||||
service: service,
|
||||
IoTDevice: iot,
|
||||
}
|
||||
}
|
||||
|
||||
func handleRequest[Req any, Res any](msg *nats.Msg, handler func(Req) (Res, error)) {
|
||||
var req Req
|
||||
if err := json.Unmarshal(msg.Data, &req); err != nil {
|
||||
msg.Respond([]byte(`{"error":"invalid request"}`))
|
||||
return
|
||||
}
|
||||
|
||||
result, err := handler(req)
|
||||
if err != nil {
|
||||
msg.Respond([]byte(`{"error":"` + err.Error() + `"}`))
|
||||
return
|
||||
}
|
||||
|
||||
response, _ := json.Marshal(result)
|
||||
msg.Respond(response)
|
||||
}
|
||||
|
||||
func (h *Handlers) SetupEndpoints() *Handlers {
|
||||
h.register()
|
||||
h.update()
|
||||
h.get()
|
||||
h.getValues()
|
||||
h.list()
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *Handlers) register() {
|
||||
h.NATS.Subscribe(subjectSensorsRegister, func(msg *nats.Msg) {
|
||||
handleRequest(msg, func(req Sensor) (Sensor, error) {
|
||||
// service layer
|
||||
|
||||
return req, nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handlers) update() {
|
||||
h.NATS.Subscribe(subjectSensorsUpdate, func(msg *nats.Msg) {
|
||||
handleRequest(msg, func(req Sensor) (Sensor, error) {
|
||||
// service layer
|
||||
|
||||
return req, nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handlers) get() {
|
||||
h.NATS.Subscribe(subjectSensorsGet, func(msg *nats.Msg) {
|
||||
handleRequest(msg, func(req struct {
|
||||
SensorID string `json:"sensor_id"`
|
||||
}) (Sensor, error) {
|
||||
// service layer
|
||||
|
||||
return Sensor{}, nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handlers) getValues() {
|
||||
h.NATS.Subscribe(subjectSensorsValuesGet, func(msg *nats.Msg) {
|
||||
handleRequest(msg, func(req struct {
|
||||
SensorID string `json:"sensor_id"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
}) ([]SensorData, error) {
|
||||
// service layer
|
||||
|
||||
return []SensorData{}, nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handlers) list() {
|
||||
h.NATS.Subscribe(subjectSensorsList, func(msg *nats.Msg) {
|
||||
handleRequest(msg, func(req struct{}) ([]Sensor, error) {
|
||||
// service layer
|
||||
|
||||
return []Sensor{}, nil
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package sensors
|
||||
|
||||
import "time"
|
||||
|
||||
type SType string
|
||||
|
||||
const (
|
||||
Temperature SType = "temperature"
|
||||
Humidity SType = "humidity"
|
||||
CarbonDioxide SType = "carbon_dioxide"
|
||||
Pressure SType = "pressure"
|
||||
Proximity SType = "proximity"
|
||||
Light SType = "light"
|
||||
// and more...
|
||||
)
|
||||
|
||||
type Sensor struct {
|
||||
SensorID string `json:"sensor_id"`
|
||||
SensorType SType `json:"sensor_type"`
|
||||
SamplingInterval time.Duration `json:"sampling"`
|
||||
ThresholdAbove float64 `json:"thresoldabove"`
|
||||
ThresholdBelow float64 `json:"thresoldbelow"`
|
||||
SensorData *[]SensorData `json:"sensor_data,omitempty"`
|
||||
}
|
||||
|
||||
type SensorData struct {
|
||||
Value float64 `json:"value"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package sensors
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
RegisterSensor(s Sensor) error
|
||||
UpdateSensorConfig(s Sensor) error
|
||||
ReadSensor(id int) (Sensor, error)
|
||||
ReadSensorValues(id int, from, to time.Time) ([]SensorData, error)
|
||||
ReadAllSensors() ([]Sensor, error)
|
||||
}
|
||||
|
||||
type pgxRepo struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func newPGXRepo(pool *pgxpool.Pool) Repository {
|
||||
return &pgxRepo{
|
||||
pool: pool,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pgxRepo) ReadSensor(id int) (Sensor, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (p *pgxRepo) UpdateSensorConfig(s Sensor) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (p *pgxRepo) RegisterSensor(s Sensor) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (p *pgxRepo) ReadSensorValues(id int, from time.Time, to time.Time) ([]SensorData, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (p *pgxRepo) ReadAllSensors() ([]Sensor, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
type inMemory struct {
|
||||
sensors map[string]*Sensor
|
||||
mu *sync.Mutex
|
||||
}
|
||||
|
||||
func newInMemoryRepo() Repository {
|
||||
return &inMemory{
|
||||
sensors: make(map[string]*Sensor),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *inMemory) RegisterSensor(s Sensor) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (i *inMemory) UpdateSensorConfig(s Sensor) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (i *inMemory) ReadSensor(id int) (Sensor, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (i *inMemory) ReadSensorValues(id int, from time.Time, to time.Time) ([]SensorData, error) {
|
||||
// holds only last 100 values for every sensor
|
||||
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (i *inMemory) ReadAllSensors() ([]Sensor, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
type DecoratorRepo struct {
|
||||
db Repository
|
||||
memory Repository
|
||||
}
|
||||
|
||||
func NewDecoratorRepo(pool *pgxpool.Pool) Repository {
|
||||
|
||||
db := newPGXRepo(pool)
|
||||
memory := newInMemoryRepo()
|
||||
|
||||
sensors, err := db.ReadAllSensors()
|
||||
if err != nil {
|
||||
slog.Error("error warming up cache")
|
||||
}
|
||||
|
||||
for _, s := range sensors {
|
||||
_ = memory.RegisterSensor(s)
|
||||
}
|
||||
|
||||
return &DecoratorRepo{
|
||||
db: db,
|
||||
memory: memory,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DecoratorRepo) RegisterSensor(s Sensor) error {
|
||||
if err := d.db.RegisterSensor(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = d.memory.RegisterSensor(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DecoratorRepo) UpdateSensorConfig(s Sensor) error {
|
||||
if err := d.db.UpdateSensorConfig(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = d.memory.UpdateSensorConfig(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DecoratorRepo) ReadSensor(id int) (Sensor, error) {
|
||||
sensor, err := d.memory.ReadSensor(id)
|
||||
if err == nil {
|
||||
return sensor, nil
|
||||
}
|
||||
|
||||
return d.db.ReadSensor(id)
|
||||
}
|
||||
|
||||
func (d *DecoratorRepo) ReadSensorValues(id int, from, to time.Time) ([]SensorData, error) {
|
||||
values, err := d.memory.ReadSensorValues(id, from, to)
|
||||
if err == nil && len(values) > 0 {
|
||||
return values, nil
|
||||
}
|
||||
|
||||
return d.db.ReadSensorValues(id, from, to)
|
||||
}
|
||||
|
||||
func (d *DecoratorRepo) ReadAllSensors() ([]Sensor, error) {
|
||||
sensors, err := d.memory.ReadAllSensors()
|
||||
if err == nil && len(sensors) > 0 {
|
||||
return sensors, nil
|
||||
}
|
||||
|
||||
return d.db.ReadAllSensors()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package sensors
|
||||
|
||||
type Service struct {
|
||||
repo Repository
|
||||
}
|
||||
|
||||
func NewService(repo Repository) *Service {
|
||||
return &Service{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package sensors
|
||||
|
||||
type Simulator struct{}
|
||||
@@ -0,0 +1,24 @@
|
||||
package iot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
)
|
||||
|
||||
func NewPGXPool(datasource string) *pgxpool.Pool {
|
||||
dbPool, err := pgxpool.New(context.Background(), datasource)
|
||||
if err != nil {
|
||||
slog.Error("error connecting to database", "error", err, "datasource", datasource)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := dbPool.Ping(context.Background()); err != nil {
|
||||
slog.Error("error pinging database, maybe incorrect datasource", "error", err, "datasource", datasource)
|
||||
panic(err)
|
||||
}
|
||||
slog.Info("connected to database", "datasource", datasource)
|
||||
return dbPool
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package iot
|
||||
|
||||
import (
|
||||
"nats-app/internal/broker"
|
||||
)
|
||||
|
||||
type IoTDevice struct {
|
||||
NATS *broker.NATS
|
||||
}
|
||||
|
||||
func Start(environment, url string) *IoTDevice {
|
||||
|
||||
startRotativeLogger(environment)
|
||||
nats := broker.NewNATS(url)
|
||||
|
||||
return &IoTDevice{
|
||||
NATS: nats,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package app
|
||||
package iot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
Reference in New Issue
Block a user