Skip to content

Commit

Permalink
[MDS-6118] FE extract permits - DRAFT (#3236)
Browse files Browse the repository at this point in the history
* make a button on the FE call the BE. Write reducer for permit service covering basic functionality

* stub out extraction method on core-api, tweak logic on view permit page

* adjust FE to better match anticipated BE. Get rid of key error on overview

* migration to store task id in a table. Stub in BE but I am getting NOT FOUND so namespace config must be wrong

* stub in BE functions

* MDS-6118 Added working permit condition flow

* MDS-6118 Fixed tasks + added tests

* MDS-6118 Fixed tests

* Mock celery task

* fix some front-end issues

* tidy up files

* update snaps

---------

Co-authored-by: Tara Epp <102187683+taraepp@users.noreply.github.com>
  • Loading branch information
simensma-fresh and taraepp authored Sep 18, 2024
1 parent 3de545f commit 4ed5561
Show file tree
Hide file tree
Showing 36 changed files with 1,758 additions and 174 deletions.
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ generate_history_table_migration:
@echo "+\n++ Generating history table migration ...\n+"
@docker compose $(DC_FILE) exec backend bash -c "flask generate_history_table_migration $(TABLE)"


# Generates a migration file for the specified table
# Usage: make generate_migration TABLE=<table_name>
generate_table_migration:
@echo "+\n++ Generating history table migration ...\n+"
@docker compose $(DC_FILE) exec backend bash -c "flask generate_table_migration $(TABLE)"

# initial project setup for local/codespaces development
init:
@./bin/setup_codespaces.sh
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- This file was generated by the generate_table_ddl command
-- The file contains the corresponding history table definition for the permit_extraction_task table
CREATE TABLE permit_extraction_task (
create_user VARCHAR(60) NOT NULL,
create_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL,
update_user VARCHAR(60) NOT NULL,
update_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL,
permit_extraction_task_id UUID NOT NULL,
task_id VARCHAR(255) NOT NULL,
task_status VARCHAR(255) NOT NULL,
task_meta JSON,
task_result JSON,
core_status_task_id VARCHAR(255),
permit_amendment_guid UUID NOT NULL,
permit_amendment_document_guid UUID NOT NULL,
PRIMARY KEY (permit_extraction_task_id),
FOREIGN KEY(permit_amendment_guid) REFERENCES permit_amendment (permit_amendment_guid),
FOREIGN KEY(permit_amendment_document_guid) REFERENCES permit_amendment_document (permit_amendment_document_guid)
);
CREATE INDEX IF NOT EXISTS permit_extraction_task_id_idx ON permit_extraction_task (task_id);
CREATE INDEX IF NOT EXISTS permit_extraction_amend_guid_idx ON permit_extraction_task (permit_amendment_guid);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE permit_conditions ADD COLUMN step VARCHAR(50);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE permit_conditions_version ADD COLUMN step VARCHAR(50);
1 change: 1 addition & 0 deletions services/common/src/components/common/ActionMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const generateActionMenuItems = (actionItems: ITableAction[], record) =>
label: (
<button
type="button"
disabled={action.disabled}
className={`full actions-dropdown-button`}
data-testid={`action-button-${action.key}`}
onClick={(event) => action.clickFunction(event, record)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export interface ITableAction {
label: string;
clickFunction: (event, record) => any;
icon?: ReactNode;
disabled?: boolean;
}

export const renderActionsColumn = ({
Expand Down
4 changes: 4 additions & 0 deletions services/common/src/constants/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ export const STANDARD_PERMIT_CONDITIONS = (noticeOfWorkType) =>
export const STANDARD_PERMIT_CONDITION = (permitConditionGuid) =>
`/mines/permits/standard-conditions/${permitConditionGuid}`;

export const PERMIT_SERVICE_EXTRACTION = `/mines/permits/condition-extraction`;
export const POLL_PERMIT_SERVICE_EXTRACTION = (taskId: string) =>
`/mines/permits/condition-extraction/${taskId}`;

// Permits - Notices of Departure
export const NOTICES_OF_DEPARTURE = () => `/notices-of-departure`;
export const NOTICE_OF_DEPARTURE = (noticeOfDepartureGuid) =>
Expand Down
2 changes: 2 additions & 0 deletions services/common/src/redux/reducers/rootReducerShared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import verifiableCredentialsReducer from "@mds/common/redux/slices/verifiableCre
import regionsReducer from "@mds/common/redux/slices/regionsSlice";
import complianceCodeReducer, { complianceCodeReducerType } from "../slices/complianceCodesSlice";
import spatialDataReducer, { spatialDataReducerType } from "../slices/spatialDataSlice";
import permitServiceReducer, { permitServiceReducerType } from "../slices/permitServiceSlice";
export const sharedReducer = {
...activityReducer,
...authenticationReducer,
Expand Down Expand Up @@ -82,4 +83,5 @@ export const sharedReducer = {
regions: regionsReducer,
[spatialDataReducerType]: spatialDataReducer,
[complianceCodeReducerType]: complianceCodeReducer,
[permitServiceReducerType]: permitServiceReducer,
};
203 changes: 203 additions & 0 deletions services/common/src/redux/slices/permitServiceSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { createAppSlice, rejectHandler } from "@mds/common/redux/createAppSlice";
import { hideLoading, showLoading } from "react-redux-loading-bar";
import CustomAxios from "@mds/common/redux/customAxios";
import { ItemMap } from "@mds/common/interfaces";
import {
ENVIRONMENT,
PERMIT_SERVICE_EXTRACTION,
POLL_PERMIT_SERVICE_EXTRACTION,
} from "@mds/common/constants";
import { createSelector } from "@reduxjs/toolkit";

const createRequestHeader = REQUEST_HEADER.createRequestHeader;

export const permitServiceReducerType = "permitService";

export enum PermitExtractionStatus {
not_started = "Not Started",
in_progress = "In Progress",
complete = "Extraction Complete",
error = "Error Extracting",
}

const permitExtractionStatusMap = {
PENDING: PermitExtractionStatus.in_progress,
RECEIVED: PermitExtractionStatus.in_progress,
PROGRESS: PermitExtractionStatus.in_progress,
RETRY: PermitExtractionStatus.in_progress,
STARTED: PermitExtractionStatus.in_progress,
REVOKED: PermitExtractionStatus.error,
FAILURE: PermitExtractionStatus.error,
SUCCESS: PermitExtractionStatus.complete,
};

interface PermitExtraction {
task_status: PermitExtractionStatus;
task_id: string;
}

interface PermitServiceState {
// object of: permit_amendment_id: {status: x, task_id: y}
extractions: ItemMap<PermitExtraction>;
}

const initialState: PermitServiceState = {
extractions: {},
};

const permitServiceSlice = createAppSlice({
name: permitServiceReducerType,
initialState,
reducers: (create) => ({
initiatePermitExtraction: create.asyncThunk(
async (
payload: { permit_amendment_id: number; permit_amendment_document_guid: string },
thunkAPI
) => {
const headers = createRequestHeader();
thunkAPI.dispatch(showLoading());

const response = await CustomAxios({
errorToastMessage: "default",
}).post(`${ENVIRONMENT.apiUrl}${PERMIT_SERVICE_EXTRACTION}`, payload, headers);
thunkAPI.dispatch(hideLoading());

return response.data;
},
{
fulfilled: (state, action) => {
const { permit_amendment_id } = action.meta.arg;
const { task_id, task_status } = action.payload;
state.extractions[permit_amendment_id] = {
task_id,
task_status: permitExtractionStatusMap[task_status],
};
},
pending: (state, action) => {
const { permit_amendment_id } = action.meta.arg;
state.extractions[permit_amendment_id] = {
task_status: PermitExtractionStatus.in_progress,
task_id: null,
};
},
rejected: (state, action) => {
const { permit_amendment_id } = action.meta.arg;
state.extractions[permit_amendment_id] = {
task_status: PermitExtractionStatus.error,
task_id: null,
};
rejectHandler(action);
},
}
),
fetchPermitExtractionTasks: create.asyncThunk(
async (payload: { permit_amendment_id: number }, thunkAPI) => {
const { permit_amendment_id } = payload;
const headers = createRequestHeader();
thunkAPI.dispatch(showLoading());

const response = await CustomAxios({
errorToastMessage: "default",
}).get(
`${ENVIRONMENT.apiUrl}${PERMIT_SERVICE_EXTRACTION}?permit_amendment_id=${permit_amendment_id}`,
headers
);

thunkAPI.dispatch(hideLoading());
return response.data.tasks[0];
},
{
fulfilled: (state, action) => {
if (!action.payload) return;
const { permit_amendment_id } = action.meta.arg;
const { task_id, task_status } = action.payload;
state.extractions[permit_amendment_id] = {
task_id: task_id,
task_status: permitExtractionStatusMap[task_status],
};
},
rejected: (state, action) => {
rejectHandler(action);
},
}
),

fetchPermitExtractionStatus: create.asyncThunk(
async (payload: { permit_amendment_id: number; task_id: string }, thunkAPI) => {
const { task_id } = payload;

const headers = createRequestHeader();
thunkAPI.dispatch(showLoading());

const response = await CustomAxios({
errorToastMessage: "default",
}).get(`${ENVIRONMENT.apiUrl}${POLL_PERMIT_SERVICE_EXTRACTION(task_id)}`, headers);

thunkAPI.dispatch(hideLoading());
return response.data;
},
{
fulfilled: (state, action) => {
const { permit_amendment_id } = action.meta.arg;
const { task_id, task_status } = action.payload;
state.extractions[permit_amendment_id] = {
task_id: task_id,
task_status: permitExtractionStatusMap[task_status],
};
},
rejected: (state, action) => {
rejectHandler(action);
},
}
),
deletePermitConditions: create.asyncThunk(
async (payload: { permit_amendment_id: number }, thunkAPI) => {
const headers = createRequestHeader();
thunkAPI.dispatch(showLoading());
const { permit_amendment_id } = payload;
const response = await CustomAxios({
errorToastMessage: "default",
}).delete(
`${ENVIRONMENT.apiUrl}${PERMIT_SERVICE_EXTRACTION}?permit_amendment_id=${permit_amendment_id}`,
headers
);

thunkAPI.dispatch(hideLoading());
return response.data;
},
{
fulfilled: (state, action) => {
const { permit_amendment_id } = action.meta.arg;
state.extractions[permit_amendment_id] = {
task_status: PermitExtractionStatus.not_started,
task_id: null,
};
},
rejected: (state, action) => {
rejectHandler(action);
},
}
),
}),
selectors: {
getPermitExtractionState: (state: PermitServiceState) => {
return state.extractions;
},
},
});

export const { getPermitExtractionState } = permitServiceSlice.selectors;
export const {
initiatePermitExtraction,
fetchPermitExtractionStatus,
fetchPermitExtractionTasks,
deletePermitConditions,
} = permitServiceSlice.actions;

export const getPermitExtractionByGuid = (permit_amendment_id: number) =>
createSelector([getPermitExtractionState], (extractions) => {
return extractions[permit_amendment_id];
});

const permitServiceReducer = permitServiceSlice.reducer;
export default permitServiceReducer;
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import uuid
from flask_restx import Resource, fields

from app.extensions import api, cache
from app.api.utils.access_decorators import requires_any_of, VIEW_ALL, MINESPACE_PROPONENT, GIS
from app.api.constants import DOWNLOAD_TOKEN, TIMEOUT_5_MINUTES
from app.api.services.document_manager_service import DocumentManagerService
from app.api.utils.access_decorators import (
GIS,
MINESPACE_PROPONENT,
VIEW_ALL,
requires_any_of,
)
from app.extensions import api
from flask_restx import Resource, fields

DOWNLOAD_TOKEN_MODEL = api.model('DownloadToken', {'token_guid': fields.String})

Expand All @@ -13,6 +17,4 @@ class DownloadTokenResource(Resource):
@api.marshal_with(DOWNLOAD_TOKEN_MODEL, code=200)
@requires_any_of([VIEW_ALL, MINESPACE_PROPONENT, GIS])
def get(self, document_guid):
token_guid = uuid.uuid4()
cache.set(DOWNLOAD_TOKEN(token_guid), document_guid, TIMEOUT_5_MINUTES)
return {'token_guid': token_guid}
return {'token_guid': DocumentManagerService.create_download_token(document_guid)}
Loading

0 comments on commit 4ed5561

Please sign in to comment.