enhance implementation
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-03-24 15:50:03 +11:00
parent ebf1d2aef3
commit 49e60f7843
23 changed files with 620 additions and 197 deletions

2
.gitignore vendored
View File

@@ -8,7 +8,7 @@
*.dll
*.so
*.dylib
mainsnow
mocksnow
build/
# Certificates

Binary file not shown.

View File

@@ -1,30 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFIjCCAwqgAwIBAgIRAJPUpRr9NQCFpDrCPlyM7JYwDQYJKoZIhvcNAQELBQAw
DzENMAsGA1UEChMERFRNUzAeFw0yNDA5MTIwMDU4NDFaFw0yNTA5MTIwMDU4NDFa
MA8xDTALBgNVBAoTBERUTVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDJhxPJHo/SqsEURebiRufmXRDYhl67PdglOGI6JC/VjCaqu42DYMO9tF6vuHBD
4/+/UpMBuOGTw+m6nEKdKKIZBuG0Hvka4bS0YPDHvqHZMWHK9qdPbmrB8q1N8W9F
Z+1gjIdeCK1wBjMTnfSxWOznKjffsz170bhOE6CHB4CP9nLMcShrvDcg0kTSbzM1
ptlxWGI5vabkx9spPU65NWEjd645Z4kMIutJZJjVKPlAqRr/fFeNgE5A9VhwzBot
5ZupKIZYuUMAMYkXjOhdqxDbWqfc8lkUvk5Jd9XtYB81uzvfpoH28Th4ILAdZd9h
vPh38sB/FyxSeKPHmW0IP1r8+EW/57ZPnQrGAOWs2z8Fp812vwQV0sr8j1B5NiTz
yu7I47vDdKZxulWS48frASxFFz4OojZAsoP6LipAMPolGIrnFn1/6UlOPnSVqkUb
D36PEIkOzE9AzMaSD/qs+k9lVHo+VTHL/qmz6u6COQiPL0u/ZInMcQmzJecCSDm4
Z8oVGeth+2vzlPo1iQHqptYYbln7oLaQZuCqdm4oNb+Hdyflhv8O78hUq7tcnQ8M
7/0jvlB6m1e4vuAxldBkaWr/cQudtzZnTiwHf2a3TH0h3ID0Gecog+hWb7EcAI2D
IK6Ji3uEBLag39XFuMzF/jLIUtYMz0Mb48v3Opbd8qe3aQIDAQABo3kwdzAOBgNV
HQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUZKrZX7aNZvhyG7HgIPznTYDeQ8QwIAYDVR0RBBkwF4IPbmF0
aG1icDE0LmxvY2FshwQKAAGhMA0GCSqGSIb3DQEBCwUAA4ICAQCqKmraTwKEGsRz
DqYSu5IDuYiXymBsIgT9A2prnd7ztX35CDi4n2msFKX0njcOAkBnapzSpDM1pbF8
a7H3F+pBfPZf1Cecgj1vooTImJgsS8SBR/otIjAQrX0G1DqcHoRfTDJ0iZZgE031
59dqMJOQDUI7KrLm4hhqCsLt9TR1MYi9V5rDZOXBO6rQ78VBrD0q3aJI/27xlPeJ
T5rEmj0HQrNfFs1QQD5JOd5EDILJnioE8wR4l14vfrVhNzr5xt7OQk6GT7txIuyK
l+k0xfClTN7PUZNcO3MnM/XeNZwIy1G0Dfi4BfdVBzR2vK7wZOJnwK02SMUrxez7
ZbtMra5hlDTT3KAgyYZs/u4rGjxG7r92A1vog3cuoVbrH3oEgqCHV6WId7ipxrmw
/3/S6X8M99jybg6Ac55pamiqx6PcJSnCKFwme1O+6yp0LEAIoRSdB8OkC6FSVmXs
tU5sNl4tqWL3BymVoiz7wpqJdZZEsinC9oE58nTN6k6pcd5pN04A/Uc7VySqN6Br
19v8Tn6JLIb/TLhUFGc25yMsDtgLN+bWC12RN2DXRakeM+330rKFxKgvIjNJzEPm
1HdxlfEMdRP1F3gTgHH8rVkh2a5VWlgnw3qKaOzjtFIdqGob7d1FJoaQgbD81Wqa
nNtoW4JrPFzi9P6EpTqci0IUCapgkQ==
MIIFITCCAwmgAwIBAgIQQDG29y2z55hHFZFmrmgW3jANBgkqhkiG9w0BAQsFADAP
MQ0wCwYDVQQKEwREVE1TMB4XDTI1MDMyNDA0MjM0NloXDTI2MDMyNDA0MjM0Nlow
DzENMAsGA1UEChMERFRNUzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
AObGFq98DIBHhZS3/5jynja3WVZIv0Tb92RlYsQkqhA5wfy9AmWtD+7fXAy3Mfy4
Q38/3RwNONWIRA9tdEhFS+veBPKRyw8HPq4jIMlF+sm+8pbU0Lr+vikMNekcaQMA
tFts6s37RAP7JYBh/IATYIVVibe9d/O0YpWZKHIa/AbyiWYSKpfnsprffBg+YyGC
kGIiqhDbt5ROKOHZloIovsReLyoj5aPDYuvGW6Nznx2PkARD4SMcWx03ytn1MRiN
/yk6ILg4xKWK0F3m97qwKAGSG1MleyfO4uuKsZGlUAjqUFFiCXUxD71YuI3f7yHB
EMPOV3zuv7iL5BDooGumBB8lmYYAbYDclSzSFbL07W6tFKHI9eMAyBiQTrnAiWm3
EoLHaNtpQsmCdUZkgSyGF8ceK5CA42yHL7vEz8gTU8DpyjShdVOM1iDiaXhMLIyw
ibMmrVjYzqjKFxudkzwDF8g25lqqkZHQHx5U4BjPAwYkApfUQaHsQLp8fyhNQBpp
rNGUe373YplRENf80wGTsgYv4IzxZYMstnG1cRXDXTQwRGRk67kHZq3mF6n7ZIMQ
vsXXnaI2UT0QfjT3krY5lnAL6Pqbe13PccpS/VATBz+ATYmaYQYo6Z+iISNDabp8
a7XWW8o1uA/+f650hqzm29ePfx/rMhlPwEIqLYXA2d9fAgMBAAGjeTB3MA4GA1Ud
DwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/
MB0GA1UdDgQWBBQV+KDGnmuf2aRaPHrCck/31UZfdzAgBgNVHREEGTAXgg9uYXRo
bWJwMTQubG9jYWyHBAoAAaEwDQYJKoZIhvcNAQELBQADggIBAGQtEg4iaIwzPRVs
m1FX/jAJcqogiWoeNE46a7mbdKFC7LIlKIj/xpEvVBrnyoHs3ZMK9bZkyMgfAtts
oy3t1s7iHrxsiYi9MbKL3XSfslczgFxZm5Z+NgXMCuK5d1p6loxwJ5cTmfB4tvLp
dQMUnHudaUAsVGoGYhGrVQqbkQty5kray6dJMYveUOSOqbKH9ZogOcHU59IsZejn
J3PLdjEBLBd2zAQ05N5wUHBWL7gMfk8gguJqIBGFdBLIc9eVrq78lMh0OD/PpNZS
VsiwlLWqbYozDnygZvooQMn+m9q5sUEbyxj5oAorm3+zfSC3Sxik+te0k81402Aw
C4zJvzWohUFV+GcEsHYwzhnXAN3pHea506itef1tvI7dAJmWKhrdfegRb6HDpMUW
C8VX0rWODsWdFkKW3/ohKeW6Xjoq/hnFSOb5bwSCMfFxA9qy6fzCCvuVzuHsKC24
+Nmwgk9LYSWzg/TUxQ9/SX+Q03oxlpnRl4hRNxluUtqCcujR/BZJjaRXtM0hv3Eq
bZnuy1yUegsBQYLFKRF0Qmh5iKDd+Q9znemeI6CTXLKqjTFYZCIaz1DoxJ+HfZdo
NrEWOC0sMChcP6Mdmnskf1xJWkFd8giCDxpuf0iY+jRNAJnHoMt1TMXiVTWHUwOj
4GxxaCrOp9+a+wQ0j3rI0c5D1H25
-----END CERTIFICATE-----

