diff --git a/src/config.js b/src/config.js index 6b7cfd0947..a50ebe2d94 100644 --- a/src/config.js +++ b/src/config.js @@ -7,6 +7,8 @@ const defaultConfig = { SENTRY_ENVIRONMENT: 'staging', } +const INTERNAL_API = `${defaultConfig.API_URL}/internal` + function removeReactAppPrefix(obj) { // in .env file, the variable must start with REACT_APP_ // to be injected in the application, so we remove that @@ -16,6 +18,7 @@ function removeReactAppPrefix(obj) { const config = { ...defaultConfig, + INTERNAL_API, ...removeReactAppPrefix(process.env), ...window.configEnv, } diff --git a/src/pages/CommitPage/CommitPage.spec.js b/src/pages/CommitPage/CommitPage.spec.js index 54fdb72917..57f9899af9 100644 --- a/src/pages/CommitPage/CommitPage.spec.js +++ b/src/pages/CommitPage/CommitPage.spec.js @@ -1,20 +1,24 @@ import { render, screen, waitFor } from 'custom-testing-library' +import { QueryClient, QueryClientProvider } from 'react-query' import { MemoryRouter, Route } from 'react-router-dom' import { useCommit } from 'services/commit' import { useFileWithMainCoverage } from 'services/file' +import { useUploadPresignedUrl } from 'services/uploadPresignedUrl' import CommitPage from './CommitPage' jest.mock('services/commit') jest.mock('services/file') +jest.mock('services/uploadPresignedUrl') jest.mock('./Header/Header.js', () => () => 'The Header') jest.mock('./subroute/CommitFileView.js', () => () => 'The Commit File View') jest.mock( './Summary/CommitDetailsSummary.js', () => () => 'Commit Details Summary' ) +const queryClient = new QueryClient() const dataReturned = { commit: { @@ -88,17 +92,22 @@ const fileData = { flagNames: [], } +const mockedPresignedUrl = {presignedUrl: "http://minio:9000/archive/v4/raw/2022-06-23/942173DE95CBF167C5683F40B7DB34C0/ee3ecad424e67419d6c4531540f1ef5df045ff12/919ccc6d-7972-4895-b289-f2d569683a17.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=codecov-default-key%2F20220705%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220705T101702Z&X-Amz-Expires=10&X-Amz-SignedHeaders=host&X-Amz-Signature=8846492d85f62187493cbff3631ec7f0ccf2d355f768eecf294f0572cf758e4c"} + describe('CommitPage', () => { function setup(data) { useCommit.mockReturnValue(data) useFileWithMainCoverage.mockReturnValue(fileData) + useUploadPresignedUrl.mockReturnValue(mockedPresignedUrl) render( - - - - - + + + + + + + ) } @@ -159,6 +168,9 @@ describe('CommitPage', () => { describe('Commits Table', () => { function setup(data) { useCommit.mockReturnValue(data) + useUploadPresignedUrl.mockReturnValue({ + data: '/archive/v4/raw/2022-06-23/..', + }) render( { describe('FileViewer', () => { function setup(data) { + useUploadPresignedUrl.mockReturnValue({ + data: '/archive/v4/raw/2022-06-23/..', + }) useCommit.mockReturnValue(data) useFileWithMainCoverage.mockReturnValue({ data: fileData, @@ -235,9 +250,7 @@ describe('CommitPage', () => { it('the impacted file', () => { expect(screen.getByTestId('spinner')).toBeInTheDocument() - waitFor(() => { - expect(screen.getByText(/index.js/)).toBeInTheDocument() - }) + waitFor(()=> expect(screen.getByText(/index.js/)).toBeInTheDocument()) }) }) diff --git a/src/pages/CommitPage/UploadsCard/Upload.js b/src/pages/CommitPage/UploadsCard/Upload.js index 690ece3e57..7b81e793f1 100644 --- a/src/pages/CommitPage/UploadsCard/Upload.js +++ b/src/pages/CommitPage/UploadsCard/Upload.js @@ -1,7 +1,6 @@ import PropTypes from 'prop-types' -import config from 'config' - +import { useUploadPresignedUrl } from 'services/uploadPresignedUrl' import { ErrorCodeEnum, UploadStateEnum, @@ -24,6 +23,8 @@ const Upload = ({ state, }) => { const isCarriedForward = uploadType === UploadTypeEnum.CARRIED_FORWARD + const { data } = useUploadPresignedUrl({ path :downloadUrl }) + const presignedGetUrl = data?.presignedUrl return (
@@ -56,13 +57,8 @@ const Upload = ({ carry-forward )}
- {downloadUrl && ( - + {presignedGetUrl && ( + Download )} diff --git a/src/pages/CommitPage/UploadsCard/Upload.spec.js b/src/pages/CommitPage/UploadsCard/Upload.spec.js index c4f37e3311..e350682597 100644 --- a/src/pages/CommitPage/UploadsCard/Upload.spec.js +++ b/src/pages/CommitPage/UploadsCard/Upload.spec.js @@ -1,22 +1,45 @@ import { render, screen } from '@testing-library/react' +import { QueryClient, QueryClientProvider } from 'react-query' +import { MemoryRouter, Route } from 'react-router-dom' +import { useUploadPresignedUrl } from 'services/uploadPresignedUrl' import { formatTimeToNow } from 'shared/utils/dates' import Upload from './Upload' +jest.mock('services/uploadPresignedUrl') + +const queryClient = new QueryClient() +const mockedPresignedUrl = { + presignedUrl: + 'http://minio:9000/archive/v4/raw/2022-06-23/942173DE95CBF167C5683F40B7DB34C0/ee3ecad424e67419d6c4531540f1ef5df045ff12/919ccc6d-7972-4895-b289-f2d569683a17.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=codecov-default-key%2F20220705%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220705T101702Z&X-Amz-Expires=10&X-Amz-SignedHeaders=host&X-Amz-Signature=8846492d85f62187493cbff3631ec7f0ccf2d355f768eecf294f0572cf758e4c', +} + describe('UploadsCard', () => { - function setup(props) { - render() + function setup({ props, data = mockedPresignedUrl }) { + useUploadPresignedUrl.mockReturnValue({ data }) + + render( + + + + + + + + ) } describe('renders', () => { beforeEach(() => { setup({ - ciUrl: 'ciUrl.com', - createdAt: '2020-08-25T16:36:19.559474+00:00', - downloadUrl: 'download.com', - buildCode: '1234', - uploadType: 'CARRIEDFORWARD', + props: { + ciUrl: 'ciUrl.com', + createdAt: '2020-08-25T16:36:19.559474+00:00', + downloadUrl: 'download.com', + buildCode: '1234', + uploadType: 'CARRIEDFORWARD', + }, }) }) @@ -35,10 +58,9 @@ describe('UploadsCard', () => { expect(screen.getByText(createDate)).toBeInTheDocument() }) it('renders a download link', () => { - expect(screen.getByRole('link', { name: /Download/ })).toHaveAttribute( - 'href', - 'download.com' - ) + expect( + screen.getByRole('link', { name: /1234 external-link.svg/ }) + ).toHaveAttribute('href') }) it('renders carry-forward text', () => { @@ -48,7 +70,7 @@ describe('UploadsCard', () => { describe('build without build link', () => { beforeEach(() => { - setup({ buildCode: '1234' }) + setup({ props: { buildCode: '1234' } }) }) it('renders a the build code', () => { expect(screen.getByText(/1234/)).toBeInTheDocument() @@ -61,7 +83,7 @@ describe('UploadsCard', () => { }) describe('missinng data renders', () => { beforeEach(() => { - setup({}) + setup({ props: {}, data: null }) }) it('renders a default build code if no code was provided', () => { @@ -81,13 +103,17 @@ describe('UploadsCard', () => { describe('rendering flags', () => { it('one flag', () => { setup({ - flags: ['flag1'], + props: { + flags: ['flag1'], + }, }) expect(screen.getByText(/flag1/)).toBeInTheDocument() }) it('multiple flags', () => { setup({ - flags: ['flag1', 'flag2', 'flag3', 'flag4'], + props: { + flags: ['flag1', 'flag2', 'flag3', 'flag4'], + }, }) expect(screen.getByText(/flag1/)).toBeInTheDocument() expect(screen.getByText(/flag2/)).toBeInTheDocument() @@ -103,30 +129,38 @@ describe('UploadsCard', () => { it('fileNotFoundInStorage error', () => { setup({ - errors: [{ errorCode: 'FILE_NOT_IN_STORAGE' }], + props: { + errors: [{ errorCode: 'FILE_NOT_IN_STORAGE' }], + }, }) expect(screen.getByText(/processing failed/)).toBeInTheDocument() }) it('reportExpired error', () => { setup({ - errors: [{ errorCode: 'REPORT_EXPIRED' }], + props: { + errors: [{ errorCode: 'REPORT_EXPIRED' }], + }, }) expect(screen.getByText(/upload expired/)).toBeInTheDocument() }) it('reportEmpty error', () => { setup({ - errors: [{ errorCode: 'REPORT_EMPTY' }], + props: { + errors: [{ errorCode: 'REPORT_EMPTY' }], + }, }) expect(screen.getByText(/upload is empty/)).toBeInTheDocument() }) it('all errors', () => { setup({ - errors: [ - { errorCode: 'FILE_NOT_IN_STORAGE' }, - { errorCode: 'REPORT_EXPIRED' }, - { errorCode: 'REPORT_EMPTY' }, - { errorCode: 'SOME_NEW_ERROR' }, - ], + props: { + errors: [ + { errorCode: 'FILE_NOT_IN_STORAGE' }, + { errorCode: 'REPORT_EXPIRED' }, + { errorCode: 'REPORT_EMPTY' }, + { errorCode: 'SOME_NEW_ERROR' }, + ], + }, }) expect(screen.getByText(/processing failed/)).toBeInTheDocument() expect(screen.getByText(/upload expired/)).toBeInTheDocument() @@ -134,39 +168,49 @@ describe('UploadsCard', () => { }) it('handles new errors the front end doesnt know how to handle', () => { setup({ - errors: [{ errorCode: 'SOME_NEW_ERROR' }], + props: { + errors: [{ errorCode: 'SOME_NEW_ERROR' }], + }, }) expect(screen.getByText(/unknown error/)).toBeInTheDocument() }) it('handles an unexpected error type', () => { setup({ - errors: [{ errorCode: { error: 'bad config or something' } }], + props: { + errors: [{ errorCode: { error: 'bad config or something' } }], + }, }) expect(screen.getByText(/unknown error/)).toBeInTheDocument() }) it('handles upload state error but no error code resolved as an known error', () => { setup({ - state: 'ERROR', + props: { + state: 'ERROR', + }, }) expect(screen.getByText(/unknown error/)).toBeInTheDocument() }) it('handles upload state error but no errors returned', () => { setup({ - state: 'ERROR', - errors: [], + props: { + state: 'ERROR', + errors: [], + }, }) expect(screen.getByText(/unknown error/)).toBeInTheDocument() }) it('If no state is provided and no errors received do not show an error', () => { setup({ - error: [], + props: { + error: [], + }, }) expect(screen.queryByText(/unknown error/)).not.toBeInTheDocument() }) }) describe('rendering uploaded type of uploads', () => { - setup({ uploadType: 'UPLOADED' }) + setup({ props: { uploadType: 'UPLOADED' } }) it('does not render carry-forward text', () => { expect(screen.queryByText('carry-forward')).not.toBeInTheDocument() }) diff --git a/src/pages/CommitPage/UploadsCard/UploadsCard.spec.js b/src/pages/CommitPage/UploadsCard/UploadsCard.spec.js index b4a8fa53e2..54921f6def 100644 --- a/src/pages/CommitPage/UploadsCard/UploadsCard.spec.js +++ b/src/pages/CommitPage/UploadsCard/UploadsCard.spec.js @@ -1,14 +1,26 @@ import { fireEvent, render, screen } from 'custom-testing-library' +import { QueryClient, QueryClientProvider } from 'react-query' +import { MemoryRouter, Route } from 'react-router-dom' + import { useUploads } from './hooks' import UploadsCard from './UploadsCard' +const queryClient = new QueryClient() jest.mock('./hooks') describe('UploadsCard', () => { function setup(mockUploads) { useUploads.mockReturnValue(mockUploads) - render() + render( + + + + + + + + ) } describe('renders', () => { diff --git a/src/services/uploadPresignedUrl/hooks.js b/src/services/uploadPresignedUrl/hooks.js new file mode 100644 index 0000000000..6b7104a002 --- /dev/null +++ b/src/services/uploadPresignedUrl/hooks.js @@ -0,0 +1,15 @@ +import { useQuery } from 'react-query' +import { useParams } from 'react-router-dom' + +import Api from 'shared/api' + +function fetchUploadPresignedUrl({ provider, path }) { + return Api.get({ path, provider, isUploadPath: true }) +} + +export function useUploadPresignedUrl({ path }) { + const { provider } = useParams() + return useQuery(['uploadPresignedUrl', provider], () => { + return fetchUploadPresignedUrl({ provider, path }) + }) +} diff --git a/src/services/uploadPresignedUrl/hooks.spec.js b/src/services/uploadPresignedUrl/hooks.spec.js new file mode 100644 index 0000000000..eb29785b7e --- /dev/null +++ b/src/services/uploadPresignedUrl/hooks.spec.js @@ -0,0 +1,68 @@ +import { renderHook } from '@testing-library/react-hooks' +import { rest } from 'msw' +import { setupServer } from 'msw/node' +import { QueryClient, QueryClientProvider } from 'react-query' +import { MemoryRouter, Route } from 'react-router-dom' + +import { useUploadPresignedUrl } from './hooks' + +const downloadUrl = + 'v4/raw/2022-06-23/storage_hash/repo_hash/commit_id/file_name.txt' + +const mockedPresignedUrl = {presignedUrl: "http://minio:9000/archive/v4/raw/2022-06-23/942173DE95CBF167C5683F40B7DB34C0/ee3ecad424e67419d6c4531540f1ef5df045ff12/919ccc6d-7972-4895-b289-f2d569683a17.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=codecov-default-key%2F20220705%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220705T101702Z&X-Amz-Expires=10&X-Amz-SignedHeaders=host&X-Amz-Signature=8846492d85f62187493cbff3631ec7f0ccf2d355f768eecf294f0572cf758e4c"} + +const queryClient = new QueryClient() +const wrapper = ({ children }) => ( + + + {children} + + +) + +const server = setupServer() + +beforeAll(() => server.listen()) +beforeEach(() => { + server.resetHandlers() + queryClient.clear() +}) +afterAll(() => server.close()) + +describe('useUploadPresignedUrl', () => { + let hookData + + function setup() { + server.use( + rest.get(downloadUrl, (req, res, ctx) => { + return res(ctx.status(200), ctx.json(mockedPresignedUrl)) + }) + ) + hookData = renderHook( + () => useUploadPresignedUrl({ path: downloadUrl }), + { + wrapper, + } + ) + } + + describe('when called', () => { + beforeEach(() => { + setup() + }) + + it('renders isLoading true', () => { + expect(hookData.result.current.isLoading).toBeTruthy() + }) + + describe('when data is loaded', () => { + beforeEach(() => { + return hookData.waitFor(() => hookData.result.current.isSuccess) + }) + + it('returns the data', () => { + expect(hookData.result.current.data).toEqual(mockedPresignedUrl) + }) + }) + }) +}) diff --git a/src/services/uploadPresignedUrl/index.js b/src/services/uploadPresignedUrl/index.js new file mode 100644 index 0000000000..fc78d35129 --- /dev/null +++ b/src/services/uploadPresignedUrl/index.js @@ -0,0 +1 @@ +export * from './hooks' diff --git a/src/shared/api/api.js b/src/shared/api/api.js index d20cbfe1a0..28fa56f07b 100644 --- a/src/shared/api/api.js +++ b/src/shared/api/api.js @@ -13,8 +13,9 @@ function _fetch({ body, provider = 'gh', extraHeaders = {}, + isUploadPath, }) { - const uri = generatePath({ path, query }) + const uri = generatePath({ path, query, isUploadPath }) const headers = { Accept: 'application/json', 'Content-Type': 'application/json; charset=utf-8', diff --git a/src/shared/api/api.test.js b/src/shared/api/api.test.js index f7cfd90ce2..7f7dc09ba4 100644 --- a/src/shared/api/api.test.js +++ b/src/shared/api/api.test.js @@ -26,11 +26,19 @@ const userData = { ], } +const mockedPresignedUrl = { + presignedUrl: + 'http://minio:9000/archive/v4/raw/2022-06-23/942173DE95CBF167C5683F40B7DB34C0/ee3ecad424e67419d6c4531540f1ef5df045ff12/919ccc6d-7972-4895-b289-f2d569683a17.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=codecov-default-key%2F20220705%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220705T101702Z&X-Amz-Expires=10&X-Amz-SignedHeaders=host&X-Amz-Signature=8846492d85f62187493cbff3631ec7f0ccf2d355f768eecf294f0572cf758e4c', +} + const server = setupServer( rest.get('/internal/test', (req, res, ctx) => { const hasTokenType = Boolean(req.headers.get('token-type')) return res(ctx.status(hasTokenType ? 200 : 401), ctx.json(rawUserData)) }), + rest.get('/upload', (req, res, ctx) => { + return res(ctx.status(200), ctx.json(mockedPresignedUrl)) + }), rest.post('/internal/test', (req, res, ctx) => { return res(ctx.status(200), ctx.json(req.body)) }), @@ -123,25 +131,19 @@ describe('when calling an endpoint with a token', () => { }) }) -describe('when using a post request', () => { - const body = { - test: 'foo', - camel_case: 'snakeCase', - } +describe('when using a get request with upload path', () => { beforeEach(() => { - return Api.post({ - path: '/test', - body, + return Api.get({ + path: '/upload', + provider: 'gh', + isUploadPath: true, }).then((data) => { result = data }) }) - it('returns the data, and transform to camelCase', () => { - expect(result).toEqual({ - test: 'foo', - camelCase: 'snakeCase', - }) + it('returns data as text', () => { + expect(result).toEqual(mockedPresignedUrl) }) }) diff --git a/src/shared/api/helpers.js b/src/shared/api/helpers.js index 3fbe089b36..f68aa9bd6a 100644 --- a/src/shared/api/helpers.js +++ b/src/shared/api/helpers.js @@ -13,8 +13,8 @@ export const ProviderCookieKeyMapping = { bitbucket: 'bitbucket-token', } -export function generatePath({ path, query }) { - const baseUrl = `${config.API_URL}/internal` +export function generatePath({ path, query, isUploadPath = false }) { + const baseUrl = isUploadPath ? config.API_URL : config.INTERNAL_API const queryString = qs.stringify(snakeifyKeys(query), { arrayFormat: 'repeat', }) diff --git a/src/shared/api/helpers.spec.js b/src/shared/api/helpers.spec.js index d1ab637690..52fab1562c 100644 --- a/src/shared/api/helpers.spec.js +++ b/src/shared/api/helpers.spec.js @@ -19,6 +19,12 @@ describe('generatePath', () => { generatePath({ path: '/epic', query: { rocket: 'league', fort: 'nite' } }) ).toStrictEqual(`${config.API_URL}/internal/epic?rocket=league&fort=nite`) }) + + it('generates a upload report path when use isUploadPath', () => { + expect(generatePath({ path: '/upload', isUploadPath: true })).toStrictEqual( + `${config.API_URL}/upload` + ) + }) }) describe('getHeaders', () => {