Skip to content

Commit

Permalink
add subscriptions list and form to portal links (#2015)
Browse files Browse the repository at this point in the history
* add subscriptions list and form to portal links

* update endpoints dropdown

* added metrics for the data plane (#2005)

* added metrics for the data plane

* updated docker-compose.dev.yml with optional prometheus image

* updated RedisQueue as a metrics collector

* updated postgres as a metrics collector

* revert error check in process_meta_event.go

* updated metrics collection for different http status codes

* updated postgres_collector.go

* fixed lint issues and updated some queries

* updated metrics to be configurable

* updated test config

* fixed failing CI tests

* gated metrics with feature flags

* fixed CI issues

* updated metrics tests

* refactored postgres_collector.go

* Update rate limit function (#2002)

* chore: update rate limit function

* fix: return when locked row is null

* fix: select only specific rows

* chore: create record if not exists

* Add subscription name filter to GetSubscriptions (#2014)

* fix: add subscription name filter to GetSubscriptions

* fix: add % directly in arg map

* add search for subscriptions on portal links

* update route for subscriptions

* move abd update portal link layout

* delete apps module

* fix and update subscriptions form

* update subscriptions/endpoints page

* fix endpoint search

* Add portal link checks to subscription apis (#2016)

* added metrics for the data plane (#2005)

* added metrics for the data plane

* updated docker-compose.dev.yml with optional prometheus image

* updated RedisQueue as a metrics collector

* updated postgres as a metrics collector

* revert error check in process_meta_event.go

* updated metrics collection for different http status codes

* updated postgres_collector.go

* fixed lint issues and updated some queries

* updated metrics to be configurable

* updated test config

* fixed failing CI tests

* gated metrics with feature flags

* fixed CI issues

* updated metrics tests

* refactored postgres_collector.go

* Update rate limit function (#2002)

* chore: update rate limit function

* fix: return when locked row is null

* fix: select only specific rows

* chore: create record if not exists

* Add subscription name filter to GetSubscriptions (#2014)

* fix: add subscription name filter to GetSubscriptions

* fix: add % directly in arg map

* fix: add portal link checks to subscription apis

* fix: remove isPortaLinkReq

* fix: use util.StringSliceContains

* fix: revert envs

* fix: check if data.FilterBy.EndpointIDs was present before setting it

---------

Co-authored-by: Smart Mekiliuwa <st.nonso@gmail.com>
Co-authored-by: Raymond Tukpe <jirevwe@users.noreply.github.com>
Co-authored-by: Pelumi Muyiwa-Oni <Pelumioni25@gmail.com>

* check option name in select component

* fix portal links form

* remove comments

---------

Co-authored-by: Smart Mekiliuwa <st.nonso@gmail.com>
Co-authored-by: Raymond Tukpe <jirevwe@users.noreply.github.com>
Co-authored-by: Daniel Oluojomu <danvixent@gmail.com>
  • Loading branch information
4 people committed May 27, 2024
1 parent ebf31d7 commit 208ba4a
Show file tree
Hide file tree
Showing 50 changed files with 950 additions and 832 deletions.
114 changes: 112 additions & 2 deletions api/handlers/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package handlers

import (
"errors"
"github.com/frain-dev/convoy/pkg/transform"
"net/http"

"github.com/frain-dev/convoy/internal/pkg/middleware"
"github.com/frain-dev/convoy/pkg/transform"

"github.com/frain-dev/convoy/pkg/log"

"github.com/frain-dev/convoy/api/models"
Expand Down Expand Up @@ -39,8 +41,43 @@ func (h *Handler) GetSubscriptions(w http.ResponseWriter, r *http.Request) {
}

var q *models.QueryListSubscription

data := q.Transform(r)

authUser := middleware.GetAuthUserFromContext(r.Context())

if h.IsReqWithPortalLinkToken(authUser) {
portalLink, err := h.retrievePortalLinkFromToken(r)
if err != nil {
_ = render.Render(w, r, util.NewServiceErrResponse(err))
return
}

endpointIDs, err := h.getEndpoints(r, portalLink)
if err != nil {
_ = render.Render(w, r, util.NewServiceErrResponse(err))
return
}

if len(endpointIDs) == 0 {
_ = render.Render(w, r, util.NewServerResponse("Subscriptions fetched successfully",
models.PagedResponse{Content: []models.SubscriptionResponse{}, Pagination: &datastore.PaginationData{PerPage: 0}}, http.StatusOK))
return
}

// verify that the listed endpoints are all in this portal link
if len(data.FilterBy.EndpointIDs) != 0 {
for _, endpointID := range data.FilterBy.EndpointIDs {
if !util.StringSliceContains(endpointIDs, endpointID) {
_ = render.Render(w, r, util.NewErrorResponse("unauthorized", http.StatusUnauthorized))
return
}
}
} else {
data.FilterBy.EndpointIDs = endpointIDs
}

}

subscriptions, paginationData, err := postgres.NewSubscriptionRepo(h.A.DB, h.A.Cache).LoadSubscriptionsPaged(r.Context(), project.UID, data.FilterBy, data.Pageable)
if err != nil {
log.FromContext(r.Context()).WithError(err).Error("an error occurred while fetching subscriptions")
Expand Down Expand Up @@ -154,6 +191,27 @@ func (h *Handler) CreateSubscription(w http.ResponseWriter, r *http.Request) {
return
}

authUser := middleware.GetAuthUserFromContext(r.Context())

if h.IsReqWithPortalLinkToken(authUser) {
portalLink, err := h.retrievePortalLinkFromToken(r)
if err != nil {
_ = render.Render(w, r, util.NewServiceErrResponse(err))
return
}

endpointIDs, err := h.getEndpoints(r, portalLink)
if err != nil {
_ = render.Render(w, r, util.NewServiceErrResponse(err))
return
}

if !util.StringSliceContains(endpointIDs, sub.EndpointID) {
_ = render.Render(w, r, util.NewErrorResponse("unauthorized", http.StatusUnauthorized))
return
}
}

cs := services.CreateSubscriptionService{
SubRepo: postgres.NewSubscriptionRepo(h.A.DB, h.A.Cache),
EndpointRepo: postgres.NewEndpointRepo(h.A.DB, h.A.Cache),
Expand Down Expand Up @@ -205,6 +263,26 @@ func (h *Handler) DeleteSubscription(w http.ResponseWriter, r *http.Request) {
return
}

authUser := middleware.GetAuthUserFromContext(r.Context())
if h.IsReqWithPortalLinkToken(authUser) {
portalLink, err := h.retrievePortalLinkFromToken(r)
if err != nil {
_ = render.Render(w, r, util.NewServiceErrResponse(err))
return
}

endpointIDs, err := h.getEndpoints(r, portalLink)
if err != nil {
_ = render.Render(w, r, util.NewServiceErrResponse(err))
return
}

if !util.StringSliceContains(endpointIDs, sub.EndpointID) {
_ = render.Render(w, r, util.NewErrorResponse("unauthorized", http.StatusUnauthorized))
return
}
}

err = postgres.NewSubscriptionRepo(h.A.DB, h.A.Cache).DeleteSubscription(r.Context(), project.UID, sub)
if err != nil {
log.FromContext(r.Context()).WithError(err).Error("failed to delete subscription")
Expand Down Expand Up @@ -251,6 +329,38 @@ func (h *Handler) UpdateSubscription(w http.ResponseWriter, r *http.Request) {
return
}

authUser := middleware.GetAuthUserFromContext(r.Context())

if h.IsReqWithPortalLinkToken(authUser) {
portalLink, err := h.retrievePortalLinkFromToken(r)
if err != nil {
_ = render.Render(w, r, util.NewServiceErrResponse(err))
return
}

endpointIDs, err := h.getEndpoints(r, portalLink)
if err != nil {
_ = render.Render(w, r, util.NewServiceErrResponse(err))
return
}

sub, err := postgres.NewSubscriptionRepo(h.A.DB, h.A.Cache).FindSubscriptionByID(r.Context(), project.UID, chi.URLParam(r, "subscriptionID"))
if err != nil {
log.FromContext(r.Context()).WithError(err).Error("failed to find subscription")
if errors.Is(err, datastore.ErrSubscriptionNotFound) {
_ = render.Render(w, r, util.NewErrorResponse("failed to find subscription", http.StatusNotFound))
return
}
_ = render.Render(w, r, util.NewServiceErrResponse(err))
return
}

if !util.StringSliceContains(endpointIDs, sub.EndpointID) {
_ = render.Render(w, r, util.NewErrorResponse("unauthorized", http.StatusUnauthorized))
return
}
}

us := services.UpdateSubscriptionService{
SubRepo: postgres.NewSubscriptionRepo(h.A.DB, h.A.Cache),
EndpointRepo: postgres.NewEndpointRepo(h.A.DB, h.A.Cache),
Expand Down
2 changes: 1 addition & 1 deletion docs/docs.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package docs Code generated by swaggo/swag at 2024-05-22 13:46:04.95428 +0100 BST m=+2.371629584. DO NOT EDIT
// Package docs Code generated by swaggo/swag at 2024-05-24 16:10:26.884378 +0100 BST m=+2.442021417. DO NOT EDIT
package docs

import "github.com/swaggo/swag"
Expand Down
10 changes: 10 additions & 0 deletions util/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,13 @@ func GenerateRandomString(n int) (string, error) {
}
return string(b), nil
}

func StringSliceContains(sl []string, s string) bool {
for _, v := range sl {
if v == s {
return true
}
}

return false
}
41 changes: 27 additions & 14 deletions web/ui/dashboard/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,40 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';


const routes: Routes = [
{
path: '',
loadChildren: () => import('./private/private.module').then(m => m.PrivateModule)
},
{
path: 'portal',
loadChildren: () => import('./public/app/app.module').then(m => m.AppModule)
},
{
path: 'portal/event-deliveries/:id',
loadChildren: () => import('./public/event-delivery/event-delivery.module').then(m => m.EventDeliveryModule)
},
{
path: 'portal/subscriptions/new',
loadChildren: () => import('./public/create-subscription/create-subscription.module').then(m => m.CreateSubscriptionPublicModule)
},
{
path: 'portal/subscriptions/:id',
loadChildren: () => import('./public/app/app.module').then(m => m.AppModule)
loadComponent: () => import('./portal/portal.component').then(m => m.PortalComponent),
children: [
{
path: '',
loadComponent: () => import('./portal/event-deliveries/event-deliveries.component').then(m => m.EventDeliveriesComponent)
},
{
path: 'endpoints',
loadComponent: () => import('./portal/endpoints/endpoints.component').then(m => m.EndpointsComponent)
},
{
path: 'endpoints/:id',
loadComponent: () => import('./portal/endpoints/endpoints.component').then(m => m.EndpointsComponent)
},
{
path: 'subscriptions',
loadComponent: () => import('./portal/subscriptions/subscriptions.component').then(m => m.SubscriptionsComponent)
},
{
path: 'subscriptions/:id',
loadComponent: () => import('./portal/subscriptions/subscriptions.component').then(m => m.SubscriptionsComponent)
},
{
path: 'event-deliveries/:id',
loadComponent: () => import('./portal/event-delivery/event-delivery.component').then(m => m.EventDeliveryComponent)
}
]
},
{
path: '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class SelectComponent implements OnInit, ControlValueAccessor {
@Input('value') value!: any;
@Input('tooltipContent') tooltipContent!: string;
@Input('searchable') searchable: boolean = false;
@Input('selectedValues') selectedValues: any = [];
@Output('selectedOption') selectedOption = new EventEmitter<any>();
@Output('searchString') searchString = new EventEmitter<any>();
@ViewChild('searchFilter', { static: false }) searchFilter!: ElementRef;
Expand Down Expand Up @@ -82,7 +83,8 @@ export class SelectComponent implements OnInit, ControlValueAccessor {
writeValue(value: string | Array<any>) {
if (value) {
if (this.options?.length && typeof this.options[0] !== 'string' && !this.multiple) return (this.selectedValue = this.options?.find(option => option.uid === value));
if (this.multiple && typeof value !== 'string' && this.selectedOptions?.length === 0) {
if (this.multiple && typeof value !== 'string' && this.selectedValues?.length !== 0) this.selectedOptions = this.selectedValues;
if (this.multiple && typeof value !== 'string' && this.selectedOptions?.length === 0 && this.selectedValues?.length === 0) {
setTimeout(() => {
value.forEach((item: any) => {
this.selectedOptions.push({
Expand Down
2 changes: 1 addition & 1 deletion web/ui/dashboard/src/app/models/endpoint.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface ENDPOINT {
owner_id?:string;
description: string;
events?: any;
status?: string;
status: string;
secrets?: SECRET[];
name?: string;
url: string;
Expand Down
Loading

0 comments on commit 208ba4a

Please sign in to comment.