Skip to content

Commit 2eb1c50

Browse files
desa121watts
andcommitted
feat(monitoring): add ability to test notification endpoint
feat(notEnd): add definition to swagger feat(wip): add endpoint test route feat(ui): add test endpoint button feat(wip): execute endpoint test query feat: implement test endpoint for slack feat: introduce http endopint test feat: implement http test endpoint with auth feat: add test PagerDuty endpoint fix: PagerDuty test query generation wip refactor: implement test endpoint for existing endpoints feat(endpoint-test): add error reporting feat(test-endpoint): hide testing on create fix: errors not caught test: remove only test: remove check for test button fix: thunk typings test: add testChannel to slack test: fix go and e2e fix: test and linting test: fix comment test test: fix test fix(notification/endpoint): add test channel to slack endpoint Co-Authored-By: Andrew Watkins <[email protected]>
1 parent 380b411 commit 2eb1c50

28 files changed

+1224
-60
lines changed

authorizer/notification_endpoint.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func (s *NotificationEndpointService) PatchNotificationEndpoint(ctx context.Cont
122122
}
123123

124124
// DeleteNotificationEndpoint checks to see if the authorizer on context has write access to the notification endpoint provided.
125-
func (s *NotificationEndpointService) DeleteNotificationEndpoint(ctx context.Context, id influxdb.ID) ([]influxdb.SecretField, influxdb.ID, error) {
125+
func (s *NotificationEndpointService) DeleteNotificationEndpoint(ctx context.Context, id influxdb.ID) ([]*influxdb.SecretField, influxdb.ID, error) {
126126
edp, err := s.FindNotificationEndpointByID(ctx, id)
127127
if err != nil {
128128
return nil, 0, err

authorizer/notification_endpoint_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ func TestNotificationEndpointService_DeleteNotificationEndpoint(t *testing.T) {
543543
},
544544
}, nil
545545
},
546-
DeleteNotificationEndpointF: func(ctx context.Context, id influxdb.ID) ([]influxdb.SecretField, influxdb.ID, error) {
546+
DeleteNotificationEndpointF: func(ctx context.Context, id influxdb.ID) ([]*influxdb.SecretField, influxdb.ID, error) {
547547
return nil, 0, nil
548548
},
549549
},
@@ -583,7 +583,7 @@ func TestNotificationEndpointService_DeleteNotificationEndpoint(t *testing.T) {
583583
},
584584
}, nil
585585
},
586-
DeleteNotificationEndpointF: func(ctx context.Context, id influxdb.ID) ([]influxdb.SecretField, influxdb.ID, error) {
586+
DeleteNotificationEndpointF: func(ctx context.Context, id influxdb.ID) ([]*influxdb.SecretField, influxdb.ID, error) {
587587
return nil, 0, nil
588588
},
589589
},

errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
// Any time this set of constants changes, you must also update the swagger for Error.properties.code.enum.
1515
const (
1616
EInternal = "internal error"
17+
EBadRequest = "bad request"
1718
ENotFound = "not found"
1819
EConflict = "conflict" // action cannot be performed
1920
EInvalid = "invalid" // validation failed

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ github.com/jsternberg/zap-logfmt v1.2.0 h1:1v+PK4/B48cy8cfQbxL4FmmNZrjnIMr2BsnyE
271271
github.com/jsternberg/zap-logfmt v1.2.0/go.mod h1:kz+1CUmCutPWABnNkOu9hOHKdT2q3TDYCcsFy9hpqb0=
272272
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
273273
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
274+
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
274275
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
275276
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef h1:2jNeR4YUziVtswNP9sEFAI913cVrzH85T+8Q6LpYbT0=
276277
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=

http/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ func InactiveUserError(ctx context.Context, h platform.HTTPErrorHandler, w http.
148148
var statusCodePlatformError = map[string]int{
149149
platform.EInternal: http.StatusInternalServerError,
150150
platform.EInvalid: http.StatusBadRequest,
151+
platform.EBadRequest: http.StatusBadRequest,
151152
platform.EUnprocessableEntity: http.StatusUnprocessableEntity,
152153
platform.EEmptyValue: http.StatusBadRequest,
153154
platform.EConflict: http.StatusUnprocessableEntity,

http/notification_endpoint.go

Lines changed: 141 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@ import (
66
"encoding/json"
77
"fmt"
88
"net/http"
9+
"time"
910

11+
"github.com/influxdata/flux"
12+
"github.com/influxdata/flux/csv"
13+
"github.com/influxdata/flux/lang"
1014
"github.com/influxdata/httprouter"
1115
"github.com/influxdata/influxdb"
1216
pctx "github.com/influxdata/influxdb/context"
1317
"github.com/influxdata/influxdb/notification/endpoint"
18+
"github.com/influxdata/influxdb/query"
1419
"go.uber.org/zap"
1520
)
1621

@@ -26,6 +31,7 @@ type NotificationEndpointBackend struct {
2631
UserService influxdb.UserService
2732
OrganizationService influxdb.OrganizationService
2833
SecretService influxdb.SecretService
34+
QueryService query.ProxyQueryService
2935
}
3036

3137
// NewNotificationEndpointBackend returns a new instance of NotificationEndpointBackend.
@@ -40,6 +46,7 @@ func NewNotificationEndpointBackend(b *APIBackend) *NotificationEndpointBackend
4046
UserService: b.UserService,
4147
OrganizationService: b.OrganizationService,
4248
SecretService: b.SecretService,
49+
QueryService: b.FluxService,
4350
}
4451
}
4552

@@ -55,11 +62,14 @@ type NotificationEndpointHandler struct {
5562
UserService influxdb.UserService
5663
OrganizationService influxdb.OrganizationService
5764
SecretService influxdb.SecretService
65+
QueryService query.ProxyQueryService
5866
}
5967

6068
const (
6169
notificationEndpointsPath = "/api/v2/notificationEndpoints"
6270
notificationEndpointsIDPath = "/api/v2/notificationEndpoints/:id"
71+
notificationEndpointsIDTestPath = "/api/v2/notificationEndpoints/:id/test"
72+
notificationEndpointsTestPath = "/api/v2/notificationEndpointsTest"
6373
notificationEndpointsIDMembersPath = "/api/v2/notificationEndpoints/:id/members"
6474
notificationEndpointsIDMembersIDPath = "/api/v2/notificationEndpoints/:id/members/:userID"
6575
notificationEndpointsIDOwnersPath = "/api/v2/notificationEndpoints/:id/owners"
@@ -81,8 +91,10 @@ func NewNotificationEndpointHandler(b *NotificationEndpointBackend) *Notificatio
8191
UserService: b.UserService,
8292
OrganizationService: b.OrganizationService,
8393
SecretService: b.SecretService,
94+
QueryService: b.QueryService,
8495
}
8596
h.HandlerFunc("POST", notificationEndpointsPath, h.handlePostNotificationEndpoint)
97+
h.HandlerFunc("PUT", notificationEndpointsIDTestPath, h.handlePutNotificationEndpointTest)
8698
h.HandlerFunc("GET", notificationEndpointsPath, h.handleGetNotificationEndpoints)
8799
h.HandlerFunc("GET", notificationEndpointsIDPath, h.handleGetNotificationEndpoint)
88100
h.HandlerFunc("DELETE", notificationEndpointsIDPath, h.handleDeleteNotificationEndpoint)
@@ -109,6 +121,7 @@ func NewNotificationEndpointHandler(b *NotificationEndpointBackend) *Notificatio
109121
UserResourceMappingService: b.UserResourceMappingService,
110122
UserService: b.UserService,
111123
}
124+
112125
h.HandlerFunc("POST", notificationEndpointsIDOwnersPath, newPostMemberHandler(ownerBackend))
113126
h.HandlerFunc("GET", notificationEndpointsIDOwnersPath, newGetMembersHandler(ownerBackend))
114127
h.HandlerFunc("DELETE", notificationEndpointsIDOwnersIDPath, newDeleteMemberHandler(ownerBackend))
@@ -229,7 +242,6 @@ func (h *NotificationEndpointHandler) handleGetNotificationEndpoints(w http.Resp
229242
h.HandleHTTPError(ctx, err, w)
230243
return
231244
}
232-
h.Logger.Debug("notificationEndpoints retrieved", zap.String("notificationEndpoints", fmt.Sprint(edps)))
233245