View File

@@ -1,6 +1,6 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.778
// templ: version: v0.3.856
package core
//lint:file-ignore SA4006 This context is only used if a nested component is present.
@@ -29,11 +29,11 @@ func Footer() templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<footer class=\"fixed p-1 bottom-0 bg-gray-100 w-full border-t\"><div class=\"rounded-lg p-4 text-xs italic text-gray-700 text-center\">&copy; Nathan Coad (nathan.coad@dell.com)</div></footer>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<footer class=\"fixed p-1 bottom-0 bg-gray-100 w-full border-t\"><div class=\"rounded-lg p-4 text-xs italic text-gray-700 text-center\">&copy; Nathan Coad (nathan.coad@dell.com)</div></footer>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
return nil
})
}

View File

@@ -10,5 +10,6 @@ templ Header() {
<title>mocksnow API</title>
<script src="/assets/js/htmx@v2.0.2.min.js"></script>
<link href={ "/assets/css/output@" + version.Value + ".css" } rel="stylesheet"/>
<link href={ "/assets/css/mvp.css" } rel="stylesheet"/>
</head>
}

View File

@@ -1,6 +1,6 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.778
// templ: version: v0.3.856
package core
//lint:file-ignore SA4006 This context is only used if a nested component is present.
@@ -31,7 +31,7 @@ func Header() templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><meta name=\"description\" content=\"mocksnow API endpoint\"><title>mocksnow API</title><script src=\"/assets/js/htmx@v2.0.2.min.js\"></script><link href=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><meta name=\"description\" content=\"mocksnow API endpoint\"><title>mocksnow API</title><script src=\"/assets/js/htmx@v2.0.2.min.js\"></script><link href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -44,11 +44,24 @@ func Header() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" rel=\"stylesheet\"></head>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" rel=\"stylesheet\"><link href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs("/assets/css/mvp.css")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 13, Col: 36}
}
_, 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, 3, "\" rel=\"stylesheet\"></head>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}

