Skip to content

Commit

Permalink
feat(api-client,react-api-client): add new hook for csv file (#15451)
Browse files Browse the repository at this point in the history
* feat(api-client,react-api-client): add new hook for csv file
  • Loading branch information
koji committed Jun 18, 2024
1 parent e60953f commit d575770
Show file tree
Hide file tree
Showing 12 changed files with 393 additions and 10 deletions.
3 changes: 3 additions & 0 deletions api-client/src/dataFiles/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { uploadCsvFile } from './uploadCsvFile'

export * from './types'
24 changes: 24 additions & 0 deletions api-client/src/dataFiles/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Represents the parameters for uploading a CSV file.
*
* @interface UploadCsvFileParams
* @property {File | string} [fileData] - File object for Desktop app and string for USB drive on ODD
*/

export type FileData = File | string

interface CsvFileData {
id: string
createdAt: string
name: string
}

export interface UploadedCsvFileResponse {
data: CsvFileData
}

export interface UploadedCsvFilesResponse {
data: {
files: CsvFileData[]
}
}
36 changes: 36 additions & 0 deletions api-client/src/dataFiles/uploadCsvFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { v4 as uuidv4 } from 'uuid'
// import { POST, request } from '../request'

// import type { ResponsePromise } from '../request'
import type { HostConfig } from '../types'
import type { FileData /** UploadedCsvFileResponse */ } from './types'

// export function uploadCsvFile(
// config: HostConfig,
// data FileData
// ): ResponsePromise<UploadedCsvFileResponse> {
// return request<UploadedCsvFileResponse>(
// POST,
// '/dataFiles',
// null,
// config,
// data
// )
// }

// ToDo (kk:06/14/2024) remove when activate the above code
export function uploadCsvFile(
config: HostConfig,
data: FileData
// Note (kk: 06/14/2024) temporary using any for useUploadCsvFileMutation
): Promise<any> {
const fileId = uuidv4()
const stub = {
data: {
id: fileId,
createdAt: '2024-06-07T19:19:56.268029+00:00',
name: 'rtp_mock_file.csv',
},
}
return Promise.resolve(stub)
}
1 change: 1 addition & 0 deletions api-client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// api client entry point
export * from './calibration'
export * from './dataFiles'
export * from './deck_configuration'
export * from './health'
export * from './instruments'
Expand Down
47 changes: 47 additions & 0 deletions api-client/src/protocols/getCsvFiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { v4 as uuidv4 } from 'uuid'

// import { GET, request } from '../request'

// import type { ResponsePromise } from '../request'
import type { HostConfig } from '../types'
import type { UploadedCsvFilesResponse } from '../dataFiles/types'

/**
export function getCsvFiles(
config: HostConfig,
protocolId: string
): ResponsePromise<UploadCsvFilesResponse> {
return request<UploadCsvFilesResponse>(
GET,
`/protocols/${protocolId}/dataFiles`,
null,
config
)
}
*/

// ToDo (kk:06/14/2024) remove when activate the above code
export function getCsvFiles(
config: HostConfig,
protocolId: string
): Promise<{ data: UploadedCsvFilesResponse }> {
const fileIdOne = uuidv4()
const fileIdTwo = uuidv4()
const stub = {
data: {
files: [
{
id: fileIdOne,
createdAt: '2024-06-07T19:19:56.268029+00:00',
name: 'rtp_mock_file1.csv',
},
{
id: fileIdTwo,
createdAt: '2024-06-17T19:19:56.268029+00:00',
name: 'rtp_mock_file2.csv',
},
],
},
}
return Promise.resolve({ data: stub })
}
9 changes: 5 additions & 4 deletions api-client/src/protocols/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
export { createProtocol } from './createProtocol'
export { createProtocolAnalysis } from './createProtocolAnalysis'
export { deleteProtocol } from './deleteProtocol'
export { getCsvFiles } from './getCsvFiles'
export { getProtocol } from './getProtocol'
export { getProtocolAnalyses } from './getProtocolAnalyses'
export { getProtocolAnalysisAsDocument } from './getProtocolAnalysisAsDocument'
export { deleteProtocol } from './deleteProtocol'
export { createProtocol } from './createProtocol'
export { createProtocolAnalysis } from './createProtocolAnalysis'
export { getProtocols } from './getProtocols'
export { getProtocolIds } from './getProtocolIds'
export { getProtocols } from './getProtocols'

export * from './types'
export * from './utils'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as React from 'react'
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { QueryClient, QueryClientProvider } from 'react-query'
import { act, renderHook, waitFor } from '@testing-library/react'
import { uploadCsvFile } from '@opentrons/api-client'
import { useHost } from '../../api'
import { useUploadCsvFileMutation } from '../useUploadCsvFileMutation'

import type {
HostConfig,
UploadedCsvFileResponse,
Response,
} from '@opentrons/api-client'

vi.mock('@opentrons/api-client')
vi.mock('../../api/useHost')

const HOST_CONFIG: HostConfig = { hostname: 'localhost' }
const mockFilePath = 'media/mock-usb-drive/mock.csv'
const mockUploadResponse = {
data: {
id: '1',
createdAt: '2024-06-07T19:19:56.268029+00:00',
name: 'rtp_mock_file.csv',
},
} as UploadedCsvFileResponse

describe('useUploadCsvFileMutation', () => {
let wrapper: React.FunctionComponent<{ children: React.ReactNode }>

beforeEach(() => {
const queryClient = new QueryClient()
const clientProvider: React.FunctionComponent<{
children: React.ReactNode
}> = ({ children }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
)

wrapper = clientProvider
})

it('should return no data if no host', () => {
vi.mocked(useHost).mockReturnValue(null)

const { result } = renderHook(
() => useUploadCsvFileMutation(mockFilePath),
{
wrapper,
}
)
expect(result.current.data).toBeUndefined()
})

it('should return no data if the request fails', async () => {
vi.mocked(useHost).mockReturnValue(HOST_CONFIG)
vi.mocked(uploadCsvFile).mockRejectedValue('oh no')

const { result } = renderHook(
() => useUploadCsvFileMutation(mockFilePath),
{
wrapper,
}
)
expect(result.current.data).toBeUndefined()
result.current.uploadCsvFile(mockFilePath)
await waitFor(() => {
expect(result.current.data).toBeUndefined()
})
})

it('should return data when calling uploadCsvFile successfully', async () => {
vi.mocked(useHost).mockReturnValue(HOST_CONFIG)
vi.mocked(uploadCsvFile).mockResolvedValue({
data: mockUploadResponse,
} as Response<UploadedCsvFileResponse>)

const { result } = renderHook(
() => useUploadCsvFileMutation(mockFilePath),
{ wrapper }
)
act(() => result.current.uploadCsvFile(mockFilePath))

await waitFor(() => {
expect(result.current.data).toEqual(mockUploadResponse)
})
})
})
1 change: 1 addition & 0 deletions react-api-client/src/dataFiles/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useUploadCsvFileMutation } from './useUploadCsvFileMutation'
66 changes: 66 additions & 0 deletions react-api-client/src/dataFiles/useUploadCsvFileMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useMutation, useQueryClient } from 'react-query'
import { uploadCsvFile } from '@opentrons/api-client'
import { useHost } from '../api'
import type { AxiosError } from 'axios'
import type {
UseMutationResult,
UseMutationOptions,
UseMutateFunction,
} from 'react-query'
import type {
ErrorResponse,
HostConfig,
FileData,
UploadedCsvFileResponse,
} from '@opentrons/api-client'

