Skip to content

Commit

Permalink
Merge pull request #40 from safeinsights/file-drop
Browse files Browse the repository at this point in the history
Replace container push screen with file upload
  • Loading branch information
nathanstitt authored Jan 8, 2025
2 parents ea9f8a4 + e57a016 commit b489104
Show file tree
Hide file tree
Showing 24 changed files with 5,810 additions and 5,416 deletions.
3 changes: 3 additions & 0 deletions bin/prod-docker-build
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ if [ -z "$$DOCKER_TAG" ]; then
exit 1
fi

# provenence is disabled because without it docker generates a "multi-platform index" that is incompatible with lambda
# https://github.com/docker/buildx/issues/1509#issuecomment-1378538197
docker build \
--file Dockerfile.prod \
--provenance=false \
--platform=linux/amd64 \
--secret id=envs,src=.env \
--progress=plain \
Expand Down
10,843 changes: 5,461 additions & 5,382 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@
"@aws-sdk/client-s3": "^3.722.0",
"@aws-sdk/credential-provider-ini": "^3.721.0",
"@aws-sdk/lib-storage": "^3.722.0",
"@aws-sdk/s3-presigned-post": "^3.697.0",
"@aws-sdk/s3-request-presigner": "^3.722.0",
"@clerk/nextjs": "^6.9.6",
"@faker-js/faker": "^9.3.0",
"@hookform/resolvers": "^3.9.1",
"@mantine/core": "^7.15",
"@mantine/dropzone": "^7.14.1",
"@mantine/form": "^7.15",
"@mantine/modals": "^7.15",
"@mantine/notifications": "^7.15.2",
Expand Down Expand Up @@ -100,6 +102,5 @@
"typescript-eslint": "^8.19.1",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "*"
},
"overrides": {}
}
}
47 changes: 47 additions & 0 deletions src/app/api/dev/upload-code/[studyRunIdentifier]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { db } from '@/database'
import { NextResponse } from 'next/server'
import { PROD_ENV } from '@/server/config'
import { getUploadTmpDirectory } from '@/server/config'
import { pathForStudyRunCode } from '@/lib/paths'
import path from 'path'
import fs from 'fs'

export const dynamic = 'force-dynamic' // defaults to auto

