From 252bf0a1c85566eeee26ecdc8fb0e6a43e6cebd2 Mon Sep 17 00:00:00 2001 From: Nathan Coad Date: Mon, 24 Mar 2025 17:03:16 +1100 Subject: [PATCH] fix incident view --- components/views/incidents.templ | 85 +++++++++ components/views/incidents_templ.go | 280 ++++++++++++++++++++++++++++ components/views/incoming_templ.go | 2 +- db/queries/query.sql | 4 + db/queries/query.sql.go | 109 +++++++++++ internal/utils/utils.go | 84 ++++++++- server/handler/incidentReport.go | 53 ++++++ server/handler/incomingReport.go | 4 +- server/router/router.go | 1 + 9 files changed, 612 insertions(+), 10 deletions(-) create mode 100644 components/views/incidents.templ create mode 100644 components/views/incidents_templ.go create mode 100644 server/handler/incidentReport.go diff --git a/components/views/incidents.templ b/components/views/incidents.templ new file mode 100644 index 0000000..5e608d8 --- /dev/null +++ b/components/views/incidents.templ @@ -0,0 +1,85 @@ +package views + +import ( + "strconv" + "mocksnow/components/core" + tpl "github.com/a-h/templ" +) + +type IncidentRow struct { + ID int64 + ExternalID string + CreatedAt string + IncidentNumber string + Description string + ShortDescription string + Urgency int64 + Impact int64 + State int64 + AllNotes string + AssignmentGroup string + AssignedTo string + Category string + Subcategory string + SysID string +} + +templ IncidentsTable(rows []IncidentRow) { + + + @core.Header() + +
+
+

Incidents

+
+ + + + + + + + + + + + + + + + + + + + + + + for _, row := range rows { + + + + + + + + + + + + + + + + + + } + +
IDCreated AtIncident #SysIdDescriptionShort DescriptionUrgencyImpactStateExternal IDWork NotesAssignment GroupAssigned ToCategorySubcategory
{ strconv.FormatInt(row.ID, 10) }{ row.CreatedAt }{ row.IncidentNumber }{ row.SysID }{ row.Description }{ row.ShortDescription }{ strconv.FormatInt(row.Urgency, 10) }{ strconv.FormatInt(row.Impact, 10) }{ strconv.FormatInt(row.State, 10) }{ row.ExternalID }@tpl.Raw(row.AllNotes){ row.AssignmentGroup }{ row.AssignedTo }{ row.Category }{ row.Subcategory }
+
+
+
+ @core.Footer() + + +} \ No newline at end of file diff --git a/components/views/incidents_templ.go b/components/views/incidents_templ.go new file mode 100644 index 0000000..5d6ddb3 --- /dev/null +++ b/components/views/incidents_templ.go @@ -0,0 +1,280 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.856 +package views + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import ( + tpl "github.com/a-h/templ" + "mocksnow/components/core" + "strconv" +) + +type IncidentRow struct { + ID int64 + ExternalID string + CreatedAt string + IncidentNumber string + Description string + ShortDescription string + Urgency int64 + Impact int64 + State int64 + AllNotes string + AssignmentGroup string + AssignedTo string + Category string + Subcategory string + SysID string +} + +func IncidentsTable(rows []IncidentRow) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = core.Header().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "

Incidents

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for _, row := range rows { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "
IDCreated AtIncident #SysIdDescriptionShort DescriptionUrgencyImpactStateExternal IDWork NotesAssignment GroupAssigned ToCategorySubcategory
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var2 string + templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(row.ID, 10)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 60, Col: 71} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var3 string + templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(row.CreatedAt) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 61, Col: 55} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 string + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(row.IncidentNumber) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 62, Col: 60} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(row.SysID) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 63, Col: 51} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 string + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(row.Description) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 64, Col: 57} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var7 string + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(row.ShortDescription) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 65, Col: 62} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var8 string + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(row.Urgency, 10)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 66, Col: 76} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var9 string + templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(row.Impact, 10)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 67, Col: 75} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var10 string + templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(row.State, 10)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 68, Col: 74} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var11 string + templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(row.ExternalID) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 69, Col: 56} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = tpl.Raw(row.AllNotes).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var12 string + templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(row.AssignmentGroup) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 71, Col: 61} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var13 string + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(row.AssignedTo) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 72, Col: 56} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(row.Category) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 73, Col: 54} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var15 string + templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(row.Subcategory) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 74, Col: 57} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = core.Footer().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/components/views/incoming_templ.go b/components/views/incoming_templ.go index cb82688..6310de3 100644 --- a/components/views/incoming_templ.go +++ b/components/views/incoming_templ.go @@ -59,7 +59,7 @@ func IncomingTable(rows []IncomingRow) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "

Incoming Incidents

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "

Incoming API Calls

IDIncident #DescriptionShort DescriptionUrgencyImpactStateExternal IDWork NotesAssignment GroupAssigned ToCategorySubcategoryCreated At
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/db/queries/query.sql b/db/queries/query.sql index 1a9963b..56deb3e 100644 --- a/db/queries/query.sql +++ b/db/queries/query.sql @@ -50,6 +50,10 @@ WHERE incident_number = sqlc.arg('incidentNumber'); -- name: ListIncidents :many SELECT * FROM incidents; +-- name: GetIncidentReport :many +SELECT incidents.*, GROUP_CONCAT(worknotes.note, '
') AS all_notes +FROM incidents LEFT JOIN worknotes ON incidents.incident_number = worknotes.incident_number; + -- name: CreateWorkNote :one INSERT into worknotes ( "incident_number", "note" diff --git a/db/queries/query.sql.go b/db/queries/query.sql.go index e3e087d..f1f9bca 100644 --- a/db/queries/query.sql.go +++ b/db/queries/query.sql.go @@ -178,6 +178,68 @@ func (q *Queries) GetIncident(ctx context.Context, incidentnumber sql.NullString return i, err } +const getIncidentReport = `-- name: GetIncidentReport :many +SELECT incidents.id, incidents.external_id, incidents.created_at, incidents.incident_number, incidents.description, incidents.short_description, incidents.urgency, incidents.impact, incidents.state, incidents.assignment_group, incidents.assigned_to, incidents.category, incidents.subcategory, incidents.sys_id, GROUP_CONCAT(worknotes.note, '
') AS all_notes +FROM incidents LEFT JOIN worknotes ON incidents.incident_number = worknotes.incident_number +` + +type GetIncidentReportRow struct { + ID int64 + ExternalID string + CreatedAt sql.NullTime + IncidentNumber sql.NullString + Description sql.NullString + ShortDescription sql.NullString + Urgency sql.NullInt64 + Impact sql.NullInt64 + State sql.NullInt64 + AssignmentGroup sql.NullString + AssignedTo sql.NullString + Category sql.NullString + Subcategory sql.NullString + SysID sql.NullString + AllNotes string +} + +func (q *Queries) GetIncidentReport(ctx context.Context) ([]GetIncidentReportRow, error) { + rows, err := q.db.QueryContext(ctx, getIncidentReport) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetIncidentReportRow + for rows.Next() { + var i GetIncidentReportRow + if err := rows.Scan( + &i.ID, + &i.ExternalID, + &i.CreatedAt, + &i.IncidentNumber, + &i.Description, + &i.ShortDescription, + &i.Urgency, + &i.Impact, + &i.State, + &i.AssignmentGroup, + &i.AssignedTo, + &i.Category, + &i.Subcategory, + &i.SysID, + &i.AllNotes, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listIncidents = `-- name: ListIncidents :many SELECT id, external_id, created_at, incident_number, description, short_description, urgency, impact, state, assignment_group, assigned_to, category, subcategory, sys_id FROM incidents ` @@ -263,6 +325,53 @@ func (q *Queries) ListIncoming(ctx context.Context) ([]Incoming, error) { return items, nil } +const updateIncident = `-- name: UpdateIncident :exec +UPDATE incidents +SET + external_id = ?, + description = ?, + short_description = ?, + urgency = ?, + impact = ?, + state = ?, + assignment_group = ?, + assigned_to = ?, + category = ?, + subcategory = ? +WHERE incident_number = ? +` + +type UpdateIncidentParams struct { + ExternalID string + Description sql.NullString + ShortDescription sql.NullString + Urgency sql.NullInt64 + Impact sql.NullInt64 + State sql.NullInt64 + AssignmentGroup sql.NullString + AssignedTo sql.NullString + Category sql.NullString + Subcategory sql.NullString + IncidentNumber sql.NullString +} + +func (q *Queries) UpdateIncident(ctx context.Context, arg UpdateIncidentParams) error { + _, err := q.db.ExecContext(ctx, updateIncident, + arg.ExternalID, + arg.Description, + arg.ShortDescription, + arg.Urgency, + arg.Impact, + arg.State, + arg.AssignmentGroup, + arg.AssignedTo, + arg.Category, + arg.Subcategory, + arg.IncidentNumber, + ) + return err +} + const updateIncidentNumber = `-- name: UpdateIncidentNumber :exec UPDATE incidents SET incident_number = ?1 diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 03e5581..e805df9 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -2,6 +2,9 @@ package utils import ( "context" + "database/sql" + "encoding/base64" + "fmt" "log" "log/slog" "net" @@ -69,7 +72,7 @@ func SleepWithContext(ctx context.Context, d time.Duration) { } } -// Generic converter using reflection +// Generic converter using reflection for all sqlc/sqlite types func ConvertStruct(src interface{}, dst interface{}) { srcVal := reflect.ValueOf(src) srcType := reflect.TypeOf(src) @@ -77,30 +80,97 @@ func ConvertStruct(src interface{}, dst interface{}) { for i := 0; i < srcVal.NumField(); i++ { srcField := srcVal.Field(i) + srcFieldType := srcField.Type() dstField := dstVal.FieldByName(srcType.Field(i).Name) + //slog.Info("Source field", "name", srcType.Field(i).Name) + if !dstField.IsValid() || !dstField.CanSet() { continue } - switch srcField.Type().Name() { - case "NullString": + switch srcFieldType { + case reflect.TypeOf(sql.NullString{}): + //slog.Info("Sourcefield is string") if srcField.FieldByName("Valid").Bool() { dstField.SetString(srcField.FieldByName("String").String()) } else { dstField.SetString("") } - case "NullTime": + + case reflect.TypeOf(sql.NullInt64{}): + //slog.Info("Sourcefield is int64") + if srcField.FieldByName("Valid").Bool() { + val := srcField.FieldByName("Int64").Int() + if dstField.Kind() == reflect.String { + dstField.SetString(fmt.Sprintf("%d", val)) + } else { + dstField.SetInt(val) + } + } else { + if dstField.Kind() == reflect.String { + dstField.SetString("") + } else { + dstField.SetInt(0) + } + } + + case reflect.TypeOf(sql.NullFloat64{}): + if srcField.FieldByName("Valid").Bool() { + val := srcField.FieldByName("Float64").Float() + if dstField.Kind() == reflect.String { + dstField.SetString(fmt.Sprintf("%f", val)) + } else { + dstField.SetFloat(val) + } + } else { + if dstField.Kind() == reflect.String { + dstField.SetString("") + } else { + dstField.SetFloat(0) + } + } + + case reflect.TypeOf(sql.NullBool{}): + if srcField.FieldByName("Valid").Bool() { + val := srcField.FieldByName("Bool").Bool() + if dstField.Kind() == reflect.String { + dstField.SetString(fmt.Sprintf("%t", val)) + } else { + dstField.SetBool(val) + } + } else { + if dstField.Kind() == reflect.String { + dstField.SetString("") + } else { + dstField.SetBool(false) + } + } + + case reflect.TypeOf(sql.NullTime{}): + //slog.Info("Sourcefield is time") if srcField.FieldByName("Valid").Bool() { t := srcField.FieldByName("Time").Interface().(time.Time) dstField.SetString(t.Format("2006-01-02 15:04:05")) } else { + slog.Info("value is not valid") dstField.SetString("") } + default: - // Handle int64 -> int conversion - if srcField.Kind() == reflect.Int64 && dstField.Kind() == reflect.Int { - dstField.SetInt(srcField.Int()) + // Handle []byte (often from BLOB fields) + if srcField.Kind() == reflect.Slice && srcField.Type().Elem().Kind() == reflect.Uint8 { + if !srcField.IsNil() { + encoded := base64.StdEncoding.EncodeToString(srcField.Bytes()) + dstField.SetString(encoded) + } else { + dstField.SetString("") + } + } + + // Fallback: copy if types are directly assignable + if srcField.Type().AssignableTo(dstField.Type()) { + dstField.Set(srcField) } } } diff --git a/server/handler/incidentReport.go b/server/handler/incidentReport.go new file mode 100644 index 0000000..662dec7 --- /dev/null +++ b/server/handler/incidentReport.go @@ -0,0 +1,53 @@ +package handler + +import ( + "context" + "encoding/json" + "fmt" + "mocksnow/components/views" + "mocksnow/db/queries" + "mocksnow/internal/utils" + "net/http" +) + +func (h *Handler) RenderIncidentTable(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + + h.Logger.Debug("Querying incidents table") + results, err := h.Database.Queries().GetIncidentReport(ctx) + if err != nil { + h.Logger.Error("Unable to query incoming table", "error", err) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + json.NewEncoder(w).Encode(map[string]string{ + "status": "ERROR", + "message": fmt.Sprintf("Unable to query incoming table: '%s'", err), + }) + return + } + + if len(results) == 0 { + h.Logger.Error("Empty updates result") + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + json.NewEncoder(w).Encode(map[string]string{ + "status": "ERROR", + "message": fmt.Sprintf("Empty updates result"), + }) + return + } + + views.IncidentsTable(ConvertIncidentList(results)).Render(r.Context(), w) +} + +// Converts a slice of Incoming to []IncomingRow +func ConvertIncidentList(list []queries.GetIncidentReportRow) []views.IncidentRow { + rows := make([]views.IncidentRow, 0, len(list)) + for _, in := range list { + //prettyPrint(in) + var row views.IncidentRow + utils.ConvertStruct(in, &row) + rows = append(rows, row) + } + return rows +} diff --git a/server/handler/incomingReport.go b/server/handler/incomingReport.go index 23e7f38..a837e93 100644 --- a/server/handler/incomingReport.go +++ b/server/handler/incomingReport.go @@ -4,16 +4,16 @@ import ( "context" "encoding/json" "fmt" - "net/http" "mocksnow/components/views" "mocksnow/db/queries" "mocksnow/internal/utils" + "net/http" ) func (h *Handler) RenderIncomingTable(w http.ResponseWriter, r *http.Request) { ctx := context.Background() - h.Logger.Debug("Querying updates table") + h.Logger.Debug("Querying incoming table") results, err := h.Database.Queries().ListIncoming(ctx) if err != nil { h.Logger.Error("Unable to query incoming table", "error", err) diff --git a/server/router/router.go b/server/router/router.go index 837d763..0f7936f 100644 --- a/server/router/router.go +++ b/server/router/router.go @@ -31,6 +31,7 @@ func New(logger *slog.Logger, database db.Database, buildTime string, sha1ver st mux.HandleFunc("/api/now/import/x_dusa2_itom_inc_imp", h.NewSnow) mux.HandleFunc("/api/now/table/incident/", h.GetIncident) mux.HandleFunc("/api/print", h.RenderIncomingTable) + mux.HandleFunc("/api/print/incidents", h.RenderIncidentTable) // TODO - fallback route that will just echo incoming payload mux.HandleFunc("/", h.Fallback)
IDIncident #DescriptionShort DescriptionUrgencyImpactStateExternal IDWork NotesAssignment GroupAssigned ToCategorySubcategoryCreated At