From 180ee9de4fe437bf7a91d7113878a268f5cf5594 Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Tue, 28 Dec 2021 12:15:25 -0800 Subject: [PATCH 01/14] add explicit templates for each notification type used --- notification/twilio/alertsms.go | 64 ++++++++++++++++++++++----------- notification/twilio/sms.go | 16 +++++---- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/notification/twilio/alertsms.go b/notification/twilio/alertsms.go index 4eb5a21fb4..b15b1792f2 100644 --- a/notification/twilio/alertsms.go +++ b/notification/twilio/alertsms.go @@ -9,6 +9,7 @@ import ( "github.com/pkg/errors" "github.com/target/goalert/config" + "github.com/target/goalert/notification" ) // 160 GSM characters (140 bytes) is the max for a single segment message. @@ -20,30 +21,36 @@ import ( const maxGSMLen = 160 type alertSMS struct { - ID int - Count int - Body string - Link string - Code int + ID int + Count int + Body string + Summary string + Link string + Code int + Type notification.MessageType } -var smsTmpl = template.Must(template.New("alertSMS").Parse(` -{{- if .ID}}Alert #{{.ID}}: {{.Body}} -{{- else if .Count}}Svc '{{.Body}}': {{.Count}} unacked alert{{if gt .Count 1}}s{{end}} -{{- end}} -{{- if .Link }} +// statusTempl uses the ID and Body to render a message +var statusTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: {{.Summary}} + +{{.Body}} +`)) + +// bundleTempl uses the Count, Body, Link, and Code to render a message +var bundleTempl = template.Must(template.New("alertSMS").Parse(`Svc '{{.Body}}': {{.Count}} unacked alert{{if gt .Count 1}}s{{end}} {{.Link}} -{{- end}} -{{- if and .Count .ID }} -{{.Count}} other alert{{if gt .Count 1}}s have{{else}} has{{end}} been updated. -{{- end}} -{{- if .Code}} +Reply '{{.Code}}aa' to ack all, '{{.Code}}cc' to close all. +`)) + +// alertTempl uses the ID, Summary, Link, and Code to render a message +var alertTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: {{.Summary}} + +{{.Link}} -Reply '{{.Code}}a{{if .Count}}a{{end}}' to ack{{if .Count}} all{{end}}, '{{.Code}}c{{if .Count}}c{{end}}' to close{{if .Count}} all{{end}}. -{{- end}}`, -)) +Reply '{{.Code}}a' to ack, '{{.Code}}c' to close. +`)) const gsmAlphabet = "@∆ 0¡P¿p£!1AQaq$Φ\"2BRbr¥Γ#3CScsèΛ¤4DTdtéΩ%5EUeuùΠ&6FVfvìΨ'7GWgwòΣ(8HXhxÇΘ)9IYiy\n Ξ *:JZjzØ+;KÄkäøÆ,NÜnüåÉ/?O§oà" @@ -112,8 +119,25 @@ func (a alertSMS) Render(maxLen int) (string, error) { a.Body = strings.Replace(a.Body, " ", " ", -1) a.Body = strings.TrimSpace(a.Body) + a.Summary = strings.Map(mapGSM, a.Summary) + a.Summary = strings.Replace(a.Summary, " ", " ", -1) + a.Summary = strings.TrimSpace(a.Summary) + var buf bytes.Buffer - err := smsTmpl.Execute(&buf, a) + + var tmpl template.Template + switch a.Type { + case notification.MessageTypeAlertStatus: + tmpl = *statusTempl + case notification.MessageTypeAlertBundle: + tmpl = *bundleTempl + case notification.MessageTypeAlert: + tmpl = *alertTempl + default: + return "", errors.Errorf("unsupported message type: ", a.Type) + } + + err := tmpl.Execute(&buf, a) if err != nil { return "", err } @@ -125,7 +149,7 @@ func (a alertSMS) Render(maxLen int) (string, error) { } a.Body = strings.TrimSpace(a.Body[:newBodyLen]) buf.Reset() - err = smsTmpl.Execute(&buf, a) + err = tmpl.Execute(&buf, a) if err != nil { return "", err } diff --git a/notification/twilio/sms.go b/notification/twilio/sms.go index 3a2ce3d237..a48351886d 100644 --- a/notification/twilio/sms.go +++ b/notification/twilio/sms.go @@ -114,8 +114,10 @@ func (s *SMS) Send(ctx context.Context, msg notification.Message) (*notification switch t := msg.(type) { case notification.AlertStatus: message, err = alertSMS{ - ID: t.AlertID, - Body: t.LogEntry, + ID: t.AlertID, + Summary: t.Summary, + Body: t.LogEntry, + Type: notification.MessageTypeAlertStatus, }.Render(maxLen) case notification.AlertBundle: var link string @@ -128,6 +130,7 @@ func (s *SMS) Send(ctx context.Context, msg notification.Message) (*notification Body: t.ServiceName, Link: link, Code: makeSMSCode(0, t.ServiceID), + Type: notification.MessageTypeAlertBundle, }.Render(maxLen) case notification.Alert: var link string @@ -136,10 +139,11 @@ func (s *SMS) Send(ctx context.Context, msg notification.Message) (*notification } message, err = alertSMS{ - ID: t.AlertID, - Body: t.Summary, - Link: link, - Code: makeSMSCode(t.AlertID, ""), + ID: t.AlertID, + Summary: t.Summary, + Link: link, + Code: makeSMSCode(t.AlertID, ""), + Type: notification.MessageTypeAlert, }.Render(maxLen) case notification.Test: message = "Test message." From 232d80008a1640c0ac542e120a1c72f950cb2445 Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Tue, 28 Dec 2021 13:53:31 -0800 Subject: [PATCH 02/14] add conditionals back to templates --- notification/twilio/alertsms.go | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/notification/twilio/alertsms.go b/notification/twilio/alertsms.go index b15b1792f2..5cc736f0eb 100644 --- a/notification/twilio/alertsms.go +++ b/notification/twilio/alertsms.go @@ -30,27 +30,32 @@ type alertSMS struct { Type notification.MessageType } -// statusTempl uses the ID and Body to render a message -var statusTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: {{.Summary}} +// alertTempl uses the ID, Summary, Link, and Code to render a message +var alertTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: {{.Summary}} -{{.Body}} -`)) +{{- if .Link }} +{{.Link}} +{{end}} + +{{- if .Code}} +Reply '{{.Code}}a' to ack, '{{.Code}}c' to close. +{{end}}`)) // bundleTempl uses the Count, Body, Link, and Code to render a message var bundleTempl = template.Must(template.New("alertSMS").Parse(`Svc '{{.Body}}': {{.Count}} unacked alert{{if gt .Count 1}}s{{end}} +{{- if .Link }} {{.Link}} +{{end}} +{{- if .Code}} Reply '{{.Code}}aa' to ack all, '{{.Code}}cc' to close all. -`)) +{{end}}`)) -// alertTempl uses the ID, Summary, Link, and Code to render a message -var alertTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: {{.Summary}} - -{{.Link}} +// statusTempl uses the ID, Summary, and Body to render a message +var statusTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: {{.Summary}} -Reply '{{.Code}}a' to ack, '{{.Code}}c' to close. -`)) +{{.Body}}`)) const gsmAlphabet = "@∆ 0¡P¿p£!1AQaq$Φ\"2BRbr¥Γ#3CScsèΛ¤4DTdtéΩ%5EUeuùΠ&6FVfvìΨ'7GWgwòΣ(8HXhxÇΘ)9IYiy\n Ξ *:JZjzØ+;KÄkäøÆ,NÜnüåÉ/?O§oà" @@ -134,7 +139,7 @@ func (a alertSMS) Render(maxLen int) (string, error) { case notification.MessageTypeAlert: tmpl = *alertTempl default: - return "", errors.Errorf("unsupported message type: ", a.Type) + return "", errors.Errorf("unsupported message type: %v", a.Type) } err := tmpl.Execute(&buf, a) From c96d61a916a3a2a6a8619cf5e90ac478d566fe48 Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Tue, 28 Dec 2021 13:53:49 -0800 Subject: [PATCH 03/14] update tests, remove deprecated status bundle tests --- notification/twilio/alertsms_test.go | 49 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/notification/twilio/alertsms_test.go b/notification/twilio/alertsms_test.go index 1b2fb13eb6..2a97f0b6ef 100644 --- a/notification/twilio/alertsms_test.go +++ b/notification/twilio/alertsms_test.go @@ -4,6 +4,8 @@ import ( "strconv" "strings" "testing" + + "github.com/target/goalert/notification" ) func TestMapGSM(t *testing.T) { @@ -47,10 +49,11 @@ func TestAlertSMS_Render(t *testing.T) { check("normal", alertSMS{ - ID: 123, - Code: 1, - Link: "https://example.com/alerts/123", - Body: "Testing", + ID: 123, + Code: 1, + Link: "https://example.com/alerts/123", + Summary: "Testing", + Type: notification.MessageTypeAlert, }, `Alert #123: Testing @@ -61,9 +64,10 @@ Reply '1a' to ack, '1c' to close.`, check("no-reply", alertSMS{ - ID: 123, - Link: "https://example.com/alerts/123", - Body: "Testing", + ID: 123, + Link: "https://example.com/alerts/123", + Summary: "Testing", + Type: notification.MessageTypeAlert, }, `Alert #123: Testing @@ -85,6 +89,7 @@ Reply '1a' to ack, '1c' to close.`, alertSMS{ ID: 123, Body: "Testing", + Type: notification.MessageTypeAlert, }, `Alert #123: Testing`, ) @@ -95,6 +100,7 @@ Reply '1a' to ack, '1c' to close.`, Code: 1, Link: "https://example.com/alerts/123", Body: "Testing with a really really obnoxiously long message that will be need to be truncated at some point.", + Type: notification.MessageTypeAlert, }, `Alert #123: Testing with a really really obnoxiously long message that will be need to be tru @@ -109,6 +115,7 @@ Reply '1a' to ack, '1c' to close.`, Code: 1, Link: "https://example.com/alerts/123", Body: "Testing with a really really obnoxiously long message that will be need to be truncated at some point.", + Type: notification.MessageTypeAlert, }, `Alert #123456789: Testing with a really really obnoxiously long message that will be need to @@ -124,6 +131,7 @@ Reply '1a' to ack, '1c' to close.`, Code: 123456789, Link: "https://example.com/alerts/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff", Body: "Testing with a really really obnoxiously long message that will be need to be truncated at some point.", + Type: notification.MessageTypeAlert, }, "", ) @@ -134,6 +142,7 @@ Reply '1a' to ack, '1c' to close.`, Body: "My Service", Code: 100, Link: "https://example.com/services/321-654/alerts", + Type: notification.MessageTypeAlertBundle, }, `Svc 'My Service': 1 unacked alert @@ -148,6 +157,7 @@ Reply '100aa' to ack all, '100cc' to close all.`, Body: "My Service", Code: 100, Link: "https://example.com/services/321-654/alerts", + Type: notification.MessageTypeAlertBundle, }, `Svc 'My Service': 5 unacked alerts @@ -156,28 +166,17 @@ https://example.com/services/321-654/alerts Reply '100aa' to ack all, '100cc' to close all.`, ) - check("status-bundle-one", + check("alert-status", // can't fit body alertSMS{ - ID: 123, - Count: 1, - Body: "Some log entry", - }, - `Alert #123: Some log entry - -1 other alert has been updated.`, - ) - - check("status-bundle", - // can't fit body - alertSMS{ - ID: 123, - Count: 2, - Body: "Some log entry", + ID: 123, + Summary: "Testing", + Body: "Some log entry", + Type: notification.MessageTypeAlertStatus, }, - `Alert #123: Some log entry + `Alert #123: Testing -2 other alerts have been updated.`, +Some log entry`, ) } From 962743a047c2f22ef2a782373f9e6bcf2142c45d Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Tue, 28 Dec 2021 14:02:41 -0800 Subject: [PATCH 04/14] fix test with last return character --- notification/twilio/alertsms.go | 6 ++---- notification/twilio/alertsms_test.go | 9 ++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/notification/twilio/alertsms.go b/notification/twilio/alertsms.go index 5cc736f0eb..fad29f0817 100644 --- a/notification/twilio/alertsms.go +++ b/notification/twilio/alertsms.go @@ -38,8 +38,7 @@ var alertTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: { {{end}} {{- if .Code}} -Reply '{{.Code}}a' to ack, '{{.Code}}c' to close. -{{end}}`)) +Reply '{{.Code}}a' to ack, '{{.Code}}c' to close.{{end}}`)) // bundleTempl uses the Count, Body, Link, and Code to render a message var bundleTempl = template.Must(template.New("alertSMS").Parse(`Svc '{{.Body}}': {{.Count}} unacked alert{{if gt .Count 1}}s{{end}} @@ -49,8 +48,7 @@ var bundleTempl = template.Must(template.New("alertSMS").Parse(`Svc '{{.Body}}': {{end}} {{- if .Code}} -Reply '{{.Code}}aa' to ack all, '{{.Code}}cc' to close all. -{{end}}`)) +Reply '{{.Code}}aa' to ack all, '{{.Code}}cc' to close all.{{end}}`)) // statusTempl uses the ID, Summary, and Body to render a message var statusTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: {{.Summary}} diff --git a/notification/twilio/alertsms_test.go b/notification/twilio/alertsms_test.go index 2a97f0b6ef..956cd7a4b9 100644 --- a/notification/twilio/alertsms_test.go +++ b/notification/twilio/alertsms_test.go @@ -76,9 +76,10 @@ https://example.com/alerts/123`, check("no-Link", alertSMS{ - ID: 123, - Code: 1, - Body: "Testing", + ID: 123, + Code: 1, + Summary: "Testing", + Type: notification.MessageTypeAlert, }, `Alert #123: Testing @@ -167,7 +168,6 @@ Reply '100aa' to ack all, '100cc' to close all.`, ) check("alert-status", - // can't fit body alertSMS{ ID: 123, Summary: "Testing", @@ -178,5 +178,4 @@ Reply '100aa' to ack all, '100cc' to close all.`, Some log entry`, ) - } From ffd54850902a6e2e7a61df9b252b20e971da62f2 Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Thu, 6 Jan 2022 14:00:06 -0800 Subject: [PATCH 05/14] give sms templates unique names --- notification/twilio/alertsms.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notification/twilio/alertsms.go b/notification/twilio/alertsms.go index fad29f0817..5b85aee0b6 100644 --- a/notification/twilio/alertsms.go +++ b/notification/twilio/alertsms.go @@ -41,7 +41,7 @@ var alertTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: { Reply '{{.Code}}a' to ack, '{{.Code}}c' to close.{{end}}`)) // bundleTempl uses the Count, Body, Link, and Code to render a message -var bundleTempl = template.Must(template.New("alertSMS").Parse(`Svc '{{.Body}}': {{.Count}} unacked alert{{if gt .Count 1}}s{{end}} +var bundleTempl = template.Must(template.New("alertBundleSMS").Parse(`Svc '{{.Body}}': {{.Count}} unacked alert{{if gt .Count 1}}s{{end}} {{- if .Link }} {{.Link}} @@ -51,7 +51,7 @@ var bundleTempl = template.Must(template.New("alertSMS").Parse(`Svc '{{.Body}}': Reply '{{.Code}}aa' to ack all, '{{.Code}}cc' to close all.{{end}}`)) // statusTempl uses the ID, Summary, and Body to render a message -var statusTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: {{.Summary}} +var statusTempl = template.Must(template.New("alertStatusSMS").Parse(`Alert #{{.ID}}: {{.Summary}} {{.Body}}`)) From d4475b07c19bc6bfd5c75efdc80a93f9d3eed1fa Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Wed, 19 Jan 2022 10:32:49 -0800 Subject: [PATCH 06/14] trim summary before body if message is too long --- notification/twilio/alertsms.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/notification/twilio/alertsms.go b/notification/twilio/alertsms.go index 5b85aee0b6..b1487e036d 100644 --- a/notification/twilio/alertsms.go +++ b/notification/twilio/alertsms.go @@ -146,6 +146,21 @@ func (a alertSMS) Render(maxLen int) (string, error) { } if buf.Len() > maxLen { + // message too long, trim summary (until empty if needed) + newSumLen := len(a.Summary) - (buf.Len() - maxLen) + if newSumLen <= 0 { + a.Summary = "" + } + a.Summary = strings.TrimSpace(a.Summary[:newSumLen]) + buf.Reset() + err = tmpl.Execute(&buf, a) + if err != nil { + return "", err + } + } + + if buf.Len() > maxLen { + // trim body of message if message still too long newBodyLen := len(a.Body) - (buf.Len() - maxLen) if newBodyLen <= 0 { return "", errors.New("message too long to include body") From df926a7938cc873dd42c105db40850bd92e011a4 Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Wed, 19 Jan 2022 10:35:07 -0800 Subject: [PATCH 07/14] omit the colon and space if summary is empty --- notification/twilio/alertsms.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notification/twilio/alertsms.go b/notification/twilio/alertsms.go index b1487e036d..9320c48da9 100644 --- a/notification/twilio/alertsms.go +++ b/notification/twilio/alertsms.go @@ -51,7 +51,7 @@ var bundleTempl = template.Must(template.New("alertBundleSMS").Parse(`Svc '{{.Bo Reply '{{.Code}}aa' to ack all, '{{.Code}}cc' to close all.{{end}}`)) // statusTempl uses the ID, Summary, and Body to render a message -var statusTempl = template.Must(template.New("alertStatusSMS").Parse(`Alert #{{.ID}}: {{.Summary}} +var statusTempl = template.Must(template.New("alertStatusSMS").Parse(`Alert #{{.ID}}{{-if .Summary }}: {{.Summary}}{{end}} {{.Body}}`)) From b281c2b7c75b699eb4041b74ecc6719fc1127a7d Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Wed, 26 Jan 2022 09:33:26 -0800 Subject: [PATCH 08/14] update template rendering logic --- notification/twilio/alertsms.go | 166 +++++++++++++++++++++----------- notification/twilio/sms.go | 23 +---- 2 files changed, 114 insertions(+), 75 deletions(-) diff --git a/notification/twilio/alertsms.go b/notification/twilio/alertsms.go index 9320c48da9..68ef979eff 100644 --- a/notification/twilio/alertsms.go +++ b/notification/twilio/alertsms.go @@ -3,11 +3,11 @@ package twilio import ( "bytes" "context" + "errors" "strings" "text/template" "unicode" - "github.com/pkg/errors" "github.com/target/goalert/config" "github.com/target/goalert/notification" ) @@ -20,18 +20,7 @@ import ( // then be 70 or 67 characters for single or multi-segmented messages, respectively. const maxGSMLen = 160 -type alertSMS struct { - ID int - Count int - Body string - Summary string - Link string - Code int - Type notification.MessageType -} - -// alertTempl uses the ID, Summary, Link, and Code to render a message -var alertTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: {{.Summary}} +var alertTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.AlertID}}: {{.Summary}} {{- if .Link }} {{.Link}} @@ -40,8 +29,7 @@ var alertTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.ID}}: { {{- if .Code}} Reply '{{.Code}}a' to ack, '{{.Code}}c' to close.{{end}}`)) -// bundleTempl uses the Count, Body, Link, and Code to render a message -var bundleTempl = template.Must(template.New("alertBundleSMS").Parse(`Svc '{{.Body}}': {{.Count}} unacked alert{{if gt .Count 1}}s{{end}} +var bundleTempl = template.Must(template.New("alertBundleSMS").Parse(`Svc '{{.ServiceName}}': {{.Count}} unacked alert{{if gt .Count 1}}s{{end}} {{- if .Link }} {{.Link}} @@ -50,10 +38,9 @@ var bundleTempl = template.Must(template.New("alertBundleSMS").Parse(`Svc '{{.Bo {{- if .Code}} Reply '{{.Code}}aa' to ack all, '{{.Code}}cc' to close all.{{end}}`)) -// statusTempl uses the ID, Summary, and Body to render a message -var statusTempl = template.Must(template.New("alertStatusSMS").Parse(`Alert #{{.ID}}{{-if .Summary }}: {{.Summary}}{{end}} +var statusTempl = template.Must(template.New("alertStatusSMS").Parse(`Alert #{{.AlertID}}{{-if .Summary }}: {{.Summary}}{{end}} -{{.Body}}`)) +{{.LogEntry}}`)) const gsmAlphabet = "@∆ 0¡P¿p£!1AQaq$Φ\"2BRbr¥Γ#3CScsèΛ¤4DTdtéΩ%5EUeuùΠ&6FVfvìΨ'7GWgwòΣ(8HXhxÇΘ)9IYiy\n Ξ *:JZjzØ+;KÄkäøÆ,NÜnüåÉ/?O§oà" @@ -113,65 +100,134 @@ func hasTwoWaySMSSupport(ctx context.Context, number string) bool { return !strings.HasPrefix(number, "+91") } -// Render will render a single-segment SMS. -// -// Non-GSM characters will be replaced with '?' and Body will be -// truncated (if needed) until the output is <= maxLen characters. -func (a alertSMS) Render(maxLen int) (string, error) { - a.Body = strings.Map(mapGSM, a.Body) - a.Body = strings.Replace(a.Body, " ", " ", -1) - a.Body = strings.TrimSpace(a.Body) +func normalizeGSM(str string) (s string) { + s = strings.Map(mapGSM, str) + s = strings.Replace(s, " ", " ", -1) + s = strings.TrimSpace(s) + return s +} + +func trimString(str *string, buf *bytes.Buffer, maxLen int) bool { + if buf.Len() <= maxLen { + return false + } + + newSumLen := len(*str) - (buf.Len() - maxLen) + if newSumLen <= 0 { + *str = "" + } + *str = strings.TrimSpace((*str)[:newSumLen]) + buf.Reset() - a.Summary = strings.Map(mapGSM, a.Summary) - a.Summary = strings.Replace(a.Summary, " ", " ", -1) - a.Summary = strings.TrimSpace(a.Summary) + return true +} +// renderAlertMessage will render a single-segment SMS for an Alert. +// +// Non-GSM characters will be replaced with '?' and fields will be +// truncated (if needed) until the output is <= maxLen characters. +func renderAlertMessage(maxLen int, a notification.Alert, link string, code int) (string, error) { var buf bytes.Buffer + a.Summary = normalizeGSM(a.Summary) - var tmpl template.Template - switch a.Type { - case notification.MessageTypeAlertStatus: - tmpl = *statusTempl - case notification.MessageTypeAlertBundle: - tmpl = *bundleTempl - case notification.MessageTypeAlert: - tmpl = *alertTempl - default: - return "", errors.Errorf("unsupported message type: %v", a.Type) + var data struct { + notification.Alert + Link string + Code int } + data.Alert = a + data.Link = link + data.Code = code - err := tmpl.Execute(&buf, a) + err := alertTempl.Execute(&buf, data) if err != nil { return "", err } + if trimString(&a.Summary, &buf, maxLen) { + err = alertTempl.Execute(&buf, data) + if err != nil { + return "", err + } + } + + // should maybe revisit templates if this starts occurring if buf.Len() > maxLen { - // message too long, trim summary (until empty if needed) - newSumLen := len(a.Summary) - (buf.Len() - maxLen) - if newSumLen <= 0 { - a.Summary = "" + return "", errors.New("message too long") + } + + return buf.String(), nil +} + +// renderAlertStatusMsg will render a single-segment SMS for an Alert Status. +// +// Non-GSM characters will be replaced with '?' and fields will be +// truncated (if needed) until the output is <= maxLen characters. +func renderAlertStatusMsg(maxLen int, a notification.AlertStatus) (string, error) { + var buf bytes.Buffer + a.Summary = normalizeGSM(a.Summary) + a.LogEntry = normalizeGSM(a.LogEntry) + + err := alertTempl.Execute(&buf, a) + if err != nil { + return "", err + } + + if trimString(&a.Summary, &buf, maxLen) { + err = alertTempl.Execute(&buf, a) + if err != nil { + return "", err } - a.Summary = strings.TrimSpace(a.Summary[:newSumLen]) - buf.Reset() - err = tmpl.Execute(&buf, a) + } + + if trimString(&a.LogEntry, &buf, maxLen) { + err = alertTempl.Execute(&buf, a) if err != nil { return "", err } } + // should maybe revisit templates if this starts occurring if buf.Len() > maxLen { - // trim body of message if message still too long - newBodyLen := len(a.Body) - (buf.Len() - maxLen) - if newBodyLen <= 0 { - return "", errors.New("message too long to include body") - } - a.Body = strings.TrimSpace(a.Body[:newBodyLen]) - buf.Reset() - err = tmpl.Execute(&buf, a) + return "", errors.New("message too long") + } + + return buf.String(), nil +} + +// renderAlertBundleMsg will render a single-segment SMS for an Alert Bundle. +// +// Non-GSM characters will be replaced with '?' and fields will be +// truncated (if needed) until the output is <= maxLen characters. +func renderAlertBundleMsg(maxLen int, a notification.AlertBundle, link string, code int) (string, error) { + var buf bytes.Buffer + a.ServiceName = normalizeGSM(a.ServiceName) + + var data struct { + notification.AlertBundle + Link string + Code int + } + data.AlertBundle = a + data.Link = link + data.Code = code + + err := alertTempl.Execute(&buf, data) + if err != nil { + return "", err + } + + if trimString(&a.ServiceName, &buf, maxLen) { + err = alertTempl.Execute(&buf, data) if err != nil { return "", err } } + // should maybe revisit templates if this starts occurring + if buf.Len() > maxLen { + return "", errors.New("message too long") + } + return buf.String(), nil } diff --git a/notification/twilio/sms.go b/notification/twilio/sms.go index a48351886d..d4b4834860 100644 --- a/notification/twilio/sms.go +++ b/notification/twilio/sms.go @@ -113,38 +113,21 @@ func (s *SMS) Send(ctx context.Context, msg notification.Message) (*notification var err error switch t := msg.(type) { case notification.AlertStatus: - message, err = alertSMS{ - ID: t.AlertID, - Summary: t.Summary, - Body: t.LogEntry, - Type: notification.MessageTypeAlertStatus, - }.Render(maxLen) + message, err = renderAlertStatusMsg(maxLen, t) case notification.AlertBundle: var link string if !cfg.General.DisableSMSLinks { link = cfg.CallbackURL(fmt.Sprintf("/services/%s/alerts", t.ServiceID)) } - message, err = alertSMS{ - Count: t.Count, - Body: t.ServiceName, - Link: link, - Code: makeSMSCode(0, t.ServiceID), - Type: notification.MessageTypeAlertBundle, - }.Render(maxLen) + message, err = renderAlertBundleMsg(maxLen, t, link, makeSMSCode(0, t.ServiceID)) case notification.Alert: var link string if !cfg.General.DisableSMSLinks { link = cfg.CallbackURL(fmt.Sprintf("/alerts/%d", t.AlertID)) } - message, err = alertSMS{ - ID: t.AlertID, - Summary: t.Summary, - Link: link, - Code: makeSMSCode(t.AlertID, ""), - Type: notification.MessageTypeAlert, - }.Render(maxLen) + message, err = renderAlertMessage(maxLen, t, link, makeSMSCode(t.AlertID, "")) case notification.Test: message = "Test message." case notification.Verification: From 2bb252d34cd3a8d17405c48bc459bb224c97036e Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Wed, 26 Jan 2022 12:36:00 -0800 Subject: [PATCH 09/14] clean up alert sms template logic and tests --- notification/twilio/alertsms.go | 28 +++-- notification/twilio/alertsms_test.go | 174 +++++++++++++++------------ notification/twilio/sms.go | 4 +- 3 files changed, 112 insertions(+), 94 deletions(-) diff --git a/notification/twilio/alertsms.go b/notification/twilio/alertsms.go index 68ef979eff..198327c13e 100644 --- a/notification/twilio/alertsms.go +++ b/notification/twilio/alertsms.go @@ -23,6 +23,7 @@ const maxGSMLen = 160 var alertTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.AlertID}}: {{.Summary}} {{- if .Link }} + {{.Link}} {{end}} @@ -32,15 +33,16 @@ Reply '{{.Code}}a' to ack, '{{.Code}}c' to close.{{end}}`)) var bundleTempl = template.Must(template.New("alertBundleSMS").Parse(`Svc '{{.ServiceName}}': {{.Count}} unacked alert{{if gt .Count 1}}s{{end}} {{- if .Link }} -{{.Link}} + + {{.Link}} {{end}} {{- if .Code}} -Reply '{{.Code}}aa' to ack all, '{{.Code}}cc' to close all.{{end}}`)) + Reply '{{.Code}}aa' to ack all, '{{.Code}}cc' to close all.{{end}}`)) -var statusTempl = template.Must(template.New("alertStatusSMS").Parse(`Alert #{{.AlertID}}{{-if .Summary }}: {{.Summary}}{{end}} +var statusTempl = template.Must(template.New("alertStatusSMS").Parse(`Alert #{{.AlertID}}{{- if .Summary }}: {{.Summary}}{{end}} -{{.LogEntry}}`)) + {{.LogEntry}}`)) const gsmAlphabet = "@∆ 0¡P¿p£!1AQaq$Φ\"2BRbr¥Γ#3CScsèΛ¤4DTdtéΩ%5EUeuùΠ&6FVfvìΨ'7GWgwòΣ(8HXhxÇΘ)9IYiy\n Ξ *:JZjzØ+;KÄkäøÆ,NÜnüåÉ/?O§oà" @@ -159,29 +161,29 @@ func renderAlertMessage(maxLen int, a notification.Alert, link string, code int) return buf.String(), nil } -// renderAlertStatusMsg will render a single-segment SMS for an Alert Status. +// renderAlertStatusMessage will render a single-segment SMS for an Alert Status. // // Non-GSM characters will be replaced with '?' and fields will be // truncated (if needed) until the output is <= maxLen characters. -func renderAlertStatusMsg(maxLen int, a notification.AlertStatus) (string, error) { +func renderAlertStatusMessage(maxLen int, a notification.AlertStatus) (string, error) { var buf bytes.Buffer a.Summary = normalizeGSM(a.Summary) a.LogEntry = normalizeGSM(a.LogEntry) - err := alertTempl.Execute(&buf, a) + err := statusTempl.Execute(&buf, a) if err != nil { return "", err } if trimString(&a.Summary, &buf, maxLen) { - err = alertTempl.Execute(&buf, a) + err = statusTempl.Execute(&buf, a) if err != nil { return "", err } } if trimString(&a.LogEntry, &buf, maxLen) { - err = alertTempl.Execute(&buf, a) + err = statusTempl.Execute(&buf, a) if err != nil { return "", err } @@ -195,11 +197,11 @@ func renderAlertStatusMsg(maxLen int, a notification.AlertStatus) (string, error return buf.String(), nil } -// renderAlertBundleMsg will render a single-segment SMS for an Alert Bundle. +// renderAlertBundleMessage will render a single-segment SMS for an Alert Bundle. // // Non-GSM characters will be replaced with '?' and fields will be // truncated (if needed) until the output is <= maxLen characters. -func renderAlertBundleMsg(maxLen int, a notification.AlertBundle, link string, code int) (string, error) { +func renderAlertBundleMessage(maxLen int, a notification.AlertBundle, link string, code int) (string, error) { var buf bytes.Buffer a.ServiceName = normalizeGSM(a.ServiceName) @@ -212,13 +214,13 @@ func renderAlertBundleMsg(maxLen int, a notification.AlertBundle, link string, c data.Link = link data.Code = code - err := alertTempl.Execute(&buf, data) + err := bundleTempl.Execute(&buf, data) if err != nil { return "", err } if trimString(&a.ServiceName, &buf, maxLen) { - err = alertTempl.Execute(&buf, data) + err = bundleTempl.Execute(&buf, data) if err != nil { return "", err } diff --git a/notification/twilio/alertsms_test.go b/notification/twilio/alertsms_test.go index 956cd7a4b9..a0d5292fd8 100644 --- a/notification/twilio/alertsms_test.go +++ b/notification/twilio/alertsms_test.go @@ -8,6 +8,24 @@ import ( "github.com/target/goalert/notification" ) +func resultCheck(t *testing.T, expected string, res string, err error) { + if len(res) > 160 { + t.Errorf("message exceeded 160 characters") + } else { + t.Log("Length", len(res)) + } + if err != nil && expected != "" { + t.Fatalf("got err %v; want nil", err) + } else if err == nil && expected == "" { + t.Log(res) + t.Fatal("got nil; want err") + } + + if res != expected { + t.Errorf("got %s; want %s", strconv.Quote(res), strconv.Quote(expected)) + } +} + func TestMapGSM(t *testing.T) { check := func(input, exp string) { t.Run("", func(t *testing.T) { @@ -25,36 +43,21 @@ func TestMapGSM(t *testing.T) { check("[Testing] {alert_message: `okay`}", "(Testing) (alert-message: 'okay')") } -func TestAlertSMS_Render(t *testing.T) { - check := func(name string, a alertSMS, exp string) { +func TestSMS_RenderAlert(t *testing.T) { + check := func(name string, a notification.Alert, link string, code int, exp string) { t.Run(name, func(t *testing.T) { - res, err := a.Render(maxGSMLen) - if len(res) > 160 { - t.Errorf("message exceeded 160 characters") - } else { - t.Log("Length", len(res)) - } - if err != nil && exp != "" { - t.Fatalf("got err %v; want nil", err) - } else if err == nil && exp == "" { - t.Log(res) - t.Fatal("got nil; want err") - } - - if res != exp { - t.Errorf("got %s; want %s", strconv.Quote(res), strconv.Quote(exp)) - } + res, err := renderAlertMessage(maxGSMLen, a, link, code) + resultCheck(t, exp, res, err) }) } check("normal", - alertSMS{ - ID: 123, - Code: 1, - Link: "https://example.com/alerts/123", + notification.Alert{ + AlertID: 123, Summary: "Testing", - Type: notification.MessageTypeAlert, }, + "https://example.com/alerts/123", + 1, `Alert #123: Testing https://example.com/alerts/123 @@ -62,47 +65,47 @@ https://example.com/alerts/123 Reply '1a' to ack, '1c' to close.`, ) - check("no-reply", - alertSMS{ - ID: 123, - Link: "https://example.com/alerts/123", + check("no-reply-code", + notification.Alert{ + AlertID: 123, Summary: "Testing", - Type: notification.MessageTypeAlert, }, + "https://example.com/alerts/123", + 0, `Alert #123: Testing https://example.com/alerts/123`, ) - check("no-Link", - alertSMS{ - ID: 123, - Code: 1, + check("no-link", + notification.Alert{ + AlertID: 123, Summary: "Testing", - Type: notification.MessageTypeAlert, }, + "", + 1, `Alert #123: Testing Reply '1a' to ack, '1c' to close.`, ) - check("no-reply-Link", - alertSMS{ - ID: 123, - Body: "Testing", - Type: notification.MessageTypeAlert, + check("no-link-or-reply-code", + notification.Alert{ + AlertID: 123, + Summary: "Testing", }, + "", + 0, `Alert #123: Testing`, ) check("truncate", - alertSMS{ - ID: 123, - Code: 1, - Link: "https://example.com/alerts/123", - Body: "Testing with a really really obnoxiously long message that will be need to be truncated at some point.", - Type: notification.MessageTypeAlert, + notification.Alert{ + AlertID: 123, + Summary: "Testing with a really really obnoxiously long message that will be need to be truncated at some point.", }, + "https://example.com/alerts/123", + 1, `Alert #123: Testing with a really really obnoxiously long message that will be need to be tru https://example.com/alerts/123 @@ -111,13 +114,12 @@ Reply '1a' to ack, '1c' to close.`, ) check("truncate-long-id", - alertSMS{ - ID: 123456789, - Code: 1, - Link: "https://example.com/alerts/123", - Body: "Testing with a really really obnoxiously long message that will be need to be truncated at some point.", - Type: notification.MessageTypeAlert, + notification.Alert{ + AlertID: 123456789, + Summary: "Testing with a really really obnoxiously long message that will be need to be truncated at some point.", }, + "https://example.com/alerts/123", + 1, `Alert #123456789: Testing with a really really obnoxiously long message that will be need to https://example.com/alerts/123 @@ -126,56 +128,70 @@ Reply '1a' to ack, '1c' to close.`, ) check("message-too-long", - // can't fit body - alertSMS{ - ID: 123456789, - Code: 123456789, - Link: "https://example.com/alerts/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff", - Body: "Testing with a really really obnoxiously long message that will be need to be truncated at some point.", - Type: notification.MessageTypeAlert, + // can't fit summary + notification.Alert{ + AlertID: 123456789, + Summary: "Testing with a really really obnoxiously long message that will be need to be truncated at some point.", }, + "https://example.com/alerts/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff/123ff", + 123456789, "", ) +} + +func TestSMS_RenderAlertBundle(t *testing.T) { + check := func(name string, a notification.AlertBundle, link string, code int, exp string) { + t.Run(name, func(t *testing.T) { + res, err := renderAlertBundleMessage(maxGSMLen, a, link, code) + resultCheck(t, exp, res, err) + }) + } check("alert-bundle-one", - alertSMS{ - Count: 1, - Body: "My Service", - Code: 100, - Link: "https://example.com/services/321-654/alerts", - Type: notification.MessageTypeAlertBundle, + notification.AlertBundle{ + Count: 1, + ServiceName: "My Service", }, + "https://example.com/services/321-654/alerts", + 100, `Svc 'My Service': 1 unacked alert -https://example.com/services/321-654/alerts + https://example.com/services/321-654/alerts -Reply '100aa' to ack all, '100cc' to close all.`, + Reply '100aa' to ack all, '100cc' to close all.`, ) check("alert-bundle", - alertSMS{ - Count: 5, - Body: "My Service", - Code: 100, - Link: "https://example.com/services/321-654/alerts", - Type: notification.MessageTypeAlertBundle, + notification.AlertBundle{ + Count: 5, + ServiceName: "My Service", }, + "https://example.com/services/321-654/alerts", + 100, `Svc 'My Service': 5 unacked alerts -https://example.com/services/321-654/alerts + https://example.com/services/321-654/alerts -Reply '100aa' to ack all, '100cc' to close all.`, + Reply '100aa' to ack all, '100cc' to close all.`, ) +} + +func TestSMS_RenderAlertStatus(t *testing.T) { + check := func(name string, a notification.AlertStatus, exp string) { + t.Run(name, func(t *testing.T) { + res, err := renderAlertStatusMessage(maxGSMLen, a) + resultCheck(t, exp, res, err) + }) + } check("alert-status", - alertSMS{ - ID: 123, - Summary: "Testing", - Body: "Some log entry", - Type: notification.MessageTypeAlertStatus, + notification.AlertStatus{ + AlertID: 123, + Summary: "Testing", + LogEntry: "Some log entry", }, `Alert #123: Testing -Some log entry`, + Some log entry`, ) } diff --git a/notification/twilio/sms.go b/notification/twilio/sms.go index d4b4834860..60ceb7674b 100644 --- a/notification/twilio/sms.go +++ b/notification/twilio/sms.go @@ -113,14 +113,14 @@ func (s *SMS) Send(ctx context.Context, msg notification.Message) (*notification var err error switch t := msg.(type) { case notification.AlertStatus: - message, err = renderAlertStatusMsg(maxLen, t) + message, err = renderAlertStatusMessage(maxLen, t) case notification.AlertBundle: var link string if !cfg.General.DisableSMSLinks { link = cfg.CallbackURL(fmt.Sprintf("/services/%s/alerts", t.ServiceID)) } - message, err = renderAlertBundleMsg(maxLen, t, link, makeSMSCode(0, t.ServiceID)) + message, err = renderAlertBundleMessage(maxLen, t, link, makeSMSCode(0, t.ServiceID)) case notification.Alert: var link string if !cfg.General.DisableSMSLinks { From bc67dd430791fe19096283058f1d6509a59e27f4 Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Mon, 31 Jan 2022 11:38:00 -0600 Subject: [PATCH 10/14] fix panic on negative value --- notification/twilio/alertsms.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/notification/twilio/alertsms.go b/notification/twilio/alertsms.go index 198327c13e..9001535776 100644 --- a/notification/twilio/alertsms.go +++ b/notification/twilio/alertsms.go @@ -109,16 +109,19 @@ func normalizeGSM(str string) (s string) { return s } +// trimString will trim the string by the difference between the maxLen and the +// buffer length. If the string is trimmed, it returns true and the buffer is reset. func trimString(str *string, buf *bytes.Buffer, maxLen int) bool { if buf.Len() <= maxLen { return false } - newSumLen := len(*str) - (buf.Len() - maxLen) - if newSumLen <= 0 { + newLen := len(*str) - (buf.Len() - maxLen) + if newLen <= 0 { *str = "" + } else { + *str = strings.TrimSpace((*str)[:newLen]) } - *str = strings.TrimSpace((*str)[:newSumLen]) buf.Reset() return true From 4267ea99d697badc26a8d4fa8d99994e59076cee Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Mon, 7 Feb 2022 10:34:57 -0800 Subject: [PATCH 11/14] fix tests --- notification/twilio/alertsms.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/notification/twilio/alertsms.go b/notification/twilio/alertsms.go index 9001535776..24b32b84ff 100644 --- a/notification/twilio/alertsms.go +++ b/notification/twilio/alertsms.go @@ -21,13 +21,11 @@ import ( const maxGSMLen = 160 var alertTempl = template.Must(template.New("alertSMS").Parse(`Alert #{{.AlertID}}: {{.Summary}} - {{- if .Link }} -{{.Link}} -{{end}} - +{{.Link}}{{end}} {{- if .Code}} + Reply '{{.Code}}a' to ack, '{{.Code}}c' to close.{{end}}`)) var bundleTempl = template.Must(template.New("alertBundleSMS").Parse(`Svc '{{.ServiceName}}': {{.Count}} unacked alert{{if gt .Count 1}}s{{end}} @@ -36,7 +34,6 @@ var bundleTempl = template.Must(template.New("alertBundleSMS").Parse(`Svc '{{.Se {{.Link}} {{end}} - {{- if .Code}} Reply '{{.Code}}aa' to ack all, '{{.Code}}cc' to close all.{{end}}`)) @@ -149,7 +146,7 @@ func renderAlertMessage(maxLen int, a notification.Alert, link string, code int) return "", err } - if trimString(&a.Summary, &buf, maxLen) { + if trimString(&data.Alert.Summary, &buf, maxLen) { err = alertTempl.Execute(&buf, data) if err != nil { return "", err @@ -222,7 +219,7 @@ func renderAlertBundleMessage(maxLen int, a notification.AlertBundle, link strin return "", err } - if trimString(&a.ServiceName, &buf, maxLen) { + if trimString(&data.AlertBundle.ServiceName, &buf, maxLen) { err = bundleTempl.Execute(&buf, data) if err != nil { return "", err From 76e1b6140606427eb3a8598be9d1f96d17109372 Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Mon, 7 Feb 2022 11:40:48 -0800 Subject: [PATCH 12/14] make generate --- go.mod | 66 ---------------------------------------------------------- 1 file changed, 66 deletions(-) diff --git a/go.mod b/go.mod index 21726eb06e..86b65b7fb2 100644 --- a/go.mod +++ b/go.mod @@ -25,14 +25,12 @@ require ( github.com/google/uuid v1.3.0 github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 github.com/gorilla/pat v1.0.1 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // pinned version - see https://github.com/target/goalert/issues/1239 github.com/ian-kent/envconf v0.0.0-20141026121121-c19809918c02 // indirect github.com/ian-kent/go-log v0.0.0-20160113211217-5731446c36ab // indirect github.com/ian-kent/goose v0.0.0-20141221090059-c3541ea826ad // indirect github.com/ian-kent/linkio v0.0.0-20170807205755-97566b872887 // indirect github.com/jackc/pgconn v1.10.1 - github.com/jackc/pgproto3/v2 v2.2.0 // indirect github.com/jackc/pgtype v1.9.1 github.com/jackc/pgx/v4 v4.14.1 github.com/jmespath/go-jmespath v0.4.0 @@ -47,7 +45,6 @@ require ( github.com/mailhog/storage v1.0.1 github.com/matcornic/hermes/v2 v2.1.0 github.com/mattn/go-colorable v0.1.12 - github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/ogier/pflag v0.0.1 // indirect github.com/pelletier/go-toml v1.9.4 github.com/pkg/errors v0.9.1 @@ -55,13 +52,10 @@ require ( github.com/prometheus/client_golang v1.11.0 github.com/rivo/uniseg v0.2.0 // indirect github.com/rubenv/sql-migrate v1.0.0 - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.8.1 github.com/slack-go/slack v0.10.1 github.com/spf13/afero v1.7.0 // indirect - github.com/spf13/cast v1.4.1 // indirect github.com/spf13/cobra v1.3.0 - github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.10.1 github.com/stretchr/testify v1.7.0 github.com/t-k/fluent-logger-golang v1.0.0 // indirect @@ -84,10 +78,8 @@ require ( google.golang.org/protobuf v1.27.1 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df - gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect honnef.co/go/tools v0.2.2 ) @@ -97,68 +89,10 @@ require ( ) require ( - cloud.google.com/go/monitoring v1.1.0 // indirect cloud.google.com/go/storage v1.18.2 // indirect - cloud.google.com/go/trace v1.0.0 // indirect - github.com/BurntSushi/toml v0.3.1 // indirect - github.com/Masterminds/goutils v1.1.0 // indirect - github.com/Masterminds/semver v1.5.0 // indirect - github.com/Masterminds/sprig v2.22.0+incompatible // indirect - github.com/PuerkitoBio/goquery v1.5.0 // indirect - github.com/VividCortex/ewma v1.1.1 // indirect - github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect - github.com/andybalholm/cascadia v1.0.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect github.com/cncf/xds/go v0.0.0-20211216145620-d92e9ce0af51 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect - github.com/envoyproxy/go-control-plane v0.10.1 // indirect - github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect - github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.6 // indirect - github.com/googleapis/gax-go/v2 v2.1.1 // indirect github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720 // indirect github.com/gorilla/context v1.1.1 // indirect - github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/mux v1.7.3 // indirect - github.com/gorilla/websocket v1.4.2 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/huandu/xstrings v1.3.2 // indirect - github.com/imdario/mergo v0.3.11 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgio v1.0.0 // indirect - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect - github.com/jackc/puddle v1.2.0 // indirect - github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 // indirect - github.com/magiconair/properties v1.8.5 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mattn/go-runewidth v0.0.12 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/mitchellh/copystructure v1.0.0 // indirect - github.com/mitchellh/reflectwalk v1.0.0 // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/philhofer/fwd v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.26.0 // indirect - github.com/prometheus/procfs v0.6.0 // indirect github.com/smartystreets/goconvey v1.7.2 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect - github.com/subosito/gotenv v1.2.0 // indirect - github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect - github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 // indirect - github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe // indirect - golang.org/x/mod v0.5.1 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/api v0.63.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect ) From 6a0cbba444c0e7fd8ae6d5525c56decff43f4895 Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Mon, 7 Feb 2022 11:45:26 -0800 Subject: [PATCH 13/14] make generate --- go.mod | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.mod b/go.mod index 229d994037..3e1f171a6b 100644 --- a/go.mod +++ b/go.mod @@ -44,8 +44,6 @@ require ( github.com/mailhog/smtp v1.0.1 // indirect github.com/mailhog/storage v1.0.1 github.com/matcornic/hermes/v2 v2.1.0 - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/ogier/pflag v0.0.1 // indirect github.com/pelletier/go-toml v1.9.4 github.com/pkg/errors v0.9.1 From 638b17dd6056d13c120197f1bd0292f39908f1be Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Tue, 8 Feb 2022 08:21:20 -0800 Subject: [PATCH 14/14] go mod tidy --- go.mod | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/go.mod b/go.mod index 3e1f171a6b..a418b51ea2 100644 --- a/go.mod +++ b/go.mod @@ -88,10 +88,77 @@ require ( ) require ( + cloud.google.com/go/monitoring v1.1.0 // indirect cloud.google.com/go/storage v1.18.2 // indirect + cloud.google.com/go/trace v1.0.0 // indirect + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/Masterminds/goutils v1.1.0 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/Masterminds/sprig v2.22.0+incompatible // indirect + github.com/PuerkitoBio/goquery v1.5.0 // indirect + github.com/VividCortex/ewma v1.1.1 // indirect + github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect + github.com/andybalholm/cascadia v1.0.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect github.com/cncf/xds/go v0.0.0-20211216145620-d92e9ce0af51 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect + github.com/envoyproxy/go-control-plane v0.10.1 // indirect + github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect + github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.6 // indirect + github.com/googleapis/gax-go/v2 v2.1.1 // indirect github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720 // indirect github.com/gorilla/context v1.1.1 // indirect + github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/mux v1.7.3 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/huandu/xstrings v1.3.2 // indirect + github.com/imdario/mergo v0.3.11 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.2.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/puddle v1.2.0 // indirect + github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 // indirect + github.com/magiconair/properties v1.8.5 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.12 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/mitchellh/reflectwalk v1.0.0 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/philhofer/fwd v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.26.0 // indirect + github.com/prometheus/procfs v0.6.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/smartystreets/goconvey v1.7.2 // indirect + github.com/spf13/cast v1.4.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect + github.com/subosito/gotenv v1.2.0 // indirect + github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect + github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 // indirect + github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe // indirect + golang.org/x/mod v0.5.1 // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/api v0.63.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect )