Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
6 changes: 4 additions & 2 deletions apps/csm-portal/backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ SCIM_SCOPES=
# restarting the backend process. Left unset or malformed, GET /dashboards
# returns an empty list and GET /dashboards/{id} 404s for every id; startup
# and every other endpoint work normally (an error is logged, not a crash).
# Example (the pilot's 5 dashboards):
# DASHBOARDS_CONFIG='[{"id":"agents_pilot","displayName":"Engineer overview","isDefault":true,"targetTeam":"cs_engineers","widgets":[{"id":"my_patches","displayName":"My Patches","resourceType":"case","shape":"count","gridWidth":3,"filters":{"filters":[{"field":"assignedUserId","op":"in","values":["__current_user__"]},{"field":"tag","op":"in","values":["patch"]},{"field":"state","op":"in","values":["open","work_in_progress","waiting_on_wso2","reopened","awaiting_info"]}]}},{"id":"my_reminders","displayName":"My Reminders","resourceType":"case","shape":"count","gridWidth":3,"filters":{"filters":[{"field":"assignedUserId","op":"in","values":["__current_user__"]},{"field":"state","op":"in","values":["awaiting_info","solution_proposed"]}]}},{"id":"open_incident_team","displayName":"Open Incident (Team)","resourceType":"case","shape":"count","gridWidth":3,"filters":{"filters":[{"field":"tag","op":"in","values":["s_dip"]},{"field":"state","op":"in","values":["work_in_progress","open","waiting_on_wso2","reopened"]}]}},{"id":"my_critical_open","displayName":"My Critical & High Cases","resourceType":"case","shape":"list","gridWidth":3,"listLimit":5,"filters":{"filters":[{"field":"assignedUserId","op":"in","values":["__current_user__"]},{"field":"severity","op":"in","values":["catastrophic","critical"]},{"field":"state","op":"in","values":["open","work_in_progress"]}]}}]},{"id":"operations","displayName":"Operations","targetTeam":"cs_operations","widgets":[{"id":"p0_p1_open","displayName":"P0/P1 Open","resourceType":"case","shape":"count","gridWidth":4,"filters":{"filters":[{"field":"severity","op":"in","values":["catastrophic","critical"]},{"field":"state","op":"in","values":["open","work_in_progress"]}]}},{"id":"open_critical_incidents","displayName":"Open Critical Incidents","resourceType":"incident","shape":"count","gridWidth":4,"filters":{"priorities":["CRITICAL","HIGH"]}},{"id":"crs_awaiting_approval","displayName":"CRs Awaiting Approval","resourceType":"change_request","shape":"count","gridWidth":4,"filters":{"states":["customer_approval"]}}]},{"id":"iam","displayName":"IAM CS","targetTeam":"iam_cs","widgets":[{"id":"iam_open_cases","displayName":"IAM Open Cases","resourceType":"case","shape":"count","gridWidth":6,"filters":{"filters":[{"field":"tag","op":"in","values":["iam"]},{"field":"state","op":"in","values":["open","work_in_progress","awaiting_info"]}]}},{"id":"asgardeo_open_cases","displayName":"Asgardeo Open Cases","resourceType":"case","shape":"count","gridWidth":6,"filters":{"filters":[{"field":"tag","op":"in","values":["asgardeo"]},{"field":"state","op":"in","values":["open","work_in_progress","awaiting_info"]}]}}]},{"id":"security","displayName":"Security center","targetTeam":"security","widgets":[{"id":"critical_vulns","displayName":"Critical Vulnerabilities","resourceType":"product_vulnerability","shape":"count","gridWidth":4,"filters":{"priority":"critical"}},{"id":"high_vulns","displayName":"High Vulnerabilities","resourceType":"product_vulnerability","shape":"count","gridWidth":4,"filters":{"priority":"high"}},{"id":"sra_cases_open","displayName":"Open SRAs","resourceType":"case","shape":"count","gridWidth":4,"filters":{"filters":[{"field":"type","op":"in","values":["security_report_analysis"]},{"field":"state","op":"in","values":["open","work_in_progress","awaiting_info"]}]}}]},{"id":"team_performance","displayName":"Team performance","targetTeam":"cs_team_leads","isTeamBased":true,"widgets":[{"id":"time_cards_pending_approval","displayName":"Time Cards Pending Approval","resourceType":"time_card","shape":"count","gridWidth":6,"filters":{"states":["pending"]}},{"id":"team_open_cases","displayName":"Team Open P0/P1","resourceType":"case","shape":"count","gridWidth":6,"filters":{"filters":[{"field":"severity","op":"in","values":["catastrophic","critical"]},{"field":"state","op":"in","values":["open","work_in_progress"]}]}}]}]'
# Example (illustrative only — a real deployment's value is set directly in
# its own environment, not committed here, and is not required to resemble
# this shape beyond the schema):
# DASHBOARDS_CONFIG='[{"id":"sample-dashboard","displayName":"Sample Dashboard","isDefault":true,"targetTeam":"sample-team","widgets":[{"id":"my-open-cases","displayName":"My Open Cases","resourceType":"case","shape":"count","gridWidth":3,"filters":{"filters":[{"field":"assignedUserId","op":"in","values":["__current_user__"]},{"field":"state","op":"in","values":["open","work_in_progress"]}]}},{"id":"recent-cases","displayName":"Recent Cases","resourceType":"case","shape":"list","gridWidth":6,"listLimit":5,"filters":{"filters":[{"field":"tag","op":"in","values":["example-tag"]},{"field":"tag","op":"notIn","values":["excluded-example-tag"]}]}},{"id":"pending-time-cards","displayName":"Pending Time Cards","resourceType":"time_card","shape":"count","gridWidth":3,"filters":{"states":["pending"]}}]},{"id":"sample-team-dashboard","displayName":"Sample Team Dashboard","targetTeam":"sample-team","isTeamBased":true,"widgets":[{"id":"team-open-cases","displayName":"Team Open Cases","section":"Overview","resourceType":"case","shape":"count","gridWidth":4,"filters":{"filters":[{"field":"severity","op":"in","values":["critical","high"]},{"field":"state","op":"in","values":["open","work_in_progress"]}]}},{"id":"unassigned-cases","displayName":"Unassigned Cases","section":"Overview","resourceType":"case","shape":"count","gridWidth":4,"filters":{"filters":[{"field":"assignedUserId","op":"isEmpty"},{"field":"state","op":"in","values":["open"]}]}},{"id":"cases-by-severity","displayName":"Cases by Severity","description":"Share of active cases at each severity level.","resourceType":"case","shape":"pie","gridWidth":4,"filters":{"filters":[{"field":"state","op":"in","values":["open","work_in_progress"]}]},"slices":[{"label":"Critical","color":"error","filters":{"filters":[{"field":"severity","op":"in","values":["critical"]}]}},{"label":"Mine","filters":{"filters":[{"field":"assignedUserId","op":"in","values":["__current_user__"]}]}}]}]}]'

