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

[TM-1531] add job connection #745

Open
wants to merge 25 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
aa5b7e6
[TM-1531] add job service url and script
egrojMonroy Dec 11, 2024
153050a
[TM-1531] apiSlice add job resource
egrojMonroy Dec 12, 2024
fa4d2c4
[TM-1531] updates for job service
egrojMonroy Dec 12, 2024
9fbece2
[TM-1531] sort alphabetical
egrojMonroy Dec 12, 2024
8303877
[TM-1531] add connection for list the delayed jobs
egrojMonroy Dec 13, 2024
06783b3
[TM-1531] remove unnecesary code and consistency in selector and API …
egrojMonroy Dec 16, 2024
4972958
[TM-1531] Merge branch 'staging' into feat/TM-1531-add-job-connection
egrojMonroy Dec 16, 2024
e8cef37
[TM-1531] add bulk update call on clear
egrojMonroy Dec 17, 2024
ded0b85
[TM-1531] add entityname and display data in notification
egrojMonroy Dec 17, 2024
c8d7c71
[TM-1531] use store and display only not acknowledged
egrojMonroy Dec 17, 2024
07e2d56
[TM-1531] remove from apifetcher call and add continous call for dela…
egrojMonroy Dec 18, 2024
9d45753
[TM-1531] Merge branch 'staging' into feat/TM-1531-add-job-service
egrojMonroy Dec 18, 2024
05fdae6
[TM-1531] Merge branch 'feat/TM-1531-add-job-service' into feat/TM-15…
egrojMonroy Dec 18, 2024
4637c98
[TM-1531] rollback apifetcher functionality
egrojMonroy Dec 18, 2024
e58c0a1
[TM-1531] adding message to notification
egrojMonroy Dec 19, 2024
996d5a7
[TM-1531] display error messages in notifications
egrojMonroy Dec 19, 2024
b6e0102
[TM-1531] add error messages for failed jobs
egrojMonroy Dec 19, 2024
fc1a52a
[TM-1531] improve connection and remove awaits and asyncs
egrojMonroy Dec 19, 2024
69d08d7
[TM-1531] changes and remove log
egrojMonroy Dec 19, 2024
d5ba353
[TM-1531] Merge branch 'release/utltimate-ulmus' into feat/TM-1531-ad…
egrojMonroy Dec 19, 2024
042ecbc
[TM-1531] yarn generate:services
egrojMonroy Dec 19, 2024
06a7958
[TM-1531] divide names loading for connections
egrojMonroy Dec 20, 2024
6cad9a9
[TM-1531] running with build
egrojMonroy Dec 20, 2024
af9b8da
[TM-1531] only send not pending
egrojMonroy Dec 20, 2024
1b73259
[TM-1531] fix loaded delayedjobs and filter
egrojMonroy Dec 20, 2024
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
19 changes: 2 additions & 17 deletions src/admin/modules/sites/components/SiteShow.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { FC, useState } from "react";
import { FC } from "react";
import { Show, TabbedShowLayout } from "react-admin";

import ShowActions from "@/admin/components/Actions/ShowActions";
import DelayedJobsProgressAlert from "@/admin/components/Alerts/DelayedJobsProgressAlert";
import AuditLogTab from "@/admin/components/ResourceTabs/AuditLogTab/AuditLogTab";
import { AuditLogButtonStates } from "@/admin/components/ResourceTabs/AuditLogTab/constants/enum";
import ChangeRequestsTab from "@/admin/components/ResourceTabs/ChangeRequestsTab/ChangeRequestsTab";
Expand All @@ -16,9 +15,6 @@ import { RecordFrameworkProvider } from "@/context/framework.provider";
import { MapAreaProvider } from "@/context/mapArea.provider";

