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

Save collaborators to report #272

Merged
merged 24 commits into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9cc4732
Can save collaborators to a report
jasalisbury Jan 22, 2021
9093dcb
Merge branch 'main' into js-109-add-collaborators
jasalisbury Jan 22, 2021
75608c9
Add authorization for submitting reports. Test fixes
jasalisbury Jan 22, 2021
418df05
Update documentation
jasalisbury Jan 25, 2021
9f139a8
Merge branch 'main' into js-109-add-collaborators
jasalisbury Jan 25, 2021
957f958
Report policy properly checks for collaborators
jasalisbury Jan 25, 2021
f925d64
Update docs/openapi/paths/index.yaml
jasalisbury Jan 25, 2021
34e9ac4
Apply suggestions from code review
jasalisbury Jan 25, 2021
f7b7b76
Fix tests
jasalisbury Jan 26, 2021
778d2a3
Merge branch 'js-109-add-collaborators' of github.com:adhocteam/Head-…
jasalisbury Jan 26, 2021
1d11e7f
Close DB after file upload tests run
jasalisbury Jan 26, 2021
6687dca
Merge branch 'main' into js-109-add-collaborators
jasalisbury Jan 26, 2021
49d77d3
Feedback from PR
jasalisbury Jan 26, 2021
3cb2751
Fix linter
jasalisbury Jan 26, 2021
49c6e13
Merge branch 'main' into js-109-add-collaborators
jasalisbury Jan 26, 2021
952954e
Merge branch 'main' into js-109-add-collaborators
jasalisbury Jan 28, 2021
abc9932
Merge pull request #127 from adhocteam/js-109-add-collaborators
jasalisbury Jan 28, 2021
d4a86c3
Check SITE_ACCESS permission as part of authMiddleware
rahearn Jan 28, 2021
4f60ea8
Grant SITE_ACCESS to the bootstrap admin
rahearn Jan 28, 2021
2ea5bab
Add SITE_ACCESS to the global permissions available via the admin UI
rahearn Jan 28, 2021
da1521c
Address PR feedback
jasalisbury Jan 29, 2021
6f02a27
Merge pull request #136 from adhocteam/js-collaborators-feedback
rahearn Jan 29, 2021
95a8339
Merge branch 'main' into lock-user-access
rahearn Jan 29, 2021
4c3a436
Merge pull request #135 from HHS/lock-user-access
rahearn Jan 29, 2021
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
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ parameters:
default: "main"
type: string
sandbox_git_branch: # change to feature branch to test deployment
default: "js-drop-attachment-enum"
default: "js-109-add-collaborators"
type: string
jobs:
build_and_lint:
Expand Down
13 changes: 11 additions & 2 deletions docs/logical_data_model.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Logical Data Model
==================

