Support async file conversions (#326)

* Support async file conversions
Fixes #325

* Add loop

* Add exit condition

* More tweaks

* Clean up

* Fix large binary STL

* Add error messages

* Reduce timeout
This commit is contained in:
Pierre Jacquier
2024-01-29 16:27:46 +01:00
committed by GitHub
parent ffbc846ca1
commit b2cf16b400
4 changed files with 57 additions and 17 deletions

View File

@ -22,7 +22,7 @@ describe('Function downloadFile', () => {
'example.obj'
)
// TODO: add hash validation or something like that
expect(response).toHaveLength(37077)
expect(await response.text()).toHaveLength(37077)
})
it('downloads a public LFS github file', async () => {
@ -36,6 +36,6 @@ describe('Function downloadFile', () => {
'Part1.SLDPRT'
)
// TODO: add hash validation or something like that
expect(response).toHaveLength(70702)
expect(await response.text()).toHaveLength(70702)
})
})

View File

@ -1,5 +1,5 @@
import { Octokit } from '@octokit/rest'
import { Client, file } from '@kittycad/lib'
import { api_calls, Client, file } from '@kittycad/lib'
import { ContentFile, DiffEntry, FileBlob, FileDiff } from './types'
import {
FileExportFormat_type,
@ -35,7 +35,7 @@ export async function downloadFile(
repo: string,
ref: string,
path: string
): Promise<string> {
): Promise<Blob> {
// First get some info on the blob with the Contents api
const content = await octokit.rest.repos.getContent({
owner,
@ -54,15 +54,16 @@ export async function downloadFile(
console.log(`Downloading ${contentFile.download_url}...`)
const response = await fetch(contentFile.download_url)
if (!response.ok) throw response
return await response.text()
return await response.blob()
}
async function convert(
client: Client,
body: string,
blob: Blob,
extension: string,
outputFormat = 'obj'
) {
const body = await blob.arrayBuffer()
if (extension === outputFormat) {
console.log(
'Skipping conversion, as extension is equal to outputFormat'
@ -76,9 +77,26 @@ async function convert(
output_format: outputFormat as FileExportFormat_type,
})
const key = `source.${outputFormat}`
if ('error_code' in response || !response.outputs[key]) throw response
const { status, id, outputs } = response
if ('error_code' in response) throw response
const { id } = response
let { status, outputs } = response
console.log(`File conversion: ${id}, ${status}`)
let retries = 0
while (status !== 'completed' && status !== 'failed') {
if (retries >= 60) {
console.log('Async conversion took too long, aborting.')
break
}
retries++
await new Promise(resolve => setTimeout(resolve, 1000))
const response = await api_calls.get_async_operation({ client, id })
if ('error_code' in response) throw response
status = response.status
console.log(`File conversion: ${id}, ${status} (retry #${retries})`)
if ('outputs' in response) {
outputs = response.outputs
}
}
return outputs[key]
}

View File

@ -6,6 +6,7 @@ import { createPortal } from 'react-dom'
import { Loading } from '../Loading'
import { CadBlob } from './CadBlob'
import { ColorModeWithAuto } from '@primer/react/lib/ThemeProvider'
import { ErrorMessage } from './ErrorMessage'
function CadBlobPortal({
element,
@ -20,6 +21,7 @@ function CadBlobPortal({
sha: string
filename: string
}): React.ReactElement {
const [loading, setLoading] = useState(true)
const [richBlob, setRichBlob] = useState<FileBlob>()
const [richSelected, setRichSelected] = useState(true)
const [toolbarContainer, setToolbarContainer] = useState<HTMLElement>()
@ -65,14 +67,17 @@ function CadBlobPortal({
useEffect(() => {
;(async () => {
setLoading(true)
const response = await chrome.runtime.sendMessage({
id: MessageIds.GetFileBlob,
data: { owner, repo, sha, filename },
})
if ('error' in response) {
console.log(response.error)
setLoading(false)
} else {
setRichBlob(response as FileBlob)
setLoading(false)
}
})()
}, [owner, repo, sha, filename])
@ -115,10 +120,16 @@ function CadBlobPortal({
width: '100%',
}}
>
{richBlob ? (
<CadBlob blob={richBlob.blob} />
) : (
{loading ? (
<Loading />
) : (
<>
{richBlob ? (
<CadBlob blob={richBlob.blob} />
) : (
<ErrorMessage />
)}
</>
)}
</Box>,
blobContainer

View File

@ -7,6 +7,7 @@ import { Loading } from '../Loading'
import { CadDiff } from './CadDiff'
import { SourceRichToggle } from './SourceRichToggle'
import { ColorModeWithAuto } from '@primer/react/lib/ThemeProvider'
import { ErrorMessage } from './ErrorMessage'
function CadDiffPortal({
element,
@ -23,6 +24,7 @@ function CadDiffPortal({
sha: string
parentSha: string
}): React.ReactElement {
const [loading, setLoading] = useState(true)
const [richDiff, setRichDiff] = useState<FileDiff>()
const [richSelected, setRichSelected] = useState(true)
const [toolbarContainer, setToolbarContainer] = useState<HTMLElement>()
@ -54,14 +56,17 @@ function CadDiffPortal({
useEffect(() => {
;(async () => {
setLoading(true)
const response = await chrome.runtime.sendMessage({
id: MessageIds.GetFileDiff,
data: { owner, repo, sha, parentSha, file },
})
if ('error' in response) {
console.log(response.error)
setLoading(false)
} else {
setRichDiff(response as FileDiff)
setLoading(false)
}
})()
}, [file, owner, repo, sha, parentSha])
@ -87,13 +92,19 @@ function CadDiffPortal({
{diffContainer &&
createPortal(
<Box sx={{ display: richSelected ? 'block' : 'none' }}>
{richDiff ? (
<CadDiff
before={richDiff.before}
after={richDiff.after}
/>
) : (
{loading ? (
<Loading />
) : (
<>
{richDiff ? (
<CadDiff
before={richDiff.before}
after={richDiff.after}
/>
) : (
<ErrorMessage />
)}
</>
)}
</Box>,
diffContainer