Compare commits

...

22 Commits

Author SHA1 Message Date
6581164fc4 add SysUpdatedOn
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-26 13:19:34 +11:00
bb999afbb7 respond correctly to incident update
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-26 12:45:10 +11:00
34552985b7 skip null worknotes
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-26 12:17:21 +11:00
0e9591e6fc add CommentsAndWorkNotes
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-26 12:15:30 +11:00
63c534869e fix sql and getIncident
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-26 10:11:05 +11:00
c1fdc1f9a6 bugfix GetIncidentReport
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-25 18:53:59 +11:00
Nathan Coad
e161219aaa bugfix getIncident
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-25 13:42:43 +11:00
Nathan Coad
4e670f8785 getIncident list response
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-25 13:24:23 +11:00
e722ebe3f4 add worknote created time
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-25 06:55:01 +11:00
6389f53ae3 bugfix subcategory
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 23:08:05 +11:00
a3de1b2b03 many fixes
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 23:05:44 +11:00
d750033efd log more info in fallback function
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 21:26:11 +11:00
8112cd08c4 return proper json
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 17:46:25 +11:00
252bf0a1c8 fix incident view
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 17:03:16 +11:00
49e60f7843 enhance implementation
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 15:50:03 +11:00
ebf1d2aef3 log query parameters also
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 11:25:24 +11:00
985b520508 fix json
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 11:08:06 +11:00
6b88436b37 improve response
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 10:52:00 +11:00
fe889a7a2c more logging
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 10:38:24 +11:00
6c432f7cb1 bugfix
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 10:15:04 +11:00
789805b587 Mod tidy
All checks were successful
continuous-integration/drone Build is passing
2025-03-22 21:28:27 +11:00
6b2b60b9ef more code 2025-03-22 21:28:09 +11:00
36 changed files with 1855 additions and 214 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

