Skip to content

Commit

Permalink
(BEDS-480) add vdb mobile widget endpoint (#869)
Browse files Browse the repository at this point in the history
  • Loading branch information
LuccaBitfly authored Sep 19, 2024
1 parent 5460d20 commit b261d85
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 39 deletions.
8 changes: 5 additions & 3 deletions backend/pkg/api/data_access/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -637,9 +637,7 @@ func (d *DummyService) GetRocketPoolOverview(ctx context.Context) (*t.RocketPool
}

func (d *DummyService) GetApiWeights(ctx context.Context) ([]t.ApiWeightItem, error) {
r := []t.ApiWeightItem{}
err := commonFakeData(&r)
return r, err
return getDummyData[[]t.ApiWeightItem]()
}

func (d *DummyService) GetHealthz(ctx context.Context, showAll bool) t.HealthzData {
Expand All @@ -654,3 +652,7 @@ func (d *DummyService) GetLatestBundleForNativeVersion(ctx context.Context, nati
func (d *DummyService) IncrementBundleDeliveryCount(ctx context.Context, bundleVerison uint64) error {
return nil
}

func (d *DummyService) GetValidatorDashboardMobileWidget(ctx context.Context, dashboardId t.VDBIdPrimary) (*t.MobileWidgetData, error) {
return getDummyStruct[t.MobileWidgetData]()
}
2 changes: 2 additions & 0 deletions backend/pkg/api/data_access/vdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,6 @@ type ValidatorDashboardRepository interface {
GetValidatorDashboardTotalRocketPool(ctx context.Context, dashboardId t.VDBId, search string) (*t.VDBRocketPoolTableRow, error)
GetValidatorDashboardNodeRocketPool(ctx context.Context, dashboardId t.VDBId, node string) (*t.VDBNodeRocketPoolData, error)
GetValidatorDashboardRocketPoolMinipools(ctx context.Context, dashboardId t.VDBId, node, cursor string, colSort t.Sort[enums.VDBRocketPoolMinipoolsColumn], search string, limit uint64) ([]t.VDBRocketPoolMinipoolsTableRow, *t.Paging, error)

GetValidatorDashboardMobileWidget(ctx context.Context, dashboardId t.VDBIdPrimary) (*t.MobileWidgetData, error)
}
6 changes: 6 additions & 0 deletions backend/pkg/api/data_access/vdb_management.go
Original file line number Diff line number Diff line change
Expand Up @@ -1213,3 +1213,9 @@ func (d *DataAccessService) GetValidatorDashboardPublicIdCount(ctx context.Conte
`, dashboardId)
return count, err
}

func (d *DataAccessService) GetValidatorDashboardMobileWidget(ctx context.Context, dashboardId t.VDBIdPrimary) (*t.MobileWidgetData, error) {
// TODO @Data-Access: Implement this function
// feel free to move this func to other file if needed
return d.dummy.GetValidatorDashboardMobileWidget(ctx, dashboardId)
}
13 changes: 8 additions & 5 deletions backend/pkg/api/handlers/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const (
sortOrderAscending = "asc"
sortOrderDescending = "desc"
defaultSortOrder = sortOrderAscending
defaultDesc = defaultSortOrder == sortOrderDescending
ethereum = "ethereum"
gnosis = "gnosis"
allowEmpty = true
Expand Down Expand Up @@ -544,7 +545,7 @@ func checkEnumIsAllowed[T enums.EnumFactory[T]](v *validationError, enum T, allo
func (v *validationError) parseSortOrder(order string) bool {
switch order {
case "":
return defaultSortOrder == sortOrderDescending
return defaultDesc
case sortOrderAscending:
return false
case sortOrderDescending:
Expand All @@ -558,19 +559,21 @@ func (v *validationError) parseSortOrder(order string) bool {
func checkSort[T enums.EnumFactory[T]](v *validationError, sortString string) *types.Sort[T] {
var c T
if sortString == "" {
return &types.Sort[T]{Column: c, Desc: false}
return &types.Sort[T]{Column: c, Desc: defaultDesc}
}
sortSplit := strings.Split(sortString, ":")
if len(sortSplit) > 2 {
v.add("sort", fmt.Sprintf("given value '%s' for parameter 'sort' is not valid, expected format is '<column_name>[:(asc|desc)]'", sortString))
return nil
}
var desc bool
if len(sortSplit) == 1 {
sortSplit = append(sortSplit, ":asc")
desc = defaultDesc
} else {
desc = v.parseSortOrder(sortSplit[1])
}
sortCol := checkEnum[T](v, sortSplit[0], "sort")
order := v.parseSortOrder(sortSplit[1])
return &types.Sort[T]{Column: sortCol, Desc: order}
return &types.Sort[T]{Column: sortCol, Desc: desc}
}

func (v *validationError) checkProtocolModes(protocolModes string) types.VDBProtocolModes {
Expand Down
33 changes: 33 additions & 0 deletions backend/pkg/api/handlers/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,39 @@ func (h *HandlerService) InternalGetValidatorDashboardRocketPoolMinipools(w http
h.PublicGetValidatorDashboardRocketPoolMinipools(w, r)
}

// even though this endpoint is internal only, it should still not be broken since it is used by the mobile app
func (h *HandlerService) InternalGetValidatorDashboardMobileWidget(w http.ResponseWriter, r *http.Request) {
var v validationError
dashboardId := v.checkPrimaryDashboardId(mux.Vars(r)["dashboard_id"])
if v.hasErrors() {
handleErr(w, r, v)
return
}
userId, err := GetUserIdByContext(r)
if err != nil {
handleErr(w, r, err)
return
}
userInfo, err := h.dai.GetUserInfo(r.Context(), userId)
if err != nil {
handleErr(w, r, err)
return
}
if userInfo.UserGroup != types.UserGroupAdmin && !userInfo.PremiumPerks.MobileAppWidget {
returnForbidden(w, r, errors.New("user does not have access to mobile app widget"))
return
}
data, err := h.dai.GetValidatorDashboardMobileWidget(r.Context(), dashboardId)
if err != nil {
handleErr(w, r, err)
return
}
response := types.InternalGetValidatorDashboardMobileWidgetResponse{
Data: *data,
}
returnOk(w, r, response)
}

// --------------------------------------
// Mobile

Expand Down
10 changes: 9 additions & 1 deletion backend/pkg/api/handlers/public.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ func (h *HandlerService) PublicGetHealthzLoadbalancer(w http.ResponseWriter, r *
returnOk(w, r, nil)
}

// PublicGetUserDashboards godoc
//
// @Description Get all dashboards of the authenticated user.
// @Security ApiKeyInHeader || ApiKeyInQuery
// @Tags Dashboards
// @Produce json
// @Success 200 {object} types.ApiDataResponse[types.UserDashboardsData]
// @Router /users/me/dashboards [get]
func (h *HandlerService) PublicGetUserDashboards(w http.ResponseWriter, r *http.Request) {
userId, err := GetUserIdByContext(r)
if err != nil {
Expand Down Expand Up @@ -859,7 +867,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."
// @Failure 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
Expand Down
1 change: 1 addition & 0 deletions backend/pkg/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ func addValidatorDashboardRoutes(hs *handlers.HandlerService, publicRouter, inte
{http.MethodGet, "/{dashboard_id}/total-rocket-pool", hs.PublicGetValidatorDashboardTotalRocketPool, hs.InternalGetValidatorDashboardTotalRocketPool},
{http.MethodGet, "/{dashboard_id}/rocket-pool/{node_address}", hs.PublicGetValidatorDashboardNodeRocketPool, hs.InternalGetValidatorDashboardNodeRocketPool},
{http.MethodGet, "/{dashboard_id}/rocket-pool/{node_address}/minipools", hs.PublicGetValidatorDashboardRocketPoolMinipools, hs.InternalGetValidatorDashboardRocketPoolMinipools},
{http.MethodGet, "/{dashboard_id}/mobile-widget", nil, hs.InternalGetValidatorDashboardMobileWidget},
}
addEndpointsToRouters(endpoints, publicDashboardRouter, internalDashboardRouter)
}
Expand Down
8 changes: 8 additions & 0 deletions backend/pkg/api/types/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,11 @@ type IndexBlocks struct {
Index uint64 `json:"index"`
Blocks []uint64 `json:"blocks"`
}

type ValidatorStateCounts struct {
Online uint64 `json:"online"`
Offline uint64 `json:"offline"`
Pending uint64 `json:"pending"`
Exited uint64 `json:"exited"`
Slashed uint64 `json:"slashed"`
}
16 changes: 8 additions & 8 deletions backend/pkg/api/types/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ type AccountDashboard struct {
Name string `json:"name"`
}
type ValidatorDashboard struct {
Id uint64 `json:"id"`
Name string `json:"name"`
Network uint64 `json:"network"`
PublicIds []VDBPublicId `json:"public_ids,omitempty"`
IsArchived bool `json:"is_archived"`
ArchivedReason string `json:"archived_reason,omitempty" tstype:"'user' | 'dashboard_limit' | 'validator_limit' | 'group_limit'"`
ValidatorCount uint64 `json:"validator_count"`
GroupCount uint64 `json:"group_count"`
Id uint64 `json:"id" extensions:"x-order=1"`
Name string `json:"name" extensions:"x-order=2"`
Network uint64 `json:"network" extensions:"x-order=3"`
PublicIds []VDBPublicId `json:"public_ids,omitempty" extensions:"x-order=4"`
IsArchived bool `json:"is_archived" extensions:"x-order=5"`
ArchivedReason string `json:"archived_reason,omitempty" tstype:"'user' | 'dashboard_limit' | 'validator_limit' | 'group_limit'" extensions:"x-order=6"`
ValidatorCount uint64 `json:"validator_count" extensions:"x-order=7"`
GroupCount uint64 `json:"group_count" extensions:"x-order=8"`
}

type UserDashboardsData struct {
Expand Down
15 changes: 15 additions & 0 deletions backend/pkg/api/types/mobile.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
package types

import "github.com/shopspring/decimal"

type MobileBundleData struct {
BundleUrl string `json:"bundle_url,omitempty"`
HasNativeUpdateAvailable bool `json:"has_native_update_available"`
}

type GetMobileLatestBundleResponse ApiDataResponse[MobileBundleData]

type MobileWidgetData struct {
ValidatorStateCounts ValidatorStateCounts `json:"validator_state_counts"`
Last24hIncome decimal.Decimal `json:"last_24h_income" faker:"eth"`
Last7dIncome decimal.Decimal `json:"last_7d_income" faker:"eth"`
Last30dApr float64 `json:"last_30d_apr"`
Last30dEfficiency decimal.Decimal `json:"last_30d_efficiency" faker:"eth"`
NetworkEfficiency float64 `json:"network_efficiency"`
RplPrice decimal.Decimal `json:"rpl_price" faker:"eth"`
RplApr float64 `json:"rpl_apr"`
}

type InternalGetValidatorDashboardMobileWidgetResponse ApiDataResponse[MobileWidgetData]
9 changes: 1 addition & 8 deletions backend/pkg/api/types/validator_dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@ import (

// ------------------------------------------------------------
// Overview
type VDBOverviewValidators struct {
Online uint64 `json:"online"`
Offline uint64 `json:"offline"`
Pending uint64 `json:"pending"`
Exited uint64 `json:"exited"`
Slashed uint64 `json:"slashed"`
}

type VDBOverviewGroup struct {
Id uint64 `json:"id"`
Expand All @@ -30,7 +23,7 @@ type VDBOverviewData struct {
Name string `json:"name,omitempty" extensions:"x-order=1"`
Network uint64 `json:"network"`
Groups []VDBOverviewGroup `json:"groups"`
Validators VDBOverviewValidators `json:"validators"`
Validators ValidatorStateCounts `json:"validators"`
Efficiency PeriodicValues[float64] `json:"efficiency"`
Rewards PeriodicValues[ClElValue[decimal.Decimal]] `json:"rewards"`
Apr PeriodicValues[ClElValue[float64]] `json:"apr"`
Expand Down
7 changes: 7 additions & 0 deletions frontend/types/api/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,10 @@ export interface IndexBlocks {
index: number /* uint64 */;
blocks: number /* uint64 */[];
}
export interface ValidatorStateCounts {
online: number /* uint64 */;
offline: number /* uint64 */;
pending: number /* uint64 */;
exited: number /* uint64 */;
slashed: number /* uint64 */;
}
13 changes: 12 additions & 1 deletion frontend/types/api/mobile.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Code generated by tygo. DO NOT EDIT.
/* eslint-disable */
import type { ApiDataResponse } from './common'
import type { ApiDataResponse, ValidatorStateCounts } from './common'

//////////
// source: mobile.go
Expand All @@ -10,3 +10,14 @@ export interface MobileBundleData {
has_native_update_available: boolean;
}
export type GetMobileLatestBundleResponse = ApiDataResponse<MobileBundleData>;
export interface MobileWidgetData {
validator_state_counts: ValidatorStateCounts;
last_24h_income: string /* decimal.Decimal */;
last_7d_income: string /* decimal.Decimal */;
last_30d_apr: number /* float64 */;
last_30d_efficiency: string /* decimal.Decimal */;
network_efficiency: number /* float64 */;
rpl_price: string /* decimal.Decimal */;
rpl_apr: number /* float64 */;
}
export type InternalGetValidatorDashboardMobileWidgetResponse = ApiDataResponse<MobileWidgetData>;
15 changes: 2 additions & 13 deletions frontend/types/api/validator_dashboard.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
// Code generated by tygo. DO NOT EDIT.
/* eslint-disable */
import type { PeriodicValues, ClElValue, ChartHistorySeconds, ApiDataResponse, StatusCount, ApiPagingResponse, Luck, ChartData, ValidatorHistoryDuties, Address, PubKey, Hash, PercentageDetails } from './common'
import type { ValidatorStateCounts, PeriodicValues, ClElValue, ChartHistorySeconds, ApiDataResponse, StatusCount, ApiPagingResponse, Luck, ChartData, ValidatorHistoryDuties, Address, PubKey, Hash, PercentageDetails } from './common'

//////////
// source: validator_dashboard.go

/**
* ------------------------------------------------------------
* Overview
*/
export interface VDBOverviewValidators {
online: number /* uint64 */;
offline: number /* uint64 */;
pending: number /* uint64 */;
exited: number /* uint64 */;
slashed: number /* uint64 */;
}
export interface VDBOverviewGroup {
id: number /* uint64 */;
name: string;
Expand All @@ -30,7 +19,7 @@ export interface VDBOverviewData {
name?: string;
network: number /* uint64 */;
groups: VDBOverviewGroup[];
validators: VDBOverviewValidators;
validators: ValidatorStateCounts;
efficiency: PeriodicValues<number /* float64 */>;
rewards: PeriodicValues<ClElValue<string /* decimal.Decimal */>>;
apr: PeriodicValues<ClElValue<number /* float64 */>>;
Expand Down

0 comments on commit b261d85

Please sign in to comment.