diff --git a/README.md b/README.md
index 634af0d..e7672ad 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,11 @@ go run start.go --help
```
to get a list of available parameters and default values
+### Generating Docs
+```bash
+$(go env GOPATH)/bin/swag init --generalInfo start.go --output ./doc/api --parseDependency
+```
+
## Environment variables
| Variable | Description |
diff --git a/configuration/config.go b/configuration/config.go
index ce01828..ace383f 100644
--- a/configuration/config.go
+++ b/configuration/config.go
@@ -20,14 +20,11 @@ package configuration
import (
"flag"
"fmt"
- "io"
"log"
"os"
"sort"
"strings"
- "gopkg.in/yaml.v3"
-
"github.com/zpatrick/go-config"
)
@@ -192,45 +189,3 @@ func InitConfig() error {
}
return nil
}
-
-func remove(arr []GroupedScenario, index int) []GroupedScenario {
- arr[index] = arr[len(arr)-1]
- return arr[:len(arr)-1]
-}
-
-func ReadGroupsFile(path string) error {
- _, err := os.Stat(path)
- if err != nil {
- return err
- }
-
- yamlFile, err := os.Open(path)
- if err != nil {
- return fmt.Errorf("error opening yaml file for groups: %v", err)
- }
- log.Println("Successfully opened yaml groups file", path)
-
- defer yamlFile.Close()
-
- byteValue, _ := io.ReadAll(yamlFile)
-
- err = yaml.Unmarshal(byteValue, &ScenarioGroupMap)
- if err != nil {
- return fmt.Errorf("error unmarshalling yaml into ScenarioGroupMap: %v", err)
- }
-
- for _, group := range ScenarioGroupMap {
- for i, scenario := range group {
- // remove invalid values that might have been introduced by typos
- // (Unmarshal sets default values when it doesn't find a field)
- if scenario.Scenario == 0 {
- log.Println("Removing entry from ScenarioGroupMap, check for typos in the yaml!")
- remove(group, i)
- }
- }
- }
-
- log.Println("ScenarioGroupMap", ScenarioGroupMap)
-
- return nil
-}
diff --git a/database/database.go b/database/database.go
index 79ec7bd..7d1c45f 100644
--- a/database/database.go
+++ b/database/database.go
@@ -105,6 +105,8 @@ func DropTables() {
DBpool.DropTableIfExists(&File{})
DBpool.DropTableIfExists(&Scenario{})
DBpool.DropTableIfExists(&User{})
+ DBpool.DropTableIfExists(&UserGroup{})
+ DBpool.DropTableIfExists(&ScenarioMapping{})
DBpool.DropTableIfExists(&Dashboard{})
DBpool.DropTableIfExists(&Widget{})
DBpool.DropTableIfExists(&Result{})
@@ -120,6 +122,8 @@ func MigrateModels() {
DBpool.AutoMigrate(&File{})
DBpool.AutoMigrate(&Scenario{})
DBpool.AutoMigrate(&User{})
+ DBpool.AutoMigrate(&UserGroup{})
+ DBpool.AutoMigrate(&ScenarioMapping{})
DBpool.AutoMigrate(&Dashboard{})
DBpool.AutoMigrate(&Widget{})
DBpool.AutoMigrate(&Result{})
diff --git a/database/models.go b/database/models.go
index 438f451..e8afd05 100644
--- a/database/models.go
+++ b/database/models.go
@@ -48,6 +48,30 @@ type User struct {
Active bool `json:"active" gorm:"default:true"`
// Scenarios to which user has access
Scenarios []*Scenario `json:"-" gorm:"many2many:user_scenarios;"`
+ // Groups of user
+ UserGroups []*UserGroup `json:"-" gorm:"many2many:user_groups_users;"`
+}
+
+// ScenarioMapping data model
+type ScenarioMapping struct {
+ Model
+ ScenarioID uint `json:"scenarioID" ` // Foreign key to Scenario
+ Scenario Scenario `json:"-" gorm:"foreignkey:ScenarioID"`
+ UserGroupID uint `json:"-"` // Foreign key to UserGroup
+ UserGroup UserGroup `json:"-" gorm:"foreignkey:UserGroupID"`
+ // Whether to duplicate Scenario or add users to existing Scenario
+ Duplicate bool `json:"duplicate"`
+}
+
+// UserGroup data model
+type UserGroup struct {
+ Model
+ // Name of user group
+ Name string `json:"name" gorm:"unique;not null"`
+ // Scenarios that belong to the user group
+ ScenarioMappings []ScenarioMapping `json:"scenarioMappings" gorm:"foreignkey:UserGroupID"`
+ // Users that belong to the user group
+ Users []*User `json:"users" gorm:"many2many:user_groups_users;"`
}
// Scenario data model
diff --git a/database/permissions.go b/database/permissions.go
index ec2fb66..30bdab7 100644
--- a/database/permissions.go
+++ b/database/permissions.go
@@ -142,6 +142,35 @@ func CheckSignalPermissions(c *gin.Context, operation CRUD) (bool, Signal) {
}
+func CheckUserGroupPermissions(c *gin.Context, operation CRUD, userGroupIDSource string, usergroupIDBody int) (bool, UserGroup) {
+
+ var usrgrp UserGroup
+
+ err := ValidateRole(c, ModelUserGroup, operation)
+ if err != nil {
+ helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation failed): %v", err.Error()))
+ return false, usrgrp
+ }
+
+ if operation == Create {
+ return true, usrgrp
+ }
+
+ groupID, err := helper.GetIDOfElement(c, "userGroupID", userGroupIDSource, usergroupIDBody)
+ if err != nil {
+ return false, usrgrp
+ }
+
+ db := GetDB()
+ err = db.Preload("ScenarioMappings.Scenario").First(&usrgrp, groupID).Error
+
+ if helper.DBNotFoundError(c, err, strconv.Itoa(groupID), "UserGroup") {
+ return false, usrgrp
+ }
+
+ return true, usrgrp
+}
+
func CheckDashboardPermissions(c *gin.Context, operation CRUD, dabIDSource string, dabIDBody int) (bool, Dashboard) {
var dab Dashboard
diff --git a/database/roles.go b/database/roles.go
index c7d24f6..05ebd03 100644
--- a/database/roles.go
+++ b/database/roles.go
@@ -19,6 +19,7 @@ package database
import (
"fmt"
+
"github.com/gin-gonic/gin"
)
@@ -33,6 +34,7 @@ type ModelName string
const ModelUser = ModelName("user")
const ModelUsers = ModelName("users")
const ModelScenario = ModelName("scenario")
+const ModelUserGroup = ModelName("usergroup")
const ModelInfrastructureComponent = ModelName("ic")
const ModelInfrastructureComponentAction = ModelName("icaction")
const ModelDashboard = ModelName("dashboard")
@@ -73,6 +75,7 @@ var Roles = RoleActions{
ModelUser: crud,
ModelUsers: crud,
ModelScenario: crud,
+ ModelUserGroup: crud,
ModelComponentConfiguration: crud,
ModelInfrastructureComponent: crud,
ModelInfrastructureComponentAction: crud,
@@ -86,6 +89,7 @@ var Roles = RoleActions{
ModelUser: _ru_,
ModelUsers: none,
ModelScenario: crud,
+ ModelUserGroup: _r__,
ModelComponentConfiguration: crud,
ModelInfrastructureComponent: _r__,
ModelInfrastructureComponentAction: _ru_,
@@ -117,6 +121,7 @@ var Roles = RoleActions{
ModelInfrastructureComponentAction: none,
ModelUser: none,
ModelUsers: none,
+ ModelUserGroup: none,
ModelSignal: none,
ModelFile: _r__,
ModelResult: none,
diff --git a/doc/api/docs.go b/doc/api/docs.go
index 31ee603..295dcdb 100644
--- a/doc/api/docs.go
+++ b/doc/api/docs.go
@@ -2702,6 +2702,454 @@ const docTemplate = `{
}
}
},
+ "/usergroups": {
+ "get": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Get all user groups",
+ "operationId": "getUserGroups",
+ "responses": {
+ "200": {
+ "description": "List of user groups",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUserGroups"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ },
+ "post": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Add a user group",
+ "operationId": "addUserGroup",
+ "parameters": [
+ {
+ "description": "User group to be added",
+ "name": "inputUserGroup",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/usergroup.addUserGroupRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "user group that was added",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUserGroup"
+ }
+ },
+ "400": {
+ "description": "Bad request",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ }
+ },
+ "/usergroups/{usergroupID}": {
+ "get": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Get user group by ID",
+ "operationId": "getUserGroup",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "requested user group",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUserGroup"
+ }
+ },
+ "403": {
+ "description": "Access forbidden.",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ },
+ "put": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Update a user group",
+ "operationId": "updateUserGroup",
+ "parameters": [
+ {
+ "description": "User group to be updated",
+ "name": "inputUserGroup",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/usergroup.updateUserGroupRequest"
+ }
+ },
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "User group that was updated",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUserGroup"
+ }
+ },
+ "400": {
+ "description": "Bad request",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ },
+ "delete": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Delete a user group",
+ "operationId": "deleteUserGroup",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "deleted user group",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUserGroup"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ }
+ },
+ "/usergroups/{usergroupID}/user": {
+ "put": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Add a user to a a user group",
+ "operationId": "addUserToUserGroup",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "User name",
+ "name": "username",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "User that was added to user group",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUser"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ },
+ "delete": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Delete a user from a user group",
+ "operationId": "deleteUserFromUserGroup",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "User name",
+ "name": "username",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "User that was deleted from user group",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUser"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ }
+ },
+ "/usergroups/{usergroupID}/users/": {
+ "get": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Get users of a user group",
+ "operationId": "getUserGroupUsers",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Array of users that are in the user group",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUsers"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ }
+ },
"/users": {
"get": {
"security": [
@@ -3330,6 +3778,12 @@ const docTemplate = `{
"api.ResponseUser": {
"type": "object"
},
+ "api.ResponseUserGroup": {
+ "type": "object"
+ },
+ "api.ResponseUserGroups": {
+ "type": "object"
+ },
"api.ResponseUsers": {
"type": "object"
},
@@ -3358,27 +3812,27 @@ const docTemplate = `{
"component_configuration.validNewConfig": {
"type": "object",
"required": [
- "Name",
- "ScenarioID",
- "StartParameters"
+ "name",
+ "scenarioID",
+ "startParameters"
],
"properties": {
- "FileIDs": {
+ "fileIDs": {
"type": "array",
"items": {
"type": "integer"
}
},
- "ICID": {
+ "icid": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "ScenarioID": {
+ "scenarioID": {
"type": "integer"
},
- "StartParameters": {
+ "startParameters": {
"$ref": "#/definitions/postgres.Jsonb"
}
}
@@ -3386,19 +3840,19 @@ const docTemplate = `{
"component_configuration.validUpdatedConfig": {
"type": "object",
"properties": {
- "FileIDs": {
+ "fileIDs": {
"type": "array",
"items": {
"type": "integer"
}
},
- "ICID": {
+ "icid": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "StartParameters": {
+ "startParameters": {
"$ref": "#/definitions/postgres.Jsonb"
}
}
@@ -3520,21 +3974,21 @@ const docTemplate = `{
"dashboard.validNewDashboard": {
"type": "object",
"required": [
- "Grid",
- "Name",
- "ScenarioID"
+ "grid",
+ "name",
+ "scenarioID"
],
"properties": {
- "Grid": {
+ "grid": {
"type": "integer"
},
- "Height": {
+ "height": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "ScenarioID": {
+ "scenarioID": {
"type": "integer"
}
}
@@ -3572,55 +4026,55 @@ const docTemplate = `{
"infrastructure_component.validNewIC": {
"type": "object",
"required": [
- "Category",
- "ManagedExternally",
- "Name",
- "Type"
+ "category",
+ "managedExternally",
+ "name",
+ "type"
],
"properties": {
- "APIURL": {
+ "apiurl": {
"type": "string"
},
- "Category": {
+ "category": {
"type": "string"
},
- "CreateParameterSchema": {
+ "createParameterSchema": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Description": {
+ "description": {
"type": "string"
},
- "Location": {
+ "location": {
"type": "string"
},
- "ManagedExternally": {
+ "managedExternally": {
"type": "boolean"
},
- "Manager": {
+ "manager": {
"type": "string"
},
- "Name": {
+ "name": {
"type": "string"
},
- "StartParameterSchema": {
+ "startParameterSchema": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "State": {
+ "state": {
"type": "string"
},
- "StatusUpdateRaw": {
+ "statusUpdateRaw": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Type": {
+ "type": {
"type": "string"
},
- "UUID": {
- "type": "string"
- },
- "Uptime": {
+ "uptime": {
"type": "number"
},
- "WebsocketURL": {
+ "uuid": {
+ "type": "string"
+ },
+ "websocketURL": {
"type": "string"
}
}
@@ -3628,46 +4082,46 @@ const docTemplate = `{
"infrastructure_component.validUpdatedIC": {
"type": "object",
"properties": {
- "APIURL": {
+ "apiurl": {
"type": "string"
},
- "Category": {
+ "category": {
"type": "string"
},
- "CreateParameterSchema": {
+ "createParameterSchema": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Description": {
+ "description": {
"type": "string"
},
- "Location": {
+ "location": {
"type": "string"
},
- "Manager": {
+ "manager": {
"type": "string"
},
- "Name": {
+ "name": {
"type": "string"
},
- "StartParameterSchema": {
+ "startParameterSchema": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "State": {
+ "state": {
"type": "string"
},
- "StatusUpdateRaw": {
+ "statusUpdateRaw": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Type": {
- "type": "string"
- },
- "UUID": {
+ "type": {
"type": "string"
},
- "Uptime": {
+ "uptime": {
"type": "number"
},
- "WebsocketURL": {
+ "uuid": {
+ "type": "string"
+ },
+ "websocketURL": {
"type": "string"
}
}
@@ -3702,23 +4156,23 @@ const docTemplate = `{
"result.validNewResult": {
"type": "object",
"required": [
- "ConfigSnapshots",
- "ScenarioID"
+ "configSnapshots",
+ "scenarioID"
],
"properties": {
- "ConfigSnapshots": {
+ "configSnapshots": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Description": {
+ "description": {
"type": "string"
},
- "ResultFileIDs": {
+ "resultFileIDs": {
"type": "array",
"items": {
"type": "integer"
}
},
- "ScenarioID": {
+ "scenarioID": {
"type": "integer"
}
}
@@ -3759,14 +4213,14 @@ const docTemplate = `{
"scenario.validNewScenario": {
"type": "object",
"required": [
- "Name",
- "StartParameters"
+ "name",
+ "startParameters"
],
"properties": {
- "Name": {
+ "name": {
"type": "string"
},
- "StartParameters": {
+ "startParameters": {
"$ref": "#/definitions/postgres.Jsonb"
}
}
@@ -3774,13 +4228,13 @@ const docTemplate = `{
"scenario.validUpdatedScenario": {
"type": "object",
"properties": {
- "IsLocked": {
+ "isLocked": {
"type": "boolean"
},
- "Name": {
+ "name": {
"type": "string"
},
- "StartParameters": {
+ "startParameters": {
"$ref": "#/definitions/postgres.Jsonb"
}
}
@@ -3804,32 +4258,32 @@ const docTemplate = `{
"signal.validNewSignal": {
"type": "object",
"required": [
- "ConfigID",
- "Direction",
- "Index",
- "Name"
+ "configID",
+ "direction",
+ "index",
+ "name"
],
"properties": {
- "ConfigID": {
+ "configID": {
"type": "integer"
},
- "Direction": {
+ "direction": {
"type": "string",
"enum": [
"in",
"out"
]
},
- "Index": {
+ "index": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "ScalingFactor": {
+ "scalingFactor": {
"type": "number"
},
- "Unit": {
+ "unit": {
"type": "string"
}
}
@@ -3837,16 +4291,16 @@ const docTemplate = `{
"signal.validUpdatedSignal": {
"type": "object",
"properties": {
- "Index": {
+ "index": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "ScalingFactor": {
+ "scalingFactor": {
"type": "number"
},
- "Unit": {
+ "unit": {
"type": "string"
}
}
@@ -3862,14 +4316,14 @@ const docTemplate = `{
"user.loginRequest": {
"type": "object",
"required": [
- "Password",
- "Username"
+ "password",
+ "username"
],
"properties": {
- "Password": {
+ "password": {
"type": "string"
},
- "Username": {
+ "username": {
"type": "string"
}
}
@@ -3885,20 +4339,20 @@ const docTemplate = `{
"user.validNewUser": {
"type": "object",
"required": [
- "Mail",
- "Password",
- "Role",
- "Username"
+ "mail",
+ "password",
+ "role",
+ "username"
],
"properties": {
- "Mail": {
+ "mail": {
"type": "string"
},
- "Password": {
+ "password": {
"type": "string",
"minLength": 6
},
- "Role": {
+ "role": {
"type": "string",
"enum": [
"Admin",
@@ -3906,7 +4360,7 @@ const docTemplate = `{
"Guest"
]
},
- "Username": {
+ "username": {
"type": "string",
"minLength": 3
}
@@ -3915,25 +4369,25 @@ const docTemplate = `{
"user.validUpdatedRequest": {
"type": "object",
"properties": {
- "Active": {
+ "active": {
"type": "string",
"enum": [
"yes",
"no"
]
},
- "Mail": {
+ "mail": {
"type": "string"
},
- "OldPassword": {
+ "oldPassword": {
"type": "string",
"minLength": 6
},
- "Password": {
+ "password": {
"type": "string",
"minLength": 6
},
- "Role": {
+ "role": {
"type": "string",
"enum": [
"Admin",
@@ -3941,9 +4395,83 @@ const docTemplate = `{
"Guest"
]
},
- "Username": {
+ "username": {
+ "type": "string",
+ "minLength": 3
+ }
+ }
+ },
+ "usergroup.addUserGroupRequest": {
+ "type": "object",
+ "properties": {
+ "userGroup": {
+ "$ref": "#/definitions/usergroup.validNewUserGroup"
+ }
+ }
+ },
+ "usergroup.updateUserGroupRequest": {
+ "type": "object",
+ "properties": {
+ "userGroup": {
+ "$ref": "#/definitions/usergroup.validUpdatedUserGroup"
+ }
+ }
+ },
+ "usergroup.validNewScenarioMapping": {
+ "type": "object",
+ "required": [
+ "scenarioID"
+ ],
+ "properties": {
+ "duplicate": {
+ "type": "boolean"
+ },
+ "scenarioID": {
+ "type": "integer"
+ }
+ }
+ },
+ "usergroup.validNewUserGroup": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
"type": "string",
+ "maxLength": 100,
"minLength": 3
+ },
+ "scenarioMappings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/usergroup.validNewScenarioMapping"
+ }
+ }
+ }
+ },
+ "usergroup.validUpdatedScenarioMapping": {
+ "type": "object",
+ "properties": {
+ "duplicate": {
+ "type": "boolean"
+ },
+ "scenarioID": {
+ "type": "integer"
+ }
+ }
+ },
+ "usergroup.validUpdatedUserGroup": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "scenarioMappings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/usergroup.validUpdatedScenarioMapping"
+ }
}
}
},
@@ -3966,52 +4494,52 @@ const docTemplate = `{
"widget.validNewWidget": {
"type": "object",
"required": [
- "DashboardID",
- "Height",
- "Type",
- "Width"
+ "dashboardID",
+ "height",
+ "type",
+ "width"
],
"properties": {
- "CustomProperties": {
+ "customProperties": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "DashboardID": {
+ "dashboardID": {
"type": "integer"
},
- "Height": {
+ "height": {
"type": "integer"
},
- "IsLocked": {
+ "isLocked": {
"type": "boolean"
},
- "MinHeight": {
+ "minHeight": {
"type": "integer"
},
- "MinWidth": {
+ "minWidth": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "SignalIDs": {
+ "signalIDs": {
"type": "array",
"items": {
"type": "integer"
}
},
- "Type": {
+ "type": {
"type": "string"
},
- "Width": {
+ "width": {
"type": "integer"
},
- "X": {
+ "x": {
"type": "integer"
},
- "Y": {
+ "y": {
"type": "integer"
},
- "Z": {
+ "z": {
"type": "integer"
}
}
@@ -4019,43 +4547,43 @@ const docTemplate = `{
"widget.validUpdatedWidget": {
"type": "object",
"properties": {
- "CustomProperties": {
+ "customProperties": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Height": {
+ "height": {
"type": "integer"
},
- "IsLocked": {
+ "isLocked": {
"type": "boolean"
},
- "MinHeight": {
+ "minHeight": {
"type": "integer"
},
- "MinWidth": {
+ "minWidth": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "SignalIDs": {
+ "signalIDs": {
"type": "array",
"items": {
"type": "integer"
}
},
- "Type": {
+ "type": {
"type": "string"
},
- "Width": {
+ "width": {
"type": "integer"
},
- "X": {
+ "x": {
"type": "integer"
},
- "Y": {
+ "y": {
"type": "integer"
},
- "Z": {
+ "z": {
"type": "integer"
}
}
diff --git a/doc/api/responses.go b/doc/api/responses.go
index e071660..66f5aaa 100644
--- a/doc/api/responses.go
+++ b/doc/api/responses.go
@@ -60,6 +60,14 @@ type ResponseScenario struct {
scenario database.Scenario
}
+type ResponseUserGroup struct {
+ usergroup database.UserGroup
+}
+
+type ResponseUserGroups struct {
+ usergroup []database.UserGroup
+}
+
type ResponseConfigs struct {
configs []database.ComponentConfiguration
}
diff --git a/doc/api/swagger.json b/doc/api/swagger.json
index 69cd77a..93e2e37 100644
--- a/doc/api/swagger.json
+++ b/doc/api/swagger.json
@@ -2694,6 +2694,454 @@
}
}
},
+ "/usergroups": {
+ "get": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Get all user groups",
+ "operationId": "getUserGroups",
+ "responses": {
+ "200": {
+ "description": "List of user groups",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUserGroups"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ },
+ "post": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Add a user group",
+ "operationId": "addUserGroup",
+ "parameters": [
+ {
+ "description": "User group to be added",
+ "name": "inputUserGroup",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/usergroup.addUserGroupRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "user group that was added",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUserGroup"
+ }
+ },
+ "400": {
+ "description": "Bad request",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ }
+ },
+ "/usergroups/{usergroupID}": {
+ "get": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Get user group by ID",
+ "operationId": "getUserGroup",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "requested user group",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUserGroup"
+ }
+ },
+ "403": {
+ "description": "Access forbidden.",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ },
+ "put": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Update a user group",
+ "operationId": "updateUserGroup",
+ "parameters": [
+ {
+ "description": "User group to be updated",
+ "name": "inputUserGroup",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/usergroup.updateUserGroupRequest"
+ }
+ },
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "User group that was updated",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUserGroup"
+ }
+ },
+ "400": {
+ "description": "Bad request",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ },
+ "delete": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Delete a user group",
+ "operationId": "deleteUserGroup",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "deleted user group",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUserGroup"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ }
+ },
+ "/usergroups/{usergroupID}/user": {
+ "put": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Add a user to a a user group",
+ "operationId": "addUserToUserGroup",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "User name",
+ "name": "username",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "User that was added to user group",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUser"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ },
+ "delete": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Delete a user from a user group",
+ "operationId": "deleteUserFromUserGroup",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "User name",
+ "name": "username",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "User that was deleted from user group",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUser"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ }
+ },
+ "/usergroups/{usergroupID}/users/": {
+ "get": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "usergroups"
+ ],
+ "summary": "Get users of a user group",
+ "operationId": "getUserGroupUsers",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "User group ID",
+ "name": "usergroupID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Array of users that are in the user group",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseUsers"
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "schema": {
+ "$ref": "#/definitions/api.ResponseError"
+ }
+ }
+ }
+ }
+ },
"/users": {
"get": {
"security": [
@@ -3322,6 +3770,12 @@
"api.ResponseUser": {
"type": "object"
},
+ "api.ResponseUserGroup": {
+ "type": "object"
+ },
+ "api.ResponseUserGroups": {
+ "type": "object"
+ },
"api.ResponseUsers": {
"type": "object"
},
@@ -3350,27 +3804,27 @@
"component_configuration.validNewConfig": {
"type": "object",
"required": [
- "Name",
- "ScenarioID",
- "StartParameters"
+ "name",
+ "scenarioID",
+ "startParameters"
],
"properties": {
- "FileIDs": {
+ "fileIDs": {
"type": "array",
"items": {
"type": "integer"
}
},
- "ICID": {
+ "icid": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "ScenarioID": {
+ "scenarioID": {
"type": "integer"
},
- "StartParameters": {
+ "startParameters": {
"$ref": "#/definitions/postgres.Jsonb"
}
}
@@ -3378,19 +3832,19 @@
"component_configuration.validUpdatedConfig": {
"type": "object",
"properties": {
- "FileIDs": {
+ "fileIDs": {
"type": "array",
"items": {
"type": "integer"
}
},
- "ICID": {
+ "icid": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "StartParameters": {
+ "startParameters": {
"$ref": "#/definitions/postgres.Jsonb"
}
}
@@ -3512,21 +3966,21 @@
"dashboard.validNewDashboard": {
"type": "object",
"required": [
- "Grid",
- "Name",
- "ScenarioID"
+ "grid",
+ "name",
+ "scenarioID"
],
"properties": {
- "Grid": {
+ "grid": {
"type": "integer"
},
- "Height": {
+ "height": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "ScenarioID": {
+ "scenarioID": {
"type": "integer"
}
}
@@ -3564,55 +4018,55 @@
"infrastructure_component.validNewIC": {
"type": "object",
"required": [
- "Category",
- "ManagedExternally",
- "Name",
- "Type"
+ "category",
+ "managedExternally",
+ "name",
+ "type"
],
"properties": {
- "APIURL": {
+ "apiurl": {
"type": "string"
},
- "Category": {
+ "category": {
"type": "string"
},
- "CreateParameterSchema": {
+ "createParameterSchema": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Description": {
+ "description": {
"type": "string"
},
- "Location": {
+ "location": {
"type": "string"
},
- "ManagedExternally": {
+ "managedExternally": {
"type": "boolean"
},
- "Manager": {
+ "manager": {
"type": "string"
},
- "Name": {
+ "name": {
"type": "string"
},
- "StartParameterSchema": {
+ "startParameterSchema": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "State": {
+ "state": {
"type": "string"
},
- "StatusUpdateRaw": {
+ "statusUpdateRaw": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Type": {
+ "type": {
"type": "string"
},
- "UUID": {
- "type": "string"
- },
- "Uptime": {
+ "uptime": {
"type": "number"
},
- "WebsocketURL": {
+ "uuid": {
+ "type": "string"
+ },
+ "websocketURL": {
"type": "string"
}
}
@@ -3620,46 +4074,46 @@
"infrastructure_component.validUpdatedIC": {
"type": "object",
"properties": {
- "APIURL": {
+ "apiurl": {
"type": "string"
},
- "Category": {
+ "category": {
"type": "string"
},
- "CreateParameterSchema": {
+ "createParameterSchema": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Description": {
+ "description": {
"type": "string"
},
- "Location": {
+ "location": {
"type": "string"
},
- "Manager": {
+ "manager": {
"type": "string"
},
- "Name": {
+ "name": {
"type": "string"
},
- "StartParameterSchema": {
+ "startParameterSchema": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "State": {
+ "state": {
"type": "string"
},
- "StatusUpdateRaw": {
+ "statusUpdateRaw": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Type": {
- "type": "string"
- },
- "UUID": {
+ "type": {
"type": "string"
},
- "Uptime": {
+ "uptime": {
"type": "number"
},
- "WebsocketURL": {
+ "uuid": {
+ "type": "string"
+ },
+ "websocketURL": {
"type": "string"
}
}
@@ -3694,23 +4148,23 @@
"result.validNewResult": {
"type": "object",
"required": [
- "ConfigSnapshots",
- "ScenarioID"
+ "configSnapshots",
+ "scenarioID"
],
"properties": {
- "ConfigSnapshots": {
+ "configSnapshots": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Description": {
+ "description": {
"type": "string"
},
- "ResultFileIDs": {
+ "resultFileIDs": {
"type": "array",
"items": {
"type": "integer"
}
},
- "ScenarioID": {
+ "scenarioID": {
"type": "integer"
}
}
@@ -3751,14 +4205,14 @@
"scenario.validNewScenario": {
"type": "object",
"required": [
- "Name",
- "StartParameters"
+ "name",
+ "startParameters"
],
"properties": {
- "Name": {
+ "name": {
"type": "string"
},
- "StartParameters": {
+ "startParameters": {
"$ref": "#/definitions/postgres.Jsonb"
}
}
@@ -3766,13 +4220,13 @@
"scenario.validUpdatedScenario": {
"type": "object",
"properties": {
- "IsLocked": {
+ "isLocked": {
"type": "boolean"
},
- "Name": {
+ "name": {
"type": "string"
},
- "StartParameters": {
+ "startParameters": {
"$ref": "#/definitions/postgres.Jsonb"
}
}
@@ -3796,32 +4250,32 @@
"signal.validNewSignal": {
"type": "object",
"required": [
- "ConfigID",
- "Direction",
- "Index",
- "Name"
+ "configID",
+ "direction",
+ "index",
+ "name"
],
"properties": {
- "ConfigID": {
+ "configID": {
"type": "integer"
},
- "Direction": {
+ "direction": {
"type": "string",
"enum": [
"in",
"out"
]
},
- "Index": {
+ "index": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "ScalingFactor": {
+ "scalingFactor": {
"type": "number"
},
- "Unit": {
+ "unit": {
"type": "string"
}
}
@@ -3829,16 +4283,16 @@
"signal.validUpdatedSignal": {
"type": "object",
"properties": {
- "Index": {
+ "index": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "ScalingFactor": {
+ "scalingFactor": {
"type": "number"
},
- "Unit": {
+ "unit": {
"type": "string"
}
}
@@ -3854,14 +4308,14 @@
"user.loginRequest": {
"type": "object",
"required": [
- "Password",
- "Username"
+ "password",
+ "username"
],
"properties": {
- "Password": {
+ "password": {
"type": "string"
},
- "Username": {
+ "username": {
"type": "string"
}
}
@@ -3877,20 +4331,20 @@
"user.validNewUser": {
"type": "object",
"required": [
- "Mail",
- "Password",
- "Role",
- "Username"
+ "mail",
+ "password",
+ "role",
+ "username"
],
"properties": {
- "Mail": {
+ "mail": {
"type": "string"
},
- "Password": {
+ "password": {
"type": "string",
"minLength": 6
},
- "Role": {
+ "role": {
"type": "string",
"enum": [
"Admin",
@@ -3898,7 +4352,7 @@
"Guest"
]
},
- "Username": {
+ "username": {
"type": "string",
"minLength": 3
}
@@ -3907,25 +4361,25 @@
"user.validUpdatedRequest": {
"type": "object",
"properties": {
- "Active": {
+ "active": {
"type": "string",
"enum": [
"yes",
"no"
]
},
- "Mail": {
+ "mail": {
"type": "string"
},
- "OldPassword": {
+ "oldPassword": {
"type": "string",
"minLength": 6
},
- "Password": {
+ "password": {
"type": "string",
"minLength": 6
},
- "Role": {
+ "role": {
"type": "string",
"enum": [
"Admin",
@@ -3933,9 +4387,83 @@
"Guest"
]
},
- "Username": {
+ "username": {
+ "type": "string",
+ "minLength": 3
+ }
+ }
+ },
+ "usergroup.addUserGroupRequest": {
+ "type": "object",
+ "properties": {
+ "userGroup": {
+ "$ref": "#/definitions/usergroup.validNewUserGroup"
+ }
+ }
+ },
+ "usergroup.updateUserGroupRequest": {
+ "type": "object",
+ "properties": {
+ "userGroup": {
+ "$ref": "#/definitions/usergroup.validUpdatedUserGroup"
+ }
+ }
+ },
+ "usergroup.validNewScenarioMapping": {
+ "type": "object",
+ "required": [
+ "scenarioID"
+ ],
+ "properties": {
+ "duplicate": {
+ "type": "boolean"
+ },
+ "scenarioID": {
+ "type": "integer"
+ }
+ }
+ },
+ "usergroup.validNewUserGroup": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
"type": "string",
+ "maxLength": 100,
"minLength": 3
+ },
+ "scenarioMappings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/usergroup.validNewScenarioMapping"
+ }
+ }
+ }
+ },
+ "usergroup.validUpdatedScenarioMapping": {
+ "type": "object",
+ "properties": {
+ "duplicate": {
+ "type": "boolean"
+ },
+ "scenarioID": {
+ "type": "integer"
+ }
+ }
+ },
+ "usergroup.validUpdatedUserGroup": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "scenarioMappings": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/usergroup.validUpdatedScenarioMapping"
+ }
}
}
},
@@ -3958,52 +4486,52 @@
"widget.validNewWidget": {
"type": "object",
"required": [
- "DashboardID",
- "Height",
- "Type",
- "Width"
+ "dashboardID",
+ "height",
+ "type",
+ "width"
],
"properties": {
- "CustomProperties": {
+ "customProperties": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "DashboardID": {
+ "dashboardID": {
"type": "integer"
},
- "Height": {
+ "height": {
"type": "integer"
},
- "IsLocked": {
+ "isLocked": {
"type": "boolean"
},
- "MinHeight": {
+ "minHeight": {
"type": "integer"
},
- "MinWidth": {
+ "minWidth": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "SignalIDs": {
+ "signalIDs": {
"type": "array",
"items": {
"type": "integer"
}
},
- "Type": {
+ "type": {
"type": "string"
},
- "Width": {
+ "width": {
"type": "integer"
},
- "X": {
+ "x": {
"type": "integer"
},
- "Y": {
+ "y": {
"type": "integer"
},
- "Z": {
+ "z": {
"type": "integer"
}
}
@@ -4011,43 +4539,43 @@
"widget.validUpdatedWidget": {
"type": "object",
"properties": {
- "CustomProperties": {
+ "customProperties": {
"$ref": "#/definitions/postgres.Jsonb"
},
- "Height": {
+ "height": {
"type": "integer"
},
- "IsLocked": {
+ "isLocked": {
"type": "boolean"
},
- "MinHeight": {
+ "minHeight": {
"type": "integer"
},
- "MinWidth": {
+ "minWidth": {
"type": "integer"
},
- "Name": {
+ "name": {
"type": "string"
},
- "SignalIDs": {
+ "signalIDs": {
"type": "array",
"items": {
"type": "integer"
}
},
- "Type": {
+ "type": {
"type": "string"
},
- "Width": {
+ "width": {
"type": "integer"
},
- "X": {
+ "x": {
"type": "integer"
},
- "Y": {
+ "y": {
"type": "integer"
},
- "Z": {
+ "z": {
"type": "integer"
}
}
diff --git a/doc/api/swagger.yaml b/doc/api/swagger.yaml
index e4d08e1..bca42f0 100644
--- a/doc/api/swagger.yaml
+++ b/doc/api/swagger.yaml
@@ -34,6 +34,10 @@ definitions:
type: object
api.ResponseUser:
type: object
+ api.ResponseUserGroup:
+ type: object
+ api.ResponseUserGroups:
+ type: object
api.ResponseUsers:
type: object
api.ResponseWidget:
@@ -52,34 +56,34 @@ definitions:
type: object
component_configuration.validNewConfig:
properties:
- FileIDs:
+ fileIDs:
items:
type: integer
type: array
- ICID:
+ icid:
type: integer
- Name:
+ name:
type: string
- ScenarioID:
+ scenarioID:
type: integer
- StartParameters:
+ startParameters:
$ref: '#/definitions/postgres.Jsonb'
required:
- - Name
- - ScenarioID
- - StartParameters
+ - name
+ - scenarioID
+ - startParameters
type: object
component_configuration.validUpdatedConfig:
properties:
- FileIDs:
+ fileIDs:
items:
type: integer
type: array
- ICID:
+ icid:
type: integer
- Name:
+ name:
type: string
- StartParameters:
+ startParameters:
$ref: '#/definitions/postgres.Jsonb'
type: object
config.Authentication:
@@ -157,18 +161,18 @@ definitions:
type: object
dashboard.validNewDashboard:
properties:
- Grid:
+ grid:
type: integer
- Height:
+ height:
type: integer
- Name:
+ name:
type: string
- ScenarioID:
+ scenarioID:
type: integer
required:
- - Grid
- - Name
- - ScenarioID
+ - grid
+ - name
+ - scenarioID
type: object
dashboard.validUpdatedDashboard:
properties:
@@ -191,71 +195,71 @@ definitions:
type: object
infrastructure_component.validNewIC:
properties:
- APIURL:
+ apiurl:
type: string
- Category:
+ category:
type: string
- CreateParameterSchema:
+ createParameterSchema:
$ref: '#/definitions/postgres.Jsonb'
- Description:
+ description:
type: string
- Location:
+ location:
type: string
- ManagedExternally:
+ managedExternally:
type: boolean
- Manager:
+ manager:
type: string
- Name:
+ name:
type: string
- StartParameterSchema:
+ startParameterSchema:
$ref: '#/definitions/postgres.Jsonb'
- State:
+ state:
type: string
- StatusUpdateRaw:
+ statusUpdateRaw:
$ref: '#/definitions/postgres.Jsonb'
- Type:
- type: string
- UUID:
+ type:
type: string
- Uptime:
+ uptime:
type: number
- WebsocketURL:
+ uuid:
+ type: string
+ websocketURL:
type: string
required:
- - Category
- - ManagedExternally
- - Name
- - Type
+ - category
+ - managedExternally
+ - name
+ - type
type: object
infrastructure_component.validUpdatedIC:
properties:
- APIURL:
+ apiurl:
type: string
- Category:
+ category:
type: string
- CreateParameterSchema:
+ createParameterSchema:
$ref: '#/definitions/postgres.Jsonb'
- Description:
+ description:
type: string
- Location:
+ location:
type: string
- Manager:
+ manager:
type: string
- Name:
+ name:
type: string
- StartParameterSchema:
+ startParameterSchema:
$ref: '#/definitions/postgres.Jsonb'
- State:
+ state:
type: string
- StatusUpdateRaw:
+ statusUpdateRaw:
$ref: '#/definitions/postgres.Jsonb'
- Type:
+ type:
type: string
- UUID:
- type: string
- Uptime:
+ uptime:
type: number
- WebsocketURL:
+ uuid:
+ type: string
+ websocketURL:
type: string
type: object
postgres.Jsonb:
@@ -277,19 +281,19 @@ definitions:
type: object
result.validNewResult:
properties:
- ConfigSnapshots:
+ configSnapshots:
$ref: '#/definitions/postgres.Jsonb'
- Description:
+ description:
type: string
- ResultFileIDs:
+ resultFileIDs:
items:
type: integer
type: array
- ScenarioID:
+ scenarioID:
type: integer
required:
- - ConfigSnapshots
- - ScenarioID
+ - configSnapshots
+ - scenarioID
type: object
result.validUpdatedResult:
properties:
@@ -314,21 +318,21 @@ definitions:
type: object
scenario.validNewScenario:
properties:
- Name:
+ name:
type: string
- StartParameters:
+ startParameters:
$ref: '#/definitions/postgres.Jsonb'
required:
- - Name
- - StartParameters
+ - name
+ - startParameters
type: object
scenario.validUpdatedScenario:
properties:
- IsLocked:
+ isLocked:
type: boolean
- Name:
+ name:
type: string
- StartParameters:
+ startParameters:
$ref: '#/definitions/postgres.Jsonb'
type: object
signal.addSignalRequest:
@@ -343,36 +347,36 @@ definitions:
type: object
signal.validNewSignal:
properties:
- ConfigID:
+ configID:
type: integer
- Direction:
+ direction:
enum:
- in
- out
type: string
- Index:
+ index:
type: integer
- Name:
+ name:
type: string
- ScalingFactor:
+ scalingFactor:
type: number
- Unit:
+ unit:
type: string
required:
- - ConfigID
- - Direction
- - Index
- - Name
+ - configID
+ - direction
+ - index
+ - name
type: object
signal.validUpdatedSignal:
properties:
- Index:
+ index:
type: integer
- Name:
+ name:
type: string
- ScalingFactor:
+ scalingFactor:
type: number
- Unit:
+ unit:
type: string
type: object
user.addUserRequest:
@@ -382,13 +386,13 @@ definitions:
type: object
user.loginRequest:
properties:
- Password:
+ password:
type: string
- Username:
+ username:
type: string
required:
- - Password
- - Username
+ - password
+ - username
type: object
user.updateUserRequest:
properties:
@@ -397,51 +401,99 @@ definitions:
type: object
user.validNewUser:
properties:
- Mail:
+ mail:
type: string
- Password:
+ password:
minLength: 6
type: string
- Role:
+ role:
enum:
- Admin
- User
- Guest
type: string
- Username:
+ username:
minLength: 3
type: string
required:
- - Mail
- - Password
- - Role
- - Username
+ - mail
+ - password
+ - role
+ - username
type: object
user.validUpdatedRequest:
properties:
- Active:
+ active:
enum:
- "yes"
- "no"
type: string
- Mail:
+ mail:
type: string
- OldPassword:
+ oldPassword:
minLength: 6
type: string
- Password:
+ password:
minLength: 6
type: string
- Role:
+ role:
enum:
- Admin
- User
- Guest
type: string
- Username:
+ username:
minLength: 3
type: string
type: object
+ usergroup.addUserGroupRequest:
+ properties:
+ userGroup:
+ $ref: '#/definitions/usergroup.validNewUserGroup'
+ type: object
+ usergroup.updateUserGroupRequest:
+ properties:
+ userGroup:
+ $ref: '#/definitions/usergroup.validUpdatedUserGroup'
+ type: object
+ usergroup.validNewScenarioMapping:
+ properties:
+ duplicate:
+ type: boolean
+ scenarioID:
+ type: integer
+ required:
+ - scenarioID
+ type: object
+ usergroup.validNewUserGroup:
+ properties:
+ name:
+ maxLength: 100
+ minLength: 3
+ type: string
+ scenarioMappings:
+ items:
+ $ref: '#/definitions/usergroup.validNewScenarioMapping'
+ type: array
+ required:
+ - name
+ type: object
+ usergroup.validUpdatedScenarioMapping:
+ properties:
+ duplicate:
+ type: boolean
+ scenarioID:
+ type: integer
+ type: object
+ usergroup.validUpdatedUserGroup:
+ properties:
+ name:
+ type: string
+ scenarioMappings:
+ items:
+ $ref: '#/definitions/usergroup.validUpdatedScenarioMapping'
+ type: array
+ type: object
widget.addWidgetRequest:
properties:
widget:
@@ -454,67 +506,67 @@ definitions:
type: object
widget.validNewWidget:
properties:
- CustomProperties:
+ customProperties:
$ref: '#/definitions/postgres.Jsonb'
- DashboardID:
+ dashboardID:
type: integer
- Height:
+ height:
type: integer
- IsLocked:
+ isLocked:
type: boolean
- MinHeight:
+ minHeight:
type: integer
- MinWidth:
+ minWidth:
type: integer
- Name:
+ name:
type: string
- SignalIDs:
+ signalIDs:
items:
type: integer
type: array
- Type:
+ type:
type: string
- Width:
+ width:
type: integer
- X:
+ x:
type: integer
- "Y":
+ "y":
type: integer
- Z:
+ z:
type: integer
required:
- - DashboardID
- - Height
- - Type
- - Width
+ - dashboardID
+ - height
+ - type
+ - width
type: object
widget.validUpdatedWidget:
properties:
- CustomProperties:
+ customProperties:
$ref: '#/definitions/postgres.Jsonb'
- Height:
+ height:
type: integer
- IsLocked:
+ isLocked:
type: boolean
- MinHeight:
+ minHeight:
type: integer
- MinWidth:
+ minWidth:
type: integer
- Name:
+ name:
type: string
- SignalIDs:
+ signalIDs:
items:
type: integer
type: array
- Type:
+ type:
type: string
- Width:
+ width:
type: integer
- X:
+ x:
type: integer
- "Y":
+ "y":
type: integer
- Z:
+ z:
type: integer
type: object
info:
@@ -2272,6 +2324,293 @@ paths:
summary: Update a signal
tags:
- signals
+ /usergroups:
+ get:
+ operationId: getUserGroups
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: List of user groups
+ schema:
+ $ref: '#/definitions/api.ResponseUserGroups'
+ "404":
+ description: Not found
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "422":
+ description: Unprocessable entity
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "500":
+ description: Internal server error
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ security:
+ - Bearer: []
+ summary: Get all user groups
+ tags:
+ - usergroups
+ post:
+ consumes:
+ - application/json
+ operationId: addUserGroup
+ parameters:
+ - description: User group to be added
+ in: body
+ name: inputUserGroup
+ required: true
+ schema:
+ $ref: '#/definitions/usergroup.addUserGroupRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: user group that was added
+ schema:
+ $ref: '#/definitions/api.ResponseUserGroup'
+ "400":
+ description: Bad request
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "404":
+ description: Not found
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "422":
+ description: Unprocessable entity
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "500":
+ description: Internal server error
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ security:
+ - Bearer: []
+ summary: Add a user group
+ tags:
+ - usergroups
+ /usergroups/{usergroupID}:
+ delete:
+ operationId: deleteUserGroup
+ parameters:
+ - description: User group ID
+ in: path
+ name: usergroupID
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: deleted user group
+ schema:
+ $ref: '#/definitions/api.ResponseUserGroup'
+ "404":
+ description: Not found
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "422":
+ description: Unprocessable entity
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "500":
+ description: Internal server error
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ security:
+ - Bearer: []
+ summary: Delete a user group
+ tags:
+ - usergroups
+ get:
+ operationId: getUserGroup
+ parameters:
+ - description: User group ID
+ in: path
+ name: usergroupID
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: requested user group
+ schema:
+ $ref: '#/definitions/api.ResponseUserGroup'
+ "403":
+ description: Access forbidden.
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "404":
+ description: Not found
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "422":
+ description: Unprocessable entity
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "500":
+ description: Internal server error
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ security:
+ - Bearer: []
+ summary: Get user group by ID
+ tags:
+ - usergroups
+ put:
+ consumes:
+ - application/json
+ operationId: updateUserGroup
+ parameters:
+ - description: User group to be updated
+ in: body
+ name: inputUserGroup
+ required: true
+ schema:
+ $ref: '#/definitions/usergroup.updateUserGroupRequest'
+ - description: User group ID
+ in: path
+ name: usergroupID
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: User group that was updated
+ schema:
+ $ref: '#/definitions/api.ResponseUserGroup'
+ "400":
+ description: Bad request
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "404":
+ description: Not found
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "422":
+ description: Unprocessable entity
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "500":
+ description: Internal server error
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ security:
+ - Bearer: []
+ summary: Update a user group
+ tags:
+ - usergroups
+ /usergroups/{usergroupID}/user:
+ delete:
+ operationId: deleteUserFromUserGroup
+ parameters:
+ - description: User group ID
+ in: path
+ name: usergroupID
+ required: true
+ type: integer
+ - description: User name
+ in: query
+ name: username
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: User that was deleted from user group
+ schema:
+ $ref: '#/definitions/api.ResponseUser'
+ "404":
+ description: Not found
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "422":
+ description: Unprocessable entity
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "500":
+ description: Internal server error
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ security:
+ - Bearer: []
+ summary: Delete a user from a user group
+ tags:
+ - usergroups
+ put:
+ operationId: addUserToUserGroup
+ parameters:
+ - description: User group ID
+ in: path
+ name: usergroupID
+ required: true
+ type: integer
+ - description: User name
+ in: query
+ name: username
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: User that was added to user group
+ schema:
+ $ref: '#/definitions/api.ResponseUser'
+ "404":
+ description: Not found
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "422":
+ description: Unprocessable entity
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "500":
+ description: Internal server error
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ security:
+ - Bearer: []
+ summary: Add a user to a a user group
+ tags:
+ - usergroups
+ /usergroups/{usergroupID}/users/:
+ get:
+ operationId: getUserGroupUsers
+ parameters:
+ - description: User group ID
+ in: path
+ name: usergroupID
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: Array of users that are in the user group
+ schema:
+ $ref: '#/definitions/api.ResponseUsers'
+ "404":
+ description: Not found
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "422":
+ description: Unprocessable entity
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ "500":
+ description: Internal server error
+ schema:
+ $ref: '#/definitions/api.ResponseError'
+ security:
+ - Bearer: []
+ summary: Get users of a user group
+ tags:
+ - usergroups
/users:
get:
operationId: GetUsers
diff --git a/routes/register.go b/routes/register.go
index 97375a8..35bfc36 100644
--- a/routes/register.go
+++ b/routes/register.go
@@ -42,6 +42,7 @@ import (
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/signal"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/usergroup"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/widget"
"github.com/gin-gonic/gin"
"github.com/zpatrick/go-config"
@@ -83,6 +84,7 @@ func RegisterEndpoints(router *gin.Engine, api *gin.RouterGroup) {
api.Use(user.Authentication())
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
+ usergroup.RegisterUserGroupEndpoints(api.Group("/usergroups"))
component_configuration.RegisterComponentConfigurationEndpoints(api.Group("/configs"))
signal.RegisterSignalEndpoints(api.Group("/signals"))
dashboard.RegisterDashboardEndpoints(api.Group("/dashboards"))
diff --git a/routes/user/authenticate_endpoint.go b/routes/user/authenticate_endpoint.go
index 51fcd1d..61588be 100644
--- a/routes/user/authenticate_endpoint.go
+++ b/routes/user/authenticate_endpoint.go
@@ -276,15 +276,8 @@ func authenticateExternal(c *gin.Context) (User, error) {
continue
}
- duplicateName := fmt.Sprintf("%s %s", so.Name, myUser.Username)
- alreadyDuplicated := isAlreadyDuplicated(duplicateName)
- if alreadyDuplicated {
- log.Printf("Scenario %d already duplicated for user %s", so.ID, myUser.Username)
- return myUser, nil
- }
-
if groupedScenario.Duplicate {
- duplicateScenarioForUser(so, &myUser.User, "")
+ DuplicateScenarioForUser(so, &myUser.User, "")
} else { // add user to scenario
err = db.Model(&so).Association("Users").Append(&(myUser.User)).Error
if err != nil {
@@ -299,11 +292,3 @@ func authenticateExternal(c *gin.Context) (User, error) {
return myUser, nil
}
-
-func isAlreadyDuplicated(duplicatedName string) bool {
- db := database.GetDB()
- var scenarios []database.Scenario
- db.Find(&scenarios, "name = ?", duplicatedName)
-
- return (len(scenarios) > 0)
-}
diff --git a/routes/user/scenario_duplication.go b/routes/user/scenario_duplication.go
index 57b8ecf..4272d5a 100644
--- a/routes/user/scenario_duplication.go
+++ b/routes/user/scenario_duplication.go
@@ -30,8 +30,39 @@ import (
"github.com/jinzhu/gorm/dialects/postgres"
)
-func duplicateScenarioForUser(s database.Scenario, user *database.User, uuidstr string) {
+func IsAlreadyDuplicated(sc *database.Scenario, u *database.User) bool {
+ duplicateName := fmt.Sprintf("%s %s", sc.Name, u.Username)
+ db := database.GetDB()
+ var scenarios []database.Scenario
+ db.Find(&scenarios, "name = ?", duplicateName)
+
+ return (len(scenarios) > 0)
+}
+// check if access of U to SC is exclusively granted by UG
+func IsExclusiveAccess(sc *database.Scenario, u *database.User, ug *database.UserGroup) bool {
+ db := database.GetDB()
+ var ugs []database.UserGroup
+ db.Model(u).Association("UserGroups").Find(&ugs)
+ for _, asc_ug := range ugs {
+ if ug.ID == asc_ug.ID {
+ continue
+ }
+ var sms []database.ScenarioMapping
+ db.Model(&asc_ug).Association("ScenarioMappings").Find(&sms)
+ for _, sm := range sms {
+ if sm.ScenarioID == sc.ID {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+func DuplicateScenarioForUser(s database.Scenario, user *database.User, uuidstr string) {
+ if IsAlreadyDuplicated(&s, user) {
+ return
+ }
// get all component configs of the scenario
db := database.GetDB()
var configs []database.ComponentConfiguration
@@ -124,6 +155,83 @@ func duplicateScenarioForUser(s database.Scenario, user *database.User, uuidstr
}
}
+func RemoveDuplicate(sc *database.Scenario, u *database.User) error {
+ db := database.GetDB()
+
+ var nsc database.Scenario
+ duplicateName := fmt.Sprintf("%s %s", sc.Name, u.Username)
+ err := db.Find(&nsc, "Name = ?", duplicateName).Error
+ if err != nil {
+ return err
+ }
+ var configs []database.ComponentConfiguration
+ err = db.Model(&nsc).Related(&configs, "ComponentConfigurations").Error
+ if err != nil {
+ return err
+ }
+
+ for _, config := range configs {
+ var ic database.InfrastructureComponent
+ err = db.Find(&ic, config.ICID).Error
+ if err != nil {
+ continue
+ }
+ if ic.Type == "kubernetes" && ic.Category == "simulator" && strings.Contains(ic.Name, u.Username) {
+
+ msg := `{"uuid": "` + ic.UUID + `"}`
+
+ type Action struct {
+ Act string `json:"action"`
+ When int64 `json:"when"`
+ Parameters json.RawMessage `json:"parameters,omitempty"`
+ Model json.RawMessage `json:"model,omitempty"`
+ Results json.RawMessage `json:"results,omitempty"`
+ }
+
+ actionCreate := Action{
+ Act: "delete",
+ When: time.Now().Unix(),
+ Parameters: json.RawMessage(msg),
+ }
+
+ payload, err := json.Marshal(actionCreate)
+ if err != nil {
+ continue
+ }
+
+ if session != nil {
+ if session.IsReady {
+ err = session.Send(payload, ic.Manager)
+ if err != nil {
+ continue
+ }
+ err = db.Delete(&ic).Error
+ if err != nil {
+ continue
+ }
+
+ } else {
+ return fmt.Errorf("could not send IC create action, AMQP session is not ready")
+ }
+ } else {
+ return fmt.Errorf("could not send IC create action, AMQP session is nil")
+ }
+ }
+ }
+ err = db.Select("Files", "Dashboards", "ComponentConfigurations", "Results").Delete(&nsc).Error
+ return err
+}
+
+func RemoveAccess(sc *database.Scenario, u *database.User, ug *database.UserGroup) error {
+ if !IsExclusiveAccess(sc, u, ug) {
+ return nil
+ }
+ db := database.GetDB()
+ db.Model(&sc).Association("Users").Delete(&u)
+ err := db.Model(&u).Association("Scenarios").Delete(&sc).Error
+ return err
+}
+
func duplicateScenario(s database.Scenario, icIds map[uint]uint, user *database.User) error {
db := database.GetDB()
@@ -581,7 +689,7 @@ func duplicateIC(ic database.InfrastructureComponent, userName string, uuidstr s
`"category": "` + lastUpdate.Properties.Category + `",` +
`"type": "` + lastUpdate.Properties.Type + `",` +
`"uuid": "` + newUUID + `",` +
- `"jobname": "` + lastUpdate.Properties.Job.MetaData.JobName + `-` + userName + `",` +
+ `"jobname": "` + strings.Replace(strings.ToLower(lastUpdate.Properties.Job.MetaData.JobName), "_", "-", -1) + `-` + strings.Replace(strings.ToLower(userName), "_", "-", -1) + `",` +
`"activeDeadlineSeconds": "` + strconv.Itoa(lastUpdate.Properties.Job.Spec.Active) + `",` +
`"containername": "` + lastUpdate.Properties.Job.Spec.Template.Spec.Containers[0].Name + `-` + userName + `",` +
`"image": "` + lastUpdate.Properties.Job.Spec.Template.Spec.Containers[0].Image + `",` +
diff --git a/routes/user/user_test.go b/routes/user/user_test.go
index a7121ee..30a81a9 100644
--- a/routes/user/user_test.go
+++ b/routes/user/user_test.go
@@ -27,7 +27,6 @@ import (
"net/http"
"net/http/httptest"
"os"
- "strings"
"testing"
"time"
@@ -159,43 +158,6 @@ func TestAuthenticate(t *testing.T) {
}
-func TestUserGroups(t *testing.T) {
- // Create new user
- // (user, email and groups are read from request headers in real case)
- var myUser User
- username := "Fridolin"
- email := "Fridolin@rwth-aachen.de"
- role := "User"
- userGroups := strings.Split("testGroup1,testGroup2", ",")
-
- err := myUser.byUsername(username)
- assert.Error(t, err)
- myUser, err = NewUser(username, "", email, role, true)
- assert.NoError(t, err)
-
- // Read groups file
- err = configuration.ReadGroupsFile("notexisting.yaml")
- assert.Error(t, err)
-
- err = configuration.ReadGroupsFile("../../configuration/groups.yaml")
- assert.NoError(t, err)
-
- // Check whether duplicate flag is saved correctly in configuration
- for _, group := range userGroups {
- if gsarray, ok := configuration.ScenarioGroupMap[group]; ok {
- for _, groupedScenario := range gsarray {
- if group == "testGroup1" && groupedScenario.Scenario == 1 {
- assert.Equal(t, true, groupedScenario.Duplicate)
- } else if group == "testGroup2" && groupedScenario.Scenario == 4 {
- assert.Equal(t, true, groupedScenario.Duplicate)
- } else {
- assert.Equal(t, false, groupedScenario.Duplicate)
- }
- }
- }
- }
-}
-
func TestAuthenticateQueryToken(t *testing.T) {
database.DropTables()
@@ -924,7 +886,7 @@ func TestDuplicateScenarioForUser(t *testing.T) {
err = addFakeIC(uuidDup, session, token)
assert.NoError(t, err)
- duplicateScenarioForUser(originalSo, &myUser.User, uuidDup)
+ DuplicateScenarioForUser(originalSo, &myUser.User, uuidDup)
/*** Check duplicated scenario for correctness ***/
var dplScenarios []database.Scenario
@@ -1125,7 +1087,7 @@ func TestScenarioDuplicationAlreadyDuplicatedIC(t *testing.T) {
}
log.Println("------------------")
- duplicateScenarioForUser(originalSo, &myUser.User, "")
+ DuplicateScenarioForUser(originalSo, &myUser.User, "")
/*** Check duplicated scenario for correctness ***/
var dplScenarios []database.Scenario
diff --git a/routes/usergroup/usergroup_endpoints.go b/routes/usergroup/usergroup_endpoints.go
new file mode 100644
index 0000000..718a056
--- /dev/null
+++ b/routes/usergroup/usergroup_endpoints.go
@@ -0,0 +1,401 @@
+/**
+* This file is part of VILLASweb-backend-go
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*********************************************************************************/
+
+package usergroup
+
+import (
+ "errors"
+ "net/http"
+ "strconv"
+
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
+ "github.com/gin-gonic/gin"
+ "github.com/jinzhu/gorm"
+)
+
+func RegisterUserGroupEndpoints(r *gin.RouterGroup) {
+ r.POST("", addUserGroup)
+ r.PUT("/:userGroupID", updateUserGroup)
+ r.GET("", getUserGroups)
+ r.GET("/:userGroupID", getUserGroup)
+ r.DELETE("/:userGroupID", deleteUserGroup)
+ r.GET("/:userGroupID/users", getUserGroupUsers)
+ r.PUT("/:userGroupID/user", addUserToUserGroup)
+ r.DELETE("/:userGroupID/user", deleteUserFromUserGroup)
+}
+
+// addUserGroup godoc
+// @Summary Add a user group
+// @ID addUserGroup
+// @Accept json
+// @Produce json
+// @Tags usergroups
+// @Success 200 {object} api.ResponseUserGroup "user group that was added"
+// @Failure 400 {object} api.ResponseError "Bad request"
+// @Failure 404 {object} api.ResponseError "Not found"
+// @Failure 422 {object} api.ResponseError "Unprocessable entity"
+// @Failure 500 {object} api.ResponseError "Internal server error"
+// @Param inputUserGroup body usergroup.addUserGroupRequest true "User group to be added"
+// @Router /usergroups [post]
+// @Security Bearer
+func addUserGroup(c *gin.Context) {
+ ok, _ := database.CheckUserGroupPermissions(c, database.Create, "none", -1)
+ if !ok {
+ return
+ }
+
+ var req addUserGroupRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ helper.BadRequestError(c, err.Error())
+ return
+ }
+
+ // Validate the request
+ if err := req.validate(); err != nil {
+ helper.UnprocessableEntityError(c, err.Error())
+ return
+ }
+
+ // Create the new user group from the request
+ newUserGroup := req.createUserGroup()
+ db := database.GetDB()
+ for _, sm := range newUserGroup.ScenarioMappings {
+ var sc database.Scenario
+ if err := db.Find(&sc, "ID = ?", sm.ScenarioID).Error; errors.Is(err, gorm.ErrRecordNotFound) {
+ helper.NotFoundError(c,
+ "Scenario mappings referencing inexistent scenario ID: "+strconv.Itoa(int(sm.ScenarioID)))
+ return
+ }
+ }
+
+ // Save the new user group to the database
+ err := newUserGroup.save()
+ if !helper.DBError(c, err) {
+ c.JSON(http.StatusOK, gin.H{"usergroup": newUserGroup.UserGroup})
+ }
+}
+
+// updateUserGroup godoc
+// @Summary Update a user group
+// @ID updateUserGroup
+// @Tags usergroups
+// @Accept json
+// @Produce json
+// @Success 200 {object} api.ResponseUserGroup "User group that was updated"
+// @Failure 400 {object} api.ResponseError "Bad request"
+// @Failure 404 {object} api.ResponseError "Not found"
+// @Failure 422 {object} api.ResponseError "Unprocessable entity"
+// @Failure 500 {object} api.ResponseError "Internal server error"
+// @Param inputUserGroup body usergroup.updateUserGroupRequest true "User group to be updated"
+// @Param usergroupID path int true "User group ID"
+// @Router /usergroups/{usergroupID} [put]
+// @Security Bearer
+func updateUserGroup(c *gin.Context) {
+ ok, oldUserGroup_r := database.CheckUserGroupPermissions(c, database.Update, "path", -1)
+ if !ok {
+ return
+ }
+
+ var req updateUserGroupRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ helper.BadRequestError(c, err.Error())
+ return
+ }
+
+ if err := req.UserGroup.validate(); err != nil {
+ helper.BadRequestError(c, err.Error())
+ return
+ }
+
+ var oldUserGroup UserGroup
+ oldUserGroup.UserGroup = oldUserGroup_r
+ updatedUserGroup := req.updatedUserGroup(oldUserGroup)
+
+ // update the user group in the database
+ err := oldUserGroup.update(updatedUserGroup, req.UserGroup.ScenarioMappings)
+ if !helper.DBError(c, err) {
+ c.JSON(http.StatusOK, gin.H{"usergroup": updatedUserGroup.UserGroup})
+ }
+}
+
+// getUserGroups godoc
+// @Summary Get all user groups
+// @ID getUserGroups
+// @Produce json
+// @Tags usergroups
+// @Success 200 {object} api.ResponseUserGroups "List of user groups"
+// @Failure 404 {object} api.ResponseError "Not found"
+// @Failure 422 {object} api.ResponseError "Unprocessable entity"
+// @Failure 500 {object} api.ResponseError "Internal server error"
+// @Router /usergroups [get]
+// @Security Bearer
+func getUserGroups(c *gin.Context) {
+
+ err := database.ValidateRole(c, database.ModelUserGroup, database.Read)
+ if err != nil {
+ helper.UnprocessableEntityError(c, err.Error())
+ return
+ }
+
+ db := database.GetDB()
+ var usergroups []database.UserGroup
+ err = db.Preload("ScenarioMappings.Scenario").Order("ID asc").Find(&usergroups).Error
+ if !helper.DBError(c, err) {
+ c.JSON(http.StatusOK, gin.H{"usergroups": usergroups})
+ }
+}
+
+// getUserGroup godoc
+// @Summary Get user group by ID
+// @ID getUserGroup
+// @Produce json
+// @Tags usergroups
+// @Success 200 {object} api.ResponseUserGroup "requested user group"
+// @Failure 403 {object} api.ResponseError "Access forbidden."
+// @Failure 404 {object} api.ResponseError "Not found"
+// @Failure 422 {object} api.ResponseError "Unprocessable entity"
+// @Failure 500 {object} api.ResponseError "Internal server error"
+// @Param usergroupID path int true "User group ID"
+// @Router /usergroups/{usergroupID} [get]
+// @Security Bearer
+func getUserGroup(c *gin.Context) {
+ ok, ug := database.CheckUserGroupPermissions(c, database.Read, "path", -1)
+ if !ok {
+ return
+ }
+
+ c.JSON(http.StatusOK, gin.H{"usergroup": ug})
+}
+
+// deleteUserGroup godoc
+// @Summary Delete a user group
+// @ID deleteUserGroup
+// @Tags usergroups
+// @Produce json
+// @Success 200 {object} api.ResponseUserGroup "deleted user group"
+// @Failure 404 {object} api.ResponseError "Not found"
+// @Failure 422 {object} api.ResponseError "Unprocessable entity"
+// @Failure 500 {object} api.ResponseError "Internal server error"
+// @Param usergroupID path int true "User group ID"
+// @Router /usergroups/{usergroupID} [delete]
+// @Security Bearer
+func deleteUserGroup(c *gin.Context) {
+
+ ok, ug_r := database.CheckUserGroupPermissions(c, database.Delete, "path", -1)
+ if !ok {
+ return
+ }
+
+ var ug UserGroup
+ ug.UserGroup = ug_r
+ users, _, err := ug.getUsers()
+ if helper.DBError(c, err) {
+ return
+ }
+
+ scenarioMappings := ug.ScenarioMappings
+ db := database.GetDB()
+ for _, sm := range scenarioMappings {
+ var sc database.Scenario
+ err = db.Find(&sc, "ID = ?", sm.ScenarioID).Error
+ if err != nil {
+ continue
+ }
+ if sm.Duplicate {
+ for _, u := range users {
+ err = user.RemoveDuplicate(&sc, &u)
+ if err != nil {
+ continue
+ }
+ }
+ } else {
+ for _, u := range users {
+ err = user.RemoveAccess(&sc, &u, &ug_r)
+ if err != nil {
+ continue
+ }
+ }
+ }
+ }
+ // Try to remove user group
+ err = ug.remove()
+ if !helper.DBError(c, err) {
+ c.JSON(http.StatusOK, gin.H{"usergroup": ug})
+ }
+
+}
+
+// getUserGroupUsers godoc
+// @Summary Get users of a user group
+// @ID getUserGroupUsers
+// @Produce json
+// @Tags usergroups
+// @Success 200 {object} api.ResponseUsers "Array of users that are in the user group"
+// @Failure 404 {object} api.ResponseError "Not found"
+// @Failure 422 {object} api.ResponseError "Unprocessable entity"
+// @Failure 500 {object} api.ResponseError "Internal server error"
+// @Param usergroupID path int true "User group ID"
+// @Router /usergroups/{usergroupID}/users/ [get]
+// @Security Bearer
+func getUserGroupUsers(c *gin.Context) {
+
+ ok, ug_r := database.CheckUserGroupPermissions(c, database.Read, "path", -1)
+ if !ok {
+ return
+ }
+
+ var ug UserGroup
+ ug.UserGroup = ug_r
+
+ // Find all users of user group
+ allUsers, _, err := ug.getUsers()
+ if helper.DBError(c, err) {
+ return
+ }
+
+ c.JSON(http.StatusOK, gin.H{"users": allUsers})
+}
+
+// addUserToUserGroup godoc
+// @Summary Add a user to a a user group
+// @ID addUserToUserGroup
+// @Tags usergroups
+// @Produce json
+// @Success 200 {object} api.ResponseUser "User that was added to user group"
+// @Failure 404 {object} api.ResponseError "Not found"
+// @Failure 422 {object} api.ResponseError "Unprocessable entity"
+// @Failure 500 {object} api.ResponseError "Internal server error"
+// @Param usergroupID path int true "User group ID"
+// @Param username query string true "User name"
+// @Router /usergroups/{usergroupID}/user [put]
+// @Security Bearer
+func addUserToUserGroup(c *gin.Context) {
+
+ ok, ug_r := database.CheckUserGroupPermissions(c, database.Update, "path", -1)
+ if !ok {
+ return
+ }
+
+ var ug UserGroup
+ ug.UserGroup = ug_r
+
+ username := c.Request.URL.Query().Get("username")
+ var u database.User
+ db := database.GetDB()
+ err := db.Find(&u, "Username = ?", username).Error
+ if helper.DBNotFoundError(c, err, username, "User") {
+ return
+ }
+
+ if !u.Active {
+ helper.BadRequestError(c, "bad user")
+ return
+ }
+
+ err = ug.addUser(&(u))
+ if helper.DBError(c, err) {
+ return
+ }
+
+ for _, sm := range ug.ScenarioMappings {
+ var s database.Scenario
+ err = db.Find(&s, "ID = ?", sm.ScenarioID).Error
+ if helper.DBNotFoundError(c, err, strconv.Itoa(int(sm.ScenarioID)), "Scenario") {
+ return
+ }
+
+ if sm.Duplicate {
+ // Duplicate scenario
+ user.DuplicateScenarioForUser(s, &u, "")
+ } else {
+ // Add user to scenario
+ err = db.Model(&s).Association("Users").Append(&u).Error
+ if helper.DBError(c, err) {
+ return
+ }
+ }
+ }
+
+ c.JSON(http.StatusOK, gin.H{"user": u})
+}
+
+// deleteUserFromUserGroup godoc
+// @Summary Delete a user from a user group
+// @ID deleteUserFromUserGroup
+// @Tags usergroups
+// @Produce json
+// @Success 200 {object} api.ResponseUser "User that was deleted from user group"
+// @Failure 404 {object} api.ResponseError "Not found"
+// @Failure 422 {object} api.ResponseError "Unprocessable entity"
+// @Failure 500 {object} api.ResponseError "Internal server error"
+// @Param usergroupID path int true "User group ID"
+// @Param username query string true "User name"
+// @Router /usergroups/{usergroupID}/user [delete]
+// @Security Bearer
+func deleteUserFromUserGroup(c *gin.Context) {
+
+ ok, ug_r := database.CheckUserGroupPermissions(c, database.Update, "path", -1)
+ if !ok {
+ return
+ }
+
+ var ug UserGroup
+ ug.UserGroup = ug_r
+
+ username := c.Request.URL.Query().Get("username")
+ var u database.User
+ db := database.GetDB()
+ err := db.Find(&u, "Username = ?", username).Error
+ if helper.DBNotFoundError(c, err, username, "User") {
+ return
+ }
+
+ if !u.Active {
+ helper.BadRequestError(c, "bad user")
+ return
+ }
+
+ for _, sm := range ug.ScenarioMappings {
+
+ var sc database.Scenario
+ err := db.Find(&sc, "ID = ?", sm.ScenarioID).Error
+ if err != nil {
+ continue
+ }
+
+ if sm.Duplicate {
+ err = user.RemoveDuplicate(&sc, &u)
+ if err != nil {
+ continue
+ }
+
+ } else {
+ err = user.RemoveAccess(&sc, &u, &ug.UserGroup)
+ if err != nil {
+ continue
+ }
+ }
+
+ }
+ err = ug.deleteUser(&u)
+ if helper.DBError(c, err) {
+ return
+ }
+ c.JSON(http.StatusOK, gin.H{"usergroup": ug})
+}
diff --git a/routes/usergroup/usergroup_methods.go b/routes/usergroup/usergroup_methods.go
new file mode 100644
index 0000000..6c60bb3
--- /dev/null
+++ b/routes/usergroup/usergroup_methods.go
@@ -0,0 +1,208 @@
+/**
+* This file is part of VILLASweb-backend-go
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*********************************************************************************/
+
+package usergroup
+
+import (
+ "fmt"
+
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
+ "github.com/jinzhu/gorm"
+)
+
+type UserGroup struct {
+ database.UserGroup
+}
+
+type ScenarioMapping struct {
+ database.ScenarioMapping
+}
+
+func (ug *UserGroup) save() error {
+ db := database.GetDB()
+ err := db.Create(ug).Error
+ return err
+}
+
+func (ug *UserGroup) update(updatedUserGroup UserGroup, reqScenarioMappings []validUpdatedScenarioMapping) error {
+ ug.Name = updatedUserGroup.Name
+
+ db := database.GetDB()
+ err := db.Model(ug).Update(updatedUserGroup).Error
+ if err != nil {
+ return err
+ }
+ /*
+ err = db.Model(ug).Updates(database.UserGroup{Name: ug.Name}).Error
+ if err != nil {
+ return err
+ }
+ */
+
+ return ug.updateScenarioMappings(ug.ID, reqScenarioMappings)
+}
+
+func (ug *UserGroup) updateScenarioMappings(groupID uint, reqScenarioMappings []validUpdatedScenarioMapping) error {
+ var oldMappings []database.ScenarioMapping
+ db := database.GetDB()
+ err := db.Where("user_group_id = ?", groupID).Find(&oldMappings).Error
+ if err != nil {
+ return err
+ }
+
+ var users []database.User
+ err = db.Model(ug).Association("Users").Find(&users).Error
+ fmt.Println(users)
+ if err != nil {
+ return err
+ }
+
+ oldMappingsMap := make(map[uint]database.ScenarioMapping)
+ for _, mapping := range oldMappings {
+ oldMappingsMap[mapping.ScenarioID] = mapping
+ }
+
+ // Handle ScenarioMappings (add/update/delete)
+ for _, reqMapping := range reqScenarioMappings {
+ var sc database.Scenario
+ err = db.Find(&sc, "ID = ?", reqMapping.ScenarioID).Error
+ if err != nil {
+ return err
+ }
+ if oldMapping, exists := oldMappingsMap[reqMapping.ScenarioID]; exists {
+ // Update
+ if oldMapping.Duplicate != reqMapping.Duplicate {
+ if reqMapping.Duplicate {
+ for _, u := range users {
+ user.RemoveAccess(&sc, &u, &ug.UserGroup)
+ user.DuplicateScenarioForUser(sc, &u, "")
+ }
+ } else {
+ for _, u := range users {
+ err = user.RemoveDuplicate(&sc, &u)
+ if err != nil {
+ return err
+ }
+ err = db.Model(&sc).Association("Users").Append(&u).Error
+ if err != nil {
+ return err
+ }
+
+ }
+ }
+ }
+ oldMapping.Duplicate = reqMapping.Duplicate
+ err = db.Save(&oldMapping).Error
+ if err != nil {
+ return err
+ }
+ delete(oldMappingsMap, reqMapping.ScenarioID)
+ } else {
+ // Add
+ newMapping := database.ScenarioMapping{
+ ScenarioID: reqMapping.ScenarioID,
+ UserGroupID: groupID,
+ Duplicate: reqMapping.Duplicate,
+ }
+
+ if reqMapping.Duplicate {
+ for _, u := range users {
+ user.DuplicateScenarioForUser(sc, &u, "")
+ }
+ } else {
+ for _, u := range users {
+ err = db.Model(&sc).Association("Users").Append(&u).Error
+ if err != nil {
+ return err
+ }
+
+ }
+ }
+ err = db.Create(&newMapping).Error
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ // Delete old mappings that were not in the request
+ for _, mapping := range oldMappingsMap {
+ var nsc database.Scenario
+ err = db.Find(&nsc, "ID = ?", mapping.ScenarioID).Error
+ if err != nil {
+ return err
+ }
+ if mapping.Duplicate {
+ for _, u := range users {
+ err = user.RemoveDuplicate(&nsc, &u)
+ if err != nil {
+ return err
+ }
+ }
+ } else {
+ for _, u := range users {
+ user.RemoveAccess(&nsc, &u, &ug.UserGroup)
+ }
+ }
+ err = db.Delete(&mapping).Error
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (u *UserGroup) remove() error {
+ db := database.GetDB()
+ err := db.Delete(u).Error
+ return err
+}
+
+func (ug *UserGroup) getUsers() ([]database.User, int, error) {
+ db := database.GetDB()
+ var users []database.User
+ err := db.Order("ID asc").Model(ug).Where("Active = ?", true).Related(&users, "Users").Error
+ return users, len(users), err
+}
+
+func (ug *UserGroup) addUser(u *database.User) error {
+ db := database.GetDB()
+ err := db.Model(ug).Association("Users").Append(u).Error
+ return err
+}
+
+func (ug *UserGroup) deleteUser(deletedUser *database.User) error {
+ db := database.GetDB()
+ no_users := db.Model(ug).Association("Users").Count()
+ if no_users > 0 {
+ // remove user from user group
+ err := db.Model(ug).Association("Users").Delete(&deletedUser).Error
+ if err != nil {
+ return err
+ }
+ // remove user group from user
+ err = db.Model(&deletedUser).Association("UserGroups").Delete(ug).Error
+ if err != nil {
+ return err
+ }
+ } else {
+ return gorm.ErrRecordNotFound
+ }
+
+ return nil
+}
diff --git a/routes/usergroup/usergroup_test.go b/routes/usergroup/usergroup_test.go
new file mode 100644
index 0000000..838f73f
--- /dev/null
+++ b/routes/usergroup/usergroup_test.go
@@ -0,0 +1,532 @@
+/**
+* This file is part of VILLASweb-backend-go
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*********************************************************************************/
+
+package usergroup
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "strconv"
+ "testing"
+
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
+ "github.com/gin-gonic/gin"
+ "github.com/stretchr/testify/assert"
+)
+
+var router *gin.Engine
+
+type ScenarioMappingRequest struct {
+ ScenarioID uint `json:"scenarioID"`
+ Duplicate bool `json:"duplicate"`
+}
+
+type UserGroupRequest struct {
+ Name string `json:"name"`
+ ScenarioMappings []ScenarioMappingRequest `json:"scenarioMappings"`
+}
+
+type UserRequest struct {
+ Username string `json:"username,omitempty"`
+ Password string `json:"password,omitempty"`
+ OldPassword string `json:"oldPassword,omitempty"`
+ Mail string `json:"mail,omitempty"`
+ Role string `json:"role,omitempty"`
+ Active string `json:"active,omitempty"`
+}
+
+var newUserGroupOneMapping = UserGroupRequest{
+ Name: "UserGroup1",
+ ScenarioMappings: []ScenarioMappingRequest{
+ {
+ ScenarioID: 1,
+ Duplicate: false,
+ },
+ },
+}
+
+var newUserGroupTwoMappings = UserGroupRequest{
+ Name: "UserGroup2",
+ ScenarioMappings: []ScenarioMappingRequest{
+ {
+ ScenarioID: 1,
+ Duplicate: false,
+ },
+ {
+ ScenarioID: 2,
+ Duplicate: true,
+ },
+ },
+}
+
+var deleteTestUg = UserGroupRequest{
+ Name: "UserGroup3",
+ ScenarioMappings: []ScenarioMappingRequest{
+ {
+ ScenarioID: 1,
+ Duplicate: false,
+ },
+ {
+ ScenarioID: 2,
+ Duplicate: true,
+ },
+ {
+ ScenarioID: 3,
+ Duplicate: true,
+ },
+ },
+}
+
+var initUpdateTestUg = UserGroupRequest{
+ Name: "UserGroup4",
+ ScenarioMappings: []ScenarioMappingRequest{
+ {
+ ScenarioID: 1,
+ Duplicate: true,
+ },
+ {
+ ScenarioID: 2,
+ Duplicate: false,
+ },
+ {
+ ScenarioID: 3,
+ Duplicate: true,
+ },
+ {
+ ScenarioID: 4,
+ Duplicate: false,
+ },
+ {
+ ScenarioID: 5,
+ Duplicate: true,
+ },
+ {
+ ScenarioID: 6,
+ Duplicate: false,
+ },
+ },
+}
+
+var updateTestUg = UserGroupRequest{
+ Name: "UserGroup4",
+ ScenarioMappings: []ScenarioMappingRequest{
+ {
+ ScenarioID: 1,
+ Duplicate: false,
+ },
+ {
+ ScenarioID: 2,
+ Duplicate: true,
+ },
+ {
+ ScenarioID: 5,
+ Duplicate: true,
+ },
+ {
+ ScenarioID: 6,
+ Duplicate: false,
+ },
+ {
+ ScenarioID: 7,
+ Duplicate: true,
+ },
+ {
+ ScenarioID: 8,
+ Duplicate: false,
+ },
+ },
+}
+
+func TestMain(m *testing.M) {
+ err := configuration.InitConfig()
+ if err != nil {
+ panic(m)
+ }
+
+ err = database.InitDB(configuration.GlobalConfig, true)
+ if err != nil {
+ panic(m)
+ }
+ defer database.DBpool.Close()
+
+ router = gin.Default()
+ api := router.Group("/api/v2")
+
+ user.RegisterAuthenticate(api.Group("/authenticate"))
+ api.Use(user.Authentication())
+ scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
+ // user endpoints required to set user to inactive
+ user.RegisterUserEndpoints(api.Group("/users"))
+ RegisterUserGroupEndpoints(api.Group("/usergroups"))
+
+ os.Exit(m.Run())
+}
+
+func TestAddUserGroup(t *testing.T) {
+
+ database.DropTables()
+ database.MigrateModels()
+ assert.NoError(t, database.AddTestUsers())
+
+ token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
+ assert.NoError(t, err)
+
+ // try to POST with non JSON body
+ // should return a bad request error
+ code, resp, err := helper.TestEndpoint(router, token,
+ "/api/v2/usergroups", "POST", "this is not a JSON")
+ assert.NoError(t, err)
+ assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
+
+ //Test with inexitent scenario
+ code, resp, err = helper.TestEndpoint(router, token, "/api/v2/usergroups",
+ "POST", helper.KeyModels{"usergroup": newUserGroupOneMapping})
+ assert.NoError(t, err)
+ assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
+
+ // Test with valid user group with one scenario mapping
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "scenario1"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "scenario2"}})
+ code, resp, err = helper.TestEndpoint(router, token, "/api/v2/usergroups",
+ "POST", helper.KeyModels{"usergroup": newUserGroupOneMapping})
+ assert.NoError(t, err)
+ assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
+
+ // Test with valid user group and two scenario mappings
+ code, resp, err = helper.TestEndpoint(router, token, "/api/v2/usergroups",
+ "POST", helper.KeyModels{"usergroup": newUserGroupTwoMappings})
+ assert.NoError(t, err)
+ assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
+
+ // Test with valid user group and multiple mappings
+ // Test with invalid user group
+ // Test with invalid user group and one mapping
+ // Test with invalid user group and multiple mappings
+}
+
+func TestAddUserToGroup(t *testing.T) {
+ // Prep DB
+ database.DropTables()
+ database.MigrateModels()
+ adminpw, _ := database.AddAdminUser(configuration.GlobalConfig)
+
+ //Auth
+ token, _ := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
+
+ //Post necessities
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "scenarioNoDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "scenarioDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/usergroups", "POST", helper.KeyModels{"usergroup": newUserGroupTwoMappings})
+
+ for n_users := 0; n_users < 3; n_users++ {
+ //Add user
+ n := strconv.Itoa(n_users + 1)
+ usr := UserRequest{Username: "usr" + n, Password: "legendre" + n, Role: "User", Mail: "usr" + n + "@harmonics.de"}
+ helper.TestEndpoint(router, token, "/api/v2/users", "POST", helper.KeyModels{"user": usr})
+ code, _, err := helper.TestEndpoint(router, token, "/api/v2/usergroups/1/user?username=usr"+n, "PUT", struct{}{})
+ assert.Equal(t, 200, code)
+ assert.NoError(t, err)
+ }
+
+ //get scenarios
+ _, res, _ := helper.TestEndpoint(router, token, "/api/v2/scenarios", "GET", struct{}{})
+ var scenariosMap map[string]([]database.Scenario)
+ json.Unmarshal(res.Bytes(), &scenariosMap)
+ scenarios := scenariosMap["scenarios"]
+
+ //Actual checks
+ assert.Equal(t, 5, len(scenarios))
+
+ for _, v := range scenarios {
+ path := fmt.Sprintf("/api/v2/scenarios/%d/users", v.ID)
+ var usersMap map[string]([]database.User)
+ _, res, _ = helper.TestEndpoint(router, token, path, "GET", struct{}{})
+ json.Unmarshal(res.Bytes(), &usersMap)
+ users := usersMap["users"]
+ switch v.ID {
+ case 1: //no dups
+ assert.Equal(t, "scenarioNoDups", v.Name)
+ assert.Equal(t, 4, len(users))
+ case 2: // with dups
+ assert.Equal(t, "scenarioDups", v.Name)
+ assert.Equal(t, 1, len(users))
+ assert.Equal(t, "admin", users[0].Username)
+ default:
+ usr := "usr" + strconv.Itoa(int(v.ID-2)) // shift ids by the first two scenarios
+ assert.Equal(t, "scenarioDups "+usr, v.Name)
+ assert.Equal(t, 1, len(users))
+ assert.Equal(t, usr, users[0].Username)
+ }
+ }
+
+}
+
+func TestDeleteUserFromGroup(t *testing.T) {
+ // Prep DB
+ database.DropTables()
+ database.MigrateModels()
+ adminpw, _ := database.AddAdminUser(configuration.GlobalConfig)
+
+ //Auth
+ token, _ := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
+
+ //Post necessities
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "scenarioNoDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "scenarioDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/usergroups", "POST", helper.KeyModels{"usergroup": newUserGroupTwoMappings})
+
+ //Add 2 users
+ for n_users := 0; n_users < 2; n_users++ {
+ //Add user
+ n := strconv.Itoa(n_users + 1)
+ usr := UserRequest{Username: "usr" + n, Password: "legendre" + n, Role: "User", Mail: "usr" + n + "@harmonics.de"}
+ helper.TestEndpoint(router, token, "/api/v2/users", "POST", helper.KeyModels{"user": usr})
+ code, _, err := helper.TestEndpoint(router, token, "/api/v2/usergroups/1/user?username=usr"+n, "PUT", struct{}{})
+ assert.Equal(t, 200, code)
+ assert.NoError(t, err)
+ }
+ //we add usr1 to a group that doubles its right of acces to scenario 1
+ helper.TestEndpoint(router, token, "/api/v2/usergroups", "POST", helper.KeyModels{"usergroup": newUserGroupOneMapping})
+ helper.TestEndpoint(router, token, "/api/v2/usergroups/2/user?username=usr1", "PUT", struct{}{})
+
+ //Delete one
+ helper.TestEndpoint(router, token, "/api/v2/usergroups/1/user?username=usr1", "DELETE", struct{}{})
+
+ //get scenarios
+ _, res, _ := helper.TestEndpoint(router, token, "/api/v2/scenarios", "GET", struct{}{})
+ var scenariosMap map[string]([]database.Scenario)
+ json.Unmarshal(res.Bytes(), &scenariosMap)
+ scenarios := scenariosMap["scenarios"]
+
+ //Actual checks
+ assert.Equal(t, 3, len(scenarios))
+ for _, v := range scenarios {
+ path := fmt.Sprintf("/api/v2/scenarios/%d/users", v.ID)
+ var usersMap map[string]([]database.User)
+ _, res, _ = helper.TestEndpoint(router, token, path, "GET", struct{}{})
+ json.Unmarshal(res.Bytes(), &usersMap)
+ users := usersMap["users"]
+ switch v.ID {
+ case 1: //no dups should still contain usr1 through ug2
+ assert.Equal(t, "scenarioNoDups", v.Name)
+ assert.Equal(t, 3, len(users))
+ case 2: // with dups
+ assert.Equal(t, "scenarioDups", v.Name)
+ assert.Equal(t, 1, len(users))
+ assert.Equal(t, "admin", users[0].Username)
+ default: // remaining duped scenario
+ assert.Equal(t, "scenarioDups usr2", v.Name)
+ assert.Equal(t, 1, len(users))
+ assert.Equal(t, "usr2", users[0].Username)
+ }
+ }
+ //Delete from other
+ helper.TestEndpoint(router, token, "/api/v2/usergroups/2/user?username=usr1", "DELETE", struct{}{})
+
+ _, res, _ = helper.TestEndpoint(router, token, "/api/v2/scenarios/1", "GET", struct{}{})
+ var scenarioMap map[string](database.Scenario)
+ json.Unmarshal(res.Bytes(), &scenarioMap)
+ scenario := scenarioMap["scenario"]
+
+ path := fmt.Sprintf("/api/v2/scenarios/%d/users", scenario.ID)
+ var usersMap map[string]([]database.User)
+ _, res, _ = helper.TestEndpoint(router, token, path, "GET", struct{}{})
+ json.Unmarshal(res.Bytes(), &usersMap)
+ users := usersMap["users"]
+
+ assert.Equal(t, 2, len(users))
+ for _, u := range users {
+ assert.NotEqual(t, "usr1", u.Username)
+ }
+}
+
+func TestDeleteUserGroup(t *testing.T) {
+ // Prep DB
+ database.DropTables()
+ database.MigrateModels()
+ adminpw, _ := database.AddAdminUser(configuration.GlobalConfig)
+
+ //Auth
+ token, _ := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
+
+ //Post necessities
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "scenarioNoDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "scenarioDups1"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "scenarioDups2"}})
+ helper.TestEndpoint(router, token, "/api/v2/usergroups", "POST", helper.KeyModels{"usergroup": deleteTestUg})
+
+ //Add 2 users
+ for n_users := 0; n_users < 2; n_users++ {
+ //Add user
+ n := strconv.Itoa(n_users + 1)
+ usr := UserRequest{Username: "usr" + n, Password: "legendre" + n, Role: "User", Mail: "usr" + n + "@harmonics.de"}
+ helper.TestEndpoint(router, token, "/api/v2/users", "POST", helper.KeyModels{"user": usr})
+ helper.TestEndpoint(router, token, "/api/v2/usergroups/1/user?username=usr"+n, "PUT", struct{}{})
+ }
+
+ //we add usr1 to a group that doubles its right of acces to scenario 1
+ helper.TestEndpoint(router, token, "/api/v2/usergroups", "POST", helper.KeyModels{"usergroup": newUserGroupOneMapping})
+ helper.TestEndpoint(router, token, "/api/v2/usergroups/2/user?username=usr1", "PUT", struct{}{})
+
+ //delete usergroup
+ code, _, err := helper.TestEndpoint(router, token, "/api/v2/usergroups/1", "DELETE", struct{}{})
+ assert.Equal(t, 200, code)
+ assert.NoError(t, err)
+
+ //get scenarios
+ _, res, _ := helper.TestEndpoint(router, token, "/api/v2/scenarios", "GET", struct{}{})
+ var scenariosMap map[string]([]database.Scenario)
+ json.Unmarshal(res.Bytes(), &scenariosMap)
+ scenarios := scenariosMap["scenarios"]
+
+ //Actual checks
+ assert.Equal(t, 3, len(scenarios))
+ var exists []string = []string{"scenarioNoDups", "scenarioDups1", "scenarioDups2"}
+ var deleted []string = []string{"scenarioDups1 usr1", "scenarioDups2 usr1", "scenarioDups1 usr2", "scenarioDups2 usr2"}
+ for _, sc := range scenarios {
+ assert.Contains(t, exists, sc.Name)
+ assert.NotContains(t, deleted, sc.Name)
+ if sc.Name == "scenarioNoDups" {
+ _, res, _ = helper.TestEndpoint(router, token, "/api/v2/scenarios/1", "GET", struct{}{})
+ var scenarioMap map[string](database.Scenario)
+ json.Unmarshal(res.Bytes(), &scenarioMap)
+ scenario := scenarioMap["scenario"]
+
+ path := fmt.Sprintf("/api/v2/scenarios/%d/users", scenario.ID)
+ var usersMap map[string]([]database.User)
+ _, res, _ = helper.TestEndpoint(router, token, path, "GET", struct{}{})
+ json.Unmarshal(res.Bytes(), &usersMap)
+ users := usersMap["users"]
+ assert.Equal(t, 2, len(users))
+ }
+ }
+ //Delete from other
+ helper.TestEndpoint(router, token, "/api/v2/usergroups/2/user?username=usr1", "DELETE", struct{}{})
+
+ _, res, _ = helper.TestEndpoint(router, token, "/api/v2/scenarios/1", "GET", struct{}{})
+ var scenarioMap map[string](database.Scenario)
+ json.Unmarshal(res.Bytes(), &scenarioMap)
+ scenario := scenarioMap["scenario"]
+
+ path := fmt.Sprintf("/api/v2/scenarios/%d/users", scenario.ID)
+ var usersMap map[string]([]database.User)
+ _, res, _ = helper.TestEndpoint(router, token, path, "GET", struct{}{})
+ json.Unmarshal(res.Bytes(), &usersMap)
+ users := usersMap["users"]
+
+ assert.Equal(t, 1, len(users))
+ for _, u := range users {
+ assert.NotEqual(t, "usr1", u.Username)
+ assert.NotEqual(t, "usr2", u.Username)
+ }
+}
+
+func TestUpdateUserGroup(t *testing.T) {
+ // Prep DB
+ database.DropTables()
+ database.MigrateModels()
+ adminpw, _ := database.AddAdminUser(configuration.GlobalConfig)
+
+ //Auth
+ token, _ := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
+
+ //Post necessities
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "changeDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "changeNoDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "removeDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "removeNoDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "unchangedDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "unchangedNoDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "addDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/scenarios", "POST", helper.KeyModels{"scenario": database.Scenario{Name: "addNoDups"}})
+ helper.TestEndpoint(router, token, "/api/v2/usergroups", "POST", helper.KeyModels{"usergroup": initUpdateTestUg})
+
+ //Add user
+ usr := UserRequest{Username: "usr1", Password: "legendre1", Role: "User", Mail: "usr1@harmonics.de"}
+ helper.TestEndpoint(router, token, "/api/v2/users", "POST", helper.KeyModels{"user": usr})
+ helper.TestEndpoint(router, token, "/api/v2/usergroups/1/user?username=usr1", "PUT", struct{}{})
+
+ //update group
+ helper.TestEndpoint(router, token, "/api/v2/usergroups/1", "PUT", helper.KeyModels{"usergroup": updateTestUg})
+
+ //get scenarios
+ _, res, _ := helper.TestEndpoint(router, token, "/api/v2/scenarios", "GET", struct{}{})
+ var scenariosRes map[string]([]database.Scenario)
+ json.Unmarshal(res.Bytes(), &scenariosRes)
+ scenarios := scenariosRes["scenarios"]
+ assert.Equal(t, 11, len(scenarios))
+ var scenariosMap map[string](database.Scenario) = make(map[string](database.Scenario))
+ for _, s := range scenarios {
+ scenariosMap[s.Name] = s
+ }
+
+ //scenarios that transformed into/remained/got added as duplicated (6)
+ for _, name := range []string{"addDups", "unchangedDups", "changeNoDups"} {
+ sc, exists := scenariosMap[name]
+ assert.True(t, exists)
+ path := fmt.Sprintf("/api/v2/scenarios/%d/users", sc.ID)
+ var usersMap map[string]([]database.User)
+ _, res, _ = helper.TestEndpoint(router, token, path, "GET", struct{}{})
+ json.Unmarshal(res.Bytes(), &usersMap)
+ users := usersMap["users"]
+ assert.Equal(t, 1, len(users))
+
+ sc, exists = scenariosMap[name+" usr1"]
+ path = fmt.Sprintf("/api/v2/scenarios/%d/users", sc.ID)
+ assert.True(t, exists)
+ _, res, _ = helper.TestEndpoint(router, token, path, "GET", struct{}{})
+ json.Unmarshal(res.Bytes(), &usersMap)
+ users = usersMap["users"]
+ assert.Equal(t, 1, len(users))
+ }
+
+ //scenarios that transformed into/remained/got added as single (+3)
+ for _, name := range []string{"addNoDups", "unchangedNoDups", "changeDups"} {
+ sc, exists := scenariosMap[name]
+ assert.True(t, exists)
+ path := fmt.Sprintf("/api/v2/scenarios/%d/users", sc.ID)
+ var usersMap map[string]([]database.User)
+ _, res, _ = helper.TestEndpoint(router, token, path, "GET", struct{}{})
+ json.Unmarshal(res.Bytes(), &usersMap)
+ users := usersMap["users"]
+ assert.Equal(t, 2, len(users))
+
+ _, exists = scenariosMap[name+" usr1"]
+ assert.False(t, exists)
+ }
+
+ //scenarios that got removed (+2 = 11)
+ for _, name := range []string{"removeDups", "removeNoDups"} {
+ sc, exists := scenariosMap[name]
+ assert.True(t, exists)
+ path := fmt.Sprintf("/api/v2/scenarios/%d/users", sc.ID)
+ var usersMap map[string]([]database.User)
+ _, res, _ = helper.TestEndpoint(router, token, path, "GET", struct{}{})
+ json.Unmarshal(res.Bytes(), &usersMap)
+ users := usersMap["users"]
+ assert.Equal(t, 1, len(users))
+
+ _, exists = scenariosMap[name+" usr1"]
+ assert.False(t, exists)
+ }
+
+}
diff --git a/routes/usergroup/usergroup_validators.go b/routes/usergroup/usergroup_validators.go
new file mode 100644
index 0000000..0186545
--- /dev/null
+++ b/routes/usergroup/usergroup_validators.go
@@ -0,0 +1,94 @@
+/**
+* This file is part of VILLASweb-backend-go
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*********************************************************************************/
+
+package usergroup
+
+import (
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
+ "gopkg.in/go-playground/validator.v9"
+)
+
+var validate *validator.Validate
+
+type validNewUserGroup struct {
+ Name string `form:"name" validate:"required,min=3,max=100"`
+ ScenarioMappings []validNewScenarioMapping `form:"scenarioMappings" validate:"dive"`
+}
+
+type validNewScenarioMapping struct {
+ ScenarioID uint `form:"scenario_id" validate:"required"`
+ Duplicate bool `form:"duplicate" validate:"omitempty"`
+}
+
+type validUpdatedUserGroup struct {
+ Name string `form:"name" validate:"omitempty"`
+ ScenarioMappings []validUpdatedScenarioMapping `form:"scenarioMappings" validate:"omitempty"`
+}
+
+type validUpdatedScenarioMapping struct {
+ ScenarioID uint `form:"scenarioID" validate:"omitempty"`
+ Duplicate bool `form:"duplicate" validate:"omitempty"`
+}
+
+type addUserGroupRequest struct {
+ UserGroup validNewUserGroup `json:"userGroup"`
+}
+
+type updateUserGroupRequest struct {
+ UserGroup validUpdatedUserGroup `json:"userGroup"`
+}
+
+func (r *addUserGroupRequest) validate() error {
+ validate = validator.New()
+ errs := validate.Struct(r)
+ return errs
+}
+
+func (r *validUpdatedUserGroup) validate() error {
+ validate = validator.New()
+ errs := validate.Struct(r)
+ return errs
+}
+
+func (r *addUserGroupRequest) createUserGroup() UserGroup {
+ var ug UserGroup
+ ug.Name = r.UserGroup.Name
+ ug.ScenarioMappings = convertScenarioMappings(r.UserGroup.ScenarioMappings)
+ return ug
+}
+
+func convertScenarioMappings(validMappings []validNewScenarioMapping) []database.ScenarioMapping {
+ scenarioMappings := make([]database.ScenarioMapping, len(validMappings))
+ for i, v := range validMappings {
+ scenarioMappings[i] = database.ScenarioMapping{
+ ScenarioID: v.ScenarioID,
+ Duplicate: v.Duplicate,
+ }
+ }
+ return scenarioMappings
+}
+
+func (r *updateUserGroupRequest) updatedUserGroup(oldUserGroup UserGroup) UserGroup {
+ // Use the old UserGroup as a basis for the updated UserGroup `ug`
+ ug := oldUserGroup
+
+ if r.UserGroup.Name != "string" && r.UserGroup.Name != "" {
+ ug.Name = r.UserGroup.Name
+ }
+
+ return ug
+}
diff --git a/start.go b/start.go
index 48c5690..6767160 100644
--- a/start.go
+++ b/start.go
@@ -18,10 +18,11 @@ package main
import (
"fmt"
- "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
"log"
"time"
+ "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
+
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
@@ -85,17 +86,6 @@ func main() {
log.Fatalf("Error reading port from global configuration: %s, aborting.", err)
}
- gPath, _ := configuration.GlobalConfig.String("groups.path")
-
- if gPath != "" {
- err = configuration.ReadGroupsFile(gPath)
- if err != nil {
- log.Fatalf("Error reading groups YAML file: %s, aborting.", err)
- }
- } else {
- log.Println("WARNING: path to groups yaml file not set, I am not initializing the scenario-groups mapping.")
- }
-
// Init database
err = database.InitDB(configuration.GlobalConfig, dbClear == "true")
if err != nil {