Files
mocksnow/server/handler/newSnow.go
Nathan Coad 49e60f7843
All checks were successful
continuous-integration/drone/push Build is passing
enhance implementation
2025-03-24 15:50:03 +11:00

218 lines
6.8 KiB
Go

package handler
import (
"database/sql"
"encoding/json"
"fmt"
"io"
"math/rand"
"mocksnow/db/queries"
"mocksnow/server/models"
"net/http"
"os"
"strconv"
"time"
"github.com/google/uuid"
)
// NewSnow receives data from the DMSP Snow New() function
func (h *Handler) NewSnow(w http.ResponseWriter, r *http.Request) {
var hostname string
reqBody, err := io.ReadAll(r.Body)
if err != nil {
h.Logger.Error("Invalid data received", "error", err)
fmt.Fprintf(w, "Invalid data received")
w.WriteHeader(http.StatusInternalServerError)
return
} else {
h.Logger.Debug("received input data", "length", len(reqBody))
}
// Decode the JSON body
var incoming models.Incoming
if err := json.Unmarshal(reqBody, &incoming); err != nil {
h.Logger.Error("unable to decode json", "error", err)
http.Error(w, "Invalid JSON body", http.StatusBadRequest)
return
} else {
h.Logger.Debug("successfully decoded JSON")
prettyPrint(incoming)
}
ctx := r.Context()
createdTime := time.Now()
// Always insert data into incoming table
params := queries.CreateIncomingParams{
IncidentNumber: nullStr(incoming.IncidentNumber),
Description: nullStr(incoming.Description),
ShortDescription: nullStr(incoming.ShortDescription),
Urgency: nullStr(incoming.Urgency),
Impact: nullStr(incoming.Impact),
State: nullStr(incoming.State),
ExternalID: nullStr(incoming.ExternalID),
WorkNotes: nullStr(incoming.WorkNotes),
AssignmentGroup: nullStr(incoming.AssignmentGroup),
AssignedTo: nullStr(incoming.AssignedTo),
Category: nullStr(incoming.Category),
Subcategory: nullStr(incoming.SubCategory),
CreatedAt: sql.NullTime{Time: createdTime, Valid: true},
}
h.Logger.Debug("database params", "params", params)
result, err := h.Database.Queries().CreateIncoming(ctx, params)
if err != nil {
h.Logger.Error("unable to log incoming data", "error", err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Error : %v\n", err)
return
} else {
h.Logger.Debug("created incoming database record", "insert_result", result)
}
// TODO:
// check to see if this record is already in incidents table
// if it is then:
// - add a worknotes entry in worknotes table
// - update status in incidents table
// otherwise, create a new incident
// External ID only provided when incident not yet created - this might not be correct?
if len(incoming.ExternalID) > 0 && len(incoming.IncidentNumber) == 0 {
// Create record in incidents table
urgency, _ := strconv.ParseInt(incoming.Urgency, 10, 64)
impact, _ := strconv.ParseInt(incoming.Impact, 10, 64)
state, _ := strconv.ParseInt(incoming.State, 10, 64)
sysID := uuid.New().String()
params2 := queries.CreateIncidentParams{
//IncidentNumber: nullStr(incoming.IncidentNumber),
Description: nullStr(incoming.Description),
ShortDescription: nullStr(incoming.ShortDescription),
Urgency: nullInt64(urgency),
Impact: nullInt64(impact),
State: nullInt64(state),
ExternalID: incoming.ExternalID,
//WorkNotes: nullStr(incoming.WorkNotes),
AssignmentGroup: nullStr(incoming.AssignmentGroup),
AssignedTo: nullStr(incoming.AssignedTo),
Category: nullStr(incoming.Category),
Subcategory: nullStr(incoming.SubCategory),
SysID: nullStr(sysID),
CreatedAt: sql.NullTime{Time: createdTime, Valid: true},
}
incidentRecord, err := h.Database.Queries().CreateIncident(ctx, params2)
if err != nil {
h.Logger.Error("failed to create incident", "error", err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Error : %v\n", err)
return
} else {
h.Logger.Debug("created incident database record", "incident_record", incidentRecord)
}
// Use the returned incidentRecordId to generate the ticket number, and update the database correspondingly
ticket := fmt.Sprintf("TKT%06d", incidentRecord.ID)
incNumParams := queries.UpdateIncidentNumberParams{
ExternalId: incoming.ExternalID,
IncidentNumber: nullStr(ticket),
}
err = h.Database.Queries().UpdateIncidentNumber(ctx, incNumParams)
if err != nil {
h.Logger.Error("failed to update incident with incident number", "number", ticket, "error", err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Error : %v\n", err)
return
} else {
h.Logger.Debug("updated incident database record")
}
// Create the worknotes entry
wnParms := queries.CreateWorkNoteParams{
IncidentNumber: ticket,
Note: nullStr(incoming.WorkNotes),
}
_, err = h.Database.Queries().CreateWorkNote(ctx, wnParms)
if err != nil {
h.Logger.Error("failed to create worknotes for incident with incident number", "number", ticket, "error", err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Error : %v\n", err)
return
} else {
h.Logger.Debug("created worknotes database record")
}
// Send the response
// Produce dummy record link
hostname, err = os.Hostname()
if err != nil {
h.Logger.Error("failed to lookup hostname", "error", err)
hostname = "localhost"
}
recordLink := fmt.Sprintf("https://%s/api/now/table/incident/%s", hostname, sysID)
response := models.IncidentResponse{
ImportSet: randomImportSet(),
StagingTable: "x_dusa2_itom_inc_imp",
Result: []models.IncidentResultItem{
{
TransformMap: "Incident Import",
Table: "incident",
DisplayName: "number",
DisplayValue: ticket,
RecordLink: recordLink,
Status: "inserted",
SysID: sysID,
},
},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
} else if len(incoming.IncidentNumber) > 0 {
// Incident already exists because we know the incident number, so update status or worknotes
h.Logger.Debug("updating incident database record")
// TODO
} else {
// Unexpected condition
// TODO - return error
h.Logger.Error("Logic error, did not expect to reach here")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Logic error, unexpected condition"),
})
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "{\"result\": [{}]}")
}
func nullStr(s string) sql.NullString {
return sql.NullString{String: s, Valid: s != ""}
}
func nullInt64(i int64) sql.NullInt64 {
return sql.NullInt64{Int64: i, Valid: i > 0}
}
// Helper to generate random import_set ID
func randomImportSet() string {
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, 8)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}