All checks were successful
continuous-integration/drone/push Build is passing
379 lines
12 KiB
Go
379 lines
12 KiB
Go
package handler
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"mocksnow/db/queries"
|
|
"mocksnow/server/models"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// NewSnow receives data from the DMSP Snow New() function
|
|
func (h *Handler) NewSnow(w http.ResponseWriter, r *http.Request) {
|
|
h.Logger.Debug("NewSnow Request received", "method", r.Method, "url", r.URL, "path", r.URL.Path, "query", r.URL.Query(), "proto", r.Proto)
|
|
|
|
// Print headers
|
|
for name, values := range r.Header {
|
|
for _, value := range values {
|
|
h.Logger.Debug("Header ", "name", name, "value", value)
|
|
}
|
|
}
|
|
|
|
// print query
|
|
for key, values := range r.URL.Query() {
|
|
for _, value := range values {
|
|
h.Logger.Debug("Query Paramater", "key", key, "value", value)
|
|
}
|
|
}
|
|
|
|
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.UpdateIncidentNumberSysIdParams{
|
|
SysId: nullStr(sysID),
|
|
IncidentNumber: nullStr(ticket),
|
|
}
|
|
err = h.Database.Queries().UpdateIncidentNumberSysId(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")
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(response)
|
|
return
|
|
|
|
} 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", "number", incoming.IncidentNumber)
|
|
|
|
inc, err := h.Database.Queries().GetIncident(ctx, nullStr(incoming.IncidentNumber))
|
|
if err != nil {
|
|
h.Logger.Error("failed to find existing incident to update", "number", incoming.IncidentNumber, "error", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
fmt.Fprintf(w, "Error : %v\n", err)
|
|
return
|
|
} else {
|
|
prettyPrint(inc)
|
|
}
|
|
|
|
updateParams := h.populateChangedFields(inc, incoming)
|
|
|
|
err = h.Database.Queries().UpdateIncident(ctx, updateParams)
|
|
if err != nil {
|
|
h.Logger.Error("failed to update incident", "number", incoming.IncidentNumber, "error", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
fmt.Fprintf(w, "Error : %v\n", err)
|
|
return
|
|
} else {
|
|
h.Logger.Debug("updated incident database record", "number", incoming.IncidentNumber)
|
|
}
|
|
|
|
// add any worknotes specified
|
|
if len(incoming.WorkNotes) > 0 {
|
|
//h.Logger.Debug("TODO add worknotes")
|
|
|
|
// Create the worknotes entry
|
|
wnParms := queries.CreateWorkNoteParams{
|
|
IncidentNumber: incoming.IncidentNumber,
|
|
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", incoming.IncidentNumber, "error", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
fmt.Fprintf(w, "Error : %v\n", err)
|
|
return
|
|
} else {
|
|
h.Logger.Debug("created worknotes database record", "number", incoming.IncidentNumber)
|
|
}
|
|
}
|
|
|
|
// Respond saying we have updated the record
|
|
|
|
response := models.IncidentResponse{
|
|
StagingTable: "x_dusa2_itom_inc_imp",
|
|
Result: []models.IncidentResultItem{
|
|
{
|
|
DisplayValue: incoming.IncidentNumber,
|
|
Status: "updated",
|
|
SysID: inc.SysID.String,
|
|
},
|
|
},
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(response)
|
|
return
|
|
|
|
} 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)
|
|
}
|
|
|
|
func (h *Handler) populateChangedFields(incRecord queries.Incident, newData models.Incoming) queries.UpdateIncidentParams {
|
|
params := queries.UpdateIncidentParams{
|
|
IncidentNumber: incRecord.IncidentNumber, // Always required
|
|
ExternalID: incRecord.ExternalID,
|
|
}
|
|
|
|
if strings.TrimSpace(newData.Description) != strings.TrimSpace(incRecord.Description.String) && len(strings.TrimSpace(newData.Description)) > 0 {
|
|
h.Logger.Debug("Updating Description", "new", newData.Description)
|
|
params.Description = nullStr(newData.Description)
|
|
} else {
|
|
params.Description = incRecord.Description
|
|
}
|
|
if strings.TrimSpace(newData.ShortDescription) != strings.TrimSpace(incRecord.ShortDescription.String) && len(strings.TrimSpace(newData.ShortDescription)) > 0 {
|
|
h.Logger.Debug("Updating ShortDescription", "new", newData.ShortDescription)
|
|
params.ShortDescription = nullStr(newData.ShortDescription)
|
|
} else {
|
|
params.ShortDescription = incRecord.ShortDescription
|
|
}
|
|
|
|
if strings.TrimSpace(newData.Urgency) != strconv.FormatInt(incRecord.Urgency.Int64, 10) {
|
|
if n, err := strconv.ParseInt(newData.Urgency, 10, 64); err == nil {
|
|
h.Logger.Debug("Updating Urgency", "new", newData.Urgency)
|
|
params.Urgency = nullInt64(n)
|
|
} else {
|
|
params.Urgency = incRecord.Urgency
|
|
}
|
|
} else {
|
|
params.Urgency = incRecord.Urgency
|
|
}
|
|
if strings.TrimSpace(newData.State) != strconv.FormatInt(incRecord.State.Int64, 10) {
|
|
if n, err := strconv.ParseInt(newData.State, 10, 64); err == nil {
|
|
h.Logger.Debug("Updating State", "new", newData.State)
|
|
params.State = nullInt64(n)
|
|
} else {
|
|
params.State = incRecord.State
|
|
}
|
|
} else {
|
|
params.State = incRecord.State
|
|
}
|
|
if strings.TrimSpace(newData.Impact) != strconv.FormatInt(incRecord.Impact.Int64, 10) {
|
|
if n, err := strconv.ParseInt(newData.Impact, 10, 64); err == nil {
|
|
h.Logger.Debug("Updating Impact", "new", newData.Impact)
|
|
params.Impact = nullInt64(n)
|
|
} else {
|
|
params.Impact = incRecord.Impact
|
|
}
|
|
} else {
|
|
params.Impact = incRecord.Impact
|
|
}
|
|
|
|
if strings.TrimSpace(newData.AssignmentGroup) != strings.TrimSpace(incRecord.AssignmentGroup.String) && len(strings.TrimSpace(newData.AssignmentGroup)) > 0 {
|
|
h.Logger.Debug("Updating AssignmentGroup", "new", newData.AssignmentGroup)
|
|
params.AssignmentGroup = nullStr(newData.AssignmentGroup)
|
|
} else {
|
|
params.AssignmentGroup = incRecord.AssignmentGroup
|
|
}
|
|
if strings.TrimSpace(newData.AssignedTo) != strings.TrimSpace(incRecord.AssignedTo.String) && len(strings.TrimSpace(newData.AssignedTo)) > 0 {
|
|
h.Logger.Debug("Updating AssignedTo", "new", newData.AssignedTo)
|
|
params.AssignedTo = nullStr(newData.AssignedTo)
|
|
} else {
|
|
params.AssignedTo = incRecord.AssignedTo
|
|
}
|
|
if strings.TrimSpace(newData.Category) != strings.TrimSpace(incRecord.Category.String) && len(strings.TrimSpace(newData.Category)) > 0 {
|
|
h.Logger.Debug("Updating Category", "new", newData.Category)
|
|
params.Category = nullStr(newData.Category)
|
|
} else {
|
|
params.Category = incRecord.Category
|
|
}
|
|
if strings.TrimSpace(newData.SubCategory) != strings.TrimSpace(incRecord.SubCategory.String) && len(strings.TrimSpace(newData.SubCategory)) > 0 {
|
|
h.Logger.Debug("Updating SubCategory", "new", newData.SubCategory)
|
|
params.Category = nullStr(newData.SubCategory)
|
|
} else {
|
|
params.SubCategory = incRecord.SubCategory
|
|
}
|
|
// TODO
|
|
|
|
h.Logger.Debug("populateChangedFields returning", "params", params)
|
|
|
|
return params
|
|
}
|