Commit 89311af1 authored by Sonja Happ's avatar Sonja Happ
Browse files

WIP: initial version of result data model, endpoints, methods, validators, middleware #38

parent adbef6ab
......@@ -93,6 +93,7 @@ func DropTables() {
DBpool.DropTableIfExists(&User{})
DBpool.DropTableIfExists(&Dashboard{})
DBpool.DropTableIfExists(&Widget{})
DBpool.DropTableIfExists(&Result{})
// The following statement deletes the many to many relationship between users and scenarios
DBpool.DropTableIfExists("user_scenarios")
}
......@@ -107,4 +108,5 @@ func MigrateModels() {
DBpool.AutoMigrate(&User{})
DBpool.AutoMigrate(&Dashboard{})
DBpool.AutoMigrate(&Widget{})
DBpool.AutoMigrate(&Result{})
}
......@@ -144,6 +144,8 @@ func TestScenarioAssociations(t *testing.T) {
dashboardB := Dashboard{}
fileA := File{}
fileB := File{}
resultA := Result{}
resultB := Result{}
// add scenarios to DB
assert.NoError(t, DBpool.Create(&scenarioA).Error)
......@@ -165,6 +167,10 @@ func TestScenarioAssociations(t *testing.T) {
assert.NoError(t, DBpool.Create(&fileA).Error)
assert.NoError(t, DBpool.Create(&fileB).Error)
// add results to DB
assert.NoError(t, DBpool.Create(&resultA).Error)
assert.NoError(t, DBpool.Create(&resultB).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)
......@@ -184,6 +190,10 @@ func TestScenarioAssociations(t *testing.T) {
assert.NoError(t, DBpool.Model(&scenarioA).Association("Dashboards").Append(&dashboardA).Error)
assert.NoError(t, DBpool.Model(&scenarioA).Association("Dashboards").Append(&dashboardB).Error)
// Scenario HM Results
assert.NoError(t, DBpool.Model(&scenarioA).Association("Results").Append(&resultA).Error)
assert.NoError(t, DBpool.Model(&scenarioA).Association("Results").Append(&resultB).Error)
var scenario1 Scenario
assert.NoError(t, DBpool.Find(&scenario1, 1).Error, fmt.Sprintf("Find Scenario with ID=1"))
......@@ -218,6 +228,14 @@ func TestScenarioAssociations(t *testing.T) {
assert.Fail(t, "Scenario Associations",
"Expected to have %v Files. Has %v.", 2, len(files))
}
// Get results of scenario1
var results []File
assert.NoError(t, DBpool.Model(&scenario1).Related(&results, "Results").Error)
if len(files) != 2 {
assert.Fail(t, "Scenario Associations",
"Expected to have %v Results. Has %v.", 2, len(results))
}
}
func TestICAssociations(t *testing.T) {
......
......@@ -32,8 +32,8 @@ import (
// except the json tags that are needed for serializing the models
type Model struct {
ID uint `json:"id,omitempty" gorm:"primary_key:true"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt *time.Time `json:"-" sql:"index"`
}
......@@ -71,6 +71,8 @@ type Scenario struct {
Dashboards []Dashboard `json:"-" gorm:"foreignkey:ScenarioID" `
// Files that belong to the Scenario (for example images, models, etc.)
Files []File `json:"-" gorm:"foreignkey:ScenarioID"`
// Results that belong to the Scenario
Results []Result `json:"-" gorm:"foreignkey:ScenarioID"`
}
// ComponentConfiguration data model
......@@ -212,3 +214,16 @@ type File struct {
// Width of an image file in pixels (optional)
ImageWidth int `json:"imageWidth" gorm:"default:0"`
}
// Result data model
type Result struct {
Model
// JSON snapshots of component configurations used to generate results
ConfigSnapshots []postgres.Jsonb `json:"configSnapshots"`
// Description of results
Description string `json:"description"`
// ID of Scenario to which result belongs
ScenarioID uint `json:"scenarioID"`
// File IDs associated with result
ResultFileIDs pq.Int64Array `json:"resultFileIDs" gorm:"type:integer[]"`
}
......@@ -45,6 +45,7 @@ const ModelWidget = ModelName("widget")
const ModelComponentConfiguration = ModelName("component-configuration")
const ModelSignal = ModelName("signal")
const ModelFile = ModelName("file")
const ModelResult = ModelName("result")
type CRUD string
......@@ -83,6 +84,7 @@ var Roles = RoleActions{
ModelDashboard: crud,
ModelSignal: crud,
ModelFile: crud,
ModelResult: crud,
},
"User": {
ModelUser: _ru_,
......@@ -95,6 +97,7 @@ var Roles = RoleActions{
ModelDashboard: crud,
ModelSignal: crud,
ModelFile: crud,
ModelResult: crud,
},
"Guest": {
ModelScenario: _r__,
......@@ -107,6 +110,7 @@ var Roles = RoleActions{
ModelUsers: none,
ModelSignal: _r__,
ModelFile: _r__,
ModelResult: none,
},
}
......
This diff is collapsed.
......@@ -101,3 +101,11 @@ type ResponseFiles struct {
type ResponseFile struct {
file database.File
}
type ResponseResults struct {
results []database.Result
}
type ResponseResult struct {
result database.Result
}
This diff is collapsed.
......@@ -83,6 +83,8 @@ definitions:
type: object
database.ComponentConfiguration:
properties:
createdAt:
type: string
fileIDs:
description: Array of file IDs used by the component configuration
type: string
......@@ -106,9 +108,13 @@ definitions:
startParameters:
description: Start parameters of Component Configuration as JSON
type: string
updatedAt:
type: string
type: object
database.Dashboard:
properties:
createdAt:
type: string
grid:
description: Grid of dashboard
type: integer
......@@ -123,14 +129,24 @@ definitions:
scenarioID:
description: ID of scenario to which dashboard belongs
type: integer
updatedAt:
type: string
type: object
database.File:
properties:
createdAt:
type: string
date:
description: Last modification time of file
type: string
id:
type: integer
imageHeight:
description: Height of an image file in pixels (optional)
type: integer
imageWidth:
description: Width of an image file in pixels (optional)
type: integer
name:
description: Name of file
type: string
......@@ -143,6 +159,8 @@ definitions:
type:
description: Type of file (MIME type)
type: string
updatedAt:
type: string
type: object
database.InfrastructureComponent:
properties:
......@@ -152,6 +170,8 @@ definitions:
category:
description: Category of IC (simulator, gateway, database, etc.)
type: string
createdAt:
type: string
description:
description: Description of the IC
type: string
......@@ -178,6 +198,8 @@ definitions:
type:
description: Type of IC (RTDS, VILLASnode, RTDS, etc.)
type: string
updatedAt:
type: string
uptime:
description: Uptime of the IC
type: number
......@@ -188,8 +210,31 @@ definitions:
description: WebsocketURL if the IC
type: string
type: object
database.Result:
properties:
configSnapshots:
description: JSON snapshots of component configurations used to generate results
type: string
createdAt:
type: string
description:
description: Description of results
type: string
id:
type: integer
resultFileIDs:
description: File IDs associated with result
type: string
scenarioID:
description: ID of Scenario to which result belongs
type: integer
updatedAt:
type: string
type: object
database.Scenario:
properties:
createdAt:
type: string
id:
type: integer
name:
......@@ -201,12 +246,16 @@ definitions:
startParameters:
description: Start parameters of scenario as JSON
type: string
updatedAt:
type: string
type: object
database.Signal:
properties:
configID:
description: ID of Component Configuration
type: integer
createdAt:
type: string
direction:
description: Direction of the signal (in or out)
type: string
......@@ -224,6 +273,8 @@ definitions:
unit:
description: Unit of Signal
type: string
updatedAt:
type: string
type: object
database.User:
properties:
......@@ -231,6 +282,8 @@ definitions:
description: Indicating status of user (false means user is inactive and should
not be able to login)
type: boolean
createdAt:
type: string
id:
type: integer
mail:
......@@ -239,12 +292,16 @@ definitions:
role:
description: Role of user
type: string
updatedAt:
type: string
username:
description: Username of user
type: string
type: object
database.Widget:
properties:
createdAt:
type: string
customProperties:
description: Custom properties of widget as JSON string
type: string
......@@ -274,6 +331,8 @@ definitions:
type:
description: Type of widget
type: string
updatedAt:
type: string
width:
description: Width of widget
type: integer
......@@ -358,6 +417,19 @@ definitions:
$ref: '#/definitions/database.InfrastructureComponent'
type: array
type: object
docs.ResponseResult:
properties:
result:
$ref: '#/definitions/database.Result'
type: object
type: object
docs.ResponseResults:
properties:
results:
items:
$ref: '#/definitions/database.Result'
type: array
type: object
docs.ResponseScenario:
properties:
scenario:
......@@ -479,6 +551,45 @@ definitions:
WebsocketURL:
type: string
type: object
result.addResultRequest:
properties:
result:
$ref: '#/definitions/result.validNewResult'
type: object
type: object
result.updateResultRequest:
properties:
result:
$ref: '#/definitions/result.validUpdatedResult'
type: object
type: object
result.validNewResult:
properties:
ConfigSnapshots:
type: string
Description:
type: string
ResultFileIDs:
items:
type: integer
type: array
ScenarioID:
type: integer
required:
- ConfigSnapshots
- ScenarioID
type: object
result.validUpdatedResult:
properties:
configSnapshots:
type: string
description:
type: string
resultFileIDs:
items:
type: integer
type: array
type: object
scenario.addScenarioRequest:
properties:
scenario:
......@@ -1616,6 +1727,342 @@ paths:
summary: Prometheus metrics endpoint
tags:
- metrics
/results:
get:
operationId: getResults
parameters:
- description: Scenario ID
in: query
name: scenarioID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: Results which belong to scenario
schema:
$ref: '#/definitions/docs.ResponseResults'
"404":
description: Not found
schema:
$ref: '#/definitions/docs.ResponseError'
"422":
description: Unprocessable entity
schema:
$ref: '#/definitions/docs.ResponseError'
"500":
description: Internal server error
schema:
$ref: '#/definitions/docs.ResponseError'
security:
- Bearer: []
summary: Get all results of scenario
tags:
- results
post:
consumes:
- application/json
operationId: addResult
parameters:
- description: Result to be added incl. ID of Scenario
in: body
name: inputResult
required: true
schema:
$ref: '#/definitions/result.addResultRequest'
type: object
produces:
- application/json
responses:
"200":
description: Result that was added
schema:
$ref: '#/definitions/docs.ResponseResult'
"400":
description: Bad request
schema:
$ref: '#/definitions/docs.ResponseError'
"404":
description: Not found
schema:
$ref: '#/definitions/docs.ResponseError'
"422":
description: Unprocessable entity
schema:
$ref: '#/definitions/docs.ResponseError'
"500":
description: Internal server error
schema:
$ref: '#/definitions/docs.ResponseError'
security:
- Bearer: []
summary: Add a result to a scenario
tags:
- results
/results/{resultID}:
delete:
operationId: deleteResult
parameters:
- description: Result ID
in: path
name: resultID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: Result that was deleted
schema:
$ref: '#/definitions/docs.ResponseResult'
"400":
description: Bad request
schema:
$ref: '#/definitions/docs.ResponseError'
"404":
description: Not found
schema:
$ref: '#/definitions/docs.ResponseError'
"422":
description: Unprocessable entity
schema:
$ref: '#/definitions/docs.ResponseError'
"500":
description: Internal server error
schema:
$ref: '#/definitions/docs.ResponseError'
security:
- Bearer: []
summary: Delete a Result
tags:
- results
get:
operationId: getResult
parameters:
- description: Result ID
in: path
name: resultID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: Result that was requested
schema:
$ref: '#/definitions/docs.ResponseResult'
"400":
description: Bad request
schema:
$ref: '#/definitions/docs.ResponseError'
"404":
description: Not found
schema:
$ref: '#/definitions/docs.ResponseError'
"422":
description: Unprocessable entity
schema:
$ref: '#/definitions/docs.ResponseError'
"500":
description: Internal server error
schema:
$ref: '#/definitions/docs.ResponseError'
security:
- Bearer: []
summary: Get a Result
tags:
- results
put:
consumes:
- application/json
operationId: updateResult
parameters:
- description: Result to be updated
in: body
name: inputResult
required: true
schema:
$ref: '#/definitions/result.updateResultRequest'
type: object
- description: Result ID
in: path
name: resultID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: Result that was updated
schema:
$ref: '#/definitions/docs.ResponseResult'
"400":
description: Bad request
schema:
$ref: '#/definitions/docs.ResponseError'
"404":
description: Not found
schema:
$ref: '#/definitions/docs.ResponseError'
"422":
description: Unprocessable entity
schema:
$ref: '#/definitions/docs.ResponseError'
"500":
description: Internal server error
schema:
$ref: '#/definitions/docs.ResponseError'
security:
- Bearer: []
summary: Update a result
tags:
- results
/results/{resultID}/file:
post:
consumes:
- text/plain
- text/csv
- application/gzip
- application/x-gtar
- application/x-tar
- application/x-ustar
- application/zip
- application/msexcel
- application/xml
- application/x-bag
operationId: addResultFile
parameters:
- description: File to be uploaded
in: formData
name: inputFile
required: true
type: file
- description: Result ID
in: path
name: resultID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: Result that was updated
schema:
$ref: '#/definitions/docs.ResponseResult'
"400":
description: Bad request
schema:
$ref: '#/definitions/docs.ResponseError'
"404":
description: Not found
schema:
$ref: '#/definitions/docs.ResponseError'
"422":
description: Unprocessable entity
schema:
$ref: '#/definitions/docs.ResponseError'
"500":
description: Internal server error
schema:
$ref: '#/definitions/docs.ResponseError'
security:
- Bearer: []
summary: Upload a result file to the DB and associate it with scenario and result
tags:
- results
/results/{resultID}/file/{fileID}:
delete:
operationId: deleteResultFile
parameters:
- description: Result ID
in: path
name: resultID
required: true
type: integer
- description: ID of the file to delete
in: path
name: fileID
required: true