234246
if err := encodeResponse(ctx, w, http.StatusOK, newNotificationEndpointsResponse(ctx, edps, h.LabelService, filter, *opts)); err != nil {
235247
logEncodingError(h.Logger, r, err)
@@ -249,7 +261,6 @@ func (h *NotificationEndpointHandler) handleGetNotificationEndpoint(w http.Respo
249261
h.HandleHTTPError(ctx, err, w)
250262
return
251263
}
252-
h.Logger.Debug("notificationEndpoint retrieved", zap.String("notificationEndpoint", fmt.Sprint(edp)))
253264

254265
labels, err := h.LabelService.FindResourceLabels(ctx, influxdb.LabelMappingFilter{ResourceID: edp.GetID()})
255266
if err != nil {
@@ -403,6 +414,134 @@ func decodePatchNotificationEndpointRequest(ctx context.Context, r *http.Request
403414
return req, nil
404415
}
405416

417+
// handlePostNotificationEndpointTest is the HTTP handler for the PUT /api/v2/notificationEndpoints/:id/test route.
418+
func (h *NotificationEndpointHandler) handlePutNotificationEndpointTest(w http.ResponseWriter, r *http.Request) {
419+
ctx := r.Context()
420+
edp, err := decodePutNotificationEndpointRequest(ctx, r)
421+
if err != nil {
422+
h.Logger.Debug("failed to decode request", zap.Error(err))
423+
h.HandleHTTPError(ctx, err, w)
424+
return
425+
}
426+
427+
auth, err := pctx.GetAuthorizer(ctx)
428+
if err != nil {
429+
h.HandleHTTPError(ctx, err, w)
430+
return
431+
}
432+
433+
var token *influxdb.Authorization
434+
switch a := auth.(type) {
435+
case *influxdb.Authorization:
436+
token = a
437+
case *influxdb.Session:
438+
token = a.EphemeralAuth(edp.GetOrgID())
439+
default:
440+
h.HandleHTTPError(ctx, influxdb.ErrAuthorizerNotSupported, w)
441+
return
442+
}
443+
444+
for _, fld := range edp.SecretFields() {
445+
if fld.Value == nil {
446+
v, err := h.SecretService.LoadSecret(ctx, edp.GetOrgID(), fld.Key)
447+
if err != nil {
448+
h.HandleHTTPError(ctx, &influxdb.Error{
449+
Err: err,
450+
}, w)
451+
return
452+
}
453+
454+
fld.Value = &v
455+
}
456+
}
457+
458+
q, err := edp.GenerateTestFlux()
459+
if err != nil {
460+
h.HandleHTTPError(ctx, err, w)
461+
}
462+
463+
compiler := lang.FluxCompiler{
464+
Now: time.Now(),
465+
Query: q,
466+
}
467+
468+
req := query.Request{
469+
Compiler: compiler,
470+
Authorization: token,
471+
OrganizationID: edp.GetOrgID(),
472+
}
473+
474+
pr := &query.ProxyRequest{
475+
Request: req,
476+
Dialect: csv.DefaultDialect(),
477+
}
478+
479+
b := bytes.NewBuffer(nil)
480+
481+
if _, err := h.QueryService.Query(ctx, b, pr); err != nil {
482+
h.Logger.Info("failed to execute query", zap.Error(err))
483+
h.HandleHTTPError(ctx, err, w)
484+
return
485+
}
486+
487+
if err := encodeTestEndpointQueryResults(b, w); err != nil {
488+
h.HandleHTTPError(ctx, err, w)
489+
return
490+
}
491+
492+
w.WriteHeader(http.StatusNoContent)
493+
}
494+
495+
func encodeTestEndpointQueryResults(b *bytes.Buffer, w http.ResponseWriter) error {
496+
dec := csv.NewResultDecoder(csv.ResultDecoderConfig{})
497+
498+
res, err := dec.Decode(b)
499+
if err != nil {
500+
return err
501+
}
502+
503+
if err := res.Tables().Do(func(t flux.Table) error {
504+
return t.Do(func(r flux.ColReader) error {
505+
cols := r.Cols()
506+
idx := -1
507+
for i, col := range cols {
508+
if col.Label == "_sent" {
509+
idx = i
510+
break
511+
}
512+
}
513+
514+
if idx == -1 {
515+
return &influxdb.Error{
516+
Msg: "failed to send message",
517+
Code: influxdb.EBadRequest,
518+
}
519+
}
520+
521+
arr := r.Strings(idx)
522+
sent := false
523+
for i := 0; i < arr.Len(); i++ {
524+
if bytes.Equal(arr.Value(i), []byte("true")) {
525+
sent = true
526+
}
527+
}
528+
529+
if !sent {
530+
return &influxdb.Error{
531+
Msg: "failed to send message",
532+
Code: influxdb.EBadRequest,
533+
}
534+
}
535+
536+
return nil
537+
})
538+
}); err != nil {
539+
return err
540+
}
541+
542+
return nil
543+
}
544+
406545
// handlePostNotificationEndpoint is the HTTP handler for the POST /api/v2/notificationEndpoints route.
407546
func (h *NotificationEndpointHandler) handlePostNotificationEndpoint(w http.ResponseWriter, r *http.Request) {
408547
ctx := r.Context()
@@ -438,8 +577,6 @@ func (h *NotificationEndpointHandler) handlePostNotificationEndpoint(w http.Resp
438577

439578
labels := h.mapNewNotificationEndpointLabels(ctx, edp.NotificationEndpoint, edp.Labels)
440579

441-
h.Logger.Debug("notificationEndpoint created", zap.String("notificationEndpoint", fmt.Sprint(edp)))
442-
443580
if err := encodeResponse(ctx, w, http.StatusCreated, newNotificationEndpointResponse(edp, labels)); err != nil {
444581
logEncodingError(h.Logger, r, err)
445582
return
@@ -516,7 +653,6 @@ func (h *NotificationEndpointHandler) handlePutNotificationEndpoint(w http.Respo
516653
h.HandleHTTPError(ctx, err, w)
517654
return
518655
}
519-
h.Logger.Debug("notificationEndpoint replaced", zap.String("notificationEndpoint", fmt.Sprint(edp)))
520656

521657
if err := encodeResponse(ctx, w, http.StatusOK, newNotificationEndpointResponse(edp, labels)); err != nil {
522658
logEncodingError(h.Logger, r, err)
@@ -545,7 +681,6 @@ func (h *NotificationEndpointHandler) handlePatchNotificationEndpoint(w http.Res
545681
h.HandleHTTPError(ctx, err, w)
546682
return
547683
}
548-
h.Logger.Debug("notificationEndpoint patch", zap.String("notificationEndpoint", fmt.Sprint(edp)))
549684

550685
if err := encodeResponse(ctx, w, http.StatusOK, newNotificationEndpointResponse(edp, labels)); err != nil {
551686
logEncodingError(h.Logger, r, err)
@@ -584,7 +719,6 @@ func (h *NotificationEndpointHandler) handleDeleteNotificationEndpoint(w http.Re
584719
}, w)
585720
return
586721
}
587-
h.Logger.Debug("notificationEndpoint deleted", zap.String("notificationEndpointID", fmt.Sprint(i)))
588722

589723
w.WriteHeader(http.StatusNoContent)
590724
}

0 commit comments

Comments
 (0)