Skip to content

Commit

Permalink
Edit the IUCN Classification section in the Project View page (#173)
Browse files Browse the repository at this point in the history
* wip

* editing of iucn

* fix
  • Loading branch information
sdevalapurkar authored Mar 26, 2021
1 parent 023768c commit 76371a0
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 40 deletions.
23 changes: 21 additions & 2 deletions api/src/models/project-update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,25 @@ import { getLogger } from '../utils/logger';

const defaultLog = getLogger('models/project-update');

export class PutIUCNData {
classificationDetails: IGetPutIUCN[];

constructor(obj?: any) {
defaultLog.debug({ label: 'PutIUCNData', message: 'params', obj });

this.classificationDetails =
(obj?.classificationDetails?.length &&
obj.classificationDetails.map((item: any) => {
return {
classification: item.classification,
subClassification1: item.subClassification1,
subClassification2: item.subClassification2
};
})) ||
[];
}
}

export class PutProjectData {
name: string;
type: number;
Expand Down Expand Up @@ -114,7 +133,7 @@ export class GetPartnershipsData {
}
}

interface IGetIUCN {
interface IGetPutIUCN {
classification: number;
subClassification1: number;
subClassification2: number;
Expand All @@ -127,7 +146,7 @@ interface IGetIUCN {
* @class GetIUCNClassificationData
*/
export class GetIUCNClassificationData {
classificationDetails: IGetIUCN[];
classificationDetails: IGetPutIUCN[];

constructor(iucnClassificationData?: any[]) {
defaultLog.debug({
Expand Down
43 changes: 42 additions & 1 deletion api/src/paths/project/{projectId}/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
PutCoordinatorData,
PutLocationData,
PutObjectivesData,
PutProjectData
PutProjectData,
PutIUCNData
} from '../../../models/project-update';
import { GetSpeciesData } from '../../../models/project-view-update';
import {
Expand All @@ -24,13 +25,15 @@ import {
getIndigenousPartnershipsByProjectSQL,
getIUCNActionClassificationByProjectSQL
} from '../../../queries/project/project-update-queries';
import { deleteIUCNSQL } from '../../../queries/project/project-delete-queries';
import {
getStakeholderPartnershipsByProjectSQL,
getFocalSpeciesByProjectSQL,
getAncillarySpeciesByProjectSQL
} from '../../../queries/project/project-view-update-queries';
import { getLogger } from '../../../utils/logger';
import { logRequest } from '../../../utils/path-utils';
import { postProjectIUCNSQL } from '../../../queries/project/project-create-queries';

const defaultLog = getLogger('paths/project/{projectId}');

Expand Down Expand Up @@ -386,6 +389,10 @@ function updateProject(): RequestHandler {
promises.push(updateProjectData(projectId, entities, connection));
}

if (entities?.iucn) {
promises.push(updateProjectIUCNData(projectId, entities, connection));
}

await Promise.all(promises);

await connection.commit();
Expand All @@ -400,6 +407,40 @@ function updateProject(): RequestHandler {
};
}

export const updateProjectIUCNData = async (
projectId: number,
entities: IUpdateProject,
connection: IDBConnection
): Promise<void> => {
const putIUCNData = (entities?.iucn && new PutIUCNData(entities.iucn)) || null;

const sqlDeleteStatement = deleteIUCNSQL(projectId);

if (!sqlDeleteStatement) {
throw new HTTP400('Failed to build SQL statement');
}

const deleteResult = await connection.query(sqlDeleteStatement.text, sqlDeleteStatement.values);

if (!deleteResult || !deleteResult.rowCount) {
throw new HTTP409('Failed to delete project IUCN data');
}

putIUCNData?.classificationDetails.forEach(async (iucnClassification) => {
const sqlInsertStatement = postProjectIUCNSQL(iucnClassification.subClassification2, projectId);

if (!sqlInsertStatement) {
throw new HTTP400('Failed to build SQL statement');
}

const insertResult = await connection.query(sqlInsertStatement.text, sqlInsertStatement.values);

if (!insertResult || !insertResult.rowCount) {
throw new HTTP409('Failed to insert project IUCN data');
}
});
};

export const updateProjectData = async (
projectId: number,
entities: IUpdateProject,
Expand Down
17 changes: 17 additions & 0 deletions api/src/queries/project/project-delete-queries.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { expect } from 'chai';
import { describe } from 'mocha';
import { deleteIUCNSQL } from './project-delete-queries';

describe('deleteIUCNSQL', () => {
it('returns null response when null projectId provided', () => {
const response = deleteIUCNSQL((null as unknown) as number);

expect(response).to.be.null;
});

it('returns non null response when valid projectId provided', () => {
const response = deleteIUCNSQL(1);

expect(response).to.not.be.null;
});
});
40 changes: 40 additions & 0 deletions api/src/queries/project/project-delete-queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { SQL, SQLStatement } from 'sql-template-strings';
import { getLogger } from '../../utils/logger';

const defaultLog = getLogger('queries/project/project-delete-queries');

/**
* SQL query to delete project IUCN rows.
*
* @param {projectId} projectId
* @returns {SQLStatement} sql query object
*/
export const deleteIUCNSQL = (projectId: number): SQLStatement | null => {
defaultLog.debug({
label: 'deleteIUCNSQL',
message: 'params',
projectId
});

if (!projectId) {
return null;
}

const sqlStatement: SQLStatement = SQL`
DELETE
from project_iucn_action_classification
WHERE
p_id = ${projectId}
RETURNING
*;
`;

defaultLog.debug({
label: 'deleteProjectSQL',
message: 'sql',
'sqlStatement.text': sqlStatement.text,
'sqlStatement.values': sqlStatement.values
});

return sqlStatement;
};
13 changes: 6 additions & 7 deletions app/src/features/projects/components/ProjectIUCNForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ const useStyles = makeStyles((theme) => ({
overflowX: 'hidden'
},
iucnInput: {
flex: '1 0 200px',
width: '33.333%',
maxWidth: '200px'
flex: '0 0 auto',
width: '33.333%'
}
}));

Expand Down Expand Up @@ -98,8 +97,8 @@ const ProjectIUCNForm: React.FC<IProjectIUCNFormProps> = (props) => {
return (
<Grid item xs={12} key={index}>
<Box display="flex" alignItems="center" mt={-2}>
<Box display="flex" className={classes.iucnInputContainer} flexWrap="wrap">
<Box className={classes.iucnInput} my={2} mr={2}>
<Box display="flex" className={classes.iucnInputContainer}>
<Box className={classes.iucnInput} my={2} pr={2}>
<FormControl variant="outlined" fullWidth>
<InputLabel id="classification">Classification</InputLabel>
<Select
Expand All @@ -124,7 +123,7 @@ const ProjectIUCNForm: React.FC<IProjectIUCNFormProps> = (props) => {
<FormHelperText>{classificationMeta.error}</FormHelperText>
</FormControl>
</Box>
<Box className={classes.iucnInput} my={2} mr={2}>
<Box className={classes.iucnInput} my={2} pr={2}>
<FormControl variant="outlined" fullWidth>
<InputLabel id="subClassification1">Sub-classification</InputLabel>
<Select
Expand Down Expand Up @@ -152,7 +151,7 @@ const ProjectIUCNForm: React.FC<IProjectIUCNFormProps> = (props) => {
<FormHelperText>{subClassification1Meta.error}</FormHelperText>
</FormControl>
</Box>
<Box my={2} mr={2} className={classes.iucnInput}>
<Box my={2} pr={2} className={classes.iucnInput}>
<FormControl variant="outlined" fullWidth>
<InputLabel id="subClassification2">Sub-classification</InputLabel>
<Select
Expand Down
2 changes: 1 addition & 1 deletion app/src/features/projects/view/ProjectDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('ProjectDetails', () => {

const { asFragment } = render(
<Router history={history}>
<ProjectDetails projectForViewData={getProjectForViewResponse} codes={codes} />
<ProjectDetails projectForViewData={getProjectForViewResponse} codes={codes} refresh={jest.fn()} />
</Router>
);

Expand Down
2 changes: 1 addition & 1 deletion app/src/features/projects/view/ProjectDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const ProjectDetails: React.FC<IProjectDetailsProps> = (props) => {
<Box mb={4}>
<Paper>
<Box m={3}>
<IUCNClassification projectForViewData={projectForViewData} codes={codes} />
<IUCNClassification projectForViewData={projectForViewData} codes={codes} refresh={props.refresh} />
</Box>
</Paper>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,43 @@ import { render, cleanup, waitFor, fireEvent } from '@testing-library/react';
import { getProjectForViewResponse } from 'test-helpers/project-helpers';
import React from 'react';
import IUCNClassification from './IUCNClassification';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import { codes } from 'test-helpers/code-helpers';
import { useBiohubApi } from 'hooks/useBioHubApi';

const history = createMemoryHistory();
import { UPDATE_GET_ENTITIES } from 'interfaces/useProjectApi.interface';

jest.mock('../../../../hooks/useBioHubApi');
const mockUseBiohubApi = {
project: {
getProjectForUpdate: jest.fn<Promise<object>, []>()
getProjectForUpdate: jest.fn<Promise<object>, []>(),
updateProject: jest.fn()
}
};

const mockBiohubApi = ((useBiohubApi as unknown) as jest.Mock<typeof mockUseBiohubApi>).mockReturnValue(
mockUseBiohubApi
);

const mockRefresh = jest.fn();

const renderContainer = () => {
return render(
<IUCNClassification projectForViewData={getProjectForViewResponse} codes={codes} refresh={mockRefresh} />
);
};

describe('IUCNClassification', () => {
beforeEach(() => {
// clear mocks before each test
mockBiohubApi().project.getProjectForUpdate.mockClear();
mockBiohubApi().project.updateProject.mockClear();
});

afterEach(() => {
cleanup();
});

it('renders correctly', () => {
const { asFragment } = render(
<Router history={history}>
<IUCNClassification projectForViewData={getProjectForViewResponse} codes={codes} />
</Router>
);
const { asFragment } = renderContainer();

expect(asFragment()).toMatchSnapshot();
});
Expand All @@ -53,18 +56,20 @@ describe('IUCNClassification', () => {
}
});

const { getByText } = render(
<Router history={history}>
<IUCNClassification projectForViewData={getProjectForViewResponse} codes={codes} />
</Router>
);
const { getByText } = renderContainer();

await waitFor(() => {
expect(getByText('IUCN Classification')).toBeVisible();
});

fireEvent.click(getByText('EDIT'));

await waitFor(() => {
expect(mockBiohubApi().project.getProjectForUpdate).toBeCalledWith(getProjectForViewResponse.id, [
UPDATE_GET_ENTITIES.iucn
]);
});

await waitFor(() => {
expect(getByText('Edit IUCN Classification')).toBeVisible();
});
Expand All @@ -84,7 +89,20 @@ describe('IUCNClassification', () => {
fireEvent.click(getByText('Save Changes'));

await waitFor(() => {
expect(history.location.pathname).toEqual(`/projects/${getProjectForViewResponse.id}/details`);
expect(mockBiohubApi().project.updateProject).toHaveBeenCalledTimes(1);
expect(mockBiohubApi().project.updateProject).toBeCalledWith(getProjectForViewResponse.id, {
iucn: {
classificationDetails: [
{
classification: 1,
subClassification1: 1,
subClassification2: 1
}
]
}
});

expect(mockRefresh).toBeCalledTimes(1);
});
});

Expand All @@ -93,11 +111,7 @@ describe('IUCNClassification', () => {
iucn: null
});

const { getByText } = render(
<Router history={history}>
<IUCNClassification projectForViewData={getProjectForViewResponse} codes={codes} />
</Router>
);
const { getByText } = renderContainer();

await waitFor(() => {
expect(getByText('IUCN Classification')).toBeVisible();
Expand Down
Loading

0 comments on commit 76371a0

Please sign in to comment.