# Auth — set to false for local testing (skips JWT signature verification)
AUTH_JWKS_ENDPOINT=
Expand Down
50 changes: 25 additions & 25 deletions apps/csm-portal/backend/internal/dashboard/widgets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,21 @@ func TestParseDashboardsConfig_MalformedShape(t *testing.T) {
func TestParseDashboardsConfig_ValidRoundTrip(t *testing.T) {
const raw = `[
{
"id": "agents_pilot",
"displayName": "Engineer overview",
"id": "sample-dashboard",
"displayName": "Sample Dashboard",
"isDefault": true,
"targetTeam": "cs_engineers",
"targetTeam": "sample-team",
"widgets": [
{
"id": "my_patches",
"displayName": "My Patches",
"id": "my-open-cases",
"displayName": "My Open Cases",
"resourceType": "case",
"shape": "count",
"gridWidth": 3,
"filters": {
"filters": [
{"field": "assignedUserId", "op": "in", "values": ["__current_user__"]},
{"field": "tag", "op": "in", "values": ["patch"]},
{"field": "tag", "op": "in", "values": ["example-tag"]},
{"field": "state", "op": "in", "values": ["open", "work_in_progress"]}
]
}
Expand All @@ -73,25 +73,25 @@ func TestParseDashboardsConfig_ValidRoundTrip(t *testing.T) {
}

d := got[0]
if d.ID != "agents_pilot" {
t.Errorf("Dashboard.ID = %q, want %q", d.ID, "agents_pilot")
if d.ID != "sample-dashboard" {
t.Errorf("Dashboard.ID = %q, want %q", d.ID, "sample-dashboard")
}
if d.DisplayName != "Engineer overview" {
t.Errorf("Dashboard.DisplayName = %q, want %q", d.DisplayName, "Engineer overview")
if d.DisplayName != "Sample Dashboard" {
t.Errorf("Dashboard.DisplayName = %q, want %q", d.DisplayName, "Sample Dashboard")
}
if !d.IsDefault {
t.Errorf("Dashboard.IsDefault = false, want true")
}
if d.TargetTeam != "cs_engineers" {
t.Errorf("Dashboard.TargetTeam = %q, want %q", d.TargetTeam, "cs_engineers")
if d.TargetTeam != "sample-team" {
t.Errorf("Dashboard.TargetTeam = %q, want %q", d.TargetTeam, "sample-team")
}
if len(d.Widgets) != 1 {
t.Fatalf("len(Dashboard.Widgets) = %d, want 1", len(d.Widgets))
}

w := d.Widgets[0]
if w.ID != "my_patches" {
t.Errorf("WidgetTemplate.ID = %q, want %q", w.ID, "my_patches")
if w.ID != "my-open-cases" {
t.Errorf("WidgetTemplate.ID = %q, want %q", w.ID, "my-open-cases")
}
if w.ResourceType != ResourceCase {
t.Errorf("WidgetTemplate.ResourceType = %q, want %q", w.ResourceType, ResourceCase)
Expand Down Expand Up @@ -147,12 +147,12 @@ func TestParseDashboardsConfig_ValidRoundTrip(t *testing.T) {
func TestParseDashboardsConfig_PieWidgetSlicesAndDescription(t *testing.T) {
const raw = `[
{
"id": "team_performance",
"displayName": "Team performance",
"id": "sample-team-dashboard",
"displayName": "Sample Team Dashboard",
"widgets": [
{
"id": "cases_by_severity",
"displayName": "Cases by severity",
"id": "cases-by-severity",
"displayName": "Cases by Severity",
"description": "Share of active cases at each severity level.",
"resourceType": "case",
"shape": "pie",
Expand Down Expand Up @@ -199,11 +199,11 @@ func TestParseDashboardsConfig_PieWidgetSlicesAndDescription(t *testing.T) {
func TestParseDashboardsConfig_WidgetSection(t *testing.T) {
const raw = `[
{
"id": "team_performance",
"displayName": "Team performance",
"id": "sample-team-dashboard",
"displayName": "Sample Team Dashboard",
"widgets": [
{"id": "team_open_cases", "displayName": "Team Open P0/P1", "resourceType": "case", "shape": "count", "gridWidth": 6, "filters": {}},
{"id": "incident_wow", "displayName": "Incident WOW", "section": "SLA Violation", "resourceType": "incident", "shape": "count", "gridWidth": 6, "filters": {}}
{"id": "team-open-cases", "displayName": "Team Open Cases", "resourceType": "case", "shape": "count", "gridWidth": 6, "filters": {}},
{"id": "escalated-incidents", "displayName": "Escalated Incidents", "section": "Escalations", "resourceType": "incident", "shape": "count", "gridWidth": 6, "filters": {}}
]
}
]`
Expand All @@ -214,9 +214,9 @@ func TestParseDashboardsConfig_WidgetSection(t *testing.T) {
}

if section := got[0].Widgets[0].Section; section != "" {
t.Errorf("team_open_cases.Section = %q, want empty (no section configured)", section)
t.Errorf("team-open-cases.Section = %q, want empty (no section configured)", section)
}
if section := got[0].Widgets[1].Section; section != "SLA Violation" {
t.Errorf("incident_wow.Section = %q, want %q", section, "SLA Violation")
if section := got[0].Widgets[1].Section; section != "Escalations" {
t.Errorf("escalated-incidents.Section = %q, want %q", section, "Escalations")
}
}
Loading