@@ -0,0 +1,85 @@
package views
import (
"strconv"
"mocksnow/components/core"
tpl "github.com/a-h/templ"
)
type IncidentRow struct {
ID int64
ExternalID string
CreatedAt string
IncidentNumber string
Description string
ShortDescription string
Urgency int64
Impact int64
State int64
AllNotes string
AssignmentGroup string
AssignedTo string
Category string
SubCategory string
SysID string
}
templ IncidentsTable(rows []IncidentRow) {
<!DOCTYPE html>
<html lang="en">
@core.Header()
<body>
<main>
<div>
<h1 class="text-5xl mt-4 font-bold">Incidents</h1>
<div>
<table>
<thead>
<tr>
<th>ID</th>
<th>Created At</th>
<th>Incident #</th>
<th>SysId</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>
</tr>
</thead>
<tbody>
for _, row := range rows {
<tr>
<td>{ strconv.FormatInt(row.ID, 10) }</td>
<td>{ row.CreatedAt }</td>
<td>{ row.IncidentNumber }</td>
<td>{ row.SysID }</td>
<td>{ row.Description }</td>
<td>{ row.ShortDescription }</td>
<td>{ strconv.FormatInt(row.Urgency, 10) }</td>
<td>{ strconv.FormatInt(row.Impact, 10) }</td>
<td>{ strconv.FormatInt(row.State, 10) }</td>
<td>{ row.ExternalID }</td>
<td>@tpl.Raw(row.AllNotes)</td>
<td>{ row.AssignmentGroup }</td>
<td>{ row.AssignedTo }</td>
<td>{ row.Category }</td>
<td>{ row.SubCategory }</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</main>
@core.Footer()
</body>
</html>
}

View File

@@ -0,0 +1,280 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.856
package views
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
tpl "github.com/a-h/templ"
"mocksnow/components/core"
"strconv"
)
type IncidentRow struct {
ID int64
ExternalID string
CreatedAt string
IncidentNumber string
Description string
ShortDescription string
Urgency int64
Impact int64
State int64
AllNotes string
AssignmentGroup string
AssignedTo string
Category string
SubCategory string
SysID string
}
func IncidentsTable(rows []IncidentRow) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = core.Header().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<body><main><div><h1 class=\"text-5xl mt-4 font-bold\">Incidents</h1><div><table><thead><tr><th>ID</th><th>Created At</th><th>Incident #</th><th>SysId</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></tr></thead> <tbody>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, row := range rows {
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.FormatInt(row.ID, 10))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 60, Col: 71}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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.CreatedAt)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 61, Col: 55}
}
_, 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, 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.IncidentNumber)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 62, Col: 60}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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.SysID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 63, Col: 51}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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.Description)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 64, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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.ShortDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 65, Col: 62}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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(strconv.FormatInt(row.Urgency, 10))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 66, Col: 76}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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(strconv.FormatInt(row.Impact, 10))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 67, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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(strconv.FormatInt(row.State, 10))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 68, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
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.ExternalID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 69, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = tpl.Raw(row.AllNotes).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</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.AssignmentGroup)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 71, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</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.AssignedTo)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 72, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</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.Category)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 73, Col: 54}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</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.SubCategory)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/incidents.templ`, Line: 74, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</td></tr>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</tbody></table></div></div></main>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = core.Footer().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</body></html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

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 API Calls</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

@@ -0,0 +1,11 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE incidents RENAME COLUMN subcategory to sub_category;
ALTER TABLE "Incoming" RENAME COLUMN subcategory to sub_category;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE incidents RENAME COLUMN sub_category to subcategory;
ALTER TABLE "Incoming" RENAME COLUMN sub_category to subcategory;
-- +goose StatementEnd

View File

@@ -0,0 +1,10 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE worknotes ADD COLUMN created_at DATETIME;
UPDATE worknotes SET created_at = CURRENT_TIMESTAMP WHERE created_at IS NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE worknotes DROP COLUMN created_at;
-- +goose StatementEnd

View File

@@ -0,0 +1,10 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE incidents ADD COLUMN updated_at DATETIME;
UPDATE incidents SET updated_at = CURRENT_TIMESTAMP WHERE updated_at IS NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE incidents DROP COLUMN updated_at;
-- +goose StatementEnd

View File

@@ -9,9 +9,21 @@ 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
UpdatedAt sql.NullTime
}
type Incoming struct {
@@ -27,6 +39,13 @@ type Incoming struct {
AssignmentGroup sql.NullString
AssignedTo sql.NullString
Category sql.NullString
Subcategory sql.NullString
SubCategory sql.NullString
CreatedAt sql.NullTime
}
type Worknote struct {
ID int64
IncidentNumber string
Note sql.NullString
CreatedAt sql.NullTime
}

View File

@@ -2,16 +2,76 @@
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"
"incident_number", "description", "short_description", "urgency", "impact", "state", "external_id", "work_notes", "assignment_group", "assigned_to", "category", "sub_category", "created_at"
) VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
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", "sub_category", "created_at", "sys_id"
) VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING *;
-- name: UpdateIncidentNumber :exec
UPDATE incidents
SET incident_number = sqlc.arg('incidentNumber')
WHERE external_id = sqlc.arg('externalId');
-- name: UpdateIncidentNumberSysId :exec
UPDATE incidents
SET incident_number = sqlc.arg('incidentNumber')
WHERE sys_id = sqlc.arg('sysId');
-- name: UpdateIncidentState :exec
UPDATE incidents
SET state = sqlc.arg('state'), updated_at = CURRENT_TIMESTAMP
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 = ?,
sub_category = ?,
updated_at = CURRENT_TIMESTAMP
WHERE incident_number = ?;
-- name: GetIncident :one
SELECT * from incidents
WHERE incident_number = sqlc.arg('incidentNumber');
-- name: ListIncidents :many
SELECT * FROM incidents;
-- name: GetIncidentReport :many
SELECT incidents.*, IFNULL(GROUP_CONCAT(worknotes.note, '<br />'), ' ') AS all_notes
FROM incidents LEFT JOIN worknotes ON incidents.incident_number = worknotes.incident_number
GROUP BY incidents.id;
-- name: CreateWorkNote :one
INSERT into worknotes (
"incident_number", "note", "created_at"
) VALUES(
?, ?, CURRENT_TIMESTAMP
)
RETURNING *;
-- name: GetIncidentWorkNotes :many
SELECT * from worknotes
WHERE incident_number = sqlc.arg('incidentNumber');
-- name: ListWorkNotes :many
SELECT * from worknotes;

View File

@@ -11,25 +11,72 @@ 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", "sub_category", "created_at", "sys_id"
) VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING id, external_id, created_at, incident_number, description, short_description, urgency, impact, state, assignment_group, assigned_to, category, sub_category, sys_id, updated_at
`
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,
&i.UpdatedAt,
)
return i, err
}
const createIncoming = `-- 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"
"incident_number", "description", "short_description", "urgency", "impact", "state", "external_id", "work_notes", "assignment_group", "assigned_to", "category", "sub_category", "created_at"
) VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING id, incident_number, description, short_description, urgency, impact, state, external_id, work_notes, assignment_group, assigned_to, category, subcategory, created_at
RETURNING id, incident_number, description, short_description, urgency, impact, state, external_id, work_notes, assignment_group, assigned_to, category, sub_category, created_at
`
type CreateIncomingParams struct {
@@ -44,7 +91,7 @@ type CreateIncomingParams struct {
AssignmentGroup sql.NullString
AssignedTo sql.NullString
Category sql.NullString
Subcategory sql.NullString
SubCategory sql.NullString
CreatedAt sql.NullTime
}
@@ -61,7 +108,7 @@ func (q *Queries) CreateIncoming(ctx context.Context, arg CreateIncomingParams)
arg.AssignmentGroup,
arg.AssignedTo,
arg.Category,
arg.Subcategory,
arg.SubCategory,
arg.CreatedAt,
)
var i Incoming
@@ -78,14 +125,209 @@ func (q *Queries) CreateIncoming(ctx context.Context, arg CreateIncomingParams)
&i.AssignmentGroup,
&i.AssignedTo,
&i.Category,
&i.Subcategory,
&i.SubCategory,
&i.CreatedAt,
)
return i, err
}
const createWorkNote = `-- name: CreateWorkNote :one
INSERT into worknotes (
"incident_number", "note", "created_at"
) VALUES(
?, ?, CURRENT_TIMESTAMP
)
RETURNING id, incident_number, note, created_at
`
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,
&i.CreatedAt,
)
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, sub_category, sys_id, updated_at 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,
&i.UpdatedAt,
)
return i, err
}
const getIncidentReport = `-- name: GetIncidentReport :many
SELECT incidents.id, incidents.external_id, incidents.created_at, incidents.incident_number, incidents.description, incidents.short_description, incidents.urgency, incidents.impact, incidents.state, incidents.assignment_group, incidents.assigned_to, incidents.category, incidents.sub_category, incidents.sys_id, incidents.updated_at, IFNULL(GROUP_CONCAT(worknotes.note, '<br />'), ' ') AS all_notes
FROM incidents LEFT JOIN worknotes ON incidents.incident_number = worknotes.incident_number
GROUP BY incidents.id
`
type GetIncidentReportRow struct {
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
UpdatedAt sql.NullTime
AllNotes interface{}
}
func (q *Queries) GetIncidentReport(ctx context.Context) ([]GetIncidentReportRow, error) {
rows, err := q.db.QueryContext(ctx, getIncidentReport)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetIncidentReportRow
for rows.Next() {
var i GetIncidentReportRow
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,
&i.UpdatedAt,
&i.AllNotes,
); 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 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, updated_at 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,
&i.UpdatedAt,
); 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"
SELECT id, incident_number, description, short_description, urgency, impact, state, external_id, work_notes, assignment_group, assigned_to, category, sub_category, created_at FROM "Incoming"
ORDER BY "id"
`
@@ -111,7 +353,7 @@ func (q *Queries) ListIncoming(ctx context.Context) ([]Incoming, error) {
&i.AssignmentGroup,
&i.AssignedTo,
&i.Category,
&i.Subcategory,
&i.SubCategory,
&i.CreatedAt,
); err != nil {
return nil, err
@@ -126,3 +368,131 @@ 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
external_id = ?,
description = ?,
short_description = ?,
urgency = ?,
impact = ?,
state = ?,
assignment_group = ?,
assigned_to = ?,
category = ?,
sub_category = ?,
updated_at = CURRENT_TIMESTAMP
WHERE incident_number = ?
`
type UpdateIncidentParams struct {
ExternalID string
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
IncidentNumber sql.NullString
}
func (q *Queries) UpdateIncident(ctx context.Context, arg UpdateIncidentParams) error {
_, err := q.db.ExecContext(ctx, updateIncident,
arg.ExternalID,
arg.Description,
arg.ShortDescription,
arg.Urgency,
arg.Impact,
arg.State,
arg.AssignmentGroup,
arg.AssignedTo,
arg.Category,
arg.SubCategory,
arg.IncidentNumber,
)
return err
}
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 updateIncidentNumberSysId = `-- name: UpdateIncidentNumberSysId :exec
UPDATE incidents
SET incident_number = ?1
WHERE sys_id = ?2
`
type UpdateIncidentNumberSysIdParams struct {
IncidentNumber sql.NullString
SysId sql.NullString
}
func (q *Queries) UpdateIncidentNumberSysId(ctx context.Context, arg UpdateIncidentNumberSysIdParams) error {
_, err := q.db.ExecContext(ctx, updateIncidentNumberSysId, arg.IncidentNumber, arg.SysId)
return err
}
const updateIncidentState = `-- name: UpdateIncidentState :exec
UPDATE incidents
SET state = ?1, updated_at = CURRENT_TIMESTAMP
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

5
go.mod
View File

@@ -3,8 +3,9 @@ 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
github.com/joho/godotenv v1.5.1
github.com/pressly/goose/v3 v3.24.1
@@ -14,10 +15,8 @@ require (
require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect

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

@@ -24,7 +24,8 @@ func GenerateCerts(tlsCert string, tlsKey string) {
// Get the hostname
hostname, err := os.Hostname()
if err != nil {
panic(err)
log.Printf("failed to lookup hostname: '%s'\n", err)
hostname = "localhost"
}
// Check that the directory exists

View File

@@ -2,6 +2,9 @@ package utils
import (
"context"
"database/sql"
"encoding/base64"
"fmt"
"log"
"log/slog"
"net"
@@ -30,6 +33,7 @@ func GetFilePath(path string) string {
}
binaryPath := filepath.Dir(ex)
path = filepath.Join(binaryPath, path)
slog.Info("Found file", "path", path)
}
return path
}
@@ -68,7 +72,7 @@ func SleepWithContext(ctx context.Context, d time.Duration) {
}
}
// Generic converter using reflection
// Generic converter using reflection for all sqlc/sqlite types
func ConvertStruct(src interface{}, dst interface{}) {
srcVal := reflect.ValueOf(src)
srcType := reflect.TypeOf(src)
@@ -76,30 +80,97 @@ func ConvertStruct(src interface{}, dst interface{}) {
for i := 0; i < srcVal.NumField(); i++ {
srcField := srcVal.Field(i)
srcFieldType := srcField.Type()
dstField := dstVal.FieldByName(srcType.Field(i).Name)
//slog.Info("Source field", "name", srcType.Field(i).Name)
if !dstField.IsValid() || !dstField.CanSet() {
continue
}
switch srcField.Type().Name() {
case "NullString":
switch srcFieldType {
case reflect.TypeOf(sql.NullString{}):
//slog.Info("Sourcefield is string")
if srcField.FieldByName("Valid").Bool() {
dstField.SetString(srcField.FieldByName("String").String())
} else {
dstField.SetString("")
}
case "NullTime":
case reflect.TypeOf(sql.NullInt64{}):
//slog.Info("Sourcefield is int64")
if srcField.FieldByName("Valid").Bool() {
val := srcField.FieldByName("Int64").Int()
if dstField.Kind() == reflect.String {
dstField.SetString(fmt.Sprintf("%d", val))
} else {
dstField.SetInt(val)
}
} else {
if dstField.Kind() == reflect.String {
dstField.SetString("")
} else {
dstField.SetInt(0)
}
}
case reflect.TypeOf(sql.NullFloat64{}):
if srcField.FieldByName("Valid").Bool() {
val := srcField.FieldByName("Float64").Float()
if dstField.Kind() == reflect.String {
dstField.SetString(fmt.Sprintf("%f", val))
} else {
dstField.SetFloat(val)
}
} else {
if dstField.Kind() == reflect.String {
dstField.SetString("")
} else {
dstField.SetFloat(0)
}
}
case reflect.TypeOf(sql.NullBool{}):
if srcField.FieldByName("Valid").Bool() {
val := srcField.FieldByName("Bool").Bool()
if dstField.Kind() == reflect.String {
dstField.SetString(fmt.Sprintf("%t", val))
} else {
dstField.SetBool(val)
}
} else {
if dstField.Kind() == reflect.String {
dstField.SetString("")
} else {
dstField.SetBool(false)
}
}
case reflect.TypeOf(sql.NullTime{}):
//slog.Info("Sourcefield is time")
if srcField.FieldByName("Valid").Bool() {
t := srcField.FieldByName("Time").Interface().(time.Time)
dstField.SetString(t.Format("2006-01-02 15:04:05"))
} else {
slog.Info("value is not valid")
dstField.SetString("")
}
default:
// Handle int64 -> int conversion
if srcField.Kind() == reflect.Int64 && dstField.Kind() == reflect.Int {
dstField.SetInt(srcField.Int())
// Handle []byte (often from BLOB fields)
if srcField.Kind() == reflect.Slice && srcField.Type().Elem().Kind() == reflect.Uint8 {
if !srcField.IsNil() {
encoded := base64.StdEncoding.EncodeToString(srcField.Bytes())
dstField.SetString(encoded)
} else {
dstField.SetString("")
}
}
// Fallback: copy if types are directly assignable
if srcField.Type().AssignableTo(dstField.Type()) {
dstField.Set(srcField)
}
}
}

10
main.go
View File

@@ -1,16 +1,17 @@
package main
import (
"context"
"fmt"
"os"
"runtime"
"time"
"mocksnow/db"
"mocksnow/internal/settings"
utils "mocksnow/internal/utils"
"mocksnow/log"
"mocksnow/server"
"mocksnow/server/router"
"os"
"runtime"
"time"
"github.com/go-co-op/gocron/v2"
"github.com/joho/godotenv"
@@ -38,6 +39,7 @@ func main() {
log.GetOutput(),
)
_, cancel := context.WithCancel(context.Background())
//ctx, cancel := context.WithCancel(context.Background())
// Configure database
@@ -217,7 +219,7 @@ func main() {
svr := server.New(
logger,
c,
//cancel,
cancel,
bindAddress,
server.WithRouter(r),
server.SetTls(bindDisableTls),

View File

@@ -0,0 +1,56 @@
package handler
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func (h *Handler) Fallback(w http.ResponseWriter, r *http.Request) {
h.Logger.Debug("Fallback 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)
}
}
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Unable to read body", http.StatusBadRequest)
return
}
defer r.Body.Close()
// Pretty-print JSON to console
var prettyJSON bytes.Buffer
if err := json.Indent(&prettyJSON, body, "", " "); err != nil {
w.Header().Set("Content-Type", "application/json")
// always send 200 just so that the requestor doesn't error out
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Invalid JSON received. Visit /about for more info."),
})
return
}
h.Logger.Debug("Received JSON payload", "payload", prettyJSON.String())
//fmt.Println("Received JSON payload:")
//fmt.Println(prettyJSON.String())
// Set content type and write back the same JSON
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(prettyJSON.Bytes())
}

View File

@@ -0,0 +1,9 @@
package handler
import (
"net/http"
)
func (h *Handler) Favicon(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}

View File

@@ -0,0 +1,255 @@
package handler
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"mocksnow/server/models"
"net/http"
"strconv"
"strings"
)
// 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, "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}
parts := strings.Split(path, "/")
h.Logger.Debug("Request path", "parts", parts)
// Parse and write query parameters
query := r.URL.Query()
if len(query) == 0 {
h.Logger.Debug("No query parameters.")
} else {
//fmt.Fprintln(w, "Query parameters:")
for key, values := range query {
for _, value := range values {
h.Logger.Debug("Query Paramater", "key", key, "value", value)
}
}
}
if len(parts) == 6 && strings.HasPrefix(path, "/api/now/table/incident/") {
// Handle the specific 'number' query parameter
number := query.Get("number")
active := query.Get("active")
if number != "" {
h.Logger.Debug("GetIncident called for specific incident number", "number", number)
b, err := h.getSingleIncident(number, ctx)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("%s", err),
})
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, string(b))
return
// Query record from database and return that
} else if active == "true" {
// Return list of all incidents
h.Logger.Debug("GetIncident called for list all incidents")
responseList := make([]models.SingleIncidentResponse, 0)
incList, err := h.Database.Queries().ListIncidents(ctx)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
h.Logger.Debug("No incidents found")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "{\"result\": [{}]}")
return
} 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 all incidents: '%s'", err),
})
return
}
}
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 {
if note.Note.Valid {
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,
SysUpdatedOn: inc.UpdatedAt.Time.String(),
CommentsAndWorkNotes: strings.Join(wkn, "\n\n"),
// TODO
}
// add to responseList
h.Logger.Debug("Adding incident to active inc response list", "incident", r)
responseList = append(responseList, r)
}
// marshal struct into json and return
wrappedList := models.MultipleIncidentResponse{
Result: responseList,
}
prettyPrint(wrappedList)
b, err := json.Marshal(wrappedList)
if err != nil {
h.Logger.Error("Unable to convert database records 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 records into json: '%s'", err),
})
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, string(b))
return
} else {
// Requested a single incident
id := parts[5] // Extract {id}
h.Logger.Debug("GetIncident called for specific incident", "id", id)
b, err := h.getSingleIncident(id, ctx)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("%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
}
func (h *Handler) getSingleIncident(number string, ctx context.Context) ([]byte, error) {
var b []byte
var wkn []string
inc, 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", "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 {
if note.Note.Valid {
wkn = append(wkn, note.Note.String)
}
}
// convert from database resposne to expected json format
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,
SysUpdatedOn: inc.UpdatedAt.Time.String(),
CommentsAndWorkNotes: strings.Join(wkn, "\n\n"),
// TODO
}
wrappedList := models.MultipleIncidentResponse{
Result: []models.SingleIncidentResponse{r},
}
prettyPrint(wrappedList)
b, err = json.Marshal(wrappedList)
if err != nil {
h.Logger.Error("Unable to convert database record into json", "error", err)
return b, err
}
return b, nil
}

View File

@@ -0,0 +1,53 @@
package handler
import (
"context"
"encoding/json"
"fmt"
"mocksnow/components/views"
"mocksnow/db/queries"
"mocksnow/internal/utils"
"net/http"
)
func (h *Handler) RenderIncidentTable(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
h.Logger.Debug("Querying incidents table")
results, err := h.Database.Queries().GetIncidentReport(ctx)
if err != nil {
h.Logger.Error("Unable to query incoming table", "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 incoming table: '%s'", err),
})
return
}
if len(results) == 0 {
h.Logger.Error("Empty updates result")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Empty updates result"),
})
return
}
views.IncidentsTable(ConvertIncidentList(results)).Render(r.Context(), w)
}
// Converts a slice of Incoming to []IncomingRow
func ConvertIncidentList(list []queries.GetIncidentReportRow) []views.IncidentRow {
rows := make([]views.IncidentRow, 0, len(list))
for _, in := range list {
prettyPrint(in)
var row views.IncidentRow
utils.ConvertStruct(in, &row)
rows = append(rows, row)
}
return rows
}

View File

@@ -4,16 +4,16 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
"mocksnow/components/views"
"mocksnow/db/queries"
"mocksnow/internal/utils"
"net/http"
)
func (h *Handler) RenderIncomingTable(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
h.Logger.Debug("Querying updates table")
h.Logger.Debug("Querying incoming table")
results, err := h.Database.Queries().ListIncoming(ctx)
if err != nil {
h.Logger.Error("Unable to query incoming table", "error", err)

View File

@@ -9,12 +9,33 @@ import (
"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 {
@@ -27,87 +48,244 @@ 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()
recordLink := "https://server.fqdn.com/api/now/table/incident/" + sysID
// 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()
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,
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")
json.NewEncoder(w).Encode(response)
/*
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"
@@ -117,3 +295,84 @@ func randomImportSet() string {
}
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
}

View File

@@ -16,7 +16,33 @@ type IncidentResultItem struct {
SysID string `json:"sys_id"`
}
type Incident 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"`
CommentsAndWorkNotes string `json:"comments_and_work_notes"`
SysUpdatedOn string `json:"sys_updated_on"`
// TODO - unvalidated from here on
// Associated DeviceID (UUID from CPDB)
ExternalID string `json:"external_id,omitempty"` // CPDB UUID for the configuration item
}
type MultipleIncidentResponse struct {
Result []SingleIncidentResponse `json:"result"`
}
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"`

