enhance implementation
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -8,8 +11,9 @@ import (
|
||||
|
||||
// GetIncident responds to the link generated in the response to a New Snow
|
||||
func (h *Handler) GetIncident(w http.ResponseWriter, r *http.Request) {
|
||||
h.Logger.Debug("GetIncident Request received", "method", r.Method, "path", r.URL.Path)
|
||||
// TODO
|
||||
h.Logger.Debug("GetIncident Request received", "method", r.Method, "url", r.URL, "path", r.URL.Path, "query", r.URL.Query())
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
path := r.URL.Path
|
||||
// Expected format: /api/now/table/incident/{id}
|
||||
@@ -30,17 +34,54 @@ func (h *Handler) GetIncident(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if len(parts) == 6 && strings.HasPrefix(path, "/api/now/table/incident/") {
|
||||
// Requested a single incident
|
||||
id := parts[5] // Extract {id}
|
||||
h.Logger.Debug("GetIncident called for specific incident", "id", id)
|
||||
|
||||
// Handle the specific 'number' query parameter
|
||||
number := query.Get("number")
|
||||
if number != "" {
|
||||
h.Logger.Debug("GetIncident called for specific incident number", "number", number)
|
||||
|
||||
// Query record from database and return that
|
||||
|
||||
} else {
|
||||
// Requested a single incident
|
||||
id := parts[5] // Extract {id}
|
||||
h.Logger.Debug("GetIncident called for specific incident", "id", id)
|
||||
|
||||
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)
|
||||
//return err
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.Marshal(incResult)
|
||||
if err != nil {
|
||||
h.Logger.Error("Unable to convert database record into json", "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 convert database record into json: '%s'", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, string(b))
|
||||
return
|
||||
}
|
||||
|
||||
} else if strings.HasPrefix(path, "/api/now/table/incident") {
|
||||
h.Logger.Debug("GetIncident called for list of incidents")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, "{\"result\": [{}]}")
|
||||
|
||||
}
|
||||
|
||||
// TODO - provide an incident list if necessary
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, "{\"result\": [{}]}")
|
||||
}
|
||||
|
@@ -10,6 +10,8 @@ import (
|
||||
"mocksnow/server/models"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@@ -29,95 +31,181 @@ func (h *Handler) NewSnow(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Decode the JSON body
|
||||
var incident models.Incident
|
||||
if err := json.Unmarshal(reqBody, &incident); err != nil {
|
||||
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(incident)
|
||||
prettyPrint(incoming)
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
createdTime := time.Now()
|
||||
|
||||
// 1. Insert into incoming table
|
||||
// Always insert data into incoming table
|
||||
params := queries.CreateIncomingParams{
|
||||
IncidentNumber: nullStr(incident.IncidentNumber),
|
||||
Description: nullStr(incident.Description),
|
||||
ShortDescription: nullStr(incident.ShortDescription),
|
||||
Urgency: nullStr(incident.Urgency),
|
||||
Impact: nullStr(incident.Impact),
|
||||
State: nullStr(incident.State),
|
||||
ExternalID: nullStr(incident.ExternalID),
|
||||
WorkNotes: nullStr(incident.WorkNotes),
|
||||
AssignmentGroup: nullStr(incident.AssignmentGroup),
|
||||
AssignedTo: nullStr(incident.AssignedTo),
|
||||
Category: nullStr(incident.Category),
|
||||
Subcategory: nullStr(incident.SubCategory),
|
||||
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)
|
||||
|
||||
// Insert the new inventory record into the database
|
||||
result, err := h.Database.Queries().CreateIncoming(ctx, params)
|
||||
if err != nil {
|
||||
h.Logger.Error("unable to perform database insert", "error", err)
|
||||
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 database record", "insert_result", result)
|
||||
h.Logger.Debug("created incoming database record", "insert_result", result)
|
||||
}
|
||||
|
||||
// Create record in incidents table
|
||||
incidentRecord, err := h.Database.Queries().CreateIncident(ctx, incident.ExternalID)
|
||||
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 database record", "incident_record", incidentRecord)
|
||||
}
|
||||
// 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
|
||||
|
||||
// Simulate response
|
||||
ticket := fmt.Sprintf("TKT%06d", incidentRecord)
|
||||
sysID := uuid.New().String()
|
||||
// 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()
|
||||
|
||||
// Produce dummy record link
|
||||
hostname, err = os.Hostname()
|
||||
if err != nil {
|
||||
h.Logger.Error("failed to lookup hostname", "error", err)
|
||||
hostname = "localhost"
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
recordLink := fmt.Sprintf("https://%s/api/now/table/incident/%s", hostname, sysID)
|
||||
// Use the returned incidentRecordId to generate the ticket number, and update the database correspondingly
|
||||
ticket := fmt.Sprintf("TKT%06d", incidentRecord.ID)
|
||||
|
||||
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,
|
||||
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")
|
||||
json.NewEncoder(w).Encode(response)
|
||||
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"
|
||||
|
Reference in New Issue
Block a user