View File

@@ -26,47 +26,47 @@ templ IncomingTable(rows []IncomingRow) {
<!DOCTYPE html>
<html lang="en">
@core.Header()
<body class="flex flex-col min-h-screen bg-gray-50">
<main class="flex-grow p-8">
<div class="max-w-7xl mx-auto">
<h1 class="text-4xl font-bold mb-6 text-gray-800">Incoming Incidents</h1>
<div class="overflow-x-auto shadow-md sm:rounded-lg">
<table class="w-full text-sm text-left text-gray-600">
<thead class="text-xs text-gray-700 uppercase bg-gray-200">
<body>
<main>
<div>
<h1>Incoming API Calls</h1>
<div>
<table>
<thead>
<tr>
<th class="px-4 py-2">ID</th>
<th class="px-4 py-2">Incident #</th>
<th class="px-4 py-2">Description</th>
<th class="px-4 py-2">Short Description</th>
<th class="px-4 py-2">Urgency</th>
<th class="px-4 py-2">Impact</th>
<th class="px-4 py-2">State</th>
<th class="px-4 py-2">External ID</th>
<th class="px-4 py-2">Work Notes</th>
<th class="px-4 py-2">Assignment Group</th>
<th class="px-4 py-2">Assigned To</th>
<th class="px-4 py-2">Category</th>
<th class="px-4 py-2">Subcategory</th>
<th class="px-4 py-2">Created At</th>
<th>ID</th>
<th>Incident #</th>
<th>Description</th>
<th>Short Description</th>
<th>Urgency</th>
<th>Impact</th>
<th>State</th>
<th>External ID</th>
<th>Work Notes</th>
<th>Assignment Group</th>
<th>Assigned To</th>
<th>Category</th>
<th>Subcategory</th>
<th>Created At</th>
</tr>
</thead>
<tbody>
for _, row := range rows {
<tr class="bg-white border-b hover:bg-gray-100">
<td class="px-4 py-2">{ strconv.Itoa(row.ID) }</td>
<td class="px-4 py-2">{ row.IncidentNumber }</td>
<td class="px-4 py-2">{ row.Description }</td>
<td class="px-4 py-2">{ row.ShortDescription }</td>
<td class="px-4 py-2">{ row.Urgency }</td>
<td class="px-4 py-2">{ row.Impact }</td>
<td class="px-4 py-2">{ row.State }</td>
<td class="px-4 py-2">{ row.ExternalID }</td>
<td class="px-4 py-2">{ row.WorkNotes }</td>
<td class="px-4 py-2">{ row.AssignmentGroup }</td>
<td class="px-4 py-2">{ row.AssignedTo }</td>
<td class="px-4 py-2">{ row.Category }</td>
<td class="px-4 py-2">{ row.Subcategory }</td>
<td class="px-4 py-2">{ row.CreatedAt }</td>
<tr>
<td>{ strconv.Itoa(row.ID) }</td>
<td>{ row.IncidentNumber }</td>
<td>{ row.Description }</td>
<td>{ row.ShortDescription }</td>
<td>{ row.Urgency }</td>
<td>{ row.Impact }</td>
<td>{ row.State }</td>
<td>{ row.ExternalID }</td>
<td>{ row.WorkNotes }</td>
<td>{ row.AssignmentGroup }</td>
<td>{ row.AssignedTo }</td>
<td>{ row.Category }</td>
<td>{ row.Subcategory }</td>
<td>{ row.CreatedAt }</td>
</tr>
}
</tbody>

View File

@@ -1,6 +1,6 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.778
// templ: version: v0.3.856
package views
//lint:file-ignore SA4006 This context is only used if a nested component is present.
@@ -9,8 +9,8 @@ import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"strconv"
"mocksnow/components/core"
"strconv"
)
type IncomingRow struct {
@@ -51,7 +51,7 @@ func IncomingTable(rows []IncomingRow) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"en\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -59,199 +59,199 @@ func IncomingTable(rows []IncomingRow) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<body class=\"flex flex-col min-h-screen bg-gray-50\"><main class=\"flex-grow p-8\"><div class=\"max-w-7xl mx-auto\"><h1 class=\"text-4xl font-bold mb-6 text-gray-800\">Incoming Incidents</h1><div class=\"overflow-x-auto shadow-md sm:rounded-lg\"><table class=\"w-full text-sm text-left text-gray-600\"><thead class=\"text-xs text-gray-700 uppercase bg-gray-200\"><tr><th class=\"px-4 py-2\">ID</th><th class=\"px-4 py-2\">Incident #</th><th class=\"px-4 py-2\">Description</th><th class=\"px-4 py-2\">Short Description</th><th class=\"px-4 py-2\">Urgency</th><th class=\"px-4 py-2\">Impact</th><th class=\"px-4 py-2\">State</th><th class=\"px-4 py-2\">External ID</th><th class=\"px-4 py-2\">Work Notes</th><th class=\"px-4 py-2\">Assignment Group</th><th class=\"px-4 py-2\">Assigned To</th><th class=\"px-4 py-2\">Category</th><th class=\"px-4 py-2\">Subcategory</th><th class=\"px-4 py-2\">Created At</th></tr></thead> <tbody>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<body><main><div><h1>Incoming Incidents</h1><div><table><thead><tr><th>ID</th><th>Incident #</th><th>Description</th><th>Short Description</th><th>Urgency</th><th>Impact</th><th>State</th><th>External ID</th><th>Work Notes</th><th>Assignment Group</th><th>Assigned To</th><th>Category</th><th>Subcategory</th><th>Created At</th></tr></thead> <tbody>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, row := range rows {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr class=\"bg-white border-b hover:bg-gray-100\"><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<tr><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(row.ID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 56, Col: 80}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 56, Col: 62}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(row.IncidentNumber)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 57, Col: 78}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 57, Col: 60}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(row.Description)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 58, Col: 75}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 58, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(row.ShortDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 59, Col: 80}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 59, Col: 62}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(row.Urgency)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 60, Col: 71}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 60, Col: 53}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(row.Impact)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 61, Col: 70}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 61, Col: 52}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(row.State)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 62, Col: 69}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 62, Col: 51}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(row.ExternalID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 63, Col: 74}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 63, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(row.WorkNotes)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 64, Col: 73}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 64, Col: 55}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(row.AssignmentGroup)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 65, Col: 79}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 65, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(row.AssignedTo)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 66, Col: 74}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 66, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(row.Category)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 67, Col: 72}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 67, Col: 54}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(row.Subcategory)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 68, Col: 75}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 68, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"px-4 py-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(row.CreatedAt)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 69, Col: 73}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incoming.templ`, Line: 69, Col: 55}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td></tr>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</td></tr>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody></table></div></div></main>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</tbody></table></div></div></main>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -259,11 +259,11 @@ func IncomingTable(rows []IncomingRow) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</body></html>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</body></html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
return nil
})
}

View File

@@ -1,6 +1,6 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.778
// templ: version: v0.3.856
package views
//lint:file-ignore SA4006 This context is only used if a nested component is present.
@@ -39,7 +39,7 @@ func Index(info BuildInfo) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"en\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -47,7 +47,7 @@ func Index(info BuildInfo) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<body class=\"flex flex-col min-h-screen\"><main class=\"flex-grow\"><div><h1 class=\"text-5xl font-bold\">Build Information</h1><p class=\"mt-4\"><strong>Build Time:</strong> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<body class=\"flex flex-col min-h-screen\"><main class=\"flex-grow\"><div><h1 class=\"text-5xl font-bold\">Build Information</h1><p class=\"mt-4\"><strong>Build Time:</strong> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -60,7 +60,7 @@ func Index(info BuildInfo) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p><p class=\"mt-4\"><strong>SHA1 Version:</strong> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</p><p class=\"mt-4\"><strong>SHA1 Version:</strong> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -73,7 +73,7 @@ func Index(info BuildInfo) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p><p class=\"mt-4\"><strong>Go Runtime Version:</strong> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</p><p class=\"mt-4\"><strong>Go Runtime Version:</strong> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -86,7 +86,7 @@ func Index(info BuildInfo) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p></div></main></body>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</p></div></main></body>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -94,11 +94,11 @@ func Index(info BuildInfo) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</html>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
return nil
})
}

View File

@@ -0,0 +1,30 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE incidents ADD COLUMN incident_number TEXT;
ALTER TABLE incidents ADD COLUMN description TEXT;
ALTER TABLE incidents ADD COLUMN short_description TEXT;
ALTER TABLE incidents ADD COLUMN urgency INTEGER;
ALTER TABLE incidents ADD COLUMN impact INTEGER;
ALTER TABLE incidents ADD COLUMN state INTEGER;
-- ALTER TABLE incidents ADD COLUMN work_notes TEXT;
ALTER TABLE incidents ADD COLUMN assignment_group TEXT;
ALTER TABLE incidents ADD COLUMN assigned_to TEXT;
ALTER TABLE incidents ADD COLUMN category TEXT;
ALTER TABLE incidents ADD COLUMN subcategory TEXT;
ALTER TABLE incidents ADD COLUMN sys_id TEXT;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE incidents DROP COLUMN incident_number;
ALTER TABLE incidents DROP COLUMN description;
ALTER TABLE incidents DROP COLUMN short_description;
ALTER TABLE incidents DROP COLUMN urgency;
ALTER TABLE incidents DROP COLUMN impact;
ALTER TABLE incidents DROP COLUMN state;
ALTER TABLE incidents DROP COLUMN assignment_group;
ALTER TABLE incidents DROP COLUMN assigned_to;
ALTER TABLE incidents DROP COLUMN category;
ALTER TABLE incidents DROP COLUMN subcategory;
ALTER TABLE incidents DROP COLUMN sys_id;
-- +goose StatementEnd

View File

@@ -0,0 +1,13 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE worknotes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
incident_number TEXT NOT NULL, -- must map to an incidents.incident_number
note TEXT
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS worknotes;
-- +goose StatementEnd

View File

@@ -9,9 +9,20 @@ import (
)
type Incident struct {
ID int64
ExternalID string
CreatedAt sql.NullTime
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
}
type Incoming struct {
@@ -30,3 +41,9 @@ type Incoming struct {
Subcategory sql.NullString
CreatedAt sql.NullTime
}
type Worknote struct {
ID int64
IncidentNumber string
Note sql.NullString
}

View File

@@ -2,7 +2,6 @@
SELECT * FROM "Incoming"
ORDER BY "id";
-- name: CreateIncoming :one
INSERT INTO "Incoming" (
"incident_number", "description", "short_description", "urgency", "impact", "state", "external_id", "work_notes", "assignment_group", "assigned_to", "category", "subcategory", "created_at"
@@ -12,6 +11,49 @@ INSERT INTO "Incoming" (
RETURNING *;
-- name: CreateIncident :one
INSERT INTO incidents (external_id)
VALUES (?)
RETURNING id;
INSERT INTO incidents (
"description", "short_description", "urgency", "impact", "state", "external_id", "assignment_group", "assigned_to", "category", "subcategory", "created_at", "sys_id"
) VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING *;
-- name: UpdateIncidentNumber :exec
UPDATE incidents
SET incident_number = sqlc.arg('incidentNumber')
WHERE external_id = sqlc.arg('externalId');
-- name: UpdateIncidentState :exec
UPDATE incidents
SET state = sqlc.arg('state')
WHERE incident_number = sqlc.arg('incidentNumber');
-- name: UpdateIncident :exec
UPDATE incidents
SET
external_id = ?,
description = ?,
short_description = ?,
urgency = ?,
impact = ?,
state = ?,
assignment_group = ?,
assigned_to = ?,
category = ?,
subcategory = ?
WHERE incident_number = ?;
-- name: GetIncident :one
SELECT * from incidents
WHERE incident_number = sqlc.arg('incidentNumber');
-- name: ListIncidents :many
SELECT * FROM incidents;
-- name: CreateWorkNote :one
INSERT into worknotes (
"incident_number", "note"
) VALUES(
?, ?
)
RETURNING *;

View File

@@ -11,16 +11,62 @@ import (
)
const createIncident = `-- name: CreateIncident :one
INSERT INTO incidents (external_id)
VALUES (?)
RETURNING id
INSERT INTO incidents (
"description", "short_description", "urgency", "impact", "state", "external_id", "assignment_group", "assigned_to", "category", "subcategory", "created_at", "sys_id"
) VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING id, external_id, created_at, incident_number, description, short_description, urgency, impact, state, assignment_group, assigned_to, category, subcategory, sys_id
`
func (q *Queries) CreateIncident(ctx context.Context, externalID string) (int64, error) {
row := q.db.QueryRowContext(ctx, createIncident, externalID)
var id int64
err := row.Scan(&id)
return id, err
type CreateIncidentParams struct {
Description sql.NullString
ShortDescription sql.NullString
Urgency sql.NullInt64
Impact sql.NullInt64
State sql.NullInt64
ExternalID string
AssignmentGroup sql.NullString
AssignedTo sql.NullString
Category sql.NullString
Subcategory sql.NullString
CreatedAt sql.NullTime
SysID sql.NullString
}
func (q *Queries) CreateIncident(ctx context.Context, arg CreateIncidentParams) (Incident, error) {
row := q.db.QueryRowContext(ctx, createIncident,
arg.Description,
arg.ShortDescription,
arg.Urgency,
arg.Impact,
arg.State,
arg.ExternalID,
arg.AssignmentGroup,
arg.AssignedTo,
arg.Category,
arg.Subcategory,
arg.CreatedAt,
arg.SysID,
)
var i Incident
err := row.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,
)
return i, err
}
const createIncoming = `-- name: CreateIncoming :one
@@ -84,6 +130,96 @@ func (q *Queries) CreateIncoming(ctx context.Context, arg CreateIncomingParams)
return i, err
}
const createWorkNote = `-- name: CreateWorkNote :one
INSERT into worknotes (
"incident_number", "note"
) VALUES(
?, ?
)
RETURNING id, incident_number, note
`
type CreateWorkNoteParams struct {
IncidentNumber string
Note sql.NullString
}
func (q *Queries) CreateWorkNote(ctx context.Context, arg CreateWorkNoteParams) (Worknote, error) {
row := q.db.QueryRowContext(ctx, createWorkNote, arg.IncidentNumber, arg.Note)
var i Worknote
err := row.Scan(&i.ID, &i.IncidentNumber, &i.Note)
return i, err
}
const getIncident = `-- name: GetIncident :one
SELECT id, external_id, created_at, incident_number, description, short_description, urgency, impact, state, assignment_group, assigned_to, category, subcategory, sys_id from incidents
WHERE incident_number = ?1
`
func (q *Queries) GetIncident(ctx context.Context, incidentnumber sql.NullString) (Incident, error) {
row := q.db.QueryRowContext(ctx, getIncident, incidentnumber)
var i Incident
err := row.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,
)
return i, err
}
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
`
func (q *Queries) ListIncidents(ctx context.Context) ([]Incident, error) {
rows, err := q.db.QueryContext(ctx, listIncidents)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Incident
for rows.Next() {
var i Incident
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,
); 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 listIncoming = `-- name: ListIncoming :many
SELECT id, incident_number, description, short_description, urgency, impact, state, external_id, work_notes, assignment_group, assigned_to, category, subcategory, created_at FROM "Incoming"
ORDER BY "id"
@@ -126,3 +262,35 @@ func (q *Queries) ListIncoming(ctx context.Context) ([]Incoming, error) {
}
return items, nil
}
const updateIncidentNumber = `-- name: UpdateIncidentNumber :exec
UPDATE incidents
SET incident_number = ?1
WHERE external_id = ?2
`
type UpdateIncidentNumberParams struct {
IncidentNumber sql.NullString
ExternalId string
}
func (q *Queries) UpdateIncidentNumber(ctx context.Context, arg UpdateIncidentNumberParams) error {
_, err := q.db.ExecContext(ctx, updateIncidentNumber, arg.IncidentNumber, arg.ExternalId)
return err
}
const updateIncidentState = `-- name: UpdateIncidentState :exec
UPDATE incidents
SET state = ?1
WHERE incident_number = ?2
`
type UpdateIncidentStateParams struct {
State sql.NullInt64
IncidentNumber sql.NullString
}
func (q *Queries) UpdateIncidentState(ctx context.Context, arg UpdateIncidentStateParams) error {
_, err := q.db.ExecContext(ctx, updateIncidentState, arg.State, arg.IncidentNumber)
return err
}

