import jszip from 'jszip' import classNames from 'classnames' import React, { useState } from 'react' import { useAsyncEffect } from 'use-async-effect' import { Report } from './Report' import { IReport } from '../report' import { IDict } from '../common' export interface IWorkflowProps { /** * A GitHub token with `public_repo` permissions */ token: string /** * A GitHub user name */ user?: string /** * A GitHub repo name */ repo?: string /** * A GitHub flow name */ flow?: string /** * A GitHub run id */ run?: string /** * A callback on submit */ callback?: ( error: Error | undefined, props: { user: string repo: string flow: string run?: string } ) => void } /** * Visual component for Frictionless Repository flow */ export function Workflow(props: IWorkflowProps) { const { token, callback } = props const [user, setUser] = useState(props.user || '') const [repo, setRepo] = useState(props.repo || '') const [flow, setFlow] = useState(props.flow || '') const [run, setRun] = useState(props.run || '') const [report, setReport] = useState(null) const [progress, setProgress] = useState(100) const [error, setError] = useState('') const [badge, setBadge] = useState(false) const isFormDisabled = !user || !repo || !flow const isLinkDisabled = !user || !repo || !flow || !run // Mount useAsyncEffect(async (isMounted) => { if (!user || !repo || !flow) return const report = await loadReport({ token, user, repo, flow, run, setRun, setProgress }) if (!report) return setError('Cannot load a report') if (!isMounted()) return if (callback) callback(undefined, { user, repo, flow, run }) setReport(report) }, []) // Submit const handleSubmit = async (ev: React.SyntheticEvent) => { ev.preventDefault() if (!user || !repo || !flow) return setReport(null) setError('') const report = await loadReport({ token, user, repo, flow, setRun, setProgress }) if (!report) return setError('Cannot load a report') if (callback) callback(undefined, { user, repo, flow, run }) setReport(report) } // Render return (
{ setUser(ev.target.value) setRun('') setReport(null) setError('') setBadge(false) }} />
{ setRepo(ev.target.value) setRun('') setReport(null) setError('') setBadge(false) }} />
{ setFlow(ev.target.value) setRun('') setReport(null) setError('') setBadge(false) }} />
{badge && (
Insert this code snippet into your README.md file
)} {error && (
{error}
)} {report && }
) } // Helpers async function loadReport(props: { token: string user: string repo: string flow: string run?: string setRun: (run: string) => void setProgress: (progress: number) => void }) { const { token, user, repo, flow, run, setRun, setProgress } = props // Request async function makeRequest(path: string) { return fetch(`https://api.github.com${path}`, { headers: { Accept: 'application/vnd.github.v3+json', Authorization: `token ${token}`, }, }) } // Workflow async function getWorkflowId() { const path = `/repos/${user}/${repo}/actions/workflows` const res = await makeRequest(path) const data = await res.json() const predicat = (item: IDict) => item.name.toLowerCase() === flow const workflow = data.workflows.filter(predicat)[0] return workflow.id } // Run async function getRunId(workflowId: string) { const path = `/repos/${user}/${repo}/actions/workflows/${workflowId}/runs` const res = await makeRequest(path) const data = await res.json() const predicat = (item: IDict) => item.status === 'completed' const run = data.workflow_runs.filter(predicat)[0] return run.id } // Artifact async function getArtifactId(runId: string) { const path = `/repos/${user}/${repo}/actions/runs/${runId}/artifacts` const res = await makeRequest(path) const data = await res.json() const predicat = (item: IDict) => item.name === 'report' const artifact = data.artifacts.filter(predicat)[0] return artifact.id } // File async function getFileBuffer(artifactId: string) { const path = `/repos/${user}/${repo}/actions/artifacts/${artifactId}/zip` const res = await makeRequest(path) const buffer = await res.blob() return buffer } // Report async function getReport(buffer: Blob) { const archive = await jszip.loadAsync(buffer) const contents = await archive.file('report.json')!.async('string') const report = JSON.parse(contents) return report } // Main try { setProgress(20) const runId = run || (await getRunId(await getWorkflowId())) setRun(runId.toString()) setProgress(40) const artifactId = await getArtifactId(runId) setProgress(60) const fileBuffer = await getFileBuffer(artifactId) setProgress(80) const report = await getReport(fileBuffer) setProgress(100) return report } catch (error) { setProgress(100) console.log(`Error in loading report: ${error}`) } }