From 64ec27c63450e6ffaee0bc19844f57a98889c1b8 Mon Sep 17 00:00:00 2001 From: Yurick Date: Sat, 25 Jan 2020 15:24:51 +0100 Subject: [PATCH] Update Queue.tsx to use top level types --- src/ui/components/Queue.tsx | 151 +++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 61 deletions(-) diff --git a/src/ui/components/Queue.tsx b/src/ui/components/Queue.tsx index a51f4c66..e98ddbea 100644 --- a/src/ui/components/Queue.tsx +++ b/src/ui/components/Queue.tsx @@ -1,3 +1,6 @@ +// REVIEW: I feel like this file has become quite chockfull of responsabilities. +// WDYT about splitting it? + import React, { useState } from 'react' import { getYear, @@ -7,10 +10,9 @@ import { formatDistanceStrict, } from 'date-fns' import Highlight from 'react-highlight' -import { Job } from 'bull' -import { Job as JobMq } from 'bullmq' -import { FIELDS, STATUSES } from './constants' +import { FIELDS, STATUSES, Status, Field } from './constants' +import { AppQueue, AppJob } from '../../@types/app' const today = new Date() @@ -26,10 +28,16 @@ const formatDate = (ts: TimeStamp) => { : format(ts, 'MM/dd/yyyy HH:mm:ss') } -const Timestamp = ({ ts, prev }: { ts?: TimeStamp; prev?: TimeStamp }) => { - if (ts === undefined) { - // REVIEW: what should be the timestamp for undefined? - // Currently we're passing `job.finishedOn` to this component, which might be undefined +const Timestamp = ({ + ts, + prev, +}: { + ts: TimeStamp | null + prev?: TimeStamp | null +}) => { + if (ts === null) { + // REVIEW: what should be the timestamp for null? + // Currently we're passing `job.finishedOn` to this component, which might be null return null } @@ -47,8 +55,14 @@ const Timestamp = ({ ts, prev }: { ts?: TimeStamp; prev?: TimeStamp }) => { ) } -// FIXME: typings -const MenuItem = ({ status, count, onClick, selected }: any) => ( +type MenuItemProps = { + status: Status + count: number + onClick: () => void + selected: boolean +} + +const MenuItem = ({ status, count, onClick, selected }: MenuItemProps) => (
( ) -const fieldComponents = { - id: ({ job }: { job: Job | JobMq }) => #{job.id}, +type FieldProps = { + job: AppJob + retryJob: () => Promise +} - timestamps: ({ job }: { job: Job | JobMq }) => ( +const fieldComponents: Record> = { + id: ({ job }) => #{job.id}, + + timestamps: ({ job }) => (
@@ -106,14 +125,11 @@ const fieldComponents = {
), - name: ({ job }: { job: Job | JobMq }) => - job.name === '__default__' ? '--' : job.name, + name: ({ job }) => <>{job.name === '__default__' ? '--' : job.name}, - finish: ({ job }: { job: Job | JobMq }) => ( - - ), + finish: ({ job }) => , - progress: ({ job }: { job: Job | JobMq }) => { + progress: ({ job }) => { switch (typeof job.progress) { case 'object': return ( @@ -140,19 +156,19 @@ const fieldComponents = {
) default: - return '--' + return <>-- } }, - // FIXME: typings - attempts: ({ job }: { job: any }) => job.attempts, + attempts: ({ job }) => <>{job.attempts}, - // FIXME: typings - delay: ({ job }: { job: any }) => - formatDistanceStrict(job.timestamp + job.delay, Date.now()), + delay: ({ job }) => ( + // REVIEW: What should we do if timestamp comes as null? + // Also, job.delay is a boolean, why are summing 1 or 0 to it? D: + <>{formatDistanceStrict((job.timestamp || 0) + +job.delay, Date.now())} + ), - // FIXME: typings - failedReason: ({ job }: any) => { + failedReason: ({ job }) => { return ( <> {job.failedReason || 'NA'} @@ -161,7 +177,7 @@ const fieldComponents = { ) }, - data: ({ job }: { job: Job | JobMq }) => { + data: ({ job }) => { const [showData, toggleData] = useState(false) return ( @@ -174,14 +190,11 @@ const fieldComponents = { ) }, - opts: ({ job }: { job: Job | JobMq }) => ( + opts: ({ job }) => ( {JSON.stringify(job.opts, null, 2)} ), - // FIXME: typings - retry: ({ retryJob }: { retryJob: () => void }) => ( - - ), + retry: ({ retryJob }) => , } const Jobs = ({ @@ -189,13 +202,9 @@ const Jobs = ({ queue: { jobs, name }, status, }: { - // FIXME: typings - retryJob: any - queue: { - jobs: any - name: any - } - status: string + retryJob: (job: AppJob) => () => Promise + queue: AppQueue + status: Status }) => { if (!jobs.length) { return <>No jobs with status {status} @@ -211,12 +220,9 @@ const Jobs = ({ - {jobs.map((job: Job | JobMq) => ( + {jobs.map(job => ( {FIELDS[status].map(field => { - // FIXME: typings - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore - // @ts-ignore const Field = fieldComponents[field] return ( @@ -232,7 +238,17 @@ const Jobs = ({ ) } -const actions = { +type Actionable = 'failed' | 'delayed' + +interface QueueActionProps { + queue: QueueProps['queue'] + retryAll: QueueProps['retryAll'] + cleanAllFailed: QueueProps['cleanAllFailed'] + cleanAllDelayed: QueueProps['cleanAllDelayed'] + status: Status +} + +const actions: Record> = { failed: ({ retryAll, cleanAllFailed }: any) => (
@@ -244,16 +260,17 @@ const actions = { ), } -// FIXME: typings -const QueueActions = (props: { - [key: string]: any - status: 'failed' | 'delayed' -}) => { - const Actions = - actions[props.status] || - (() => { - return null - }) +const isStatusActionable = (status: Status): status is Actionable => + status in actions + +const QueueActions = (props: QueueActionProps) => { + const { status } = props + + if (!isStatusActionable(status)) { + return
+ } + + const Actions = actions[status] return (
@@ -262,7 +279,22 @@ const QueueActions = (props: { ) } -// FIXME: typings +interface QueueProps { + queue: AppQueue + selectedStatus: Status + selectStatus: (statuses: Record) => void + cleanAllDelayed: () => Promise + cleanAllFailed: () => Promise + retryAll: () => Promise + retryJob: (job: AppJob) => () => Promise +} + +// REVIEW: this is in place so we can manipulate the return type +// of Object.keys which is string[] by default +// We need to extend so babel doesn't think it's JSX +const keysOf = (target: Target) => + Object.keys(target) as (keyof Target)[] + export const Queue = ({ cleanAllDelayed, cleanAllFailed, @@ -271,14 +303,11 @@ export const Queue = ({ retryJob, selectedStatus, selectStatus, -}: any) => ( +}: QueueProps) => (
-

- {queue.name} - {queue.version === 4 && bullmq} -

+

{queue.name}

- {Object.keys(STATUSES).map(status => ( + {keysOf(STATUSES).map(status => (