Skip to content

Commit

Permalink
[#734] Public account filtering by unstructured properties (#735)
Browse files Browse the repository at this point in the history
* add public accounts filtering by unstructured properties

* update changelog

* sort public accounts by last name, first name, use slices.SortFunc instead of sort.Slice
  • Loading branch information
roberlander2 authored Dec 10, 2024
1 parent 9e89199 commit de67d7b
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 32 deletions.
4 changes: 2 additions & 2 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@
"filename": "driver\\web\\docs\\gen\\gen_types.go",
"hashed_secret": "c9739eab2dfa093cc0e450bf0ea81a43ae67b581",
"is_verified": false,
"line_number": 1876
"line_number": 1879
}
],
"driver\\web\\docs\\resources\\admin\\auth\\login.yaml": [
Expand Down Expand Up @@ -347,5 +347,5 @@
}
]
},
"generated_at": "2024-11-27T23:13:12Z"
"generated_at": "2024-12-06T23:03:11Z"
}
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
### Added
- Public account filtering by unstructured properties [#734](https://github.com/rokwire/core-building-block/issues/734)

## [1.44.1] - 2024-12-03
### Fixed
- Fix Privacy.FieldVisibility validation [#729](https://github.com/rokwire/core-building-block/issues/729)
Expand Down
4 changes: 2 additions & 2 deletions core/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,8 @@ func (s *servicesImpl) SerGetAccounts(limit int, offset int, appID string, orgID
}

func (s *servicesImpl) SerGetPublicAccounts(appID string, orgID string, limit int, offset int, search *string,
firstName *string, lastName *string, username *string, followingID *string, followerID *string, userID string) ([]model.PublicAccount, error) {
return s.app.serGetPublicAccounts(appID, orgID, limit, offset, search, firstName, lastName, username, followingID, followerID, userID)
firstName *string, lastName *string, username *string, followingID *string, followerID *string, unstructuredProperties map[string]string, userID string) ([]model.PublicAccount, error) {
return s.app.serGetPublicAccounts(appID, orgID, limit, offset, search, firstName, lastName, username, followingID, followerID, unstructuredProperties, userID)
}

func (s *servicesImpl) SerAddFollow(follow model.Follow) error {
Expand Down
9 changes: 7 additions & 2 deletions core/app_services.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package core
import (
"core-building-block/core/model"
"core-building-block/driven/storage"
"slices"
"time"

"github.com/google/uuid"
Expand Down Expand Up @@ -155,13 +156,17 @@ func (app *application) serGetAccounts(limit int, offset int, appID string, orgI
}

func (app *application) serGetPublicAccounts(appID string, orgID string, limit int, offset int, search *string, firstName *string,
lastName *string, username *string, followingID *string, followerID *string, userID string) ([]model.PublicAccount, error) {
lastName *string, username *string, followingID *string, followerID *string, unstructuredProperties map[string]string, userID string) ([]model.PublicAccount, error) {
//find the accounts
accounts, err := app.storage.FindPublicAccounts(nil, appID, orgID, &limit, &offset, search, firstName, lastName, username, followingID, followerID, userID)
accounts, err := app.storage.FindPublicAccounts(nil, appID, orgID, &limit, &offset, search, firstName, lastName, username, followingID, followerID, unstructuredProperties, userID)
if err != nil {
return nil, errors.WrapErrorAction(logutils.ActionFind, model.TypeAccount, nil, err)
}

slices.SortFunc(accounts, func(i, j model.PublicAccount) int {
return i.OrderForSort(&j)
})

return accounts, nil
}

Expand Down
6 changes: 3 additions & 3 deletions core/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type Services interface {
authTypeIdentifier *string, anonymous *bool, hasPermissions *bool, permissions []string, roleIDs []string, groupIDs []string) ([]model.Account, error)

SerGetPublicAccounts(appID string, orgID string, limit int, offset int, search *string, firstName *string, lastName *string,
username *string, followingID *string, followerID *string, userID string) ([]model.PublicAccount, error)
username *string, followingID *string, followerID *string, unstructuredProperties map[string]string, userID string) ([]model.PublicAccount, error)

SerAddFollow(follow model.Follow) error
SerDeleteFollow(appID string, orgID string, followingID string, followerID string) error
Expand Down Expand Up @@ -173,8 +173,8 @@ type Storage interface {
FindAccountByIDV2(context storage.TransactionContext, cOrgID string, cAppID string, id string) (*model.Account, error)
FindAccounts(context storage.TransactionContext, limit *int, offset *int, appID string, orgID string, accountID *string, firstName *string, lastName *string, authType *string,
authTypeIdentifier *string, anonymous *bool, hasPermissions *bool, permissions []string, roleIDs []string, groupIDs []string) ([]model.Account, error)
FindPublicAccounts(context storage.TransactionContext, appID string, orgID string, limit *int, offset *int,
search *string, firstName *string, lastName *string, username *string, followingID *string, followerID *string, userID string) ([]model.PublicAccount, error)
FindPublicAccounts(context storage.TransactionContext, appID string, orgID string, limit *int, offset *int, search *string, firstName *string, lastName *string,
username *string, followingID *string, followerID *string, unstructuredProperties map[string]string, userID string) ([]model.PublicAccount, error)
FindAccountsByParams(searchParams map[string]interface{}, appID string, orgID string, limit int, offset int, allAccess bool, approvedKeys []string) ([]map[string]interface{}, error)
CountAccountsByParams(searchParams map[string]interface{}, appID string, orgID string) (int64, error)
FindAccountsByAccountID(context storage.TransactionContext, appID string, orgID string, accountIDs []string) ([]model.Account, error)
Expand Down
18 changes: 9 additions & 9 deletions core/mocks/Storage.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 51 additions & 3 deletions core/model/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"core-building-block/utils"
"fmt"
"reflect"
"sort"
"slices"
"time"

"github.com/rokwire/logging-library-go/v2/errors"
Expand Down Expand Up @@ -275,8 +275,11 @@ func (a Account) GetAccountAuthType(authTypeID string, identifier string) *Accou

// SortAccountAuthTypes sorts account auth types by matching the given uid
func (a Account) SortAccountAuthTypes(uid string) {
sort.Slice(a.AuthTypes, func(i, _ int) bool {
return a.AuthTypes[i].Identifier == uid
slices.SortFunc(a.AuthTypes, func(i, _ AccountAuthType) int {
if i.Identifier == uid {
return -1
}
return 0
})
}

Expand Down Expand Up @@ -934,6 +937,51 @@ type PublicAccount struct {
Identifiers []PublicAccountIdentifier `json:"identifiers"`
}

// OrderForSort provides an ordering for sorting PublicAccounts using slices.SortFunc
// It returns -1 when a < other, 1 when a > other, and 0 when a == other or a and other are both nil
func (a *PublicAccount) OrderForSort(other *PublicAccount) int {
if a == nil {
if other == nil {
return 0
}
return 1
}
if other == nil {
return -1
}

lastName := ""
if a.Profile.LastName != nil {
lastName = *a.Profile.LastName
}
lastName2 := ""
if other.Profile.LastName != nil {
lastName2 = *other.Profile.LastName
}

if lastName < lastName2 {
return -1
} else if lastName > lastName2 {
return 1
}

firstName := ""
if a.Profile.FirstName != nil {
firstName = *a.Profile.FirstName
}
firstName2 := ""
if other.Profile.FirstName != nil {
firstName2 = *other.Profile.FirstName
}

if firstName < firstName2 {
return -1
} else if firstName > firstName2 {
return 1
}
return 0
}

// PublicAccountIdentifier represents an account identifier made publicly-known by a user
type PublicAccountIdentifier struct {
Code string `json:"code"`
Expand Down
8 changes: 6 additions & 2 deletions driven/storage/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -1469,8 +1469,8 @@ func (sa *Adapter) FindAccounts(context TransactionContext, limit *int, offset *
}

// FindPublicAccounts finds accounts and returns name and username
func (sa *Adapter) FindPublicAccounts(context TransactionContext, appID string, orgID string, limit *int, offset *int,
search *string, firstName *string, lastName *string, username *string, followingID *string, followerID *string, userID string) ([]model.PublicAccount, error) {
func (sa *Adapter) FindPublicAccounts(context TransactionContext, appID string, orgID string, limit *int, offset *int, search *string, firstName *string, lastName *string,
username *string, followingID *string, followerID *string, unstructuredProperties map[string]string, userID string) ([]model.PublicAccount, error) {
appOrg, err := sa.FindApplicationOrganization(appID, orgID)
if err != nil {
return nil, errors.WrapErrorAction(logutils.ActionFind, model.TypeApplicationOrganization, nil, err)
Expand Down Expand Up @@ -1550,6 +1550,10 @@ func (sa *Adapter) FindPublicAccounts(context TransactionContext, appID string,
pipeline = append(pipeline, bson.M{"$match": bson.M{"followings.follower_id": *followerID}})
}

for k, v := range unstructuredProperties {
pipeline = append(pipeline, bson.M{"$match": bson.M{"profile.unstructured_properties." + k: v}})
}

// adds boolean value whether API calling user is following account
pipeline = append(pipeline, bson.M{"$addFields": bson.M{"is_following": bson.M{"$in": bson.A{userID, "$followings.follower_id"}}}})

Expand Down
27 changes: 18 additions & 9 deletions driver/web/apis_services.go
Original file line number Diff line number Diff line change
Expand Up @@ -910,18 +910,19 @@ func (h ServicesApisHandler) getAccounts(l *logs.Log, r *http.Request, claims *t

func (h ServicesApisHandler) getPublicAccounts(l *logs.Log, r *http.Request, claims *tokenauth.Claims) logs.HTTPResponse {
var err error
query := r.URL.Query()

//limit and offset
limit := 20
limitArg := r.URL.Query().Get("limit")
limitArg := query.Get("limit")
if limitArg != "" {
limit, err = strconv.Atoi(limitArg)
if err != nil {
return l.HTTPResponseErrorAction(logutils.ActionParse, logutils.TypeArg, logutils.StringArgs("limit"), err, http.StatusBadRequest, false)
}
}
offset := 0
offsetArg := r.URL.Query().Get("offset")
offsetArg := query.Get("offset")
if offsetArg != "" {
offset, err = strconv.Atoi(offsetArg)
if err != nil {
Expand All @@ -931,47 +932,55 @@ func (h ServicesApisHandler) getPublicAccounts(l *logs.Log, r *http.Request, cla

//search
var search *string
searchParam := r.URL.Query().Get("search")
searchParam := query.Get("search")
if len(searchParam) > 0 {
search = &searchParam
}

//username
var username *string
usernameParam := r.URL.Query().Get("username")
usernameParam := query.Get("username")
if len(usernameParam) > 0 {
username = &usernameParam
}

//first name
var firstName *string
firstNameParam := r.URL.Query().Get("firstname")
firstNameParam := query.Get("firstname")
if len(firstNameParam) > 0 {
firstName = &firstNameParam
}
//last name
var lastName *string
lastNameParam := r.URL.Query().Get("lastname")
lastNameParam := query.Get("lastname")
if len(lastNameParam) > 0 {
lastName = &lastNameParam
}

//following id
var followingID *string
followingIDParam := r.URL.Query().Get("following-id")
followingIDParam := query.Get("following-id")
if len(followingIDParam) > 0 {
followingID = &followingIDParam
}

//following id
var followerID *string
followerIDParam := r.URL.Query().Get("follower-id")
followerIDParam := query.Get("follower-id")
if len(followerIDParam) > 0 {
followerID = &followerIDParam
}

unstructuredProperties := make(map[string]string)
explicitQueryParams := []string{"limit", "offset", "search", "username", "firstname", "lastname", "following-id", "follower-id"}
for k := range query {
if !utils.Contains(explicitQueryParams, k) {
unstructuredProperties[k] = query.Get(k)
}
}

accounts, err := h.coreAPIs.Services.SerGetPublicAccounts(claims.AppID, claims.OrgID, limit, offset, search,
firstName, lastName, username, followingID, followerID, claims.Subject)
firstName, lastName, username, followingID, followerID, unstructuredProperties, claims.Subject)
if err != nil {
return l.HTTPResponseErrorAction(logutils.ActionGet, model.TypeAccount, nil, err, http.StatusInternalServerError, true)
}
Expand Down
8 changes: 8 additions & 0 deletions driver/web/docs/gen/def.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,14 @@ paths:
explode: false
schema:
type: string
- name: unstructured_properties
in: query
description: Map containing filters by unstructured properties in profile
required: false
style: form
explode: true
schema:
type: object
responses:
'200':
description: Success
Expand Down
3 changes: 3 additions & 0 deletions driver/web/docs/gen/gen_types.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions driver/web/docs/resources/services/accounts-public.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ get:
explode: false
schema:
type: string
- name: unstructured_properties
in: query
description: Map containing filters by unstructured properties in profile
required: false
style: form
explode: true
schema:
type: object
responses:
200:
description: Success
Expand Down

0 comments on commit de67d7b

Please sign in to comment.