From 2ab67023eb25a6678482ef54805fcb0f6a32b1db Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Wed, 11 Oct 2023 05:37:22 +0300 Subject: [PATCH 01/12] add service base --- .gitignore | 1 + cmd/app/main.go | 19 +++ config.yaml | 4 + go.mod | 38 ++++++ go.sum | 99 ++++++++++++++ internal/app/app.go | 77 +++++++++++ internal/config/config.go | 28 ++++ internal/entity/manipulator.go | 49 +++++++ internal/entity/setup.go | 15 +++ internal/handlers/schemas.go | 18 +++ internal/handlers/setups_router.go | 79 ++++++++++++ internal/handlers/utils.go | 22 ++++ .../repositories/manipulator_repository.go | 83 ++++++++++++ internal/repositories/setup_repository.go | 121 ++++++++++++++++++ internal/repositories/utils.go | 1 + .../services/setup_manipulator_service.go | 70 ++++++++++ internal/services/setup_service.go | 55 ++++++++ pkg/errs/errors.go | 19 +++ 18 files changed, 798 insertions(+) create mode 100644 .gitignore create mode 100644 cmd/app/main.go create mode 100644 config.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/app/app.go create mode 100644 internal/config/config.go create mode 100644 internal/entity/manipulator.go create mode 100644 internal/entity/setup.go create mode 100644 internal/handlers/schemas.go create mode 100644 internal/handlers/setups_router.go create mode 100644 internal/handlers/utils.go create mode 100644 internal/repositories/manipulator_repository.go create mode 100644 internal/repositories/setup_repository.go create mode 100644 internal/repositories/utils.go create mode 100644 internal/services/setup_manipulator_service.go create mode 100644 internal/services/setup_service.go create mode 100644 pkg/errs/errors.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfd0ade --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/db.sqlite diff --git a/cmd/app/main.go b/cmd/app/main.go new file mode 100644 index 0000000..3ff7891 --- /dev/null +++ b/cmd/app/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "flag" + "git.miem.hse.ru/hubman/configurator/internal/app" + "git.miem.hse.ru/hubman/configurator/internal/config" + "log" +) + +func main() { + confPath := flag.String("conf", "config.yaml", "Filepath to config") + flag.Parse() + conf, err := config.NewConfig(*confPath) + if err != nil { + log.Fatalln("Failed to load config") + } + app := app.NewApp(conf) + app.Run() +} diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..40a16e5 --- /dev/null +++ b/config.yaml @@ -0,0 +1,4 @@ +db: + path: "db.sqlite" +http: + port: 8080 \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e842870 --- /dev/null +++ b/go.mod @@ -0,0 +1,38 @@ +module git.miem.hse.ru/hubman/configurator + +go 1.20 + +require ( + github.com/BurntSushi/toml v1.2.1 // indirect + github.com/bytedance/sonic v1.10.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.9.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.15.5 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/ilyakaznacheev/cleanenv v1.5.0 // indirect + github.com/jmoiron/sqlx v1.3.5 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-sqlite3 v1.14.17 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + golang.org/x/arch v0.5.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/net v0.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7cce769 --- /dev/null +++ b/go.sum @@ -0,0 +1,99 @@ +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= +github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= +github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4= +github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y= +golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ= +olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 0000000..27c4c25 --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1,77 @@ +package app + +import ( + "fmt" + "git.miem.hse.ru/hubman/configurator/internal/config" + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/internal/handlers" + "git.miem.hse.ru/hubman/configurator/internal/repositories" + "git.miem.hse.ru/hubman/configurator/internal/services" + "github.com/gin-gonic/gin" + "github.com/jmoiron/sqlx" + _ "github.com/mattn/go-sqlite3" + "log" +) + +type App struct { + httpEngine *gin.Engine + conf *config.Config +} + +func NewApp(conf *config.Config) *App { + httpEngine := gin.Default() + db, err := initDB(conf) + if err != nil { + log.Fatalln("Can't init DB: ", err) + } + setupRepository := repositories.NewSqliteSetupRepository(db) + manipulatorRepository := repositories.NewSqliteManipulatorRepository(db) + + setupService := services.NewSetupService(setupRepository) + manipulatorService := services.NewSetupManipulatorService(setupService, manipulatorRepository) + + setupRouter := handlers.NewSetupRouter(setupService) + httpEngine.GET("/setups", setupRouter.GetList) + httpEngine.POST("/setups", setupRouter.Post) + httpEngine.GET("/setups/:setup_id", setupRouter.Get) + httpEngine.PATCH("/setups/:setup_id", setupRouter.Patch) + httpEngine.DELETE("/setups/:setup_id", setupRouter.Delete) + + setupManipulatorRouter := handlers.NewSetupManipulatorRouter(manipulatorService) + httpEngine.GET("/setups/:setup_id/manipulators", setupManipulatorRouter.GetList) + httpEngine.POST("/setups/:setup_id/manipulators", setupManipulatorRouter.Post) + httpEngine.GET("/setups/:setup_id/manipulators/:manipulator_id", setupManipulatorRouter.Get) + httpEngine.PATCH("/setups/:setup_id/manipulators/:manipulator_id", setupManipulatorRouter.Patch) + httpEngine.DELETE("/setups/:setup_id/manipulators/:manipulator_id", setupManipulatorRouter.Delete) + + return &App{ + conf: conf, + httpEngine: httpEngine, + } +} + +func (a *App) Run() { + log.Println("Server started") + err := a.httpEngine.Run( + fmt.Sprintf(":%d", a.conf.Http.Port), + ) + if err != nil { + log.Fatalln("Error in htpEngine: ", err) + } +} + +func initDB(conf *config.Config) (*sqlx.DB, error) { + db, err := sqlx.Connect("sqlite3", conf.Db.Path) + if err != nil { + return nil, err + } + _, err = db.Exec(entity.SetupDML) + if err != nil { + return nil, err + } + _, err = db.Exec(entity.ManipulatorDML) + if err != nil { + return nil, err + } + return db, nil +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..fc5eb8a --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,28 @@ +package config + +import ( + "fmt" + "github.com/ilyakaznacheev/cleanenv" +) + +type Config struct { + Http `yaml:"http"` + Db `yaml:"db""` +} + +type Http struct { + Port int `yaml:"port"` +} + +type Db struct { + Path string `yaml:"path"` +} + +func NewConfig(path string) (*Config, error) { + cfg := &Config{} + err := cleanenv.ReadConfig(path, cfg) + if err != nil { + return nil, fmt.Errorf("config error: %w", err) + } + return cfg, nil +} diff --git a/internal/entity/manipulator.go b/internal/entity/manipulator.go new file mode 100644 index 0000000..318f88c --- /dev/null +++ b/internal/entity/manipulator.go @@ -0,0 +1,49 @@ +package entity + +import ( + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "net/url" + "regexp" +) + +type Manipulator struct { + Id int `db:"id" json:"id"` + SetupId int `db:"setup_id" json:"setup_id"` + Name string `db:"name" json:"name"` + Alias string `db:"alias" json:"alias"` + Description *string `db:"description" json:"description"` + URL string `db:"url" json:"url"` +} + +func (m *Manipulator) CheckAlias() error { + match, err := regexp.MatchString(`^[A-Za-z_][0-9A-Za-z_].*$`, m.Alias) + + if err != nil || !match { + return errs.ErrManipulatorIncorrectParams + } + return nil +} + +func (m *Manipulator) CheckURL() error { + _, err := url.ParseRequestURI(m.URL) + if err != nil { + return errs.ErrManipulatorIncorrectParams + } + return nil +} + +const ManipulatorDML = ` +CREATE TABLE IF NOT EXISTS t_manipulator ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + setup_id INTEGER, + name TEXT NOT NULL, + alias TEXT NOT NULL, + description TEXT, + url TEXT, + + FOREIGN KEY (setup_id) REFERENCES t_setup(id), + UNIQUE (setup_id, name), + UNIQUE (setup_id, alias), + UNIQUE (setup_id, url) +) +` diff --git a/internal/entity/setup.go b/internal/entity/setup.go new file mode 100644 index 0000000..7755947 --- /dev/null +++ b/internal/entity/setup.go @@ -0,0 +1,15 @@ +package entity + +type Setup struct { + Id int `db:"id" json:"id"` + Name string `db:"name" json:"name"` + Description string `db:"description" json:"description"` +} + +const SetupDML = ` +CREATE TABLE IF NOT EXISTS t_setup ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + description TEXT +) +` diff --git a/internal/handlers/schemas.go b/internal/handlers/schemas.go new file mode 100644 index 0000000..5bc4a4a --- /dev/null +++ b/internal/handlers/schemas.go @@ -0,0 +1,18 @@ +package handlers + +type SetupPost struct { + Name string `json:"name"` + Description string `json:"description"` +} + +type SetupPatch struct { + Name *string `json:"name"` + Description *string `json:"description"` +} + +type ManipulatorPost struct { + Name string `json:"name"` + URL string `json:"url"` + Description *string `json:"description"` + Alias string `json:"alias"` +} diff --git a/internal/handlers/setups_router.go b/internal/handlers/setups_router.go new file mode 100644 index 0000000..b0a6866 --- /dev/null +++ b/internal/handlers/setups_router.go @@ -0,0 +1,79 @@ +package handlers + +import ( + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/internal/services" + "github.com/gin-gonic/gin" + "net/http" +) + +type SetupRouter struct { + setupService *services.SetupService +} + +func NewSetupRouter(setupService *services.SetupService) *SetupRouter { + return &SetupRouter{setupService: setupService} +} + +func (r *SetupRouter) Post(c *gin.Context) { + setupPost := &SetupPost{} + if err := c.Bind(setupPost); err != nil { + return + } + newSetup := &entity.Setup{ + Name: setupPost.Name, + Description: setupPost.Description, + } + if err := r.setupService.Add(newSetup); err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusCreated, newSetup) +} + +func (r *SetupRouter) GetList(c *gin.Context) { + setups, err := r.setupService.GetList() + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, setups) +} + +func (r *SetupRouter) Get(c *gin.Context) { + setup, err := r.setupService.GetById( + c.Params.ByName("setup_id"), + ) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, setup) +} + +func (r *SetupRouter) Delete(c *gin.Context) { + err := r.setupService.Delete(c.Params.ByName("setup_id")) + if err != nil { + BuildErrResp(c, err) + return + } + c.Status(http.StatusOK) +} + +func (r *SetupRouter) Patch(c *gin.Context) { + setupPatch := &SetupPatch{} + if err := c.Bind(setupPatch); err != nil { + return + } + + setup, err := r.setupService.Update( + c.Params.ByName("setup_id"), + setupPatch.Name, + setupPatch.Description, + ) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, setup) +} diff --git a/internal/handlers/utils.go b/internal/handlers/utils.go new file mode 100644 index 0000000..3dcc6ec --- /dev/null +++ b/internal/handlers/utils.go @@ -0,0 +1,22 @@ +package handlers + +import ( + "errors" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "github.com/gin-gonic/gin" + "net/http" +) + +type ErrorSchema struct { + Code string `json:"status"` +} + +func BuildErrResp(c *gin.Context, err error) { + if errors.Is(err, errs.ErrNotFound) { + c.JSON(http.StatusNotFound, &ErrorSchema{err.Error()}) + } else if errors.Is(err, errs.ErrIncorrectParams) { + c.JSON(http.StatusBadRequest, &ErrorSchema{err.Error()}) + } else { + c.JSON(http.StatusInternalServerError, &ErrorSchema{err.Error()}) + } +} diff --git a/internal/repositories/manipulator_repository.go b/internal/repositories/manipulator_repository.go new file mode 100644 index 0000000..9e55159 --- /dev/null +++ b/internal/repositories/manipulator_repository.go @@ -0,0 +1,83 @@ +package repositories + +import ( + "errors" + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "github.com/jmoiron/sqlx" + "github.com/mattn/go-sqlite3" +) + +type ManipulatorRepository interface { + GetBySetupId(setupId int) ([]*entity.Manipulator, error) + GetById(id int) (*entity.Manipulator, error) + Add(manipulator *entity.Manipulator) error + //Update(id int, name *string, description *string) error + Delete(id int) error +} + +type SqliteManipulatorRepository struct { + db *sqlx.DB +} + +func NewSqliteManipulatorRepository(db *sqlx.DB) *SqliteManipulatorRepository { + return &SqliteManipulatorRepository{db: db} +} + +func (r SqliteManipulatorRepository) GetBySetupId(setupId int) ([]*entity.Manipulator, error) { + manipulators := []*entity.Manipulator{} + err := r.db.Select(&manipulators, "SELECT * FROM t_manipulator WHERE setup_id=$1", setupId) + if err != nil { + return nil, errs.ErrInternalError + } + return manipulators, nil +} + +func (r SqliteManipulatorRepository) GetById(id int) (*entity.Manipulator, error) { + manipulators := []*entity.Manipulator{} + err := r.db.Select(&manipulators, "SELECT * FROM t_manipulator WHERE id=$1", id) + if err != nil { + return nil, errs.ErrInternalError + } + + var manipulator *entity.Manipulator + if len(manipulators) == 1 { + manipulator = manipulators[0] + } else { + return nil, errs.ErrManipulatorNotFound + } + return manipulator, nil +} + +func (r SqliteManipulatorRepository) Add(manipulator *entity.Manipulator) error { + res, err := r.db.NamedExec(` + INSERT INTO t_manipulator(setup_id, name, alias, description, url) + VALUES (:setup_id, :name, :alias, :description, :url) RETURNING id`, + manipulator, + ) + if err != nil { + return wrapSqilteManipulatorError(err) + } + id, err := res.LastInsertId() + if err != nil { + return errs.ErrInternalError + } + manipulator.Id = int(id) + return nil +} + +func (r SqliteManipulatorRepository) Delete(id int) error { + + panic("implement me") +} + +func wrapSqilteManipulatorError(err error) error { + var sqliteErr sqlite3.Error + if errors.As(err, &sqliteErr) { + if errors.Is(sqliteErr.Code, sqlite3.ErrConstraint) { + return errs.ErrManipulatorIncorrectParams + } + return errs.ErrInternalError + } + return err +} diff --git a/internal/repositories/setup_repository.go b/internal/repositories/setup_repository.go new file mode 100644 index 0000000..ce351cb --- /dev/null +++ b/internal/repositories/setup_repository.go @@ -0,0 +1,121 @@ +package repositories + +import ( + "errors" + "fmt" + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + querybuilder "git.miem.hse.ru/hubman/configurator/pkg/query-builder" + "github.com/jmoiron/sqlx" + "github.com/mattn/go-sqlite3" +) + +type SetupRepository interface { + GetAll() ([]*entity.Setup, error) + GetById(id int) (*entity.Setup, error) + Add(setup *entity.Setup) error + Update(id int, name *string, description *string) error + Delete(id int) error +} + +type SqliteSetupRepository struct { + db *sqlx.DB +} + +func NewSqliteSetupRepository(db *sqlx.DB) *SqliteSetupRepository { + return &SqliteSetupRepository{db: db} +} + +func (r SqliteSetupRepository) GetAll() ([]*entity.Setup, error) { + setups := []*entity.Setup{} + err := r.db.Select(&setups, "SELECT * FROM t_setup") + if err != nil { + return setups, errs.ErrInternalError + } + return setups, nil +} + +func (r SqliteSetupRepository) GetById(id int) (*entity.Setup, error) { + setups := []*entity.Setup{} + err := r.db.Select(&setups, "SELECT * FROM t_setup WHERE id=$1", id) + if err != nil { + return nil, errs.ErrInternalError + } + + var setup *entity.Setup + if len(setups) == 1 { + setup = setups[0] + } else { + return nil, errs.ErrSetupNotFound + } + return setup, nil +} + +func (r SqliteSetupRepository) Delete(id int) error { + res, err := r.db.Exec("DELETE FROM t_setup WHERE id=$1", id) + if err != nil { + return errs.ErrInternalError + } + affected, err := res.RowsAffected() + if err != nil { + return errs.ErrInternalError + } + if affected == 0 { + return errs.ErrNotFound + } + + return nil +} + +func (r SqliteSetupRepository) Add(setup *entity.Setup) error { + res, err := r.db.NamedExec( + "INSERT INTO t_setup(name, description) VALUES (:name, :description) RETURNING id", + setup, + ) + if err != nil { + return wrapSqilteSetupError(err) + } + id, err := res.LastInsertId() + if err != nil { + return errs.ErrInternalError + } + setup.Id = int(id) + return nil +} + +func (r SqliteSetupRepository) Update(id int, name *string, description *string) error { + setClause := querybuilder.BuildUpdateSetClause( + querybuilder.AttrValue{"name", name}, + querybuilder.AttrValue{"description", description}, + ) + res, err := r.db.NamedExec( + fmt.Sprintf("UPDATE t_setup SET %s WHERE id=:id", setClause), + struct { + Id int + Name *string + Description *string + }{id, name, description}, + ) + if err != nil { + return wrapSqilteSetupError(err) + } + affected, err := res.RowsAffected() + if err != nil { + return errs.ErrInternalError + } + if affected == 0 { + return errs.ErrSetupNotFound + } + return nil +} + +func wrapSqilteSetupError(err error) error { + var sqliteErr sqlite3.Error + if errors.As(err, &sqliteErr) { + if errors.Is(sqliteErr.Code, sqlite3.ErrConstraint) { + return errs.ErrSetupIncorrectParams + } + return errs.ErrInternalError + } + return err +} diff --git a/internal/repositories/utils.go b/internal/repositories/utils.go new file mode 100644 index 0000000..3f43206 --- /dev/null +++ b/internal/repositories/utils.go @@ -0,0 +1 @@ +package repositories diff --git a/internal/services/setup_manipulator_service.go b/internal/services/setup_manipulator_service.go new file mode 100644 index 0000000..a826279 --- /dev/null +++ b/internal/services/setup_manipulator_service.go @@ -0,0 +1,70 @@ +package services + +import ( + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/internal/repositories" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "strconv" +) + +type SetupManipulatorService struct { + setupService *SetupService + setupRepository repositories.SetupRepository + manipulatorRepository repositories.ManipulatorRepository +} + +func NewSetupManipulatorService( + setupService *SetupService, + manipulatorRepository repositories.ManipulatorRepository, +) *SetupManipulatorService { + return &SetupManipulatorService{ + setupService: setupService, + manipulatorRepository: manipulatorRepository} +} + +func (s *SetupManipulatorService) GetList(setupIdStr string) ([]*entity.Manipulator, error) { + setup, err := s.setupService.GetById(setupIdStr) + if err != nil { + return nil, err + } + return s.manipulatorRepository.GetBySetupId(setup.Id) +} + +func (s *SetupManipulatorService) Add( + setupIdStr string, manipulator *entity.Manipulator, +) error { + setup, err := s.setupService.GetById(setupIdStr) + if err != nil { + return err + } + if manipulator == nil { + return errs.ErrInternalError + } + if err := manipulator.CheckAlias(); err != nil { + return err + } + if err := manipulator.CheckURL(); err != nil { + return err + } + manipulator.SetupId = setup.Id + return s.manipulatorRepository.Add(manipulator) +} + +func (s *SetupManipulatorService) GetById(setupIdStr, manipulatorIdStr string) (*entity.Manipulator, error) { + setupId, err := strconv.Atoi(setupIdStr) + if err != nil { + return nil, errs.ErrSetupNotFound + } + manipulatorId, err := strconv.Atoi(manipulatorIdStr) + if err != nil { + return nil, errs.ErrManipulatorNotFound + } + manipulator, err := s.manipulatorRepository.GetById(manipulatorId) + if err != nil { + return nil, err + } + if manipulator.SetupId != setupId { + return nil, errs.ErrManipulatorNotFound + } + return manipulator, nil +} diff --git a/internal/services/setup_service.go b/internal/services/setup_service.go new file mode 100644 index 0000000..a78e525 --- /dev/null +++ b/internal/services/setup_service.go @@ -0,0 +1,55 @@ +package services + +import ( + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/internal/repositories" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "strconv" +) + +type SetupService struct { + r repositories.SetupRepository +} + +func NewSetupService(r repositories.SetupRepository) *SetupService { + return &SetupService{r: r} +} + +func (s *SetupService) GetList() ([]*entity.Setup, error) { + return s.r.GetAll() +} + +func (s *SetupService) Delete(setupId string) error { + id, err := strconv.Atoi(setupId) + if err != nil { + return errs.ErrSetupNotFound + } + return s.r.Delete(id) +} + +func (s *SetupService) GetById(setupId string) (*entity.Setup, error) { + id, err := strconv.Atoi(setupId) + if err != nil { + return nil, errs.ErrSetupNotFound + } + return s.r.GetById(id) +} + +func (s *SetupService) Add(setup *entity.Setup) error { + return s.r.Add(setup) +} + +func (s *SetupService) Update(setupId string, name *string, description *string) (*entity.Setup, error) { + id, err := strconv.Atoi(setupId) + if err != nil { + return nil, errs.ErrSetupNotFound + } + + if description != nil && name != nil { + err := s.r.Update(id, name, description) + if err != nil { + return nil, err + } + } + return s.r.GetById(id) +} diff --git a/pkg/errs/errors.go b/pkg/errs/errors.go new file mode 100644 index 0000000..643cf9e --- /dev/null +++ b/pkg/errs/errors.go @@ -0,0 +1,19 @@ +package errs + +import ( + "errors" + "fmt" +) + +var ( + ErrNotFound = errors.New("NOT_FOUND") + ErrIncorrectParams = errors.New("INCORRECT_PARAMS") + + ErrInternalError = errors.New("INTERNAL_ERROR") + + ErrSetupNotFound = fmt.Errorf("SETUP_%w", ErrNotFound) + ErrSetupIncorrectParams = fmt.Errorf("SETUP_%w", ErrIncorrectParams) + + ErrManipulatorIncorrectParams = fmt.Errorf("MANIPULATOR_%w", ErrIncorrectParams) + ErrManipulatorNotFound = fmt.Errorf("MANIPULATOR_%w", ErrNotFound) +) -- GitLab From d9f9097e5f2a00aa9e58a3b5a40406a5f3c96dc4 Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Sun, 15 Oct 2023 03:29:26 +0300 Subject: [PATCH 02/12] add logic for device and executor --- go.mod | 3 + go.sum | 7 + internal/app/app.go | 46 +++++- internal/entity/device.go | 63 +++++++ internal/handlers/setup_executor_router.go | 69 ++++++++ internal/handlers/setup_manipulator_router.go | 66 ++++++++ internal/handlers/setup_util.go | 42 +++++ internal/repositories/device_repository.go | 156 ++++++++++++++++++ internal/services/device_store_service.go | 45 +++++ internal/services/executor_service.go | 43 +++++ internal/services/setup_service.go | 8 + pkg/errs/errors.go | 3 + pkg/query-builder/builder.go | 21 +++ 13 files changed, 564 insertions(+), 8 deletions(-) create mode 100644 internal/entity/device.go create mode 100644 internal/handlers/setup_executor_router.go create mode 100644 internal/handlers/setup_manipulator_router.go create mode 100644 internal/handlers/setup_util.go create mode 100644 internal/repositories/device_repository.go create mode 100644 internal/services/device_store_service.go create mode 100644 internal/services/executor_service.go create mode 100644 pkg/query-builder/builder.go diff --git a/go.mod b/go.mod index e842870..25a39b2 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.20 require ( github.com/BurntSushi/toml v1.2.1 // indirect + github.com/Masterminds/squirrel v1.5.4 // indirect github.com/bytedance/sonic v1.10.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.0 // indirect @@ -19,6 +20,8 @@ require ( github.com/joho/godotenv v1.5.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-sqlite3 v1.14.17 // indirect diff --git a/go.sum b/go.sum index 7cce769..84908a2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= +github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= @@ -42,6 +44,10 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -61,6 +67,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/internal/app/app.go b/internal/app/app.go index 27c4c25..f7a10d4 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -26,23 +26,49 @@ func NewApp(conf *config.Config) *App { } setupRepository := repositories.NewSqliteSetupRepository(db) manipulatorRepository := repositories.NewSqliteManipulatorRepository(db) + deviceRepository := repositories.NewSqliteDeviceRepository(db) setupService := services.NewSetupService(setupRepository) manipulatorService := services.NewSetupManipulatorService(setupService, manipulatorRepository) + deviceStoreService := services.NewDeviceStoreService(deviceRepository) + executorService := services.NewExecutorService(deviceStoreService) setupRouter := handlers.NewSetupRouter(setupService) httpEngine.GET("/setups", setupRouter.GetList) httpEngine.POST("/setups", setupRouter.Post) - httpEngine.GET("/setups/:setup_id", setupRouter.Get) - httpEngine.PATCH("/setups/:setup_id", setupRouter.Patch) - httpEngine.DELETE("/setups/:setup_id", setupRouter.Delete) + + setupedNS := httpEngine.Group("/setups/:setup_id") + setupedNS.Use(handlers.CheckSetupId(setupService)) + { + setupedNS.GET("/", setupRouter.Get) + setupedNS.PATCH("/", setupRouter.Patch) + setupedNS.DELETE("/", setupRouter.Delete) + } setupManipulatorRouter := handlers.NewSetupManipulatorRouter(manipulatorService) - httpEngine.GET("/setups/:setup_id/manipulators", setupManipulatorRouter.GetList) - httpEngine.POST("/setups/:setup_id/manipulators", setupManipulatorRouter.Post) - httpEngine.GET("/setups/:setup_id/manipulators/:manipulator_id", setupManipulatorRouter.Get) - httpEngine.PATCH("/setups/:setup_id/manipulators/:manipulator_id", setupManipulatorRouter.Patch) - httpEngine.DELETE("/setups/:setup_id/manipulators/:manipulator_id", setupManipulatorRouter.Delete) + setupManipulatorNS := httpEngine.Group("/setups/:setup_id/manipulators") + setupManipulatorNS.Use(handlers.CheckSetupId(setupService)) + { + setupManipulatorNS.GET("/", setupManipulatorRouter.GetList) + //httpEngine.POST("", setupManipulatorRouter.Post) + //httpEngine.GET("/:manipulator_id", setupManipulatorRouter.Get) + //httpEngine.PATCH("/:manipulator_id", setupManipulatorRouter.Patch) + //httpEngine.DELETE("/:manipulator_id", setupManipulatorRouter.Delete) + } + + setupExecutorRouter := handlers.NewSetupExecutorRouter(executorService) + setupExecutorNS := httpEngine.Group("/setups/:setup_id/executors") + setupExecutorNS.Use(handlers.CheckSetupId(setupService)) + { + setupExecutorNS.GET("/", setupExecutorRouter.GetList) + setupExecutorNS.POST("/", setupExecutorRouter.Post) + + executoredNS := setupExecutorNS.Group("/:executor_id") + executoredNS.Use(handlers.CheckExecutorId(executorService)) + executoredNS.GET("/", setupExecutorRouter.Get) + executoredNS.PATCH("/", setupExecutorRouter.Patch) + executoredNS.DELETE("/", setupExecutorRouter.Delete) + } return &App{ conf: conf, @@ -73,5 +99,9 @@ func initDB(conf *config.Config) (*sqlx.DB, error) { if err != nil { return nil, err } + _, err = db.Exec(entity.DeviceDML) + if err != nil { + return nil, err + } return db, nil } diff --git a/internal/entity/device.go b/internal/entity/device.go new file mode 100644 index 0000000..0c123d2 --- /dev/null +++ b/internal/entity/device.go @@ -0,0 +1,63 @@ +package entity + +type DeviceType string + +const DeviceTypeManipulator DeviceType = "manipulator" +const DeviceTypeExecutor DeviceType = "executor" + +type Device struct { + Id int `db:"id" json:"id"` + DeviceType DeviceType `db:"device_type" json:"-"` + SetupId int `db:"setup_id" json:"setup_id"` + Name string `db:"name" json:"name"` + Alias string `db:"alias" json:"alias"` + Description *string `db:"description" json:"description"` + URL string `db:"url" json:"url"` +} + +type NewDevice struct { + Name string `db:"name" json:"name"` + Alias string `db:"alias" json:"alias"` + Description *string `db:"description" json:"description"` + URL string `db:"url" json:"url"` +} + +type UpdDevice struct { + Name *string `db:"name" json:"name"` + Alias *string `db:"alias" json:"alias"` + Description *string `db:"description" json:"description"` + URL *string `db:"url" json:"url"` +} + +// func (m *Manipulator) CheckAlias() error { +// match, err := regexp.MatchString(`^[A-Za-z_][0-9A-Za-z_].*$`, m.Alias) +// +// if err != nil || !match { +// return errs.ErrManipulatorIncorrectParams +// } +// return nil +// } +// +// func (m *Manipulator) CheckURL() error { +// _, err := url.ParseRequestURI(m.URL) +// if err != nil { +// return errs.ErrManipulatorIncorrectParams +// } +// return nil +// } +const DeviceDML = ` +CREATE TABLE IF NOT EXISTS Device ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + device_type TEXT NOT NULL CHECK (device_type IN ('manipulator', 'executor')), + setup_id INTEGER, + name TEXT NOT NULL, + alias TEXT NOT NULL, + description TEXT, + url TEXT NOT NULL, + + FOREIGN KEY (setup_id) REFERENCES t_setup(id), + UNIQUE (setup_id, name), + UNIQUE (setup_id, alias), + UNIQUE (setup_id, device_type, url) +) +` diff --git a/internal/handlers/setup_executor_router.go b/internal/handlers/setup_executor_router.go new file mode 100644 index 0000000..dca9b86 --- /dev/null +++ b/internal/handlers/setup_executor_router.go @@ -0,0 +1,69 @@ +package handlers + +import ( + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/internal/services" + "github.com/gin-gonic/gin" + "net/http" +) + +type SetupExecutorRouter struct { + executorService *services.ExecutorService +} + +func NewSetupExecutorRouter(executorService *services.ExecutorService) *SetupExecutorRouter { + return &SetupExecutorRouter{executorService} +} + +func (r *SetupExecutorRouter) GetList(c *gin.Context) { + executors, err := r.executorService.GetList(getSetupId(c)) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, executors) +} + +func (r *SetupExecutorRouter) Post(c *gin.Context) { + newDevice := &entity.NewDevice{} + if err := c.BindJSON(newDevice); err != nil { + return + } + executor, err := r.executorService.Add(getSetupId(c), newDevice) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, executor) +} + +func (r *SetupExecutorRouter) Get(c *gin.Context) { + executor, err := r.executorService.GetOne(getSetupId(c), getExecutorId(c)) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, executor) +} + +func (r *SetupExecutorRouter) Patch(c *gin.Context) { + updDevice := &entity.UpdDevice{} + if err := c.BindJSON(updDevice); err != nil { + return + } + executor, err := r.executorService.Update(getSetupId(c), getExecutorId(c), updDevice) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, executor) +} + +func (r *SetupExecutorRouter) Delete(c *gin.Context) { + err := r.executorService.Delete(getSetupId(c), getExecutorId(c)) + if err != nil { + BuildErrResp(c, err) + return + } + c.Status(http.StatusOK) +} diff --git a/internal/handlers/setup_manipulator_router.go b/internal/handlers/setup_manipulator_router.go new file mode 100644 index 0000000..9490cba --- /dev/null +++ b/internal/handlers/setup_manipulator_router.go @@ -0,0 +1,66 @@ +package handlers + +import ( + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/internal/services" + "github.com/gin-gonic/gin" + "net/http" +) + +type SetupManipulatorRouter struct { + manipulatorService *services.SetupManipulatorService +} + +func NewSetupManipulatorRouter(setupService *services.SetupManipulatorService) *SetupManipulatorRouter { + return &SetupManipulatorRouter{setupService} +} + +func (r *SetupManipulatorRouter) GetList(c *gin.Context) { + manipulators, err := r.manipulatorService.GetList( + c.Params.ByName("setup_id"), + ) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, manipulators) +} + +func (r *SetupManipulatorRouter) Get(c *gin.Context) { + manipulator, err := r.manipulatorService.GetById( + c.Params.ByName("setup_id"), + c.Params.ByName("manipulator_id"), + ) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, manipulator) +} + +func (r *SetupManipulatorRouter) Post(c *gin.Context) { + manipulatorPost := &ManipulatorPost{} + if err := c.Bind(manipulatorPost); err != nil { + return + } + newManipulator := &entity.Manipulator{ + Name: manipulatorPost.Name, + Description: manipulatorPost.Description, + URL: manipulatorPost.URL, + Alias: manipulatorPost.Alias, + } + err := r.manipulatorService.Add(c.Params.ByName("setup_id"), newManipulator) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusCreated, newManipulator) +} + +func (r *SetupManipulatorRouter) Patch(c *gin.Context) { + +} + +func (r *SetupManipulatorRouter) Delete(c *gin.Context) { + +} diff --git a/internal/handlers/setup_util.go b/internal/handlers/setup_util.go new file mode 100644 index 0000000..7f50448 --- /dev/null +++ b/internal/handlers/setup_util.go @@ -0,0 +1,42 @@ +package handlers + +import ( + "git.miem.hse.ru/hubman/configurator/internal/services" + "github.com/gin-gonic/gin" +) + +func CheckSetupId(setupService *services.SetupService) gin.HandlerFunc { + return func(c *gin.Context) { + setupId, err := setupService.CheckId(c.Params.ByName("setup_id")) + if err != nil { + BuildErrResp(c, err) + c.Abort() + return + } + //_, err = setupService.GetById(setupId) + c.Set("setupId", setupId) + c.Next() + return + } +} + +func getSetupId(c *gin.Context) int { + return c.MustGet("setupId").(int) +} + +func CheckExecutorId(executorService *services.ExecutorService) gin.HandlerFunc { + return func(c *gin.Context) { + executorId, err := executorService.CheckId(c.Params.ByName("executor_id")) + if err != nil { + BuildErrResp(c, err) + c.Abort() + return + } + c.Set("executorId", executorId) + return + } +} + +func getExecutorId(c *gin.Context) int { + return c.MustGet("executorId").(int) +} diff --git a/internal/repositories/device_repository.go b/internal/repositories/device_repository.go new file mode 100644 index 0000000..604779c --- /dev/null +++ b/internal/repositories/device_repository.go @@ -0,0 +1,156 @@ +package repositories + +import ( + "database/sql" + "errors" + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + sq "github.com/Masterminds/squirrel" + "github.com/jmoiron/sqlx" + "github.com/mattn/go-sqlite3" + "log" +) + +type DeviceRepository interface { + GetBySetupAndType(setupId int, dtype entity.DeviceType) ([]*entity.Device, error) + GetOne(setupId int, deviceType entity.DeviceType, id int) (*entity.Device, error) + Add(setupId int, deviceType entity.DeviceType, newDevice *entity.NewDevice) (*entity.Device, error) + Delete(setupId int, dtype entity.DeviceType, id int) error + Update(setupId int, dtype entity.DeviceType, id int, updDevice *entity.UpdDevice) error +} + +type SqliteDeviceRepository struct { + db *sqlx.DB +} + +func NewSqliteDeviceRepository(db *sqlx.DB) *SqliteDeviceRepository { + return &SqliteDeviceRepository{db: db} +} + +func (r *SqliteDeviceRepository) GetBySetupAndType(setupId int, dtype entity.DeviceType) ([]*entity.Device, error) { + query, params := sq.Select("*").From("Device").Where(sq.Eq{ + "setup_id": setupId, + "device_type": dtype, + }).MustSql() + + devices := make([]*entity.Device, 0, 0) + err := r.db.Select(&devices, query, params...) + if err != nil { + return nil, errs.ErrInternalError + } + return devices, nil +} + +func (r *SqliteDeviceRepository) Add( + setupId int, deviceType entity.DeviceType, newDevice *entity.NewDevice, +) (*entity.Device, error) { + res, err := sq.Insert("Device").SetMap(map[string]any{ + "setup_id": setupId, + "device_type": deviceType, + "name": newDevice.Name, + "alias": newDevice.Alias, + "description": newDevice.Description, + "url": newDevice.URL, + }).RunWith(r.db).Exec() + if err != nil { + return nil, wrapSqliteDeviceError(err) + } + + id, err := res.LastInsertId() + if err != nil { + return nil, errs.ErrInternalError + } + return &entity.Device{ + Id: int(id), + SetupId: setupId, + DeviceType: deviceType, + Name: newDevice.Name, + Alias: newDevice.Alias, + Description: newDevice.Description, + URL: newDevice.URL, + }, nil +} + +func (r *SqliteDeviceRepository) GetOne(setupId int, dtype entity.DeviceType, id int) (*entity.Device, error) { + query, params := sq.Select("*").From("Device").Where(sq.Eq{ + "id": id, + "setup_id": setupId, + "device_type": dtype, + }).MustSql() + row := r.db.QueryRowx(query, params...) + if row == nil || row.Err() != nil { + log.Println(row) + return nil, wrapSqliteDeviceError(row.Err()) + } + var device entity.Device + err := row.StructScan(&device) + if err != nil { + return nil, wrapSqliteDeviceError(err) + } + return &device, err +} + +func (r *SqliteDeviceRepository) Update(setupId int, dtype entity.DeviceType, id int, updDevice *entity.UpdDevice) error { + setMap := make(map[string]any) + if updDevice.Name != nil { + setMap["name"] = updDevice.Name + } + if updDevice.Description != nil { + setMap["description"] = updDevice.Description + } + if updDevice.URL != nil { + setMap["url"] = updDevice.URL + } + if updDevice.Alias != nil { + setMap["alias"] = updDevice.Alias + } + query, params := sq.Update("Device").SetMap(setMap).Where(sq.Eq{ + "setup_id": setupId, "device_type": dtype, "id": id, + }).MustSql() + res, err := r.db.Exec(query, params...) + if err != nil { + return wrapSqliteDeviceError(err) + } + affected, err := res.RowsAffected() + if err != nil { + return errs.ErrInternalError + } + if affected == 0 { + return errs.ErrDeviceNotFound + } + return nil +} + +func (r *SqliteDeviceRepository) Delete(setupId int, dtype entity.DeviceType, id int) error { + query, params := sq.Delete("Device").Where(sq.Eq{ + "setup_id": setupId, + "device_type": dtype, + "id": id, + }).MustSql() + res, err := r.db.Exec(query, params...) + if err != nil { + return wrapSqliteDeviceError(err) + } + affected, err := res.RowsAffected() + if err != nil { + return errs.ErrInternalError + } + if affected == 0 { + return errs.ErrDeviceNotFound + } + return nil +} + +func wrapSqliteDeviceError(err error) error { + var sqliteErr sqlite3.Error + if errors.Is(err, sql.ErrNoRows) { + return errs.ErrDeviceNotFound + } + if errors.As(err, &sqliteErr) { + if errors.Is(sqliteErr.Code, sqlite3.ErrConstraint) { + return errs.ErrDeviceIncorrectParams + } + return errs.ErrInternalError + } + return err +} diff --git a/internal/services/device_store_service.go b/internal/services/device_store_service.go new file mode 100644 index 0000000..de5a019 --- /dev/null +++ b/internal/services/device_store_service.go @@ -0,0 +1,45 @@ +package services + +import ( + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/internal/repositories" +) + +type DeviceStoreService struct { + deviceRepository repositories.DeviceRepository +} + +func NewDeviceStoreService(deviceRepository repositories.DeviceRepository) *DeviceStoreService { + return &DeviceStoreService{deviceRepository: deviceRepository} +} + +func (s *DeviceStoreService) GetBySetupAndType(setupId int, dtype entity.DeviceType) ([]*entity.Device, error) { + return s.deviceRepository.GetBySetupAndType(setupId, dtype) +} + +func (s *DeviceStoreService) Add( + setupId int, dtype entity.DeviceType, newDevice *entity.NewDevice, +) (*entity.Device, error) { + return s.deviceRepository.Add(setupId, dtype, newDevice) +} + +func (s *DeviceStoreService) GetOne( + setupId int, dtype entity.DeviceType, id int, +) (*entity.Device, error) { + return s.deviceRepository.GetOne(setupId, dtype, id) +} + +func (s *DeviceStoreService) Update( + setupId int, dtype entity.DeviceType, id int, updDevice *entity.UpdDevice, +) (*entity.Device, error) { + if err := s.deviceRepository.Update(setupId, dtype, id, updDevice); err != nil { + return nil, err + } + return s.deviceRepository.GetOne(setupId, dtype, id) +} + +func (s *DeviceStoreService) Delete( + setupId int, dtype entity.DeviceType, id int, +) error { + return s.deviceRepository.Delete(setupId, dtype, id) +} diff --git a/internal/services/executor_service.go b/internal/services/executor_service.go new file mode 100644 index 0000000..488812b --- /dev/null +++ b/internal/services/executor_service.go @@ -0,0 +1,43 @@ +package services + +import ( + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "strconv" +) + +type ExecutorService struct { + deviceStoreService *DeviceStoreService +} + +func NewExecutorService(deviceStoreService *DeviceStoreService) *ExecutorService { + return &ExecutorService{deviceStoreService: deviceStoreService} +} + +func (s *ExecutorService) GetList(setupId int) ([]*entity.Device, error) { + return s.deviceStoreService.GetBySetupAndType(setupId, entity.DeviceTypeExecutor) +} + +func (s *ExecutorService) CheckId(ExecutorIdStr string) (int, error) { + setupId, err := strconv.Atoi(ExecutorIdStr) + if err != nil { + return 0, errs.ErrDeviceNotFound + } + return setupId, nil +} + +func (s *ExecutorService) Add(setupId int, newDevice *entity.NewDevice) (*entity.Device, error) { + return s.deviceStoreService.Add(setupId, entity.DeviceTypeExecutor, newDevice) +} + +func (s *ExecutorService) GetOne(setupId int, id int) (*entity.Device, error) { + return s.deviceStoreService.GetOne(setupId, entity.DeviceTypeExecutor, id) +} + +func (s *ExecutorService) Update(setupId int, id int, updDevice *entity.UpdDevice) (*entity.Device, error) { + return s.deviceStoreService.Update(setupId, entity.DeviceTypeExecutor, id, updDevice) +} + +func (s *ExecutorService) Delete(setupId int, id int) error { + return s.deviceStoreService.Delete(setupId, entity.DeviceTypeExecutor, id) +} diff --git a/internal/services/setup_service.go b/internal/services/setup_service.go index a78e525..f006e6a 100644 --- a/internal/services/setup_service.go +++ b/internal/services/setup_service.go @@ -53,3 +53,11 @@ func (s *SetupService) Update(setupId string, name *string, description *string) } return s.r.GetById(id) } + +func (s *SetupService) CheckId(setupIdStr string) (int, error) { + setupId, err := strconv.Atoi(setupIdStr) + if err != nil { + return 0, errs.ErrSetupNotFound + } + return setupId, nil +} diff --git a/pkg/errs/errors.go b/pkg/errs/errors.go index 643cf9e..0fdbe4a 100644 --- a/pkg/errs/errors.go +++ b/pkg/errs/errors.go @@ -14,6 +14,9 @@ var ( ErrSetupNotFound = fmt.Errorf("SETUP_%w", ErrNotFound) ErrSetupIncorrectParams = fmt.Errorf("SETUP_%w", ErrIncorrectParams) + ErrDeviceIncorrectParams = fmt.Errorf("DEVICE_%w", ErrIncorrectParams) + ErrDeviceNotFound = fmt.Errorf("DEVICE_%w", ErrNotFound) + ErrManipulatorIncorrectParams = fmt.Errorf("MANIPULATOR_%w", ErrIncorrectParams) ErrManipulatorNotFound = fmt.Errorf("MANIPULATOR_%w", ErrNotFound) ) diff --git a/pkg/query-builder/builder.go b/pkg/query-builder/builder.go new file mode 100644 index 0000000..bb5ebbb --- /dev/null +++ b/pkg/query-builder/builder.go @@ -0,0 +1,21 @@ +package querybuilder + +import ( + "fmt" + "strings" +) + +type AttrValue struct { + Attr string + Value *string +} + +func BuildUpdateSetClause(pairs ...AttrValue) string { + var usedClauses []string + for _, pair := range pairs { + if pair.Value != nil { + usedClauses = append(usedClauses, fmt.Sprintf("%s=:%s", pair.Attr, pair.Attr)) + } + } + return strings.Join(usedClauses, ", ") +} -- GitLab From 2a614490b2690a08a9514673def60d2eeaff3bb7 Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Mon, 16 Oct 2023 02:53:59 +0300 Subject: [PATCH 03/12] add logic for device and executor --- internal/adapters/device_adapter.go | 14 +++ internal/app/app.go | 35 ++++---- internal/entity/device.go | 38 ++++---- internal/entity/device_descriptor.go | 22 +++++ internal/entity/rule.go | 54 ++++++++++++ internal/entity/setup.go | 18 +++- internal/handlers/schemas.go | 18 ---- internal/handlers/setup_manipulator_router.go | 59 +++++++------ internal/handlers/setup_util.go | 17 ++++ internal/handlers/setups_router.go | 28 +++--- .../device_descriptor_repository.go | 1 + internal/repositories/device_repository.go | 4 +- .../repositories/device_status_repository.go | 1 + .../repositories/manipulator_repository.go | 83 ----------------- internal/repositories/rule_repository.go | 18 ++++ internal/repositories/setup_repository.go | 88 ++++++++++--------- internal/services/active_setup_service.go | 16 ++++ internal/services/manipulator_service.go | 43 +++++++++ internal/services/rule_service.go | 26 ++++++ .../services/setup_manipulator_service.go | 70 --------------- internal/services/setup_service.go | 29 ++---- pkg/query-builder/builder.go | 21 ----- 22 files changed, 360 insertions(+), 343 deletions(-) create mode 100644 internal/adapters/device_adapter.go create mode 100644 internal/entity/device_descriptor.go create mode 100644 internal/entity/rule.go delete mode 100644 internal/handlers/schemas.go create mode 100644 internal/repositories/device_descriptor_repository.go create mode 100644 internal/repositories/device_status_repository.go delete mode 100644 internal/repositories/manipulator_repository.go create mode 100644 internal/repositories/rule_repository.go create mode 100644 internal/services/active_setup_service.go create mode 100644 internal/services/manipulator_service.go create mode 100644 internal/services/rule_service.go delete mode 100644 internal/services/setup_manipulator_service.go delete mode 100644 pkg/query-builder/builder.go diff --git a/internal/adapters/device_adapter.go b/internal/adapters/device_adapter.go new file mode 100644 index 0000000..19c9627 --- /dev/null +++ b/internal/adapters/device_adapter.go @@ -0,0 +1,14 @@ +package adapters + +import ( + "net/url" +) + +type DeviceAdapter struct { + deviceUrl url.URL +} + +//func (a *DeviceAdapter) CheckStatus() (Status, err) { +// a.deviceUrl.JoinPath("/") +// //resp, err := http.Get(a.deviceUrl) +//} diff --git a/internal/app/app.go b/internal/app/app.go index f7a10d4..1f0a319 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -25,12 +25,11 @@ func NewApp(conf *config.Config) *App { log.Fatalln("Can't init DB: ", err) } setupRepository := repositories.NewSqliteSetupRepository(db) - manipulatorRepository := repositories.NewSqliteManipulatorRepository(db) deviceRepository := repositories.NewSqliteDeviceRepository(db) setupService := services.NewSetupService(setupRepository) - manipulatorService := services.NewSetupManipulatorService(setupService, manipulatorRepository) deviceStoreService := services.NewDeviceStoreService(deviceRepository) + manipulatorService := services.NewManipulatorService(deviceStoreService) executorService := services.NewExecutorService(deviceStoreService) setupRouter := handlers.NewSetupRouter(setupService) @@ -50,10 +49,13 @@ func NewApp(conf *config.Config) *App { setupManipulatorNS.Use(handlers.CheckSetupId(setupService)) { setupManipulatorNS.GET("/", setupManipulatorRouter.GetList) - //httpEngine.POST("", setupManipulatorRouter.Post) - //httpEngine.GET("/:manipulator_id", setupManipulatorRouter.Get) - //httpEngine.PATCH("/:manipulator_id", setupManipulatorRouter.Patch) - //httpEngine.DELETE("/:manipulator_id", setupManipulatorRouter.Delete) + setupManipulatorNS.POST("/", setupManipulatorRouter.Post) + + manipulatoredNS := setupManipulatorNS.Group("/:manipuator_id") + manipulatoredNS.Use(handlers.CheckManipulatorId(executorService)) + manipulatoredNS.GET("/", setupManipulatorRouter.Get) + manipulatoredNS.PATCH("/", setupManipulatorRouter.Patch) + manipulatoredNS.DELETE("/", setupManipulatorRouter.Delete) } setupExecutorRouter := handlers.NewSetupExecutorRouter(executorService) @@ -88,20 +90,13 @@ func (a *App) Run() { func initDB(conf *config.Config) (*sqlx.DB, error) { db, err := sqlx.Connect("sqlite3", conf.Db.Path) - if err != nil { - return nil, err - } - _, err = db.Exec(entity.SetupDML) - if err != nil { - return nil, err - } - _, err = db.Exec(entity.ManipulatorDML) - if err != nil { - return nil, err - } - _, err = db.Exec(entity.DeviceDML) - if err != nil { - return nil, err + for _, dmlString := range []string{ + entity.SetupDML, entity.DeviceDML, entity.RuleDML, + } { + _, err = db.Exec(dmlString) + if err != nil { + return nil, err + } } return db, nil } diff --git a/internal/entity/device.go b/internal/entity/device.go index 0c123d2..1736e28 100644 --- a/internal/entity/device.go +++ b/internal/entity/device.go @@ -1,5 +1,11 @@ package entity +import ( + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "net/url" + "regexp" +) + type DeviceType string const DeviceTypeManipulator DeviceType = "manipulator" @@ -29,22 +35,22 @@ type UpdDevice struct { URL *string `db:"url" json:"url"` } -// func (m *Manipulator) CheckAlias() error { -// match, err := regexp.MatchString(`^[A-Za-z_][0-9A-Za-z_].*$`, m.Alias) -// -// if err != nil || !match { -// return errs.ErrManipulatorIncorrectParams -// } -// return nil -// } -// -// func (m *Manipulator) CheckURL() error { -// _, err := url.ParseRequestURI(m.URL) -// if err != nil { -// return errs.ErrManipulatorIncorrectParams -// } -// return nil -// } +func (m *Device) CheckAlias() error { + match, err := regexp.MatchString(`^[A-Za-z_][0-9A-Za-z_].*$`, m.Alias) + if err != nil || !match { + return errs.ErrManipulatorIncorrectParams + } + return nil +} + +func (m *Device) CheckURL() error { + _, err := url.ParseRequestURI(m.URL) + if err != nil { + return errs.ErrManipulatorIncorrectParams + } + return nil +} + const DeviceDML = ` CREATE TABLE IF NOT EXISTS Device ( id INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/internal/entity/device_descriptor.go b/internal/entity/device_descriptor.go new file mode 100644 index 0000000..a534366 --- /dev/null +++ b/internal/entity/device_descriptor.go @@ -0,0 +1,22 @@ +package entity + +type DeviceDescriptor struct { + Id int `db:"id" json:"id"` + DeviceId int `db:"device_id" json:"device_id"` + Code string `db:"code" json:"code"` + Description string `db:"description" json:"description"` + Schema string `db:"descriptor_schema" json:"schema"` +} + +const DeviceDescriptorDML = ` + CREATE TABLE IF NOT EXISTS DeviceDescriptor ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + device_id INTEGER NOT NULL, + code TEXT NOT NULL, + description TEXT NOT NULL, + descriptor_schema TEXT NOT NULL CHECK ( json(descriptor_schema) ), + + FOREIGN KEY (device_id) REFERENCES Device(id), + UNIQUE (code, device_id) +) +` diff --git a/internal/entity/rule.go b/internal/entity/rule.go new file mode 100644 index 0000000..88e8a8a --- /dev/null +++ b/internal/entity/rule.go @@ -0,0 +1,54 @@ +package entity + +type Rule struct { + Id int `db:"id" json:"id"` + Name string `db:"name" json:"name"` + SetupId int `db:"setup_id" json:"setup_id"` + ManipulatorId int `db:"manipulator_id" json:"manipulator_id"` + SignalDescriptorId int `db:"signal_descriptor_id" json:"signal_descriptor_id"` + ExecutorId int `db:"executor_id" json:"executor_id"` + CommandDescriptorId int `db:"command_descriptor_id" json:"command_descriptor_id"` + Trigger string `db:"trigger" json:"trigger"` + Transofrms []string `db:"transforms" json:"transforms"` +} + +type NewRule struct { + Name string `db:"name" json:"name"` + ManipulatorId int `db:"manipulator_id" json:"manipulator_id"` + SignalDescriptorId int `db:"signal_descriptor_id" json:"signal_descriptor_id"` + ExecutorId int `db:"executor_id" json:"executor_id"` + CommandDescriptorId int `db:"command_descriptor_id" json:"command_descriptor_id"` + Trigger string `db:"trigger" json:"trigger"` + Transofrms []string `db:"transforms" json:"transforms"` +} + +type UpdRule struct { + Name *string `db:"name" json:"name"` + ManipulatorId *int `db:"manipulator_id" json:"manipulator_id"` + SignalDescriptorId *int `db:"signal_descriptor_id" json:"signal_descriptor_id"` + ExecutorId *int `db:"executor_id" json:"executor_id"` + CommandDescriptorId *int `db:"command_descriptor_id" json:"command_descriptor_id"` + Trigger *string `db:"trigger" json:"trigger"` + Transofrms *[]string `db:"transforms" json:"transforms"` +} + +const RuleDML = ` + CREATE TABLE IF NOT EXISTS Rule ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + setup_id INTEGER NOT NULL, + manipulator_id INTEGER NOT NULL, + signal_descriptor_id INTEGER NOT NULL, + executor_id INTEGER NOT NULL, + command_descriptor_id INTEGER NOT NULL, + trigger TEXT NOT NULL CHECK ( json(trigger) ), + transofrms TEXT NOT NULL CHECK ( json_array(transofrms) ), + + FOREIGN KEY (setup_id) REFERENCES t_setup(id), + FOREIGN KEY (manipulator_id) REFERENCES Device(id), + FOREIGN KEY (executor_id) REFERENCES Device(id), + FOREIGN KEY (executor_id, command_descriptor_id) REFERENCES DeviceDescriptor(device_id, id), + FOREIGN KEY (manipulator_id, signal_descriptor_id) REFERENCES DeviceDescriptor(device_id, id), + UNIQUE (setup_id, name) +) +` diff --git a/internal/entity/setup.go b/internal/entity/setup.go index 7755947..722e320 100644 --- a/internal/entity/setup.go +++ b/internal/entity/setup.go @@ -1,13 +1,23 @@ package entity type Setup struct { - Id int `db:"id" json:"id"` - Name string `db:"name" json:"name"` - Description string `db:"description" json:"description"` + Id int `db:"id" json:"id"` + Name string `db:"name" json:"name"` + Description *string `db:"description" json:"description"` +} + +type NewSetup struct { + Name string `db:"name" json:"name"` + Description *string `db:"description" json:"description"` +} + +type UpdSetup struct { + Name *string `db:"name" json:"name"` + Description *string `db:"description" json:"description"` } const SetupDML = ` -CREATE TABLE IF NOT EXISTS t_setup ( +CREATE TABLE IF NOT EXISTS Setup ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, description TEXT diff --git a/internal/handlers/schemas.go b/internal/handlers/schemas.go deleted file mode 100644 index 5bc4a4a..0000000 --- a/internal/handlers/schemas.go +++ /dev/null @@ -1,18 +0,0 @@ -package handlers - -type SetupPost struct { - Name string `json:"name"` - Description string `json:"description"` -} - -type SetupPatch struct { - Name *string `json:"name"` - Description *string `json:"description"` -} - -type ManipulatorPost struct { - Name string `json:"name"` - URL string `json:"url"` - Description *string `json:"description"` - Alias string `json:"alias"` -} diff --git a/internal/handlers/setup_manipulator_router.go b/internal/handlers/setup_manipulator_router.go index 9490cba..c8706db 100644 --- a/internal/handlers/setup_manipulator_router.go +++ b/internal/handlers/setup_manipulator_router.go @@ -8,59 +8,62 @@ import ( ) type SetupManipulatorRouter struct { - manipulatorService *services.SetupManipulatorService + manipulatorService *services.ExecutorService } -func NewSetupManipulatorRouter(setupService *services.SetupManipulatorService) *SetupManipulatorRouter { - return &SetupManipulatorRouter{setupService} +func NewSetupManipulatorRouter(executorService *services.ExecutorService) *SetupManipulatorRouter { + return &SetupManipulatorRouter{executorService} } func (r *SetupManipulatorRouter) GetList(c *gin.Context) { - manipulators, err := r.manipulatorService.GetList( - c.Params.ByName("setup_id"), - ) + executors, err := r.manipulatorService.GetList(getSetupId(c)) if err != nil { BuildErrResp(c, err) return } - c.JSON(http.StatusOK, manipulators) + c.JSON(http.StatusOK, executors) } -func (r *SetupManipulatorRouter) Get(c *gin.Context) { - manipulator, err := r.manipulatorService.GetById( - c.Params.ByName("setup_id"), - c.Params.ByName("manipulator_id"), - ) +func (r *SetupManipulatorRouter) Post(c *gin.Context) { + newDevice := &entity.NewDevice{} + if err := c.BindJSON(newDevice); err != nil { + return + } + executor, err := r.manipulatorService.Add(getSetupId(c), newDevice) if err != nil { BuildErrResp(c, err) return } - c.JSON(http.StatusOK, manipulator) + c.JSON(http.StatusOK, executor) } -func (r *SetupManipulatorRouter) Post(c *gin.Context) { - manipulatorPost := &ManipulatorPost{} - if err := c.Bind(manipulatorPost); err != nil { - return - } - newManipulator := &entity.Manipulator{ - Name: manipulatorPost.Name, - Description: manipulatorPost.Description, - URL: manipulatorPost.URL, - Alias: manipulatorPost.Alias, - } - err := r.manipulatorService.Add(c.Params.ByName("setup_id"), newManipulator) +func (r *SetupManipulatorRouter) Get(c *gin.Context) { + executor, err := r.manipulatorService.GetOne(getSetupId(c), GetManipulatorId(c)) if err != nil { BuildErrResp(c, err) return } - c.JSON(http.StatusCreated, newManipulator) + c.JSON(http.StatusOK, executor) } func (r *SetupManipulatorRouter) Patch(c *gin.Context) { - + updDevice := &entity.UpdDevice{} + if err := c.BindJSON(updDevice); err != nil { + return + } + executor, err := r.manipulatorService.Update(getSetupId(c), GetManipulatorId(c), updDevice) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, executor) } func (r *SetupManipulatorRouter) Delete(c *gin.Context) { - + err := r.manipulatorService.Delete(getSetupId(c), GetManipulatorId(c)) + if err != nil { + BuildErrResp(c, err) + return + } + c.Status(http.StatusOK) } diff --git a/internal/handlers/setup_util.go b/internal/handlers/setup_util.go index 7f50448..da519dd 100644 --- a/internal/handlers/setup_util.go +++ b/internal/handlers/setup_util.go @@ -40,3 +40,20 @@ func CheckExecutorId(executorService *services.ExecutorService) gin.HandlerFunc func getExecutorId(c *gin.Context) int { return c.MustGet("executorId").(int) } + +func CheckManipulatorId(executorService *services.ExecutorService) gin.HandlerFunc { + return func(c *gin.Context) { + executorId, err := executorService.CheckId(c.Params.ByName("manipulator_id")) + if err != nil { + BuildErrResp(c, err) + c.Abort() + return + } + c.Set("manipulatorId", executorId) + return + } +} + +func GetManipulatorId(c *gin.Context) int { + return c.MustGet("manipulatorId").(int) +} diff --git a/internal/handlers/setups_router.go b/internal/handlers/setups_router.go index b0a6866..63e32a8 100644 --- a/internal/handlers/setups_router.go +++ b/internal/handlers/setups_router.go @@ -16,19 +16,16 @@ func NewSetupRouter(setupService *services.SetupService) *SetupRouter { } func (r *SetupRouter) Post(c *gin.Context) { - setupPost := &SetupPost{} - if err := c.Bind(setupPost); err != nil { + newSetup := &entity.NewSetup{} + if err := c.BindJSON(newSetup); err != nil { return } - newSetup := &entity.Setup{ - Name: setupPost.Name, - Description: setupPost.Description, - } - if err := r.setupService.Add(newSetup); err != nil { + setup, err := r.setupService.Add(newSetup) + if err != nil { BuildErrResp(c, err) return } - c.JSON(http.StatusCreated, newSetup) + c.JSON(http.StatusCreated, setup) } func (r *SetupRouter) GetList(c *gin.Context) { @@ -41,9 +38,7 @@ func (r *SetupRouter) GetList(c *gin.Context) { } func (r *SetupRouter) Get(c *gin.Context) { - setup, err := r.setupService.GetById( - c.Params.ByName("setup_id"), - ) + setup, err := r.setupService.GetById(getSetupId(c)) if err != nil { BuildErrResp(c, err) return @@ -52,7 +47,7 @@ func (r *SetupRouter) Get(c *gin.Context) { } func (r *SetupRouter) Delete(c *gin.Context) { - err := r.setupService.Delete(c.Params.ByName("setup_id")) + err := r.setupService.Delete(getSetupId(c)) if err != nil { BuildErrResp(c, err) return @@ -61,15 +56,12 @@ func (r *SetupRouter) Delete(c *gin.Context) { } func (r *SetupRouter) Patch(c *gin.Context) { - setupPatch := &SetupPatch{} - if err := c.Bind(setupPatch); err != nil { + updSetup := &entity.UpdSetup{} + if err := c.BindJSON(updSetup); err != nil { return } - setup, err := r.setupService.Update( - c.Params.ByName("setup_id"), - setupPatch.Name, - setupPatch.Description, + getSetupId(c), updSetup, ) if err != nil { BuildErrResp(c, err) diff --git a/internal/repositories/device_descriptor_repository.go b/internal/repositories/device_descriptor_repository.go new file mode 100644 index 0000000..3f43206 --- /dev/null +++ b/internal/repositories/device_descriptor_repository.go @@ -0,0 +1 @@ +package repositories diff --git a/internal/repositories/device_repository.go b/internal/repositories/device_repository.go index 604779c..df2f92e 100644 --- a/internal/repositories/device_repository.go +++ b/internal/repositories/device_repository.go @@ -8,7 +8,6 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/jmoiron/sqlx" "github.com/mattn/go-sqlite3" - "log" ) type DeviceRepository interface { @@ -79,7 +78,6 @@ func (r *SqliteDeviceRepository) GetOne(setupId int, dtype entity.DeviceType, id }).MustSql() row := r.db.QueryRowx(query, params...) if row == nil || row.Err() != nil { - log.Println(row) return nil, wrapSqliteDeviceError(row.Err()) } var device entity.Device @@ -142,10 +140,10 @@ func (r *SqliteDeviceRepository) Delete(setupId int, dtype entity.DeviceType, id } func wrapSqliteDeviceError(err error) error { - var sqliteErr sqlite3.Error if errors.Is(err, sql.ErrNoRows) { return errs.ErrDeviceNotFound } + var sqliteErr sqlite3.Error if errors.As(err, &sqliteErr) { if errors.Is(sqliteErr.Code, sqlite3.ErrConstraint) { return errs.ErrDeviceIncorrectParams diff --git a/internal/repositories/device_status_repository.go b/internal/repositories/device_status_repository.go new file mode 100644 index 0000000..3f43206 --- /dev/null +++ b/internal/repositories/device_status_repository.go @@ -0,0 +1 @@ +package repositories diff --git a/internal/repositories/manipulator_repository.go b/internal/repositories/manipulator_repository.go deleted file mode 100644 index 9e55159..0000000 --- a/internal/repositories/manipulator_repository.go +++ /dev/null @@ -1,83 +0,0 @@ -package repositories - -import ( - "errors" - "git.miem.hse.ru/hubman/configurator/internal/entity" - "git.miem.hse.ru/hubman/configurator/pkg/errs" - "github.com/jmoiron/sqlx" - "github.com/mattn/go-sqlite3" -) - -type ManipulatorRepository interface { - GetBySetupId(setupId int) ([]*entity.Manipulator, error) - GetById(id int) (*entity.Manipulator, error) - Add(manipulator *entity.Manipulator) error - //Update(id int, name *string, description *string) error - Delete(id int) error -} - -type SqliteManipulatorRepository struct { - db *sqlx.DB -} - -func NewSqliteManipulatorRepository(db *sqlx.DB) *SqliteManipulatorRepository { - return &SqliteManipulatorRepository{db: db} -} - -func (r SqliteManipulatorRepository) GetBySetupId(setupId int) ([]*entity.Manipulator, error) { - manipulators := []*entity.Manipulator{} - err := r.db.Select(&manipulators, "SELECT * FROM t_manipulator WHERE setup_id=$1", setupId) - if err != nil { - return nil, errs.ErrInternalError - } - return manipulators, nil -} - -func (r SqliteManipulatorRepository) GetById(id int) (*entity.Manipulator, error) { - manipulators := []*entity.Manipulator{} - err := r.db.Select(&manipulators, "SELECT * FROM t_manipulator WHERE id=$1", id) - if err != nil { - return nil, errs.ErrInternalError - } - - var manipulator *entity.Manipulator - if len(manipulators) == 1 { - manipulator = manipulators[0] - } else { - return nil, errs.ErrManipulatorNotFound - } - return manipulator, nil -} - -func (r SqliteManipulatorRepository) Add(manipulator *entity.Manipulator) error { - res, err := r.db.NamedExec(` - INSERT INTO t_manipulator(setup_id, name, alias, description, url) - VALUES (:setup_id, :name, :alias, :description, :url) RETURNING id`, - manipulator, - ) - if err != nil { - return wrapSqilteManipulatorError(err) - } - id, err := res.LastInsertId() - if err != nil { - return errs.ErrInternalError - } - manipulator.Id = int(id) - return nil -} - -func (r SqliteManipulatorRepository) Delete(id int) error { - - panic("implement me") -} - -func wrapSqilteManipulatorError(err error) error { - var sqliteErr sqlite3.Error - if errors.As(err, &sqliteErr) { - if errors.Is(sqliteErr.Code, sqlite3.ErrConstraint) { - return errs.ErrManipulatorIncorrectParams - } - return errs.ErrInternalError - } - return err -} diff --git a/internal/repositories/rule_repository.go b/internal/repositories/rule_repository.go new file mode 100644 index 0000000..cdbe341 --- /dev/null +++ b/internal/repositories/rule_repository.go @@ -0,0 +1,18 @@ +package repositories + +import ( + "git.miem.hse.ru/hubman/configurator/internal/entity" + "github.com/jmoiron/sqlx" +) + +type RuleRepository interface { + GetBySetupId(setupId int) ([]*entity.Rule, error) + GetById(setupId int, id int) (*entity.Rule, error) + Add(setupId int, rule *entity.NewRule) (*entity.Rule, error) + Update(setupId int, id int, updSetup *entity.UpdRule) error + Delete(setupId int, id int) error +} + +type SqliteRuleRepository struct { + db *sqlx.DB +} diff --git a/internal/repositories/setup_repository.go b/internal/repositories/setup_repository.go index ce351cb..65257af 100644 --- a/internal/repositories/setup_repository.go +++ b/internal/repositories/setup_repository.go @@ -1,11 +1,11 @@ package repositories import ( + "database/sql" "errors" - "fmt" "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/pkg/errs" - querybuilder "git.miem.hse.ru/hubman/configurator/pkg/query-builder" + sq "github.com/Masterminds/squirrel" "github.com/jmoiron/sqlx" "github.com/mattn/go-sqlite3" ) @@ -13,8 +13,8 @@ import ( type SetupRepository interface { GetAll() ([]*entity.Setup, error) GetById(id int) (*entity.Setup, error) - Add(setup *entity.Setup) error - Update(id int, name *string, description *string) error + Add(setup *entity.NewSetup) (*entity.Setup, error) + Update(id int, updSetup *entity.UpdSetup) error Delete(id int) error } @@ -27,8 +27,9 @@ func NewSqliteSetupRepository(db *sqlx.DB) *SqliteSetupRepository { } func (r SqliteSetupRepository) GetAll() ([]*entity.Setup, error) { - setups := []*entity.Setup{} - err := r.db.Select(&setups, "SELECT * FROM t_setup") + query, params := sq.Select("*").From("Setup").MustSql() + setups := make([]*entity.Setup, 0, 0) + err := r.db.Select(&setups, query, params...) if err != nil { return setups, errs.ErrInternalError } @@ -36,23 +37,24 @@ func (r SqliteSetupRepository) GetAll() ([]*entity.Setup, error) { } func (r SqliteSetupRepository) GetById(id int) (*entity.Setup, error) { - setups := []*entity.Setup{} - err := r.db.Select(&setups, "SELECT * FROM t_setup WHERE id=$1", id) - if err != nil { - return nil, errs.ErrInternalError + query, params := sq.Select("*").From("Setup").Where(sq.Eq{"id": id}).MustSql() + + row := r.db.QueryRowx(query, params...) + if row == nil || row.Err() != nil { + return nil, wrapSqliteSetupError(row.Err()) } var setup *entity.Setup - if len(setups) == 1 { - setup = setups[0] - } else { - return nil, errs.ErrSetupNotFound + err := row.StructScan(&setup) + if err != nil { + return nil, errs.ErrInternalError } return setup, nil } func (r SqliteSetupRepository) Delete(id int) error { - res, err := r.db.Exec("DELETE FROM t_setup WHERE id=$1", id) + query, params := sq.Delete("Setup").Where(sq.Eq{"id": id}).MustSql() + res, err := r.db.Exec(query, params...) if err != nil { return errs.ErrInternalError } @@ -61,43 +63,45 @@ func (r SqliteSetupRepository) Delete(id int) error { return errs.ErrInternalError } if affected == 0 { - return errs.ErrNotFound + return errs.ErrSetupNotFound } return nil } -func (r SqliteSetupRepository) Add(setup *entity.Setup) error { - res, err := r.db.NamedExec( - "INSERT INTO t_setup(name, description) VALUES (:name, :description) RETURNING id", - setup, - ) +func (r SqliteSetupRepository) Add(newSetup *entity.NewSetup) (*entity.Setup, error) { + query, param := sq.Insert("Setup").SetMap(map[string]any{ + "name": newSetup.Name, "description": newSetup.Description, + }).MustSql() + res, err := r.db.Exec(query, param...) if err != nil { - return wrapSqilteSetupError(err) + return nil, wrapSqliteSetupError(err) } id, err := res.LastInsertId() if err != nil { - return errs.ErrInternalError + return nil, wrapSqliteSetupError(err) } - setup.Id = int(id) - return nil + + return &entity.Setup{ + Id: int(id), + Name: newSetup.Name, + Description: newSetup.Description, + }, nil } -func (r SqliteSetupRepository) Update(id int, name *string, description *string) error { - setClause := querybuilder.BuildUpdateSetClause( - querybuilder.AttrValue{"name", name}, - querybuilder.AttrValue{"description", description}, - ) - res, err := r.db.NamedExec( - fmt.Sprintf("UPDATE t_setup SET %s WHERE id=:id", setClause), - struct { - Id int - Name *string - Description *string - }{id, name, description}, - ) +func (r SqliteSetupRepository) Update(id int, updSetup *entity.UpdSetup) error { + updMap := make(map[string]any) + if updSetup.Name != nil { + updMap["name"] = updSetup.Name + } + if updSetup.Name != nil { + updMap["description"] = updSetup.Description + } + + query, params := sq.Update("Setup").Where(sq.Eq{"id": id}).SetMap(updMap).MustSql() + res, err := r.db.Exec(query, params...) if err != nil { - return wrapSqilteSetupError(err) + return wrapSqliteSetupError(err) } affected, err := res.RowsAffected() if err != nil { @@ -109,7 +113,11 @@ func (r SqliteSetupRepository) Update(id int, name *string, description *string) return nil } -func wrapSqilteSetupError(err error) error { +func wrapSqliteSetupError(err error) error { + if errors.Is(err, sql.ErrNoRows) { + return errs.ErrSetupNotFound + } + var sqliteErr sqlite3.Error if errors.As(err, &sqliteErr) { if errors.Is(sqliteErr.Code, sqlite3.ErrConstraint) { diff --git a/internal/services/active_setup_service.go b/internal/services/active_setup_service.go new file mode 100644 index 0000000..76490f2 --- /dev/null +++ b/internal/services/active_setup_service.go @@ -0,0 +1,16 @@ +package services + +import "context" + +type ActiveSetupService struct { +} + +func (s *ActiveSetupService) RunDeviceChecker(ctx *context.Context) error { + _ = ctx + return nil + // DeviceStautsService.GetStatusesOrderByUpdate() + // + // DeviceStatusService.CheckStatus + manual check alive + // RuleService.BuildBindingForExecutor() + // Device.UpdateExecutorBinding() +} diff --git a/internal/services/manipulator_service.go b/internal/services/manipulator_service.go new file mode 100644 index 0000000..06ee665 --- /dev/null +++ b/internal/services/manipulator_service.go @@ -0,0 +1,43 @@ +package services + +import ( + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "strconv" +) + +type ManipulatorService struct { + deviceStoreService *DeviceStoreService +} + +func NewManipulatorService(deviceStoreService *DeviceStoreService) *ExecutorService { + return &ExecutorService{deviceStoreService: deviceStoreService} +} + +func (s *ManipulatorService) GetList(setupId int) ([]*entity.Device, error) { + return s.deviceStoreService.GetBySetupAndType(setupId, entity.DeviceTypeManipulator) +} + +func (s *ManipulatorService) CheckId(ExecutorIdStr string) (int, error) { + setupId, err := strconv.Atoi(ExecutorIdStr) + if err != nil { + return 0, errs.ErrDeviceNotFound + } + return setupId, nil +} + +func (s *ManipulatorService) Add(setupId int, newDevice *entity.NewDevice) (*entity.Device, error) { + return s.deviceStoreService.Add(setupId, entity.DeviceTypeManipulator, newDevice) +} + +func (s *ManipulatorService) GetOne(setupId int, id int) (*entity.Device, error) { + return s.deviceStoreService.GetOne(setupId, entity.DeviceTypeManipulator, id) +} + +func (s *ManipulatorService) Update(setupId int, id int, updDevice *entity.UpdDevice) (*entity.Device, error) { + return s.deviceStoreService.Update(setupId, entity.DeviceTypeManipulator, id, updDevice) +} + +func (s *ManipulatorService) Delete(setupId int, id int) error { + return s.deviceStoreService.Delete(setupId, entity.DeviceTypeManipulator, id) +} diff --git a/internal/services/rule_service.go b/internal/services/rule_service.go new file mode 100644 index 0000000..b496493 --- /dev/null +++ b/internal/services/rule_service.go @@ -0,0 +1,26 @@ +package services + +import ( + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/internal/repositories" +) + +type RuleService struct { + ruleRepository repositories.RuleRepository +} + +func (s *RuleService) Add(setupId int, newRule *entity.NewRule) (*entity.Rule, error) { + return s.ruleRepository.Add(setupId, newRule) +} + +func (s *RuleService) GetBySetupId(setupId int) ([]*entity.Rule, error) { + return s.ruleRepository.GetBySetupId(setupId) +} + +func (s *RuleService) GetById(setupId int, id int) (*entity.Rule, error) { + return s.ruleRepository.GetById(setupId, id) +} + +func (s *RuleService) Delete(setupId int, id int) error { + return s.ruleRepository.Delete(setupId, id) +} diff --git a/internal/services/setup_manipulator_service.go b/internal/services/setup_manipulator_service.go deleted file mode 100644 index a826279..0000000 --- a/internal/services/setup_manipulator_service.go +++ /dev/null @@ -1,70 +0,0 @@ -package services - -import ( - "git.miem.hse.ru/hubman/configurator/internal/entity" - "git.miem.hse.ru/hubman/configurator/internal/repositories" - "git.miem.hse.ru/hubman/configurator/pkg/errs" - "strconv" -) - -type SetupManipulatorService struct { - setupService *SetupService - setupRepository repositories.SetupRepository - manipulatorRepository repositories.ManipulatorRepository -} - -func NewSetupManipulatorService( - setupService *SetupService, - manipulatorRepository repositories.ManipulatorRepository, -) *SetupManipulatorService { - return &SetupManipulatorService{ - setupService: setupService, - manipulatorRepository: manipulatorRepository} -} - -func (s *SetupManipulatorService) GetList(setupIdStr string) ([]*entity.Manipulator, error) { - setup, err := s.setupService.GetById(setupIdStr) - if err != nil { - return nil, err - } - return s.manipulatorRepository.GetBySetupId(setup.Id) -} - -func (s *SetupManipulatorService) Add( - setupIdStr string, manipulator *entity.Manipulator, -) error { - setup, err := s.setupService.GetById(setupIdStr) - if err != nil { - return err - } - if manipulator == nil { - return errs.ErrInternalError - } - if err := manipulator.CheckAlias(); err != nil { - return err - } - if err := manipulator.CheckURL(); err != nil { - return err - } - manipulator.SetupId = setup.Id - return s.manipulatorRepository.Add(manipulator) -} - -func (s *SetupManipulatorService) GetById(setupIdStr, manipulatorIdStr string) (*entity.Manipulator, error) { - setupId, err := strconv.Atoi(setupIdStr) - if err != nil { - return nil, errs.ErrSetupNotFound - } - manipulatorId, err := strconv.Atoi(manipulatorIdStr) - if err != nil { - return nil, errs.ErrManipulatorNotFound - } - manipulator, err := s.manipulatorRepository.GetById(manipulatorId) - if err != nil { - return nil, err - } - if manipulator.SetupId != setupId { - return nil, errs.ErrManipulatorNotFound - } - return manipulator, nil -} diff --git a/internal/services/setup_service.go b/internal/services/setup_service.go index f006e6a..4ff8a25 100644 --- a/internal/services/setup_service.go +++ b/internal/services/setup_service.go @@ -19,37 +19,22 @@ func (s *SetupService) GetList() ([]*entity.Setup, error) { return s.r.GetAll() } -func (s *SetupService) Delete(setupId string) error { - id, err := strconv.Atoi(setupId) - if err != nil { - return errs.ErrSetupNotFound - } +func (s *SetupService) Delete(id int) error { return s.r.Delete(id) } -func (s *SetupService) GetById(setupId string) (*entity.Setup, error) { - id, err := strconv.Atoi(setupId) - if err != nil { - return nil, errs.ErrSetupNotFound - } +func (s *SetupService) GetById(id int) (*entity.Setup, error) { return s.r.GetById(id) } -func (s *SetupService) Add(setup *entity.Setup) error { - return s.r.Add(setup) +func (s *SetupService) Add(newSetup *entity.NewSetup) (*entity.Setup, error) { + return s.r.Add(newSetup) } -func (s *SetupService) Update(setupId string, name *string, description *string) (*entity.Setup, error) { - id, err := strconv.Atoi(setupId) +func (s *SetupService) Update(id int, updSetup *entity.UpdSetup) (*entity.Setup, error) { + err := s.r.Update(id, updSetup) if err != nil { - return nil, errs.ErrSetupNotFound - } - - if description != nil && name != nil { - err := s.r.Update(id, name, description) - if err != nil { - return nil, err - } + return nil, err } return s.r.GetById(id) } diff --git a/pkg/query-builder/builder.go b/pkg/query-builder/builder.go deleted file mode 100644 index bb5ebbb..0000000 --- a/pkg/query-builder/builder.go +++ /dev/null @@ -1,21 +0,0 @@ -package querybuilder - -import ( - "fmt" - "strings" -) - -type AttrValue struct { - Attr string - Value *string -} - -func BuildUpdateSetClause(pairs ...AttrValue) string { - var usedClauses []string - for _, pair := range pairs { - if pair.Value != nil { - usedClauses = append(usedClauses, fmt.Sprintf("%s=:%s", pair.Attr, pair.Attr)) - } - } - return strings.Join(usedClauses, ", ") -} -- GitLab From ab409aafb9ad961d121f08285078df9c00c30db3 Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Sat, 21 Oct 2023 15:48:14 +0300 Subject: [PATCH 04/12] add tx logic --- config.yaml | 6 +- internal/adapters/device_adapter.go | 14 --- internal/app/app.go | 58 +++------ internal/config/config.go | 12 +- internal/entity/device.go | 38 ++---- internal/entity/device_descriptor.go | 7 ++ internal/entity/manipulator.go | 49 -------- internal/entity/rule.go | 55 ++++---- internal/entity/setup.go | 14 +-- internal/handlers/setup_executor_router.go | 20 ++- internal/handlers/setup_manipulator_router.go | 25 +++- internal/handlers/setup_util.go | 6 +- internal/handlers/setups_router.go | 43 ++++++- .../device_descriptor_repository.go | 78 ++++++++++++ internal/repositories/device_repository.go | 31 ++--- internal/repositories/setup_repository.go | 117 +++++++++++++----- internal/repositories/utils.go | 45 +++++++ internal/services/device_store_service.go | 3 +- internal/services/manipulator_service.go | 4 +- internal/services/setup_service.go | 23 ++-- pkg/errs/errors.go | 4 +- pkg/transactions/transactions.go | 33 +++++ 22 files changed, 424 insertions(+), 261 deletions(-) delete mode 100644 internal/adapters/device_adapter.go delete mode 100644 internal/entity/manipulator.go create mode 100644 pkg/transactions/transactions.go diff --git a/config.yaml b/config.yaml index 40a16e5..8219879 100644 --- a/config.yaml +++ b/config.yaml @@ -1,4 +1,2 @@ -db: - path: "db.sqlite" -http: - port: 8080 \ No newline at end of file +db_path: "db.sqlite" +http_port: 8080 \ No newline at end of file diff --git a/internal/adapters/device_adapter.go b/internal/adapters/device_adapter.go deleted file mode 100644 index 19c9627..0000000 --- a/internal/adapters/device_adapter.go +++ /dev/null @@ -1,14 +0,0 @@ -package adapters - -import ( - "net/url" -) - -type DeviceAdapter struct { - deviceUrl url.URL -} - -//func (a *DeviceAdapter) CheckStatus() (Status, err) { -// a.deviceUrl.JoinPath("/") -// //resp, err := http.Get(a.deviceUrl) -//} diff --git a/internal/app/app.go b/internal/app/app.go index 1f0a319..569004a 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -7,6 +7,7 @@ import ( "git.miem.hse.ru/hubman/configurator/internal/handlers" "git.miem.hse.ru/hubman/configurator/internal/repositories" "git.miem.hse.ru/hubman/configurator/internal/services" + "git.miem.hse.ru/hubman/configurator/pkg/transactions" "github.com/gin-gonic/gin" "github.com/jmoiron/sqlx" _ "github.com/mattn/go-sqlite3" @@ -18,58 +19,34 @@ type App struct { conf *config.Config } +type IApplyRouter interface { + ApplyTo(httpEngine gin.IRouter) +} + func NewApp(conf *config.Config) *App { - httpEngine := gin.Default() db, err := initDB(conf) if err != nil { log.Fatalln("Can't init DB: ", err) } setupRepository := repositories.NewSqliteSetupRepository(db) deviceRepository := repositories.NewSqliteDeviceRepository(db) + //deviceDescriptorRepository := repositories.NewSqliteDeviceDescriptorRepository(db) setupService := services.NewSetupService(setupRepository) deviceStoreService := services.NewDeviceStoreService(deviceRepository) manipulatorService := services.NewManipulatorService(deviceStoreService) executorService := services.NewExecutorService(deviceStoreService) - setupRouter := handlers.NewSetupRouter(setupService) - httpEngine.GET("/setups", setupRouter.GetList) - httpEngine.POST("/setups", setupRouter.Post) - - setupedNS := httpEngine.Group("/setups/:setup_id") - setupedNS.Use(handlers.CheckSetupId(setupService)) - { - setupedNS.GET("/", setupRouter.Get) - setupedNS.PATCH("/", setupRouter.Patch) - setupedNS.DELETE("/", setupRouter.Delete) - } - - setupManipulatorRouter := handlers.NewSetupManipulatorRouter(manipulatorService) - setupManipulatorNS := httpEngine.Group("/setups/:setup_id/manipulators") - setupManipulatorNS.Use(handlers.CheckSetupId(setupService)) - { - setupManipulatorNS.GET("/", setupManipulatorRouter.GetList) - setupManipulatorNS.POST("/", setupManipulatorRouter.Post) + httpEngine := gin.Default() + httpEngine.Use(transactions.InjectStorage(db)) - manipulatoredNS := setupManipulatorNS.Group("/:manipuator_id") - manipulatoredNS.Use(handlers.CheckManipulatorId(executorService)) - manipulatoredNS.GET("/", setupManipulatorRouter.Get) - manipulatoredNS.PATCH("/", setupManipulatorRouter.Patch) - manipulatoredNS.DELETE("/", setupManipulatorRouter.Delete) + routerToApply := []IApplyRouter{ + handlers.NewSetupRouter(setupService), + handlers.NewSetupManipulatorRouter(setupService, manipulatorService), + handlers.NewSetupExecutorRouter(setupService, executorService), } - - setupExecutorRouter := handlers.NewSetupExecutorRouter(executorService) - setupExecutorNS := httpEngine.Group("/setups/:setup_id/executors") - setupExecutorNS.Use(handlers.CheckSetupId(setupService)) - { - setupExecutorNS.GET("/", setupExecutorRouter.GetList) - setupExecutorNS.POST("/", setupExecutorRouter.Post) - - executoredNS := setupExecutorNS.Group("/:executor_id") - executoredNS.Use(handlers.CheckExecutorId(executorService)) - executoredNS.GET("/", setupExecutorRouter.Get) - executoredNS.PATCH("/", setupExecutorRouter.Patch) - executoredNS.DELETE("/", setupExecutorRouter.Delete) + for _, router := range routerToApply { + router.ApplyTo(httpEngine) } return &App{ @@ -81,7 +58,7 @@ func NewApp(conf *config.Config) *App { func (a *App) Run() { log.Println("Server started") err := a.httpEngine.Run( - fmt.Sprintf(":%d", a.conf.Http.Port), + fmt.Sprintf(":%d", a.conf.HTTPPort), ) if err != nil { log.Fatalln("Error in htpEngine: ", err) @@ -89,9 +66,10 @@ func (a *App) Run() { } func initDB(conf *config.Config) (*sqlx.DB, error) { - db, err := sqlx.Connect("sqlite3", conf.Db.Path) + db, err := sqlx.Connect("sqlite3", conf.DBPath) for _, dmlString := range []string{ - entity.SetupDML, entity.DeviceDML, entity.RuleDML, + entity.SetupDML, entity.DeviceDML, entity.DeviceDescriptorDML, + entity.RuleDML, } { _, err = db.Exec(dmlString) if err != nil { diff --git a/internal/config/config.go b/internal/config/config.go index fc5eb8a..bce22d6 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -6,16 +6,8 @@ import ( ) type Config struct { - Http `yaml:"http"` - Db `yaml:"db""` -} - -type Http struct { - Port int `yaml:"port"` -} - -type Db struct { - Path string `yaml:"path"` + HTTPPort int `yaml:"http_port"` + DBPath string `yaml:"db_path"` } func NewConfig(path string) (*Config, error) { diff --git a/internal/entity/device.go b/internal/entity/device.go index 1736e28..022c754 100644 --- a/internal/entity/device.go +++ b/internal/entity/device.go @@ -3,7 +3,6 @@ package entity import ( "git.miem.hse.ru/hubman/configurator/pkg/errs" "net/url" - "regexp" ) type DeviceType string @@ -12,41 +11,27 @@ const DeviceTypeManipulator DeviceType = "manipulator" const DeviceTypeExecutor DeviceType = "executor" type Device struct { - Id int `db:"id" json:"id"` - DeviceType DeviceType `db:"device_type" json:"-"` - SetupId int `db:"setup_id" json:"setup_id"` - Name string `db:"name" json:"name"` - Alias string `db:"alias" json:"alias"` - Description *string `db:"description" json:"description"` - URL string `db:"url" json:"url"` + Id int `db:"id" json:"id"` + DeviceType DeviceType `db:"device_type" json:"-"` + SetupId int `db:"setup_id" json:"setup_id"` + Name string `db:"name" json:"name"` + URL string `db:"url" json:"url"` } type NewDevice struct { - Name string `db:"name" json:"name"` - Alias string `db:"alias" json:"alias"` - Description *string `db:"description" json:"description"` - URL string `db:"url" json:"url"` + Name string `db:"name" json:"name"` + URL string `db:"url" json:"url"` } type UpdDevice struct { - Name *string `db:"name" json:"name"` - Alias *string `db:"alias" json:"alias"` - Description *string `db:"description" json:"description"` - URL *string `db:"url" json:"url"` -} - -func (m *Device) CheckAlias() error { - match, err := regexp.MatchString(`^[A-Za-z_][0-9A-Za-z_].*$`, m.Alias) - if err != nil || !match { - return errs.ErrManipulatorIncorrectParams - } - return nil + Name *string `db:"name" json:"name"` + URL *string `db:"url" json:"url"` } func (m *Device) CheckURL() error { _, err := url.ParseRequestURI(m.URL) if err != nil { - return errs.ErrManipulatorIncorrectParams + return errs.ErrDeviceIncorrectParams } return nil } @@ -57,13 +42,10 @@ CREATE TABLE IF NOT EXISTS Device ( device_type TEXT NOT NULL CHECK (device_type IN ('manipulator', 'executor')), setup_id INTEGER, name TEXT NOT NULL, - alias TEXT NOT NULL, - description TEXT, url TEXT NOT NULL, FOREIGN KEY (setup_id) REFERENCES t_setup(id), UNIQUE (setup_id, name), - UNIQUE (setup_id, alias), UNIQUE (setup_id, device_type, url) ) ` diff --git a/internal/entity/device_descriptor.go b/internal/entity/device_descriptor.go index a534366..f15fc98 100644 --- a/internal/entity/device_descriptor.go +++ b/internal/entity/device_descriptor.go @@ -8,6 +8,13 @@ type DeviceDescriptor struct { Schema string `db:"descriptor_schema" json:"schema"` } +type NewDeviceDescriptor struct { + DeviceId int `db:"device_id" json:"device_id"` + Code string `db:"code" json:"code"` + Description string `db:"description" json:"description"` + Schema string `db:"descriptor_schema" json:"schema"` +} + const DeviceDescriptorDML = ` CREATE TABLE IF NOT EXISTS DeviceDescriptor ( id INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/internal/entity/manipulator.go b/internal/entity/manipulator.go deleted file mode 100644 index 318f88c..0000000 --- a/internal/entity/manipulator.go +++ /dev/null @@ -1,49 +0,0 @@ -package entity - -import ( - "git.miem.hse.ru/hubman/configurator/pkg/errs" - "net/url" - "regexp" -) - -type Manipulator struct { - Id int `db:"id" json:"id"` - SetupId int `db:"setup_id" json:"setup_id"` - Name string `db:"name" json:"name"` - Alias string `db:"alias" json:"alias"` - Description *string `db:"description" json:"description"` - URL string `db:"url" json:"url"` -} - -func (m *Manipulator) CheckAlias() error { - match, err := regexp.MatchString(`^[A-Za-z_][0-9A-Za-z_].*$`, m.Alias) - - if err != nil || !match { - return errs.ErrManipulatorIncorrectParams - } - return nil -} - -func (m *Manipulator) CheckURL() error { - _, err := url.ParseRequestURI(m.URL) - if err != nil { - return errs.ErrManipulatorIncorrectParams - } - return nil -} - -const ManipulatorDML = ` -CREATE TABLE IF NOT EXISTS t_manipulator ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - setup_id INTEGER, - name TEXT NOT NULL, - alias TEXT NOT NULL, - description TEXT, - url TEXT, - - FOREIGN KEY (setup_id) REFERENCES t_setup(id), - UNIQUE (setup_id, name), - UNIQUE (setup_id, alias), - UNIQUE (setup_id, url) -) -` diff --git a/internal/entity/rule.go b/internal/entity/rule.go index 88e8a8a..72e7921 100644 --- a/internal/entity/rule.go +++ b/internal/entity/rule.go @@ -1,54 +1,51 @@ package entity type Rule struct { - Id int `db:"id" json:"id"` - Name string `db:"name" json:"name"` - SetupId int `db:"setup_id" json:"setup_id"` - ManipulatorId int `db:"manipulator_id" json:"manipulator_id"` - SignalDescriptorId int `db:"signal_descriptor_id" json:"signal_descriptor_id"` - ExecutorId int `db:"executor_id" json:"executor_id"` - CommandDescriptorId int `db:"command_descriptor_id" json:"command_descriptor_id"` - Trigger string `db:"trigger" json:"trigger"` - Transofrms []string `db:"transforms" json:"transforms"` + Id int `db:"id" json:"id"` + SetupId int `db:"setup_id" json:"setup_id"` + ManipulatorId int `db:"manipulator_id" json:"manipulator_id"` + SignalDescriptorId int `db:"signal_descriptor_id" json:"signal_descriptor_id"` + ExecutorId int `db:"executor_id" json:"executor_id"` + CommandDescriptorId int `db:"command_descriptor_id" json:"command_descriptor_id"` + Trigger string `db:"trigger" json:"trigger"` + Logic map[string]any `db:"logic" json:"logic"` } type NewRule struct { - Name string `db:"name" json:"name"` - ManipulatorId int `db:"manipulator_id" json:"manipulator_id"` - SignalDescriptorId int `db:"signal_descriptor_id" json:"signal_descriptor_id"` - ExecutorId int `db:"executor_id" json:"executor_id"` - CommandDescriptorId int `db:"command_descriptor_id" json:"command_descriptor_id"` - Trigger string `db:"trigger" json:"trigger"` - Transofrms []string `db:"transforms" json:"transforms"` + ManipulatorId int `db:"manipulator_id" json:"manipulator_id"` + SignalDescriptorId int `db:"signal_descriptor_id" json:"signal_descriptor_id"` + ExecutorId int `db:"executor_id" json:"executor_id"` + CommandDescriptorId int `db:"command_descriptor_id" json:"command_descriptor_id"` + Trigger string `db:"trigger" json:"trigger"` + Logic map[string]any `db:"logic" json:"logic"` } type UpdRule struct { - Name *string `db:"name" json:"name"` - ManipulatorId *int `db:"manipulator_id" json:"manipulator_id"` - SignalDescriptorId *int `db:"signal_descriptor_id" json:"signal_descriptor_id"` - ExecutorId *int `db:"executor_id" json:"executor_id"` - CommandDescriptorId *int `db:"command_descriptor_id" json:"command_descriptor_id"` - Trigger *string `db:"trigger" json:"trigger"` - Transofrms *[]string `db:"transforms" json:"transforms"` + ManipulatorId *int `db:"manipulator_id" json:"manipulator_id"` + SignalDescriptorId *int `db:"signal_descriptor_id" json:"signal_descriptor_id"` + ExecutorId *int `db:"executor_id" json:"executor_id"` + CommandDescriptorId *int `db:"command_descriptor_id" json:"command_descriptor_id"` + Trigger *string `db:"trigger" json:"trigger"` + Logic *map[string]any `db:"logic" json:"logic"` } const RuleDML = ` CREATE TABLE IF NOT EXISTS Rule ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, + description TEXT NOT NULL, setup_id INTEGER NOT NULL, manipulator_id INTEGER NOT NULL, signal_descriptor_id INTEGER NOT NULL, executor_id INTEGER NOT NULL, command_descriptor_id INTEGER NOT NULL, trigger TEXT NOT NULL CHECK ( json(trigger) ), - transofrms TEXT NOT NULL CHECK ( json_array(transofrms) ), + logic TEXT NOT NULL CHECK ( json(logic) ), - FOREIGN KEY (setup_id) REFERENCES t_setup(id), - FOREIGN KEY (manipulator_id) REFERENCES Device(id), - FOREIGN KEY (executor_id) REFERENCES Device(id), + FOREIGN KEY (setup_id) REFERENCES Setup(id), + FOREIGN KEY (manipulator_id) REFERENCES Setup(id), + FOREIGN KEY (executor_id) REFERENCES Setup(id), FOREIGN KEY (executor_id, command_descriptor_id) REFERENCES DeviceDescriptor(device_id, id), - FOREIGN KEY (manipulator_id, signal_descriptor_id) REFERENCES DeviceDescriptor(device_id, id), - UNIQUE (setup_id, name) + FOREIGN KEY (manipulator_id, signal_descriptor_id) REFERENCES DeviceDescriptor(device_id, id) ) ` diff --git a/internal/entity/setup.go b/internal/entity/setup.go index 722e320..7fa4ca9 100644 --- a/internal/entity/setup.go +++ b/internal/entity/setup.go @@ -1,25 +1,21 @@ package entity type Setup struct { - Id int `db:"id" json:"id"` - Name string `db:"name" json:"name"` - Description *string `db:"description" json:"description"` + Id int `db:"id" json:"id"` + Name string `db:"name" json:"name"` } type NewSetup struct { - Name string `db:"name" json:"name"` - Description *string `db:"description" json:"description"` + Name string `db:"name" json:"name"` } type UpdSetup struct { - Name *string `db:"name" json:"name"` - Description *string `db:"description" json:"description"` + Name *string `db:"name" json:"name"` } const SetupDML = ` CREATE TABLE IF NOT EXISTS Setup ( id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL UNIQUE, - description TEXT + name TEXT NOT NULL UNIQUE ) ` diff --git a/internal/handlers/setup_executor_router.go b/internal/handlers/setup_executor_router.go index dca9b86..a6ca8d1 100644 --- a/internal/handlers/setup_executor_router.go +++ b/internal/handlers/setup_executor_router.go @@ -8,11 +8,27 @@ import ( ) type SetupExecutorRouter struct { + setupService *services.SetupService executorService *services.ExecutorService } -func NewSetupExecutorRouter(executorService *services.ExecutorService) *SetupExecutorRouter { - return &SetupExecutorRouter{executorService} +func NewSetupExecutorRouter(setupService *services.SetupService, executorService *services.ExecutorService) *SetupExecutorRouter { + return &SetupExecutorRouter{setupService, executorService} +} + +func (r *SetupExecutorRouter) ApplyTo(httpEngine gin.IRouter) { + setupExecutorNS := httpEngine.Group("/setups/:setup_id/executors") + setupExecutorNS.Use(CheckSetupId(r.setupService)) + { + setupExecutorNS.GET("/", r.GetList) + setupExecutorNS.POST("/", r.Post) + + executoredNS := setupExecutorNS.Group("/:executor_id") + executoredNS.Use(CheckExecutorId(r.executorService)) + executoredNS.GET("/", r.Get) + executoredNS.PATCH("/", r.Patch) + executoredNS.DELETE("/", r.Delete) + } } func (r *SetupExecutorRouter) GetList(c *gin.Context) { diff --git a/internal/handlers/setup_manipulator_router.go b/internal/handlers/setup_manipulator_router.go index c8706db..1f41126 100644 --- a/internal/handlers/setup_manipulator_router.go +++ b/internal/handlers/setup_manipulator_router.go @@ -8,11 +8,30 @@ import ( ) type SetupManipulatorRouter struct { - manipulatorService *services.ExecutorService + setupService *services.SetupService + manipulatorService *services.ManipulatorService } -func NewSetupManipulatorRouter(executorService *services.ExecutorService) *SetupManipulatorRouter { - return &SetupManipulatorRouter{executorService} +func (r *SetupManipulatorRouter) ApplyTo(httpEngine gin.IRouter) { + setupManipulatorNS := httpEngine.Group("/setups/:setup_id/manipulators") + setupManipulatorNS.Use(CheckSetupId(r.setupService)) + { + setupManipulatorNS.GET("/", r.GetList) + setupManipulatorNS.POST("/", r.Post) + + manipulatoredNS := setupManipulatorNS.Group("/:manipulator_id") + manipulatoredNS.Use(CheckManipulatorId(r.manipulatorService)) + manipulatoredNS.GET("/", r.Get) + manipulatoredNS.PATCH("/", r.Patch) + manipulatoredNS.DELETE("/", r.Delete) + } +} + +func NewSetupManipulatorRouter(setupService *services.SetupService, manipulatorService *services.ManipulatorService) *SetupManipulatorRouter { + return &SetupManipulatorRouter{ + setupService, + manipulatorService, + } } func (r *SetupManipulatorRouter) GetList(c *gin.Context) { diff --git a/internal/handlers/setup_util.go b/internal/handlers/setup_util.go index da519dd..587bc01 100644 --- a/internal/handlers/setup_util.go +++ b/internal/handlers/setup_util.go @@ -41,15 +41,15 @@ func getExecutorId(c *gin.Context) int { return c.MustGet("executorId").(int) } -func CheckManipulatorId(executorService *services.ExecutorService) gin.HandlerFunc { +func CheckManipulatorId(manipulatorService *services.ManipulatorService) gin.HandlerFunc { return func(c *gin.Context) { - executorId, err := executorService.CheckId(c.Params.ByName("manipulator_id")) + manipulatorId, err := manipulatorService.CheckId(c.Params.ByName("manipulator_id")) if err != nil { BuildErrResp(c, err) c.Abort() return } - c.Set("manipulatorId", executorId) + c.Set("manipulatorId", manipulatorId) return } } diff --git a/internal/handlers/setups_router.go b/internal/handlers/setups_router.go index 63e32a8..dcd45c9 100644 --- a/internal/handlers/setups_router.go +++ b/internal/handlers/setups_router.go @@ -3,6 +3,8 @@ package handlers import ( "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/internal/services" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "git.miem.hse.ru/hubman/configurator/pkg/transactions" "github.com/gin-gonic/gin" "net/http" ) @@ -15,12 +17,23 @@ func NewSetupRouter(setupService *services.SetupService) *SetupRouter { return &SetupRouter{setupService: setupService} } +func (r *SetupRouter) ApplyTo(httpEngine gin.IRouter) { + httpEngine.GET("/setups", r.GetList) + httpEngine.POST("/setups", r.Post) + setupedNS := httpEngine.Group("/setups/:setup_id") + setupedNS.Use(CheckSetupId(r.setupService)) + setupedNS.GET("/", r.Get) + setupedNS.PATCH("/", r.Patch) + setupedNS.DELETE("/", r.Delete) +} + func (r *SetupRouter) Post(c *gin.Context) { + ctx := c.Request.Context() newSetup := &entity.NewSetup{} if err := c.BindJSON(newSetup); err != nil { return } - setup, err := r.setupService.Add(newSetup) + setup, err := r.setupService.Add(ctx, newSetup) if err != nil { BuildErrResp(c, err) return @@ -29,7 +42,8 @@ func (r *SetupRouter) Post(c *gin.Context) { } func (r *SetupRouter) GetList(c *gin.Context) { - setups, err := r.setupService.GetList() + ctx := c.Request.Context() + setups, err := r.setupService.GetList(ctx) if err != nil { BuildErrResp(c, err) return @@ -38,7 +52,8 @@ func (r *SetupRouter) GetList(c *gin.Context) { } func (r *SetupRouter) Get(c *gin.Context) { - setup, err := r.setupService.GetById(getSetupId(c)) + ctx := c.Request.Context() + setup, err := r.setupService.GetById(ctx, getSetupId(c)) if err != nil { BuildErrResp(c, err) return @@ -47,7 +62,8 @@ func (r *SetupRouter) Get(c *gin.Context) { } func (r *SetupRouter) Delete(c *gin.Context) { - err := r.setupService.Delete(getSetupId(c)) + ctx := c.Request.Context() + err := r.setupService.Delete(ctx, getSetupId(c)) if err != nil { BuildErrResp(c, err) return @@ -56,16 +72,33 @@ func (r *SetupRouter) Delete(c *gin.Context) { } func (r *SetupRouter) Patch(c *gin.Context) { + ctx := c.Request.Context() updSetup := &entity.UpdSetup{} if err := c.BindJSON(updSetup); err != nil { return } + + tx, err := transactions.ExtractStorage(c).Beginx() + if err != nil { + BuildErrResp(c, err) + return + } + ctx = transactions.InjectTx(ctx, tx) + setup, err := r.setupService.Update( - getSetupId(c), updSetup, + ctx, getSetupId(c), updSetup, ) + if err != nil { + if err2 := tx.Rollback(); err2 != nil { + err = errs.ErrInternalError + } BuildErrResp(c, err) return } + + if err2 := tx.Commit(); err2 != nil { + BuildErrResp(c, errs.ErrInternalError) + } c.JSON(http.StatusOK, setup) } diff --git a/internal/repositories/device_descriptor_repository.go b/internal/repositories/device_descriptor_repository.go index 3f43206..bcf4612 100644 --- a/internal/repositories/device_descriptor_repository.go +++ b/internal/repositories/device_descriptor_repository.go @@ -1 +1,79 @@ package repositories + +import ( + "database/sql" + "errors" + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + sq "github.com/Masterminds/squirrel" + "github.com/jmoiron/sqlx" + "github.com/mattn/go-sqlite3" +) + +type DeviceDescriptorRepository interface { + Add(newDescriptor *entity.NewDeviceDescriptor) (*entity.DeviceDescriptor, error) + DeleteByDeviceId(deviceId int) error +} + +type SqliteDeviceDescriptorRepository struct { + db *sqlx.DB +} + +func NewSqliteDeviceDescriptorRepository(db *sqlx.DB) *SqliteDeviceDescriptorRepository { + return &SqliteDeviceDescriptorRepository{db: db} +} + +func (r *SqliteDeviceDescriptorRepository) Add(newDescriptor *entity.NewDeviceDescriptor) (*entity.DeviceDescriptor, error) { + res, err := sq.Insert("DeviceDescriptor").SetMap(map[string]any{ + "device_id": newDescriptor.DeviceId, + "code": newDescriptor.Code, + "description": newDescriptor.Description, + }).RunWith(r.db).Exec() + if err != nil { + return nil, wrapSqliteDeviceDescriptorError(err) + } + + id, err := res.LastInsertId() + if err != nil { + return nil, errs.ErrInternalError + } + return &entity.DeviceDescriptor{ + Id: int(id), + DeviceId: newDescriptor.DeviceId, + Code: newDescriptor.Code, + Description: newDescriptor.Description, + Schema: newDescriptor.Schema, + }, nil +} + +func (r *SqliteDeviceDescriptorRepository) DeleteByDeviceId(deviceId int) error { + query, params := sq.Delete("DeviceDescriptor").Where(sq.Eq{ + "device_id": deviceId, + }).MustSql() + res, err := r.db.Exec(query, params...) + if err != nil { + return wrapSqliteDeviceDescriptorError(err) + } + affected, err := res.RowsAffected() + if err != nil { + return errs.ErrInternalError + } + if affected == 0 { + return errs.ErrDeviceNotFound + } + return nil +} + +func wrapSqliteDeviceDescriptorError(err error) error { + if errors.Is(err, sql.ErrNoRows) { + return errs.ErrDeviceDescriptorNotFound + } + var sqliteErr sqlite3.Error + if errors.As(err, &sqliteErr) { + if errors.Is(sqliteErr.Code, sqlite3.ErrConstraint) { + return errs.ErrDeviceDescriptorIncorrectParams + } + return errs.ErrInternalError + } + return err +} diff --git a/internal/repositories/device_repository.go b/internal/repositories/device_repository.go index df2f92e..69c18c2 100644 --- a/internal/repositories/device_repository.go +++ b/internal/repositories/device_repository.go @@ -8,6 +8,7 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/jmoiron/sqlx" "github.com/mattn/go-sqlite3" + "log" ) type DeviceRepository interface { @@ -47,8 +48,6 @@ func (r *SqliteDeviceRepository) Add( "setup_id": setupId, "device_type": deviceType, "name": newDevice.Name, - "alias": newDevice.Alias, - "description": newDevice.Description, "url": newDevice.URL, }).RunWith(r.db).Exec() if err != nil { @@ -60,13 +59,11 @@ func (r *SqliteDeviceRepository) Add( return nil, errs.ErrInternalError } return &entity.Device{ - Id: int(id), - SetupId: setupId, - DeviceType: deviceType, - Name: newDevice.Name, - Alias: newDevice.Alias, - Description: newDevice.Description, - URL: newDevice.URL, + Id: int(id), + SetupId: setupId, + DeviceType: deviceType, + Name: newDevice.Name, + URL: newDevice.URL, }, nil } @@ -80,28 +77,26 @@ func (r *SqliteDeviceRepository) GetOne(setupId int, dtype entity.DeviceType, id if row == nil || row.Err() != nil { return nil, wrapSqliteDeviceError(row.Err()) } - var device entity.Device - err := row.StructScan(&device) + device := new(entity.Device) + err := row.StructScan(device) if err != nil { return nil, wrapSqliteDeviceError(err) } - return &device, err + return device, err } func (r *SqliteDeviceRepository) Update(setupId int, dtype entity.DeviceType, id int, updDevice *entity.UpdDevice) error { + log.Println("updDevice", updDevice) + if (updDevice == nil) || (*updDevice == entity.UpdDevice{}) { + return errs.ErrDeviceIncorrectParams + } setMap := make(map[string]any) if updDevice.Name != nil { setMap["name"] = updDevice.Name } - if updDevice.Description != nil { - setMap["description"] = updDevice.Description - } if updDevice.URL != nil { setMap["url"] = updDevice.URL } - if updDevice.Alias != nil { - setMap["alias"] = updDevice.Alias - } query, params := sq.Update("Device").SetMap(setMap).Where(sq.Eq{ "setup_id": setupId, "device_type": dtype, "id": id, }).MustSql() diff --git a/internal/repositories/setup_repository.go b/internal/repositories/setup_repository.go index 65257af..eac88d2 100644 --- a/internal/repositories/setup_repository.go +++ b/internal/repositories/setup_repository.go @@ -1,6 +1,7 @@ package repositories import ( + "context" "database/sql" "errors" "git.miem.hse.ru/hubman/configurator/internal/entity" @@ -8,14 +9,15 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/jmoiron/sqlx" "github.com/mattn/go-sqlite3" + "log" ) type SetupRepository interface { - GetAll() ([]*entity.Setup, error) - GetById(id int) (*entity.Setup, error) - Add(setup *entity.NewSetup) (*entity.Setup, error) - Update(id int, updSetup *entity.UpdSetup) error - Delete(id int) error + GetAll(ctx context.Context) ([]*entity.Setup, error) + GetById(ctx context.Context, id int) (*entity.Setup, error) + Add(ctx context.Context, setup *entity.NewSetup) (*entity.Setup, error) + Update(ctx context.Context, id int, updSetup *entity.UpdSetup) error + Delete(ctx context.Context, id int) error } type SqliteSetupRepository struct { @@ -26,91 +28,144 @@ func NewSqliteSetupRepository(db *sqlx.DB) *SqliteSetupRepository { return &SqliteSetupRepository{db: db} } -func (r SqliteSetupRepository) GetAll() ([]*entity.Setup, error) { +func (r SqliteSetupRepository) GetAll(ctx context.Context) ([]*entity.Setup, error) { + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, rtx.RollbackTxOrIgnore() + } query, params := sq.Select("*").From("Setup").MustSql() setups := make([]*entity.Setup, 0, 0) - err := r.db.Select(&setups, query, params...) + err = rtx.tx.Select(&setups, query, params...) + if err != nil { - return setups, errs.ErrInternalError + if err := rtx.RollbackTxOrIgnore(); err != nil { + return nil, err + } + return setups, err + } + + if err := rtx.RollbackTxOrIgnore(); err != nil { + return nil, err } return setups, nil } -func (r SqliteSetupRepository) GetById(id int) (*entity.Setup, error) { +func (r SqliteSetupRepository) GetById(ctx context.Context, id int) (*entity.Setup, error) { + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + query, params := sq.Select("*").From("Setup").Where(sq.Eq{"id": id}).MustSql() - row := r.db.QueryRowx(query, params...) + row := rtx.tx.QueryRowx(query, params...) if row == nil || row.Err() != nil { + if err := rtx.RollbackTxOrIgnore(); err != nil { + return nil, err + } return nil, wrapSqliteSetupError(row.Err()) } - var setup *entity.Setup - err := row.StructScan(&setup) + setup := new(entity.Setup) + err = row.StructScan(setup) if err != nil { - return nil, errs.ErrInternalError + if err := rtx.RollbackTxOrIgnore(); err != nil { + return nil, err + } + return nil, wrapSqliteSetupError(err) } - return setup, nil + + return setup, rtx.RollbackTxOrIgnore() } -func (r SqliteSetupRepository) Delete(id int) error { +func (r SqliteSetupRepository) Delete(ctx context.Context, id int) error { + rtx, err := extractSqlxTxOrNew(ctx, r.db) query, params := sq.Delete("Setup").Where(sq.Eq{"id": id}).MustSql() - res, err := r.db.Exec(query, params...) + res, err := rtx.tx.Exec(query, params...) if err != nil { + if err := rtx.RollbackTxOrIgnore(); err != nil { + return err + } return errs.ErrInternalError } affected, err := res.RowsAffected() if err != nil { + if err := rtx.RollbackTxOrIgnore(); err != nil { + return err + } return errs.ErrInternalError } if affected == 0 { + if err := rtx.RollbackTxOrIgnore(); err != nil { + return err + } return errs.ErrSetupNotFound } - return nil + return rtx.CommitTxOrIgnore() } -func (r SqliteSetupRepository) Add(newSetup *entity.NewSetup) (*entity.Setup, error) { +func (r SqliteSetupRepository) Add(ctx context.Context, newSetup *entity.NewSetup) (*entity.Setup, error) { + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } query, param := sq.Insert("Setup").SetMap(map[string]any{ - "name": newSetup.Name, "description": newSetup.Description, + "name": newSetup.Name, }).MustSql() - res, err := r.db.Exec(query, param...) + res, err := rtx.tx.Exec(query, param...) if err != nil { + if err := rtx.RollbackTxOrIgnore(); err != nil { + return nil, err + } return nil, wrapSqliteSetupError(err) } id, err := res.LastInsertId() if err != nil { + if err := rtx.RollbackTxOrIgnore(); err != nil { + return nil, err + } return nil, wrapSqliteSetupError(err) } - + log.Println(err) return &entity.Setup{ - Id: int(id), - Name: newSetup.Name, - Description: newSetup.Description, - }, nil + Id: int(id), + Name: newSetup.Name, + }, rtx.CommitTxOrIgnore() } -func (r SqliteSetupRepository) Update(id int, updSetup *entity.UpdSetup) error { +func (r SqliteSetupRepository) Update(ctx context.Context, id int, updSetup *entity.UpdSetup) error { + rtx, err := extractSqlxTxOrNew(ctx, r.db) updMap := make(map[string]any) - if updSetup.Name != nil { - updMap["name"] = updSetup.Name + if (updSetup == nil) || (*updSetup == entity.UpdSetup{}) { + return errs.ErrDeviceIncorrectParams } if updSetup.Name != nil { - updMap["description"] = updSetup.Description + updMap["name"] = updSetup.Name } query, params := sq.Update("Setup").Where(sq.Eq{"id": id}).SetMap(updMap).MustSql() - res, err := r.db.Exec(query, params...) + res, err := rtx.tx.Exec(query, params...) if err != nil { + if err := rtx.RollbackTxOrIgnore(); err != nil { + return err + } return wrapSqliteSetupError(err) } affected, err := res.RowsAffected() if err != nil { + if err := rtx.RollbackTxOrIgnore(); err != nil { + return err + } return errs.ErrInternalError } if affected == 0 { + if err := rtx.RollbackTxOrIgnore(); err != nil { + return err + } return errs.ErrSetupNotFound } - return nil + return rtx.CommitTxOrIgnore() } func wrapSqliteSetupError(err error) error { diff --git a/internal/repositories/utils.go b/internal/repositories/utils.go index 3f43206..b5002b8 100644 --- a/internal/repositories/utils.go +++ b/internal/repositories/utils.go @@ -1 +1,46 @@ package repositories + +import ( + "context" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "git.miem.hse.ru/hubman/configurator/pkg/transactions" + "github.com/jmoiron/sqlx" + "log" +) + +func extractSqlxTxOrNew(ctx context.Context, db *sqlx.DB) (*RTx, error) { + tx := transactions.ExtractTx(ctx) + if tx != nil { + return &RTx{tx, false}, nil + } + tx, err := db.Beginx() + if err != nil { + return &RTx{tx, false}, errs.ErrInternalError + } + return &RTx{tx, true}, nil +} + +type RTx struct { + tx *sqlx.Tx + isNewTx bool +} + +func (rtx *RTx) CommitTxOrIgnore() error { + if !rtx.isNewTx { + return nil + } + return rtx.tx.Commit() +} + +func (rtx *RTx) RollbackTxOrIgnore() error { + if !rtx.isNewTx { + return nil + } + err := rtx.tx.Rollback() + if err != nil { + log.Println(err) + return errs.ErrInternalError + } + log.Println("rollback") + return nil +} diff --git a/internal/services/device_store_service.go b/internal/services/device_store_service.go index de5a019..c8dc1ea 100644 --- a/internal/services/device_store_service.go +++ b/internal/services/device_store_service.go @@ -6,7 +6,8 @@ import ( ) type DeviceStoreService struct { - deviceRepository repositories.DeviceRepository + deviceRepository repositories.DeviceRepository + deviceDescriptorRepository repositories.DeviceDescriptorRepository } func NewDeviceStoreService(deviceRepository repositories.DeviceRepository) *DeviceStoreService { diff --git a/internal/services/manipulator_service.go b/internal/services/manipulator_service.go index 06ee665..a89f961 100644 --- a/internal/services/manipulator_service.go +++ b/internal/services/manipulator_service.go @@ -10,8 +10,8 @@ type ManipulatorService struct { deviceStoreService *DeviceStoreService } -func NewManipulatorService(deviceStoreService *DeviceStoreService) *ExecutorService { - return &ExecutorService{deviceStoreService: deviceStoreService} +func NewManipulatorService(deviceStoreService *DeviceStoreService) *ManipulatorService { + return &ManipulatorService{deviceStoreService: deviceStoreService} } func (s *ManipulatorService) GetList(setupId int) ([]*entity.Device, error) { diff --git a/internal/services/setup_service.go b/internal/services/setup_service.go index 4ff8a25..58dcdbd 100644 --- a/internal/services/setup_service.go +++ b/internal/services/setup_service.go @@ -1,6 +1,7 @@ package services import ( + "context" "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/internal/repositories" "git.miem.hse.ru/hubman/configurator/pkg/errs" @@ -15,28 +16,28 @@ func NewSetupService(r repositories.SetupRepository) *SetupService { return &SetupService{r: r} } -func (s *SetupService) GetList() ([]*entity.Setup, error) { - return s.r.GetAll() +func (s *SetupService) GetList(ctx context.Context) ([]*entity.Setup, error) { + return s.r.GetAll(ctx) } -func (s *SetupService) Delete(id int) error { - return s.r.Delete(id) +func (s *SetupService) Delete(ctx context.Context, id int) error { + return s.r.Delete(ctx, id) } -func (s *SetupService) GetById(id int) (*entity.Setup, error) { - return s.r.GetById(id) +func (s *SetupService) GetById(ctx context.Context, id int) (*entity.Setup, error) { + return s.r.GetById(ctx, id) } -func (s *SetupService) Add(newSetup *entity.NewSetup) (*entity.Setup, error) { - return s.r.Add(newSetup) +func (s *SetupService) Add(ctx context.Context, newSetup *entity.NewSetup) (*entity.Setup, error) { + return s.r.Add(ctx, newSetup) } -func (s *SetupService) Update(id int, updSetup *entity.UpdSetup) (*entity.Setup, error) { - err := s.r.Update(id, updSetup) +func (s *SetupService) Update(ctx context.Context, id int, updSetup *entity.UpdSetup) (*entity.Setup, error) { + err := s.r.Update(ctx, id, updSetup) if err != nil { return nil, err } - return s.r.GetById(id) + return s.r.GetById(ctx, id) } func (s *SetupService) CheckId(setupIdStr string) (int, error) { diff --git a/pkg/errs/errors.go b/pkg/errs/errors.go index 0fdbe4a..b8e38c0 100644 --- a/pkg/errs/errors.go +++ b/pkg/errs/errors.go @@ -17,6 +17,6 @@ var ( ErrDeviceIncorrectParams = fmt.Errorf("DEVICE_%w", ErrIncorrectParams) ErrDeviceNotFound = fmt.Errorf("DEVICE_%w", ErrNotFound) - ErrManipulatorIncorrectParams = fmt.Errorf("MANIPULATOR_%w", ErrIncorrectParams) - ErrManipulatorNotFound = fmt.Errorf("MANIPULATOR_%w", ErrNotFound) + ErrDeviceDescriptorIncorrectParams = fmt.Errorf("DEVICE_DESCRIPTOR_%w", ErrIncorrectParams) + ErrDeviceDescriptorNotFound = fmt.Errorf("DEVICE_DESCRIPTOR_%w", ErrNotFound) ) diff --git a/pkg/transactions/transactions.go b/pkg/transactions/transactions.go new file mode 100644 index 0000000..6aec3b3 --- /dev/null +++ b/pkg/transactions/transactions.go @@ -0,0 +1,33 @@ +package transactions + +import ( + "github.com/gin-gonic/gin" + "github.com/jmoiron/sqlx" + "golang.org/x/net/context" +) + +type txTypeKey struct{} + +const GinCtxStorage = "Storage" + +func InjectStorage(db *sqlx.DB) gin.HandlerFunc { + return func(c *gin.Context) { + c.Set(GinCtxStorage, db) + } +} + +func InjectTx(ctx context.Context, tx *sqlx.Tx) context.Context { + return context.WithValue(ctx, txTypeKey{}, tx) +} + +func ExtractTx(ctx context.Context) *sqlx.Tx { + tx := ctx.Value(txTypeKey{}) + if tx == nil { + return nil + } + return tx.(*sqlx.Tx) +} + +func ExtractStorage(c *gin.Context) *sqlx.DB { + return c.MustGet(GinCtxStorage).(*sqlx.DB) +} -- GitLab From d3230ed619533ef76937acff6431163caa84ca2d Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Mon, 23 Oct 2023 02:56:04 +0300 Subject: [PATCH 05/12] oh no.... --- go.mod | 18 ++++- go.sum | 38 +++++++++ internal/app/app.go | 12 ++- internal/entity/device.go | 5 ++ internal/entity/device_descriptor.go | 17 +++- internal/handlers/setup_executor_router.go | 42 ++++++++-- internal/handlers/setup_manipulator_router.go | 8 +- .../device_descriptor_repository.go | 62 ++++++++++++--- internal/repositories/device_repository.go | 78 ++++++++++++++----- internal/repositories/setup_repository.go | 10 +-- internal/repositories/utils.go | 17 ++-- internal/services/device_store_service.go | 56 ++++++++++--- internal/services/executor_service.go | 44 ++++++++--- internal/services/manipulator_service.go | 12 +-- 14 files changed, 333 insertions(+), 86 deletions(-) diff --git a/go.mod b/go.mod index 25a39b2..83b0523 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,17 @@ module git.miem.hse.ru/hubman/configurator go 1.20 require ( - github.com/BurntSushi/toml v1.2.1 // indirect + git.miem.hse.ru/hubman/hubman-lib v0.0.12 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/bytedance/sonic v1.10.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/diegoholiveira/jsonlogic/v3 v3.3.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect @@ -15,7 +21,9 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.15.5 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/google/uuid v1.3.1 // indirect github.com/ilyakaznacheev/cleanenv v1.5.0 // indirect + github.com/invopop/jsonschema v0.12.0 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -23,13 +31,21 @@ require ( github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.4 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-sqlite3 v1.14.17 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oapi-codegen/runtime v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/redis/go-redis/v9 v9.1.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.5.0 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/net v0.16.0 // indirect diff --git a/go.sum b/go.sum index 84908a2..9e08dd7 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,23 @@ +git.miem.hse.ru/hubman/hubman-lib v0.0.11 h1:xXUBFnPOtmeFhfyp0m2pXy2d8vJ0fnqLQ3QfG0M3GA4= +git.miem.hse.ru/hubman/hubman-lib v0.0.11/go.mod h1:tH+GFZj6eQtZTqIcT4j0rExq5EVFpCZz+9uMFEqZkhg= +git.miem.hse.ru/hubman/hubman-lib v0.0.12 h1:XMOaIAxLtdZxJBL45BTMfPCKJv3OCR2gk4A/osM3gZY= +git.miem.hse.ru/hubman/hubman-lib v0.0.12/go.mod h1:tH+GFZj6eQtZTqIcT4j0rExq5EVFpCZz+9uMFEqZkhg= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= @@ -14,6 +26,10 @@ github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/diegoholiveira/jsonlogic/v3 v3.3.0 h1:XdIxQ+ICFcQB9tVf46cmiCkc5K9MN8Sh/x+XDHL+iXM= +github.com/diegoholiveira/jsonlogic/v3 v3.3.0/go.mod h1:9oE8z9G+0OMxOoLHF3fhek3KuqD5CBqM0B6XFL08MSg= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -32,12 +48,17 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4= github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk= +github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= +github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -51,24 +72,35 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6Fm github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo= +github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= +github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -79,6 +111,12 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y= golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= diff --git a/internal/app/app.go b/internal/app/app.go index 569004a..1d71ce9 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -30,10 +30,10 @@ func NewApp(conf *config.Config) *App { } setupRepository := repositories.NewSqliteSetupRepository(db) deviceRepository := repositories.NewSqliteDeviceRepository(db) - //deviceDescriptorRepository := repositories.NewSqliteDeviceDescriptorRepository(db) + deviceDescriptorRepository := repositories.NewSqliteDeviceDescriptorRepository(db) setupService := services.NewSetupService(setupRepository) - deviceStoreService := services.NewDeviceStoreService(deviceRepository) + deviceStoreService := services.NewDeviceStoreService(deviceRepository, deviceDescriptorRepository) manipulatorService := services.NewManipulatorService(deviceStoreService) executorService := services.NewExecutorService(deviceStoreService) @@ -66,7 +66,13 @@ func (a *App) Run() { } func initDB(conf *config.Config) (*sqlx.DB, error) { - db, err := sqlx.Connect("sqlite3", conf.DBPath) + db, err := sqlx.Connect( + "sqlite3", + fmt.Sprintf("%s?%s", conf.DBPath, "_txlock=DEFERRED"), + ) + if err != nil { + return nil, err + } for _, dmlString := range []string{ entity.SetupDML, entity.DeviceDML, entity.DeviceDescriptorDML, entity.RuleDML, diff --git a/internal/entity/device.go b/internal/entity/device.go index 022c754..4dccf29 100644 --- a/internal/entity/device.go +++ b/internal/entity/device.go @@ -28,6 +28,11 @@ type UpdDevice struct { URL *string `db:"url" json:"url"` } +type ExtendedDeviceModel struct { + *Device + Signals []*DeviceDescriptor `json:"signals"` +} + func (m *Device) CheckURL() error { _, err := url.ParseRequestURI(m.URL) if err != nil { diff --git a/internal/entity/device_descriptor.go b/internal/entity/device_descriptor.go index f15fc98..aba5ba7 100644 --- a/internal/entity/device_descriptor.go +++ b/internal/entity/device_descriptor.go @@ -1,5 +1,10 @@ package entity +import ( + "encoding/json" + "git.miem.hse.ru/hubman/hubman-lib/client" +) + type DeviceDescriptor struct { Id int `db:"id" json:"id"` DeviceId int `db:"device_id" json:"device_id"` @@ -9,19 +14,27 @@ type DeviceDescriptor struct { } type NewDeviceDescriptor struct { - DeviceId int `db:"device_id" json:"device_id"` Code string `db:"code" json:"code"` Description string `db:"description" json:"description"` Schema string `db:"descriptor_schema" json:"schema"` } +func BuildNewCommandDescriptor(data client.CommandDescriptor) *NewDeviceDescriptor { + jsonBytes, _ := json.Marshal(data.Args) + return &NewDeviceDescriptor{ + data.Code, + data.Description, + string(jsonBytes), + } +} + const DeviceDescriptorDML = ` CREATE TABLE IF NOT EXISTS DeviceDescriptor ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_id INTEGER NOT NULL, code TEXT NOT NULL, description TEXT NOT NULL, - descriptor_schema TEXT NOT NULL CHECK ( json(descriptor_schema) ), + descriptor_schema TEXT NOT NULL, FOREIGN KEY (device_id) REFERENCES Device(id), UNIQUE (code, device_id) diff --git a/internal/handlers/setup_executor_router.go b/internal/handlers/setup_executor_router.go index a6ca8d1..d9440ce 100644 --- a/internal/handlers/setup_executor_router.go +++ b/internal/handlers/setup_executor_router.go @@ -3,6 +3,8 @@ package handlers import ( "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/internal/services" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "git.miem.hse.ru/hubman/configurator/pkg/transactions" "github.com/gin-gonic/gin" "net/http" ) @@ -32,7 +34,8 @@ func (r *SetupExecutorRouter) ApplyTo(httpEngine gin.IRouter) { } func (r *SetupExecutorRouter) GetList(c *gin.Context) { - executors, err := r.executorService.GetList(getSetupId(c)) + ctx := c.Request.Context() + executors, err := r.executorService.GetList(ctx, getSetupId(c)) if err != nil { BuildErrResp(c, err) return @@ -41,20 +44,34 @@ func (r *SetupExecutorRouter) GetList(c *gin.Context) { } func (r *SetupExecutorRouter) Post(c *gin.Context) { + ctx := c.Request.Context() newDevice := &entity.NewDevice{} if err := c.BindJSON(newDevice); err != nil { return } - executor, err := r.executorService.Add(getSetupId(c), newDevice) + tx, err := transactions.ExtractStorage(c).Beginx() if err != nil { BuildErrResp(c, err) return } + ctx = transactions.InjectTx(ctx, tx) + defer tx.Rollback() + + executor, err := r.executorService.Add(ctx, getSetupId(c), newDevice) + if err != nil { + BuildErrResp(c, err) + return + } + if err := tx.Commit(); err != nil { + BuildErrResp(c, errs.ErrInternalError) + return + } c.JSON(http.StatusOK, executor) } func (r *SetupExecutorRouter) Get(c *gin.Context) { - executor, err := r.executorService.GetOne(getSetupId(c), getExecutorId(c)) + ctx := c.Request.Context() + executor, err := r.executorService.GetOne(ctx, getSetupId(c), getExecutorId(c)) if err != nil { BuildErrResp(c, err) return @@ -63,20 +80,35 @@ func (r *SetupExecutorRouter) Get(c *gin.Context) { } func (r *SetupExecutorRouter) Patch(c *gin.Context) { + ctx := c.Request.Context() + updDevice := &entity.UpdDevice{} if err := c.BindJSON(updDevice); err != nil { return } - executor, err := r.executorService.Update(getSetupId(c), getExecutorId(c), updDevice) + tx, err := transactions.ExtractStorage(c).Beginx() if err != nil { BuildErrResp(c, err) return } + defer tx.Rollback() + + executor, err := r.executorService.Update(ctx, getSetupId(c), getExecutorId(c), updDevice) + if err != nil { + BuildErrResp(c, err) + return + } + + if err := tx.Commit(); err != nil { + BuildErrResp(c, errs.ErrInternalError) + return + } c.JSON(http.StatusOK, executor) } func (r *SetupExecutorRouter) Delete(c *gin.Context) { - err := r.executorService.Delete(getSetupId(c), getExecutorId(c)) + ctx := c.Request.Context() + err := r.executorService.Delete(ctx, getSetupId(c), getExecutorId(c)) if err != nil { BuildErrResp(c, err) return diff --git a/internal/handlers/setup_manipulator_router.go b/internal/handlers/setup_manipulator_router.go index 1f41126..36444c8 100644 --- a/internal/handlers/setup_manipulator_router.go +++ b/internal/handlers/setup_manipulator_router.go @@ -57,12 +57,12 @@ func (r *SetupManipulatorRouter) Post(c *gin.Context) { } func (r *SetupManipulatorRouter) Get(c *gin.Context) { - executor, err := r.manipulatorService.GetOne(getSetupId(c), GetManipulatorId(c)) + manipulator, err := r.manipulatorService.GetOne(getSetupId(c), GetManipulatorId(c)) if err != nil { BuildErrResp(c, err) return } - c.JSON(http.StatusOK, executor) + c.JSON(http.StatusOK, manipulator) } func (r *SetupManipulatorRouter) Patch(c *gin.Context) { @@ -70,12 +70,12 @@ func (r *SetupManipulatorRouter) Patch(c *gin.Context) { if err := c.BindJSON(updDevice); err != nil { return } - executor, err := r.manipulatorService.Update(getSetupId(c), GetManipulatorId(c), updDevice) + manipulator, err := r.manipulatorService.Update(getSetupId(c), GetManipulatorId(c), updDevice) if err != nil { BuildErrResp(c, err) return } - c.JSON(http.StatusOK, executor) + c.JSON(http.StatusOK, manipulator) } func (r *SetupManipulatorRouter) Delete(c *gin.Context) { diff --git a/internal/repositories/device_descriptor_repository.go b/internal/repositories/device_descriptor_repository.go index bcf4612..aa13dda 100644 --- a/internal/repositories/device_descriptor_repository.go +++ b/internal/repositories/device_descriptor_repository.go @@ -1,6 +1,7 @@ package repositories import ( + "context" "database/sql" "errors" "git.miem.hse.ru/hubman/configurator/internal/entity" @@ -11,8 +12,9 @@ import ( ) type DeviceDescriptorRepository interface { - Add(newDescriptor *entity.NewDeviceDescriptor) (*entity.DeviceDescriptor, error) - DeleteByDeviceId(deviceId int) error + Add(ctx context.Context, deviceId int, newDescriptor *entity.NewDeviceDescriptor) (*entity.DeviceDescriptor, error) + DeleteByDeviceId(ctx context.Context, deviceId int) error + GetByDeviceId(ctx context.Context, deviceId int) ([]*entity.DeviceDescriptor, error) } type SqliteDeviceDescriptorRepository struct { @@ -23,34 +25,50 @@ func NewSqliteDeviceDescriptorRepository(db *sqlx.DB) *SqliteDeviceDescriptorRep return &SqliteDeviceDescriptorRepository{db: db} } -func (r *SqliteDeviceDescriptorRepository) Add(newDescriptor *entity.NewDeviceDescriptor) (*entity.DeviceDescriptor, error) { +func (r *SqliteDeviceDescriptorRepository) Add(ctx context.Context, deviceId int, newDescriptor *entity.NewDeviceDescriptor) (*entity.DeviceDescriptor, error) { + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + defer rtx.RollbackTxOrIgnore() + res, err := sq.Insert("DeviceDescriptor").SetMap(map[string]any{ - "device_id": newDescriptor.DeviceId, - "code": newDescriptor.Code, - "description": newDescriptor.Description, - }).RunWith(r.db).Exec() + "device_id": deviceId, + "code": newDescriptor.Code, + "description": newDescriptor.Description, + "descriptor_schema": newDescriptor.Schema, + }).RunWith(rtx.tx).ExecContext(ctx) if err != nil { return nil, wrapSqliteDeviceDescriptorError(err) } - id, err := res.LastInsertId() if err != nil { return nil, errs.ErrInternalError } + + if err := rtx.CommitTxOrIgnore(); err != nil { + return nil, errs.ErrInternalError + } return &entity.DeviceDescriptor{ Id: int(id), - DeviceId: newDescriptor.DeviceId, + DeviceId: deviceId, Code: newDescriptor.Code, Description: newDescriptor.Description, Schema: newDescriptor.Schema, }, nil } -func (r *SqliteDeviceDescriptorRepository) DeleteByDeviceId(deviceId int) error { +func (r *SqliteDeviceDescriptorRepository) DeleteByDeviceId(ctx context.Context, deviceId int) error { + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return err + } + defer rtx.RollbackTxOrIgnore() + query, params := sq.Delete("DeviceDescriptor").Where(sq.Eq{ "device_id": deviceId, }).MustSql() - res, err := r.db.Exec(query, params...) + res, err := rtx.tx.ExecContext(ctx, query, params...) if err != nil { return wrapSqliteDeviceDescriptorError(err) } @@ -61,9 +79,31 @@ func (r *SqliteDeviceDescriptorRepository) DeleteByDeviceId(deviceId int) error if affected == 0 { return errs.ErrDeviceNotFound } + + if err := rtx.CommitTxOrIgnore(); err != nil { + return errs.ErrInternalError + } return nil } +func (r *SqliteDeviceDescriptorRepository) GetByDeviceId(ctx context.Context, deviceId int) ([]*entity.DeviceDescriptor, error) { + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + defer rtx.RollbackTxOrIgnore() + + query, params := sq.Select("*").From("DeviceDescriptor").Where(sq.Eq{ + "device_id": deviceId, + }).MustSql() + var descriptors []*entity.DeviceDescriptor + err = rtx.tx.SelectContext(ctx, &descriptors, query, params...) + if err != nil { + return descriptors, wrapSqliteDeviceDescriptorError(err) + } + return descriptors, nil +} + func wrapSqliteDeviceDescriptorError(err error) error { if errors.Is(err, sql.ErrNoRows) { return errs.ErrDeviceDescriptorNotFound diff --git a/internal/repositories/device_repository.go b/internal/repositories/device_repository.go index 69c18c2..3fc5e2c 100644 --- a/internal/repositories/device_repository.go +++ b/internal/repositories/device_repository.go @@ -1,6 +1,7 @@ package repositories import ( + "context" "database/sql" "errors" "git.miem.hse.ru/hubman/configurator/internal/entity" @@ -8,15 +9,14 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/jmoiron/sqlx" "github.com/mattn/go-sqlite3" - "log" ) type DeviceRepository interface { - GetBySetupAndType(setupId int, dtype entity.DeviceType) ([]*entity.Device, error) - GetOne(setupId int, deviceType entity.DeviceType, id int) (*entity.Device, error) - Add(setupId int, deviceType entity.DeviceType, newDevice *entity.NewDevice) (*entity.Device, error) - Delete(setupId int, dtype entity.DeviceType, id int) error - Update(setupId int, dtype entity.DeviceType, id int, updDevice *entity.UpdDevice) error + GetBySetupAndType(ctx context.Context, setupId int, dtype entity.DeviceType) ([]*entity.Device, error) + GetOne(ctx context.Context, setupId int, deviceType entity.DeviceType, id int) (*entity.Device, error) + Add(ctx context.Context, setupId int, deviceType entity.DeviceType, newDevice *entity.NewDevice) (*entity.Device, error) + Delete(ctx context.Context, setupId int, dtype entity.DeviceType, id int) error + Update(ctx context.Context, setupId int, dtype entity.DeviceType, id int, updDevice *entity.UpdDevice) error } type SqliteDeviceRepository struct { @@ -27,14 +27,20 @@ func NewSqliteDeviceRepository(db *sqlx.DB) *SqliteDeviceRepository { return &SqliteDeviceRepository{db: db} } -func (r *SqliteDeviceRepository) GetBySetupAndType(setupId int, dtype entity.DeviceType) ([]*entity.Device, error) { +func (r *SqliteDeviceRepository) GetBySetupAndType(ctx context.Context, setupId int, dtype entity.DeviceType) ([]*entity.Device, error) { query, params := sq.Select("*").From("Device").Where(sq.Eq{ "setup_id": setupId, "device_type": dtype, }).MustSql() + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + defer rtx.RollbackTxOrIgnore() + devices := make([]*entity.Device, 0, 0) - err := r.db.Select(&devices, query, params...) + err = rtx.tx.SelectContext(ctx, &devices, query, params...) if err != nil { return nil, errs.ErrInternalError } @@ -42,22 +48,31 @@ func (r *SqliteDeviceRepository) GetBySetupAndType(setupId int, dtype entity.Dev } func (r *SqliteDeviceRepository) Add( - setupId int, deviceType entity.DeviceType, newDevice *entity.NewDevice, + ctx context.Context, setupId int, deviceType entity.DeviceType, newDevice *entity.NewDevice, ) (*entity.Device, error) { + + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + defer rtx.RollbackTxOrIgnore() + res, err := sq.Insert("Device").SetMap(map[string]any{ "setup_id": setupId, "device_type": deviceType, "name": newDevice.Name, "url": newDevice.URL, - }).RunWith(r.db).Exec() + }).RunWith(rtx.tx).ExecContext(ctx) if err != nil { return nil, wrapSqliteDeviceError(err) } - id, err := res.LastInsertId() if err != nil { return nil, errs.ErrInternalError } + if err := rtx.CommitTxOrIgnore(); err != nil { + return nil, errs.ErrInternalError + } return &entity.Device{ Id: int(id), SetupId: setupId, @@ -67,26 +82,39 @@ func (r *SqliteDeviceRepository) Add( }, nil } -func (r *SqliteDeviceRepository) GetOne(setupId int, dtype entity.DeviceType, id int) (*entity.Device, error) { +func (r *SqliteDeviceRepository) GetOne(ctx context.Context, setupId int, dtype entity.DeviceType, id int) (*entity.Device, error) { query, params := sq.Select("*").From("Device").Where(sq.Eq{ "id": id, "setup_id": setupId, "device_type": dtype, }).MustSql() - row := r.db.QueryRowx(query, params...) + + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + defer rtx.RollbackTxOrIgnore() + + row := rtx.tx.QueryRowx(query, params...) if row == nil || row.Err() != nil { return nil, wrapSqliteDeviceError(row.Err()) } device := new(entity.Device) - err := row.StructScan(device) + err = row.StructScan(device) if err != nil { return nil, wrapSqliteDeviceError(err) } return device, err } -func (r *SqliteDeviceRepository) Update(setupId int, dtype entity.DeviceType, id int, updDevice *entity.UpdDevice) error { - log.Println("updDevice", updDevice) +func (r *SqliteDeviceRepository) Update(ctx context.Context, setupId int, dtype entity.DeviceType, id int, updDevice *entity.UpdDevice) error { + + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return err + } + defer rtx.RollbackTxOrIgnore() + if (updDevice == nil) || (*updDevice == entity.UpdDevice{}) { return errs.ErrDeviceIncorrectParams } @@ -100,7 +128,7 @@ func (r *SqliteDeviceRepository) Update(setupId int, dtype entity.DeviceType, id query, params := sq.Update("Device").SetMap(setMap).Where(sq.Eq{ "setup_id": setupId, "device_type": dtype, "id": id, }).MustSql() - res, err := r.db.Exec(query, params...) + res, err := rtx.tx.Exec(query, params...) if err != nil { return wrapSqliteDeviceError(err) } @@ -111,16 +139,25 @@ func (r *SqliteDeviceRepository) Update(setupId int, dtype entity.DeviceType, id if affected == 0 { return errs.ErrDeviceNotFound } + if err := rtx.CommitTxOrIgnore(); err != nil { + return errs.ErrInternalError + } return nil } -func (r *SqliteDeviceRepository) Delete(setupId int, dtype entity.DeviceType, id int) error { +func (r *SqliteDeviceRepository) Delete(ctx context.Context, setupId int, dtype entity.DeviceType, id int) error { query, params := sq.Delete("Device").Where(sq.Eq{ "setup_id": setupId, "device_type": dtype, "id": id, }).MustSql() - res, err := r.db.Exec(query, params...) + + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return err + } + defer rtx.RollbackTxOrIgnore() + res, err := rtx.tx.ExecContext(ctx, query, params...) if err != nil { return wrapSqliteDeviceError(err) } @@ -131,6 +168,9 @@ func (r *SqliteDeviceRepository) Delete(setupId int, dtype entity.DeviceType, id if affected == 0 { return errs.ErrDeviceNotFound } + if err := rtx.CommitTxOrIgnore(); err != nil { + return errs.ErrInternalError + } return nil } diff --git a/internal/repositories/setup_repository.go b/internal/repositories/setup_repository.go index eac88d2..19520da 100644 --- a/internal/repositories/setup_repository.go +++ b/internal/repositories/setup_repository.go @@ -35,7 +35,7 @@ func (r SqliteSetupRepository) GetAll(ctx context.Context) ([]*entity.Setup, err } query, params := sq.Select("*").From("Setup").MustSql() setups := make([]*entity.Setup, 0, 0) - err = rtx.tx.Select(&setups, query, params...) + err = rtx.tx.SelectContext(ctx, &setups, query, params...) if err != nil { if err := rtx.RollbackTxOrIgnore(); err != nil { @@ -58,7 +58,7 @@ func (r SqliteSetupRepository) GetById(ctx context.Context, id int) (*entity.Set query, params := sq.Select("*").From("Setup").Where(sq.Eq{"id": id}).MustSql() - row := rtx.tx.QueryRowx(query, params...) + row := rtx.tx.QueryRowxContext(ctx, query, params...) if row == nil || row.Err() != nil { if err := rtx.RollbackTxOrIgnore(); err != nil { return nil, err @@ -81,7 +81,7 @@ func (r SqliteSetupRepository) GetById(ctx context.Context, id int) (*entity.Set func (r SqliteSetupRepository) Delete(ctx context.Context, id int) error { rtx, err := extractSqlxTxOrNew(ctx, r.db) query, params := sq.Delete("Setup").Where(sq.Eq{"id": id}).MustSql() - res, err := rtx.tx.Exec(query, params...) + res, err := rtx.tx.ExecContext(ctx, query, params...) if err != nil { if err := rtx.RollbackTxOrIgnore(); err != nil { return err @@ -113,7 +113,7 @@ func (r SqliteSetupRepository) Add(ctx context.Context, newSetup *entity.NewSetu query, param := sq.Insert("Setup").SetMap(map[string]any{ "name": newSetup.Name, }).MustSql() - res, err := rtx.tx.Exec(query, param...) + res, err := rtx.tx.ExecContext(ctx, query, param...) if err != nil { if err := rtx.RollbackTxOrIgnore(); err != nil { return nil, err @@ -145,7 +145,7 @@ func (r SqliteSetupRepository) Update(ctx context.Context, id int, updSetup *ent } query, params := sq.Update("Setup").Where(sq.Eq{"id": id}).SetMap(updMap).MustSql() - res, err := rtx.tx.Exec(query, params...) + res, err := rtx.tx.ExecContext(ctx, query, params...) if err != nil { if err := rtx.RollbackTxOrIgnore(); err != nil { return err diff --git a/internal/repositories/utils.go b/internal/repositories/utils.go index b5002b8..aeac243 100644 --- a/internal/repositories/utils.go +++ b/internal/repositories/utils.go @@ -5,7 +5,6 @@ import ( "git.miem.hse.ru/hubman/configurator/pkg/errs" "git.miem.hse.ru/hubman/configurator/pkg/transactions" "github.com/jmoiron/sqlx" - "log" ) func extractSqlxTxOrNew(ctx context.Context, db *sqlx.DB) (*RTx, error) { @@ -15,7 +14,7 @@ func extractSqlxTxOrNew(ctx context.Context, db *sqlx.DB) (*RTx, error) { } tx, err := db.Beginx() if err != nil { - return &RTx{tx, false}, errs.ErrInternalError + return nil, errs.ErrInternalError } return &RTx{tx, true}, nil } @@ -25,22 +24,20 @@ type RTx struct { isNewTx bool } -func (rtx *RTx) CommitTxOrIgnore() error { - if !rtx.isNewTx { +func (r *RTx) CommitTxOrIgnore() error { + if !r.isNewTx { return nil } - return rtx.tx.Commit() + return r.tx.Commit() } -func (rtx *RTx) RollbackTxOrIgnore() error { - if !rtx.isNewTx { +func (r *RTx) RollbackTxOrIgnore() error { + if !r.isNewTx { return nil } - err := rtx.tx.Rollback() + err := r.tx.Rollback() if err != nil { - log.Println(err) return errs.ErrInternalError } - log.Println("rollback") return nil } diff --git a/internal/services/device_store_service.go b/internal/services/device_store_service.go index c8dc1ea..5aee100 100644 --- a/internal/services/device_store_service.go +++ b/internal/services/device_store_service.go @@ -1,6 +1,7 @@ package services import ( + "context" "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/internal/repositories" ) @@ -10,37 +11,72 @@ type DeviceStoreService struct { deviceDescriptorRepository repositories.DeviceDescriptorRepository } -func NewDeviceStoreService(deviceRepository repositories.DeviceRepository) *DeviceStoreService { - return &DeviceStoreService{deviceRepository: deviceRepository} +func NewDeviceStoreService( + deviceRepository repositories.DeviceRepository, + descriptorRepository repositories.DeviceDescriptorRepository, +) *DeviceStoreService { + return &DeviceStoreService{ + deviceRepository: deviceRepository, + deviceDescriptorRepository: descriptorRepository, + } } -func (s *DeviceStoreService) GetBySetupAndType(setupId int, dtype entity.DeviceType) ([]*entity.Device, error) { - return s.deviceRepository.GetBySetupAndType(setupId, dtype) +func (s *DeviceStoreService) GetBySetupAndType(ctx context.Context, setupId int, dtype entity.DeviceType) ([]*entity.Device, error) { + return s.deviceRepository.GetBySetupAndType(ctx, setupId, dtype) } func (s *DeviceStoreService) Add( + ctx context.Context, setupId int, dtype entity.DeviceType, newDevice *entity.NewDevice, + commands []*entity.NewDeviceDescriptor, ) (*entity.Device, error) { - return s.deviceRepository.Add(setupId, dtype, newDevice) + device, err := s.deviceRepository.Add(ctx, setupId, dtype, newDevice) + if err != nil { + return nil, err + } + for _, cmd := range commands { + _, err := s.deviceDescriptorRepository.Add(ctx, device.Id, cmd) + if err != nil { + return nil, err + } + } + return device, nil } func (s *DeviceStoreService) GetOne( + ctx context.Context, setupId int, dtype entity.DeviceType, id int, -) (*entity.Device, error) { - return s.deviceRepository.GetOne(setupId, dtype, id) +) (*entity.ExtendedDeviceModel, error) { + device, err := s.deviceRepository.GetOne(ctx, setupId, dtype, id) + if err != nil { + return nil, err + } + descriptors, err := s.deviceDescriptorRepository.GetByDeviceId(ctx, id) + if err != nil { + return nil, err + } + return &entity.ExtendedDeviceModel{ + Device: device, Signals: descriptors, + }, nil } func (s *DeviceStoreService) Update( + ctx context.Context, setupId int, dtype entity.DeviceType, id int, updDevice *entity.UpdDevice, ) (*entity.Device, error) { - if err := s.deviceRepository.Update(setupId, dtype, id, updDevice); err != nil { + if err := s.deviceRepository.Update(ctx, setupId, dtype, id, updDevice); err != nil { return nil, err } - return s.deviceRepository.GetOne(setupId, dtype, id) + return s.deviceRepository.GetOne(ctx, setupId, dtype, id) } func (s *DeviceStoreService) Delete( + ctx context.Context, setupId int, dtype entity.DeviceType, id int, ) error { - return s.deviceRepository.Delete(setupId, dtype, id) + err := s.deviceDescriptorRepository.DeleteByDeviceId(ctx, id) + if err != nil { + return err + } + return s.deviceRepository.Delete(ctx, setupId, dtype, id) } diff --git a/internal/services/executor_service.go b/internal/services/executor_service.go index 488812b..2b2a5c2 100644 --- a/internal/services/executor_service.go +++ b/internal/services/executor_service.go @@ -1,8 +1,11 @@ package services import ( + "context" "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/pkg/errs" + hubmanclient "git.miem.hse.ru/hubman/hubman-lib/client" + "log" "strconv" ) @@ -14,8 +17,8 @@ func NewExecutorService(deviceStoreService *DeviceStoreService) *ExecutorService return &ExecutorService{deviceStoreService: deviceStoreService} } -func (s *ExecutorService) GetList(setupId int) ([]*entity.Device, error) { - return s.deviceStoreService.GetBySetupAndType(setupId, entity.DeviceTypeExecutor) +func (s *ExecutorService) GetList(ctx context.Context, setupId int) ([]*entity.Device, error) { + return s.deviceStoreService.GetBySetupAndType(ctx, setupId, entity.DeviceTypeExecutor) } func (s *ExecutorService) CheckId(ExecutorIdStr string) (int, error) { @@ -26,18 +29,39 @@ func (s *ExecutorService) CheckId(ExecutorIdStr string) (int, error) { return setupId, nil } -func (s *ExecutorService) Add(setupId int, newDevice *entity.NewDevice) (*entity.Device, error) { - return s.deviceStoreService.Add(setupId, entity.DeviceTypeExecutor, newDevice) +func (s *ExecutorService) Add(ctx context.Context, setupId int, newDevice *entity.NewDevice) (*entity.Device, error) { + client, _ := hubmanclient.NewClientWithResponses(newDevice.URL) + statusResp, err := client.StatusWithResponse(ctx) + //rsp := statusResp.HTTPResponse + //log.Println("gp:", rsp.Header, strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200) + //var gg interface{} + //_ = json.Unmarshal(statusResp.Body, &gg) + //log.Println(statusResp.JSON200, statusResp.StatusCode(), gg) + if err != nil || statusResp.JSON200 == nil || !statusResp.JSON200.IsExecutor { + return nil, errs.ErrIncorrectParams + } + + cmdResp, err := client.GetCommandsWithResponse(ctx) + if err != nil || cmdResp.JSON200 == nil { + log.Println(err) + return nil, errs.ErrIncorrectParams + } + commands := make([]*entity.NewDeviceDescriptor, len(*cmdResp.JSON200)) + for i, cmd := range *cmdResp.JSON200 { + commands[i] = entity.BuildNewCommandDescriptor(cmd) + } + + return s.deviceStoreService.Add(ctx, setupId, entity.DeviceTypeExecutor, newDevice, commands) } -func (s *ExecutorService) GetOne(setupId int, id int) (*entity.Device, error) { - return s.deviceStoreService.GetOne(setupId, entity.DeviceTypeExecutor, id) +func (s *ExecutorService) GetOne(ctx context.Context, setupId int, id int) (*entity.ExtendedDeviceModel, error) { + return s.deviceStoreService.GetOne(ctx, setupId, entity.DeviceTypeExecutor, id) } -func (s *ExecutorService) Update(setupId int, id int, updDevice *entity.UpdDevice) (*entity.Device, error) { - return s.deviceStoreService.Update(setupId, entity.DeviceTypeExecutor, id, updDevice) +func (s *ExecutorService) Update(ctx context.Context, setupId int, id int, updDevice *entity.UpdDevice) (*entity.Device, error) { + return s.deviceStoreService.Update(ctx, setupId, entity.DeviceTypeExecutor, id, updDevice) } -func (s *ExecutorService) Delete(setupId int, id int) error { - return s.deviceStoreService.Delete(setupId, entity.DeviceTypeExecutor, id) +func (s *ExecutorService) Delete(ctx context.Context, setupId int, id int) error { + return s.deviceStoreService.Delete(ctx, setupId, entity.DeviceTypeExecutor, id) } diff --git a/internal/services/manipulator_service.go b/internal/services/manipulator_service.go index a89f961..9096061 100644 --- a/internal/services/manipulator_service.go +++ b/internal/services/manipulator_service.go @@ -15,7 +15,7 @@ func NewManipulatorService(deviceStoreService *DeviceStoreService) *ManipulatorS } func (s *ManipulatorService) GetList(setupId int) ([]*entity.Device, error) { - return s.deviceStoreService.GetBySetupAndType(setupId, entity.DeviceTypeManipulator) + return s.deviceStoreService.GetBySetupAndType(nil, setupId, entity.DeviceTypeManipulator) } func (s *ManipulatorService) CheckId(ExecutorIdStr string) (int, error) { @@ -27,17 +27,17 @@ func (s *ManipulatorService) CheckId(ExecutorIdStr string) (int, error) { } func (s *ManipulatorService) Add(setupId int, newDevice *entity.NewDevice) (*entity.Device, error) { - return s.deviceStoreService.Add(setupId, entity.DeviceTypeManipulator, newDevice) + return s.deviceStoreService.Add(nil, setupId, entity.DeviceTypeManipulator, newDevice, nil) } -func (s *ManipulatorService) GetOne(setupId int, id int) (*entity.Device, error) { - return s.deviceStoreService.GetOne(setupId, entity.DeviceTypeManipulator, id) +func (s *ManipulatorService) GetOne(setupId int, id int) (*entity.ExtendedDeviceModel, error) { + return s.deviceStoreService.GetOne(nil, setupId, entity.DeviceTypeManipulator, id) } func (s *ManipulatorService) Update(setupId int, id int, updDevice *entity.UpdDevice) (*entity.Device, error) { - return s.deviceStoreService.Update(setupId, entity.DeviceTypeManipulator, id, updDevice) + return s.deviceStoreService.Update(nil, setupId, entity.DeviceTypeManipulator, id, updDevice) } func (s *ManipulatorService) Delete(setupId int, id int) error { - return s.deviceStoreService.Delete(setupId, entity.DeviceTypeManipulator, id) + return s.deviceStoreService.Delete(nil, setupId, entity.DeviceTypeManipulator, id) } -- GitLab From 8b9b94c97040a2004140d9c45ad6ba8706ac6ac0 Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Sun, 29 Oct 2023 23:29:17 +0300 Subject: [PATCH 06/12] oh no.... --- internal/app/app.go | 10 +- internal/entity/device.go | 4 +- internal/entity/device_descriptor.go | 54 ++++-- internal/entity/rule.go | 98 +++++++--- internal/handlers/setup_executor_router.go | 15 +- internal/handlers/setup_manipulator_router.go | 56 +++++- internal/handlers/setup_rule_router.go | 132 +++++++++++++ internal/handlers/setup_util.go | 17 ++ internal/handlers/setups_router.go | 23 ++- .../device_descriptor_repository.go | 38 ++-- internal/repositories/device_repository.go | 20 ++ internal/repositories/rule_repository.go | 176 +++++++++++++++++- internal/services/device_store_service.go | 51 ++++- internal/services/executor_service.go | 52 +++++- internal/services/manipulator_service.go | 53 +++++- internal/services/rule_service.go | 47 ++++- internal/services/setup_service.go | 23 +++ pkg/errs/errors.go | 3 + 18 files changed, 759 insertions(+), 113 deletions(-) create mode 100644 internal/handlers/setup_rule_router.go diff --git a/internal/app/app.go b/internal/app/app.go index 1d71ce9..f6a6136 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -31,21 +31,25 @@ func NewApp(conf *config.Config) *App { setupRepository := repositories.NewSqliteSetupRepository(db) deviceRepository := repositories.NewSqliteDeviceRepository(db) deviceDescriptorRepository := repositories.NewSqliteDeviceDescriptorRepository(db) + ruleRepository := repositories.NewSqliteRuleRepository(db) setupService := services.NewSetupService(setupRepository) deviceStoreService := services.NewDeviceStoreService(deviceRepository, deviceDescriptorRepository) manipulatorService := services.NewManipulatorService(deviceStoreService) executorService := services.NewExecutorService(deviceStoreService) + ruleService := services.NewRuleService(ruleRepository, manipulatorService, executorService) + deleteSetupService := services.NewDeleteSetupService(setupService, executorService, manipulatorService) httpEngine := gin.Default() httpEngine.Use(transactions.InjectStorage(db)) - routerToApply := []IApplyRouter{ - handlers.NewSetupRouter(setupService), + routersToApply := []IApplyRouter{ + handlers.NewSetupRouter(setupService, deleteSetupService), handlers.NewSetupManipulatorRouter(setupService, manipulatorService), handlers.NewSetupExecutorRouter(setupService, executorService), + handlers.NewSetupRuleRouter(setupService, ruleService), } - for _, router := range routerToApply { + for _, router := range routersToApply { router.ApplyTo(httpEngine) } diff --git a/internal/entity/device.go b/internal/entity/device.go index 4dccf29..4509235 100644 --- a/internal/entity/device.go +++ b/internal/entity/device.go @@ -30,7 +30,7 @@ type UpdDevice struct { type ExtendedDeviceModel struct { *Device - Signals []*DeviceDescriptor `json:"signals"` + Descriptors []*DeviceDescriptor `json:"descriptors"` } func (m *Device) CheckURL() error { @@ -49,7 +49,7 @@ CREATE TABLE IF NOT EXISTS Device ( name TEXT NOT NULL, url TEXT NOT NULL, - FOREIGN KEY (setup_id) REFERENCES t_setup(id), + FOREIGN KEY (setup_id) REFERENCES Setup(id) ON DELETE CASCADE, UNIQUE (setup_id, name), UNIQUE (setup_id, device_type, url) ) diff --git a/internal/entity/device_descriptor.go b/internal/entity/device_descriptor.go index aba5ba7..f81c5ce 100644 --- a/internal/entity/device_descriptor.go +++ b/internal/entity/device_descriptor.go @@ -5,26 +5,58 @@ import ( "git.miem.hse.ru/hubman/hubman-lib/client" ) +type DescriptorArgs map[string]any + type DeviceDescriptor struct { + Id int `db:"id" json:"id"` + DeviceId int `db:"device_id" json:"device_id"` + Code string `db:"code" json:"code"` + Description string `db:"description" json:"description"` + Args DescriptorArgs `json:"args"` +} + +type RawDeviceDescriptor struct { Id int `db:"id" json:"id"` DeviceId int `db:"device_id" json:"device_id"` Code string `db:"code" json:"code"` Description string `db:"description" json:"description"` - Schema string `db:"descriptor_schema" json:"schema"` + Args string `db:"args" json:"args"` } -type NewDeviceDescriptor struct { - Code string `db:"code" json:"code"` - Description string `db:"description" json:"description"` - Schema string `db:"descriptor_schema" json:"schema"` +func BuildDeviceDescriptorFrom(rawDescriptor *RawDeviceDescriptor) (*DeviceDescriptor, error) { + var parsedMap map[string]any + err := json.Unmarshal([]byte(rawDescriptor.Args), &parsedMap) + if err != nil { + return nil, err + } + return &DeviceDescriptor{ + Id: rawDescriptor.Id, + DeviceId: rawDescriptor.DeviceId, + Code: rawDescriptor.Code, + Description: rawDescriptor.Description, + Args: parsedMap, + }, nil +} + +type DeviceMessageDescriptor struct { + Code string `db:"code" json:"code"` + Description string `db:"description" json:"description"` + Args DescriptorArgs `db:"args" json:"args"` +} + +func BuildNewCommandDescriptor(data client.CommandDescriptor) *DeviceMessageDescriptor { + return &DeviceMessageDescriptor{ + data.Code, + data.Description, + data.Args, + } } -func BuildNewCommandDescriptor(data client.CommandDescriptor) *NewDeviceDescriptor { - jsonBytes, _ := json.Marshal(data.Args) - return &NewDeviceDescriptor{ +func BuildNewSignalDescriptor(data client.SignalDescriptor) *DeviceMessageDescriptor { + return &DeviceMessageDescriptor{ data.Code, data.Description, - string(jsonBytes), + data.Vars, } } @@ -34,9 +66,9 @@ const DeviceDescriptorDML = ` device_id INTEGER NOT NULL, code TEXT NOT NULL, description TEXT NOT NULL, - descriptor_schema TEXT NOT NULL, + args TEXT NOT NULL, - FOREIGN KEY (device_id) REFERENCES Device(id), + FOREIGN KEY (device_id) REFERENCES Device(id) ON DELETE CASCADE, UNIQUE (code, device_id) ) ` diff --git a/internal/entity/rule.go b/internal/entity/rule.go index 72e7921..88f8775 100644 --- a/internal/entity/rule.go +++ b/internal/entity/rule.go @@ -1,50 +1,98 @@ package entity +import "encoding/json" + +type RuleLogic map[string]any + type Rule struct { - Id int `db:"id" json:"id"` - SetupId int `db:"setup_id" json:"setup_id"` - ManipulatorId int `db:"manipulator_id" json:"manipulator_id"` - SignalDescriptorId int `db:"signal_descriptor_id" json:"signal_descriptor_id"` - ExecutorId int `db:"executor_id" json:"executor_id"` - CommandDescriptorId int `db:"command_descriptor_id" json:"command_descriptor_id"` - Trigger string `db:"trigger" json:"trigger"` - Logic map[string]any `db:"logic" json:"logic"` + Id int `db:"id" json:"id"` + Name string `db:"name" json:"name"` + Description *string `db:"description" json:"description"` + SetupId int `db:"setup_id" json:"setup_id"` + ManipulatorId int `db:"manipulator_id" json:"manipulator_id"` + SignalDescriptorId int `db:"signal_descriptor_id" json:"signal_descriptor_id"` + ExecutorId int `db:"executor_id" json:"executor_id"` + CommandDescriptorId int `db:"command_descriptor_id" json:"command_descriptor_id"` + Trigger any `db:"trigger" json:"trigger"` + Logic RuleLogic `db:"logic" json:"logic"` +} + +type RawRule struct { + Id int `db:"id" json:"id"` + Name string `db:"name" json:"name"` + Description *string `db:"description" json:"description"` + SetupId int `db:"setup_id" json:"setup_id"` + ManipulatorId int `db:"manipulator_id" json:"manipulator_id"` + SignalDescriptorId int `db:"signal_descriptor_id" json:"signal_descriptor_id"` + ExecutorId int `db:"executor_id" json:"executor_id"` + CommandDescriptorId int `db:"command_descriptor_id" json:"command_descriptor_id"` + Trigger string `db:"trigger" json:"trigger"` + Logic string `db:"logic" json:"logic"` +} + +func BuildRuleFrom(rawRule *RawRule) (*Rule, error) { + var parsedTrigger any + err := json.Unmarshal([]byte(rawRule.Trigger), &parsedTrigger) + if err != nil { + return nil, err + } + var parsedLogic RuleLogic + err = json.Unmarshal([]byte(rawRule.Logic), &parsedLogic) + if err != nil { + return nil, err + } + return &Rule{ + Id: rawRule.Id, + Name: rawRule.Name, + Description: rawRule.Description, + SetupId: rawRule.SetupId, + ManipulatorId: rawRule.ManipulatorId, + SignalDescriptorId: rawRule.SignalDescriptorId, + ExecutorId: rawRule.ExecutorId, + CommandDescriptorId: rawRule.CommandDescriptorId, + Trigger: parsedTrigger, + Logic: parsedLogic, + }, nil } type NewRule struct { - ManipulatorId int `db:"manipulator_id" json:"manipulator_id"` - SignalDescriptorId int `db:"signal_descriptor_id" json:"signal_descriptor_id"` - ExecutorId int `db:"executor_id" json:"executor_id"` - CommandDescriptorId int `db:"command_descriptor_id" json:"command_descriptor_id"` - Trigger string `db:"trigger" json:"trigger"` - Logic map[string]any `db:"logic" json:"logic"` + Name string `db:"name" json:"name"` + Description *string `db:"description" json:"description"` + ManipulatorId int `db:"manipulator_id" json:"manipulator_id"` + SignalDescriptorId int `db:"signal_descriptor_id" json:"signal_descriptor_id"` + ExecutorId int `db:"executor_id" json:"executor_id"` + CommandDescriptorId int `db:"command_descriptor_id" json:"command_descriptor_id"` + Trigger any `db:"trigger" json:"trigger"` + Logic RuleLogic `db:"logic" json:"logic"` } type UpdRule struct { - ManipulatorId *int `db:"manipulator_id" json:"manipulator_id"` - SignalDescriptorId *int `db:"signal_descriptor_id" json:"signal_descriptor_id"` - ExecutorId *int `db:"executor_id" json:"executor_id"` - CommandDescriptorId *int `db:"command_descriptor_id" json:"command_descriptor_id"` - Trigger *string `db:"trigger" json:"trigger"` - Logic *map[string]any `db:"logic" json:"logic"` + Name *string `db:"name" json:"name"` + Description *string `db:"description" json:"description"` + ManipulatorId *int `db:"manipulator_id" json:"manipulator_id"` + SignalDescriptorId *int `db:"signal_descriptor_id" json:"signal_descriptor_id"` + ExecutorId *int `db:"executor_id" json:"executor_id"` + CommandDescriptorId *int `db:"command_descriptor_id" json:"command_descriptor_id"` + Trigger *string `db:"trigger" json:"trigger"` + Logic *RuleLogic `db:"logic" json:"logic"` } const RuleDML = ` CREATE TABLE IF NOT EXISTS Rule ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, - description TEXT NOT NULL, + description TEXT, setup_id INTEGER NOT NULL, manipulator_id INTEGER NOT NULL, signal_descriptor_id INTEGER NOT NULL, executor_id INTEGER NOT NULL, command_descriptor_id INTEGER NOT NULL, - trigger TEXT NOT NULL CHECK ( json(trigger) ), - logic TEXT NOT NULL CHECK ( json(logic) ), + trigger TEXT NOT NULL, + logic TEXT NOT NULL, FOREIGN KEY (setup_id) REFERENCES Setup(id), - FOREIGN KEY (manipulator_id) REFERENCES Setup(id), - FOREIGN KEY (executor_id) REFERENCES Setup(id), + FOREIGN KEY (manipulator_id) REFERENCES Device(id), + FOREIGN KEY (executor_id) REFERENCES Device(id), FOREIGN KEY (executor_id, command_descriptor_id) REFERENCES DeviceDescriptor(device_id, id), FOREIGN KEY (manipulator_id, signal_descriptor_id) REFERENCES DeviceDescriptor(device_id, id) ) diff --git a/internal/handlers/setup_executor_router.go b/internal/handlers/setup_executor_router.go index d9440ce..c587686 100644 --- a/internal/handlers/setup_executor_router.go +++ b/internal/handlers/setup_executor_router.go @@ -91,6 +91,7 @@ func (r *SetupExecutorRouter) Patch(c *gin.Context) { BuildErrResp(c, err) return } + ctx = transactions.InjectTx(ctx, tx) defer tx.Rollback() executor, err := r.executorService.Update(ctx, getSetupId(c), getExecutorId(c), updDevice) @@ -108,10 +109,22 @@ func (r *SetupExecutorRouter) Patch(c *gin.Context) { func (r *SetupExecutorRouter) Delete(c *gin.Context) { ctx := c.Request.Context() - err := r.executorService.Delete(ctx, getSetupId(c), getExecutorId(c)) + tx, err := transactions.ExtractStorage(c).Beginx() if err != nil { BuildErrResp(c, err) return } + ctx = transactions.InjectTx(ctx, tx) + defer tx.Rollback() + + err = r.executorService.Delete(ctx, getSetupId(c), getExecutorId(c)) + if err != nil { + BuildErrResp(c, err) + return + } + if err := tx.Commit(); err != nil { + BuildErrResp(c, errs.ErrInternalError) + return + } c.Status(http.StatusOK) } diff --git a/internal/handlers/setup_manipulator_router.go b/internal/handlers/setup_manipulator_router.go index 36444c8..042481e 100644 --- a/internal/handlers/setup_manipulator_router.go +++ b/internal/handlers/setup_manipulator_router.go @@ -3,6 +3,8 @@ package handlers import ( "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/internal/services" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "git.miem.hse.ru/hubman/configurator/pkg/transactions" "github.com/gin-gonic/gin" "net/http" ) @@ -35,7 +37,8 @@ func NewSetupManipulatorRouter(setupService *services.SetupService, manipulatorS } func (r *SetupManipulatorRouter) GetList(c *gin.Context) { - executors, err := r.manipulatorService.GetList(getSetupId(c)) + ctx := c.Request.Context() + executors, err := r.manipulatorService.GetList(ctx, getSetupId(c)) if err != nil { BuildErrResp(c, err) return @@ -44,20 +47,34 @@ func (r *SetupManipulatorRouter) GetList(c *gin.Context) { } func (r *SetupManipulatorRouter) Post(c *gin.Context) { + ctx := c.Request.Context() newDevice := &entity.NewDevice{} if err := c.BindJSON(newDevice); err != nil { return } - executor, err := r.manipulatorService.Add(getSetupId(c), newDevice) + tx, err := transactions.ExtractStorage(c).Beginx() if err != nil { BuildErrResp(c, err) return } + ctx = transactions.InjectTx(ctx, tx) + defer tx.Rollback() + executor, err := r.manipulatorService.Add(ctx, getSetupId(c), newDevice) + if err != nil { + BuildErrResp(c, err) + return + } + + if err := tx.Commit(); err != nil { + BuildErrResp(c, errs.ErrInternalError) + return + } c.JSON(http.StatusOK, executor) } func (r *SetupManipulatorRouter) Get(c *gin.Context) { - manipulator, err := r.manipulatorService.GetOne(getSetupId(c), GetManipulatorId(c)) + ctx := c.Request.Context() + manipulator, err := r.manipulatorService.GetOne(ctx, getSetupId(c), GetManipulatorId(c)) if err != nil { BuildErrResp(c, err) return @@ -66,23 +83,52 @@ func (r *SetupManipulatorRouter) Get(c *gin.Context) { } func (r *SetupManipulatorRouter) Patch(c *gin.Context) { + ctx := c.Request.Context() + updDevice := &entity.UpdDevice{} if err := c.BindJSON(updDevice); err != nil { return } - manipulator, err := r.manipulatorService.Update(getSetupId(c), GetManipulatorId(c), updDevice) + tx, err := transactions.ExtractStorage(c).Beginx() + if err != nil { + BuildErrResp(c, err) + return + } + ctx = transactions.InjectTx(ctx, tx) + defer tx.Rollback() + + manipulator, err := r.manipulatorService.Update(ctx, getSetupId(c), GetManipulatorId(c), updDevice) if err != nil { BuildErrResp(c, err) return } + + if err := tx.Commit(); err != nil { + BuildErrResp(c, errs.ErrInternalError) + return + } c.JSON(http.StatusOK, manipulator) } func (r *SetupManipulatorRouter) Delete(c *gin.Context) { - err := r.manipulatorService.Delete(getSetupId(c), GetManipulatorId(c)) + ctx := c.Request.Context() + tx, err := transactions.ExtractStorage(c).Beginx() + if err != nil { + BuildErrResp(c, err) + return + } + ctx = transactions.InjectTx(ctx, tx) + defer tx.Rollback() + + err = r.manipulatorService.Delete(ctx, getSetupId(c), GetManipulatorId(c)) if err != nil { BuildErrResp(c, err) return } + + if err := tx.Commit(); err != nil { + BuildErrResp(c, errs.ErrInternalError) + return + } c.Status(http.StatusOK) } diff --git a/internal/handlers/setup_rule_router.go b/internal/handlers/setup_rule_router.go new file mode 100644 index 0000000..d6c1c98 --- /dev/null +++ b/internal/handlers/setup_rule_router.go @@ -0,0 +1,132 @@ +package handlers + +import ( + "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/internal/services" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "git.miem.hse.ru/hubman/configurator/pkg/transactions" + "github.com/gin-gonic/gin" + "net/http" +) + +type SetupRuleRouter struct { + setupService *services.SetupService + ruleService *services.RuleService +} + +func NewSetupRuleRouter( + setupService *services.SetupService, + ruleService *services.RuleService, +) *SetupRuleRouter { + return &SetupRuleRouter{setupService, ruleService} +} + +func (r *SetupRuleRouter) ApplyTo(httpEngine gin.IRouter) { + setupExecutorNS := httpEngine.Group("/setups/:setup_id/rules") + setupExecutorNS.Use(CheckSetupId(r.setupService)) + { + setupExecutorNS.GET("/", r.GetList) + setupExecutorNS.POST("/", r.Post) + + executoredNS := setupExecutorNS.Group("/:rule_id") + executoredNS.Use(CheckRuleId(r.ruleService)) + executoredNS.GET("/", r.Get) + executoredNS.PATCH("/", r.Patch) + executoredNS.DELETE("/", r.Delete) + } +} + +func (r *SetupRuleRouter) GetList(c *gin.Context) { + ctx := c.Request.Context() + rules, err := r.ruleService.GetBySetupId(ctx, getSetupId(c)) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, rules) +} + +func (r *SetupRuleRouter) Post(c *gin.Context) { + ctx := c.Request.Context() + newRule := &entity.NewRule{} + if err := c.BindJSON(newRule); err != nil { + return + } + tx, err := transactions.ExtractStorage(c).Beginx() + if err != nil { + BuildErrResp(c, err) + return + } + ctx = transactions.InjectTx(ctx, tx) + defer tx.Rollback() + rule, err := r.ruleService.Add(ctx, getSetupId(c), newRule) + if err != nil { + BuildErrResp(c, err) + return + } + if err := tx.Commit(); err != nil { + BuildErrResp(c, errs.ErrInternalError) + return + } + c.JSON(http.StatusOK, rule) +} + +func (r *SetupRuleRouter) Get(c *gin.Context) { + ctx := c.Request.Context() + rule, err := r.ruleService.GetOne(ctx, getSetupId(c), getRuleId(c)) + if err != nil { + BuildErrResp(c, err) + return + } + c.JSON(http.StatusOK, rule) +} + +func (r *SetupRuleRouter) Patch(c *gin.Context) { + ctx := c.Request.Context() + + updRule := &entity.UpdRule{} + if err := c.BindJSON(updRule); err != nil { + return + } + tx, err := transactions.ExtractStorage(c).Beginx() + if err != nil { + BuildErrResp(c, err) + return + } + ctx = transactions.InjectTx(ctx, tx) + defer tx.Rollback() + + rule, err := r.ruleService.Update(ctx, getSetupId(c), getRuleId(c), updRule) + if err != nil { + BuildErrResp(c, err) + return + } + + if err := tx.Commit(); err != nil { + BuildErrResp(c, errs.ErrInternalError) + return + } + c.JSON(http.StatusOK, rule) +} + +func (r *SetupRuleRouter) Delete(c *gin.Context) { + ctx := c.Request.Context() + tx, err := transactions.ExtractStorage(c).Beginx() + if err != nil { + BuildErrResp(c, err) + return + } + ctx = transactions.InjectTx(ctx, tx) + defer tx.Rollback() + + err = r.ruleService.Delete(ctx, getSetupId(c), getRuleId(c)) + if err != nil { + BuildErrResp(c, err) + return + } + if err := tx.Commit(); err != nil { + BuildErrResp(c, errs.ErrInternalError) + return + } + c.Status(http.StatusOK) +} diff --git a/internal/handlers/setup_util.go b/internal/handlers/setup_util.go index 587bc01..b1418de 100644 --- a/internal/handlers/setup_util.go +++ b/internal/handlers/setup_util.go @@ -41,6 +41,23 @@ func getExecutorId(c *gin.Context) int { return c.MustGet("executorId").(int) } +func CheckRuleId(ruleService *services.RuleService) gin.HandlerFunc { + return func(c *gin.Context) { + executorId, err := ruleService.CheckId(c.Params.ByName("rule_id")) + if err != nil { + BuildErrResp(c, err) + c.Abort() + return + } + c.Set("ruleId", executorId) + return + } +} + +func getRuleId(c *gin.Context) int { + return c.MustGet("ruleId").(int) +} + func CheckManipulatorId(manipulatorService *services.ManipulatorService) gin.HandlerFunc { return func(c *gin.Context) { manipulatorId, err := manipulatorService.CheckId(c.Params.ByName("manipulator_id")) diff --git a/internal/handlers/setups_router.go b/internal/handlers/setups_router.go index dcd45c9..f023512 100644 --- a/internal/handlers/setups_router.go +++ b/internal/handlers/setups_router.go @@ -10,11 +10,12 @@ import ( ) type SetupRouter struct { - setupService *services.SetupService + setupService *services.SetupService + deleteSetupService *services.DeleteSetupService } -func NewSetupRouter(setupService *services.SetupService) *SetupRouter { - return &SetupRouter{setupService: setupService} +func NewSetupRouter(setupService *services.SetupService, deleteSetupService *services.DeleteSetupService) *SetupRouter { + return &SetupRouter{setupService, deleteSetupService} } func (r *SetupRouter) ApplyTo(httpEngine gin.IRouter) { @@ -63,11 +64,23 @@ func (r *SetupRouter) Get(c *gin.Context) { func (r *SetupRouter) Delete(c *gin.Context) { ctx := c.Request.Context() - err := r.setupService.Delete(ctx, getSetupId(c)) + tx, err := transactions.ExtractStorage(c).Beginx() + if err != nil { + BuildErrResp(c, err) + return + } + ctx = transactions.InjectTx(ctx, tx) + defer tx.Rollback() + + err = r.deleteSetupService.Delete(ctx, getSetupId(c)) if err != nil { BuildErrResp(c, err) return } + if err := tx.Commit(); err != nil { + BuildErrResp(c, errs.ErrInternalError) + return + } c.Status(http.StatusOK) } @@ -97,7 +110,7 @@ func (r *SetupRouter) Patch(c *gin.Context) { return } - if err2 := tx.Commit(); err2 != nil { + if err := tx.Commit(); err != nil { BuildErrResp(c, errs.ErrInternalError) } c.JSON(http.StatusOK, setup) diff --git a/internal/repositories/device_descriptor_repository.go b/internal/repositories/device_descriptor_repository.go index aa13dda..ce6d9c0 100644 --- a/internal/repositories/device_descriptor_repository.go +++ b/internal/repositories/device_descriptor_repository.go @@ -3,6 +3,7 @@ package repositories import ( "context" "database/sql" + "encoding/json" "errors" "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/pkg/errs" @@ -12,7 +13,7 @@ import ( ) type DeviceDescriptorRepository interface { - Add(ctx context.Context, deviceId int, newDescriptor *entity.NewDeviceDescriptor) (*entity.DeviceDescriptor, error) + Add(ctx context.Context, deviceId int, newDescriptor *entity.DeviceMessageDescriptor) (*entity.DeviceDescriptor, error) DeleteByDeviceId(ctx context.Context, deviceId int) error GetByDeviceId(ctx context.Context, deviceId int) ([]*entity.DeviceDescriptor, error) } @@ -25,18 +26,19 @@ func NewSqliteDeviceDescriptorRepository(db *sqlx.DB) *SqliteDeviceDescriptorRep return &SqliteDeviceDescriptorRepository{db: db} } -func (r *SqliteDeviceDescriptorRepository) Add(ctx context.Context, deviceId int, newDescriptor *entity.NewDeviceDescriptor) (*entity.DeviceDescriptor, error) { +func (r *SqliteDeviceDescriptorRepository) Add(ctx context.Context, deviceId int, newDescriptor *entity.DeviceMessageDescriptor) (*entity.DeviceDescriptor, error) { rtx, err := extractSqlxTxOrNew(ctx, r.db) if err != nil { return nil, err } defer rtx.RollbackTxOrIgnore() + argsLine, _ := json.Marshal(newDescriptor.Args) res, err := sq.Insert("DeviceDescriptor").SetMap(map[string]any{ - "device_id": deviceId, - "code": newDescriptor.Code, - "description": newDescriptor.Description, - "descriptor_schema": newDescriptor.Schema, + "device_id": deviceId, + "code": newDescriptor.Code, + "description": newDescriptor.Description, + "args": string(argsLine), }).RunWith(rtx.tx).ExecContext(ctx) if err != nil { return nil, wrapSqliteDeviceDescriptorError(err) @@ -54,7 +56,7 @@ func (r *SqliteDeviceDescriptorRepository) Add(ctx context.Context, deviceId int DeviceId: deviceId, Code: newDescriptor.Code, Description: newDescriptor.Description, - Schema: newDescriptor.Schema, + Args: newDescriptor.Args, }, nil } @@ -68,17 +70,10 @@ func (r *SqliteDeviceDescriptorRepository) DeleteByDeviceId(ctx context.Context, query, params := sq.Delete("DeviceDescriptor").Where(sq.Eq{ "device_id": deviceId, }).MustSql() - res, err := rtx.tx.ExecContext(ctx, query, params...) + _, err = rtx.tx.ExecContext(ctx, query, params...) if err != nil { return wrapSqliteDeviceDescriptorError(err) } - affected, err := res.RowsAffected() - if err != nil { - return errs.ErrInternalError - } - if affected == 0 { - return errs.ErrDeviceNotFound - } if err := rtx.CommitTxOrIgnore(); err != nil { return errs.ErrInternalError @@ -96,10 +91,17 @@ func (r *SqliteDeviceDescriptorRepository) GetByDeviceId(ctx context.Context, de query, params := sq.Select("*").From("DeviceDescriptor").Where(sq.Eq{ "device_id": deviceId, }).MustSql() - var descriptors []*entity.DeviceDescriptor - err = rtx.tx.SelectContext(ctx, &descriptors, query, params...) + var rawDescriptors []*entity.RawDeviceDescriptor + err = rtx.tx.SelectContext(ctx, &rawDescriptors, query, params...) if err != nil { - return descriptors, wrapSqliteDeviceDescriptorError(err) + return nil, wrapSqliteDeviceDescriptorError(err) + } + descriptors := make([]*entity.DeviceDescriptor, len(rawDescriptors)) + for i, d := range rawDescriptors { + descriptors[i], err = entity.BuildDeviceDescriptorFrom(d) + if err != nil { + return nil, errs.ErrInternalError + } } return descriptors, nil } diff --git a/internal/repositories/device_repository.go b/internal/repositories/device_repository.go index 3fc5e2c..a3c8318 100644 --- a/internal/repositories/device_repository.go +++ b/internal/repositories/device_repository.go @@ -13,6 +13,7 @@ import ( type DeviceRepository interface { GetBySetupAndType(ctx context.Context, setupId int, dtype entity.DeviceType) ([]*entity.Device, error) + DeleteBySetupId(ctx context.Context, setupId int, dtype entity.DeviceType) error GetOne(ctx context.Context, setupId int, deviceType entity.DeviceType, id int) (*entity.Device, error) Add(ctx context.Context, setupId int, deviceType entity.DeviceType, newDevice *entity.NewDevice) (*entity.Device, error) Delete(ctx context.Context, setupId int, dtype entity.DeviceType, id int) error @@ -82,6 +83,25 @@ func (r *SqliteDeviceRepository) Add( }, nil } +func (r *SqliteDeviceRepository) DeleteBySetupId(ctx context.Context, setupId int, dtype entity.DeviceType) error { + query, params := sq.Delete("Device").Where(sq.Eq{ + "setup_id": setupId, + "device_type": dtype, + }).MustSql() + + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return err + } + defer rtx.RollbackTxOrIgnore() + + _, err = rtx.tx.ExecContext(ctx, query, params...) + if err != nil { + return wrapSqliteDeviceError(err) + } + return nil +} + func (r *SqliteDeviceRepository) GetOne(ctx context.Context, setupId int, dtype entity.DeviceType, id int) (*entity.Device, error) { query, params := sq.Select("*").From("Device").Where(sq.Eq{ "id": id, diff --git a/internal/repositories/rule_repository.go b/internal/repositories/rule_repository.go index cdbe341..80637d1 100644 --- a/internal/repositories/rule_repository.go +++ b/internal/repositories/rule_repository.go @@ -1,18 +1,184 @@ package repositories import ( + "context" + "database/sql" + "encoding/json" + "errors" "git.miem.hse.ru/hubman/configurator/internal/entity" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + sq "github.com/Masterminds/squirrel" "github.com/jmoiron/sqlx" + "github.com/mattn/go-sqlite3" + "log" ) type RuleRepository interface { - GetBySetupId(setupId int) ([]*entity.Rule, error) - GetById(setupId int, id int) (*entity.Rule, error) - Add(setupId int, rule *entity.NewRule) (*entity.Rule, error) - Update(setupId int, id int, updSetup *entity.UpdRule) error - Delete(setupId int, id int) error + GetBySetupId(ctx context.Context, setupId int) ([]*entity.Rule, error) + GetOne(ctx context.Context, setupId int, id int) (*entity.Rule, error) + Add(ctx context.Context, setupId int, rule *entity.NewRule) (*entity.Rule, error) + Update(ctx context.Context, setupId int, id int, updSetup *entity.UpdRule) error + Delete(ctx context.Context, setupId int, id int) error } type SqliteRuleRepository struct { db *sqlx.DB } + +func NewSqliteRuleRepository(db *sqlx.DB) *SqliteRuleRepository { + return &SqliteRuleRepository{db: db} +} + +func (r *SqliteRuleRepository) GetBySetupId(ctx context.Context, setupId int) ([]*entity.Rule, error) { + query, params := sq.Select("*").From("Rule").Where(sq.Eq{ + "setup_id": setupId, + }).MustSql() + + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + defer rtx.RollbackTxOrIgnore() + + rawRules := make([]*entity.RawRule, 0, 0) + err = rtx.tx.SelectContext(ctx, &rawRules, query, params...) + log.Println(err) + if err != nil { + return nil, errs.ErrInternalError + } + rules := make([]*entity.Rule, len(rawRules)) + for i, rawRule := range rawRules { + rules[i], err = entity.BuildRuleFrom(rawRule) + if err != nil { + return nil, errs.ErrInternalError + } + + } + return rules, nil +} + +func (r *SqliteRuleRepository) GetOne(ctx context.Context, setupId int, id int) (*entity.Rule, error) { + query, params := sq.Select("*").From("Rule").Where(sq.Eq{ + "id": id, + "setup_id": setupId, + }).MustSql() + + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + defer rtx.RollbackTxOrIgnore() + + row := rtx.tx.QueryRowx(query, params...) + if row == nil || row.Err() != nil { + return nil, wrapSqliteRuleError(row.Err()) + } + rawRule := new(entity.RawRule) + err = row.StructScan(rawRule) + if err != nil { + return nil, wrapSqliteRuleError(err) + } + rule, err := entity.BuildRuleFrom(rawRule) + if err != nil { + return nil, errs.ErrInternalError + } + return rule, nil +} + +func (r *SqliteRuleRepository) Add(ctx context.Context, setupId int, rule *entity.NewRule) (*entity.Rule, error) { + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + defer rtx.RollbackTxOrIgnore() + + trigger, err := json.Marshal(rule.Trigger) + if err != nil { + return nil, errs.ErrInternalError + } + logic, err := json.Marshal(rule.Logic) + if err != nil { + return nil, errs.ErrInternalError + } + res, err := sq.Insert("Rule").SetMap(map[string]any{ + "setup_id": setupId, + "name": rule.Name, + "description": rule.Description, + "manipulator_id": rule.ManipulatorId, + "signal_descriptor_id": rule.SignalDescriptorId, + "executor_id": rule.ExecutorId, + "command_descriptor_id": rule.CommandDescriptorId, + "trigger": string(trigger), + "logic": string(logic), + }).RunWith(rtx.tx).ExecContext(ctx) + if err != nil { + return nil, wrapSqliteRuleError(err) + } + id, err := res.LastInsertId() + if err != nil { + return nil, errs.ErrInternalError + } + if err := rtx.CommitTxOrIgnore(); err != nil { + return nil, errs.ErrInternalError + } + return &entity.Rule{ + Id: int(id), + SetupId: setupId, + Name: rule.Name, + Description: rule.Description, + ManipulatorId: rule.ManipulatorId, + SignalDescriptorId: rule.SignalDescriptorId, + ExecutorId: rule.ExecutorId, + CommandDescriptorId: rule.CommandDescriptorId, + Trigger: rule.Trigger, + Logic: rule.Logic, + }, nil +} + +func (r *SqliteRuleRepository) Update(ctx context.Context, setupId int, id int, updSetup *entity.UpdRule) error { + //TODO implement me + panic("implement me") +} + +func (r *SqliteRuleRepository) Delete(ctx context.Context, setupId int, id int) error { + query, params := sq.Delete("Rule").Where(sq.Eq{ + "setup_id": setupId, + "id": id, + }).MustSql() + + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return err + } + defer rtx.RollbackTxOrIgnore() + + res, err := rtx.tx.ExecContext(ctx, query, params...) + if err != nil { + return wrapSqliteRuleError(err) + } + affected, err := res.RowsAffected() + if err != nil { + return errs.ErrInternalError + } + if affected == 0 { + return errs.ErrDeviceNotFound + } + if err := rtx.CommitTxOrIgnore(); err != nil { + return errs.ErrInternalError + } + return nil +} + +func wrapSqliteRuleError(err error) error { + if errors.Is(err, sql.ErrNoRows) { + return errs.ErrRuleNotFound + } + var sqliteErr sqlite3.Error + if errors.As(err, &sqliteErr) { + if errors.Is(sqliteErr.Code, sqlite3.ErrConstraint) { + return errs.ErrRuleIncorrectParams + } + return errs.ErrInternalError + } + return err +} diff --git a/internal/services/device_store_service.go b/internal/services/device_store_service.go index 5aee100..fe4ff7b 100644 --- a/internal/services/device_store_service.go +++ b/internal/services/device_store_service.go @@ -25,22 +25,35 @@ func (s *DeviceStoreService) GetBySetupAndType(ctx context.Context, setupId int, return s.deviceRepository.GetBySetupAndType(ctx, setupId, dtype) } +func (s *DeviceStoreService) DeleteBySetupId(ctx context.Context, setupId int, dtype entity.DeviceType) error { + devices, err := s.deviceRepository.GetBySetupAndType(ctx, setupId, dtype) + if err != nil { + return nil + } + for _, device := range devices { + if err := s.deviceDescriptorRepository.DeleteByDeviceId(ctx, device.Id); err != nil { + return err + } + } + return s.deviceRepository.DeleteBySetupId(ctx, setupId, dtype) +} + func (s *DeviceStoreService) Add( ctx context.Context, setupId int, dtype entity.DeviceType, newDevice *entity.NewDevice, - commands []*entity.NewDeviceDescriptor, -) (*entity.Device, error) { + newDescriptors []*entity.DeviceMessageDescriptor, +) (*entity.ExtendedDeviceModel, error) { device, err := s.deviceRepository.Add(ctx, setupId, dtype, newDevice) if err != nil { return nil, err } - for _, cmd := range commands { - _, err := s.deviceDescriptorRepository.Add(ctx, device.Id, cmd) - if err != nil { - return nil, err - } + descriptors, err := s.UpdateDescriptors(ctx, device.Id, newDescriptors) + if err != nil { + return nil, err } - return device, nil + return &entity.ExtendedDeviceModel{ + Device: device, Descriptors: descriptors, + }, nil } func (s *DeviceStoreService) GetOne( @@ -56,14 +69,34 @@ func (s *DeviceStoreService) GetOne( return nil, err } return &entity.ExtendedDeviceModel{ - Device: device, Signals: descriptors, + Device: device, Descriptors: descriptors, }, nil } +func (s *DeviceStoreService) UpdateDescriptors( + ctx context.Context, + deviceId int, newDescriptors []*entity.DeviceMessageDescriptor, +) ([]*entity.DeviceDescriptor, error) { + err := s.deviceDescriptorRepository.DeleteByDeviceId(ctx, deviceId) + if err != nil { + return nil, err + } + descriptors := make([]*entity.DeviceDescriptor, len(newDescriptors)) + for i, cmd := range newDescriptors { + d, err := s.deviceDescriptorRepository.Add(ctx, deviceId, cmd) + if err != nil { + return nil, err + } + descriptors[i] = d + } + return descriptors, nil +} + func (s *DeviceStoreService) Update( ctx context.Context, setupId int, dtype entity.DeviceType, id int, updDevice *entity.UpdDevice, ) (*entity.Device, error) { + if err := s.deviceRepository.Update(ctx, setupId, dtype, id, updDevice); err != nil { return nil, err } diff --git a/internal/services/executor_service.go b/internal/services/executor_service.go index 2b2a5c2..7dcab4b 100644 --- a/internal/services/executor_service.go +++ b/internal/services/executor_service.go @@ -21,6 +21,10 @@ func (s *ExecutorService) GetList(ctx context.Context, setupId int) ([]*entity.D return s.deviceStoreService.GetBySetupAndType(ctx, setupId, entity.DeviceTypeExecutor) } +func (s *ExecutorService) DeleteBySetupId(ctx context.Context, setupId int) error { + return s.deviceStoreService.DeleteBySetupId(ctx, setupId, entity.DeviceTypeExecutor) +} + func (s *ExecutorService) CheckId(ExecutorIdStr string) (int, error) { setupId, err := strconv.Atoi(ExecutorIdStr) if err != nil { @@ -29,28 +33,37 @@ func (s *ExecutorService) CheckId(ExecutorIdStr string) (int, error) { return setupId, nil } -func (s *ExecutorService) Add(ctx context.Context, setupId int, newDevice *entity.NewDevice) (*entity.Device, error) { - client, _ := hubmanclient.NewClientWithResponses(newDevice.URL) +func (s *ExecutorService) getClient(ctx context.Context, url string) (*hubmanclient.ClientWithResponses, error) { + client, _ := hubmanclient.NewClientWithResponses(url) statusResp, err := client.StatusWithResponse(ctx) - //rsp := statusResp.HTTPResponse - //log.Println("gp:", rsp.Header, strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200) - //var gg interface{} - //_ = json.Unmarshal(statusResp.Body, &gg) - //log.Println(statusResp.JSON200, statusResp.StatusCode(), gg) if err != nil || statusResp.JSON200 == nil || !statusResp.JSON200.IsExecutor { return nil, errs.ErrIncorrectParams } + return client, nil +} +func (s *ExecutorService) getCommands(ctx context.Context, client *hubmanclient.ClientWithResponses) ([]*entity.DeviceMessageDescriptor, error) { cmdResp, err := client.GetCommandsWithResponse(ctx) if err != nil || cmdResp.JSON200 == nil { log.Println(err) return nil, errs.ErrIncorrectParams } - commands := make([]*entity.NewDeviceDescriptor, len(*cmdResp.JSON200)) + commands := make([]*entity.DeviceMessageDescriptor, len(*cmdResp.JSON200)) for i, cmd := range *cmdResp.JSON200 { commands[i] = entity.BuildNewCommandDescriptor(cmd) } + return commands, nil +} +func (s *ExecutorService) Add(ctx context.Context, setupId int, newDevice *entity.NewDevice) (*entity.ExtendedDeviceModel, error) { + client, err := s.getClient(ctx, newDevice.URL) + if err != nil { + return nil, err + } + commands, err := s.getCommands(ctx, client) + if err != nil { + return nil, err + } return s.deviceStoreService.Add(ctx, setupId, entity.DeviceTypeExecutor, newDevice, commands) } @@ -58,8 +71,27 @@ func (s *ExecutorService) GetOne(ctx context.Context, setupId int, id int) (*ent return s.deviceStoreService.GetOne(ctx, setupId, entity.DeviceTypeExecutor, id) } -func (s *ExecutorService) Update(ctx context.Context, setupId int, id int, updDevice *entity.UpdDevice) (*entity.Device, error) { - return s.deviceStoreService.Update(ctx, setupId, entity.DeviceTypeExecutor, id, updDevice) +func (s *ExecutorService) Update(ctx context.Context, setupId, id int, updDevice *entity.UpdDevice) (*entity.ExtendedDeviceModel, error) { + executor, err := s.deviceStoreService.GetOne(ctx, setupId, entity.DeviceTypeExecutor, id) + if err != nil { + return nil, err + } + if updDevice.URL != nil && executor.URL != *updDevice.URL { + client, err := s.getClient(ctx, *updDevice.URL) + commands, err := s.getCommands(ctx, client) + if err != nil { + return nil, err + } + _, err = s.deviceStoreService.UpdateDescriptors(ctx, id, commands) + if err != nil { + return nil, err + } + } + device, err := s.deviceStoreService.Update(ctx, setupId, entity.DeviceTypeExecutor, id, updDevice) + if err != nil { + return nil, err + } + return s.deviceStoreService.GetOne(ctx, device.SetupId, entity.DeviceTypeExecutor, id) } func (s *ExecutorService) Delete(ctx context.Context, setupId int, id int) error { diff --git a/internal/services/manipulator_service.go b/internal/services/manipulator_service.go index 9096061..fb0e65c 100644 --- a/internal/services/manipulator_service.go +++ b/internal/services/manipulator_service.go @@ -1,8 +1,11 @@ package services import ( + "context" "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/pkg/errs" + hubmanclient "git.miem.hse.ru/hubman/hubman-lib/client" + "log" "strconv" ) @@ -14,8 +17,30 @@ func NewManipulatorService(deviceStoreService *DeviceStoreService) *ManipulatorS return &ManipulatorService{deviceStoreService: deviceStoreService} } -func (s *ManipulatorService) GetList(setupId int) ([]*entity.Device, error) { - return s.deviceStoreService.GetBySetupAndType(nil, setupId, entity.DeviceTypeManipulator) +func (s *ManipulatorService) getClient(ctx context.Context, url string) (*hubmanclient.ClientWithResponses, error) { + client, _ := hubmanclient.NewClientWithResponses(url) + statusResp, err := client.StatusWithResponse(ctx) + if err != nil || statusResp.JSON200 == nil || !statusResp.JSON200.IsManipulator { + return nil, errs.ErrIncorrectParams + } + return client, nil +} + +func (s *ManipulatorService) getSignals(ctx context.Context, client *hubmanclient.ClientWithResponses) ([]*entity.DeviceMessageDescriptor, error) { + cmdResp, err := client.GetSignalsWithResponse(ctx) + if err != nil || cmdResp.JSON200 == nil { + log.Println(err) + return nil, errs.ErrIncorrectParams + } + signals := make([]*entity.DeviceMessageDescriptor, len(*cmdResp.JSON200)) + for i, signal := range *cmdResp.JSON200 { + signals[i] = entity.BuildNewSignalDescriptor(signal) + } + return signals, nil +} + +func (s *ManipulatorService) GetList(ctx context.Context, setupId int) ([]*entity.Device, error) { + return s.deviceStoreService.GetBySetupAndType(ctx, setupId, entity.DeviceTypeManipulator) } func (s *ManipulatorService) CheckId(ExecutorIdStr string) (int, error) { @@ -26,18 +51,26 @@ func (s *ManipulatorService) CheckId(ExecutorIdStr string) (int, error) { return setupId, nil } -func (s *ManipulatorService) Add(setupId int, newDevice *entity.NewDevice) (*entity.Device, error) { - return s.deviceStoreService.Add(nil, setupId, entity.DeviceTypeManipulator, newDevice, nil) +func (s *ManipulatorService) Add(ctx context.Context, setupId int, newDevice *entity.NewDevice) (*entity.ExtendedDeviceModel, error) { + client, err := s.getClient(ctx, newDevice.URL) + if err != nil { + return nil, err + } + signals, err := s.getSignals(ctx, client) + if err != nil { + return nil, err + } + return s.deviceStoreService.Add(ctx, setupId, entity.DeviceTypeManipulator, newDevice, signals) } -func (s *ManipulatorService) GetOne(setupId int, id int) (*entity.ExtendedDeviceModel, error) { - return s.deviceStoreService.GetOne(nil, setupId, entity.DeviceTypeManipulator, id) +func (s *ManipulatorService) GetOne(ctx context.Context, setupId int, id int) (*entity.ExtendedDeviceModel, error) { + return s.deviceStoreService.GetOne(ctx, setupId, entity.DeviceTypeManipulator, id) } -func (s *ManipulatorService) Update(setupId int, id int, updDevice *entity.UpdDevice) (*entity.Device, error) { - return s.deviceStoreService.Update(nil, setupId, entity.DeviceTypeManipulator, id, updDevice) +func (s *ManipulatorService) Update(ctx context.Context, setupId int, id int, updDevice *entity.UpdDevice) (*entity.Device, error) { + return s.deviceStoreService.Update(ctx, setupId, entity.DeviceTypeManipulator, id, updDevice) } -func (s *ManipulatorService) Delete(setupId int, id int) error { - return s.deviceStoreService.Delete(nil, setupId, entity.DeviceTypeManipulator, id) +func (s *ManipulatorService) Delete(ctx context.Context, setupId int, id int) error { + return s.deviceStoreService.Delete(ctx, setupId, entity.DeviceTypeManipulator, id) } diff --git a/internal/services/rule_service.go b/internal/services/rule_service.go index b496493..0e8259b 100644 --- a/internal/services/rule_service.go +++ b/internal/services/rule_service.go @@ -1,26 +1,55 @@ package services import ( + "context" "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/internal/repositories" + "git.miem.hse.ru/hubman/configurator/pkg/errs" + "strconv" ) type RuleService struct { - ruleRepository repositories.RuleRepository + ruleRepository repositories.RuleRepository + manipulatorService *ManipulatorService + executorService *ExecutorService } -func (s *RuleService) Add(setupId int, newRule *entity.NewRule) (*entity.Rule, error) { - return s.ruleRepository.Add(setupId, newRule) +func NewRuleService( + ruleRepository repositories.RuleRepository, + manipulatorService *ManipulatorService, + executorService *ExecutorService, +) *RuleService { + return &RuleService{ruleRepository: ruleRepository, manipulatorService: manipulatorService, executorService: executorService} } -func (s *RuleService) GetBySetupId(setupId int) ([]*entity.Rule, error) { - return s.ruleRepository.GetBySetupId(setupId) +func (s *RuleService) Add(ctx context.Context, setupId int, newRule *entity.NewRule) (*entity.Rule, error) { + return s.ruleRepository.Add(ctx, setupId, newRule) } -func (s *RuleService) GetById(setupId int, id int) (*entity.Rule, error) { - return s.ruleRepository.GetById(setupId, id) +func (s *RuleService) GetBySetupId(ctx context.Context, setupId int) ([]*entity.Rule, error) { + return s.ruleRepository.GetBySetupId(ctx, setupId) } -func (s *RuleService) Delete(setupId int, id int) error { - return s.ruleRepository.Delete(setupId, id) +func (s *RuleService) Update(ctx context.Context, setupId int, id int, updRule *entity.UpdRule) (*entity.Rule, error) { + err := s.ruleRepository.Update(ctx, setupId, id, updRule) + if err != nil { + return nil, err + } + return s.ruleRepository.GetOne(ctx, setupId, id) +} + +func (s *RuleService) GetOne(ctx context.Context, setupId int, id int) (*entity.Rule, error) { + return s.ruleRepository.GetOne(ctx, setupId, id) +} + +func (s *RuleService) Delete(ctx context.Context, setupId int, id int) error { + return s.ruleRepository.Delete(ctx, setupId, id) +} + +func (s *RuleService) CheckId(RuleIdStr string) (int, error) { + ruleId, err := strconv.Atoi(RuleIdStr) + if err != nil { + return 0, errs.ErrDeviceNotFound + } + return ruleId, nil } diff --git a/internal/services/setup_service.go b/internal/services/setup_service.go index 58dcdbd..4917386 100644 --- a/internal/services/setup_service.go +++ b/internal/services/setup_service.go @@ -47,3 +47,26 @@ func (s *SetupService) CheckId(setupIdStr string) (int, error) { } return setupId, nil } + +type DeleteSetupService struct { + setupService *SetupService + executorService *ExecutorService + manipulatorService *ManipulatorService +} + +func NewDeleteSetupService(setupService *SetupService, executorService *ExecutorService, manipulatorService *ManipulatorService) *DeleteSetupService { + return &DeleteSetupService{setupService: setupService, executorService: executorService, manipulatorService: manipulatorService} +} + +func (s *DeleteSetupService) Delete(ctx context.Context, id int) error { + if err := s.executorService.DeleteBySetupId(ctx, id); err != nil { + return err + } + //if err := s.manipulatorService.DeleteBySetupId(ctx, id); err != nil { + // return err + //} + if err := s.setupService.Delete(ctx, id); err != nil { + return err + } + return nil +} diff --git a/pkg/errs/errors.go b/pkg/errs/errors.go index b8e38c0..6aac47a 100644 --- a/pkg/errs/errors.go +++ b/pkg/errs/errors.go @@ -19,4 +19,7 @@ var ( ErrDeviceDescriptorIncorrectParams = fmt.Errorf("DEVICE_DESCRIPTOR_%w", ErrIncorrectParams) ErrDeviceDescriptorNotFound = fmt.Errorf("DEVICE_DESCRIPTOR_%w", ErrNotFound) + + ErrRuleIncorrectParams = fmt.Errorf("RULE_%w", ErrIncorrectParams) + ErrRuleNotFound = fmt.Errorf("RULE_%w", ErrNotFound) ) -- GitLab From db4df24d2faf5d3f7f797cccb70cc9d35b3c74d7 Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Mon, 30 Oct 2023 00:57:58 +0300 Subject: [PATCH 07/12] oh no.... --- go.mod | 1 + go.sum | 2 + .../device_descriptor_repository.go | 30 ++++++ internal/repositories/rule_repository.go | 97 +++++++++++++++++-- internal/services/delete_service.go | 64 ++++++++++++ internal/services/device_store_service.go | 14 +++ internal/services/executor_service.go | 4 + internal/services/manipulator_service.go | 8 ++ internal/services/rule_service.go | 39 ++++++++ internal/services/setup_service.go | 23 ----- 10 files changed, 253 insertions(+), 29 deletions(-) create mode 100644 internal/services/delete_service.go diff --git a/go.mod b/go.mod index 83b0523..857bc00 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/diegoholiveira/jsonlogic v1.0.0 // indirect github.com/diegoholiveira/jsonlogic/v3 v3.3.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect diff --git a/go.sum b/go.sum index 9e08dd7..5a6a7e1 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/diegoholiveira/jsonlogic v1.0.0 h1:6ab0Q3563hZj3515knOkrpy8Ui3pgIGoBvy428w1/hA= +github.com/diegoholiveira/jsonlogic v1.0.0/go.mod h1:jVLve2Xh7aEJep0nNAYlb/Ookjl4qIHcU8TqFPg99lI= github.com/diegoholiveira/jsonlogic/v3 v3.3.0 h1:XdIxQ+ICFcQB9tVf46cmiCkc5K9MN8Sh/x+XDHL+iXM= github.com/diegoholiveira/jsonlogic/v3 v3.3.0/go.mod h1:9oE8z9G+0OMxOoLHF3fhek3KuqD5CBqM0B6XFL08MSg= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= diff --git a/internal/repositories/device_descriptor_repository.go b/internal/repositories/device_descriptor_repository.go index ce6d9c0..0f12c76 100644 --- a/internal/repositories/device_descriptor_repository.go +++ b/internal/repositories/device_descriptor_repository.go @@ -16,6 +16,7 @@ type DeviceDescriptorRepository interface { Add(ctx context.Context, deviceId int, newDescriptor *entity.DeviceMessageDescriptor) (*entity.DeviceDescriptor, error) DeleteByDeviceId(ctx context.Context, deviceId int) error GetByDeviceId(ctx context.Context, deviceId int) ([]*entity.DeviceDescriptor, error) + GetById(ctx context.Context, deviceId, id int) (*entity.DeviceDescriptor, error) } type SqliteDeviceDescriptorRepository struct { @@ -106,6 +107,35 @@ func (r *SqliteDeviceDescriptorRepository) GetByDeviceId(ctx context.Context, de return descriptors, nil } +func (r *SqliteDeviceDescriptorRepository) GetById(ctx context.Context, deviceId, id int) (*entity.DeviceDescriptor, error) { + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + defer rtx.RollbackTxOrIgnore() + + query, params := sq.Select("*").From("DeviceDescriptor").Where(sq.Eq{ + "device_id": deviceId, + "id": id, + }).MustSql() + + row := rtx.tx.QueryRowx(query, params...) + if row == nil || row.Err() != nil { + return nil, wrapSqliteDeviceDescriptorError(row.Err()) + } + deviceDescriptor := new(entity.RawDeviceDescriptor) + err = row.StructScan(deviceDescriptor) + if err != nil { + return nil, wrapSqliteDeviceDescriptorError(err) + } + + descriptor, err := entity.BuildDeviceDescriptorFrom(deviceDescriptor) + if err != nil { + return nil, errs.ErrInternalError + } + return descriptor, err +} + func wrapSqliteDeviceDescriptorError(err error) error { if errors.Is(err, sql.ErrNoRows) { return errs.ErrDeviceDescriptorNotFound diff --git a/internal/repositories/rule_repository.go b/internal/repositories/rule_repository.go index 80637d1..8baa174 100644 --- a/internal/repositories/rule_repository.go +++ b/internal/repositories/rule_repository.go @@ -10,15 +10,17 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/jmoiron/sqlx" "github.com/mattn/go-sqlite3" - "log" ) type RuleRepository interface { GetBySetupId(ctx context.Context, setupId int) ([]*entity.Rule, error) - GetOne(ctx context.Context, setupId int, id int) (*entity.Rule, error) + GetByManipulatorId(ctx context.Context, setupId, manipulatorId int) ([]*entity.Rule, error) + GetByExecutorId(ctx context.Context, setupId, executorId int) ([]*entity.Rule, error) + GetOne(ctx context.Context, setupId, id int) (*entity.Rule, error) Add(ctx context.Context, setupId int, rule *entity.NewRule) (*entity.Rule, error) - Update(ctx context.Context, setupId int, id int, updSetup *entity.UpdRule) error - Delete(ctx context.Context, setupId int, id int) error + Update(ctx context.Context, setupId, id int, updSetup *entity.UpdRule) error + Delete(ctx context.Context, setupId, id int) error + DeleteBySetupId(ctx context.Context, setupId int) error } type SqliteRuleRepository struct { @@ -29,6 +31,62 @@ func NewSqliteRuleRepository(db *sqlx.DB) *SqliteRuleRepository { return &SqliteRuleRepository{db: db} } +func (r *SqliteRuleRepository) GetByManipulatorId(ctx context.Context, setupId, manipulatorId int) ([]*entity.Rule, error) { + query, params := sq.Select("*").From("Rule").Where(sq.Eq{ + "setup_id": setupId, + "manipulator_id": manipulatorId, + }).MustSql() + + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + defer rtx.RollbackTxOrIgnore() + + rawRules := make([]*entity.RawRule, 0, 0) + err = rtx.tx.SelectContext(ctx, &rawRules, query, params...) + if err != nil { + return nil, errs.ErrInternalError + } + rules := make([]*entity.Rule, len(rawRules)) + for i, rawRule := range rawRules { + rules[i], err = entity.BuildRuleFrom(rawRule) + if err != nil { + return nil, errs.ErrInternalError + } + + } + return rules, nil +} + +func (r *SqliteRuleRepository) GetByExecutorId(ctx context.Context, setupId, executorId int) ([]*entity.Rule, error) { + query, params := sq.Select("*").From("Rule").Where(sq.Eq{ + "setup_id": setupId, + "executor_id": executorId, + }).MustSql() + + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return nil, err + } + defer rtx.RollbackTxOrIgnore() + + rawRules := make([]*entity.RawRule, 0, 0) + err = rtx.tx.SelectContext(ctx, &rawRules, query, params...) + if err != nil { + return nil, errs.ErrInternalError + } + rules := make([]*entity.Rule, len(rawRules)) + for i, rawRule := range rawRules { + rules[i], err = entity.BuildRuleFrom(rawRule) + if err != nil { + return nil, errs.ErrInternalError + } + + } + return rules, nil +} + func (r *SqliteRuleRepository) GetBySetupId(ctx context.Context, setupId int) ([]*entity.Rule, error) { query, params := sq.Select("*").From("Rule").Where(sq.Eq{ "setup_id": setupId, @@ -42,7 +100,6 @@ func (r *SqliteRuleRepository) GetBySetupId(ctx context.Context, setupId int) ([ rawRules := make([]*entity.RawRule, 0, 0) err = rtx.tx.SelectContext(ctx, &rawRules, query, params...) - log.Println(err) if err != nil { return nil, errs.ErrInternalError } @@ -161,7 +218,35 @@ func (r *SqliteRuleRepository) Delete(ctx context.Context, setupId int, id int) return errs.ErrInternalError } if affected == 0 { - return errs.ErrDeviceNotFound + return errs.ErrRuleNotFound + } + if err := rtx.CommitTxOrIgnore(); err != nil { + return errs.ErrInternalError + } + return nil +} + +func (r *SqliteRuleRepository) DeleteBySetupId(ctx context.Context, setupId int) error { + query, params := sq.Delete("Rule").Where(sq.Eq{ + "setup_id": setupId, + }).MustSql() + + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return err + } + defer rtx.RollbackTxOrIgnore() + + res, err := rtx.tx.ExecContext(ctx, query, params...) + if err != nil { + return wrapSqliteRuleError(err) + } + affected, err := res.RowsAffected() + if err != nil { + return errs.ErrInternalError + } + if affected == 0 { + return errs.ErrRuleNotFound } if err := rtx.CommitTxOrIgnore(); err != nil { return errs.ErrInternalError diff --git a/internal/services/delete_service.go b/internal/services/delete_service.go new file mode 100644 index 0000000..ba7c9cd --- /dev/null +++ b/internal/services/delete_service.go @@ -0,0 +1,64 @@ +package services + +import ( + "context" + "errors" + "git.miem.hse.ru/hubman/configurator/pkg/errs" +) + +type DeleteService struct { + setupService *SetupService + executorService *ExecutorService + manipulatorService *ManipulatorService + ruleService *RuleService +} + +func NewDeleteService( + setupService *SetupService, executorService *ExecutorService, + manipulatorService *ManipulatorService, ruleService *RuleService, +) *DeleteService { + return &DeleteService{ + setupService: setupService, + executorService: executorService, + manipulatorService: manipulatorService, + ruleService: ruleService, + } +} + +func (s *DeleteService) DeleteSetup(ctx context.Context, id int) error { + if err := s.ruleService.DeleteBySetupId(ctx, id); err != nil && !errors.Is(err, errs.ErrRuleNotFound) { + return err + } + if err := s.executorService.DeleteBySetupId(ctx, id); err != nil && !errors.Is(err, errs.ErrDeviceNotFound) { + return err + } + if err := s.manipulatorService.DeleteBySetupId(ctx, id); err != nil && !errors.Is(err, errs.ErrDeviceNotFound) { + return err + } + if err := s.setupService.Delete(ctx, id); err != nil { + return err + } + return nil +} + +func (s *DeleteService) DeleteManipulator(ctx context.Context, setupId, id int) error { + rules, err := s.ruleService.GetByManipulatorId(ctx, setupId, id) + if err != nil { + return err + } + if len(rules) != 0 { + return errs.ErrRuleIncorrectParams + } + return s.manipulatorService.Delete(ctx, setupId, id) +} + +func (s *DeleteService) DeleteExecutor(ctx context.Context, setupId, id int) error { + rules, err := s.ruleService.GetByExecutorId(ctx, setupId, id) + if err != nil { + return err + } + if len(rules) != 0 { + return errs.ErrRuleIncorrectParams + } + return s.manipulatorService.Delete(ctx, setupId, id) +} diff --git a/internal/services/device_store_service.go b/internal/services/device_store_service.go index fe4ff7b..5bc5f43 100644 --- a/internal/services/device_store_service.go +++ b/internal/services/device_store_service.go @@ -73,6 +73,20 @@ func (s *DeviceStoreService) GetOne( }, nil } +func (s *DeviceStoreService) GetOneDescriptor( + ctx context.Context, setupId, deviceId, id int, dtype entity.DeviceType, +) (*entity.DeviceDescriptor, error) { + _, err := s.deviceRepository.GetOne(ctx, setupId, dtype, id) + if err != nil { + return nil, err + } + descriptor, err := s.deviceDescriptorRepository.GetById(ctx, deviceId, id) + if err != nil { + return nil, err + } + return descriptor, nil +} + func (s *DeviceStoreService) UpdateDescriptors( ctx context.Context, deviceId int, newDescriptors []*entity.DeviceMessageDescriptor, diff --git a/internal/services/executor_service.go b/internal/services/executor_service.go index 7dcab4b..f820ca4 100644 --- a/internal/services/executor_service.go +++ b/internal/services/executor_service.go @@ -42,6 +42,10 @@ func (s *ExecutorService) getClient(ctx context.Context, url string) (*hubmancli return client, nil } +func (s *ExecutorService) GetCommandById(ctx context.Context, setupId, deviceId, descriptorId int) (*entity.DeviceDescriptor, error) { + return s.deviceStoreService.GetOneDescriptor(ctx, setupId, deviceId, descriptorId, entity.DeviceTypeManipulator) +} + func (s *ExecutorService) getCommands(ctx context.Context, client *hubmanclient.ClientWithResponses) ([]*entity.DeviceMessageDescriptor, error) { cmdResp, err := client.GetCommandsWithResponse(ctx) if err != nil || cmdResp.JSON200 == nil { diff --git a/internal/services/manipulator_service.go b/internal/services/manipulator_service.go index fb0e65c..1f1a4ef 100644 --- a/internal/services/manipulator_service.go +++ b/internal/services/manipulator_service.go @@ -39,6 +39,10 @@ func (s *ManipulatorService) getSignals(ctx context.Context, client *hubmanclien return signals, nil } +func (s *ManipulatorService) GetSignalById(ctx context.Context, setupId, deviceId, descriptorId int) (*entity.DeviceDescriptor, error) { + return s.deviceStoreService.GetOneDescriptor(ctx, setupId, deviceId, descriptorId, entity.DeviceTypeManipulator) +} + func (s *ManipulatorService) GetList(ctx context.Context, setupId int) ([]*entity.Device, error) { return s.deviceStoreService.GetBySetupAndType(ctx, setupId, entity.DeviceTypeManipulator) } @@ -74,3 +78,7 @@ func (s *ManipulatorService) Update(ctx context.Context, setupId int, id int, up func (s *ManipulatorService) Delete(ctx context.Context, setupId int, id int) error { return s.deviceStoreService.Delete(ctx, setupId, entity.DeviceTypeManipulator, id) } + +func (s *ManipulatorService) DeleteBySetupId(ctx context.Context, setupId int) error { + return s.deviceStoreService.DeleteBySetupId(ctx, setupId, entity.DeviceTypeExecutor) +} diff --git a/internal/services/rule_service.go b/internal/services/rule_service.go index 0e8259b..4b5ca80 100644 --- a/internal/services/rule_service.go +++ b/internal/services/rule_service.go @@ -5,6 +5,7 @@ import ( "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/internal/repositories" "git.miem.hse.ru/hubman/configurator/pkg/errs" + "github.com/diegoholiveira/jsonlogic" "strconv" ) @@ -22,7 +23,33 @@ func NewRuleService( return &RuleService{ruleRepository: ruleRepository, manipulatorService: manipulatorService, executorService: executorService} } +func (s *RuleService) validLogic(trigger any, logic entity.RuleLogic) error { + // TODO: Добавить проверку на иÑпользоуемые переменные. Да рефлект, да придетÑÑ Ð»Ð¸Ð±Ñƒ патчить/контрибутить + // TODO: Добавить проверку того, что пропиÑаны правила Ð´Ð»Ñ Ð¿Ð¾Ð»ÐµÐ¹ внутри logic + if !jsonlogic.IsValid(trigger) { + return errs.ErrRuleIncorrectParams + } + for _, logicStatement := range logic { + if !jsonlogic.IsValid(logicStatement) { + return errs.ErrRuleIncorrectParams + } + } + return nil +} + func (s *RuleService) Add(ctx context.Context, setupId int, newRule *entity.NewRule) (*entity.Rule, error) { + // TODO: СтатуÑ-коды неправильные, нужно что-то глобальное придумать + _, err := s.manipulatorService.GetSignalById(ctx, setupId, newRule.ManipulatorId, newRule.SignalDescriptorId) + if err != nil { + return nil, err + } + _, err = s.executorService.GetCommandById(ctx, setupId, newRule.ManipulatorId, newRule.SignalDescriptorId) + if err != nil { + return nil, err + } + if err := s.validLogic(newRule.Trigger, newRule.Logic); err != nil { + return nil, err + } return s.ruleRepository.Add(ctx, setupId, newRule) } @@ -30,6 +57,14 @@ func (s *RuleService) GetBySetupId(ctx context.Context, setupId int) ([]*entity. return s.ruleRepository.GetBySetupId(ctx, setupId) } +func (s *RuleService) GetByManipulatorId(ctx context.Context, setupId, manipulatorId int) ([]*entity.Rule, error) { + return s.ruleRepository.GetByManipulatorId(ctx, setupId, manipulatorId) +} + +func (s *RuleService) GetByExecutorId(ctx context.Context, setupId, executorId int) ([]*entity.Rule, error) { + return s.ruleRepository.GetByExecutorId(ctx, setupId, executorId) +} + func (s *RuleService) Update(ctx context.Context, setupId int, id int, updRule *entity.UpdRule) (*entity.Rule, error) { err := s.ruleRepository.Update(ctx, setupId, id, updRule) if err != nil { @@ -46,6 +81,10 @@ func (s *RuleService) Delete(ctx context.Context, setupId int, id int) error { return s.ruleRepository.Delete(ctx, setupId, id) } +func (s *RuleService) DeleteBySetupId(ctx context.Context, setupId int) error { + return s.ruleRepository.DeleteBySetupId(ctx, setupId) +} + func (s *RuleService) CheckId(RuleIdStr string) (int, error) { ruleId, err := strconv.Atoi(RuleIdStr) if err != nil { diff --git a/internal/services/setup_service.go b/internal/services/setup_service.go index 4917386..58dcdbd 100644 --- a/internal/services/setup_service.go +++ b/internal/services/setup_service.go @@ -47,26 +47,3 @@ func (s *SetupService) CheckId(setupIdStr string) (int, error) { } return setupId, nil } - -type DeleteSetupService struct { - setupService *SetupService - executorService *ExecutorService - manipulatorService *ManipulatorService -} - -func NewDeleteSetupService(setupService *SetupService, executorService *ExecutorService, manipulatorService *ManipulatorService) *DeleteSetupService { - return &DeleteSetupService{setupService: setupService, executorService: executorService, manipulatorService: manipulatorService} -} - -func (s *DeleteSetupService) Delete(ctx context.Context, id int) error { - if err := s.executorService.DeleteBySetupId(ctx, id); err != nil { - return err - } - //if err := s.manipulatorService.DeleteBySetupId(ctx, id); err != nil { - // return err - //} - if err := s.setupService.Delete(ctx, id); err != nil { - return err - } - return nil -} -- GitLab From c6b8a5e05417fe12fa810076d991195a7e167194 Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Mon, 30 Oct 2023 01:44:07 +0300 Subject: [PATCH 08/12] oh no.... --- internal/app/app.go | 6 +- internal/handlers/setup_executor_router.go | 15 +++- internal/handlers/setup_manipulator_router.go | 14 ++-- internal/handlers/setups_router.go | 8 +-- internal/repositories/rule_repository.go | 68 +++++++++++++++++-- internal/services/delete_service.go | 4 +- internal/services/rule_service.go | 23 ++++++- 7 files changed, 117 insertions(+), 21 deletions(-) diff --git a/internal/app/app.go b/internal/app/app.go index f6a6136..fcfcab8 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -38,15 +38,15 @@ func NewApp(conf *config.Config) *App { manipulatorService := services.NewManipulatorService(deviceStoreService) executorService := services.NewExecutorService(deviceStoreService) ruleService := services.NewRuleService(ruleRepository, manipulatorService, executorService) - deleteSetupService := services.NewDeleteSetupService(setupService, executorService, manipulatorService) + deleteSetupService := services.NewDeleteService(setupService, executorService, manipulatorService, ruleService) httpEngine := gin.Default() httpEngine.Use(transactions.InjectStorage(db)) routersToApply := []IApplyRouter{ handlers.NewSetupRouter(setupService, deleteSetupService), - handlers.NewSetupManipulatorRouter(setupService, manipulatorService), - handlers.NewSetupExecutorRouter(setupService, executorService), + handlers.NewSetupManipulatorRouter(setupService, manipulatorService, deleteSetupService), + handlers.NewSetupExecutorRouter(setupService, executorService, deleteSetupService), handlers.NewSetupRuleRouter(setupService, ruleService), } for _, router := range routersToApply { diff --git a/internal/handlers/setup_executor_router.go b/internal/handlers/setup_executor_router.go index c587686..d3ad37b 100644 --- a/internal/handlers/setup_executor_router.go +++ b/internal/handlers/setup_executor_router.go @@ -12,10 +12,19 @@ import ( type SetupExecutorRouter struct { setupService *services.SetupService executorService *services.ExecutorService + deleteService *services.DeleteService } -func NewSetupExecutorRouter(setupService *services.SetupService, executorService *services.ExecutorService) *SetupExecutorRouter { - return &SetupExecutorRouter{setupService, executorService} +func NewSetupExecutorRouter( + setupService *services.SetupService, + executorService *services.ExecutorService, + deleteService *services.DeleteService, +) *SetupExecutorRouter { + return &SetupExecutorRouter{ + setupService: setupService, + executorService: executorService, + deleteService: deleteService, + } } func (r *SetupExecutorRouter) ApplyTo(httpEngine gin.IRouter) { @@ -117,7 +126,7 @@ func (r *SetupExecutorRouter) Delete(c *gin.Context) { ctx = transactions.InjectTx(ctx, tx) defer tx.Rollback() - err = r.executorService.Delete(ctx, getSetupId(c), getExecutorId(c)) + err = r.deleteService.DeleteExecutor(ctx, getSetupId(c), getExecutorId(c)) if err != nil { BuildErrResp(c, err) return diff --git a/internal/handlers/setup_manipulator_router.go b/internal/handlers/setup_manipulator_router.go index 042481e..8c907f6 100644 --- a/internal/handlers/setup_manipulator_router.go +++ b/internal/handlers/setup_manipulator_router.go @@ -12,6 +12,7 @@ import ( type SetupManipulatorRouter struct { setupService *services.SetupService manipulatorService *services.ManipulatorService + deleteService *services.DeleteService } func (r *SetupManipulatorRouter) ApplyTo(httpEngine gin.IRouter) { @@ -29,10 +30,15 @@ func (r *SetupManipulatorRouter) ApplyTo(httpEngine gin.IRouter) { } } -func NewSetupManipulatorRouter(setupService *services.SetupService, manipulatorService *services.ManipulatorService) *SetupManipulatorRouter { +func NewSetupManipulatorRouter( + setupService *services.SetupService, + manipulatorService *services.ManipulatorService, + deleteService *services.DeleteService, +) *SetupManipulatorRouter { return &SetupManipulatorRouter{ - setupService, - manipulatorService, + setupService: setupService, + manipulatorService: manipulatorService, + deleteService: deleteService, } } @@ -120,7 +126,7 @@ func (r *SetupManipulatorRouter) Delete(c *gin.Context) { ctx = transactions.InjectTx(ctx, tx) defer tx.Rollback() - err = r.manipulatorService.Delete(ctx, getSetupId(c), GetManipulatorId(c)) + err = r.deleteService.DeleteManipulator(ctx, getSetupId(c), GetManipulatorId(c)) if err != nil { BuildErrResp(c, err) return diff --git a/internal/handlers/setups_router.go b/internal/handlers/setups_router.go index f023512..e782014 100644 --- a/internal/handlers/setups_router.go +++ b/internal/handlers/setups_router.go @@ -11,11 +11,11 @@ import ( type SetupRouter struct { setupService *services.SetupService - deleteSetupService *services.DeleteSetupService + deleteSetupService *services.DeleteService } -func NewSetupRouter(setupService *services.SetupService, deleteSetupService *services.DeleteSetupService) *SetupRouter { - return &SetupRouter{setupService, deleteSetupService} +func NewSetupRouter(setupService *services.SetupService, deleteService *services.DeleteService) *SetupRouter { + return &SetupRouter{setupService, deleteService} } func (r *SetupRouter) ApplyTo(httpEngine gin.IRouter) { @@ -72,7 +72,7 @@ func (r *SetupRouter) Delete(c *gin.Context) { ctx = transactions.InjectTx(ctx, tx) defer tx.Rollback() - err = r.deleteSetupService.Delete(ctx, getSetupId(c)) + err = r.deleteSetupService.DeleteSetup(ctx, getSetupId(c)) if err != nil { BuildErrResp(c, err) return diff --git a/internal/repositories/rule_repository.go b/internal/repositories/rule_repository.go index 8baa174..588f206 100644 --- a/internal/repositories/rule_repository.go +++ b/internal/repositories/rule_repository.go @@ -18,7 +18,7 @@ type RuleRepository interface { GetByExecutorId(ctx context.Context, setupId, executorId int) ([]*entity.Rule, error) GetOne(ctx context.Context, setupId, id int) (*entity.Rule, error) Add(ctx context.Context, setupId int, rule *entity.NewRule) (*entity.Rule, error) - Update(ctx context.Context, setupId, id int, updSetup *entity.UpdRule) error + Update(ctx context.Context, setupId, id int, updRule *entity.UpdRule) error Delete(ctx context.Context, setupId, id int) error DeleteBySetupId(ctx context.Context, setupId int) error } @@ -192,9 +192,69 @@ func (r *SqliteRuleRepository) Add(ctx context.Context, setupId int, rule *entit }, nil } -func (r *SqliteRuleRepository) Update(ctx context.Context, setupId int, id int, updSetup *entity.UpdRule) error { - //TODO implement me - panic("implement me") +func (r *SqliteRuleRepository) Update(ctx context.Context, setupId int, id int, updRule *entity.UpdRule) error { + rtx, err := extractSqlxTxOrNew(ctx, r.db) + if err != nil { + return err + } + defer rtx.RollbackTxOrIgnore() + + if (updRule == nil) || (*updRule == entity.UpdRule{}) { + return errs.ErrRuleIncorrectParams + } + + setMap := make(map[string]any) + if updRule.Name != nil { + setMap["name"] = updRule.Name + } + if updRule.Description != nil { + setMap["description"] = updRule.Description + } + if updRule.ManipulatorId != nil { + setMap["manipulator_id"] = updRule.ManipulatorId + } + if updRule.SignalDescriptorId != nil { + setMap["signal_descriptor_id"] = updRule.SignalDescriptorId + } + if updRule.ExecutorId != nil { + setMap["executor_id"] = updRule.ExecutorId + } + if updRule.CommandDescriptorId != nil { + setMap["command_descriptor_id"] = updRule.CommandDescriptorId + } + if updRule.Trigger != nil { + triggerBytes, err := json.Marshal(updRule.Trigger) + if err != nil { + return errs.ErrInternalError + } + setMap["trigger"] = string(triggerBytes) + } + if updRule.Logic != nil { + logicBytes, err := json.Marshal(updRule.Logic) + if err != nil { + return errs.ErrInternalError + } + setMap["logic"] = string(logicBytes) + } + + query, params := sq.Update("Rule").SetMap(setMap).Where(sq.Eq{ + "setup_id": setupId, "id": id, + }).MustSql() + res, err := rtx.tx.Exec(query, params...) + if err != nil { + return wrapSqliteRuleError(err) + } + affected, err := res.RowsAffected() + if err != nil { + return errs.ErrInternalError + } + if affected == 0 { + return errs.ErrRuleNotFound + } + if err := rtx.CommitTxOrIgnore(); err != nil { + return errs.ErrInternalError + } + return nil } func (r *SqliteRuleRepository) Delete(ctx context.Context, setupId int, id int) error { diff --git a/internal/services/delete_service.go b/internal/services/delete_service.go index ba7c9cd..2bf3360 100644 --- a/internal/services/delete_service.go +++ b/internal/services/delete_service.go @@ -4,6 +4,7 @@ import ( "context" "errors" "git.miem.hse.ru/hubman/configurator/pkg/errs" + "log" ) type DeleteService struct { @@ -54,11 +55,12 @@ func (s *DeleteService) DeleteManipulator(ctx context.Context, setupId, id int) func (s *DeleteService) DeleteExecutor(ctx context.Context, setupId, id int) error { rules, err := s.ruleService.GetByExecutorId(ctx, setupId, id) + log.Println(rules, err) if err != nil { return err } if len(rules) != 0 { return errs.ErrRuleIncorrectParams } - return s.manipulatorService.Delete(ctx, setupId, id) + return s.executorService.Delete(ctx, setupId, id) } diff --git a/internal/services/rule_service.go b/internal/services/rule_service.go index 4b5ca80..a0265e5 100644 --- a/internal/services/rule_service.go +++ b/internal/services/rule_service.go @@ -23,12 +23,18 @@ func NewRuleService( return &RuleService{ruleRepository: ruleRepository, manipulatorService: manipulatorService, executorService: executorService} } -func (s *RuleService) validLogic(trigger any, logic entity.RuleLogic) error { +func (s *RuleService) validTrigger(trigger any) error { // TODO: Добавить проверку на иÑпользоуемые переменные. Да рефлект, да придетÑÑ Ð»Ð¸Ð±Ñƒ патчить/контрибутить // TODO: Добавить проверку того, что пропиÑаны правила Ð´Ð»Ñ Ð¿Ð¾Ð»ÐµÐ¹ внутри logic if !jsonlogic.IsValid(trigger) { return errs.ErrRuleIncorrectParams } + return nil +} + +func (s *RuleService) validLogic(logic entity.RuleLogic) error { + // TODO: Добавить проверку на иÑпользоуемые переменные. Да рефлект, да придетÑÑ Ð»Ð¸Ð±Ñƒ патчить/контрибутить + // TODO: Добавить проверку того, что пропиÑаны правила Ð´Ð»Ñ Ð¿Ð¾Ð»ÐµÐ¹ внутри logic for _, logicStatement := range logic { if !jsonlogic.IsValid(logicStatement) { return errs.ErrRuleIncorrectParams @@ -47,7 +53,10 @@ func (s *RuleService) Add(ctx context.Context, setupId int, newRule *entity.NewR if err != nil { return nil, err } - if err := s.validLogic(newRule.Trigger, newRule.Logic); err != nil { + if err := s.validTrigger(newRule.Trigger); err != nil { + return nil, err + } + if err := s.validLogic(newRule.Logic); err != nil { return nil, err } return s.ruleRepository.Add(ctx, setupId, newRule) @@ -66,6 +75,16 @@ func (s *RuleService) GetByExecutorId(ctx context.Context, setupId, executorId i } func (s *RuleService) Update(ctx context.Context, setupId int, id int, updRule *entity.UpdRule) (*entity.Rule, error) { + if updRule.Trigger != nil { + if err := s.validTrigger(updRule.Trigger); err != nil { + return nil, err + } + } + if updRule.Trigger != nil { + if err := s.validLogic(*updRule.Logic); err != nil { + return nil, err + } + } err := s.ruleRepository.Update(ctx, setupId, id, updRule) if err != nil { return nil, err -- GitLab From 57e2611e237d124a0aa19cecc96ebb759c522d6a Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Mon, 30 Oct 2023 12:41:58 +0300 Subject: [PATCH 09/12] fix issues --- internal/handlers/utils.go | 2 +- internal/repositories/device_repository.go | 3 +++ internal/repositories/rule_repository.go | 9 +-------- internal/repositories/setup_repository.go | 2 -- internal/services/delete_service.go | 2 -- internal/services/executor_service.go | 2 -- internal/services/manipulator_service.go | 2 -- 7 files changed, 5 insertions(+), 17 deletions(-) diff --git a/internal/handlers/utils.go b/internal/handlers/utils.go index 3dcc6ec..71efabd 100644 --- a/internal/handlers/utils.go +++ b/internal/handlers/utils.go @@ -8,7 +8,7 @@ import ( ) type ErrorSchema struct { - Code string `json:"status"` + Code string `json:"err_code"` } func BuildErrResp(c *gin.Context, err error) { diff --git a/internal/repositories/device_repository.go b/internal/repositories/device_repository.go index a3c8318..5a74a39 100644 --- a/internal/repositories/device_repository.go +++ b/internal/repositories/device_repository.go @@ -99,6 +99,9 @@ func (r *SqliteDeviceRepository) DeleteBySetupId(ctx context.Context, setupId in if err != nil { return wrapSqliteDeviceError(err) } + if err := rtx.CommitTxOrIgnore(); err != nil { + return errs.ErrInternalError + } return nil } diff --git a/internal/repositories/rule_repository.go b/internal/repositories/rule_repository.go index 588f206..1733675 100644 --- a/internal/repositories/rule_repository.go +++ b/internal/repositories/rule_repository.go @@ -297,17 +297,10 @@ func (r *SqliteRuleRepository) DeleteBySetupId(ctx context.Context, setupId int) } defer rtx.RollbackTxOrIgnore() - res, err := rtx.tx.ExecContext(ctx, query, params...) + _, err = rtx.tx.ExecContext(ctx, query, params...) if err != nil { return wrapSqliteRuleError(err) } - affected, err := res.RowsAffected() - if err != nil { - return errs.ErrInternalError - } - if affected == 0 { - return errs.ErrRuleNotFound - } if err := rtx.CommitTxOrIgnore(); err != nil { return errs.ErrInternalError } diff --git a/internal/repositories/setup_repository.go b/internal/repositories/setup_repository.go index 19520da..0a14e42 100644 --- a/internal/repositories/setup_repository.go +++ b/internal/repositories/setup_repository.go @@ -9,7 +9,6 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/jmoiron/sqlx" "github.com/mattn/go-sqlite3" - "log" ) type SetupRepository interface { @@ -127,7 +126,6 @@ func (r SqliteSetupRepository) Add(ctx context.Context, newSetup *entity.NewSetu } return nil, wrapSqliteSetupError(err) } - log.Println(err) return &entity.Setup{ Id: int(id), Name: newSetup.Name, diff --git a/internal/services/delete_service.go b/internal/services/delete_service.go index 2bf3360..404b270 100644 --- a/internal/services/delete_service.go +++ b/internal/services/delete_service.go @@ -4,7 +4,6 @@ import ( "context" "errors" "git.miem.hse.ru/hubman/configurator/pkg/errs" - "log" ) type DeleteService struct { @@ -55,7 +54,6 @@ func (s *DeleteService) DeleteManipulator(ctx context.Context, setupId, id int) func (s *DeleteService) DeleteExecutor(ctx context.Context, setupId, id int) error { rules, err := s.ruleService.GetByExecutorId(ctx, setupId, id) - log.Println(rules, err) if err != nil { return err } diff --git a/internal/services/executor_service.go b/internal/services/executor_service.go index f820ca4..f4bbb9b 100644 --- a/internal/services/executor_service.go +++ b/internal/services/executor_service.go @@ -5,7 +5,6 @@ import ( "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/pkg/errs" hubmanclient "git.miem.hse.ru/hubman/hubman-lib/client" - "log" "strconv" ) @@ -49,7 +48,6 @@ func (s *ExecutorService) GetCommandById(ctx context.Context, setupId, deviceId, func (s *ExecutorService) getCommands(ctx context.Context, client *hubmanclient.ClientWithResponses) ([]*entity.DeviceMessageDescriptor, error) { cmdResp, err := client.GetCommandsWithResponse(ctx) if err != nil || cmdResp.JSON200 == nil { - log.Println(err) return nil, errs.ErrIncorrectParams } commands := make([]*entity.DeviceMessageDescriptor, len(*cmdResp.JSON200)) diff --git a/internal/services/manipulator_service.go b/internal/services/manipulator_service.go index 1f1a4ef..a36210a 100644 --- a/internal/services/manipulator_service.go +++ b/internal/services/manipulator_service.go @@ -5,7 +5,6 @@ import ( "git.miem.hse.ru/hubman/configurator/internal/entity" "git.miem.hse.ru/hubman/configurator/pkg/errs" hubmanclient "git.miem.hse.ru/hubman/hubman-lib/client" - "log" "strconv" ) @@ -29,7 +28,6 @@ func (s *ManipulatorService) getClient(ctx context.Context, url string) (*hubman func (s *ManipulatorService) getSignals(ctx context.Context, client *hubmanclient.ClientWithResponses) ([]*entity.DeviceMessageDescriptor, error) { cmdResp, err := client.GetSignalsWithResponse(ctx) if err != nil || cmdResp.JSON200 == nil { - log.Println(err) return nil, errs.ErrIncorrectParams } signals := make([]*entity.DeviceMessageDescriptor, len(*cmdResp.JSON200)) -- GitLab From 58bd5cdf33118acbb5567e223c97b61feabb2873 Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Mon, 30 Oct 2023 19:45:23 +0300 Subject: [PATCH 10/12] fix update rule --- internal/services/rule_service.go | 40 +++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/internal/services/rule_service.go b/internal/services/rule_service.go index a0265e5..18746d4 100644 --- a/internal/services/rule_service.go +++ b/internal/services/rule_service.go @@ -6,6 +6,7 @@ import ( "git.miem.hse.ru/hubman/configurator/internal/repositories" "git.miem.hse.ru/hubman/configurator/pkg/errs" "github.com/diegoholiveira/jsonlogic" + "log" "strconv" ) @@ -75,17 +76,52 @@ func (s *RuleService) GetByExecutorId(ctx context.Context, setupId, executorId i } func (s *RuleService) Update(ctx context.Context, setupId int, id int, updRule *entity.UpdRule) (*entity.Rule, error) { + rule, err := s.ruleRepository.GetOne(ctx, setupId, id) + if err != nil { + return nil, err + } + if updRule.ManipulatorId != nil && updRule.SignalDescriptorId == nil { + return nil, errs.ErrRuleIncorrectParams + } + if updRule.ExecutorId != nil && updRule.CommandDescriptorId == nil { + return nil, errs.ErrRuleIncorrectParams + } + log.Println("") + if updRule.ManipulatorId != nil { + signalDescriptorId := rule.SignalDescriptorId + if updRule.SignalDescriptorId != nil { + signalDescriptorId = *updRule.SignalDescriptorId + } + _, err := s.manipulatorService.GetSignalById( + ctx, setupId, *updRule.ManipulatorId, signalDescriptorId, + ) + if err != nil { + return nil, err + } + } + if updRule.ExecutorId != nil { + commandDescriptorId := rule.CommandDescriptorId + if updRule.SignalDescriptorId != nil { + commandDescriptorId = *updRule.CommandDescriptorId + } + _, err := s.executorService.GetCommandById( + ctx, setupId, *updRule.ManipulatorId, commandDescriptorId, + ) + if err != nil { + return nil, err + } + } if updRule.Trigger != nil { if err := s.validTrigger(updRule.Trigger); err != nil { return nil, err } } - if updRule.Trigger != nil { + if updRule.Logic != nil { if err := s.validLogic(*updRule.Logic); err != nil { return nil, err } } - err := s.ruleRepository.Update(ctx, setupId, id, updRule) + err = s.ruleRepository.Update(ctx, setupId, id, updRule) if err != nil { return nil, err } -- GitLab From 60af89eaa74b5c1cb694ef3f22db3b8e6f917c8c Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Mon, 30 Oct 2023 19:48:29 +0300 Subject: [PATCH 11/12] rm unused print --- internal/services/rule_service.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/services/rule_service.go b/internal/services/rule_service.go index 18746d4..f84e39e 100644 --- a/internal/services/rule_service.go +++ b/internal/services/rule_service.go @@ -6,7 +6,6 @@ import ( "git.miem.hse.ru/hubman/configurator/internal/repositories" "git.miem.hse.ru/hubman/configurator/pkg/errs" "github.com/diegoholiveira/jsonlogic" - "log" "strconv" ) @@ -86,7 +85,6 @@ func (s *RuleService) Update(ctx context.Context, setupId int, id int, updRule * if updRule.ExecutorId != nil && updRule.CommandDescriptorId == nil { return nil, errs.ErrRuleIncorrectParams } - log.Println("") if updRule.ManipulatorId != nil { signalDescriptorId := rule.SignalDescriptorId if updRule.SignalDescriptorId != nil { -- GitLab From c3e51bd3c90d0d9760e136030363700bedb4ea1d Mon Sep 17 00:00:00 2001 From: Sergei Loshkarev <saloshkarev@miem.hse.ru> Date: Mon, 30 Oct 2023 20:11:43 +0300 Subject: [PATCH 12/12] fix update-rule --- internal/services/executor_service.go | 2 +- internal/services/rule_service.go | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/services/executor_service.go b/internal/services/executor_service.go index f4bbb9b..aac388a 100644 --- a/internal/services/executor_service.go +++ b/internal/services/executor_service.go @@ -42,7 +42,7 @@ func (s *ExecutorService) getClient(ctx context.Context, url string) (*hubmancli } func (s *ExecutorService) GetCommandById(ctx context.Context, setupId, deviceId, descriptorId int) (*entity.DeviceDescriptor, error) { - return s.deviceStoreService.GetOneDescriptor(ctx, setupId, deviceId, descriptorId, entity.DeviceTypeManipulator) + return s.deviceStoreService.GetOneDescriptor(ctx, setupId, deviceId, descriptorId, entity.DeviceTypeExecutor) } func (s *ExecutorService) getCommands(ctx context.Context, client *hubmanclient.ClientWithResponses) ([]*entity.DeviceMessageDescriptor, error) { diff --git a/internal/services/rule_service.go b/internal/services/rule_service.go index f84e39e..4d67d2c 100644 --- a/internal/services/rule_service.go +++ b/internal/services/rule_service.go @@ -85,25 +85,25 @@ func (s *RuleService) Update(ctx context.Context, setupId int, id int, updRule * if updRule.ExecutorId != nil && updRule.CommandDescriptorId == nil { return nil, errs.ErrRuleIncorrectParams } - if updRule.ManipulatorId != nil { - signalDescriptorId := rule.SignalDescriptorId - if updRule.SignalDescriptorId != nil { - signalDescriptorId = *updRule.SignalDescriptorId + if updRule.SignalDescriptorId != nil { + manipulatorId := rule.ManipulatorId + if updRule.ManipulatorId != nil { + manipulatorId = *updRule.ManipulatorId } _, err := s.manipulatorService.GetSignalById( - ctx, setupId, *updRule.ManipulatorId, signalDescriptorId, + ctx, setupId, manipulatorId, *updRule.SignalDescriptorId, ) if err != nil { return nil, err } } - if updRule.ExecutorId != nil { - commandDescriptorId := rule.CommandDescriptorId - if updRule.SignalDescriptorId != nil { - commandDescriptorId = *updRule.CommandDescriptorId + if updRule.CommandDescriptorId != nil { + executorId := rule.ExecutorId + if updRule.ExecutorId != nil { + executorId = *updRule.ExecutorId } _, err := s.executorService.GetCommandById( - ctx, setupId, *updRule.ManipulatorId, commandDescriptorId, + ctx, setupId, executorId, *updRule.CommandDescriptorId, ) if err != nil { return nil, err -- GitLab