-
Notifications
You must be signed in to change notification settings - Fork 179
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(api-client, app): add subsystem types and update flows (#12957)
fix RLIQ-356
- Loading branch information
Showing
44 changed files
with
628 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { GET, request } from '../request' | ||
|
||
import type { ResponsePromise } from '../request' | ||
import type { HostConfig } from '../types' | ||
import type { SubsystemUpdateProgressData } from './types' | ||
|
||
export function getSubsystemUpdate( | ||
config: HostConfig, | ||
updateId: string | ||
): ResponsePromise<SubsystemUpdateProgressData> { | ||
return request<SubsystemUpdateProgressData>( | ||
GET, | ||
`/subsystems/updates/all/${updateId}`, | ||
null, | ||
config | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export { updateSubsystem } from './updateSubsystem' | ||
export { getSubsystemUpdate } from './getSubsystemUpdate' | ||
export * from './types' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
export type Subsystem = | ||
| 'gantry_x' | ||
| 'gantry_y' | ||
| 'head' | ||
| 'pipette_left' | ||
| 'pipette_right' | ||
| 'gripper' | ||
| 'rear_panel' | ||
|
||
export interface SubsystemUpdateProgressData { | ||
data: { | ||
id: string | ||
createdAt: string | ||
subsystem: Subsystem | ||
updateStatus: 'queued' | 'updating' | 'done' | ||
updateProgress: number | ||
updateError: string | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { POST, request } from '../request' | ||
|
||
import type { ResponsePromise } from '../request' | ||
import type { HostConfig } from '../types' | ||
import type { SubsystemUpdateProgressData } from './types' | ||
|
||
export function updateSubsystem( | ||
config: HostConfig, | ||
subsystem: string | ||
): ResponsePromise<SubsystemUpdateProgressData> { | ||
return request<SubsystemUpdateProgressData>( | ||
POST, | ||
`/subsystems/updates/${subsystem}`, | ||
null, | ||
config | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
app/src/molecules/FirmwareUpdateModal/__tests__/FirmwareUpdateModal.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import * as React from 'react' | ||
import { waitFor } from '@testing-library/react' | ||
import { renderWithProviders } from '@opentrons/components' | ||
import { | ||
useInstrumentsQuery, | ||
useSubsystemUpdateQuery, | ||
useUpdateSubsystemMutation, | ||
} from '@opentrons/react-api-client' | ||
import { i18n } from '../../../i18n' | ||
import { FirmwareUpdateModal } from '../' | ||
import { | ||
BadPipette, | ||
PipetteData, | ||
SubsystemUpdateProgressData, | ||
} from '@opentrons/api-client' | ||
|
||
jest.mock('@opentrons/react-api-client') | ||
|
||
const mockUseInstrumentQuery = useInstrumentsQuery as jest.MockedFunction< | ||
typeof useInstrumentsQuery | ||
> | ||
const mockUseSubsystemUpdateQuery = useSubsystemUpdateQuery as jest.MockedFunction< | ||
typeof useSubsystemUpdateQuery | ||
> | ||
const mockUseUpdateSubsystemMutation = useUpdateSubsystemMutation as jest.MockedFunction< | ||
typeof useUpdateSubsystemMutation | ||
> | ||
|
||
const render = (props: React.ComponentProps<typeof FirmwareUpdateModal>) => { | ||
return renderWithProviders(<FirmwareUpdateModal {...props} />, { | ||
i18nInstance: i18n, | ||
})[0] | ||
} | ||
|
||
describe('FirmwareUpdateModal', () => { | ||
let props: React.ComponentProps<typeof FirmwareUpdateModal> | ||
const refetch = jest.fn(() => Promise.resolve()) | ||
const updateSubsystem = jest.fn(() => Promise.resolve()) | ||
beforeEach(() => { | ||
props = { | ||
proceed: jest.fn(), | ||
description: 'A firmware update is required, instrument is updating', | ||
subsystem: 'pipette_left', | ||
} | ||
mockUseInstrumentQuery.mockReturnValue({ | ||
data: { | ||
data: [ | ||
{ | ||
subsystem: 'pipette_left', | ||
ok: false, | ||
} as BadPipette, | ||
], | ||
}, | ||
refetch, | ||
} as any) | ||
mockUseSubsystemUpdateQuery.mockReturnValue({ | ||
data: { | ||
data: { | ||
id: 'update id', | ||
updateStatus: 'done', | ||
} as any, | ||
} as SubsystemUpdateProgressData, | ||
} as any) | ||
mockUseUpdateSubsystemMutation.mockReturnValue({ | ||
data: { | ||
data: { | ||
id: 'update id', | ||
updateStatus: 'in progress', | ||
updateProgress: 20, | ||
} as any, | ||
} as SubsystemUpdateProgressData, | ||
updateSubsystem, | ||
} as any) | ||
}) | ||
it('calls proceed if no update is needed', () => { | ||
mockUseInstrumentQuery.mockReturnValue({ | ||
data: { | ||
data: [ | ||
{ | ||
subsystem: 'pipette_left', | ||
ok: true, | ||
} as PipetteData, | ||
], | ||
}, | ||
} as any) | ||
mockUseSubsystemUpdateQuery.mockReturnValue({} as any) | ||
const { getByText } = render(props) | ||
getByText('A firmware update is required, instrument is updating') | ||
expect(props.proceed).toHaveBeenCalled() | ||
}) | ||
it('calls update subsystem if update is needed', () => { | ||
mockUseSubsystemUpdateQuery.mockReturnValue({} as any) | ||
const { getByText } = render(props) | ||
getByText('A firmware update is required, instrument is updating') | ||
expect(updateSubsystem).toHaveBeenCalled() | ||
}) | ||
it('calls refetch instruments and then proceed once update is complete', async () => { | ||
const { getByText } = render(props) | ||
getByText('A firmware update is required, instrument is updating') | ||
await waitFor(() => expect(refetch).toHaveBeenCalled()) | ||
await waitFor(() => expect(props.proceed).toHaveBeenCalled()) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import * as React from 'react' | ||
import { css } from 'styled-components' | ||
import { | ||
ALIGN_CENTER, | ||
DIRECTION_COLUMN, | ||
TYPOGRAPHY, | ||
SPACING, | ||
Flex, | ||
RESPONSIVENESS, | ||
JUSTIFY_CENTER, | ||
BORDERS, | ||
COLORS, | ||
} from '@opentrons/components' | ||
import { | ||
useInstrumentsQuery, | ||
useSubsystemUpdateQuery, | ||
useUpdateSubsystemMutation, | ||
} from '@opentrons/react-api-client' | ||
import { ProgressBar } from '../../atoms/ProgressBar' | ||
import { StyledText } from '../../atoms/text' | ||
import { BadGripper, BadPipette, Subsystem } from '@opentrons/api-client' | ||
|
||
interface FirmwareUpdateModalProps { | ||
description: string | ||
proceed: () => void | ||
subsystem: Subsystem | ||
} | ||
|
||
const DESCRIPTION_STYLE = css` | ||
${TYPOGRAPHY.h1Default} | ||
margin-top: ${SPACING.spacing8}; | ||
margin-bottom: ${SPACING.spacing24}; | ||
@media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} { | ||
font-weight: ${TYPOGRAPHY.fontWeightBold}; | ||
font-size: ${TYPOGRAPHY.fontSize32}; | ||
margin-top: ${SPACING.spacing4}; | ||
margin-bottom: ${SPACING.spacing32}; | ||
margin-left: 4.5rem; | ||
margin-right: 4.5rem; | ||
text-align: ${TYPOGRAPHY.textAlignCenter}; | ||
line-height: ${TYPOGRAPHY.lineHeight42}; | ||
} | ||
` | ||
const MODAL_STYLE = css` | ||
align-items: ${ALIGN_CENTER}; | ||
flex-direction: ${DIRECTION_COLUMN}; | ||
justify-content: ${JUSTIFY_CENTER}; | ||
padding: ${SPACING.spacing32}; | ||
height: 24.625rem; | ||
@media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} { | ||
height: 31.5625rem; | ||
} | ||
` | ||
const OUTER_STYLES = css` | ||
border-radius: ${BORDERS.borderRadiusSize4}; | ||
background: ${COLORS.medGreyEnabled}; | ||
width: 13.374rem; | ||
` | ||
|
||
export const FirmwareUpdateModal = ( | ||
props: FirmwareUpdateModalProps | ||
): JSX.Element => { | ||
const { proceed, subsystem, description } = props | ||
const [updateId, setUpdateId] = React.useState('') | ||
const { | ||
data: attachedInstruments, | ||
refetch: refetchInstruments, | ||
} = useInstrumentsQuery() | ||
const { updateSubsystem } = useUpdateSubsystemMutation({ | ||
onSuccess: data => { | ||
setUpdateId(data.data.id) | ||
}, | ||
}) | ||
const updateNeeded = | ||
attachedInstruments?.data?.some( | ||
(i): i is BadGripper | BadPipette => !i.ok && i.subsystem === subsystem | ||
) ?? false | ||
React.useEffect(() => { | ||
if (!updateNeeded) { | ||
proceed() | ||
} else { | ||
updateSubsystem(subsystem) | ||
} | ||
}, []) | ||
const { data: updateData } = useSubsystemUpdateQuery(updateId) | ||
const status = updateData?.data.updateStatus | ||
const percentComplete = updateData?.data.updateProgress ?? 0 | ||
|
||
React.useEffect(() => { | ||
if (status === 'done') { | ||
refetchInstruments() | ||
.then(() => { | ||
proceed() | ||
}) | ||
.catch(() => { | ||
proceed() | ||
}) | ||
} | ||
}, [status, proceed, refetchInstruments]) | ||
return ( | ||
<Flex css={MODAL_STYLE}> | ||
<StyledText css={DESCRIPTION_STYLE}>{description}</StyledText> | ||
<ProgressBar | ||
percentComplete={percentComplete} | ||
outerStyles={OUTER_STYLES} | ||
/> | ||
</Flex> | ||
) | ||
} |
Oops, something went wrong.