Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
# Revision history for Decentralized-Belt-System


## 0.2.3.0 -- 2025-01-06

### Query API Enhancements

**Extended Profiles and Promotions APIs with Count and Frequency Endpoints**

#### Profiles API Extensions
- **GET /profiles/count**: Get total count of profiles with optional `profile_type` filter
- **GET /profiles/frequency**: Get profile counts grouped by type (`Practitioner`, `Organization`)

#### Promotions API Extensions
- **GET /promotions/count**: Get total count of promotions with optional filters (`profile`, `belt`, `achieved_by`, `awarded_by`)
- **GET /promotions/frequency**: Get promotion counts grouped by belt type

#### Implementation Details
- Added `getProfileTypeTotals` to `Query/Projected.hs` and `Query/Live.hs`
- Added `getPromotionBeltTotals` to `Query/Projected.hs` and `Query/Live.hs`
- All new endpoints support `liveprojection` query flag for live blockchain vs projected database queries
- Follows the same pattern as existing Belts API Count and Frequency endpoints

## 0.2.2.0 -- 2024-12-21

### API Architecture Split
Expand Down
14 changes: 14 additions & 0 deletions src/exe/query-api/Query/Live.hs
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,17 @@ getBeltTotals = do
let allBelts = Prelude.map rankBelt allRanks
let beltTotals = toOccurList . fromList $ allBelts
return beltTotals

getProfileTypeTotals :: (MonadReader QueryAppContext m, MonadIO m) => m [(ProfileType, Int)]
getProfileTypeTotals = do
allProfiles <- getProfiles Nothing Nothing Nothing
let allTypes = Prelude.map profileType allProfiles
let typeTotals = toOccurList . fromList $ allTypes
return typeTotals

getPromotionBeltTotals :: (MonadReader QueryAppContext m, MonadIO m) => m [(BJJBelt, Int)]
getPromotionBeltTotals = do
allPromotions <- getPromotions Nothing Nothing Nothing
let allBelts = Prelude.map promotionBelt allPromotions
let beltTotals = toOccurList . fromList $ allBelts
return beltTotals
22 changes: 22 additions & 0 deletions src/exe/query-api/Query/Projected.hs
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,25 @@ getBeltTotals = do
let belts = Prelude.map unValue rows
pure (toOccurList . fromList $ belts)
) pool

getProfileTypeTotals :: (MonadIO m, MonadReader QueryAppContext m) => m [(ProfileType, Int)]
getProfileTypeTotals = do
pool <- asks pgPool
liftIO $ runSqlPool (do
rows <- select $ do
pp <- from $ table @ProfileProjection
pure (pp ^. ProfileProjectionProfileType)
let profileTypes = Prelude.map unValue rows
pure (toOccurList . fromList $ profileTypes)
) pool

getPromotionBeltTotals :: (MonadIO m, MonadReader QueryAppContext m) => m [(BJJBelt, Int)]
getPromotionBeltTotals = do
pool <- asks pgPool
liftIO $ runSqlPool (do
rows <- select $ do
pr <- from $ table @PromotionProjection
pure (pr ^. PromotionProjectionPromotionBelt)
let belts = Prelude.map unValue rows
pure (toOccurList . fromList $ belts)
) pool
88 changes: 86 additions & 2 deletions src/exe/query-api/RestAPI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,25 @@ type Profiles =
:> QueryFlag "liveprojection"
:> Get '[JSON] [Profile]
)
:<|>
-- Get profiles count endpoint
( Summary "Get Profiles Count"
:> Description "Get Profiles Count"
:> "profiles"
:> "count"
:> QueryParam' '[Optional] "profile_type" ProfileType
:> QueryFlag "liveprojection"
:> Get '[JSON] Int
)
:<|>
-- Get profiles frequency endpoint
( Summary "Get Profiles Frequency"
:> Description "Get Profiles Frequency by Type"
:> "profiles"
:> QueryFlag "liveprojection"
:> "frequency"
:> Get '[JSON] [(ProfileType, Int)]
)

