Skip to content

Commit c0a4ea3

Browse files
committed
Performance improvement : def permissions
1 parent e889217 commit c0a4ea3

1 file changed

Lines changed: 52 additions & 35 deletions

File tree

obp-api/src/main/scala/code/views/MapperViews.scala

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import code.api.Constant._
66
import code.api.util.APIUtil._
77
import code.api.util.ErrorMessages._
88
import code.api.util.{APIUtil, CallContext}
9+
import code.model.dataAccess.ResourceUser
910
import code.util.Helper.MdcLoggable
1011
import code.views.system.ViewDefinition.create
1112
import code.views.system.{AccountAccess, ViewDefinition, ViewPermission}
@@ -61,18 +62,28 @@ object MapperViews extends Views with MdcLoggable {
6162
// 1. Separate system vs custom view access
6263
val (systemAccessList, customAccessList) = accountAccessList.partition(a => isValidSystemViewId(a.view_id.get))
6364

64-
// 2. Batch-load system views: one query per distinct view_id (small fixed set)
65+
// 2. Batch-load system views: one query per distinct view_id (small fixed set).
66+
// We cache which view_ids exist to avoid repeated DB lookups, but we must call
67+
// findSystemView per access record because the returned ViewDefinition is mutable
68+
// and we need to set bank_id/account_id per access record without cross-contamination.
6569
val distinctSystemViewIds = systemAccessList.map(_.view_id.get).distinct
66-
val systemViewMap: Map[String, ViewDefinition] = distinctSystemViewIds
67-
.flatMap(vid => ViewDefinition.findSystemView(vid).toList.map(v => vid -> v))
68-
.toMap
70+
val validSystemViewIds: Set[String] = distinctSystemViewIds
71+
.filter(vid => ViewDefinition.findSystemView(vid).isDefined)
72+
.toSet
6973

7074
val systemPairs: List[(AccountAccess, ViewDefinition)] = systemAccessList.flatMap { aa =>
71-
systemViewMap.get(aa.view_id.get).map { v =>
72-
// Clone and set bank_id/account_id per access record (system views don't store these)
73-
val viewWithContext = v.bank_id(aa.bank_id.get).account_id(aa.account_id.get)
74-
(aa, viewWithContext)
75-
}
75+
// Skip view_ids we already know don't exist in the DB (avoids pointless queries)
76+
if (validSystemViewIds.contains(aa.view_id.get)) {
77+
// We must call findSystemView per access record (not cache the ViewDefinition) because
78+
// ViewDefinition is a mutable Mapper object. v.bank_id(...) is a setter that mutates v
79+
// in place — caching one instance and reusing it across access records would cause each
80+
// call to overwrite the previous bank_id/account_id, corrupting earlier results.
81+
// System views are stored without bank_id/account_id (they're generic), so we stamp
82+
// each fresh instance with the specific account's context from the access record.
83+
ViewDefinition.findSystemView(aa.view_id.get).toList.map { v =>
84+
(aa, v.bank_id(aa.bank_id.get).account_id(aa.account_id.get))
85+
}
86+
} else Nil
7687
}
7788

7889
// 3. Batch-load custom views: one query using ByList on view_id, then filter in memory
@@ -100,29 +111,35 @@ object MapperViews extends Views with MdcLoggable {
100111
}
101112

102113
private def getViewsCommonPart(accountAccessList: List[AccountAccess]): List[View] = {
103-
//we need to get views from accountAccess
104-
val views: List[ViewDefinition] = accountAccessList.flatMap(getViewFromAccountAccess).filter(
105-
v =>
106-
if (allowPublicViews) {
107-
true // All views
108-
} else {
109-
v.isPrivate == true // Only private views
110-
}
111-
)
112-
views
114+
batchLoadViewsForAccountAccess(accountAccessList).map(_._2)
113115
}
114116

115117
def permissions(account : BankIdAccountId) : List[Permission] = {
116-
117-
val users = AccountAccess.findAll(
118+
// 1. Single query: get all AccountAccess for this account
119+
val allAccountAccess = AccountAccess.findAll(
118120
By(AccountAccess.bank_id, account.bankId.value),
119121
By(AccountAccess.account_id, account.accountId.value)
120-
).flatMap(_.user_fk.obj.toList).distinct
121-
122-
for {
123-
user <- users
124-
} yield {
125-
Permission(user, getViewsForUserAndAccount(user, account))
122+
)
123+
124+
// 2. Batch-load users: one query using ByList instead of N individual FK lookups
125+
val distinctUserFks = allAccountAccess.map(_.user_fk.get).distinct
126+
val usersMap: Map[Long, ResourceUser] = if (distinctUserFks.nonEmpty) {
127+
ResourceUser.findAll(ByList(ResourceUser.id, distinctUserFks))
128+
.map(u => u.id.get -> u).toMap
129+
} else Map.empty
130+
131+
// 3. Batch-load views for all access records
132+
val viewPairs = batchLoadViewsForAccountAccess(allAccountAccess)
133+
134+
// 4. Group views by user FK and build Permission objects
135+
val viewsByUserFk: Map[Long, List[View]] = viewPairs
136+
.groupBy(_._1.user_fk.get)
137+
.map { case (userFk, pairs) => userFk -> pairs.map(_._2: View) }
138+
139+
distinctUserFks.flatMap { userFk =>
140+
usersMap.get(userFk).map { user =>
141+
Permission(user, viewsByUserFk.getOrElse(userFk, Nil))
142+
}
126143
}
127144
}
128145

@@ -601,33 +618,33 @@ object MapperViews extends Views with MdcLoggable {
601618
}
602619

603620
def privateViewsUserCanAccess(user: User): (List[View], List[AccountAccess]) ={
604-
val allAccess = AccountAccess.findAllByUserPrimaryKey(user.userPrimaryKey)
605-
val pairs = batchLoadViewsForAccountAccess(allAccess)
621+
val allAccountAccess = AccountAccess.findAllByUserPrimaryKey(user.userPrimaryKey)
622+
val pairs = batchLoadViewsForAccountAccess(allAccountAccess)
606623
(pairs.map(_._2).distinct, pairs.map(_._1))
607624
}
608625
def privateViewsUserCanAccess(user: User, viewIds: List[ViewId]): (List[View], List[AccountAccess]) ={
609-
val allAccess = AccountAccess.findAll(
626+
val allAccountAccess = AccountAccess.findAll(
610627
By(AccountAccess.user_fk, user.userPrimaryKey.value),
611628
ByList(AccountAccess.view_id, viewIds.map(_.value))
612629
)
613-
val pairs = batchLoadViewsForAccountAccess(allAccess)
630+
val pairs = batchLoadViewsForAccountAccess(allAccountAccess)
614631
(pairs.map(_._2), pairs.map(_._1))
615632
}
616633
def privateViewsUserCanAccessAtBank(user: User, bankId: BankId): (List[View], List[AccountAccess]) ={
617-
val allAccess = AccountAccess.findAll(
634+
val allAccountAccess = AccountAccess.findAll(
618635
By(AccountAccess.user_fk, user.userPrimaryKey.value),
619636
By(AccountAccess.bank_id, bankId.value)
620637
)
621-
val pairs = batchLoadViewsForAccountAccess(allAccess)
638+
val pairs = batchLoadViewsForAccountAccess(allAccountAccess)
622639
(pairs.map(_._2), pairs.map(_._1))
623640
}
624641
def getAccountAccessAtBankThroughView(user: User, bankId: BankId, viewId: ViewId): (List[View], List[AccountAccess]) ={
625-
val allAccess = AccountAccess.findAll(
642+
val allAccountAccess = AccountAccess.findAll(
626643
By(AccountAccess.user_fk, user.userPrimaryKey.value),
627644
By(AccountAccess.bank_id, bankId.value),
628645
By(AccountAccess.view_id, viewId.value)
629646
)
630-
val pairs = batchLoadViewsForAccountAccess(allAccess)
647+
val pairs = batchLoadViewsForAccountAccess(allAccountAccess)
631648
(pairs.map(_._2), pairs.map(_._1))
632649
}
633650

0 commit comments

Comments
 (0)