Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -16442,6 +16442,58 @@ const docTemplate = `{
]
}
},
"/oscal/system-security-plans/{id}/dashboard-suggestions/control-results": {
"get": {
"description": "Returns the latest dashboard-suggestion evaluation outcome per evaluated control.",
"produces": [
"application/json"
],
"tags": [
"Dashboard Suggestions"
],
"summary": "List dashboard suggestion control results for an SSP",
"parameters": [
{
"type": "string",
"description": "System Security Plan ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.GenericDataListResponse-oscal_controlSuggestionResultResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
},
"security": [
{
"OAuth2Password": []
}
]
}
},
"/oscal/system-security-plans/{id}/dashboard-suggestions/edit-group": {
"post": {
"description": "Edits the title, proposed filter labels, and control membership of a pending suggestion group. User-provided labels are stored verbatim (the evidence-subset rule is bypassed) and the rows are flagged as user-edited.",
Expand Down Expand Up @@ -29416,6 +29468,19 @@ const docTemplate = `{
"meta": {}
}
},
"handler.GenericDataListResponse-oscal_controlSuggestionResultResponse": {
"type": "object",
"properties": {
"data": {
"description": "Items from the list response",
"type": "array",
"items": {
"$ref": "#/definitions/oscal.controlSuggestionResultResponse"
}
},
"meta": {}
}
},
"handler.GenericDataListResponse-oscal_dashboardSuggestionEventResponse": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -33312,6 +33377,29 @@ const docTemplate = `{
}
}
},
"oscal.controlSuggestionResultResponse": {
"type": "object",
"properties": {
"controlCatalogId": {
"type": "string"
},
"controlId": {
"type": "string"
},
"evaluatedAt": {
"type": "string"
},
"outcome": {
"type": "string"
},
"runId": {
"type": "string"
},
"suggestionCount": {
"type": "integer"
}
}
},
"oscal.dashboardSuggestionConfigResponse": {
"type": "object",
"properties": {
Expand Down
88 changes: 88 additions & 0 deletions docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -16436,6 +16436,58 @@
]
}
},
"/oscal/system-security-plans/{id}/dashboard-suggestions/control-results": {
"get": {
"description": "Returns the latest dashboard-suggestion evaluation outcome per evaluated control.",
"produces": [
"application/json"
],
"tags": [
"Dashboard Suggestions"
],
"summary": "List dashboard suggestion control results for an SSP",
"parameters": [
{
"type": "string",
"description": "System Security Plan ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.GenericDataListResponse-oscal_controlSuggestionResultResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
},
"security": [
{
"OAuth2Password": []
}
]
}
},
"/oscal/system-security-plans/{id}/dashboard-suggestions/edit-group": {
"post": {
"description": "Edits the title, proposed filter labels, and control membership of a pending suggestion group. User-provided labels are stored verbatim (the evidence-subset rule is bypassed) and the rows are flagged as user-edited.",
Expand Down Expand Up @@ -29410,6 +29462,19 @@
"meta": {}
}
},
"handler.GenericDataListResponse-oscal_controlSuggestionResultResponse": {
"type": "object",
"properties": {
"data": {
"description": "Items from the list response",
"type": "array",
"items": {
"$ref": "#/definitions/oscal.controlSuggestionResultResponse"
}
},
"meta": {}
}
},
"handler.GenericDataListResponse-oscal_dashboardSuggestionEventResponse": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -33306,6 +33371,29 @@
}
}
},
"oscal.controlSuggestionResultResponse": {
"type": "object",
"properties": {
"controlCatalogId": {
"type": "string"
},
"controlId": {
"type": "string"
},
"evaluatedAt": {
"type": "string"
},
"outcome": {
"type": "string"
},
"runId": {
"type": "string"
},
"suggestionCount": {
"type": "integer"
}
}
},
"oscal.dashboardSuggestionConfigResponse": {
"type": "object",
"properties": {
Expand Down
58 changes: 58 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,15 @@ definitions:
type: array
meta: {}
type: object
handler.GenericDataListResponse-oscal_controlSuggestionResultResponse:
properties:
data:
description: Items from the list response
items:
$ref: '#/definitions/oscal.controlSuggestionResultResponse'
type: array
meta: {}
type: object
handler.GenericDataListResponse-oscal_dashboardSuggestionEventResponse:
properties:
data:
Expand Down Expand Up @@ -3377,6 +3386,21 @@ definitions:
profileId:
type: string
type: object
oscal.controlSuggestionResultResponse:
properties:
controlCatalogId:
type: string
controlId:
type: string
evaluatedAt:
type: string
outcome:
type: string
runId:
type: string
suggestionCount:
type: integer
type: object
oscal.dashboardSuggestionConfigResponse:
properties:
enabled:
Expand Down Expand Up @@ -21223,6 +21247,40 @@ paths:
summary: Accept dashboard suggestions
tags:
- Dashboard Suggestions
/oscal/system-security-plans/{id}/dashboard-suggestions/control-results:
get:
description: Returns the latest dashboard-suggestion evaluation outcome per
evaluated control.
parameters:
- description: System Security Plan ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.GenericDataListResponse-oscal_controlSuggestionResultResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/api.Error'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/api.Error'
security:
- OAuth2Password: []
summary: List dashboard suggestion control results for an SSP
tags:
- Dashboard Suggestions
/oscal/system-security-plans/{id}/dashboard-suggestions/edit-group:
post:
consumes:
Expand Down
50 changes: 50 additions & 0 deletions internal/api/handler/oscal/dashboard_suggestions.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ type dashboardSuggestionResponse struct {
TargetFilterName string `json:"targetFilterName,omitempty"`
}

type controlSuggestionResultResponse struct {
ControlID string `json:"controlId"`
ControlCatalogID uuid.UUID `json:"controlCatalogId"`
Outcome string `json:"outcome"`
SuggestionCount int `json:"suggestionCount"`
RunID uuid.UUID `json:"runId"`
EvaluatedAt *time.Time `json:"evaluatedAt"`
}

// suggestionEventActor carries the resolved display details for the user that
// triggered a dashboard suggestion event, so the UI can render who acted rather
// than just an opaque user ID.
Expand Down Expand Up @@ -148,6 +157,7 @@ func (h *DashboardSuggestionHandler) Register(apiGroup *echo.Group, auth echo.Mi
apiGroup.GET("/:id/dashboard-suggestion-runs/latest", h.LatestRun, auth)
apiGroup.GET("/:id/dashboard-suggestion-runs/:runId", h.GetRun, auth)
apiGroup.GET("/:id/dashboard-suggestions", h.ListSuggestions, auth)
apiGroup.GET("/:id/dashboard-suggestions/control-results", h.ControlResults, auth)
apiGroup.POST("/:id/dashboard-suggestions/accept", h.Accept, auth)
apiGroup.POST("/:id/dashboard-suggestions/reject", h.Reject, auth)
apiGroup.POST("/:id/dashboard-suggestions/edit-group", h.EditGroup, auth)
Expand Down Expand Up @@ -532,6 +542,46 @@ func (h *DashboardSuggestionHandler) ListSuggestions(ctx echo.Context) error {
return ctx.JSON(http.StatusOK, handler.GenericDataListResponse[dashboardSuggestionResponse]{Data: suggestions})
}

// ControlResults godoc
//
// @Summary List dashboard suggestion control results for an SSP
// @Description Returns the latest dashboard-suggestion evaluation outcome per evaluated control.
// @Tags Dashboard Suggestions
// @Produce json
// @Param id path string true "System Security Plan ID"
// @Success 200 {object} handler.GenericDataListResponse[oscal.controlSuggestionResultResponse]
// @Failure 400 {object} api.Error
// @Failure 401 {object} api.Error
// @Failure 500 {object} api.Error
// @Security OAuth2Password
// @Router /oscal/system-security-plans/{id}/dashboard-suggestions/control-results [get]
func (h *DashboardSuggestionHandler) ControlResults(ctx echo.Context) error {
sspID, err := parseUUIDParam(ctx, "id")
if err != nil {
return ctx.JSON(http.StatusBadRequest, api.NewError(err))
}
var results []controlSuggestionResultResponse
if err := h.db.WithContext(ctx.Request().Context()).Raw(`
SELECT DISTINCT ON (control_results.control_catalog_id, control_results.control_id)
control_results.control_id,
control_results.control_catalog_id,
control_results.outcome,
control_results.suggestion_count,
control_results.run_id,
control_results.evaluated_at
FROM dashboard_suggestion_control_results AS control_results
JOIN dashboard_suggestion_runs AS runs ON runs.id = control_results.run_id
WHERE control_results.ssp_id = ?
ORDER BY control_results.control_catalog_id ASC,
control_results.control_id ASC,
runs.started_at DESC NULLS LAST,
runs.id DESC
`, sspID).Scan(&results).Error; err != nil {
return ctx.JSON(http.StatusInternalServerError, api.NewError(err))
}
return ctx.JSON(http.StatusOK, handler.GenericDataListResponse[controlSuggestionResultResponse]{Data: results})
}

// Accept godoc
//
// @Summary Accept dashboard suggestions
Expand Down
Loading
Loading