Skip to content

Presigned url #3724

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -16,6 +18,7 @@ function removeReactAppPrefix(obj) {

const config = {
...defaultConfig,
INTERNAL_API,
...removeReactAppPrefix(process.env),
...window.configEnv,
}
Expand Down
29 changes: 21 additions & 8 deletions src/pages/CommitPage/CommitPage.spec.js
Original file line number Diff line number Diff line change
@@ -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: {
Expand Down Expand Up @@ -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(
<MemoryRouter initialEntries={['/gh/test/test-repo/commit/abcd']}>
<Route path="/:provider/:owner/:repo/commit/:commit">
<CommitPage />
</Route>
</MemoryRouter>
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={['/gh/test/test-repo/commit/abcd']}>
<Route path="/:provider/:owner/:repo/commit/:commit">
<CommitPage />
</Route>
</MemoryRouter>
</QueryClientProvider>
)
}

Expand Down Expand Up @@ -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(
<MemoryRouter
Expand Down Expand Up @@ -212,6 +224,9 @@ describe('CommitPage', () => {

describe('FileViewer', () => {
function setup(data) {
useUploadPresignedUrl.mockReturnValue({
data: '/archive/v4/raw/2022-06-23/..',
})
useCommit.mockReturnValue(data)
useFileWithMainCoverage.mockReturnValue({
data: fileData,
Expand All @@ -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())
})
})

Expand Down
14 changes: 5 additions & 9 deletions src/pages/CommitPage/UploadsCard/Upload.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import PropTypes from 'prop-types'

import config from 'config'

import { useUploadPresignedUrl } from 'services/uploadPresignedUrl'
import {
ErrorCodeEnum,
UploadStateEnum,
Expand All @@ -24,6 +23,8 @@ const Upload = ({
state,
}) => {
const isCarriedForward = uploadType === UploadTypeEnum.CARRIED_FORWARD
const { data } = useUploadPresignedUrl({ path :downloadUrl })
const presignedGetUrl = data?.presignedUrl

return (
<div className="py-2 px-4 flex flex-col gap-1">
Expand Down Expand Up @@ -56,13 +57,8 @@ const Upload = ({
<span className="text-ds-gray-quinary text-xs">carry-forward</span>
)}
</div>
{downloadUrl && (
<A
href={`${config.API_URL}${downloadUrl}`}
hook="download report"
download
isExternal
>
{presignedGetUrl && (
<A href={presignedGetUrl} isExternal hook="get-presigned-url" download>
Download
</A>
)}
Expand Down
106 changes: 75 additions & 31 deletions src/pages/CommitPage/UploadsCard/Upload.spec.js
Original file line number Diff line number Diff line change
@@ -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(<Upload {...props} />)
function setup({ props, data = mockedPresignedUrl }) {
useUploadPresignedUrl.mockReturnValue({ data })

render(
<MemoryRouter initialEntries={['/gh/codecov/test']}>
<Route path="/:provider/:owner/:repo">
<QueryClientProvider client={queryClient}>
<Upload {...props} />
</QueryClientProvider>
</Route>
</MemoryRouter>
)
}

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',
},
})
})

Expand All @@ -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', () => {
Expand All @@ -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()
Expand All @@ -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', () => {
Expand All @@ -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()
Expand All @@ -103,70 +129,88 @@ 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()
expect(screen.getByText(/upload is empty/)).toBeInTheDocument()
})
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()
})
Expand Down
14 changes: 13 additions & 1 deletion src/pages/CommitPage/UploadsCard/UploadsCard.spec.js
Original file line number Diff line number Diff line change
@@ -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(<UploadsCard />)
render(
<MemoryRouter initialEntries={['/gh/codecov/test']}>
<Route path="/:provider/:owner/:repo">
<QueryClientProvider client={queryClient}>
<UploadsCard />
</QueryClientProvider>
</Route>
</MemoryRouter>
)
}

describe('renders', () => {
Expand Down
15 changes: 15 additions & 0 deletions src/services/uploadPresignedUrl/hooks.js
Original file line number Diff line number Diff line change
@@ -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 })
})
}
Loading