View File

@@ -2,13 +2,13 @@ package router
import (
"log/slog"
"net/http"
"net/http/pprof"
"mocksnow/db"
"mocksnow/dist"
"mocksnow/internal/settings"
"mocksnow/server/handler"
"mocksnow/server/middleware"
"net/http"
"net/http/pprof"
)
func New(logger *slog.Logger, database db.Database, buildTime string, sha1ver string, goVersion string, settings *settings.Settings) http.Handler {
@@ -26,9 +26,17 @@ func New(logger *slog.Logger, database db.Database, buildTime string, sha1ver st
mux := http.NewServeMux()
mux.Handle("/assets/", middleware.CacheMiddleware(http.FileServer(http.FS(dist.AssetsDir))))
mux.HandleFunc("/", h.Home)
mux.HandleFunc("/about", h.Home)
mux.HandleFunc("/api/now/import/x_dusa2_itom_inc_imp", h.NewSnow)
mux.HandleFunc("/api/now/table/incident/", h.GetIncident)
mux.HandleFunc("/api/print", h.RenderIncomingTable)
mux.HandleFunc("/api/print/incidents", h.RenderIncidentTable)
mux.HandleFunc("/favicon.ico", h.Favicon)
// TODO - fallback route that will just echo incoming payload
mux.HandleFunc("/", h.Fallback)
// mux.HandleFunc("/api/event/vm/create", h.VmCreateEvent)
// mux.HandleFunc("/api/event/vm/modify", h.VmModifyEvent)

View File

@@ -25,7 +25,7 @@ type Server struct {
}
// New creates a new server with the given logger, address and options.
func New(logger *slog.Logger, cron gocron.Scheduler, addr string, opts ...Option) *Server {
func New(logger *slog.Logger, cron gocron.Scheduler, cancel context.CancelFunc, addr string, opts ...Option) *Server {
// Set some options for TLS
tlsConfig := &tls.Config{
@@ -55,7 +55,7 @@ func New(logger *slog.Logger, cron gocron.Scheduler, addr string, opts ...Option
srv: srv,
logger: logger,
cron: cron,
//cancel: cancel,
cancel: cancel,
}
// Apply any options