diff --git a/src/components/artifacts/TaskArtifacts.tsx b/src/components/artifacts/TaskArtifacts.tsx index ff2e88df0..d8d60041c 100644 --- a/src/components/artifacts/TaskArtifacts.tsx +++ b/src/components/artifacts/TaskArtifacts.tsx @@ -1,24 +1,25 @@ import React from 'react'; -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; import ArtifactsView from './ArtifactsView'; -function TaskArtifacts(props) { - if (!props.task.artifacts || props.task.artifacts.length === 0) return
; - return ; -} - -export default createFragmentContainer(TaskArtifacts, { - task: graphql` - fragment TaskArtifacts_task on Task { - id - artifacts { - name - files { - path - size +export default function TaskArtifacts(props) { + let task = useFragment( + graphql` + fragment TaskArtifacts_task on Task { + id + artifacts { + name + files { + path + size + } } } - } - `, -}); + `, + props.task, + ); + + if (!task.artifacts || task.artifacts.length === 0) return
; + return ; +} diff --git a/src/components/builds/BuildsTable.tsx b/src/components/builds/BuildsTable.tsx index 64e80a0ee..399d8e4ab 100644 --- a/src/components/builds/BuildsTable.tsx +++ b/src/components/builds/BuildsTable.tsx @@ -21,6 +21,7 @@ import InfoIcon from '@mui/icons-material/Info'; import CommitIcon from '@mui/icons-material/Commit'; import Typography from '@mui/material/Typography'; import CallSplitIcon from '@mui/icons-material/CallSplit'; +import BookOutlinedIcon from '@mui/icons-material/BookOutlined'; import UnarchiveIcon from '@mui/icons-material/UnarchiveOutlined'; import BuildStatusChipNew from '../chips/BuildStatusChipNew'; @@ -33,8 +34,6 @@ import { navigateBuildHelper } from '../../utils/navigateHelper'; import { BuildsTable_builds } from './__generated__/BuildsTable_builds.graphql'; -import BookOutlinedIcon from '@mui/icons-material/BookOutlined'; - // todo: move custom values to mui theme adjustments const useStyles = makeStyles(theme => { return { diff --git a/src/components/chips/TaskCancellerChip.tsx b/src/components/chips/TaskCancellerChip.tsx index 9a3761fc2..6e68be7a4 100644 --- a/src/components/chips/TaskCancellerChip.tsx +++ b/src/components/chips/TaskCancellerChip.tsx @@ -3,17 +3,28 @@ import React from 'react'; import Avatar from '@mui/material/Avatar'; import Chip from '@mui/material/Chip'; -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; -import { TaskCancellerChip_task } from './__generated__/TaskCancellerChip_task.graphql'; +import { TaskCancellerChip_task$key } from './__generated__/TaskCancellerChip_task.graphql'; interface Props { - task: TaskCancellerChip_task; + task: TaskCancellerChip_task$key; className?: string; } -function TaskCancellerChip(props: Props) { - let { cancelledBy } = props.task; +export default function TaskCancellerChip(props: Props) { + let task = useFragment( + graphql` + fragment TaskCancellerChip_task on Task { + cancelledBy { + avatarURL + } + } + `, + props.task, + ); + + let { cancelledBy } = task; if (!cancelledBy) return <>; @@ -21,13 +32,3 @@ function TaskCancellerChip(props: Props) { } /> ); } - -export default createFragmentContainer(TaskCancellerChip, { - task: graphql` - fragment TaskCancellerChip_task on Task { - cancelledBy { - avatarURL - } - } - `, -}); diff --git a/src/components/chips/TaskCreatedChip.tsx b/src/components/chips/TaskCreatedChip.tsx index 98e3ce4b1..d8de0559a 100644 --- a/src/components/chips/TaskCreatedChip.tsx +++ b/src/components/chips/TaskCreatedChip.tsx @@ -4,21 +4,30 @@ import Icon from '@mui/material/Icon'; import Tooltip from '@mui/material/Tooltip'; import { graphql } from 'babel-plugin-relay/macro'; import React, { useEffect } from 'react'; -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { useTaskStatusColor } from '../../utils/colors'; import { taskStatusIconName } from '../../utils/status'; import { roundAndPresentDuration } from '../../utils/time'; -import { TaskCreatedChip_task } from './__generated__/TaskCreatedChip_task.graphql'; +import { TaskCreatedChip_task$key } from './__generated__/TaskCreatedChip_task.graphql'; import { useTheme } from '@mui/material'; interface Props { - task: TaskCreatedChip_task; + task: TaskCreatedChip_task$key; className?: string; } -let TaskCreatedChip = (props: Props) => { +export default function TaskCreatedChip(props: Props) { + let task = useFragment( + graphql` + fragment TaskCreatedChip_task on Task { + creationTimestamp + } + `, + props.task, + ); + let theme = useTheme(); - const creationTimestamp = props.task.creationTimestamp; + const creationTimestamp = task.creationTimestamp; const [durationAgoInSeconds, setDurationAgoInSeconds] = React.useState((Date.now() - creationTimestamp) / 1000); @@ -51,12 +60,4 @@ let TaskCreatedChip = (props: Props) => { /> ); -}; - -export default createFragmentContainer(TaskCreatedChip, { - task: graphql` - fragment TaskCreatedChip_task on Task { - creationTimestamp - } - `, -}); +} diff --git a/src/components/chips/TaskExperimentalChip.tsx b/src/components/chips/TaskExperimentalChip.tsx index 33a16bb90..16ac06d19 100644 --- a/src/components/chips/TaskExperimentalChip.tsx +++ b/src/components/chips/TaskExperimentalChip.tsx @@ -4,19 +4,27 @@ import Avatar from '@mui/material/Avatar'; import Chip from '@mui/material/Chip'; import CasinoIcon from '@mui/icons-material/Casino'; -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; -import { TaskExperimentalChip_task } from './__generated__/TaskExperimentalChip_task.graphql'; +import { TaskExperimentalChip_task$key } from './__generated__/TaskExperimentalChip_task.graphql'; import { useTheme } from '@mui/material'; interface Props { className?: string; - task: TaskExperimentalChip_task; + task: TaskExperimentalChip_task$key; } -let TaskExperimentalChip = (props: Props) => { +export default function TaskExperimentalChip(props: Props) { + let task = useFragment( + graphql` + fragment TaskExperimentalChip_task on Task { + experimental + } + `, + props.task, + ); + let theme = useTheme(); - const { task } = props; const { experimental } = task; if (!experimental) return
; @@ -32,12 +40,4 @@ let TaskExperimentalChip = (props: Props) => { } /> ); -}; - -export default createFragmentContainer(TaskExperimentalChip, { - task: graphql` - fragment TaskExperimentalChip_task on Task { - experimental - } - `, -}); +} diff --git a/src/components/chips/TaskOptionalChip.tsx b/src/components/chips/TaskOptionalChip.tsx index 3fc840dd4..0c43a8a6a 100644 --- a/src/components/chips/TaskOptionalChip.tsx +++ b/src/components/chips/TaskOptionalChip.tsx @@ -4,20 +4,29 @@ import Avatar from '@mui/material/Avatar'; import Chip from '@mui/material/Chip'; import Report from '@mui/icons-material/Report'; -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; -import { TaskOptionalChip_task } from './__generated__/TaskOptionalChip_task.graphql'; +import { TaskOptionalChip_task$key } from './__generated__/TaskOptionalChip_task.graphql'; import { useTheme } from '@mui/material'; interface Props { - task: TaskOptionalChip_task; + task: TaskOptionalChip_task$key; className?: string; } -function TaskOptionalChip(props: Props) { +export default function TaskOptionalChip(props: Props) { + let task = useFragment( + graphql` + fragment TaskOptionalChip_task on Task { + optional + } + `, + props.task, + ); + let theme = useTheme(); - let { optional } = props.task; + let { optional } = task; if (!optional) return
; return ( @@ -32,11 +41,3 @@ function TaskOptionalChip(props: Props) { /> ); } - -export default createFragmentContainer(TaskOptionalChip, { - task: graphql` - fragment TaskOptionalChip_task on Task { - optional - } - `, -}); diff --git a/src/components/chips/TaskRerunnerChip.tsx b/src/components/chips/TaskRerunnerChip.tsx index 611101b37..646381433 100644 --- a/src/components/chips/TaskRerunnerChip.tsx +++ b/src/components/chips/TaskRerunnerChip.tsx @@ -3,29 +3,30 @@ import React from 'react'; import Avatar from '@mui/material/Avatar'; import Chip from '@mui/material/Chip'; -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; -import { TaskRerunnerChip_task } from './__generated__/TaskRerunnerChip_task.graphql'; +import { TaskRerunnerChip_task$key } from './__generated__/TaskRerunnerChip_task.graphql'; interface Props { - task: TaskRerunnerChip_task; + task: TaskRerunnerChip_task$key; className?: string; } -function TaskRerunnerChip(props: Props) { - let { reranBy } = props.task; +export default function TaskRerunnerChip(props: Props) { + let task = useFragment( + graphql` + fragment TaskRerunnerChip_task on Task { + reranBy { + avatarURL + } + } + `, + props.task, + ); + + let { reranBy } = task; if (!reranBy) return <>; return } />; } - -export default createFragmentContainer(TaskRerunnerChip, { - task: graphql` - fragment TaskRerunnerChip_task on Task { - reranBy { - avatarURL - } - } - `, -}); diff --git a/src/components/chips/TaskResourcesChip.tsx b/src/components/chips/TaskResourcesChip.tsx index c27241619..d25b108a1 100644 --- a/src/components/chips/TaskResourcesChip.tsx +++ b/src/components/chips/TaskResourcesChip.tsx @@ -4,10 +4,10 @@ import Avatar from '@mui/material/Avatar'; import Chip from '@mui/material/Chip'; import Tooltip from '@mui/material/Tooltip'; import Memory from '@mui/icons-material/Memory'; -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; import { makeStyles } from '@mui/styles'; -import { TaskResourcesChip_task } from './__generated__/TaskResourcesChip_task.graphql'; +import { TaskResourcesChip_task$key } from './__generated__/TaskResourcesChip_task.graphql'; const useStyles = makeStyles(theme => { return { @@ -21,13 +21,25 @@ const useStyles = makeStyles(theme => { }); interface Props { - task: TaskResourcesChip_task; + task: TaskResourcesChip_task$key; className?: string; } -function TaskResourcesChip(props: Props) { +export default function TaskResourcesChip(props: Props) { + let task = useFragment( + graphql` + fragment TaskResourcesChip_task on Task { + instanceResources { + cpu + memory + } + } + `, + props.task, + ); + let classes = useStyles(); - let { task, className } = props; + let { className } = props; let resources = task.instanceResources; if (!resources) { return null; @@ -48,14 +60,3 @@ function TaskResourcesChip(props: Props) { ); } - -export default createFragmentContainer(TaskResourcesChip, { - task: graphql` - fragment TaskResourcesChip_task on Task { - instanceResources { - cpu - memory - } - } - `, -}); diff --git a/src/components/chips/TaskScheduledChip.tsx b/src/components/chips/TaskScheduledChip.tsx index 56ccd4ecf..7b8025275 100644 --- a/src/components/chips/TaskScheduledChip.tsx +++ b/src/components/chips/TaskScheduledChip.tsx @@ -7,19 +7,32 @@ import Tooltip from '@mui/material/Tooltip'; import { useTaskStatusColor } from '../../utils/colors'; import { taskStatusIconName } from '../../utils/status'; import { formatDuration } from '../../utils/time'; -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; -import { TaskScheduledChip_task } from './__generated__/TaskScheduledChip_task.graphql'; +import { TaskScheduledChip_task$key } from './__generated__/TaskScheduledChip_task.graphql'; import { useTheme } from '@mui/material'; interface Props { - task: TaskScheduledChip_task; + task: TaskScheduledChip_task$key; className?: string; } -function TaskScheduledChip(props: Props) { +export default function TaskScheduledChip(props: Props) { + let task = useFragment( + graphql` + fragment TaskScheduledChip_task on Task { + status + statusDurations { + status + durationInSeconds + } + } + `, + props.task, + ); + let theme = useTheme(); - let { task, className } = props; + let { className } = props; let scheduledColor = useTaskStatusColor('SCHEDULED'); let scheduledStatusDuration = task.statusDurations.find(it => it.status === 'SCHEDULED'); @@ -40,15 +53,3 @@ function TaskScheduledChip(props: Props) { } return
; } - -export default createFragmentContainer(TaskScheduledChip, { - task: graphql` - fragment TaskScheduledChip_task on Task { - status - statusDurations { - status - durationInSeconds - } - } - `, -}); diff --git a/src/components/chips/TaskStatefulChip.tsx b/src/components/chips/TaskStatefulChip.tsx index c77f94866..1ca75e99d 100644 --- a/src/components/chips/TaskStatefulChip.tsx +++ b/src/components/chips/TaskStatefulChip.tsx @@ -4,19 +4,27 @@ import Avatar from '@mui/material/Avatar'; import Chip from '@mui/material/Chip'; import SecurityIcon from '@mui/icons-material/Security'; import { graphql } from 'babel-plugin-relay/macro'; -import { TaskStatefulChip_task } from './__generated__/TaskStatefulChip_task.graphql'; -import { createFragmentContainer } from 'react-relay'; +import { TaskStatefulChip_task$key } from './__generated__/TaskStatefulChip_task.graphql'; +import { useFragment } from 'react-relay'; import { useTheme } from '@mui/material'; interface Props { - task: TaskStatefulChip_task; + task: TaskStatefulChip_task$key; className?: string; } -function TaskStatefulChip(props: Props) { +export default function TaskStatefulChip(props: Props) { + let task = useFragment( + graphql` + fragment TaskStatefulChip_task on Task { + stateful + } + `, + props.task, + ); + let theme = useTheme(); - let { task } = props; let { stateful } = task; if (!stateful) return
; @@ -32,11 +40,3 @@ function TaskStatefulChip(props: Props) { /> ); } - -export default createFragmentContainer(TaskStatefulChip, { - task: graphql` - fragment TaskStatefulChip_task on Task { - stateful - } - `, -}); diff --git a/src/components/chips/TaskStatusChip.tsx b/src/components/chips/TaskStatusChip.tsx index 917ecf59f..ddcc4945b 100644 --- a/src/components/chips/TaskStatusChip.tsx +++ b/src/components/chips/TaskStatusChip.tsx @@ -6,18 +6,29 @@ import Icon from '@mui/material/Icon'; import Tooltip from '@mui/material/Tooltip'; import { useTaskStatusColor } from '../../utils/colors'; import { taskStatusIconName, taskStatusMessage } from '../../utils/status'; -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; -import { TaskStatusChip_task } from './__generated__/TaskStatusChip_task.graphql'; +import { TaskStatusChip_task$key } from './__generated__/TaskStatusChip_task.graphql'; import { useTheme } from '@mui/material'; interface Props { - task: TaskStatusChip_task; + task: TaskStatusChip_task$key; className?: string; } -function TaskStatusChip(props: Props) { - let { task, className } = props; +export default function TaskStatusChip(props: Props) { + let task = useFragment( + graphql` + fragment TaskStatusChip_task on Task { + status + durationInSeconds + executingTimestamp + } + `, + props.task, + ); + + let { className } = props; let theme = useTheme(); let chip = ( ; @@ -36,11 +45,3 @@ function TaskTimeoutChip(props: Props) { ); } - -export default createFragmentContainer(TaskTimeoutChip, { - task: graphql` - fragment TaskTimeoutChip_task on Task { - timeoutInSeconds - } - `, -}); diff --git a/src/components/chips/TaskTransactionChip.tsx b/src/components/chips/TaskTransactionChip.tsx index 95c100023..b63e05b49 100644 --- a/src/components/chips/TaskTransactionChip.tsx +++ b/src/components/chips/TaskTransactionChip.tsx @@ -6,19 +6,32 @@ import Star from '@mui/icons-material/Star'; import Tooltip from '@mui/material/Tooltip'; import { graphql } from 'babel-plugin-relay/macro'; import { isTaskFinalStatus } from '../../utils/status'; -import { createFragmentContainer } from 'react-relay'; -import { TaskTransactionChip_task } from './__generated__/TaskTransactionChip_task.graphql'; +import { useFragment } from 'react-relay'; +import { TaskTransactionChip_task$key } from './__generated__/TaskTransactionChip_task.graphql'; import { useTheme } from '@mui/material'; interface Props { - task: TaskTransactionChip_task; + task: TaskTransactionChip_task$key; className?: string; } -function TaskTransactionChip(props: Props) { +export default function TaskTransactionChip(props: Props) { + let task = useFragment( + graphql` + fragment TaskTransactionChip_task on Task { + status + usedComputeCredits + transaction { + creditsAmount + initialCreditsAmount + } + } + `, + props.task, + ); + let theme = useTheme(); - let task = props.task; let { transaction, usedComputeCredits } = task; if (!usedComputeCredits) { return
; @@ -49,16 +62,3 @@ function TaskTransactionChip(props: Props) { ); } - -export default createFragmentContainer(TaskTransactionChip, { - task: graphql` - fragment TaskTransactionChip_task on Task { - status - usedComputeCredits - transaction { - creditsAmount - initialCreditsAmount - } - } - `, -}); diff --git a/src/components/common/AppBreadcrumbs.tsx b/src/components/common/AppBreadcrumbs.tsx index ea38972e7..a312b62fd 100644 --- a/src/components/common/AppBreadcrumbs.tsx +++ b/src/components/common/AppBreadcrumbs.tsx @@ -1,4 +1,6 @@ import * as React from 'react'; +import { useFragment } from 'react-relay'; +import { graphql } from 'babel-plugin-relay/macro'; import { makeStyles } from '@mui/styles'; import Link from '@mui/material/Link'; @@ -14,15 +16,13 @@ import BookmarkBorderIcon from '@mui/icons-material/BookmarkBorder'; import { absoluteLink } from '../../utils/link'; import RepositoryIcon from './RepositoryIcon'; -import { createFragmentContainer } from 'react-relay'; -import { graphql } from 'babel-plugin-relay/macro'; import AccountSwitch from './AccountSwitch'; -import { AppBreadcrumbs_build } from './__generated__/AppBreadcrumbs_build.graphql'; -import { AppBreadcrumbs_repository } from './__generated__/AppBreadcrumbs_repository.graphql'; -import { AppBreadcrumbs_task } from './__generated__/AppBreadcrumbs_task.graphql'; -import { AppBreadcrumbs_info } from './__generated__/AppBreadcrumbs_info.graphql'; -import { AppBreadcrumbs_viewer } from './__generated__/AppBreadcrumbs_viewer.graphql'; +import { AppBreadcrumbs_build$key } from './__generated__/AppBreadcrumbs_build.graphql'; +import { AppBreadcrumbs_repository$key } from './__generated__/AppBreadcrumbs_repository.graphql'; +import { AppBreadcrumbs_task$key } from './__generated__/AppBreadcrumbs_task.graphql'; +import { AppBreadcrumbs_info$key } from './__generated__/AppBreadcrumbs_info.graphql'; +import { AppBreadcrumbs_viewer$key } from './__generated__/AppBreadcrumbs_viewer.graphql'; const useStyles = makeStyles(theme => { return { @@ -51,21 +51,86 @@ const useStyles = makeStyles(theme => { }); interface Props { - info?: AppBreadcrumbs_info; - repository?: AppBreadcrumbs_repository; - build?: AppBreadcrumbs_build; - task?: AppBreadcrumbs_task; + info?: AppBreadcrumbs_info$key; + repository?: AppBreadcrumbs_repository$key; + build?: AppBreadcrumbs_build$key; + task?: AppBreadcrumbs_task$key; branch?: string; extraCrumbs?: Array<{ name: string; href?: string; Icon: typeof SvgIcon | React.ElementType; }>; - viewer?: AppBreadcrumbs_viewer; + viewer?: AppBreadcrumbs_viewer$key; } -const AppBreadcrumbs = (props: Props) => { - let { branch, extraCrumbs, info, repository, build, task, viewer } = props; +export default function AppBreadcrumbs(props: Props) { + let info = useFragment( + graphql` + fragment AppBreadcrumbs_info on OwnerInfo { + platform + name + } + `, + props.info, + ); + let repository = useFragment( + graphql` + fragment AppBreadcrumbs_repository on Repository { + id + platform + owner + name + } + `, + props.repository, + ); + let build = useFragment( + graphql` + fragment AppBreadcrumbs_build on Build { + id + branch + changeIdInRepo + repository { + id + platform + owner + name + } + } + `, + props.build, + ); + let task = useFragment( + graphql` + fragment AppBreadcrumbs_task on Task { + id + name + build { + id + branch + changeIdInRepo + repository { + id + platform + owner + name + } + } + } + `, + props.task, + ); + let viewer = useFragment( + graphql` + fragment AppBreadcrumbs_viewer on User { + ...AccountSwitch_viewer + } + `, + props.viewer, + ); + + let { branch, extraCrumbs } = props; let classes = useStyles(); let ownerName = task?.build?.repository?.owner || build?.repository?.owner || repository?.owner || info?.name; @@ -130,7 +195,7 @@ const AppBreadcrumbs = (props: Props) => { ); -}; +} interface CrumbProps { active: boolean; @@ -161,55 +226,3 @@ const Crumb = ({ active, name, href, Icon }: CrumbProps) => { ); }; - -export default createFragmentContainer(AppBreadcrumbs, { - info: graphql` - fragment AppBreadcrumbs_info on OwnerInfo { - platform - name - } - `, - repository: graphql` - fragment AppBreadcrumbs_repository on Repository { - id - platform - owner - name - } - `, - build: graphql` - fragment AppBreadcrumbs_build on Build { - id - branch - changeIdInRepo - repository { - id - platform - owner - name - } - } - `, - task: graphql` - fragment AppBreadcrumbs_task on Task { - id - name - build { - id - branch - changeIdInRepo - repository { - id - platform - owner - name - } - } - } - `, - viewer: graphql` - fragment AppBreadcrumbs_viewer on User { - ...AccountSwitch_viewer - } - `, -}); diff --git a/src/components/common/Notification.tsx b/src/components/common/Notification.tsx index d0ea24914..8725c76c2 100644 --- a/src/components/common/Notification.tsx +++ b/src/components/common/Notification.tsx @@ -1,21 +1,31 @@ -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; import React from 'react'; import { useNotificationColor } from '../../utils/colors'; import IconButton from '@mui/material/IconButton'; import Icon from '@mui/material/Icon'; import { navigateHelper } from '../../utils/navigateHelper'; -import { Notification_notification } from './__generated__/Notification_notification.graphql'; +import { Notification_notification$key } from './__generated__/Notification_notification.graphql'; import { useNavigate } from 'react-router-dom'; import { ListItem, ListItemText } from '@mui/material'; interface Props { - notification: Notification_notification; + notification: Notification_notification$key; } -function Notification(props: Props) { +export default function Notification(props: Props) { + let notification = useFragment( + graphql` + fragment Notification_notification on Notification { + level + message + link + } + `, + props.notification, + ); + let navigate = useNavigate(); - let { notification } = props; return ( ); } - -export default createFragmentContainer(Notification, { - notification: graphql` - fragment Notification_notification on Notification { - level - message - link - } - `, -}); diff --git a/src/components/common/TaskExecutionInfo.tsx b/src/components/common/TaskExecutionInfo.tsx index 084ab2b0c..dd6d2cbcf 100644 --- a/src/components/common/TaskExecutionInfo.tsx +++ b/src/components/common/TaskExecutionInfo.tsx @@ -1,11 +1,11 @@ -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; import React from 'react'; import { makeStyles } from '@mui/styles'; import Chip from '@mui/material/Chip'; import Typography from '@mui/material/Typography'; import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip, YAxis } from 'recharts'; -import { TaskExecutionInfo_task } from './__generated__/TaskExecutionInfo_task.graphql'; +import { TaskExecutionInfo_task$key } from './__generated__/TaskExecutionInfo_task.graphql'; import { formatDuration } from '../../utils/time'; import { Box, useTheme } from '@mui/material'; import { useRecoilState } from 'recoil'; @@ -22,13 +22,41 @@ const useStyles = makeStyles(theme => { }); interface Props { - task: TaskExecutionInfo_task; + task: TaskExecutionInfo_task$key; } -function TaskExecutionInfo(props: Props) { +export default function TaskExecutionInfo(props: Props) { + let task = useFragment( + graphql` + fragment TaskExecutionInfo_task on Task { + instanceResources { + cpu + memory + } + executionInfo { + labels + cpuChart { + maxValue + points { + value + secondsFromStart + } + } + memoryChart { + maxValue + points { + value + secondsFromStart + } + } + } + } + `, + props.task, + ); + let theme = useTheme(); const [prefersDarkMode] = useRecoilState(prefersDarkModeState); - let { task } = props; let classes = useStyles(); if (!task.executionInfo) return null; @@ -132,31 +160,3 @@ function TaskExecutionInfo(props: Props) {
); } - -export default createFragmentContainer(TaskExecutionInfo, { - task: graphql` - fragment TaskExecutionInfo_task on Task { - instanceResources { - cpu - memory - } - executionInfo { - labels - cpuChart { - maxValue - points { - value - secondsFromStart - } - } - memoryChart { - maxValue - points { - value - secondsFromStart - } - } - } - } - `, -}); diff --git a/src/components/tasks/TaskCommandList.tsx b/src/components/tasks/TaskCommandList.tsx index 585c6b01c..a92b45fc1 100644 --- a/src/components/tasks/TaskCommandList.tsx +++ b/src/components/tasks/TaskCommandList.tsx @@ -10,10 +10,10 @@ import Accordion from '@mui/material/Accordion'; import AccordionDetails from '@mui/material/AccordionDetails'; import AccordionSummary from '@mui/material/AccordionSummary'; import Typography from '@mui/material/Typography'; -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; import * as queryString from 'query-string'; -import { TaskCommandList_task } from './__generated__/TaskCommandList_task.graphql'; +import { TaskCommandList_task, TaskCommandList_task$key } from './__generated__/TaskCommandList_task.graphql'; import { ItemOfArray } from '../../utils/utility-types'; import { useLocation } from 'react-router-dom'; import { makeStyles } from '@mui/styles'; @@ -30,12 +30,28 @@ const useStyles = makeStyles(theme => { }); interface Props { - task: TaskCommandList_task; + task: TaskCommandList_task$key; } -function TaskCommandList(props: Props) { +export default function TaskCommandList(props: Props) { + let task = useFragment( + graphql` + fragment TaskCommandList_task on Task { + id + status + executingTimestamp + commands { + name + type + status + durationInSeconds + } + } + `, + props.task, + ); + let classes = useStyles(); - let task = props.task; let commands = task.commands; let commandComponents = []; @@ -107,7 +123,7 @@ function TaskCommandList(props: Props) {
- + @@ -121,19 +137,3 @@ function TaskCommandList(props: Props) { } return
{commandComponents}
; } - -export default createFragmentContainer(TaskCommandList, { - task: graphql` - fragment TaskCommandList_task on Task { - id - status - executingTimestamp - commands { - name - type - status - durationInSeconds - } - } - `, -}); diff --git a/src/components/tasks/TaskCommandsProgress.tsx b/src/components/tasks/TaskCommandsProgress.tsx index edb311a7b..71c58a49f 100644 --- a/src/components/tasks/TaskCommandsProgress.tsx +++ b/src/components/tasks/TaskCommandsProgress.tsx @@ -4,9 +4,9 @@ import { useTaskStatusColorMapping } from '../../utils/colors'; import Tooltip from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; import { formatDuration } from '../../utils/time'; -import { createFragmentContainer } from 'react-relay'; +import { useFragment } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; -import { TaskCommandsProgress_task } from './__generated__/TaskCommandsProgress_task.graphql'; +import { TaskCommandsProgress_task$key } from './__generated__/TaskCommandsProgress_task.graphql'; import { Box } from '@mui/material'; import { makeStyles } from '@mui/styles'; @@ -24,12 +24,25 @@ const useStyles = makeStyles(theme => { }); interface Props { - task: TaskCommandsProgress_task; + task: TaskCommandsProgress_task$key; className?: string; } -function TaskCommandsProgress(props: Props) { - let { task } = props; +export default function TaskCommandsProgress(props: Props) { + let task = useFragment( + graphql` + fragment TaskCommandsProgress_task on Task { + status + creationTimestamp + statusDurations { + status + durationInSeconds + } + } + `, + props.task, + ); + let classes = useStyles(); let [totalDuration, setTotalDuration] = useState( task.statusDurations.reduce((sum, statusDuration) => sum + statusDuration.durationInSeconds, 0), @@ -98,16 +111,3 @@ function TaskCommandsProgress(props: Props) { ); } - -export default createFragmentContainer(TaskCommandsProgress, { - task: graphql` - fragment TaskCommandsProgress_task on Task { - status - creationTimestamp - statusDurations { - status - durationInSeconds - } - } - `, -}); diff --git a/src/components/tasks/TaskDebuggingInformation.tsx b/src/components/tasks/TaskDebuggingInformation.tsx index 6a8bec392..ef0cd4966 100644 --- a/src/components/tasks/TaskDebuggingInformation.tsx +++ b/src/components/tasks/TaskDebuggingInformation.tsx @@ -1,11 +1,10 @@ import React from 'react'; import { CardContent, List } from '@mui/material'; -import { QueryRenderer } from 'react-relay'; +import { useLazyLoadQuery } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; import Typography from '@mui/material/Typography'; import Card from '@mui/material/Card'; import InlineLogs from '../logs/InlineLogs'; -import environment from '../../createRelayEnvironment'; import { TaskDebuggingInformationQuery } from './__generated__/TaskDebuggingInformationQuery.graphql'; import Notification from '../common/Notification'; @@ -14,66 +13,64 @@ interface Props { } function TaskDebuggingInformation(props: Props) { - return ( - - environment={environment} - variables={{ taskId: props.taskId }} - query={graphql` - query TaskDebuggingInformationQuery($taskId: ID!) { - task(id: $taskId) { - executionInfo { - events { - timestamp - message - } - agentNotifications { - message - ...Notification_notification - } + const response = useLazyLoadQuery( + graphql` + query TaskDebuggingInformationQuery($taskId: ID!) { + task(id: $taskId) { + executionInfo { + events { + timestamp + message + } + agentNotifications { + message + ...Notification_notification } - commandLogsTail(name: "cirrus-agent-logs") } + commandLogsTail(name: "cirrus-agent-logs") } - `} - render={response => { - if (!response.props) { - return null; - } - let task = response.props.task; - let events = - task.executionInfo?.events?.map(event => { - let prettyTime = new Date(event.timestamp).toLocaleTimeString(); - return `${prettyTime} ${event.message}`; - }) || []; + } + `, + { taskId: props.taskId }, + ); - const agentNotificationsComponent = - !task.executionInfo || - !task.executionInfo.agentNotifications || - task.executionInfo.agentNotifications.length === 0 ? null : ( - <> - - Agent notifications - - - {task.executionInfo.agentNotifications.map(notification => ( - - ))} - - - ); + if (!response.task) { + return null; + } - return ( - - - Debugging Information - - {agentNotificationsComponent} - - - - ); - }} - /> + let task = response.task; + + let events = + task.executionInfo?.events?.map(event => { + let prettyTime = new Date(event.timestamp).toLocaleTimeString(); + return `${prettyTime} ${event.message}`; + }) || []; + + const agentNotificationsComponent = + !task.executionInfo || + !task.executionInfo.agentNotifications || + task.executionInfo.agentNotifications.length === 0 ? null : ( + <> + + Agent notifications + + + {task.executionInfo.agentNotifications.map(notification => ( + + ))} + + + ); + + return ( + + + Debugging Information + + {agentNotificationsComponent} + + + ); } diff --git a/src/components/tasks/TaskDetails.tsx b/src/components/tasks/TaskDetails.tsx index 4ca7cb117..99b4eed17 100644 --- a/src/components/tasks/TaskDetails.tsx +++ b/src/components/tasks/TaskDetails.tsx @@ -1,4 +1,5 @@ import { makeStyles } from '@mui/styles'; +import environment from '../../createRelayEnvironment'; import Button from '@mui/material/Button'; import Card from '@mui/material/Card'; import CardActions from '@mui/material/CardActions'; @@ -8,10 +9,9 @@ import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; import { graphql } from 'babel-plugin-relay/macro'; import classNames from 'classnames'; -import React, { Suspense, useEffect, useState } from 'react'; -import { commitMutation, createFragmentContainer, requestSubscription } from 'react-relay'; +import React, { Suspense, useEffect, useState, useMemo } from 'react'; +import { useFragment, useMutation, requestSubscription } from 'react-relay'; import { useLocation, useNavigate } from 'react-router-dom'; -import environment from '../../createRelayEnvironment'; import { navigateBuildHelper, navigateTaskHelper } from '../../utils/navigateHelper'; import { hasWritePermissions } from '../../utils/permissions'; import { isTaskFinalStatus } from '../../utils/status'; @@ -26,8 +26,9 @@ import CirrusFavicon from '../common/CirrusFavicon'; import TaskCommandList from './TaskCommandList'; import TaskCommandsProgress from './TaskCommandsProgress'; import TaskList from './TaskList'; -import { TaskDetails_task } from './__generated__/TaskDetails_task.graphql'; +import { TaskDetails_task, TaskDetails_task$key } from './__generated__/TaskDetails_task.graphql'; import { + TaskDetailsReRunMutation, TaskDetailsReRunMutationResponse, TaskDetailsReRunMutationVariables, } from './__generated__/TaskDetailsReRunMutation.graphql'; @@ -44,7 +45,8 @@ import TaskTimeoutChip from '../chips/TaskTimeoutChip'; import Notification from '../common/Notification'; import HookList from '../hooks/HookList'; import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; -import { TabContext, TabList, TabPanel, ToggleButton } from '@mui/lab'; +import { TabContext, TabList, TabPanel } from '@mui/lab'; +import { ToggleButton } from '@mui/material'; import { Badge, ButtonGroup, @@ -78,47 +80,6 @@ import TaskDebuggingInformation from './TaskDebuggingInformation'; import CirrusLinearProgress from '../common/CirrusLinearProgress'; import CommitMessage from '../common/CommitMessage'; -const taskReRunMutation = graphql` - mutation TaskDetailsReRunMutation($input: TaskReRunInput!) { - rerun(input: $input) { - newTask { - id - } - } - } -`; - -const taskTriggerMutation = graphql` - mutation TaskDetailsTriggerMutation($input: TaskTriggerInput!) { - trigger(input: $input) { - task { - id - status - triggerType - } - } - } -`; - -const taskCancelMutation = graphql` - mutation TaskDetailsCancelMutation($input: TaskAbortInput!) { - abortTask(input: $input) { - abortedTask { - id - status - } - } - } -`; - -const invalidateCachesMutation = graphql` - mutation TaskDetailsInvalidateCachesMutation($input: InvalidateCacheEntriesInput!) { - invalidateCacheEntries(input: $input) { - clientMutationId - } - } -`; - const taskSubscription = graphql` subscription TaskDetailsSubscription($taskID: ID!) { task(id: $taskID) { @@ -172,29 +133,166 @@ const useStyles = makeStyles(theme => { }); interface Props { - task: TaskDetails_task; + task: TaskDetails_task$key; } -function TaskDetails(props: Props) { +export default function TaskDetails(props: Props) { + let task = useFragment( + graphql` + fragment TaskDetails_task on Task { + id + name + buildId + status + triggerType + automaticReRun + ...TaskNameChip_task + ...TaskCreatedChip_task + ...TaskScheduledChip_task + ...TaskStatusChip_task + commands { + name + } + ...TaskCommandsProgress_task + ...TaskCommandList_task + ...TaskArtifacts_task + ...TaskTransactionChip_task + ...TaskOptionalChip_task + ...TaskResourcesChip_task + ...TaskExperimentalChip_task + ...TaskTimeoutChip_task + ...TaskStatefulChip_task + ...TaskOptionalChip_task + ...TaskRerunnerChip_task + ...TaskCancellerChip_task + labels + artifacts { + name + } + notifications { + message + ...Notification_notification + } + build { + branch + changeIdInRepo + changeMessageTitle + viewerPermission + ...BuildBranchNameChip_build + ...BuildChangeChip_build + } + repository { + cloneUrl + ...RepositoryOwnerChip_repository + ...RepositoryNameChip_repository + } + allOtherRuns { + id + localGroupId + requiredGroups + scheduledTimestamp + executingTimestamp + finalStatusTimestamp + ...TaskListRow_task + } + dependencies { + id + localGroupId + requiredGroups + scheduledTimestamp + executingTimestamp + finalStatusTimestamp + ...TaskListRow_task + } + ...TaskExecutionInfo_task + hooks { + timestamp + ...HookListRow_hook + } + executionInfo { + cacheRetrievalAttempts { + hits { + key + valid + } + } + agentNotifications { + message + } + } + terminalCredential { + locator + trustedSecret + } + } + `, + props.task, + ); let navigate = useNavigate(); + + const isFinalStatus = useMemo(() => isTaskFinalStatus(task.status), [task.status]); useEffect(() => { - if (isTaskFinalStatus(props.task.status)) { + if (isFinalStatus) { return; } - let variables = { taskID: props.task.id }; + let variables = { taskID: task.id }; let subscription = requestSubscription(environment, { subscription: taskSubscription, variables: variables, }); return () => subscription.dispose(); - }, [props.task.id, props.task.status]); + }, [task.id, isFinalStatus]); - let { task } = props; let classes = useStyles(); let build = task.build; let repository = task.repository; + const [commitTaskReRunMutation] = useMutation(graphql` + mutation TaskDetailsReRunMutation($input: TaskReRunInput!) { + rerun(input: $input) { + newTask { + id + } + } + } + `); + + const [commitTaskTriggerMutation] = useMutation( + graphql` + mutation TaskDetailsTriggerMutation($input: TaskTriggerInput!) { + trigger(input: $input) { + task { + id + status + triggerType + } + } + } + `, + ); + + const [commitTaskCancelMutation] = useMutation( + graphql` + mutation TaskDetailsCancelMutation($input: TaskAbortInput!) { + abortTask(input: $input) { + abortedTask { + id + status + } + } + } + `, + ); + + const [commitInvalidateCachesMutation] = useMutation(graphql` + mutation TaskDetailsInvalidateCachesMutation($input: InvalidateCacheEntriesInput!) { + invalidateCacheEntries(input: $input) { + clientMutationId + } + } + `); + function trigger(taskId) { const variables: TaskDetailsTriggerMutationVariables = { input: { @@ -203,8 +301,7 @@ function TaskDetails(props: Props) { }, }; - commitMutation(environment, { - mutation: taskTriggerMutation, + commitTaskTriggerMutation({ variables: variables, onError: err => console.error(err), }); @@ -218,8 +315,7 @@ function TaskDetails(props: Props) { }, }; - commitMutation(environment, { - mutation: taskCancelMutation, + commitTaskCancelMutation({ variables: variables, onError: err => console.error(err), }); @@ -265,8 +361,7 @@ function TaskDetails(props: Props) { }, }; - commitMutation(environment, { - mutation: taskReRunMutation, + commitTaskReRunMutation({ variables: variables, onCompleted: (response: TaskDetailsReRunMutationResponse, errors) => { if (errors) { @@ -280,7 +375,7 @@ function TaskDetails(props: Props) { } let reRunButton = - !hasWritePermissions(build.viewerPermission) || !isTaskFinalStatus(task.status) ? null : ( + !hasWritePermissions(build.viewerPermission) || !isFinalStatus ? null : ( <> @@ -367,8 +462,7 @@ function TaskDetails(props: Props) { }, }; - commitMutation(environment, { - mutation: invalidateCachesMutation, + commitInvalidateCachesMutation({ variables: variables, onCompleted: (response: TaskDetailsInvalidateCachesMutationResponse, errors) => { if (errors) { @@ -452,7 +546,7 @@ function TaskDetails(props: Props) { return !(label.startsWith('canceller_') || label.startsWith('rerunner_')); } - const shouldRunTerminal = props.task.terminalCredential != null && !isTaskFinalStatus(props.task.status); + const shouldRunTerminal = task.terminalCredential != null && !isFinalStatus; useEffect(() => { let ct = new CirrusTerminal(document.getElementById('terminal')); @@ -460,15 +554,15 @@ function TaskDetails(props: Props) { if (shouldRunTerminal) { ct.connect( 'https://terminal.cirrus-ci.com', - props.task.terminalCredential.locator, - props.task.terminalCredential.trustedSecret, + task.terminalCredential.locator, + task.terminalCredential.trustedSecret, ); } return () => { ct.dispose(); }; - }, [shouldRunTerminal, props.task.terminalCredential]); + }, [shouldRunTerminal, task.terminalCredential]); let taskLabelsToShow = task.labels.filter(desiredLabel); let MAX_TASK_LABELS_TO_SHOW = 5; @@ -491,7 +585,7 @@ function TaskDetails(props: Props) { ); } - const hasNoAgentNotifications = props.task.executionInfo?.agentNotifications?.length === 0; + const hasNoAgentNotifications = task.executionInfo?.agentNotifications?.length === 0; return (
@@ -507,7 +601,7 @@ function TaskDetails(props: Props) {
- + @@ -586,94 +680,3 @@ function TaskDetails(props: Props) {
); } - -export default createFragmentContainer(TaskDetails, { - task: graphql` - fragment TaskDetails_task on Task { - id - name - buildId - status - triggerType - automaticReRun - ...TaskNameChip_task - ...TaskCreatedChip_task - ...TaskScheduledChip_task - ...TaskStatusChip_task - commands { - name - } - ...TaskCommandsProgress_task - ...TaskCommandList_task - ...TaskArtifacts_task - ...TaskTransactionChip_task - ...TaskOptionalChip_task - ...TaskResourcesChip_task - ...TaskExperimentalChip_task - ...TaskTimeoutChip_task - ...TaskStatefulChip_task - ...TaskOptionalChip_task - ...TaskRerunnerChip_task - ...TaskCancellerChip_task - labels - artifacts { - name - } - notifications { - message - ...Notification_notification - } - build { - branch - changeIdInRepo - changeMessageTitle - viewerPermission - ...BuildBranchNameChip_build - ...BuildChangeChip_build - } - repository { - cloneUrl - ...RepositoryOwnerChip_repository - ...RepositoryNameChip_repository - } - allOtherRuns { - id - localGroupId - requiredGroups - scheduledTimestamp - executingTimestamp - finalStatusTimestamp - ...TaskListRow_task - } - dependencies { - id - localGroupId - requiredGroups - scheduledTimestamp - executingTimestamp - finalStatusTimestamp - ...TaskListRow_task - } - ...TaskExecutionInfo_task - hooks { - timestamp - ...HookListRow_hook - } - executionInfo { - cacheRetrievalAttempts { - hits { - key - valid - } - } - agentNotifications { - message - } - } - terminalCredential { - locator - trustedSecret - } - } - `, -}); diff --git a/src/scenes/Task/Task.tsx b/src/scenes/Task/Task.tsx index 33c05cdee..0423c6e69 100644 --- a/src/scenes/Task/Task.tsx +++ b/src/scenes/Task/Task.tsx @@ -1,11 +1,9 @@ import React from 'react'; -import { QueryRenderer } from 'react-relay'; +import { useLazyLoadQuery } from 'react-relay'; import { graphql } from 'babel-plugin-relay/macro'; -import environment from '../../createRelayEnvironment'; import TaskDetails from '../../components/tasks/TaskDetails'; -import CirrusLinearProgress from '../../components/common/CirrusLinearProgress'; import NotFound from '../NotFound'; import { TaskQuery } from './__generated__/TaskQuery.graphql'; import { useParams } from 'react-router-dom'; @@ -13,35 +11,29 @@ import AppBreadcrumbs from '../../components/common/AppBreadcrumbs'; export default function Task(): JSX.Element { let { taskId } = useParams(); - return ( - - environment={environment} - variables={{ taskId }} - query={graphql` - query TaskQuery($taskId: ID!) { - task(id: $taskId) { - ...TaskDetails_task - ...AppBreadcrumbs_task - } - viewer { - ...AppBreadcrumbs_viewer - } - } - `} - render={({ error, props }) => { - if (!props) { - return ; + + const response = useLazyLoadQuery( + graphql` + query TaskQuery($taskId: ID!) { + task(id: $taskId) { + ...TaskDetails_task + ...AppBreadcrumbs_task } - if (!props.task) { - return ; + viewer { + ...AppBreadcrumbs_viewer } - return ( - <> - - - - ); - }} - /> + } + `, + { taskId }, + ); + + if (!response.task) { + return ; + } + return ( + <> + + + ); }