Skip to content

Commit

Permalink
[TM-1384] Include direct framework associations on users/me response.
Browse files Browse the repository at this point in the history
  • Loading branch information
roguenet committed Oct 31, 2024
1 parent 4b68376 commit 9bb1a4d
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 22 deletions.
7 changes: 5 additions & 2 deletions apps/user-service/src/users/users.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,16 @@ export class UsersController {
@Request() { authenticatedUserId }
): Promise<JsonApiDocument> {
const userId = pathId === 'me' ? authenticatedUserId : parseInt(pathId);
const user = await User.findOne({ include: ['roles', 'organisation'], where: { id: userId }, });
const user = await User.findOne({
include: ['roles', 'organisation', 'frameworks'],
where: { id: userId },
});
if (user == null) throw new NotFoundException();

await this.policyService.authorize('read', user);

const document = buildJsonApi();
const userResource = document.addData(user.uuid, new UserDto(user, await user.frameworks()));
const userResource = document.addData(user.uuid, new UserDto(user, await user.myFrameworks()));

const org = await user.primaryOrganisation();
if (org != null) {
Expand Down
20 changes: 20 additions & 0 deletions libs/database/src/lib/entities/framework-user.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { AutoIncrement, Column, ForeignKey, Model, PrimaryKey, Table } from 'sequelize-typescript';
import { BIGINT } from 'sequelize';
import { Framework } from './framework.entity';
import { User } from './user.entity';

@Table({ tableName: 'framework_user', underscored: true })
export class FrameworkUser extends Model {
@PrimaryKey
@AutoIncrement
@Column(BIGINT.UNSIGNED)
override id: number;

@ForeignKey(() => Framework)
@Column(BIGINT.UNSIGNED)
frameworkId: number;

@ForeignKey(() => User)
@Column(BIGINT.UNSIGNED)
userId: number;
}
1 change: 1 addition & 0 deletions libs/database/src/lib/entities/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './delayed-job.entity';
export * from './framework.entity';
export * from './framework-user.entity';
export * from './model-has-role.entity'
export * from './organisation.entity';
export * from './organisation-user.entity';
Expand Down
63 changes: 43 additions & 20 deletions libs/database/src/lib/entities/user.entity.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { uniq } from 'lodash';
import {
AllowNull,
AutoIncrement,
BelongsTo,
BelongsToMany,
Column, Default,
Column,
Default,
ForeignKey,
Index,
Model,
PrimaryKey,
Table,
Unique
Unique,
} from 'sequelize-typescript';
import { BIGINT, BOOLEAN, col, DATE, fn, Op, STRING, UUID } from 'sequelize';
import { Role } from './role.entity';
Expand All @@ -20,6 +22,7 @@ import { Project } from './project.entity';
import { ProjectUser } from './project-user.entity';
import { Organisation } from './organisation.entity';
import { OrganisationUser } from './organisation-user.entity';
import { FrameworkUser } from './framework-user.entity';

@Table({ tableName: 'users', underscored: true, paranoid: true })
export class User extends Model {
Expand Down Expand Up @@ -257,39 +260,59 @@ export class User extends Model {
: this._primaryOrganisation;
}

private _frameworks?: Framework[];
async frameworks(): Promise<Framework[]> {
if (this._frameworks == null) {
@BelongsToMany(() => Framework, () => FrameworkUser)
frameworks: Framework[];

async loadFrameworks() {
if (this.frameworks == null) {
this.frameworks = await (this as User).$get('frameworks');
}
return this.frameworks;
}

private _myFrameworks?: Framework[];
async myFrameworks(): Promise<Framework[]> {
if (this._myFrameworks == null) {
await this.loadRoles();
const isAdmin =
this.roles.find(({ name }) => name.startsWith('admin-')) != null;

let frameworkSlugs: string[];
await this.loadFrameworks();

let frameworkSlugs: string[] = this.frameworks.map(({ slug }) => slug);
if (isAdmin) {
// Admins have access to all frameworks their permissions say they do
const permissions = await Permission.getUserPermissionNames(this.id);
const prefix = 'framework-';
frameworkSlugs = permissions
.filter((permission) => permission.startsWith(prefix))
.map((permission) => permission.substring(prefix.length));
frameworkSlugs = [
...frameworkSlugs,
...permissions
.filter((permission) => permission.startsWith(prefix))
.map((permission) => permission.substring(prefix.length)),
];
} else {
// Other users have access to the frameworks embodied by their set of projects
frameworkSlugs = (
await (this as User).$get('projects', {
attributes: [
[fn('DISTINCT', col('Project.framework_key')), 'frameworkKey'],
],
raw: true,
})
).map(({ frameworkKey }) => frameworkKey);
frameworkSlugs = [
...frameworkSlugs,
...(
await (this as User).$get('projects', {
attributes: [
[fn('DISTINCT', col('Project.framework_key')), 'frameworkKey'],
],
raw: true,
})
).map(({ frameworkKey }) => frameworkKey),
];
}

if (frameworkSlugs.length == 0) return (this._frameworks = []);
return (this._frameworks = await Framework.findAll({
if (frameworkSlugs.length == 0) return (this._myFrameworks = []);

frameworkSlugs = uniq(frameworkSlugs);
return (this._myFrameworks = await Framework.findAll({
where: { slug: { [Op.in]: frameworkSlugs } },
}));
}

return this._frameworks;
return this._myFrameworks;
}
}
8 changes: 8 additions & 0 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"lodash": "^4.17.21",
"mariadb": "^3.3.2",
"mysql2": "^3.11.2",
"nestjs-request-context": "^3.0.0",
Expand Down Expand Up @@ -47,6 +48,7 @@
"@swc/helpers": "~0.5.11",
"@types/bcryptjs": "^2.4.6",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.13",
"@types/node": "~18.16.9",
"@types/supertest": "^6.0.2",
"@types/validator": "^13.12.2",
Expand Down

0 comments on commit 9bb1a4d

Please sign in to comment.