diff --git a/.gitignore b/.gitignore index 1ef4f328c..2dd59cc36 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,13 @@ unitylog.txt # APP Ignore deploy/docker/*.env -deploy/docker/app-deploy \ No newline at end of file +deploy/docker/app-deploy + +#ARANGO_API Ignore +ARANGO_API/arango-api +ARANGO_API/*env + +#BFF Ignore +BFF/*env +BFF/ogree-bff + diff --git a/APP/ogree_app/lib/models/tenant.dart b/APP/ogree_app/lib/models/tenant.dart index 77e67dfb7..4eac23879 100644 --- a/APP/ogree_app/lib/models/tenant.dart +++ b/APP/ogree_app/lib/models/tenant.dart @@ -15,6 +15,7 @@ class Tenant { String docUrl; String docPort; String imageTag; + bool hasBff; TenantStatus? status; Tenant( @@ -29,6 +30,7 @@ class Tenant { this.docUrl, this.docPort, this.imageTag, + this.hasBff, {this.status}); Map toMap() { @@ -44,22 +46,25 @@ class Tenant { 'docUrl': docUrl, 'docPort': docPort, 'imageTag': imageTag, + 'hasBff': hasBff, }; } factory Tenant.fromMap(Map map) { return Tenant( - map['name'].toString(), - map['customerPassword'].toString(), - map['apiUrl'].toString(), - map['webUrl'].toString(), - map['apiPort'].toString(), - map['webPort'].toString(), - map['hasWeb'], - map['hasDoc'], - map['docUrl'].toString(), - map['docPort'].toString(), - map['imageTag'].toString()); + map['name'].toString(), + map['customerPassword'].toString(), + map['apiUrl'].toString(), + map['webUrl'].toString(), + map['apiPort'].toString(), + map['webPort'].toString(), + map['hasWeb'], + map['hasDoc'], + map['docUrl'].toString(), + map['docPort'].toString(), + map['imageTag'].toString(), + map['hasBff'], + ); } String toJson() => json.encode(toMap()); diff --git a/APP/ogree_app/lib/widgets/tenants/popups/create_tenant_popup.dart b/APP/ogree_app/lib/widgets/tenants/popups/create_tenant_popup.dart index ea1be5e8e..95fe86e44 100644 --- a/APP/ogree_app/lib/widgets/tenants/popups/create_tenant_popup.dart +++ b/APP/ogree_app/lib/widgets/tenants/popups/create_tenant_popup.dart @@ -28,6 +28,7 @@ class _CreateTenantPopupState extends State { String _docPort = ""; bool _hasWeb = true; bool _hasDoc = false; + bool _hasBff = false; bool _isLoading = false; PlatformFile? _loadedImage; String _imageTag = "main"; @@ -78,8 +79,8 @@ class _CreateTenantPopupState extends State { alignment: WrapAlignment.center, crossAxisAlignment: WrapCrossAlignment.center, children: [ - getCheckBox("API", true, (_) {}, - enabled: false), + // getCheckBox("API", true, (_) {}, + // enabled: false), getCheckBox( "WEB", _hasWeb, @@ -92,6 +93,12 @@ class _CreateTenantPopupState extends State { (value) => setState(() { _hasDoc = value!; })), + getCheckBox( + "Arango", + _hasBff, + (value) => setState(() { + _hasBff = value!; + })), ], ), const SizedBox(height: 10), @@ -259,7 +266,8 @@ class _CreateTenantPopupState extends State { _hasDoc, _docUrl, _docPort, - _imageTag)); + _imageTag, + _hasBff)); switch (result) { case Success(value: final value): widget.parentCallback(); @@ -278,7 +286,7 @@ class _CreateTenantPopupState extends State { getCheckBox(String title, bool value, Function(bool?) onChange, {bool enabled = true}) { return SizedBox( - width: 95, + width: 110, child: CheckboxListTile( activeColor: Colors.blue.shade600, contentPadding: EdgeInsets.zero, diff --git a/APP/ogree_app/lib/widgets/tenants/popups/update_tenant_popup.dart b/APP/ogree_app/lib/widgets/tenants/popups/update_tenant_popup.dart index 6fef88ed7..5e4417969 100644 --- a/APP/ogree_app/lib/widgets/tenants/popups/update_tenant_popup.dart +++ b/APP/ogree_app/lib/widgets/tenants/popups/update_tenant_popup.dart @@ -54,7 +54,7 @@ class _UpdateTenantPopupState extends State { alignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.center, children: [ - getCheckBox("API", true, (_) {}, enabled: false), + // getCheckBox("API", true, (_) {}, enabled: false), getCheckBox( "WEB", widget.tenant.hasWeb, @@ -67,6 +67,8 @@ class _UpdateTenantPopupState extends State { (value) => setState(() { widget.tenant.hasDoc = value!; })), + getCheckBox("Arango", widget.tenant.hasBff, (_) {}, + enabled: false), ], ), getFormField( @@ -182,7 +184,7 @@ class _UpdateTenantPopupState extends State { getCheckBox(String title, bool value, Function(bool?) onChange, {bool enabled = true}) { return SizedBox( - width: 95, + width: 105, child: CheckboxListTile( activeColor: Colors.blue.shade600, contentPadding: EdgeInsets.zero, diff --git a/APP/ogree_app_backend/api.go b/APP/ogree_app_backend/api.go index 257292bc2..db652c107 100644 --- a/APP/ogree_app_backend/api.go +++ b/APP/ogree_app_backend/api.go @@ -15,6 +15,7 @@ import ( ) var tmplt *template.Template +var bfftmplt *template.Template var apptmplt *template.Template var servertmplt *template.Template var netboxtmplt *template.Template @@ -35,6 +36,7 @@ func init() { // []byte("password"), bcrypt.DefaultCost) // println(string(hashedPassword)) tmplt = template.Must(template.ParseFiles("backend-assets/docker-env-template.txt")) + bfftmplt = template.Must(template.ParseFiles("backend-assets/bff-api-list-template.txt")) apptmplt = template.Must(template.ParseFiles("flutter-assets/flutter-env-template.txt")) servertmplt = template.Must(template.ParseFiles("backend-assets/template.service")) netboxtmplt = template.Must(template.ParseFiles("tools-assets/netbox-docker-template.txt")) diff --git a/APP/ogree_app_backend/backend-assets/bff-api-list-template.txt b/APP/ogree_app_backend/backend-assets/bff-api-list-template.txt new file mode 100644 index 000000000..cd29bee03 --- /dev/null +++ b/APP/ogree_app_backend/backend-assets/bff-api-list-template.txt @@ -0,0 +1,4 @@ +[ + {"name":"server", "url": "http://{{.Name}}_arango_api:8080"}, + {"name":"objects", "url": "http://{{.Name}}_api:{{.ApiPort}}"} +] \ No newline at end of file diff --git a/APP/ogree_app_backend/backend-assets/docker-env-template.txt b/APP/ogree_app_backend/backend-assets/docker-env-template.txt index 4fade7380..f6516ebba 100644 --- a/APP/ogree_app_backend/backend-assets/docker-env-template.txt +++ b/APP/ogree_app_backend/backend-assets/docker-env-template.txt @@ -9,3 +9,15 @@ API_EXTERNALURL={{.ApiUrl}} COMPOSE_PROJECT_NAME={{.Name}} APP_ASSETS_DIR={{.AssetsDir}} IMAGE_TAG={{.ImageTag}} + +ARANGO_API_BUILD_DIR=ARANGO_API +ARANGO_API_PORT=8080 +ARANGO_PASS=ogree +ARANGO_USER=root +ARANGO_DB=_system + +BFF_BUILD_DIR=BFF +BFF_PORT={{.BffPort}} +BFF_API_LIST={{.BffApiListFile}} + +AUTH_SECRET=myAwesomeApiSecret \ No newline at end of file diff --git a/APP/ogree_app_backend/tenant.go b/APP/ogree_app_backend/tenant.go index ddcd9efe2..43cb87de0 100644 --- a/APP/ogree_app_backend/tenant.go +++ b/APP/ogree_app_backend/tenant.go @@ -12,6 +12,7 @@ import ( "os" "os/exec" "regexp" + "strconv" "strings" "time" @@ -27,10 +28,13 @@ type tenant struct { WebPort string `json:"webPort"` DocUrl string `json:"docUrl"` DocPort string `json:"docPort"` + BffPort string `json:"bffPort"` HasWeb bool `json:"hasWeb"` HasDoc bool `json:"hasDoc"` + HasBff bool `json:"hasBff"` AssetsDir string `json:"assetsDir"` ImageTag string `json:"imageTag"` + BffApiListFile string } type container struct { @@ -182,6 +186,12 @@ func addTenant(c *gin.Context) { func dockerCreateTenant(newTenant tenant) string { tenantLower := strings.ToLower(newTenant.Name) + appDeployDir := DOCKER_DIR + "app-deploy/" + tenantLower + "/" + err := os.MkdirAll(appDeployDir, 0755) + if err != nil && !strings.Contains(err.Error(), "already") { + println(err.Error()) + } + // Image tagging if newTenant.ImageTag == "" { newTenant.ImageTag = "main" @@ -193,29 +203,53 @@ func dockerCreateTenant(newTenant tenant) string { args = append(args, "--profile") args = append(args, "web") // Create flutter assets folder - newTenant.AssetsDir = DOCKER_DIR + "app-deploy/" + tenantLower + newTenant.AssetsDir = appDeployDir + "flutter" addAppAssets(newTenant) } else { // docker does not accept it empty, even if it wont be created newTenant.AssetsDir = DOCKER_DIR } + if newTenant.HasDoc { args = append(args, "--profile") args = append(args, "doc") } + + // Default values, empty vars not accepted on docker compose + newTenant.BffApiListFile = "./bff_api_list.json" + newTenant.BffPort = newTenant.ApiPort + if newTenant.HasBff { + args = append(args, "--profile") + args = append(args, "arango") + if newTenant.BffPort == "" { + // Set API Port to BFF Port + 1 + port, _ := strconv.Atoi(newTenant.ApiPort) + newTenant.ApiPort = strconv.Itoa(port + 1) + } + // Create bff api list json file + file, _ := os.Create(appDeployDir + tenantLower + "-bff-api-list.json") + err := bfftmplt.Execute(file, newTenant) + if err != nil { + fmt.Println("Error creating bff api list file: " + err.Error()) + } else { + newTenant.BffApiListFile = "./app-deploy/" + tenantLower + "/" + tenantLower + "-bff-api-list.json" + } + file.Close() + } + args = append(args, "up") args = append(args, "--build") args = append(args, "-d") // Create .env file file, _ := os.Create(DOCKER_DIR + ".env") - err := tmplt.Execute(file, newTenant) + err = tmplt.Execute(file, newTenant) if err != nil { panic(err) } file.Close() // Create tenantName.env as a copy - file, _ = os.Create(DOCKER_DIR + tenantLower + ".env") + file, _ = os.Create(appDeployDir + tenantLower + ".env") err = tmplt.Execute(file, newTenant) if err != nil { fmt.Println("Error creating .env copy: " + err.Error()) @@ -285,7 +319,7 @@ func addTenantLogo(c *gin.Context) { c.String(http.StatusInternalServerError, err.Error()) } // Make sure destination dir is created - assetsDir := DOCKER_DIR + "app-deploy/" + tenantName + assetsDir := DOCKER_DIR + "app-deploy/" + tenantName + "/flutter" err = os.MkdirAll(assetsDir, 0755) if err != nil && !strings.Contains(err.Error(), "already") { c.String(http.StatusInternalServerError, err.Error()) @@ -302,7 +336,7 @@ func removeTenant(c *gin.Context) { tenantName := strings.ToLower(c.Param("name")) // Stop and remove containers - for _, str := range []string{"_webapp", "_api", "_db", "_doc"} { + for _, str := range []string{"_webapp", "_api", "_db", "_doc", "_bff", "_arango_api", "_arango_db"} { cmd := exec.Command("docker", "rm", "--force", strings.ToLower(tenantName)+str) cmd.Dir = DOCKER_DIR var stderr bytes.Buffer @@ -315,7 +349,7 @@ func removeTenant(c *gin.Context) { } // Remove assets - os.RemoveAll(DOCKER_DIR + "app-deploy/" + tenantName) + os.RemoveAll(DOCKER_DIR + "app-deploy/" + tenantName + "/flutter") os.Remove(DOCKER_DIR + tenantName + ".env") // Update local file diff --git a/ARANGO_API/database/common.go b/ARANGO_API/database/common.go new file mode 100644 index 000000000..75589961c --- /dev/null +++ b/ARANGO_API/database/common.go @@ -0,0 +1,154 @@ +package database + +import ( + "arango-api/models" + "encoding/json" + h "net/http" + + driver "github.com/arangodb/go-driver" + "github.com/arangodb/go-driver/http" + "github.com/gin-gonic/gin" +) + +func GetDBConn(c *gin.Context) (*driver.Database, *models.ErrorMessage) { + dbConn, ok := c.Value("database").(*driver.Database) + //dbConn, ok := c.MustGet("database").(driver.Database) + if !ok { + return nil, &models.ErrorMessage{StatusCode: h.StatusInternalServerError, Message: "Failed to get database"} + } + if *dbConn == nil { + return nil, &models.ErrorMessage{StatusCode: h.StatusNotFound, Message: "Failed to get database"} + } + return dbConn, nil +} + +func ParseToString(obj interface{}) (string, *models.ErrorMessage) { + + asJson, err := json.Marshal(obj) + if err != nil { + return "", &models.ErrorMessage{StatusCode: h.StatusInternalServerError, Message: "Failed to parse query string"} + } + return string(asJson), nil + +} + +func ExecQuerry(db driver.Database, query string) ([]interface{}, *models.ErrorMessage) { + + var result []interface{} + cursor, err := db.Query(nil, query, nil) + + if err != nil { + return result, &models.ErrorMessage{StatusCode: h.StatusInternalServerError, Message: err.Error()} + } + + defer cursor.Close() + + for { + var doc interface{} + _, err = cursor.ReadDocument(nil, &doc) + if driver.IsNoMoreDocuments(err) { + break + } else if err != nil { + return result, &models.ErrorMessage{StatusCode: h.StatusInternalServerError, Message: err.Error()} + } else { + result = append(result, doc) + } + } + return result, nil + +} + +func GetAll(c *gin.Context, col string) ([]interface{}, *models.ErrorMessage) { + db, err := GetDBConn(c) + if err != nil { + return nil, err + } + values := c.Request.URL.Query() + + querystring := "FOR doc IN " + col + + for key, value := range values { + querystring += " FILTER doc." + key + " LIKE \"" + value[0] + "\" " + } + querystring += " RETURN doc" + result, err := ExecQuerry(*db, querystring) + if err != nil { + return nil, err + } + return result, nil +} + +func Delete(c *gin.Context, key, col string) ([]interface{}, *models.ErrorMessage) { + + db, err := GetDBConn(c) + if err != nil { + return nil, err + } + + querystring := "FOR doc IN " + col + " FILTER doc.`_key`== \"" + key + "\" REMOVE doc IN " + col + + result, err := ExecQuerry(*db, querystring) + if err != nil { + return nil, err + } + return result, nil + +} + +func Update(c *gin.Context, doc interface{}, key, col string) ([]interface{}, *models.ErrorMessage) { + db, err := GetDBConn(c) + if err != nil { + return nil, err + } + + docStr, err := ParseToString(doc) + if err != nil { + return nil, err + } + querystring := "UPDATE \"" + key + "\" WITH " + docStr + " IN " + col + " RETURN " + key + + result, err := ExecQuerry(*db, querystring) + if err != nil { + return nil, err + } + return result, nil +} + +func ConnectToArango(addr, database, user, password string) (driver.Database, *models.ErrorMessage) { + + conn, err := http.NewConnection(http.ConnectionConfig{ + Endpoints: []string{addr}, + }) + if err != nil { + return nil, &models.ErrorMessage{StatusCode: h.StatusBadRequest, Message: err.Error()} + } + client, err := driver.NewClient(driver.ClientConfig{ + Connection: conn, + Authentication: driver.BasicAuthentication(user, password), + }) + if err != nil { + return nil, &models.ErrorMessage{StatusCode: h.StatusBadRequest, Message: err.Error()} + } + db, err := client.Database(nil, database) + if err != nil { + return nil, &models.ErrorMessage{StatusCode: h.StatusBadRequest, Message: err.Error()} + } + return db, nil + +} + +func CreateCollection(db driver.Database, collectionName string) (driver.Collection, error) { + var col driver.Collection + coll_exists, err := db.CollectionExists(nil, collectionName) + + if !coll_exists { + col, err = db.CreateCollection(nil, collectionName, nil) + + if err != nil { + return nil, err + } + } + + return col, nil + +} diff --git a/ARANGO_API/database/connection.go b/ARANGO_API/database/connection.go new file mode 100644 index 000000000..9a658cf04 --- /dev/null +++ b/ARANGO_API/database/connection.go @@ -0,0 +1,46 @@ +package database + +import ( + // driver "github.com/arangodb/go-driver" + "arango-api/models" + "net/http" + + "github.com/gin-gonic/gin" +) + +func InsertConnection(c *gin.Context, conn map[string]string) ([]interface{}, *models.ErrorMessage) { + db, err := GetDBConn(c) + if err != nil { + return nil, err + } + + // check if devices existed + existed, err := DeviceExistedById(*db, conn["_from"]) + if err != nil { + return nil, err + } + if !existed { + return nil, &models.ErrorMessage{StatusCode: http.StatusNotFound, Message: "Device " + conn["_from"] + " not found"} + } + existed, err = DeviceExistedById(*db, conn["_to"]) + if err != nil { + return nil, err + } + if !existed { + return nil, &models.ErrorMessage{StatusCode: http.StatusNotFound, Message: "Device " + conn["_to"] + " not found"} + } + connStr, err := ParseToString(conn) + + if err != nil { + return nil, err + } + querystring := "INSERT " + connStr + " INTO links RETURN NEW" + + result, err := ExecQuerry(*db, querystring) + if err != nil { + return nil, err + } + return result, nil +} + + diff --git a/ARANGO_API/database/devices.go b/ARANGO_API/database/devices.go new file mode 100644 index 000000000..b9d2f6362 --- /dev/null +++ b/ARANGO_API/database/devices.go @@ -0,0 +1,96 @@ +package database + +import ( + "arango-api/models" + "net/http" + + driver "github.com/arangodb/go-driver" + "github.com/gin-gonic/gin" + +) + +func DeviceExistedById(db driver.Database, id string) (bool, *models.ErrorMessage) { + + querystring := "FOR devices IN devices " + querystring += "FILTER devices.`_id` == \"" + id + "\" " + querystring += "RETURN devices" + + created, err := ExecQuerry(db, querystring) + if err != nil { + return false, err + } + if len(created) != 0 { + return true, nil + } + return false, nil +} +func DeviceExisted(db driver.Database, device map[string]string) (bool, *models.ErrorMessage) { + + querystring := "FOR devices IN devices " + querystring += "FILTER devices.`_name` == \"" + device["_name"] + "\" " + querystring += "&& devices.`group_name` == \"" + device["group_name"] + "\" " + querystring += "&& devices.`created` == \"" + device["created"] + "\" " + querystring += "RETURN devices" + + created, err := ExecQuerry(db, querystring) + if err != nil { + return false, err + } + if len(created) != 0 { + return true, nil + } + return false, nil +} + +func InsertDevices(c *gin.Context, device map[string]string) ([]interface{}, *models.ErrorMessage) { + db, err := GetDBConn(c) + if err != nil { + return nil, err + } + + // check if devices existed + existed, err := DeviceExisted(*db, device) + if err != nil { + return nil, err + } + if existed { + return nil, &models.ErrorMessage{StatusCode: http.StatusBadRequest, Message: "Device already existed"} + } + deviceStr, err := ParseToString(device) + + if err != nil { + return nil, err + } + querystring := "INSERT " + deviceStr + " INTO devices RETURN NEW" + + result, err := ExecQuerry(*db, querystring) + if err != nil { + return nil, err + } + return result, nil +} + + +func GetDevicesConnectedTo(c *gin.Context, keyDevice string)([]interface{}, *models.ErrorMessage){ + db, err := GetDBConn(c) + if err != nil { + return nil, err + } + values := c.Request.URL.Query() + + querystring := "FOR doc IN links" + querystring += " FILTER doc._from == \"devices/"+keyDevice+"\"" + querystring += " FOR device IN devices" + querystring += " FILTER device._id == doc._to" + + for key, value := range values { + querystring += " FILTER device." + key + " LIKE \"" + value[0] + "\" " + } + querystring += " RETURN device" + + result, err := ExecQuerry(*db, querystring) + if err != nil { + return nil, err + } + return result, nil +} diff --git a/ARANGO_API/docker-compose.yaml b/ARANGO_API/docker-compose.yaml new file mode 100644 index 000000000..e0e8f1345 --- /dev/null +++ b/ARANGO_API/docker-compose.yaml @@ -0,0 +1,39 @@ +version: '3.9' +services: + arango_db: + image: arangodb/arangodb:3.11.2 + container_name: arango_db + environment: + - ARANGO_ROOT_PASSWORD=password + volumes: + - arangodb-persist:/var/lib/arangodb3 + ports: + - 8529:8529 + restart: on-failure:10 + + + arrango_api: + build: + dockerfile: Dockerfile + image: arango-api:latest + restart: always + container_name: arango_api + environment: + - ENV=production + - ARRANGO_URL=http://arango_db:8529 + - ARRANGO_DATABASE=_system + - ARRANGO_USER=root + - ARRANGO_PASSWORD=password + ports: + - 8080:8080 + healthcheck: + test: ["CMD", "curl","-f","http://arango_db:5829/_api/version"] + timeout: 30s + interval: 1m + retries: 3 + +volumes: + arangodb-persist: + external: true + + \ No newline at end of file diff --git a/ARANGO_API/dockerfile b/ARANGO_API/dockerfile new file mode 100644 index 000000000..dfada514d --- /dev/null +++ b/ARANGO_API/dockerfile @@ -0,0 +1,29 @@ +FROM golang:1.18.2-alpine3.16 AS build + +# Set the Current Working Directory inside the container +WORKDIR /build + +# We want to populate the module cache based on the go.{mod,sum} files. +COPY go.mod . +COPY go.sum . + +RUN go mod download + +COPY . . + + +# Build the Go app +RUN CGO_ENABLED=0 GOOS=linux go build -a -o arango-api -ldflags "-X main.version=${VERSION} -X 'main.build=$(date)'" . + +# Start fresh from a smaller image +FROM alpine:3.16 + +WORKDIR /bin + +COPY --from=build /build/arango-api . +COPY --from=build /build/swagger.json . + + +# Run the binary program produced by `go install` +CMD ["./arango-api"] + diff --git a/ARANGO_API/go.mod b/ARANGO_API/go.mod new file mode 100644 index 000000000..3d6e01852 --- /dev/null +++ b/ARANGO_API/go.mod @@ -0,0 +1,53 @@ +module arango-api + +go 1.20 + +require ( + github.com/arangodb/go-driver v1.6.0 // indirect + github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // 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-openapi/analysis v0.21.4 // indirect + github.com/go-openapi/errors v0.20.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/loads v0.21.2 // indirect + github.com/go-openapi/runtime v0.26.0 // indirect + github.com/go-openapi/spec v0.20.8 // indirect + github.com/go-openapi/strfmt v0.21.7 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/validate v0.22.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.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // 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/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + go.mongodb.org/mongo-driver v1.11.3 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/ARANGO_API/go.sum b/ARANGO_API/go.sum new file mode 100644 index 000000000..aa2663bf4 --- /dev/null +++ b/ARANGO_API/go.sum @@ -0,0 +1,259 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/arangodb/go-driver v1.6.0 h1:NFWj/idqXZxhFVueihMSI2R9NotNIsgvNfM/xmpekb4= +github.com/arangodb/go-driver v1.6.0/go.mod h1:HQmdGkvNMVBTE3SIPSQ8T/ZddC6iwNsfMR+dDJQxIsI= +github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e h1:Xg+hGrY2LcQBbxd0ZFdbGSyRKTYMZCfBbw/pMJFOk1g= +github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e/go.mod h1:mq7Shfa/CaixoDxiyAAc5jZ6CVBAyPaNQCGS7mkj4Ho= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +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-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= +github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= +github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= +github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= +github.com/go-openapi/runtime v0.26.0 h1:HYOFtG00FM1UvqrcxbEJg/SwvDRvYLQKGhw2zaQjTcc= +github.com/go-openapi/runtime v0.26.0/go.mod h1:QgRGeZwrUcSHdeh4Ka9Glvo0ug1LC5WyE+EV88plZrQ= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= +github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= +github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= +github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= +github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= +github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +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.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +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/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +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 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +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/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +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/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +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= +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.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +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= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= +go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y= +go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/ARANGO_API/handlers/connection.go b/ARANGO_API/handlers/connection.go new file mode 100644 index 000000000..d6195d059 --- /dev/null +++ b/ARANGO_API/handlers/connection.go @@ -0,0 +1,128 @@ +package handlers + +import ( + "net/http" + + "arango-api/database" + + "github.com/gin-gonic/gin" +) + +// swagger:operation GET /Connections Connections Connection +// Get Connection list +// +// --- +// parameters: +// - name: _key +// in: query +// description: Key of connection +// required: false +// type: string +// - name: _from +// in: query +// description: From witch device +// required: false +// type: string +// - name: _to +// in: query +// description: To witch device +// required: false +// type: string +// security: +// - Bearer: [] +// responses: +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessConResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func GetConnection(c *gin.Context) { + + conn, err := database.GetAll(c, "links") + if err != nil { + c.IndentedJSON(err.StatusCode, gin.H{"message": err.Message}) + return + } + if len(conn) == 0 { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "no connection found"}) + return + } + c.IndentedJSON(http.StatusOK, conn) +} + +// swagger:operation POST /Connections Connections CreateConnection +// Create new Connection +// +// --- +// security: +// - Bearer: [] +// responses: +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessConResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func PostConnection(c *gin.Context) { + + var newConn map[string]string + + // Call BindJSON to bind the received JSON to + if err := c.BindJSON(&newConn); err != nil { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + + result, err := database.InsertConnection(c, newConn) + if err != nil { + c.IndentedJSON(err.StatusCode, gin.H{"message": err.Message}) + return + } + c.IndentedJSON(http.StatusCreated, result) +} + +// swagger:operation DELETE /Connections/{connection} Connections DeleteConnection +// Delete Connection by key +// +// --- +// security: +// - Bearer: [] +// parameters: +// - name: connection +// in: path +// description: connection looking for +// required: true +// type: string +// +// responses: +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func DeleteConnection(c *gin.Context) { + key := c.Param("key") + + conn, err := database.Delete(c, key, "links") + if err != nil { + c.IndentedJSON(err.StatusCode, gin.H{"message": err.Message}) + return + } + c.IndentedJSON(http.StatusOK, conn) +} + + diff --git a/ARANGO_API/handlers/devices.go b/ARANGO_API/handlers/devices.go new file mode 100644 index 000000000..59b9f37f8 --- /dev/null +++ b/ARANGO_API/handlers/devices.go @@ -0,0 +1,203 @@ +package handlers + +import ( + "net/http" + + "arango-api/database" + + "github.com/gin-gonic/gin" +) + +// swagger:operation GET /devices Devices Devices +// Get Devices list +// +// --- +// parameters: +// - name: _key +// in: query +// description: Key of device +// required: false +// type: string +// - name: _name +// in: query +// description: Name of device +// required: false +// type: string +// - name: group_name +// in: query +// description: Group_name of device +// required: false +// type: string +// - name: serial +// in: query +// description: Serial number of device +// required: false +// type: string +// security: +// - Bearer: [] +// responses: +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" + +func GetDevices(c *gin.Context) { + + devices, err := database.GetAll(c, "devices") + if err != nil { + c.IndentedJSON(err.StatusCode, gin.H{"message": err.Message}) + return + } + if len(devices) == 0 { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "Devices not found"}) + return + } + c.IndentedJSON(http.StatusOK, devices) +} + +// swagger:operation GET /devices/ConnecteTo/{device} Devices GetDevicesConnectedTo +// Get Devices connected to a device +// +// --- +// parameters: +// - name: device +// in: path +// description: Key of device +// required: true +// type: string +// - name: _key +// in: query +// description: Filter devices by key +// required: false +// type: string +// - name: _name +// in: query +// description: Name of device +// required: false +// type: string +// - name: group_name +// in: query +// description: Group_name of device +// required: false +// type: string +// - name: serial +// in: query +// description: Serial number of device +// required: false +// type: string +// security: +// - Bearer: [] +// responses: +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" + +func GetDevicesConnectedTo(c *gin.Context) { + + key := c.Param("key") + devices, err := database.GetDevicesConnectedTo(c, key) + if err != nil { + c.IndentedJSON(err.StatusCode, gin.H{"message": err.Message}) + return + } + if len(devices) == 0 { + c.IndentedJSON(http.StatusNotFound, gin.H{"message": "Devices not found"}) + return + } + c.IndentedJSON(http.StatusOK, devices) +} + +// swagger:operation POST /devices Devices CreateDevices +// Create new Devices +// +// --- +// security: +// - Bearer: [] +// responses: +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func PostDevices(c *gin.Context) { + + var newDevices map[string]string + + // Call BindJSON to bind the received JSON to + if err := c.BindJSON(&newDevices); err != nil { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + + //Checking minimal configuration + if newDevices["_name"] == "" { + c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Device needs Name"}) + return + } + if newDevices["created"] == "" { + c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Device needs created date"}) + return + } + if newDevices["group_name"] == "" { + c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Device needs roup Name"}) + return + } + result, err := database.InsertDevices(c, newDevices) + if err != nil { + c.IndentedJSON(err.StatusCode, gin.H{"message": err.Message}) + return + } + c.IndentedJSON(http.StatusCreated, result) +} + +// swagger:operation DELETE /devices/{device} Devices DeleteDevices +// Delete Devices by key +// +// --- +// parameters: +// - name: device +// in: path +// description: device looking for +// required: true +// type: string +// security: +// - Bearer: [] +// responses: +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func DeleteDevice(c *gin.Context) { + key := c.Param("key") + + devices, err := database.Delete(c, key, "devices") + if err != nil { + c.IndentedJSON(err.StatusCode, gin.H{"message": err.Message}) + return + } + c.IndentedJSON(http.StatusOK, devices) +} diff --git a/ARANGO_API/handlers/swagger.go b/ARANGO_API/handlers/swagger.go new file mode 100644 index 000000000..a146b12eb --- /dev/null +++ b/ARANGO_API/handlers/swagger.go @@ -0,0 +1,19 @@ +package handlers + +import( + "net/http" + "github.com/go-openapi/runtime/middleware" + "github.com/gorilla/mux" +) + +func SwaggerHandler() *mux.Router{ + pr := mux.NewRouter() + + pr.Handle("/swagger.json", http.FileServer(http.Dir("./"))) + opts := middleware.SwaggerUIOpts{SpecURL: "swagger.json"} + sh := middleware.SwaggerUI(opts, nil) + pr.Handle("/docs", sh) + + return pr + +} \ No newline at end of file diff --git a/ARANGO_API/main.go b/ARANGO_API/main.go new file mode 100644 index 000000000..51670fceb --- /dev/null +++ b/ARANGO_API/main.go @@ -0,0 +1,55 @@ +// Arrango API: +// version: 1.0.0 +// title: Awsome API +// Schemes: http, https +// Host: +// BasePath: /api +// Consumes: +// - application/json +// Produces: +// - application/json +// SecurityDefinitions: +// Bearer: +// type: apiKey +// name: Authorization +// in: header +// swagger:meta +package main + +import ( + "arango-api/database" + "arango-api/services" + "fmt" + "os" + e "github.com/joho/godotenv" +) + +func main() { + + env := os.Getenv("ENV") + if env != "production" { + fmt.Println("Loading environment variables from .env") + err := e.Load() + if err != nil { + fmt.Println("Some error occured. Err: ", err) + return + } + } + + addr := os.Getenv("ARANGO_URL") + bdd := os.Getenv("ARANGO_DATABASE") + user := os.Getenv("ARANGO_USER") + password := os.Getenv("ARANGO_PASSWORD") + port := os.Getenv("API_PORT") + + db, err2 := database.ConnectToArango(addr,bdd, user, password) + if err2 != nil { + fmt.Println("Error connecting to database: ", err2.Message) + return + } + + database.CreateCollection(db, "devices") + + router := services.InitRouter(db,addr) + router.Run(":"+port) +} diff --git a/ARANGO_API/models/connections.go b/ARANGO_API/models/connections.go new file mode 100644 index 000000000..8c33751a6 --- /dev/null +++ b/ARANGO_API/models/connections.go @@ -0,0 +1,43 @@ +package models + +// swagger:model Connection +type Connection struct { + + // Primary key of device + // in: _key + // read only: true + Key string `json:"_key"` + + // from Device + // in: _from + // example: devices/* + From string `json:"_from"` + + // To device + // in: _to + // example: devices/* + To string `json:"_to"` + + // Type of connection + // in: type + // example: parent of (between partens) + Type string `json:"type"` + + // Date of connection's creation + // in: created + // example: 2016-04-22 + Created string `json:"created"` + + // Date of connection's expiration + // in: expired + // example: 3000-01-01 + Expired string `json:"expired"` + +} + +// swagger:model SuccessConResponse +type SuccessConResponse struct { + // Success + // in : array + Connections []Connection +} \ No newline at end of file diff --git a/ARANGO_API/models/devices.go b/ARANGO_API/models/devices.go new file mode 100644 index 000000000..568549d6e --- /dev/null +++ b/ARANGO_API/models/devices.go @@ -0,0 +1,61 @@ +package models + +// swagger:model Devices +type Devices struct { + + // Primary key of device + // in: _key + // read only: true + Key string `json:"_key"` + // name of Devices + // in: _name + // example: storage_bay + Name string `json:"_name"` + // group_name of Devices + // in: group_name + // example: GS00OPSAN06 + GroupName string `json:"group_name"` + // category of Devices + // in: category + // example: port + Category string `json:"category"` + // sp_name of Devices + // in: sp_name + // example: sp_b + SpName string `json:"sp_name"` + + // sp_port_id of Devices + // in: sp_port_id + // example: 0 + SpPortId string `json:"sp_port_id"` + + // hba_device_name of Devices + // in: hba_device_name + // example: nsa.* + HbaDeviceName string `json:"hba_device_name"` + + // storage_group_name of Devices + // in: storage_group_name + // example: storage + StorageGroupName string `json:"storage_group_name"` + + // Date of device's creation + // in: created + // example: 2016-04-22 + Created string `json:"created"` + + // Date of device's expiration + // in: expired + // example: 3000-01-01 + Expired string `json:"expired"` + +} + +// swagger:model SuccessResponse +type SuccessResponse struct { + // Success + // in : array + Devices []Devices +} + + diff --git a/ARANGO_API/models/http.go b/ARANGO_API/models/http.go new file mode 100644 index 000000000..94eb98c4c --- /dev/null +++ b/ARANGO_API/models/http.go @@ -0,0 +1,54 @@ +package models + +// swagger:model ErrorResponse +type ErrorResponse struct { + // Error Response Message + // in: message + Message string `json:"message"` +} + +// swagger:parameters CreateDevices +type ReqDevicesBody struct { + // in: body + Body Devices `json:"body"` +} + +// swagger:parameters CreateConnection +type ReqConnBody struct { + // in: body + Body Connection `json:"body"` +} + +type ErrorMessage struct { + + StatusCode int `json:"statuscode"` + Message string `json:"message"` +} +// swagger:model DatabaseInfo +type DatabaseInfo struct { + // Host url of database + // in: host + // example: http://localhost:8529 + Host string `json:"host"` + + // Database name + // in: database + // example: _system + Database string `json:"database"` + + // User of database + // in: user + // example: root + User string `json:"user"` + + // Password of the user + // in: password + // example: password + Password string `json:"password"` +} + +// swagger:parameters ConnectBDD +type ReqBDDBody struct { + // in: body + Body DatabaseInfo `json:"body"` +} \ No newline at end of file diff --git a/ARANGO_API/readme.md b/ARANGO_API/readme.md new file mode 100644 index 000000000..b087559ef --- /dev/null +++ b/ARANGO_API/readme.md @@ -0,0 +1,122 @@ +# ArangoDB Database API in GoLang + +This GoLang API enables management of "devices" and "connections" elements within an ArangoDB database. It provides endpoints to perform CRUD (Create, Read, Update, Delete) operations on these elements. + + +## Configuration + +Before you begin, ensure you have an ArangoDB database up and running. Set the following environment variables in a .env file or as part of your runtime environment: + +- `ARANGO_URL`: The URL of your ArangoDB instance. +- `ARANGO_DATABASE`: The name of the ArangoDB database you'll be using. +- `ARANGO_USER`: The username for authentication. +- `ARANGO_PASSWORD`: The password for authentication. +- `ÀPI_PORT`: The port where API listenning + + +For example, you can set these environment variables in a `.env` file in the root directory of your project: + +```env +ARANGO_URL=http://localhost:8529 +ARANGO_DATABASE=mydatabase +ARANGO_USER=user +ARANGO_PASSWORD=password +API_PORT="8081" +``` + +## Installation and Execution + +1. Clone this repository to your local machine. +2. Install any necessary dependencies if required. +3. Compile and run the API using the following command: + +```bash +go run main.go +``` +The server should start and be ready to accept requests. + +## Endpoints +### Devices + +- GET /devices : Retrieve the list of all devices. +- GET /devices/:id : Retrieve information about a specific device based on its ID. +- GET /devices/ConnectedTo/:id : Retrieve the list of all devices connected to a specific device +- POST /devices :Add a new device to the database. Data must be provided in the request body in JSON format. +- DELETE /devices/:id :Delete a specific device based on its ID. + + +Example JSON data for a new device: + +```json +{ + "_name": "storage_bay", + "category": "port", + "created": "2016-04-22", + "expired": "3000-01-01", + "group_name": "GS00OPSAN06", + "hba_device_name": "nsa.*", + "sp_name": "sp_b", + "sp_port_id": "0", + "storage_group_name": "storage" +} +``` + +### Connections + + +- GET /connections : Retrieve the list of all connections. +- POST /connections : Add a new connection to the database. Data must be provided in the request body in JSON format. +- DELETE /connections/:id : Delete a specific connection based on its ID. + + +Example JSON data for a new connection: + +```json + { + "_from": "devices/*", + "_to": "devices/*", + "created": "2016-04-22", + "expired": "3000-01-01", + "type": "parent of (between partens)" +} +``` + +### Responses +The API returns data in JSON format for all operations. Responses typically include a array containing the requested data or an error message in case of an issue. + +Example successful response: + +```json + +{ +[ +{ + "_name": "storage_bay", + "category": "port", + "created": "2016-04-22", + "expired": "3000-01-01", + "group_name": "GS00OPSAN06", + "hba_device_name": "nsa.*", + "sp_name": "sp_b", + "sp_port_id": "0", + "storage_group_name": "storage" +} +] +} +``` +Example error response: + +```json +{ + "message": "Error message" +} +``` + +## API Documentation +You can explore the API documentation using Swagger UI. After starting the server, navigate to /docs in your web browser to access the interactive documentation and explore the available endpoints, request and response schemas, and even test the API directly from the documentation. + +For example, if the API is running locally, you can access Swagger UI at: + +```bash +http://localhost:8080/docs +`````` \ No newline at end of file diff --git a/ARANGO_API/services/router.go b/ARANGO_API/services/router.go new file mode 100644 index 000000000..9b2ebcccc --- /dev/null +++ b/ARANGO_API/services/router.go @@ -0,0 +1,61 @@ +package services + +import ( + "arango-api/handlers" + "os" + "arango-api/utils/token" + "net/http" + driver "github.com/arangodb/go-driver" + "github.com/gin-gonic/gin" +) + +func DBMiddleware(db driver.Database, addr string) gin.HandlerFunc { + + return func(c *gin.Context) { + c.Set("database", &db) + c.Set("addr", &addr) + c.Next() + } +} + +func JwtAuthMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + err := token.TokenValid(c) + if err != nil { + c.IndentedJSON(http.StatusUnauthorized,gin.H{"message":"Unauthorized"}) + c.Abort() + return + } + c.Next() + } +} + +func InitRouter(db driver.Database, addr string) *gin.Engine { + env := os.Getenv("ENV") + if env == "production" { + gin.SetMode(gin.ReleaseMode) + } + + router := gin.Default() + + router.Use(DBMiddleware(db, addr)) + proteted := router.Group("/api") + //proteted.Use(JwtAuthMiddleware()) + proteted.GET("/:devices", handlers.GetDevices) + proteted.POST("/:devices", handlers.PostDevices) + proteted.DELETE(":devices/:key", handlers.DeleteDevice) + proteted.GET(":devices/ConnecteTo/:key", handlers.GetDevicesConnectedTo) + + proteted.GET("/Connections", handlers.GetConnection) + proteted.POST("/Connections", handlers.PostConnection) + proteted.DELETE("/Connections/:key", handlers.DeleteConnection) + + router.GET("/api/health",func(c *gin.Context){ + c.String(http.StatusAccepted,"") + }) + + swagger := handlers.SwaggerHandler() + router.Use(gin.WrapH(swagger)) + + return router +} diff --git a/ARANGO_API/swagger.json b/ARANGO_API/swagger.json new file mode 100644 index 000000000..a28b4a59d --- /dev/null +++ b/ARANGO_API/swagger.json @@ -0,0 +1,541 @@ +{ + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "swagger": "2.0", + "info": { + "title": "Arrango API:", + "version": "1.0.0" + }, + "basePath": "/api", + "paths": { + "/Connections": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Get Connection list", + "tags": [ + "Connections" + ], + "operationId": "Connection", + "parameters": [ + { + "type": "string", + "description": "Key of connection", + "name": "_key", + "in": "query" + }, + { + "type": "string", + "description": "From witch device", + "name": "_from", + "in": "query" + }, + { + "type": "string", + "description": "To witch device", + "name": "_to", + "in": "query" + } + ], + "responses": { + "200": { + "description": "successful", + "schema": { + "items": { + "$ref": "#/definitions/SuccessConResponse" + } + } + }, + "500": { + "description": "Error", + "schema": { + "items": { + "$ref": "#/definitions/ErrorResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Create new Connection", + "tags": [ + "Connections" + ], + "operationId": "CreateConnection", + "parameters": [ + { + "x-go-name": "Body", + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/Connection" + } + } + ], + "responses": { + "200": { + "description": "successful", + "schema": { + "items": { + "$ref": "#/definitions/SuccessConResponse" + } + } + }, + "500": { + "description": "Error", + "schema": { + "items": { + "$ref": "#/definitions/ErrorResponse" + } + } + } + } + } + }, + "/Connections/{connection}": { + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Delete Connection by key", + "tags": [ + "Connections" + ], + "operationId": "DeleteConnection", + "parameters": [ + { + "type": "string", + "description": "connection looking for", + "name": "connection", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "successful", + "schema": { + "items": { + "$ref": "#/definitions/SuccessResponse" + } + } + }, + "500": { + "description": "Error", + "schema": { + "items": { + "$ref": "#/definitions/ErrorResponse" + } + } + } + } + } + }, + "/devices": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Get Devices list", + "tags": [ + "Devices" + ], + "operationId": "Devices", + "parameters": [ + { + "type": "string", + "description": "Key of device", + "name": "_key", + "in": "query" + }, + { + "type": "string", + "description": "Name of device", + "name": "_name", + "in": "query" + }, + { + "type": "string", + "description": "Group_name of device", + "name": "group_name", + "in": "query" + }, + { + "type": "string", + "description": "Serial number of device", + "name": "serial", + "in": "query" + } + ], + "responses": { + "200": { + "description": "successful", + "schema": { + "items": { + "$ref": "#/definitions/SuccessResponse" + } + } + }, + "500": { + "description": "Error", + "schema": { + "items": { + "$ref": "#/definitions/ErrorResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Create new Devices", + "tags": [ + "Devices" + ], + "operationId": "CreateDevices", + "parameters": [ + { + "x-go-name": "Body", + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/Devices" + } + } + ], + "responses": { + "200": { + "description": "successful", + "schema": { + "items": { + "$ref": "#/definitions/SuccessResponse" + } + } + }, + "500": { + "description": "Error", + "schema": { + "items": { + "$ref": "#/definitions/ErrorResponse" + } + } + } + } + } + }, + "/devices/ConnecteTo/{device}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Get Devices connected to a device", + "tags": [ + "Devices" + ], + "operationId": "GetDevicesConnectedTo", + "parameters": [ + { + "type": "string", + "description": "Key of device", + "name": "device", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Filter devices by key", + "name": "_key", + "in": "query" + }, + { + "type": "string", + "description": "Name of device", + "name": "_name", + "in": "query" + }, + { + "type": "string", + "description": "Group_name of device", + "name": "group_name", + "in": "query" + }, + { + "type": "string", + "description": "Serial number of device", + "name": "serial", + "in": "query" + } + ], + "responses": { + "200": { + "description": "successful", + "schema": { + "items": { + "$ref": "#/definitions/SuccessResponse" + } + } + }, + "500": { + "description": "Error", + "schema": { + "items": { + "$ref": "#/definitions/ErrorResponse" + } + } + } + } + } + }, + "/devices/{device}": { + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Delete Devices by key", + "tags": [ + "Devices" + ], + "operationId": "DeleteDevices", + "parameters": [ + { + "type": "string", + "description": "device looking for", + "name": "device", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "successful", + "schema": { + "items": { + "$ref": "#/definitions/SuccessResponse" + } + } + }, + "500": { + "description": "Error", + "schema": { + "items": { + "$ref": "#/definitions/ErrorResponse" + } + } + } + } + } + } + }, + "definitions": { + "Connection": { + "type": "object", + "properties": { + "_from": { + "description": "from Device\nin: _from", + "type": "string", + "x-go-name": "From", + "example": "devices/*" + }, + "_key": { + "description": "Primary key of device\nin: _key", + "type": "string", + "x-go-name": "Key", + "readOnly": true + }, + "_to": { + "description": "To device\nin: _to", + "type": "string", + "x-go-name": "To", + "example": "devices/*" + }, + "created": { + "description": "Date of connection's creation\nin: created", + "type": "string", + "x-go-name": "Created", + "example": "2016-04-22" + }, + "expired": { + "description": "Date of connection's expiration\nin: expired", + "type": "string", + "x-go-name": "Expired", + "example": "3000-01-01" + }, + "type": { + "description": "Type of connection\nin: type", + "type": "string", + "x-go-name": "Type", + "example": "parent of (between partens)" + } + }, + "x-go-package": "arango-api/models" + }, + "DatabaseInfo": { + "type": "object", + "properties": { + "database": { + "description": "Database name\nin: database", + "type": "string", + "x-go-name": "Database", + "example": "_system" + }, + "host": { + "description": "Host url of database\nin: host", + "type": "string", + "x-go-name": "Host", + "example": "http://localhost:8529" + }, + "password": { + "description": "Password of the user\nin: password", + "type": "string", + "x-go-name": "Password", + "example": "password" + }, + "user": { + "description": "User of database\nin: user", + "type": "string", + "x-go-name": "User", + "example": "root" + } + }, + "x-go-package": "arango-api/models" + }, + "Devices": { + "type": "object", + "properties": { + "_key": { + "description": "Primary key of device\nin: _key", + "type": "string", + "x-go-name": "Key", + "readOnly": true + }, + "_name": { + "description": "name of Devices\nin: _name", + "type": "string", + "x-go-name": "Name", + "example": "storage_bay" + }, + "category": { + "description": "category of Devices\nin: category", + "type": "string", + "x-go-name": "Category", + "example": "port" + }, + "created": { + "description": "Date of device's creation\nin: created", + "type": "string", + "x-go-name": "Created", + "example": "2016-04-22" + }, + "expired": { + "description": "Date of device's expiration\nin: expired", + "type": "string", + "x-go-name": "Expired", + "example": "3000-01-01" + }, + "group_name": { + "description": "group_name of Devices\nin: group_name", + "type": "string", + "x-go-name": "GroupName", + "example": "GS00OPSAN06" + }, + "hba_device_name": { + "description": "hba_device_name of Devices\nin: hba_device_name", + "type": "string", + "x-go-name": "HbaDeviceName", + "example": "nsa.*" + }, + "sp_name": { + "description": "sp_name of Devices\nin: sp_name", + "type": "string", + "x-go-name": "SpName", + "example": "sp_b" + }, + "sp_port_id": { + "description": "sp_port_id of Devices\nin: sp_port_id", + "type": "string", + "x-go-name": "SpPortId", + "example": "0" + }, + "storage_group_name": { + "description": "storage_group_name of Devices\nin: storage_group_name", + "type": "string", + "x-go-name": "StorageGroupName", + "example": "storage" + } + }, + "x-go-package": "arango-api/models" + }, + "ErrorResponse": { + "type": "object", + "properties": { + "message": { + "description": "Error Response Message\nin: message", + "type": "string", + "x-go-name": "Message" + } + }, + "x-go-package": "arango-api/models" + }, + "SuccessConResponse": { + "type": "object", + "properties": { + "Connections": { + "description": "Success\nin : array", + "type": "array", + "items": { + "$ref": "#/definitions/Connection" + } + } + }, + "x-go-package": "arango-api/models" + }, + "SuccessResponse": { + "type": "object", + "properties": { + "Devices": { + "description": "Success\nin : array", + "type": "array", + "items": { + "$ref": "#/definitions/Devices" + } + } + }, + "x-go-package": "arango-api/models" + } + }, + "securityDefinitions": { + "Bearer": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + } +} \ No newline at end of file diff --git a/ARANGO_API/utils/token/token.go b/ARANGO_API/utils/token/token.go new file mode 100644 index 000000000..eff595103 --- /dev/null +++ b/ARANGO_API/utils/token/token.go @@ -0,0 +1,74 @@ +package token + +import ( + "fmt" + "os" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + jwt "github.com/dgrijalva/jwt-go" +) + +func GenerateToken(user_id uint) (string, error) { + + token_lifespan := 24 + claims := jwt.MapClaims{} + claims["authorized"] = true + claims["user_id"] = user_id + claims["exp"] = time.Now().Add(time.Hour * time.Duration(token_lifespan)).Unix() + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + return token.SignedString([]byte(os.Getenv("API_SECRET"))) + +} + +func TokenValid(c *gin.Context) error { + tokenString := ExtractToken(c) + _, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) + } + return []byte(os.Getenv("API_SECRET")), nil + }) + if err != nil { + return err + } + return nil +} + +func ExtractToken(c *gin.Context) string { + token := c.Query("token") + if token != "" { + return token + } + bearerToken := c.Request.Header.Get("Authorization") + if len(strings.Split(bearerToken, " ")) == 2 { + return strings.Split(bearerToken, " ")[1] + } + return "" +} + +func ExtractTokenID(c *gin.Context) (uint, error) { + + tokenString := ExtractToken(c) + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) + } + return []byte(os.Getenv("API_SECRET")), nil + }) + if err != nil { + return 0, err + } + claims, ok := token.Claims.(jwt.MapClaims) + if ok && token.Valid { + uid, err := strconv.ParseUint(fmt.Sprintf("%.0f", claims["user_id"]), 10, 32) + if err != nil { + return 0, err + } + return uint(uid), nil + } + return 0, nil +} \ No newline at end of file diff --git a/BFF/Dockerfile b/BFF/Dockerfile new file mode 100644 index 000000000..703510c1b --- /dev/null +++ b/BFF/Dockerfile @@ -0,0 +1,29 @@ +FROM golang:1.18.2-alpine3.16 AS build + +# Set the Current Working Directory inside the container +WORKDIR /build + +# We want to populate the module cache based on the go.{mod,sum} files. +COPY go.mod . +COPY go.sum . + +RUN go mod download + +COPY . . + + +# Build the Go app +RUN CGO_ENABLED=0 GOOS=linux go build -a -o ogree-bff -ldflags "-X main.version=${VERSION} -X 'main.build=$(date)'" . + +# Start fresh from a smaller image +FROM alpine:3.16 + +WORKDIR /bin + +COPY --from=build /build/ogree-bff . +COPY --from=build /build/api.json . +COPY --from=build /build/swagger.json . + + +# Run the binary program produced by `go install` +CMD ["./ogree-bff"] \ No newline at end of file diff --git a/BFF/api.json b/BFF/api.json new file mode 100644 index 000000000..89be85aaa --- /dev/null +++ b/BFF/api.json @@ -0,0 +1,5 @@ +[ + {"name":"server", "url": "http://localhost:8080"}, + {"name":"bay", "url": "http://localhost:8081"}, + {"name":"objects", "url": "http://localhost:3001"} +] \ No newline at end of file diff --git a/BFF/controllers/devices.go b/BFF/controllers/devices.go new file mode 100644 index 000000000..d91743e8b --- /dev/null +++ b/BFF/controllers/devices.go @@ -0,0 +1,128 @@ +package controllers + +import ( + "fmt" + "net/http" + "ogree-bff/utils/token" + + "github.com/gin-gonic/gin" +) + +func DeviceBindingObject(c *gin.Context) { + entity := c.Param("entity") + + deviceURL, ok := c.Value(entity).(string) + if !ok { + c.IndentedJSON(http.StatusBadRequest, gin.H{"message": entity + " has not database"}) + return + } + objAttr := c.Param("objAttr") + + obj := c.Param("obj") + + //MONGO Check + mongoURL, ok := c.Value("objects").(string) + if !ok { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Failed to get api connection"}) + return + } + key := token.ExtractToken(c) + mongoResp, err := Send("GET", mongoURL+"/api/objects/"+obj, "", key, nil) + if err != nil { + if mongoResp != nil { + result := GetJSONBody(mongoResp) + c.IndentedJSON(mongoResp.StatusCode, result.Message) + return + } + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + mongoBody := GetJSONBody(mongoResp) + if mongoBody.StatusCode != http.StatusOK { + c.IndentedJSON(mongoBody.StatusCode, mongoBody.Message) + return + } + mongoDataResult := mongoBody.Message.(map[string]interface{}) + mongoResult := mongoDataResult["data"].(map[string]interface{}) + if mongoResult["category"] != "device" { + c.IndentedJSON(http.StatusBadRequest, gin.H{"message": obj + " is not a device"}) + return + } + mongoAttr := mongoResult["attributes"].(map[string]interface{}) + if mongoAttr[objAttr] == nil && mongoResult[objAttr] == nil { + c.IndentedJSON(http.StatusBadRequest, gin.H{"message": obj + " has not attributes :" + objAttr}) + return + } + var attributeNeed string + if mongoResult[objAttr] != nil { + attributeNeed = mongoResult[objAttr].(string) + } else { + attributeNeed = mongoAttr[objAttr].(string) + } + + //ARANGO + deviceAttr := c.Param("deviceAttr") + + query := GetQueryString(c) + if query == "" { + query += "?" + deviceAttr + "=" + attributeNeed + } else { + query += "&" + deviceAttr + "=" + attributeNeed + } + + deviceResp, err := Send("GET", deviceURL+"/api/devices", query, key, nil) + if err != nil { + if deviceResp != nil { + result := GetJSONBody(deviceResp) + c.IndentedJSON(deviceResp.StatusCode, result.Message) + return + } + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + result := GetJSONBody(deviceResp) + c.IndentedJSON(deviceResp.StatusCode, result.Message) + return + +} + + +func GetDevice(c *gin.Context,url,methode string){ + + key := token.ExtractToken(c) + query := GetQueryString(c) + fmt.Println(c.Request.URL.Query(),query) + deviceResp, err := Send(methode, url, query, key, nil) + if err != nil { + if deviceResp != nil { + result := GetJSONBody(deviceResp) + c.IndentedJSON(deviceResp.StatusCode, result.Message) + return + } + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + result := GetJSONBody(deviceResp) + c.IndentedJSON(deviceResp.StatusCode, result.Message) + return +} + +func PostDevice(c *gin.Context,url, method string){ + + var data interface{} + // Call BindJSON to bind the received JSON to + if err := c.ShouldBindJSON(&data); err != nil { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + + key := token.ExtractToken(c) + resp,err := Send(method,url,"",key,data) + fmt.Println(err) + if err != nil && resp == nil{ + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + result := GetJSONBody(resp) + c.IndentedJSON(resp.StatusCode, result.Message) +} diff --git a/BFF/controllers/sender.go b/BFF/controllers/sender.go new file mode 100644 index 000000000..235cb1da9 --- /dev/null +++ b/BFF/controllers/sender.go @@ -0,0 +1,168 @@ +package controllers + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "ogree-bff/models" + "ogree-bff/utils/token" + "net/url" + "github.com/gin-gonic/gin" +) + +func GetQueryString(c *gin.Context) (string) { + query := "" + for key,value := range c.Request.URL.Query() { + if query == ""{ + query+="?"+key+"="+url.QueryEscape(value[0]) + }else{ + query+="&"+key+"="+url.QueryEscape(value[0]) + } + } + return query +} + +func GetPath(c *gin.Context) (string) { + return c.Request.URL.Path +} + +func Send(method, URL, query ,key string, data interface{}) (*http.Response,error) { + client := &http.Client{} + dataJSON, err := json.Marshal(data) + if err != nil { + return nil,err + } + req, err := http.NewRequest(method, URL+query, bytes.NewBuffer(dataJSON)) + if err != nil { + return nil,err + } + if(key != ""){ + req.Header.Set("Authorization", "Bearer "+key) + } + + return client.Do(req) +} + +func GetJSONBody(resp *http.Response) models.Message { + defer resp.Body.Close() + var responseBody interface{} + body, err := io.ReadAll(resp.Body) + + if err != nil { + return models.Message{StatusCode: http.StatusInternalServerError,Message: err.Error()} + } + err = json.Unmarshal(body, &responseBody) + if err != nil { + return models.Message{StatusCode: resp.StatusCode,Message: gin.H{"message":string(body)}} + } + return models.Message{StatusCode: resp.StatusCode,Message: responseBody} +} + +func Get(c *gin.Context, api string){ + url, ok := c.Value(api).(string) + if !ok { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Failed to get api connection"}) + return + } + key := token.ExtractToken(c) + query := GetQueryString(c) + path := GetPath(c) + resp,err := Send("GET",url+path,query,key,nil) + if err != nil && resp == nil{ + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + result := GetJSONBody(resp) + c.IndentedJSON(resp.StatusCode, result.Message) + return +} + +func Post(c *gin.Context, api string){ + url, ok := c.Value(api).(string) + if !ok { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Failed to get api connection"}) + return + } + var data interface{} + // Call BindJSON to bind the received JSON to + if err := c.ShouldBindJSON(&data); err != nil { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + + key := token.ExtractToken(c) + path := GetPath(c) + resp,err := Send("POST",url+path,"",key,data) + fmt.Println(err) + if err != nil && resp == nil{ + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + result := GetJSONBody(resp) + c.IndentedJSON(resp.StatusCode, result.Message) +} + +func Delete(c *gin.Context, api string){ + url, ok := c.Value(api).(string) + if !ok { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Failed to get api connection"}) + return + } + key := token.ExtractToken(c) + path := GetPath(c) + resp,err := Send("DELETE",url+path,"",key,nil) + if err != nil && resp == nil{ + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + result := GetJSONBody(resp) + c.IndentedJSON(resp.StatusCode, result.Message) +} + +func Patch(c *gin.Context, api string){ + url, ok := c.Value(api).(string) + if !ok { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Failed to get api connection"}) + return + } + var data interface{} + // Call BindJSON to bind the received JSON to + if err := c.ShouldBindJSON(&data); err != nil { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + key := token.ExtractToken(c) + path := GetPath(c) + resp,err := Send("PATCH",url+path,"",key,data) + if err != nil && resp == nil{ + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + result := GetJSONBody(resp) + c.IndentedJSON(resp.StatusCode, result.Message) +} + +func Put(c *gin.Context, api string){ + url, ok := c.Value(api).(string) + if !ok { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Failed to get api connection"}) + return + } + var data interface{} + // Call BindJSON to bind the received JSON to + if err := c.ShouldBindJSON(&data); err != nil { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + key := token.ExtractToken(c) + path := GetPath(c) + resp,err := Send("PUT",url+path,"",key,data) + if err != nil && resp == nil{ + c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + result := GetJSONBody(resp) + c.IndentedJSON(resp.StatusCode, result.Message) +} \ No newline at end of file diff --git a/BFF/go.mod b/BFF/go.mod new file mode 100644 index 000000000..134365fb4 --- /dev/null +++ b/BFF/go.mod @@ -0,0 +1,54 @@ +module ogree-bff + +go 1.20 + +require ( + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/gin-gonic/gin v1.9.1 + github.com/go-openapi/runtime v0.26.0 + github.com/gorilla/mux v1.8.0 + github.com/joho/godotenv v1.5.1 +) + +require ( + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/cors v1.4.0 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-openapi/analysis v0.21.4 // indirect + github.com/go-openapi/errors v0.20.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/loads v0.21.2 // indirect + github.com/go-openapi/spec v0.20.8 // indirect + github.com/go-openapi/strfmt v0.21.7 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/validate v0.22.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.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // 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/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + go.mongodb.org/mongo-driver v1.11.3 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/BFF/go.sum b/BFF/go.sum new file mode 100644 index 000000000..5fbef52c3 --- /dev/null +++ b/BFF/go.sum @@ -0,0 +1,278 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +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/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= +github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs= +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.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +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-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= +github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= +github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= +github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= +github.com/go-openapi/runtime v0.26.0 h1:HYOFtG00FM1UvqrcxbEJg/SwvDRvYLQKGhw2zaQjTcc= +github.com/go-openapi/runtime v0.26.0/go.mod h1:QgRGeZwrUcSHdeh4Ka9Glvo0ug1LC5WyE+EV88plZrQ= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= +github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= +github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= +github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= +github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= +github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +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.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +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.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +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/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +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 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +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/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +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/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +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/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/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= +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.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +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 v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +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/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= +go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y= +go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +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.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/BFF/handlers/about.go b/BFF/handlers/about.go new file mode 100644 index 000000000..5dd9a5a6d --- /dev/null +++ b/BFF/handlers/about.go @@ -0,0 +1,44 @@ +package handlers + +import ( + "ogree-bff/controllers" + + "github.com/gin-gonic/gin" +) + +// swagger:operation GET /stats About GetStats +// Displays DB statistics. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// responses: +// +// '200': +// description: 'Request is valid.' +// '504': +// description: Server error. +func GetStats(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation GET /version About GetAPIVersion +// Gets the API version. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// responses: +// +// '200': +// description: 'OK. A response body will be returned with +// version details.' +func GetAPIVersion(c *gin.Context) { + controllers.Get(c, "objects") +} diff --git a/BFF/handlers/auth.go b/BFF/handlers/auth.go new file mode 100644 index 000000000..c33786609 --- /dev/null +++ b/BFF/handlers/auth.go @@ -0,0 +1,142 @@ +package handlers + +import ( + "ogree-bff/controllers" + + "github.com/gin-gonic/gin" +) + +// swagger:operation POST /login Authentication Authenticate +// Generates a new JWT Key for the client. +// Create a new JWT Key. This can also be used to verify credentials +// The authorize and 'Try it out' buttons don't work +// --- +// produces: +// - application/json +// parameters: +// - name: body +// in: body +// description: 'Mandatory: email and password.' +// required: true +// format: object +// example: '{"email": "user@test.com", "password": "secret123"}' +// +// responses: +// '200': +// description: Authenticated +// '400': +// description: Bad request +// '500': +// description: Internal server error +func Login(c *gin.Context) { + controllers.Post(c, "objects") +} + +// swagger:operation GET /token/valid Authentication VerifyToken +// Verify if token sent in the header is valid. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// responses: +// '200': +// description: Token is valid. +// '403': +// description: Unauthorized +// '500': +// description: Internal server error +func ValidToken(c *gin.Context) { + controllers.Get(c, "objects") + +} + +// swagger:operation POST /users/password/change Authentication ModifyUserPassword +// For logged in user to change its own password. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: body +// in: body +// description: 'Mandatory: currentPassword and newPassword.' +// type: json +// required: true +// example: '{"currentPassword": "myOldPassword", "newPassword": "myNewPassword"}' +// +// responses: +// +// '200': +// description: Password changed +// '400': +// description: Bad request +// '500': +// description: Internal server error +func ModifyUserPassword(c *gin.Context) { + controllers.Post(c, "objects") +} + +// swagger:operation POST /users/password/reset Authentication ResetUserPassword +// Reset password after forgot. +// For user that first called forgot enpoint to change its password. +// A reset token generated by the forgot endpoint should be provided as the Authentication header. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: body +// in: body +// description: 'Mandatory: currentPassword and newPassword.' +// type: json +// required: true +// example: '{"newPassword": "myNewPassword"}' +// +// responses: +// +// '200': +// description: Password changed +// '400': +// description: Bad request +// '500': +// description: Internal server error +func ResetUserPassword(c *gin.Context) { + controllers.Post(c, "objects") +} + +// swagger:operation POST /users/password/forgot Authentication UserForgotPassword +// Forgot my password. +// Public endpoint to request a reset of a user's password (forgot my password). +// If the email is valid, an email with a reset token/link will be sent to the user. +// --- +// produces: +// - application/json +// +// parameters: +// - name: body +// in: body +// description: 'Mandatory: email.' +// type: string +// required: true +// example: '{"email": "user@test.com"}' +// +// responses: +// +// '200': +// description: request processed. If account exists, an email with a reset token is sent +// '400': +// description: Bad request +// '500': +// description: Internal server error +func UserForgotPassword(c *gin.Context) { + controllers.Post(c, "objects") +} diff --git a/BFF/handlers/devices.go b/BFF/handlers/devices.go new file mode 100644 index 000000000..46f895e91 --- /dev/null +++ b/BFF/handlers/devices.go @@ -0,0 +1,374 @@ +package handlers + +import ( + "net/http" + "ogree-bff/controllers" + + "github.com/gin-gonic/gin" +) + +// swagger:operation GET /deviceComps/{entity} Devices Devices +// Get Devices list +// +// --- +// parameters: +// - name: _key +// in: query +// description: Key of device +// required: false +// type: string +// - name: entity +// in: path +// description: database to retrieve devices +// required: true +// type: string +// - name: _name +// in: query +// description: Name of device +// required: false +// type: string +// - name: group_name +// in: query +// description: Group_name of device +// required: false +// type: string +// - name: serial +// in: query +// description: Serial number of device +// required: false +// type: string +// security: +// - Bearer: [] +// responses: +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" + +func GetDevices(c *gin.Context) { + entity := c.Param("entity") + + deviceURL, ok := c.Value(entity).(string) + if !ok { + c.IndentedJSON(http.StatusBadRequest, gin.H{"message": entity + " has not database"}) + return + } + controllers.GetDevice(c, deviceURL+"/api/devices", "GET") +} + +// swagger:operation GET /deviceComp/{entity}/ConnecteTo/{device} Devices GetDevicesConnectedTo +// Get Devices connected to a device +// +// --- +// parameters: +// - name: entity +// in: path +// description: database to retrieve devices +// required: true +// type: string +// - name: device +// in: path +// description: Key of device +// required: true +// type: string +// - name: _key +// in: query +// description: Filter devices by key +// required: false +// type: string +// - name: _name +// in: query +// description: Name of device +// required: false +// type: string +// - name: group_name +// in: query +// description: Group_name of device +// required: false +// type: string +// - name: serial +// in: query +// description: Serial number of device +// required: false +// type: string +// +// security: +// - Bearer: [] +// +// responses: +// +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func GetDevicesConnectedTo(c *gin.Context) { + + entity := c.Param("entity") + id := c.Param("id") + deviceURL, ok := c.Value(entity).(string) + if !ok { + c.IndentedJSON(http.StatusBadRequest, gin.H{"message": entity + " has not database"}) + return + } + controllers.GetDevice(c, deviceURL+"/api/deviceComp/ConnecteTo/"+id, "GET") +} + +// swagger:operation POST /deviceComp/{entity} Devices CreateDevices +// Create new Devices +// +// --- +// security: +// - Bearer: [] +// +// parameters: +// - name: entity +// in: path +// description: database to retrieve devices +// required: true +// type: string +// - name: body +// in: body +// description: 'Mandatory: _name, group_name,created.' +// required: true +// format: object +// example: '{"_name": "server", "group_name": "exwipen22","created": "2022-07-18"}' +// +// responses: +// +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func CreateDevices(c *gin.Context) { + entity := c.Param("entity") + + deviceURL, ok := c.Value(entity).(string) + if !ok { + c.IndentedJSON(http.StatusBadRequest, gin.H{"message": entity + " has not database"}) + return + } + controllers.PostDevice(c, deviceURL+"/api/devices", "POST") +} + +// swagger:operation DELETE /deviceComp/{entity}{device} Devices DeleteDevices +// Delete Devices by key +// +// --- +// parameters: +// - name: entity +// in: path +// description: database to retrieve devices +// required: true +// type: string +// - name: device +// in: path +// description: device looking for +// required: true +// type: string +// +// security: +// - Bearer: [] +// +// responses: +// +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func DeleteDevice(c *gin.Context) { + entity := c.Param("entity") + id := c.Param("id") + deviceURL, ok := c.Value(entity).(string) + if !ok { + c.IndentedJSON(http.StatusBadRequest, gin.H{"message": entity + " has not database"}) + return + } + controllers.GetDevice(c, deviceURL+"/api/deviceComp/"+id, "DELETE") +} + +// swagger:operation GET /Connections Devices GetConnections +// Get Connection list +// +// --- +// parameters: +// - name: _key +// in: query +// description: Key of connection +// required: false +// type: string +// - name: _from +// in: query +// description: From witch device +// required: false +// type: string +// - name: _to +// in: query +// description: To witch device +// required: false +// type: string +// +// security: +// - Bearer: [] +// +// responses: +// +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func GetConnections(c *gin.Context) { + controllers.Get(c, "devices") +} + +// swagger:operation POST /Connections Devices CreateConnection +// Create new Connection +// +// --- +// security: +// - Bearer: [] +// +// parameters: +// - name: body +// in: body +// description: 'Mandatory: _from, _to.' +// required: true +// format: object +// example: '{"_from": "devices/123", "_to": "devices/111"}' +// +// responses: +// +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func CreateConnections(c *gin.Context) { + controllers.Post(c, "devices") +} + +// swagger:operation DELETE /Connections/{connection} Devices DeleteConnection +// Delete Connection by key +// +// --- +// security: +// - Bearer: [] +// +// parameters: +// - name: connection +// in: path +// description: connection looking for +// required: true +// type: string +// +// responses: +// +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func DeleteConnections(c *gin.Context) { + controllers.Delete(c, "devices") +} + +// swagger:operation GET /deviceComp/{entity}/{obj}/{objAttr}/{deviceAttr} Devices GetDeviceBindingObject +// Get Devices list +// +// --- +// parameters: +// - name: obj +// in: path +// description: object for binding +// required: true +// type: string +// - name: objAttr +// in: path +// description: object attribute for binding +// required: true +// type: string +// - name: deviceAttr +// in: path +// description: devices attribute for binding +// required: true +// type: string +// - name: _key +// in: query +// description: Key of device +// required: false +// type: string +// - name: _name +// in: query +// description: Name of device +// required: false +// type: string +// - name: group_name +// in: query +// description: Group_name of device +// required: false +// type: string +// - name: serial +// in: query +// description: Serial number of device +// required: false +// type: string +// +// security: +// - Bearer: [] +// +// responses: +// +// '200': +// description: successful +// schema: +// items: +// "$ref": "#/definitions/SuccessResponse" +// '500': +// description: Error +// schema: +// items: +// "$ref": "#/definitions/ErrorResponse" +func GetDeviceBindingObject(c *gin.Context) { + + controllers.DeviceBindingObject(c) + +} diff --git a/BFF/handlers/flutterApp.go b/BFF/handlers/flutterApp.go new file mode 100644 index 000000000..17793aa73 --- /dev/null +++ b/BFF/handlers/flutterApp.go @@ -0,0 +1,135 @@ +package handlers + +import ( + "ogree-bff/controllers" + + "github.com/gin-gonic/gin" +) + +// swagger:operation GET /projects FlutterApp GetProjects +// Get a list of projects for the specified user. +// --- +// security: +// - Bearer: [] +// produces: +// - application/json +// parameters: +// - name: user +// in: query +// description: 'Email of the user whose projects are being requested. +// Example: /api/projects?user=user@test.com' +// required: false +// type: string +// default: user@test.com +// responses: +// '200': +// description: 'Return all possible projects.' +// '400': +// description: 'Bad Request. Invalid user query param.' +// '500': +// description: 'Internal server error.' + +func GetProjects(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation POST /projects FlutterApp CreateProjects +// Create a new project +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: body +// in: body +// description: 'Mandatory: name, dateRange, namespace, attributes, +// objects, permissions, authorLastUpdate, lastUpdate. +// Optional: showAvg, showSum, isPublic.' +// required: true +// format: object +// example: '{"attributes":["domain"],"authorLastUpdate":"helder","dateRange":"01/01/2023-02/02/2023", +// "lastUpdate":"02/02/2023","name":"test 1","namespace":"physical","objects":["siteB"],"showAvg":false, +// "showSum":false,"permissions":["user@test.com","admin"]}' +// +// responses: +// +// '200': +// description: 'Project successfully created.' +// '400': +// description: 'Bad Request. Invalid project format.' +// '500': +// description: 'Internal server error.' +func CreateProjects(c *gin.Context) { + controllers.Post(c, "objects") +} + +// swagger:operation PUT /projects/{ProjectID} FlutterApp UpdateProjects +// Replace the data of an existing project. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: ProjectID +// in: path +// description: 'ID of the project to update.' +// required: true +// type: string +// default: "1234" +// - name: body +// in: body +// description: 'Mandatory: name, dateRange, namespace, attributes, +// objects, permissions, authorLastUpdate, lastUpdate. +// Optional: showAvg, showSum, isPublic.' +// required: true +// format: object +// example: '{"attributes":["domain"],"authorLastUpdate":"helder","dateRange":"01/01/2023-02/02/2023", +// "lastUpdate":"02/02/2023","name":"test 1","namespace":"physical","objects":["siteB"],"showAvg":false, +// "showSum":false,"permissions":["user@test.com","admin"]}' +// +// responses: +// +// '200': +// description: Project successfully updated. +// '400': +// description: Bad Request. Invalid project format. +// '500': +// description: Internal server error +func UpdateProjects(c *gin.Context) { + controllers.Put(c, "objects") +} + +// swagger:operation DELETE /projects/{ProjectID} FlutterApp DeleteProjects +// Delete an existing project. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: ProjectID +// in: path +// description: 'ID of the project to delete.' +// required: true +// type: string +// default: "1234" +// +// responses: +// +// '200': +// description: Project successfully updated. +// '404': +// description: Not Found. Invalid project ID. +// '500': +// description: Internal server error +func DeleteProjects(c *gin.Context) { + controllers.Delete(c, "objects") +} diff --git a/BFF/handlers/object.go b/BFF/handlers/object.go new file mode 100644 index 000000000..08fb627c4 --- /dev/null +++ b/BFF/handlers/object.go @@ -0,0 +1,633 @@ +package handlers + +import ( + "ogree-bff/controllers" + + "github.com/gin-gonic/gin" +) + +// swagger:operation POST /{entity} Objects CreateObject +// Creates an object of the given entity in the system. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: entity +// in: path +// description: 'Entity (same as category) of the object. Accepted values: sites, domains, +// buildings, rooms, racks, devices, acs, panels, +// cabinets, groups, corridors, +// room-templates, obj-templates, bldg-templates, stray-devices.' +// required: true +// type: string +// default: "sites" +// - name: body +// in: body +// required: true +// default: {} +// +// responses: +// +// '201': +// description: 'Created. A response body will be returned with +// a meaningful message.' +// '400': +// description: 'Bad request. A response body with an error +// message will be returned.' +func CreateObject(c *gin.Context) { + controllers.Post(c, "objects") +} + +// swagger:operation GET /objects/{hierarchyName} Objects GetGenericObject +// Get an object from any entity. +// Gets an object from any of the physical entities with no need to specify it. +// The hierarchyName must be provided in the URL as a parameter. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: hierarchyName +// in: path +// description: hierarchyName of the object +// required: true +// - name: fieldOnly +// in: query +// description: 'specify which object field to show in response. +// Multiple fieldOnly can be added. An invalid field is simply ignored.' +// - name: startDate +// in: query +// description: 'filter objects by lastUpdated >= startDate. +// Format: yyyy-mm-dd' +// - name: endDate +// in: query +// description: 'filter objects by lastUpdated <= endDate. +// Format: yyyy-mm-dd' +// +// responses: +// +// '200': +// description: 'Found. A response body will be returned with +// a meaningful message.' +// '404': +// description: Not Found. An error message will be returned. +func GetGenericObject(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation GET /{entity}/{IdOrHierarchyName} Objects GetEntity +// Gets an Object of the given entity. +// The ID or hierarchy name must be provided in the URL parameter. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: entity +// in: path +// description: 'Entity (same as category) of the object. Accepted values: sites, domains, +// buildings, rooms, racks, devices, acs, panels, +// cabinets, groups, corridors, +// room-templates, obj-templates, bldg-templates, stray-devices.' +// required: true +// type: string +// default: "sites" +// - name: IdOrHierarchyName +// in: path +// description: 'ID or hierarchy name of desired object. +// For templates the slug is the ID.' +// required: true +// type: string +// default: "siteA" +// - name: fieldOnly +// in: query +// description: 'specify which object field to show in response. +// Multiple fieldOnly can be added. An invalid field is simply ignored.' +// - name: startDate +// in: query +// description: 'filter objects by lastUpdated >= startDate. +// Format: yyyy-mm-dd' +// - name: endDate +// in: query +// description: 'filter objects by lastUpdated <= endDate. +// Format: yyyy-mm-dd' +// +// responses: +// +// '200': +// description: 'Found. A response body will be returned with +// a meaningful message.' +// '400': +// description: Bad request. An error message will be returned. +// '404': +// description: Not Found. An error message will be returned. +func GetEntity(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation GET /{entity} Objects GetAllEntities +// Gets all present objects for specified entity (category). +// Returns JSON body with all specified objects of type. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: entity +// in: path +// description: 'Entity (same as category) of the object. Accepted values: sites, domains, +// buildings, rooms, racks, devices, acs, panels, +// cabinets, groups, corridors, +// room-templates, obj-templates, bldg-templates, stray-devices' +// required: true +// type: string +// default: "sites" +// - name: fieldOnly +// in: query +// description: 'specify which object field to show in response. +// Multiple fieldOnly can be added. An invalid field is simply ignored.' +// - name: startDate +// in: query +// description: 'filter objects by lastUpdated >= startDate. +// Format: yyyy-mm-dd' +// - name: endDate +// in: query +// description: 'filter objects by lastUpdated <= endDate. +// Format: yyyy-mm-dd' +// +// responses: +// +// '200': +// description: 'Found. A response body will be returned with +// a meaningful message.' +// '404': +// description: Nothing Found. An error message will be returned. +func GetAllEntities(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation DELETE /{entity}/{IdOrHierarchyName} Objects DeleteObject +// Deletes an Object in the system. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: entity +// in: path +// description: 'Entity (same as category) of the object. Accepted values: sites, domains, +// buildings, rooms, racks, devices, acs, panels, +// cabinets, groups, corridors, +// room-templates, obj-templates, bldg-templates, stray-devices.' +// required: true +// type: string +// default: "sites" +// - name: IdOrHierarchyName +// in: path +// description: 'ID or hierarchy name of desired object. +// For templates the slug is the ID.' +// required: true +// type: string +// default: "siteA" +// +// responses: +// +// '204': +// description: 'Successfully deleted object. +// No response body will be returned' +// '404': +// description: Not found. An error message will be returned +func DeleteObject(c *gin.Context) { + controllers.Delete(c, "objects") +} + +// swagger:operation PATCH /{entity}/{IdOrHierarchyName} Objects PartialUpdateObject +// Partially update object. +// This is the preferred method for modifying data in the system. +// If you want to do a full data replace, please use PUT instead. +// If no data is effectively changed, an OK will still be returned. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: entity +// in: path +// description: 'Entity (same as category) of the object. Accepted values: sites, domains, +// buildings, rooms, racks, devices, acs, panels, +// cabinets, groups, corridors, +// room-templates, obj-templates, bldg-templates, stray-devices.' +// required: true +// type: string +// default: "sites" +// - name: IdOrHierarchyName +// in: path +// description: 'ID or hierarchy name of desired object. +// For templates the slug is the ID.' +// required: true +// type: string +// default: "siteA" +// +// responses: +// +// '200': +// description: 'Updated. A response body will be returned with +// a meaningful message.' +// '400': +// description: Bad request. An error message will be returned. +// '404': +// description: Not Found. An error message will be returned. +func PartialUpdateObject(c *gin.Context) { + controllers.Patch(c, "objects") +} + +// swagger:operation PUT /{objs}/{IdOrHierarchyName} Objects UpdateObject +// Completely update object. +// This method will replace the existing data with the JSON +// received, thus fully replacing the data. If you do not +// want to do this, please use PATCH. +// If no data is effectively changed, an OK will still be returned. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: entity +// in: path +// description: 'Entity (same as category) of the object. Accepted values: sites, domains, +// buildings, rooms, racks, devices, acs, panels, +// cabinets, groups, corridors, +// room-templates, obj-templates, bldg-templates, stray-devices.' +// required: true +// type: string +// default: "sites" +// - name: IdOrHierarchyName +// in: path +// description: 'ID or hierarchy name of desired object. +// For templates the slug is the ID.' +// required: true +// type: string +// default: "siteA" +// +// responses: +// +// '200': +// description: 'Updated. A response body will be returned with +// a meaningful message.' +// '400': +// description: Bad request. An error message will be returned. +// '404': +// description: Not Found. An error message will be returned. +func UpdateObject(c *gin.Context) { + controllers.Put(c, "objects") +} + +// swagger:operation GET /{entity}? Objects GetEntityByQuery +// Gets an object filtering by attribute. +// Gets an Object using any attribute (with the exception of description) +// via query in the system +// The attributes are in the form {attr}=xyz&{attr1}=abc +// And any combination can be used given that at least 1 is provided. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: entity +// in: path +// description: 'Entity (same as category) of the object. Accepted values: sites, domains, +// buildings, rooms, racks, devices, acs, panels, +// cabinets, groups, corridors, +// room-templates, obj-templates, bldg-templates, stray-devices.' +// required: true +// type: string +// default: "sites" +// - name: fieldOnly +// in: query +// description: 'specify which object field to show in response. +// Multiple fieldOnly can be added. An invalid field is simply ignored.' +// - name: startDate +// in: query +// description: 'filter objects by lastUpdated >= startDate. +// Format: yyyy-mm-dd' +// - name: endDate +// in: query +// description: 'filter objects by lastUpdated <= endDate. +// Format: yyyy-mm-dd' +// - name: attributes +// in: query +// description: 'Any other object attributes can be queried. +// Replace attributes here by the name of the attribute followed by its value.' +// required: false +// type: string +// default: domain=DemoDomain +// example: vendor=ibm ; name=siteA ; orientation=front +// +// responses: +// +// '204': +// description: 'Found. A response body will be returned with +// a meaningful message.' +// '404': +// description: Not found. An error message will be returned. +func GetEntityByQuery(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation GET /tempunits/{IdOrHierarchyName} Objects GetTempUnit +// Gets the temperatureUnit attribute of the parent site of given object. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: IdOrHierarchyName +// in: path +// description: 'ID or hierarchy name of desired object. +// For templates the slug is the ID.' +// required: true +// type: string +// default: "siteA" +// +// responses: +// +// '200': +// description: 'Found. A response body will be returned with +// a meaningful message.' +// '404': +// description: 'Nothing Found. An error message will be returned.' +func GetTempUnit(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation GET /{entity}/{id}/{subent} Objects GetEntitiesOfAncestor +// Obtain all objects 2 levels lower in the system. +// For Example: /api/sites/{id}/buildings +// Will return all buildings of a site +// Returns JSON body with all subobjects under the Object +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: entity +// in: query +// description: 'Indicates the entity. Only values of "sites", +// "buildings", "rooms" are acceptable' +// required: true +// type: string +// default: "sites" +// - name: ID +// in: query +// description: ID of object +// required: true +// type: int +// default: 999 +// - name: subent +// in: query +// description: Objects which 2 are levels lower in the hierarchy. +// required: true +// type: string +// default: buildings +// +// responses: +// +// '200': +// description: 'Found. A response body will be returned with +// a meaningful message.' +// '404': +// description: Nothing Found. An error message will be returned. +func GetEntitiesOfAncestor(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation GET /{entity}/{IdOrHierarchyName}/all Objects GetEntityHierarchy +// Get object and all its children. +// Returns JSON body with all subobjects under the Object. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: entity +// in: path +// description: 'Entity (same as category) of the object. Accepted values: sites, domains, +// buildings, rooms, racks, devices, acs, panels, +// cabinets, groups, corridors, +// room-templates, obj-templates, bldg-templates, stray-devices.' +// required: true +// type: string +// default: "sites" +// - name: IdOrHierarchyName +// in: path +// description: 'ID or hierarchy name of desired object. +// For templates the slug is the ID.' +// required: true +// type: string +// default: "siteA" +// - name: limit +// in: query +// description: 'Limits the level of hierarchy for retrieval. if not +// specified for devices then the default value is maximum. +// Example: /api/devices/{id}/all?limit=2' +// required: false +// type: string +// default: 1 +// - name: fieldOnly +// in: query +// description: 'specify which object field to show in response. +// Multiple fieldOnly can be added. An invalid field is simply ignored.' +// - name: startDate +// in: query +// description: 'filter objects by lastUpdated >= startDate. +// Format: yyyy-mm-dd' +// - name: endDate +// in: query +// description: 'filter objects by lastUpdated <= endDate. +// Format: yyyy-mm-dd' +// +// responses: +// +// '200': +// description: 'Found. A response body will be returned with +// a meaningful message.' +// '404': +// description: Nothing Found. An error message will be returned. +func GetEntityHierarchy(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation GET /hierarchy Objects GetCompleteHierarchy +// Returns system complete hierarchy. +// Return is arranged by relationship (father:[children]) +// and category (category:[objects]), starting with "Root":[sites]. +// User permissions apply. +// --- +// security: +// - Bearer: [] +// produces: +// - application/json +// responses: +// '200': +// description: 'Request is valid.' +// '500': +// description: Server error. + +func GetCompleteHierarchy(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation GET /hierarchy/attributes Objects GetCompleteHierarchyAttrs +// Returns attributes of all objects. +// Return is arranged by hierarchyName (objHierarchyName:{attributes}). +// User permissions apply. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// responses: +// +// '200': +// description: 'Request is valid.' +// '500': +// description: Server error. +func GetCompleteHierarchyAttrs(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation GET /{firstEntity}/{id}/{HierarchalPath} Objects GetEntitiesUsingNamesOfParents +// Get an object with its full hierarchal path. +// The path should begin with an entity name (firstEntity) and the ID of an object of this entity +// followed by a hierarchal path until the desired objet, that is, +// a sequence of entity names (category) and object names. +// --- +// security: +// - Bearer: [] +// produces: +// - application/json +// parameters: +// - name: firstEntity +// in: query +// description: 'Root entity followed by an id. Can be: sites, buildings, rooms, racks or devices' +// required: true +// type: string +// default: "sites" +// - name: id +// in: path +// description: 'id of object of firstEntity' +// required: true +// type: string +// default: "123" +// - name: HierarchalPath +// in: path +// description: 'Hierarchal path to desired object.' +// required: true +// type: string +// example: '/api/sites/{id}/buildings/{building_name} ; +// /api/sites/{id}/buildings/{building_name}/rooms/{room_name} ; +// /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/acs/{ac_name} ; +// /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/corridors/{corridor_name} ; +// /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/panels/{panel_name} ; +// /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/groups/{group_name} ; +// /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/racks/{rack_name}/devices/{device_name} ; +// /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/racks/{rack_name} ; +// /api/buildings/{id}/rooms/{room_name} ; +// /api/buildings/{id}/rooms/{room_name}/acs/{ac_name} ; +// /api/buildings/{id}/rooms/{room_name}/corridors/{corridor_name} ; +// /api/buildings/{id}/rooms/{room_name}/panels/{panel_name} ; +// /api/buildings/{id}/rooms/{room_name}/groups/{group_name} ; +// /api/buildings/{id}/rooms/{room_name}/rack/{rack_name} ; +// /api/buildings/{id}/rooms/{room_name}/racks/{rack_name}/devices/{device_name} ; +// /api/rooms/{id}/acs/{ac_name} ; +// /api/rooms/{id}/corridors/{corridor_name} ; +// /api/rooms/{id}/panels/{panel_name} ; +// /api/rooms/{id}/groups/{group_name} ; +// /api/rooms/{id}/racks/{rack_name}/devices/{device_name} ; +// /api/racks/{id}/devices/{device_name} ; +// /api/devices/{id}/devices/{device_name} ;' +// default: "/buildings/BuildingB/rooms/RoomA" +// responses: +// '200': +// description: 'Found. A response body will be returned with +// a meaningful message.' +// '404': +// description: Not Found. An error message will be returned. + +func GetEntitiesUsingNamesOfParents(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation POST /validate/{entity} Objects ValidateObject +// Checks the received data and verifies if the object can be created in the system. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: entity +// in: path +// description: 'Entity (same as category) of the object. Accepted values: sites, domains, +// buildings, rooms, racks, devices, acs, panels, +// cabinets, groups, corridors, +// room-templates, obj-templates, bldg-templates, stray-devices.' +// required: true +// type: string +// default: "sites" +// - name: body +// in: body +// required: true +// default: {} +// +// responses: +// +// '200': +// description: 'Createable. A response body will be returned with +// a meaningful message.' +// '400': +// description: 'Bad request. A response body with an error +// message will be returned.' +// '404': +// description: Not Found. An error message will be returned. +func ValidateObject(c *gin.Context) { + controllers.Post(c, "objects") +} diff --git a/BFF/handlers/organization.go b/BFF/handlers/organization.go new file mode 100644 index 000000000..661616455 --- /dev/null +++ b/BFF/handlers/organization.go @@ -0,0 +1,204 @@ +package handlers + +import ( + "ogree-bff/controllers" + + "github.com/gin-gonic/gin" +) + +// swagger:operation POST /users Organization CreateAccount +// Create a new user user. +// Create an account with email and password credentials, it returns +// a JWT key to use with the API. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: body +// in: body +// description: 'Mandatory: email, password and roles. Optional: name. +// Roles is an object with domains as keys and roles as values. +// Possible roles: manager, user and viewer' +// required: true +// format: object +// example: '{"name": "John Doe", "roles": {"*": "manager"}, "email": "user@test.com", "password": "secret123"}' +// +// responses: +// +// '201': +// description: New account created +// '400': +// description: Bad request +// '403': +// description: User not authorised to create an account +// '500': +// description: Internal server error +func CreateAccount(c *gin.Context) { + controllers.Post(c, "objects") +} + +// swagger:operation POST /users/bulk Organization CreateBulk +// Create multiples users with one request. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// parameters: +// - name: body +// in: body +// description: 'An array of users. Same mandatory and optional parameters as user apply, +// except for password. If not provided, one will be automatically created by the API.' +// required: true +// format: object +// example: '[{"name": "John Doe", "roles": {"*": "manager"}, "email": "user@test.com"}]' +// +// responses: +// +// '200': +// description: Request processed, check response body for results +// '400': +// description: Bad request +// '500': +// description: Internal server error +func CreateBulk(c *gin.Context) { + controllers.Post(c, "objects") +} + +// swagger:operation GET /users Organization GetAllAccounts +// Get a list of users that the caller is allowed to see. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// responses: +// +// '200': +// description: Return all possible users +// '500': +// description: Internal server error +func GetAllAccounts(c *gin.Context) { + controllers.Get(c, "objects") +} + +// swagger:operation DELETE /users/{userid} Organization RemoveAccount +// Remove the specified user account. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// parameters: +// - name: userid +// in: path +// description: 'The ID of the user to delete' +// required: true +// type: string +// example: "someUserId" +// +// responses: +// +// '200': +// description: User removed +// '400': +// description: User ID not valid or not found +// '403': +// description: Caller not authorised to delete this user +// '500': +// description: Internal server error +func RemoveAccount(c *gin.Context) { + controllers.Delete(c, "objects") +} + +// swagger:operation PATCH /users/{userid} Organization ModifyUserRoles +// Modify user permissions: domain and role. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: userid +// in: path +// description: 'The ID of the user to modify roles' +// required: true +// type: string +// example: "someUserId" +// - name: roles +// in: body +// description: An object with domains as keys and roles as values +// type: json +// required: true +// example: '{"roles": {"*": "manager"}}' +// +// responses: +// +// '200': +// description: User roles modified +// '400': +// description: Bad request +// '403': +// description: Caller not authorised to modify this user +// '500': +// description: Internal server error +func ModifyUserRoles(c *gin.Context) { + controllers.Patch(c, "objects") +} + +// swagger:operation POST /domains/bulk Organization CreateBulkDomain +// Create multiple domains in a single request. +// An array of domains should be provided in the body. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// parameters: +// - name: body +// in: body +// required: true +// default: [{}] +// +// responses: +// +// '200': +// description: 'Request processed. Check the response body +// for individual results for each of the sent domains' +// '400': +// description: 'Bad format: body is not a valid list of domains.' +func CreateBulkDomain(c *gin.Context) { + controllers.Post(c, "objects") +} + +// swagger:operation GET /hierarchy/domains Organization GetCompleteDomainHierarchy +// Returns domain complete hierarchy. +// Return is arranged by relationship (father:[children]), +// starting with "Root":[root domains]. +// --- +// security: +// - Bearer: [] +// +// produces: +// - application/json +// +// responses: +// +// '200': +// description: 'Request is valid.' +// '500': +// description: Server error. +func GetCompleteDomainHierarchy(c *gin.Context) { + controllers.Get(c, "objects") +} diff --git a/BFF/handlers/swagger.go b/BFF/handlers/swagger.go new file mode 100644 index 000000000..a146b12eb --- /dev/null +++ b/BFF/handlers/swagger.go @@ -0,0 +1,19 @@ +package handlers + +import( + "net/http" + "github.com/go-openapi/runtime/middleware" + "github.com/gorilla/mux" +) + +func SwaggerHandler() *mux.Router{ + pr := mux.NewRouter() + + pr.Handle("/swagger.json", http.FileServer(http.Dir("./"))) + opts := middleware.SwaggerUIOpts{SpecURL: "swagger.json"} + sh := middleware.SwaggerUI(opts, nil) + pr.Handle("/docs", sh) + + return pr + +} \ No newline at end of file diff --git a/BFF/main.go b/BFF/main.go new file mode 100644 index 000000000..640b7a75e --- /dev/null +++ b/BFF/main.go @@ -0,0 +1,62 @@ +// Ogree BFF: +// version: 1.0.0 +// title: Awsome API +// Schemes: http, https +// Host: +// BasePath: /api +// Consumes: +// - application/json +// Produces: +// - application/json +// SecurityDefinitions: +// Bearer: +// type: apiKey +// name: Authorization +// in: header +// swagger:meta +package main + +import ( + "encoding/json" + "fmt" + "io" + "ogree-bff/models" + "ogree-bff/services" + "os" + + e "github.com/joho/godotenv" +) + + +func GetAPIInfo() ([]models.API) { + jsonFile, err := os.Open("api.json") + // if we os.Open returns an error then handle it + if err != nil { + fmt.Println(err) + } + // defer the closing of our jsonFile so that we can parse it later on + defer jsonFile.Close() + byteValue, _ := io.ReadAll(jsonFile) + var api []models.API + json.Unmarshal(byteValue, &api) + + return api +} + +func main() { + + env := os.Getenv("ENV") + if env != "production" { + fmt.Println("Loading environment variables from .env") + err := e.Load() + if err != nil { + fmt.Println("Some error occured. Err: ", err) + return + } + } + BFF_PORT := os.Getenv("BFF_PORT") + apiList := GetAPIInfo() + fmt.Println(apiList) + router := services.InitRouter(apiList,env) + router.Run(":"+BFF_PORT) +} diff --git a/BFF/models/api.go b/BFF/models/api.go new file mode 100644 index 000000000..368ae490b --- /dev/null +++ b/BFF/models/api.go @@ -0,0 +1,8 @@ +package models + +type API struct { + + Name string `json:"name"` + URL string `json:"url"` + +} \ No newline at end of file diff --git a/BFF/models/auth.go b/BFF/models/auth.go new file mode 100644 index 000000000..cd0fe068d --- /dev/null +++ b/BFF/models/auth.go @@ -0,0 +1,29 @@ +package models + + +// swagger:model LoginInput +type LoginInput struct { + // email + // in: email + Email string `json:"email" binding:"required"` + // username + // in: password + Password string `json:"password" binding:"required"` +} + +type User struct { + ID uint `json:"id"` + Email string `json:"email" binding:"required"` + Password string `json:"password" binding:"required"` +} + +// swagger:model SuccessLogin +type SuccessLogin struct { + Token string `json:"token" binding:"required"` +} + +// swagger:parameters LoginToApi +type ReqLoginBody struct { + // in: body + Body LoginInput `json:"body"` +} diff --git a/BFF/models/http.go b/BFF/models/http.go new file mode 100644 index 000000000..3a6f8df07 --- /dev/null +++ b/BFF/models/http.go @@ -0,0 +1,22 @@ +package models + +// swagger:model ErrorResponse +type ErrorResponse struct { + // Error Response Message + // in: message + Message string `json:"message"` +} + +// swagger:model SuccessResponse +type SuccessResponse struct { + // Error Response Message + // in: message + Message string `json:"message"` +} + + +type Message struct { + + StatusCode int `json:"statuscode"` + Message interface{} `json:"message"` +} diff --git a/BFF/readme.md b/BFF/readme.md new file mode 100644 index 000000000..53881c952 --- /dev/null +++ b/BFF/readme.md @@ -0,0 +1,50 @@ +# Ogree BFF (Back for Frontend) for API Routing and Aggregation + +This is the Ogree BFF (Back for Frontend) service that handles API redirection and aggregation, primarily designed to streamline frontend-backend communication. It acts as a gateway for routing requests to various backend APIs and provides a unified interface for frontend applications. + +## Configuration + +Before you begin, set the following environment variables in a .env file or as part of your runtime: + +- `ARANGO_PASSWORD`: The password for authentication. +- `BFF_PORT`: The port where BFF listenning + +You also need a api.json file, that contains an array of `name` and `url` of all API you when to use. + +Notice that this minimal configuration you need is an API name 'objects': + +Exemple of api.json with an API for objects and another for server: + +```json +[ + {"name":"server", "url": "http://localhost:8080"}, + {"name":"objects", "url": "http://localhost:3001"} +] +``` +## Installation and Execution + +1. Clone this repository to your local machine. +2. Install any necessary dependencies if required. +3. Compile and run the BFF using the following command: + +```bash +go run main.go +``` +The server should start and be ready to accept requests. + +## Endpoints + +### Bindings Data between objects's API and another + +- GET /api/deviceComp/:apiName/:objects/:objectAttributes/:devicesAttributes : Retrieve the list of all devices in the database connect to apiName where the specific attributes of the objects match the specific attributes of devices + + + +## API Documentation +You can explore the BFF documentation using Swagger UI. After starting the server, navigate to /docs in your web browser to access the interactive documentation and explore the available endpoints, request and response schemas, and even test the API directly from the documentation. + +For example, if the API is running locally, you can access Swagger UI at: + +```bash +http://localhost:/docs +`````` \ No newline at end of file diff --git a/BFF/services/router.go b/BFF/services/router.go new file mode 100644 index 000000000..c82caa848 --- /dev/null +++ b/BFF/services/router.go @@ -0,0 +1,155 @@ +package services + +import ( + "net/http" + "ogree-bff/handlers" + "ogree-bff/models" + "ogree-bff/utils/token" + + "github.com/gin-gonic/gin" +) + +func JwtAuthMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + err := token.TokenValid(c) + if err != nil { + c.IndentedJSON(http.StatusUnauthorized, gin.H{"message": "Unauthorized"}) + c.Abort() + return + } + c.Next() + } +} + +func APIMiddleware(apiList []models.API) gin.HandlerFunc { + return func(c *gin.Context) { + for _, api := range apiList { + c.Set(api.Name, api.URL) + + } + c.Next() + + } + +} + +func CORSMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + c.Writer.Header().Set("Access-Control-Allow-Origin", "*") + c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") + c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") + c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT,DELETE") + + if c.Request.Method == "OPTIONS" { + c.AbortWithStatus(204) + return + } + + c.Next() + } +} + + +func initDevices(protected, unprotected *gin.RouterGroup) { + + protected.GET("/deviceComp/:entity", handlers.GetDevices) + protected.GET("/deviceComp/:entity/ConnecteTo/:id", handlers.GetDevicesConnectedTo) + protected.POST("/deviceComp/:entity", handlers.CreateDevices) + protected.DELETE("/deviceComp/:entity/:id", handlers.DeleteDevice) + + protected.GET("/Connections", handlers.GetConnections) + protected.POST("/Connections", handlers.CreateConnections) + protected.DELETE("/Connections/:id", handlers.DeleteConnections) + + protected.GET("/deviceComp/:entity/:obj/:objAttr/:deviceAttr", handlers.GetDeviceBindingObject) +} + +func initAuth(protected, unprotected *gin.RouterGroup) { + + protected.GET("/token/valid", handlers.ValidToken) + protected.POST("/users/password/reset", handlers.ResetUserPassword) + protected.POST("/users/password/change", handlers.ModifyUserPassword) + + unprotected.POST("/login", handlers.Login) + unprotected.POST("/users/password/forgot", handlers.UserForgotPassword) +} + +func initOrganization(protected, unprotected *gin.RouterGroup) { + protected.POST("/users", handlers.CreateAccount) + protected.POST("/users/bulk", handlers.CreateBulk) + protected.GET("/users", handlers.GetAllAccounts) + protected.DELETE("/users/:user", handlers.RemoveAccount) + protected.PATCH("/users/:user", handlers.ModifyUserRoles) + protected.POST("/domains/bulk", handlers.CreateBulkDomain) + protected.GET("/hierarchy/domains", handlers.GetCompleteDomainHierarchy) +} + +func initFlutterApp(protected, unprotected *gin.RouterGroup) { + protected.POST("/projects", handlers.CreateProjects) + protected.GET("/projects", handlers.GetProjects) + protected.DELETE("/projects/*id", handlers.DeleteProjects) + protected.PUT("/projects/*id", handlers.UpdateProjects) + +} +func initAbout(protected, unprotected *gin.RouterGroup) { + protected.GET("/stats", handlers.GetStats) + protected.POST("/versions", handlers.GetAPIVersion) +} + +func initObjects(protected, unprotected *gin.RouterGroup) { + + protected.GET("/:entity", handlers.GetAllEntities) + protected.POST("/:entity", handlers.CreateObject) + + protected.GET("/objects/*hierarchyName", handlers.GetGenericObject) + + protected.GET("/:entity/*id", handlers.GetEntity) + protected.DELETE("/:entity/*id", handlers.DeleteObject) + protected.PATCH("/:entity/*id", handlers.PartialUpdateObject) + protected.PUT("/:entity/*id", handlers.UpdateObject) + + protected.GET("/tempunits/*id", handlers.GetTempUnit) + + //protected.GET("/:entity/:id/:subent",handlers.GetEntitiesOfAncestor) + + protected.GET("/hierarchy", handlers.GetCompleteHierarchy) + protected.GET("/hierarchy/attributes", handlers.GetCompleteHierarchyAttrs) + + //protected.GET("/:entity/:id/:HierarchalPath",handlers.GetEntitiesUsingNamesOfParents) + + protected.POST("/validate/*entity", handlers.ValidateObject) + +} + +func InitRouter(apiList []models.API, env string) *gin.Engine { + if env == "production" { + gin.SetMode(gin.ReleaseMode) + } + + router := gin.Default() + router.Use(CORSMiddleware()) + + protected := router.Group("/api") + protected.Use(JwtAuthMiddleware()) + protected.Use(APIMiddleware(apiList)) + + + unprotected := router.Group("/api") + unprotected.Use(APIMiddleware(apiList)) + + initDevices(protected, unprotected) + initAuth(protected, unprotected) + initOrganization(protected, unprotected) + initObjects(protected, unprotected) + initAbout(protected, unprotected) + + //init healthcheck route + unprotected.GET("/health", func(c *gin.Context) { + c.String(http.StatusAccepted, "") + }) + + swagger := handlers.SwaggerHandler() + router.Use(gin.WrapH(swagger)) + + return router +} diff --git a/BFF/swagger.json b/BFF/swagger.json new file mode 100644 index 000000000..a66d1e230 --- /dev/null +++ b/BFF/swagger.json @@ -0,0 +1,1502 @@ +{ + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "swagger": "2.0", + "info": { + "title": "Ogree BFF:", + "version": "1.0.0" + }, + "basePath": "/api", + "paths": { + "/Connections": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Get Connection list", + "tags": [ + "Devices" + ], + "operationId": "GetConnections", + "parameters": [ + { + "type": "string", + "description": "Key of connection", + "name": "_key", + "in": "query" + }, + { + "type": "string", + "description": "From witch device", + "name": "_from", + "in": "query" + }, + { + "type": "string", + "description": "To witch device", + "name": "_to", + "in": "query" + } + ] + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Create new Connection", + "tags": [ + "Devices" + ], + "operationId": "CreateConnection", + "parameters": [ + { + "format": "object", + "example": "{\"_from\": \"devices/123\", \"_to\": \"devices/111\"}", + "description": "Mandatory: _from, _to.", + "name": "body", + "in": "body", + "required": true + } + ] + } + }, + "/Connections/{connection}": { + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Delete Connection by key", + "tags": [ + "Devices" + ], + "operationId": "DeleteConnection", + "parameters": [ + { + "type": "string", + "description": "connection looking for", + "name": "connection", + "in": "path", + "required": true + } + ] + } + }, + "/deviceComp/{entity}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Create new Devices", + "tags": [ + "Devices" + ], + "operationId": "CreateDevices", + "parameters": [ + { + "type": "string", + "description": "database to retrieve devices", + "name": "entity", + "in": "path", + "required": true + }, + { + "format": "object", + "example": "{\"_name\": \"server\", \"group_name\": \"exwipen22\",\"created\": \"2022-07-18\"}", + "description": "Mandatory: _name, group_name,created.", + "name": "body", + "in": "body", + "required": true + } + ] + } + }, + "/deviceComp/{entity}/ConnecteTo/{device}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Get Devices connected to a device", + "tags": [ + "Devices" + ], + "operationId": "GetDevicesConnectedTo", + "parameters": [ + { + "type": "string", + "description": "database to retrieve devices", + "name": "entity", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Key of device", + "name": "device", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Filter devices by key", + "name": "_key", + "in": "query" + }, + { + "type": "string", + "description": "Name of device", + "name": "_name", + "in": "query" + }, + { + "type": "string", + "description": "Group_name of device", + "name": "group_name", + "in": "query" + }, + { + "type": "string", + "description": "Serial number of device", + "name": "serial", + "in": "query" + } + ] + } + }, + "/deviceComp/{entity}/{obj}/{objAttr}/{deviceAttr}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Get Devices list", + "tags": [ + "Devices" + ], + "operationId": "GetDeviceBindingObject", + "parameters": [ + { + "type": "string", + "description": "object for binding", + "name": "obj", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "object attribute for binding", + "name": "objAttr", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "devices attribute for binding", + "name": "deviceAttr", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Key of device", + "name": "_key", + "in": "query" + }, + { + "type": "string", + "description": "Name of device", + "name": "_name", + "in": "query" + }, + { + "type": "string", + "description": "Group_name of device", + "name": "group_name", + "in": "query" + }, + { + "type": "string", + "description": "Serial number of device", + "name": "serial", + "in": "query" + } + ] + } + }, + "/deviceComp/{entity}{device}": { + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Delete Devices by key", + "tags": [ + "Devices" + ], + "operationId": "DeleteDevices", + "parameters": [ + { + "type": "string", + "description": "database to retrieve devices", + "name": "entity", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "device looking for", + "name": "device", + "in": "path", + "required": true + } + ] + } + }, + "/deviceComps/{entity}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Get Devices list", + "tags": [ + "Devices" + ], + "operationId": "Devices", + "parameters": [ + { + "type": "string", + "description": "Key of device", + "name": "_key", + "in": "query" + }, + { + "type": "string", + "description": "database to retrieve devices", + "name": "entity", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Name of device", + "name": "_name", + "in": "query" + }, + { + "type": "string", + "description": "Group_name of device", + "name": "group_name", + "in": "query" + }, + { + "type": "string", + "description": "Serial number of device", + "name": "serial", + "in": "query" + } + ], + "responses": { + "200": { + "description": "successful", + "schema": { + "items": { + "$ref": "#/definitions/SuccessResponse" + } + } + }, + "500": { + "description": "Error", + "schema": { + "items": { + "$ref": "#/definitions/ErrorResponse" + } + } + } + } + } + }, + "/domains/bulk": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "An array of domains should be provided in the body.", + "produces": [ + "application/json" + ], + "tags": [ + "Organization" + ], + "summary": "Create multiple domains in a single request.", + "operationId": "CreateBulkDomain", + "parameters": [ + { + "default": [ + {} + ], + "name": "body", + "in": "body", + "required": true + } + ] + } + }, + "/hierarchy": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Return is arranged by relationship (father:[children])\nand category (category:[objects]), starting with \"Root\":[sites].\nUser permissions apply.", + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Returns system complete hierarchy.", + "operationId": "GetCompleteHierarchy", + "responses": { + "200": { + "description": "Request is valid." + }, + "500": { + "description": "Server error." + } + } + } + }, + "/hierarchy/attributes": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Return is arranged by hierarchyName (objHierarchyName:{attributes}).\nUser permissions apply.", + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Returns attributes of all objects.", + "operationId": "GetCompleteHierarchyAttrs" + } + }, + "/hierarchy/domains": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Return is arranged by relationship (father:[children]),\nstarting with \"Root\":[root domains].", + "produces": [ + "application/json" + ], + "tags": [ + "Organization" + ], + "summary": "Returns domain complete hierarchy.", + "operationId": "GetCompleteDomainHierarchy" + } + }, + "/login": { + "post": { + "description": "Create a new JWT Key. This can also be used to verify credentials\nThe authorize and 'Try it out' buttons don't work", + "produces": [ + "application/json" + ], + "tags": [ + "Authentication" + ], + "summary": "Generates a new JWT Key for the client.", + "operationId": "Authenticate", + "parameters": [ + { + "format": "object", + "example": "{\"email\": \"user@test.com\", \"password\": \"secret123\"}", + "description": "Mandatory: email and password.", + "name": "body", + "in": "body", + "required": true + } + ], + "responses": { + "200": { + "description": "Authenticated" + }, + "400": { + "description": "Bad request" + }, + "500": { + "description": "Internal server error" + } + } + } + }, + "/objects/{hierarchyName}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Gets an object from any of the physical entities with no need to specify it.\nThe hierarchyName must be provided in the URL as a parameter.", + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Get an object from any entity.", + "operationId": "GetGenericObject", + "parameters": [ + { + "description": "hierarchyName of the object", + "name": "hierarchyName", + "in": "path", + "required": true + }, + { + "description": "specify which object field to show in response. Multiple fieldOnly can be added. An invalid field is simply ignored.", + "name": "fieldOnly", + "in": "query" + }, + { + "description": "filter objects by lastUpdated \u003e= startDate. Format: yyyy-mm-dd", + "name": "startDate", + "in": "query" + }, + { + "description": "filter objects by lastUpdated \u003c= endDate. Format: yyyy-mm-dd", + "name": "endDate", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Found. A response body will be returned with a meaningful message." + }, + "404": { + "description": "Not Found. An error message will be returned." + } + } + } + }, + "/projects": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "FlutterApp" + ], + "summary": "Get a list of projects for the specified user.", + "operationId": "GetProjects", + "parameters": [ + { + "type": "string", + "default": "user@test.com", + "description": "Email of the user whose projects are being requested. Example: /api/projects?user=user@test.com", + "name": "user", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Return all possible projects." + }, + "400": { + "description": "Bad Request. Invalid user query param." + }, + "500": { + "description": "Internal server error." + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Create a new project", + "produces": [ + "application/json" + ], + "tags": [ + "FlutterApp" + ], + "operationId": "CreateProjects", + "parameters": [ + { + "format": "object", + "example": "{\"attributes\":[\"domain\"],\"authorLastUpdate\":\"helder\",\"dateRange\":\"01/01/2023-02/02/2023\", \"lastUpdate\":\"02/02/2023\",\"name\":\"test 1\",\"namespace\":\"physical\",\"objects\":[\"siteB\"],\"showAvg\":false, \"showSum\":false,\"permissions\":[\"user@test.com\",\"admin\"]}", + "description": "Mandatory: name, dateRange, namespace, attributes, objects, permissions, authorLastUpdate, lastUpdate. Optional: showAvg, showSum, isPublic.", + "name": "body", + "in": "body", + "required": true + } + ] + } + }, + "/projects/{ProjectID}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "FlutterApp" + ], + "summary": "Replace the data of an existing project.", + "operationId": "UpdateProjects", + "parameters": [ + { + "type": "string", + "default": "1234", + "description": "ID of the project to update.", + "name": "ProjectID", + "in": "path", + "required": true + }, + { + "format": "object", + "example": "{\"attributes\":[\"domain\"],\"authorLastUpdate\":\"helder\",\"dateRange\":\"01/01/2023-02/02/2023\", \"lastUpdate\":\"02/02/2023\",\"name\":\"test 1\",\"namespace\":\"physical\",\"objects\":[\"siteB\"],\"showAvg\":false, \"showSum\":false,\"permissions\":[\"user@test.com\",\"admin\"]}", + "description": "Mandatory: name, dateRange, namespace, attributes, objects, permissions, authorLastUpdate, lastUpdate. Optional: showAvg, showSum, isPublic.", + "name": "body", + "in": "body", + "required": true + } + ] + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "FlutterApp" + ], + "summary": "Delete an existing project.", + "operationId": "DeleteProjects", + "parameters": [ + { + "type": "string", + "default": "1234", + "description": "ID of the project to delete.", + "name": "ProjectID", + "in": "path", + "required": true + } + ] + } + }, + "/stats": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "About" + ], + "summary": "Displays DB statistics.", + "operationId": "GetStats", + "responses": { + "200": { + "description": "Request is valid." + }, + "504": { + "description": "Server error." + } + } + } + }, + "/tempunits/{IdOrHierarchyName}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Gets the temperatureUnit attribute of the parent site of given object.", + "operationId": "GetTempUnit", + "parameters": [ + { + "type": "string", + "default": "siteA", + "description": "ID or hierarchy name of desired object. For templates the slug is the ID.", + "name": "IdOrHierarchyName", + "in": "path", + "required": true + } + ] + } + }, + "/token/valid": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authentication" + ], + "summary": "Verify if token sent in the header is valid.", + "operationId": "VerifyToken", + "responses": { + "200": { + "description": "Token is valid." + }, + "403": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal server error" + } + } + } + }, + "/users": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Organization" + ], + "summary": "Get a list of users that the caller is allowed to see.", + "operationId": "GetAllAccounts" + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Create an account with email and password credentials, it returns\na JWT key to use with the API.", + "produces": [ + "application/json" + ], + "tags": [ + "Organization" + ], + "summary": "Create a new user user.", + "operationId": "CreateAccount", + "parameters": [ + { + "format": "object", + "example": "{\"name\": \"John Doe\", \"roles\": {\"*\": \"manager\"}, \"email\": \"user@test.com\", \"password\": \"secret123\"}", + "description": "Mandatory: email, password and roles. Optional: name. Roles is an object with domains as keys and roles as values. Possible roles: manager, user and viewer", + "name": "body", + "in": "body", + "required": true + } + ] + } + }, + "/users/bulk": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Organization" + ], + "summary": "Create multiples users with one request.", + "operationId": "CreateBulk", + "parameters": [ + { + "format": "object", + "example": "[{\"name\": \"John Doe\", \"roles\": {\"*\": \"manager\"}, \"email\": \"user@test.com\"}]", + "description": "An array of users. Same mandatory and optional parameters as user apply, except for password. If not provided, one will be automatically created by the API.", + "name": "body", + "in": "body", + "required": true + } + ] + } + }, + "/users/password/change": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Authentication" + ], + "summary": "For logged in user to change its own password.", + "operationId": "ModifyUserPassword", + "parameters": [ + { + "type": "json", + "example": "{\"currentPassword\": \"myOldPassword\", \"newPassword\": \"myNewPassword\"}", + "description": "Mandatory: currentPassword and newPassword.", + "name": "body", + "in": "body", + "required": true + } + ], + "responses": { + "200": { + "description": "Password changed" + }, + "400": { + "description": "Bad request" + }, + "500": { + "description": "Internal server error" + } + } + } + }, + "/users/password/forgot": { + "post": { + "description": "Public endpoint to request a reset of a user's password (forgot my password).\nIf the email is valid, an email with a reset token/link will be sent to the user.", + "produces": [ + "application/json" + ], + "tags": [ + "Authentication" + ], + "summary": "Forgot my password.", + "operationId": "UserForgotPassword", + "parameters": [ + { + "type": "string", + "example": "{\"email\": \"user@test.com\"}", + "description": "Mandatory: email.", + "name": "body", + "in": "body", + "required": true + } + ], + "responses": { + "200": { + "description": "request processed. If account exists, an email with a reset token is sent" + }, + "400": { + "description": "Bad request" + }, + "500": { + "description": "Internal server error" + } + } + } + }, + "/users/password/reset": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "For user that first called forgot enpoint to change its password.\nA reset token generated by the forgot endpoint should be provided as the Authentication header.", + "produces": [ + "application/json" + ], + "tags": [ + "Authentication" + ], + "summary": "Reset password after forgot.", + "operationId": "ResetUserPassword", + "parameters": [ + { + "type": "json", + "example": "{\"newPassword\": \"myNewPassword\"}", + "description": "Mandatory: currentPassword and newPassword.", + "name": "body", + "in": "body", + "required": true + } + ], + "responses": { + "200": { + "description": "Password changed" + }, + "400": { + "description": "Bad request" + }, + "500": { + "description": "Internal server error" + } + } + } + }, + "/users/{userid}": { + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Organization" + ], + "summary": "Remove the specified user account.", + "operationId": "RemoveAccount", + "parameters": [ + { + "type": "string", + "example": "someUserId", + "description": "The ID of the user to delete", + "name": "userid", + "in": "path", + "required": true + } + ] + }, + "patch": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Organization" + ], + "summary": "Modify user permissions: domain and role.", + "operationId": "ModifyUserRoles", + "parameters": [ + { + "type": "string", + "example": "someUserId", + "description": "The ID of the user to modify roles", + "name": "userid", + "in": "path", + "required": true + }, + { + "type": "json", + "example": "{\"roles\": {\"*\": \"manager\"}}", + "description": "An object with domains as keys and roles as values", + "name": "roles", + "in": "body", + "required": true + } + ] + } + }, + "/validate/{entity}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Checks the received data and verifies if the object can be created in the system.", + "operationId": "ValidateObject", + "parameters": [ + { + "type": "string", + "default": "sites", + "description": "Entity (same as category) of the object. Accepted values: sites, domains, buildings, rooms, racks, devices, acs, panels, cabinets, groups, corridors, room-templates, obj-templates, bldg-templates, stray-devices.", + "name": "entity", + "in": "path", + "required": true + }, + { + "default": {}, + "name": "body", + "in": "body", + "required": true + } + ] + } + }, + "/version": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "About" + ], + "summary": "Gets the API version.", + "operationId": "GetAPIVersion", + "responses": { + "200": { + "description": "OK. A response body will be returned with version details." + } + } + } + }, + "/{entity}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Returns JSON body with all specified objects of type.", + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Gets all present objects for specified entity (category).", + "operationId": "GetAllEntities", + "parameters": [ + { + "type": "string", + "default": "sites", + "description": "Entity (same as category) of the object. Accepted values: sites, domains, buildings, rooms, racks, devices, acs, panels, cabinets, groups, corridors, room-templates, obj-templates, bldg-templates, stray-devices", + "name": "entity", + "in": "path", + "required": true + }, + { + "description": "specify which object field to show in response. Multiple fieldOnly can be added. An invalid field is simply ignored.", + "name": "fieldOnly", + "in": "query" + }, + { + "description": "filter objects by lastUpdated \u003e= startDate. Format: yyyy-mm-dd", + "name": "startDate", + "in": "query" + }, + { + "description": "filter objects by lastUpdated \u003c= endDate. Format: yyyy-mm-dd", + "name": "endDate", + "in": "query" + } + ] + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Creates an object of the given entity in the system.", + "operationId": "CreateObject", + "parameters": [ + { + "type": "string", + "default": "sites", + "description": "Entity (same as category) of the object. Accepted values: sites, domains, buildings, rooms, racks, devices, acs, panels, cabinets, groups, corridors, room-templates, obj-templates, bldg-templates, stray-devices.", + "name": "entity", + "in": "path", + "required": true + }, + { + "default": {}, + "name": "body", + "in": "body", + "required": true + } + ] + } + }, + "/{entity}/{IdOrHierarchyName}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "The ID or hierarchy name must be provided in the URL parameter.", + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Gets an Object of the given entity.", + "operationId": "GetEntity", + "parameters": [ + { + "type": "string", + "default": "sites", + "description": "Entity (same as category) of the object. Accepted values: sites, domains, buildings, rooms, racks, devices, acs, panels, cabinets, groups, corridors, room-templates, obj-templates, bldg-templates, stray-devices.", + "name": "entity", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "siteA", + "description": "ID or hierarchy name of desired object. For templates the slug is the ID.", + "name": "IdOrHierarchyName", + "in": "path", + "required": true + }, + { + "description": "specify which object field to show in response. Multiple fieldOnly can be added. An invalid field is simply ignored.", + "name": "fieldOnly", + "in": "query" + }, + { + "description": "filter objects by lastUpdated \u003e= startDate. Format: yyyy-mm-dd", + "name": "startDate", + "in": "query" + }, + { + "description": "filter objects by lastUpdated \u003c= endDate. Format: yyyy-mm-dd", + "name": "endDate", + "in": "query" + } + ] + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Deletes an Object in the system.", + "operationId": "DeleteObject", + "parameters": [ + { + "type": "string", + "default": "sites", + "description": "Entity (same as category) of the object. Accepted values: sites, domains, buildings, rooms, racks, devices, acs, panels, cabinets, groups, corridors, room-templates, obj-templates, bldg-templates, stray-devices.", + "name": "entity", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "siteA", + "description": "ID or hierarchy name of desired object. For templates the slug is the ID.", + "name": "IdOrHierarchyName", + "in": "path", + "required": true + } + ] + }, + "patch": { + "security": [ + { + "Bearer": [] + } + ], + "description": "This is the preferred method for modifying data in the system.\nIf you want to do a full data replace, please use PUT instead.\nIf no data is effectively changed, an OK will still be returned.", + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Partially update object.", + "operationId": "PartialUpdateObject", + "parameters": [ + { + "type": "string", + "default": "sites", + "description": "Entity (same as category) of the object. Accepted values: sites, domains, buildings, rooms, racks, devices, acs, panels, cabinets, groups, corridors, room-templates, obj-templates, bldg-templates, stray-devices.", + "name": "entity", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "siteA", + "description": "ID or hierarchy name of desired object. For templates the slug is the ID.", + "name": "IdOrHierarchyName", + "in": "path", + "required": true + } + ] + } + }, + "/{entity}/{IdOrHierarchyName}/all": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Returns JSON body with all subobjects under the Object.", + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Get object and all its children.", + "operationId": "GetEntityHierarchy", + "parameters": [ + { + "type": "string", + "default": "sites", + "description": "Entity (same as category) of the object. Accepted values: sites, domains, buildings, rooms, racks, devices, acs, panels, cabinets, groups, corridors, room-templates, obj-templates, bldg-templates, stray-devices.", + "name": "entity", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "siteA", + "description": "ID or hierarchy name of desired object. For templates the slug is the ID.", + "name": "IdOrHierarchyName", + "in": "path", + "required": true + }, + { + "type": "string", + "default": 1, + "description": "Limits the level of hierarchy for retrieval. if not specified for devices then the default value is maximum. Example: /api/devices/{id}/all?limit=2", + "name": "limit", + "in": "query" + }, + { + "description": "specify which object field to show in response. Multiple fieldOnly can be added. An invalid field is simply ignored.", + "name": "fieldOnly", + "in": "query" + }, + { + "description": "filter objects by lastUpdated \u003e= startDate. Format: yyyy-mm-dd", + "name": "startDate", + "in": "query" + }, + { + "description": "filter objects by lastUpdated \u003c= endDate. Format: yyyy-mm-dd", + "name": "endDate", + "in": "query" + } + ] + } + }, + "/{entity}/{id}/{subent}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "For Example: /api/sites/{id}/buildings\nWill return all buildings of a site\nReturns JSON body with all subobjects under the Object", + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Obtain all objects 2 levels lower in the system.", + "operationId": "GetEntitiesOfAncestor", + "parameters": [ + { + "type": "string", + "default": "sites", + "description": "Indicates the entity. Only values of \"sites\", \"buildings\", \"rooms\" are acceptable", + "name": "entity", + "in": "query", + "required": true + }, + { + "type": "int", + "default": 999, + "description": "ID of object", + "name": "ID", + "in": "query", + "required": true + }, + { + "type": "string", + "default": "buildings", + "description": "Objects which 2 are levels lower in the hierarchy.", + "name": "subent", + "in": "query", + "required": true + } + ] + } + }, + "/{entity}?": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Gets an Object using any attribute (with the exception of description)\nvia query in the system\nThe attributes are in the form {attr}=xyz\u0026{attr1}=abc\nAnd any combination can be used given that at least 1 is provided.", + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Gets an object filtering by attribute.", + "operationId": "GetEntityByQuery", + "parameters": [ + { + "type": "string", + "default": "sites", + "description": "Entity (same as category) of the object. Accepted values: sites, domains, buildings, rooms, racks, devices, acs, panels, cabinets, groups, corridors, room-templates, obj-templates, bldg-templates, stray-devices.", + "name": "entity", + "in": "path", + "required": true + }, + { + "description": "specify which object field to show in response. Multiple fieldOnly can be added. An invalid field is simply ignored.", + "name": "fieldOnly", + "in": "query" + }, + { + "description": "filter objects by lastUpdated \u003e= startDate. Format: yyyy-mm-dd", + "name": "startDate", + "in": "query" + }, + { + "description": "filter objects by lastUpdated \u003c= endDate. Format: yyyy-mm-dd", + "name": "endDate", + "in": "query" + }, + { + "type": "string", + "default": "domain=DemoDomain", + "example": "vendor=ibm ; name=siteA ; orientation=front", + "description": "Any other object attributes can be queried. Replace attributes here by the name of the attribute followed by its value.", + "name": "attributes", + "in": "query" + } + ] + } + }, + "/{firstEntity}/{id}/{HierarchalPath}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "The path should begin with an entity name (firstEntity) and the ID of an object of this entity\nfollowed by a hierarchal path until the desired objet, that is,\na sequence of entity names (category) and object names.", + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Get an object with its full hierarchal path.", + "operationId": "GetEntitiesUsingNamesOfParents", + "parameters": [ + { + "type": "string", + "default": "sites", + "description": "Root entity followed by an id. Can be: sites, buildings, rooms, racks or devices", + "name": "firstEntity", + "in": "query", + "required": true + }, + { + "type": "string", + "default": "123", + "description": "id of object of firstEntity", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "/buildings/BuildingB/rooms/RoomA", + "example": "/api/sites/{id}/buildings/{building_name} ; /api/sites/{id}/buildings/{building_name}/rooms/{room_name} ; /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/acs/{ac_name} ; /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/corridors/{corridor_name} ; /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/panels/{panel_name} ; /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/groups/{group_name} ; /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/racks/{rack_name}/devices/{device_name} ; /api/sites/{id}/buildings/{building_name}/rooms/{room_name}/racks/{rack_name} ; /api/buildings/{id}/rooms/{room_name} ; /api/buildings/{id}/rooms/{room_name}/acs/{ac_name} ; /api/buildings/{id}/rooms/{room_name}/corridors/{corridor_name} ; /api/buildings/{id}/rooms/{room_name}/panels/{panel_name} ; /api/buildings/{id}/rooms/{room_name}/groups/{group_name} ; /api/buildings/{id}/rooms/{room_name}/rack/{rack_name} ; /api/buildings/{id}/rooms/{room_name}/racks/{rack_name}/devices/{device_name} ; /api/rooms/{id}/acs/{ac_name} ; /api/rooms/{id}/corridors/{corridor_name} ; /api/rooms/{id}/panels/{panel_name} ; /api/rooms/{id}/groups/{group_name} ; /api/rooms/{id}/racks/{rack_name}/devices/{device_name} ; /api/racks/{id}/devices/{device_name} ; /api/devices/{id}/devices/{device_name} ;", + "description": "Hierarchal path to desired object.", + "name": "HierarchalPath", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Found. A response body will be returned with a meaningful message." + }, + "404": { + "description": "Not Found. An error message will be returned." + } + } + } + }, + "/{objs}/{IdOrHierarchyName}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "This method will replace the existing data with the JSON\nreceived, thus fully replacing the data. If you do not\nwant to do this, please use PATCH.\nIf no data is effectively changed, an OK will still be returned.", + "produces": [ + "application/json" + ], + "tags": [ + "Objects" + ], + "summary": "Completely update object.", + "operationId": "UpdateObject", + "parameters": [ + { + "type": "string", + "default": "sites", + "description": "Entity (same as category) of the object. Accepted values: sites, domains, buildings, rooms, racks, devices, acs, panels, cabinets, groups, corridors, room-templates, obj-templates, bldg-templates, stray-devices.", + "name": "entity", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "siteA", + "description": "ID or hierarchy name of desired object. For templates the slug is the ID.", + "name": "IdOrHierarchyName", + "in": "path", + "required": true + } + ] + } + } + }, + "definitions": { + "ErrorResponse": { + "type": "object", + "properties": { + "message": { + "description": "Error Response Message\nin: message", + "type": "string", + "x-go-name": "Message" + } + }, + "x-go-package": "ogree-bff/models" + }, + "LoginInput": { + "type": "object", + "properties": { + "email": { + "description": "email\nin: email", + "type": "string", + "x-go-name": "Email" + }, + "password": { + "description": "username\nin: password", + "type": "string", + "x-go-name": "Password" + } + }, + "x-go-package": "ogree-bff/models" + }, + "SuccessLogin": { + "type": "object", + "properties": { + "token": { + "type": "string", + "x-go-name": "Token" + } + }, + "x-go-package": "ogree-bff/models" + }, + "SuccessResponse": { + "type": "object", + "properties": { + "message": { + "description": "Error Response Message\nin: message", + "type": "string", + "x-go-name": "Message" + } + }, + "x-go-package": "ogree-bff/models" + } + }, + "securityDefinitions": { + "Bearer": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + } +} \ No newline at end of file diff --git a/BFF/utils/token/token.go b/BFF/utils/token/token.go new file mode 100644 index 000000000..e3805b145 --- /dev/null +++ b/BFF/utils/token/token.go @@ -0,0 +1,80 @@ +package token + +import ( + "fmt" + "os" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + jwt "github.com/dgrijalva/jwt-go" +) + +func GenerateToken(user_id uint) (string, error) { + /* + token_lifespan,err := strconv.Atoi(os.Getenv("TOKEN_HOUR_LIFESPAN")) + + if err != nil { + return "",err + } + */ + token_lifespan := 24 + claims := jwt.MapClaims{} + claims["authorized"] = true + claims["user_id"] = user_id + claims["exp"] = time.Now().Add(time.Hour * time.Duration(token_lifespan)).Unix() + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + return token.SignedString([]byte(os.Getenv("BFF_SECRET"))) + +} + +func TokenValid(c *gin.Context) error { + tokenString := ExtractToken(c) + _, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) + } + return []byte(os.Getenv("BFF_SECRET")), nil + }) + if err != nil { + return err + } + return nil +} + +func ExtractToken(c *gin.Context) string { + token := c.Query("token") + if token != "" { + return token + } + bearerToken := c.Request.Header.Get("Authorization") + if len(strings.Split(bearerToken, " ")) == 2 { + return strings.Split(bearerToken, " ")[1] + } + return "" +} + +func ExtractTokenID(c *gin.Context) (uint, error) { + + tokenString := ExtractToken(c) + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) + } + return []byte(os.Getenv("BFF_SECRET")), nil + }) + if err != nil { + return 0, err + } + claims, ok := token.Claims.(jwt.MapClaims) + if ok && token.Valid { + uid, err := strconv.ParseUint(fmt.Sprintf("%.0f", claims["user_id"]), 10, 32) + if err != nil { + return 0, err + } + return uint(uid), nil + } + return 0, nil +} \ No newline at end of file diff --git a/CLI/ast.go b/CLI/ast.go index 5939d8a44..4578a5dfb 100644 --- a/CLI/ast.go +++ b/CLI/ast.go @@ -368,6 +368,36 @@ func (n *getObjectNode) execute() (interface{}, error) { return obj, nil } +type getPNode struct { + path node + filter node +} + +func (n *getPNode) execute() (interface{}, error) { + val, err := n.path.execute() + if err != nil { + return nil, err + } + path, ok := val.(string) + if !ok { + return nil, fmt.Errorf("Object path should be a string") + } + val ,err = n.filter.execute() + if err != nil { + return nil, err + } + filter, ok := val.(string) + if !ok { + return nil, fmt.Errorf("Object filter should be a string") + } + obj,err := cmd.GetDevicesInfo(path,filter) + if err != nil { + return nil, err + } + cmd.DisplayObject(obj) + return obj,nil +} + type selectObjectNode struct { path node } diff --git a/CLI/controllers/commandController.go b/CLI/controllers/commandController.go index f78a6d3a7..c24671a61 100755 --- a/CLI/controllers/commandController.go +++ b/CLI/controllers/commandController.go @@ -10,6 +10,7 @@ import ( "log" "math/rand" "net/http" + "net/url" "os" "os/exec" pathutil "path" @@ -141,6 +142,70 @@ func PollObject(path string) (map[string]any, error) { func GetObject(path string) (map[string]any, error) { return GetObjectWithChildren(path, 0) } +func ObjectAttributes(path string)(string){ + attr := strings.Split(path, "%"); + if(len(attr) !=2 ){ + return "name" + } + return attr[1] +} +func DevicesAttrs(devices string)(string){ + attr := strings.Split(strings.Split(devices,".")[0],"%"); + if(len(attr) !=2 ){ + return "group_name" + } + return attr[1] +} +func removeFirstOccurrence(input string, pattern string) string { + inputs := strings.Split(input,pattern) + if len(inputs) <3 { + return input + } + return strings.Join(inputs[2:],".") +} +func GetDevicesInfo(path string,filters string) (map[string]any, error) { + + objAttr := ObjectAttributes(path) + devicesAttr := DevicesAttrs(filters) + + path = removeFirstOccurrence(path,"/") + + url:= DevicesUrl(strings.Split(path,"%")[0],objAttr,devicesAttr,filters) + resp, err := RequestAPI("GET", url, nil, http.StatusOK) + if err != nil { + if resp != nil && resp.status == http.StatusNotFound { + device := strings.Split(strings.Split(filters, ".")[0],"%")[0] + return nil, fmt.Errorf(device+" not found") + } + return nil, err + } + obj, ok := resp.body["data"].(map[string]any) + if !ok { + return nil, fmt.Errorf("invalid response from API on GET %s", url) + } + return obj, nil +} +func DevicesUrl(path,objAttr,devAttr,filters string) string{ + query := GenerateDeviceQuery(filters) + device := strings.Split(strings.Split(filters, ".")[0],"%")[0] + return "/api/deviceComp/"+device+"/"+path+"/"+objAttr+"/"+devAttr+query + +} +func GenerateDeviceQuery(filters string) (string) { + queries:= strings.Split(filters,".") + result:="" + for _,query := range queries { + if strings.Contains(query,"="){ + q := strings.Split(query,"=") + if result == "" { + result+= "?"+q[0]+"="+url.QueryEscape(q[1]) + }else{ + result+="&"+q[0]+"="+url.QueryEscape(q[1]) + } + } + } + return result +} func Ls(path string) ([]string, error) { n, err := Tree(path, 1) diff --git a/CLI/other/man/get.md b/CLI/other/man/get.md index e7b865a08..ca6beb6d9 100644 --- a/CLI/other/man/get.md +++ b/CLI/other/man/get.md @@ -1,11 +1,23 @@ -USAGE: get [PATH](optional) +USAGE: get [PATH] [DEVICES](optional) Retrieves object information from API and displays it's information in JSON format. NOTE -If path is not specified then the current path will be used. +If path is not specified then the current path will be used. EXAMPLE get get /Physical/SiteA get ../rack01/device-ibm3 + +OPTION DEVICES: +Retrives devices informations links to an object +It will bind object's name with device's group_name by default +DEVICES take multiple arguments to filter devices + +EXAMPLE + +get /Physical/SiteA/BuildingA/RoomA/Rack01/device-ibm server -> Get all devices where group_name match device-ibm's name +get /Physical/SiteA/BuildingA/RoomA/Rack01/device-ibm%serialNumber server%serial_number -> Binding attributes serialNumber to serial_number +get /Physical/SiteA/BuildingA/RoomA/Rack01/device-ibm server._name=test -> Filter results by device's name + diff --git a/CLI/parser.go b/CLI/parser.go index a1baa07e5..eeb788cfd 100644 --- a/CLI/parser.go +++ b/CLI/parser.go @@ -621,7 +621,14 @@ func (p *parser) parseLs() node { func (p *parser) parseGet() node { defer un(trace(p, "get")) - return &getObjectNode{p.parsePath("")} + path := p.parsePath("") + if p.commandEnd() { + return &getObjectNode{path} + } + + obj := p.parseString("") + return &getPNode{path,obj} + } func (p *parser) parseGetU() node { diff --git a/deploy/docker/.env b/deploy/docker/.env index 864f759b2..ed7fec830 100644 --- a/deploy/docker/.env +++ b/deploy/docker/.env @@ -9,3 +9,19 @@ CUSTOMER_API_PASSWORD=pass123 API_EXTERNALURL=localhost APP_ASSETS_DIR=../../APP/ogree_app/assets/custom IMAGE_TAG=main + +#ARANGODB_ +ARANGO_API_BUILD_DIR=ARANGO_API +ARANGO_API_PORT=8080 +ARANGO_PASS=ogree +ARANGO_USER=root +ARANGO_DB=_system + +#BFF +BFF_BUILD_DIR=BFF +BFF_PORT=8085 +BFF_API_LIST=./bff_api_list.json + +#AUTH +AUTH_SECRET=myAwesomeApiSecret + diff --git a/deploy/docker/bff_api_list.json b/deploy/docker/bff_api_list.json new file mode 100644 index 000000000..139d9e276 --- /dev/null +++ b/deploy/docker/bff_api_list.json @@ -0,0 +1,4 @@ +[ + {"name":"server", "url": "http://ogree-core_arango_api:8080"}, + {"name":"objects", "url": "http://ogree-core_api:3001"} +] \ No newline at end of file diff --git a/deploy/docker/docker-compose.yml b/deploy/docker/docker-compose.yml index 0db00004b..b0dd399f7 100644 --- a/deploy/docker/docker-compose.yml +++ b/deploy/docker/docker-compose.yml @@ -6,15 +6,15 @@ services: image: ogree/api:${IMAGE_TAG} container_name: ${COMPOSE_PROJECT_NAME}_api environment: - - api_port=3001 + - api_port=${API_PORT} - db_host=${COMPOSE_PROJECT_NAME}_db - db_port=27017 - db_user=${COMPOSE_PROJECT_NAME} - db_pass=${CUSTOMER_API_PASSWORD} - db=${COMPOSE_PROJECT_NAME} - - token_password=yourSecretPasswordGoesHere + - token_password=${AUTH_SECRET} ports: - - ${API_PORT}:3001 + - ${API_PORT}:${API_PORT} depends_on: - ogree_db restart: on-failure:10 @@ -47,7 +47,8 @@ services: ports: - ${API_DOC_UI_PORT}:8080 environment: - SWAGGER_JSON_URL: https://raw.githubusercontent.com/ditrit/OGrEE-Core/${IMAGE_TAG}/API/swagger.json + SWAGGER_JSON_URL: https://raw.githubusercontent.com/ditrit/OGrEE-Core/${IMAGE_TAG}/BFF/swagger.json + restart: on-failure:10 ogree_webapp: build: @@ -63,5 +64,59 @@ services: - ogree_api restart: on-failure:10 + arango_db: + image: arangodb/arangodb:3.11.2 + profiles: ["arango"] + container_name: ${COMPOSE_PROJECT_NAME}_arango_db + environment: + - ARANGO_ROOT_PASSWORD=${ARANGO_PASS} + volumes: + - arangodb-persist:/var/lib/arangodb3 + restart: on-failure:10 + + + arango_api: + build: + context: ${CORE_DIR}/${ARANGO_API_BUILD_DIR} + image: arango-api:${IMAGE_TAG} + profiles: ["arango"] + restart: always + container_name: ${COMPOSE_PROJECT_NAME}_arango_api + environment: + - ENV=production + - ARANGO_URL=http://${COMPOSE_PROJECT_NAME}_arango_db:8529 + - ARANGO_DATABASE=${ARANGO_DB} + - ARANGO_USER=${ARANGO_USER} + - ARANGO_PASSWORD=${ARANGO_PASS} + - API_SECRET=${AUTH_SECRET} + - API_PORT=${ARANGO_API_PORT} + healthcheck: + test: ["CMD", "curl","-f","http://${COMPOSE_PROJECT_NAME}_arango_db:8529/_api/version"] + timeout: 30s + interval: 1m + retries: 3 + + ogree-bff: + build: + context: ${CORE_DIR}/${BFF_BUILD_DIR} + image: ogree-bff:${IMAGE_TAG} + profiles: ["arango"] + restart: on-failure:10 + container_name: ${COMPOSE_PROJECT_NAME}_bff + environment: + - ENV=production + - BFF_PORT=${BFF_PORT} + - BFF_SECRET=${AUTH_SECRET} + volumes: + - ${BFF_API_LIST}:/bin/api.json + depends_on: + - ogree_api + - arango_api + ports: + - ${BFF_PORT}:${BFF_PORT} + volumes: - db: \ No newline at end of file + db: + arangodb-persist: + +