-- Health handlers moved to WebAPI.Health

Expand Down Expand Up @@ -133,8 +152,23 @@ handleGetProfiles limit offset profileRefs profileType orderBy sortOrder livePro
then L.getProfiles limitOffset profileFilter order
else P.getProfiles limitOffset profileFilter order

handleGetProfilesCount :: Maybe ProfileType -> Bool -> QueryAppMonad Int
handleGetProfilesCount profileType liveProjection =
if liveProjection
then L.getProfilesCount profileType
else P.getProfilesCount profileType

handleGetProfilesFrequency :: Bool -> QueryAppMonad [(ProfileType, Int)]
handleGetProfilesFrequency liveProjection =
if liveProjection then L.getProfileTypeTotals else P.getProfileTypeTotals

profilesServer :: ServerT Profiles QueryAppMonad
profilesServer = handleGetPractitionerProfile :<|> handleGetOrganizationProfile :<|> handleGetProfiles
profilesServer =
handleGetPractitionerProfile
:<|> handleGetOrganizationProfile
:<|> handleGetProfiles
:<|> handleGetProfilesCount
:<|> handleGetProfilesFrequency

------------------------------------------------------------------------------------------------

Expand All @@ -158,6 +192,28 @@ type Promotions =
:> QueryFlag "liveprojection"
:> Get '[JSON] [Promotion]
)
:<|>
-- Get promotions count endpoint
( Summary "Get Promotions Count"
:> Description "Get Promotions Count"
:> "promotions"
:> "count"
:> QueryParams "profile" ProfileRefAC
:> QueryParams "belt" BJJBelt
:> QueryParams "achieved_by" ProfileRefAC
:> QueryParams "awarded_by" ProfileRefAC
:> QueryFlag "liveprojection"
:> Get '[JSON] Int
)
:<|>
-- Get promotions frequency endpoint
( Summary "Get Promotions Frequency"
:> Description "Get Promotions Frequency by Belt"
:> "promotions"
:> QueryFlag "liveprojection"
:> "frequency"
:> Get '[JSON] [(BJJBelt, Int)]
)

handleGetPromotions ::
Maybe Int ->
Expand Down Expand Up @@ -193,8 +249,36 @@ handleGetPromotions limit offset profileRefs beltRefs achievedByRefs awardedByRe
then L.getPromotions limitOffset promotionsFilter order
else P.getPromotions limitOffset promotionsFilter order

handleGetPromotionsCount ::
[ProfileRefAC] ->
[BJJBelt] ->
[ProfileRefAC] ->
[ProfileRefAC] ->
Bool ->
QueryAppMonad Int
handleGetPromotionsCount profileRefs beltRefs achievedByRefs awardedByRefs liveProjection = do
let promotionsFilter =
Just $
C.PromotionFilter
{ C.promotionFilterId = if Prelude.null profileRefs then Nothing else Just profileRefs,
C.promotionFilterBelt = if Prelude.null beltRefs then Nothing else Just beltRefs,
C.promotionFilterAchievedByProfileId = if Prelude.null achievedByRefs then Nothing else Just achievedByRefs,
C.promotionFilterAwardedByProfileId = if Prelude.null awardedByRefs then Nothing else Just awardedByRefs,
C.promotionFilterAchievementDateInterval = (Nothing, Nothing)
}
if liveProjection
then L.getPromotionsCount promotionsFilter
else P.getPromotionsCount promotionsFilter

handleGetPromotionsFrequency :: Bool -> QueryAppMonad [(BJJBelt, Int)]
handleGetPromotionsFrequency liveProjection =
if liveProjection then L.getPromotionBeltTotals else P.getPromotionBeltTotals

promotionsServer :: ServerT Promotions QueryAppMonad
promotionsServer = handleGetPromotions
promotionsServer =
handleGetPromotions
:<|> handleGetPromotionsCount
:<|> handleGetPromotionsFrequency

------------------------------------------------------------------------------------------------

Expand Down