Skip to content

Commit

Permalink
refactor: membership filtering
Browse files Browse the repository at this point in the history
  • Loading branch information
jbrunton committed Aug 23, 2024
1 parent 949167d commit ceecf0d
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 42 deletions.
2 changes: 1 addition & 1 deletion services/api/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ module.exports = {
},
{
from: ['*'],
allow: ['rambda', 'rxjs', 'zod'],
allow: ['rambda', 'remeda', 'rxjs', 'zod'],
},
],
},
Expand Down
36 changes: 36 additions & 0 deletions services/api/package-lock.json

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

1 change: 1 addition & 0 deletions services/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"passport-jwt": "4.0.0",
"rambda": "7.4.0",
"reflect-metadata": "^0.1.13",
"remeda": "2.11.0",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"ts-pattern": "5.3.1",
Expand Down
24 changes: 14 additions & 10 deletions services/api/src/app/auth/permissions/roles.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { AbilityBuilder, createMongoAbility } from '@casl/ability';
import { Membership, MembershipStatus } from '@entities/membership.entity';
import {
Membership,
isActive,
isPendingInvite,
} from '@entities/membership.entity';
import { ContentPolicy, JoinPolicy } from '@entities/room.entity';
import { User } from '@entities/users';
import { Role } from '@usecases/auth.service';
import { pluck } from 'rambda';
import { filter, pipe, map, prop } from 'remeda';

export const defineRolesForUser = (user: User, memberships: Membership[]) => {
const { can, build } = new AbilityBuilder(createMongoAbility);

const activeMemberships = memberships.filter(
(membership) =>
!membership.until && membership.status === MembershipStatus.Joined,
const joinedRoomIds = pipe(
memberships,
filter(isActive),
map(prop('roomId')),
);
const joinedRoomIds = pluck('roomId', activeMemberships);

const pendingInvitations = memberships.filter(
(membership) =>
!membership.until && membership.status === MembershipStatus.PendingInvite,
const pendingInviteRoomIds = pipe(
memberships,
filter(isPendingInvite),
map(prop('roomId')),
);
const pendingInviteRoomIds = pluck('roomId', pendingInvitations);

can(Role.Manage, 'Room', {
ownerId: user.id,
Expand Down
10 changes: 3 additions & 7 deletions services/api/src/app/users/users.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { MembershipStatus } from '@entities/membership.entity';
import { isActive } from '@entities/membership.entity';
import { MembershipsRepository } from '@entities/memberships.repository';
import { Room } from '@entities/room.entity';
import { RoomsRepository } from '@entities/rooms.repository';
import { User, systemUser } from '@entities/users';
import { UsersRepository } from '@entities/users';
import { Injectable } from '@nestjs/common';
import { filter, pluck, uniq } from 'rambda';
import { filter, map, prop, pipe } from 'remeda';

@Injectable()
export class UsersService {
Expand All @@ -24,11 +24,7 @@ export class UsersService {

async getUserRooms(userId: string): Promise<Room[]> {
const memberships = await this.membershipsRepo.getMemberships(userId);
const activeMemberships = filter(
(m) => !m.until && m.status === MembershipStatus.Joined,
memberships,
);
const roomIds = uniq(pluck('roomId', activeMemberships));
const roomIds = pipe(memberships, filter(isActive), map(prop('roomId')));
const rooms = await Promise.all(
roomIds.map((roomId) => this.roomsRepo.getRoom(roomId)),
);
Expand Down
44 changes: 20 additions & 24 deletions services/api/src/domain/entities/membership.entity.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isNil } from 'rambda';
import { allPass, filter, pipe } from 'remeda';

/**
* The status of a user's membership to a room.
Expand Down Expand Up @@ -62,32 +63,27 @@ export type Membership = {
until?: number;
};

export const isCurrent =
(status: MembershipStatus) => (roomId: string) => (membership: Membership) =>
isNil(membership.until) &&
membership.status === status &&
membership.roomId === roomId;
export const isCurrent = (membership: Membership) => isNil(membership.until);

export const isActive = isCurrent(MembershipStatus.Joined);
export const isPendingInvite = isCurrent(MembershipStatus.PendingInvite);
export const withStatus =
(status: MembershipStatus) => (membership: Membership) =>
membership.status === status;

export const forRoom = (roomId: string) => (membership: Membership) =>
membership.roomId === roomId;

export const isActive = allPass([
isCurrent,
withStatus(MembershipStatus.Joined),
]);

export const isPendingInvite = allPass([
isCurrent,
withStatus(MembershipStatus.PendingInvite),
]);

export const isMemberOf = (roomId: string, memberships: Membership[]) =>
memberships.some(isActive(roomId));
memberships.some(allPass([isActive, forRoom(roomId)]));

export const hasInviteTo = (roomId: string, memberships: Membership[]) =>
memberships.some(isPendingInvite(roomId));

// export const isCurrent = (
// membership: Membership,
// status?: MembershipStatus,
// roomId?: string,
// ) =>
// isNil(membership.until) &&
// (isNil(status) || membership.status === status) &&
// (isNil(roomId) || membership.roomId == roomId);

// export const isActive = (membership: Membership, roomId?: string) =>
// isCurrent(membership, MembershipStatus.Joined, roomId);

// export const isMemberOf = (roomId: string, memberships: Membership[]) =>
// memberships.some((membership) => isActive(membership, roomId));
memberships.some(allPass([isPendingInvite, forRoom(roomId)]));

0 comments on commit ceecf0d

Please sign in to comment.