Chore optimize iam queries (#1198)

* chore: 🤖 add scope column to role principals and group members

* chore: 🤖 add recursion and filtering to user and group mocks

* feat: 🎸 expose groups and roles on projects

* feat: 🎸 enable cross-scope assignment of group members

* chore: 🤖 query group members in a single call via filtering

* chore: 🤖 query role prinicipals in two calls via filtering

* chore: 🤖 use const instead of let
This commit is contained in:
Randall Morey authored and GitHub committed 2022-07-19 12:11:16 -07:00
1 parent bc76d49729
commit ca1b2ce278
3 files changed
+55 -42

No files matched your search

@@ -1,7 +1,7 @@
import Route from '@ember/routing/route';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import { hash, all } from 'rsvp';
import { hash } from 'rsvp';
import { loading } from 'ember-loading';
import { confirm } from 'core/decorators/confirm';
import { notifySuccess, notifyError } from 'core/decorators/notify';
@@ -11,6 +11,7 @@ export default class ScopesScopeGroupsGroupMembersRoute extends Route {
@service intl;
@service notify;
@service resourceFilterStore;
// =methods
@@ -19,15 +20,16 @@ export default class ScopesScopeGroupsGroupMembersRoute extends Route {
* @return {Promise{group: GroupModel, members: Promise{[UserModel]}}}
*/
model() {
const { id: scopeID } = this.modelFor('scopes.scope');
const group = this.modelFor('scopes.scope.groups.group');
return hash({
group,
members: all(
group.member_ids.map((id) =>
this.store.findRecord('user', id, { adapterOptions: { scopeID } })
)
),
members: group.member_ids?.length
? this.resourceFilterStore.queryBy(
'user',
{ id: group.member_ids },
{ scope_id: 'global', recursive: true }
)
: [],
});
}
@@ -1,5 +1,4 @@
import Route from '@ember/routing/route';
import { all, hash } from 'rsvp';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import { loading } from 'ember-loading';
@@ -10,6 +9,7 @@ export default class ScopesScopeRolesRolePrincipalsRoute extends Route {
// =services
@service intl;
@service notify;
@service resourceFilterStore;
// =methods
@@ -18,31 +18,43 @@ export default class ScopesScopeRolesRolePrincipalsRoute extends Route {
* @param {object} params
* @return {Promise{RoleModel}}
*/
model() {
const adapterOptions = { scopeID: this.modelFor('scopes.scope').id };
async model() {
const role = this.modelFor('scopes.scope.roles.role');
const users = role.principals
// Gather user and group IDs as separate arrays, since these
// will be queried in separate API queries.
const userIDs = role.principals
.filterBy('type', 'user')
.map(({ principal_id }) =>
this.store
.findRecord('user', principal_id, { adapterOptions })
.then((model) => ({
type: 'user',
model,
}))
);
const groups = role.principals
.map(({ principal_id }) => principal_id);
const groupIDs = role.principals
.filterBy('type', 'group')
.map(({ principal_id }) =>
this.store.findRecord('group', principal_id).then((model) => ({
type: 'group',
model,
}))
);
return hash({
.map(({ principal_id }) => principal_id);
// Query for users.
const users = userIDs?.length
? (
await this.resourceFilterStore.queryBy(
'user',
{ id: userIDs },
{ scope_id: 'global', recursive: true }
)
).map((model) => model)
: [];
// Query for groups.
const groups = groupIDs?.length
? (
await this.resourceFilterStore.queryBy(
'group',
{ id: groupIDs },
{ scope_id: 'global', recursive: true }
)
).map((model) => model)
: [];
return {
role,
principals: all(users.concat(groups)),
});
principals: users.concat(groups),
};
}
// =actions
@@ -46,36 +46,38 @@
{{#each @model.principals as |principal|}}
<body.row as |row|>
<row.headerCell>
{{principal.model.displayName}}
{{#if principal.model.accountName}}
<br /><Rose::Badge
>{{principal.model.accountName}}</Rose::Badge>
{{principal.displayName}}
{{#if principal.accountName}}
<br /><Rose::Badge>{{principal.accountName}}</Rose::Badge>
{{/if}}
</row.headerCell>
<row.cell>
<Copyable
@text={{principal.model.id}}
@text={{principal.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{principal.model.id}}</code>
<code>{{principal.id}}</code>
</Copyable>
</row.cell>
<row.cell>
<Rose::Badge
@icon={{if
(eq principal.type 'group')
(eq principal.constructor.modelName 'group')
'flight-icons/svg/folder-users-16'
'flight-icons/svg/user-16'
}}
>
{{t
(concat 'resources.role.principal.types.' principal.type)
(concat
'resources.role.principal.types.'
principal.constructor.modelName
)
}}
</Rose::Badge>
</row.cell>
<row.cell>
<ScopeBadge @scope={{principal.model.scopeModel}} />
<ScopeBadge @scope={{principal.scopeModel}} />
</row.cell>
<row.cell>
{{#if (can 'removePrincipals role' @model.role)}}
@@ -89,10 +91,7 @@
>
<dropdown.button
@style='danger'
{{on
'click'
(route-action 'removePrincipal' principal.model)
}}
{{on 'click' (route-action 'removePrincipal' principal)}}
>
{{t 'actions.remove'}}
</dropdown.button>