Change of data model: associate files with scenario instead of widgets and component configuration #34

This commit is contained in:
Sonja Happ 2020-05-26 11:27:00 +02:00
parent 0e54b7ba33
commit 8b60430d84
10 changed files with 231 additions and 561 deletions

View file

@ -142,6 +142,8 @@ func TestScenarioAssociations(t *testing.T) {
configB := ComponentConfiguration{}
dashboardA := Dashboard{}
dashboardB := Dashboard{}
fileA := File{}
fileB := File{}
// add scenarios to DB
assert.NoError(t, DBpool.Create(&scenarioA).Error)
@ -159,6 +161,10 @@ func TestScenarioAssociations(t *testing.T) {
assert.NoError(t, DBpool.Create(&dashboardA).Error)
assert.NoError(t, DBpool.Create(&dashboardB).Error)
// add files to DB
assert.NoError(t, DBpool.Create(&fileA).Error)
assert.NoError(t, DBpool.Create(&fileB).Error)
// add many-to-many associations between users and scenarios
// User HM Scenarios, Scenario HM Users (Many-to-Many)
assert.NoError(t, DBpool.Model(&scenarioA).Association("Users").Append(&userA).Error)
@ -166,6 +172,10 @@ func TestScenarioAssociations(t *testing.T) {
assert.NoError(t, DBpool.Model(&scenarioB).Association("Users").Append(&userA).Error)
assert.NoError(t, DBpool.Model(&scenarioB).Association("Users").Append(&userB).Error)
// add Component Configuration has many files associations
assert.NoError(t, DBpool.Model(&scenarioA).Association("Files").Append(&fileA).Error)
assert.NoError(t, DBpool.Model(&scenarioA).Association("Files").Append(&fileB).Error)
// add scenario has many component configurations associations
assert.NoError(t, DBpool.Model(&scenarioA).Association("ComponentConfigurations").Append(&configA).Error)
assert.NoError(t, DBpool.Model(&scenarioA).Association("ComponentConfigurations").Append(&configB).Error)
@ -200,6 +210,14 @@ func TestScenarioAssociations(t *testing.T) {
assert.Fail(t, "Scenario Associations",
"Expected to have %v Dashboards. Has %v.", 2, len(dashboards))
}
// Get files of scenario1
var files []File
assert.NoError(t, DBpool.Model(&scenario1).Related(&files, "Files").Error)
if len(files) != 2 {
assert.Fail(t, "Scenario Associations",
"Expected to have %v Files. Has %v.", 2, len(files))
}
}
func TestICAssociations(t *testing.T) {
@ -249,10 +267,6 @@ func TestComponentConfigurationAssociations(t *testing.T) {
outSignalB := Signal{Direction: "out"}
inSignalA := Signal{Direction: "in"}
inSignalB := Signal{Direction: "in"}
fileA := File{}
fileB := File{}
fileC := File{}
fileD := File{}
icA := InfrastructureComponent{}
icB := InfrastructureComponent{}
@ -266,12 +280,6 @@ func TestComponentConfigurationAssociations(t *testing.T) {
assert.NoError(t, DBpool.Create(&inSignalA).Error)
assert.NoError(t, DBpool.Create(&inSignalB).Error)
// add files to DB
assert.NoError(t, DBpool.Create(&fileA).Error)
assert.NoError(t, DBpool.Create(&fileB).Error)
assert.NoError(t, DBpool.Create(&fileC).Error)
assert.NoError(t, DBpool.Create(&fileD).Error)
// add ICs to DB
assert.NoError(t, DBpool.Create(&icA).Error)
assert.NoError(t, DBpool.Create(&icB).Error)
@ -282,10 +290,6 @@ func TestComponentConfigurationAssociations(t *testing.T) {
assert.NoError(t, DBpool.Model(&configA).Association("OutputMapping").Append(&outSignalA).Error)
assert.NoError(t, DBpool.Model(&configA).Association("OutputMapping").Append(&outSignalB).Error)
// add Component Configuration has many files associations
assert.NoError(t, DBpool.Model(&configA).Association("Files").Append(&fileC).Error)
assert.NoError(t, DBpool.Model(&configA).Association("Files").Append(&fileD).Error)
// associate Component Configurations with IC
assert.NoError(t, DBpool.Model(&icA).Association("ComponentConfigurations").Append(&configA).Error)
assert.NoError(t, DBpool.Model(&icA).Association("ComponentConfigurations").Append(&configB).Error)
@ -306,13 +310,6 @@ func TestComponentConfigurationAssociations(t *testing.T) {
"Expected to have %v Output Signals. Has %v.", 2, len(signals))
}
// Get files of config1
var files []File
assert.NoError(t, DBpool.Model(&config1).Related(&files, "Files").Error)
if len(files) != 2 {
assert.Fail(t, "ComponentConfiguration Associations",
"Expected to have %v Files. Has %v.", 2, len(files))
}
}
func TestDashboardAssociations(t *testing.T) {
@ -358,35 +355,13 @@ func TestWidgetAssociations(t *testing.T) {
// create copies of global test data
widgetA := Widget{}
widgetB := Widget{}
fileA := File{}
fileB := File{}
fileC := File{}
fileD := File{}
// add widgets to DB
assert.NoError(t, DBpool.Create(&widgetA).Error)
assert.NoError(t, DBpool.Create(&widgetB).Error)
// add files to DB
assert.NoError(t, DBpool.Create(&fileA).Error)
assert.NoError(t, DBpool.Create(&fileB).Error)
assert.NoError(t, DBpool.Create(&fileC).Error)
assert.NoError(t, DBpool.Create(&fileD).Error)
// add widget has many files associations to DB
assert.NoError(t, DBpool.Model(&widgetA).Association("Files").Append(&fileA).Error)
assert.NoError(t, DBpool.Model(&widgetA).Association("Files").Append(&fileB).Error)
var widget1 Widget
assert.NoError(t, DBpool.Find(&widget1, 1).Error, fmt.Sprintf("Find Widget with ID=1"))
// Get files of widget
var files []File
assert.NoError(t, DBpool.Model(&widget1).Related(&files, "Files").Error)
if len(files) != 2 {
assert.Fail(t, "Widget Associations",
"Expected to have %v Files. Has %v.", 2, len(files))
}
}
func TestFileAssociations(t *testing.T) {

View file

@ -69,6 +69,8 @@ type Scenario struct {
ComponentConfigurations []ComponentConfiguration `json:"-" gorm:"foreignkey:ScenarioID" `
// Dashboards that belong to the Scenario
Dashboards []Dashboard `json:"-" gorm:"foreignkey:ScenarioID" `
// Files that belong to the Scenario (for example images, models, etc.)
Files []File `json:"-" gorm:"foreignkey:ScenarioID"`
}
// ComponentConfiguration data model
@ -90,8 +92,6 @@ type ComponentConfiguration struct {
OutputMapping []Signal `json:"-" gorm:"foreignkey:ConfigID"`
// Mapping of input signals of the Component Configuration, order of signals is important
InputMapping []Signal `json:"-" gorm:"foreignkey:ConfigID"`
// Files of Component Configuration (can be CIM and other ComponentConfiguration file formats)
Files []File `json:"-" gorm:"foreignkey:ConfigID"`
// Currently selected FileID
SelectedFileID uint `json:"selectedFileID" gorm:"default:0"`
}
@ -178,8 +178,6 @@ type Widget struct {
CustomProperties postgres.Jsonb `json:"customProperties"`
// ID of dashboard to which widget belongs
DashboardID uint `json:"dashboardID"`
// Files that belong to widget (for example images)
Files []File `json:"-" gorm:"foreignkey:WidgetID"`
// IDs of signals that widget uses
SignalIDs pq.Int64Array `json:"signalIDs" gorm:"type:integer[]"`
}
@ -195,10 +193,8 @@ type File struct {
Size uint `json:"size"`
// Last modification time of file
Date string `json:"date"`
// ID of Component Configuration to which file belongs
ConfigID uint `json:"configID"`
// ID of widget to which file belongs
WidgetID uint `json:"widgetID"`
// ID of Scenario to which file belongs
ScenarioID uint `json:"scenarioID"`
// File itself
FileData []byte `json:"-" gorm:"column:FileData"`
}

View file

@ -1,6 +1,6 @@
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag at
// 2020-03-13 17:14:16.841484501 +0100 CET m=+0.100488476
// 2020-05-26 11:22:58.121837163 +0200 CEST m=+0.130733859
package docs
@ -751,7 +751,7 @@ var doc = `{
"tags": [
"files"
],
"summary": "Get all files of a specific component configuration or widget",
"summary": "Get all files of a specific scenario",
"operationId": "getFiles",
"parameters": [
{
@ -761,24 +761,17 @@ var doc = `{
"in": "header",
"required": true
},
{
"type": "string",
"description": "Set to config for files of component configuration, set to widget for files of widget",
"name": "objectType",
"in": "query",
"required": true
},
{
"type": "integer",
"description": "ID of either config or widget of which files are requested",
"name": "objectID",
"description": "Scenario ID",
"name": "scenarioID",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "Files which belong to config or widget",
"description": "Files which belong to scenario",
"schema": {
"type": "object",
"$ref": "#/definitions/docs.ResponseFiles"
@ -822,7 +815,7 @@ var doc = `{
"tags": [
"files"
],
"summary": "Add a file to a specific component config or widget",
"summary": "Add a file to a specific scenario",
"operationId": "addFile",
"parameters": [
{
@ -839,17 +832,10 @@ var doc = `{
"in": "formData",
"required": true
},
{
"type": "string",
"description": "Set to config for files of component config, set to widget for files of widget",
"name": "objectType",
"in": "query",
"required": true
},
{
"type": "integer",
"description": "ID of either config or widget of which files are requested",
"name": "objectID",
"description": "ID of scenario to which file shall be added",
"name": "scenarioID",
"in": "query",
"required": true
}
@ -3248,10 +3234,6 @@ var doc = `{
"database.File": {
"type": "object",
"properties": {
"configID": {
"description": "ID of Component Configuration to which file belongs",
"type": "integer"
},
"date": {
"description": "Last modification time of file",
"type": "string"
@ -3263,6 +3245,10 @@ var doc = `{
"description": "Name of file",
"type": "string"
},
"scenarioID": {
"description": "ID of Scenario to which file belongs",
"type": "integer"
},
"size": {
"description": "Size of file (in byte)",
"type": "integer"
@ -3270,16 +3256,16 @@ var doc = `{
"type": {
"description": "Type of file (MIME type)",
"type": "string"
},
"widgetID": {
"description": "ID of widget to which file belongs",
"type": "integer"
}
}
},
"database.InfrastructureComponent": {
"type": "object",
"properties": {
"category": {
"description": "Category of IC (simulator, gateway, database, etc.)",
"type": "string"
},
"host": {
"description": "Host if the IC",
"type": "string"
@ -3287,8 +3273,8 @@ var doc = `{
"id": {
"type": "integer"
},
"modelType": {
"description": "Model type supported by the IC",
"name": {
"description": "Name of the IC",
"type": "string"
},
"properties": {
@ -3307,6 +3293,10 @@ var doc = `{
"description": "Time of last state update",
"type": "string"
},
"type": {
"description": "Type of IC (RTDS, VILLASnode, RTDS, etc.)",
"type": "string"
},
"uptime": {
"description": "Uptime of the IC",
"type": "integer"
@ -3659,16 +3649,20 @@ var doc = `{
"infrastructure_component.validNewIC": {
"type": "object",
"required": [
"Category",
"Host",
"Name",
"Type",
"Properties",
"UUID"
],
"properties": {
"Category": {
"type": "string"
},
"Host": {
"type": "string"
},
"Type": {
"Name": {
"type": "string"
},
"Properties": {
@ -3677,6 +3671,9 @@ var doc = `{
"State": {
"type": "string"
},
"Type": {
"type": "string"
},
"UUID": {
"type": "string"
}
@ -3685,10 +3682,13 @@ var doc = `{
"infrastructure_component.validUpdatedIC": {
"type": "object",
"properties": {
"Category": {
"type": "string"
},
"Host": {
"type": "string"
},
"Type": {
"Name": {
"type": "string"
},
"Properties": {
@ -3697,6 +3697,9 @@ var doc = `{
"State": {
"type": "string"
},
"Type": {
"type": "string"
},
"UUID": {
"type": "string"
}

View file

@ -736,7 +736,7 @@
"tags": [
"files"
],
"summary": "Get all files of a specific component configuration or widget",
"summary": "Get all files of a specific scenario",
"operationId": "getFiles",
"parameters": [
{
@ -746,24 +746,17 @@
"in": "header",
"required": true
},
{
"type": "string",
"description": "Set to config for files of component configuration, set to widget for files of widget",
"name": "objectType",
"in": "query",
"required": true
},
{
"type": "integer",
"description": "ID of either config or widget of which files are requested",
"name": "objectID",
"description": "Scenario ID",
"name": "scenarioID",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "Files which belong to config or widget",
"description": "Files which belong to scenario",
"schema": {
"type": "object",
"$ref": "#/definitions/docs.ResponseFiles"
@ -807,7 +800,7 @@
"tags": [
"files"
],
"summary": "Add a file to a specific component config or widget",
"summary": "Add a file to a specific scenario",
"operationId": "addFile",
"parameters": [
{
@ -824,17 +817,10 @@
"in": "formData",
"required": true
},
{
"type": "string",
"description": "Set to config for files of component config, set to widget for files of widget",
"name": "objectType",
"in": "query",
"required": true
},
{
"type": "integer",
"description": "ID of either config or widget of which files are requested",
"name": "objectID",
"description": "ID of scenario to which file shall be added",
"name": "scenarioID",
"in": "query",
"required": true
}
@ -3233,10 +3219,6 @@
"database.File": {
"type": "object",
"properties": {
"configID": {
"description": "ID of Component Configuration to which file belongs",
"type": "integer"
},
"date": {
"description": "Last modification time of file",
"type": "string"
@ -3248,6 +3230,10 @@
"description": "Name of file",
"type": "string"
},
"scenarioID": {
"description": "ID of Scenario to which file belongs",
"type": "integer"
},
"size": {
"description": "Size of file (in byte)",
"type": "integer"
@ -3255,16 +3241,16 @@
"type": {
"description": "Type of file (MIME type)",
"type": "string"
},
"widgetID": {
"description": "ID of widget to which file belongs",
"type": "integer"
}
}
},
"database.InfrastructureComponent": {
"type": "object",
"properties": {
"category": {
"description": "Category of IC (simulator, gateway, database, etc.)",
"type": "string"
},
"host": {
"description": "Host if the IC",
"type": "string"
@ -3272,8 +3258,8 @@
"id": {
"type": "integer"
},
"modelType": {
"description": "Model type supported by the IC",
"name": {
"description": "Name of the IC",
"type": "string"
},
"properties": {
@ -3292,6 +3278,10 @@
"description": "Time of last state update",
"type": "string"
},
"type": {
"description": "Type of IC (RTDS, VILLASnode, RTDS, etc.)",
"type": "string"
},
"uptime": {
"description": "Uptime of the IC",
"type": "integer"
@ -3644,16 +3634,20 @@
"infrastructure_component.validNewIC": {
"type": "object",
"required": [
"Category",
"Host",
"Modeltype",
"Properties",
"Name",
"Type",
"UUID"
],
"properties": {
"Category": {
"type": "string"
},
"Host": {
"type": "string"
},
"Modeltype": {
"Name": {
"type": "string"
},
"Properties": {
@ -3662,6 +3656,9 @@
"State": {
"type": "string"
},
"Type": {
"type": "string"
},
"UUID": {
"type": "string"
}
@ -3670,10 +3667,13 @@
"infrastructure_component.validUpdatedIC": {
"type": "object",
"properties": {
"Category": {
"type": "string"
},
"Host": {
"type": "string"
},
"Modeltype": {
"Name": {
"type": "string"
},
"Properties": {
@ -3682,6 +3682,9 @@
"State": {
"type": "string"
},
"Type": {
"type": "string"
},
"UUID": {
"type": "string"
}

View file

@ -115,9 +115,6 @@ definitions:
type: object
database.File:
properties:
configID:
description: ID of Component Configuration to which file belongs
type: integer
date:
description: Last modification time of file
type: string
@ -126,25 +123,28 @@ definitions:
name:
description: Name of file
type: string
scenarioID:
description: ID of Scenario to which file belongs
type: integer
size:
description: Size of file (in byte)
type: integer
type:
description: Type of file (MIME type)
type: string
widgetID:
description: ID of widget to which file belongs
type: integer
type: object
database.InfrastructureComponent:
properties:
category:
description: Category of IC (simulator, gateway, database, etc.)
type: string
host:
description: Host if the IC
type: string
id:
type: integer
modelType:
description: Model type supported by the IC
name:
description: Name of the IC
type: string
properties:
description: Properties of IC as JSON string
@ -158,6 +158,9 @@ definitions:
stateUpdateAt:
description: Time of last state update
type: string
type:
description: Type of IC (RTDS, VILLASnode, RTDS, etc.)
type: string
uptime:
description: Uptime of the IC
type: integer
@ -398,32 +401,41 @@ definitions:
type: object
infrastructure_component.validNewIC:
properties:
Category:
type: string
Host:
type: string
Modeltype:
Name:
type: string
Properties:
type: string
State:
type: string
Type:
type: string
UUID:
type: string
required:
- Category
- Host
- Modeltype
- Properties
- Name
- Type
- UUID
type: object
infrastructure_component.validUpdatedIC:
properties:
Category:
type: string
Host:
type: string
Modeltype:
Name:
type: string
Properties:
type: string
State:
type: string
Type:
type: string
UUID:
type: string
type: object
@ -1150,22 +1162,16 @@ paths:
name: Authorization
required: true
type: string
- description: Set to config for files of component configuration, set to widget
for files of widget
- description: Scenario ID
in: query
name: objectType
required: true
type: string
- description: ID of either config or widget of which files are requested
in: query
name: objectID
name: scenarioID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: Files which belong to config or widget
description: Files which belong to scenario
schema:
$ref: '#/definitions/docs.ResponseFiles'
type: object
@ -1184,7 +1190,7 @@ paths:
schema:
$ref: '#/definitions/docs.ResponseError'
type: object
summary: Get all files of a specific component configuration or widget
summary: Get all files of a specific scenario
tags:
- files
post:
@ -1207,15 +1213,9 @@ paths:
name: inputFile
required: true
type: file
- description: Set to config for files of component config, set to widget for
files of widget
- description: ID of scenario to which file shall be added
in: query
name: objectType
required: true
type: string
- description: ID of either config or widget of which files are requested
in: query
name: objectID
name: scenarioID
required: true
type: integer
produces:
@ -1246,7 +1246,7 @@ paths:
schema:
$ref: '#/definitions/docs.ResponseError'
type: object
summary: Add a file to a specific component config or widget
summary: Add a file to a specific scenario
tags:
- files
/files/{fileID}:

View file

@ -24,14 +24,11 @@ package file
import (
"fmt"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
"net/http"
"strconv"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
"github.com/gin-gonic/gin"
"net/http"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/component-configuration"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/widget"
)
func RegisterFileEndpoints(r *gin.RouterGroup) {
@ -43,57 +40,28 @@ func RegisterFileEndpoints(r *gin.RouterGroup) {
}
// getFiles godoc
// @Summary Get all files of a specific component configuration or widget
// @Summary Get all files of a specific scenario
// @ID getFiles
// @Tags files
// @Produce json
// @Success 200 {object} docs.ResponseFiles "Files which belong to config or widget"
// @Success 200 {object} docs.ResponseFiles "Files which belong to scenario"
// @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param Authorization header string true "Authorization token"
// @Param objectType query string true "Set to config for files of component configuration, set to widget for files of widget"
// @Param objectID query int true "ID of either config or widget of which files are requested"
// @Param scenarioID query int true "Scenario ID"
// @Router /files [get]
func getFiles(c *gin.Context) {
var err error
objectType := c.Request.URL.Query().Get("objectType")
if objectType != "config" && objectType != "widget" {
helper.BadRequestError(c, fmt.Sprintf("Object type not supported for files: %s", objectType))
return
}
objectID_s := c.Request.URL.Query().Get("objectID")
objectID, err := strconv.Atoi(objectID_s)
if err != nil {
helper.BadRequestError(c, fmt.Sprintf("Error on ID conversion: %s", err.Error()))
return
}
//Check access
var ok bool
var m component_configuration.ComponentConfiguration
var w widget.Widget
if objectType == "config" {
ok, m = component_configuration.CheckPermissions(c, database.Read, "body", objectID)
} else {
ok, w = widget.CheckPermissions(c, database.Read, objectID)
}
ok, so := scenario.CheckPermissions(c, database.Read, "query", -1)
if !ok {
return
}
// get meta data of files
db := database.GetDB()
var files []database.File
if objectType == "config" {
err = db.Order("ID asc").Model(&m).Related(&files, "Files").Error
} else {
err = db.Order("ID asc").Model(&w).Related(&files, "Files").Error
}
err := db.Order("ID asc").Model(so).Related(&files, "Files").Error
if !helper.DBError(c, err) {
c.JSON(http.StatusOK, gin.H{"files": files})
}
@ -101,7 +69,7 @@ func getFiles(c *gin.Context) {
}
// addFile godoc
// @Summary Add a file to a specific component config or widget
// @Summary Add a file to a specific scenario
// @ID addFile
// @Tags files
// @Produce json
@ -118,36 +86,14 @@ func getFiles(c *gin.Context) {
// @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param Authorization header string true "Authorization token"
// @Param inputFile formData file true "File to be uploaded"
// @Param objectType query string true "Set to config for files of component config, set to widget for files of widget"
// @Param objectID query int true "ID of either config or widget of which files are requested"
// @Param scenarioID query int true "ID of scenario to which file shall be added"
// @Router /files [post]
func addFile(c *gin.Context) {
objectType := c.Request.URL.Query().Get("objectType")
if objectType != "config" && objectType != "widget" {
helper.BadRequestError(c, fmt.Sprintf("Object type not supported for files: %s", objectType))
ok, so := scenario.CheckPermissions(c, database.Read, "query", -1)
if !ok {
return
}
objectID_s := c.Request.URL.Query().Get("objectID")
objectID, err := strconv.Atoi(objectID_s)
if err != nil {
helper.BadRequestError(c, fmt.Sprintf("Error on ID conversion: %s", err.Error()))
return
}
// Check access
var ok bool
if objectType == "config" {
ok, _ = component_configuration.CheckPermissions(c, database.Update, "body", objectID)
if !ok {
return
}
} else {
ok, _ = widget.CheckPermissions(c, database.Update, objectID)
if !ok {
return
}
}
// Extract file from POST request form
file_header, err := c.FormFile("file")
@ -157,7 +103,7 @@ func addFile(c *gin.Context) {
}
var newFile File
err = newFile.register(file_header, objectType, uint(objectID))
err = newFile.register(file_header, so.ID)
if !helper.DBError(c, err) {
c.JSON(http.StatusOK, gin.H{"file": newFile.File})
}

View file

@ -22,6 +22,7 @@
package file
import (
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
"github.com/gin-gonic/gin"
"io/ioutil"
"mime/multipart"
@ -31,8 +32,6 @@ import (
"time"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/component-configuration"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/widget"
)
type File struct {
@ -69,37 +68,14 @@ func (f *File) download(c *gin.Context) error {
return nil
}
func (f *File) register(fileHeader *multipart.FileHeader, objectType string, objectID uint) error {
func (f *File) register(fileHeader *multipart.FileHeader, scenarioID uint) error {
// Obtain properties of file
f.Type = fileHeader.Header.Get("Content-Type")
f.Name = filepath.Base(fileHeader.Filename)
//f.Path = filepath.Join(getFolderName(objectType, objectID), f.Name)
f.Size = uint(fileHeader.Size)
f.Date = time.Now().String()
var m component_configuration.ComponentConfiguration
var w widget.Widget
var err error
if objectType == "config" {
// check if config exists
err = m.ByID(objectID)
f.WidgetID = 0
f.ConfigID = objectID
if err != nil {
return err
}
} else {
// check if widget exists
f.WidgetID = objectID
f.ConfigID = 0
err = w.ByID(uint(objectID))
if err != nil {
return err
}
}
f.ScenarioID = scenarioID
// set file data
fileContent, err := fileHeader.Open()
@ -116,22 +92,18 @@ func (f *File) register(fileHeader *multipart.FileHeader, objectType string, obj
return err
}
// Create association to config or widget
if objectType == "config" {
db := database.GetDB()
err := db.Model(&m).Association("Files").Append(f).Error
if err != nil {
return err
}
} else {
db := database.GetDB()
err := db.Model(&w).Association("Files").Append(f).Error
if err != nil {
return err
}
// Create association to scenario
db := database.GetDB()
var so scenario.Scenario
err = so.ByID(scenarioID)
if err != nil {
return err
}
return nil
err = db.Model(&so).Association("Files").Append(f).Error
return err
}
func (f *File) update(fileHeader *multipart.FileHeader) error {
@ -156,32 +128,19 @@ func (f *File) delete() error {
db := database.GetDB()
if f.WidgetID > 0 {
// remove association between file and widget
var w widget.Widget
err := w.ByID(f.WidgetID)
if err != nil {
return err
}
err = db.Model(&w).Association("Files").Delete(f).Error
if err != nil {
return err
}
} else {
// remove association between file and config
var m component_configuration.ComponentConfiguration
err := m.ByID(f.ConfigID)
if err != nil {
return err
}
err = db.Model(&m).Association("Files").Delete(f).Error
if err != nil {
return err
}
// remove association between file and scenario
var so scenario.Scenario
err := so.ByID(f.ScenarioID)
if err != nil {
return err
}
err = db.Model(&so).Association("Files").Delete(f).Error
if err != nil {
return err
}
// delete file from DB
err := db.Delete(f).Error
err = db.Delete(f).Error
return err
}

View file

@ -25,8 +25,7 @@ import (
"fmt"
"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/component-configuration"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/widget"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
"github.com/gin-gonic/gin"
)
@ -50,16 +49,9 @@ func checkPermissions(c *gin.Context, operation database.CRUD) (bool, File) {
return false, f
}
if f.ConfigID > 0 {
ok, _ := component_configuration.CheckPermissions(c, operation, "body", int(f.ConfigID))
if !ok {
return false, f
}
} else {
ok, _ := widget.CheckPermissions(c, operation, int(f.WidgetID))
if !ok {
return false, f
}
ok, _ := scenario.CheckPermissions(c, operation, "body", int(f.ScenarioID))
if !ok {
return false, f
}
return true, f

View file

@ -27,12 +27,8 @@ import (
"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/component-configuration"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/dashboard"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/widget"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm/dialects/postgres"
"github.com/stretchr/testify/assert"
@ -47,72 +43,18 @@ import (
var router *gin.Engine
type ConfigRequest struct {
Name string `json:"name,omitempty"`
ScenarioID uint `json:"scenarioID,omitempty"`
ICID uint `json:"icID,omitempty"`
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
}
type ICRequest struct {
UUID string `json:"uuid,omitempty"`
Host string `json:"host,omitempty"`
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Category string `json:"category,omitempty"`
State string `json:"state,omitempty"`
Properties postgres.Jsonb `json:"properties,omitempty"`
}
type ScenarioRequest struct {
Name string `json:"name,omitempty"`
Running bool `json:"running,omitempty"`
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
}
type DashboardRequest struct {
Name string `json:"name,omitempty"`
Grid int `json:"grid,omitempty"`
ScenarioID uint `json:"scenarioID,omitempty"`
}
type WidgetRequest struct {
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
Width uint `json:"width,omitempty"`
Height uint `json:"height,omitempty"`
MinWidth uint `json:"minWidth,omitempty"`
MinHeight uint `json:"minHeight,omitempty"`
X int `json:"x,omitempty"`
Y int `json:"y,omitempty"`
Z int `json:"z,omitempty"`
DashboardID uint `json:"dashboardID,omitempty"`
IsLocked bool `json:"isLocked,omitempty"`
CustomProperties postgres.Jsonb `json:"customProperties,omitempty"`
}
func addScenarioAndICAndConfigAndDashboardAndWidget() (scenarioID uint, ICID uint, configID uint, dashboardID uint, widgetID uint) {
func addScenario() (scenarioID uint) {
// authenticate as admin
token, _ := helper.AuthenticateForTest(router,
"/api/authenticate", "POST", helper.AdminCredentials)
// POST $newICA
newICA := ICRequest{
UUID: helper.ICA.UUID,
Host: helper.ICA.Host,
Type: helper.ICA.Type,
Name: helper.ICA.Name,
Category: helper.ICA.Category,
State: helper.ICA.State,
Properties: helper.ICA.Properties,
}
_, resp, _ := helper.TestEndpoint(router, token,
"/api/ic", "POST", helper.KeyModels{"ic": newICA})
// Read newIC's ID from the response
newICID, _ := helper.GetResponseID(resp)
// authenticate as normal user
token, _ = helper.AuthenticateForTest(router,
"/api/authenticate", "POST", helper.UserACredentials)
@ -123,63 +65,17 @@ func addScenarioAndICAndConfigAndDashboardAndWidget() (scenarioID uint, ICID uin
Running: helper.ScenarioA.Running,
StartParameters: helper.ScenarioA.StartParameters,
}
_, resp, _ = helper.TestEndpoint(router, token,
_, resp, _ := helper.TestEndpoint(router, token,
"/api/scenarios", "POST", helper.KeyModels{"scenario": newScenario})
// Read newScenario's ID from the response
newScenarioID, _ := helper.GetResponseID(resp)
// POST new component config
newConfig := ConfigRequest{
Name: helper.ConfigA.Name,
ScenarioID: uint(newScenarioID),
ICID: uint(newICID),
StartParameters: helper.ConfigA.StartParameters,
}
_, resp, _ = helper.TestEndpoint(router, token,
"/api/configs", "POST", helper.KeyModels{"config": newConfig})
// Read newConfig's ID from the response
newConfigID, _ := helper.GetResponseID(resp)
// POST new dashboard
newDashboard := DashboardRequest{
Name: helper.DashboardA.Name,
Grid: helper.DashboardA.Grid,
ScenarioID: uint(newScenarioID),
}
_, resp, _ = helper.TestEndpoint(router, token,
"/api/dashboards", "POST", helper.KeyModels{"dashboard": newDashboard})
// Read newDashboard's ID from the response
newDashboardID, _ := helper.GetResponseID(resp)
// POST new widget
newWidget := WidgetRequest{
Name: helper.WidgetA.Name,
Type: helper.WidgetA.Type,
Width: helper.WidgetA.Width,
Height: helper.WidgetA.Height,
MinWidth: helper.WidgetA.MinWidth,
MinHeight: helper.WidgetA.MinHeight,
X: helper.WidgetA.X,
Y: helper.WidgetA.Y,
Z: helper.WidgetA.Z,
IsLocked: helper.WidgetA.IsLocked,
CustomProperties: helper.WidgetA.CustomProperties,
DashboardID: uint(newDashboardID),
}
_, resp, _ = helper.TestEndpoint(router, token,
"/api/widgets", "POST", helper.KeyModels{"widget": newWidget})
// Read newWidget's ID from the response
newWidgetID, _ := helper.GetResponseID(resp)
// add the guest user to the new scenario
_, resp, _ = helper.TestEndpoint(router, token,
fmt.Sprintf("/api/scenarios/%v/user?username=User_C", newScenarioID), "PUT", nil)
return uint(newScenarioID), uint(newICID), uint(newConfigID), uint(newDashboardID), uint(newWidgetID)
return uint(newScenarioID)
}
func TestMain(m *testing.M) {
@ -198,21 +94,8 @@ func TestMain(m *testing.M) {
user.RegisterAuthenticate(api.Group("/authenticate"))
api.Use(user.Authentication(true))
// component-configuration endpoints required here to first add a config to the DB
// that can be associated with a new file
component_configuration.RegisterComponentConfigurationEndpoints(api.Group("/configs"))
// scenario endpoints required here to first add a scenario to the DB
// that can be associated with a new component config
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
// IC endpoints required here to first add a IC to the DB
// that can be associated with a new component config
infrastructure_component.RegisterICEndpoints(api.Group("/ic"))
// dashboard endpoints required here to first add a dashboard to the DB
// that can be associated with a new widget
dashboard.RegisterDashboardEndpoints(api.Group("/dashboards"))
// widget endpoints required here to first add a widget to the DB
// that can be associated with a new file
widget.RegisterWidgetEndpoints(api.Group("/widgets"))
RegisterFileEndpoints(api.Group("/files"))
@ -226,7 +109,7 @@ func TestAddFile(t *testing.T) {
// prepare the content of the DB for testing
// using the respective endpoints of the API
_, _, configID, _, widgetID := addScenarioAndICAndConfigAndDashboardAndWidget()
scenarioID := addScenario()
// authenticate as userB who has no access to the elements in the DB
token, err := helper.AuthenticateForTest(router,
@ -235,17 +118,10 @@ func TestAddFile(t *testing.T) {
emptyBuf := &bytes.Buffer{}
// try to POST to a component config to which UserB has no access
// try to POST to a scenario to which UserB has no access
// should return a 422 unprocessable entity error
code, resp, err := helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=config", configID), "POST", emptyBuf)
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
// try to POST to a widget to which UserB has no access
// should return a 422 unprocessable entity error
code, resp, err = helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=widget", widgetID), "POST", emptyBuf)
fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), "POST", emptyBuf)
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
@ -254,24 +130,17 @@ func TestAddFile(t *testing.T) {
"/api/authenticate", "POST", helper.UserACredentials)
assert.NoError(t, err)
// try to POST to an invalid object type
// try to POST without a scenario ID
// should return a bad request error
code, resp, err = helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=wrongtype", widgetID), "POST", emptyBuf)
assert.NoError(t, err)
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
// try to POST without an object ID
// should return a bad request error
code, resp, err = helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files?objectType=config"), "POST", emptyBuf)
fmt.Sprintf("/api/files"), "POST", emptyBuf)
assert.NoError(t, err)
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
// try to POST an invalid file
// should return a bad request
code, resp, err = helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=config", configID), "POST", emptyBuf)
fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), "POST", emptyBuf)
assert.NoError(t, err)
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
@ -297,11 +166,10 @@ func TestAddFile(t *testing.T) {
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
//req, err := http.NewRequest("POST", "/api/files?objectID=1&objectType=widget", bodyBuf)
// Create the request
w := httptest.NewRecorder()
req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=config", configID), bodyBuf)
req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), bodyBuf)
assert.NoError(t, err, "create request")
req.Header.Set("Content-Type", contentType)
@ -342,7 +210,7 @@ func TestUpdateFile(t *testing.T) {
// prepare the content of the DB for testing
// using the respective endpoints of the API
_, _, configID, _, _ := addScenarioAndICAndConfigAndDashboardAndWidget()
scenarioID := addScenario()
// authenticate as normal user
token, err := helper.AuthenticateForTest(router,
@ -370,11 +238,10 @@ func TestUpdateFile(t *testing.T) {
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
//req, err := http.NewRequest("POST", "/api/files?objectID=1&objectType=widget", bodyBuf)
// Create the POST request
w := httptest.NewRecorder()
req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=config", configID), bodyBuf)
req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), bodyBuf)
assert.NoError(t, err, "create request")
req.Header.Set("Content-Type", contentType)
@ -476,7 +343,7 @@ func TestDeleteFile(t *testing.T) {
// prepare the content of the DB for testing
// using the respective endpoints of the API
_, _, configID, _, widgetID := addScenarioAndICAndConfigAndDashboardAndWidget()
scenarioID := addScenario()
// authenticate as normal user
token, err := helper.AuthenticateForTest(router,
@ -506,7 +373,7 @@ func TestDeleteFile(t *testing.T) {
// Create the request
w := httptest.NewRecorder()
req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=config", configID), bodyBuf)
req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), bodyBuf)
assert.NoError(t, err, "create request")
req.Header.Set("Content-Type", contentType)
req.Header.Add("Authorization", "Bearer "+token)
@ -516,7 +383,7 @@ func TestDeleteFile(t *testing.T) {
newFileID, err := helper.GetResponseID(w.Body)
assert.NoError(t, err)
// add a second file to a widget, this time to a widget
// add a second file to a scenario
bodyBuf2 := &bytes.Buffer{}
bodyWriter2 := multipart.NewWriter(bodyBuf2)
fileWriter2, err := bodyWriter2.CreateFormFile("file", "testuploadfile.txt")
@ -529,7 +396,7 @@ func TestDeleteFile(t *testing.T) {
// Create the request
w2 := httptest.NewRecorder()
req2, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=widget", widgetID), bodyBuf2)
req2, err := http.NewRequest("POST", fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), bodyBuf2)
assert.NoError(t, err, "create request")
req2.Header.Set("Content-Type", contentType2)
req2.Header.Add("Authorization", "Bearer "+token)
@ -544,28 +411,21 @@ func TestDeleteFile(t *testing.T) {
"/api/authenticate", "POST", helper.UserBCredentials)
assert.NoError(t, err)
// try to DELETE file of component config to which userB has no access
// try to DELETE file from scenario to which userB has no access
// should return an unprocessable entity error
code, resp, err := helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files/%v", newFileID), "DELETE", nil)
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
// try to DELETE file of widget to which userB has no access
// should return an unprocessable entity error
code, resp, err = helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files/%v", newFileID2), "DELETE", nil)
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
// authenticate as normal user
token, err = helper.AuthenticateForTest(router,
"/api/authenticate", "POST", helper.UserACredentials)
assert.NoError(t, err)
// Count the number of all files returned for component config
// Count the number of all files returned for scenario
initialNumber, err := helper.LengthOfResponse(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=config", configID), "GET", nil)
fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), "GET", nil)
assert.NoError(t, err)
// try to DELETE non-existing fileID
@ -580,20 +440,13 @@ func TestDeleteFile(t *testing.T) {
"/api/authenticate", "POST", helper.GuestCredentials)
assert.NoError(t, err)
// try to DELETE file of component config as guest
// try to DELETE file of scenario as guest
// should return an unprocessable entity error
code, resp, err = helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files/%v", newFileID), "DELETE", nil)
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
// try to DELETE file of widget as guest
// should return an unprocessable entity error
code, resp, err = helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files/%v", newFileID2), "DELETE", nil)
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
// authenticate as normal user
token, err = helper.AuthenticateForTest(router,
"/api/authenticate", "POST", helper.UserACredentials)
@ -611,15 +464,15 @@ func TestDeleteFile(t *testing.T) {
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Again count the number of all the files returned for component config
// Again count the number of all the files returned for scenario
finalNumber, err := helper.LengthOfResponse(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=config", configID), "GET", nil)
fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), "GET", nil)
assert.NoError(t, err)
assert.Equal(t, initialNumber-1, finalNumber)
assert.Equal(t, initialNumber-2, finalNumber)
}
func TestGetAllFilesOfConfig(t *testing.T) {
func TestGetAllFilesOfScenario(t *testing.T) {
database.DropTables()
database.MigrateModels()
@ -627,24 +480,17 @@ func TestGetAllFilesOfConfig(t *testing.T) {
// prepare the content of the DB for testing
// using the respective endpoints of the API
_, _, ConfigID, _, widgetID := addScenarioAndICAndConfigAndDashboardAndWidget()
scenarioID := addScenario()
// authenticate as userB who has no access to the elements in the DB
token, err := helper.AuthenticateForTest(router,
"/api/authenticate", "POST", helper.UserBCredentials)
assert.NoError(t, err)
// try to get all files for component config to which userB has not access
// try to get all files for scenario to which userB has not access
// should return unprocessable entity error
code, resp, err := helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=config", ConfigID), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
// try to get all files for widget to which userB has not access
// should return unprocessable entity error
code, resp, err = helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=widget", widgetID), "GET", nil)
fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
@ -653,26 +499,15 @@ func TestGetAllFilesOfConfig(t *testing.T) {
"/api/authenticate", "POST", helper.UserACredentials)
assert.NoError(t, err)
//try to get all files for unsupported object type; should return a bad request error
//try to get all files with missing scenario ID; should return a bad request error
code, resp, err = helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=wrongtype", ConfigID), "GET", nil)
fmt.Sprintf("/api/files"), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
//try to get all files with missing object ID; should return a bad request error
code, resp, err = helper.TestEndpoint(router, token,
fmt.Sprintf("/api/files?objectType=config"), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
// Count the number of all files returned for component config
initialNumberConfig, err := helper.LengthOfResponse(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=config", ConfigID), "GET", nil)
assert.NoError(t, err)
// Count the number of all files returned for widget
initialNumberWidget, err := helper.LengthOfResponse(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=widget", widgetID), "GET", nil)
// Count the number of all files returned for scenario
initialNumber, err := helper.LengthOfResponse(router, token,
fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), "GET", nil)
assert.NoError(t, err)
// create a testfile.txt in local folder
@ -685,94 +520,55 @@ func TestGetAllFilesOfConfig(t *testing.T) {
assert.NoError(t, err, "opening file")
defer fh.Close()
// test POST a file to component config and widget
bodyBufConfig1 := &bytes.Buffer{}
bodyBufWidget1 := &bytes.Buffer{}
bodyWriterConfig1 := multipart.NewWriter(bodyBufConfig1)
bodyWriterWidget1 := multipart.NewWriter(bodyBufWidget1)
fileWriterConfig1, err := bodyWriterConfig1.CreateFormFile("file", "testuploadfile.txt")
assert.NoError(t, err, "writing to buffer")
fileWriterWidget1, err := bodyWriterWidget1.CreateFormFile("file", "testuploadfile.txt")
// test POST a file to scenario
bodyBuf1 := &bytes.Buffer{}
bodyWriter1 := multipart.NewWriter(bodyBuf1)
fileWriter1, err := bodyWriter1.CreateFormFile("file", "testuploadfile.txt")
assert.NoError(t, err, "writing to buffer")
// io copy
_, err = io.Copy(fileWriterConfig1, fh)
_, err = io.Copy(fileWriter1, fh)
assert.NoError(t, err, "IO copy")
_, err = io.Copy(fileWriterWidget1, fh)
assert.NoError(t, err, "IO copy")
contentTypeConfig1 := bodyWriterConfig1.FormDataContentType()
contentTypeWidget1 := bodyWriterWidget1.FormDataContentType()
bodyWriterConfig1.Close()
bodyWriterWidget1.Close()
contentType1 := bodyWriter1.FormDataContentType()
bodyWriter1.Close()
// Create the request for component config
// Create the request
w := httptest.NewRecorder()
req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=config", ConfigID), bodyBufConfig1)
req, err := http.NewRequest("POST", fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), bodyBuf1)
assert.NoError(t, err, "create request")
req.Header.Set("Content-Type", contentTypeConfig1)
req.Header.Set("Content-Type", contentType1)
req.Header.Add("Authorization", "Bearer "+token)
router.ServeHTTP(w, req)
assert.Equalf(t, 200, w.Code, "Response body: \n%v\n", w.Body)
// Create the request for widget
w2 := httptest.NewRecorder()
req2, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=widget", widgetID), bodyBufWidget1)
assert.NoError(t, err, "create request")
req2.Header.Set("Content-Type", contentTypeWidget1)
req2.Header.Add("Authorization", "Bearer "+token)
router.ServeHTTP(w2, req2)
assert.Equalf(t, 200, w2.Code, "Response body: \n%v\n", w2.Body)
// POST a second file to component config and widget
// POST a second file to scenario
// open a second file handle
fh2, err := os.Open("testfile.txt")
assert.NoError(t, err, "opening file")
defer fh2.Close()
bodyBufConfig2 := &bytes.Buffer{}
bodyBufWidget2 := &bytes.Buffer{}
bodyWriterConfig2 := multipart.NewWriter(bodyBufConfig2)
bodyWriterWidget2 := multipart.NewWriter(bodyBufWidget2)
fileWriterConfig2, err := bodyWriterConfig2.CreateFormFile("file", "testuploadfile2.txt")
assert.NoError(t, err, "writing to buffer")
fileWriterWidget2, err := bodyWriterWidget2.CreateFormFile("file", "testuploadfile2.txt")
bodyBuf2 := &bytes.Buffer{}
bodyWriter2 := multipart.NewWriter(bodyBuf2)
fileWriter2, err := bodyWriter2.CreateFormFile("file", "testuploadfile2.txt")
assert.NoError(t, err, "writing to buffer")
// io copy
_, err = io.Copy(fileWriterConfig2, fh2)
_, err = io.Copy(fileWriter2, fh2)
assert.NoError(t, err, "IO copy")
_, err = io.Copy(fileWriterWidget2, fh2)
assert.NoError(t, err, "IO copy")
contentTypeConfig2 := bodyWriterConfig2.FormDataContentType()
contentTypeWidget2 := bodyWriterWidget2.FormDataContentType()
bodyWriterConfig2.Close()
bodyWriterWidget2.Close()
contentType2 := bodyWriter2.FormDataContentType()
bodyWriter2.Close()
w3 := httptest.NewRecorder()
req3, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=config", ConfigID), bodyBufConfig2)
w2 := httptest.NewRecorder()
req2, err := http.NewRequest("POST", fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), bodyBuf2)
assert.NoError(t, err, "create request")
req3.Header.Set("Content-Type", contentTypeConfig2)
req3.Header.Add("Authorization", "Bearer "+token)
router.ServeHTTP(w3, req3)
assert.Equalf(t, 200, w3.Code, "Response body: \n%v\n", w3.Body)
req2.Header.Set("Content-Type", contentType2)
req2.Header.Add("Authorization", "Bearer "+token)
router.ServeHTTP(w2, req2)
assert.Equalf(t, 200, w2.Code, "Response body: \n%v\n", w2.Body)
w4 := httptest.NewRecorder()
req4, err := http.NewRequest("POST", fmt.Sprintf("/api/files?objectID=%v&objectType=widget", widgetID), bodyBufWidget2)
assert.NoError(t, err, "create request")
req4.Header.Set("Content-Type", contentTypeWidget2)
req4.Header.Add("Authorization", "Bearer "+token)
router.ServeHTTP(w4, req4)
assert.Equalf(t, 200, w4.Code, "Response body: \n%v\n", w4.Body)
// Again count the number of all the files returned for component config
finalNumberConfig, err := helper.LengthOfResponse(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=config", ConfigID), "GET", nil)
// Again count the number of all the files returned for scenario
finalNumber, err := helper.LengthOfResponse(router, token,
fmt.Sprintf("/api/files?scenarioID=%v", scenarioID), "GET", nil)
assert.NoError(t, err)
assert.Equal(t, initialNumberConfig+2, finalNumberConfig)
// Again count the number of all the files returned for widget
finalNumberWidget, err := helper.LengthOfResponse(router, token,
fmt.Sprintf("/api/files?objectID=%v&objectType=widget", widgetID), "GET", nil)
assert.NoError(t, err)
assert.Equal(t, initialNumberWidget+2, finalNumberWidget)
assert.Equal(t, initialNumber+2, finalNumber)
}

View file

@ -264,9 +264,9 @@ func AddTestData(basePath string, router *gin.Engine) (*bytes.Buffer, error) {
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
// Create the request and add file to component config
// Create the request and add file to scenario
w1 := httptest.NewRecorder()
req1, _ := http.NewRequest("POST", basePath+"/files?objectID=1&objectType=config", bodyBuf)
req1, _ := http.NewRequest("POST", basePath+"/files?scenarioID=1", bodyBuf)
req1.Header.Set("Content-Type", contentType)
req1.Header.Add("Authorization", "Bearer "+token)
router.ServeHTTP(w1, req1)
@ -283,9 +283,9 @@ func AddTestData(basePath string, router *gin.Engine) (*bytes.Buffer, error) {
contentType = bodyWriter.FormDataContentType()
bodyWriter.Close()
// Create the request and add file to widget
// Create the request and add a second file to scenario
w2 := httptest.NewRecorder()
req2, _ := http.NewRequest("POST", basePath+"/files?objectID=1&objectType=widget", bodyBuf)
req2, _ := http.NewRequest("POST", basePath+"/files?scenarioID=1", bodyBuf)
req2.Header.Set("Content-Type", contentType)
req2.Header.Add("Authorization", "Bearer "+token)
router.ServeHTTP(w2, req2)