const SiteShow: FC = () => {
const [isLoadingDelayedJob, setIsLoadingDelayedJob] = useState(false);
const [alertTitle, setAlertTitle] = useState("");

return (
<Show
title={<ShowTitle moduleName="Site" getTitle={record => record?.name} />}
Expand All @@ -30,13 +26,7 @@ const SiteShow: FC = () => {
<InformationTab type="sites" />
<TabbedShowLayout.Tab label="Polygon Review">
<MapAreaProvider>
<PolygonReviewTab
label=""
type={"sites"}
setIsLoadingDelayedJob={setIsLoadingDelayedJob!}
isLoadingDelayedJob={isLoadingDelayedJob!}
setAlertTitle={setAlertTitle!}
/>
<PolygonReviewTab label="" type={"sites"} />
</MapAreaProvider>
</TabbedShowLayout.Tab>
<GalleryTab label="Site Gallery" entity="sites" />
Expand All @@ -46,11 +36,6 @@ const SiteShow: FC = () => {
<AuditLogTab entity={AuditLogButtonStates.SITE} />
</TabbedShowLayout>
</RecordFrameworkProvider>
<DelayedJobsProgressAlert
show={isLoadingDelayedJob}
title={alertTitle}
setIsLoadingDelayedJob={setIsLoadingDelayedJob!}
/>
</Show>
);
};
Expand Down
42 changes: 32 additions & 10 deletions src/components/elements/Notification/FloatNotification.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LinearProgress } from "@mui/material";
import classNames from "classnames";
import { useEffect, useState } from "react";
import { When } from "react-if";
Expand Down Expand Up @@ -41,6 +42,11 @@ const FloatNotification = () => {
const notAcknowledgedJobs = delayedJobs.filter((job: DelayedJobDto) => !job.isAcknowledged);
setNotAcknowledgedJobs(notAcknowledgedJobs);
}, [delayedJobs]);
useEffect(() => {
if (!notAcknowledgedJobs.length) {
setOpenModalNotification(false);
}
}, [notAcknowledgedJobs]);
return (
<div className="fixed bottom-10 right-10 z-50">
<div className="relative">
Expand Down Expand Up @@ -78,14 +84,30 @@ const FloatNotification = () => {
Site: <b>{item.entityName}</b>
</Text>
<div className="mt-2 flex items-center gap-2">
<LinearProgressBar
value={((item.processedContent ?? 0) / (item.totalContent ?? 1)) * 100}
className="h-2 bg-success-40"
color="success-600"
/>
<Text variant="text-12-semibold" className="text-black">
{(item.processedContent ?? 0) / (item.totalContent ?? 1)}%
</Text>
{item.name === "Polygon Upload" &&
(item.processedContent === null || item.totalContent === null) &&
item.status === "pending" ? (
<div style={{ width: "100%" }}>
<LinearProgress className="h-2 rounded-full" />
</div>
) : (
<LinearProgressBar
value={
item.status === "succeeded"
? 100
: ((item.processedContent ?? 0) / (item.totalContent ?? 1)) * 100
}
className="h-2 bg-success-40"
color="success-600"
/>
)}
{item.name !== "Polygon Upload" && (
<Text variant="text-12-semibold" className="text-black">
{item.status === "succeeded"
? "100%"
: `${Math.round(((item.processedContent ?? 0) / (item.totalContent ?? 1)) * 100)}%`}
</Text>
)}
</div>
</div>
))}
Expand All @@ -104,8 +126,8 @@ const FloatNotification = () => {
className={classNames(
"z-10 flex h-15 w-15 items-center justify-center rounded-full border border-grey-950 bg-primary duration-300 hover:scale-105",
{
hidden: (notAcknowledgedJobs?.length ?? 0) < 1 && isLoaded,
visible: (notAcknowledgedJobs?.length ?? 0) > 0 && isLoaded
hidden: (notAcknowledgedJobs?.length ?? 0) === 0,
visible: (notAcknowledgedJobs?.length ?? 0) > 0
}
)}
>
Expand Down
18 changes: 17 additions & 1 deletion src/connections/DelayedJob.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useEffect } from "react";
import { createSelector } from "reselect";

