mirror of
https://github.com/absmach/supermq.git
synced 2026-06-23 06:40:19 +00:00
MG-94 - Add backend support for reports (#107)
* initial implementation Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix missing variable Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix api and add report config to rule engine Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix repo command Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix failing linter Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix download request Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix download api Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix failing linter Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix add report config Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove unused parameters Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * add limit field to config Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * add test and address comments Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove unused code Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * add logger Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove logger Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * uncomment code Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * add status check Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * address comments Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * resolve conflicts Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix failing linter Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * rebase code Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix startdate when zero Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove unused code Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * address comments Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * add time expression parser and logics Signed-off-by: Arvindh <arvindh91@gmail.com> * fix postgres methods Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix failing linter Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix pdf and csv generation Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix failing linter Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * add description for reports Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove aggregation field Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove unused code Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove logs Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix go mod file Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix endpoint and postgres methods Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * address comments Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * update report config update methods Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix failing linter Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix service test Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove unnecessary check Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * address comments Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove endpoints Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove unused code Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix generate PDF and CSV Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove unused code Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * address comments Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix failing linter Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * revert UI variable Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * add empty line Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix go mod file Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * update download api Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * revert UI variable Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix download endpoint Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * update generateREport method Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix failing tests Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * refactor generate api Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix failing linter Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix failing linter Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix csv column Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * fix csv generator Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * remove logs Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> * updated reports logic and api Signed-off-by: Arvindh <arvindh91@gmail.com> * fix time conversion Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> --------- Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> Signed-off-by: Arvindh <arvindh91@gmail.com> Co-authored-by: Arvindh <arvindh91@gmail.com>
This commit is contained in:
@@ -472,11 +472,11 @@ type SenMLMessage struct {
|
||||
Unit string `protobuf:"bytes,3,opt,name=unit,proto3" json:"unit,omitempty"`
|
||||
Time float64 `protobuf:"fixed64,4,opt,name=time,proto3" json:"time,omitempty"`
|
||||
UpdateTime float64 `protobuf:"fixed64,5,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"`
|
||||
Value float64 `protobuf:"fixed64,6,opt,name=value,proto3" json:"value,omitempty"`
|
||||
StringValue string `protobuf:"bytes,7,opt,name=string_value,json=stringValue,proto3" json:"string_value,omitempty"`
|
||||
DataValue string `protobuf:"bytes,8,opt,name=data_value,json=dataValue,proto3" json:"data_value,omitempty"`
|
||||
BoolValue bool `protobuf:"varint,9,opt,name=bool_value,json=boolValue,proto3" json:"bool_value,omitempty"`
|
||||
Sum float64 `protobuf:"fixed64,10,opt,name=sum,proto3" json:"sum,omitempty"`
|
||||
Value *float64 `protobuf:"fixed64,6,opt,name=value,proto3,oneof" json:"value,omitempty"`
|
||||
StringValue *string `protobuf:"bytes,7,opt,name=string_value,json=stringValue,proto3,oneof" json:"string_value,omitempty"`
|
||||
DataValue *string `protobuf:"bytes,8,opt,name=data_value,json=dataValue,proto3,oneof" json:"data_value,omitempty"`
|
||||
BoolValue *bool `protobuf:"varint,9,opt,name=bool_value,json=boolValue,proto3,oneof" json:"bool_value,omitempty"`
|
||||
Sum *float64 `protobuf:"fixed64,10,opt,name=sum,proto3,oneof" json:"sum,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -547,36 +547,36 @@ func (x *SenMLMessage) GetUpdateTime() float64 {
|
||||
}
|
||||
|
||||
func (x *SenMLMessage) GetValue() float64 {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
if x != nil && x.Value != nil {
|
||||
return *x.Value
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SenMLMessage) GetStringValue() string {
|
||||
if x != nil {
|
||||
return x.StringValue
|
||||
if x != nil && x.StringValue != nil {
|
||||
return *x.StringValue
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SenMLMessage) GetDataValue() string {
|
||||
if x != nil {
|
||||
return x.DataValue
|
||||
if x != nil && x.DataValue != nil {
|
||||
return *x.DataValue
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SenMLMessage) GetBoolValue() bool {
|
||||
if x != nil {
|
||||
return x.BoolValue
|
||||
if x != nil && x.BoolValue != nil {
|
||||
return *x.BoolValue
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *SenMLMessage) GetSum() float64 {
|
||||
if x != nil {
|
||||
return x.Sum
|
||||
if x != nil && x.Sum != nil {
|
||||
return *x.Sum
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -742,22 +742,27 @@ const file_readers_v1_readers_proto_rawDesc = "" +
|
||||
"\achannel\x18\x01 \x01(\tR\achannel\x12\x1a\n" +
|
||||
"\bsubtopic\x18\x02 \x01(\tR\bsubtopic\x12\x1c\n" +
|
||||
"\tpublisher\x18\x03 \x01(\tR\tpublisher\x12\x1a\n" +
|
||||
"\bprotocol\x18\x04 \x01(\tR\bprotocol\"\xa1\x02\n" +
|
||||
"\bprotocol\x18\x04 \x01(\tR\bprotocol\"\xfb\x02\n" +
|
||||
"\fSenMLMessage\x12+\n" +
|
||||
"\x04base\x18\x01 \x01(\v2\x17.readers.v1.BaseMessageR\x04base\x12\x12\n" +
|
||||
"\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n" +
|
||||
"\x04unit\x18\x03 \x01(\tR\x04unit\x12\x12\n" +
|
||||
"\x04time\x18\x04 \x01(\x01R\x04time\x12\x1f\n" +
|
||||
"\vupdate_time\x18\x05 \x01(\x01R\n" +
|
||||
"updateTime\x12\x14\n" +
|
||||
"\x05value\x18\x06 \x01(\x01R\x05value\x12!\n" +
|
||||
"\fstring_value\x18\a \x01(\tR\vstringValue\x12\x1d\n" +
|
||||
"updateTime\x12\x19\n" +
|
||||
"\x05value\x18\x06 \x01(\x01H\x00R\x05value\x88\x01\x01\x12&\n" +
|
||||
"\fstring_value\x18\a \x01(\tH\x01R\vstringValue\x88\x01\x01\x12\"\n" +
|
||||
"\n" +
|
||||
"data_value\x18\b \x01(\tR\tdataValue\x12\x1d\n" +
|
||||
"data_value\x18\b \x01(\tH\x02R\tdataValue\x88\x01\x01\x12\"\n" +
|
||||
"\n" +
|
||||
"bool_value\x18\t \x01(\bR\tboolValue\x12\x10\n" +
|
||||
"bool_value\x18\t \x01(\bH\x03R\tboolValue\x88\x01\x01\x12\x15\n" +
|
||||
"\x03sum\x18\n" +
|
||||
" \x01(\x01R\x03sum\"n\n" +
|
||||
" \x01(\x01H\x04R\x03sum\x88\x01\x01B\b\n" +
|
||||
"\x06_valueB\x0f\n" +
|
||||
"\r_string_valueB\r\n" +
|
||||
"\v_data_valueB\r\n" +
|
||||
"\v_bool_valueB\x06\n" +
|
||||
"\x04_sum\"n\n" +
|
||||
"\vJsonMessage\x12+\n" +
|
||||
"\x04base\x18\x01 \x01(\v2\x17.readers.v1.BaseMessageR\x04base\x12\x18\n" +
|
||||
"\acreated\x18\x02 \x01(\x03R\acreated\x12\x18\n" +
|
||||
@@ -828,6 +833,7 @@ func file_readers_v1_readers_proto_init() {
|
||||
(*Message_Senml)(nil),
|
||||
(*Message_Json)(nil),
|
||||
}
|
||||
file_readers_v1_readers_proto_msgTypes[4].OneofWrappers = []any{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
|
||||
+69
-15
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
chclient "github.com/absmach/callhome/pkg/client"
|
||||
abrokers "github.com/absmach/magistrala/alarms/brokers"
|
||||
grpcReadersV1 "github.com/absmach/magistrala/api/grpc/readers/v1"
|
||||
"github.com/absmach/magistrala/consumers/writers/brokers"
|
||||
"github.com/absmach/magistrala/internal/email"
|
||||
"github.com/absmach/magistrala/re"
|
||||
@@ -22,9 +23,13 @@ import (
|
||||
"github.com/absmach/magistrala/re/emailer"
|
||||
"github.com/absmach/magistrala/re/middleware"
|
||||
repg "github.com/absmach/magistrala/re/postgres"
|
||||
grpcClient "github.com/absmach/magistrala/readers/api/grpc"
|
||||
"github.com/absmach/supermq"
|
||||
smqlog "github.com/absmach/supermq/logger"
|
||||
authnsvc "github.com/absmach/supermq/pkg/authn/authsvc"
|
||||
mgauthz "github.com/absmach/supermq/pkg/authz"
|
||||
authzsvc "github.com/absmach/supermq/pkg/authz/authsvc"
|
||||
domainsAuthz "github.com/absmach/supermq/pkg/domains/grpcclient"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
jaegerclient "github.com/absmach/supermq/pkg/jaeger"
|
||||
"github.com/absmach/supermq/pkg/messaging"
|
||||
@@ -40,21 +45,26 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
svcName = "rules_engine"
|
||||
envPrefixDB = "MG_RE_DB_"
|
||||
envPrefixHTTP = "MG_RE_HTTP_"
|
||||
envPrefixAuth = "SMQ_AUTH_GRPC_"
|
||||
defDB = "r"
|
||||
defSvcHTTPPort = "9008"
|
||||
svcName = "rules_engine"
|
||||
envPrefixDB = "MG_RE_DB_"
|
||||
envPrefixHTTP = "MG_RE_HTTP_"
|
||||
envPrefixAuth = "SMQ_AUTH_GRPC_"
|
||||
defDB = "r"
|
||||
defSvcHTTPPort = "9008"
|
||||
envPrefixGrpc = "MG_TIMESCALE_READER_GRPC_"
|
||||
envPrefixDomains = "SMQ_DOMAINS_GRPC_"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
LogLevel string `env:"MG_RE_LOG_LEVEL" envDefault:"info"`
|
||||
InstanceID string `env:"MG_RE_INSTANCE_ID" envDefault:""`
|
||||
JaegerURL url.URL `env:"SMQ_JAEGER_URL" envDefault:"http://localhost:4318/v1/traces"`
|
||||
SendTelemetry bool `env:"SMQ_SEND_TELEMETRY" envDefault:"true"`
|
||||
TraceRatio float64 `env:"SMQ_JAEGER_TRACE_RATIO" envDefault:"1.0"`
|
||||
BrokerURL string `env:"SMQ_MESSAGE_BROKER_URL" envDefault:"nats://localhost:4222"`
|
||||
LogLevel string `env:"MG_RE_LOG_LEVEL" envDefault:"info"`
|
||||
InstanceID string `env:"MG_RE_INSTANCE_ID" envDefault:""`
|
||||
JaegerURL url.URL `env:"SMQ_JAEGER_URL" envDefault:"http://localhost:4318/v1/traces"`
|
||||
SendTelemetry bool `env:"SMQ_SEND_TELEMETRY" envDefault:"true"`
|
||||
ESURL string `env:"SMQ_ES_URL" envDefault:"nats://localhost:4222"`
|
||||
CacheURL string `env:"MG_RE_CACHE_URL" envDefault:"redis://localhost:6379/0"`
|
||||
CacheKeyDuration time.Duration `env:"MG_RE_CACHE_KEY_DURATION" envDefault:"10m"`
|
||||
TraceRatio float64 `env:"SMQ_JAEGER_TRACE_RATIO" envDefault:"1.0"`
|
||||
BrokerURL string `env:"SMQ_MESSAGE_BROKER_URL" envDefault:"nats://localhost:4222"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -177,8 +187,48 @@ func main() {
|
||||
logger.Info("AuthN successfully connected to auth gRPC server " + authnClient.Secure())
|
||||
errs := make(chan error)
|
||||
|
||||
domsGrpcCfg := grpcclient.Config{}
|
||||
if err := env.ParseWithOptions(&domsGrpcCfg, env.Options{Prefix: envPrefixDomains}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load domains gRPC client configuration : %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
domAuthz, _, domainsHandler, err := domainsAuthz.NewAuthorization(ctx, domsGrpcCfg)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer domainsHandler.Close()
|
||||
|
||||
authz, authzClient, err := authzsvc.NewAuthorization(ctx, grpcCfg, domAuthz)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer authzClient.Close()
|
||||
logger.Info("AuthZ successfully connected to auth gRPC server " + authnClient.Secure())
|
||||
|
||||
database := pgclient.NewDatabase(db, dbConfig, tracer)
|
||||
svc, err := newService(database, errs, msgSub, writersPub, alarmsPub, ec, logger)
|
||||
regrpcCfg := grpcclient.Config{}
|
||||
if err := env.ParseWithOptions(®rpcCfg, env.Options{Prefix: envPrefixGrpc}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load clients gRPC client configuration : %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
client, err := grpcclient.NewHandler(regrpcCfg)
|
||||
if err != nil {
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
readersClient := grpcClient.NewReadersClient(client.Connection(), regrpcCfg.Timeout)
|
||||
logger.Info("Readers gRPC client successfully connected to readers gRPC server " + client.Secure())
|
||||
|
||||
svc, err := newService(database, errs, msgSub, writersPub, alarmsPub, authz, ec, logger, readersClient)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to create services: %s", err))
|
||||
exitCode = 1
|
||||
@@ -236,7 +286,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(db pgclient.Database, errs chan error, rePubSub messaging.PubSub, writersPub, alarmsPub messaging.Publisher, ec email.Config, logger *slog.Logger) (re.Service, error) {
|
||||
func newService(db pgclient.Database, errs chan error, rePubSub messaging.PubSub, writersPub, alarmsPub messaging.Publisher, authz mgauthz.Authorization, ec email.Config, logger *slog.Logger, readersClient grpcReadersV1.ReadersServiceClient) (re.Service, error) {
|
||||
repo := repg.NewRepository(db)
|
||||
idp := uuid.New()
|
||||
|
||||
@@ -245,7 +295,11 @@ func newService(db pgclient.Database, errs chan error, rePubSub messaging.PubSub
|
||||
logger.Error(fmt.Sprintf("failed to configure e-mailing util: %s", err.Error()))
|
||||
}
|
||||
|
||||
csvc := re.NewService(repo, errs, idp, rePubSub, writersPub, alarmsPub, re.NewTicker(time.Minute), emailerClient)
|
||||
csvc := re.NewService(repo, errs, idp, rePubSub, writersPub, alarmsPub, re.NewTicker(time.Minute), emailerClient, readersClient)
|
||||
csvc, err = middleware.AuthorizationMiddleware(csvc, authz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
csvc = middleware.LoggingMiddleware(csvc, logger)
|
||||
|
||||
return csvc, nil
|
||||
|
||||
@@ -36,5 +36,5 @@ func (n *notifier) Notify(from string, to []string, msg *messaging.Message) erro
|
||||
values := string(msg.GetPayload())
|
||||
content := fmt.Sprintf(contentTemplate, msg.GetPublisher(), msg.GetProtocol(), values)
|
||||
|
||||
return n.agent.Send(to, from, subject, "", "", content, footer)
|
||||
return n.agent.Send(to, from, subject, "", "", content, footer, map[string][]byte{})
|
||||
}
|
||||
|
||||
@@ -192,6 +192,16 @@ services:
|
||||
MG_EMAIL_FROM_ADDRESS: ${MG_EMAIL_FROM_ADDRESS}
|
||||
MG_EMAIL_FROM_NAME: ${MG_EMAIL_FROM_NAME}
|
||||
MG_EMAIL_TEMPLATE: ${MG_EMAIL_TEMPLATE}
|
||||
MG_TIMESCALE_READER_GRPC_URL: ${MG_TIMESCALE_READER_GRPC_URL}
|
||||
MG_TIMESCALE_READER_GRPC_TIMEOUT: ${MG_TIMESCALE_READER_GRPC_TIMEOUT}
|
||||
MG_TIMESCALE_READER_GRPC_CLIENT_CERT: ${MG_TIMESCALE_READER_GRPC_CLIENT_CERT}
|
||||
MG_TIMESCALE_READER_GRPC_CLIENT_CA_CERTS: ${MG_TIMESCALE_READER_GRPC_CLIENT_CA_CERTS}
|
||||
MG_TIMESCALE_READER_GRPC_CLIENT_KEY: ${MG_TIMESCALE_READER_GRPC_CLIENT_KEY}
|
||||
SMQ_DOMAINS_GRPC_URL: ${SMQ_DOMAINS_GRPC_URL}
|
||||
SMQ_DOMAINS_GRPC_TIMEOUT: ${SMQ_DOMAINS_GRPC_TIMEOUT}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_CERT: ${SMQ_DOMAINS_GRPC_CLIENT_CERT:+/domains-grpc-client.crt}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_KEY: ${SMQ_DOMAINS_GRPC_CLIENT_KEY:+/domains-grpc-client.key}
|
||||
SMQ_DOMAINS_GRPC_SERVER_CA_CERTS: ${SMQ_DOMAINS_GRPC_SERVER_CA_CERTS:+/domains-grpc-server-ca.crt}
|
||||
ports:
|
||||
- ${MG_RE_HTTP_PORT}:${MG_RE_HTTP_PORT}
|
||||
networks:
|
||||
|
||||
@@ -23,6 +23,8 @@ require (
|
||||
github.com/jackc/pgtype v1.14.4
|
||||
github.com/jackc/pgx/v5 v5.7.4
|
||||
github.com/jmoiron/sqlx v1.4.0
|
||||
github.com/johnfercher/maroto v1.0.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/ory/dockertest/v3 v3.12.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
@@ -43,6 +45,12 @@ require (
|
||||
moul.io/http2curl v1.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/jung-kurt/gofpdf v1.16.2 // indirect
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
@@ -50,7 +58,7 @@ require (
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/absmach/certs v0.0.0-20250303232207-ef00d309ca02 // indirect
|
||||
github.com/absmach/senml v1.0.7 // indirect
|
||||
github.com/absmach/senml v1.0.7
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
|
||||
@@ -85,7 +93,6 @@ require (
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jzelinskie/stringz v0.0.3 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
|
||||
@@ -50,6 +50,9 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
|
||||
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
@@ -185,6 +188,7 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
@@ -264,6 +268,8 @@ github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFr
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||
github.com/johnfercher/maroto v1.0.0 h1:yo26a/Mxj2YbHCzpIW7FypKtdvv9BdeLNHaApHwLCXU=
|
||||
github.com/johnfercher/maroto v1.0.0/go.mod h1:qeujdhKT+677jMjGWlIa5OCgR04GgIHvByJ6pSC+hOw=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@@ -272,6 +278,9 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
|
||||
github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
|
||||
github.com/jzelinskie/stringz v0.0.3 h1:0GhG3lVMYrYtIvRbxvQI6zqRTT1P1xyQlpa0FhfUXas=
|
||||
github.com/jzelinskie/stringz v0.0.3/go.mod h1:hHYbgxJuNLRw91CmpuFsYEOyQqpDVFg8pvEh23vy4P0=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
@@ -367,6 +376,9 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
|
||||
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -415,6 +427,9 @@ github.com/rubenv/sql-migrate v1.8.0 h1:dXnYiJk9k3wetp7GfQbKJcPHjVJL6YK19tKj8t2N
|
||||
github.com/rubenv/sql-migrate v1.8.0/go.mod h1:F2bGFBwCU+pnmbtNYDeKvSuvL6lBVtXDXUUv5t+u1qw=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 h1:K1Xf3bKttbF+koVGaX5xngRIZ5bVjbmPnaxE/dR08uY=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
|
||||
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
@@ -552,6 +567,7 @@ golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbV
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
|
||||
+19
-1
@@ -5,6 +5,8 @@ package email
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/mail"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -71,7 +73,7 @@ func New(c *Config) (*Agent, error) {
|
||||
}
|
||||
|
||||
// Send sends e-mail.
|
||||
func (a *Agent) Send(to []string, from, subject, header, user, content, footer string) error {
|
||||
func (a *Agent) Send(to []string, from, subject, header, user, content, footer string, attachments map[string][]byte) error {
|
||||
if a.tmpl == nil {
|
||||
return errMissingEmailTemplate
|
||||
}
|
||||
@@ -102,6 +104,22 @@ func (a *Agent) Send(to []string, from, subject, header, user, content, footer s
|
||||
m.SetHeader("Subject", subject)
|
||||
m.SetBody("text/plain", buff.String())
|
||||
|
||||
for filename, data := range attachments {
|
||||
reader := bytes.NewReader(data)
|
||||
|
||||
settings := []gomail.FileSetting{
|
||||
gomail.SetHeader(map[string][]string{
|
||||
"Content-Disposition": {fmt.Sprintf(`attachment; filename="%s"`, filename)},
|
||||
}),
|
||||
gomail.SetCopyFunc(func(w io.Writer) error {
|
||||
_, err := io.Copy(w, reader)
|
||||
return err
|
||||
}),
|
||||
}
|
||||
|
||||
m.Attach(filename, settings...)
|
||||
}
|
||||
|
||||
if err := a.dial.DialAndSend(m); err != nil {
|
||||
return errors.Wrap(errSendMail, err)
|
||||
}
|
||||
|
||||
@@ -60,11 +60,11 @@ message SenMLMessage {
|
||||
string unit = 3;
|
||||
double time = 4;
|
||||
double update_time = 5;
|
||||
double value = 6;
|
||||
string string_value = 7;
|
||||
string data_value = 8;
|
||||
bool bool_value = 9;
|
||||
double sum = 10;
|
||||
optional double value = 6;
|
||||
optional string string_value = 7;
|
||||
optional string data_value = 8;
|
||||
optional bool bool_value = 9;
|
||||
optional double sum = 10;
|
||||
}
|
||||
|
||||
message JsonMessage {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package reltime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
re = regexp.MustCompile(`(?i)^now\(\)([\+\-])(.+)$`)
|
||||
|
||||
ErrInvalidDuration = errors.New("invalid duration format")
|
||||
ErrInvalidExpression = errors.New("invalid time expression")
|
||||
ErrUnsupportedUnit = errors.New("unsupported unit")
|
||||
)
|
||||
|
||||
func Parse(expr string) (time.Time, error) {
|
||||
now := time.Now()
|
||||
expr = strings.ReplaceAll(expr, " ", "")
|
||||
|
||||
if strings.EqualFold(expr, "now()") {
|
||||
return now, nil
|
||||
}
|
||||
|
||||
matches := re.FindStringSubmatch(expr)
|
||||
if len(matches) != 3 {
|
||||
return time.Time{}, errors.Wrap(ErrInvalidExpression, fmt.Errorf("%s", expr))
|
||||
}
|
||||
|
||||
sign := matches[1]
|
||||
durStr := matches[2]
|
||||
if strings.ContainsAny(durStr, "+-") {
|
||||
return time.Time{}, errors.Wrap(ErrInvalidExpression, fmt.Errorf("%s", expr))
|
||||
}
|
||||
|
||||
dur, err := parseComplexDuration(durStr)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
if sign == "-" {
|
||||
return now.Add(-dur), nil
|
||||
}
|
||||
return now.Add(dur), nil
|
||||
}
|
||||
|
||||
func parseComplexDuration(s string) (time.Duration, error) {
|
||||
var total time.Duration
|
||||
re := regexp.MustCompile(`(\d+)([smhdwMY])`)
|
||||
matches := re.FindAllStringSubmatch(s, -1)
|
||||
|
||||
if matches == nil {
|
||||
return 0, errors.Wrap(ErrInvalidDuration, fmt.Errorf("%s", s))
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
val, _ := strconv.Atoi(match[1])
|
||||
unit := match[2]
|
||||
|
||||
var d time.Duration
|
||||
switch unit {
|
||||
case "s":
|
||||
d = time.Duration(val) * time.Second
|
||||
case "m":
|
||||
d = time.Duration(val) * time.Minute
|
||||
case "h":
|
||||
d = time.Duration(val) * time.Hour
|
||||
case "d":
|
||||
d = time.Duration(val) * 24 * time.Hour
|
||||
case "w":
|
||||
d = time.Duration(val) * 7 * 24 * time.Hour
|
||||
default:
|
||||
return 0, errors.Wrap(ErrUnsupportedUnit, fmt.Errorf("%s", unit))
|
||||
}
|
||||
|
||||
total += d
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package reltime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
expr string
|
||||
expected time.Time
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "testing expression now()-5d",
|
||||
expr: "now()-5d",
|
||||
expected: now.Add(-5 * 24 * time.Hour),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "testing expression now()+2h30m",
|
||||
expr: "now()+2h30m",
|
||||
expected: now.Add(2*time.Hour + 30*time.Minute),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "testing expression now()-1w3d10h40m",
|
||||
expr: "now()-1w3d10h40m",
|
||||
expected: now.Add(-(7*24+3*24+10)*time.Hour - 40*time.Minute),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "testing expression yesterday",
|
||||
expr: "yesterday",
|
||||
err: ErrInvalidExpression,
|
||||
},
|
||||
{
|
||||
desc: "testing expression now()--5d",
|
||||
expr: "now()--5d",
|
||||
err: ErrInvalidExpression,
|
||||
},
|
||||
{
|
||||
desc: "testing expression now()+",
|
||||
expr: "now()+",
|
||||
err: ErrInvalidExpression,
|
||||
},
|
||||
{
|
||||
desc: "testing expression now()+5r",
|
||||
expr: "now()+5r",
|
||||
err: ErrInvalidDuration,
|
||||
},
|
||||
{
|
||||
desc: "testing expression now()+5M",
|
||||
expr: "now()+5M",
|
||||
err: ErrUnsupportedUnit,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
got, err := Parse(tc.expr)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v got %v and response time %v\n", tc.desc, tc.err, err, got))
|
||||
if err == nil {
|
||||
assert.WithinDuration(t, tc.expected, got, time.Duration(10*time.Second))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,3 +177,227 @@ func disableRuleEndpoint(s re.Service) endpoint.Endpoint {
|
||||
return updateRuleStatusRes{Rule: rule}, err
|
||||
}
|
||||
}
|
||||
|
||||
func generateReportEndpoint(svc re.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
session, ok := ctx.Value(api.SessionKey).(authn.Session)
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
req := request.(generateReportReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return generateReportResp{}, err
|
||||
}
|
||||
|
||||
res, err := svc.GenerateReport(ctx, session, re.ReportConfig{
|
||||
Name: req.Name,
|
||||
DomainID: req.DomainID,
|
||||
Config: req.Config,
|
||||
Metrics: req.Metrics,
|
||||
Email: req.Email,
|
||||
}, req.action)
|
||||
if err != nil {
|
||||
return generateReportResp{}, err
|
||||
}
|
||||
|
||||
switch req.action {
|
||||
case re.DownloadReport:
|
||||
return downloadReportResp{
|
||||
File: res.File,
|
||||
}, nil
|
||||
case re.EmailReport:
|
||||
return emailReportResp{}, nil
|
||||
default:
|
||||
return generateReportResp{
|
||||
Total: res.Total,
|
||||
From: res.From,
|
||||
To: res.To,
|
||||
Aggregation: res.Aggregation,
|
||||
Reports: res.Reports,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listReportsConfigEndpoint(svc re.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
session, ok := ctx.Value(api.SessionKey).(authn.Session)
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
req := request.(listReportsConfigReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return listReportsConfigRes{}, err
|
||||
}
|
||||
|
||||
page, err := svc.ListReportsConfig(ctx, session, req.PageMeta)
|
||||
if err != nil {
|
||||
return listReportsConfigRes{}, err
|
||||
}
|
||||
|
||||
return listReportsConfigRes{
|
||||
pageRes: pageRes{
|
||||
Limit: page.Limit,
|
||||
Offset: page.Offset,
|
||||
Total: page.Total,
|
||||
},
|
||||
ReportConfigs: page.ReportConfigs,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func deleteReportConfigEndpoint(svc re.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
session, ok := ctx.Value(api.SessionKey).(authn.Session)
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
req := request.(deleteReportConfigReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return deleteReportConfigRes{}, err
|
||||
}
|
||||
|
||||
err := svc.RemoveReportConfig(ctx, session, req.ID)
|
||||
if err != nil {
|
||||
return deleteReportConfigRes{false}, err
|
||||
}
|
||||
|
||||
return deleteReportConfigRes{true}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateReportConfigEndpoint(svc re.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
session, ok := ctx.Value(api.SessionKey).(authn.Session)
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
req := request.(updateReportConfigReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return updateReportConfigRes{}, err
|
||||
}
|
||||
|
||||
cfg, err := svc.UpdateReportConfig(ctx, session, req.ReportConfig)
|
||||
if err != nil {
|
||||
return updateReportConfigRes{}, err
|
||||
}
|
||||
|
||||
return updateReportConfigRes{ReportConfig: cfg}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateReportScheduleEndpoint(s re.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
session, ok := ctx.Value(api.SessionKey).(authn.Session)
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
req := request.(updateReportScheduleReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return updateReportConfigRes{}, err
|
||||
}
|
||||
|
||||
rpt := re.ReportConfig{
|
||||
ID: req.id,
|
||||
Schedule: req.Schedule,
|
||||
}
|
||||
|
||||
updatedReport, err := s.UpdateReportSchedule(ctx, session, rpt)
|
||||
if err != nil {
|
||||
return updateReportConfigRes{}, err
|
||||
}
|
||||
return updateReportConfigRes{ReportConfig: updatedReport}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func viewReportConfigEndpoint(svc re.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
session, ok := ctx.Value(api.SessionKey).(authn.Session)
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
req := request.(viewReportConfigReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return viewReportConfigRes{}, err
|
||||
}
|
||||
|
||||
cfg, err := svc.ViewReportConfig(ctx, session, req.ID)
|
||||
if err != nil {
|
||||
return viewReportConfigRes{}, err
|
||||
}
|
||||
|
||||
return viewReportConfigRes{ReportConfig: cfg}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func addReportConfigEndpoint(svc re.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
session, ok := ctx.Value(api.SessionKey).(authn.Session)
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
req := request.(addReportConfigReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return addReportConfigRes{}, err
|
||||
}
|
||||
|
||||
cfg, err := svc.AddReportConfig(ctx, session, req.ReportConfig)
|
||||
if err != nil {
|
||||
return addReportConfigRes{}, err
|
||||
}
|
||||
|
||||
return addReportConfigRes{
|
||||
ReportConfig: cfg,
|
||||
created: true,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func enableReportConfigEndpoint(svc re.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
session, ok := ctx.Value(api.SessionKey).(authn.Session)
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
req := request.(updateReportStatusReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return updateReportConfigRes{}, err
|
||||
}
|
||||
|
||||
cfg, err := svc.EnableReportConfig(ctx, session, req.id)
|
||||
if err != nil {
|
||||
return updateReportConfigRes{}, err
|
||||
}
|
||||
|
||||
return updateReportConfigRes{ReportConfig: cfg}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func disableReportConfigEndpoint(svc re.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
session, ok := ctx.Value(api.SessionKey).(authn.Session)
|
||||
if !ok {
|
||||
return nil, svcerr.ErrAuthorization
|
||||
}
|
||||
|
||||
req := request.(updateReportStatusReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return updateReportConfigRes{}, err
|
||||
}
|
||||
|
||||
cfg, err := svc.DisableReportConfig(ctx, session, req.id)
|
||||
if err != nil {
|
||||
return updateReportConfigRes{}, err
|
||||
}
|
||||
|
||||
return updateReportConfigRes{ReportConfig: cfg}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,29 @@ var (
|
||||
"name": "test",
|
||||
},
|
||||
}
|
||||
reportConfig = re.ReportConfig{
|
||||
ID: validID,
|
||||
Name: namegen.Generate(),
|
||||
DomainID: domainID,
|
||||
Schedule: schedule,
|
||||
Status: re.EnabledStatus,
|
||||
Metrics: []re.Metric{
|
||||
{
|
||||
ChannelID: "channel1",
|
||||
ClientID: "channel2",
|
||||
Name: "metric_name",
|
||||
},
|
||||
},
|
||||
Config: &re.MetricConfig{
|
||||
From: "now()-1h",
|
||||
To: "now()",
|
||||
Aggregation: re.AggConfig{AggType: re.AggregationAVG, Interval: "1h"},
|
||||
},
|
||||
Email: &re.EmailSetting{
|
||||
To: []string{"test@example.com"},
|
||||
Subject: "Test Report",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type testRequest struct {
|
||||
@@ -966,3 +989,686 @@ type respBody struct {
|
||||
ID string `json:"id"`
|
||||
Status re.Status `json:"status"`
|
||||
}
|
||||
|
||||
func TestAddReportConfigEndpoint(t *testing.T) {
|
||||
ts, svc, authn := newRuleEngineServer()
|
||||
defer ts.Close()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
cfg re.ReportConfig
|
||||
domainID string
|
||||
token string
|
||||
contentType string
|
||||
status int
|
||||
authnRes smqauthn.Session
|
||||
authnErr error
|
||||
svcRes re.ReportConfig
|
||||
svcErr error
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "add report config successfully",
|
||||
cfg: reportConfig,
|
||||
token: validToken,
|
||||
contentType: contentType,
|
||||
domainID: domainID,
|
||||
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
||||
status: http.StatusCreated,
|
||||
svcRes: reportConfig,
|
||||
},
|
||||
{
|
||||
desc: "add report config with invalid token",
|
||||
cfg: reportConfig,
|
||||
token: invalidToken,
|
||||
authnRes: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
contentType: contentType,
|
||||
authnErr: svcerr.ErrAuthentication,
|
||||
status: http.StatusUnauthorized,
|
||||
err: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "add report config with empty token",
|
||||
token: "",
|
||||
authnRes: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
cfg: reportConfig,
|
||||
contentType: contentType,
|
||||
status: http.StatusUnauthorized,
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "add report config with empty domainID",
|
||||
token: validToken,
|
||||
cfg: reportConfig,
|
||||
contentType: contentType,
|
||||
status: http.StatusBadRequest,
|
||||
err: apiutil.ErrMissingDomainID,
|
||||
},
|
||||
{
|
||||
desc: "add report config with invalid content type",
|
||||
token: validToken,
|
||||
domainID: domainID,
|
||||
cfg: reportConfig,
|
||||
contentType: "application/xml",
|
||||
status: http.StatusUnsupportedMediaType,
|
||||
err: apiutil.ErrUnsupportedContentType,
|
||||
},
|
||||
{
|
||||
desc: "add report config with service error",
|
||||
token: validToken,
|
||||
domainID: domainID,
|
||||
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
||||
cfg: reportConfig,
|
||||
contentType: contentType,
|
||||
svcErr: svcerr.ErrAuthorization,
|
||||
status: http.StatusForbidden,
|
||||
err: svcerr.ErrAuthorization,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
data := toJSON(tc.cfg)
|
||||
req := testRequest{
|
||||
client: ts.Client(),
|
||||
method: http.MethodPost,
|
||||
url: fmt.Sprintf("%s/%s/reports/configs", ts.URL, tc.domainID),
|
||||
contentType: tc.contentType,
|
||||
token: tc.token,
|
||||
body: strings.NewReader(data),
|
||||
}
|
||||
|
||||
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
|
||||
svcCall := svc.On("AddReportConfig", mock.Anything, tc.authnRes, mock.Anything).Return(tc.svcRes, tc.svcErr)
|
||||
res, err := req.make()
|
||||
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
var errRes respBody
|
||||
err = json.NewDecoder(res.Body).Decode(&errRes)
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
||||
if errRes.Err != "" || errRes.Message != "" {
|
||||
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
||||
}
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
svcCall.Unset()
|
||||
authCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestViewReportConfigEndpoint(t *testing.T) {
|
||||
ts, svc, authn := newRuleEngineServer()
|
||||
defer ts.Close()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
domainID string
|
||||
token string
|
||||
contentType string
|
||||
status int
|
||||
authnRes smqauthn.Session
|
||||
authnErr error
|
||||
svcRes re.ReportConfig
|
||||
svcErr error
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "view report config successfully",
|
||||
id: validID,
|
||||
token: validToken,
|
||||
contentType: contentType,
|
||||
domainID: domainID,
|
||||
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
||||
status: http.StatusOK,
|
||||
svcRes: reportConfig,
|
||||
},
|
||||
{
|
||||
desc: "view report config with invalid token",
|
||||
id: validID,
|
||||
token: invalidToken,
|
||||
authnRes: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
contentType: contentType,
|
||||
authnErr: svcerr.ErrAuthentication,
|
||||
status: http.StatusUnauthorized,
|
||||
err: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "view report config with empty token",
|
||||
token: "",
|
||||
authnRes: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
contentType: contentType,
|
||||
status: http.StatusUnauthorized,
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "view report config with empty domainID",
|
||||
token: validToken,
|
||||
id: validID,
|
||||
contentType: contentType,
|
||||
status: http.StatusBadRequest,
|
||||
err: apiutil.ErrMissingDomainID,
|
||||
},
|
||||
{
|
||||
desc: "view report config with service error",
|
||||
token: validToken,
|
||||
domainID: domainID,
|
||||
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
||||
id: validID,
|
||||
contentType: contentType,
|
||||
svcErr: svcerr.ErrAuthorization,
|
||||
status: http.StatusForbidden,
|
||||
err: svcerr.ErrAuthorization,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
req := testRequest{
|
||||
client: ts.Client(),
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/%s/reports/configs/%s", ts.URL, tc.domainID, tc.id),
|
||||
contentType: tc.contentType,
|
||||
token: tc.token,
|
||||
}
|
||||
|
||||
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
|
||||
svcCall := svc.On("ViewReportConfig", mock.Anything, tc.authnRes, tc.id).Return(tc.svcRes, tc.svcErr)
|
||||
res, err := req.make()
|
||||
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
var errRes respBody
|
||||
err = json.NewDecoder(res.Body).Decode(&errRes)
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
||||
if errRes.Err != "" || errRes.Message != "" {
|
||||
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
||||
}
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
svcCall.Unset()
|
||||
authCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListReportsConfigEndpoint(t *testing.T) {
|
||||
ts, svc, authn := newRuleEngineServer()
|
||||
defer ts.Close()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
query string
|
||||
domainID string
|
||||
token string
|
||||
session smqauthn.Session
|
||||
listReportsResponse re.ReportConfigPage
|
||||
status int
|
||||
authnErr error
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "list reports config successfully",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
status: http.StatusOK,
|
||||
listReportsResponse: re.ReportConfigPage{
|
||||
ReportConfigs: []re.ReportConfig{reportConfig},
|
||||
PageMeta: re.PageMeta{Total: 1},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "list reports config with empty token",
|
||||
domainID: domainID,
|
||||
token: "",
|
||||
status: http.StatusUnauthorized,
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "list reports config with invalid token",
|
||||
domainID: domainID,
|
||||
token: invalidToken,
|
||||
status: http.StatusUnauthorized,
|
||||
authnErr: svcerr.ErrAuthentication,
|
||||
err: svcerr.ErrAuthentication,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
req := testRequest{
|
||||
client: ts.Client(),
|
||||
method: http.MethodGet,
|
||||
url: ts.URL + "/" + tc.domainID + "/reports/configs?" + tc.query,
|
||||
contentType: contentType,
|
||||
token: tc.token,
|
||||
}
|
||||
if tc.token == validToken {
|
||||
tc.session = smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID}
|
||||
}
|
||||
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authnErr)
|
||||
svcCall := svc.On("ListReportsConfig", mock.Anything, tc.session, mock.Anything).Return(tc.listReportsResponse, tc.err)
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
var bodyRes respBody
|
||||
err = json.NewDecoder(res.Body).Decode(&bodyRes)
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
||||
if bodyRes.Err != "" || bodyRes.Message != "" {
|
||||
err = errors.Wrap(errors.New(bodyRes.Err), errors.New(bodyRes.Message))
|
||||
}
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
svcCall.Unset()
|
||||
authCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateReportConfigEndpoint(t *testing.T) {
|
||||
ts, svc, authn := newRuleEngineServer()
|
||||
defer ts.Close()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
id string
|
||||
domainID string
|
||||
updateReq re.ReportConfig
|
||||
contentType string
|
||||
session smqauthn.Session
|
||||
svcResp re.ReportConfig
|
||||
svcErr error
|
||||
status int
|
||||
authnErr error
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "update report config successfully",
|
||||
token: validToken,
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
updateReq: reportConfig,
|
||||
contentType: contentType,
|
||||
svcResp: reportConfig,
|
||||
status: http.StatusOK,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "update report config with invalid token",
|
||||
token: invalidToken,
|
||||
session: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
updateReq: reportConfig,
|
||||
contentType: contentType,
|
||||
authnErr: svcerr.ErrAuthentication,
|
||||
status: http.StatusUnauthorized,
|
||||
err: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "update report config with empty token",
|
||||
token: "",
|
||||
session: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
updateReq: reportConfig,
|
||||
contentType: contentType,
|
||||
status: http.StatusUnauthorized,
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "update report config with empty domainID",
|
||||
token: validToken,
|
||||
id: validID,
|
||||
updateReq: reportConfig,
|
||||
contentType: contentType,
|
||||
status: http.StatusBadRequest,
|
||||
err: apiutil.ErrMissingDomainID,
|
||||
},
|
||||
{
|
||||
desc: "update report config with invalid content type",
|
||||
token: validToken,
|
||||
id: validID,
|
||||
domainID: domainID,
|
||||
updateReq: reportConfig,
|
||||
contentType: "application/xml",
|
||||
svcResp: reportConfig,
|
||||
status: http.StatusUnsupportedMediaType,
|
||||
err: apiutil.ErrUnsupportedContentType,
|
||||
},
|
||||
{
|
||||
desc: "update report config with service error",
|
||||
token: validToken,
|
||||
id: validID,
|
||||
domainID: domainID,
|
||||
updateReq: reportConfig,
|
||||
contentType: contentType,
|
||||
svcResp: re.ReportConfig{},
|
||||
svcErr: svcerr.ErrAuthorization,
|
||||
status: http.StatusForbidden,
|
||||
err: svcerr.ErrAuthorization,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
data := toJSON(tc.updateReq)
|
||||
req := testRequest{
|
||||
client: ts.Client(),
|
||||
method: http.MethodPatch,
|
||||
url: fmt.Sprintf("%s/%s/reports/configs/%s", ts.URL, tc.domainID, tc.id),
|
||||
contentType: tc.contentType,
|
||||
token: tc.token,
|
||||
body: strings.NewReader(data),
|
||||
}
|
||||
if tc.token == validToken {
|
||||
tc.session = smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID}
|
||||
}
|
||||
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authnErr)
|
||||
svcCall := svc.On("UpdateReportConfig", mock.Anything, tc.session, mock.Anything).Return(tc.svcResp, tc.svcErr)
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
var errRes respBody
|
||||
err = json.NewDecoder(res.Body).Decode(&errRes)
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
||||
if errRes.Err != "" || errRes.Message != "" {
|
||||
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
||||
}
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
svcCall.Unset()
|
||||
authCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteReportConfigEndpoint(t *testing.T) {
|
||||
ts, svc, authn := newRuleEngineServer()
|
||||
defer ts.Close()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
id string
|
||||
domainID string
|
||||
session smqauthn.Session
|
||||
svcErr error
|
||||
status int
|
||||
authnErr error
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "delete report config successfully",
|
||||
token: validToken,
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
svcErr: nil,
|
||||
status: http.StatusNoContent,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "delete report config with invalid token",
|
||||
token: invalidToken,
|
||||
session: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
authnErr: svcerr.ErrAuthentication,
|
||||
status: http.StatusUnauthorized,
|
||||
err: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "delete report config with empty token",
|
||||
token: "",
|
||||
session: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
status: http.StatusUnauthorized,
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "delete report config with empty domainID",
|
||||
token: validToken,
|
||||
id: validID,
|
||||
status: http.StatusBadRequest,
|
||||
err: apiutil.ErrMissingDomainID,
|
||||
},
|
||||
{
|
||||
desc: "delete report config with service error",
|
||||
token: validToken,
|
||||
id: validID,
|
||||
domainID: domainID,
|
||||
svcErr: svcerr.ErrAuthorization,
|
||||
status: http.StatusForbidden,
|
||||
err: svcerr.ErrAuthorization,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
req := testRequest{
|
||||
client: ts.Client(),
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/%s/reports/configs/%s", ts.URL, tc.domainID, tc.id),
|
||||
token: tc.token,
|
||||
}
|
||||
if tc.token == validToken {
|
||||
tc.session = smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID}
|
||||
}
|
||||
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authnErr)
|
||||
svcCall := svc.On("RemoveReportConfig", mock.Anything, tc.session, tc.id).Return(tc.svcErr)
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
svcCall.Unset()
|
||||
authCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableReportConfigEndpoint(t *testing.T) {
|
||||
ts, svc, authn := newRuleEngineServer()
|
||||
defer ts.Close()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
id string
|
||||
domainID string
|
||||
session smqauthn.Session
|
||||
svcResp re.ReportConfig
|
||||
svcErr error
|
||||
status int
|
||||
authnErr error
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "enable report config successfully",
|
||||
token: validToken,
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
svcResp: reportConfig,
|
||||
svcErr: nil,
|
||||
status: http.StatusOK,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "enable report config with invalid token",
|
||||
token: invalidToken,
|
||||
session: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
authnErr: svcerr.ErrAuthentication,
|
||||
status: http.StatusUnauthorized,
|
||||
err: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "enable report config with empty token",
|
||||
token: "",
|
||||
session: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
status: http.StatusUnauthorized,
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "enable report config with empty domainID",
|
||||
token: validToken,
|
||||
id: validID,
|
||||
status: http.StatusBadRequest,
|
||||
err: apiutil.ErrMissingDomainID,
|
||||
},
|
||||
{
|
||||
desc: "enable report config with service error",
|
||||
token: validToken,
|
||||
id: validID,
|
||||
domainID: domainID,
|
||||
svcResp: re.ReportConfig{},
|
||||
svcErr: svcerr.ErrAuthorization,
|
||||
status: http.StatusForbidden,
|
||||
err: svcerr.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "enable report config with empty id",
|
||||
token: validToken,
|
||||
id: "",
|
||||
domainID: domainID,
|
||||
status: http.StatusBadRequest,
|
||||
err: apiutil.ErrMissingID,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
req := testRequest{
|
||||
client: ts.Client(),
|
||||
method: http.MethodPost,
|
||||
url: fmt.Sprintf("%s/%s/reports/configs/%s/enable", ts.URL, tc.domainID, tc.id),
|
||||
token: tc.token,
|
||||
}
|
||||
if tc.token == validToken {
|
||||
tc.session = smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID}
|
||||
}
|
||||
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authnErr)
|
||||
svcCall := svc.On("EnableReportConfig", mock.Anything, tc.session, tc.id).Return(tc.svcResp, tc.svcErr)
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
var errRes respBody
|
||||
err = json.NewDecoder(res.Body).Decode(&errRes)
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
||||
if errRes.Err != "" || errRes.Message != "" {
|
||||
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
||||
}
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
svcCall.Unset()
|
||||
authCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisableReportConfigEndpoint(t *testing.T) {
|
||||
ts, svc, authn := newRuleEngineServer()
|
||||
defer ts.Close()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
id string
|
||||
domainID string
|
||||
session smqauthn.Session
|
||||
svcResp re.ReportConfig
|
||||
svcErr error
|
||||
status int
|
||||
authnErr error
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "disable report config successfully",
|
||||
token: validToken,
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
svcResp: reportConfig,
|
||||
svcErr: nil,
|
||||
status: http.StatusOK,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "disable report config with invalid token",
|
||||
token: invalidToken,
|
||||
session: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
authnErr: svcerr.ErrAuthentication,
|
||||
status: http.StatusUnauthorized,
|
||||
err: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "disable report config with empty token",
|
||||
token: "",
|
||||
session: smqauthn.Session{},
|
||||
domainID: domainID,
|
||||
id: validID,
|
||||
status: http.StatusUnauthorized,
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "disable report config with empty domainID",
|
||||
token: validToken,
|
||||
id: validID,
|
||||
status: http.StatusBadRequest,
|
||||
err: apiutil.ErrMissingDomainID,
|
||||
},
|
||||
{
|
||||
desc: "disable report config with service error",
|
||||
token: validToken,
|
||||
id: validID,
|
||||
domainID: domainID,
|
||||
svcResp: re.ReportConfig{},
|
||||
svcErr: svcerr.ErrAuthorization,
|
||||
status: http.StatusForbidden,
|
||||
err: svcerr.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "disable report config with empty id",
|
||||
token: validToken,
|
||||
id: "",
|
||||
domainID: domainID,
|
||||
status: http.StatusBadRequest,
|
||||
err: apiutil.ErrMissingID,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
req := testRequest{
|
||||
client: ts.Client(),
|
||||
method: http.MethodPost,
|
||||
url: fmt.Sprintf("%s/%s/reports/configs/%s/disable", ts.URL, tc.domainID, tc.id),
|
||||
token: tc.token,
|
||||
}
|
||||
if tc.token == validToken {
|
||||
tc.session = smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID}
|
||||
}
|
||||
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authnErr)
|
||||
svcCall := svc.On("DisableReportConfig", mock.Anything, tc.session, tc.id).Return(tc.svcResp, tc.svcErr)
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
var errRes respBody
|
||||
err = json.NewDecoder(res.Body).Decode(&errRes)
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
||||
if errRes.Err != "" || errRes.Message != "" {
|
||||
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
||||
}
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
svcCall.Unset()
|
||||
authCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,28 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/absmach/magistrala/re"
|
||||
api "github.com/absmach/supermq/api/http"
|
||||
apiutil "github.com/absmach/supermq/api/http/util"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidReportAction = errors.New("invalid report action")
|
||||
errMetricsNotProvided = errors.New("metrics not provided")
|
||||
errMissingReportConfig = errors.New("missing report config")
|
||||
errMissingReportEmailConfig = errors.New("missing report email config")
|
||||
errInvalidRecurringPeriod = errors.New("invalid recurring period")
|
||||
)
|
||||
|
||||
const (
|
||||
maxLimitSize = 1000
|
||||
MaxNameSize = 1024
|
||||
|
||||
errInvalidMetric = "invalid metric[%d]: %w"
|
||||
)
|
||||
|
||||
type addRuleReq struct {
|
||||
@@ -103,3 +117,139 @@ func (req deleteRuleReq) validate() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type updateReportConfigReq struct {
|
||||
re.ReportConfig `json:",inline"`
|
||||
}
|
||||
|
||||
func (req updateReportConfigReq) validate() error {
|
||||
if req.ID == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
return validateReportConfig(req.ReportConfig, false, false)
|
||||
}
|
||||
|
||||
type updateReportScheduleReq struct {
|
||||
id string
|
||||
Schedule re.Schedule `json:"schedule,omitempty"`
|
||||
}
|
||||
|
||||
func (req updateReportScheduleReq) validate() error {
|
||||
if req.id == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type addReportConfigReq struct {
|
||||
re.ReportConfig `json:",inline"`
|
||||
}
|
||||
|
||||
func (req addReportConfigReq) validate() error {
|
||||
if req.Name == "" {
|
||||
return apiutil.ErrMissingName
|
||||
}
|
||||
return validateReportConfig(req.ReportConfig, false, false)
|
||||
}
|
||||
|
||||
type viewReportConfigReq struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
func (req viewReportConfigReq) validate() error {
|
||||
if req.ID == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type listReportsConfigReq struct {
|
||||
re.PageMeta `json:",inline"`
|
||||
}
|
||||
|
||||
func (req listReportsConfigReq) validate() error {
|
||||
if req.Limit > maxLimitSize {
|
||||
return svcerr.ErrMalformedEntity
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type deleteReportConfigReq struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
func (req deleteReportConfigReq) validate() error {
|
||||
if req.ID == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type generateReportReq struct {
|
||||
re.ReportConfig
|
||||
action re.ReportAction
|
||||
}
|
||||
|
||||
func (req generateReportReq) validate() error {
|
||||
switch req.action {
|
||||
case re.ViewReport, re.DownloadReport:
|
||||
return validateReportConfig(req.ReportConfig, true, true)
|
||||
case re.EmailReport:
|
||||
return validateReportConfig(req.ReportConfig, false, true)
|
||||
default:
|
||||
return errors.Wrap(apiutil.ErrValidation, errInvalidReportAction)
|
||||
}
|
||||
}
|
||||
|
||||
type updateReportStatusReq struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func (req updateReportStatusReq) validate() error {
|
||||
if req.id == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateReportConfig(req re.ReportConfig, skipEmailValidation bool, skipSchedularValidation bool) error {
|
||||
if len(req.Metrics) == 0 {
|
||||
return errors.Wrap(apiutil.ErrValidation, errMetricsNotProvided)
|
||||
}
|
||||
for i, metric := range req.Metrics {
|
||||
if err := metric.Validate(); err != nil {
|
||||
return errors.Wrap(apiutil.ErrValidation, fmt.Errorf(errInvalidMetric, i+1, err))
|
||||
}
|
||||
}
|
||||
|
||||
if req.Config == nil {
|
||||
return errMissingReportConfig
|
||||
}
|
||||
if err := req.Config.Validate(); err != nil {
|
||||
return errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
if skipEmailValidation {
|
||||
return nil
|
||||
}
|
||||
if req.Email == nil {
|
||||
return errMissingReportEmailConfig
|
||||
}
|
||||
if err := req.Email.Validate(); err != nil {
|
||||
return errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
if skipSchedularValidation {
|
||||
return nil
|
||||
}
|
||||
|
||||
return validateScheduler(req.Schedule)
|
||||
}
|
||||
|
||||
func validateScheduler(sch re.Schedule) error {
|
||||
if sch.Recurring != re.None && sch.RecurringPeriod < 1 {
|
||||
return errInvalidRecurringPeriod
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/re"
|
||||
"github.com/absmach/supermq"
|
||||
@@ -18,6 +19,11 @@ var (
|
||||
_ supermq.Response = (*rulesPageRes)(nil)
|
||||
_ supermq.Response = (*updateRuleRes)(nil)
|
||||
_ supermq.Response = (*deleteRuleRes)(nil)
|
||||
_ supermq.Response = (*addReportConfigRes)(nil)
|
||||
_ supermq.Response = (*viewReportConfigRes)(nil)
|
||||
_ supermq.Response = (*updateReportConfigRes)(nil)
|
||||
_ supermq.Response = (*deleteReportConfigRes)(nil)
|
||||
_ supermq.Response = (*listReportsConfigRes)(nil)
|
||||
)
|
||||
|
||||
type pageRes struct {
|
||||
@@ -136,3 +142,144 @@ func (res deleteRuleRes) Headers() map[string]string {
|
||||
func (res deleteRuleRes) Empty() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type generateReportResp struct {
|
||||
Total uint64 `json:"total"`
|
||||
From time.Time `json:"from,omitempty"`
|
||||
To time.Time `json:"to,omitempty"`
|
||||
Aggregation re.AggConfig `json:"aggregation,omitempty"`
|
||||
Reports []re.Report `json:"reports,omitempty"`
|
||||
}
|
||||
|
||||
func (res generateReportResp) Code() int {
|
||||
return http.StatusCreated
|
||||
}
|
||||
|
||||
func (res generateReportResp) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res generateReportResp) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type addReportConfigRes struct {
|
||||
re.ReportConfig `json:",inline"`
|
||||
created bool
|
||||
}
|
||||
|
||||
func (res addReportConfigRes) Code() int {
|
||||
if res.created {
|
||||
return http.StatusCreated
|
||||
}
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res addReportConfigRes) Headers() map[string]string {
|
||||
if res.created {
|
||||
return map[string]string{}
|
||||
}
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res addReportConfigRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type viewReportConfigRes struct {
|
||||
re.ReportConfig `json:",inline"`
|
||||
}
|
||||
|
||||
func (res viewReportConfigRes) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res viewReportConfigRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res viewReportConfigRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type updateReportConfigRes struct {
|
||||
re.ReportConfig `json:",inline"`
|
||||
}
|
||||
|
||||
func (res updateReportConfigRes) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res updateReportConfigRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res updateReportConfigRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type deleteReportConfigRes struct {
|
||||
deleted bool
|
||||
}
|
||||
|
||||
func (res deleteReportConfigRes) Code() int {
|
||||
if res.deleted {
|
||||
return http.StatusNoContent
|
||||
}
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res deleteReportConfigRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res deleteReportConfigRes) Empty() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type listReportsConfigRes struct {
|
||||
pageRes
|
||||
ReportConfigs []re.ReportConfig `json:"report_configs"`
|
||||
}
|
||||
|
||||
func (res listReportsConfigRes) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res listReportsConfigRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res listReportsConfigRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type downloadReportResp struct {
|
||||
File re.ReportFile
|
||||
}
|
||||
|
||||
func (res downloadReportResp) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res downloadReportResp) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res downloadReportResp) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type emailReportResp struct{}
|
||||
|
||||
func (res emailReportResp) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res emailReportResp) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res emailReportResp) Empty() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
+258
-53
@@ -6,6 +6,7 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -23,10 +24,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
idKey = "ruleID"
|
||||
ruleIdKey = "ruleID"
|
||||
reportIdKey = "reportID"
|
||||
inputChannelKey = "input_channel"
|
||||
outputChannelKey = "output_channel"
|
||||
statusKey = "status"
|
||||
actionKey = "action"
|
||||
defAction = "view"
|
||||
)
|
||||
|
||||
// MakeHandler creates an HTTP handler for the service endpoints.
|
||||
@@ -36,63 +40,131 @@ func MakeHandler(svc re.Service, authn mgauthn.Authentication, mux *chi.Mux, log
|
||||
}
|
||||
mux.Group(func(r chi.Router) {
|
||||
r.Use(api.AuthenticateMiddleware(authn, true))
|
||||
r.Route("/{domainID}/rules", func(r chi.Router) {
|
||||
r.Post("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
addRuleEndpoint(svc),
|
||||
decodeAddRuleRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "create_rule").ServeHTTP)
|
||||
r.Route("/{domainID}", func(r chi.Router) {
|
||||
r.Route("/rules", func(r chi.Router) {
|
||||
r.Post("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
addRuleEndpoint(svc),
|
||||
decodeAddRuleRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "create_rule").ServeHTTP)
|
||||
|
||||
r.Get("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
listRulesEndpoint(svc),
|
||||
decodeListRulesRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "list_rules").ServeHTTP)
|
||||
|
||||
r.Route("/{ruleID}", func(r chi.Router) {
|
||||
r.Get("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
viewRuleEndpoint(svc),
|
||||
decodeViewRuleRequest,
|
||||
listRulesEndpoint(svc),
|
||||
decodeListRulesRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "view_rule").ServeHTTP)
|
||||
), "list_rules").ServeHTTP)
|
||||
|
||||
r.Patch("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateRuleEndpoint(svc),
|
||||
decodeUpdateRuleRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_rule").ServeHTTP)
|
||||
r.Route("/{ruleID}", func(r chi.Router) {
|
||||
r.Get("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
viewRuleEndpoint(svc),
|
||||
decodeViewRuleRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "view_rule").ServeHTTP)
|
||||
|
||||
r.Patch("/schedule", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateRuleScheduleEndpoint(svc),
|
||||
decodeUpdateRuleScheduleRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_rule_scheduler").ServeHTTP)
|
||||
r.Patch("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateRuleEndpoint(svc),
|
||||
decodeUpdateRuleRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_rule").ServeHTTP)
|
||||
|
||||
r.Delete("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
deleteRuleEndpoint(svc),
|
||||
decodeDeleteRuleRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "delete_rule").ServeHTTP)
|
||||
r.Patch("/schedule", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateRuleScheduleEndpoint(svc),
|
||||
decodeUpdateRuleScheduleRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_rule_scheduler").ServeHTTP)
|
||||
|
||||
r.Post("/enable", otelhttp.NewHandler(kithttp.NewServer(
|
||||
enableRuleEndpoint(svc),
|
||||
decodeUpdateRuleStatusRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "enable_rule").ServeHTTP)
|
||||
r.Delete("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
deleteRuleEndpoint(svc),
|
||||
decodeDeleteRuleRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "delete_rule").ServeHTTP)
|
||||
|
||||
r.Post("/disable", otelhttp.NewHandler(kithttp.NewServer(
|
||||
disableRuleEndpoint(svc),
|
||||
decodeUpdateRuleStatusRequest,
|
||||
api.EncodeResponse,
|
||||
r.Post("/enable", otelhttp.NewHandler(kithttp.NewServer(
|
||||
enableRuleEndpoint(svc),
|
||||
decodeUpdateRuleStatusRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "enable_rule").ServeHTTP)
|
||||
|
||||
r.Post("/disable", otelhttp.NewHandler(kithttp.NewServer(
|
||||
disableRuleEndpoint(svc),
|
||||
decodeUpdateRuleStatusRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "disable_rule").ServeHTTP)
|
||||
})
|
||||
})
|
||||
r.Route("/reports", func(r chi.Router) {
|
||||
r.Post("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
generateReportEndpoint(svc),
|
||||
decodeGenerateReportRequest,
|
||||
encodeFileDownloadResponse,
|
||||
opts...,
|
||||
), "disable_rule").ServeHTTP)
|
||||
), "generate_report").ServeHTTP)
|
||||
|
||||
r.Route("/configs", func(r chi.Router) {
|
||||
r.Post("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
addReportConfigEndpoint(svc),
|
||||
decodeAddReportConfigRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "add_report_config").ServeHTTP)
|
||||
|
||||
r.Get("/{reportID}", otelhttp.NewHandler(kithttp.NewServer(
|
||||
viewReportConfigEndpoint(svc),
|
||||
decodeViewReportConfigRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "view_report_config").ServeHTTP)
|
||||
|
||||
r.Patch("/{reportID}", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateReportConfigEndpoint(svc),
|
||||
decodeUpdateReportConfigRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_report_config").ServeHTTP)
|
||||
|
||||
r.Patch("/{reportID}/schedule", otelhttp.NewHandler(kithttp.NewServer(
|
||||
updateReportScheduleEndpoint(svc),
|
||||
decodeUpdateReportScheduleRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "update_report_scheduler").ServeHTTP)
|
||||
|
||||
r.Delete("/{reportID}", otelhttp.NewHandler(kithttp.NewServer(
|
||||
deleteReportConfigEndpoint(svc),
|
||||
decodeDeleteReportConfigRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "delete_report_config").ServeHTTP)
|
||||
|
||||
r.Get("/", otelhttp.NewHandler(kithttp.NewServer(
|
||||
listReportsConfigEndpoint(svc),
|
||||
decodeListReportsConfigRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "list_reports_config").ServeHTTP)
|
||||
|
||||
r.Post("/{reportID}/enable", otelhttp.NewHandler(kithttp.NewServer(
|
||||
enableReportConfigEndpoint(svc),
|
||||
decodeUpdateReportStatusRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "enable_report_config").ServeHTTP)
|
||||
|
||||
r.Post("/{reportID}/disable", otelhttp.NewHandler(kithttp.NewServer(
|
||||
disableReportConfigEndpoint(svc),
|
||||
decodeUpdateReportStatusRequest,
|
||||
api.EncodeResponse,
|
||||
opts...,
|
||||
), "disable_report_config").ServeHTTP)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -115,7 +187,7 @@ func decodeAddRuleRequest(_ context.Context, r *http.Request) (interface{}, erro
|
||||
}
|
||||
|
||||
func decodeViewRuleRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
id := chi.URLParam(r, idKey)
|
||||
id := chi.URLParam(r, ruleIdKey)
|
||||
return viewRuleReq{id: id}, nil
|
||||
}
|
||||
|
||||
@@ -127,7 +199,7 @@ func decodeUpdateRuleRequest(_ context.Context, r *http.Request) (interface{}, e
|
||||
if err := json.NewDecoder(r.Body).Decode(&rule); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
|
||||
}
|
||||
rule.ID = chi.URLParam(r, idKey)
|
||||
rule.ID = chi.URLParam(r, ruleIdKey)
|
||||
return updateRuleReq{Rule: rule}, nil
|
||||
}
|
||||
|
||||
@@ -137,7 +209,7 @@ func decodeUpdateRuleScheduleRequest(_ context.Context, r *http.Request) (interf
|
||||
}
|
||||
|
||||
req := updateRuleScheduleReq{
|
||||
id: chi.URLParam(r, idKey),
|
||||
id: chi.URLParam(r, ruleIdKey),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
|
||||
@@ -148,7 +220,7 @@ func decodeUpdateRuleScheduleRequest(_ context.Context, r *http.Request) (interf
|
||||
|
||||
func decodeUpdateRuleStatusRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
req := updateRuleStatusReq{
|
||||
id: chi.URLParam(r, idKey),
|
||||
id: chi.URLParam(r, ruleIdKey),
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
@@ -195,7 +267,140 @@ func decodeListRulesRequest(_ context.Context, r *http.Request) (interface{}, er
|
||||
}
|
||||
|
||||
func decodeDeleteRuleRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
id := chi.URLParam(r, idKey)
|
||||
id := chi.URLParam(r, ruleIdKey)
|
||||
|
||||
return deleteRuleReq{id: id}, nil
|
||||
}
|
||||
|
||||
func decodeGenerateReportRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
a, err := apiutil.ReadStringQuery(r, actionKey, defAction)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
action, err := re.ToReportAction(a)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
req := generateReportReq{
|
||||
action: action,
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(err, apiutil.ErrValidation)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeAddReportConfigRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
var config re.ReportConfig
|
||||
if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
|
||||
return nil, errors.Wrap(err, apiutil.ErrValidation)
|
||||
}
|
||||
return addReportConfigReq{ReportConfig: config}, nil
|
||||
}
|
||||
|
||||
func decodeViewReportConfigRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
id := chi.URLParam(r, reportIdKey)
|
||||
return viewReportConfigReq{ID: id}, nil
|
||||
}
|
||||
|
||||
func decodeUpdateReportConfigRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
var config re.ReportConfig
|
||||
if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
|
||||
return nil, errors.Wrap(err, apiutil.ErrValidation)
|
||||
}
|
||||
config.ID = chi.URLParam(r, reportIdKey)
|
||||
return updateReportConfigReq{ReportConfig: config}, nil
|
||||
}
|
||||
|
||||
func decodeUpdateReportScheduleRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
}
|
||||
|
||||
req := updateReportScheduleReq{
|
||||
id: chi.URLParam(r, reportIdKey),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeUpdateReportStatusRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
req := updateReportStatusReq{
|
||||
id: chi.URLParam(r, reportIdKey),
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeDeleteReportConfigRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
id := chi.URLParam(r, reportIdKey)
|
||||
return deleteReportConfigReq{ID: id}, nil
|
||||
}
|
||||
|
||||
func decodeListReportsConfigRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
offset, err := apiutil.ReadNumQuery[uint64](r, api.OffsetKey, api.DefOffset)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
limit, err := apiutil.ReadNumQuery[uint64](r, api.LimitKey, api.DefLimit)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
status, err := apiutil.ReadStringQuery(r, api.StatusKey, api.DefStatus)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
st, err := re.ToStatus(status)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
name, err := apiutil.ReadStringQuery(r, api.NameKey, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
return listReportsConfigReq{
|
||||
PageMeta: re.PageMeta{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Status: st,
|
||||
Name: name,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func encodeFileDownloadResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
|
||||
switch resp := response.(type) {
|
||||
case downloadReportResp:
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", resp.File.Name))
|
||||
w.Header().Set("Content-Type", resp.File.Format.ContentType())
|
||||
_, err := w.Write(resp.File.Data)
|
||||
return err
|
||||
default:
|
||||
if ar, ok := response.(supermq.Response); ok {
|
||||
for k, v := range ar.Headers() {
|
||||
w.Header().Set(k, v)
|
||||
}
|
||||
w.Header().Set("Content-Type", api.ContentType)
|
||||
w.WriteHeader(ar.Code())
|
||||
|
||||
if ar.Empty() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ func (re *re) sendEmail(l *lua.LState) int {
|
||||
}
|
||||
})
|
||||
|
||||
if err := re.email.SendEmailNotification(recipients, "", subject, "", "", content, ""); err != nil {
|
||||
if err := re.email.SendEmailNotification(recipients, "", subject, "", "", content, "", make(map[string][]byte)); err != nil {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
|
||||
+1
-2
@@ -3,8 +3,7 @@
|
||||
|
||||
package re
|
||||
|
||||
//go:generate mockery --name Emailer --output=./mocks --filename emailer.go --quiet --note "Copyright (c) Abstract Machines"
|
||||
type Emailer interface {
|
||||
// SendEmailNotification sends an email to the recipients based on a trigger.
|
||||
SendEmailNotification(to []string, from, subject, header, user, content, footer string) error
|
||||
SendEmailNotification(to []string, from, subject, header, user, content, footer string, attachments map[string][]byte) error
|
||||
}
|
||||
|
||||
@@ -19,6 +19,6 @@ func New(a *email.Config) (re.Emailer, error) {
|
||||
return &emailer{agent: e}, err
|
||||
}
|
||||
|
||||
func (e *emailer) SendEmailNotification(to []string, from, subject, header, user, content, footer string) error {
|
||||
return e.agent.Send(to, from, subject, header, user, content, footer)
|
||||
func (e *emailer) SendEmailNotification(to []string, from, subject, header, user, content, footer string, attachments map[string][]byte) error {
|
||||
return e.agent.Send(to, from, subject, header, user, content, footer, attachments)
|
||||
}
|
||||
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package re
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
||||
"github.com/absmach/supermq/pkg/transformers/senml"
|
||||
"github.com/johnfercher/maroto/pkg/color"
|
||||
"github.com/johnfercher/maroto/pkg/consts"
|
||||
"github.com/johnfercher/maroto/pkg/pdf"
|
||||
"github.com/johnfercher/maroto/pkg/props"
|
||||
)
|
||||
|
||||
func generatePDFReport(reports []Report) ([]byte, error) {
|
||||
m := pdf.NewMaroto(consts.Portrait, consts.A4)
|
||||
m.SetPageMargins(10, 15, 10)
|
||||
|
||||
primaryColor := color.Color{Red: 41, Green: 128, Blue: 185} // Blue
|
||||
secondaryColor := color.Color{Red: 26, Green: 82, Blue: 118} // Darker blue
|
||||
subtleColor := color.Color{Red: 189, Green: 195, Blue: 199} // Light gray
|
||||
tableHeaderBg := color.Color{Red: 236, Green: 240, Blue: 241} // Very light gray
|
||||
alternateRow := color.Color{Red: 245, Green: 247, Blue: 249} // Even lighter gray
|
||||
textPrimary := color.Color{Red: 44, Green: 62, Blue: 80} // Dark blue-gray
|
||||
textSecondary := color.Color{Red: 127, Green: 140, Blue: 141} // Medium gray
|
||||
white := color.NewWhite()
|
||||
|
||||
m.RegisterHeader(func() {
|
||||
m.SetBackgroundColor(primaryColor)
|
||||
m.Row(2, func() { m.Col(12, func() {}) })
|
||||
m.SetBackgroundColor(white)
|
||||
|
||||
m.Row(20, func() {
|
||||
m.Col(2, func() {})
|
||||
|
||||
m.Col(8, func() {
|
||||
m.Text("Magistrala IoT Report", props.Text{
|
||||
Size: 20,
|
||||
Style: consts.Bold,
|
||||
Color: primaryColor,
|
||||
Align: consts.Center,
|
||||
Top: 6,
|
||||
})
|
||||
})
|
||||
|
||||
m.Col(2, func() {
|
||||
m.Text(time.Now().Format("02 Jan 2006"), props.Text{
|
||||
Size: 10,
|
||||
Style: consts.Italic,
|
||||
Align: consts.Right,
|
||||
Color: textSecondary,
|
||||
Top: 8,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
m.SetBackgroundColor(subtleColor)
|
||||
m.Row(0.5, func() { m.Col(12, func() {}) })
|
||||
m.SetBackgroundColor(white)
|
||||
m.Row(0.25, func() {})
|
||||
m.SetBackgroundColor(subtleColor)
|
||||
m.Row(0.25, func() { m.Col(12, func() {}) })
|
||||
m.SetBackgroundColor(white)
|
||||
|
||||
m.Row(5, func() {})
|
||||
})
|
||||
|
||||
m.RegisterFooter(func() {
|
||||
currentPage := m.GetCurrentPage()
|
||||
|
||||
m.Row(5, func() {})
|
||||
m.SetBackgroundColor(subtleColor)
|
||||
m.Row(0.25, func() { m.Col(12, func() {}) })
|
||||
m.SetBackgroundColor(white)
|
||||
m.Row(0.25, func() {})
|
||||
m.SetBackgroundColor(subtleColor)
|
||||
m.Row(0.5, func() { m.Col(12, func() {}) })
|
||||
m.SetBackgroundColor(white)
|
||||
|
||||
m.Row(10, func() {
|
||||
m.Col(4, func() {
|
||||
m.Text("Generated: "+time.Now().Format("15:04:05"), props.Text{
|
||||
Size: 8,
|
||||
Style: consts.Italic,
|
||||
Align: consts.Left,
|
||||
Color: textSecondary,
|
||||
Top: 3,
|
||||
})
|
||||
})
|
||||
|
||||
m.Col(4, func() {
|
||||
m.Text(fmt.Sprintf("Page %d", currentPage+1), props.Text{
|
||||
Size: 9,
|
||||
Style: consts.Bold,
|
||||
Align: consts.Center,
|
||||
Color: textPrimary,
|
||||
Top: 3,
|
||||
})
|
||||
})
|
||||
|
||||
m.Col(4, func() {
|
||||
m.Text("Magistrala System", props.Text{
|
||||
Size: 8,
|
||||
Style: consts.Italic,
|
||||
Align: consts.Right,
|
||||
Color: textSecondary,
|
||||
Top: 3,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
headers := []string{"Time", "Name", "Value", "Unit", "Subtopic"}
|
||||
widths := []uint{4, 2, 2, 2, 2}
|
||||
|
||||
for i, report := range reports {
|
||||
if i > 0 {
|
||||
m.AddPage()
|
||||
}
|
||||
|
||||
m.Row(0.5, func() {
|
||||
m.Col(1, func() {})
|
||||
})
|
||||
m.SetBackgroundColor(white)
|
||||
|
||||
m.Row(10, func() {
|
||||
m.Col(12, func() {
|
||||
m.Text("Metrics", props.Text{
|
||||
Size: 16,
|
||||
Style: consts.Bold,
|
||||
Color: secondaryColor,
|
||||
Top: 2,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
m.SetBackgroundColor(alternateRow)
|
||||
m.Row(0.5, func() { m.Col(12, func() {}) })
|
||||
|
||||
m.Row(8, func() {
|
||||
m.Col(2, func() {
|
||||
m.Text("Device ID: ", props.Text{
|
||||
Size: 11,
|
||||
Style: consts.Bold,
|
||||
Align: consts.Left,
|
||||
Color: textPrimary,
|
||||
Top: 1,
|
||||
})
|
||||
})
|
||||
|
||||
m.Col(10, func() {
|
||||
m.Text(report.Metric.ClientID, props.Text{
|
||||
Size: 11,
|
||||
Style: consts.Italic,
|
||||
Color: textPrimary,
|
||||
Top: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
m.Row(8, func() {
|
||||
m.Col(2, func() {
|
||||
m.Text("Channel ID: ", props.Text{
|
||||
Size: 11,
|
||||
Style: consts.Bold,
|
||||
Align: consts.Left,
|
||||
Color: textPrimary,
|
||||
Top: 1,
|
||||
})
|
||||
})
|
||||
|
||||
m.Col(10, func() {
|
||||
m.Text(report.Metric.ChannelID, props.Text{
|
||||
Size: 11,
|
||||
Style: consts.Italic,
|
||||
Color: textPrimary,
|
||||
Top: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
m.SetBackgroundColor(alternateRow)
|
||||
m.Row(0.5, func() { m.Col(12, func() {}) })
|
||||
m.SetBackgroundColor(white)
|
||||
|
||||
m.Row(10, func() {
|
||||
m.Col(12, func() {
|
||||
m.Text(fmt.Sprintf("Total Records: %d", len(report.Messages)), props.Text{
|
||||
Size: 10,
|
||||
Style: consts.Italic,
|
||||
Align: consts.Right,
|
||||
Color: textSecondary,
|
||||
Top: 2,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
m.SetBackgroundColor(primaryColor)
|
||||
m.Row(1, func() { m.Col(12, func() {}) })
|
||||
m.SetBackgroundColor(tableHeaderBg)
|
||||
m.Row(10, func() {
|
||||
for i, header := range headers {
|
||||
m.Col(widths[i], func() {
|
||||
m.Text(header, props.Text{
|
||||
Size: 11,
|
||||
Style: consts.Bold,
|
||||
Align: consts.Center,
|
||||
Top: 2,
|
||||
Color: secondaryColor,
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
m.SetBackgroundColor(subtleColor)
|
||||
m.Row(0.5, func() { m.Col(12, func() {}) })
|
||||
m.SetBackgroundColor(white)
|
||||
|
||||
useAlternateColor := false
|
||||
for _, msg := range report.Messages {
|
||||
if useAlternateColor {
|
||||
m.SetBackgroundColor(alternateRow)
|
||||
}
|
||||
|
||||
m.Row(9, func() {
|
||||
m.Col(widths[0], func() {
|
||||
m.Text(formatTime(msg.Time), props.Text{
|
||||
Size: 10,
|
||||
Align: consts.Center,
|
||||
Top: 2,
|
||||
Color: textPrimary,
|
||||
})
|
||||
})
|
||||
|
||||
m.Col(widths[1], func() {
|
||||
m.Text(msg.Name, props.Text{
|
||||
Size: 10,
|
||||
Style: consts.Bold,
|
||||
Align: consts.Center,
|
||||
Top: 2,
|
||||
Color: primaryColor,
|
||||
})
|
||||
})
|
||||
|
||||
m.Col(widths[2], func() {
|
||||
m.Text(formatValue(msg), props.Text{
|
||||
Size: 10,
|
||||
Style: consts.Normal,
|
||||
Align: consts.Center,
|
||||
Top: 2,
|
||||
Color: textPrimary,
|
||||
})
|
||||
})
|
||||
|
||||
m.Col(widths[3], func() {
|
||||
m.Text(msg.Unit, props.Text{
|
||||
Size: 10,
|
||||
Style: consts.Italic,
|
||||
Align: consts.Center,
|
||||
Top: 2,
|
||||
Color: textSecondary,
|
||||
})
|
||||
})
|
||||
|
||||
m.Col(widths[4], func() {
|
||||
m.Text(msg.Subtopic, props.Text{
|
||||
Size: 10,
|
||||
Align: consts.Center,
|
||||
Top: 2,
|
||||
Color: secondaryColor,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
if !useAlternateColor {
|
||||
m.Row(0.2, func() {
|
||||
m.Col(12, func() {})
|
||||
})
|
||||
}
|
||||
|
||||
useAlternateColor = !useAlternateColor
|
||||
m.SetBackgroundColor(white)
|
||||
}
|
||||
}
|
||||
|
||||
buf, err := m.Output()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func formatTime(t float64) string {
|
||||
if t > 9999999999 {
|
||||
return time.Unix(0, int64(t)).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
return time.Unix(int64(t), 0).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
func formatValue(msg senml.Message) string {
|
||||
switch {
|
||||
case msg.Value != nil:
|
||||
return fmt.Sprintf("%.2f", *msg.Value)
|
||||
case msg.StringValue != nil:
|
||||
return *msg.StringValue
|
||||
case msg.BoolValue != nil:
|
||||
return fmt.Sprintf("%t", *msg.BoolValue)
|
||||
case msg.DataValue != nil:
|
||||
return *msg.DataValue
|
||||
default:
|
||||
return "N/A"
|
||||
}
|
||||
}
|
||||
|
||||
func generateCSVReport(reports []Report) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
writer := csv.NewWriter(&buf)
|
||||
|
||||
headers := []string{"Time", "Name", "Subtopic", "Value", "Unit"}
|
||||
|
||||
for i, report := range reports {
|
||||
if len(report.Messages) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
if err := writer.Write([]string{""}); err != nil {
|
||||
return nil, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
if err := writer.Write([]string{"=== NEW REPORT ==="}); err != nil {
|
||||
return nil, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
if err := writer.Write([]string{""}); err != nil {
|
||||
return nil, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := writer.Write([]string{"Report Information:"}); err != nil {
|
||||
return nil, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
if err := writer.Write([]string{"Device ID", report.Metric.ClientID}); err != nil {
|
||||
return nil, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
if err := writer.Write([]string{"Channel ID", report.Metric.ChannelID}); err != nil {
|
||||
return nil, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
if err := writer.Write([]string{""}); err != nil {
|
||||
return nil, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
if err := writer.Write(headers); err != nil {
|
||||
return nil, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
sort.Slice(report.Messages, func(i, j int) bool {
|
||||
return report.Messages[i].Time < report.Messages[j].Time
|
||||
})
|
||||
|
||||
for _, msg := range report.Messages {
|
||||
timeStr := formatTime(msg.Time)
|
||||
|
||||
var valueStr string
|
||||
if msg.Value != nil {
|
||||
valueStr = fmt.Sprintf("%.2f", *msg.Value)
|
||||
} else if msg.StringValue != nil {
|
||||
valueStr = *msg.StringValue
|
||||
} else if msg.BoolValue != nil {
|
||||
valueStr = fmt.Sprintf("%v", *msg.BoolValue)
|
||||
} else if msg.DataValue != nil {
|
||||
valueStr = *msg.DataValue
|
||||
} else {
|
||||
valueStr = "N/A"
|
||||
}
|
||||
|
||||
row := []string{
|
||||
timeStr,
|
||||
msg.Name,
|
||||
msg.Subtopic,
|
||||
valueStr,
|
||||
msg.Unit,
|
||||
}
|
||||
|
||||
if err := writer.Write(row); err != nil {
|
||||
return nil, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.Flush()
|
||||
if err := writer.Error(); err != nil {
|
||||
return nil, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
+48
-14
@@ -35,11 +35,25 @@ func (re *re) Handle(msg *messaging.Message) error {
|
||||
return err
|
||||
}
|
||||
|
||||
reportConfigs, err := re.repo.ListReportsConfig(ctx, pm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range page.Rules {
|
||||
go func(ctx context.Context) {
|
||||
re.errors <- re.process(ctx, r, msg)
|
||||
}(ctx)
|
||||
}
|
||||
|
||||
for _, cfg := range reportConfigs.ReportConfigs {
|
||||
go func(ctx context.Context) {
|
||||
if err := re.processReportConfig(ctx, cfg); err != nil {
|
||||
re.errors <- err
|
||||
}
|
||||
}(ctx)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -83,6 +97,13 @@ func (re *re) process(ctx context.Context, r Rule, msg *messaging.Message) error
|
||||
return err
|
||||
}
|
||||
|
||||
func (re *re) processReportConfig(ctx context.Context, cfg ReportConfig) error {
|
||||
if _, err := re.generateReport(ctx, cfg, EmailReport); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (re *re) handleOutput(ctx context.Context, o ScriptOutput, r Rule, msg *messaging.Message, val interface{}) error {
|
||||
switch o {
|
||||
case Channels:
|
||||
@@ -119,7 +140,7 @@ func (re *re) StartScheduler(ctx context.Context) error {
|
||||
}
|
||||
|
||||
for _, rule := range page.Rules {
|
||||
if rule.shouldRun(startTime) {
|
||||
if rule.Schedule.ShouldRun(startTime) {
|
||||
go func(r Rule) {
|
||||
msg := &messaging.Message{
|
||||
Channel: r.InputChannel,
|
||||
@@ -129,48 +150,61 @@ func (re *re) StartScheduler(ctx context.Context) error {
|
||||
}(rule)
|
||||
}
|
||||
}
|
||||
|
||||
reportConfigs, err := re.repo.ListReportsConfig(ctx, pm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, cfg := range reportConfigs.ReportConfigs {
|
||||
if cfg.Schedule.ShouldRun(startTime) {
|
||||
go func(config ReportConfig) {
|
||||
re.errors <- re.processReportConfig(ctx, config)
|
||||
}(cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r Rule) shouldRun(startTime time.Time) bool {
|
||||
func (s Schedule) ShouldRun(startTime time.Time) bool {
|
||||
// Don't run if the rule's start time is in the future
|
||||
// This allows scheduling rules to start at a specific future time
|
||||
if r.Schedule.StartDateTime.After(startTime) {
|
||||
if s.StartDateTime.After(startTime) {
|
||||
return false
|
||||
}
|
||||
|
||||
t := r.Schedule.Time.Truncate(time.Minute).UTC()
|
||||
t := s.Time.Truncate(time.Minute).UTC()
|
||||
startTimeOnly := time.Date(0, 1, 1, startTime.Hour(), startTime.Minute(), 0, 0, time.UTC)
|
||||
if t.Equal(startTimeOnly) {
|
||||
return true
|
||||
}
|
||||
|
||||
if r.Schedule.RecurringPeriod == 0 {
|
||||
if s.RecurringPeriod == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
period := int(r.Schedule.RecurringPeriod)
|
||||
period := int(s.RecurringPeriod)
|
||||
|
||||
switch r.Schedule.Recurring {
|
||||
switch s.Recurring {
|
||||
case Daily:
|
||||
if r.Schedule.RecurringPeriod > 0 {
|
||||
daysSinceStart := startTime.Sub(r.Schedule.StartDateTime).Hours() / hoursInDay
|
||||
if s.RecurringPeriod > 0 {
|
||||
daysSinceStart := startTime.Sub(s.StartDateTime).Hours() / hoursInDay
|
||||
if int(daysSinceStart)%period == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case Weekly:
|
||||
if r.Schedule.RecurringPeriod > 0 {
|
||||
weeksSinceStart := startTime.Sub(r.Schedule.StartDateTime).Hours() / (hoursInDay * daysInWeek)
|
||||
if s.RecurringPeriod > 0 {
|
||||
weeksSinceStart := startTime.Sub(s.StartDateTime).Hours() / (hoursInDay * daysInWeek)
|
||||
if int(weeksSinceStart)%period == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case Monthly:
|
||||
if r.Schedule.RecurringPeriod > 0 {
|
||||
monthsSinceStart := (startTime.Year()-r.Schedule.StartDateTime.Year())*monthsInYear +
|
||||
int(startTime.Month()-r.Schedule.StartDateTime.Month())
|
||||
if s.RecurringPeriod > 0 {
|
||||
monthsSinceStart := (startTime.Year()-s.StartDateTime.Year())*monthsInYear +
|
||||
int(startTime.Month()-s.StartDateTime.Month())
|
||||
if monthsSinceStart%period == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/absmach/magistrala/re"
|
||||
"github.com/absmach/supermq/pkg/authn"
|
||||
smqauthz "github.com/absmach/supermq/pkg/authz"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/messaging"
|
||||
"github.com/absmach/supermq/pkg/policies"
|
||||
)
|
||||
|
||||
var (
|
||||
errDomainCreateConfigs = errors.New("not authorized to create report configs in domain")
|
||||
errDomainViewConfigs = errors.New("not authorized to view report configs in domain")
|
||||
errDomainUpdateConfigs = errors.New("not authorized to update report configs in domain")
|
||||
errDomainDeleteConfigs = errors.New("not authorized to delete report configs in domain")
|
||||
errDomainCreateRules = errors.New("not authorized to create rules in domain")
|
||||
errDomainViewRules = errors.New("not authorized to view rules in domain")
|
||||
errDomainUpdateRules = errors.New("not authorized to update rules in domain")
|
||||
errDomainDeleteRules = errors.New("not authorized to delete rules in domain")
|
||||
errDomainGenerateReports = errors.New("not authorized to generate reports in domain")
|
||||
)
|
||||
|
||||
type authorizationMiddleware struct {
|
||||
svc re.Service
|
||||
authz smqauthz.Authorization
|
||||
}
|
||||
|
||||
// AuthorizationMiddleware adds authorization to the re service.
|
||||
func AuthorizationMiddleware(svc re.Service, authz smqauthz.Authorization) (re.Service, error) {
|
||||
return &authorizationMiddleware{
|
||||
svc: svc,
|
||||
authz: authz,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) AddRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.Rule{}, errors.Wrap(errDomainCreateRules, err)
|
||||
}
|
||||
|
||||
return am.svc.AddRule(ctx, session, r)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) ViewRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.Rule{}, errors.Wrap(errDomainViewRules, err)
|
||||
}
|
||||
|
||||
return am.svc.ViewRule(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) UpdateRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
|
||||
}
|
||||
|
||||
return am.svc.UpdateRule(ctx, session, r)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) UpdateRuleSchedule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
|
||||
}
|
||||
|
||||
return am.svc.UpdateRuleSchedule(ctx, session, r)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) ListRules(ctx context.Context, session authn.Session, pm re.PageMeta) (re.Page, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.Page{}, errors.Wrap(errDomainViewRules, err)
|
||||
}
|
||||
|
||||
return am.svc.ListRules(ctx, session, pm)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) RemoveRule(ctx context.Context, session authn.Session, id string) error {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return errors.Wrap(errDomainDeleteRules, err)
|
||||
}
|
||||
|
||||
return am.svc.RemoveRule(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) EnableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
|
||||
}
|
||||
|
||||
return am.svc.EnableRule(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) DisableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
|
||||
}
|
||||
|
||||
return am.svc.DisableRule(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) AddReportConfig(ctx context.Context, session authn.Session, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(errDomainCreateConfigs, err)
|
||||
}
|
||||
|
||||
return am.svc.AddReportConfig(ctx, session, cfg)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) ViewReportConfig(ctx context.Context, session authn.Session, id string) (re.ReportConfig, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(errDomainViewConfigs, err)
|
||||
}
|
||||
|
||||
return am.svc.ViewReportConfig(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) UpdateReportConfig(ctx context.Context, session authn.Session, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(errDomainUpdateConfigs, err)
|
||||
}
|
||||
|
||||
return am.svc.UpdateReportConfig(ctx, session, cfg)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) UpdateReportSchedule(ctx context.Context, session authn.Session, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(errDomainDeleteConfigs, err)
|
||||
}
|
||||
|
||||
return am.svc.UpdateReportSchedule(ctx, session, cfg)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) RemoveReportConfig(ctx context.Context, session authn.Session, id string) error {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return errors.Wrap(errDomainDeleteConfigs, err)
|
||||
}
|
||||
|
||||
return am.svc.RemoveReportConfig(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) ListReportsConfig(ctx context.Context, session authn.Session, pm re.PageMeta) (re.ReportConfigPage, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.ReportConfigPage{}, errors.Wrap(errDomainViewConfigs, err)
|
||||
}
|
||||
|
||||
return am.svc.ListReportsConfig(ctx, session, pm)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) EnableReportConfig(ctx context.Context, session authn.Session, id string) (re.ReportConfig, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(errDomainUpdateConfigs, err)
|
||||
}
|
||||
|
||||
return am.svc.EnableReportConfig(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) DisableReportConfig(ctx context.Context, session authn.Session, id string) (re.ReportConfig, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(errDomainUpdateConfigs, err)
|
||||
}
|
||||
|
||||
return am.svc.DisableReportConfig(ctx, session, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) GenerateReport(ctx context.Context, session authn.Session, config re.ReportConfig, action re.ReportAction) (re.ReportPage, error) {
|
||||
if err := am.authorize(ctx, smqauthz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Subject: session.DomainUserID,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
Permission: policies.MembershipPermission,
|
||||
}); err != nil {
|
||||
return re.ReportPage{}, errors.Wrap(errDomainGenerateReports, err)
|
||||
}
|
||||
|
||||
return am.svc.GenerateReport(ctx, session, config, action)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) StartScheduler(ctx context.Context) error {
|
||||
return am.svc.StartScheduler(ctx)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) Handle(msg *messaging.Message) error {
|
||||
return am.svc.Handle(msg)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) Cancel() error {
|
||||
return am.svc.Cancel()
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) authorize(ctx context.Context, pr smqauthz.PolicyReq) error {
|
||||
if err := am.authz.Authorize(ctx, pr); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -220,3 +220,174 @@ func (lm *loggingMiddleware) Handle(msg *messaging.Message) (err error) {
|
||||
func (lm *loggingMiddleware) Cancel() error {
|
||||
return lm.Cancel()
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) GenerateReport(ctx context.Context, session authn.Session, config re.ReportConfig, action re.ReportAction) (page re.ReportPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.String("error", err.Error()))
|
||||
lm.logger.Warn("Generate report failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Generate report completed", args...)
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.GenerateReport(ctx, session, config, action)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) AddReportConfig(ctx context.Context, session authn.Session, config re.ReportConfig) (res re.ReportConfig, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.String("domain_id", session.DomainID),
|
||||
slog.String("report_name", config.Name),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.String("error", err.Error()))
|
||||
lm.logger.Warn("Add report config failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Add report config completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.AddReportConfig(ctx, session, config)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ViewReportConfig(ctx context.Context, session authn.Session, id string) (res re.ReportConfig, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.String("domain_id", session.DomainID),
|
||||
slog.Group("report_config",
|
||||
slog.String("id", res.ID),
|
||||
slog.String("name", res.Name),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.String("error", err.Error()))
|
||||
lm.logger.Warn("View report config failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("View report config completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.ViewReportConfig(ctx, session, id)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) UpdateReportConfig(ctx context.Context, session authn.Session, config re.ReportConfig) (res re.ReportConfig, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.String("domain_id", session.DomainID),
|
||||
slog.Group("report_config",
|
||||
slog.String("id", config.ID),
|
||||
slog.String("name", config.Name),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.String("error", err.Error()))
|
||||
lm.logger.Warn("Update report config failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Update report config completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.UpdateReportConfig(ctx, session, config)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) UpdateReportSchedule(ctx context.Context, session authn.Session, cfg re.ReportConfig) (res re.ReportConfig, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.String("domain_id", session.DomainID),
|
||||
slog.Group("report",
|
||||
slog.String("id", cfg.ID),
|
||||
slog.Any("schedule", cfg.Schedule),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.String("error", err.Error()))
|
||||
lm.logger.Warn("Update report schedule failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Update report schedule completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.UpdateReportSchedule(ctx, session, cfg)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ListReportsConfig(ctx context.Context, session authn.Session, pm re.PageMeta) (pg re.ReportConfigPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.String("domain_id", session.DomainID),
|
||||
slog.Group("page",
|
||||
slog.Uint64("offset", pm.Offset),
|
||||
slog.Uint64("limit", pm.Limit),
|
||||
slog.Uint64("total", pg.Total),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.String("error", err.Error()))
|
||||
lm.logger.Warn("List reports config failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("List reports config completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.ListReportsConfig(ctx, session, pm)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) DisableReportConfig(ctx context.Context, session authn.Session, id string) (res re.ReportConfig, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.String("domain_id", session.DomainID),
|
||||
slog.Group("report_config",
|
||||
slog.String("id", res.ID),
|
||||
slog.String("name", res.Name),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.String("error", err.Error()))
|
||||
lm.logger.Warn("Disable report config failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Disable report config completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.DisableReportConfig(ctx, session, id)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) EnableReportConfig(ctx context.Context, session authn.Session, id string) (res re.ReportConfig, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.String("domain_id", session.DomainID),
|
||||
slog.Group("report_config",
|
||||
slog.String("id", res.ID),
|
||||
slog.String("name", res.Name),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.String("error", err.Error()))
|
||||
lm.logger.Warn("Enable report config failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Enable report config completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.EnableReportConfig(ctx, session, id)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) RemoveReportConfig(ctx context.Context, session authn.Session, id string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
slog.String("domain_id", session.DomainID),
|
||||
slog.String("report_config_id", id),
|
||||
}
|
||||
if err != nil {
|
||||
args = append(args, slog.String("error", err.Error()))
|
||||
lm.logger.Warn("Remove report config failed", args...)
|
||||
return
|
||||
}
|
||||
lm.logger.Info("Remove report config completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.RemoveReportConfig(ctx, session, id)
|
||||
}
|
||||
|
||||
+73
-26
@@ -1,33 +1,15 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
// Code generated by mockery; DO NOT EDIT.
|
||||
// github.com/vektra/mockery
|
||||
// template: testify
|
||||
// Copyright (c) Abstract Machines
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mocks
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// Emailer is an autogenerated mock type for the Emailer type
|
||||
type Emailer struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// SendEmailNotification provides a mock function with given fields: to, from, subject, header, user, content, footer
|
||||
func (_m *Emailer) SendEmailNotification(to []string, from string, subject string, header string, user string, content string, footer string) error {
|
||||
ret := _m.Called(to, from, subject, header, user, content, footer)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SendEmailNotification")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func([]string, string, string, string, string, string, string) error); ok {
|
||||
r0 = rf(to, from, subject, header, user, content, footer)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// NewEmailer creates a new instance of Emailer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
@@ -42,3 +24,68 @@ func NewEmailer(t interface {
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// Emailer is an autogenerated mock type for the Emailer type
|
||||
type Emailer struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type Emailer_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *Emailer) EXPECT() *Emailer_Expecter {
|
||||
return &Emailer_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// SendEmailNotification provides a mock function for the type Emailer
|
||||
func (_mock *Emailer) SendEmailNotification(to []string, from string, subject string, header string, user string, content string, footer string, attachments map[string][]byte) error {
|
||||
ret := _mock.Called(to, from, subject, header, user, content, footer, attachments)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SendEmailNotification")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func([]string, string, string, string, string, string, string, map[string][]byte) error); ok {
|
||||
r0 = returnFunc(to, from, subject, header, user, content, footer, attachments)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Emailer_SendEmailNotification_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendEmailNotification'
|
||||
type Emailer_SendEmailNotification_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SendEmailNotification is a helper method to define mock.On call
|
||||
// - to
|
||||
// - from
|
||||
// - subject
|
||||
// - header
|
||||
// - user
|
||||
// - content
|
||||
// - footer
|
||||
// - attachments
|
||||
func (_e *Emailer_Expecter) SendEmailNotification(to interface{}, from interface{}, subject interface{}, header interface{}, user interface{}, content interface{}, footer interface{}, attachments interface{}) *Emailer_SendEmailNotification_Call {
|
||||
return &Emailer_SendEmailNotification_Call{Call: _e.mock.On("SendEmailNotification", to, from, subject, header, user, content, footer, attachments)}
|
||||
}
|
||||
|
||||
func (_c *Emailer_SendEmailNotification_Call) Run(run func(to []string, from string, subject string, header string, user string, content string, footer string, attachments map[string][]byte)) *Emailer_SendEmailNotification_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].([]string), args[1].(string), args[2].(string), args[3].(string), args[4].(string), args[5].(string), args[6].(string), args[7].(map[string][]byte))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Emailer_SendEmailNotification_Call) Return(err error) *Emailer_SendEmailNotification_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Emailer_SendEmailNotification_Call) RunAndReturn(run func(to []string, from string, subject string, header string, user string, content string, footer string, attachments map[string][]byte) error) *Emailer_SendEmailNotification_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
+390
-15
@@ -41,6 +41,61 @@ func (_m *Repository) EXPECT() *Repository_Expecter {
|
||||
return &Repository_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// AddReportConfig provides a mock function for the type Repository
|
||||
func (_mock *Repository) AddReportConfig(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
ret := _mock.Called(ctx, cfg)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for AddReportConfig")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfig
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.ReportConfig) (re.ReportConfig, error)); ok {
|
||||
return returnFunc(ctx, cfg)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.ReportConfig) re.ReportConfig); ok {
|
||||
r0 = returnFunc(ctx, cfg)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfig)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, re.ReportConfig) error); ok {
|
||||
r1 = returnFunc(ctx, cfg)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Repository_AddReportConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddReportConfig'
|
||||
type Repository_AddReportConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// AddReportConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - cfg
|
||||
func (_e *Repository_Expecter) AddReportConfig(ctx interface{}, cfg interface{}) *Repository_AddReportConfig_Call {
|
||||
return &Repository_AddReportConfig_Call{Call: _e.mock.On("AddReportConfig", ctx, cfg)}
|
||||
}
|
||||
|
||||
func (_c *Repository_AddReportConfig_Call) Run(run func(ctx context.Context, cfg re.ReportConfig)) *Repository_AddReportConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(re.ReportConfig))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_AddReportConfig_Call) Return(reportConfig re.ReportConfig, err error) *Repository_AddReportConfig_Call {
|
||||
_c.Call.Return(reportConfig, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_AddReportConfig_Call) RunAndReturn(run func(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error)) *Repository_AddReportConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// AddRule provides a mock function for the type Repository
|
||||
func (_mock *Repository) AddRule(ctx context.Context, r re.Rule) (re.Rule, error) {
|
||||
ret := _mock.Called(ctx, r)
|
||||
@@ -96,6 +151,61 @@ func (_c *Repository_AddRule_Call) RunAndReturn(run func(ctx context.Context, r
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListReportsConfig provides a mock function for the type Repository
|
||||
func (_mock *Repository) ListReportsConfig(ctx context.Context, pm re.PageMeta) (re.ReportConfigPage, error) {
|
||||
ret := _mock.Called(ctx, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListReportsConfig")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfigPage
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.PageMeta) (re.ReportConfigPage, error)); ok {
|
||||
return returnFunc(ctx, pm)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.PageMeta) re.ReportConfigPage); ok {
|
||||
r0 = returnFunc(ctx, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfigPage)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, re.PageMeta) error); ok {
|
||||
r1 = returnFunc(ctx, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Repository_ListReportsConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListReportsConfig'
|
||||
type Repository_ListReportsConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListReportsConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - pm
|
||||
func (_e *Repository_Expecter) ListReportsConfig(ctx interface{}, pm interface{}) *Repository_ListReportsConfig_Call {
|
||||
return &Repository_ListReportsConfig_Call{Call: _e.mock.On("ListReportsConfig", ctx, pm)}
|
||||
}
|
||||
|
||||
func (_c *Repository_ListReportsConfig_Call) Run(run func(ctx context.Context, pm re.PageMeta)) *Repository_ListReportsConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(re.PageMeta))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_ListReportsConfig_Call) Return(reportConfigPage re.ReportConfigPage, err error) *Repository_ListReportsConfig_Call {
|
||||
_c.Call.Return(reportConfigPage, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_ListReportsConfig_Call) RunAndReturn(run func(ctx context.Context, pm re.PageMeta) (re.ReportConfigPage, error)) *Repository_ListReportsConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListRules provides a mock function for the type Repository
|
||||
func (_mock *Repository) ListRules(ctx context.Context, pm re.PageMeta) (re.Page, error) {
|
||||
ret := _mock.Called(ctx, pm)
|
||||
@@ -151,6 +261,52 @@ func (_c *Repository_ListRules_Call) RunAndReturn(run func(ctx context.Context,
|
||||
return _c
|
||||
}
|
||||
|
||||
// RemoveReportConfig provides a mock function for the type Repository
|
||||
func (_mock *Repository) RemoveReportConfig(ctx context.Context, id string) error {
|
||||
ret := _mock.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RemoveReportConfig")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok {
|
||||
r0 = returnFunc(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Repository_RemoveReportConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveReportConfig'
|
||||
type Repository_RemoveReportConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// RemoveReportConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - id
|
||||
func (_e *Repository_Expecter) RemoveReportConfig(ctx interface{}, id interface{}) *Repository_RemoveReportConfig_Call {
|
||||
return &Repository_RemoveReportConfig_Call{Call: _e.mock.On("RemoveReportConfig", ctx, id)}
|
||||
}
|
||||
|
||||
func (_c *Repository_RemoveReportConfig_Call) Run(run func(ctx context.Context, id string)) *Repository_RemoveReportConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_RemoveReportConfig_Call) Return(err error) *Repository_RemoveReportConfig_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_RemoveReportConfig_Call) RunAndReturn(run func(ctx context.Context, id string) error) *Repository_RemoveReportConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// RemoveRule provides a mock function for the type Repository
|
||||
func (_mock *Repository) RemoveRule(ctx context.Context, id string) error {
|
||||
ret := _mock.Called(ctx, id)
|
||||
@@ -197,6 +353,171 @@ func (_c *Repository_RemoveRule_Call) RunAndReturn(run func(ctx context.Context,
|
||||
return _c
|
||||
}
|
||||
|
||||
// UpdateReportConfig provides a mock function for the type Repository
|
||||
func (_mock *Repository) UpdateReportConfig(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
ret := _mock.Called(ctx, cfg)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateReportConfig")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfig
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.ReportConfig) (re.ReportConfig, error)); ok {
|
||||
return returnFunc(ctx, cfg)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.ReportConfig) re.ReportConfig); ok {
|
||||
r0 = returnFunc(ctx, cfg)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfig)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, re.ReportConfig) error); ok {
|
||||
r1 = returnFunc(ctx, cfg)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Repository_UpdateReportConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateReportConfig'
|
||||
type Repository_UpdateReportConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// UpdateReportConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - cfg
|
||||
func (_e *Repository_Expecter) UpdateReportConfig(ctx interface{}, cfg interface{}) *Repository_UpdateReportConfig_Call {
|
||||
return &Repository_UpdateReportConfig_Call{Call: _e.mock.On("UpdateReportConfig", ctx, cfg)}
|
||||
}
|
||||
|
||||
func (_c *Repository_UpdateReportConfig_Call) Run(run func(ctx context.Context, cfg re.ReportConfig)) *Repository_UpdateReportConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(re.ReportConfig))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_UpdateReportConfig_Call) Return(reportConfig re.ReportConfig, err error) *Repository_UpdateReportConfig_Call {
|
||||
_c.Call.Return(reportConfig, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_UpdateReportConfig_Call) RunAndReturn(run func(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error)) *Repository_UpdateReportConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// UpdateReportConfigStatus provides a mock function for the type Repository
|
||||
func (_mock *Repository) UpdateReportConfigStatus(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
ret := _mock.Called(ctx, cfg)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateReportConfigStatus")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfig
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.ReportConfig) (re.ReportConfig, error)); ok {
|
||||
return returnFunc(ctx, cfg)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.ReportConfig) re.ReportConfig); ok {
|
||||
r0 = returnFunc(ctx, cfg)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfig)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, re.ReportConfig) error); ok {
|
||||
r1 = returnFunc(ctx, cfg)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Repository_UpdateReportConfigStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateReportConfigStatus'
|
||||
type Repository_UpdateReportConfigStatus_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// UpdateReportConfigStatus is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - cfg
|
||||
func (_e *Repository_Expecter) UpdateReportConfigStatus(ctx interface{}, cfg interface{}) *Repository_UpdateReportConfigStatus_Call {
|
||||
return &Repository_UpdateReportConfigStatus_Call{Call: _e.mock.On("UpdateReportConfigStatus", ctx, cfg)}
|
||||
}
|
||||
|
||||
func (_c *Repository_UpdateReportConfigStatus_Call) Run(run func(ctx context.Context, cfg re.ReportConfig)) *Repository_UpdateReportConfigStatus_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(re.ReportConfig))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_UpdateReportConfigStatus_Call) Return(reportConfig re.ReportConfig, err error) *Repository_UpdateReportConfigStatus_Call {
|
||||
_c.Call.Return(reportConfig, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_UpdateReportConfigStatus_Call) RunAndReturn(run func(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error)) *Repository_UpdateReportConfigStatus_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// UpdateReportSchedule provides a mock function for the type Repository
|
||||
func (_mock *Repository) UpdateReportSchedule(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
ret := _mock.Called(ctx, cfg)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateReportSchedule")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfig
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.ReportConfig) (re.ReportConfig, error)); ok {
|
||||
return returnFunc(ctx, cfg)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.ReportConfig) re.ReportConfig); ok {
|
||||
r0 = returnFunc(ctx, cfg)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfig)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, re.ReportConfig) error); ok {
|
||||
r1 = returnFunc(ctx, cfg)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Repository_UpdateReportSchedule_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateReportSchedule'
|
||||
type Repository_UpdateReportSchedule_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// UpdateReportSchedule is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - cfg
|
||||
func (_e *Repository_Expecter) UpdateReportSchedule(ctx interface{}, cfg interface{}) *Repository_UpdateReportSchedule_Call {
|
||||
return &Repository_UpdateReportSchedule_Call{Call: _e.mock.On("UpdateReportSchedule", ctx, cfg)}
|
||||
}
|
||||
|
||||
func (_c *Repository_UpdateReportSchedule_Call) Run(run func(ctx context.Context, cfg re.ReportConfig)) *Repository_UpdateReportSchedule_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(re.ReportConfig))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_UpdateReportSchedule_Call) Return(reportConfig re.ReportConfig, err error) *Repository_UpdateReportSchedule_Call {
|
||||
_c.Call.Return(reportConfig, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_UpdateReportSchedule_Call) RunAndReturn(run func(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error)) *Repository_UpdateReportSchedule_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// UpdateRule provides a mock function for the type Repository
|
||||
func (_mock *Repository) UpdateRule(ctx context.Context, r re.Rule) (re.Rule, error) {
|
||||
ret := _mock.Called(ctx, r)
|
||||
@@ -308,8 +629,8 @@ func (_c *Repository_UpdateRuleSchedule_Call) RunAndReturn(run func(ctx context.
|
||||
}
|
||||
|
||||
// UpdateRuleStatus provides a mock function for the type Repository
|
||||
func (_mock *Repository) UpdateRuleStatus(ctx context.Context, id string, status re.Status) (re.Rule, error) {
|
||||
ret := _mock.Called(ctx, id, status)
|
||||
func (_mock *Repository) UpdateRuleStatus(ctx context.Context, r re.Rule) (re.Rule, error) {
|
||||
ret := _mock.Called(ctx, r)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateRuleStatus")
|
||||
@@ -317,16 +638,16 @@ func (_mock *Repository) UpdateRuleStatus(ctx context.Context, id string, status
|
||||
|
||||
var r0 re.Rule
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, string, re.Status) (re.Rule, error)); ok {
|
||||
return returnFunc(ctx, id, status)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.Rule) (re.Rule, error)); ok {
|
||||
return returnFunc(ctx, r)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, string, re.Status) re.Rule); ok {
|
||||
r0 = returnFunc(ctx, id, status)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, re.Rule) re.Rule); ok {
|
||||
r0 = returnFunc(ctx, r)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.Rule)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, string, re.Status) error); ok {
|
||||
r1 = returnFunc(ctx, id, status)
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, re.Rule) error); ok {
|
||||
r1 = returnFunc(ctx, r)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -340,15 +661,14 @@ type Repository_UpdateRuleStatus_Call struct {
|
||||
|
||||
// UpdateRuleStatus is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - id
|
||||
// - status
|
||||
func (_e *Repository_Expecter) UpdateRuleStatus(ctx interface{}, id interface{}, status interface{}) *Repository_UpdateRuleStatus_Call {
|
||||
return &Repository_UpdateRuleStatus_Call{Call: _e.mock.On("UpdateRuleStatus", ctx, id, status)}
|
||||
// - r
|
||||
func (_e *Repository_Expecter) UpdateRuleStatus(ctx interface{}, r interface{}) *Repository_UpdateRuleStatus_Call {
|
||||
return &Repository_UpdateRuleStatus_Call{Call: _e.mock.On("UpdateRuleStatus", ctx, r)}
|
||||
}
|
||||
|
||||
func (_c *Repository_UpdateRuleStatus_Call) Run(run func(ctx context.Context, id string, status re.Status)) *Repository_UpdateRuleStatus_Call {
|
||||
func (_c *Repository_UpdateRuleStatus_Call) Run(run func(ctx context.Context, r re.Rule)) *Repository_UpdateRuleStatus_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(string), args[2].(re.Status))
|
||||
run(args[0].(context.Context), args[1].(re.Rule))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
@@ -358,7 +678,62 @@ func (_c *Repository_UpdateRuleStatus_Call) Return(rule re.Rule, err error) *Rep
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_UpdateRuleStatus_Call) RunAndReturn(run func(ctx context.Context, id string, status re.Status) (re.Rule, error)) *Repository_UpdateRuleStatus_Call {
|
||||
func (_c *Repository_UpdateRuleStatus_Call) RunAndReturn(run func(ctx context.Context, r re.Rule) (re.Rule, error)) *Repository_UpdateRuleStatus_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ViewReportConfig provides a mock function for the type Repository
|
||||
func (_mock *Repository) ViewReportConfig(ctx context.Context, id string) (re.ReportConfig, error) {
|
||||
ret := _mock.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ViewReportConfig")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfig
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, string) (re.ReportConfig, error)); ok {
|
||||
return returnFunc(ctx, id)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, string) re.ReportConfig); ok {
|
||||
r0 = returnFunc(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfig)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = returnFunc(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Repository_ViewReportConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ViewReportConfig'
|
||||
type Repository_ViewReportConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ViewReportConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - id
|
||||
func (_e *Repository_Expecter) ViewReportConfig(ctx interface{}, id interface{}) *Repository_ViewReportConfig_Call {
|
||||
return &Repository_ViewReportConfig_Call{Call: _e.mock.On("ViewReportConfig", ctx, id)}
|
||||
}
|
||||
|
||||
func (_c *Repository_ViewReportConfig_Call) Run(run func(ctx context.Context, id string)) *Repository_ViewReportConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_ViewReportConfig_Call) Return(reportConfig re.ReportConfig, err error) *Repository_ViewReportConfig_Call {
|
||||
_c.Call.Return(reportConfig, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Repository_ViewReportConfig_Call) RunAndReturn(run func(ctx context.Context, id string) (re.ReportConfig, error)) *Repository_ViewReportConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
@@ -43,6 +43,62 @@ func (_m *Service) EXPECT() *Service_Expecter {
|
||||
return &Service_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// AddReportConfig provides a mock function for the type Service
|
||||
func (_mock *Service) AddReportConfig(ctx context.Context, session authn.Session, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
ret := _mock.Called(ctx, session, cfg)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for AddReportConfig")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfig
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, re.ReportConfig) (re.ReportConfig, error)); ok {
|
||||
return returnFunc(ctx, session, cfg)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, re.ReportConfig) re.ReportConfig); ok {
|
||||
r0 = returnFunc(ctx, session, cfg)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfig)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, authn.Session, re.ReportConfig) error); ok {
|
||||
r1 = returnFunc(ctx, session, cfg)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Service_AddReportConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddReportConfig'
|
||||
type Service_AddReportConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// AddReportConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - session
|
||||
// - cfg
|
||||
func (_e *Service_Expecter) AddReportConfig(ctx interface{}, session interface{}, cfg interface{}) *Service_AddReportConfig_Call {
|
||||
return &Service_AddReportConfig_Call{Call: _e.mock.On("AddReportConfig", ctx, session, cfg)}
|
||||
}
|
||||
|
||||
func (_c *Service_AddReportConfig_Call) Run(run func(ctx context.Context, session authn.Session, cfg re.ReportConfig)) *Service_AddReportConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(authn.Session), args[2].(re.ReportConfig))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_AddReportConfig_Call) Return(reportConfig re.ReportConfig, err error) *Service_AddReportConfig_Call {
|
||||
_c.Call.Return(reportConfig, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_AddReportConfig_Call) RunAndReturn(run func(ctx context.Context, session authn.Session, cfg re.ReportConfig) (re.ReportConfig, error)) *Service_AddReportConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// AddRule provides a mock function for the type Service
|
||||
func (_mock *Service) AddRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
|
||||
ret := _mock.Called(ctx, session, r)
|
||||
@@ -143,6 +199,62 @@ func (_c *Service_Cancel_Call) RunAndReturn(run func() error) *Service_Cancel_Ca
|
||||
return _c
|
||||
}
|
||||
|
||||
// DisableReportConfig provides a mock function for the type Service
|
||||
func (_mock *Service) DisableReportConfig(ctx context.Context, session authn.Session, id string) (re.ReportConfig, error) {
|
||||
ret := _mock.Called(ctx, session, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DisableReportConfig")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfig
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, string) (re.ReportConfig, error)); ok {
|
||||
return returnFunc(ctx, session, id)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, string) re.ReportConfig); ok {
|
||||
r0 = returnFunc(ctx, session, id)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfig)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, authn.Session, string) error); ok {
|
||||
r1 = returnFunc(ctx, session, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Service_DisableReportConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisableReportConfig'
|
||||
type Service_DisableReportConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// DisableReportConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - session
|
||||
// - id
|
||||
func (_e *Service_Expecter) DisableReportConfig(ctx interface{}, session interface{}, id interface{}) *Service_DisableReportConfig_Call {
|
||||
return &Service_DisableReportConfig_Call{Call: _e.mock.On("DisableReportConfig", ctx, session, id)}
|
||||
}
|
||||
|
||||
func (_c *Service_DisableReportConfig_Call) Run(run func(ctx context.Context, session authn.Session, id string)) *Service_DisableReportConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(authn.Session), args[2].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_DisableReportConfig_Call) Return(reportConfig re.ReportConfig, err error) *Service_DisableReportConfig_Call {
|
||||
_c.Call.Return(reportConfig, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_DisableReportConfig_Call) RunAndReturn(run func(ctx context.Context, session authn.Session, id string) (re.ReportConfig, error)) *Service_DisableReportConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// DisableRule provides a mock function for the type Service
|
||||
func (_mock *Service) DisableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
|
||||
ret := _mock.Called(ctx, session, id)
|
||||
@@ -199,6 +311,62 @@ func (_c *Service_DisableRule_Call) RunAndReturn(run func(ctx context.Context, s
|
||||
return _c
|
||||
}
|
||||
|
||||
// EnableReportConfig provides a mock function for the type Service
|
||||
func (_mock *Service) EnableReportConfig(ctx context.Context, session authn.Session, id string) (re.ReportConfig, error) {
|
||||
ret := _mock.Called(ctx, session, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for EnableReportConfig")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfig
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, string) (re.ReportConfig, error)); ok {
|
||||
return returnFunc(ctx, session, id)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, string) re.ReportConfig); ok {
|
||||
r0 = returnFunc(ctx, session, id)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfig)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, authn.Session, string) error); ok {
|
||||
r1 = returnFunc(ctx, session, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Service_EnableReportConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnableReportConfig'
|
||||
type Service_EnableReportConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// EnableReportConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - session
|
||||
// - id
|
||||
func (_e *Service_Expecter) EnableReportConfig(ctx interface{}, session interface{}, id interface{}) *Service_EnableReportConfig_Call {
|
||||
return &Service_EnableReportConfig_Call{Call: _e.mock.On("EnableReportConfig", ctx, session, id)}
|
||||
}
|
||||
|
||||
func (_c *Service_EnableReportConfig_Call) Run(run func(ctx context.Context, session authn.Session, id string)) *Service_EnableReportConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(authn.Session), args[2].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_EnableReportConfig_Call) Return(reportConfig re.ReportConfig, err error) *Service_EnableReportConfig_Call {
|
||||
_c.Call.Return(reportConfig, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_EnableReportConfig_Call) RunAndReturn(run func(ctx context.Context, session authn.Session, id string) (re.ReportConfig, error)) *Service_EnableReportConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// EnableRule provides a mock function for the type Service
|
||||
func (_mock *Service) EnableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
|
||||
ret := _mock.Called(ctx, session, id)
|
||||
@@ -255,6 +423,63 @@ func (_c *Service_EnableRule_Call) RunAndReturn(run func(ctx context.Context, se
|
||||
return _c
|
||||
}
|
||||
|
||||
// GenerateReport provides a mock function for the type Service
|
||||
func (_mock *Service) GenerateReport(ctx context.Context, session authn.Session, config re.ReportConfig, action re.ReportAction) (re.ReportPage, error) {
|
||||
ret := _mock.Called(ctx, session, config, action)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GenerateReport")
|
||||
}
|
||||
|
||||
var r0 re.ReportPage
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, re.ReportConfig, re.ReportAction) (re.ReportPage, error)); ok {
|
||||
return returnFunc(ctx, session, config, action)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, re.ReportConfig, re.ReportAction) re.ReportPage); ok {
|
||||
r0 = returnFunc(ctx, session, config, action)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportPage)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, authn.Session, re.ReportConfig, re.ReportAction) error); ok {
|
||||
r1 = returnFunc(ctx, session, config, action)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Service_GenerateReport_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GenerateReport'
|
||||
type Service_GenerateReport_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GenerateReport is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - session
|
||||
// - config
|
||||
// - action
|
||||
func (_e *Service_Expecter) GenerateReport(ctx interface{}, session interface{}, config interface{}, action interface{}) *Service_GenerateReport_Call {
|
||||
return &Service_GenerateReport_Call{Call: _e.mock.On("GenerateReport", ctx, session, config, action)}
|
||||
}
|
||||
|
||||
func (_c *Service_GenerateReport_Call) Run(run func(ctx context.Context, session authn.Session, config re.ReportConfig, action re.ReportAction)) *Service_GenerateReport_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(authn.Session), args[2].(re.ReportConfig), args[3].(re.ReportAction))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_GenerateReport_Call) Return(reportPage re.ReportPage, err error) *Service_GenerateReport_Call {
|
||||
_c.Call.Return(reportPage, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_GenerateReport_Call) RunAndReturn(run func(ctx context.Context, session authn.Session, config re.ReportConfig, action re.ReportAction) (re.ReportPage, error)) *Service_GenerateReport_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Handle provides a mock function for the type Service
|
||||
func (_mock *Service) Handle(msg *messaging.Message) error {
|
||||
ret := _mock.Called(msg)
|
||||
@@ -300,6 +525,62 @@ func (_c *Service_Handle_Call) RunAndReturn(run func(msg *messaging.Message) err
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListReportsConfig provides a mock function for the type Service
|
||||
func (_mock *Service) ListReportsConfig(ctx context.Context, session authn.Session, pm re.PageMeta) (re.ReportConfigPage, error) {
|
||||
ret := _mock.Called(ctx, session, pm)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListReportsConfig")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfigPage
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, re.PageMeta) (re.ReportConfigPage, error)); ok {
|
||||
return returnFunc(ctx, session, pm)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, re.PageMeta) re.ReportConfigPage); ok {
|
||||
r0 = returnFunc(ctx, session, pm)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfigPage)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, authn.Session, re.PageMeta) error); ok {
|
||||
r1 = returnFunc(ctx, session, pm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Service_ListReportsConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListReportsConfig'
|
||||
type Service_ListReportsConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListReportsConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - session
|
||||
// - pm
|
||||
func (_e *Service_Expecter) ListReportsConfig(ctx interface{}, session interface{}, pm interface{}) *Service_ListReportsConfig_Call {
|
||||
return &Service_ListReportsConfig_Call{Call: _e.mock.On("ListReportsConfig", ctx, session, pm)}
|
||||
}
|
||||
|
||||
func (_c *Service_ListReportsConfig_Call) Run(run func(ctx context.Context, session authn.Session, pm re.PageMeta)) *Service_ListReportsConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(authn.Session), args[2].(re.PageMeta))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_ListReportsConfig_Call) Return(reportConfigPage re.ReportConfigPage, err error) *Service_ListReportsConfig_Call {
|
||||
_c.Call.Return(reportConfigPage, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_ListReportsConfig_Call) RunAndReturn(run func(ctx context.Context, session authn.Session, pm re.PageMeta) (re.ReportConfigPage, error)) *Service_ListReportsConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListRules provides a mock function for the type Service
|
||||
func (_mock *Service) ListRules(ctx context.Context, session authn.Session, pm re.PageMeta) (re.Page, error) {
|
||||
ret := _mock.Called(ctx, session, pm)
|
||||
@@ -356,6 +637,53 @@ func (_c *Service_ListRules_Call) RunAndReturn(run func(ctx context.Context, ses
|
||||
return _c
|
||||
}
|
||||
|
||||
// RemoveReportConfig provides a mock function for the type Service
|
||||
func (_mock *Service) RemoveReportConfig(ctx context.Context, session authn.Session, id string) error {
|
||||
ret := _mock.Called(ctx, session, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RemoveReportConfig")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, string) error); ok {
|
||||
r0 = returnFunc(ctx, session, id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Service_RemoveReportConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveReportConfig'
|
||||
type Service_RemoveReportConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// RemoveReportConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - session
|
||||
// - id
|
||||
func (_e *Service_Expecter) RemoveReportConfig(ctx interface{}, session interface{}, id interface{}) *Service_RemoveReportConfig_Call {
|
||||
return &Service_RemoveReportConfig_Call{Call: _e.mock.On("RemoveReportConfig", ctx, session, id)}
|
||||
}
|
||||
|
||||
func (_c *Service_RemoveReportConfig_Call) Run(run func(ctx context.Context, session authn.Session, id string)) *Service_RemoveReportConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(authn.Session), args[2].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_RemoveReportConfig_Call) Return(err error) *Service_RemoveReportConfig_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_RemoveReportConfig_Call) RunAndReturn(run func(ctx context.Context, session authn.Session, id string) error) *Service_RemoveReportConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// RemoveRule provides a mock function for the type Service
|
||||
func (_mock *Service) RemoveRule(ctx context.Context, session authn.Session, id string) error {
|
||||
ret := _mock.Called(ctx, session, id)
|
||||
@@ -448,6 +776,118 @@ func (_c *Service_StartScheduler_Call) RunAndReturn(run func(ctx context.Context
|
||||
return _c
|
||||
}
|
||||
|
||||
// UpdateReportConfig provides a mock function for the type Service
|
||||
func (_mock *Service) UpdateReportConfig(ctx context.Context, session authn.Session, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
ret := _mock.Called(ctx, session, cfg)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateReportConfig")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfig
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, re.ReportConfig) (re.ReportConfig, error)); ok {
|
||||
return returnFunc(ctx, session, cfg)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, re.ReportConfig) re.ReportConfig); ok {
|
||||
r0 = returnFunc(ctx, session, cfg)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfig)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, authn.Session, re.ReportConfig) error); ok {
|
||||
r1 = returnFunc(ctx, session, cfg)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Service_UpdateReportConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateReportConfig'
|
||||
type Service_UpdateReportConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// UpdateReportConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - session
|
||||
// - cfg
|
||||
func (_e *Service_Expecter) UpdateReportConfig(ctx interface{}, session interface{}, cfg interface{}) *Service_UpdateReportConfig_Call {
|
||||
return &Service_UpdateReportConfig_Call{Call: _e.mock.On("UpdateReportConfig", ctx, session, cfg)}
|
||||
}
|
||||
|
||||
func (_c *Service_UpdateReportConfig_Call) Run(run func(ctx context.Context, session authn.Session, cfg re.ReportConfig)) *Service_UpdateReportConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(authn.Session), args[2].(re.ReportConfig))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_UpdateReportConfig_Call) Return(reportConfig re.ReportConfig, err error) *Service_UpdateReportConfig_Call {
|
||||
_c.Call.Return(reportConfig, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_UpdateReportConfig_Call) RunAndReturn(run func(ctx context.Context, session authn.Session, cfg re.ReportConfig) (re.ReportConfig, error)) *Service_UpdateReportConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// UpdateReportSchedule provides a mock function for the type Service
|
||||
func (_mock *Service) UpdateReportSchedule(ctx context.Context, session authn.Session, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
ret := _mock.Called(ctx, session, cfg)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateReportSchedule")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfig
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, re.ReportConfig) (re.ReportConfig, error)); ok {
|
||||
return returnFunc(ctx, session, cfg)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, re.ReportConfig) re.ReportConfig); ok {
|
||||
r0 = returnFunc(ctx, session, cfg)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfig)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, authn.Session, re.ReportConfig) error); ok {
|
||||
r1 = returnFunc(ctx, session, cfg)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Service_UpdateReportSchedule_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateReportSchedule'
|
||||
type Service_UpdateReportSchedule_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// UpdateReportSchedule is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - session
|
||||
// - cfg
|
||||
func (_e *Service_Expecter) UpdateReportSchedule(ctx interface{}, session interface{}, cfg interface{}) *Service_UpdateReportSchedule_Call {
|
||||
return &Service_UpdateReportSchedule_Call{Call: _e.mock.On("UpdateReportSchedule", ctx, session, cfg)}
|
||||
}
|
||||
|
||||
func (_c *Service_UpdateReportSchedule_Call) Run(run func(ctx context.Context, session authn.Session, cfg re.ReportConfig)) *Service_UpdateReportSchedule_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(authn.Session), args[2].(re.ReportConfig))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_UpdateReportSchedule_Call) Return(reportConfig re.ReportConfig, err error) *Service_UpdateReportSchedule_Call {
|
||||
_c.Call.Return(reportConfig, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_UpdateReportSchedule_Call) RunAndReturn(run func(ctx context.Context, session authn.Session, cfg re.ReportConfig) (re.ReportConfig, error)) *Service_UpdateReportSchedule_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// UpdateRule provides a mock function for the type Service
|
||||
func (_mock *Service) UpdateRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
|
||||
ret := _mock.Called(ctx, session, r)
|
||||
@@ -560,6 +1000,62 @@ func (_c *Service_UpdateRuleSchedule_Call) RunAndReturn(run func(ctx context.Con
|
||||
return _c
|
||||
}
|
||||
|
||||
// ViewReportConfig provides a mock function for the type Service
|
||||
func (_mock *Service) ViewReportConfig(ctx context.Context, session authn.Session, id string) (re.ReportConfig, error) {
|
||||
ret := _mock.Called(ctx, session, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ViewReportConfig")
|
||||
}
|
||||
|
||||
var r0 re.ReportConfig
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, string) (re.ReportConfig, error)); ok {
|
||||
return returnFunc(ctx, session, id)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authn.Session, string) re.ReportConfig); ok {
|
||||
r0 = returnFunc(ctx, session, id)
|
||||
} else {
|
||||
r0 = ret.Get(0).(re.ReportConfig)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, authn.Session, string) error); ok {
|
||||
r1 = returnFunc(ctx, session, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Service_ViewReportConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ViewReportConfig'
|
||||
type Service_ViewReportConfig_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ViewReportConfig is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - session
|
||||
// - id
|
||||
func (_e *Service_Expecter) ViewReportConfig(ctx interface{}, session interface{}, id interface{}) *Service_ViewReportConfig_Call {
|
||||
return &Service_ViewReportConfig_Call{Call: _e.mock.On("ViewReportConfig", ctx, session, id)}
|
||||
}
|
||||
|
||||
func (_c *Service_ViewReportConfig_Call) Run(run func(ctx context.Context, session authn.Session, id string)) *Service_ViewReportConfig_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(authn.Session), args[2].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_ViewReportConfig_Call) Return(reportConfig re.ReportConfig, err error) *Service_ViewReportConfig_Call {
|
||||
_c.Call.Return(reportConfig, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Service_ViewReportConfig_Call) RunAndReturn(run func(ctx context.Context, session authn.Session, id string) (re.ReportConfig, error)) *Service_ViewReportConfig_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ViewRule provides a mock function for the type Service
|
||||
func (_mock *Service) ViewRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
|
||||
ret := _mock.Called(ctx, session, id)
|
||||
|
||||
@@ -43,6 +43,32 @@ func Migration() *migrate.MemoryMigrationSource {
|
||||
`DROP TABLE IF EXISTS rules`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "rules_02",
|
||||
Up: []string{
|
||||
`CREATE TABLE IF NOT EXISTS report_config (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(1024),
|
||||
description TEXT,
|
||||
domain_id VARCHAR(36) NOT NULL,
|
||||
status SMALLINT NOT NULL DEFAULT 0 CHECK (status >= 0),
|
||||
created_at TIMESTAMP,
|
||||
created_by VARCHAR(254),
|
||||
updated_at TIMESTAMP,
|
||||
updated_by VARCHAR(254),
|
||||
time TIMESTAMP,
|
||||
recurring SMALLINT,
|
||||
recurring_period SMALLINT,
|
||||
start_datetime TIMESTAMP,
|
||||
config JSONB,
|
||||
email JSONB,
|
||||
metrics JSONB
|
||||
);`,
|
||||
},
|
||||
Down: []string{
|
||||
`DROP TABLE IF EXISTS report_config;`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+305
-22
@@ -33,7 +33,7 @@ func (repo *PostgresRepository) AddRule(ctx context.Context, r re.Rule) (re.Rule
|
||||
`
|
||||
dbr, err := ruleToDb(r)
|
||||
if err != nil {
|
||||
return re.Rule{}, errors.Wrap(repoerr.ErrCreateEntity, err)
|
||||
return re.Rule{}, err
|
||||
}
|
||||
row, err := repo.DB.NamedQueryContext(ctx, q, dbr)
|
||||
if err != nil {
|
||||
@@ -79,30 +79,39 @@ func (repo *PostgresRepository) ViewRule(ctx context.Context, id string) (re.Rul
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (repo *PostgresRepository) UpdateRuleStatus(ctx context.Context, id string, status re.Status) (re.Rule, error) {
|
||||
q := `
|
||||
UPDATE rules
|
||||
SET status = $2
|
||||
WHERE id = $1
|
||||
RETURNING id, name, domain_id, metadata, input_channel, input_topic, logic_type, logic_output, logic_value,
|
||||
output_channel, output_topic, start_datetime, time, recurring, recurring_period, created_at, created_by, updated_at, updated_by, status;
|
||||
`
|
||||
row := repo.DB.QueryRowxContext(ctx, q, id, status)
|
||||
if err := row.Err(); err != nil {
|
||||
return re.Rule{}, postgres.HandleError(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
func (repo *PostgresRepository) UpdateRuleStatus(ctx context.Context, r re.Rule) (re.Rule, error) {
|
||||
q := `UPDATE rules
|
||||
SET status = :status, updated_at = :updated_at, updated_by = :updated_by
|
||||
WHERE id = :id
|
||||
RETURNING id, name, domain_id, metadata, input_channel, input_topic, logic_type, logic_output, logic_value,
|
||||
output_channel, output_topic, start_datetime, time, recurring, recurring_period, created_at, created_by, updated_at, updated_by, status;`
|
||||
|
||||
var dbr dbRule
|
||||
if err := row.StructScan(&dbr); err != nil {
|
||||
return re.Rule{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
rule, err := dbToRule(dbr)
|
||||
dbr, err := ruleToDb(r)
|
||||
if err != nil {
|
||||
return re.Rule{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
return rule, nil
|
||||
row, err := repo.DB.NamedQueryContext(ctx, q, dbr)
|
||||
if err != nil {
|
||||
return re.Rule{}, postgres.HandleError(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
var res dbRule
|
||||
if row.Next() {
|
||||
if err := row.StructScan(&res); err != nil {
|
||||
return re.Rule{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
rule, err := dbToRule(res)
|
||||
if err != nil {
|
||||
return re.Rule{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
return re.Rule{}, repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
func (repo *PostgresRepository) UpdateRule(ctx context.Context, r re.Rule) (re.Rule, error) {
|
||||
@@ -229,7 +238,7 @@ func (repo *PostgresRepository) ListRules(ctx context.Context, pm re.PageMeta) (
|
||||
if pm.Offset != 0 {
|
||||
pgData += " OFFSET :offset"
|
||||
}
|
||||
pq := pageQuery(pm)
|
||||
pq := pageRulesQuery(pm)
|
||||
q := fmt.Sprintf(`
|
||||
SELECT id, name, domain_id, input_channel, input_topic, logic_type, logic_output, logic_value, output_channel,
|
||||
output_topic, start_datetime, time, recurring, recurring_period, created_at, created_by, updated_at, updated_by, status
|
||||
@@ -271,7 +280,7 @@ func (repo *PostgresRepository) ListRules(ctx context.Context, pm re.PageMeta) (
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func pageQuery(pm re.PageMeta) string {
|
||||
func pageRulesQuery(pm re.PageMeta) string {
|
||||
var query []string
|
||||
if pm.InputChannel != "" {
|
||||
query = append(query, "r.input_channel = :input_channel")
|
||||
@@ -297,3 +306,277 @@ func pageQuery(pm re.PageMeta) string {
|
||||
|
||||
return q
|
||||
}
|
||||
|
||||
func (repo *PostgresRepository) AddReportConfig(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
q := `
|
||||
INSERT INTO report_config (id, name, description, domain_id, config, metrics,
|
||||
email, start_datetime, time, recurring, recurring_period, created_at, created_by, updated_at, updated_by, status)
|
||||
VALUES (:id, :name, :description, :domain_id, :config, :metrics,
|
||||
:email, :start_datetime, :time, :recurring, :recurring_period, :created_at, :created_by, :updated_at, :updated_by, :status)
|
||||
RETURNING id, name, description, domain_id, config, metrics,
|
||||
email, start_datetime, time, recurring, recurring_period, created_at, created_by, updated_at, updated_by, status;
|
||||
`
|
||||
dbr, err := reportToDb(cfg)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
row, err := repo.DB.NamedQueryContext(ctx, q, dbr)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
var dbReport dbReport
|
||||
if row.Next() {
|
||||
if err := row.StructScan(&dbReport); err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
}
|
||||
|
||||
report, err := dbToReport(dbReport)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
|
||||
return report, nil
|
||||
}
|
||||
|
||||
func (repo *PostgresRepository) ViewReportConfig(ctx context.Context, id string) (re.ReportConfig, error) {
|
||||
q := `
|
||||
SELECT id, name, description, domain_id, config, metrics,
|
||||
email, start_datetime, time, recurring, recurring_period, created_at, created_by, updated_at, updated_by, status
|
||||
FROM report_config
|
||||
WHERE id = $1;
|
||||
`
|
||||
row := repo.DB.QueryRowxContext(ctx, q, id)
|
||||
if err := row.Err(); err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
var dbr dbReport
|
||||
if err := row.StructScan(&dbr); err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
rpt, err := dbToReport(dbr)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
|
||||
return rpt, nil
|
||||
}
|
||||
|
||||
func (repo *PostgresRepository) UpdateReportConfigStatus(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
q := `UPDATE report_config SET status = :status, updated_at = :updated_at, updated_by = :updated_by
|
||||
WHERE id = :id
|
||||
RETURNING id, name, description, domain_id, metrics, email, config,
|
||||
start_datetime, time, recurring, recurring_period, created_at, created_by, updated_at, updated_by, status;`
|
||||
|
||||
dbRpt, err := reportToDb(cfg)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
row, err := repo.DB.NamedQueryContext(ctx, q, dbRpt)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, postgres.HandleError(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
dbr := dbReport{}
|
||||
if row.Next() {
|
||||
if err := row.StructScan(&dbr); err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
|
||||
res, err := dbToReport(dbr)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
return re.ReportConfig{}, repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
func (repo *PostgresRepository) UpdateReportConfig(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
var query []string
|
||||
|
||||
if cfg.Name != "" {
|
||||
query = append(query, "name = :name")
|
||||
}
|
||||
|
||||
if cfg.Description != "" {
|
||||
query = append(query, "description = :description")
|
||||
}
|
||||
|
||||
if len(cfg.Metrics) > 0 {
|
||||
query = append(query, "metrics = :metrics")
|
||||
}
|
||||
|
||||
if cfg.Email != nil {
|
||||
query = append(query, "email = :email")
|
||||
}
|
||||
|
||||
if cfg.Config != nil {
|
||||
query = append(query, "config = :config")
|
||||
}
|
||||
|
||||
var q string
|
||||
if len(query) > 0 {
|
||||
q = fmt.Sprintf("%s", strings.Join(query, ", "))
|
||||
}
|
||||
|
||||
q = fmt.Sprintf(`
|
||||
UPDATE report_config
|
||||
SET %s,
|
||||
updated_at = :updated_at, updated_by = :updated_by
|
||||
WHERE id = :id
|
||||
RETURNING id, name, description, domain_id, config, metrics,
|
||||
email, start_datetime, time, recurring, recurring_period, created_at, created_by, updated_at, updated_by, status;
|
||||
`, q)
|
||||
|
||||
dbr, err := reportToDb(cfg)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
row, err := repo.DB.NamedQueryContext(ctx, q, dbr)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
var dbReport dbReport
|
||||
if row.Next() {
|
||||
if err := row.StructScan(&dbReport); err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
}
|
||||
rpt, err := dbToReport(dbReport)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, err
|
||||
}
|
||||
|
||||
return rpt, nil
|
||||
}
|
||||
|
||||
func (repo *PostgresRepository) UpdateReportSchedule(ctx context.Context, cfg re.ReportConfig) (re.ReportConfig, error) {
|
||||
q := `
|
||||
UPDATE report_config
|
||||
SET start_datetime = :start_datetime, time = :time, recurring = :recurring,
|
||||
recurring_period = :recurring_period, updated_at = :updated_at, updated_by = :updated_by WHERE id = :id
|
||||
RETURNING id, name, description, domain_id, config, metrics,
|
||||
email, start_datetime, time, recurring, recurring_period, created_at, created_by, updated_at, updated_by, status;
|
||||
`
|
||||
|
||||
dbr, err := reportToDb(cfg)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
row, err := repo.DB.NamedQueryContext(ctx, q, dbr)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, postgres.HandleError(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
var dbReport dbReport
|
||||
if row.Next() {
|
||||
if err := row.StructScan(&dbReport); err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
}
|
||||
report, err := dbToReport(dbReport)
|
||||
if err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
return report, nil
|
||||
}
|
||||
|
||||
func (repo *PostgresRepository) RemoveReportConfig(ctx context.Context, id string) error {
|
||||
q := `
|
||||
DELETE FROM report_config
|
||||
WHERE id = $1;
|
||||
`
|
||||
|
||||
result, err := repo.DB.ExecContext(ctx, q, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := result.RowsAffected(); err != nil {
|
||||
return repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *PostgresRepository) ListReportsConfig(ctx context.Context, pm re.PageMeta) (re.ReportConfigPage, error) {
|
||||
listReportsQuery := `
|
||||
SELECT id, name, description, domain_id, metrics, email, config,
|
||||
start_datetime, time, recurring, recurring_period, created_at, created_by, updated_at, updated_by, status
|
||||
FROM report_config rc %s %s;
|
||||
`
|
||||
|
||||
pgData := ""
|
||||
if pm.Limit != 0 {
|
||||
pgData = "LIMIT :limit"
|
||||
}
|
||||
if pm.Offset != 0 {
|
||||
pgData += " OFFSET :offset"
|
||||
}
|
||||
pq := pageReportQuery(pm)
|
||||
q := fmt.Sprintf(listReportsQuery, pq, pgData)
|
||||
rows, err := repo.DB.NamedQueryContext(ctx, q, pm)
|
||||
if err != nil {
|
||||
return re.ReportConfigPage{}, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
cfgs := []re.ReportConfig{}
|
||||
for rows.Next() {
|
||||
var r dbReport
|
||||
if err := rows.StructScan(&r); err != nil {
|
||||
return re.ReportConfigPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
rpt, err := dbToReport(r)
|
||||
if err != nil {
|
||||
return re.ReportConfigPage{}, err
|
||||
}
|
||||
cfgs = append(cfgs, rpt)
|
||||
}
|
||||
|
||||
cq := fmt.Sprintf(`SELECT COUNT(*) FROM report_config rc %s;`, pq)
|
||||
|
||||
total, err := postgres.Total(ctx, repo.DB, cq, pm)
|
||||
if err != nil {
|
||||
return re.ReportConfigPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
pm.Total = total
|
||||
ret := re.ReportConfigPage{
|
||||
PageMeta: pm,
|
||||
ReportConfigs: cfgs,
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func pageReportQuery(pm re.PageMeta) string {
|
||||
var query []string
|
||||
if pm.Status != re.AllStatus {
|
||||
query = append(query, "rc.status = :status")
|
||||
}
|
||||
|
||||
if pm.Domain != "" {
|
||||
query = append(query, "rc.domain_id = :domain_id")
|
||||
}
|
||||
|
||||
if pm.Name != "" {
|
||||
query = append(query, "rc.name ILIKE '%' || :name || '%'")
|
||||
}
|
||||
|
||||
var q string
|
||||
if len(query) > 0 {
|
||||
q = fmt.Sprintf("WHERE %s", strings.Join(query, " AND "))
|
||||
}
|
||||
|
||||
return q
|
||||
}
|
||||
|
||||
+115
-1
@@ -37,6 +37,26 @@ type dbRule struct {
|
||||
UpdatedBy string `db:"updated_by"`
|
||||
}
|
||||
|
||||
// dbReport represents the database structure for a Report.
|
||||
type dbReport struct {
|
||||
ID string `db:"id"`
|
||||
Name string `db:"name"`
|
||||
Description string `db:"description"`
|
||||
DomainID string `db:"domain_id"`
|
||||
StartDateTime time.Time `db:"start_datetime"`
|
||||
Time time.Time `db:"time"`
|
||||
Recurring re.Recurring `db:"recurring"`
|
||||
RecurringPeriod uint `db:"recurring_period"`
|
||||
Status re.Status `db:"status"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
CreatedBy string `db:"created_by"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
UpdatedBy string `db:"updated_by"`
|
||||
Config []byte `db:"config,omitempty"`
|
||||
Metrics []byte `db:"metrics"`
|
||||
Email []byte `db:"email"`
|
||||
}
|
||||
|
||||
func ruleToDb(r re.Rule) (dbRule, error) {
|
||||
metadata := []byte("{}")
|
||||
if len(r.Metadata) > 0 {
|
||||
@@ -105,7 +125,7 @@ func dbToRule(dto dbRule) (re.Rule, error) {
|
||||
Recurring: dto.Recurring,
|
||||
RecurringPeriod: dto.RecurringPeriod,
|
||||
},
|
||||
Status: re.Status(dto.Status),
|
||||
Status: dto.Status,
|
||||
CreatedAt: dto.CreatedAt,
|
||||
CreatedBy: dto.CreatedBy,
|
||||
UpdatedAt: dto.UpdatedAt,
|
||||
@@ -113,6 +133,100 @@ func dbToRule(dto dbRule) (re.Rule, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func reportToDb(r re.ReportConfig) (dbReport, error) {
|
||||
config := []byte("{}")
|
||||
if r.Config != nil {
|
||||
b, err := json.Marshal(r.Config)
|
||||
if err != nil {
|
||||
return dbReport{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
config = b
|
||||
}
|
||||
|
||||
metrics := []byte("{}")
|
||||
if r.Metrics != nil {
|
||||
m, err := json.Marshal(r.Metrics)
|
||||
if err != nil {
|
||||
return dbReport{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
metrics = m
|
||||
}
|
||||
|
||||
email := []byte("{}")
|
||||
if r.Email != nil {
|
||||
e, err := json.Marshal(r.Email)
|
||||
if err != nil {
|
||||
return dbReport{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
email = e
|
||||
}
|
||||
|
||||
return dbReport{
|
||||
ID: r.ID,
|
||||
Name: r.Name,
|
||||
Description: r.Description,
|
||||
DomainID: r.DomainID,
|
||||
StartDateTime: r.Schedule.StartDateTime,
|
||||
Time: r.Schedule.Time,
|
||||
Recurring: r.Schedule.Recurring,
|
||||
RecurringPeriod: r.Schedule.RecurringPeriod,
|
||||
Status: r.Status,
|
||||
CreatedAt: r.CreatedAt,
|
||||
CreatedBy: r.CreatedBy,
|
||||
UpdatedAt: r.UpdatedAt,
|
||||
UpdatedBy: r.UpdatedBy,
|
||||
Config: config,
|
||||
Metrics: metrics,
|
||||
Email: email,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func dbToReport(dto dbReport) (re.ReportConfig, error) {
|
||||
var config re.MetricConfig
|
||||
if dto.Config != nil {
|
||||
if err := json.Unmarshal(dto.Config, &config); err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
|
||||
var email re.EmailSetting
|
||||
if dto.Email != nil {
|
||||
if err := json.Unmarshal(dto.Email, &email); err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
|
||||
var metrics []re.Metric
|
||||
if dto.Metrics != nil {
|
||||
if err := json.Unmarshal(dto.Metrics, &metrics); err != nil {
|
||||
return re.ReportConfig{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
|
||||
rpt := re.ReportConfig{
|
||||
ID: dto.ID,
|
||||
Name: dto.Name,
|
||||
Description: dto.Description,
|
||||
DomainID: dto.DomainID,
|
||||
Config: &config,
|
||||
Metrics: metrics,
|
||||
Schedule: re.Schedule{
|
||||
StartDateTime: dto.StartDateTime,
|
||||
Time: dto.Time,
|
||||
Recurring: dto.Recurring,
|
||||
RecurringPeriod: dto.RecurringPeriod,
|
||||
},
|
||||
Email: &email,
|
||||
Status: dto.Status,
|
||||
CreatedAt: dto.CreatedAt,
|
||||
CreatedBy: dto.CreatedBy,
|
||||
UpdatedAt: dto.UpdatedAt,
|
||||
UpdatedBy: dto.UpdatedBy,
|
||||
}
|
||||
|
||||
return rpt, nil
|
||||
}
|
||||
|
||||
func toNullString(value string) sql.NullString {
|
||||
if value == "" {
|
||||
return sql.NullString{Valid: false}
|
||||
|
||||
+360
@@ -0,0 +1,360 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package re
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/reltime"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/transformers/senml"
|
||||
)
|
||||
|
||||
var (
|
||||
errFromTimeNotProvided = errors.New("\"from time\" not provided")
|
||||
errInvalidFromTime = errors.New("invalid \"from time\" ")
|
||||
errToTimeNotProvided = errors.New("\"to time\" not provided")
|
||||
errInvalidToTime = errors.New("invalid \"to time\"")
|
||||
errAggIntervalTimeNotProvided = errors.New("aggregation interval time not provided")
|
||||
errInvalidAggInterval = errors.New("invalid aggregation interval time")
|
||||
errNoToEmail = errors.New("no \"To\" email address found")
|
||||
errChannelIDNotProvided = errors.New("channel id not provided")
|
||||
errClientIDNotProvided = errors.New("client id not provided")
|
||||
errNameNotProvided = errors.New("name not provided")
|
||||
)
|
||||
|
||||
const (
|
||||
errInvalidFormatFmt = "invalid format %s"
|
||||
errInvalidReportActionFmt = "invalid action %s"
|
||||
errInvalidToEmail = "invalid \"To\" email %s"
|
||||
|
||||
errUnknownAggregationFmt = "unknown aggregation type %d"
|
||||
errUnknownAggregationStringFmt = "unknown aggregation type %s"
|
||||
)
|
||||
|
||||
type Report struct {
|
||||
Metric Metric `json:"metric,omitempty"`
|
||||
Messages []senml.Message `json:"messages,omitempty"`
|
||||
}
|
||||
|
||||
type ReportPage struct {
|
||||
Total uint64 `json:"total"`
|
||||
From time.Time `json:"from,omitempty"`
|
||||
To time.Time `json:"to,omitempty"`
|
||||
Aggregation AggConfig `json:"aggregation,omitempty"`
|
||||
Reports []Report `json:"reports,omitempty"`
|
||||
File ReportFile `json:"file,omitempty"`
|
||||
}
|
||||
|
||||
type ReportFile struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Data []byte `json:"data,omitempty"`
|
||||
Format Format `json:"format,omitempty"`
|
||||
}
|
||||
|
||||
type AggConfig struct {
|
||||
AggType Aggregation `json:"agg_type,omitempty"` // Optional field
|
||||
Interval string `json:"interval,omitempty"` // Mandatory field if "AggType" field is set MAX, MIN, COUNT, SUM, AVG
|
||||
}
|
||||
|
||||
func (ac AggConfig) Validate() error {
|
||||
if ac.AggType != AggregationNONE {
|
||||
if ac.Interval == "" {
|
||||
return errAggIntervalTimeNotProvided
|
||||
}
|
||||
|
||||
if _, err := time.ParseDuration(ac.Interval); err != nil {
|
||||
return errInvalidAggInterval
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type MetricConfig struct {
|
||||
From string `json:"from,omitempty"` // Mandatory field
|
||||
To string `json:"to,omitempty"` // Mandatory field
|
||||
|
||||
FileFormat Format `json:"file_format,omitempty"` // Optional field
|
||||
|
||||
Aggregation AggConfig `json:"aggregation,omitempty"` // Optional field
|
||||
}
|
||||
|
||||
func (mc MetricConfig) Validate() error {
|
||||
if mc.From == "" {
|
||||
return errFromTimeNotProvided
|
||||
}
|
||||
|
||||
if _, err := reltime.Parse(mc.From); err != nil {
|
||||
return errInvalidFromTime
|
||||
}
|
||||
|
||||
if mc.To == "" {
|
||||
return errToTimeNotProvided
|
||||
}
|
||||
|
||||
if _, err := reltime.Parse(mc.To); err != nil {
|
||||
return errInvalidToTime
|
||||
}
|
||||
if err := mc.Aggregation.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Metric struct {
|
||||
ChannelID string `json:"channel_id,omitempty"` // Mandatory field
|
||||
ClientID string `json:"client_id,omitempty"` // Mandatory field
|
||||
Name string `json:"name,omitempty"` // Mandatory field
|
||||
Subtopic string `json:"subtopic,omitempty"` // Optional field
|
||||
Protocol string `json:"protocol,omitempty"` // Optional field
|
||||
Format string `json:"format,omitiempty"` // Optional field
|
||||
}
|
||||
|
||||
func (m Metric) Validate() error {
|
||||
if m.ChannelID == "" {
|
||||
return errChannelIDNotProvided
|
||||
}
|
||||
if m.ClientID == "" {
|
||||
return errClientIDNotProvided
|
||||
}
|
||||
if m.Name == "" {
|
||||
return errNameNotProvided
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReportConfig struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
DomainID string `json:"domain_id"`
|
||||
Schedule Schedule `json:"schedule,omitempty"`
|
||||
Config *MetricConfig `json:"config,omitempty"`
|
||||
Email *EmailSetting `json:"email,omitempty"`
|
||||
Metrics []Metric `json:"metrics,omitempty"`
|
||||
Status Status `json:"status"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
CreatedBy string `json:"created_by,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
}
|
||||
|
||||
type ReportConfigPage struct {
|
||||
PageMeta
|
||||
ReportConfigs []ReportConfig `json:"report_configs"`
|
||||
}
|
||||
|
||||
type EmailSetting struct {
|
||||
To []string `json:"to,omitempty"`
|
||||
Subject string `json:"subject,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
}
|
||||
|
||||
func (es *EmailSetting) Validate() error {
|
||||
if len(es.To) == 0 {
|
||||
return errNoToEmail
|
||||
}
|
||||
for _, to := range es.To {
|
||||
if _, err := mail.ParseAddress(to); err != nil {
|
||||
return errors.Wrap(fmt.Errorf(errInvalidToEmail, to), err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Format uint8
|
||||
|
||||
const (
|
||||
PDF = iota
|
||||
CSV
|
||||
AllFormats
|
||||
)
|
||||
|
||||
const (
|
||||
PdfFormat = "pdf"
|
||||
CsvFormat = "csv"
|
||||
All_Formats = "AllFormats"
|
||||
)
|
||||
|
||||
func (f Format) String() string {
|
||||
switch f {
|
||||
case PDF:
|
||||
return PdfFormat
|
||||
case CSV:
|
||||
return CsvFormat
|
||||
case AllFormats:
|
||||
return All_Formats
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
func (f Format) Extension() string {
|
||||
switch f {
|
||||
case PDF:
|
||||
return PdfFormat
|
||||
case CSV:
|
||||
return CsvFormat
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
func (f Format) ContentType() string {
|
||||
switch f {
|
||||
case PDF:
|
||||
return "application/pdf"
|
||||
case CSV:
|
||||
return "text/csv"
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
func ToFormat(format string) (Format, error) {
|
||||
switch format {
|
||||
case "", PdfFormat:
|
||||
return PDF, nil
|
||||
case CsvFormat:
|
||||
return CSV, nil
|
||||
case All_Formats:
|
||||
return AllFormats, nil
|
||||
}
|
||||
return Format(0), fmt.Errorf(errInvalidFormatFmt, format)
|
||||
}
|
||||
|
||||
func (f Format) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(f.String())
|
||||
}
|
||||
|
||||
func (f *Format) UnmarshalJSON(data []byte) error {
|
||||
str := strings.Trim(string(data), "\"")
|
||||
val, err := ToFormat(str)
|
||||
*f = val
|
||||
return err
|
||||
}
|
||||
|
||||
type ReportAction uint8
|
||||
|
||||
const (
|
||||
ViewReport = iota
|
||||
DownloadReport
|
||||
EmailReport
|
||||
)
|
||||
|
||||
const (
|
||||
ViewReportAction = "view"
|
||||
DownloadReportAction = "download"
|
||||
EmailReportAction = "email"
|
||||
)
|
||||
|
||||
func (ra ReportAction) String() string {
|
||||
switch ra {
|
||||
case ViewReport:
|
||||
return ViewReportAction
|
||||
case DownloadReport:
|
||||
return DownloadReportAction
|
||||
case EmailReport:
|
||||
return EmailReportAction
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
func ToReportAction(action string) (ReportAction, error) {
|
||||
switch action {
|
||||
case "", ViewReportAction:
|
||||
return ViewReport, nil
|
||||
case DownloadReportAction:
|
||||
return DownloadReport, nil
|
||||
case EmailReportAction:
|
||||
return EmailReport, nil
|
||||
}
|
||||
return ReportAction(0), fmt.Errorf(errInvalidReportActionFmt, action)
|
||||
}
|
||||
|
||||
func (ra ReportAction) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(ra.String())
|
||||
}
|
||||
|
||||
func (ra *ReportAction) UnmarshalJSON(data []byte) error {
|
||||
str := strings.Trim(string(data), "\"")
|
||||
val, err := ToReportAction(str)
|
||||
*ra = val
|
||||
return err
|
||||
}
|
||||
|
||||
type Aggregation uint8
|
||||
|
||||
const (
|
||||
AggregationNONE = iota
|
||||
AggregationMAX
|
||||
AggregationMIN
|
||||
AggregationSUM
|
||||
AggregationCOUNT
|
||||
AggregationAVG
|
||||
)
|
||||
|
||||
const (
|
||||
aggregationNONE = "none"
|
||||
aggregationMAX = "max"
|
||||
aggregationMIN = "min"
|
||||
aggregationSUM = "sum"
|
||||
aggregationCOUNT = "count"
|
||||
aggregationAVG = "avg"
|
||||
)
|
||||
|
||||
func (a Aggregation) String() string {
|
||||
switch a {
|
||||
case AggregationNONE:
|
||||
return aggregationNONE
|
||||
case AggregationMAX:
|
||||
return aggregationMAX
|
||||
case AggregationMIN:
|
||||
return aggregationMIN
|
||||
case AggregationSUM:
|
||||
return aggregationSUM
|
||||
case AggregationCOUNT:
|
||||
return aggregationCOUNT
|
||||
case AggregationAVG:
|
||||
return aggregationAVG
|
||||
default:
|
||||
return fmt.Sprintf(errUnknownAggregationFmt, a)
|
||||
}
|
||||
}
|
||||
|
||||
func ToAggregation(agg string) (Aggregation, error) {
|
||||
switch strings.ToLower(agg) {
|
||||
case "", aggregationNONE:
|
||||
return AggregationNONE, nil
|
||||
case aggregationMAX:
|
||||
return AggregationMAX, nil
|
||||
case aggregationMIN:
|
||||
return AggregationMIN, nil
|
||||
case aggregationSUM:
|
||||
return AggregationSUM, nil
|
||||
case aggregationCOUNT:
|
||||
return AggregationCOUNT, nil
|
||||
case aggregationAVG:
|
||||
return AggregationAVG, nil
|
||||
default:
|
||||
return Aggregation(0), fmt.Errorf(errUnknownAggregationStringFmt, agg)
|
||||
}
|
||||
}
|
||||
|
||||
func (a Aggregation) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(a.String())
|
||||
}
|
||||
|
||||
func (a *Aggregation) UnmarshalJSON(data []byte) error {
|
||||
str := strings.Trim(string(data), "\"")
|
||||
val, err := ToAggregation(str)
|
||||
*a = val
|
||||
return err
|
||||
}
|
||||
+354
-4
@@ -5,23 +5,38 @@ package re
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
grpcReadersV1 "github.com/absmach/magistrala/api/grpc/readers/v1"
|
||||
"github.com/absmach/magistrala/pkg/reltime"
|
||||
"github.com/absmach/supermq"
|
||||
"github.com/absmach/supermq/pkg/authn"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
||||
"github.com/absmach/supermq/pkg/messaging"
|
||||
"github.com/absmach/supermq/pkg/transformers/senml"
|
||||
)
|
||||
|
||||
const limit = 1000
|
||||
|
||||
type Repository interface {
|
||||
AddRule(ctx context.Context, r Rule) (Rule, error)
|
||||
ViewRule(ctx context.Context, id string) (Rule, error)
|
||||
UpdateRule(ctx context.Context, r Rule) (Rule, error)
|
||||
UpdateRuleSchedule(ctx context.Context, r Rule) (Rule, error)
|
||||
RemoveRule(ctx context.Context, id string) error
|
||||
UpdateRuleStatus(ctx context.Context, id string, status Status) (Rule, error)
|
||||
UpdateRuleStatus(ctx context.Context, r Rule) (Rule, error)
|
||||
ListRules(ctx context.Context, pm PageMeta) (Page, error)
|
||||
|
||||
AddReportConfig(ctx context.Context, cfg ReportConfig) (ReportConfig, error)
|
||||
ViewReportConfig(ctx context.Context, id string) (ReportConfig, error)
|
||||
UpdateReportConfig(ctx context.Context, cfg ReportConfig) (ReportConfig, error)
|
||||
UpdateReportSchedule(ctx context.Context, cfg ReportConfig) (ReportConfig, error)
|
||||
RemoveReportConfig(ctx context.Context, id string) error
|
||||
UpdateReportConfigStatus(ctx context.Context, cfg ReportConfig) (ReportConfig, error)
|
||||
ListReportsConfig(ctx context.Context, pm PageMeta) (ReportConfigPage, error)
|
||||
}
|
||||
|
||||
// PageMeta contains page metadata that helps navigation.
|
||||
@@ -58,6 +73,17 @@ type Service interface {
|
||||
RemoveRule(ctx context.Context, session authn.Session, id string) error
|
||||
EnableRule(ctx context.Context, session authn.Session, id string) (Rule, error)
|
||||
DisableRule(ctx context.Context, session authn.Session, id string) (Rule, error)
|
||||
|
||||
AddReportConfig(ctx context.Context, session authn.Session, cfg ReportConfig) (ReportConfig, error)
|
||||
ViewReportConfig(ctx context.Context, session authn.Session, id string) (ReportConfig, error)
|
||||
UpdateReportConfig(ctx context.Context, session authn.Session, cfg ReportConfig) (ReportConfig, error)
|
||||
UpdateReportSchedule(ctx context.Context, session authn.Session, cfg ReportConfig) (ReportConfig, error)
|
||||
RemoveReportConfig(ctx context.Context, session authn.Session, id string) error
|
||||
ListReportsConfig(ctx context.Context, session authn.Session, pm PageMeta) (ReportConfigPage, error)
|
||||
EnableReportConfig(ctx context.Context, session authn.Session, id string) (ReportConfig, error)
|
||||
DisableReportConfig(ctx context.Context, session authn.Session, id string) (ReportConfig, error)
|
||||
|
||||
GenerateReport(ctx context.Context, session authn.Session, config ReportConfig, action ReportAction) (ReportPage, error)
|
||||
StartScheduler(ctx context.Context) error
|
||||
}
|
||||
|
||||
@@ -70,9 +96,10 @@ type re struct {
|
||||
alarmsPub messaging.Publisher
|
||||
ticker Ticker
|
||||
email Emailer
|
||||
readers grpcReadersV1.ReadersServiceClient
|
||||
}
|
||||
|
||||
func NewService(repo Repository, errors chan (error), idp supermq.IDProvider, rePubSub messaging.PubSub, writersPub, alarmsPub messaging.Publisher, tck Ticker, emailer Emailer) Service {
|
||||
func NewService(repo Repository, errors chan (error), idp supermq.IDProvider, rePubSub messaging.PubSub, writersPub, alarmsPub messaging.Publisher, tck Ticker, emailer Emailer, readers grpcReadersV1.ReadersServiceClient) Service {
|
||||
return &re{
|
||||
repo: repo,
|
||||
idp: idp,
|
||||
@@ -82,6 +109,7 @@ func NewService(repo Repository, errors chan (error), idp supermq.IDProvider, re
|
||||
alarmsPub: alarmsPub,
|
||||
ticker: tck,
|
||||
email: emailer,
|
||||
readers: readers,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +190,13 @@ func (re *re) EnableRule(ctx context.Context, session authn.Session, id string)
|
||||
if err != nil {
|
||||
return Rule{}, err
|
||||
}
|
||||
rule, err := re.repo.UpdateRuleStatus(ctx, id, status)
|
||||
r := Rule{
|
||||
ID: id,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: session.UserID,
|
||||
Status: status,
|
||||
}
|
||||
rule, err := re.repo.UpdateRuleStatus(ctx, r)
|
||||
if err != nil {
|
||||
return Rule{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
@@ -174,7 +208,13 @@ func (re *re) DisableRule(ctx context.Context, session authn.Session, id string)
|
||||
if err != nil {
|
||||
return Rule{}, err
|
||||
}
|
||||
rule, err := re.repo.UpdateRuleStatus(ctx, id, status)
|
||||
r := Rule{
|
||||
ID: id,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: session.UserID,
|
||||
Status: status,
|
||||
}
|
||||
rule, err := re.repo.UpdateRuleStatus(ctx, r)
|
||||
if err != nil {
|
||||
return Rule{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
@@ -188,3 +228,313 @@ func (re *re) Cancel() error {
|
||||
func (re *re) Errors() <-chan error {
|
||||
return re.errors
|
||||
}
|
||||
|
||||
func (re *re) AddReportConfig(ctx context.Context, session authn.Session, cfg ReportConfig) (ReportConfig, error) {
|
||||
id, err := re.idp.ID()
|
||||
if err != nil {
|
||||
return ReportConfig{}, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
cfg.ID = id
|
||||
cfg.CreatedAt = now
|
||||
cfg.CreatedBy = session.UserID
|
||||
cfg.DomainID = session.DomainID
|
||||
cfg.Status = EnabledStatus
|
||||
|
||||
if cfg.Schedule.StartDateTime.IsZero() {
|
||||
cfg.Schedule.StartDateTime = now
|
||||
}
|
||||
|
||||
reportConfig, err := re.repo.AddReportConfig(ctx, cfg)
|
||||
if err != nil {
|
||||
return ReportConfig{}, errors.Wrap(svcerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
return reportConfig, nil
|
||||
}
|
||||
|
||||
func (re *re) ViewReportConfig(ctx context.Context, session authn.Session, id string) (ReportConfig, error) {
|
||||
cfg, err := re.repo.ViewReportConfig(ctx, id)
|
||||
if err != nil {
|
||||
return ReportConfig{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (re *re) UpdateReportConfig(ctx context.Context, session authn.Session, cfg ReportConfig) (ReportConfig, error) {
|
||||
cfg.UpdatedAt = time.Now()
|
||||
cfg.UpdatedBy = session.UserID
|
||||
reportConfig, err := re.repo.UpdateReportConfig(ctx, cfg)
|
||||
if err != nil {
|
||||
return ReportConfig{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
return reportConfig, nil
|
||||
}
|
||||
|
||||
func (re *re) UpdateReportSchedule(ctx context.Context, session authn.Session, cfg ReportConfig) (ReportConfig, error) {
|
||||
cfg.UpdatedAt = time.Now()
|
||||
cfg.UpdatedBy = session.UserID
|
||||
c, err := re.repo.UpdateReportSchedule(ctx, cfg)
|
||||
if err != nil {
|
||||
return ReportConfig{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (re *re) RemoveReportConfig(ctx context.Context, session authn.Session, id string) error {
|
||||
if err := re.repo.RemoveReportConfig(ctx, id); err != nil {
|
||||
return errors.Wrap(svcerr.ErrRemoveEntity, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (re *re) ListReportsConfig(ctx context.Context, session authn.Session, pm PageMeta) (ReportConfigPage, error) {
|
||||
pm.Domain = session.DomainID
|
||||
page, err := re.repo.ListReportsConfig(ctx, pm)
|
||||
if err != nil {
|
||||
return ReportConfigPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func (re *re) EnableReportConfig(ctx context.Context, session authn.Session, id string) (ReportConfig, error) {
|
||||
status, err := ToStatus(Enabled)
|
||||
if err != nil {
|
||||
return ReportConfig{}, err
|
||||
}
|
||||
cfg := ReportConfig{
|
||||
ID: id,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: session.UserID,
|
||||
Status: status,
|
||||
}
|
||||
cfg, err = re.repo.UpdateReportConfigStatus(ctx, cfg)
|
||||
if err != nil {
|
||||
return ReportConfig{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (re *re) DisableReportConfig(ctx context.Context, session authn.Session, id string) (ReportConfig, error) {
|
||||
status, err := ToStatus(Disabled)
|
||||
if err != nil {
|
||||
return ReportConfig{}, err
|
||||
}
|
||||
cfg := ReportConfig{
|
||||
ID: id,
|
||||
UpdatedAt: time.Now(),
|
||||
UpdatedBy: session.UserID,
|
||||
Status: status,
|
||||
}
|
||||
cfg, err = re.repo.UpdateReportConfigStatus(ctx, cfg)
|
||||
if err != nil {
|
||||
return ReportConfig{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (re *re) GenerateReport(ctx context.Context, session authn.Session, config ReportConfig, action ReportAction) (ReportPage, error) {
|
||||
config.DomainID = session.DomainID
|
||||
|
||||
if config.Status != EnabledStatus {
|
||||
return ReportPage{}, svcerr.ErrInvalidStatus
|
||||
}
|
||||
|
||||
reportPage, err := re.generateReport(ctx, config, action)
|
||||
if err != nil {
|
||||
return ReportPage{}, err
|
||||
}
|
||||
|
||||
return reportPage, nil
|
||||
}
|
||||
|
||||
func (re *re) generateReport(ctx context.Context, cfg ReportConfig, action ReportAction) (ReportPage, error) {
|
||||
genReportFile, err := generateFileFunc(action, cfg.Config.FileFormat)
|
||||
if err != nil {
|
||||
return ReportPage{}, err
|
||||
}
|
||||
|
||||
agg := grpcReadersV1.Aggregation_AGGREGATION_UNSPECIFIED
|
||||
switch cfg.Config.Aggregation.AggType {
|
||||
case AggregationMAX:
|
||||
agg = grpcReadersV1.Aggregation_MAX
|
||||
case AggregationMIN:
|
||||
agg = grpcReadersV1.Aggregation_MIN
|
||||
case AggregationCOUNT:
|
||||
agg = grpcReadersV1.Aggregation_COUNT
|
||||
case AggregationAVG:
|
||||
agg = grpcReadersV1.Aggregation_AVG
|
||||
case AggregationSUM:
|
||||
agg = grpcReadersV1.Aggregation_SUM
|
||||
}
|
||||
|
||||
from, err := reltime.Parse(cfg.Config.From)
|
||||
if err != nil {
|
||||
return ReportPage{}, err
|
||||
}
|
||||
to, err := reltime.Parse(cfg.Config.To)
|
||||
if err != nil {
|
||||
return ReportPage{}, err
|
||||
}
|
||||
pm := &grpcReadersV1.PageMetadata{
|
||||
Aggregation: agg,
|
||||
Limit: limit,
|
||||
From: float64(from.UnixMicro()),
|
||||
To: float64(to.UnixNano()),
|
||||
Interval: cfg.Config.Aggregation.Interval,
|
||||
}
|
||||
|
||||
var reports []Report
|
||||
for _, metric := range cfg.Metrics {
|
||||
sMsgs := []senml.Message{}
|
||||
|
||||
pm.Offset = uint64(0)
|
||||
pm.Publisher = metric.ClientID
|
||||
pm.Name = metric.Name
|
||||
if metric.Subtopic != "" {
|
||||
pm.Subtopic = metric.Subtopic
|
||||
}
|
||||
if metric.Protocol != "" {
|
||||
pm.Protocol = metric.Protocol
|
||||
}
|
||||
if metric.Format != "" {
|
||||
pm.Format = metric.Format
|
||||
}
|
||||
|
||||
msgs, err := re.readers.ReadMessages(ctx, &grpcReadersV1.ReadMessagesReq{
|
||||
ChannelId: metric.ChannelID,
|
||||
DomainId: cfg.DomainID,
|
||||
PageMetadata: pm,
|
||||
})
|
||||
if err != nil {
|
||||
return ReportPage{}, err
|
||||
}
|
||||
for _, msg := range msgs.Messages {
|
||||
sMsgs = append(sMsgs, convertToSenml(msg.GetSenml()))
|
||||
}
|
||||
|
||||
for msgs.GetTotal() > (pm.Offset + pm.Limit) {
|
||||
pm.Offset = pm.Offset + pm.Limit
|
||||
msgs, err := re.readers.ReadMessages(ctx, &grpcReadersV1.ReadMessagesReq{
|
||||
ChannelId: metric.ChannelID,
|
||||
DomainId: cfg.DomainID,
|
||||
PageMetadata: pm,
|
||||
})
|
||||
if err != nil {
|
||||
return ReportPage{}, err
|
||||
}
|
||||
for _, msg := range msgs.Messages {
|
||||
sMsgs = append(sMsgs, convertToSenml(msg.GetSenml()))
|
||||
}
|
||||
}
|
||||
|
||||
reports = append(reports, Report{
|
||||
Metric: metric,
|
||||
Messages: sMsgs,
|
||||
})
|
||||
}
|
||||
|
||||
switch {
|
||||
case genReportFile != nil:
|
||||
data, err := genReportFile(reports)
|
||||
if err != nil {
|
||||
return ReportPage{}, err
|
||||
}
|
||||
timeStr := strings.ReplaceAll(time.Now().Format(time.RFC3339), ":", "")
|
||||
filePrefix := cfg.Name
|
||||
if filePrefix == "" {
|
||||
filePrefix = "report"
|
||||
}
|
||||
fileName := fmt.Sprintf("%s_%s.%s", filePrefix, timeStr, cfg.Config.FileFormat.Extension())
|
||||
|
||||
file := ReportFile{
|
||||
Name: fileName,
|
||||
Data: data,
|
||||
Format: cfg.Config.FileFormat,
|
||||
}
|
||||
|
||||
switch action {
|
||||
case EmailReport:
|
||||
if err := re.emailReports(*cfg.Email, file); err != nil {
|
||||
return ReportPage{}, errors.Wrap(err, svcerr.ErrCreateEntity)
|
||||
}
|
||||
|
||||
return ReportPage{}, nil
|
||||
default:
|
||||
return ReportPage{
|
||||
File: file,
|
||||
}, nil
|
||||
}
|
||||
|
||||
default:
|
||||
return ReportPage{
|
||||
From: from,
|
||||
To: to,
|
||||
Aggregation: cfg.Config.Aggregation,
|
||||
Total: uint64(len(reports)),
|
||||
Reports: reports,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func generateFileFunc(action ReportAction, format Format) (func([]Report) ([]byte, error), error) {
|
||||
switch action {
|
||||
case DownloadReport, EmailReport:
|
||||
switch format {
|
||||
case PDF:
|
||||
return generatePDFReport, nil
|
||||
case CSV:
|
||||
return generateCSVReport, nil
|
||||
default:
|
||||
return nil, errors.New("file format not supported")
|
||||
}
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (re *re) emailReports(es EmailSetting, file ReportFile) error {
|
||||
if err := es.Validate(); err != nil {
|
||||
return errors.Wrap(svcerr.ErrMalformedEntity, err)
|
||||
}
|
||||
|
||||
attachments := map[string][]byte{
|
||||
file.Name: file.Data,
|
||||
}
|
||||
|
||||
if err := re.email.SendEmailNotification(
|
||||
es.To,
|
||||
"",
|
||||
es.Subject,
|
||||
"",
|
||||
"",
|
||||
es.Content,
|
||||
"",
|
||||
attachments,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertToSenml(g *grpcReadersV1.SenMLMessage) senml.Message {
|
||||
return senml.Message{
|
||||
Protocol: g.Base.GetProtocol(),
|
||||
Subtopic: g.Base.GetSubtopic(),
|
||||
Unit: g.GetUnit(),
|
||||
Time: g.GetTime(),
|
||||
UpdateTime: g.GetUpdateTime(),
|
||||
Name: g.GetName(),
|
||||
Value: g.Value,
|
||||
StringValue: g.StringValue,
|
||||
DataValue: g.DataValue,
|
||||
BoolValue: g.BoolValue,
|
||||
Sum: g.Sum,
|
||||
}
|
||||
}
|
||||
|
||||
+438
-3
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/absmach/magistrala/internal/testsutil"
|
||||
"github.com/absmach/magistrala/re"
|
||||
"github.com/absmach/magistrala/re/mocks"
|
||||
readmocks "github.com/absmach/magistrala/readers/mocks"
|
||||
"github.com/absmach/supermq/pkg/authn"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
repoerr "github.com/absmach/supermq/pkg/errors/repository"
|
||||
@@ -54,6 +55,17 @@ var (
|
||||
Recurring: re.None,
|
||||
},
|
||||
}
|
||||
reportName = namegen.Generate()
|
||||
rptConfig = re.ReportConfig{
|
||||
ID: testsutil.GenerateUUID(&testing.T{}),
|
||||
Name: reportName,
|
||||
DomainID: domainID,
|
||||
Status: re.EnabledStatus,
|
||||
Schedule: schedule,
|
||||
CreatedBy: userID,
|
||||
UpdatedBy: userID,
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
)
|
||||
|
||||
func newService(t *testing.T, errs chan error) (re.Service, *mocks.Repository, *pubsubmocks.PubSub, *mocks.Ticker) {
|
||||
@@ -61,8 +73,9 @@ func newService(t *testing.T, errs chan error) (re.Service, *mocks.Repository, *
|
||||
mockTicker := new(mocks.Ticker)
|
||||
idProvider := uuid.NewMock()
|
||||
pubsub := pubsubmocks.NewPubSub(t)
|
||||
readersSvc := new(readmocks.ReadersServiceClient)
|
||||
e := new(mocks.Emailer)
|
||||
return re.NewService(repo, errs, idProvider, pubsub, pubsub, pubsub, mockTicker, e), repo, pubsub, mockTicker
|
||||
return re.NewService(repo, errs, idProvider, pubsub, pubsub, pubsub, mockTicker, e, readersSvc), repo, pubsub, mockTicker
|
||||
}
|
||||
|
||||
func TestAddRule(t *testing.T) {
|
||||
@@ -431,6 +444,8 @@ func TestRemoveRule(t *testing.T) {
|
||||
func TestEnableRule(t *testing.T) {
|
||||
svc, repo, _, _ := newService(t, make(chan error))
|
||||
|
||||
now := time.Now()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
session authn.Session
|
||||
@@ -454,6 +469,8 @@ func TestEnableRule(t *testing.T) {
|
||||
InputChannel: inputChannel,
|
||||
Status: re.EnabledStatus,
|
||||
Schedule: schedule,
|
||||
UpdatedBy: userID,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
@@ -471,7 +488,7 @@ func TestEnableRule(t *testing.T) {
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := repo.On("UpdateRuleStatus", context.Background(), tc.id, tc.status).Return(tc.res, tc.err)
|
||||
repoCall := repo.On("UpdateRuleStatus", context.Background(), mock.Anything).Return(tc.res, tc.err)
|
||||
res, err := svc.EnableRule(context.Background(), tc.session, tc.id)
|
||||
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
@@ -486,6 +503,8 @@ func TestEnableRule(t *testing.T) {
|
||||
func TestDisableRule(t *testing.T) {
|
||||
svc, repo, _, _ := newService(t, make(chan error))
|
||||
|
||||
now := time.Now()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
session authn.Session
|
||||
@@ -509,6 +528,8 @@ func TestDisableRule(t *testing.T) {
|
||||
InputChannel: inputChannel,
|
||||
Status: re.DisabledStatus,
|
||||
Schedule: schedule,
|
||||
UpdatedBy: userID,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
@@ -526,7 +547,7 @@ func TestDisableRule(t *testing.T) {
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := repo.On("UpdateRuleStatus", mock.Anything, tc.id, tc.status).Return(tc.res, tc.err)
|
||||
repoCall := repo.On("UpdateRuleStatus", mock.Anything, mock.Anything).Return(tc.res, tc.err)
|
||||
res, err := svc.DisableRule(context.Background(), tc.session, tc.id)
|
||||
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
@@ -608,6 +629,7 @@ func TestHandle(t *testing.T) {
|
||||
}
|
||||
})
|
||||
repoCall1 := pubmocks.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(tc.publishErr)
|
||||
repoCall2 := repo.On("ListReportsConfig", mock.Anything, mock.Anything).Return(re.ReportConfigPage{}, nil)
|
||||
|
||||
err = svc.Handle(tc.message)
|
||||
assert.Nil(t, err)
|
||||
@@ -616,6 +638,417 @@ func TestHandle(t *testing.T) {
|
||||
|
||||
repoCall.Unset()
|
||||
repoCall1.Unset()
|
||||
repoCall2.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddReportConfig(t *testing.T) {
|
||||
svc, repo, _, _ := newService(t, make(chan error))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
session authn.Session
|
||||
cfg re.ReportConfig
|
||||
res re.ReportConfig
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Add report config successfully",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
cfg: re.ReportConfig{
|
||||
Name: reportName,
|
||||
Schedule: schedule,
|
||||
},
|
||||
res: rptConfig,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Add report config with failed repo",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
cfg: re.ReportConfig{
|
||||
Name: reportName,
|
||||
Schedule: schedule,
|
||||
},
|
||||
err: repoerr.ErrCreateEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := repo.On("AddReportConfig", mock.Anything, mock.Anything).Return(tc.res, tc.err)
|
||||
res, err := svc.AddReportConfig(context.Background(), tc.session, tc.cfg)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
if err == nil {
|
||||
assert.NotEmpty(t, res.ID, "expected non-empty result in ID")
|
||||
assert.Equal(t, tc.cfg.Name, res.Name)
|
||||
assert.Equal(t, tc.cfg.Schedule, res.Schedule)
|
||||
}
|
||||
defer repoCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestViewReportConfig(t *testing.T) {
|
||||
svc, repo, _, _ := newService(t, make(chan error))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
session authn.Session
|
||||
id string
|
||||
res re.ReportConfig
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "view report config successfully",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
id: rptConfig.ID,
|
||||
res: rptConfig,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "view report config with failed repo",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
id: rptConfig.ID,
|
||||
err: svcerr.ErrViewEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := repo.On("ViewReportConfig", mock.Anything, mock.Anything).Return(tc.res, tc.err)
|
||||
res, err := svc.ViewReportConfig(context.Background(), tc.session, tc.id)
|
||||
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
if err == nil {
|
||||
assert.Equal(t, tc.res, res)
|
||||
}
|
||||
defer repoCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateReportConfig(t *testing.T) {
|
||||
svc, repo, _, _ := newService(t, make(chan error))
|
||||
|
||||
newName := namegen.Generate()
|
||||
now := time.Now().Add(time.Hour)
|
||||
cases := []struct {
|
||||
desc string
|
||||
session authn.Session
|
||||
cfg re.ReportConfig
|
||||
res re.ReportConfig
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "update report config successfully",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
cfg: re.ReportConfig{
|
||||
Name: newName,
|
||||
ID: rptConfig.ID,
|
||||
Schedule: schedule,
|
||||
},
|
||||
res: re.ReportConfig{
|
||||
Name: newName,
|
||||
ID: rptConfig.ID,
|
||||
DomainID: rptConfig.DomainID,
|
||||
Status: rptConfig.Status,
|
||||
Schedule: rptConfig.Schedule,
|
||||
UpdatedAt: now,
|
||||
UpdatedBy: userID,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "update report config with failed repo",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
cfg: re.ReportConfig{
|
||||
Name: rptConfig.Name,
|
||||
ID: rptConfig.ID,
|
||||
Schedule: schedule,
|
||||
},
|
||||
err: svcerr.ErrUpdateEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := repo.On("UpdateReportConfig", mock.Anything, mock.Anything).Return(tc.res, tc.err)
|
||||
res, err := svc.UpdateReportConfig(context.Background(), tc.session, tc.cfg)
|
||||
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
if err == nil {
|
||||
assert.Equal(t, tc.res, res)
|
||||
}
|
||||
defer repoCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListReportsConfig(t *testing.T) {
|
||||
svc, repo, _, _ := newService(t, make(chan error))
|
||||
numConfigs := 50
|
||||
now := time.Now().Add(time.Hour)
|
||||
var configs []re.ReportConfig
|
||||
for i := 0; i < numConfigs; i++ {
|
||||
c := re.ReportConfig{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: namegen.Generate(),
|
||||
DomainID: domainID,
|
||||
Status: re.EnabledStatus,
|
||||
CreatedAt: now,
|
||||
CreatedBy: userID,
|
||||
Schedule: schedule,
|
||||
}
|
||||
configs = append(configs, c)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
session authn.Session
|
||||
pageMeta re.PageMeta
|
||||
res re.ReportConfigPage
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "list report configs successfully",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
pageMeta: re.PageMeta{},
|
||||
res: re.ReportConfigPage{
|
||||
PageMeta: re.PageMeta{
|
||||
Total: uint64(numConfigs),
|
||||
Offset: 0,
|
||||
Limit: 10,
|
||||
},
|
||||
ReportConfigs: configs[0:10],
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "list report configs successfully with limit",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
pageMeta: re.PageMeta{
|
||||
Limit: 100,
|
||||
},
|
||||
res: re.ReportConfigPage{
|
||||
PageMeta: re.PageMeta{
|
||||
Total: uint64(numConfigs),
|
||||
Offset: 0,
|
||||
Limit: 100,
|
||||
},
|
||||
ReportConfigs: configs[0:numConfigs],
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "list report configs successfully with offset",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
pageMeta: re.PageMeta{
|
||||
Offset: 20,
|
||||
Limit: 10,
|
||||
},
|
||||
res: re.ReportConfigPage{
|
||||
PageMeta: re.PageMeta{
|
||||
Total: uint64(numConfigs),
|
||||
Offset: 20,
|
||||
Limit: 10,
|
||||
},
|
||||
ReportConfigs: configs[20:30],
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "list report configs with failed repo",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
pageMeta: re.PageMeta{},
|
||||
err: svcerr.ErrViewEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := repo.On("ListReportsConfig", mock.Anything, mock.Anything).Return(tc.res, tc.err)
|
||||
res, err := svc.ListReportsConfig(context.Background(), tc.session, tc.pageMeta)
|
||||
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
if err == nil {
|
||||
assert.Equal(t, tc.res, res)
|
||||
}
|
||||
defer repoCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveReportConfig(t *testing.T) {
|
||||
svc, repo, _, _ := newService(t, make(chan error))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
session authn.Session
|
||||
id string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "remove report config successfully",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
id: rptConfig.ID,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "remove report config with failed repo",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
id: rptConfig.ID,
|
||||
err: svcerr.ErrRemoveEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := repo.On("RemoveReportConfig", mock.Anything, mock.Anything).Return(tc.err)
|
||||
err := svc.RemoveReportConfig(context.Background(), tc.session, tc.id)
|
||||
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
defer repoCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableReportConfig(t *testing.T) {
|
||||
svc, repo, _, _ := newService(t, make(chan error))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
session authn.Session
|
||||
id string
|
||||
status re.Status
|
||||
res re.ReportConfig
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "enable report config successfully",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
id: rptConfig.ID,
|
||||
status: re.EnabledStatus,
|
||||
res: rptConfig,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "enable report config with failed repo",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
id: rptConfig.ID,
|
||||
status: re.EnabledStatus,
|
||||
err: svcerr.ErrUpdateEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := repo.On("UpdateReportConfigStatus", context.Background(), mock.Anything).Return(tc.res, tc.err)
|
||||
res, err := svc.EnableReportConfig(context.Background(), tc.session, tc.id)
|
||||
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
if err == nil {
|
||||
assert.Equal(t, tc.res, res)
|
||||
}
|
||||
defer repoCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisableReportConfig(t *testing.T) {
|
||||
svc, repo, _, _ := newService(t, make(chan error))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
session authn.Session
|
||||
id string
|
||||
status re.Status
|
||||
res re.ReportConfig
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "disable report config successfully",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
id: rptConfig.ID,
|
||||
status: re.DisabledStatus,
|
||||
res: re.ReportConfig{
|
||||
ID: rptConfig.ID,
|
||||
Name: rptConfig.Name,
|
||||
DomainID: rptConfig.DomainID,
|
||||
Status: re.DisabledStatus,
|
||||
Schedule: schedule,
|
||||
UpdatedBy: userID,
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "disable report config with failed repo",
|
||||
session: authn.Session{
|
||||
UserID: userID,
|
||||
DomainID: domainID,
|
||||
},
|
||||
id: rptConfig.ID,
|
||||
status: re.DisabledStatus,
|
||||
err: svcerr.ErrUpdateEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := repo.On("UpdateReportConfigStatus", mock.Anything, mock.Anything).Return(tc.res, tc.err)
|
||||
res, err := svc.DisableReportConfig(context.Background(), tc.session, tc.id)
|
||||
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
if err == nil {
|
||||
assert.Equal(t, tc.res, res)
|
||||
}
|
||||
defer repoCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -821,6 +1254,7 @@ func TestStartScheduler(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := repo.On("ListRules", mock.Anything, mock.Anything).Return(tc.page, tc.listErr)
|
||||
repoCall1 := repo.On("ListReportsConfig", mock.Anything, mock.Anything).Return(re.ReportConfigPage{}, nil)
|
||||
tickChan := make(chan time.Time)
|
||||
tickCall := ticker.On("Tick").Return((<-chan time.Time)(tickChan))
|
||||
tickCall1 := ticker.On("Stop").Return()
|
||||
@@ -850,6 +1284,7 @@ func TestStartScheduler(t *testing.T) {
|
||||
err := <-errc
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("expected error %v but got %v", tc.err, err))
|
||||
repoCall.Unset()
|
||||
repoCall1.Unset()
|
||||
tickCall.Unset()
|
||||
tickCall1.Unset()
|
||||
})
|
||||
|
||||
@@ -194,11 +194,11 @@ func TestReadMessages(t *testing.T) {
|
||||
Unit: "C",
|
||||
Time: 1672531200,
|
||||
UpdateTime: 1672531300,
|
||||
Value: 22.5,
|
||||
StringValue: "ok",
|
||||
DataValue: "binary",
|
||||
BoolValue: true,
|
||||
Sum: 123.4,
|
||||
Value: float64Ptr(22.5),
|
||||
StringValue: stringPtr("ok"),
|
||||
DataValue: stringPtr("binary"),
|
||||
BoolValue: boolPtr(true),
|
||||
Sum: float64Ptr(123.4),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -97,11 +97,11 @@ func toResponseMessages(messages []readers.Message) []*grpcReadersV1.Message {
|
||||
Unit: typed.Unit,
|
||||
Time: typed.Time,
|
||||
UpdateTime: typed.UpdateTime,
|
||||
Value: derefFloat64(typed.Value),
|
||||
StringValue: derefString(typed.StringValue),
|
||||
DataValue: derefString(typed.DataValue),
|
||||
BoolValue: derefBool(typed.BoolValue),
|
||||
Sum: derefFloat64(typed.Sum),
|
||||
Value: typed.Value,
|
||||
StringValue: typed.StringValue,
|
||||
DataValue: typed.DataValue,
|
||||
BoolValue: typed.BoolValue,
|
||||
Sum: typed.Sum,
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -149,27 +149,6 @@ func stringifyAggregation(agg grpcReadersV1.Aggregation) string {
|
||||
}
|
||||
}
|
||||
|
||||
func derefString(s *string) string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return *s
|
||||
}
|
||||
|
||||
func derefFloat64(f *float64) float64 {
|
||||
if f == nil {
|
||||
return 0
|
||||
}
|
||||
return *f
|
||||
}
|
||||
|
||||
func derefBool(b *bool) bool {
|
||||
if b == nil {
|
||||
return false
|
||||
}
|
||||
return *b
|
||||
}
|
||||
|
||||
func safeString(v interface{}) string {
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// Code generated by mockery; DO NOT EDIT.
|
||||
// github.com/vektra/mockery
|
||||
// template: testify
|
||||
// Copyright (c) Abstract Machines
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "github.com/absmach/magistrala/api/grpc/readers/v1"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// NewReadersServiceClient creates a new instance of ReadersServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewReadersServiceClient(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *ReadersServiceClient {
|
||||
mock := &ReadersServiceClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// ReadersServiceClient is an autogenerated mock type for the ReadersServiceClient type
|
||||
type ReadersServiceClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type ReadersServiceClient_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *ReadersServiceClient) EXPECT() *ReadersServiceClient_Expecter {
|
||||
return &ReadersServiceClient_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// ReadMessages provides a mock function for the type ReadersServiceClient
|
||||
func (_mock *ReadersServiceClient) ReadMessages(ctx context.Context, in *v1.ReadMessagesReq, opts ...grpc.CallOption) (*v1.ReadMessagesRes, error) {
|
||||
var tmpRet mock.Arguments
|
||||
if len(opts) > 0 {
|
||||
tmpRet = _mock.Called(ctx, in, opts)
|
||||
} else {
|
||||
tmpRet = _mock.Called(ctx, in)
|
||||
}
|
||||
ret := tmpRet
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ReadMessages")
|
||||
}
|
||||
|
||||
var r0 *v1.ReadMessagesRes
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *v1.ReadMessagesReq, []grpc.CallOption) (*v1.ReadMessagesRes, error)); ok {
|
||||
return returnFunc(ctx, in, opts)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *v1.ReadMessagesReq, ...grpc.CallOption) *v1.ReadMessagesRes); ok {
|
||||
r0 = returnFunc(ctx, in, opts...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1.ReadMessagesRes)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *v1.ReadMessagesReq, ...grpc.CallOption) error); ok {
|
||||
r1 = returnFunc(ctx, in, opts...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ReadersServiceClient_ReadMessages_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReadMessages'
|
||||
type ReadersServiceClient_ReadMessages_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ReadMessages is a helper method to define mock.On call
|
||||
// - ctx
|
||||
// - in
|
||||
// - opts
|
||||
func (_e *ReadersServiceClient_Expecter) ReadMessages(ctx interface{}, in interface{}, opts ...interface{}) *ReadersServiceClient_ReadMessages_Call {
|
||||
return &ReadersServiceClient_ReadMessages_Call{Call: _e.mock.On("ReadMessages",
|
||||
append([]interface{}{ctx, in}, opts...)...)}
|
||||
}
|
||||
|
||||
func (_c *ReadersServiceClient_ReadMessages_Call) Run(run func(ctx context.Context, in *v1.ReadMessagesReq, opts ...grpc.CallOption)) *ReadersServiceClient_ReadMessages_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]grpc.CallOption, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(grpc.CallOption)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*v1.ReadMessagesReq), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *ReadersServiceClient_ReadMessages_Call) Return(readMessagesRes *v1.ReadMessagesRes, err error) *ReadersServiceClient_ReadMessages_Call {
|
||||
_c.Call.Return(readMessagesRes, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *ReadersServiceClient_ReadMessages_Call) RunAndReturn(run func(ctx context.Context, in *v1.ReadMessagesReq, opts ...grpc.CallOption) (*v1.ReadMessagesRes, error)) *ReadersServiceClient_ReadMessages_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
@@ -21,6 +21,7 @@ packages:
|
||||
Repository:
|
||||
Service:
|
||||
Ticker:
|
||||
Emailer:
|
||||
github.com/absmach/magistrala/bootstrap:
|
||||
interfaces:
|
||||
ConfigRepository:
|
||||
@@ -36,4 +37,11 @@ packages:
|
||||
github.com/absmach/magistrala/alarms:
|
||||
interfaces:
|
||||
Service:
|
||||
Repository:
|
||||
Repository:
|
||||
github.com/absmach/magistrala/api/grpc/readers/v1:
|
||||
interfaces:
|
||||
ReadersServiceClient:
|
||||
config:
|
||||
dir: "./readers/mocks"
|
||||
mockname: "ReadersServiceClient"
|
||||
filename: "readers_client.go"
|
||||
|
||||
Reference in New Issue
Block a user