From 4e6b7183478802ca16bcc5b299ec68116c24a716 Mon Sep 17 00:00:00 2001 From: Daniel Oluojomu Date: Fri, 17 Nov 2023 22:09:49 +0000 Subject: [PATCH] Improve event deliveries filtering (#1824) - add sort url query for LoadEventDeliveriesPaged & LoadEventsPaged - add event type url query for outgoing projects * feat: test event type filter * fix: fix LoadEventDeliveriesPaged call * fix: fix build issues * fix: fix LoadEventDeliveriesPaged setup * feat: update event filters (#1840) * update date picker component * update dropdown component * update endpoint filter component * add sort events toggle * update event filters * update event delivery filter component * update: update filter component * update filter button * fix sort toggle * remove comments, fix sort toggle * update pagaination * update event filters * update events filter * update events filter buttons * fix filter toggle * fix: fix asc & desc sorting - remove print stmts - change case "DESC" to default in SetCursors - remove CTE from baseEventsPagedBackward * fix: fix getFwdEventPageQuery - fix issues where any prev page request jumps to the first page - fix ASC issue where there's always a previous page even on the first page --------- Co-authored-by: oluwadaminiola --- api/dashboard/event.go | 3 +- api/models/event_delivery.go | 1 + api/portal-api/event.go | 3 +- api/public/event.go | 6 +- database/postgres/event.go | 67 +++- database/postgres/event_delivery.go | 78 ++++- database/postgres/event_delivery_test.go | 27 +- database/postgres/event_test.go | 9 + datastore/filter.go | 1 + datastore/models.go | 24 ++ datastore/repository.go | 2 +- docs/docs.go | 278 ++++++++++++++++- docs/swagger.json | 276 ++++++++++++++++- docs/swagger.yaml | 170 +++++++++- docs/v3/openapi3.json | 293 +++++++++++++++++- docs/v3/openapi3.yaml | 178 ++++++++++- go.mod | 7 +- go.sum | 67 ---- internal/pkg/middleware/middleware.go | 11 +- mocks/repository.go | 8 +- services/batch_retry_event_delivery.go | 2 +- services/batch_retry_event_delivery_test.go | 4 +- .../date-picker/date-picker.component.html | 26 +- .../date-picker/date-picker.component.ts | 7 +- .../dropdown-container.component.ts | 11 +- .../dropdown/dropdown.component.html | 2 +- .../components/dropdown/dropdown.component.ts | 6 +- .../dashboard/src/app/models/event.model.ts | 7 +- .../endpoints-filter.component.html | 51 +-- .../endpoints-filter.component.ts | 9 +- .../event-delivery-filter.component.html | 225 ++++++++++++++ .../event-delivery-filter.component.scss | 0 .../event-delivery-filter.component.spec.ts | 23 ++ .../event-delivery-filter.component.ts | 265 ++++++++++++++++ .../event-logs/event-logs.component.html | 93 +----- .../event-logs/event-logs.component.ts | 156 ++-------- .../project/event-logs/event-logs.service.ts | 4 +- .../event-deliveries.component.html | 152 +-------- .../event-deliveries.component.ts | 160 ++-------- .../event-deliveries.module.ts | 4 +- .../project/events/events.component.html | 15 +- .../pages/project/events/events.component.ts | 5 + .../pages/project/events/events.service.ts | 2 +- .../portal-links/portal-links.component.html | 17 +- .../portal-links/portal-links.component.ts | 4 +- .../app/services/general/general.service.ts | 25 +- web/ui/dashboard/src/index.html | 15 + worker/task/retry_event_deliveries.go | 5 +- 48 files changed, 2072 insertions(+), 732 deletions(-) create mode 100644 web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.html create mode 100644 web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.scss create mode 100644 web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.spec.ts create mode 100644 web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.ts diff --git a/api/dashboard/event.go b/api/dashboard/event.go index 23d3a1a5fc..ca328ffa4d 100644 --- a/api/dashboard/event.go +++ b/api/dashboard/event.go @@ -325,7 +325,8 @@ func (a *DashboardHandler) GetEventDeliveriesPaged(w http.ResponseWriter, r *htt } f := data.Filter - ed, paginationData, err := postgres.NewEventDeliveryRepo(a.A.DB, a.A.Cache).LoadEventDeliveriesPaged(r.Context(), project.UID, f.EndpointIDs, f.EventID, f.SubscriptionID, f.Status, f.SearchParams, f.Pageable, f.IdempotencyKey) + + ed, paginationData, err := postgres.NewEventDeliveryRepo(a.A.DB, a.A.Cache).LoadEventDeliveriesPaged(r.Context(), project.UID, f.EndpointIDs, f.EventID, f.SubscriptionID, f.Status, f.SearchParams, f.Pageable, f.IdempotencyKey, f.EventType) if err != nil { log.FromContext(r.Context()).WithError(err).Error("failed to fetch event deliveries") _ = render.Render(w, r, util.NewErrorResponse("an error occurred while fetching event deliveries", http.StatusInternalServerError)) diff --git a/api/models/event_delivery.go b/api/models/event_delivery.go index d87f0ff0dd..a26f2c2268 100644 --- a/api/models/event_delivery.go +++ b/api/models/event_delivery.go @@ -81,6 +81,7 @@ func (ql *QueryListEventDelivery) Transform(r *http.Request) (*QueryListEventDel SubscriptionID: r.URL.Query().Get("subscriptionId"), IdempotencyKey: r.URL.Query().Get("idempotencyKey"), EventID: r.URL.Query().Get("eventId"), + EventType: r.URL.Query().Get("eventType"), Status: getEventDeliveryStatus(r), Pageable: m.GetPageableFromContext(r.Context()), SearchParams: searchParams, diff --git a/api/portal-api/event.go b/api/portal-api/event.go index 7335aa99d2..5694fadf59 100644 --- a/api/portal-api/event.go +++ b/api/portal-api/event.go @@ -398,7 +398,8 @@ func (a *PortalLinkHandler) GetEventDeliveriesPaged(w http.ResponseWriter, r *ht data.Filter.EndpointIDs = endpointIDs f := data.Filter - ed, paginationData, err := postgres.NewEventDeliveryRepo(a.A.DB, a.A.Cache).LoadEventDeliveriesPaged(r.Context(), project.UID, f.EndpointIDs, f.EventID, f.SubscriptionID, f.Status, f.SearchParams, f.Pageable, f.IdempotencyKey) + + ed, paginationData, err := postgres.NewEventDeliveryRepo(a.A.DB, a.A.Cache).LoadEventDeliveriesPaged(r.Context(), project.UID, f.EndpointIDs, f.EventID, f.SubscriptionID, f.Status, f.SearchParams, f.Pageable, f.IdempotencyKey, f.EventType) if err != nil { log.FromContext(r.Context()).WithError(err).Error("failed to fetch event deliveries") _ = render.Render(w, r, util.NewErrorResponse("an error occurred while fetching event deliveries", http.StatusInternalServerError)) diff --git a/api/public/event.go b/api/public/event.go index 9e0aa7eb89..ae3ee031ba 100644 --- a/api/public/event.go +++ b/api/public/event.go @@ -3,12 +3,13 @@ package public import ( "encoding/json" "fmt" + "net/http" + "github.com/frain-dev/convoy" "github.com/frain-dev/convoy/pkg/msgpack" "github.com/frain-dev/convoy/queue" "github.com/frain-dev/convoy/worker/task" "github.com/oklog/ulid/v2" - "net/http" "github.com/frain-dev/convoy/pkg/log" @@ -528,7 +529,8 @@ func (a *PublicHandler) GetEventDeliveriesPaged(w http.ResponseWriter, r *http.R } f := data.Filter - ed, paginationData, err := postgres.NewEventDeliveryRepo(a.A.DB, a.A.Cache).LoadEventDeliveriesPaged(r.Context(), project.UID, f.EndpointIDs, f.EventID, f.SubscriptionID, f.Status, f.SearchParams, f.Pageable, f.IdempotencyKey) + + ed, paginationData, err := postgres.NewEventDeliveryRepo(a.A.DB, a.A.Cache).LoadEventDeliveriesPaged(r.Context(), project.UID, f.EndpointIDs, f.EventID, f.SubscriptionID, f.Status, f.SearchParams, f.Pageable, f.IdempotencyKey, f.EventType) if err != nil { log.FromContext(r.Context()).WithError(err).Error("failed to fetch event deliveries") _ = render.Render(w, r, util.NewErrorResponse("an error occurred while fetching event deliveries", http.StatusInternalServerError)) diff --git a/database/postgres/event.go b/database/postgres/event.go index 6515414264..f161c39ba0 100644 --- a/database/postgres/event.go +++ b/database/postgres/event.go @@ -5,9 +5,11 @@ import ( "database/sql" "errors" "fmt" + "strings" + "time" + "github.com/frain-dev/convoy/cache" "github.com/frain-dev/convoy/config" - "time" "github.com/frain-dev/convoy/database" "github.com/frain-dev/convoy/datastore" @@ -112,21 +114,26 @@ const ( LEFT JOIN convoy.sources s ON s.id = ev.source_id WHERE ev.deleted_at IS NULL` - baseEventsPagedForward = `%s %s AND ev.id <= :cursor - GROUP BY ev.id, s.id - ORDER BY ev.id DESC - LIMIT :limit + baseEventsPagedForward = ` + WITH events AS ( + %s %s AND ev.id <= :cursor + GROUP BY ev.id, s.id + ORDER BY ev.id %s + LIMIT :limit + ) + + SELECT * FROM events ORDER BY id %s ` baseEventsPagedBackward = ` WITH events AS ( - %s %s AND ev.id >= :cursor + %s %s AND ev.id >= :cursor GROUP BY ev.id, s.id - ORDER BY ev.id ASC + ORDER BY ev.id %s LIMIT :limit ) - SELECT * FROM events ORDER BY id DESC + SELECT * FROM events ORDER BY id %s ` baseEventFilter = ` AND ev.project_id = :project_id @@ -153,7 +160,7 @@ const ( LEFT JOIN convoy.events_endpoints ee ON ev.id = ee.event_id WHERE ev.deleted_at IS NULL ` - countPrevEvents = ` AND ev.id > :cursor GROUP BY ev.id ORDER BY ev.id DESC LIMIT 1` + countPrevEvents = ` AND ev.id > :cursor GROUP BY ev.id ORDER BY ev.id %s LIMIT 1` softDeleteProjectEvents = ` UPDATE convoy.events SET deleted_at = NOW() @@ -364,12 +371,12 @@ func (e *eventRepo) LoadEventsPaged(ctx context.Context, projectID string, filte "event_id": filter.Query, } - var base = baseEventsPaged + base := baseEventsPaged var baseQueryPagination string if filter.Pageable.Direction == datastore.Next { - baseQueryPagination = baseEventsPagedForward + baseQueryPagination = getFwdEventPageQuery(filter.Pageable.SortOrder()) } else { - baseQueryPagination = baseEventsPagedBackward + baseQueryPagination = getBackwardEventPageQuery(filter.Pageable.SortOrder()) } filterQuery = baseEventFilter @@ -382,7 +389,12 @@ func (e *eventRepo) LoadEventsPaged(ctx context.Context, projectID string, filte base = baseEventsSearch } - query = fmt.Sprintf(baseQueryPagination, base, filterQuery) + preOrder := filter.Pageable.SortOrder() + if filter.Pageable.Direction == datastore.Prev { + preOrder = reverseOrder(preOrder) + } + + query = fmt.Sprintf(baseQueryPagination, base, filterQuery, preOrder, filter.Pageable.SortOrder()) query, args, err = sqlx.Named(query, arg) if err != nil { return nil, datastore.PaginationData{}, err @@ -423,7 +435,10 @@ func (e *eventRepo) LoadEventsPaged(ctx context.Context, projectID string, filte baseCountEvents = baseCountPrevEventSearch } - cq := baseCountEvents + filterQuery + countPrevEvents + tmp := getCountDeliveriesPrevRowQuery(filter.Pageable.SortOrder()) + tmp = fmt.Sprintf(tmp, filter.Pageable.SortOrder()) + + cq := baseCountEvents + filterQuery + tmp countQuery, qargs, err = sqlx.Named(cq, qarg) if err != nil { return nil, datastore.PaginationData{}, err @@ -524,3 +539,27 @@ type EventEndpoint struct { EventID string `db:"event_id"` EndpointID string `db:"endpoint_id"` } + +func getFwdEventPageQuery(sortOrder string) string { + if sortOrder == "ASC" { + return strings.Replace(baseEventsPagedForward, "<=", ">=", 1) + } + + return baseEventsPagedForward +} + +func getBackwardEventPageQuery(sortOrder string) string { + if sortOrder == "ASC" { + return strings.Replace(baseEventsPagedBackward, ">=", "<=", 1) + } + + return baseEventsPagedBackward +} + +func getCountDeliveriesPrevRowQuery(sortOrder string) string { + if sortOrder == "ASC" { + return strings.Replace(countPrevEvents, ">", "<", 1) + } + + return countPrevEvents +} diff --git a/database/postgres/event_delivery.go b/database/postgres/event_delivery.go index d17a6260c6..70d57f0420 100644 --- a/database/postgres/event_delivery.go +++ b/database/postgres/event_delivery.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "strings" "time" "github.com/frain-dev/convoy/cache" @@ -74,12 +75,18 @@ const ( ` baseEventDeliveryPagedForward = ` - %s - %s - AND ed.id <= :cursor - GROUP BY ed.id, ep.id, ev.id, d.id, s.id - ORDER BY ed.id DESC - LIMIT :limit + WITH event_deliveries AS ( + %s + %s + AND ed.id <= :cursor + GROUP BY ed.id, ep.id, ev.id, d.id, s.id + ORDER BY ed.id %s + LIMIT :limit + ) + + SELECT * FROM event_deliveries + WHERE ("event_metadata.event_type" = :event_type OR :event_type = '') + ORDER BY id %s ` baseEventDeliveryPagedBackward = ` @@ -88,11 +95,13 @@ const ( %s AND ed.id >= :cursor GROUP BY ed.id, ep.id, ev.id, d.id, s.id - ORDER BY ed.id ASC + ORDER BY ed.id %s LIMIT :limit ) - SELECT * FROM event_deliveries ORDER BY id DESC + SELECT * FROM event_deliveries + WHERE ("event_metadata.event_type" = :event_type OR :event_type = '') + ORDER BY id %s ` fetchEventDeliveryByID = baseFetchEventDelivery + ` AND ed.id = $1 AND ed.project_id = $2` @@ -108,7 +117,7 @@ const ( FROM convoy.event_deliveries ed WHERE ed.deleted_at IS NULL %s - AND ed.id > :cursor GROUP BY ed.id ORDER BY ed.id DESC LIMIT 1` + AND ed.id > :cursor GROUP BY ed.id ORDER BY ed.id %s LIMIT 1` loadEventDeliveriesIntervals = ` SELECT @@ -466,7 +475,7 @@ func (e *eventDeliveryRepo) DeleteProjectEventDeliveries(ctx context.Context, pr return nil } -func (e *eventDeliveryRepo) LoadEventDeliveriesPaged(ctx context.Context, projectID string, endpointIDs []string, eventID, subscriptionID string, status []datastore.EventDeliveryStatus, params datastore.SearchParams, pageable datastore.Pageable, idempotencyKey string) ([]datastore.EventDelivery, datastore.PaginationData, error) { +func (e *eventDeliveryRepo) LoadEventDeliveriesPaged(ctx context.Context, projectID string, endpointIDs []string, eventID, subscriptionID string, status []datastore.EventDeliveryStatus, params datastore.SearchParams, pageable datastore.Pageable, idempotencyKey, eventType string) ([]datastore.EventDelivery, datastore.PaginationData, error) { eventDeliveriesP := make([]EventDeliveryPaginated, 0) start := time.Unix(params.CreatedAtStart, 0) @@ -479,6 +488,7 @@ func (e *eventDeliveryRepo) LoadEventDeliveriesPaged(ctx context.Context, projec "subscription_id": subscriptionID, "start_date": start, "event_id": eventID, + "event_type": eventType, "end_date": end, "status": status, "cursor": pageable.Cursor(), @@ -487,9 +497,9 @@ func (e *eventDeliveryRepo) LoadEventDeliveriesPaged(ctx context.Context, projec var query, filterQuery string if pageable.Direction == datastore.Next { - query = baseEventDeliveryPagedForward + query = getFwdDeliveryPageQuery(pageable.SortOrder()) } else { - query = baseEventDeliveryPagedBackward + query = getBackwardDeliveryPageQuery(pageable.SortOrder()) } filterQuery = baseEventDeliveryFilter @@ -505,7 +515,12 @@ func (e *eventDeliveryRepo) LoadEventDeliveriesPaged(ctx context.Context, projec filterQuery += ` AND ed.subscription_id = :subscription_id` } - query = fmt.Sprintf(query, baseFetchEventDelivery, filterQuery) + preOrder := pageable.SortOrder() + if pageable.Direction == datastore.Prev { + preOrder = reverseOrder(preOrder) + } + + query = fmt.Sprintf(query, baseFetchEventDelivery, filterQuery, preOrder, pageable.SortOrder()) query, args, err := sqlx.Named(query, arg) if err != nil { @@ -594,7 +609,9 @@ func (e *eventDeliveryRepo) LoadEventDeliveriesPaged(ctx context.Context, projec qarg := arg qarg["cursor"] = first.UID - cq := fmt.Sprintf(countPrevEventDeliveries, filterQuery) + tmp := getCountEventPrevRowQuery(pageable.SortOrder()) + + cq := fmt.Sprintf(tmp, filterQuery, pageable.SortOrder()) countQuery, qargs, err = sqlx.Named(cq, qarg) if err != nil { return nil, datastore.PaginationData{}, err @@ -835,3 +852,36 @@ func (m *CLIMetadata) Scan(value interface{}) error { return nil } + +func getFwdDeliveryPageQuery(sortOrder string) string { + if sortOrder == "ASC" { + return strings.Replace(baseEventDeliveryPagedForward, "<=", ">=", 1) + } + + return baseEventDeliveryPagedForward +} + +func getBackwardDeliveryPageQuery(sortOrder string) string { + if sortOrder == "ASC" { + return strings.Replace(baseEventDeliveryPagedBackward, ">=", "<=", 1) + } + + return baseEventDeliveryPagedBackward +} + +func getCountEventPrevRowQuery(sortOrder string) string { + if sortOrder == "ASC" { + return strings.Replace(countPrevEventDeliveries, ">", "<", 1) + } + + return countPrevEventDeliveries +} + +func reverseOrder(sortOrder string) string { + switch sortOrder { + case "ASC": + return "DESC" + default: + return "ASC" + } +} diff --git a/database/postgres/event_delivery_test.go b/database/postgres/event_delivery_test.go index 356ea7b472..6716b2793c 100644 --- a/database/postgres/event_delivery_test.go +++ b/database/postgres/event_delivery_test.go @@ -463,7 +463,7 @@ func Test_eventDeliveryRepo_LoadEventDeliveriesPaged(t *testing.T) { datastore.Pageable{ PerPage: 10, }, - "", + "", "", ) require.NoError(t, err) @@ -491,4 +491,29 @@ func Test_eventDeliveryRepo_LoadEventDeliveriesPaged(t *testing.T) { require.Equal(t, ed, dbEventDelivery) } + + evType := "file" + event = seedEventWithEventType(t, db, project, evType) + + ed := generateEventDelivery(project, endpoint, event, device, sub) + + err = edRepo.CreateEventDelivery(context.Background(), ed) + require.NoError(t, err) + + filteredDeliveries, _, err := edRepo.LoadEventDeliveriesPaged( + context.Background(), project.UID, []string{endpoint.UID}, event.UID, sub.UID, + []datastore.EventDeliveryStatus{datastore.SuccessEventStatus}, + datastore.SearchParams{ + CreatedAtStart: time.Now().Add(-time.Hour).Unix(), + CreatedAtEnd: time.Now().Add(time.Hour).Unix(), + }, + datastore.Pageable{ + PerPage: 10, + }, + "", evType, + ) + + require.NoError(t, err) + require.Equal(t, 1, len(filteredDeliveries)) + require.Equal(t, ed.UID, filteredDeliveries[0].UID) } diff --git a/database/postgres/event_test.go b/database/postgres/event_test.go index 30772de1ce..8ae21e8919 100644 --- a/database/postgres/event_test.go +++ b/database/postgres/event_test.go @@ -426,3 +426,12 @@ func seedEvent(t *testing.T, db database.Database, project *datastore.Project) * require.NoError(t, NewEventRepo(db, nil).CreateEvent(context.Background(), ev)) return ev } + +func seedEventWithEventType(t *testing.T, db database.Database, project *datastore.Project, eventType string) *datastore.Event { + ev := generateEvent(t, db) + ev.EventType = datastore.EventType(eventType) + ev.ProjectID = project.UID + + require.NoError(t, NewEventRepo(db, nil).CreateEvent(context.Background(), ev)) + return ev +} diff --git a/datastore/filter.go b/datastore/filter.go index 764479516e..5a858cb725 100644 --- a/datastore/filter.go +++ b/datastore/filter.go @@ -13,6 +13,7 @@ type Filter struct { EndpointIDs []string SubscriptionID string EventID string + EventType string SourceID string Pageable Pageable IdempotencyKey string diff --git a/datastore/models.go b/datastore/models.go index b207ebde6e..7ab6b658c5 100644 --- a/datastore/models.go +++ b/datastore/models.go @@ -8,6 +8,7 @@ import ( "fmt" "math" "net/http" + "strings" "time" "github.com/oklog/ulid/v2" @@ -24,6 +25,7 @@ import ( type Pageable struct { PerPage int `json:"per_page"` Direction PageDirection `json:"direction"` + Sort string `json:"sort"` PrevCursor string `json:"prev_page_cursor"` NextCursor string `json:"next_page_cursor"` } @@ -43,10 +45,32 @@ func (p Pageable) Cursor() string { return p.PrevCursor } +func (p Pageable) SortOrder() string { + if p.Sort == "ASC" || p.Sort == "DESC" { + return p.Sort + } + + return "DESC" +} + func (p Pageable) Limit() int { return p.PerPage + 1 } +func (p *Pageable) SetCursors() { + switch p.Sort { + case "ASC": + if isStringEmpty(p.NextCursor) { + p.NextCursor = "" // still set it empty, it might be filled with spaces + } + default: + if isStringEmpty(p.NextCursor) { + p.NextCursor = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF" + } + } +} +func isStringEmpty(s string) bool { return len(strings.TrimSpace(s)) == 0 } + type PaginationData struct { PrevRowCount PrevRowCount `json:"-"` PerPage int64 `json:"per_page"` diff --git a/datastore/repository.go b/datastore/repository.go index ee8ac1c8cd..3b3d7eb7b4 100644 --- a/datastore/repository.go +++ b/datastore/repository.go @@ -30,7 +30,7 @@ type EventDeliveryRepository interface { UpdateEventDeliveryWithAttempt(ctx context.Context, projectID string, eventDelivery EventDelivery, attempt DeliveryAttempt) error CountEventDeliveries(ctx context.Context, projectID string, endpointIDs []string, eventID string, status []EventDeliveryStatus, params SearchParams) (int64, error) DeleteProjectEventDeliveries(ctx context.Context, projectID string, filter *EventDeliveryFilter, hardDelete bool) error - LoadEventDeliveriesPaged(ctx context.Context, projectID string, endpointIDs []string, eventID, subscriptionID string, status []EventDeliveryStatus, params SearchParams, pageable Pageable, idempotencyKey string) ([]EventDelivery, PaginationData, error) + LoadEventDeliveriesPaged(ctx context.Context, projectID string, endpointIDs []string, eventID, subscriptionID string, status []EventDeliveryStatus, params SearchParams, pageable Pageable, idempotencyKey, eventType string) ([]EventDelivery, PaginationData, error) LoadEventDeliveriesIntervals(ctx context.Context, projectID string, params SearchParams, period Period) ([]EventInterval, error) } diff --git a/docs/docs.go b/docs/docs.go index 1dc72742d9..f70e3c2cea 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1,6 +1,6 @@ // Package docs GENERATED BY SWAG; DO NOT EDIT // This file was generated by swaggo/swag at -// 2023-07-27 11:10:15.044479 +0100 BST m=+1.062988834 +// 2023-10-30 13:16:41.281988 +0000 GMT m=+1.906100168 package docs import "github.com/swaggo/swag" @@ -2722,6 +2722,11 @@ const docTemplate = `{ "name": "eventID", "in": "query" }, + { + "type": "string", + "name": "eventType", + "in": "query" + }, { "type": "string", "name": "idempotencyKey", @@ -5192,6 +5197,118 @@ const docTemplate = `{ } } }, + "/v1/projects/{projectID}/subscriptions/test_function": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "This endpoint validates that a filter will match a certain payload structure.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Subscriptions" + ], + "summary": "Validate subscription filter", + "parameters": [ + { + "type": "string", + "description": "Project ID", + "name": "projectID", + "in": "path", + "required": true + }, + { + "description": "Function Details", + "name": "filter", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.TestWebhookFunction" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.ServerResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.SubscriptionFunctionResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.ServerResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/public.Stub" + } + } + } + ] + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.ServerResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/public.Stub" + } + } + } + ] + } + }, + "404": { + "description": "Not Found", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.ServerResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/public.Stub" + } + } + } + ] + } + } + } + } + }, "/v1/projects/{projectID}/subscriptions/{subscriptionID}": { "get": { "security": [ @@ -5953,6 +6070,46 @@ const docTemplate = `{ "type": "string" } }, + "datastore.KafkaAuth": { + "type": "object", + "properties": { + "hash": { + "type": "string" + }, + "password": { + "type": "string" + }, + "tls": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "datastore.KafkaPubSubConfig": { + "type": "object", + "properties": { + "auth": { + "$ref": "#/definitions/datastore.KafkaAuth" + }, + "brokers": { + "type": "array", + "items": { + "type": "string" + } + }, + "consumer_group_id": { + "type": "string" + }, + "topic_name": { + "type": "string" + } + } + }, "datastore.KeyType": { "type": "string", "enum": [ @@ -6079,6 +6236,9 @@ const docTemplate = `{ }, "prev_page_cursor": { "type": "string" + }, + "sort": { + "type": "string" } } }, @@ -6143,6 +6303,9 @@ const docTemplate = `{ "datastore.ProjectConfig": { "type": "object", "properties": { + "add_event_id_trace_headers": { + "type": "boolean" + }, "disable_endpoint": { "type": "boolean" }, @@ -6214,6 +6377,9 @@ const docTemplate = `{ "google": { "$ref": "#/definitions/datastore.GooglePubSubConfig" }, + "kafka": { + "$ref": "#/definitions/datastore.KafkaPubSubConfig" + }, "sqs": { "$ref": "#/definitions/datastore.SQSPubSubConfig" }, @@ -6229,11 +6395,13 @@ const docTemplate = `{ "type": "string", "enum": [ "sqs", - "google" + "google", + "kafka" ], "x-enum-varnames": [ "SqsPubSub", - "GooglePubSub" + "GooglePubSub", + "KafkaPubSub" ] }, "datastore.RateLimitConfiguration": { @@ -6252,6 +6420,9 @@ const docTemplate = `{ "properties": { "policy": { "type": "string" + }, + "search_policy": { + "type": "string" } } }, @@ -6652,7 +6823,6 @@ const docTemplate = `{ } }, "data": { - "description": "Data is an arbitrary JSON value that gets sent as the body of the\nwebhook to the endpoints", "type": "array", "items": { "type": "integer" @@ -6666,6 +6836,12 @@ const docTemplate = `{ }, "idempotency_key": { "type": "string" + }, + "owner_id": { + "type": "string" + }, + "uid": { + "type": "string" } } }, @@ -6748,6 +6924,9 @@ const docTemplate = `{ "filter_config": { "$ref": "#/definitions/models.FilterConfiguration" }, + "function": { + "type": "string" + }, "name": { "type": "string" }, @@ -7205,6 +7384,46 @@ const docTemplate = `{ } } }, + "models.KafkaAuth": { + "type": "object", + "properties": { + "hash": { + "type": "string" + }, + "password": { + "type": "string" + }, + "tls": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "models.KafkaPubSubConfig": { + "type": "object", + "properties": { + "auth": { + "$ref": "#/definitions/models.KafkaAuth" + }, + "brokers": { + "type": "array", + "items": { + "type": "string" + } + }, + "consumer_group_id": { + "type": "string" + }, + "topic_name": { + "type": "string" + } + } + }, "models.MetaEventConfiguration": { "type": "object", "properties": { @@ -7333,6 +7552,9 @@ const docTemplate = `{ "models.ProjectConfig": { "type": "object", "properties": { + "add_event_id_trace_headers": { + "type": "boolean" + }, "disable_endpoint": { "type": "boolean" }, @@ -7406,6 +7628,9 @@ const docTemplate = `{ "google": { "$ref": "#/definitions/models.GooglePubSubConfig" }, + "kafka": { + "$ref": "#/definitions/models.KafkaPubSubConfig" + }, "sqs": { "$ref": "#/definitions/models.SQSPubSubConfig" }, @@ -7433,6 +7658,9 @@ const docTemplate = `{ "properties": { "policy": { "type": "string" + }, + "search_policy": { + "type": "string" } } }, @@ -7591,6 +7819,18 @@ const docTemplate = `{ } } }, + "models.SubscriptionFunctionResponse": { + "type": "object", + "properties": { + "log": { + "type": "array", + "items": { + "type": "string" + } + }, + "payload": {} + } + }, "models.SubscriptionResponse": { "type": "object", "properties": { @@ -7617,6 +7857,9 @@ const docTemplate = `{ "filter_config": { "$ref": "#/definitions/datastore.FilterConfiguration" }, + "function": { + "$ref": "#/definitions/null.String" + }, "name": { "type": "string" }, @@ -7651,6 +7894,18 @@ const docTemplate = `{ } } }, + "models.TestWebhookFunction": { + "type": "object", + "properties": { + "function": { + "type": "string" + }, + "payload": { + "type": "object", + "additionalProperties": true + } + } + }, "models.UpdateCustomResponse": { "type": "object", "properties": { @@ -7779,6 +8034,9 @@ const docTemplate = `{ "filter_config": { "$ref": "#/definitions/models.FilterConfiguration" }, + "function": { + "type": "string" + }, "name": { "type": "string" }, @@ -7810,6 +8068,18 @@ const docTemplate = `{ } } }, + "null.String": { + "type": "object", + "properties": { + "string": { + "type": "string" + }, + "valid": { + "description": "Valid is true if String is not NULL", + "type": "boolean" + } + } + }, "public.Stub": { "type": "object" }, diff --git a/docs/swagger.json b/docs/swagger.json index a7e3c3369f..3439ebec3c 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2717,6 +2717,11 @@ "name": "eventID", "in": "query" }, + { + "type": "string", + "name": "eventType", + "in": "query" + }, { "type": "string", "name": "idempotencyKey", @@ -5187,6 +5192,118 @@ } } }, + "/v1/projects/{projectID}/subscriptions/test_function": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "This endpoint validates that a filter will match a certain payload structure.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Subscriptions" + ], + "summary": "Validate subscription filter", + "parameters": [ + { + "type": "string", + "description": "Project ID", + "name": "projectID", + "in": "path", + "required": true + }, + { + "description": "Function Details", + "name": "filter", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.TestWebhookFunction" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.ServerResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.SubscriptionFunctionResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.ServerResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/public.Stub" + } + } + } + ] + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.ServerResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/public.Stub" + } + } + } + ] + } + }, + "404": { + "description": "Not Found", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.ServerResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/public.Stub" + } + } + } + ] + } + } + } + } + }, "/v1/projects/{projectID}/subscriptions/{subscriptionID}": { "get": { "security": [ @@ -5948,6 +6065,46 @@ "type": "string" } }, + "datastore.KafkaAuth": { + "type": "object", + "properties": { + "hash": { + "type": "string" + }, + "password": { + "type": "string" + }, + "tls": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "datastore.KafkaPubSubConfig": { + "type": "object", + "properties": { + "auth": { + "$ref": "#/definitions/datastore.KafkaAuth" + }, + "brokers": { + "type": "array", + "items": { + "type": "string" + } + }, + "consumer_group_id": { + "type": "string" + }, + "topic_name": { + "type": "string" + } + } + }, "datastore.KeyType": { "type": "string", "enum": [ @@ -6074,6 +6231,9 @@ }, "prev_page_cursor": { "type": "string" + }, + "sort": { + "type": "string" } } }, @@ -6138,6 +6298,9 @@ "datastore.ProjectConfig": { "type": "object", "properties": { + "add_event_id_trace_headers": { + "type": "boolean" + }, "disable_endpoint": { "type": "boolean" }, @@ -6209,6 +6372,9 @@ "google": { "$ref": "#/definitions/datastore.GooglePubSubConfig" }, + "kafka": { + "$ref": "#/definitions/datastore.KafkaPubSubConfig" + }, "sqs": { "$ref": "#/definitions/datastore.SQSPubSubConfig" }, @@ -6224,11 +6390,13 @@ "type": "string", "enum": [ "sqs", - "google" + "google", + "kafka" ], "x-enum-varnames": [ "SqsPubSub", - "GooglePubSub" + "GooglePubSub", + "KafkaPubSub" ] }, "datastore.RateLimitConfiguration": { @@ -6247,6 +6415,9 @@ "properties": { "policy": { "type": "string" + }, + "search_policy": { + "type": "string" } } }, @@ -6647,7 +6818,6 @@ } }, "data": { - "description": "Data is an arbitrary JSON value that gets sent as the body of the\nwebhook to the endpoints", "type": "array", "items": { "type": "integer" @@ -6661,6 +6831,12 @@ }, "idempotency_key": { "type": "string" + }, + "owner_id": { + "type": "string" + }, + "uid": { + "type": "string" } } }, @@ -6743,6 +6919,9 @@ "filter_config": { "$ref": "#/definitions/models.FilterConfiguration" }, + "function": { + "type": "string" + }, "name": { "type": "string" }, @@ -7200,6 +7379,46 @@ } } }, + "models.KafkaAuth": { + "type": "object", + "properties": { + "hash": { + "type": "string" + }, + "password": { + "type": "string" + }, + "tls": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "models.KafkaPubSubConfig": { + "type": "object", + "properties": { + "auth": { + "$ref": "#/definitions/models.KafkaAuth" + }, + "brokers": { + "type": "array", + "items": { + "type": "string" + } + }, + "consumer_group_id": { + "type": "string" + }, + "topic_name": { + "type": "string" + } + } + }, "models.MetaEventConfiguration": { "type": "object", "properties": { @@ -7328,6 +7547,9 @@ "models.ProjectConfig": { "type": "object", "properties": { + "add_event_id_trace_headers": { + "type": "boolean" + }, "disable_endpoint": { "type": "boolean" }, @@ -7401,6 +7623,9 @@ "google": { "$ref": "#/definitions/models.GooglePubSubConfig" }, + "kafka": { + "$ref": "#/definitions/models.KafkaPubSubConfig" + }, "sqs": { "$ref": "#/definitions/models.SQSPubSubConfig" }, @@ -7428,6 +7653,9 @@ "properties": { "policy": { "type": "string" + }, + "search_policy": { + "type": "string" } } }, @@ -7586,6 +7814,18 @@ } } }, + "models.SubscriptionFunctionResponse": { + "type": "object", + "properties": { + "log": { + "type": "array", + "items": { + "type": "string" + } + }, + "payload": {} + } + }, "models.SubscriptionResponse": { "type": "object", "properties": { @@ -7612,6 +7852,9 @@ "filter_config": { "$ref": "#/definitions/datastore.FilterConfiguration" }, + "function": { + "$ref": "#/definitions/null.String" + }, "name": { "type": "string" }, @@ -7646,6 +7889,18 @@ } } }, + "models.TestWebhookFunction": { + "type": "object", + "properties": { + "function": { + "type": "string" + }, + "payload": { + "type": "object", + "additionalProperties": true + } + } + }, "models.UpdateCustomResponse": { "type": "object", "properties": { @@ -7774,6 +8029,9 @@ "filter_config": { "$ref": "#/definitions/models.FilterConfiguration" }, + "function": { + "type": "string" + }, "name": { "type": "string" }, @@ -7805,6 +8063,18 @@ } } }, + "null.String": { + "type": "object", + "properties": { + "string": { + "type": "string" + }, + "valid": { + "description": "Valid is true if String is not NULL", + "type": "boolean" + } + } + }, "public.Stub": { "type": "object" }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 62026b5c1f..0ac5a1f431 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -288,6 +288,32 @@ definitions: additionalProperties: type: string type: object + datastore.KafkaAuth: + properties: + hash: + type: string + password: + type: string + tls: + type: boolean + type: + type: string + username: + type: string + type: object + datastore.KafkaPubSubConfig: + properties: + auth: + $ref: '#/definitions/datastore.KafkaAuth' + brokers: + items: + type: string + type: array + consumer_group_id: + type: string + topic_name: + type: string + type: object datastore.KeyType: enum: - project @@ -378,6 +404,8 @@ definitions: type: integer prev_page_cursor: type: string + sort: + type: string type: object datastore.PaginationData: properties: @@ -419,6 +447,8 @@ definitions: type: object datastore.ProjectConfig: properties: + add_event_id_trace_headers: + type: boolean disable_endpoint: type: boolean max_payload_read_size: @@ -466,6 +496,8 @@ definitions: properties: google: $ref: '#/definitions/datastore.GooglePubSubConfig' + kafka: + $ref: '#/definitions/datastore.KafkaPubSubConfig' sqs: $ref: '#/definitions/datastore.SQSPubSubConfig' type: @@ -477,10 +509,12 @@ definitions: enum: - sqs - google + - kafka type: string x-enum-varnames: - SqsPubSub - GooglePubSub + - KafkaPubSub datastore.RateLimitConfiguration: properties: count: @@ -492,6 +526,8 @@ definitions: properties: policy: type: string + search_policy: + type: string type: object datastore.RetryConfiguration: properties: @@ -761,9 +797,6 @@ definitions: type: string type: object data: - description: |- - Data is an arbitrary JSON value that gets sent as the body of the - webhook to the endpoints items: type: integer type: array @@ -773,6 +806,10 @@ definitions: type: string idempotency_key: type: string + owner_id: + type: string + uid: + type: string type: object models.CreateProject: properties: @@ -826,6 +863,8 @@ definitions: type: string filter_config: $ref: '#/definitions/models.FilterConfiguration' + function: + type: string name: type: string rate_limit_config: @@ -1132,6 +1171,32 @@ definitions: type: string type: array type: object + models.KafkaAuth: + properties: + hash: + type: string + password: + type: string + tls: + type: boolean + type: + type: string + username: + type: string + type: object + models.KafkaPubSubConfig: + properties: + auth: + $ref: '#/definitions/models.KafkaAuth' + brokers: + items: + type: string + type: array + consumer_group_id: + type: string + topic_name: + type: string + type: object models.MetaEventConfiguration: properties: event_type: @@ -1216,6 +1281,8 @@ definitions: type: object models.ProjectConfig: properties: + add_event_id_trace_headers: + type: boolean disable_endpoint: type: boolean max_payload_read_size: @@ -1264,6 +1331,8 @@ definitions: properties: google: $ref: '#/definitions/models.GooglePubSubConfig' + kafka: + $ref: '#/definitions/models.KafkaPubSubConfig' sqs: $ref: '#/definitions/models.SQSPubSubConfig' type: @@ -1282,6 +1351,8 @@ definitions: properties: policy: type: string + search_policy: + type: string type: object models.RetryConfiguration: properties: @@ -1384,6 +1455,14 @@ definitions: type: type: string type: object + models.SubscriptionFunctionResponse: + properties: + log: + items: + type: string + type: array + payload: {} + type: object models.SubscriptionResponse: properties: alert_config: @@ -1400,6 +1479,8 @@ definitions: $ref: '#/definitions/datastore.Endpoint' filter_config: $ref: '#/definitions/datastore.FilterConfiguration' + function: + $ref: '#/definitions/null.String' name: type: string rate_limit_config: @@ -1422,6 +1503,14 @@ definitions: schema: $ref: '#/definitions/models.FilterSchema' type: object + models.TestWebhookFunction: + properties: + function: + type: string + payload: + additionalProperties: true + type: object + type: object models.UpdateCustomResponse: properties: body: @@ -1506,6 +1595,8 @@ definitions: type: string filter_config: $ref: '#/definitions/models.FilterConfiguration' + function: + type: string name: type: string rate_limit_config: @@ -1526,6 +1617,14 @@ definitions: type: $ref: '#/definitions/datastore.VerifierType' type: object + null.String: + properties: + string: + type: string + valid: + description: Valid is true if String is not NULL + type: boolean + type: object public.Stub: type: object public.pagedResponse: @@ -3168,6 +3267,9 @@ paths: - in: query name: eventID type: string + - in: query + name: eventType + type: string - in: query name: idempotencyKey type: string @@ -4598,6 +4700,68 @@ paths: summary: Validate subscription filter tags: - Subscriptions + /v1/projects/{projectID}/subscriptions/test_function: + post: + consumes: + - application/json + description: This endpoint validates that a filter will match a certain payload + structure. + parameters: + - description: Project ID + in: path + name: projectID + required: true + type: string + - description: Function Details + in: body + name: filter + required: true + schema: + $ref: '#/definitions/models.TestWebhookFunction' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/util.ServerResponse' + - properties: + data: + $ref: '#/definitions/models.SubscriptionFunctionResponse' + type: object + "400": + description: Bad Request + schema: + allOf: + - $ref: '#/definitions/util.ServerResponse' + - properties: + data: + $ref: '#/definitions/public.Stub' + type: object + "401": + description: Unauthorized + schema: + allOf: + - $ref: '#/definitions/util.ServerResponse' + - properties: + data: + $ref: '#/definitions/public.Stub' + type: object + "404": + description: Not Found + schema: + allOf: + - $ref: '#/definitions/util.ServerResponse' + - properties: + data: + $ref: '#/definitions/public.Stub' + type: object + security: + - ApiKeyAuth: [] + summary: Validate subscription filter + tags: + - Subscriptions schemes: - https securityDefinitions: diff --git a/docs/v3/openapi3.json b/docs/v3/openapi3.json index de850c3ea4..a5a6d4a014 100644 --- a/docs/v3/openapi3.json +++ b/docs/v3/openapi3.json @@ -425,6 +425,46 @@ }, "type": "object" }, + "datastore.KafkaAuth": { + "properties": { + "hash": { + "type": "string" + }, + "password": { + "type": "string" + }, + "tls": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "type": "object" + }, + "datastore.KafkaPubSubConfig": { + "properties": { + "auth": { + "$ref": "#/components/schemas/datastore.KafkaAuth" + }, + "brokers": { + "items": { + "type": "string" + }, + "type": "array" + }, + "consumer_group_id": { + "type": "string" + }, + "topic_name": { + "type": "string" + } + }, + "type": "object" + }, "datastore.KeyType": { "enum": [ "project", @@ -550,6 +590,9 @@ }, "prev_page_cursor": { "type": "string" + }, + "sort": { + "type": "string" } }, "type": "object" @@ -614,6 +657,9 @@ }, "datastore.ProjectConfig": { "properties": { + "add_event_id_trace_headers": { + "type": "boolean" + }, "disable_endpoint": { "type": "boolean" }, @@ -685,6 +731,9 @@ "google": { "$ref": "#/components/schemas/datastore.GooglePubSubConfig" }, + "kafka": { + "$ref": "#/components/schemas/datastore.KafkaPubSubConfig" + }, "sqs": { "$ref": "#/components/schemas/datastore.SQSPubSubConfig" }, @@ -700,12 +749,14 @@ "datastore.PubSubType": { "enum": [ "sqs", - "google" + "google", + "kafka" ], "type": "string", "x-enum-varnames": [ "SqsPubSub", - "GooglePubSub" + "GooglePubSub", + "KafkaPubSub" ] }, "datastore.RateLimitConfiguration": { @@ -723,6 +774,9 @@ "properties": { "policy": { "type": "string" + }, + "search_policy": { + "type": "string" } }, "type": "object" @@ -1123,7 +1177,6 @@ "type": "object" }, "data": { - "description": "Data is an arbitrary JSON value that gets sent as the body of the\nwebhook to the endpoints", "items": { "type": "integer" }, @@ -1137,6 +1190,12 @@ }, "idempotency_key": { "type": "string" + }, + "owner_id": { + "type": "string" + }, + "uid": { + "type": "string" } }, "type": "object" @@ -1219,6 +1278,9 @@ "filter_config": { "$ref": "#/components/schemas/models.FilterConfiguration" }, + "function": { + "type": "string" + }, "name": { "type": "string" }, @@ -1677,6 +1739,46 @@ }, "type": "object" }, + "models.KafkaAuth": { + "properties": { + "hash": { + "type": "string" + }, + "password": { + "type": "string" + }, + "tls": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "type": "object" + }, + "models.KafkaPubSubConfig": { + "properties": { + "auth": { + "$ref": "#/components/schemas/models.KafkaAuth" + }, + "brokers": { + "items": { + "type": "string" + }, + "type": "array" + }, + "consumer_group_id": { + "type": "string" + }, + "topic_name": { + "type": "string" + } + }, + "type": "object" + }, "models.MetaEventConfiguration": { "properties": { "event_type": { @@ -1804,6 +1906,9 @@ }, "models.ProjectConfig": { "properties": { + "add_event_id_trace_headers": { + "type": "boolean" + }, "disable_endpoint": { "type": "boolean" }, @@ -1877,6 +1982,9 @@ "google": { "$ref": "#/components/schemas/models.GooglePubSubConfig" }, + "kafka": { + "$ref": "#/components/schemas/models.KafkaPubSubConfig" + }, "sqs": { "$ref": "#/components/schemas/models.SQSPubSubConfig" }, @@ -1904,6 +2012,9 @@ "properties": { "policy": { "type": "string" + }, + "search_policy": { + "type": "string" } }, "type": "object" @@ -2063,6 +2174,18 @@ }, "type": "object" }, + "models.SubscriptionFunctionResponse": { + "properties": { + "log": { + "items": { + "type": "string" + }, + "type": "array" + }, + "payload": {} + }, + "type": "object" + }, "models.SubscriptionResponse": { "properties": { "alert_config": { @@ -2088,6 +2211,9 @@ "filter_config": { "$ref": "#/components/schemas/datastore.FilterConfiguration" }, + "function": { + "$ref": "#/components/schemas/null.String" + }, "name": { "type": "string" }, @@ -2123,6 +2249,18 @@ }, "type": "object" }, + "models.TestWebhookFunction": { + "properties": { + "function": { + "type": "string" + }, + "payload": { + "additionalProperties": true, + "type": "object" + } + }, + "type": "object" + }, "models.UpdateCustomResponse": { "properties": { "body": { @@ -2250,6 +2388,9 @@ "filter_config": { "$ref": "#/components/schemas/models.FilterConfiguration" }, + "function": { + "type": "string" + }, "name": { "type": "string" }, @@ -2282,6 +2423,18 @@ }, "type": "object" }, + "null.String": { + "properties": { + "string": { + "type": "string" + }, + "valid": { + "description": "Valid is true if String is not NULL", + "type": "boolean" + } + }, + "type": "object" + }, "public.Stub": { "type": "object" }, @@ -5376,6 +5529,13 @@ "type": "string" } }, + { + "in": "query", + "name": "eventType", + "schema": { + "type": "string" + } + }, { "in": "query", "name": "idempotencyKey", @@ -8186,6 +8346,133 @@ ] } }, + "/v1/projects/{projectID}/subscriptions/test_function": { + "post": { + "description": "This endpoint validates that a filter will match a certain payload structure.", + "parameters": [ + { + "description": "Project ID", + "in": "path", + "name": "projectID", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/models.TestWebhookFunction" + } + } + }, + "description": "Function Details", + "required": true, + "x-originalParamName": "filter" + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/util.ServerResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/models.SubscriptionFunctionResponse" + } + }, + "type": "object" + } + ] + } + } + }, + "description": "OK" + }, + "400": { + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/util.ServerResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/public.Stub" + } + }, + "type": "object" + } + ] + } + } + }, + "description": "Bad Request" + }, + "401": { + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/util.ServerResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/public.Stub" + } + }, + "type": "object" + } + ] + } + } + }, + "description": "Unauthorized" + }, + "404": { + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/util.ServerResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/public.Stub" + } + }, + "type": "object" + } + ] + } + } + }, + "description": "Not Found" + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "summary": "Validate subscription filter", + "tags": [ + "Subscriptions" + ] + } + }, "/v1/projects/{projectID}/subscriptions/{subscriptionID}": { "delete": { "description": "This endpoint deletes a subscription", diff --git a/docs/v3/openapi3.yaml b/docs/v3/openapi3.yaml index d87a87ed97..cfda578e1b 100644 --- a/docs/v3/openapi3.yaml +++ b/docs/v3/openapi3.yaml @@ -288,6 +288,32 @@ components: additionalProperties: type: string type: object + datastore.KafkaAuth: + properties: + hash: + type: string + password: + type: string + tls: + type: boolean + type: + type: string + username: + type: string + type: object + datastore.KafkaPubSubConfig: + properties: + auth: + $ref: '#/components/schemas/datastore.KafkaAuth' + brokers: + items: + type: string + type: array + consumer_group_id: + type: string + topic_name: + type: string + type: object datastore.KeyType: enum: - project @@ -378,6 +404,8 @@ components: type: integer prev_page_cursor: type: string + sort: + type: string type: object datastore.PaginationData: properties: @@ -419,6 +447,8 @@ components: type: object datastore.ProjectConfig: properties: + add_event_id_trace_headers: + type: boolean disable_endpoint: type: boolean max_payload_read_size: @@ -466,6 +496,8 @@ components: properties: google: $ref: '#/components/schemas/datastore.GooglePubSubConfig' + kafka: + $ref: '#/components/schemas/datastore.KafkaPubSubConfig' sqs: $ref: '#/components/schemas/datastore.SQSPubSubConfig' type: @@ -477,10 +509,12 @@ components: enum: - sqs - google + - kafka type: string x-enum-varnames: - SqsPubSub - GooglePubSub + - KafkaPubSub datastore.RateLimitConfiguration: properties: count: @@ -492,6 +526,8 @@ components: properties: policy: type: string + search_policy: + type: string type: object datastore.RetryConfiguration: properties: @@ -761,9 +797,6 @@ components: type: string type: object data: - description: |- - Data is an arbitrary JSON value that gets sent as the body of the - webhook to the endpoints items: type: integer type: array @@ -773,6 +806,10 @@ components: type: string idempotency_key: type: string + owner_id: + type: string + uid: + type: string type: object models.CreateProject: properties: @@ -826,6 +863,8 @@ components: type: string filter_config: $ref: '#/components/schemas/models.FilterConfiguration' + function: + type: string name: type: string rate_limit_config: @@ -1132,6 +1171,32 @@ components: type: string type: array type: object + models.KafkaAuth: + properties: + hash: + type: string + password: + type: string + tls: + type: boolean + type: + type: string + username: + type: string + type: object + models.KafkaPubSubConfig: + properties: + auth: + $ref: '#/components/schemas/models.KafkaAuth' + brokers: + items: + type: string + type: array + consumer_group_id: + type: string + topic_name: + type: string + type: object models.MetaEventConfiguration: properties: event_type: @@ -1216,6 +1281,8 @@ components: type: object models.ProjectConfig: properties: + add_event_id_trace_headers: + type: boolean disable_endpoint: type: boolean max_payload_read_size: @@ -1264,6 +1331,8 @@ components: properties: google: $ref: '#/components/schemas/models.GooglePubSubConfig' + kafka: + $ref: '#/components/schemas/models.KafkaPubSubConfig' sqs: $ref: '#/components/schemas/models.SQSPubSubConfig' type: @@ -1282,6 +1351,8 @@ components: properties: policy: type: string + search_policy: + type: string type: object models.RetryConfiguration: properties: @@ -1384,6 +1455,14 @@ components: type: type: string type: object + models.SubscriptionFunctionResponse: + properties: + log: + items: + type: string + type: array + payload: {} + type: object models.SubscriptionResponse: properties: alert_config: @@ -1400,6 +1479,8 @@ components: $ref: '#/components/schemas/datastore.Endpoint' filter_config: $ref: '#/components/schemas/datastore.FilterConfiguration' + function: + $ref: '#/components/schemas/null.String' name: type: string rate_limit_config: @@ -1422,6 +1503,14 @@ components: schema: $ref: '#/components/schemas/models.FilterSchema' type: object + models.TestWebhookFunction: + properties: + function: + type: string + payload: + additionalProperties: true + type: object + type: object models.UpdateCustomResponse: properties: body: @@ -1506,6 +1595,8 @@ components: type: string filter_config: $ref: '#/components/schemas/models.FilterConfiguration' + function: + type: string name: type: string rate_limit_config: @@ -1526,6 +1617,14 @@ components: type: $ref: '#/components/schemas/datastore.VerifierType' type: object + null.String: + properties: + string: + type: string + valid: + description: Valid is true if String is not NULL + type: boolean + type: object public.Stub: type: object public.pagedResponse: @@ -3336,6 +3435,10 @@ paths: name: eventID schema: type: string + - in: query + name: eventType + schema: + type: string - in: query name: idempotencyKey schema: @@ -4915,6 +5018,75 @@ paths: summary: Validate subscription filter tags: - Subscriptions + /v1/projects/{projectID}/subscriptions/test_function: + post: + description: This endpoint validates that a filter will match a certain payload + structure. + parameters: + - description: Project ID + in: path + name: projectID + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/models.TestWebhookFunction' + description: Function Details + required: true + x-originalParamName: filter + responses: + "200": + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/util.ServerResponse' + - properties: + data: + $ref: '#/components/schemas/models.SubscriptionFunctionResponse' + type: object + description: OK + "400": + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/util.ServerResponse' + - properties: + data: + $ref: '#/components/schemas/public.Stub' + type: object + description: Bad Request + "401": + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/util.ServerResponse' + - properties: + data: + $ref: '#/components/schemas/public.Stub' + type: object + description: Unauthorized + "404": + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/util.ServerResponse' + - properties: + data: + $ref: '#/components/schemas/public.Stub' + type: object + description: Not Found + security: + - ApiKeyAuth: [] + summary: Validate subscription filter + tags: + - Subscriptions servers: - url: https://dashboard.getconvoy.io/api tags: diff --git a/go.mod b/go.mod index 2b9eba06aa..be14223522 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,8 @@ require ( github.com/aws/aws-sdk-go v1.34.28 github.com/danvixent/asynqmon v0.7.3 github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 + github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e + github.com/dop251/goja_nodejs v0.0.0-20230821135201-94e508132562 github.com/dukex/mixpanel v0.0.0-20220410140740-e82251311162 github.com/felixge/httpsnoop v1.0.2 github.com/getkin/kin-openapi v0.80.0 @@ -48,7 +50,6 @@ require ( github.com/stretchr/testify v1.8.1 github.com/swaggo/swag v1.8.12 github.com/tidwall/gjson v1.14.4 - github.com/typesense/typesense-go v0.4.0 github.com/xdg-go/pbkdf2 v1.0.0 golang.org/x/crypto v0.5.0 google.golang.org/api v0.102.0 @@ -58,8 +59,6 @@ require ( require ( github.com/dlclark/regexp2 v1.7.0 // indirect - github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e // indirect - github.com/dop251/goja_nodejs v0.0.0-20230821135201-94e508132562 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect @@ -84,7 +83,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/deepmap/oapi-codegen v1.9.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-gorp/gorp/v3 v3.0.2 // indirect @@ -112,7 +110,6 @@ require ( github.com/prometheus/procfs v0.10.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/sergi/go-diff v1.0.0 // indirect - github.com/sony/gobreaker v0.5.0 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/vmihailenco/go-tinylfu v0.2.2 // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 diff --git a/go.sum b/go.sum index d368a2ec87..10ad0bd156 100644 --- a/go.sum +++ b/go.sum @@ -109,7 +109,6 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/danvixent/asynqmon v0.7.3 h1:HHNleSIcklkBSYR6cN5y/j2sgQrKRrpccNbAyNWmNmg= github.com/danvixent/asynqmon v0.7.3/go.mod h1:Z4R8kC3PSACYzsFaL/qB9g85dycMhT0zXqRjxKnptgs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -117,12 +116,7 @@ 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= github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 h1:RAV05c0xOkJ3dZGS0JFybxFKZ2WMLabgx3uXnd7rpGs= github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= -github.com/deepmap/oapi-codegen v1.9.0 h1:qpyRY+dzjMai5QejjA53ebnBtcSvIcZOtYwVlsgdxOc= -github.com/deepmap/oapi-codegen v1.9.0/go.mod h1:7t4DbSxmAffcTEgrWvsPYEE2aOARZ8ZKWp3hDuZkHNc= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -159,9 +153,6 @@ github.com/getkin/kin-openapi v0.80.0 h1:W/s5/DNnDCR8P+pYyafEWlGk4S7/AfQUWXgrRSS github.com/getkin/kin-openapi v0.80.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-chi/chi/v5 v5.0.6 h1:CHIMAkr36TRf/zYvOqNKklMDxEm9HuqdiK+syK+tYtw= github.com/go-chi/chi/v5 v5.0.6/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= @@ -188,13 +179,6 @@ github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-redis/cache/v9 v9.0.0 h1:0thdtFo0xJi0/WXbRVu8B066z8OvVymXTJGaXrVWnN0= github.com/go-redis/cache/v9 v9.0.0/go.mod h1:cMwi1N8ASBOufbIvk7cdXe2PbPjK/WMRL95FFHWsSgI= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= @@ -225,7 +209,6 @@ github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0 github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= -github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -268,7 +251,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= @@ -375,7 +357,6 @@ github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nu github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jaswdr/faker v1.10.2 h1:GK03wuDqa8V6BE+2VRr3DJ/G4T0iUDCzVoBCj5TM4b8= github.com/jaswdr/faker v1.10.2/go.mod h1:x7ZlyB1AZqwqKZgyQlnqEG8FDptmHlncA5u2zY/yi6w= -github.com/jinzhu/copier v0.3.4 h1:mfU6jI9PtCeUjkjQ322dlff9ELjGDu975C2p/nrubVI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -386,10 +367,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -418,17 +397,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= -github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= -github.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM= -github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE= -github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= -github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA= -github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -445,16 +413,10 @@ github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -488,7 +450,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/newrelic/go-agent/v3 v3.0.0/go.mod h1:H28zDNUC0U/b7kLoY4EFOhuth10Xu/9dchozUiOseQQ= @@ -617,8 +578,6 @@ github.com/slack-go/slack v0.10.2 h1:KMN/h2sgUninHXvQI8PrR/PHBUuWp2NPvz2Kr66tki4 github.com/slack-go/slack v0.10.2/go.mod h1:5FLdBRv7VW/d9EBxx/eEktOptWygbA9K2QK/KW7ds1s= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= -github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -656,15 +615,6 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/typesense/typesense-go v0.4.0 h1:VY5PhhIWfXIrR/dnnluHkV/jgliHdnLRRVyMw+PS7QI= -github.com/typesense/typesense-go v0.4.0/go.mod h1:F9T3neLDqRr9ufFNhv1y0Qxe1Zs1GT85JlgijSjtKFo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vmihailenco/go-tinylfu v0.2.2 h1:H1eiG6HM36iniK6+21n9LLpzx1G9R3DJa2UjUjbynsI= github.com/vmihailenco/go-tinylfu v0.2.2/go.mod h1:CutYi2Q9puTxfcolkliPq4npPuofg9N9t8JVrjzwa3Q= github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= @@ -716,8 +666,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= @@ -819,8 +767,6 @@ golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -859,7 +805,6 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -868,7 +813,6 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -894,7 +838,6 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -915,10 +858,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -933,7 +874,6 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -941,7 +881,6 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -958,15 +897,11 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1014,14 +949,12 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= diff --git a/internal/pkg/middleware/middleware.go b/internal/pkg/middleware/middleware.go index bb34404ee5..02c882111f 100644 --- a/internal/pkg/middleware/middleware.go +++ b/internal/pkg/middleware/middleware.go @@ -227,6 +227,7 @@ func GetAuthFromRequest(r *http.Request) (*auth.Credential, error) { func Pagination(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { rawPerPage := r.URL.Query().Get("perPage") + sort := r.URL.Query().Get("sort") rawDirection := r.URL.Query().Get("direction") rawNextCursor := r.URL.Query().Get("next_page_cursor") rawPrevCursor := r.URL.Query().Get("prev_page_cursor") @@ -239,25 +240,19 @@ func Pagination(next http.Handler) http.Handler { rawDirection = "next" } - if len(rawNextCursor) == 0 { - rawNextCursor = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF" - } - - if len(rawPrevCursor) == 0 { - rawPrevCursor = "" - } - perPage, err := strconv.Atoi(rawPerPage) if err != nil { perPage = 20 } pageable := datastore.Pageable{ + Sort: strings.ToUpper(sort), PerPage: perPage, Direction: datastore.PageDirection(rawDirection), NextCursor: rawNextCursor, PrevCursor: rawPrevCursor, } + pageable.SetCursors() r = r.WithContext(setPageableInContext(r.Context(), pageable)) next.ServeHTTP(w, r) diff --git a/mocks/repository.go b/mocks/repository.go index 0243d2f74e..411888ddfa 100644 --- a/mocks/repository.go +++ b/mocks/repository.go @@ -312,9 +312,9 @@ func (mr *MockEventDeliveryRepositoryMockRecorder) LoadEventDeliveriesIntervals( } // LoadEventDeliveriesPaged mocks base method. -func (m *MockEventDeliveryRepository) LoadEventDeliveriesPaged(ctx context.Context, projectID string, endpointIDs []string, eventID, subscriptionID string, status []datastore.EventDeliveryStatus, params datastore.SearchParams, pageable datastore.Pageable, idempotencyKey string) ([]datastore.EventDelivery, datastore.PaginationData, error) { +func (m *MockEventDeliveryRepository) LoadEventDeliveriesPaged(ctx context.Context, projectID string, endpointIDs []string, eventID, subscriptionID string, status []datastore.EventDeliveryStatus, params datastore.SearchParams, pageable datastore.Pageable, idempotencyKey, eventType string) ([]datastore.EventDelivery, datastore.PaginationData, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LoadEventDeliveriesPaged", ctx, projectID, endpointIDs, eventID, subscriptionID, status, params, pageable, idempotencyKey) + ret := m.ctrl.Call(m, "LoadEventDeliveriesPaged", ctx, projectID, endpointIDs, eventID, subscriptionID, status, params, pageable, idempotencyKey, eventType) ret0, _ := ret[0].([]datastore.EventDelivery) ret1, _ := ret[1].(datastore.PaginationData) ret2, _ := ret[2].(error) @@ -322,9 +322,9 @@ func (m *MockEventDeliveryRepository) LoadEventDeliveriesPaged(ctx context.Conte } // LoadEventDeliveriesPaged indicates an expected call of LoadEventDeliveriesPaged. -func (mr *MockEventDeliveryRepositoryMockRecorder) LoadEventDeliveriesPaged(ctx, projectID, endpointIDs, eventID, subscriptionID, status, params, pageable, idempotencyKey interface{}) *gomock.Call { +func (mr *MockEventDeliveryRepositoryMockRecorder) LoadEventDeliveriesPaged(ctx, projectID, endpointIDs, eventID, subscriptionID, status, params, pageable, idempotencyKey, eventType interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadEventDeliveriesPaged", reflect.TypeOf((*MockEventDeliveryRepository)(nil).LoadEventDeliveriesPaged), ctx, projectID, endpointIDs, eventID, subscriptionID, status, params, pageable, idempotencyKey) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadEventDeliveriesPaged", reflect.TypeOf((*MockEventDeliveryRepository)(nil).LoadEventDeliveriesPaged), ctx, projectID, endpointIDs, eventID, subscriptionID, status, params, pageable, idempotencyKey, eventType) } // UpdateEventDeliveryWithAttempt mocks base method. diff --git a/services/batch_retry_event_delivery.go b/services/batch_retry_event_delivery.go index 58e1139226..104479d848 100644 --- a/services/batch_retry_event_delivery.go +++ b/services/batch_retry_event_delivery.go @@ -18,7 +18,7 @@ type BatchRetryEventDeliveryService struct { } func (e *BatchRetryEventDeliveryService) Run(ctx context.Context) (int, int, error) { - deliveries, _, err := e.EventDeliveryRepo.LoadEventDeliveriesPaged(ctx, e.Filter.Project.UID, e.Filter.EndpointIDs, e.Filter.EventID, e.Filter.SubscriptionID, e.Filter.Status, e.Filter.SearchParams, e.Filter.Pageable, e.Filter.IdempotencyKey) + deliveries, _, err := e.EventDeliveryRepo.LoadEventDeliveriesPaged(ctx, e.Filter.Project.UID, e.Filter.EndpointIDs, e.Filter.EventID, e.Filter.SubscriptionID, e.Filter.Status, e.Filter.SearchParams, e.Filter.Pageable, e.Filter.IdempotencyKey, e.Filter.EventType) if err != nil { log.FromContext(ctx).WithError(err).Error("failed to fetch event deliveries by ids") return 0, 0, &ServiceError{ErrMsg: "failed to fetch event deliveries", Err: err} diff --git a/services/batch_retry_event_delivery_test.go b/services/batch_retry_event_delivery_test.go index 3e3b1b955b..3b5303f102 100644 --- a/services/batch_retry_event_delivery_test.go +++ b/services/batch_retry_event_delivery_test.go @@ -83,7 +83,7 @@ func TestBatchRetryEventDeliveryService_Run(t *testing.T) { Direction: datastore.Next, NextCursor: datastore.DefaultCursor, }, - gomock.Any()). + gomock.Any(), gomock.Any()). Times(1). Return( []datastore.EventDelivery{ @@ -154,7 +154,7 @@ func TestBatchRetryEventDeliveryService_Run(t *testing.T) { Direction: datastore.Next, NextCursor: datastore.DefaultCursor, }, - gomock.Any()). + gomock.Any(), gomock.Any()). Times(1). Return( []datastore.EventDelivery{ diff --git a/web/ui/dashboard/src/app/components/date-picker/date-picker.component.html b/web/ui/dashboard/src/app/components/date-picker/date-picker.component.html index 5364ffa3a9..e31eac99b0 100644 --- a/web/ui/dashboard/src/app/components/date-picker/date-picker.component.html +++ b/web/ui/dashboard/src/app/components/date-picker/date-picker.component.html @@ -1,26 +1,10 @@ -
+
- - +
+ +
-
+
diff --git a/web/ui/dashboard/src/app/components/date-picker/date-picker.component.ts b/web/ui/dashboard/src/app/components/date-picker/date-picker.component.ts index 8b8889d6a0..64a74f770e 100644 --- a/web/ui/dashboard/src/app/components/date-picker/date-picker.component.ts +++ b/web/ui/dashboard/src/app/components/date-picker/date-picker.component.ts @@ -26,7 +26,10 @@ export class DatePickerComponent implements OnInit { @Output() selectedDateRange = new EventEmitter(); @Output() selectedDate = new EventEmitter(); @Output() clearDates = new EventEmitter(); - @Input('formType') formType: 'filter' = 'filter'; + @Output() close = new EventEmitter(); + @Input('show') show = false; + @Input('formType') formType: 'filter' | 'form' = 'filter'; + @Input('position') position: 'right' | 'left' | 'center' | 'right-side' = 'left'; @Input('dateRangeValue') dateRangeValue?: { startDate: string | Date; endDate: string | Date; @@ -75,6 +78,8 @@ export class DatePickerComponent implements OnInit { if (this.dateValue) this.selectedStartDay = new Date(this.dateValue).getTime(); this.initDatePicker(); + + if (this.show) this.showPicker = true; } initDatePicker() { diff --git a/web/ui/dashboard/src/app/components/dropdown-container/dropdown-container.component.ts b/web/ui/dashboard/src/app/components/dropdown-container/dropdown-container.component.ts index 8bccb291cd..ef7811e953 100644 --- a/web/ui/dashboard/src/app/components/dropdown-container/dropdown-container.component.ts +++ b/web/ui/dashboard/src/app/components/dropdown-container/dropdown-container.component.ts @@ -3,10 +3,10 @@ import { Directive, Input, OnInit } from '@angular/core'; @Directive({ selector: '[convoy-dropdown-container]', standalone: true, - host: { class: 'absolute top-[110%] w-full bg-white-100 border border-grey-10 rounded-12px shadow-default z-10 transition-all ease-in-out duration-300 h-fit max-h-[440px]', '[class]': 'classes' } + host: { class: 'absolute w-full bg-white-100 border border-grey-10 rounded-12px shadow-default z-10 transition-all ease-in-out duration-300 h-fit max-h-[440px]', '[class]': 'classes' } }) export class DropdownContainerComponent implements OnInit { - @Input('position') position: 'right' | 'left' | 'center' = 'right'; + @Input('position') position: 'right' | 'left' | 'center' | 'right-side' = 'right'; @Input('size') size: 'sm' | 'md' | 'lg' | 'xl' | 'full' = 'md'; @Input('show') show = false; @Input('className') class!: string; @@ -18,9 +18,10 @@ export class DropdownContainerComponent implements OnInit { get classes(): string { const positions = { - right: 'right-[5%]', - left: 'left-[5%]', - center: 'left-0' + right: 'top-[110%] right-[5%]', + left: 'top-[110%] left-[5%]', + center: 'top-[110%] left-0', + 'right-side': 'top-0 left-[105%]' }; return `${this.sizes[this.size]} ${positions[this.position]} ${this.show ? 'opacity-100 h-fit pointer-events-auto overflow-y-auto overflow-x-hidden' : 'opacity-0 h-0 overflow-hidden pointer-events-none'} ${this.class}`; } diff --git a/web/ui/dashboard/src/app/components/dropdown/dropdown.component.html b/web/ui/dashboard/src/app/components/dropdown/dropdown.component.html index 122e3e0dac..935df2e022 100644 --- a/web/ui/dashboard/src/app/components/dropdown/dropdown.component.html +++ b/web/ui/dashboard/src/app/components/dropdown/dropdown.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/web/ui/dashboard/src/app/components/dropdown/dropdown.component.ts b/web/ui/dashboard/src/app/components/dropdown/dropdown.component.ts index 89178bbed1..b9775efd8c 100644 --- a/web/ui/dashboard/src/app/components/dropdown/dropdown.component.ts +++ b/web/ui/dashboard/src/app/components/dropdown/dropdown.component.ts @@ -35,16 +35,20 @@ export class DropdownOptionDirective { changeDetection: ChangeDetectionStrategy.Default }) export class DropdownComponent implements OnInit { - @Input('position') position: 'right' | 'left' | 'center' = 'right'; + @Input('position') position: 'right' | 'left' | 'center' | 'right-side' = 'right'; @Input('size') size: 'sm' | 'md' | 'lg' | 'xl' | 'full' = 'md'; + @Input('show') showDropdown = false; @ViewChild('dropdownTriggerContainer', { static: true }) dropdownTriggerContainer!: ElementRef; @ViewChild('dropdownContainer', { static: true }) dropdownOptions!: ElementRef; @Output() readonly onSelect = new EventEmitter; + @Output('close') close = new EventEmitter(); show = false; constructor() {} ngOnInit(): void { + if(this.showDropdown) this.show = true; + this.dropdownTriggerContainer.nativeElement.children[0].addEventListener('click', () => (this.show = !this.show)); } } diff --git a/web/ui/dashboard/src/app/models/event.model.ts b/web/ui/dashboard/src/app/models/event.model.ts index 95fa253aba..7a972c372d 100644 --- a/web/ui/dashboard/src/app/models/event.model.ts +++ b/web/ui/dashboard/src/app/models/event.model.ts @@ -9,7 +9,7 @@ export interface EVENT { uid: string; updated_at: string; app_id?: string; - idempotency_key: string; + idempotency_key: string; is_duplicate_event: boolean; event_type: string; data: any; @@ -68,7 +68,6 @@ export interface EVENT_DELIVERY_ATTEMPT { error?: string; } - export interface FILTER_QUERY_PARAM { startDate?: string; endDate?: string; @@ -81,5 +80,7 @@ export interface FILTER_QUERY_PARAM { prev_page_cursor?: string; direction?: 'next' | 'prev'; showLoader?: boolean; - query?:string; + query?: string; + sort?: string; + eventType?: string; } diff --git a/web/ui/dashboard/src/app/private/components/endpoints-filter/endpoints-filter.component.html b/web/ui/dashboard/src/app/private/components/endpoints-filter/endpoints-filter.component.html index f71dd660f4..4fa4643a37 100644 --- a/web/ui/dashboard/src/app/private/components/endpoints-filter/endpoints-filter.component.html +++ b/web/ui/dashboard/src/app/private/components/endpoints-filter/endpoints-filter.component.html @@ -1,12 +1,7 @@ -
- +
+ + +
@@ -16,29 +11,17 @@
    -
  • - - +
  • +
  • @@ -63,9 +46,5 @@
-
- - -
diff --git a/web/ui/dashboard/src/app/private/components/endpoints-filter/endpoints-filter.component.ts b/web/ui/dashboard/src/app/private/components/endpoints-filter/endpoints-filter.component.ts index ca2ef786e2..ab5b456998 100644 --- a/web/ui/dashboard/src/app/private/components/endpoints-filter/endpoints-filter.component.ts +++ b/web/ui/dashboard/src/app/private/components/endpoints-filter/endpoints-filter.component.ts @@ -19,11 +19,14 @@ import { ButtonComponent } from 'src/app/components/button/button.component'; export class EndpointFilterComponent implements OnInit { @ViewChild('endpoint', { static: true }) eventDelsEndpointFilter!: ElementRef; @Input('endpoint') endpointId!: string | undefined; + @Input('show') show: boolean = false; + @Input('position') position: 'right' | 'left' | 'center' | 'right-side' = 'left'; @Output('clear') clearEndpoint = new EventEmitter(); @Output('set') setEndpoint = new EventEmitter(); + @Output('setEndpoint') setSelectedEndpoint = new EventEmitter(); loadingFilterEndpoints = false; endpoints$!: Observable; - selectedEndpoint!: string; + selectedEndpoint!: ENDPOINT; constructor(public privateService: PrivateService) {} @@ -39,10 +42,6 @@ export class EndpointFilterComponent implements OnInit { ); } - selectEndpoint(event: any, endpointId: string) { - this.selectedEndpoint = event.target.checked ? endpointId : ''; - } - async getEndpointsForFilter(search: string): Promise { return await ( await this.privateService.getEndpoints({ q: search }) diff --git a/web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.html b/web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.html new file mode 100644 index 0000000000..6440efea8f --- /dev/null +++ b/web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.html @@ -0,0 +1,225 @@ +
+
+
+
+
+ search icon + + +
+
+ + + +
+ + +
+ +
+
+ +
+
+
Date
+
+ {{ queryParams.startDate | date: 'dd/MM/yy, h:mm a' }} - {{ queryParams.endDate | date: 'dd/MM/yy, h:mm a' }} + + +
+
+ + + + + + + +
+
Status
+
+ {{ status || 'None' }} + + +
+
+ + +
+
Status
+ + +
    +
  • + +
  • +
+ +
+ + +
+
+
+
+ +
+
Source
+
+ {{ eventDeliveriesSourceData?.name }} + + +
+
+ + +
+
Sources
+ +
+
    +
  • + +
  • +
+
+
+
+ +
+
Endpoint
+
+ {{ eventDeliveriesEndpointData?.title || eventDeliveriesEndpoint }} + + +
+
+ + + + + + + +
+
Event Type
+
+ {{ eventDelEventType }} + + +
+
+ + +
+
Event Type
+ + +
+
+ search icon + + +
+
+
+
+
+ +
+
+ + +
    +
  • + +
  • +
+
+ + + +
+
+
+ + +
diff --git a/web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.scss b/web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.spec.ts b/web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.spec.ts new file mode 100644 index 0000000000..027d627203 --- /dev/null +++ b/web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EventDeliveryFilterComponent } from './event-delivery-filter.component'; + +describe('EventDeliveryFilterComponent', () => { + let component: EventDeliveryFilterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ EventDeliveryFilterComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(EventDeliveryFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.ts b/web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.ts new file mode 100644 index 0000000000..41f01301ae --- /dev/null +++ b/web/ui/dashboard/src/app/private/components/event-delivery-filter/event-delivery-filter.component.ts @@ -0,0 +1,265 @@ +import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { CommonModule, Location } from '@angular/common'; +import { ButtonComponent } from 'src/app/components/button/button.component'; +import { DatePickerComponent } from 'src/app/components/date-picker/date-picker.component'; +import { EndpointFilterComponent } from '../endpoints-filter/endpoints-filter.component'; +import { DropdownComponent, DropdownOptionDirective } from 'src/app/components/dropdown/dropdown.component'; +import { FILTER_QUERY_PARAM } from 'src/app/models/event.model'; +import { ActivatedRoute } from '@angular/router'; +import { ListItemComponent } from 'src/app/components/list-item/list-item.component'; +import { ProjectService } from '../../pages/project/project.service'; +import { SOURCE } from 'src/app/models/source.model'; +import { PrivateService } from '../../private.service'; +import { ENDPOINT } from 'src/app/models/endpoint.model'; +import { FormsModule } from '@angular/forms'; +import { GeneralService } from 'src/app/services/general/general.service'; + +@Component({ + selector: 'convoy-event-delivery-filter', + standalone: true, + imports: [CommonModule, ButtonComponent, DatePickerComponent, EndpointFilterComponent, DropdownComponent, DropdownOptionDirective, ListItemComponent, FormsModule], + templateUrl: './event-delivery-filter.component.html', + styleUrls: ['./event-delivery-filter.component.scss'] +}) +export class EventDeliveryFilterComponent implements OnInit { + @ViewChild('datePicker', { static: true }) datePicker!: DatePickerComponent; + @ViewChild('eventTypeFilter', { static: false }) eventTypeFilter?: any; + + @Input('type') type: 'deliveries' | 'logs' = 'deliveries'; + + @Output('sortEvents') sort = new EventEmitter(); + @Output('filter') filter = new EventEmitter(); + @Output('tail') tail = new EventEmitter(); + @Output('batchRetry') batchRetry = new EventEmitter(); + + sortOrder: 'asc' | 'desc' | string = 'desc'; + + eventDeliveryStatuses = ['Success', 'Failure', 'Retry', 'Scheduled', 'Processing', 'Discarded']; + eventDeliveryFilteredByStatus: string[] = []; + + eventDeliveriesSource?: string; + eventDeliveriesSourceData?: SOURCE; + filterSources?: SOURCE[]; + + eventDeliveriesEndpoint?: string; + eventDeliveriesEndpointData?: ENDPOINT; + + eventDelEventType?: string; + eventsTypeSearchString!: string; + + eventsSearchString!: string; + + portalToken = this.route.snapshot.queryParams?.token; + + queryParams: FILTER_QUERY_PARAM = {}; + enableTailMode = false; + filterOptions = [ + { name: 'Date', id: 'date', show: false }, + { name: 'Status', id: 'status', show: false }, + { name: 'Source', id: 'source', show: false }, + { name: 'Endpoint', id: 'endpoint', show: false }, + { name: 'Event type', id: 'eventType', show: false } + ]; + constructor(private route: ActivatedRoute, private _location: Location, public projectService: ProjectService, private privateService: PrivateService, private generalService: GeneralService) {} + + async ngOnInit() { + const data = this.getFiltersFromURL(); + if (this.checkIfTailModeIsEnabled()) this.tail.emit({ data: this.queryParams, tailModeConfig: this.checkIfTailModeIsEnabled() }); + this.filter.emit(data); + + if (this.type === 'logs') this.projectService.activeProjectDetails?.type == 'outgoing' ? this.filterOptions.splice(1, 4) : this.filterOptions.splice(1, 4, { name: 'Source', id: 'source', show: false }); + else this.projectService.activeProjectDetails?.type == 'incoming' ? this.filterOptions.splice(3, 2) : this.filterOptions.splice(2, 1); + + if (this.portalToken) this.filterOptions = this.filterOptions.filter(key => key.id !== 'endpoint'); + + if (this.eventDeliveriesSource) this.eventDeliveriesSourceData = await this.getSelectedSourceData(); + + if (this.eventDeliveriesEndpoint) this.eventDeliveriesEndpointData = await this.getSelectedEndpointData(); + + if (!this.portalToken || this.projectService.activeProjectDetails?.type == 'incoming') this.getSourcesForFilter(); + } + + getFiltersFromURL() { + this.queryParams = { ...this.queryParams, ...this.route.snapshot.queryParams }; + + // set filter status if any exists in URL + this.eventDeliveryFilteredByStatus = this.queryParams.status ? JSON.parse(this.queryParams.status) : []; + + this.eventDeliveriesSource = this.queryParams?.sourceId; + this.eventDeliveriesEndpoint = this.queryParams?.endpointId; + + this.eventDelEventType = this.queryParams?.eventType; + + this.sortOrder = this.queryParams?.sort || 'desc'; + + this.eventsSearchString = this.queryParams?.query || ''; + + return this.queryParams; + } + + clearFilters(filterType?: 'startDate' | 'endDate' | 'eventId' | 'endpointId' | 'status' | 'sourceId' | 'next_page_cursor' | 'prev_page_cursor' | 'direction' | 'eventType') { + if (filterType && this.queryParams) { + // if filter to clear start date or end date, it means clear date filter. :) + if (filterType === 'startDate' || filterType === 'endDate') { + delete this.queryParams['startDate']; + delete this.queryParams['endDate']; + this.datePicker?.clearDate(); + } else if (filterType === 'sourceId') { + this.eventDeliveriesSource = ''; + delete this.queryParams['sourceId']; + } else if (filterType === 'endpointId') { + this.eventDeliveriesEndpoint = ''; + delete this.queryParams['endpointId']; + } else if (filterType === 'eventType') { + this.eventDelEventType = ''; + this.eventsTypeSearchString = ''; + delete this.queryParams['eventType']; + } else if (filterType === 'eventId') { + delete this.queryParams['eventId']; + delete this.queryParams['idempotencyKey']; + } else delete this.queryParams[filterType]; + + const cleanedQuery: any = Object.fromEntries(Object.entries(this.queryParams).filter(([_, q]) => q !== '' && q !== undefined && q !== null)); + const queryParams = new URLSearchParams(cleanedQuery).toString(); + this._location.go(`${location.pathname}?${this.portalToken ? `token=${this.portalToken}` : ''}${queryParams}`); + } else { + this.eventDelEventType = ''; + this.eventsTypeSearchString = ''; + this.eventDeliveriesSource = ''; + this.eventDeliveriesEndpoint = ''; + this.eventDeliveryFilteredByStatus = []; + this.datePicker?.clearDate(); + const sortParam = this.queryParams.sort; + + this.queryParams = {}; + this._location.go(`${location.pathname}${this.portalToken ? `?token=${this.portalToken}` : ''}`); + } + + this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); + this.filter.emit(); + } + + getSelectedDateRange(dateRange: { startDate: string; endDate: string }) { + this.queryParams = this.generalService.addFilterToURL({ ...this.queryParams, ...dateRange }); + this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); + this.filter.emit(this.queryParams); + } + + selectStatusFilter(status: string) { + if (!this.eventDeliveryFilteredByStatus?.includes(status)) this.eventDeliveryFilteredByStatus.push(status); + } + + removeStatusFilter(status: string) { + this.eventDeliveryFilteredByStatus = this.eventDeliveryFilteredByStatus.filter(e => e !== status); + this.getSelectedStatusFilter(); + } + + getSelectedStatusFilter() { + const eventDelsStatus = this.eventDeliveryFilteredByStatus.length > 0 ? JSON.stringify(this.eventDeliveryFilteredByStatus) : ''; + this.queryParams = this.generalService.addFilterToURL({ ...this.queryParams, status: eventDelsStatus }); + this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); + this.filter.emit(this.queryParams); + this.toggleFilter('status', false); + } + + updateSourceFilter(source: SOURCE) { + this.eventDeliveriesSource = source.uid; + this.eventDeliveriesSourceData = source; + this.queryParams = this.generalService.addFilterToURL({ ...this.queryParams, sourceId: this.eventDeliveriesSource }); + this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); + this.filter.emit(this.queryParams); + } + + updateEndpointFilter(endpoint: ENDPOINT) { + this.eventDeliveriesEndpoint = endpoint.uid; + this.eventDeliveriesEndpointData = endpoint; + this.queryParams = this.generalService.addFilterToURL({ ...this.queryParams, endpointId: this.eventDeliveriesEndpoint }); + this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); + this.filter.emit(this.queryParams); + } + + searchEvents() { + this.queryParams = this.generalService.addFilterToURL({ ...this.queryParams, query: this.eventsSearchString }); + this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); + this.filter.emit(this.queryParams); + } + + setEventType() { + this.eventDelEventType = this.eventsTypeSearchString; + this.queryParams = this.generalService.addFilterToURL({ ...this.queryParams, eventType: this.eventsTypeSearchString }); + this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); + this.filter.emit(this.queryParams); + this.toggleFilter('eventType', false); + } + + toggleSortOrder() { + this.sortOrder === 'asc' ? (this.sortOrder = 'desc') : (this.sortOrder = 'asc'); + this.queryParams = this.generalService.addFilterToURL({ ...this.queryParams, sort: this.sortOrder }); + // this.type === 'logs' ? localStorage.setItem('EVENTS_LOGS_SORT_ORDER', this.sortOrder) : localStorage.setItem('EVENTS_SORT_ORDER', this.sortOrder); + this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); + this.filter.emit(this.queryParams); + } + + // getSortOrder() { + // const sortOrderConfig = this.type === 'logs' ? localStorage.getItem('EVENTS_LOGS_SORT_ORDER') : localStorage.getItem('EVENTS_SORT_ORDER'); + // this.sortOrder = sortOrderConfig || 'desc'; + // } + + toggleTailMode(e?: any, status?: 'on' | 'off') { + let tailModeConfig: boolean; + if (status) tailModeConfig = status === 'on'; + else tailModeConfig = e.target.checked; + + this.enableTailMode = tailModeConfig; + this.type === 'logs' ? localStorage.setItem('EVENT_LOGS_TAIL_MODE', JSON.stringify(tailModeConfig)) : localStorage.setItem('EVENTS_TAIL_MODE', JSON.stringify(tailModeConfig)); + + this.tail.emit({ data: this.queryParams, tailModeConfig }); + } + + checkIfTailModeIsEnabled() { + const tailModeConfig = this.type === 'logs' ? localStorage.getItem('EVENT_LOGS_TAIL_MODE') : localStorage.getItem('EVENTS_TAIL_MODE'); + this.enableTailMode = tailModeConfig ? JSON.parse(tailModeConfig) : false; + + return this.enableTailMode; + } + + toggleFilter(filterValue: string, show: boolean) { + this.filterOptions.forEach(filter => { + if (filter.id === filterValue) filter.show = show; + }); + } + + showFilter(filterValue: string): boolean { + return this.filterOptions.find(filter => filter.id === filterValue)?.show || false; + } + + isAnyFilterSelected(): Boolean { + return ( + (this.queryParams && + (Object.keys(this.queryParams).includes('sort') || !Object.keys(this.queryParams).includes('sort')) && + (Object.keys(this.queryParams).includes('token') || !Object.keys(this.queryParams).includes('token')) && + ((Object.keys(this.queryParams).length > 0 && !Object.keys(this.queryParams).includes('sort') && !Object.keys(this.queryParams).includes('token')) || + (Object.keys(this.queryParams).length > 1 && (Object.keys(this.queryParams).includes('sort') || Object.keys(this.queryParams).includes('token'))))) || + false + ); + } + + async getSelectedEndpointData(): Promise { + return await (await this.privateService.getEndpoints()).data.content.find((item: ENDPOINT) => item.uid === this.eventDeliveriesEndpoint); + } + + async getSelectedSourceData(): Promise { + return await (await this.privateService.getSources()).data.content.find((item: SOURCE) => item.uid === this.eventDeliveriesSource); + } + + async getSourcesForFilter() { + try { + const sourcesResponse = (await this.privateService.getSources()).data.content; + this.filterSources = sourcesResponse; + } catch (error) {} + } + + showBatchRetry() { + this.batchRetry.emit(this.queryParams); + } +} diff --git a/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.component.html b/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.component.html index 683d7aac08..ac26fd00b7 100644 --- a/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.component.html +++ b/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.component.html @@ -5,100 +5,9 @@

Events Log

-
-
-
- search icon - - -
- - - -
- - -
-
    -
  • - - -
  • -
- -
- - -
-
-
+ - -
- - -
- -
- - -
-
diff --git a/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.component.ts b/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.component.ts index 6e481d1fe1..e7768c200e 100644 --- a/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.component.ts +++ b/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.component.ts @@ -1,5 +1,5 @@ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; -import { CommonModule, Location } from '@angular/common'; +import { CommonModule } from '@angular/common'; import { PrivateService } from 'src/app/private/private.service'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { CardComponent } from 'src/app/components/card/card.component'; @@ -24,6 +24,7 @@ import { EventsService } from '../events/events.service'; import { PaginationComponent } from 'src/app/private/components/pagination/pagination.component'; import { CopyButtonComponent } from 'src/app/components/copy-button/copy-button.component'; import { ListItemComponent } from 'src/app/components/list-item/list-item.component'; +import { EventDeliveryFilterComponent } from 'src/app/private/components/event-delivery-filter/event-delivery-filter.component'; @Component({ selector: 'convoy-event-logs', @@ -51,7 +52,8 @@ import { ListItemComponent } from 'src/app/components/list-item/list-item.compon CopyButtonComponent, ListItemComponent, DropdownOptionDirective, - DialogDirective + DialogDirective, + EventDeliveryFilterComponent ], templateUrl: './event-logs.component.html', styleUrls: ['./event-logs.component.scss'] @@ -61,7 +63,6 @@ export class EventLogsComponent implements OnInit { eventsDateFilterFromURL: { startDate: string; endDate: string } = { startDate: '', endDate: '' }; eventLogsTableHead: string[] = ['Event ID', 'Source', 'Time', '']; dateOptions = ['Last Year', 'Last Month', 'Last Week', 'Yesterday']; - eventsSearchString?: string; eventSource?: string; isloadingEvents: boolean = false; eventDetailsTabs = [ @@ -84,129 +85,46 @@ export class EventLogsComponent implements OnInit { isFetchingDuplicateEvents = false; batchRetryCount: any; getEventsInterval: any; - queryParams?: FILTER_QUERY_PARAM; + queryParams: FILTER_QUERY_PARAM = {}; enableTailMode = false; + sortOrder: 'asc' | 'desc' = 'desc'; - constructor(private eventsLogService: EventLogsService, public generalService: GeneralService, public route: ActivatedRoute, private router: Router, public privateService: PrivateService, private eventsService: EventsService, private _location: Location) {} + constructor(private eventsLogService: EventLogsService, public generalService: GeneralService, public route: ActivatedRoute, private router: Router, public privateService: PrivateService, private eventsService: EventsService) {} - async ngOnInit() { - const data = this.getFiltersFromURL(); - this.getEventLogs({ ...data, showLoader: true }); - if (this.checkIfTailModeIsEnabled()) this.getEventsAtInterval(); - if (!this.portalToken) this.getSourcesForFilter(); - } + ngOnInit() {} ngOnDestroy() { clearInterval(this.getEventsInterval); } - async getSourcesForFilter() { - try { - const sourcesResponse = (await this.privateService.getSources()).data.content; - this.filterSources = sourcesResponse; - } catch (error) {} - } - - searchEvents() { - const data = this.addFilterToURL({ query: this.eventsSearchString }); - this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); - this.getEventLogs({ ...data, showLoader: true }); - } - - paginateEvents(event: CURSOR) { - const data = this.addFilterToURL(event); - this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); - this.getEventLogs({ ...data, showLoader: true }); - } - - updateSourceFilter() { - const data = this.addFilterToURL({ sourceId: this.eventSource }); - this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); - this.getEventLogs({ ...data, showLoader: true }); - } - - getSelectedDateRange(dateRange: { startDate: string; endDate: string }) { - this.eventsDateFilterFromURL = dateRange; - const data = this.addFilterToURL(dateRange); - this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); + fetchEventLogs(requestDetails: FILTER_QUERY_PARAM) { + const data = requestDetails; + this.queryParams = data; this.getEventLogs({ ...data, showLoader: true }); } - // fetch filters from url - getFiltersFromURL() { - this.queryParams = { ...this.queryParams, ...this.route.snapshot.queryParams }; - - this.eventsDateFilterFromURL = { startDate: this.queryParams?.startDate || '', endDate: this.queryParams?.endDate || '' }; - this.eventsSearchString = this.queryParams.query ?? undefined; - this.eventSource = this.queryParams.sourceId; + handleTailing(tailDetails: { data: FILTER_QUERY_PARAM; tailModeConfig: boolean }) { + this.queryParams = tailDetails.data; - return this.queryParams; + clearInterval(this.getEventsInterval); + if (tailDetails.tailModeConfig) this.getEventsAtInterval(tailDetails.data); } - // fetch and add new filter to url - addFilterToURL(params?: FILTER_QUERY_PARAM) { - this.queryParams = { ...this.queryParams, ...this.route.snapshot.queryParams, ...params }; - - if (!params?.next_page_cursor) delete this.queryParams.next_page_cursor; - if (!params?.prev_page_cursor) delete this.queryParams.prev_page_cursor; - - const cleanedQuery: any = Object.fromEntries(Object.entries(this.queryParams).filter(([_, q]) => q !== '' && q !== undefined && q !== null)); - const queryParams = new URLSearchParams(cleanedQuery).toString(); - this._location.go(`${location.pathname}?${queryParams}`); - - return this.queryParams; + getEventsAtInterval(data: FILTER_QUERY_PARAM) { + this.getEventsInterval = setInterval(() => { + this.getEventLogs(data); + }, 5000); } - // clear filters - clearEventFilters(filterType?: 'startDate' | 'endDate' | 'sourceId' | 'next_page_cursor' | 'prev_page_cursor' | 'direction') { - if (filterType && this.queryParams) { - if (filterType === 'startDate' || filterType === 'endDate') { - delete this.queryParams['startDate']; - delete this.queryParams['endDate']; - } else if (filterType === 'sourceId') { - this.eventSource = ''; - delete this.queryParams['sourceId']; - } else delete this.queryParams[filterType]; - - const cleanedQuery: any = Object.fromEntries(Object.entries(this.queryParams).filter(([_, q]) => q !== '' && q !== undefined && q !== null)); - const queryParams = new URLSearchParams(cleanedQuery).toString(); - this._location.go(`${location.pathname}?${queryParams}`); - } else { - this.datePicker.clearDate(); - this.queryParams = {}; - this.eventsDateFilterFromURL = { startDate: '', endDate: '' }; - this.eventsSearchString = ''; - this.eventSource = ''; - this._location.go(`${location.pathname}`); - } - - this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); - this.getEventLogs({ showLoader: true }); + paginateEvents(event: CURSOR) { + this.queryParams = this.generalService.addFilterToURL({...this.queryParams, ...event}); + this.handleTailing({ data: this.queryParams, tailModeConfig: this.checkIfTailModeIsEnabled() }); + this.getEventLogs({ ...this.queryParams, showLoader: true }); } checkIfTailModeIsEnabled() { const tailModeConfig = localStorage.getItem('EVENT_LOGS_TAIL_MODE'); - this.enableTailMode = tailModeConfig ? JSON.parse(tailModeConfig) : false; - return this.enableTailMode; - } - - toggleTailMode(e: any, status?: 'on' | 'off') { - let tailModeConfig: boolean; - if (status) tailModeConfig = status === 'on'; - else tailModeConfig = e.target.checked; - - this.enableTailMode = tailModeConfig; - localStorage.setItem('EVENT_LOGS_TAIL_MODE', JSON.stringify(tailModeConfig)); - - clearInterval(this.getEventsInterval); - if (tailModeConfig) this.getEventsAtInterval(); - } - - getEventsAtInterval() { - this.getEventsInterval = setInterval(() => { - const data = { ...this.queryParams, ...this.route.snapshot.queryParams }; - this.getEventLogs(data); - }, 5000); + return tailModeConfig ? JSON.parse(tailModeConfig) : false; } async getEventLogs(requestDetails?: FILTER_QUERY_PARAM) { @@ -216,7 +134,7 @@ export class EventLogsComponent implements OnInit { const eventsResponse = await this.eventsService.getEvents(requestDetails); this.events = eventsResponse.data; - this.displayedEvents = await this.generalService.setContentDisplayed(eventsResponse.data.content); + this.displayedEvents = await this.generalService.setContentDisplayed(eventsResponse.data.content, this.queryParams?.sort || 'desc'); this.isloadingEvents = false; if (this.eventsDetailsItem) return; @@ -266,16 +184,14 @@ export class EventLogsComponent implements OnInit { } } - async fetchRetryCount() { + async fetchRetryCount(data: FILTER_QUERY_PARAM) { + this.queryParams = data; + + if (!data) return; const page = this.route.snapshot.queryParams.page || 1; this.fetchingCount = true; try { - const response = await this.eventsLogService.getRetryCount({ - page: page, - startDate: this.eventsDateFilterFromURL.startDate, - endDate: this.eventsDateFilterFromURL.endDate, - sourceId: this.eventSource || '' - }); + const response = await this.eventsLogService.getRetryCount(data); this.batchRetryCount = response.data.num; this.fetchingCount = false; @@ -299,16 +215,10 @@ export class EventLogsComponent implements OnInit { } async batchReplayEvent() { - const page = this.route.snapshot.queryParams.page || 1; this.isRetrying = true; try { - const response = await this.eventsLogService.batchRetryEvent({ - page: page || 1, - startDate: this.eventsDateFilterFromURL.startDate, - endDate: this.eventsDateFilterFromURL.endDate, - sourceId: this.eventSource || '' - }); + const response = await this.eventsLogService.batchRetryEvent(this.queryParams); this.generalService.showNotification({ message: response.message, style: 'success' }); this.batchDialog.nativeElement.close(); @@ -331,8 +241,4 @@ export class EventLogsComponent implements OnInit { this.router.navigate([`/projects/${this.privateService.getProjectDetails?.uid}/events`], { queryParams }); } - - toggleSouceFilter(event: any, sourceId: string) { - this.eventSource = event.target.checked ? sourceId : ''; - } } diff --git a/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.service.ts b/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.service.ts index 64e80e9780..e07d453da7 100644 --- a/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.service.ts +++ b/web/ui/dashboard/src/app/private/pages/project/event-logs/event-logs.service.ts @@ -8,7 +8,7 @@ import { HttpService } from 'src/app/services/http/http.service'; export class EventLogsService { constructor(private http: HttpService) {} - getRetryCount(requestDetails: { endpointId?: string; page: number; startDate: string; endDate: string; sourceId?: string }): Promise { + getRetryCount(requestDetails: { endpointId?: string; page?: number; startDate?: string; endDate?: string; sourceId?: string }): Promise { return new Promise(async (resolve, reject) => { try { const response = await this.http.request({ @@ -41,7 +41,7 @@ export class EventLogsService { }); } - batchRetryEvent(requestDetails: { page: number; startDate: string; endDate: string; endpointId?: string; sourceId?: string }): Promise { + batchRetryEvent(requestDetails: { startDate?: string; endDate?: string; endpointId?: string; sourceId?: string }): Promise { return new Promise(async (resolve, reject) => { try { const response = await this.http.request({ diff --git a/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.component.html b/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.component.html index a390b5c9e9..d87dabed30 100644 --- a/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.component.html +++ b/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.component.html @@ -17,158 +17,8 @@

Deliveries missing?

-
-
- + -
- - - -
    -
  • - - -
  • -
- -
- - -
-
-
- - - -
- - -
-
    -
  • - - -
  • -
- -
- - -
-
-
- -
- Event Filtered - -
- - - -
- - -
- -
- - -
-
diff --git a/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.component.ts b/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.component.ts index ea98c63280..967734f4a8 100644 --- a/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.component.ts +++ b/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.component.ts @@ -1,17 +1,13 @@ import { Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs'; -import { ENDPOINT } from 'src/app/models/endpoint.model'; import { EVENT_DELIVERY, FILTER_QUERY_PARAM } from 'src/app/models/event.model'; import { CURSOR, PAGINATION } from 'src/app/models/global.model'; import { HTTP_RESPONSE } from 'src/app/models/global.model'; import { GeneralService } from 'src/app/services/general/general.service'; import { EventsService } from '../events.service'; import { PrivateService } from 'src/app/private/private.service'; -import { SOURCE } from 'src/app/models/source.model'; import { DatePickerComponent } from 'src/app/components/date-picker/date-picker.component'; import { ProjectService } from '../../project.service'; -import { Location } from '@angular/common'; @Component({ selector: 'app-event-deliveries', @@ -27,70 +23,42 @@ export class EventDeliveriesComponent implements OnInit { isloadingEventDeliveries = false; isRetrying = false; batchRetryCount!: number; - eventDeliveriesEndpoint?: string; - eventDeliveriesSource?: string; displayedEventDeliveries!: { date: string; content: any[] }[]; eventDeliveries?: { pagination: PAGINATION; content: EVENT_DELIVERY[] }; - eventDeliveryFilteredByStatus: string[] = []; - eventsDelEndpointFilter$!: Observable; - @ViewChild('eventDelsEndpointFilter', { static: true }) eventDelsEndpointFilter!: ElementRef; - @ViewChild('datePicker', { static: true }) datePicker!: DatePickerComponent; @ViewChild('batchRetryDialog', { static: true }) dialog!: ElementRef; portalToken = this.route.snapshot.queryParams?.token; - filterSources: SOURCE[] = []; queryParams?: FILTER_QUERY_PARAM; getEventDeliveriesInterval: any; - enableTailMode = false; - loadingFilterEndpoints = false; - constructor(private generalService: GeneralService, private eventsService: EventsService, public route: ActivatedRoute, public projectService: ProjectService, public privateService: PrivateService, private _location: Location) {} + constructor(private generalService: GeneralService, private eventsService: EventsService, public route: ActivatedRoute, public projectService: ProjectService, public privateService: PrivateService) {} - ngOnInit() { - const data = this.getFiltersFromURL(); - this.getEventDeliveries({ ...data, showLoader: true }); - if (this.checkIfTailModeIsEnabled()) this.getEventDeliveriesAtInterval(); - - if (!this.portalToken || this.projectService.activeProjectDetails?.type == 'incoming') this.getSourcesForFilter(); - } + ngOnInit() {} ngOnDestroy() { clearInterval(this.getEventDeliveriesInterval); } - getFiltersFromURL() { - this.queryParams = { ...this.queryParams, ...this.route.snapshot.queryParams }; - - // set filter status if any exists in URL - this.eventDeliveryFilteredByStatus = this.queryParams.status ? JSON.parse(this.queryParams.status) : []; - - this.eventDeliveriesSource = this.queryParams?.sourceId; - this.eventDeliveriesEndpoint = this.queryParams?.endpointId; - - return this.queryParams; + fetchEventDeliveries(requestDetails?: FILTER_QUERY_PARAM) { + const data = requestDetails; + this.queryParams = data; + this.getEventDeliveries({ ...data, showLoader: true }); } checkIfTailModeIsEnabled() { const tailModeConfig = localStorage.getItem('EVENTS_TAIL_MODE'); - this.enableTailMode = tailModeConfig ? JSON.parse(tailModeConfig) : false; - return this.enableTailMode; + return tailModeConfig ? JSON.parse(tailModeConfig) : false; } - toggleTailMode(e?: any, status?: 'on' | 'off') { - let tailModeConfig: boolean; - if (status) tailModeConfig = status === 'on'; - else tailModeConfig = e.target.checked; - - this.enableTailMode = tailModeConfig; - localStorage.setItem('EVENTS_TAIL_MODE', JSON.stringify(tailModeConfig)); + handleTailing(tailDetails: { data: FILTER_QUERY_PARAM; tailModeConfig: boolean }) { + this.queryParams = tailDetails.data; clearInterval(this.getEventDeliveriesInterval); - if (tailModeConfig) this.getEventDeliveriesAtInterval(); + if (tailDetails.tailModeConfig) this.getEventDeliveriesAtInterval(tailDetails.data); } - getEventDeliveriesAtInterval() { + getEventDeliveriesAtInterval(data: FILTER_QUERY_PARAM) { this.getEventDeliveriesInterval = setInterval(() => { - const data = { ...this.queryParams, ...this.route.snapshot.queryParams }; this.getEventDeliveries(data); }, 5000); } @@ -117,7 +85,7 @@ export class EventDeliveriesComponent implements OnInit { const finalEventDels: any = []; let filteredEventDeliveries: any = []; - const filteredEventDeliveriesByDate = this.generalService.setContentDisplayed(eventDeliveriesData); + const filteredEventDeliveriesByDate = this.generalService.setContentDisplayed(eventDeliveriesData, this.queryParams?.sort || 'desc'); eventDeliveriesData.forEach((item: any) => { eventIds.push(item.event_id); @@ -147,81 +115,14 @@ export class EventDeliveriesComponent implements OnInit { } } - addFilterToURL(params?: FILTER_QUERY_PARAM) { - this.queryParams = { ...this.queryParams, ...this.route.snapshot.queryParams, ...params }; - - if (!params?.next_page_cursor) delete this.queryParams.next_page_cursor; - if (!params?.prev_page_cursor) delete this.queryParams.prev_page_cursor; - - const cleanedQuery: any = Object.fromEntries(Object.entries(this.queryParams).filter(([_, q]) => q !== '' && q !== undefined && q !== null)); - const queryParams = new URLSearchParams(cleanedQuery).toString(); - this._location.go(`${location.pathname}?${queryParams}`); - - return this.queryParams; - } - - checkIfEventDeliveryStatusFilterOptionIsSelected(status: string): boolean { - return this.eventDeliveryFilteredByStatus?.length > 0 ? this.eventDeliveryFilteredByStatus.includes(status) : false; - } - - updateEventDevliveryStatusFilter(status: string, isChecked: any) { - if (isChecked.target.checked) { - this.eventDeliveryFilteredByStatus.push(status); - } else { - let index = this.eventDeliveryFilteredByStatus.findIndex(x => x === status); - this.eventDeliveryFilteredByStatus.splice(index, 1); - } - } - - getSelectedDateRange(dateRange: { startDate: string; endDate: string }) { - const data = this.addFilterToURL(dateRange); - this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); - this.getEventDeliveries({ ...data, showLoader: true }); - } - - getSelectedStatusFilter() { - const eventDelsStatus = this.eventDeliveryFilteredByStatus.length > 0 ? JSON.stringify(this.eventDeliveryFilteredByStatus) : ''; - const data = this.addFilterToURL({ status: eventDelsStatus }); - this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); - this.getEventDeliveries({ ...data, showLoader: true }); - } - - clearFilters(filterType?: 'startDate' | 'endDate' | 'eventId' | 'endpointId' | 'status' | 'sourceId' | 'next_page_cursor' | 'prev_page_cursor' | 'direction') { - if (filterType && this.queryParams) { - // if filter to clear start date or end date, it means clear date filter. :) - if (filterType === 'startDate' || filterType === 'endDate') { - delete this.queryParams['startDate']; - delete this.queryParams['endDate']; - } else if (filterType === 'eventId') { - delete this.queryParams['eventId']; - delete this.queryParams['idempotencyKey']; - } else if (filterType === 'endpointId') { - this.eventDeliveriesEndpoint = ''; - delete this.queryParams['endpointId']; - } else if (filterType === 'sourceId') { - this.eventDeliveriesSource = ''; - delete this.queryParams['sourceId']; - } else delete this.queryParams[filterType]; - - const cleanedQuery: any = Object.fromEntries(Object.entries(this.queryParams).filter(([_, q]) => q !== '' && q !== undefined && q !== null)); - const queryParams = new URLSearchParams(cleanedQuery).toString(); - this._location.go(`${location.pathname}?${queryParams}`); - } else { - this.datePicker.clearDate(); - this.queryParams = {}; - this._location.go(`${location.pathname}`); - } - - this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); - this.getEventDeliveries({ showLoader: true }); - } + async fetchRetryCount(data: FILTER_QUERY_PARAM) { + this.queryParams = data; - async fetchRetryCount() { - if (!this.queryParams) return; + if (!data) return; this.fetchingCount = true; try { - const response = await this.eventsService.getRetryCount(this.queryParams); + const response = await this.eventsService.getRetryCount(data); this.batchRetryCount = response.data.num; this.fetchingCount = false; @@ -231,29 +132,10 @@ export class EventDeliveriesComponent implements OnInit { } } - async getSourcesForFilter() { - try { - const sourcesResponse = (await this.privateService.getSources()).data.content; - this.filterSources = sourcesResponse; - } catch (error) {} - } - - updateEndpointFilter() { - const data = this.addFilterToURL({ endpointId: this.eventDeliveriesEndpoint }); - this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); - this.getEventDeliveries({ ...data, showLoader: true }); - } - - updateSourceFilter() { - const data = this.addFilterToURL({ sourceId: this.eventDeliveriesSource }); - this.checkIfTailModeIsEnabled() ? this.toggleTailMode(false, 'on') : this.toggleTailMode(false, 'off'); - this.getEventDeliveries({ ...data, showLoader: true }); - } - paginateEvents(event: CURSOR) { - const data = this.addFilterToURL({ next_page_cursor: event.next_page_cursor, prev_page_cursor: event.prev_page_cursor }); - if (this.checkIfTailModeIsEnabled()) this.toggleTailMode(false, 'off'); - this.getEventDeliveries({ ...data, showLoader: true }); + this.queryParams = this.generalService.addFilterToURL({ ...this.queryParams, ...event }); + this.handleTailing({ data: this.queryParams, tailModeConfig: this.checkIfTailModeIsEnabled() }); + this.getEventDeliveries({ ...this.queryParams, showLoader: true }); } async retryEvent(requestDetails: { e: any; index: number; eventDeliveryId: string }) { @@ -300,8 +182,4 @@ export class EventDeliveriesComponent implements OnInit { return error; } } - - toggleSouceFilter(event: any, sourceId: string) { - this.eventDeliveriesSource = event.target.checked ? sourceId : ''; - } } diff --git a/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.module.ts b/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.module.ts index bfcbbe7310..69ecfbc329 100644 --- a/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.module.ts +++ b/web/ui/dashboard/src/app/private/pages/project/events/event-deliveries/event-deliveries.module.ts @@ -20,6 +20,7 @@ import { TimePickerComponent } from 'src/app/components/time-picker/time-picker. import { TooltipComponent } from 'src/app/components/tooltip/tooltip.component'; import { PaginationComponent } from 'src/app/private/components/pagination/pagination.component'; import { EndpointFilterComponent } from 'src/app/private/components/endpoints-filter/endpoints-filter.component'; +import { EventDeliveryFilterComponent } from 'src/app/private/components/event-delivery-filter/event-delivery-filter.component'; @NgModule({ declarations: [EventDeliveriesComponent], @@ -49,7 +50,8 @@ import { EndpointFilterComponent } from 'src/app/private/components/endpoints-fi TooltipComponent, PaginationComponent, DialogDirective, - EndpointFilterComponent + EndpointFilterComponent, + EventDeliveryFilterComponent ], exports: [EventDeliveriesComponent], providers: [DatePipe] diff --git a/web/ui/dashboard/src/app/private/pages/project/events/events.component.html b/web/ui/dashboard/src/app/private/pages/project/events/events.component.html index e22d01228d..fb631baa32 100644 --- a/web/ui/dashboard/src/app/private/pages/project/events/events.component.html +++ b/web/ui/dashboard/src/app/private/pages/project/events/events.component.html @@ -70,7 +70,20 @@

{{ privateService.getProjectDetails?.type

Events Summary

- + + + +
--> - + + +
+ +
diff --git a/web/ui/dashboard/src/app/private/pages/project/portal-links/portal-links.component.ts b/web/ui/dashboard/src/app/private/pages/project/portal-links/portal-links.component.ts index 6eb18eabf9..828fca4bcc 100644 --- a/web/ui/dashboard/src/app/private/pages/project/portal-links/portal-links.component.ts +++ b/web/ui/dashboard/src/app/private/pages/project/portal-links/portal-links.component.ts @@ -132,8 +132,8 @@ export class PortalLinksComponent implements OnInit { ).data.content; } - updateEndpointFilter(endpointId: string) { - this.linkEndpoint = endpointId; + updateEndpointFilter(endpoint: ENDPOINT) { + this.linkEndpoint = endpoint?.uid; this.getPortalLinks(); } diff --git a/web/ui/dashboard/src/app/services/general/general.service.ts b/web/ui/dashboard/src/app/services/general/general.service.ts index 0f9d65c2b3..7f8328d349 100644 --- a/web/ui/dashboard/src/app/services/general/general.service.ts +++ b/web/ui/dashboard/src/app/services/general/general.service.ts @@ -1,5 +1,8 @@ +import { Location } from '@angular/common'; import { Injectable } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; +import { FILTER_QUERY_PARAM } from 'src/app/models/event.model'; import { NOTIFICATION_STATUS } from 'src/app/models/global.model'; import { environment } from 'src/environments/environment'; @@ -9,7 +12,8 @@ import { environment } from 'src/environments/environment'; export class GeneralService { alertStatus: BehaviorSubject<{ message: string; style: NOTIFICATION_STATUS; type?: string; show: boolean }> = new BehaviorSubject<{ message: string; style: NOTIFICATION_STATUS; type?: string; show: boolean }>({ message: 'testing', style: 'info', type: 'alert', show: false }); - constructor() {} + + constructor(private route: ActivatedRoute, private _location: Location) {} showNotification(details: { message: string; style: NOTIFICATION_STATUS; type?: string }) { this.alertStatus.next({ message: details.message, style: details.style, show: true, type: details.type ? details.type : 'alert' }); @@ -97,7 +101,7 @@ export class GeneralService { return `${day} ${months[month]}, ${year}`; } - setContentDisplayed(content: { created_at: Date }[]) { + setContentDisplayed(content: { created_at: Date }[], sortOrder?: string) { const dateCreateds = content.map((item: { created_at: Date }) => this.getDate(item.created_at)); const uniqueDateCreateds = [...new Set(dateCreateds)]; let displayedItems: any = []; @@ -105,11 +109,26 @@ export class GeneralService { const filteredItemDate = content.filter((item: { created_at: Date }) => this.getDate(item.created_at) === itemDate); const contents = { date: itemDate, content: filteredItemDate }; displayedItems.push(contents); - displayedItems = displayedItems.sort((a: any, b: any) => Number(new Date(b.date)) - Number(new Date(a.date))); + + displayedItems = sortOrder === 'desc' ? displayedItems.sort((a: any, b: any) => Number(new Date(b.date)) - Number(new Date(a.date))) : displayedItems.sort((a: any, b: any) => Number(new Date(a.date)) - Number(new Date(b.date))); }); + return displayedItems; } + addFilterToURL(params?: FILTER_QUERY_PARAM) { + const queryParams = { ...this.route.snapshot.queryParams, ...params }; + + if (!params?.next_page_cursor) delete queryParams.next_page_cursor; + if (!params?.prev_page_cursor) delete queryParams.prev_page_cursor; + + const cleanedQuery: any = Object.fromEntries(Object.entries(queryParams).filter(([_, q]) => q !== '' && q !== undefined && q !== null)); + const cleanedQueryParams = new URLSearchParams(cleanedQuery).toString(); + this._location.go(`${location.pathname}?${cleanedQueryParams}`); + + return queryParams; + } + getCodeSnippetString(type: 'event_data' | 'res_body' | 'res_header' | 'req_header' | 'error' | 'log', data: any) { let displayMessage = ''; switch (type) { diff --git a/web/ui/dashboard/src/index.html b/web/ui/dashboard/src/index.html index 4d9cafa532..33a8d53faa 100644 --- a/web/ui/dashboard/src/index.html +++ b/web/ui/dashboard/src/index.html @@ -11,6 +11,7 @@