Skip to content

Commit

Permalink
Merge pull request #584 from IABTechLab/ans-UID2-4200-manage-particip…
Browse files Browse the repository at this point in the history
…ants-screen-improvements

manage participants screen improvements
  • Loading branch information
ashleysmithTTD authored Dec 12, 2024
2 parents c181bb8 + 4123220 commit de0d0d8
Show file tree
Hide file tree
Showing 41 changed files with 323 additions and 1,155 deletions.
42 changes: 12 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ This is the self-serve portal for UID2 participants. It enables a range of opera

Recommended VS Code extensions:

| Extension | Details | Required? |
| --------- | ------- | --------- |
| ESLint | Lints your code. We expect PRs to be free of linter errors, and preferably free of warnings as well. | Yes |
| i18n Ally | Checks your front-end code for accessibility rules. We expect PRs to maintain a high standard of A11y. | Yes |
| Prettier - Code formatter | Formats your code. We want PRs to contain functionality changes, not whitespace fixes and linebreak changes. Prettier makes us all use the same code style so we can focus on the things which matter. | Yes |
| Stylelint | Same as ESLint, but for your style files. | Yes |
| Code Spell Checker | Popular extension by Street Side Software - checks your spelling. | Yes, or similar alternative |
| Wallaby.js | Live, in-your-IDE, as-you-type test runner. It is free for open source projects, but please read the license and satisfy yourself that you're in compliance if you use the free version. | No |
| Docker | Helps you manage docker containers straight from VS Code. | No |
| Auto Rename Tag | Fixes your closing tags as you edit opening tags | No |
| Toggle Quotes | You can hit `ctrl-'` to cycle between quote styles (', ", and `) for the string you're editing. | No |
| SonarLint | Detects and highlights issues that can lead to bugs, vulnerabilities, and code smells. | No |
| Extension | Details | Required? |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------- |
| ESLint | Lints your code. We expect PRs to be free of linter errors, and preferably free of warnings as well. | Yes |
| i18n Ally | Checks your front-end code for accessibility rules. We expect PRs to maintain a high standard of A11y. | Yes |
| Prettier - Code formatter | Formats your code. We want PRs to contain functionality changes, not whitespace fixes and linebreak changes. Prettier makes us all use the same code style so we can focus on the things which matter. | Yes |
| Stylelint | Same as ESLint, but for your style files. | Yes |
| Code Spell Checker | Popular extension by Street Side Software - checks your spelling. | Yes, or similar alternative |
| Wallaby.js | Live, in-your-IDE, as-you-type test runner. It is free for open source projects, but please read the license and satisfy yourself that you're in compliance if you use the free version. | No |
| Docker | Helps you manage docker containers straight from VS Code. | No |
| Auto Rename Tag | Fixes your closing tags as you edit opening tags | No |
| Toggle Quotes | You can hit `ctrl-'` to cycle between quote styles (', ", and `) for the string you're editing. | No |
| SonarLint | Detects and highlights issues that can lead to bugs, vulnerabilities, and code smells. | No |

## Docker

Expand Down Expand Up @@ -268,24 +268,6 @@ The following steps describe the minimal steps required to successfully log in t
1. Fill in the form however you want and submit the form
1. Connect to the database server `localhost,11433` using the credentials in [docker-compose.yml](docker-compose.yml) under `KC_DB_USERNAME` and `KC_DB_PASSWORD`
1. In the `uid2_selfserve` database, observe that `dbo.users` now contains a row with with the details you just filled out.
1. Approve your account by updating the `status` of the row in `dbo.participants` that corresponds to your new user, i.e.

```
use [uid2_selfserve]
declare @email as nvarchar(256) = '<Enter your email here>'
update p
set status = 'approved'
from dbo.participants p
where p.id in (
select upr.participantId
from dbo.users u
join dbo.usersToParticipantRoles upr on u.id = upr.userId
where u.email = @email
);
```

1. Assign yourself the `api-participant-member` role by following these steps: [Assign Role to a Particular User](./KeycloakAdvancedSetup.md#assign-role-to-a-particular-user)
1. Run the Admin service locally by following [Connecting to local Admin service](#connecting-to-local-admin-service)
1. Optionally give your user access to the [UID2 Support Screens/Routes](#uid2-support-screensroutes)
Expand Down
8 changes: 2 additions & 6 deletions src/api/controllers/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
} from 'inversify-express-utils';

import { TYPES } from '../constant/types';
import { ParticipantStatus } from '../entities/Participant';
import { UserRoleId } from '../entities/UserRole';
import { getTraceId } from '../helpers/loggingHelpers';
import { getKcAdminClient } from '../keycloakAdminClient';
Expand Down Expand Up @@ -56,12 +55,9 @@ export class UserController {

@httpPut('/current/acceptTerms')
public async acceptTerms(@request() req: UserRequest, @response() res: Response): Promise<void> {
const doesUserHaveAnApprovedParticipant =
req.user?.participants?.some(
(participant) => participant.status === ParticipantStatus.Approved
) ?? false;
const doesUserHaveAParticipant = (req.user?.participants?.length ?? 0) >= 1 ?? false;

if (!doesUserHaveAnApprovedParticipant) {
if (!doesUserHaveAParticipant) {
res.status(403).json({
message: 'Unauthorized. You do not have the necessary permissions.',
errorHash: req.headers.traceId,
Expand Down
8 changes: 0 additions & 8 deletions src/api/entities/Participant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ import { ParticipantType, ParticipantTypeDTO, ParticipantTypeSchema } from './Pa
import { type User, UserDTO, UserSchema } from './User';
import { UserToParticipantRole } from './UserToParticipantRole';

export enum ParticipantStatus {
AwaitingSigning = 'awaitingSigning',
AwaitingApproval = 'awaitingApproval',
Approved = 'approved',
}

export class Participant extends BaseModel {
static get tableName() {
return 'participants';
Expand Down Expand Up @@ -82,7 +76,6 @@ export class Participant extends BaseModel {
};
declare id: number;
declare name: string;
declare status: ParticipantStatus;
declare allowSharing: boolean;
declare completedRecommendations: boolean;
declare siteId?: number;
Expand All @@ -107,7 +100,6 @@ export type ParticipantDTO = Omit<ModelObjectOpt<Participant>, 'types' | 'users'
export const ParticipantSchema = z.object({
id: z.number(),
name: z.string(),
status: z.nativeEnum(ParticipantStatus),
types: z.array(ParticipantTypeSchema).optional(),
apiRoles: z.array(ApiRoleSchema).optional(),
users: z.array(UserSchema).optional(),
Expand Down
7 changes: 7 additions & 0 deletions src/api/routers/participants/participants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Response } from 'express';

import { Participant } from '../../entities/Participant';
import {
getAllParticipants,
ParticipantRequest,
updateParticipant,
UserParticipantRequest,
Expand All @@ -21,6 +22,12 @@ export const handleGetParticipant = async (req: ParticipantRequest, res: Respons
return res.status(200).json(participant);
};

export const handleGetAllParticipants = async (_req: ParticipantRequest, res: Response) => {
const participants = await getAllParticipants();
const result = participants.sort((a, b) => a.name.localeCompare(b.name));
return res.status(200).json(result);
};

export const handleCompleteRecommendations = async (req: ParticipantRequest, res: Response) => {
const { participant } = req;
const updatedParticipant = await Participant.query()
Expand Down
98 changes: 0 additions & 98 deletions src/api/routers/participants/participantsApproval.ts

This file was deleted.

3 changes: 1 addition & 2 deletions src/api/routers/participants/participantsCreation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { z } from 'zod';
import { getRoleNamesByIds } from '../../../web/utils/apiRoles';
import { ApiRole } from '../../entities/ApiRole';
import { AuditAction, AuditTrailEvents } from '../../entities/AuditTrail';
import { Participant, ParticipantStatus } from '../../entities/Participant';
import { Participant } from '../../entities/Participant';
import { User, UserCreationPartial } from '../../entities/User';
import { UserRoleId } from '../../entities/UserRole';
import { UserToParticipantRole } from '../../entities/UserToParticipantRole';
Expand Down Expand Up @@ -136,7 +136,6 @@ async function createParticipant(
await performAsyncOperationWithAuditTrail(auditTrailInsertObject, traceId, async () => {
const participantData = {
...parsedParticipantRequest,
status: ParticipantStatus.Approved,
approverId: requestingUser?.id,
dateApproved: new Date(),
};
Expand Down
4 changes: 1 addition & 3 deletions src/api/routers/participants/participantsKeyPairs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';

import { createResponseObject } from '../../../testHelpers/apiTestHelpers';
import { Participant, ParticipantStatus } from '../../entities/Participant';
import { Participant } from '../../entities/Participant';
import { SSP_ADMIN_SERVICE_BASE_URL } from '../../envars';
import { KeyPairDTO } from '../../services/adminServiceHelpers';
import { ParticipantRequest } from '../../services/participantsService';
Expand Down Expand Up @@ -96,7 +96,6 @@ describe('Get participant key pairs', () => {
id: 5,
allowSharing: true,
completedRecommendations: true,
status: ParticipantStatus.Approved,
apiRoles: [],
});

Expand All @@ -122,7 +121,6 @@ describe('Get participant key pairs', () => {
id: 5,
allowSharing: true,
completedRecommendations: true,
status: ParticipantStatus.Approved,
apiRoles: [],
siteId,
});
Expand Down
14 changes: 2 additions & 12 deletions src/api/routers/participants/participantsRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createBusinessContactsRouter } from '../businessContactsRouter';
import { createParticipantUsersRouter } from '../participantUsersRouter';
import {
handleCompleteRecommendations,
handleGetAllParticipants,
handleGetParticipant,
handleUpdateParticipant,
} from './participants';
Expand All @@ -19,11 +20,6 @@ import {
} from './participantsApiKeys';
import { handleGetParticipantApiRoles } from './participantsApiRoles';
import { handleGetParticipantAppNames, handleSetParticipantAppNames } from './participantsAppIds';
import {
handleApproveParticipant,
handleGetApprovedParticipants,
handleGetParticipantsAwaitingApproval,
} from './participantsApproval';
import { handleGetAuditTrail } from './participantsAuditTrail';
import { handleCreateParticipant } from './participantsCreation';
import {
Expand All @@ -50,12 +46,7 @@ export function createParticipantsRouter() {

participantsRouter.get('/signed', handleGetSignedParticipants);

participantsRouter.get(
'/awaitingApproval',
isUid2SupportCheck,
handleGetParticipantsAwaitingApproval
);
participantsRouter.get('/approved', isUid2SupportCheck, handleGetApprovedParticipants);
participantsRouter.get('/allParticipants', isUid2SupportCheck, handleGetAllParticipants);

participantsRouter.put('/', handleCreateParticipant);

Expand All @@ -65,7 +56,6 @@ export function createParticipantsRouter() {
participantsRouter.get('/:participantId/apiRoles', handleGetParticipantApiRoles);
participantsRouter.put('/:participantId', handleUpdateParticipant);
participantsRouter.put('/:participantId/completeRecommendations', handleCompleteRecommendations);
participantsRouter.put('/:participantId/approve', handleApproveParticipant);

participantsRouter.post(
'/:participantId/invite',
Expand Down
25 changes: 4 additions & 21 deletions src/api/services/participantsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@ import { z } from 'zod';
import { getRoleNamesByIds } from '../../web/utils/apiRoles';
import { ApiRole } from '../entities/ApiRole';
import { AuditAction, AuditTrailEvents } from '../entities/AuditTrail';
import {
Participant,
ParticipantApprovalPartial,
ParticipantDTO,
ParticipantStatus,
} from '../entities/Participant';
import { Participant, ParticipantApprovalPartial, ParticipantDTO } from '../entities/Participant';
import { ParticipantType } from '../entities/ParticipantType';
import { User, UserDTO } from '../entities/User';
import { SSP_WEB_BASE_URL } from '../envars';
Expand Down Expand Up @@ -38,7 +33,7 @@ export interface UserParticipantRequest extends ParticipantRequest {

export type ParticipantRequestDTO = Pick<
ParticipantDTO,
'id' | 'name' | 'siteId' | 'types' | 'status' | 'apiRoles'
'id' | 'name' | 'siteId' | 'types' | 'apiRoles'
> & {
requestingUser: Pick<UserDTO, 'email'> &
Partial<Pick<UserDTO, 'jobFunction'>> & { fullName: string };
Expand All @@ -61,7 +56,6 @@ export const mapParticipantToApprovalRequest = (
siteId: participant.siteId,
types: participant.types,
apiRoles: participant.apiRoles,
status: participant.status,
requestingUser: {
email: firstUser ? firstUser.email : '',
jobFunction: firstUser?.jobFunction,
Expand All @@ -72,13 +66,6 @@ export const mapParticipantToApprovalRequest = (
};
};

export const getParticipantsAwaitingApproval = async (): Promise<Participant[]> => {
const participantsAwaitingApproval = await Participant.query()
.withGraphFetched('[types, users]')
.where('status', ParticipantStatus.AwaitingApproval);
return participantsAwaitingApproval;
};

type SiteIdType = NonNullable<ParticipantDTO['siteId']>;
export const getAttachedSiteIDs = async (): Promise<SiteIdType[]> => {
const sites = await Participant.query()
Expand All @@ -88,10 +75,8 @@ export const getAttachedSiteIDs = async (): Promise<SiteIdType[]> => {
return sites.map((s) => s.siteId);
};

export const getParticipantsApproved = async (): Promise<Participant[]> => {
return Participant.query()
.where('status', ParticipantStatus.Approved)
.withGraphFetched('[apiRoles, approver, types, users]');
export const getAllParticipants = async (): Promise<Participant[]> => {
return Participant.query().withGraphFetched('[apiRoles, approver, types, users]');
};

export const getParticipantsBySiteIds = async (siteIds: number[]) => {
Expand Down Expand Up @@ -166,7 +151,6 @@ export const updateParticipantApiRolesWithTransaction = async (
export const updateParticipantAndTypesAndApiRoles = async (
participant: Participant,
participantApprovalPartial: z.infer<typeof ParticipantApprovalPartial> & {
status: ParticipantStatus;
approverId: number | undefined;
dateApproved: Date;
}
Expand All @@ -175,7 +159,6 @@ export const updateParticipantAndTypesAndApiRoles = async (
await participant.$query(trx).patch({
name: participantApprovalPartial.name,
siteId: participantApprovalPartial.siteId,
status: participantApprovalPartial.status,
approverId: participantApprovalPartial.approverId,
dateApproved: participantApprovalPartial.dateApproved,
});
Expand Down
Loading

0 comments on commit de0d0d8

Please sign in to comment.