Skip to content

Commit

Permalink
HAI-2041 Initial
Browse files Browse the repository at this point in the history
  • Loading branch information
pitkni committed Oct 27, 2023
1 parent e7e1b7b commit eab0156
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 53 deletions.
10 changes: 5 additions & 5 deletions src/domain/application/applicationView/ApplicationView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { ApplicationCancel } from '../components/ApplicationCancel';
import AttachmentSummary from '../components/AttachmentSummary';
import useAttachments from '../hooks/useAttachments';
import FeatureFlags from '../../../common/components/featureFlags/FeatureFlags';
import UserRightsCheck from '../../hanke/hankeUsers/UserRightsCheck';
import { CheckRightsByHanke } from '../../hanke/hankeUsers/UserRightsCheck';

type Props = {
application: Application;
Expand Down Expand Up @@ -120,25 +120,25 @@ function ApplicationView({ application, hanke, onEditApplication }: Props) {

<InformationViewHeaderButtons>
{isPending ? (
<UserRightsCheck requiredRight="EDIT_APPLICATIONS" hankeTunnus={hanke?.hankeTunnus}>
<CheckRightsByHanke requiredRight="EDIT_APPLICATIONS" hankeTunnus={hanke?.hankeTunnus}>
<Button
theme="coat"
iconLeft={<IconPen aria-hidden="true" />}
onClick={onEditApplication}
>
{t('hakemus:buttons:editApplication')}
</Button>
</UserRightsCheck>
</CheckRightsByHanke>
) : null}
{hanke ? (
<UserRightsCheck requiredRight="EDIT_APPLICATIONS" hankeTunnus={hanke?.hankeTunnus}>
<CheckRightsByHanke requiredRight="EDIT_APPLICATIONS" hankeTunnus={hanke?.hankeTunnus}>
<ApplicationCancel
applicationId={id}
alluStatus={alluStatus}
hankeTunnus={hanke?.hankeTunnus}
buttonIcon={<IconTrash aria-hidden />}
/>
</UserRightsCheck>
</CheckRightsByHanke>
) : null}
</InformationViewHeaderButtons>
</InformationViewHeader>
Expand Down
4 changes: 2 additions & 2 deletions src/domain/hanke/accessRights/AccessRightsViewContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ErrorLoadingText from '../../../common/components/errorLoadingText/ErrorL
import { useHankeUsers } from '../hankeUsers/hooks/useHankeUsers';
import useHanke from '../hooks/useHanke';
import AccessRightsView from './AccessRightsView';
import useUserRightsForHanke from '../hankeUsers/hooks/useUserRightsForHanke';
import { usePermissionsForHanke } from '../hankeUsers/hooks/useUserRightsForHanke';

type Props = {
hankeTunnus: string;
Expand All @@ -17,7 +17,7 @@ function AccessRightsViewContainer({ hankeTunnus }: Props) {
const { t } = useTranslation();
const { data: hankeUsers, isLoading, isError, error } = useHankeUsers(hankeTunnus);
const { data: hankeData } = useHanke(hankeTunnus);
const { data: signedInUser } = useUserRightsForHanke(hankeTunnus);
const { data: signedInUser } = usePermissionsForHanke(hankeTunnus);

if (isLoading) {
return (
Expand Down
14 changes: 7 additions & 7 deletions src/domain/hanke/hankeUsers/UserRightsCheck.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { rest } from 'msw';
import { render, screen, waitFor } from '../../../testUtils/render';
import { server } from '../../mocks/test-server';
import { SignedInUser } from './hankeUser';
import UserRightsCheck from './UserRightsCheck';
import { CheckRightsByHanke } from './UserRightsCheck';

test('Should render children if user has required right', async () => {
render(
<UserRightsCheck requiredRight="EDIT" hankeTunnus="HAI22-2">
<CheckRightsByHanke requiredRight="EDIT" hankeTunnus="HAI22-2">
<p>Children</p>
</UserRightsCheck>,
</CheckRightsByHanke>,
);

await waitFor(() => {
Expand All @@ -32,9 +32,9 @@ test('Should not render children if user does not have required right', async ()
);

render(
<UserRightsCheck requiredRight="EDIT" hankeTunnus="HAI22-2">
<CheckRightsByHanke requiredRight="EDIT" hankeTunnus="HAI22-2">
<p>Children</p>
</UserRightsCheck>,
</CheckRightsByHanke>,
);

await waitFor(() => {
Expand All @@ -47,9 +47,9 @@ test('Should render children when access right feature is not enabled', async ()
window._env_.REACT_APP_FEATURE_ACCESS_RIGHTS = 0;

render(
<UserRightsCheck requiredRight="EDIT" hankeTunnus="HAI22-2">
<CheckRightsByHanke requiredRight="EDIT" hankeTunnus="HAI22-2">
<p>Children</p>
</UserRightsCheck>,
</CheckRightsByHanke>,
);

await waitFor(() => {
Expand Down
30 changes: 25 additions & 5 deletions src/domain/hanke/hankeUsers/UserRightsCheck.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react';
import useUserRightsForHanke from './hooks/useUserRightsForHanke';
import { Rights } from './hankeUser';
import { Rights, SignedInUser } from './hankeUser';
import { useFeatureFlags } from '../../../common/components/featureFlags/FeatureFlagsContext';
import { usePermissionsForHanke } from './hooks/useUserRightsForHanke';

/**
* Check that user has required rights.
* If they have, render children.
*/
function UserRightsCheck({
export function CheckRightsByHanke({
requiredRight,
hankeTunnus,
children,
Expand All @@ -18,7 +18,7 @@ function UserRightsCheck({
hankeTunnus?: string;
children: React.ReactElement | null;
}) {
const { data: signedInUser } = useUserRightsForHanke(hankeTunnus);
const { data: signedInUser } = usePermissionsForHanke(hankeTunnus);
const features = useFeatureFlags();

if (!features.accessRights) {
Expand All @@ -32,4 +32,24 @@ function UserRightsCheck({
return null;
}

export default UserRightsCheck;
/**
* Check that user has required rights. Verified by SignedInUser data.
* If permitted, render children.
*/
export function CheckRightsByUser({
requiredRight,
signedUser,
children,
}: {
requiredRight: keyof typeof Rights;
signedUser: SignedInUser | undefined;
children: React.ReactElement | null;
}) {
const features = useFeatureFlags();

if (!features.accessRights) {
return children;
}

return signedUser?.kayttooikeudet.includes(requiredRight) ? children : null;
}
4 changes: 4 additions & 0 deletions src/domain/hanke/hankeUsers/hankeUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export type SignedInUser = {
kayttooikeudet: UserRights;
};

export type SignedInUserByHanke = {
[hankeTunnus: string]: SignedInUser;
};

export type IdentificationResponse = {
kayttajaId: string;
hankeTunnus: string;
Expand Down
7 changes: 6 additions & 1 deletion src/domain/hanke/hankeUsers/hankeUsersApi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import api from '../../api/api';
import { HankeUser, IdentificationResponse, SignedInUser } from './hankeUser';
import { HankeUser, IdentificationResponse, SignedInUser, SignedInUserByHanke } from './hankeUser';

export async function getHankeUsers(hankeTunnus: string) {
const { data } = await api.get<{ kayttajat: HankeUser[] }>(`hankkeet/${hankeTunnus}/kayttajat`);
Expand All @@ -23,6 +23,11 @@ export async function getSignedInUserForHanke(hankeTunnus?: string): Promise<Sig
return data;
}

export async function getSignedInUserByHanke(): Promise<SignedInUserByHanke> {
const { data } = await api.get<SignedInUserByHanke>('hankkeet/my-permissions');
return data;
}

export async function identifyUser(id: string) {
const { data } = await api.post<IdentificationResponse>('kayttajat', { tunniste: id });
return data;
Expand Down
14 changes: 11 additions & 3 deletions src/domain/hanke/hankeUsers/hooks/useUserRightsForHanke.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useQuery } from 'react-query';
import { SignedInUser } from '../hankeUser';
import { getSignedInUserForHanke } from '../hankeUsersApi';
import { SignedInUser, SignedInUserByHanke } from '../hankeUser';
import { getSignedInUserForHanke, getSignedInUserByHanke } from '../hankeUsersApi';
import { useFeatureFlags } from '../../../../common/components/featureFlags/FeatureFlagsContext';

export default function useSignedInUserRightsForHanke(hankeTunnus?: string) {
export function usePermissionsForHanke(hankeTunnus?: string) {
const features = useFeatureFlags();

return useQuery<SignedInUser>(
Expand All @@ -14,3 +14,11 @@ export default function useSignedInUserRightsForHanke(hankeTunnus?: string) {
},
);
}

export function usePermissionsByHanke() {
const features = useFeatureFlags();

return useQuery<SignedInUserByHanke>(['signedUserByHanke'], () => getSignedInUserByHanke(), {
enabled: features.accessRights,
});
}
21 changes: 12 additions & 9 deletions src/domain/hanke/hankeView/HankeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import {
import FeatureFlags from '../../../common/components/featureFlags/FeatureFlags';
import { useFeatureFlags } from '../../../common/components/featureFlags/FeatureFlagsContext';
import { SignedInUser } from '../hankeUsers/hankeUser';
import UserRightsCheck from '../hankeUsers/UserRightsCheck';
import { CheckRightsByHanke } from '../hankeUsers/UserRightsCheck';

type AreaProps = {
area: HankeAlue;
Expand Down Expand Up @@ -217,7 +217,7 @@ const HankeView: React.FC<Props> = ({

<InformationViewHeaderButtons>
<FeatureFlags flags={['hanke']}>
<UserRightsCheck requiredRight="EDIT" hankeTunnus={hankeData.hankeTunnus}>
<CheckRightsByHanke requiredRight="EDIT" hankeTunnus={hankeData.hankeTunnus}>
<Button
onClick={onEditHanke}
variant="primary"
Expand All @@ -226,8 +226,11 @@ const HankeView: React.FC<Props> = ({
>
{t('hankeList:buttons:edit')}
</Button>
</UserRightsCheck>
<UserRightsCheck requiredRight="EDIT_APPLICATIONS" hankeTunnus={hankeData.hankeTunnus}>
</CheckRightsByHanke>
<CheckRightsByHanke
requiredRight="EDIT_APPLICATIONS"
hankeTunnus={hankeData.hankeTunnus}
>
{isHankePublic ? (
<Button
variant="primary"
Expand All @@ -238,7 +241,7 @@ const HankeView: React.FC<Props> = ({
{t('hankeList:buttons:addApplication')}
</Button>
) : null}
</UserRightsCheck>
</CheckRightsByHanke>
</FeatureFlags>
<FeatureFlags flags={['hanke', 'accessRights']}>
<Button
Expand All @@ -251,22 +254,22 @@ const HankeView: React.FC<Props> = ({
</Button>
</FeatureFlags>
<FeatureFlags flags={['hanke']}>
<UserRightsCheck requiredRight="DELETE" hankeTunnus={hankeData.hankeTunnus}>
<CheckRightsByHanke requiredRight="DELETE" hankeTunnus={hankeData.hankeTunnus}>
<Button variant="primary" iconLeft={<IconCross aria-hidden="true" />} theme="black">
{t('hankeList:buttons:endHanke')}
</Button>
</UserRightsCheck>
</CheckRightsByHanke>
</FeatureFlags>
{!isLoading && isCancelPossible && (
<UserRightsCheck requiredRight="DELETE" hankeTunnus={hankeData.hankeTunnus}>
<CheckRightsByHanke requiredRight="DELETE" hankeTunnus={hankeData.hankeTunnus}>
<Button
onClick={onCancelHanke}
variant="danger"
iconLeft={<IconTrash aria-hidden="true" />}
>
{t('hankeForm:cancelButton')}
</Button>
</UserRightsCheck>
</CheckRightsByHanke>
)}
</InformationViewHeaderButtons>
</InformationViewHeader>
Expand Down
4 changes: 2 additions & 2 deletions src/domain/hanke/hankeView/HankeViewContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { ROUTES } from '../../../common/types/route';
import HankeDelete from '../edit/components/HankeDelete';
import useHanke from '../hooks/useHanke';
import HankeView from './HankeView';
import useUserRightsForHanke from '../hankeUsers/hooks/useUserRightsForHanke';
import { usePermissionsForHanke } from '../hankeUsers/hooks/useUserRightsForHanke';

type Props = {
hankeTunnus?: string;
};

const HankeViewContainer: React.FC<Props> = ({ hankeTunnus }) => {
const { data: hankeData } = useHanke(hankeTunnus);
const { data: signedInUser } = useUserRightsForHanke(hankeTunnus);
const { data: signedInUser } = usePermissionsForHanke(hankeTunnus);
const getEditHankePath = useLinkPath(ROUTES.EDIT_HANKE);
const getEditRightsPath = useLinkPath(ROUTES.ACCESS_RIGHTS);
const navigate = useNavigate();
Expand Down
28 changes: 21 additions & 7 deletions src/domain/hanke/portfolio/HankePortfolio.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import HankePortfolioComponent from './HankePortfolioComponent';
import { render, screen, waitFor } from '../../../testUtils/render';
import hankeList from '../../mocks/hankeList';
import { changeFilterDate } from '../../../testUtils/helperFunctions';
import { USER_VIEW, signedInUserByHanke } from '../../mocks/signedInUser';
import { SignedInUserByHanke } from '../hankeUsers/hankeUser';

const startDateLabel = 'Ajanjakson alku';
const endDateLabel = 'Ajanjakson loppu';
Expand All @@ -16,7 +18,7 @@ jest.setTimeout(30000);

describe.only('HankePortfolio', () => {
test('Changing search text filters correct number of projects', async () => {
const { user } = render(<HankePortfolioComponent hankkeet={hankeList} />);
const { user } = render(<HankePortfolioComponent hankkeet={hankeList} permissions={{}} />);

await user.type(screen.getByLabelText('Haku'), 'Mannerheimintie autottomaksi');
await waitFor(() => {
Expand All @@ -41,7 +43,9 @@ describe.only('HankePortfolio', () => {
});

test('Changing filter startDates filters correct number of projects', async () => {
const renderedComponent = render(<HankePortfolioComponent hankkeet={hankeList} />);
const renderedComponent = render(
<HankePortfolioComponent hankkeet={hankeList} permissions={{}} />,
);
expect(renderedComponent.getByTestId('numberOfFilteredRows')).toHaveTextContent('2');
changeFilterDate(startDateLabel, renderedComponent, '02.10.2022');
expect(renderedComponent.getByTestId('numberOfFilteredRows')).toHaveTextContent('2');
Expand All @@ -56,7 +60,9 @@ describe.only('HankePortfolio', () => {
});

test('Changing filter endDates filters correct number of projects', async () => {
const renderedComponent = render(<HankePortfolioComponent hankkeet={hankeList} />);
const renderedComponent = render(
<HankePortfolioComponent hankkeet={hankeList} permissions={{}} />,
);
expect(renderedComponent.getByTestId('numberOfFilteredRows')).toHaveTextContent('2');
changeFilterDate(endDateLabel, renderedComponent, '01.10.2022');
expect(renderedComponent.getByTestId('numberOfFilteredRows')).toHaveTextContent('0');
Expand All @@ -72,7 +78,9 @@ describe.only('HankePortfolio', () => {
});

test('Changing Hanke type filters correct number of projects', async () => {
const renderedComponent = render(<HankePortfolioComponent hankkeet={hankeList} />);
const renderedComponent = render(
<HankePortfolioComponent hankkeet={hankeList} permissions={{}} />,
);
expect(renderedComponent.getByTestId('numberOfFilteredRows')).toHaveTextContent('2');
await renderedComponent.user.click(
renderedComponent.getByRole('button', { name: 'Työn tyyppi' }),
Expand All @@ -98,21 +106,27 @@ describe.only('HankePortfolio', () => {
});

test('Having no projects renders correct text', () => {
render(<HankePortfolioComponent hankkeet={[]} />);
render(<HankePortfolioComponent hankkeet={[]} permissions={{}} />);

expect(screen.queryByText('Hankesalkussasi ei ole hankkeita')).toBeInTheDocument();
});

test('Should render edit hanke links for hankkeet that user has edit rights', async () => {
render(<HankePortfolioComponent hankkeet={hankeList} />);
const hankeTunnusList = hankeList.map((hanke) => hanke.hankeTunnus);
const signedUserData: SignedInUserByHanke = {
...signedInUserByHanke(hankeTunnusList),
[hankeTunnusList[0]]: USER_VIEW,
};

render(<HankePortfolioComponent hankkeet={hankeList} permissions={signedUserData} />);

await waitFor(() => {
expect(screen.queryAllByTestId('hankeEditLink')).toHaveLength(1);
});
});

test('Should show draft state notification for hankkeet that are in draft state', async () => {
render(<HankePortfolioComponent hankkeet={hankeList} />);
render(<HankePortfolioComponent hankkeet={hankeList} permissions={{}} />);

expect(
screen.getAllByText(
Expand Down
Loading

0 comments on commit eab0156

Please sign in to comment.