import { bulkUpdateJobs, listDelayedJobs } from "@/generated/v3/jobService/jobServiceComponents";
Expand Down Expand Up @@ -49,6 +50,22 @@ const delayedJobsConnection: Connection<DelayedJobsConnection> = {
)
};

export const useDelayedJobs = () => {
const connection = connectionHook(delayedJobsConnection)();
roguenet marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
const intervalId = setInterval(() => {
listDelayedJobs();
}, 1500);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This will cause this interval to keep refreshing the delayed jobs for as long as the surrounding component is mounted. Is that intentional? It seems like clearing the interval when all the delayed jobs have completed would be good to keep from continuing to hit the jobs endpoint. I'm OK with letting this live to prod if that's complicated to wire up; it's just not ideal and should be cleaned up in a future ticket.

Please either decide to implement an eventual stop like that in this PR, or see that a tech debt ticket is created to clean up in a future release.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes indeed, it is supposed to be attentive for any job created on any session.
Nonetheless I think it might not be good.
So for now the debt ticket will be needed, because we would have to think on the user cases.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Is the component using this hook expected to be mounted a lot of the time, or does the user have to go to a specific view for it? If this is up all the time, then I think we need to consider a better approach. If it's a view that's only mounted when the user is actively watching for something to finish, then I think this OK for now.


return () => {
clearInterval(intervalId);
};
roguenet marked this conversation as resolved.
Show resolved Hide resolved
}, []);

return connection;
};

export const triggerBulkUpdate = (jobs: DelayedJobData[]) => {
return bulkUpdateJobs({ body: { data: jobs } });
roguenet marked this conversation as resolved.
Show resolved Hide resolved
};
Expand Down Expand Up @@ -83,5 +100,4 @@ const connectionBulkUpdateIsLoaded = ({ isLoading, hasLoadFailed }: { isLoading:
return !isLoading && !hasLoadFailed;
};
roguenet marked this conversation as resolved.
Show resolved Hide resolved

export const useDelayedJobs = connectionHook(delayedJobsConnection);
export const useBulkUpdateJobs = connectionLoader(bulkUpdateJobsConnection);
25 changes: 7 additions & 18 deletions src/generated/apiFetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,32 +180,21 @@ async function loadJob(signal: AbortSignal | undefined, delayedJobId: string, re
throw error;
}
}

async function processDelayedJob<TData>(signal: AbortSignal | undefined, delayedJobId: string): Promise<TData> {
const headers: HeadersInit = { "Content-Type": "application/json" };
const accessToken = typeof window !== "undefined" && getAccessToken();
if (accessToken != null) headers.Authorization = `Bearer ${accessToken}`;
const jobResult = await loadJob(signal, delayedJobId);

let jobResult;
for (
jobResult = await loadJob(signal, delayedJobId);
jobResult.data?.attributes?.status === "pending";
jobResult = await loadJob(signal, delayedJobId)
) {
//@ts-ignore
const { total_content, processed_content, progress_message } = jobResult.data?.attributes;
if (total_content != null) {
ApiSlice.addTotalContent(total_content);
ApiSlice.addProgressContent(processed_content);
ApiSlice.addProgressMessage(progress_message);
}
const { status, statusCode, payload } = jobResult.data!.attributes;

if (signal?.aborted || ApiSlice.apiDataStore.abort_delayed_job) throw new Error("Aborted");
await new Promise(resolve => setTimeout(resolve, JOB_POLL_TIMEOUT));
if (status === "failed") {
throw { statusCode, ...payload };
}

const { status, statusCode, payload } = jobResult.data!.attributes;
if (status === "failed") throw { statusCode, ...payload };
if (status === "pending") {
throw new Error("Job is still pending. Handle pending jobs appropriately.");
}
roguenet marked this conversation as resolved.
Show resolved Hide resolved

return payload as TData;
}
Loading