Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

manage participants screen improvements #584

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed and moved over from participantsApproval.ts

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
Loading