export type UseUploadCsvFileMutationResult = UseMutationResult<
UploadedCsvFileResponse,
AxiosError<ErrorResponse>,
FileData
> & {
uploadCsvFile: UseMutateFunction<
UploadedCsvFileResponse,
AxiosError<ErrorResponse>,
FileData
>
}

export type UseUploadCsvFileMutationOption = UseMutationOptions<
UploadedCsvFileResponse,
AxiosError<ErrorResponse>,
FileData
>

export function useUploadCsvFileMutation(
fileData: FileData,
options: UseUploadCsvFileMutationOption = {},
hostOverride?: HostConfig | null
): UseUploadCsvFileMutationResult {
const host = useHost()
const queryClient = useQueryClient()

const mutation = useMutation<
UploadedCsvFileResponse,
AxiosError<ErrorResponse>,
FileData
>(
(fileData: FileData) =>
uploadCsvFile(host as HostConfig, fileData).then(response => {
queryClient
.invalidateQueries([host, 'dataFiles'])
.then(() =>
queryClient.setQueryData([host, 'dataFiles'], response.data)
)
.catch((e: Error) => {
throw e
})
return response.data
}),
options
)
return {
...mutation,
uploadCsvFile: mutation.mutate,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as React from 'react'
import { describe, it, vi, beforeEach, expect } from 'vitest'
import { QueryClient, QueryClientProvider } from 'react-query'
import { renderHook /** waitFor */ } from '@testing-library/react'
// import { getCsvFiles } from '@opentrons/api-client'
import { useHost } from '../../api'
import { useAllCsvFilesQuery } from '../useAllCsvFilesQuery'

// import type {
// HostConfig,
// Response,
// UploadedCsvFilesResponse,
// } from '@opentrons/api-client'

vi.mock('@oopentrons/api-client')
vi.mock('../../api/useHost')

// const HOST_CONFIG: HostConfig = { hostname: 'localhost' }
// const CSV_FILES_RESPONSE = {
// data: {
// files: [
// {
// id: '1',
// createdAt: '2024-06-07T19:19:56.268029+00:00',
// name: 'rtp_mock_file1.csv',
// },
// {
// id: '2',
// createdAt: '2024-06-17T19:19:56.268029+00:00',
// name: 'rtp_mock_file2.csv',
// },
// ],
// },
// } as UploadedCsvFilesResponse
const PROTOCOL_ID = '1'

describe('useAllCsvFilesQuery', () => {
let wrapper: React.FunctionComponent<{ children: React.ReactNode }>

beforeEach(() => {
const queryClient = new QueryClient()
const clientProvider: React.FunctionComponent<{
children: React.ReactNode
}> = ({ children }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
)

wrapper = clientProvider
})

it('should return no data if no host', () => {
vi.mocked(useHost).mockReturnValue(null)

const { result } = renderHook(() => useAllCsvFilesQuery(PROTOCOL_ID), {
wrapper,
})
expect(result.current.data).toBeUndefined()
})

// ToDo (kk:06/14/2024) remove comment when remove stub
// it('should return no data if the get csv files request fails', () => {
// vi.mocked(useHost).mockReturnValue(HOST_CONFIG)
// vi.mocked(getCsvFiles).mockRejectedValue('oh no')

// const { result } = renderHook(() => useAllCsvFilesQuery(PROTOCOL_ID), {
// wrapper,
// })

// expect(result.current.data).toBeUndefined()
// })

// it('should return csv files data', async () => {
// vi.mocked(useHost).mockReturnValue(HOST_CONFIG)
// vi.mocked(getCsvFiles).mockResolvedValue({
// data: CSV_FILES_RESPONSE,
// } as Response<UploadedCsvFilesResponse>)

// const { result } = renderHook(() => useAllCsvFilesQuery(PROTOCOL_ID), {
// wrapper,
// })

// await waitFor(() => {
// expect(result.current.data).toEqual(CSV_FILES_RESPONSE)
// })
// })
})
13 changes: 7 additions & 6 deletions react-api-client/src/protocols/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export { useAllProtocolsQuery } from './useAllProtocolsQuery'
export { useAllCsvFilesQuery } from './useAllCsvFilesQuery'
export { useAllProtocolIdsQuery } from './useAllProtocolIdsQuery'
export { useProtocolQuery } from './useProtocolQuery'
export { useProtocolAnalysesQuery } from './useProtocolAnalysesQuery'
export { useMostRecentSuccessfulAnalysisAsDocumentQuery } from './useMostRecentSuccessfulAnalysisAsDocumentQuery'
export { useProtocolAnalysisAsDocumentQuery } from './useProtocolAnalysisAsDocumentQuery'
export { useCreateProtocolMutation } from './useCreateProtocolMutation'
export { useAllProtocolsQuery } from './useAllProtocolsQuery'
export { useCreateProtocolAnalysisMutation } from './useCreateProtocolAnalysisMutation'
export { useCreateProtocolMutation } from './useCreateProtocolMutation'
export { useDeleteProtocolMutation } from './useDeleteProtocolMutation'
export { useMostRecentSuccessfulAnalysisAsDocumentQuery } from './useMostRecentSuccessfulAnalysisAsDocumentQuery'
export { useProtocolAnalysesQuery } from './useProtocolAnalysesQuery'
export { useProtocolAnalysisAsDocumentQuery } from './useProtocolAnalysisAsDocumentQuery'
export { useProtocolQuery } from './useProtocolQuery'
Loading

0 comments on commit d575770

Please sign in to comment.