mirror of
https://github.com/SecurityBrewery/catalyst.git
synced 2025-12-06 15:22:47 +01:00
Add Dashboards (#41)
This commit is contained in:
117
database/dashboard.go
Normal file
117
database/dashboard.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/arangodb/go-driver"
|
||||||
|
"github.com/iancoleman/strcase"
|
||||||
|
|
||||||
|
"github.com/SecurityBrewery/catalyst/caql"
|
||||||
|
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||||
|
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func toDashboardResponse(key string, doc *model.Dashboard) *model.DashboardResponse {
|
||||||
|
return &model.DashboardResponse{
|
||||||
|
ID: key,
|
||||||
|
Name: doc.Name,
|
||||||
|
Widgets: doc.Widgets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) DashboardCreate(ctx context.Context, dashboard *model.Dashboard) (*model.DashboardResponse, error) {
|
||||||
|
if dashboard == nil {
|
||||||
|
return nil, errors.New("requires dashboard")
|
||||||
|
}
|
||||||
|
if dashboard.Name == "" {
|
||||||
|
return nil, errors.New("requires dashboard name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.parseWidgets(dashboard); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var doc model.Dashboard
|
||||||
|
newctx := driver.WithReturnNew(ctx, &doc)
|
||||||
|
|
||||||
|
meta, err := db.dashboardCollection.CreateDocument(ctx, newctx, strcase.ToKebab(dashboard.Name), dashboard)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return toDashboardResponse(meta.Key, &doc), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) DashboardGet(ctx context.Context, id string) (*model.DashboardResponse, error) {
|
||||||
|
var doc model.Dashboard
|
||||||
|
meta, err := db.dashboardCollection.ReadDocument(ctx, id, &doc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return toDashboardResponse(meta.Key, &doc), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) DashboardUpdate(ctx context.Context, id string, dashboard *model.Dashboard) (*model.DashboardResponse, error) {
|
||||||
|
if err := db.parseWidgets(dashboard); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var doc model.Dashboard
|
||||||
|
ctx = driver.WithReturnNew(ctx, &doc)
|
||||||
|
|
||||||
|
meta, err := db.dashboardCollection.ReplaceDocument(ctx, id, dashboard)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return toDashboardResponse(meta.Key, &doc), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) DashboardDelete(ctx context.Context, id string) error {
|
||||||
|
_, err := db.dashboardCollection.RemoveDocument(ctx, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) DashboardList(ctx context.Context) ([]*model.DashboardResponse, error) {
|
||||||
|
query := "FOR d IN @@collection RETURN d"
|
||||||
|
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": DashboardCollectionName}, busdb.ReadOperation)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cursor.Close()
|
||||||
|
var docs []*model.DashboardResponse
|
||||||
|
for {
|
||||||
|
var doc model.Dashboard
|
||||||
|
meta, err := cursor.ReadDocument(ctx, &doc)
|
||||||
|
if driver.IsNoMoreDocuments(err) {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
docs = append(docs, toDashboardResponse(meta.Key, &doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
return docs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) parseWidgets(dashboard *model.Dashboard) error {
|
||||||
|
for _, widget := range dashboard.Widgets {
|
||||||
|
parser := &caql.Parser{Searcher: db.Index, Prefix: "d."}
|
||||||
|
|
||||||
|
_, err := parser.Parse(widget.Aggregation)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid aggregation query (%s): syntax error\n", widget.Aggregation)
|
||||||
|
}
|
||||||
|
|
||||||
|
if widget.Filter != nil {
|
||||||
|
_, err := parser.Parse(*widget.Filter)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid filter query (%s): syntax error\n", *widget.Filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ const (
|
|||||||
TicketTypeCollectionName = "tickettypes"
|
TicketTypeCollectionName = "tickettypes"
|
||||||
JobCollectionName = "jobs"
|
JobCollectionName = "jobs"
|
||||||
SettingsCollectionName = "settings"
|
SettingsCollectionName = "settings"
|
||||||
|
DashboardCollectionName = "dashboards"
|
||||||
|
|
||||||
TicketArtifactsGraphName = "Graph"
|
TicketArtifactsGraphName = "Graph"
|
||||||
RelatedTicketsCollectionName = "related"
|
RelatedTicketsCollectionName = "related"
|
||||||
@@ -46,6 +47,7 @@ type Database struct {
|
|||||||
tickettypeCollection *busdb.Collection
|
tickettypeCollection *busdb.Collection
|
||||||
jobCollection *busdb.Collection
|
jobCollection *busdb.Collection
|
||||||
settingsCollection *busdb.Collection
|
settingsCollection *busdb.Collection
|
||||||
|
dashboardCollection *busdb.Collection
|
||||||
|
|
||||||
relatedCollection *busdb.Collection
|
relatedCollection *busdb.Collection
|
||||||
// containsCollection *busdb.Collection
|
// containsCollection *busdb.Collection
|
||||||
@@ -128,6 +130,10 @@ func New(ctx context.Context, index *index.Index, bus *bus.Bus, hooks *hooks.Hoo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
dashboardCollection, err := arangoDB.Collection(ctx, DashboardCollectionName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
hookedDB, err := busdb.NewDatabase(ctx, arangoDB, bus)
|
hookedDB, err := busdb.NewDatabase(ctx, arangoDB, bus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -149,6 +155,7 @@ func New(ctx context.Context, index *index.Index, bus *bus.Bus, hooks *hooks.Hoo
|
|||||||
tickettypeCollection: busdb.NewCollection(tickettypeCollection, hookedDB),
|
tickettypeCollection: busdb.NewCollection(tickettypeCollection, hookedDB),
|
||||||
jobCollection: busdb.NewCollection(jobCollection, hookedDB),
|
jobCollection: busdb.NewCollection(jobCollection, hookedDB),
|
||||||
settingsCollection: busdb.NewCollection(settingsCollection, hookedDB),
|
settingsCollection: busdb.NewCollection(settingsCollection, hookedDB),
|
||||||
|
dashboardCollection: busdb.NewCollection(dashboardCollection, hookedDB),
|
||||||
}
|
}
|
||||||
|
|
||||||
return db, nil
|
return db, nil
|
||||||
@@ -197,5 +204,6 @@ func (db *Database) Truncate(ctx context.Context) {
|
|||||||
db.jobCollection.Truncate(ctx)
|
db.jobCollection.Truncate(ctx)
|
||||||
db.relatedCollection.Truncate(ctx)
|
db.relatedCollection.Truncate(ctx)
|
||||||
db.settingsCollection.Truncate(ctx)
|
db.settingsCollection.Truncate(ctx)
|
||||||
|
db.dashboardCollection.Truncate(ctx)
|
||||||
// db.containsCollection.Truncate(ctx)
|
// db.containsCollection.Truncate(ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ func generateMigrations() ([]Migration, error) {
|
|||||||
&createDocument{ID: "create-settings-global", Collection: "settings", Document: &busdb.Keyed{Key: "global", Doc: model.Settings{ArtifactStates: []*model.Type{{Icon: "mdi-help-circle-outline", ID: "unknown", Name: "Unknown", Color: pointer.String(model.TypeColorInfo)}, {Icon: "mdi-skull", ID: "malicious", Name: "Malicious", Color: pointer.String(model.TypeColorError)}, {Icon: "mdi-check", ID: "clean", Name: "Clean", Color: pointer.String(model.TypeColorSuccess)}}, ArtifactKinds: []*model.Type{{Icon: "mdi-server", ID: "asset", Name: "Asset"}, {Icon: "mdi-bullseye", ID: "ioc", Name: "IOC"}}, Timeformat: "YYYY-MM-DDThh:mm:ss"}}},
|
&createDocument{ID: "create-settings-global", Collection: "settings", Document: &busdb.Keyed{Key: "global", Doc: model.Settings{ArtifactStates: []*model.Type{{Icon: "mdi-help-circle-outline", ID: "unknown", Name: "Unknown", Color: pointer.String(model.TypeColorInfo)}, {Icon: "mdi-skull", ID: "malicious", Name: "Malicious", Color: pointer.String(model.TypeColorError)}, {Icon: "mdi-check", ID: "clean", Name: "Clean", Color: pointer.String(model.TypeColorSuccess)}}, ArtifactKinds: []*model.Type{{Icon: "mdi-server", ID: "asset", Name: "Asset"}, {Icon: "mdi-bullseye", ID: "ioc", Name: "IOC"}}, Timeformat: "YYYY-MM-DDThh:mm:ss"}}},
|
||||||
|
|
||||||
&updateSchema{ID: "update-ticket-collection", Name: "tickets", DataType: "ticket", Schema: `{"properties":{"artifacts":{"items":{"properties":{"enrichments":{"additionalProperties":{"properties":{"created":{"format":"date-time","type":"string"},"data":{"example":{"hash":"b7a067a742c20d07a7456646de89bc2d408a1153"},"properties":{},"type":"object"},"name":{"example":"hash.sha1","type":"string"}},"required":["created","data","name"],"type":"object"},"type":"object"},"name":{"example":"2.2.2.2","type":"string"},"status":{"example":"Unknown","type":"string"},"type":{"type":"string"},"kind":{"type":"string"}},"required":["name"],"type":"object"},"type":"array"},"comments":{"items":{"properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"required":["created","creator","message"],"type":"object"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"example":{"description":"my little incident"},"properties":{},"type":"object"},"files":{"items":{"properties":{"key":{"example":"myfile","type":"string"},"name":{"example":"notes.docx","type":"string"}},"required":["key","name"],"type":"object"},"type":"array"},"modified":{"format":"date-time","type":"string"},"name":{"example":"WannyCry","type":"string"},"owner":{"example":"bob","type":"string"},"playbooks":{"additionalProperties":{"properties":{"name":{"example":"Phishing","type":"string"},"tasks":{"additionalProperties":{"properties":{"automation":{"type":"string"},"closed":{"format":"date-time","type":"string"},"created":{"format":"date-time","type":"string"},"data":{"properties":{},"type":"object"},"done":{"type":"boolean"},"join":{"example":false,"type":"boolean"},"payload":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"example":"Inform user","type":"string"},"next":{"additionalProperties":{"type":"string"},"type":"object"},"owner":{"type":"string"},"schema":{"properties":{},"type":"object"},"type":{"enum":["task","input","automation"],"example":"task","type":"string"}},"required":["created","done","name","type"],"type":"object"},"type":"object"}},"required":["name","tasks"],"type":"object"},"type":"object"},"read":{"example":["bob"],"items":{"type":"string"},"type":"array"},"references":{"items":{"properties":{"href":{"example":"https://cve.mitre.org/cgi-bin/cvename.cgi?name=cve-2017-0144","type":"string"},"name":{"example":"CVE-2017-0144","type":"string"}},"required":["href","name"],"type":"object"},"type":"array"},"schema":{"example":"{}","type":"string"},"status":{"example":"open","type":"string"},"type":{"example":"incident","type":"string"},"write":{"example":["alice"],"items":{"type":"string"},"type":"array"}},"required":["created","modified","name","schema","status","type"],"type":"object"}`},
|
&updateSchema{ID: "update-ticket-collection", Name: "tickets", DataType: "ticket", Schema: `{"properties":{"artifacts":{"items":{"properties":{"enrichments":{"additionalProperties":{"properties":{"created":{"format":"date-time","type":"string"},"data":{"example":{"hash":"b7a067a742c20d07a7456646de89bc2d408a1153"},"properties":{},"type":"object"},"name":{"example":"hash.sha1","type":"string"}},"required":["created","data","name"],"type":"object"},"type":"object"},"name":{"example":"2.2.2.2","type":"string"},"status":{"example":"Unknown","type":"string"},"type":{"type":"string"},"kind":{"type":"string"}},"required":["name"],"type":"object"},"type":"array"},"comments":{"items":{"properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"required":["created","creator","message"],"type":"object"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"example":{"description":"my little incident"},"properties":{},"type":"object"},"files":{"items":{"properties":{"key":{"example":"myfile","type":"string"},"name":{"example":"notes.docx","type":"string"}},"required":["key","name"],"type":"object"},"type":"array"},"modified":{"format":"date-time","type":"string"},"name":{"example":"WannyCry","type":"string"},"owner":{"example":"bob","type":"string"},"playbooks":{"additionalProperties":{"properties":{"name":{"example":"Phishing","type":"string"},"tasks":{"additionalProperties":{"properties":{"automation":{"type":"string"},"closed":{"format":"date-time","type":"string"},"created":{"format":"date-time","type":"string"},"data":{"properties":{},"type":"object"},"done":{"type":"boolean"},"join":{"example":false,"type":"boolean"},"payload":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"example":"Inform user","type":"string"},"next":{"additionalProperties":{"type":"string"},"type":"object"},"owner":{"type":"string"},"schema":{"properties":{},"type":"object"},"type":{"enum":["task","input","automation"],"example":"task","type":"string"}},"required":["created","done","name","type"],"type":"object"},"type":"object"}},"required":["name","tasks"],"type":"object"},"type":"object"},"read":{"example":["bob"],"items":{"type":"string"},"type":"array"},"references":{"items":{"properties":{"href":{"example":"https://cve.mitre.org/cgi-bin/cvename.cgi?name=cve-2017-0144","type":"string"},"name":{"example":"CVE-2017-0144","type":"string"}},"required":["href","name"],"type":"object"},"type":"array"},"schema":{"example":"{}","type":"string"},"status":{"example":"open","type":"string"},"type":{"example":"incident","type":"string"},"write":{"example":["alice"],"items":{"type":"string"},"type":"array"}},"required":["created","modified","name","schema","status","type"],"type":"object"}`},
|
||||||
|
|
||||||
|
&createCollection{ID: "create-dashboard-collection", Name: "dashboards", DataType: "dashboards", Schema: `{"type":"object","properties":{"name":{"type":"string"},"widgets":{"items":{"type":"object","properties":{"aggregation":{"type":"string"},"filter":{"type":"string"},"name":{"type":"string"},"type":{"enum":[ "bar", "line", "pie" ]},"width": { "type": "integer", "minimum": 1, "maximum": 12 }},"required":["name","aggregation", "type", "width"]},"type":"array"}},"required":["name","widgets"]}`},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package database
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/SecurityBrewery/catalyst/caql"
|
||||||
"github.com/SecurityBrewery/catalyst/database/busdb"
|
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||||
)
|
)
|
||||||
@@ -41,3 +43,49 @@ func (db *Database) Statistics(ctx context.Context) (*model.Statistics, error) {
|
|||||||
|
|
||||||
return &statistics, nil
|
return &statistics, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Database) WidgetData(ctx context.Context, aggregation string, filter *string) (map[string]interface{}, error) {
|
||||||
|
parser := &caql.Parser{Searcher: db.Index, Prefix: "d."}
|
||||||
|
|
||||||
|
queryTree, err := parser.Parse(aggregation)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid aggregation query (%s): syntax error\n", aggregation)
|
||||||
|
}
|
||||||
|
aggregationString, err := queryTree.String()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid widget aggregation query (%s): %w", aggregation, err)
|
||||||
|
}
|
||||||
|
aggregation = aggregationString
|
||||||
|
|
||||||
|
filterQ := ""
|
||||||
|
if filter != nil && *filter != "" {
|
||||||
|
queryTree, err := parser.Parse(*filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid filter query (%s): syntax error\n", *filter)
|
||||||
|
}
|
||||||
|
filterString, err := queryTree.String()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid widget filter query (%s): %w", *filter, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filterQ = "FILTER " + filterString
|
||||||
|
}
|
||||||
|
|
||||||
|
query := `RETURN MERGE(FOR d in tickets
|
||||||
|
` + filterQ + `
|
||||||
|
COLLECT field = ` + aggregation + ` WITH COUNT INTO count
|
||||||
|
RETURN ZIP([field], [count]))`
|
||||||
|
|
||||||
|
cur, _, err := db.Query(ctx, query, nil, busdb.ReadOperation)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cur.Close()
|
||||||
|
|
||||||
|
statistics := map[string]interface{}{}
|
||||||
|
if _, err := cur.ReadDocument(ctx, &statistics); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return statistics, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -449,8 +449,6 @@ func (db *Database) TicketDelete(ctx context.Context, ticketID int64) error {
|
|||||||
func (db *Database) TicketList(ctx context.Context, ticketType string, query string, sorts []string, desc []bool, offset, count int64) (*model.TicketList, error) {
|
func (db *Database) TicketList(ctx context.Context, ticketType string, query string, sorts []string, desc []bool, offset, count int64) (*model.TicketList, error) {
|
||||||
binVars := map[string]interface{}{}
|
binVars := map[string]interface{}{}
|
||||||
|
|
||||||
parser := &caql.Parser{Searcher: db.Index, Prefix: "d."}
|
|
||||||
|
|
||||||
var typeString = ""
|
var typeString = ""
|
||||||
if ticketType != "" {
|
if ticketType != "" {
|
||||||
typeString = "FILTER d.type == @type "
|
typeString = "FILTER d.type == @type "
|
||||||
@@ -459,6 +457,7 @@ func (db *Database) TicketList(ctx context.Context, ticketType string, query str
|
|||||||
|
|
||||||
var filterString = ""
|
var filterString = ""
|
||||||
if query != "" {
|
if query != "" {
|
||||||
|
parser := &caql.Parser{Searcher: db.Index, Prefix: "d."}
|
||||||
queryTree, err := parser.Parse(query)
|
queryTree, err := parser.Parse(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("invalid filter query: syntax error")
|
return nil, errors.New("invalid filter query: syntax error")
|
||||||
|
|||||||
167
definition/dashboards.yaml
Normal file
167
definition/dashboards.yaml
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
swagger: "2.0"
|
||||||
|
info: { version: "", title: "" }
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/statistics:
|
||||||
|
get:
|
||||||
|
tags: [ "statistics" ]
|
||||||
|
summary: "Get statistics"
|
||||||
|
operationId: "getStatistics"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "successful operation"
|
||||||
|
schema: { $ref: '#/definitions/Statistics' }
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
unassigned: 0
|
||||||
|
open_tickets_per_user: { }
|
||||||
|
tickets_per_week: { "2021-39": 3 }
|
||||||
|
tickets_per_type: { "alert": 2, "incident": 1 }
|
||||||
|
security: [ { roles: [ "ticket:read" ] } ]
|
||||||
|
|
||||||
|
/dashboards:
|
||||||
|
get:
|
||||||
|
tags: [ "dashboards" ]
|
||||||
|
summary: "List dashboards"
|
||||||
|
operationId: "listDashboards"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "successful operation"
|
||||||
|
schema: { type: array, items: { $ref: "#/definitions/DashboardResponse" } }
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
- id: simple
|
||||||
|
name: Simple
|
||||||
|
widgets:
|
||||||
|
- name: "open_tickets_per_user"
|
||||||
|
aggregation: "owner"
|
||||||
|
filter: 'status == "open"'
|
||||||
|
type: "bar"
|
||||||
|
width: 4
|
||||||
|
- name: "tickets_per_week"
|
||||||
|
aggregation: 'CONCAT(DATE_YEAR(created), "-", DATE_ISOWEEK(created) < 10 ? "0" : "", DATE_ISOWEEK(created))'
|
||||||
|
type: "line"
|
||||||
|
width: 8
|
||||||
|
security: [ { roles: [ "dashboard:read" ] } ]
|
||||||
|
post:
|
||||||
|
tags: [ "dashboards" ]
|
||||||
|
summary: "Create a new dashboard"
|
||||||
|
operationId: "createDashboard"
|
||||||
|
parameters:
|
||||||
|
- { name: "template", in: "body", description: "New template", required: true, schema: { $ref: "#/definitions/Dashboard" }, x-example: { name: "My Dashboard", widgets: [ ] } }
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "successful operation"
|
||||||
|
schema: { $ref: "#/definitions/DashboardResponse" }
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
id: "my-dashboard"
|
||||||
|
name: "My Dashboard"
|
||||||
|
widgets: []
|
||||||
|
security: [ { roles: [ "dashboard:write" ] } ]
|
||||||
|
|
||||||
|
/dashboards/{id}:
|
||||||
|
get:
|
||||||
|
tags: [ "dashboards" ]
|
||||||
|
summary: "Get a single dashboard"
|
||||||
|
operationId: "getDashboard"
|
||||||
|
parameters:
|
||||||
|
- { name: "id", in: "path", description: "Dashboard ID", required: true, type: string, x-example: "simple" }
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "successful operation"
|
||||||
|
schema: { $ref: "#/definitions/DashboardResponse" }
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
id: simple
|
||||||
|
name: Simple
|
||||||
|
widgets:
|
||||||
|
- name: "open_tickets_per_user"
|
||||||
|
aggregation: "owner"
|
||||||
|
filter: 'status == "open"'
|
||||||
|
type: "bar"
|
||||||
|
width: 4
|
||||||
|
- name: "tickets_per_week"
|
||||||
|
aggregation: 'CONCAT(DATE_YEAR(created), "-", DATE_ISOWEEK(created) < 10 ? "0" : "", DATE_ISOWEEK(created))'
|
||||||
|
type: "line"
|
||||||
|
width: 8
|
||||||
|
security: [ { roles: [ "dashboard:read" ] } ]
|
||||||
|
put:
|
||||||
|
tags: [ "dashboards" ]
|
||||||
|
summary: "Update an existing dashboard"
|
||||||
|
operationId: "updateDashboard"
|
||||||
|
parameters:
|
||||||
|
- { name: "id", in: "path", description: "Dashboard ID", required: true, type: string, x-example: "simple" }
|
||||||
|
- { name: "dashboard", in: "body", description: "Dashboard object that needs to be added", required: true, schema: { $ref: "#/definitions/Dashboard" }, x-example: { name: "Simple", widgets: [] } }
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "successful operation"
|
||||||
|
schema: { $ref: "#/definitions/DashboardResponse" }
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
id: simple
|
||||||
|
name: Simple
|
||||||
|
widgets: []
|
||||||
|
security: [ { roles: [ "dashboard:write" ] } ]
|
||||||
|
delete:
|
||||||
|
tags: [ "dashboards" ]
|
||||||
|
summary: "Delete a dashboard"
|
||||||
|
operationId: "deleteDashboard"
|
||||||
|
parameters:
|
||||||
|
- { name: "id", in: "path", description: "Dashboard ID", required: true, type: string, x-example: "simple" }
|
||||||
|
responses:
|
||||||
|
"204": { description: "successful operation" }
|
||||||
|
security: [ { roles: [ "dashboard:write" ] } ]
|
||||||
|
|
||||||
|
/dashboard/data:
|
||||||
|
get:
|
||||||
|
tags: [ "dashboards" ]
|
||||||
|
summary: "Get widget data"
|
||||||
|
operationId: "dashboardData"
|
||||||
|
parameters:
|
||||||
|
- { name: "aggregation", in: "query", description: "Aggregation", required: true, type: string, x-example: "type" }
|
||||||
|
- { name: "filter", in: "query", description: "Filter", type: string, x-example: 'status == "closed"' }
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "successful operation"
|
||||||
|
schema: { type: object }
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
alert: 2
|
||||||
|
incident: 1
|
||||||
|
security: [ { roles: [ "dashboard:read" ] } ]
|
||||||
|
|
||||||
|
definitions:
|
||||||
|
Statistics:
|
||||||
|
type: object
|
||||||
|
required: [ unassigned, open_tickets_per_user, tickets_per_week, tickets_per_type ]
|
||||||
|
properties:
|
||||||
|
unassigned: { type: integer }
|
||||||
|
open_tickets_per_user: { type: object, additionalProperties: { type: integer } }
|
||||||
|
tickets_per_week: { type: object, additionalProperties: { type: integer } }
|
||||||
|
tickets_per_type: { type: object, additionalProperties: { type: integer } }
|
||||||
|
|
||||||
|
Dashboard:
|
||||||
|
type: object
|
||||||
|
required: [ name, widgets ]
|
||||||
|
properties:
|
||||||
|
name: { type: string }
|
||||||
|
widgets: { type: array, items: { $ref: "#/definitions/Widget" } }
|
||||||
|
|
||||||
|
DashboardResponse:
|
||||||
|
type: object
|
||||||
|
required: [ id, name, widgets ]
|
||||||
|
properties:
|
||||||
|
id: { type: string }
|
||||||
|
name: { type: string }
|
||||||
|
widgets: { type: array, items: { $ref: "#/definitions/Widget" } }
|
||||||
|
|
||||||
|
Widget:
|
||||||
|
type: object
|
||||||
|
required: [ name, type, aggregation, width ]
|
||||||
|
properties:
|
||||||
|
name: { type: string }
|
||||||
|
type: { type: string, enum: [ "bar", "line", "pie" ] }
|
||||||
|
filter: { type: string }
|
||||||
|
aggregation: { type: string }
|
||||||
|
width: { type: integer, minimum: 1, maximum: 12 }
|
||||||
@@ -29,10 +29,10 @@ paths:
|
|||||||
- { icon: "mdi-skull", id: "malicious", name: "Malicious", color: "error" }
|
- { icon: "mdi-skull", id: "malicious", name: "Malicious", color: "error" }
|
||||||
- { icon: "mdi-check", id: "clean", name: "Clean", color: "success" }
|
- { icon: "mdi-check", id: "clean", name: "Clean", color: "success" }
|
||||||
roles: [
|
roles: [
|
||||||
"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write",
|
"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write",
|
||||||
"admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read",
|
"admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read",
|
||||||
"admin:userdata:write", "analyst:automation:read",
|
"admin:userdata:write", "analyst:automation:read",
|
||||||
"analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read",
|
"analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read",
|
||||||
"analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read",
|
"analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read",
|
||||||
"analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write",
|
"analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write",
|
||||||
"analyst:tickettype:read", "analyst:user:read", "engineer:automation:write",
|
"analyst:tickettype:read", "analyst:user:read", "engineer:automation:write",
|
||||||
@@ -67,10 +67,10 @@ paths:
|
|||||||
- { icon: "mdi-skull", id: "malicious", name: "Malicious", color: "error" }
|
- { icon: "mdi-skull", id: "malicious", name: "Malicious", color: "error" }
|
||||||
- { icon: "mdi-check", id: "clean", name: "Clean", color: "success" }
|
- { icon: "mdi-check", id: "clean", name: "Clean", color: "success" }
|
||||||
roles: [
|
roles: [
|
||||||
"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write",
|
"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write",
|
||||||
"admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read",
|
"admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read",
|
||||||
"admin:userdata:write", "analyst:automation:read",
|
"admin:userdata:write", "analyst:automation:read",
|
||||||
"analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read",
|
"analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read",
|
||||||
"analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read",
|
"analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read",
|
||||||
"analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write",
|
"analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write",
|
||||||
"analyst:tickettype:read", "analyst:user:read", "engineer:automation:write",
|
"analyst:tickettype:read", "analyst:user:read", "engineer:automation:write",
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
swagger: "2.0"
|
|
||||||
info: { version: "", title: "" }
|
|
||||||
|
|
||||||
paths:
|
|
||||||
/statistics:
|
|
||||||
get:
|
|
||||||
tags: [ "statistics" ]
|
|
||||||
summary: "Get statistics"
|
|
||||||
operationId: "getStatistics"
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: "successful operation"
|
|
||||||
schema: { $ref: '#/definitions/Statistics' }
|
|
||||||
examples:
|
|
||||||
test:
|
|
||||||
unassigned: 0
|
|
||||||
open_tickets_per_user: { }
|
|
||||||
tickets_per_week: { "2021-39": 3 }
|
|
||||||
tickets_per_type: { "alert": 2, "incident": 1 }
|
|
||||||
security: [ { roles: [ "ticket:read" ] } ]
|
|
||||||
|
|
||||||
definitions:
|
|
||||||
|
|
||||||
Statistics:
|
|
||||||
type: object
|
|
||||||
required: [ unassigned, open_tickets_per_user, tickets_per_week, tickets_per_type ]
|
|
||||||
properties:
|
|
||||||
unassigned: { type: integer }
|
|
||||||
open_tickets_per_user: { type: object, additionalProperties: { type: integer } }
|
|
||||||
tickets_per_week: { type: object, additionalProperties: { type: integer } }
|
|
||||||
tickets_per_type: { type: object, additionalProperties: { type: integer } }
|
|
||||||
@@ -12,7 +12,7 @@ paths:
|
|||||||
description: "successful operation"
|
description: "successful operation"
|
||||||
schema: { $ref: "#/definitions/UserResponse" }
|
schema: { $ref: "#/definitions/UserResponse" }
|
||||||
examples:
|
examples:
|
||||||
test: { id: bob, roles: [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: false }
|
test: { id: bob, roles: [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read","analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: false }
|
||||||
security: [ { roles: [ "currentuser:read" ] } ]
|
security: [ { roles: [ "currentuser:read" ] } ]
|
||||||
|
|
||||||
/users:
|
/users:
|
||||||
@@ -26,8 +26,8 @@ paths:
|
|||||||
schema: { type: array, items: { $ref: "#/definitions/UserResponse" } }
|
schema: { type: array, items: { $ref: "#/definitions/UserResponse" } }
|
||||||
examples:
|
examples:
|
||||||
test:
|
test:
|
||||||
- { id: bob, blocked: false, roles: [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], apikey: false }
|
- { id: bob, blocked: false, roles: [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], apikey: false }
|
||||||
- { id: script, roles: [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: true }
|
- { id: script, roles: [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: true }
|
||||||
security: [ { roles: [ "user:read" ] } ]
|
security: [ { roles: [ "user:read" ] } ]
|
||||||
post:
|
post:
|
||||||
tags: [ "users" ]
|
tags: [ "users" ]
|
||||||
@@ -40,7 +40,7 @@ paths:
|
|||||||
description: "successful operation"
|
description: "successful operation"
|
||||||
schema: { $ref: "#/definitions/NewUserResponse" }
|
schema: { $ref: "#/definitions/NewUserResponse" }
|
||||||
examples:
|
examples:
|
||||||
test: { id: "syncscript", roles: [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read" ], secret: "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH", blocked: false }
|
test: { id: "syncscript", roles: [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read" ], secret: "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH", blocked: false }
|
||||||
security: [ { roles: [ "user:write" ] } ]
|
security: [ { roles: [ "user:write" ] } ]
|
||||||
/users/{id}:
|
/users/{id}:
|
||||||
get:
|
get:
|
||||||
@@ -54,7 +54,7 @@ paths:
|
|||||||
description: "successful operation"
|
description: "successful operation"
|
||||||
schema: { $ref: "#/definitions/UserResponse" }
|
schema: { $ref: "#/definitions/UserResponse" }
|
||||||
examples:
|
examples:
|
||||||
test: { id: "script", roles: [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: true }
|
test: { id: "script", roles: [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: true }
|
||||||
security: [ { roles: [ "user:read" ] } ]
|
security: [ { roles: [ "user:read" ] } ]
|
||||||
put:
|
put:
|
||||||
tags: [ "users" ]
|
tags: [ "users" ]
|
||||||
@@ -70,7 +70,7 @@ paths:
|
|||||||
examples:
|
examples:
|
||||||
test:
|
test:
|
||||||
id: bob
|
id: bob
|
||||||
roles: [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
roles: [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||||
apikey: false
|
apikey: false
|
||||||
blocked: false
|
blocked: false
|
||||||
security: [ { roles: [ "user:write" ] } ]
|
security: [ { roles: [ "user:write" ] } ]
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ type Service interface {
|
|||||||
CurrentUser(context.Context) (*model.UserResponse, error)
|
CurrentUser(context.Context) (*model.UserResponse, error)
|
||||||
CurrentUserData(context.Context) (*model.UserDataResponse, error)
|
CurrentUserData(context.Context) (*model.UserDataResponse, error)
|
||||||
UpdateCurrentUserData(context.Context, *model.UserData) (*model.UserDataResponse, error)
|
UpdateCurrentUserData(context.Context, *model.UserData) (*model.UserDataResponse, error)
|
||||||
|
DashboardData(context.Context, string, *string) (map[string]interface{}, error)
|
||||||
|
ListDashboards(context.Context) ([]*model.DashboardResponse, error)
|
||||||
|
CreateDashboard(context.Context, *model.Dashboard) (*model.DashboardResponse, error)
|
||||||
|
GetDashboard(context.Context, string) (*model.DashboardResponse, error)
|
||||||
|
UpdateDashboard(context.Context, string, *model.Dashboard) (*model.DashboardResponse, error)
|
||||||
|
DeleteDashboard(context.Context, string) error
|
||||||
ListJobs(context.Context) ([]*model.JobResponse, error)
|
ListJobs(context.Context) ([]*model.JobResponse, error)
|
||||||
RunJob(context.Context, *model.JobForm) (*model.JobResponse, error)
|
RunJob(context.Context, *model.JobForm) (*model.JobResponse, error)
|
||||||
GetJob(context.Context, string) (*model.JobResponse, error)
|
GetJob(context.Context, string) (*model.JobResponse, error)
|
||||||
@@ -91,6 +97,12 @@ func NewServer(service Service, roleAuth func([]string) func(http.Handler) http.
|
|||||||
r.With(roleAuth([]string{"currentuser:read"})).Get("/currentuser", s.currentUserHandler)
|
r.With(roleAuth([]string{"currentuser:read"})).Get("/currentuser", s.currentUserHandler)
|
||||||
r.With(roleAuth([]string{"currentuserdata:read"})).Get("/currentuserdata", s.currentUserDataHandler)
|
r.With(roleAuth([]string{"currentuserdata:read"})).Get("/currentuserdata", s.currentUserDataHandler)
|
||||||
r.With(roleAuth([]string{"currentuserdata:write"})).Put("/currentuserdata", s.updateCurrentUserDataHandler)
|
r.With(roleAuth([]string{"currentuserdata:write"})).Put("/currentuserdata", s.updateCurrentUserDataHandler)
|
||||||
|
r.With(roleAuth([]string{"dashboard:read"})).Get("/dashboard/data", s.dashboardDataHandler)
|
||||||
|
r.With(roleAuth([]string{"dashboard:read"})).Get("/dashboards", s.listDashboardsHandler)
|
||||||
|
r.With(roleAuth([]string{"dashboard:write"})).Post("/dashboards", s.createDashboardHandler)
|
||||||
|
r.With(roleAuth([]string{"dashboard:read"})).Get("/dashboards/{id}", s.getDashboardHandler)
|
||||||
|
r.With(roleAuth([]string{"dashboard:write"})).Put("/dashboards/{id}", s.updateDashboardHandler)
|
||||||
|
r.With(roleAuth([]string{"dashboard:write"})).Delete("/dashboards/{id}", s.deleteDashboardHandler)
|
||||||
r.With(roleAuth([]string{"job:read"})).Get("/jobs", s.listJobsHandler)
|
r.With(roleAuth([]string{"job:read"})).Get("/jobs", s.listJobsHandler)
|
||||||
r.With(roleAuth([]string{"job:write"})).Post("/jobs", s.runJobHandler)
|
r.With(roleAuth([]string{"job:write"})).Post("/jobs", s.runJobHandler)
|
||||||
r.With(roleAuth([]string{"job:read"})).Get("/jobs/{id}", s.getJobHandler)
|
r.With(roleAuth([]string{"job:read"})).Get("/jobs/{id}", s.getJobHandler)
|
||||||
@@ -247,6 +259,77 @@ func (s *server) updateCurrentUserDataHandler(w http.ResponseWriter, r *http.Req
|
|||||||
response(w, result, err)
|
response(w, result, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *server) dashboardDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
aggregationP := r.URL.Query().Get("aggregation")
|
||||||
|
|
||||||
|
filterP := r.URL.Query().Get("filter")
|
||||||
|
|
||||||
|
result, err := s.service.DashboardData(r.Context(), aggregationP, &filterP)
|
||||||
|
response(w, result, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) listDashboardsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
result, err := s.service.ListDashboards(r.Context())
|
||||||
|
response(w, result, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) createDashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
body, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
JSONError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if validateSchema(body, model.DashboardSchema, w) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var templateP *model.Dashboard
|
||||||
|
if err := parseBody(body, &templateP); err != nil {
|
||||||
|
JSONError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := s.service.CreateDashboard(r.Context(), templateP)
|
||||||
|
response(w, result, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) getDashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
idP := chi.URLParam(r, "id")
|
||||||
|
|
||||||
|
result, err := s.service.GetDashboard(r.Context(), idP)
|
||||||
|
response(w, result, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) updateDashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
idP := chi.URLParam(r, "id")
|
||||||
|
|
||||||
|
body, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
JSONError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if validateSchema(body, model.DashboardSchema, w) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var dashboardP *model.Dashboard
|
||||||
|
if err := parseBody(body, &dashboardP); err != nil {
|
||||||
|
JSONError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := s.service.UpdateDashboard(r.Context(), idP, dashboardP)
|
||||||
|
response(w, result, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) deleteDashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
idP := chi.URLParam(r, "id")
|
||||||
|
|
||||||
|
response(w, nil, s.service.DeleteDashboard(r.Context(), idP))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *server) listJobsHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *server) listJobsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
result, err := s.service.ListJobs(r.Context())
|
result, err := s.service.ListJobs(r.Context())
|
||||||
response(w, result, err)
|
response(w, result, err)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ var Tests = []struct {
|
|||||||
Args: Args{Method: "Get", URL: "/currentuser"},
|
Args: Args{Method: "Get", URL: "/currentuser"},
|
||||||
Want: Want{
|
Want: Want{
|
||||||
Status: 200,
|
Status: 200,
|
||||||
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
|
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -90,6 +90,60 @@ var Tests = []struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "DashboardData",
|
||||||
|
Args: Args{Method: "Get", URL: "/dashboard/data?aggregation=type&filter=status+%3D%3D+%22closed%22"},
|
||||||
|
Want: Want{
|
||||||
|
Status: 200,
|
||||||
|
Body: map[string]interface{}{"alert": 2, "incident": 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "ListDashboards",
|
||||||
|
Args: Args{Method: "Get", URL: "/dashboards"},
|
||||||
|
Want: Want{
|
||||||
|
Status: 200,
|
||||||
|
Body: []interface{}{map[string]interface{}{"id": "simple", "name": "Simple", "widgets": []interface{}{map[string]interface{}{"aggregation": "owner", "filter": "status == \"open\"", "name": "open_tickets_per_user", "type": "bar", "width": 4}, map[string]interface{}{"aggregation": "CONCAT(DATE_YEAR(created), \"-\", DATE_ISOWEEK(created) < 10 ? \"0\" : \"\", DATE_ISOWEEK(created))", "name": "tickets_per_week", "type": "line", "width": 8}}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "CreateDashboard",
|
||||||
|
Args: Args{Method: "Post", URL: "/dashboards", Data: map[string]interface{}{"name": "My Dashboard", "widgets": []interface{}{}}},
|
||||||
|
Want: Want{
|
||||||
|
Status: 200,
|
||||||
|
Body: map[string]interface{}{"id": "my-dashboard", "name": "My Dashboard", "widgets": []interface{}{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "GetDashboard",
|
||||||
|
Args: Args{Method: "Get", URL: "/dashboards/simple"},
|
||||||
|
Want: Want{
|
||||||
|
Status: 200,
|
||||||
|
Body: map[string]interface{}{"id": "simple", "name": "Simple", "widgets": []interface{}{map[string]interface{}{"aggregation": "owner", "filter": "status == \"open\"", "name": "open_tickets_per_user", "type": "bar", "width": 4}, map[string]interface{}{"aggregation": "CONCAT(DATE_YEAR(created), \"-\", DATE_ISOWEEK(created) < 10 ? \"0\" : \"\", DATE_ISOWEEK(created))", "name": "tickets_per_week", "type": "line", "width": 8}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "UpdateDashboard",
|
||||||
|
Args: Args{Method: "Put", URL: "/dashboards/simple", Data: map[string]interface{}{"name": "Simple", "widgets": []interface{}{}}},
|
||||||
|
Want: Want{
|
||||||
|
Status: 200,
|
||||||
|
Body: map[string]interface{}{"id": "simple", "name": "Simple", "widgets": []interface{}{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "DeleteDashboard",
|
||||||
|
Args: Args{Method: "Delete", URL: "/dashboards/simple"},
|
||||||
|
Want: Want{
|
||||||
|
Status: 204,
|
||||||
|
Body: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
Name: "ListJobs",
|
Name: "ListJobs",
|
||||||
Args: Args{Method: "Get", URL: "/jobs"},
|
Args: Args{Method: "Get", URL: "/jobs"},
|
||||||
@@ -185,7 +239,7 @@ var Tests = []struct {
|
|||||||
Args: Args{Method: "Get", URL: "/settings"},
|
Args: Args{Method: "Get", URL: "/settings"},
|
||||||
Want: Want{
|
Want: Want{
|
||||||
Status: 200,
|
Status: 200,
|
||||||
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "YYYY-MM-DDThh:mm:ss", "version": "0.0.0-test"},
|
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "YYYY-MM-DDThh:mm:ss", "version": "0.0.0-test"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -194,7 +248,7 @@ var Tests = []struct {
|
|||||||
Args: Args{Method: "Post", URL: "/settings", Data: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "timeformat": "YYYY-MM-DDThh:mm:ss"}},
|
Args: Args{Method: "Post", URL: "/settings", Data: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "timeformat": "YYYY-MM-DDThh:mm:ss"}},
|
||||||
Want: Want{
|
Want: Want{
|
||||||
Status: 200,
|
Status: 200,
|
||||||
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "YYYY-MM-DDThh:mm:ss", "version": "0.0.0-test"},
|
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "YYYY-MM-DDThh:mm:ss", "version": "0.0.0-test"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -554,7 +608,7 @@ var Tests = []struct {
|
|||||||
Args: Args{Method: "Get", URL: "/users"},
|
Args: Args{Method: "Get", URL: "/users"},
|
||||||
Want: Want{
|
Want: Want{
|
||||||
Status: 200,
|
Status: 200,
|
||||||
Body: []interface{}{map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}, map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}},
|
Body: []interface{}{map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}, map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -563,7 +617,7 @@ var Tests = []struct {
|
|||||||
Args: Args{Method: "Post", URL: "/users", Data: map[string]interface{}{"apikey": true, "blocked": false, "id": "syncscript", "roles": []interface{}{"analyst"}}},
|
Args: Args{Method: "Post", URL: "/users", Data: map[string]interface{}{"apikey": true, "blocked": false, "id": "syncscript", "roles": []interface{}{"analyst"}}},
|
||||||
Want: Want{
|
Want: Want{
|
||||||
Status: 200,
|
Status: 200,
|
||||||
Body: map[string]interface{}{"blocked": false, "id": "syncscript", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read"}, "secret": "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH"},
|
Body: map[string]interface{}{"blocked": false, "id": "syncscript", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read"}, "secret": "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -572,7 +626,7 @@ var Tests = []struct {
|
|||||||
Args: Args{Method: "Get", URL: "/users/script"},
|
Args: Args{Method: "Get", URL: "/users/script"},
|
||||||
Want: Want{
|
Want: Want{
|
||||||
Status: 200,
|
Status: 200,
|
||||||
Body: map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
|
Body: map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -581,7 +635,7 @@ var Tests = []struct {
|
|||||||
Args: Args{Method: "Put", URL: "/users/bob", Data: map[string]interface{}{"apikey": false, "blocked": false, "id": "syncscript", "roles": []interface{}{"analyst", "admin"}}},
|
Args: Args{Method: "Put", URL: "/users/bob", Data: map[string]interface{}{"apikey": false, "blocked": false, "id": "syncscript", "roles": []interface{}{"analyst", "admin"}}},
|
||||||
Want: Want{
|
Want: Want{
|
||||||
Status: 200,
|
Status: 200,
|
||||||
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
|
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -225,7 +225,7 @@
|
|||||||
"apikey" : false,
|
"apikey" : false,
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "bob",
|
"id" : "bob",
|
||||||
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -307,6 +307,257 @@
|
|||||||
"x-codegen-request-body-name" : "userdata"
|
"x-codegen-request-body-name" : "userdata"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/dashboard/data" : {
|
||||||
|
"get" : {
|
||||||
|
"operationId" : "dashboardData",
|
||||||
|
"parameters" : [ {
|
||||||
|
"description" : "Aggregation",
|
||||||
|
"example" : "type",
|
||||||
|
"in" : "query",
|
||||||
|
"name" : "aggregation",
|
||||||
|
"required" : true,
|
||||||
|
"schema" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"description" : "Filter",
|
||||||
|
"example" : "status == \"closed\"",
|
||||||
|
"in" : "query",
|
||||||
|
"name" : "filter",
|
||||||
|
"schema" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
} ],
|
||||||
|
"responses" : {
|
||||||
|
"200" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"type" : "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test" : {
|
||||||
|
"example" : {
|
||||||
|
"alert" : 2,
|
||||||
|
"incident" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:read" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "Get widget data",
|
||||||
|
"tags" : [ "dashboards" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/dashboards" : {
|
||||||
|
"get" : {
|
||||||
|
"operationId" : "listDashboards",
|
||||||
|
"responses" : {
|
||||||
|
"200" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"items" : {
|
||||||
|
"$ref" : "#/components/schemas/DashboardResponse"
|
||||||
|
},
|
||||||
|
"type" : "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test" : {
|
||||||
|
"example" : [ {
|
||||||
|
"id" : "simple",
|
||||||
|
"name" : "Simple",
|
||||||
|
"widgets" : [ {
|
||||||
|
"aggregation" : "owner",
|
||||||
|
"filter" : "status == \"open\"",
|
||||||
|
"name" : "open_tickets_per_user",
|
||||||
|
"type" : "bar",
|
||||||
|
"width" : 4
|
||||||
|
}, {
|
||||||
|
"aggregation" : "CONCAT(DATE_YEAR(created), \"-\", DATE_ISOWEEK(created) < 10 ? \"0\" : \"\", DATE_ISOWEEK(created))",
|
||||||
|
"name" : "tickets_per_week",
|
||||||
|
"type" : "line",
|
||||||
|
"width" : 8
|
||||||
|
} ]
|
||||||
|
} ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:read" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "List dashboards",
|
||||||
|
"tags" : [ "dashboards" ]
|
||||||
|
},
|
||||||
|
"post" : {
|
||||||
|
"operationId" : "createDashboard",
|
||||||
|
"requestBody" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"$ref" : "#/components/schemas/Dashboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "New template",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"responses" : {
|
||||||
|
"200" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"$ref" : "#/components/schemas/DashboardResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test" : {
|
||||||
|
"example" : {
|
||||||
|
"id" : "my-dashboard",
|
||||||
|
"name" : "My Dashboard",
|
||||||
|
"widgets" : [ ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:write" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "Create a new dashboard",
|
||||||
|
"tags" : [ "dashboards" ],
|
||||||
|
"x-codegen-request-body-name" : "template"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/dashboards/{id}" : {
|
||||||
|
"delete" : {
|
||||||
|
"operationId" : "deleteDashboard",
|
||||||
|
"parameters" : [ {
|
||||||
|
"description" : "Dashboard ID",
|
||||||
|
"example" : "simple",
|
||||||
|
"in" : "path",
|
||||||
|
"name" : "id",
|
||||||
|
"required" : true,
|
||||||
|
"schema" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
} ],
|
||||||
|
"responses" : {
|
||||||
|
"204" : {
|
||||||
|
"content" : { },
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:write" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "Delete a dashboard",
|
||||||
|
"tags" : [ "dashboards" ]
|
||||||
|
},
|
||||||
|
"get" : {
|
||||||
|
"operationId" : "getDashboard",
|
||||||
|
"parameters" : [ {
|
||||||
|
"description" : "Dashboard ID",
|
||||||
|
"example" : "simple",
|
||||||
|
"in" : "path",
|
||||||
|
"name" : "id",
|
||||||
|
"required" : true,
|
||||||
|
"schema" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
} ],
|
||||||
|
"responses" : {
|
||||||
|
"200" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"$ref" : "#/components/schemas/DashboardResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test" : {
|
||||||
|
"example" : {
|
||||||
|
"id" : "simple",
|
||||||
|
"name" : "Simple",
|
||||||
|
"widgets" : [ {
|
||||||
|
"aggregation" : "owner",
|
||||||
|
"filter" : "status == \"open\"",
|
||||||
|
"name" : "open_tickets_per_user",
|
||||||
|
"type" : "bar",
|
||||||
|
"width" : 4
|
||||||
|
}, {
|
||||||
|
"aggregation" : "CONCAT(DATE_YEAR(created), \"-\", DATE_ISOWEEK(created) < 10 ? \"0\" : \"\", DATE_ISOWEEK(created))",
|
||||||
|
"name" : "tickets_per_week",
|
||||||
|
"type" : "line",
|
||||||
|
"width" : 8
|
||||||
|
} ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:read" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "Get a single dashboard",
|
||||||
|
"tags" : [ "dashboards" ]
|
||||||
|
},
|
||||||
|
"put" : {
|
||||||
|
"operationId" : "updateDashboard",
|
||||||
|
"parameters" : [ {
|
||||||
|
"description" : "Dashboard ID",
|
||||||
|
"example" : "simple",
|
||||||
|
"in" : "path",
|
||||||
|
"name" : "id",
|
||||||
|
"required" : true,
|
||||||
|
"schema" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
} ],
|
||||||
|
"requestBody" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"$ref" : "#/components/schemas/Dashboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "Dashboard object that needs to be added",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"responses" : {
|
||||||
|
"200" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"$ref" : "#/components/schemas/DashboardResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test" : {
|
||||||
|
"example" : {
|
||||||
|
"id" : "simple",
|
||||||
|
"name" : "Simple",
|
||||||
|
"widgets" : [ ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:write" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "Update an existing dashboard",
|
||||||
|
"tags" : [ "dashboards" ],
|
||||||
|
"x-codegen-request-body-name" : "dashboard"
|
||||||
|
}
|
||||||
|
},
|
||||||
"/graph/{col}/{id}" : {
|
"/graph/{col}/{id}" : {
|
||||||
"get" : {
|
"get" : {
|
||||||
"operationId" : "graph",
|
"operationId" : "graph",
|
||||||
@@ -1173,7 +1424,7 @@
|
|||||||
"id" : "clean",
|
"id" : "clean",
|
||||||
"name" : "Clean"
|
"name" : "Clean"
|
||||||
} ],
|
} ],
|
||||||
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
|
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
|
||||||
"ticketTypes" : [ {
|
"ticketTypes" : [ {
|
||||||
"default_playbooks" : [ ],
|
"default_playbooks" : [ ],
|
||||||
"default_template" : "default",
|
"default_template" : "default",
|
||||||
@@ -1262,7 +1513,7 @@
|
|||||||
"id" : "clean",
|
"id" : "clean",
|
||||||
"name" : "Clean"
|
"name" : "Clean"
|
||||||
} ],
|
} ],
|
||||||
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
|
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
|
||||||
"ticketTypes" : [ {
|
"ticketTypes" : [ {
|
||||||
"default_playbooks" : [ ],
|
"default_playbooks" : [ ],
|
||||||
"default_template" : "default",
|
"default_template" : "default",
|
||||||
@@ -5092,12 +5343,12 @@
|
|||||||
"apikey" : false,
|
"apikey" : false,
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "bob",
|
"id" : "bob",
|
||||||
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||||
}, {
|
}, {
|
||||||
"apikey" : true,
|
"apikey" : true,
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "script",
|
"id" : "script",
|
||||||
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||||
} ]
|
} ]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5135,7 +5386,7 @@
|
|||||||
"example" : {
|
"example" : {
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "syncscript",
|
"id" : "syncscript",
|
||||||
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read" ],
|
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read" ],
|
||||||
"secret" : "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH"
|
"secret" : "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5201,7 +5452,7 @@
|
|||||||
"apikey" : true,
|
"apikey" : true,
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "script",
|
"id" : "script",
|
||||||
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5250,7 +5501,7 @@
|
|||||||
"apikey" : false,
|
"apikey" : false,
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "bob",
|
"id" : "bob",
|
||||||
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5431,6 +5682,39 @@
|
|||||||
},
|
},
|
||||||
"type" : "object"
|
"type" : "object"
|
||||||
},
|
},
|
||||||
|
"Dashboard" : {
|
||||||
|
"properties" : {
|
||||||
|
"name" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"widgets" : {
|
||||||
|
"items" : {
|
||||||
|
"$ref" : "#/components/schemas/Widget"
|
||||||
|
},
|
||||||
|
"type" : "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required" : [ "name", "widgets" ],
|
||||||
|
"type" : "object"
|
||||||
|
},
|
||||||
|
"DashboardResponse" : {
|
||||||
|
"properties" : {
|
||||||
|
"id" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"name" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"widgets" : {
|
||||||
|
"items" : {
|
||||||
|
"$ref" : "#/components/schemas/Widget"
|
||||||
|
},
|
||||||
|
"type" : "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required" : [ "id", "name", "widgets" ],
|
||||||
|
"type" : "object"
|
||||||
|
},
|
||||||
"Enrichment" : {
|
"Enrichment" : {
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"created" : {
|
"created" : {
|
||||||
@@ -6895,6 +7179,30 @@
|
|||||||
},
|
},
|
||||||
"required" : [ "apikey", "blocked", "id", "roles" ],
|
"required" : [ "apikey", "blocked", "id", "roles" ],
|
||||||
"type" : "object"
|
"type" : "object"
|
||||||
|
},
|
||||||
|
"Widget" : {
|
||||||
|
"properties" : {
|
||||||
|
"aggregation" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"filter" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"name" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"type" : {
|
||||||
|
"enum" : [ "bar", "line", "pie" ],
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"width" : {
|
||||||
|
"maximum" : 12,
|
||||||
|
"minimum" : 1,
|
||||||
|
"type" : "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required" : [ "aggregation", "name", "type", "width" ],
|
||||||
|
"type" : "object"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -141,6 +141,33 @@ definitions:
|
|||||||
ticket:
|
ticket:
|
||||||
$ref: '#/definitions/TicketResponse'
|
$ref: '#/definitions/TicketResponse'
|
||||||
type: object
|
type: object
|
||||||
|
Dashboard:
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
widgets:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Widget'
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- widgets
|
||||||
|
type: object
|
||||||
|
DashboardResponse:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
widgets:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Widget'
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
- widgets
|
||||||
|
type: object
|
||||||
Enrichment:
|
Enrichment:
|
||||||
properties:
|
properties:
|
||||||
created:
|
created:
|
||||||
@@ -1335,6 +1362,30 @@ definitions:
|
|||||||
- roles
|
- roles
|
||||||
- apikey
|
- apikey
|
||||||
type: object
|
type: object
|
||||||
|
Widget:
|
||||||
|
properties:
|
||||||
|
aggregation:
|
||||||
|
type: string
|
||||||
|
filter:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
enum:
|
||||||
|
- bar
|
||||||
|
- line
|
||||||
|
- pie
|
||||||
|
type: string
|
||||||
|
width:
|
||||||
|
maximum: 12
|
||||||
|
minimum: 1
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- type
|
||||||
|
- aggregation
|
||||||
|
- width
|
||||||
|
type: object
|
||||||
host: .
|
host: .
|
||||||
info:
|
info:
|
||||||
description: API for the catalyst incident response platform.
|
description: API for the catalyst incident response platform.
|
||||||
@@ -1576,6 +1627,7 @@ paths:
|
|||||||
roles:
|
roles:
|
||||||
- admin:backup:read
|
- admin:backup:read
|
||||||
- admin:backup:restore
|
- admin:backup:restore
|
||||||
|
- admin:dashboard:write
|
||||||
- admin:group:write
|
- admin:group:write
|
||||||
- admin:job:read
|
- admin:job:read
|
||||||
- admin:job:write
|
- admin:job:write
|
||||||
@@ -1589,6 +1641,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -1659,6 +1712,183 @@ paths:
|
|||||||
summary: Update current user data
|
summary: Update current user data
|
||||||
tags:
|
tags:
|
||||||
- userdata
|
- userdata
|
||||||
|
/dashboard/data:
|
||||||
|
get:
|
||||||
|
operationId: dashboardData
|
||||||
|
parameters:
|
||||||
|
- description: Aggregation
|
||||||
|
in: query
|
||||||
|
name: aggregation
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
x-example: type
|
||||||
|
- description: Filter
|
||||||
|
in: query
|
||||||
|
name: filter
|
||||||
|
type: string
|
||||||
|
x-example: status == "closed"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
alert: 2
|
||||||
|
incident: 1
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:read
|
||||||
|
summary: Get widget data
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
|
/dashboards:
|
||||||
|
get:
|
||||||
|
operationId: listDashboards
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
- id: simple
|
||||||
|
name: Simple
|
||||||
|
widgets:
|
||||||
|
- aggregation: owner
|
||||||
|
filter: status == "open"
|
||||||
|
name: open_tickets_per_user
|
||||||
|
type: bar
|
||||||
|
width: 4
|
||||||
|
- aggregation: 'CONCAT(DATE_YEAR(created), "-", DATE_ISOWEEK(created)
|
||||||
|
< 10 ? "0" : "", DATE_ISOWEEK(created))'
|
||||||
|
name: tickets_per_week
|
||||||
|
type: line
|
||||||
|
width: 8
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/DashboardResponse'
|
||||||
|
type: array
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:read
|
||||||
|
summary: List dashboards
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
|
post:
|
||||||
|
operationId: createDashboard
|
||||||
|
parameters:
|
||||||
|
- description: New template
|
||||||
|
in: body
|
||||||
|
name: template
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Dashboard'
|
||||||
|
x-example:
|
||||||
|
name: My Dashboard
|
||||||
|
widgets: []
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
id: my-dashboard
|
||||||
|
name: My Dashboard
|
||||||
|
widgets: []
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/DashboardResponse'
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:write
|
||||||
|
summary: Create a new dashboard
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
|
/dashboards/{id}:
|
||||||
|
delete:
|
||||||
|
operationId: deleteDashboard
|
||||||
|
parameters:
|
||||||
|
- description: Dashboard ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
x-example: simple
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: successful operation
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:write
|
||||||
|
summary: Delete a dashboard
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
|
get:
|
||||||
|
operationId: getDashboard
|
||||||
|
parameters:
|
||||||
|
- description: Dashboard ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
x-example: simple
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
id: simple
|
||||||
|
name: Simple
|
||||||
|
widgets:
|
||||||
|
- aggregation: owner
|
||||||
|
filter: status == "open"
|
||||||
|
name: open_tickets_per_user
|
||||||
|
type: bar
|
||||||
|
width: 4
|
||||||
|
- aggregation: 'CONCAT(DATE_YEAR(created), "-", DATE_ISOWEEK(created)
|
||||||
|
< 10 ? "0" : "", DATE_ISOWEEK(created))'
|
||||||
|
name: tickets_per_week
|
||||||
|
type: line
|
||||||
|
width: 8
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/DashboardResponse'
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:read
|
||||||
|
summary: Get a single dashboard
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
|
put:
|
||||||
|
operationId: updateDashboard
|
||||||
|
parameters:
|
||||||
|
- description: Dashboard ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
x-example: simple
|
||||||
|
- description: Dashboard object that needs to be added
|
||||||
|
in: body
|
||||||
|
name: dashboard
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Dashboard'
|
||||||
|
x-example:
|
||||||
|
name: Simple
|
||||||
|
widgets: []
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
id: simple
|
||||||
|
name: Simple
|
||||||
|
widgets: []
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/DashboardResponse'
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:write
|
||||||
|
summary: Update an existing dashboard
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
/graph/{col}/{id}:
|
/graph/{col}/{id}:
|
||||||
get:
|
get:
|
||||||
operationId: graph
|
operationId: graph
|
||||||
@@ -2601,6 +2831,7 @@ paths:
|
|||||||
roles:
|
roles:
|
||||||
- admin:backup:read
|
- admin:backup:read
|
||||||
- admin:backup:restore
|
- admin:backup:restore
|
||||||
|
- admin:dashboard:write
|
||||||
- admin:group:write
|
- admin:group:write
|
||||||
- admin:job:read
|
- admin:job:read
|
||||||
- admin:job:write
|
- admin:job:write
|
||||||
@@ -2614,6 +2845,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -2720,6 +2952,7 @@ paths:
|
|||||||
roles:
|
roles:
|
||||||
- admin:backup:read
|
- admin:backup:read
|
||||||
- admin:backup:restore
|
- admin:backup:restore
|
||||||
|
- admin:dashboard:write
|
||||||
- admin:group:write
|
- admin:group:write
|
||||||
- admin:job:read
|
- admin:job:read
|
||||||
- admin:job:write
|
- admin:job:write
|
||||||
@@ -2733,6 +2966,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -7122,6 +7356,7 @@ paths:
|
|||||||
roles:
|
roles:
|
||||||
- admin:backup:read
|
- admin:backup:read
|
||||||
- admin:backup:restore
|
- admin:backup:restore
|
||||||
|
- admin:dashboard:write
|
||||||
- admin:group:write
|
- admin:group:write
|
||||||
- admin:job:read
|
- admin:job:read
|
||||||
- admin:job:write
|
- admin:job:write
|
||||||
@@ -7135,6 +7370,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -7158,6 +7394,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -7210,6 +7447,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -7270,6 +7508,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -7326,6 +7565,7 @@ paths:
|
|||||||
roles:
|
roles:
|
||||||
- admin:backup:read
|
- admin:backup:read
|
||||||
- admin:backup:restore
|
- admin:backup:restore
|
||||||
|
- admin:dashboard:write
|
||||||
- admin:group:write
|
- admin:group:write
|
||||||
- admin:job:read
|
- admin:job:read
|
||||||
- admin:job:write
|
- admin:job:write
|
||||||
@@ -7339,6 +7579,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
|
|||||||
@@ -225,7 +225,7 @@
|
|||||||
"apikey" : false,
|
"apikey" : false,
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "bob",
|
"id" : "bob",
|
||||||
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -307,6 +307,257 @@
|
|||||||
"x-codegen-request-body-name" : "userdata"
|
"x-codegen-request-body-name" : "userdata"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/dashboard/data" : {
|
||||||
|
"get" : {
|
||||||
|
"operationId" : "dashboardData",
|
||||||
|
"parameters" : [ {
|
||||||
|
"description" : "Aggregation",
|
||||||
|
"example" : "type",
|
||||||
|
"in" : "query",
|
||||||
|
"name" : "aggregation",
|
||||||
|
"required" : true,
|
||||||
|
"schema" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"description" : "Filter",
|
||||||
|
"example" : "status == \"closed\"",
|
||||||
|
"in" : "query",
|
||||||
|
"name" : "filter",
|
||||||
|
"schema" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
} ],
|
||||||
|
"responses" : {
|
||||||
|
"200" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"type" : "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test" : {
|
||||||
|
"example" : {
|
||||||
|
"alert" : 2,
|
||||||
|
"incident" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:read" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "Get widget data",
|
||||||
|
"tags" : [ "dashboards" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/dashboards" : {
|
||||||
|
"get" : {
|
||||||
|
"operationId" : "listDashboards",
|
||||||
|
"responses" : {
|
||||||
|
"200" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"items" : {
|
||||||
|
"$ref" : "#/components/schemas/DashboardResponse"
|
||||||
|
},
|
||||||
|
"type" : "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test" : {
|
||||||
|
"example" : [ {
|
||||||
|
"id" : "simple",
|
||||||
|
"name" : "Simple",
|
||||||
|
"widgets" : [ {
|
||||||
|
"aggregation" : "owner",
|
||||||
|
"filter" : "status == \"open\"",
|
||||||
|
"name" : "open_tickets_per_user",
|
||||||
|
"type" : "bar",
|
||||||
|
"width" : 4
|
||||||
|
}, {
|
||||||
|
"aggregation" : "CONCAT(DATE_YEAR(created), \"-\", DATE_ISOWEEK(created) < 10 ? \"0\" : \"\", DATE_ISOWEEK(created))",
|
||||||
|
"name" : "tickets_per_week",
|
||||||
|
"type" : "line",
|
||||||
|
"width" : 8
|
||||||
|
} ]
|
||||||
|
} ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:read" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "List dashboards",
|
||||||
|
"tags" : [ "dashboards" ]
|
||||||
|
},
|
||||||
|
"post" : {
|
||||||
|
"operationId" : "createDashboard",
|
||||||
|
"requestBody" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"$ref" : "#/components/schemas/Dashboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "New template",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"responses" : {
|
||||||
|
"200" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"$ref" : "#/components/schemas/DashboardResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test" : {
|
||||||
|
"example" : {
|
||||||
|
"id" : "my-dashboard",
|
||||||
|
"name" : "My Dashboard",
|
||||||
|
"widgets" : [ ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:write" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "Create a new dashboard",
|
||||||
|
"tags" : [ "dashboards" ],
|
||||||
|
"x-codegen-request-body-name" : "template"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/dashboards/{id}" : {
|
||||||
|
"delete" : {
|
||||||
|
"operationId" : "deleteDashboard",
|
||||||
|
"parameters" : [ {
|
||||||
|
"description" : "Dashboard ID",
|
||||||
|
"example" : "simple",
|
||||||
|
"in" : "path",
|
||||||
|
"name" : "id",
|
||||||
|
"required" : true,
|
||||||
|
"schema" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
} ],
|
||||||
|
"responses" : {
|
||||||
|
"204" : {
|
||||||
|
"content" : { },
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:write" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "Delete a dashboard",
|
||||||
|
"tags" : [ "dashboards" ]
|
||||||
|
},
|
||||||
|
"get" : {
|
||||||
|
"operationId" : "getDashboard",
|
||||||
|
"parameters" : [ {
|
||||||
|
"description" : "Dashboard ID",
|
||||||
|
"example" : "simple",
|
||||||
|
"in" : "path",
|
||||||
|
"name" : "id",
|
||||||
|
"required" : true,
|
||||||
|
"schema" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
} ],
|
||||||
|
"responses" : {
|
||||||
|
"200" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"$ref" : "#/components/schemas/DashboardResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test" : {
|
||||||
|
"example" : {
|
||||||
|
"id" : "simple",
|
||||||
|
"name" : "Simple",
|
||||||
|
"widgets" : [ {
|
||||||
|
"aggregation" : "owner",
|
||||||
|
"filter" : "status == \"open\"",
|
||||||
|
"name" : "open_tickets_per_user",
|
||||||
|
"type" : "bar",
|
||||||
|
"width" : 4
|
||||||
|
}, {
|
||||||
|
"aggregation" : "CONCAT(DATE_YEAR(created), \"-\", DATE_ISOWEEK(created) < 10 ? \"0\" : \"\", DATE_ISOWEEK(created))",
|
||||||
|
"name" : "tickets_per_week",
|
||||||
|
"type" : "line",
|
||||||
|
"width" : 8
|
||||||
|
} ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:read" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "Get a single dashboard",
|
||||||
|
"tags" : [ "dashboards" ]
|
||||||
|
},
|
||||||
|
"put" : {
|
||||||
|
"operationId" : "updateDashboard",
|
||||||
|
"parameters" : [ {
|
||||||
|
"description" : "Dashboard ID",
|
||||||
|
"example" : "simple",
|
||||||
|
"in" : "path",
|
||||||
|
"name" : "id",
|
||||||
|
"required" : true,
|
||||||
|
"schema" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
} ],
|
||||||
|
"requestBody" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"$ref" : "#/components/schemas/Dashboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "Dashboard object that needs to be added",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"responses" : {
|
||||||
|
"200" : {
|
||||||
|
"content" : {
|
||||||
|
"application/json" : {
|
||||||
|
"schema" : {
|
||||||
|
"$ref" : "#/components/schemas/DashboardResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test" : {
|
||||||
|
"example" : {
|
||||||
|
"id" : "simple",
|
||||||
|
"name" : "Simple",
|
||||||
|
"widgets" : [ ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description" : "successful operation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security" : [ {
|
||||||
|
"roles" : [ "dashboard:write" ]
|
||||||
|
} ],
|
||||||
|
"summary" : "Update an existing dashboard",
|
||||||
|
"tags" : [ "dashboards" ],
|
||||||
|
"x-codegen-request-body-name" : "dashboard"
|
||||||
|
}
|
||||||
|
},
|
||||||
"/jobs" : {
|
"/jobs" : {
|
||||||
"get" : {
|
"get" : {
|
||||||
"operationId" : "listJobs",
|
"operationId" : "listJobs",
|
||||||
@@ -743,7 +994,7 @@
|
|||||||
"id" : "clean",
|
"id" : "clean",
|
||||||
"name" : "Clean"
|
"name" : "Clean"
|
||||||
} ],
|
} ],
|
||||||
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
|
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
|
||||||
"ticketTypes" : [ {
|
"ticketTypes" : [ {
|
||||||
"default_playbooks" : [ ],
|
"default_playbooks" : [ ],
|
||||||
"default_template" : "default",
|
"default_template" : "default",
|
||||||
@@ -832,7 +1083,7 @@
|
|||||||
"id" : "clean",
|
"id" : "clean",
|
||||||
"name" : "Clean"
|
"name" : "Clean"
|
||||||
} ],
|
} ],
|
||||||
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
|
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
|
||||||
"ticketTypes" : [ {
|
"ticketTypes" : [ {
|
||||||
"default_playbooks" : [ ],
|
"default_playbooks" : [ ],
|
||||||
"default_template" : "default",
|
"default_template" : "default",
|
||||||
@@ -4662,12 +4913,12 @@
|
|||||||
"apikey" : false,
|
"apikey" : false,
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "bob",
|
"id" : "bob",
|
||||||
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||||
}, {
|
}, {
|
||||||
"apikey" : true,
|
"apikey" : true,
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "script",
|
"id" : "script",
|
||||||
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||||
} ]
|
} ]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4705,7 +4956,7 @@
|
|||||||
"example" : {
|
"example" : {
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "syncscript",
|
"id" : "syncscript",
|
||||||
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read" ],
|
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read" ],
|
||||||
"secret" : "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH"
|
"secret" : "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4771,7 +5022,7 @@
|
|||||||
"apikey" : true,
|
"apikey" : true,
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "script",
|
"id" : "script",
|
||||||
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
"roles" : [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4820,7 +5071,7 @@
|
|||||||
"apikey" : false,
|
"apikey" : false,
|
||||||
"blocked" : false,
|
"blocked" : false,
|
||||||
"id" : "bob",
|
"id" : "bob",
|
||||||
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5001,6 +5252,39 @@
|
|||||||
},
|
},
|
||||||
"type" : "object"
|
"type" : "object"
|
||||||
},
|
},
|
||||||
|
"Dashboard" : {
|
||||||
|
"properties" : {
|
||||||
|
"name" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"widgets" : {
|
||||||
|
"items" : {
|
||||||
|
"$ref" : "#/components/schemas/Widget"
|
||||||
|
},
|
||||||
|
"type" : "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required" : [ "name", "widgets" ],
|
||||||
|
"type" : "object"
|
||||||
|
},
|
||||||
|
"DashboardResponse" : {
|
||||||
|
"properties" : {
|
||||||
|
"id" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"name" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"widgets" : {
|
||||||
|
"items" : {
|
||||||
|
"$ref" : "#/components/schemas/Widget"
|
||||||
|
},
|
||||||
|
"type" : "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required" : [ "id", "name", "widgets" ],
|
||||||
|
"type" : "object"
|
||||||
|
},
|
||||||
"Enrichment" : {
|
"Enrichment" : {
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"created" : {
|
"created" : {
|
||||||
@@ -6316,6 +6600,30 @@
|
|||||||
},
|
},
|
||||||
"required" : [ "apikey", "blocked", "id", "roles" ],
|
"required" : [ "apikey", "blocked", "id", "roles" ],
|
||||||
"type" : "object"
|
"type" : "object"
|
||||||
|
},
|
||||||
|
"Widget" : {
|
||||||
|
"properties" : {
|
||||||
|
"aggregation" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"filter" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"name" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"type" : {
|
||||||
|
"enum" : [ "bar", "line", "pie" ],
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"width" : {
|
||||||
|
"maximum" : 12,
|
||||||
|
"minimum" : 1,
|
||||||
|
"type" : "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required" : [ "aggregation", "name", "type", "width" ],
|
||||||
|
"type" : "object"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -141,6 +141,33 @@ definitions:
|
|||||||
ticket:
|
ticket:
|
||||||
$ref: '#/definitions/TicketResponse'
|
$ref: '#/definitions/TicketResponse'
|
||||||
type: object
|
type: object
|
||||||
|
Dashboard:
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
widgets:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Widget'
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- widgets
|
||||||
|
type: object
|
||||||
|
DashboardResponse:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
widgets:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Widget'
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
- widgets
|
||||||
|
type: object
|
||||||
Enrichment:
|
Enrichment:
|
||||||
properties:
|
properties:
|
||||||
created:
|
created:
|
||||||
@@ -1216,6 +1243,30 @@ definitions:
|
|||||||
- roles
|
- roles
|
||||||
- apikey
|
- apikey
|
||||||
type: object
|
type: object
|
||||||
|
Widget:
|
||||||
|
properties:
|
||||||
|
aggregation:
|
||||||
|
type: string
|
||||||
|
filter:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
enum:
|
||||||
|
- bar
|
||||||
|
- line
|
||||||
|
- pie
|
||||||
|
type: string
|
||||||
|
width:
|
||||||
|
maximum: 12
|
||||||
|
minimum: 1
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- type
|
||||||
|
- aggregation
|
||||||
|
- width
|
||||||
|
type: object
|
||||||
host: .
|
host: .
|
||||||
info:
|
info:
|
||||||
description: API for the catalyst incident response platform.
|
description: API for the catalyst incident response platform.
|
||||||
@@ -1457,6 +1508,7 @@ paths:
|
|||||||
roles:
|
roles:
|
||||||
- admin:backup:read
|
- admin:backup:read
|
||||||
- admin:backup:restore
|
- admin:backup:restore
|
||||||
|
- admin:dashboard:write
|
||||||
- admin:group:write
|
- admin:group:write
|
||||||
- admin:job:read
|
- admin:job:read
|
||||||
- admin:job:write
|
- admin:job:write
|
||||||
@@ -1470,6 +1522,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -1540,6 +1593,183 @@ paths:
|
|||||||
summary: Update current user data
|
summary: Update current user data
|
||||||
tags:
|
tags:
|
||||||
- userdata
|
- userdata
|
||||||
|
/dashboard/data:
|
||||||
|
get:
|
||||||
|
operationId: dashboardData
|
||||||
|
parameters:
|
||||||
|
- description: Aggregation
|
||||||
|
in: query
|
||||||
|
name: aggregation
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
x-example: type
|
||||||
|
- description: Filter
|
||||||
|
in: query
|
||||||
|
name: filter
|
||||||
|
type: string
|
||||||
|
x-example: status == "closed"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
alert: 2
|
||||||
|
incident: 1
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:read
|
||||||
|
summary: Get widget data
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
|
/dashboards:
|
||||||
|
get:
|
||||||
|
operationId: listDashboards
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
- id: simple
|
||||||
|
name: Simple
|
||||||
|
widgets:
|
||||||
|
- aggregation: owner
|
||||||
|
filter: status == "open"
|
||||||
|
name: open_tickets_per_user
|
||||||
|
type: bar
|
||||||
|
width: 4
|
||||||
|
- aggregation: 'CONCAT(DATE_YEAR(created), "-", DATE_ISOWEEK(created)
|
||||||
|
< 10 ? "0" : "", DATE_ISOWEEK(created))'
|
||||||
|
name: tickets_per_week
|
||||||
|
type: line
|
||||||
|
width: 8
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/DashboardResponse'
|
||||||
|
type: array
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:read
|
||||||
|
summary: List dashboards
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
|
post:
|
||||||
|
operationId: createDashboard
|
||||||
|
parameters:
|
||||||
|
- description: New template
|
||||||
|
in: body
|
||||||
|
name: template
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Dashboard'
|
||||||
|
x-example:
|
||||||
|
name: My Dashboard
|
||||||
|
widgets: []
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
id: my-dashboard
|
||||||
|
name: My Dashboard
|
||||||
|
widgets: []
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/DashboardResponse'
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:write
|
||||||
|
summary: Create a new dashboard
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
|
/dashboards/{id}:
|
||||||
|
delete:
|
||||||
|
operationId: deleteDashboard
|
||||||
|
parameters:
|
||||||
|
- description: Dashboard ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
x-example: simple
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: successful operation
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:write
|
||||||
|
summary: Delete a dashboard
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
|
get:
|
||||||
|
operationId: getDashboard
|
||||||
|
parameters:
|
||||||
|
- description: Dashboard ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
x-example: simple
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
id: simple
|
||||||
|
name: Simple
|
||||||
|
widgets:
|
||||||
|
- aggregation: owner
|
||||||
|
filter: status == "open"
|
||||||
|
name: open_tickets_per_user
|
||||||
|
type: bar
|
||||||
|
width: 4
|
||||||
|
- aggregation: 'CONCAT(DATE_YEAR(created), "-", DATE_ISOWEEK(created)
|
||||||
|
< 10 ? "0" : "", DATE_ISOWEEK(created))'
|
||||||
|
name: tickets_per_week
|
||||||
|
type: line
|
||||||
|
width: 8
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/DashboardResponse'
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:read
|
||||||
|
summary: Get a single dashboard
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
|
put:
|
||||||
|
operationId: updateDashboard
|
||||||
|
parameters:
|
||||||
|
- description: Dashboard ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
x-example: simple
|
||||||
|
- description: Dashboard object that needs to be added
|
||||||
|
in: body
|
||||||
|
name: dashboard
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Dashboard'
|
||||||
|
x-example:
|
||||||
|
name: Simple
|
||||||
|
widgets: []
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
examples:
|
||||||
|
test:
|
||||||
|
id: simple
|
||||||
|
name: Simple
|
||||||
|
widgets: []
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/DashboardResponse'
|
||||||
|
security:
|
||||||
|
- roles:
|
||||||
|
- dashboard:write
|
||||||
|
summary: Update an existing dashboard
|
||||||
|
tags:
|
||||||
|
- dashboards
|
||||||
/jobs:
|
/jobs:
|
||||||
get:
|
get:
|
||||||
operationId: listJobs
|
operationId: listJobs
|
||||||
@@ -2189,6 +2419,7 @@ paths:
|
|||||||
roles:
|
roles:
|
||||||
- admin:backup:read
|
- admin:backup:read
|
||||||
- admin:backup:restore
|
- admin:backup:restore
|
||||||
|
- admin:dashboard:write
|
||||||
- admin:group:write
|
- admin:group:write
|
||||||
- admin:job:read
|
- admin:job:read
|
||||||
- admin:job:write
|
- admin:job:write
|
||||||
@@ -2202,6 +2433,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -2308,6 +2540,7 @@ paths:
|
|||||||
roles:
|
roles:
|
||||||
- admin:backup:read
|
- admin:backup:read
|
||||||
- admin:backup:restore
|
- admin:backup:restore
|
||||||
|
- admin:dashboard:write
|
||||||
- admin:group:write
|
- admin:group:write
|
||||||
- admin:job:read
|
- admin:job:read
|
||||||
- admin:job:write
|
- admin:job:write
|
||||||
@@ -2321,6 +2554,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -6710,6 +6944,7 @@ paths:
|
|||||||
roles:
|
roles:
|
||||||
- admin:backup:read
|
- admin:backup:read
|
||||||
- admin:backup:restore
|
- admin:backup:restore
|
||||||
|
- admin:dashboard:write
|
||||||
- admin:group:write
|
- admin:group:write
|
||||||
- admin:job:read
|
- admin:job:read
|
||||||
- admin:job:write
|
- admin:job:write
|
||||||
@@ -6723,6 +6958,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -6746,6 +6982,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -6798,6 +7035,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -6858,6 +7096,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
@@ -6914,6 +7153,7 @@ paths:
|
|||||||
roles:
|
roles:
|
||||||
- admin:backup:read
|
- admin:backup:read
|
||||||
- admin:backup:restore
|
- admin:backup:restore
|
||||||
|
- admin:dashboard:write
|
||||||
- admin:group:write
|
- admin:group:write
|
||||||
- admin:job:read
|
- admin:job:read
|
||||||
- admin:job:write
|
- admin:job:write
|
||||||
@@ -6927,6 +7167,7 @@ paths:
|
|||||||
- analyst:currentsettings:write
|
- analyst:currentsettings:write
|
||||||
- analyst:currentuser:read
|
- analyst:currentuser:read
|
||||||
- analyst:currentuserdata:read
|
- analyst:currentuserdata:read
|
||||||
|
- analyst:dashboard:read
|
||||||
- analyst:file
|
- analyst:file
|
||||||
- analyst:group:read
|
- analyst:group:read
|
||||||
- analyst:playbook:read
|
- analyst:playbook:read
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ var (
|
|||||||
CommentSchema = new(gojsonschema.Schema)
|
CommentSchema = new(gojsonschema.Schema)
|
||||||
CommentFormSchema = new(gojsonschema.Schema)
|
CommentFormSchema = new(gojsonschema.Schema)
|
||||||
ContextSchema = new(gojsonschema.Schema)
|
ContextSchema = new(gojsonschema.Schema)
|
||||||
|
DashboardSchema = new(gojsonschema.Schema)
|
||||||
|
DashboardResponseSchema = new(gojsonschema.Schema)
|
||||||
EnrichmentSchema = new(gojsonschema.Schema)
|
EnrichmentSchema = new(gojsonschema.Schema)
|
||||||
EnrichmentFormSchema = new(gojsonschema.Schema)
|
EnrichmentFormSchema = new(gojsonschema.Schema)
|
||||||
FileSchema = new(gojsonschema.Schema)
|
FileSchema = new(gojsonschema.Schema)
|
||||||
@@ -60,6 +62,7 @@ var (
|
|||||||
UserDataResponseSchema = new(gojsonschema.Schema)
|
UserDataResponseSchema = new(gojsonschema.Schema)
|
||||||
UserFormSchema = new(gojsonschema.Schema)
|
UserFormSchema = new(gojsonschema.Schema)
|
||||||
UserResponseSchema = new(gojsonschema.Schema)
|
UserResponseSchema = new(gojsonschema.Schema)
|
||||||
|
WidgetSchema = new(gojsonschema.Schema)
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -72,6 +75,8 @@ func init() {
|
|||||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"required":["creator","created","message"],"$id":"#/definitions/Comment"}`),
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"required":["creator","created","message"],"$id":"#/definitions/Comment"}`),
|
||||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"required":["message"],"$id":"#/definitions/CommentForm"}`),
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"required":["message"],"$id":"#/definitions/CommentForm"}`),
|
||||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifact":{"$ref":"#/definitions/Artifact"},"playbook":{"$ref":"#/definitions/PlaybookResponse"},"task":{"$ref":"#/definitions/TaskResponse"},"ticket":{"$ref":"#/definitions/TicketResponse"}},"$id":"#/definitions/Context"}`),
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifact":{"$ref":"#/definitions/Artifact"},"playbook":{"$ref":"#/definitions/PlaybookResponse"},"task":{"$ref":"#/definitions/TaskResponse"},"ticket":{"$ref":"#/definitions/TicketResponse"}},"$id":"#/definitions/Context"}`),
|
||||||
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"name":{"type":"string"},"widgets":{"items":{"$ref":"#/definitions/Widget"},"type":"array"}},"required":["name","widgets"],"$id":"#/definitions/Dashboard"}`),
|
||||||
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"widgets":{"items":{"$ref":"#/definitions/Widget"},"type":"array"}},"required":["id","name","widgets"],"$id":"#/definitions/DashboardResponse"}`),
|
||||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"created":{"format":"date-time","type":"string"},"data":{"type":"object"},"name":{"type":"string"}},"required":["name","data","created"],"$id":"#/definitions/Enrichment"}`),
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"created":{"format":"date-time","type":"string"},"data":{"type":"object"},"name":{"type":"string"}},"required":["name","data","created"],"$id":"#/definitions/Enrichment"}`),
|
||||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"data":{"type":"object"},"name":{"type":"string"}},"required":["name","data"],"$id":"#/definitions/EnrichmentForm"}`),
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"data":{"type":"object"},"name":{"type":"string"}},"required":["name","data"],"$id":"#/definitions/EnrichmentForm"}`),
|
||||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"}},"required":["key","name"],"$id":"#/definitions/File"}`),
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"}},"required":["key","name"],"$id":"#/definitions/File"}`),
|
||||||
@@ -116,6 +121,7 @@ func init() {
|
|||||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"email":{"type":"string"},"id":{"type":"string"},"image":{"type":"string"},"name":{"type":"string"},"timeformat":{"title":"Time Format (https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens)","type":"string"}},"required":["id"],"$id":"#/definitions/UserDataResponse"}`),
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"email":{"type":"string"},"id":{"type":"string"},"image":{"type":"string"},"name":{"type":"string"},"timeformat":{"title":"Time Format (https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens)","type":"string"}},"required":["id"],"$id":"#/definitions/UserDataResponse"}`),
|
||||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserForm"}`),
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserForm"}`),
|
||||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserResponse"}`),
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserResponse"}`),
|
||||||
|
gojsonschema.NewStringLoader(`{"type":"object","properties":{"aggregation":{"type":"string"},"filter":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["bar","line","pie"]},"width":{"maximum":12,"type":"integer"}},"required":["name","type","aggregation","width"],"$id":"#/definitions/Widget"}`),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -129,6 +135,8 @@ func init() {
|
|||||||
CommentSchema = mustCompile(`#/definitions/Comment`)
|
CommentSchema = mustCompile(`#/definitions/Comment`)
|
||||||
CommentFormSchema = mustCompile(`#/definitions/CommentForm`)
|
CommentFormSchema = mustCompile(`#/definitions/CommentForm`)
|
||||||
ContextSchema = mustCompile(`#/definitions/Context`)
|
ContextSchema = mustCompile(`#/definitions/Context`)
|
||||||
|
DashboardSchema = mustCompile(`#/definitions/Dashboard`)
|
||||||
|
DashboardResponseSchema = mustCompile(`#/definitions/DashboardResponse`)
|
||||||
EnrichmentSchema = mustCompile(`#/definitions/Enrichment`)
|
EnrichmentSchema = mustCompile(`#/definitions/Enrichment`)
|
||||||
EnrichmentFormSchema = mustCompile(`#/definitions/EnrichmentForm`)
|
EnrichmentFormSchema = mustCompile(`#/definitions/EnrichmentForm`)
|
||||||
FileSchema = mustCompile(`#/definitions/File`)
|
FileSchema = mustCompile(`#/definitions/File`)
|
||||||
@@ -173,6 +181,7 @@ func init() {
|
|||||||
UserDataResponseSchema = mustCompile(`#/definitions/UserDataResponse`)
|
UserDataResponseSchema = mustCompile(`#/definitions/UserDataResponse`)
|
||||||
UserFormSchema = mustCompile(`#/definitions/UserForm`)
|
UserFormSchema = mustCompile(`#/definitions/UserForm`)
|
||||||
UserResponseSchema = mustCompile(`#/definitions/UserResponse`)
|
UserResponseSchema = mustCompile(`#/definitions/UserResponse`)
|
||||||
|
WidgetSchema = mustCompile(`#/definitions/Widget`)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Artifact struct {
|
type Artifact struct {
|
||||||
@@ -230,6 +239,17 @@ type Context struct {
|
|||||||
Ticket *TicketResponse `json:"ticket,omitempty"`
|
Ticket *TicketResponse `json:"ticket,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Dashboard struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Widgets []*Widget `json:"widgets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DashboardResponse struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Widgets []*Widget `json:"widgets"`
|
||||||
|
}
|
||||||
|
|
||||||
type Enrichment struct {
|
type Enrichment struct {
|
||||||
Created time.Time `json:"created"`
|
Created time.Time `json:"created"`
|
||||||
Data map[string]interface{} `json:"data"`
|
Data map[string]interface{} `json:"data"`
|
||||||
@@ -600,6 +620,14 @@ type UserResponse struct {
|
|||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Widget struct {
|
||||||
|
Aggregation string `json:"aggregation"`
|
||||||
|
Filter *string `json:"filter,omitempty"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
}
|
||||||
|
|
||||||
func mustCompile(uri string) *gojsonschema.Schema {
|
func mustCompile(uri string) *gojsonschema.Schema {
|
||||||
s, err := schemaLoader.Compile(gojsonschema.NewReferenceLoader(uri))
|
s, err := schemaLoader.Compile(gojsonschema.NewReferenceLoader(uri))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -632,4 +660,10 @@ const (
|
|||||||
TypeColorSuccess = "success"
|
TypeColorSuccess = "success"
|
||||||
|
|
||||||
TypeColorWarning = "warning"
|
TypeColorWarning = "warning"
|
||||||
|
|
||||||
|
WidgetTypeBar = "bar"
|
||||||
|
|
||||||
|
WidgetTypeLine = "line"
|
||||||
|
|
||||||
|
WidgetTypePie = "pie"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -78,3 +78,7 @@ func (i *Index) Truncate() error {
|
|||||||
i.internal = index
|
i.internal = index
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Index) Close() error {
|
||||||
|
return i.internal.Close()
|
||||||
|
}
|
||||||
|
|||||||
30
role/role.go
30
role/role.go
@@ -16,9 +16,10 @@ const (
|
|||||||
Admin string = "admin"
|
Admin string = "admin"
|
||||||
|
|
||||||
AutomationRead Role = "analyst:automation:read"
|
AutomationRead Role = "analyst:automation:read"
|
||||||
|
CurrentuserRead Role = "analyst:currentuser:read"
|
||||||
CurrentuserdataRead Role = "analyst:currentuserdata:read"
|
CurrentuserdataRead Role = "analyst:currentuserdata:read"
|
||||||
CurrentuserdataWrite Role = "analyst:currentsettings:write"
|
CurrentuserdataWrite Role = "analyst:currentsettings:write"
|
||||||
CurrentuserRead Role = "analyst:currentuser:read"
|
DashboardRead Role = "analyst:dashboard:read"
|
||||||
FileReadWrite Role = "analyst:file"
|
FileReadWrite Role = "analyst:file"
|
||||||
GroupRead Role = "analyst:group:read"
|
GroupRead Role = "analyst:group:read"
|
||||||
PlaybookRead Role = "analyst:playbook:read"
|
PlaybookRead Role = "analyst:playbook:read"
|
||||||
@@ -26,8 +27,8 @@ const (
|
|||||||
SettingsRead Role = "analyst:settings:read"
|
SettingsRead Role = "analyst:settings:read"
|
||||||
TemplateRead Role = "analyst:template:read"
|
TemplateRead Role = "analyst:template:read"
|
||||||
TicketRead Role = "analyst:ticket:read"
|
TicketRead Role = "analyst:ticket:read"
|
||||||
TickettypeRead Role = "analyst:tickettype:read"
|
|
||||||
TicketWrite Role = "analyst:ticket:write"
|
TicketWrite Role = "analyst:ticket:write"
|
||||||
|
TickettypeRead Role = "analyst:tickettype:read"
|
||||||
UserRead Role = "analyst:user:read"
|
UserRead Role = "analyst:user:read"
|
||||||
|
|
||||||
AutomationWrite Role = "engineer:automation:write"
|
AutomationWrite Role = "engineer:automation:write"
|
||||||
@@ -36,17 +37,18 @@ const (
|
|||||||
TemplateWrite Role = "engineer:template:write"
|
TemplateWrite Role = "engineer:template:write"
|
||||||
TickettypeWrite Role = "engineer:tickettype:write"
|
TickettypeWrite Role = "engineer:tickettype:write"
|
||||||
|
|
||||||
BackupRead Role = "admin:backup:read"
|
BackupRead Role = "admin:backup:read"
|
||||||
BackupRestore Role = "admin:backup:restore"
|
BackupRestore Role = "admin:backup:restore"
|
||||||
GroupWrite Role = "admin:group:write"
|
DashboardWrite Role = "admin:dashboard:write"
|
||||||
JobWrite Role = "admin:job:write"
|
GroupWrite Role = "admin:group:write"
|
||||||
JobRead Role = "admin:job:read"
|
JobRead Role = "admin:job:read"
|
||||||
LogRead Role = "admin:log:read"
|
JobWrite Role = "admin:job:write"
|
||||||
UserdataRead Role = "admin:userdata:read"
|
LogRead Role = "admin:log:read"
|
||||||
UserdataWrite Role = "admin:userdata:write"
|
SettingsWrite Role = "admin:settings:write"
|
||||||
SettingsWrite Role = "admin:settings:write"
|
TicketDelete Role = "admin:ticket:delete"
|
||||||
TicketDelete Role = "admin:ticket:delete"
|
UserWrite Role = "admin:user:write"
|
||||||
UserWrite Role = "admin:user:write"
|
UserdataRead Role = "admin:userdata:read"
|
||||||
|
UserdataWrite Role = "admin:userdata:write"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p Role) String() string {
|
func (p Role) String() string {
|
||||||
@@ -146,7 +148,7 @@ func List() []Role {
|
|||||||
TicketWrite, UserRead, AutomationWrite, PlaybookWrite, RuleWrite,
|
TicketWrite, UserRead, AutomationWrite, PlaybookWrite, RuleWrite,
|
||||||
TemplateWrite, TickettypeWrite, BackupRead, BackupRestore, GroupWrite,
|
TemplateWrite, TickettypeWrite, BackupRead, BackupRestore, GroupWrite,
|
||||||
LogRead, UserdataWrite, TicketDelete, UserWrite, JobRead, JobWrite,
|
LogRead, UserdataWrite, TicketDelete, UserWrite, JobRead, JobWrite,
|
||||||
SettingsWrite,
|
SettingsWrite, DashboardRead, DashboardWrite,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
49
service/dashboard.go
Normal file
49
service/dashboard.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/arangodb/go-driver"
|
||||||
|
|
||||||
|
"github.com/SecurityBrewery/catalyst/database"
|
||||||
|
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dashboardResponseID(doc *model.DashboardResponse) []driver.DocumentID {
|
||||||
|
if doc == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return templateID(doc.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dashboardID(id string) []driver.DocumentID {
|
||||||
|
return []driver.DocumentID{driver.DocumentID(fmt.Sprintf("%s/%s", database.DashboardCollectionName, id))}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ListDashboards(ctx context.Context) ([]*model.DashboardResponse, error) {
|
||||||
|
return s.database.DashboardList(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CreateDashboard(ctx context.Context, dashboard *model.Dashboard) (doc *model.DashboardResponse, err error) {
|
||||||
|
defer s.publishRequest(ctx, err, "CreateDashboard", dashboardResponseID(doc))
|
||||||
|
return s.database.DashboardCreate(ctx, dashboard)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetDashboard(ctx context.Context, id string) (*model.DashboardResponse, error) {
|
||||||
|
return s.database.DashboardGet(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) UpdateDashboard(ctx context.Context, id string, form *model.Dashboard) (doc *model.DashboardResponse, err error) {
|
||||||
|
defer s.publishRequest(ctx, err, "UpdateDashboard", dashboardResponseID(doc))
|
||||||
|
return s.database.DashboardUpdate(ctx, id, form)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) DeleteDashboard(ctx context.Context, id string) (err error) {
|
||||||
|
defer s.publishRequest(ctx, err, "DeleteDashboard", dashboardID(id))
|
||||||
|
return s.database.DashboardDelete(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) DashboardData(ctx context.Context, aggregation string, filter *string) (map[string]interface{}, error) {
|
||||||
|
return s.database.WidgetData(ctx, aggregation, filter)
|
||||||
|
}
|
||||||
21
test/data.go
21
test/data.go
@@ -78,6 +78,27 @@ func SetupTestData(ctx context.Context, db *database.Database) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := db.DashboardCreate(ctx, &model.Dashboard{
|
||||||
|
Name: "Simple",
|
||||||
|
Widgets: []*model.Widget{
|
||||||
|
{
|
||||||
|
Name: "open_tickets_per_user",
|
||||||
|
Type: model.WidgetTypeBar,
|
||||||
|
Aggregation: "owner",
|
||||||
|
Filter: pointer.String(`status == "open"`),
|
||||||
|
Width: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "tickets_per_week",
|
||||||
|
Type: model.WidgetTypeLine,
|
||||||
|
Aggregation: `CONCAT(DATE_YEAR(created), "-", DATE_ISOWEEK(created) < 10 ? "0" : "", DATE_ISOWEEK(created))`,
|
||||||
|
Width: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ func Index(t *testing.T) (*index.Index, func(), error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return catalystIndex, func() { os.RemoveAll(dir) }, nil
|
return catalystIndex, func() { catalystIndex.Close(); os.RemoveAll(dir) }, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Bus(t *testing.T) (context.Context, *catalyst.Config, *bus.Bus, error) {
|
func Bus(t *testing.T) (context.Context, *catalyst.Config, *bus.Bus, error) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<v-app class="background">
|
<v-app class="background">
|
||||||
<v-navigation-drawer dark permanent :mini-variant="mini" :expand-on-hover="mini" app color="statusbar">
|
<v-navigation-drawer dark permanent :mini-variant="mini" :expand-on-hover="mini" app color="statusbar">
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-item class="px-2" :to="{ name: 'Dashboard' }">
|
<v-list-item class="px-2" :to="{ name: 'Home' }">
|
||||||
<v-list-item-avatar rounded="0">
|
<v-list-item-avatar rounded="0">
|
||||||
<v-img src="/flask_white.svg" :width="40"></v-img>
|
<v-img src="/flask_white.svg" :width="40"></v-img>
|
||||||
</v-list-item-avatar>
|
</v-list-item-avatar>
|
||||||
@@ -182,6 +182,7 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
internal: function (): Array<any> {
|
internal: function (): Array<any> {
|
||||||
return [
|
return [
|
||||||
|
{ icon: "mdi-view-dashboard", name: "Dashboards", to: "DashboardList", role: "admin:dashboard:write" },
|
||||||
{ icon: "mdi-check-bold", name: "Open Tasks", to: "TaskList", count: this.$store.state.task_count },
|
{ icon: "mdi-check-bold", name: "Open Tasks", to: "TaskList", count: this.$store.state.task_count },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -290,6 +290,50 @@ export interface Context {
|
|||||||
*/
|
*/
|
||||||
'ticket'?: TicketResponse;
|
'ticket'?: TicketResponse;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface Dashboard
|
||||||
|
*/
|
||||||
|
export interface Dashboard {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Dashboard
|
||||||
|
*/
|
||||||
|
'name': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<Widget>}
|
||||||
|
* @memberof Dashboard
|
||||||
|
*/
|
||||||
|
'widgets': Array<Widget>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface DashboardResponse
|
||||||
|
*/
|
||||||
|
export interface DashboardResponse {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof DashboardResponse
|
||||||
|
*/
|
||||||
|
'id': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof DashboardResponse
|
||||||
|
*/
|
||||||
|
'name': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<Widget>}
|
||||||
|
* @memberof DashboardResponse
|
||||||
|
*/
|
||||||
|
'widgets': Array<Widget>;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
@@ -2279,6 +2323,52 @@ export interface UserResponse {
|
|||||||
*/
|
*/
|
||||||
'roles': Array<string>;
|
'roles': Array<string>;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface Widget
|
||||||
|
*/
|
||||||
|
export interface Widget {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Widget
|
||||||
|
*/
|
||||||
|
'aggregation': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Widget
|
||||||
|
*/
|
||||||
|
'filter'?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Widget
|
||||||
|
*/
|
||||||
|
'name': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Widget
|
||||||
|
*/
|
||||||
|
'type': WidgetTypeEnum;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof Widget
|
||||||
|
*/
|
||||||
|
'width': number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WidgetTypeEnum = {
|
||||||
|
Bar: 'bar',
|
||||||
|
Line: 'line',
|
||||||
|
Pie: 'pie'
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type WidgetTypeEnum = typeof WidgetTypeEnum[keyof typeof WidgetTypeEnum];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AutomationsApi - axios parameter creator
|
* AutomationsApi - axios parameter creator
|
||||||
@@ -2657,6 +2747,461 @@ export class AutomationsApi extends BaseAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DashboardsApi - axios parameter creator
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const DashboardsApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Create a new dashboard
|
||||||
|
* @param {Dashboard} template New template
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
createDashboard: async (template: Dashboard, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// verify required parameter 'template' is not null or undefined
|
||||||
|
assertParamExists('createDashboard', 'template', template)
|
||||||
|
const localVarPath = `/dashboards`;
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
localVarRequestOptions.data = serializeDataIfNeeded(template, localVarRequestOptions, configuration)
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Get widget data
|
||||||
|
* @param {string} aggregation Aggregation
|
||||||
|
* @param {string} [filter] Filter
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
dashboardData: async (aggregation: string, filter?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// verify required parameter 'aggregation' is not null or undefined
|
||||||
|
assertParamExists('dashboardData', 'aggregation', aggregation)
|
||||||
|
const localVarPath = `/dashboard/data`;
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
if (aggregation !== undefined) {
|
||||||
|
localVarQueryParameter['aggregation'] = aggregation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter !== undefined) {
|
||||||
|
localVarQueryParameter['filter'] = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Delete a dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
deleteDashboard: async (id: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// verify required parameter 'id' is not null or undefined
|
||||||
|
assertParamExists('deleteDashboard', 'id', id)
|
||||||
|
const localVarPath = `/dashboards/{id}`
|
||||||
|
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Get a single dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
getDashboard: async (id: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// verify required parameter 'id' is not null or undefined
|
||||||
|
assertParamExists('getDashboard', 'id', id)
|
||||||
|
const localVarPath = `/dashboards/{id}`
|
||||||
|
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary List dashboards
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
listDashboards: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
const localVarPath = `/dashboards`;
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Update an existing dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {Dashboard} dashboard Dashboard object that needs to be added
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
updateDashboard: async (id: string, dashboard: Dashboard, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// verify required parameter 'id' is not null or undefined
|
||||||
|
assertParamExists('updateDashboard', 'id', id)
|
||||||
|
// verify required parameter 'dashboard' is not null or undefined
|
||||||
|
assertParamExists('updateDashboard', 'dashboard', dashboard)
|
||||||
|
const localVarPath = `/dashboards/{id}`
|
||||||
|
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
localVarRequestOptions.data = serializeDataIfNeeded(dashboard, localVarRequestOptions, configuration)
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DashboardsApi - functional programming interface
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const DashboardsApiFp = function(configuration?: Configuration) {
|
||||||
|
const localVarAxiosParamCreator = DashboardsApiAxiosParamCreator(configuration)
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Create a new dashboard
|
||||||
|
* @param {Dashboard} template New template
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async createDashboard(template: Dashboard, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<DashboardResponse>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.createDashboard(template, options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Get widget data
|
||||||
|
* @param {string} aggregation Aggregation
|
||||||
|
* @param {string} [filter] Filter
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async dashboardData(aggregation: string, filter?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.dashboardData(aggregation, filter, options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Delete a dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async deleteDashboard(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.deleteDashboard(id, options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Get a single dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async getDashboard(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<DashboardResponse>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.getDashboard(id, options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary List dashboards
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async listDashboards(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<DashboardResponse>>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listDashboards(options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Update an existing dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {Dashboard} dashboard Dashboard object that needs to be added
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async updateDashboard(id: string, dashboard: Dashboard, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<DashboardResponse>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.updateDashboard(id, dashboard, options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DashboardsApi - factory interface
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const DashboardsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||||
|
const localVarFp = DashboardsApiFp(configuration)
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Create a new dashboard
|
||||||
|
* @param {Dashboard} template New template
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
createDashboard(template: Dashboard, options?: any): AxiosPromise<DashboardResponse> {
|
||||||
|
return localVarFp.createDashboard(template, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Get widget data
|
||||||
|
* @param {string} aggregation Aggregation
|
||||||
|
* @param {string} [filter] Filter
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
dashboardData(aggregation: string, filter?: string, options?: any): AxiosPromise<object> {
|
||||||
|
return localVarFp.dashboardData(aggregation, filter, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Delete a dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
deleteDashboard(id: string, options?: any): AxiosPromise<void> {
|
||||||
|
return localVarFp.deleteDashboard(id, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Get a single dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
getDashboard(id: string, options?: any): AxiosPromise<DashboardResponse> {
|
||||||
|
return localVarFp.getDashboard(id, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary List dashboards
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
listDashboards(options?: any): AxiosPromise<Array<DashboardResponse>> {
|
||||||
|
return localVarFp.listDashboards(options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Update an existing dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {Dashboard} dashboard Dashboard object that needs to be added
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
updateDashboard(id: string, dashboard: Dashboard, options?: any): AxiosPromise<DashboardResponse> {
|
||||||
|
return localVarFp.updateDashboard(id, dashboard, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DashboardsApi - object-oriented interface
|
||||||
|
* @export
|
||||||
|
* @class DashboardsApi
|
||||||
|
* @extends {BaseAPI}
|
||||||
|
*/
|
||||||
|
export class DashboardsApi extends BaseAPI {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Create a new dashboard
|
||||||
|
* @param {Dashboard} template New template
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof DashboardsApi
|
||||||
|
*/
|
||||||
|
public createDashboard(template: Dashboard, options?: AxiosRequestConfig) {
|
||||||
|
return DashboardsApiFp(this.configuration).createDashboard(template, options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Get widget data
|
||||||
|
* @param {string} aggregation Aggregation
|
||||||
|
* @param {string} [filter] Filter
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof DashboardsApi
|
||||||
|
*/
|
||||||
|
public dashboardData(aggregation: string, filter?: string, options?: AxiosRequestConfig) {
|
||||||
|
return DashboardsApiFp(this.configuration).dashboardData(aggregation, filter, options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Delete a dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof DashboardsApi
|
||||||
|
*/
|
||||||
|
public deleteDashboard(id: string, options?: AxiosRequestConfig) {
|
||||||
|
return DashboardsApiFp(this.configuration).deleteDashboard(id, options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Get a single dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof DashboardsApi
|
||||||
|
*/
|
||||||
|
public getDashboard(id: string, options?: AxiosRequestConfig) {
|
||||||
|
return DashboardsApiFp(this.configuration).getDashboard(id, options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary List dashboards
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof DashboardsApi
|
||||||
|
*/
|
||||||
|
public listDashboards(options?: AxiosRequestConfig) {
|
||||||
|
return DashboardsApiFp(this.configuration).listDashboards(options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @summary Update an existing dashboard
|
||||||
|
* @param {string} id Dashboard ID
|
||||||
|
* @param {Dashboard} dashboard Dashboard object that needs to be added
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof DashboardsApi
|
||||||
|
*/
|
||||||
|
public updateDashboard(id: string, dashboard: Dashboard, options?: AxiosRequestConfig) {
|
||||||
|
return DashboardsApiFp(this.configuration).updateDashboard(id, dashboard, options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GraphApi - axios parameter creator
|
* GraphApi - axios parameter creator
|
||||||
* @export
|
* @export
|
||||||
|
|||||||
@@ -68,19 +68,26 @@ const v = new Vue({
|
|||||||
}).$mount("#app");
|
}).$mount("#app");
|
||||||
|
|
||||||
axios.interceptors.response.use(
|
axios.interceptors.response.use(
|
||||||
response => response,
|
// response => response,
|
||||||
|
response => {
|
||||||
|
lodash.unset(response.data, 'notoast');
|
||||||
|
|
||||||
|
return Promise.resolve(response);
|
||||||
|
},
|
||||||
error => {
|
error => {
|
||||||
console.log(error)
|
if (!lodash.has(error.response.data, 'notoast')) {
|
||||||
if (error.response.data && 'title' in error.response.data && 'detail' in error.response.data) {
|
if (error.response.data && 'title' in error.response.data && 'detail' in error.response.data) {
|
||||||
const problem = error.response.data as Problem;
|
const problem = error.response.data as Problem;
|
||||||
v.$store.dispatch("alertError", { name: problem.title, detail: problem.detail });
|
v.$store.dispatch("alertError", { name: problem.title, detail: problem.detail });
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
if (error.response.data && 'error' in error.response.data) {
|
||||||
|
v.$store.dispatch("alertError", { name: "Error", detail: error.response.data.error });
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
v.$store.dispatch("alertError", { name: "Error", detail: JSON.stringify(error.response.data) });
|
||||||
}
|
}
|
||||||
if (error.response.data && 'error' in error.response.data) {
|
|
||||||
v.$store.dispatch("alertError", { name: "Error", detail: error.response.data.error });
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
v.$store.dispatch("alertError", { name: "Error", detail: JSON.stringify(error.response.data) });
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,13 +15,15 @@ import Rule from "../views/Rule.vue";
|
|||||||
import RuleList from "../views/RuleList.vue";
|
import RuleList from "../views/RuleList.vue";
|
||||||
import Template from "../views/Template.vue";
|
import Template from "../views/Template.vue";
|
||||||
import TemplateList from "../views/TemplateList.vue";
|
import TemplateList from "../views/TemplateList.vue";
|
||||||
|
import Dashboard from "../views/Dashboard.vue";
|
||||||
|
import DashboardList from "../views/DashboardList.vue";
|
||||||
import API from "../views/API.vue";
|
import API from "../views/API.vue";
|
||||||
import User from '../views/User.vue';
|
import User from '../views/User.vue';
|
||||||
import UserList from "@/views/UserList.vue";
|
import UserList from "@/views/UserList.vue";
|
||||||
import Job from '../views/Job.vue';
|
import Job from '../views/Job.vue';
|
||||||
import JobList from "@/views/JobList.vue";
|
import JobList from "@/views/JobList.vue";
|
||||||
import GroupList from "@/views/GroupList.vue";
|
import GroupList from "@/views/GroupList.vue";
|
||||||
import Dashboard from "@/views/Dashboard.vue";
|
import Home from "@/views/Home.vue";
|
||||||
import Group from "@/views/Group.vue";
|
import Group from "@/views/Group.vue";
|
||||||
import TicketType from '../views/TicketType.vue';
|
import TicketType from '../views/TicketType.vue';
|
||||||
import TicketTypeList from "@/views/TicketTypeList.vue";
|
import TicketTypeList from "@/views/TicketTypeList.vue";
|
||||||
@@ -59,10 +61,10 @@ const routes: Array<RouteConfig> = [
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: "/dashboard",
|
path: "/home",
|
||||||
name: "Dashboard",
|
name: "Home",
|
||||||
component: Dashboard,
|
component: Home,
|
||||||
meta: { title: "Dashboard" },
|
meta: { title: "Home" },
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -227,6 +229,21 @@ const routes: Array<RouteConfig> = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
path: "/dashboards",
|
||||||
|
name: "DashboardList",
|
||||||
|
component: DashboardList,
|
||||||
|
meta: { title: "Dashboards" },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: ":id",
|
||||||
|
name: "Dashboard",
|
||||||
|
component: Dashboard,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: "/settings",
|
path: "/settings",
|
||||||
name: "Settings",
|
name: "Settings",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import {
|
|||||||
SettingsApi,
|
SettingsApi,
|
||||||
SettingsApiFactory,
|
SettingsApiFactory,
|
||||||
JobsApi,
|
JobsApi,
|
||||||
JobsApiFactory,
|
JobsApiFactory, DashboardsApiFactory, DashboardsApi,
|
||||||
} from "@/client";
|
} from "@/client";
|
||||||
|
|
||||||
const config = new Configuration({
|
const config = new Configuration({
|
||||||
@@ -56,7 +56,8 @@ export const API: TicketsApi &
|
|||||||
SettingsApi &
|
SettingsApi &
|
||||||
TickettypesApi &
|
TickettypesApi &
|
||||||
JobsApi &
|
JobsApi &
|
||||||
TasksApi = Object.assign(
|
TasksApi &
|
||||||
|
DashboardsApi = Object.assign(
|
||||||
{},
|
{},
|
||||||
TicketsApiFactory(config),
|
TicketsApiFactory(config),
|
||||||
PlaybooksApiFactory(config),
|
PlaybooksApiFactory(config),
|
||||||
@@ -74,5 +75,6 @@ export const API: TicketsApi &
|
|||||||
TickettypesApiFactory(config),
|
TickettypesApiFactory(config),
|
||||||
TasksApiFactory(config),
|
TasksApiFactory(config),
|
||||||
SettingsApiFactory(config),
|
SettingsApiFactory(config),
|
||||||
JobsApiFactory(config)
|
JobsApiFactory(config),
|
||||||
|
DashboardsApiFactory(config)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import Vuex, {ActionContext} from "vuex";
|
import Vuex, {ActionContext} from "vuex";
|
||||||
import {API} from "@/services/api";
|
import {API} from "@/services/api";
|
||||||
import {UserData, TicketList, Settings, UserResponse, SettingsResponse} from "@/client";
|
import {UserData, TicketList, UserResponse, SettingsResponse} from "@/client";
|
||||||
import {AxiosResponse} from "axios";
|
import {AxiosResponse} from "axios";
|
||||||
import {Alert} from "@/types/types";
|
import {Alert} from "@/types/types";
|
||||||
import {templateStore} from "./modules/templates";
|
import {templateStore} from "./modules/templates";
|
||||||
@@ -19,7 +19,7 @@ export default new Vuex.Store({
|
|||||||
counts: {} as Record<string, number>,
|
counts: {} as Record<string, number>,
|
||||||
task_count: 0 as number,
|
task_count: 0 as number,
|
||||||
|
|
||||||
settings: {} as Settings,
|
settings: {} as SettingsResponse,
|
||||||
userdata: {} as UserData,
|
userdata: {} as UserData,
|
||||||
|
|
||||||
alert: {} as Alert,
|
alert: {} as Alert,
|
||||||
@@ -46,7 +46,7 @@ export default new Vuex.Store({
|
|||||||
setUserData (state, msg: UserData) {
|
setUserData (state, msg: UserData) {
|
||||||
state.userdata = msg
|
state.userdata = msg
|
||||||
},
|
},
|
||||||
setSettings (state, msg: Settings) {
|
setSettings (state, msg: SettingsResponse) {
|
||||||
state.settings = msg
|
state.settings = msg
|
||||||
},
|
},
|
||||||
setAlert (state, msg: Alert) {
|
setAlert (state, msg: Alert) {
|
||||||
|
|||||||
@@ -1,110 +1,123 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-main>
|
<div v-if="dashboard">
|
||||||
|
<h2 class="d-flex">
|
||||||
|
<span v-if="!editmode">{{ dashboard.name }}</span>
|
||||||
|
<v-text-field v-else v-model="dashboard.name" outlined dense class="mb-0" hide-details></v-text-field>
|
||||||
|
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn v-if="editmode" small outlined @click="addWidget" class="mr-1">
|
||||||
|
<v-icon>mdi-plus</v-icon>
|
||||||
|
Add Widget
|
||||||
|
</v-btn>
|
||||||
|
<v-btn v-if="editmode" small outlined @click="save" class="mr-1">
|
||||||
|
<v-icon>mdi-content-save</v-icon>
|
||||||
|
Save
|
||||||
|
</v-btn>
|
||||||
|
<v-btn v-if="editmode && $route.params.id !== 'new'" small outlined @click="cancel">
|
||||||
|
<v-icon>mdi-cancel</v-icon>
|
||||||
|
Cancel
|
||||||
|
</v-btn>
|
||||||
|
<v-btn v-if="!editmode" small outlined @click="edit">
|
||||||
|
<v-icon>mdi-pencil</v-icon>
|
||||||
|
Edit
|
||||||
|
</v-btn>
|
||||||
|
</h2>
|
||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col v-if="statistics" cols="12" lg="7">
|
<v-col v-for="(widget, index) in dashboard.widgets" :key="index" :cols="widget.width">
|
||||||
<v-row>
|
<v-card class="mb-2">
|
||||||
<v-col cols="4">
|
<v-card-title>
|
||||||
<v-subheader>Unassigned tickets</v-subheader>
|
<span v-if="!editmode">{{ widget.name }}</span>
|
||||||
<span style="font-size: 60pt; text-align: center; display: block">
|
<v-text-field v-else outlined dense hide-details v-model="widget.name" class="mr-1"></v-text-field>
|
||||||
<router-link :to="{
|
<v-btn v-if="editmode" outlined @click="removeWidget(index)">
|
||||||
name: 'TicketList',
|
<v-icon>mdi-close</v-icon>
|
||||||
params: { query: 'status == \'open\' AND !owner' }
|
Remove
|
||||||
}">
|
</v-btn>
|
||||||
{{ statistics.unassigned }}
|
</v-card-title>
|
||||||
</router-link>
|
|
||||||
</span>
|
<v-card-text v-if="editmode">
|
||||||
<v-subheader>Your tickets</v-subheader>
|
<v-row>
|
||||||
<span style="font-size: 60pt; text-align: center; display: block">
|
<v-col cols="8">
|
||||||
<router-link :to="{
|
<v-select label="Type" v-model="widget.type" :items="['line', 'bar', 'pie']"></v-select>
|
||||||
name: 'TicketList',
|
</v-col>
|
||||||
params: { query: 'status == \'open\' AND owner == \'' + $store.state.user.id + '\'' }
|
<v-col cols="4">
|
||||||
}">
|
<v-text-field label="Width" type="number" v-model="widget.width"></v-text-field>
|
||||||
{{ $store.state.user.id in statistics.open_tickets_per_user ? statistics.open_tickets_per_user[$store.state.user.id] : 0 }}
|
</v-col>
|
||||||
</router-link>
|
</v-row>
|
||||||
</span>
|
<v-text-field label="Aggregation" v-model="widget.aggregation"></v-text-field>
|
||||||
</v-col>
|
<v-text-field label="Filter" v-model="widget.filter" clearable></v-text-field>
|
||||||
<v-col cols="8">
|
|
||||||
<v-subheader>Open tickets per owner</v-subheader>
|
</v-card-text>
|
||||||
<bar-chart
|
|
||||||
v-if="open_tickets_per_user"
|
<v-card-text v-if="data[index] === null">
|
||||||
:chart-data="open_tickets_per_user"
|
{{ widgetErrors[index] }}
|
||||||
:styles="{
|
</v-card-text>
|
||||||
width: '100%',
|
<div v-else>
|
||||||
'max-height': '400px',
|
|
||||||
position: 'relative'
|
|
||||||
}"
|
|
||||||
:chart-options="{
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
legend: undefined,
|
|
||||||
scales: { xAxes: [ { ticks: { beginAtZero: true, precision: 0 } } ] },
|
|
||||||
onClick: clickUser,
|
|
||||||
hover: {
|
|
||||||
onHover: function(e) {
|
|
||||||
var point = this.getElementAtEvent(e);
|
|
||||||
if (point.length) e.target.style.cursor = 'pointer';
|
|
||||||
else e.target.style.cursor = 'default';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
></bar-chart>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="7">
|
|
||||||
<v-subheader>Tickets created per week</v-subheader>
|
|
||||||
<line-chart
|
<line-chart
|
||||||
v-if="tickets_per_week"
|
v-if="widget.type === 'line' && data[index]"
|
||||||
:chart-data="tickets_per_week"
|
:chart-data="data[index]"
|
||||||
:styles="{ width: '100%', position: 'relative' }"
|
:styles="{ width: '100%', position: 'relative' }"
|
||||||
:chart-options="{
|
:chart-options="{
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
legend: undefined,
|
legend: false,
|
||||||
scales: { yAxes: [ { ticks: { beginAtZero: true, precision: 0 } } ] }
|
scales: { yAxes: [ { ticks: { beginAtZero: true, precision: 0 } } ] }
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
</line-chart>
|
</line-chart>
|
||||||
</v-col>
|
|
||||||
<v-col cols="5">
|
|
||||||
<v-subheader>Ticket Types</v-subheader>
|
|
||||||
<pie-chart
|
<pie-chart
|
||||||
v-if="tickets_per_type"
|
v-if="widget.type === 'pie' && data[index]"
|
||||||
:chart-data="tickets_per_type"
|
:chart-data="data[index]"
|
||||||
:styles="{ width: '100%', position: 'relative' }"
|
:styles="{ width: '100%', position: 'relative' }"
|
||||||
:chart-options="{
|
:chart-options="{
|
||||||
onClick: clickPie,
|
responsive: true,
|
||||||
hover: {
|
maintainAspectRatio: false,
|
||||||
onHover: function(e) {
|
}"
|
||||||
var point = this.getElementAtEvent(e);
|
|
||||||
if (point.length) e.target.style.cursor = 'pointer';
|
|
||||||
else e.target.style.cursor = 'default';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
>
|
>
|
||||||
</pie-chart>
|
</pie-chart>
|
||||||
</v-col>
|
|
||||||
</v-row>
|
<bar-chart
|
||||||
</v-col>
|
v-if="widget.type === 'bar' && data[index]"
|
||||||
<v-col cols="12" lg="5">
|
:chart-data="data[index]"
|
||||||
<TicketList :type="this.$route.params.type" @click="open"></TicketList>
|
:styles="{
|
||||||
|
width: '100%',
|
||||||
|
'max-height': '400px',
|
||||||
|
position: 'relative'
|
||||||
|
}"
|
||||||
|
:chart-options="{
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
legend: false,
|
||||||
|
scales: { xAxes: [ { ticks: { beginAtZero: true, precision: 0 } } ] },
|
||||||
|
}"
|
||||||
|
></bar-chart>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-main>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
|
import {DashboardResponse, Widget} from "@/client";
|
||||||
|
import { API } from "@/services/api";
|
||||||
|
import {createHash} from "crypto";
|
||||||
|
import {colors} from "@/plugins/vuetify";
|
||||||
import LineChart from "../components/charts/Line";
|
import LineChart from "../components/charts/Line";
|
||||||
import BarChart from "../components/charts/Bar";
|
import BarChart from "../components/charts/Bar";
|
||||||
import PieChart from "../components/charts/Doughnut";
|
import PieChart from "../components/charts/Doughnut";
|
||||||
import { API } from "@/services/api";
|
import {ChartData} from "chart.js";
|
||||||
import {Statistics, TicketResponse} from "@/client";
|
import {AxiosError, AxiosTransformer} from "axios";
|
||||||
import {DateTime} from "luxon";
|
|
||||||
import { colors } from "@/plugins/vuetify";
|
interface State {
|
||||||
import TicketList from "@/components/TicketList.vue";
|
dashboard?: DashboardResponse;
|
||||||
import { createHash } from "crypto";
|
undodashboard?: DashboardResponse;
|
||||||
|
data: Record<string, any>;
|
||||||
|
editmode: boolean;
|
||||||
|
widgetErrors: Record<number, string>;
|
||||||
|
}
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: "Dashboard",
|
name: "Dashboard",
|
||||||
@@ -112,108 +125,130 @@ export default Vue.extend({
|
|||||||
LineChart,
|
LineChart,
|
||||||
BarChart,
|
BarChart,
|
||||||
PieChart,
|
PieChart,
|
||||||
TicketList
|
|
||||||
},
|
},
|
||||||
data() {
|
data: (): State => ({
|
||||||
return {
|
dashboard: undefined,
|
||||||
statistics: (undefined as unknown) as Statistics
|
undodashboard: undefined,
|
||||||
};
|
data: {},
|
||||||
},
|
editmode: false,
|
||||||
computed: {
|
widgetErrors: {},
|
||||||
tickets_per_type: function () {
|
}),
|
||||||
let data = { labels: [] as Array<string>, datasets: [{ backgroundColor: [] as Array<string>, data: [] as Array<number> }] }
|
watch: {
|
||||||
this.lodash.forEach(this.statistics.tickets_per_type, (count, type) => {
|
$route: function () {
|
||||||
data.labels.push(type);
|
this.loadDashboard();
|
||||||
data.datasets[0].data.push(count);
|
|
||||||
|
|
||||||
data.datasets[0].backgroundColor.push(this.color(type));
|
|
||||||
})
|
|
||||||
return data
|
|
||||||
},
|
},
|
||||||
open_tickets_per_user: function () {
|
|
||||||
let data = { labels: [] as Array<string>, datasets: [{ backgroundColor: [] as Array<string>, data: [] as Array<number> }] }
|
|
||||||
this.lodash.forEach(this.statistics.open_tickets_per_user, (count, user) => {
|
|
||||||
if (!user) {
|
|
||||||
data.labels.push("unassigned");
|
|
||||||
} else {
|
|
||||||
data.labels.push(user);
|
|
||||||
}
|
|
||||||
data.datasets[0].data.push(count);
|
|
||||||
data.datasets[0].backgroundColor.push(this.color(user));
|
|
||||||
})
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
tickets_per_week: function () {
|
|
||||||
let data = {labels: [] as Array<string>, datasets: [{backgroundColor: [] as Array<string>, data: [] as Array<number> }]}
|
|
||||||
this.lodash.forEach(this.weeks(), (week) => {
|
|
||||||
data.labels.push(week);
|
|
||||||
if (week in this.statistics.tickets_per_week) {
|
|
||||||
data.datasets[0].data.push(this.statistics.tickets_per_week[week]);
|
|
||||||
} else {
|
|
||||||
data.datasets[0].data.push(0);
|
|
||||||
}
|
|
||||||
data.datasets[0].backgroundColor.push("#607d8b");
|
|
||||||
})
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
open: function (ticket: TicketResponse) {
|
edit: function () {
|
||||||
if (ticket.id === undefined) {
|
this.undodashboard = this.lodash.cloneDeep(this.dashboard);
|
||||||
return;
|
this.editmode = true;
|
||||||
|
},
|
||||||
|
save: function () {
|
||||||
|
if (!this.dashboard) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$router.push({
|
let widgets = [] as Array<Widget>;
|
||||||
name: "Ticket",
|
this.lodash.forEach(this.dashboard.widgets, (widget) => {
|
||||||
params: {type: '-', id: ticket.id.toString()}
|
widget.width = this.lodash.toInteger(widget.width);
|
||||||
});
|
if (!widget.filter) {
|
||||||
},
|
this.lodash.unset(widget, "filter")
|
||||||
clickUser: function (evt, elem) {
|
}
|
||||||
let owner = this.open_tickets_per_user.labels[elem[0]._index];
|
widgets.push(widget);
|
||||||
let query = 'status == \'open\' AND owner == \'' + owner + '\'';
|
})
|
||||||
|
this.dashboard.widgets = widgets;
|
||||||
|
|
||||||
if (owner == 'unassigned') {
|
if (this.$route.params.id === 'new') {
|
||||||
query = 'status == \'open\' AND !owner';
|
API.createDashboard(this.dashboard).then((response) => {
|
||||||
|
this.loadWidgetData(response.data.widgets);
|
||||||
|
|
||||||
|
this.dashboard = response.data;
|
||||||
|
this.editmode = false;
|
||||||
|
|
||||||
|
this.$router.push({ name: "Dashboard", params: { id: response.data.id }})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
API.updateDashboard(this.dashboard.id, this.dashboard).then((response) => {
|
||||||
|
this.loadWidgetData(response.data.widgets);
|
||||||
|
|
||||||
|
this.dashboard = response.data;
|
||||||
|
this.editmode = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancel: function () {
|
||||||
|
this.dashboard = this.lodash.cloneDeep(this.undodashboard);
|
||||||
|
this.editmode = false;
|
||||||
|
},
|
||||||
|
addWidget: function () {
|
||||||
|
if (!this.dashboard) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$router.push({
|
this.dashboard.widgets.push({name: "new widget", width: 6, aggregation: "", type: "line"})
|
||||||
name: "TicketList",
|
|
||||||
params: {query: query}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
clickPie: function (evt, elem) {
|
removeWidget: function (id: number) {
|
||||||
this.$router.push({
|
if (!this.dashboard) {
|
||||||
name: "TicketList",
|
return
|
||||||
params: {type: this.tickets_per_type.labels[elem[0]._index]}
|
}
|
||||||
});
|
|
||||||
|
console.log(id);
|
||||||
|
let widgets = this.lodash.cloneDeep(this.dashboard.widgets);
|
||||||
|
this.lodash.pullAt(widgets, [id]);
|
||||||
|
Vue.set(this.dashboard, "widgets", widgets);
|
||||||
|
},
|
||||||
|
loadDashboard: function () {
|
||||||
|
if (this.$route.params.id === 'new') {
|
||||||
|
this.dashboard = {
|
||||||
|
name: "New dashboard",
|
||||||
|
widgets: [{name: "new widget", width: 6, aggregation: "", type: "line"}],
|
||||||
|
} as DashboardResponse
|
||||||
|
this.editmode = true;
|
||||||
|
} else {
|
||||||
|
API.getDashboard(this.$route.params.id).then((response) => {
|
||||||
|
this.loadWidgetData(response.data.widgets);
|
||||||
|
|
||||||
|
this.dashboard = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadWidgetData: function (widgets: Array<Widget>) {
|
||||||
|
this.lodash.forEach(widgets, (widget: Widget, index: number) => {
|
||||||
|
let widgetErrors = {};
|
||||||
|
let defaultTransformers = this.axios.defaults.transformResponse as AxiosTransformer[]
|
||||||
|
let transformResponse = defaultTransformers.concat((data) => {
|
||||||
|
data.notoast = true;
|
||||||
|
return data
|
||||||
|
});
|
||||||
|
API.dashboardData(widget.aggregation, widget.filter, {transformResponse: transformResponse}).then((response) => {
|
||||||
|
let d = { labels: [], datasets: [{data: [], backgroundColor: []}] } as ChartData;
|
||||||
|
this.lodash.forEach(response.data, (v: any, k: string) => {
|
||||||
|
// @ts-expect-error T2532
|
||||||
|
d.labels.push(k)
|
||||||
|
// @ts-expect-error T2532
|
||||||
|
d.datasets[0].data.push(v)
|
||||||
|
|
||||||
|
if (widget.type !== 'line') {
|
||||||
|
// @ts-expect-error T2532
|
||||||
|
d.datasets[0].backgroundColor.push(this.color(this.lodash.toString(v)));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Vue.set(this.data, index, d);
|
||||||
|
}).catch((err: AxiosError) => {
|
||||||
|
widgetErrors[index] = this.lodash.toString(err.response?.data.error);
|
||||||
|
Vue.set(this.data, index, null);
|
||||||
|
})
|
||||||
|
Vue.set(this, 'widgetErrors', widgetErrors);
|
||||||
|
})
|
||||||
},
|
},
|
||||||
color: function (s: string): string {
|
color: function (s: string): string {
|
||||||
let pos = createHash('md5').update(s).digest().readUInt32BE(0) % colors.length;
|
let pos = createHash('md5').update(s).digest().readUInt32BE(0) % colors.length;
|
||||||
return colors[pos];
|
return colors[pos];
|
||||||
},
|
},
|
||||||
fillData() {
|
|
||||||
API.getStatistics().then(response => {
|
|
||||||
this.statistics = response.data;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
weeks: function () {
|
|
||||||
let w = [] as Array<string>;
|
|
||||||
for (let i = 0; i < 53; i++) {
|
|
||||||
w.push(DateTime.utc().minus({ weeks: i }).toFormat("kkkk-WW"))
|
|
||||||
}
|
|
||||||
this.lodash.reverse(w);
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.fillData();
|
this.loadDashboard();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
canvas {
|
|
||||||
position: relative !important;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
64
ui/src/views/DashboardList.vue
Normal file
64
ui/src/views/DashboardList.vue
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<v-main style="min-height: 100vh;">
|
||||||
|
<List
|
||||||
|
:items="dashboards"
|
||||||
|
routername="Dashboard"
|
||||||
|
itemid="id"
|
||||||
|
itemname="name"
|
||||||
|
singular="Dashboard"
|
||||||
|
plural="Dashboards"
|
||||||
|
writepermission="admin:dashboard:write"
|
||||||
|
@delete="deleteDashboard"
|
||||||
|
></List>
|
||||||
|
</v-main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from "vue";
|
||||||
|
|
||||||
|
import {Dashboard} from "@/client";
|
||||||
|
import {API} from "@/services/api";
|
||||||
|
import List from "../components/List.vue";
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
dashboards: Array<Dashboard>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
name: "DashboardList",
|
||||||
|
components: {List},
|
||||||
|
data: (): State => ({
|
||||||
|
dashboards: [],
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
loadDashboards() {
|
||||||
|
API.listDashboards().then((response) => {
|
||||||
|
this.dashboards = response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteDashboard(id: string) {
|
||||||
|
API.deleteDashboard(id).then(() => {
|
||||||
|
this.loadDashboards();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadDashboards();
|
||||||
|
|
||||||
|
this.$store.subscribeAction((action, state) => {
|
||||||
|
if (!action.payload || !(this.lodash.has(action.payload, "ids")) || !action.payload["ids"]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let reload = false;
|
||||||
|
Vue.lodash.forEach(action.payload["ids"], (id) => {
|
||||||
|
if (this.lodash.startsWith(id, "dashboard/")) {
|
||||||
|
reload = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (reload) {
|
||||||
|
this.loadDashboards()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
219
ui/src/views/Home.vue
Normal file
219
ui/src/views/Home.vue
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
<template>
|
||||||
|
<v-main>
|
||||||
|
<v-row>
|
||||||
|
<v-col v-if="statistics" cols="12" lg="7">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="4">
|
||||||
|
<v-subheader>Unassigned tickets</v-subheader>
|
||||||
|
<span style="font-size: 60pt; text-align: center; display: block">
|
||||||
|
<router-link :to="{
|
||||||
|
name: 'TicketList',
|
||||||
|
params: { query: 'status == \'open\' AND !owner' }
|
||||||
|
}">
|
||||||
|
{{ statistics.unassigned }}
|
||||||
|
</router-link>
|
||||||
|
</span>
|
||||||
|
<v-subheader>Your tickets</v-subheader>
|
||||||
|
<span style="font-size: 60pt; text-align: center; display: block">
|
||||||
|
<router-link :to="{
|
||||||
|
name: 'TicketList',
|
||||||
|
params: { query: 'status == \'open\' AND owner == \'' + $store.state.user.id + '\'' }
|
||||||
|
}">
|
||||||
|
{{ $store.state.user.id in statistics.open_tickets_per_user ? statistics.open_tickets_per_user[$store.state.user.id] : 0 }}
|
||||||
|
</router-link>
|
||||||
|
</span>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="8">
|
||||||
|
<v-subheader>Open tickets per owner</v-subheader>
|
||||||
|
<bar-chart
|
||||||
|
v-if="open_tickets_per_user"
|
||||||
|
:chart-data="open_tickets_per_user"
|
||||||
|
:styles="{
|
||||||
|
width: '100%',
|
||||||
|
'max-height': '400px',
|
||||||
|
position: 'relative'
|
||||||
|
}"
|
||||||
|
:chart-options="{
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
legend: undefined,
|
||||||
|
scales: { xAxes: [ { ticks: { beginAtZero: true, precision: 0 } } ] },
|
||||||
|
onClick: clickUser,
|
||||||
|
hover: {
|
||||||
|
onHover: function(e) {
|
||||||
|
var point = this.getElementAtEvent(e);
|
||||||
|
if (point.length) e.target.style.cursor = 'pointer';
|
||||||
|
else e.target.style.cursor = 'default';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
></bar-chart>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="7">
|
||||||
|
<v-subheader>Tickets created per week</v-subheader>
|
||||||
|
<line-chart
|
||||||
|
v-if="tickets_per_week"
|
||||||
|
:chart-data="tickets_per_week"
|
||||||
|
:styles="{ width: '100%', position: 'relative' }"
|
||||||
|
:chart-options="{
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
legend: undefined,
|
||||||
|
scales: { yAxes: [ { ticks: { beginAtZero: true, precision: 0 } } ] }
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
</line-chart>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="5">
|
||||||
|
<v-subheader>Ticket Types</v-subheader>
|
||||||
|
<pie-chart
|
||||||
|
v-if="tickets_per_type"
|
||||||
|
:chart-data="tickets_per_type"
|
||||||
|
:styles="{ width: '100%', position: 'relative' }"
|
||||||
|
:chart-options="{
|
||||||
|
onClick: clickPie,
|
||||||
|
hover: {
|
||||||
|
onHover: function(e) {
|
||||||
|
var point = this.getElementAtEvent(e);
|
||||||
|
if (point.length) e.target.style.cursor = 'pointer';
|
||||||
|
else e.target.style.cursor = 'default';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
</pie-chart>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" lg="5">
|
||||||
|
<TicketList :type="this.$route.params.type" @click="open"></TicketList>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from "vue";
|
||||||
|
import LineChart from "../components/charts/Line";
|
||||||
|
import BarChart from "../components/charts/Bar";
|
||||||
|
import PieChart from "../components/charts/Doughnut";
|
||||||
|
import { API } from "@/services/api";
|
||||||
|
import {Statistics, TicketResponse} from "@/client";
|
||||||
|
import {DateTime} from "luxon";
|
||||||
|
import { colors } from "@/plugins/vuetify";
|
||||||
|
import TicketList from "@/components/TicketList.vue";
|
||||||
|
import { createHash } from "crypto";
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
name: "Home",
|
||||||
|
components: {
|
||||||
|
LineChart,
|
||||||
|
BarChart,
|
||||||
|
PieChart,
|
||||||
|
TicketList
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
statistics: (undefined as unknown) as Statistics
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
tickets_per_type: function () {
|
||||||
|
let data = { labels: [] as Array<string>, datasets: [{ backgroundColor: [] as Array<string>, data: [] as Array<number> }] }
|
||||||
|
this.lodash.forEach(this.statistics.tickets_per_type, (count, type) => {
|
||||||
|
data.labels.push(type);
|
||||||
|
data.datasets[0].data.push(count);
|
||||||
|
|
||||||
|
data.datasets[0].backgroundColor.push(this.color(type));
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
open_tickets_per_user: function () {
|
||||||
|
let data = { labels: [] as Array<string>, datasets: [{ backgroundColor: [] as Array<string>, data: [] as Array<number> }] }
|
||||||
|
this.lodash.forEach(this.statistics.open_tickets_per_user, (count, user) => {
|
||||||
|
if (!user) {
|
||||||
|
data.labels.push("unassigned");
|
||||||
|
} else {
|
||||||
|
data.labels.push(user);
|
||||||
|
}
|
||||||
|
data.datasets[0].data.push(count);
|
||||||
|
data.datasets[0].backgroundColor.push(this.color(user));
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
tickets_per_week: function () {
|
||||||
|
let data = {labels: [] as Array<string>, datasets: [{backgroundColor: [] as Array<string>, data: [] as Array<number> }]}
|
||||||
|
this.lodash.forEach(this.weeks(), (week) => {
|
||||||
|
data.labels.push(week);
|
||||||
|
if (week in this.statistics.tickets_per_week) {
|
||||||
|
data.datasets[0].data.push(this.statistics.tickets_per_week[week]);
|
||||||
|
} else {
|
||||||
|
data.datasets[0].data.push(0);
|
||||||
|
}
|
||||||
|
data.datasets[0].backgroundColor.push("#607d8b");
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open: function (ticket: TicketResponse) {
|
||||||
|
if (ticket.id === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$router.push({
|
||||||
|
name: "Ticket",
|
||||||
|
params: {type: '-', id: ticket.id.toString()}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
clickUser: function (evt, elem) {
|
||||||
|
let owner = this.open_tickets_per_user.labels[elem[0]._index];
|
||||||
|
let query = 'status == \'open\' AND owner == \'' + owner + '\'';
|
||||||
|
|
||||||
|
if (owner == 'unassigned') {
|
||||||
|
query = 'status == \'open\' AND !owner';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$router.push({
|
||||||
|
name: "TicketList",
|
||||||
|
params: {query: query}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
clickPie: function (evt, elem) {
|
||||||
|
this.$router.push({
|
||||||
|
name: "TicketList",
|
||||||
|
params: {type: this.tickets_per_type.labels[elem[0]._index]}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
color: function (s: string): string {
|
||||||
|
let pos = createHash('md5').update(s).digest().readUInt32BE(0) % colors.length;
|
||||||
|
return colors[pos];
|
||||||
|
},
|
||||||
|
fillData() {
|
||||||
|
API.getStatistics().then(response => {
|
||||||
|
this.statistics = response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
weeks: function () {
|
||||||
|
let w = [] as Array<string>;
|
||||||
|
for (let i = 0; i < 53; i++) {
|
||||||
|
w.push(DateTime.utc().minus({ weeks: i }).toFormat("kkkk-WW"))
|
||||||
|
}
|
||||||
|
this.lodash.reverse(w);
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fillData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
canvas {
|
||||||
|
position: relative !important;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user