View File

View File

2
go.mod
View File

@@ -3,7 +3,7 @@ module mocksnow
go 1.24.1
require (
github.com/a-h/templ v0.3.833
github.com/a-h/templ v0.3.856
github.com/go-co-op/gocron/v2 v2.16.1
github.com/google/uuid v1.6.0
github.com/jmoiron/sqlx v1.4.0

4
go.sum
View File

@@ -1,7 +1,7 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/a-h/templ v0.3.833 h1:L/KOk/0VvVTBegtE0fp2RJQiBm7/52Zxv5fqlEHiQUU=
github.com/a-h/templ v0.3.833/go.mod h1:cAu4AiZhtJfBjMY0HASlyzvkrtjnHWPeEsyGK2YYmfk=
github.com/a-h/templ v0.3.856 h1:rMSlGIaQCqctylqM49VinpN7LlrptrFj0dMbYDj9GEQ=
github.com/a-h/templ v0.3.856/go.mod h1:qhrhAkRFubE7khxLZHsBFHfX+gWwVNKbzKeF9GlPV4M=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

View File

@@ -30,6 +30,7 @@ func GetFilePath(path string) string {
}
binaryPath := filepath.Dir(ex)
path = filepath.Join(binaryPath, path)
slog.Info("Found file", "path", path)
}
return path
}

View File

@@ -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\": [{}]}")
}

View File

@@ -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"

View File

@@ -16,7 +16,16 @@ type IncidentResultItem struct {
SysID string `json:"sys_id"`
}
type Incident struct {
// TODO - populate more fields here
type SingleIncidentResponse struct {
IncidentNumber string `json:"incident_number,omitempty"` // The incident number in ServiceNow. If blank, creates a new incident, if populated with a valid value it updates that record.
State string `json:"state,omitempty"` // integer value, 1-6 (6 is resolved)
// Associated DeviceID (UUID from CPDB)
ExternalID string `json:"external_id,omitempty"` // CPDB UUID for the configuration item
SysID string `json:"sys_id,omitempty"`
}
type Incoming struct {
IncidentNumber string `json:"incident_number,omitempty"` // The incident number in ServiceNow. If blank, creates a new incident, if populated with a valid value it updates that record.
Description string `json:"description,omitempty"`
ShortDescription string `json:"short_description,omitempty"`