<img src="http://www.plantuml.com/plantuml/png/nLRRRkCs47tNL_2jRO0OHOkYm29OxANU8WzrWoJx0HDn8hCbEUnmbAqHvzzhYbX2yYfRnYhG5niwSpyylBo90YoziqrimQBwVlNpJqtpZO87Cbe5fZkBBgfFHhOAV6THgvRfKrJtYDB4FuqFmE2KDHvb5r0HovQQfhKGe_eOaTLZezIPCbgzKiOBTWVeug93Zmo2-lNhWtaRCIRVo-GKXOtl3gW7X-Fli2MFczxT8O_XCuKEZ1r3OiJYAuM-ToaxERp6pf2_73NnxOy_VAUkrn_Mr-lD-_MD2lsTDUqgEyQLqKFgbZ5r-LOEWM8SHW4N1aiVz8pbwVFeEK_Dy6T7WvOKDoq5F2M3KXfZooQ88L_GHUDS8JiJO_L8VKIkO3ijUk6QJroZc7Yg21lu_2zMzgyUewoPYMCD6nJIXo4_Ng6UpVZJeMnfedxEPTwHtatX6CX7V0v_JneBld1GidFo-vfaRocOjZwcbCbnLSfYiZMBdRAMjPQK_bB_MypGnNA1_4fWwpXGAWf87yT8IdZFHsucVc1oDmvOuXPHRWMkB2H1tH5P1B-ius7mhxNT7MmLfASK_nVf35wmKg_BAprRv_XwElbKw_90DQndpnGu8wY4iFm2WaU3ejSpw59YaWHgDl890qwfgOrBKQTeU1aN6_AvwrCky6MbtxPY7epihZ4GIqrvnaWzjyCRR787WTOcNLnWDoGuKLKM-k_tLy1YMXCIqvEzeD6Q1-JTRr_SRBelrw76rZYm4pMcuOfi9n2KoGu53NgvtGMSkJEFteSpUo8mTGmknISSc65taQs9407kKAuez7ReTCw74IBv6KEOCd9apiVYh44WzJDZQSaB_YsJ0Q336ydi_179d_peiX3bOnRKkzqBWvThTAIUkdq24t5Nz3lT3Rdn5vqVESIN_LmHjodE2uXAHAlz_fmULRuPCl94v-Vxnz5xl1cUjJDuZfepa2t-m-tR3B_gQJz0wG1iSl_5hHpjJVhv0iqfLokL_aSfvw5Sj6GhoBXmchoK-3pA11iJD5taPlgPVUOabuib-xovh2C-x_C6lUwT_GS0">
<img src="http://www.plantuml.com/plantuml/png/nLVRRjms47tNL_2jhG4RHOkYG604ITnDuOTk3TlvW5bfh6LBSjZXoEd2zhzNNAQgg9LTPjNBIsATkHyUNlPD463fhAb23GRLrsVVVrTLNoYu9zqepxGnQ8Fwg6MhmBK66rKpVGpg3b6gyAjs7XYiCjgXx3mgOjbILTK6Gb0V0h9wg9GwKNssLqexmRQ7pizRTCWWsBn-tPkt0KFqlOnEGLYxjaSTM1n_-oqvN7TsWpo6JnHQq6OCYHQ3hnIwpiRiPF4QMqtkSjJ4bzz-yvMwNhrVNQ_M5wiRvRkDqVLPSWndkjcdhXbZbs-b3nHjCGXOlxTqlfcnFFqvUifJClp9PD0XnKrD7e_9e5I3eMRjHPCRq4NZN25R7KBnI5r0Bc0xBlh0DH-vHY7oL12sv_EVMDdVkXvRCHE74ZR8nmzDRhn27UlnfqNPKgP-JcNUKRFBuU391NmElw061tpXe6JcvFUIP6_9wxeyPYYJuegKnMHA5Zjc7TPQOlhBvkzYzbqi5yYF1AQC0wM2W7HXZCI4Tpnnq-4zaxsnm1AsY78hS6KW2cf3P13SeexJihtKTjlR2aYFAVulqXcSOA5UbrTwjixqUZhwL6lJG2Mi9yyKE15KGbX-0C79eEYQ0tGvCL421Hlvc05pQahZKjGH6bx4nPfSwlgO2tnPwRUrw7ijkslqn59IdZ5Gn_N-3PRTGT0qEbvSODOacAag2VtdkojWqRNsaUbiBpHez3toxgU3cwtfXkkmmLfRC9aQut23R3C8XidwWXgTtEuypjoPq_VXuPxmJ2s3ZV4H1sROdITh94I0MvGhyftPTphdmmY1t8p1vunCpEbOd3K8f7aEB3KvmTyb6m1Ql974pY-1tD6FBWD1FYH1lTlzoo-sJs7BfJqINTdsWcnF53ISRwBK-6STEod43ST0GTnwS5vELDR-1Ha36s8Gul-AhyyLP0pfsuz_giV7KtfGIJC9UQBJqyU7qQ-GAlquEiHJr9o1pE2_VtDKwT7vzBY7uh5V9Ve7j-52g-8_1sXAUNQcul-ZbFDGAZfaco2fS9nyAF5fb1pBUOv9db_gHDsBBXdXcTkqLEQJxS3KVBJFc_-IzVC-Rz0rdJL_0000">

UML Source
----------
Expand Down Expand Up @@ -152,6 +152,7 @@ class ActivityReport {
pageState : json
* userId : integer(32) REFERENCES public.Users.id
* lastUpdatedById : integer(32) REFERENCES public.Users.id
* regionId : integer(32) REFERENCES public.Region.id
* createdAt : timestamp
* updatedAt : timestamp
}
Expand All @@ -165,6 +166,12 @@ class ActivityParticipant {
* updatedAt : timestamp
}

class ActivityReportCollaborator {
* id : integer <<generated>>
* activityReportId : integer(32) REFERENCES public.ActivityReport.id
* userId : integer(32) REFERENCES public.User.id
}

