From 96b42c1ce0ecc79196d580ef0eb5a37a39efdbf6 Mon Sep 17 00:00:00 2001 From: LUCCA DUKIC <109136188+LuccaBitfly@users.noreply.github.com> Date: Tue, 17 Sep 2024 14:28:47 +0200 Subject: [PATCH 1/5] (BEDS-464) move notification endpoints to public --- backend/pkg/api/handlers/internal.go | 419 ++--------------------- backend/pkg/api/handlers/public.go | 479 ++++++++++++++++++++++++++- backend/pkg/api/router.go | 38 +-- 3 files changed, 507 insertions(+), 429 deletions(-) diff --git a/backend/pkg/api/handlers/internal.go b/backend/pkg/api/handlers/internal.go index be1d96ecb..0a0f5c006 100644 --- a/backend/pkg/api/handlers/internal.go +++ b/backend/pkg/api/handlers/internal.go @@ -2,7 +2,6 @@ package handlers import ( "errors" - "math" "net/http" "github.com/gobitfly/beaconchain/pkg/api/enums" @@ -526,459 +525,79 @@ func (h *HandlerService) InternalPostMobileBundleDeliveries(w http.ResponseWrite // Notifications func (h *HandlerService) InternalGetUserNotifications(w http.ResponseWriter, r *http.Request) { - userId, err := GetUserIdByContext(r) - if err != nil { - handleErr(w, r, err) - return - } - data, err := h.dai.GetNotificationOverview(r.Context(), userId) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalGetUserNotificationsResponse{ - Data: *data, - } - returnOk(w, r, response) + h.PublicGetUserNotifications(w, r) } func (h *HandlerService) InternalGetUserNotificationDashboards(w http.ResponseWriter, r *http.Request) { - var v validationError - userId, err := GetUserIdByContext(r) - if err != nil { - handleErr(w, r, err) - return - } - q := r.URL.Query() - pagingParams := v.checkPagingParams(q) - sort := checkSort[enums.NotificationDashboardsColumn](&v, q.Get("sort")) - chainId := v.checkNetworkParameter(q.Get("network")) - if v.hasErrors() { - handleErr(w, r, v) - return - } - data, paging, err := h.dai.GetDashboardNotifications(r.Context(), userId, chainId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalGetUserNotificationDashboardsResponse{ - Data: data, - Paging: *paging, - } - returnOk(w, r, response) + h.PublicGetUserNotificationDashboards(w, r) } func (h *HandlerService) InternalGetUserNotificationsValidatorDashboard(w http.ResponseWriter, r *http.Request) { - var v validationError - notificationId := v.checkRegex(reNonEmpty, mux.Vars(r)["notification_id"], "notification_id") - if v.hasErrors() { - handleErr(w, r, v) - return - } - data, err := h.dai.GetValidatorDashboardNotificationDetails(r.Context(), notificationId) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalGetUserNotificationsValidatorDashboardResponse{ - Data: *data, - } - returnOk(w, r, response) + h.PublicGetUserNotificationsValidatorDashboard(w, r) } func (h *HandlerService) InternalGetUserNotificationsAccountDashboard(w http.ResponseWriter, r *http.Request) { - var v validationError - notificationId := v.checkRegex(reNonEmpty, mux.Vars(r)["notification_id"], "notification_id") - if v.hasErrors() { - handleErr(w, r, v) - return - } - data, err := h.dai.GetAccountDashboardNotificationDetails(r.Context(), notificationId) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalGetUserNotificationsAccountDashboardResponse{ - Data: *data, - } - returnOk(w, r, response) + h.PublicGetUserNotificationsAccountDashboard(w, r) } func (h *HandlerService) InternalGetUserNotificationMachines(w http.ResponseWriter, r *http.Request) { - var v validationError - userId, err := GetUserIdByContext(r) - if err != nil { - handleErr(w, r, err) - return - } - q := r.URL.Query() - pagingParams := v.checkPagingParams(q) - sort := checkSort[enums.NotificationMachinesColumn](&v, q.Get("sort")) - if v.hasErrors() { - handleErr(w, r, v) - return - } - data, paging, err := h.dai.GetMachineNotifications(r.Context(), userId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalGetUserNotificationMachinesResponse{ - Data: data, - Paging: *paging, - } - returnOk(w, r, response) + h.PublicGetUserNotificationMachines(w, r) } func (h *HandlerService) InternalGetUserNotificationClients(w http.ResponseWriter, r *http.Request) { - var v validationError - userId, err := GetUserIdByContext(r) - if err != nil { - handleErr(w, r, err) - return - } - q := r.URL.Query() - pagingParams := v.checkPagingParams(q) - sort := checkSort[enums.NotificationClientsColumn](&v, q.Get("sort")) - if v.hasErrors() { - handleErr(w, r, v) - return - } - data, paging, err := h.dai.GetClientNotifications(r.Context(), userId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalGetUserNotificationClientsResponse{ - Data: data, - Paging: *paging, - } - returnOk(w, r, response) + h.PublicGetUserNotificationClients(w, r) } func (h *HandlerService) InternalGetUserNotificationRocketPool(w http.ResponseWriter, r *http.Request) { - var v validationError - userId, err := GetUserIdByContext(r) - if err != nil { - handleErr(w, r, err) - return - } - q := r.URL.Query() - pagingParams := v.checkPagingParams(q) - sort := checkSort[enums.NotificationRocketPoolColumn](&v, q.Get("sort")) - if v.hasErrors() { - handleErr(w, r, v) - return - } - data, paging, err := h.dai.GetRocketPoolNotifications(r.Context(), userId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalGetUserNotificationRocketPoolResponse{ - Data: data, - Paging: *paging, - } - returnOk(w, r, response) + h.PublicGetUserNotificationRocketPool(w, r) } func (h *HandlerService) InternalGetUserNotificationNetworks(w http.ResponseWriter, r *http.Request) { - var v validationError - userId, err := GetUserIdByContext(r) - if err != nil { - handleErr(w, r, err) - return - } - q := r.URL.Query() - pagingParams := v.checkPagingParams(q) - sort := checkSort[enums.NotificationNetworksColumn](&v, q.Get("sort")) - if v.hasErrors() { - handleErr(w, r, v) - return - } - data, paging, err := h.dai.GetNetworkNotifications(r.Context(), userId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalGetUserNotificationNetworksResponse{ - Data: data, - Paging: *paging, - } - returnOk(w, r, response) + h.PublicGetUserNotificationNetworks(w, r) } func (h *HandlerService) InternalGetUserNotificationSettings(w http.ResponseWriter, r *http.Request) { - userId, err := GetUserIdByContext(r) - if err != nil { - handleErr(w, r, err) - return - } - data, err := h.dai.GetNotificationSettings(r.Context(), userId) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalGetUserNotificationSettingsResponse{ - Data: *data, - } - returnOk(w, r, response) + h.PublicGetUserNotificationSettings(w, r) } func (h *HandlerService) InternalPutUserNotificationSettingsGeneral(w http.ResponseWriter, r *http.Request) { - var v validationError - userId, err := GetUserIdByContext(r) - if err != nil { - handleErr(w, r, err) - return - } - var req types.NotificationSettingsGeneral - if err := v.checkBody(&req, r); err != nil { - handleErr(w, r, err) - return - } - checkMinMax(&v, req.MachineStorageUsageThreshold, 0, 1, "machine_storage_usage_threshold") - checkMinMax(&v, req.MachineCpuUsageThreshold, 0, 1, "machine_cpu_usage_threshold") - checkMinMax(&v, req.MachineMemoryUsageThreshold, 0, 1, "machine_memory_usage_threshold") - checkMinMax(&v, req.RocketPoolMaxCollateralThreshold, 0, 1, "rocket_pool_max_collateral_threshold") - checkMinMax(&v, req.RocketPoolMinCollateralThreshold, 0, 1, "rocket_pool_min_collateral_threshold") - // TODO: check validity of clients - if v.hasErrors() { - handleErr(w, r, v) - return - } - err = h.dai.UpdateNotificationSettingsGeneral(r.Context(), userId, req) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalPutUserNotificationSettingsGeneralResponse{ - Data: req, - } - returnOk(w, r, response) + h.PublicPutUserNotificationSettingsGeneral(w, r) } func (h *HandlerService) InternalPutUserNotificationSettingsNetworks(w http.ResponseWriter, r *http.Request) { - var v validationError - userId, err := GetUserIdByContext(r) - if err != nil { - handleErr(w, r, err) - return - } - var req types.NotificationSettingsNetwork - if err := v.checkBody(&req, r); err != nil { - handleErr(w, r, err) - return - } - checkMinMax(&v, req.ParticipationRateThreshold, 0, 1, "participation_rate_threshold") - - chainId := v.checkNetworkParameter(mux.Vars(r)["network"]) - if v.hasErrors() { - handleErr(w, r, v) - return - } - err = h.dai.UpdateNotificationSettingsNetworks(r.Context(), userId, chainId, req) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalPutUserNotificationSettingsNetworksResponse{ - Data: types.NotificationNetwork{ - ChainId: chainId, - Settings: req, - }, - } - returnOk(w, r, response) + h.PublicPutUserNotificationSettingsNetworks(w, r) } func (h *HandlerService) InternalPutUserNotificationSettingsPairedDevices(w http.ResponseWriter, r *http.Request) { - var v validationError - req := struct { - Name string `json:"name,omitempty"` - IsNotificationsEnabled bool `json:"is_notifications_enabled"` - }{} - if err := v.checkBody(&req, r); err != nil { - handleErr(w, r, err) - return - } - // TODO use a better way to validate the paired device id - pairedDeviceId := v.checkRegex(reNonEmpty, mux.Vars(r)["paired_device_id"], "paired_device_id") - name := v.checkNameNotEmpty(req.Name) - if v.hasErrors() { - handleErr(w, r, v) - return - } - err := h.dai.UpdateNotificationSettingsPairedDevice(r.Context(), pairedDeviceId, name, req.IsNotificationsEnabled) - if err != nil { - handleErr(w, r, err) - return - } - // TODO timestamp - response := types.InternalPutUserNotificationSettingsPairedDevicesResponse{ - Data: types.NotificationPairedDevice{ - Id: pairedDeviceId, - Name: req.Name, - IsNotificationsEnabled: req.IsNotificationsEnabled, - }, - } - - returnOk(w, r, response) + h.PublicPutUserNotificationSettingsPairedDevices(w, r) } func (h *HandlerService) InternalDeleteUserNotificationSettingsPairedDevices(w http.ResponseWriter, r *http.Request) { - var v validationError - // TODO use a better way to validate the paired device id - pairedDeviceId := v.checkRegex(reNonEmpty, mux.Vars(r)["paired_device_id"], "paired_device_id") - if v.hasErrors() { - handleErr(w, r, v) - return - } - err := h.dai.DeleteNotificationSettingsPairedDevice(r.Context(), pairedDeviceId) - if err != nil { - handleErr(w, r, err) - return - } - returnNoContent(w, r) + h.PublicDeleteUserNotificationSettingsPairedDevices(w, r) } func (h *HandlerService) InternalGetUserNotificationSettingsDashboards(w http.ResponseWriter, r *http.Request) { - var v validationError - userId, err := GetUserIdByContext(r) - if err != nil { - handleErr(w, r, err) - return - } - q := r.URL.Query() - pagingParams := v.checkPagingParams(q) - sort := checkSort[enums.NotificationSettingsDashboardColumn](&v, q.Get("sort")) - if v.hasErrors() { - handleErr(w, r, v) - return - } - data, paging, err := h.dai.GetNotificationSettingsDashboards(r.Context(), userId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalGetUserNotificationSettingsDashboardsResponse{ - Data: data, - Paging: *paging, - } - returnOk(w, r, response) + h.PublicGetUserNotificationSettingsDashboards(w, r) } func (h *HandlerService) InternalPutUserNotificationSettingsValidatorDashboard(w http.ResponseWriter, r *http.Request) { - var v validationError - var req types.NotificationSettingsValidatorDashboard - if err := v.checkBody(&req, r); err != nil { - handleErr(w, r, err) - return - } - checkMinMax(&v, req.GroupOfflineThreshold, 0, 1, "group_offline_threshold") - vars := mux.Vars(r) - dashboardId := v.checkPrimaryDashboardId(vars["dashboard_id"]) - groupId := v.checkExistingGroupId(vars["group_id"]) - if v.hasErrors() { - handleErr(w, r, v) - return - } - err := h.dai.UpdateNotificationSettingsValidatorDashboard(r.Context(), dashboardId, groupId, req) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalPutUserNotificationSettingsValidatorDashboardResponse{ - Data: req, - } - returnOk(w, r, response) + h.PublicPutUserNotificationSettingsValidatorDashboard(w, r) } func (h *HandlerService) InternalPutUserNotificationSettingsAccountDashboard(w http.ResponseWriter, r *http.Request) { - var v validationError - req := struct { - WebhookUrl string `json:"webhook_url"` - IsWebhookDiscordEnabled bool `json:"is_webhook_discord_enabled"` - IsIgnoreSpamTransactionsEnabled bool `json:"is_ignore_spam_transactions_enabled"` - SubscribedChainIds []intOrString `json:"subscribed_chain_ids"` - - IsIncomingTransactionsSubscribed bool `json:"is_incoming_transactions_subscribed"` - IsOutgoingTransactionsSubscribed bool `json:"is_outgoing_transactions_subscribed"` - IsERC20TokenTransfersSubscribed bool `json:"is_erc20_token_transfers_subscribed"` - ERC20TokenTransfersValueThreshold float64 `json:"erc20_token_transfers_value_threshold"` // 0 does not disable, is_erc20_token_transfers_subscribed determines if it's enabled - IsERC721TokenTransfersSubscribed bool `json:"is_erc721_token_transfers_subscribed"` - IsERC1155TokenTransfersSubscribed bool `json:"is_erc1155_token_transfers_subscribed"` - }{} - if err := v.checkBody(&req, r); err != nil { - handleErr(w, r, err) - return - } - chainIdMap := v.checkNetworkSlice(req.SubscribedChainIds) - // convert to uint64[] slice - chainIds := make([]uint64, len(chainIdMap)) - i := 0 - for k := range chainIdMap { - chainIds[i] = k - i++ - } - checkMinMax(&v, req.ERC20TokenTransfersValueThreshold, 0, math.MaxFloat64, "group_offline_threshold") - vars := mux.Vars(r) - dashboardId := v.checkPrimaryDashboardId(vars["dashboard_id"]) - groupId := v.checkExistingGroupId(vars["group_id"]) - if v.hasErrors() { - handleErr(w, r, v) - return - } - settings := types.NotificationSettingsAccountDashboard{ - WebhookUrl: req.WebhookUrl, - IsWebhookDiscordEnabled: req.IsWebhookDiscordEnabled, - IsIgnoreSpamTransactionsEnabled: req.IsIgnoreSpamTransactionsEnabled, - SubscribedChainIds: chainIds, - - IsIncomingTransactionsSubscribed: req.IsIncomingTransactionsSubscribed, - IsOutgoingTransactionsSubscribed: req.IsOutgoingTransactionsSubscribed, - IsERC20TokenTransfersSubscribed: req.IsERC20TokenTransfersSubscribed, - ERC20TokenTransfersValueThreshold: req.ERC20TokenTransfersValueThreshold, - IsERC721TokenTransfersSubscribed: req.IsERC721TokenTransfersSubscribed, - IsERC1155TokenTransfersSubscribed: req.IsERC1155TokenTransfersSubscribed, - } - err := h.dai.UpdateNotificationSettingsAccountDashboard(r.Context(), dashboardId, groupId, settings) - if err != nil { - handleErr(w, r, err) - return - } - response := types.InternalPutUserNotificationSettingsAccountDashboardResponse{ - Data: settings, - } - returnOk(w, r, response) + h.PublicPutUserNotificationSettingsAccountDashboard(w, r) } func (h *HandlerService) InternalPostUserNotificationsTestEmail(w http.ResponseWriter, r *http.Request) { - // TODO - returnOk(w, r, nil) + h.PublicPostUserNotificationsTestEmail(w, r) } func (h *HandlerService) InternalPostUserNotificationsTestPush(w http.ResponseWriter, r *http.Request) { - // TODO - returnOk(w, r, nil) + h.PublicPostUserNotificationsTestPush(w, r) } func (h *HandlerService) InternalPostUserNotificationsTestWebhook(w http.ResponseWriter, r *http.Request) { - var v validationError - req := struct { - WebhookUrl string `json:"webhook_url"` - IsDiscordWebhookEnabled bool `json:"is_discord_webhook_enabled,omitempty"` - }{} - if err := v.checkBody(&req, r); err != nil { - handleErr(w, r, err) - return - } - if v.hasErrors() { - handleErr(w, r, v) - return - } - // TODO - returnOk(w, r, nil) + h.PublicPostUserNotificationsTestWebhook(w, r) } // -------------------------------------- diff --git a/backend/pkg/api/handlers/public.go b/backend/pkg/api/handlers/public.go index 95df23ea7..4316aac3d 100644 --- a/backend/pkg/api/handlers/public.go +++ b/backend/pkg/api/handlers/public.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "math" "net/http" "reflect" "time" @@ -618,10 +619,7 @@ func (h *HandlerService) PublicPostValidatorDashboardValidators(w http.ResponseW // // @Description Get a list of groups in a specified validator dashboard. // @Tags Validator Dashboard -// @Produce json -// @Param dashboard_id path string true "The ID of the dashboard." // @Param group_id query string false "The ID of the group." -// @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." // @Param limit query string false "The maximum number of results that may be returned." // @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order." Enums(index, public_key, balance, status, withdrawal_credentials) // @Param search query string false "Search for Address, ENS." @@ -660,13 +658,14 @@ func (h *HandlerService) PublicGetValidatorDashboardValidators(w http.ResponseWr // @Description Remove validators from a specified dashboard. // @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Validator Dashboard Management -// @Accept json -// @Produce json -// @Param dashboard_id path string true "The ID of the dashboard." -// @Param request body handlers.PublicDeleteValidatorDashboardValidators.request true "`validators`: Provide an array of validator indices or public keys that should get removed from the dashboard." -// @Success 204 "Validators removed successfully." -// @Failure 400 {object} types.ApiErrorResponse -// @Router /validator-dashboards/{dashboard_id}/validators/bulk-deletions [post] + +// @Accept json +// @Produce json +// @Param dashboard_id path string true "The ID of the dashboard." +// @Param request body handlers.PublicDeleteValidatorDashboardValidators.request true "`validators`: Provide an array of validator indices or public keys that should get removed from the dashboard." +// @Success 204 "Validators removed successfully." +// @Failure 400 {object} types.ApiErrorResponse +// @Router /validator-dashboards/{dashboard_id}/validators/bulk-deletions [post] func (h *HandlerService) PublicDeleteValidatorDashboardValidators(w http.ResponseWriter, r *http.Request) { var v validationError dashboardId := v.checkPrimaryDashboardId(mux.Vars(r)["dashboard_id"]) @@ -1856,6 +1855,466 @@ func (h *HandlerService) PublicGetValidatorDashboardRocketPoolMinipools(w http.R returnOk(w, r, response) } +// ---------------------------------------------- +// Notifications +// ---------------------------------------------- + +func (h *HandlerService) PublicGetUserNotifications(w http.ResponseWriter, r *http.Request) { + userId, err := GetUserIdByContext(r) + if err != nil { + handleErr(w, r, err) + return + } + data, err := h.dai.GetNotificationOverview(r.Context(), userId) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalGetUserNotificationsResponse{ + Data: *data, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicGetUserNotificationDashboards(w http.ResponseWriter, r *http.Request) { + var v validationError + userId, err := GetUserIdByContext(r) + if err != nil { + handleErr(w, r, err) + return + } + q := r.URL.Query() + pagingParams := v.checkPagingParams(q) + sort := checkSort[enums.NotificationDashboardsColumn](&v, q.Get("sort")) + chainId := v.checkNetworkParameter(q.Get("network")) + if v.hasErrors() { + handleErr(w, r, v) + return + } + data, paging, err := h.dai.GetDashboardNotifications(r.Context(), userId, chainId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalGetUserNotificationDashboardsResponse{ + Data: data, + Paging: *paging, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicGetUserNotificationsValidatorDashboard(w http.ResponseWriter, r *http.Request) { + var v validationError + notificationId := v.checkRegex(reNonEmpty, mux.Vars(r)["notification_id"], "notification_id") + if v.hasErrors() { + handleErr(w, r, v) + return + } + data, err := h.dai.GetValidatorDashboardNotificationDetails(r.Context(), notificationId) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalGetUserNotificationsValidatorDashboardResponse{ + Data: *data, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicGetUserNotificationsAccountDashboard(w http.ResponseWriter, r *http.Request) { + var v validationError + notificationId := v.checkRegex(reNonEmpty, mux.Vars(r)["notification_id"], "notification_id") + if v.hasErrors() { + handleErr(w, r, v) + return + } + data, err := h.dai.GetAccountDashboardNotificationDetails(r.Context(), notificationId) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalGetUserNotificationsAccountDashboardResponse{ + Data: *data, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicGetUserNotificationMachines(w http.ResponseWriter, r *http.Request) { + var v validationError + userId, err := GetUserIdByContext(r) + if err != nil { + handleErr(w, r, err) + return + } + q := r.URL.Query() + pagingParams := v.checkPagingParams(q) + sort := checkSort[enums.NotificationMachinesColumn](&v, q.Get("sort")) + if v.hasErrors() { + handleErr(w, r, v) + return + } + data, paging, err := h.dai.GetMachineNotifications(r.Context(), userId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalGetUserNotificationMachinesResponse{ + Data: data, + Paging: *paging, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicGetUserNotificationClients(w http.ResponseWriter, r *http.Request) { + var v validationError + userId, err := GetUserIdByContext(r) + if err != nil { + handleErr(w, r, err) + return + } + q := r.URL.Query() + pagingParams := v.checkPagingParams(q) + sort := checkSort[enums.NotificationClientsColumn](&v, q.Get("sort")) + if v.hasErrors() { + handleErr(w, r, v) + return + } + data, paging, err := h.dai.GetClientNotifications(r.Context(), userId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalGetUserNotificationClientsResponse{ + Data: data, + Paging: *paging, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicGetUserNotificationRocketPool(w http.ResponseWriter, r *http.Request) { + var v validationError + userId, err := GetUserIdByContext(r) + if err != nil { + handleErr(w, r, err) + return + } + q := r.URL.Query() + pagingParams := v.checkPagingParams(q) + sort := checkSort[enums.NotificationRocketPoolColumn](&v, q.Get("sort")) + if v.hasErrors() { + handleErr(w, r, v) + return + } + data, paging, err := h.dai.GetRocketPoolNotifications(r.Context(), userId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalGetUserNotificationRocketPoolResponse{ + Data: data, + Paging: *paging, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicGetUserNotificationNetworks(w http.ResponseWriter, r *http.Request) { + var v validationError + userId, err := GetUserIdByContext(r) + if err != nil { + handleErr(w, r, err) + return + } + q := r.URL.Query() + pagingParams := v.checkPagingParams(q) + sort := checkSort[enums.NotificationNetworksColumn](&v, q.Get("sort")) + if v.hasErrors() { + handleErr(w, r, v) + return + } + data, paging, err := h.dai.GetNetworkNotifications(r.Context(), userId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalGetUserNotificationNetworksResponse{ + Data: data, + Paging: *paging, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicGetUserNotificationSettings(w http.ResponseWriter, r *http.Request) { + userId, err := GetUserIdByContext(r) + if err != nil { + handleErr(w, r, err) + return + } + data, err := h.dai.GetNotificationSettings(r.Context(), userId) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalGetUserNotificationSettingsResponse{ + Data: *data, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicPutUserNotificationSettingsGeneral(w http.ResponseWriter, r *http.Request) { + var v validationError + userId, err := GetUserIdByContext(r) + if err != nil { + handleErr(w, r, err) + return + } + var req types.NotificationSettingsGeneral + if err := v.checkBody(&req, r); err != nil { + handleErr(w, r, err) + return + } + checkMinMax(&v, req.MachineStorageUsageThreshold, 0, 1, "machine_storage_usage_threshold") + checkMinMax(&v, req.MachineCpuUsageThreshold, 0, 1, "machine_cpu_usage_threshold") + checkMinMax(&v, req.MachineMemoryUsageThreshold, 0, 1, "machine_memory_usage_threshold") + checkMinMax(&v, req.RocketPoolMaxCollateralThreshold, 0, 1, "rocket_pool_max_collateral_threshold") + checkMinMax(&v, req.RocketPoolMinCollateralThreshold, 0, 1, "rocket_pool_min_collateral_threshold") + // TODO: check validity of clients + if v.hasErrors() { + handleErr(w, r, v) + return + } + err = h.dai.UpdateNotificationSettingsGeneral(r.Context(), userId, req) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalPutUserNotificationSettingsGeneralResponse{ + Data: req, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicPutUserNotificationSettingsNetworks(w http.ResponseWriter, r *http.Request) { + var v validationError + userId, err := GetUserIdByContext(r) + if err != nil { + handleErr(w, r, err) + return + } + var req types.NotificationSettingsNetwork + if err := v.checkBody(&req, r); err != nil { + handleErr(w, r, err) + return + } + checkMinMax(&v, req.ParticipationRateThreshold, 0, 1, "participation_rate_threshold") + + chainId := v.checkNetworkParameter(mux.Vars(r)["network"]) + if v.hasErrors() { + handleErr(w, r, v) + return + } + err = h.dai.UpdateNotificationSettingsNetworks(r.Context(), userId, chainId, req) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalPutUserNotificationSettingsNetworksResponse{ + Data: types.NotificationNetwork{ + ChainId: chainId, + Settings: req, + }, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicPutUserNotificationSettingsPairedDevices(w http.ResponseWriter, r *http.Request) { + var v validationError + req := struct { + Name string `json:"name,omitempty"` + IsNotificationsEnabled bool `json:"is_notifications_enabled"` + }{} + if err := v.checkBody(&req, r); err != nil { + handleErr(w, r, err) + return + } + // TODO use a better way to validate the paired device id + pairedDeviceId := v.checkRegex(reNonEmpty, mux.Vars(r)["paired_device_id"], "paired_device_id") + name := v.checkNameNotEmpty(req.Name) + if v.hasErrors() { + handleErr(w, r, v) + return + } + err := h.dai.UpdateNotificationSettingsPairedDevice(r.Context(), pairedDeviceId, name, req.IsNotificationsEnabled) + if err != nil { + handleErr(w, r, err) + return + } + // TODO timestamp + response := types.InternalPutUserNotificationSettingsPairedDevicesResponse{ + Data: types.NotificationPairedDevice{ + Id: pairedDeviceId, + Name: req.Name, + IsNotificationsEnabled: req.IsNotificationsEnabled, + }, + } + + returnOk(w, r, response) +} + +func (h *HandlerService) PublicDeleteUserNotificationSettingsPairedDevices(w http.ResponseWriter, r *http.Request) { + var v validationError + // TODO use a better way to validate the paired device id + pairedDeviceId := v.checkRegex(reNonEmpty, mux.Vars(r)["paired_device_id"], "paired_device_id") + if v.hasErrors() { + handleErr(w, r, v) + return + } + err := h.dai.DeleteNotificationSettingsPairedDevice(r.Context(), pairedDeviceId) + if err != nil { + handleErr(w, r, err) + return + } + returnNoContent(w, r) +} + +func (h *HandlerService) PublicGetUserNotificationSettingsDashboards(w http.ResponseWriter, r *http.Request) { + var v validationError + userId, err := GetUserIdByContext(r) + if err != nil { + handleErr(w, r, err) + return + } + q := r.URL.Query() + pagingParams := v.checkPagingParams(q) + sort := checkSort[enums.NotificationSettingsDashboardColumn](&v, q.Get("sort")) + if v.hasErrors() { + handleErr(w, r, v) + return + } + data, paging, err := h.dai.GetNotificationSettingsDashboards(r.Context(), userId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalGetUserNotificationSettingsDashboardsResponse{ + Data: data, + Paging: *paging, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicPutUserNotificationSettingsValidatorDashboard(w http.ResponseWriter, r *http.Request) { + var v validationError + var req types.NotificationSettingsValidatorDashboard + if err := v.checkBody(&req, r); err != nil { + handleErr(w, r, err) + return + } + checkMinMax(&v, req.GroupOfflineThreshold, 0, 1, "group_offline_threshold") + vars := mux.Vars(r) + dashboardId := v.checkPrimaryDashboardId(vars["dashboard_id"]) + groupId := v.checkExistingGroupId(vars["group_id"]) + if v.hasErrors() { + handleErr(w, r, v) + return + } + err := h.dai.UpdateNotificationSettingsValidatorDashboard(r.Context(), dashboardId, groupId, req) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalPutUserNotificationSettingsValidatorDashboardResponse{ + Data: req, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicPutUserNotificationSettingsAccountDashboard(w http.ResponseWriter, r *http.Request) { + var v validationError + req := struct { + WebhookUrl string `json:"webhook_url"` + IsWebhookDiscordEnabled bool `json:"is_webhook_discord_enabled"` + IsIgnoreSpamTransactionsEnabled bool `json:"is_ignore_spam_transactions_enabled"` + SubscribedChainIds []intOrString `json:"subscribed_chain_ids"` + + IsIncomingTransactionsSubscribed bool `json:"is_incoming_transactions_subscribed"` + IsOutgoingTransactionsSubscribed bool `json:"is_outgoing_transactions_subscribed"` + IsERC20TokenTransfersSubscribed bool `json:"is_erc20_token_transfers_subscribed"` + ERC20TokenTransfersValueThreshold float64 `json:"erc20_token_transfers_value_threshold"` // 0 does not disable, is_erc20_token_transfers_subscribed determines if it's enabled + IsERC721TokenTransfersSubscribed bool `json:"is_erc721_token_transfers_subscribed"` + IsERC1155TokenTransfersSubscribed bool `json:"is_erc1155_token_transfers_subscribed"` + }{} + if err := v.checkBody(&req, r); err != nil { + handleErr(w, r, err) + return + } + chainIdMap := v.checkNetworkSlice(req.SubscribedChainIds) + // convert to uint64[] slice + chainIds := make([]uint64, len(chainIdMap)) + i := 0 + for k := range chainIdMap { + chainIds[i] = k + i++ + } + checkMinMax(&v, req.ERC20TokenTransfersValueThreshold, 0, math.MaxFloat64, "group_offline_threshold") + vars := mux.Vars(r) + dashboardId := v.checkPrimaryDashboardId(vars["dashboard_id"]) + groupId := v.checkExistingGroupId(vars["group_id"]) + if v.hasErrors() { + handleErr(w, r, v) + return + } + settings := types.NotificationSettingsAccountDashboard{ + WebhookUrl: req.WebhookUrl, + IsWebhookDiscordEnabled: req.IsWebhookDiscordEnabled, + IsIgnoreSpamTransactionsEnabled: req.IsIgnoreSpamTransactionsEnabled, + SubscribedChainIds: chainIds, + + IsIncomingTransactionsSubscribed: req.IsIncomingTransactionsSubscribed, + IsOutgoingTransactionsSubscribed: req.IsOutgoingTransactionsSubscribed, + IsERC20TokenTransfersSubscribed: req.IsERC20TokenTransfersSubscribed, + ERC20TokenTransfersValueThreshold: req.ERC20TokenTransfersValueThreshold, + IsERC721TokenTransfersSubscribed: req.IsERC721TokenTransfersSubscribed, + IsERC1155TokenTransfersSubscribed: req.IsERC1155TokenTransfersSubscribed, + } + err := h.dai.UpdateNotificationSettingsAccountDashboard(r.Context(), dashboardId, groupId, settings) + if err != nil { + handleErr(w, r, err) + return + } + response := types.InternalPutUserNotificationSettingsAccountDashboardResponse{ + Data: settings, + } + returnOk(w, r, response) +} + +func (h *HandlerService) PublicPostUserNotificationsTestEmail(w http.ResponseWriter, r *http.Request) { + // TODO + returnOk(w, r, nil) +} + +func (h *HandlerService) PublicPostUserNotificationsTestPush(w http.ResponseWriter, r *http.Request) { + // TODO + returnOk(w, r, nil) +} + +func (h *HandlerService) PublicPostUserNotificationsTestWebhook(w http.ResponseWriter, r *http.Request) { + var v validationError + req := struct { + WebhookUrl string `json:"webhook_url"` + IsDiscordWebhookEnabled bool `json:"is_discord_webhook_enabled,omitempty"` + }{} + if err := v.checkBody(&req, r); err != nil { + handleErr(w, r, err) + return + } + if v.hasErrors() { + handleErr(w, r, v) + return + } + // TODO + returnOk(w, r, nil) +} + func (h *HandlerService) PublicGetNetworkValidators(w http.ResponseWriter, r *http.Request) { returnOk(w, r, nil) } diff --git a/backend/pkg/api/router.go b/backend/pkg/api/router.go index 5338b5ccd..854194bbb 100644 --- a/backend/pkg/api/router.go +++ b/backend/pkg/api/router.go @@ -317,23 +317,23 @@ func addNotificationRoutes(hs *handlers.HandlerService, publicRouter, internalRo publicNotificationRouter.Use(hs.ManageViaApiCheckMiddleware) } endpoints := []endpoint{ - {http.MethodGet, "", nil, hs.InternalGetUserNotifications}, - {http.MethodGet, "/dashboards", nil, hs.InternalGetUserNotificationDashboards}, - {http.MethodGet, "/validator-dashboards/{notification_id}", nil, hs.InternalGetUserNotificationsValidatorDashboard}, - {http.MethodGet, "/account-dashboards/{notification_id}", nil, hs.InternalGetUserNotificationsAccountDashboard}, - {http.MethodGet, "/machines", nil, hs.InternalGetUserNotificationMachines}, - {http.MethodGet, "/clients", nil, hs.InternalGetUserNotificationClients}, - {http.MethodGet, "/rocket-pool", nil, hs.InternalGetUserNotificationRocketPool}, - {http.MethodGet, "/networks", nil, hs.InternalGetUserNotificationNetworks}, - {http.MethodGet, "/settings", nil, hs.InternalGetUserNotificationSettings}, - {http.MethodPut, "/settings/general", nil, hs.InternalPutUserNotificationSettingsGeneral}, - {http.MethodPut, "/settings/networks/{network}", nil, hs.InternalPutUserNotificationSettingsNetworks}, - {http.MethodPut, "/settings/paired-devices/{paired_device_id}", nil, hs.InternalPutUserNotificationSettingsPairedDevices}, - {http.MethodDelete, "/settings/paired-devices/{paired_device_id}", nil, hs.InternalDeleteUserNotificationSettingsPairedDevices}, - {http.MethodGet, "/settings/dashboards", nil, hs.InternalGetUserNotificationSettingsDashboards}, - {http.MethodPost, "/test-email", nil, hs.InternalPostUserNotificationsTestEmail}, - {http.MethodPost, "/test-push", nil, hs.InternalPostUserNotificationsTestPush}, - {http.MethodPost, "/test-webhook", nil, hs.InternalPostUserNotificationsTestWebhook}, + {http.MethodGet, "", hs.PublicGetUserNotifications, hs.InternalGetUserNotifications}, + {http.MethodGet, "/dashboards", hs.PublicGetUserNotificationDashboards, hs.InternalGetUserNotificationDashboards}, + {http.MethodGet, "/validator-dashboards/{notification_id}", hs.PublicGetUserNotificationsValidatorDashboard, hs.InternalGetUserNotificationsValidatorDashboard}, + {http.MethodGet, "/account-dashboards/{notification_id}", hs.PublicGetUserNotificationsAccountDashboard, hs.InternalGetUserNotificationsAccountDashboard}, + {http.MethodGet, "/machines", hs.PublicGetUserNotificationMachines, hs.InternalGetUserNotificationMachines}, + {http.MethodGet, "/clients", hs.PublicGetUserNotificationClients, hs.InternalGetUserNotificationClients}, + {http.MethodGet, "/rocket-pool", hs.PublicGetUserNotificationRocketPool, hs.InternalGetUserNotificationRocketPool}, + {http.MethodGet, "/networks", hs.PublicGetUserNotificationNetworks, hs.InternalGetUserNotificationNetworks}, + {http.MethodGet, "/settings", hs.PublicGetUserNotificationSettings, hs.InternalGetUserNotificationSettings}, + {http.MethodPut, "/settings/general", hs.PublicPutUserNotificationSettingsGeneral, hs.InternalPutUserNotificationSettingsGeneral}, + {http.MethodPut, "/settings/networks/{network}", hs.PublicPutUserNotificationSettingsNetworks, hs.InternalPutUserNotificationSettingsNetworks}, + {http.MethodPut, "/settings/paired-devices/{paired_device_id}", hs.PublicPutUserNotificationSettingsPairedDevices, hs.InternalPutUserNotificationSettingsPairedDevices}, + {http.MethodDelete, "/settings/paired-devices/{paired_device_id}", hs.PublicDeleteUserNotificationSettingsPairedDevices, hs.InternalDeleteUserNotificationSettingsPairedDevices}, + {http.MethodGet, "/settings/dashboards", hs.PublicGetUserNotificationSettingsDashboards, hs.InternalGetUserNotificationSettingsDashboards}, + {http.MethodPost, "/test-email", hs.PublicPostUserNotificationsTestEmail, hs.InternalPostUserNotificationsTestEmail}, + {http.MethodPost, "/test-push", hs.PublicPostUserNotificationsTestPush, hs.InternalPostUserNotificationsTestPush}, + {http.MethodPost, "/test-webhook", hs.PublicPostUserNotificationsTestWebhook, hs.InternalPostUserNotificationsTestWebhook}, } addEndpointsToRouters(endpoints, publicNotificationRouter, internalNotificationRouter) @@ -344,8 +344,8 @@ func addNotificationRoutes(hs *handlers.HandlerService, publicRouter, internalRo internalDashboardNotificationSettingsRouter.Use(hs.VDBAuthMiddleware) } dashboardSettingsEndpoints := []endpoint{ - {http.MethodPut, "/settings/validator-dashboards/{dashboard_id}/groups/{group_id}", nil, hs.InternalPutUserNotificationSettingsValidatorDashboard}, - {http.MethodPut, "/settings/account-dashboards/{dashboard_id}/groups/{group_id}", nil, hs.InternalPutUserNotificationSettingsAccountDashboard}, + {http.MethodPut, "/settings/validator-dashboards/{dashboard_id}/groups/{group_id}", hs.PublicPutUserNotificationSettingsValidatorDashboard, hs.InternalPutUserNotificationSettingsValidatorDashboard}, + {http.MethodPut, "/settings/account-dashboards/{dashboard_id}/groups/{group_id}", hs.PublicPutUserNotificationSettingsAccountDashboard, hs.InternalPutUserNotificationSettingsAccountDashboard}, } addEndpointsToRouters(dashboardSettingsEndpoints, publicDashboardNotificationSettingsRouter, internalDashboardNotificationSettingsRouter) } From 7e94af1d620e3dcfe799bd4c2a8e2de5433a2624 Mon Sep 17 00:00:00 2001 From: LUCCA DUKIC <109136188+LuccaBitfly@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:08:54 +0200 Subject: [PATCH 2/5] (BEDS-464) annotate notification endpoints --- backend/pkg/api/data_access/dummy.go | 4 +- backend/pkg/api/data_access/notifications.go | 12 +- .../api/enums/validator_dashboard_enums.go | 2 +- backend/pkg/api/handlers/public.go | 259 ++++++++++++++++-- backend/pkg/api/router.go | 4 +- 5 files changed, 246 insertions(+), 35 deletions(-) diff --git a/backend/pkg/api/data_access/dummy.go b/backend/pkg/api/data_access/dummy.go index 4590a106b..1dc80125d 100644 --- a/backend/pkg/api/data_access/dummy.go +++ b/backend/pkg/api/data_access/dummy.go @@ -452,11 +452,11 @@ func (d *DummyService) GetDashboardNotifications(ctx context.Context, userId uin return getDummyWithPaging[t.NotificationDashboardsTableRow]() } -func (d *DummyService) GetValidatorDashboardNotificationDetails(ctx context.Context, notificationId string) (*t.NotificationValidatorDashboardDetail, error) { +func (d *DummyService) GetValidatorDashboardNotificationDetails(ctx context.Context, dashboardId t.VDBIdPrimary, groupId uint64, epoch uint64) (*t.NotificationValidatorDashboardDetail, error) { return getDummyStruct[t.NotificationValidatorDashboardDetail]() } -func (d *DummyService) GetAccountDashboardNotificationDetails(ctx context.Context, notificationId string) (*t.NotificationAccountDashboardDetail, error) { +func (d *DummyService) GetAccountDashboardNotificationDetails(ctx context.Context, dashboardId uint64, groupId uint64, epoch uint64) (*t.NotificationAccountDashboardDetail, error) { return getDummyStruct[t.NotificationAccountDashboardDetail]() } diff --git a/backend/pkg/api/data_access/notifications.go b/backend/pkg/api/data_access/notifications.go index 4859b770c..94530d441 100644 --- a/backend/pkg/api/data_access/notifications.go +++ b/backend/pkg/api/data_access/notifications.go @@ -12,8 +12,8 @@ type NotificationsRepository interface { GetDashboardNotifications(ctx context.Context, userId uint64, chainId uint64, cursor string, colSort t.Sort[enums.NotificationDashboardsColumn], search string, limit uint64) ([]t.NotificationDashboardsTableRow, *t.Paging, error) // depending on how notifications are implemented, we may need to use something other than `notificationId` for identifying the notification - GetValidatorDashboardNotificationDetails(ctx context.Context, notificationId string) (*t.NotificationValidatorDashboardDetail, error) - GetAccountDashboardNotificationDetails(ctx context.Context, notificationId string) (*t.NotificationAccountDashboardDetail, error) + GetValidatorDashboardNotificationDetails(ctx context.Context, dashboardId t.VDBIdPrimary, groupId uint64, epoch uint64) (*t.NotificationValidatorDashboardDetail, error) + GetAccountDashboardNotificationDetails(ctx context.Context, dashboardId uint64, groupId uint64, epoch uint64) (*t.NotificationAccountDashboardDetail, error) GetMachineNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationMachinesColumn], search string, limit uint64) ([]t.NotificationMachinesTableRow, *t.Paging, error) GetClientNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationClientsColumn], search string, limit uint64) ([]t.NotificationClientsTableRow, *t.Paging, error) @@ -37,12 +37,12 @@ func (d *DataAccessService) GetDashboardNotifications(ctx context.Context, userI return d.dummy.GetDashboardNotifications(ctx, userId, chainId, cursor, colSort, search, limit) } -func (d *DataAccessService) GetValidatorDashboardNotificationDetails(ctx context.Context, notificationId string) (*t.NotificationValidatorDashboardDetail, error) { - return d.dummy.GetValidatorDashboardNotificationDetails(ctx, notificationId) +func (d *DataAccessService) GetValidatorDashboardNotificationDetails(ctx context.Context, dashboardId t.VDBIdPrimary, groupId uint64, epoch uint64) (*t.NotificationValidatorDashboardDetail, error) { + return d.dummy.GetValidatorDashboardNotificationDetails(ctx, dashboardId, groupId, epoch) } -func (d *DataAccessService) GetAccountDashboardNotificationDetails(ctx context.Context, notificationId string) (*t.NotificationAccountDashboardDetail, error) { - return d.dummy.GetAccountDashboardNotificationDetails(ctx, notificationId) +func (d *DataAccessService) GetAccountDashboardNotificationDetails(ctx context.Context, dashboardId uint64, groupId uint64, epoch uint64) (*t.NotificationAccountDashboardDetail, error) { + return d.dummy.GetAccountDashboardNotificationDetails(ctx, dashboardId, groupId, epoch) } func (d *DataAccessService) GetMachineNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationMachinesColumn], search string, limit uint64) ([]t.NotificationMachinesTableRow, *t.Paging, error) { diff --git a/backend/pkg/api/enums/validator_dashboard_enums.go b/backend/pkg/api/enums/validator_dashboard_enums.go index 928d5742c..fcbfec71b 100644 --- a/backend/pkg/api/enums/validator_dashboard_enums.go +++ b/backend/pkg/api/enums/validator_dashboard_enums.go @@ -437,7 +437,7 @@ func (c VDBRocketPoolMinipoolsColumn) Int() int { func (VDBRocketPoolMinipoolsColumn) NewFromString(s string) VDBRocketPoolMinipoolsColumn { switch s { - case "group": + case "group_id": return VDBRocketPoolMinipoolsGroup default: return VDBRocketPoolMinipoolsColumn(-1) diff --git a/backend/pkg/api/handlers/public.go b/backend/pkg/api/handlers/public.go index 4316aac3d..7b84e0bf7 100644 --- a/backend/pkg/api/handlers/public.go +++ b/backend/pkg/api/handlers/public.go @@ -37,6 +37,8 @@ import ( // @in query // @name api_key +// @Validator Dashboard Management.n + func (h *HandlerService) PublicGetHealthz(w http.ResponseWriter, r *http.Request) { var v validationError showAll := v.checkBool(r.URL.Query().Get("show_all"), "show_all") @@ -136,7 +138,7 @@ func (h *HandlerService) PublicPutAccountDashboardTransactionsSettings(w http.Re // // @Description Create a new validator dashboard. **Note**: New dashboards will automatically have a default group created. // @Security ApiKeyInHeader || ApiKeyInQuery -// @Tags Validator Dashboard +// @Tags Validator Dashboard Management // @Accept json // @Produce json // @Param request body handlers.PublicPostValidatorDashboards.request true "`name`: Specify the name of the dashboard.
`network`: Specify the network for the dashboard. Possible options are:" @@ -619,6 +621,8 @@ func (h *HandlerService) PublicPostValidatorDashboardValidators(w http.ResponseW // // @Description Get a list of groups in a specified validator dashboard. // @Tags Validator Dashboard +// @Produce json +// @Param dashboard_id path string true "The ID of the dashboard." // @Param group_id query string false "The ID of the group." // @Param limit query string false "The maximum number of results that may be returned." // @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order." Enums(index, public_key, balance, status, withdrawal_credentials) @@ -658,14 +662,13 @@ func (h *HandlerService) PublicGetValidatorDashboardValidators(w http.ResponseWr // @Description Remove validators from a specified dashboard. // @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Validator Dashboard Management - -// @Accept json -// @Produce json -// @Param dashboard_id path string true "The ID of the dashboard." -// @Param request body handlers.PublicDeleteValidatorDashboardValidators.request true "`validators`: Provide an array of validator indices or public keys that should get removed from the dashboard." -// @Success 204 "Validators removed successfully." -// @Failure 400 {object} types.ApiErrorResponse -// @Router /validator-dashboards/{dashboard_id}/validators/bulk-deletions [post] +// @Accept json +// @Produce json +// @Param dashboard_id path string true "The ID of the dashboard." +// @Param request body handlers.PublicDeleteValidatorDashboardValidators.request true "`validators`: Provide an array of validator indices or public keys that should get removed from the dashboard." +// @Success 204 "Validators removed successfully." +// @Failure 400 {object} types.ApiErrorResponse +// @Router /validator-dashboards/{dashboard_id}/validators/bulk-deletions [post] func (h *HandlerService) PublicDeleteValidatorDashboardValidators(w http.ResponseWriter, r *http.Request) { var v validationError dashboardId := v.checkPrimaryDashboardId(mux.Vars(r)["dashboard_id"]) @@ -856,7 +859,7 @@ func (h *HandlerService) PublicDeleteValidatorDashboardPublicId(w http.ResponseW // @Param request body handlers.PublicPutValidatorDashboardArchiving.request true "request" // @Success 200 {object} types.ApiDataResponse[types.VDBPostArchivingReturnData] // @Failure 400 {object} types.ApiErrorResponse -// @Conflict 409 {object} types.ApiErrorResponse "Conflict. The request could not be performed by the server because the authenticated user has already reached their subscription limit." +// @Conflict 409 {object} types.ApiErrorResponse "Conflict. The request could not be performed by the server because the authenticated user has already reached their subscription limit." // @Router /validator-dashboards/{dashboard_id}/archiving [put] func (h *HandlerService) PublicPutValidatorDashboardArchiving(w http.ResponseWriter, r *http.Request) { var v validationError @@ -1820,7 +1823,7 @@ func (h *HandlerService) PublicGetValidatorDashboardNodeRocketPool(w http.Respon // @Param node_address path string true "The address of the node." // @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." // @Param limit query string false "The maximum number of results that may be returned." -// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order. Possible values are TODO." +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order." Enums(group_id) // @Param search query string false "Search for Index, Node." // @Success 200 {object} types.GetValidatorDashboardRocketPoolMinipoolsResponse // @Failure 400 {object} types.ApiErrorResponse @@ -1859,6 +1862,13 @@ func (h *HandlerService) PublicGetValidatorDashboardRocketPoolMinipools(w http.R // Notifications // ---------------------------------------------- +// PublicGetUserNotifications godoc +// +// @Description Get an overview of your recent notifications. +// @Tags Notifications +// @Produce json +// @Success 200 {object} types.InternalGetUserNotificationsResponse +// @Router /users/me/notifications [get] func (h *HandlerService) PublicGetUserNotifications(w http.ResponseWriter, r *http.Request) { userId, err := GetUserIdByContext(r) if err != nil { @@ -1876,6 +1886,19 @@ func (h *HandlerService) PublicGetUserNotifications(w http.ResponseWriter, r *ht returnOk(w, r, response) } +// PublicGetUserNotificationDashboards godoc +// +// @Description Get a list of triggered notifications related to your dashboards. +// @Tags Notifications +// @Produce json +// @Param network query string false "If set, results will be filtered to only include networks given. Provide a comma seperated list." +// @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." +// @Param limit query string false "The maximum number of results that may be returned." +// @Param sort query string false "The field you want to sort by. TODO Enums" +// @Param search query string false "Search for TODO" +// @Success 200 {object} types.InternalGetUserNotificationDashboardsResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/dashboards [get] func (h *HandlerService) PublicGetUserNotificationDashboards(w http.ResponseWriter, r *http.Request) { var v validationError userId, err := GetUserIdByContext(r) @@ -1903,14 +1926,28 @@ func (h *HandlerService) PublicGetUserNotificationDashboards(w http.ResponseWrit returnOk(w, r, response) } +// PublicGetUserNotificationValidators godoc +// +// @Description Get a detailed view of a triggered notification related to a validator dashboard group at a specific epoch. +// @Tags Notifications +// @Produce json +// @Param dashboard_id path string true "The ID of the dashboard." +// @Param group_id path string true "The ID of the group." +// @Param epoch path string true "The epoch of the notification." +// @Success 200 {object} types.InternalGetUserNotificationsValidatorDashboardResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/validator-dashboards/{dashboard_id}/groups/{group_id}/epochs/{epoch} [get] func (h *HandlerService) PublicGetUserNotificationsValidatorDashboard(w http.ResponseWriter, r *http.Request) { var v validationError - notificationId := v.checkRegex(reNonEmpty, mux.Vars(r)["notification_id"], "notification_id") + vars := mux.Vars(r) + dashboardId := v.checkPrimaryDashboardId(vars["dashboard_id"]) + groupId := v.checkExistingGroupId(vars["group_id"]) + epoch := v.checkUint(vars["epoch"], "epoch") if v.hasErrors() { handleErr(w, r, v) return } - data, err := h.dai.GetValidatorDashboardNotificationDetails(r.Context(), notificationId) + data, err := h.dai.GetValidatorDashboardNotificationDetails(r.Context(), dashboardId, groupId, epoch) if err != nil { handleErr(w, r, err) return @@ -1921,14 +1958,28 @@ func (h *HandlerService) PublicGetUserNotificationsValidatorDashboard(w http.Res returnOk(w, r, response) } +// PublicGetUserNotificationsAccountDashboard godoc +// +// @Description Get a detailed view of a triggered notification related to an account dashboard group at a specific epoch. +// @Tags Notifications +// @Produce json +// @Param dashboard_id path string true "The ID of the dashboard." +// @Param group_id path string true "The ID of the group." +// @Param epoch path string true "The epoch of the notification." +// @Success 200 {object} types.InternalGetUserNotificationsAccountDashboardResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/account-dashboards/{dashboard_id}/groups/{group_id}/epochs/{epoch} [get] func (h *HandlerService) PublicGetUserNotificationsAccountDashboard(w http.ResponseWriter, r *http.Request) { var v validationError - notificationId := v.checkRegex(reNonEmpty, mux.Vars(r)["notification_id"], "notification_id") + vars := mux.Vars(r) + dashboardId := v.checkUint(vars["dashboard_id"], "dashboard_id") + groupId := v.checkExistingGroupId(vars["group_id"]) + epoch := v.checkUint(vars["epoch"], "epoch") if v.hasErrors() { handleErr(w, r, v) return } - data, err := h.dai.GetAccountDashboardNotificationDetails(r.Context(), notificationId) + data, err := h.dai.GetAccountDashboardNotificationDetails(r.Context(), dashboardId, groupId, epoch) if err != nil { handleErr(w, r, err) return @@ -1939,6 +1990,18 @@ func (h *HandlerService) PublicGetUserNotificationsAccountDashboard(w http.Respo returnOk(w, r, response) } +// PublicGetUserNotificationMachines godoc +// +// @Description Get a list of triggered notifications related to your machines. +// @Tags Notifications +// @Produce json +// @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." +// @Param limit query string false "The maximum number of results that may be returned." +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order. TODO Enums" +// @Param search query string false "Search for TODO" +// @Success 200 {object} types.InternalGetUserNotificationMachinesResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/machines [get] func (h *HandlerService) PublicGetUserNotificationMachines(w http.ResponseWriter, r *http.Request) { var v validationError userId, err := GetUserIdByContext(r) @@ -1965,6 +2028,18 @@ func (h *HandlerService) PublicGetUserNotificationMachines(w http.ResponseWriter returnOk(w, r, response) } +// PublicGetUserNotificationClients godoc +// +// @Description Get a list of triggered notifications related to your clients. +// @Tags Notifications +// @Produce json +// @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." +// @Param limit query string false "The maximum number of results that may be returned." +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order. TODO Enums" +// @Param search query string false "Search for TODO" +// @Success 200 {object} types.InternalGetUserNotificationClientsResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/clients [get] func (h *HandlerService) PublicGetUserNotificationClients(w http.ResponseWriter, r *http.Request) { var v validationError userId, err := GetUserIdByContext(r) @@ -1991,6 +2066,18 @@ func (h *HandlerService) PublicGetUserNotificationClients(w http.ResponseWriter, returnOk(w, r, response) } +// PublicGetUserNotificationRocketPool godoc +// +// @Description Get a list of triggered notifications related to Rocket Pool. +// @Tags Notifications +// @Produce json +// @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." +// @Param limit query string false "The maximum number of results that may be returned." +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order. TODO Enums" +// @Param search query string false "Search for TODO" +// @Success 200 {object} types.InternalGetUserNotificationRocketPoolResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/rocket-pool [get] func (h *HandlerService) PublicGetUserNotificationRocketPool(w http.ResponseWriter, r *http.Request) { var v validationError userId, err := GetUserIdByContext(r) @@ -2017,6 +2104,18 @@ func (h *HandlerService) PublicGetUserNotificationRocketPool(w http.ResponseWrit returnOk(w, r, response) } +// PublicGetUserNotificationNetworks godoc +// +// @Description Get a list of triggered notifications related to networks. +// @Tags Notifications +// @Produce json +// @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." +// @Param limit query string false "The maximum number of results that may be returned." +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order. TODO Enums" +// @Param search query string false "Search for TODO" +// @Success 200 {object} types.InternalGetUserNotificationNetworksResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/networks [get] func (h *HandlerService) PublicGetUserNotificationNetworks(w http.ResponseWriter, r *http.Request) { var v validationError userId, err := GetUserIdByContext(r) @@ -2043,6 +2142,13 @@ func (h *HandlerService) PublicGetUserNotificationNetworks(w http.ResponseWriter returnOk(w, r, response) } +// PublicGetUserNotificationPairedDevices godoc +// +// @Description Get notification settings for the authenticated user. Excludes dashboard notification settings. +// @Tags Notification Settings +// @Produce json +// @Success 200 {object} types.InternalGetUserNotificationSettingsResponse +// @Router /users/me/notifications/settings [get] func (h *HandlerService) PublicGetUserNotificationSettings(w http.ResponseWriter, r *http.Request) { userId, err := GetUserIdByContext(r) if err != nil { @@ -2060,6 +2166,16 @@ func (h *HandlerService) PublicGetUserNotificationSettings(w http.ResponseWriter returnOk(w, r, response) } +// PublicPutUserNotificationSettingsGeneral godoc +// +// @Description Update general notification settings for the authenticated user. +// @Tags Notification Settings +// @Accept json +// @Produce json +// @Param request body types.NotificationSettingsGeneral true "Notification settings" +// @Success 200 {object} types.InternalPutUserNotificationSettingsGeneralResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/settings/general [put] func (h *HandlerService) PublicPutUserNotificationSettingsGeneral(w http.ResponseWriter, r *http.Request) { var v validationError userId, err := GetUserIdByContext(r) @@ -2093,6 +2209,17 @@ func (h *HandlerService) PublicPutUserNotificationSettingsGeneral(w http.Respons returnOk(w, r, response) } +// PublicPutUserNotificationSettingsNetworks godoc +// +// @Description Update network notification settings for the authenticated user. +// @Tags Notification Settings +// @Accept json +// @Produce json +// @Param network path string true "The networks name or chain ID." +// @Param request body types.NotificationSettingsNetwork true "Notification settings" +// @Success 200 {object} types.InternalPutUserNotificationSettingsNetworksResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/settings/networks/{network} [put] func (h *HandlerService) PublicPutUserNotificationSettingsNetworks(w http.ResponseWriter, r *http.Request) { var v validationError userId, err := GetUserIdByContext(r) @@ -2126,12 +2253,24 @@ func (h *HandlerService) PublicPutUserNotificationSettingsNetworks(w http.Respon returnOk(w, r, response) } +// PublicPutUserNotificationSettingsPairedDevices godoc +// +// @Description Update paired device notification settings for the authenticated user. +// @Tags Notification Settings +// @Accept json +// @Produce json +// @Param paired_device_id path string true "The paired device ID." +// @Param request body handlers.PublicPutUserNotificationSettingsPairedDevices.request true "Notification settings" +// @Success 200 {object} types.InternalPutUserNotificationSettingsPairedDevicesResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/settings/paired-devices/{paired_device_id} [put] func (h *HandlerService) PublicPutUserNotificationSettingsPairedDevices(w http.ResponseWriter, r *http.Request) { var v validationError - req := struct { + type request struct { Name string `json:"name,omitempty"` IsNotificationsEnabled bool `json:"is_notifications_enabled"` - }{} + } + var req request if err := v.checkBody(&req, r); err != nil { handleErr(w, r, err) return @@ -2160,6 +2299,15 @@ func (h *HandlerService) PublicPutUserNotificationSettingsPairedDevices(w http.R returnOk(w, r, response) } +// PublicDeleteUserNotificationSettingsPairedDevices godoc +// +// @Description Delete paired device notification settings for the authenticated user. +// @Tags Notification Settings +// @Produce json +// @Param paired_device_id path string true "The paired device ID." +// @Success 204 +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/settings/paired-devices/{paired_device_id} [delete] func (h *HandlerService) PublicDeleteUserNotificationSettingsPairedDevices(w http.ResponseWriter, r *http.Request) { var v validationError // TODO use a better way to validate the paired device id @@ -2176,6 +2324,18 @@ func (h *HandlerService) PublicDeleteUserNotificationSettingsPairedDevices(w htt returnNoContent(w, r) } +// PublicGetUserNotificationSettingsDashboards godoc +// +// @Description Get a list of notification settings for the dashboards of the authenticated user. +// @Tags Notification Settings +// @Produce json +// @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." +// @Param limit query string false "The maximum number of results that may be returned." +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order. TODO Enums" +// @Param search query string false "Search for TODO" +// @Success 200 {object} types.InternalGetUserNotificationSettingsDashboardsResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/settings/dashboards [get] func (h *HandlerService) PublicGetUserNotificationSettingsDashboards(w http.ResponseWriter, r *http.Request) { var v validationError userId, err := GetUserIdByContext(r) @@ -2202,6 +2362,18 @@ func (h *HandlerService) PublicGetUserNotificationSettingsDashboards(w http.Resp returnOk(w, r, response) } +// PublicPutUserNotificationSettingsValidatorDashboard godoc +// +// @Description Update the notification settings for a specific group of a validator dashboard for the authenticated user. +// @Tags Notification Settings +// @Accept json +// @Produce json +// @Param dashboard_id path string true "The ID of the dashboard." +// @Param group_id path string true "The ID of the group." +// @Param request body types.NotificationSettingsValidatorDashboard true "Notification settings" +// @Success 200 {object} types.InternalPutUserNotificationSettingsValidatorDashboardResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/settings/validator-dashboards/{dashboard_id}/groups/{group_id} [put] func (h *HandlerService) PublicPutUserNotificationSettingsValidatorDashboard(w http.ResponseWriter, r *http.Request) { var v validationError var req types.NotificationSettingsValidatorDashboard @@ -2228,9 +2400,22 @@ func (h *HandlerService) PublicPutUserNotificationSettingsValidatorDashboard(w h returnOk(w, r, response) } +// PublicPutUserNotificationSettingsAccountDashboard godoc +// +// @Description Update the notification settings for a specific group of an account dashboard for the authenticated user. +// @Tags Notification Settings +// @Accept json +// @Produce json +// @Param dashboard_id path string true "The ID of the dashboard." +// @Param group_id path string true "The ID of the group." +// @Param request body handlers.PublicPutUserNotificationSettingsAccountDashboard.request true "Notification settings" +// @Success 200 {object} types.InternalPutUserNotificationSettingsAccountDashboardResponse +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/settings/account-dashboards/{dashboard_id}/groups/{group_id} [put] func (h *HandlerService) PublicPutUserNotificationSettingsAccountDashboard(w http.ResponseWriter, r *http.Request) { var v validationError - req := struct { + // uses a different struct due to `subscribed_chain_ids`, which is a slice of intOrString in the payload but a slice of uint64 in the response + type request struct { WebhookUrl string `json:"webhook_url"` IsWebhookDiscordEnabled bool `json:"is_webhook_discord_enabled"` IsIgnoreSpamTransactionsEnabled bool `json:"is_ignore_spam_transactions_enabled"` @@ -2242,7 +2427,8 @@ func (h *HandlerService) PublicPutUserNotificationSettingsAccountDashboard(w htt ERC20TokenTransfersValueThreshold float64 `json:"erc20_token_transfers_value_threshold"` // 0 does not disable, is_erc20_token_transfers_subscribed determines if it's enabled IsERC721TokenTransfersSubscribed bool `json:"is_erc721_token_transfers_subscribed"` IsERC1155TokenTransfersSubscribed bool `json:"is_erc1155_token_transfers_subscribed"` - }{} + } + var req request if err := v.checkBody(&req, r); err != nil { handleErr(w, r, err) return @@ -2287,22 +2473,47 @@ func (h *HandlerService) PublicPutUserNotificationSettingsAccountDashboard(w htt returnOk(w, r, response) } +// PublicPostUserNotificationsTestEmail godoc +// +// @Description Send a test email notification to the authenticated user. +// @Tags Notification Settings +// @Produce json +// @Success 204 +// @Router /users/me/notifications/test-email [post] func (h *HandlerService) PublicPostUserNotificationsTestEmail(w http.ResponseWriter, r *http.Request) { // TODO - returnOk(w, r, nil) + returnNoContent(w, r) } +// PublicPostUserNotificationsTestPush godoc +// +// @Description Send a test push notification to the authenticated user. +// @Tags Notification Settings +// @Produce json +// @Success 204 +// @Router /users/me/notifications/test-push [post] func (h *HandlerService) PublicPostUserNotificationsTestPush(w http.ResponseWriter, r *http.Request) { // TODO - returnOk(w, r, nil) + returnNoContent(w, r) } +// PublicPostUserNotificationsTestWebhook godoc +// +// @Description Send a test webhook notification from the authenticated user to the given URL. +// @Tags Notification Settings +// @Accept json +// @Produce json +// @Param request body handlers.PublicPostUserNotificationsTestWebhook.request true "Request" +// @Success 204 +// @Failure 400 {object} types.ApiErrorResponse +// @Router /users/me/notifications/test-webhook [post] func (h *HandlerService) PublicPostUserNotificationsTestWebhook(w http.ResponseWriter, r *http.Request) { var v validationError - req := struct { + type request struct { WebhookUrl string `json:"webhook_url"` IsDiscordWebhookEnabled bool `json:"is_discord_webhook_enabled,omitempty"` - }{} + } + var req request if err := v.checkBody(&req, r); err != nil { handleErr(w, r, err) return @@ -2312,7 +2523,7 @@ func (h *HandlerService) PublicPostUserNotificationsTestWebhook(w http.ResponseW return } // TODO - returnOk(w, r, nil) + returnNoContent(w, r) } func (h *HandlerService) PublicGetNetworkValidators(w http.ResponseWriter, r *http.Request) { diff --git a/backend/pkg/api/router.go b/backend/pkg/api/router.go index 854194bbb..f6ba60902 100644 --- a/backend/pkg/api/router.go +++ b/backend/pkg/api/router.go @@ -319,8 +319,8 @@ func addNotificationRoutes(hs *handlers.HandlerService, publicRouter, internalRo endpoints := []endpoint{ {http.MethodGet, "", hs.PublicGetUserNotifications, hs.InternalGetUserNotifications}, {http.MethodGet, "/dashboards", hs.PublicGetUserNotificationDashboards, hs.InternalGetUserNotificationDashboards}, - {http.MethodGet, "/validator-dashboards/{notification_id}", hs.PublicGetUserNotificationsValidatorDashboard, hs.InternalGetUserNotificationsValidatorDashboard}, - {http.MethodGet, "/account-dashboards/{notification_id}", hs.PublicGetUserNotificationsAccountDashboard, hs.InternalGetUserNotificationsAccountDashboard}, + {http.MethodGet, "/validator-dashboards/{dashboard_id}/groups/{group_id}/epochs/{epoch}", hs.PublicGetUserNotificationsValidatorDashboard, hs.InternalGetUserNotificationsValidatorDashboard}, + {http.MethodGet, "/account-dashboards/{dashboard_id}/groups/{group_id}/epochs/{epoch}", hs.PublicGetUserNotificationsAccountDashboard, hs.InternalGetUserNotificationsAccountDashboard}, {http.MethodGet, "/machines", hs.PublicGetUserNotificationMachines, hs.InternalGetUserNotificationMachines}, {http.MethodGet, "/clients", hs.PublicGetUserNotificationClients, hs.InternalGetUserNotificationClients}, {http.MethodGet, "/rocket-pool", hs.PublicGetUserNotificationRocketPool, hs.InternalGetUserNotificationRocketPool}, From ca3b0e2cb8c5920bcab45591464961d8fa8e4955 Mon Sep 17 00:00:00 2001 From: LUCCA DUKIC <109136188+LuccaBitfly@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:17:03 +0200 Subject: [PATCH 3/5] (BEDS-464) add todos and security tags --- backend/pkg/api/handlers/public.go | 41 ++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/backend/pkg/api/handlers/public.go b/backend/pkg/api/handlers/public.go index 7b84e0bf7..91f047b0b 100644 --- a/backend/pkg/api/handlers/public.go +++ b/backend/pkg/api/handlers/public.go @@ -859,7 +859,7 @@ func (h *HandlerService) PublicDeleteValidatorDashboardPublicId(w http.ResponseW // @Param request body handlers.PublicPutValidatorDashboardArchiving.request true "request" // @Success 200 {object} types.ApiDataResponse[types.VDBPostArchivingReturnData] // @Failure 400 {object} types.ApiErrorResponse -// @Conflict 409 {object} types.ApiErrorResponse "Conflict. The request could not be performed by the server because the authenticated user has already reached their subscription limit." +// @Conflict 409 {object} types.ApiErrorResponse "Conflict. The request could not be performed by the server because the authenticated user has already reached their subscription limit." // @Router /validator-dashboards/{dashboard_id}/archiving [put] func (h *HandlerService) PublicPutValidatorDashboardArchiving(w http.ResponseWriter, r *http.Request) { var v validationError @@ -1865,6 +1865,7 @@ func (h *HandlerService) PublicGetValidatorDashboardRocketPoolMinipools(w http.R // PublicGetUserNotifications godoc // // @Description Get an overview of your recent notifications. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notifications // @Produce json // @Success 200 {object} types.InternalGetUserNotificationsResponse @@ -1889,13 +1890,14 @@ func (h *HandlerService) PublicGetUserNotifications(w http.ResponseWriter, r *ht // PublicGetUserNotificationDashboards godoc // // @Description Get a list of triggered notifications related to your dashboards. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notifications // @Produce json // @Param network query string false "If set, results will be filtered to only include networks given. Provide a comma seperated list." // @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." // @Param limit query string false "The maximum number of results that may be returned." -// @Param sort query string false "The field you want to sort by. TODO Enums" -// @Param search query string false "Search for TODO" +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order." " Enums(chain_id, timestamp, dashboard_id) +// @Param search query string false "Search for Dashboard, Group" // @Success 200 {object} types.InternalGetUserNotificationDashboardsResponse // @Failure 400 {object} types.ApiErrorResponse // @Router /users/me/notifications/dashboards [get] @@ -1929,6 +1931,7 @@ func (h *HandlerService) PublicGetUserNotificationDashboards(w http.ResponseWrit // PublicGetUserNotificationValidators godoc // // @Description Get a detailed view of a triggered notification related to a validator dashboard group at a specific epoch. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notifications // @Produce json // @Param dashboard_id path string true "The ID of the dashboard." @@ -1961,6 +1964,7 @@ func (h *HandlerService) PublicGetUserNotificationsValidatorDashboard(w http.Res // PublicGetUserNotificationsAccountDashboard godoc // // @Description Get a detailed view of a triggered notification related to an account dashboard group at a specific epoch. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notifications // @Produce json // @Param dashboard_id path string true "The ID of the dashboard." @@ -1993,12 +1997,13 @@ func (h *HandlerService) PublicGetUserNotificationsAccountDashboard(w http.Respo // PublicGetUserNotificationMachines godoc // // @Description Get a list of triggered notifications related to your machines. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notifications // @Produce json // @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." // @Param limit query string false "The maximum number of results that may be returned." -// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order. TODO Enums" -// @Param search query string false "Search for TODO" +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order." Enums(machine_name, threshold, event_type, timestamp) +// @Param search query string false "Search for Machine" // @Success 200 {object} types.InternalGetUserNotificationMachinesResponse // @Failure 400 {object} types.ApiErrorResponse // @Router /users/me/notifications/machines [get] @@ -2031,12 +2036,13 @@ func (h *HandlerService) PublicGetUserNotificationMachines(w http.ResponseWriter // PublicGetUserNotificationClients godoc // // @Description Get a list of triggered notifications related to your clients. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notifications // @Produce json // @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." // @Param limit query string false "The maximum number of results that may be returned." -// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order. TODO Enums" -// @Param search query string false "Search for TODO" +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order." Enums(client_name, timestamp) +// @Param search query string false "Search for Client" // @Success 200 {object} types.InternalGetUserNotificationClientsResponse // @Failure 400 {object} types.ApiErrorResponse // @Router /users/me/notifications/clients [get] @@ -2069,11 +2075,12 @@ func (h *HandlerService) PublicGetUserNotificationClients(w http.ResponseWriter, // PublicGetUserNotificationRocketPool godoc // // @Description Get a list of triggered notifications related to Rocket Pool. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notifications // @Produce json // @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." // @Param limit query string false "The maximum number of results that may be returned." -// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order. TODO Enums" +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order." Enums(timestamp, event_type, node_address) // @Param search query string false "Search for TODO" // @Success 200 {object} types.InternalGetUserNotificationRocketPoolResponse // @Failure 400 {object} types.ApiErrorResponse @@ -2107,11 +2114,12 @@ func (h *HandlerService) PublicGetUserNotificationRocketPool(w http.ResponseWrit // PublicGetUserNotificationNetworks godoc // // @Description Get a list of triggered notifications related to networks. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notifications // @Produce json // @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." // @Param limit query string false "The maximum number of results that may be returned." -// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order. TODO Enums" +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order." Enums(timestamp, event_type) // @Param search query string false "Search for TODO" // @Success 200 {object} types.InternalGetUserNotificationNetworksResponse // @Failure 400 {object} types.ApiErrorResponse @@ -2145,6 +2153,7 @@ func (h *HandlerService) PublicGetUserNotificationNetworks(w http.ResponseWriter // PublicGetUserNotificationPairedDevices godoc // // @Description Get notification settings for the authenticated user. Excludes dashboard notification settings. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notification Settings // @Produce json // @Success 200 {object} types.InternalGetUserNotificationSettingsResponse @@ -2169,6 +2178,7 @@ func (h *HandlerService) PublicGetUserNotificationSettings(w http.ResponseWriter // PublicPutUserNotificationSettingsGeneral godoc // // @Description Update general notification settings for the authenticated user. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notification Settings // @Accept json // @Produce json @@ -2212,6 +2222,7 @@ func (h *HandlerService) PublicPutUserNotificationSettingsGeneral(w http.Respons // PublicPutUserNotificationSettingsNetworks godoc // // @Description Update network notification settings for the authenticated user. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notification Settings // @Accept json // @Produce json @@ -2256,6 +2267,7 @@ func (h *HandlerService) PublicPutUserNotificationSettingsNetworks(w http.Respon // PublicPutUserNotificationSettingsPairedDevices godoc // // @Description Update paired device notification settings for the authenticated user. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notification Settings // @Accept json // @Produce json @@ -2302,6 +2314,7 @@ func (h *HandlerService) PublicPutUserNotificationSettingsPairedDevices(w http.R // PublicDeleteUserNotificationSettingsPairedDevices godoc // // @Description Delete paired device notification settings for the authenticated user. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notification Settings // @Produce json // @Param paired_device_id path string true "The paired device ID." @@ -2327,12 +2340,13 @@ func (h *HandlerService) PublicDeleteUserNotificationSettingsPairedDevices(w htt // PublicGetUserNotificationSettingsDashboards godoc // // @Description Get a list of notification settings for the dashboards of the authenticated user. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notification Settings // @Produce json // @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." // @Param limit query string false "The maximum number of results that may be returned." -// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order. TODO Enums" -// @Param search query string false "Search for TODO" +// @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order." Enums (dashboard_id, group_name) +// @Param search query string false "Search for Dashboard, Group" // @Success 200 {object} types.InternalGetUserNotificationSettingsDashboardsResponse // @Failure 400 {object} types.ApiErrorResponse // @Router /users/me/notifications/settings/dashboards [get] @@ -2365,6 +2379,7 @@ func (h *HandlerService) PublicGetUserNotificationSettingsDashboards(w http.Resp // PublicPutUserNotificationSettingsValidatorDashboard godoc // // @Description Update the notification settings for a specific group of a validator dashboard for the authenticated user. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notification Settings // @Accept json // @Produce json @@ -2403,6 +2418,7 @@ func (h *HandlerService) PublicPutUserNotificationSettingsValidatorDashboard(w h // PublicPutUserNotificationSettingsAccountDashboard godoc // // @Description Update the notification settings for a specific group of an account dashboard for the authenticated user. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notification Settings // @Accept json // @Produce json @@ -2476,6 +2492,7 @@ func (h *HandlerService) PublicPutUserNotificationSettingsAccountDashboard(w htt // PublicPostUserNotificationsTestEmail godoc // // @Description Send a test email notification to the authenticated user. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notification Settings // @Produce json // @Success 204 @@ -2488,6 +2505,7 @@ func (h *HandlerService) PublicPostUserNotificationsTestEmail(w http.ResponseWri // PublicPostUserNotificationsTestPush godoc // // @Description Send a test push notification to the authenticated user. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notification Settings // @Produce json // @Success 204 @@ -2500,6 +2518,7 @@ func (h *HandlerService) PublicPostUserNotificationsTestPush(w http.ResponseWrit // PublicPostUserNotificationsTestWebhook godoc // // @Description Send a test webhook notification from the authenticated user to the given URL. +// @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notification Settings // @Accept json // @Produce json From d41db98c95ee9eab64b9a6616fa5da71ff5a6516 Mon Sep 17 00:00:00 2001 From: LUCCA DUKIC <109136188+LuccaBitfly@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:20:21 +0200 Subject: [PATCH 4/5] (BEDS-464) typo --- backend/pkg/api/handlers/public.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pkg/api/handlers/public.go b/backend/pkg/api/handlers/public.go index 91f047b0b..a31ad6bb2 100644 --- a/backend/pkg/api/handlers/public.go +++ b/backend/pkg/api/handlers/public.go @@ -1893,7 +1893,7 @@ func (h *HandlerService) PublicGetUserNotifications(w http.ResponseWriter, r *ht // @Security ApiKeyInHeader || ApiKeyInQuery // @Tags Notifications // @Produce json -// @Param network query string false "If set, results will be filtered to only include networks given. Provide a comma seperated list." +// @Param network query string false "If set, results will be filtered to only include networks given. Provide a comma separated list." // @Param cursor query string false "Return data for the given cursor value. Pass the `paging.next_cursor`` value of the previous response to navigate to forward, or pass the `paging.prev_cursor`` value of the previous response to navigate to backward." // @Param limit query string false "The maximum number of results that may be returned." // @Param sort query string false "The field you want to sort by. Append with `:desc` for descending order." " Enums(chain_id, timestamp, dashboard_id) From 3e3a76044af300f10d5d3f888f7e344b79606ec7 Mon Sep 17 00:00:00 2001 From: LUCCA DUKIC <109136188+LuccaBitfly@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:26:54 +0200 Subject: [PATCH 5/5] (BEDS-464) auth check for dashboard endpoints --- backend/pkg/api/router.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/pkg/api/router.go b/backend/pkg/api/router.go index f6ba60902..3238257bd 100644 --- a/backend/pkg/api/router.go +++ b/backend/pkg/api/router.go @@ -319,8 +319,6 @@ func addNotificationRoutes(hs *handlers.HandlerService, publicRouter, internalRo endpoints := []endpoint{ {http.MethodGet, "", hs.PublicGetUserNotifications, hs.InternalGetUserNotifications}, {http.MethodGet, "/dashboards", hs.PublicGetUserNotificationDashboards, hs.InternalGetUserNotificationDashboards}, - {http.MethodGet, "/validator-dashboards/{dashboard_id}/groups/{group_id}/epochs/{epoch}", hs.PublicGetUserNotificationsValidatorDashboard, hs.InternalGetUserNotificationsValidatorDashboard}, - {http.MethodGet, "/account-dashboards/{dashboard_id}/groups/{group_id}/epochs/{epoch}", hs.PublicGetUserNotificationsAccountDashboard, hs.InternalGetUserNotificationsAccountDashboard}, {http.MethodGet, "/machines", hs.PublicGetUserNotificationMachines, hs.InternalGetUserNotificationMachines}, {http.MethodGet, "/clients", hs.PublicGetUserNotificationClients, hs.InternalGetUserNotificationClients}, {http.MethodGet, "/rocket-pool", hs.PublicGetUserNotificationRocketPool, hs.InternalGetUserNotificationRocketPool}, @@ -339,11 +337,14 @@ func addNotificationRoutes(hs *handlers.HandlerService, publicRouter, internalRo publicDashboardNotificationSettingsRouter := publicNotificationRouter.NewRoute().Subrouter() internalDashboardNotificationSettingsRouter := internalNotificationRouter.NewRoute().Subrouter() + // TODO add adb auth middleware to account dashboard endpoints once they are implemented if !debug { publicDashboardNotificationSettingsRouter.Use(hs.VDBAuthMiddleware) internalDashboardNotificationSettingsRouter.Use(hs.VDBAuthMiddleware) } dashboardSettingsEndpoints := []endpoint{ + {http.MethodGet, "/validator-dashboards/{dashboard_id}/groups/{group_id}/epochs/{epoch}", hs.PublicGetUserNotificationsValidatorDashboard, hs.InternalGetUserNotificationsValidatorDashboard}, + {http.MethodGet, "/account-dashboards/{dashboard_id}/groups/{group_id}/epochs/{epoch}", hs.PublicGetUserNotificationsAccountDashboard, hs.InternalGetUserNotificationsAccountDashboard}, {http.MethodPut, "/settings/validator-dashboards/{dashboard_id}/groups/{group_id}", hs.PublicPutUserNotificationSettingsValidatorDashboard, hs.InternalPutUserNotificationSettingsValidatorDashboard}, {http.MethodPut, "/settings/account-dashboards/{dashboard_id}/groups/{group_id}", hs.PublicPutUserNotificationSettingsAccountDashboard, hs.InternalPutUserNotificationSettingsAccountDashboard}, }