export async function POST(req: Request, { params }: { params: Promise<{ studyRunIdentifier: string }> }) {
const { studyRunIdentifier } = await params
if (PROD_ENV) {
return NextResponse.json({ error: 'This route is only available in development' }, { status: 403 })
}

const info = await db
.selectFrom('studyRun')
.innerJoin('study', 'study.id', 'studyRun.studyId')
.innerJoin('member', 'member.id', 'study.memberId')
.select(['studyRun.id as studyRunId', 'studyId', 'member.identifier as memberIdentifier'])
.where('studyRun.id', '=', studyRunIdentifier)
.executeTakeFirst()

if (!info) {
return NextResponse.json({ status: 'fail', error: 'run not found' }, { status: 404 })
}

const formData = await req.formData()
const file = formData.get('file') as File

const dir = path.join(getUploadTmpDirectory(), pathForStudyRunCode(info), path.dirname(file.name))
fs.mkdirSync(dir, { recursive: true })
const filePath = path.join(dir, path.basename(file.name))
const buffer = await file.arrayBuffer()
await fs.promises.writeFile(filePath, Buffer.from(buffer))

await db
.updateTable('studyRun')
.set({
status: 'CODE-SUBMITTED',
})
.where('id', '=', info.studyRunId)
.executeTakeFirstOrThrow()

return new NextResponse('ok', { status: 200 })
}
6 changes: 3 additions & 3 deletions src/app/api/run/[runId]/results/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const POST = wrapApiMemberAction(async (req: Request, { params }: { param
}

// join is a security check to ensure the run is owned by the member
const run = await db
const info = await db
.selectFrom('studyRun')
.innerJoin('study', (join) =>
join.onRef('study.id', '=', 'studyRun.studyId').on('study.memberId', '=', member.id),
Expand All @@ -29,13 +29,13 @@ export const POST = wrapApiMemberAction(async (req: Request, { params }: { param
.where('studyRun.id', '=', runId)
.executeTakeFirst()

if (!run) {
if (!info) {
return NextResponse.json({ status: 'fail', error: 'run not found' }, { status: 404 })
}

await attachResultsToStudyRun(
{
...run,
...info,
memberIdentifier: member.identifier,
},
contents,
Expand Down
2 changes: 0 additions & 2 deletions src/app/api/services/code-push/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { NextResponse } from 'next/server'

const schema = z.object({
runId: z.string().uuid(),
codePath: z.string(),
fileSize: z.number(),
fileCount: z.number(),
})
Expand All @@ -20,7 +19,6 @@ export async function POST(req: Request) {
.where('status', '=', 'INITIATED')
.set({
status: 'CODE-SUBMITTED',
codePath: body.codePath,
uploadedAt: new Date(),
fileSize: body.fileSize,
fileCount: body.fileCount,
Expand Down
2 changes: 1 addition & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Metadata } from 'next'

import './globals.css'

import '@mantine/core/styles.layer.css'
import 'mantine-datatable/styles.layer.css'
import '@mantine/dropzone/styles.layer.css'

import { Providers } from './providers'
import { AppLayout } from '@/components/app-layout'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const updateStudyRunStatusAction = async (info: MinimalRunInfo, status: S
}

export const dataForRunAction = async (studyRunIdentifier: string) => {
const runId = b64toUUID(studyRunIdentifier)
const run = await db
.selectFrom('studyRun')
.innerJoin('study', 'study.id', 'studyRun.studyId')
Expand All @@ -27,10 +28,12 @@ export const dataForRunAction = async (studyRunIdentifier: string) => {
'study.title as studyTitle',
'member.identifier as memberIdentifier',
])
.where('studyRun.id', '=', b64toUUID(studyRunIdentifier))
.where('studyRun.id', '=', runId)
.executeTakeFirst()

let manifest: CodeManifest = {
runId,
language: 'r',
files: {},
size: 0,
tree: { label: '', value: '', size: 0, children: [] },
Expand Down
11 changes: 0 additions & 11 deletions src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,6 @@ function getQueryClient() {
}
}

export const TestingProviders: React.FC<Props> = ({ children }) => {
const queryClient = getQueryClient()
return (
<MantineProvider theme={theme}>
<QueryClientProvider client={queryClient}>
<ModalsProvider>{children}</ModalsProvider>
</QueryClientProvider>
</MantineProvider>
)
}

export const Providers: React.FC<Props> = ({ children }) => {
const queryClient = getQueryClient()

Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { uuidToB64 } from '@/lib/uuid'
import { revalidatePath } from 'next/cache'
import { attachSimulatedResultsToStudyRun, storageForResultsFile } from '@/server/results'
import { sleep } from '@/lib/util'
import { SIMULATE_RESULTS_UPLOAD, USING_CONTAINER_REGISTRY } from '@/server/config'
import { queryRunResult } from '@/server/queries'
import { SIMULATE_RESULTS_UPLOAD, USING_CONTAINER_REGISTRY } from '@/server/config'
import { fetchStudyRunResults } from '@/server/aws'

const AllowedStatusChanges: Array<StudyStatus> = ['APPROVED', 'REJECTED'] as const
Expand Down
15 changes: 15 additions & 0 deletions src/app/researcher/study/[encodedStudyId]/upload/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import { b64toUUID } from '@/lib/uuid'
import { db } from '@/database'
import { MinimalRunInfo } from '@/lib/types'
import { urlForStudyRunCodeUpload, type PresignedPost } from '@/server/aws'
import { USING_S3_STORAGE } from '@/server/config'

export const getLatestStudyRunAction = async ({ encodedStudyId }: { encodedStudyId: string }) => {
return await db
Expand All @@ -11,6 +14,7 @@ export const getLatestStudyRunAction = async ({ encodedStudyId }: { encodedStudy
'study.id',
'study.title',
'study.containerLocation',
'member.identifier as memberIdentifier',
'member.name as memberName',
({ selectFrom }) =>
selectFrom('studyRun')
Expand All @@ -23,3 +27,14 @@ export const getLatestStudyRunAction = async ({ encodedStudyId }: { encodedStudy
.where('study.id', '=', b64toUUID(encodedStudyId))
.executeTakeFirst()
}

export async function getUploadUrlForStudyRunCodeAction(info: MinimalRunInfo): Promise<PresignedPost> {
if (USING_S3_STORAGE) {
return urlForStudyRunCodeUpload(info)
} else {
return {
url: `/api/dev/upload-code/${info.studyRunId}`,
fields: {},
}
}
}
10 changes: 7 additions & 3 deletions src/app/researcher/study/[encodedStudyId]/upload/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import React from 'react'
import { Button, Flex, Text, Title, Container } from '@mantine/core'
import { getLatestStudyRunAction } from './actions'
import Link from 'next/link'
import { PushInstructions } from '@/components/push-instructions'
import { UploadStudyRunCode } from '@/components/upload-study-run-code'
import { AlertNotFound } from '@/components/errors'
import { getUploadUrlForStudyRunCodeAction } from './actions'

export default async function UploadPage(props: { params: Promise<{ encodedStudyId: string }> }) {
const params = await props.params
Expand All @@ -19,15 +20,18 @@ export default async function UploadPage(props: { params: Promise<{ encodedStudy

return (
<Container w="100%">
<Title mb="lg">OpenStax Study Proposal Step 2)</Title>
<Title mb="lg">{study.memberName} Study Proposal Step 2)</Title>
<Text
pt={10}
fs="italic"
>{`{ For the Pilot, communications steps and member review/approval are skipped }`}</Text>
<Text mb="xl" mt="lg" fw="bold">
For the Pilot, engineers use the following to containerize and upload code:
</Text>
<PushInstructions containerLocation={study.containerLocation} runId={study.pendingRunId} />
<UploadStudyRunCode
run={{ memberIdentifier: study.memberIdentifier, studyId: study.id, studyRunId: study.pendingRunId }}
getSignedURL={getUploadUrlForStudyRunCodeAction}
/>
<Flex justify="end" mt="lg">
<Link href="edit" passHref>
<Button>Next</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect, describe, it, vi } from 'vitest'
import { render } from '@testing-library/react'
import { Form } from './form'
import { TestingProviders } from '@/app/providers'
import { TestingProvidersWrapper } from '@/tests/providers'
import userEvent from '@testing-library/user-event'

import { onCreateStudyAction } from './actions'
Expand All @@ -14,9 +14,7 @@ describe('Member Start Page Form', () => {
it('submits form', async () => {
const { getByLabelText, getByRole, container } = render(
<Form memberId="1234" memberIdentifier="hello-world" />,
{
wrapper: TestingProviders,
},
TestingProvidersWrapper,
)
let title = '2srt'

Expand Down
Loading

0 comments on commit b489104

Please sign in to comment.