User ||-o{ Region
User }o--|{ Permission
Scope }o--|{ Permission
Expand All @@ -179,6 +186,8 @@ Topic .. TopicGoal
Goal .. TopicGoal
Grantee ||--|{ Grant
Region ||--|{ Grant
ActivityReport .. ActivityReportCollaborator
User .. ActivityReportCollaborator

User ||-o{ ActivityReport
ActivityReport ||-o{ ActivityParticipant
Expand All @@ -190,7 +199,7 @@ NonGrantee ||-{ ActivityParticipant
Instructions
------------

1. [Edit this diagram with plantuml.com](http://www.plantuml.com/plantuml/umla/nLRRRkCs47tNL_2jRO0OHOkYm29OxANU8WzrWoJx0HDn8hCbEUnmbAqHvzzhYbX2yYfRnYhG5niwSpyylBo90YoziqrimQBwVlNpJqtpZO87Cbe5fZkBBgfFHhOAV6THgvRfKrJtYDB4FuqFmE2KDHvb5r0HovQQfhKGe_eOaTLZezIPCbgzKiOBTWVeug93Zmo2-lNhWtaRCIRVo-GKXOtl3gW7X-Fli2MFczxT8O_XCuKEZ1r3OiJYAuM-ToaxERp6pf2_73NnxOy_VAUkrn_Mr-lD-_MD2lsTDUqgEyQLqKFgbZ5r-LOEWM8SHW4N1aiVz8pbwVFeEK_Dy6T7WvOKDoq5F2M3KXfZooQ88L_GHUDS8JiJO_L8VKIkO3ijUk6QJroZc7Yg21lu_2zMzgyUewoPYMCD6nJIXo4_Ng6UpVZJeMnfedxEPTwHtatX6CX7V0v_JneBld1GidFo-vfaRocOjZwcbCbnLSfYiZMBdRAMjPQK_bB_MypGnNA1_4fWwpXGAWf87yT8IdZFHsucVc1oDmvOuXPHRWMkB2H1tH5P1B-ius7mhxNT7MmLfASK_nVf35wmKg_BAprRv_XwElbKw_90DQndpnGu8wY4iFm2WaU3ejSpw59YaWHgDl890qwfgOrBKQTeU1aN6_AvwrCky6MbtxPY7epihZ4GIqrvnaWzjyCRR787WTOcNLnWDoGuKLKM-k_tLy1YMXCIqvEzeD6Q1-JTRr_SRBelrw76rZYm4pMcuOfi9n2KoGu53NgvtGMSkJEFteSpUo8mTGmknISSc65taQs9407kKAuez7ReTCw74IBv6KEOCd9apiVYh44WzJDZQSaB_YsJ0Q336ydi_179d_peiX3bOnRKkzqBWvThTAIUkdq24t5Nz3lT3Rdn5vqVESIN_LmHjodE2uXAHAlz_fmULRuPCl94v-Vxnz5xl1cUjJDuZfepa2t-m-tR3B_gQJz0wG1iSl_5hHpjJVhv0iqfLokL_aSfvw5Sj6GhoBXmchoK-3pA11iJD5taPlgPVUOabuib-xovh2C-x_C6lUwT_GS0)
1. [Edit this diagram with plantuml.com](http://www.plantuml.com/plantuml/umla/nLVRRjms47tNL_2jhG4RHOkYG604ITnDuOTk3TlvW5bfh6LBSjZXoEd2zhzNNAQgg9LTPjNBIsATkHyUNlPD463fhAb23GRLrsVVVrTLNoYu9zqepxGnQ8Fwg6MhmBK66rKpVGpg3b6gyAjs7XYiCjgXx3mgOjbILTK6Gb0V0h9wg9GwKNssLqexmRQ7pizRTCWWsBn-tPkt0KFqlOnEGLYxjaSTM1n_-oqvN7TsWpo6JnHQq6OCYHQ3hnIwpiRiPF4QMqtkSjJ4bzz-yvMwNhrVNQ_M5wiRvRkDqVLPSWndkjcdhXbZbs-b3nHjCGXOlxTqlfcnFFqvUifJClp9PD0XnKrD7e_9e5I3eMRjHPCRq4NZN25R7KBnI5r0Bc0xBlh0DH-vHY7oL12sv_EVMDdVkXvRCHE74ZR8nmzDRhn27UlnfqNPKgP-JcNUKRFBuU391NmElw061tpXe6JcvFUIP6_9wxeyPYYJuegKnMHA5Zjc7TPQOlhBvkzYzbqi5yYF1AQC0wM2W7HXZCI4Tpnnq-4zaxsnm1AsY78hS6KW2cf3P13SeexJihtKTjlR2aYFAVulqXcSOA5UbrTwjixqUZhwL6lJG2Mi9yyKE15KGbX-0C79eEYQ0tGvCL421Hlvc05pQahZKjGH6bx4nPfSwlgO2tnPwRUrw7ijkslqn59IdZ5Gn_N-3PRTGT0qEbvSODOacAag2VtdkojWqRNsaUbiBpHez3toxgU3cwtfXkkmmLfRC9aQut23R3C8XidwWXgTtEuypjoPq_VXuPxmJ2s3ZV4H1sROdITh94I0MvGhyftPTphdmmY1t8p1vunCpEbOd3K8f7aEB3KvmTyb6m1Ql974pY-1tD6FBWD1FYH1lTlzoo-sJs7BfJqINTdsWcnF53ISRwBK-6STEod43ST0GTnwS5vELDR-1Ha36s8Gul-AhyyLP0pfsuz_giV7KtfGIJC9UQBJqyU7qQ-GAlquEiHJr9o1pE2_VtDKwT7vzBY7uh5V9Ve7j-52g-8_1sXAUNQcul-ZbFDGAZfaco2fS9nyAF5fb1pBUOv9db_gHDsBBXdXcTkqLEQJxS3KVBJFc_-IzVC-Rz0rdJL_0000)
2. Copy and paste the final UML into the UML Source section
3. Update the img src and edit link target to the current values

Expand Down
6 changes: 4 additions & 2 deletions docs/openapi/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,11 @@ components:
description: The type of the activity, Training or Technical Assistance (or both)
items:
type: string
approvingUser:
selectableUser:
type: object
description: Users with permission to approve reports
description: >
Users with only their id and name, ready to be placed in a select box on the frontend without
further modification
properties:
id:
type: number
Expand Down
8 changes: 7 additions & 1 deletion docs/openapi/paths/activity-reports/approvers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ get:
tags:
- activity-reports
summary: Potential activity report approvers
parameters:
- in: query
name: region
schema:
type: integer
description: The region id of the approvers
operationId: getApprovers
description: Gets all approvers for any region the current user has write permissions
responses:
Expand All @@ -12,4 +18,4 @@ get:
schema:
type: array
items:
$ref: '../../index.yaml#/components/schemas/approvingUser'
$ref: '../../index.yaml#/components/schemas/selectableUser'
23 changes: 23 additions & 0 deletions docs/openapi/paths/collaborators.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
get:
tags:
- activity-reports
operationId: getCollaborators
summary: Possible report collaborators
parameters:
- in: query
name: region
schema:
type: integer
description: The region id of the collaborators
description: >
Get users that have write permissions on a given region.
These users can be flagged as collaborators on a report.
responses:
200:
description: Possible collaborators
content:
application/json:
schema:
type: array
items:
$ref: '../../index.yaml#/components/schemas/selectableUser'
2 changes: 2 additions & 0 deletions docs/openapi/paths/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
$ref: './hello.yaml'
'/user':
$ref: './user.yaml'
'/users/collaborators':
$ref: './collaborators.yaml'
'/logout':
$ref: './logout.yaml'
'/login':
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/Constants.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const SCOPE_IDS = {
SITE_ACCESS: 1,
ADMIN: 2,
READ_WRITE_ACTIVITY_REPORTS: 3,
READ_ACTIVITY_REPORTS: 4,
Expand All @@ -21,6 +22,10 @@ export const REGIONAL_SCOPES = {
};

export const GLOBAL_SCOPES = {
[SCOPE_IDS.SITE_ACCESS]: {
name: 'SITE_ACCESS',
description: 'User can login and view the TTAHUB site',
},
[SCOPE_IDS.ADMIN]: {
name: 'ADMIN',
description: 'User can view the admin panel and change user permissions (including their own)',
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/__tests__/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,15 @@ describe('App', () => {
expect(await screen.findByText('HSES Login')).toBeVisible();
});
});

describe('when user is locked', () => {
beforeEach(() => {
fetchMock.get(userUrl, 403);
render(<App />);
});

it('displays the login button for now. in the future this should show the "request permissions" UI', async () => {
expect(await screen.findByText('HSES Login')).toBeVisible();
});
});
});
2 changes: 1 addition & 1 deletion frontend/src/components/MultiSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function MultiSelect({
if (event) {
onChange(event, controllerOnChange);
} else {
controllerOnChange(event);
controllerOnChange([]);
}
}}
styles={styles}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/__tests__/MutliSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,6 @@ describe('MultiSelect', () => {
await act(async () => {
userEvent.click(screen.getByTestId('submit'));
});
expect(onSubmit).toHaveBeenCalledWith({ name: null });
expect(onSubmit).toHaveBeenCalledWith({ name: [] });
});
});
10 changes: 8 additions & 2 deletions frontend/src/fetchers/activityReports.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { get, put, post } from './index';

const activityReportUrl = join('/', 'api', 'activity-reports');

export const fetchApprovers = async () => {
const res = await get(join(activityReportUrl, 'approvers'));
export const getApprovers = async (region) => {
const res = await get(join(activityReportUrl, 'approvers', `?region=${region}`));
return res.json();
};

Expand Down Expand Up @@ -33,3 +33,9 @@ export const getRecipients = async () => {
const recipients = await get(join(activityReportUrl, 'activity-recipients'));
return recipients.json();
};

export const getCollaborators = async (region) => {
const url = join('/', 'api', 'users', 'collaborators', `?region=${region}`);
const collaborators = await get(url);
return collaborators.json();
};
33 changes: 9 additions & 24 deletions frontend/src/pages/ActivityReport/Pages/ReviewSubmit.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,23 @@
import React, { useEffect, useState } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import {
Dropdown, Form, Label, Fieldset, Textarea, Alert, Button, Accordion,
} from '@trussworks/react-uswds';
import { Helmet } from 'react-helmet';

import { fetchApprovers } from '../../../fetchers/activityReports';
import Container from '../../../components/Container';

const ReviewSubmit = ({
hookForm, allComplete, onSubmit, submitted, reviewItems,
hookForm, allComplete, onSubmit, submitted, reviewItems, approvers,
}) => {
const [loading, updateLoading] = useState(true);
const [possibleApprovers, updatePossibleApprovers] = useState([]);
const { handleSubmit, register, formState } = hookForm;
const { isValid } = formState;
const valid = allComplete && isValid;

useEffect(() => {
updateLoading(true);
const fetch = async () => {
const approvers = await fetchApprovers();
updatePossibleApprovers(approvers);
updateLoading(false);
};
fetch();
}, []);

const onFormSubmit = (data) => {
onSubmit(data);
};

if (loading) {
return (
<div>
loading...
</div>
);
}

const setValue = (e) => {
if (e === '') {
return null;
Expand Down Expand Up @@ -84,7 +63,7 @@ const ReviewSubmit = ({
<Label htmlFor="approvingManagerId">Approving manager</Label>
<Dropdown id="approvingManagerId" name="approvingManagerId" inputRef={register({ setValueAs: setValue, required: true })}>
<option name="default" value="" disabled hidden>Select a Manager...</option>
{possibleApprovers.map((approver) => (
{approvers.map((approver) => (
<option key={approver.id} value={approver.id}>{approver.name}</option>
))}
</Dropdown>
Expand All @@ -97,6 +76,12 @@ const ReviewSubmit = ({
};

ReviewSubmit.propTypes = {
approvers: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
}),
).isRequired,
allComplete: PropTypes.bool.isRequired,
onSubmit: PropTypes.func.isRequired,
submitted: PropTypes.bool.isRequired,
Expand Down
38 changes: 12 additions & 26 deletions frontend/src/pages/ActivityReport/Pages/__tests__/ReviewSubmit.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,50 @@
import '@testing-library/jest-dom';
import { render, screen, waitFor } from '@testing-library/react';
import React from 'react';
import fetchMock from 'fetch-mock';
import join from 'url-join';
import userEvent from '@testing-library/user-event';
import { useForm } from 'react-hook-form';

import ReviewSubmit from '../ReviewSubmit';

const Review = ({
const approvers = [
{ id: 1, name: 'user 1' },
{ id: 2, name: 'user 2' },
];

const RenderReview = ({
// eslint-disable-next-line react/prop-types
allComplete, submitted, formData, onSubmit,
allComplete, submitted, initialData, onSubmit,
}) => {
const hookForm = useForm({
mode: 'onChange',
defaultValues: formData,
defaultValues: { ...initialData, approvingManagerId: null },
});

return (
<ReviewSubmit
allComplete={allComplete}
submitted={submitted}
formData={formData}
onSubmit={onSubmit}
reviewItems={[]}
approvers={approvers}
hookForm={hookForm}
/>
);
};

const renderReview = (
allComplete,
submitted,
formData = { approvingManagerId: null },
onSubmit = () => {},
) => {
const renderReview = (allComplete, submitted, initialData = {}, onSubmit = () => {}) => {
render(
<Review
<RenderReview
allComplete={allComplete}
submitted={submitted}
formData={formData}
onSubmit={onSubmit}
initialData={initialData}
/>,
);
};

const approvers = [
{ id: 1, name: 'user 1' },
{ id: 2, name: 'user 2' },
];

const selectLabel = 'Approving manager';

describe('ReviewSubmit', () => {
afterEach(() => fetchMock.restore());
beforeEach(() => {
const approversUrl = join('/', 'api', 'activity-reports', 'approvers');
fetchMock.get(approversUrl, approvers);
});

describe('when the form is not complete', () => {
it('an error alert is shown', async () => {
renderReview(false, false);
Expand Down
Loading