From 0e9591e6fcbe8fc4b598d8aa447dcd7855cfcb60 Mon Sep 17 00:00:00 2001 From: Nathan Coad Date: Wed, 26 Mar 2025 12:15:30 +1100 Subject: [PATCH] add CommentsAndWorkNotes --- db/queries/query.sql | 9 +++- db/queries/query.sql.go | 65 +++++++++++++++++++++++++++ server/handler/getIncident.go | 83 ++++++++++++++++++++++++++--------- server/models/models.go | 23 +++++----- 4 files changed, 147 insertions(+), 33 deletions(-) diff --git a/db/queries/query.sql b/db/queries/query.sql index 85dce46..ffc92d3 100644 --- a/db/queries/query.sql +++ b/db/queries/query.sql @@ -66,4 +66,11 @@ INSERT into worknotes ( ) VALUES( ?, ?, CURRENT_TIMESTAMP ) -RETURNING *; \ No newline at end of file +RETURNING *; + +-- name: GetIncidentWorkNotes :many +SELECT * from worknotes +WHERE incident_number = sqlc.arg('incidentNumber'); + +-- name: ListWorkNotes :many +SELECT * from worknotes; \ No newline at end of file diff --git a/db/queries/query.sql.go b/db/queries/query.sql.go index 408f241..6ba2dad 100644 --- a/db/queries/query.sql.go +++ b/db/queries/query.sql.go @@ -246,6 +246,39 @@ func (q *Queries) GetIncidentReport(ctx context.Context) ([]GetIncidentReportRow return items, nil } +const getIncidentWorkNotes = `-- name: GetIncidentWorkNotes :many +SELECT id, incident_number, note, created_at from worknotes +WHERE incident_number = ?1 +` + +func (q *Queries) GetIncidentWorkNotes(ctx context.Context, incidentnumber string) ([]Worknote, error) { + rows, err := q.db.QueryContext(ctx, getIncidentWorkNotes, incidentnumber) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Worknote + for rows.Next() { + var i Worknote + if err := rows.Scan( + &i.ID, + &i.IncidentNumber, + &i.Note, + &i.CreatedAt, + ); 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, sub_category, sys_id FROM incidents ` @@ -331,6 +364,38 @@ func (q *Queries) ListIncoming(ctx context.Context) ([]Incoming, error) { return items, nil } +const listWorkNotes = `-- name: ListWorkNotes :many +SELECT id, incident_number, note, created_at from worknotes +` + +func (q *Queries) ListWorkNotes(ctx context.Context) ([]Worknote, error) { + rows, err := q.db.QueryContext(ctx, listWorkNotes) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Worknote + for rows.Next() { + var i Worknote + if err := rows.Scan( + &i.ID, + &i.IncidentNumber, + &i.Note, + &i.CreatedAt, + ); 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 updateIncident = `-- name: UpdateIncident :exec UPDATE incidents SET diff --git a/server/handler/getIncident.go b/server/handler/getIncident.go index c110fa1..9567d4e 100644 --- a/server/handler/getIncident.go +++ b/server/handler/getIncident.go @@ -87,20 +87,45 @@ func (h *Handler) GetIncident(w http.ResponseWriter, r *http.Request) { } } + wknList, err := h.Database.Queries().ListWorkNotes(ctx) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + h.Logger.Debug("No incident worknotes found") + } else { + h.Logger.Error("", "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 database for incident worknotes: '%s'", err), + }) + return + } + } + // convert incList into json output for _, inc := range incList { + var wkn []string + // get worknotes for this incident + for _, note := range wknList { + if note.IncidentNumber == inc.IncidentNumber.String { + wkn = append(wkn, note.Note.String) + } + } + // transform inc to SingleIncidentResponse r := models.SingleIncidentResponse{ - Number: inc.IncidentNumber.String, - SysID: inc.SysID.String, - IncidentState: strconv.FormatInt(inc.State.Int64, 10), - State: strconv.FormatInt(inc.State.Int64, 10), - Impact: strconv.FormatInt(inc.Impact.Int64, 10), - Urgency: strconv.FormatInt(inc.Urgency.Int64, 10), - ShortDescription: inc.ShortDescription.String, - AssignedTo: inc.AssignedTo.String, - Category: inc.Category.String, - Subcategory: inc.SubCategory.String, + Number: inc.IncidentNumber.String, + SysID: inc.SysID.String, + IncidentState: strconv.FormatInt(inc.State.Int64, 10), + State: strconv.FormatInt(inc.State.Int64, 10), + Impact: strconv.FormatInt(inc.Impact.Int64, 10), + Urgency: strconv.FormatInt(inc.Urgency.Int64, 10), + ShortDescription: inc.ShortDescription.String, + AssignedTo: inc.AssignedTo.String, + Category: inc.Category.String, + Subcategory: inc.SubCategory.String, + CommentsAndWorkNotes: strings.Join(wkn, "\n\n"), // TODO } // add to responseList @@ -167,28 +192,44 @@ func (h *Handler) GetIncident(w http.ResponseWriter, r *http.Request) { func (h *Handler) getSingleIncident(number string, ctx context.Context) ([]byte, error) { var b []byte + var wkn []string incResult, err := h.Database.Queries().GetIncident(ctx, nullStr(number)) if err != nil { if errors.Is(err, sql.ErrNoRows) { h.Logger.Debug("No incident record found", "number", number) } else { - h.Logger.Error("Unable to query database for incident number", "error", err) + h.Logger.Error("Unable to query database for incident number", "number", number, "error", err) return b, err } } + wknotes, err := h.Database.Queries().GetIncidentWorkNotes(ctx, number) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + h.Logger.Debug("No incident worknotes found", "number", number) + } else { + h.Logger.Error("Unable to query database for incident worknotes", "number", number, "error", err) + return b, err + } + } + + for _, note := range wknotes { + wkn = append(wkn, note.Note.String) + } + // convert from database resposne to expected json format r := models.SingleIncidentResponse{ - Number: incResult.IncidentNumber.String, - SysID: incResult.SysID.String, - IncidentState: strconv.FormatInt(incResult.State.Int64, 10), - State: strconv.FormatInt(incResult.State.Int64, 10), - Impact: strconv.FormatInt(incResult.Impact.Int64, 10), - Urgency: strconv.FormatInt(incResult.Urgency.Int64, 10), - ShortDescription: incResult.ShortDescription.String, - AssignedTo: incResult.AssignedTo.String, - Category: incResult.Category.String, - Subcategory: incResult.SubCategory.String, + Number: incResult.IncidentNumber.String, + SysID: incResult.SysID.String, + IncidentState: strconv.FormatInt(incResult.State.Int64, 10), + State: strconv.FormatInt(incResult.State.Int64, 10), + Impact: strconv.FormatInt(incResult.Impact.Int64, 10), + Urgency: strconv.FormatInt(incResult.Urgency.Int64, 10), + ShortDescription: incResult.ShortDescription.String, + AssignedTo: incResult.AssignedTo.String, + Category: incResult.Category.String, + Subcategory: incResult.SubCategory.String, + CommentsAndWorkNotes: strings.Join(wkn, "\n\n"), // TODO } diff --git a/server/models/models.go b/server/models/models.go index c8cfa0f..0ed2cd7 100644 --- a/server/models/models.go +++ b/server/models/models.go @@ -18,17 +18,18 @@ type IncidentResultItem struct { // TODO - populate more fields here type SingleIncidentResponse struct { - Number string `json:"number"` // corresponds with incident_number - SysID string `json:"sys_id,omitempty"` - IncidentState string `json:"incident_state"` // integer value, 1-6 (6 is resolved) - State string `json:"state,omitempty"` // unsure how this differs from IncidentState - ShortDescription string `json:"short_description"` - AssignedTo interface{} `json:"assigned_to,omitempty"` - Description string `json:"description"` - Urgency string `json:"urgency"` - Impact string `json:"impact"` - Category string `json:"category"` - Subcategory string `json:"subcategory"` + Number string `json:"number"` // corresponds with incident_number + SysID string `json:"sys_id,omitempty"` + IncidentState string `json:"incident_state"` // integer value, 1-6 (6 is resolved) + State string `json:"state,omitempty"` // unsure how this differs from IncidentState + ShortDescription string `json:"short_description"` + AssignedTo interface{} `json:"assigned_to,omitempty"` + Description string `json:"description"` + Urgency string `json:"urgency"` + Impact string `json:"impact"` + Category string `json:"category"` + Subcategory string `json:"subcategory"` + CommentsAndWorkNotes string `json:"comments_and_work_notes"` // TODO - unvalidated from here on // Associated DeviceID (UUID from CPDB)