Skip to content

Commit eeebf70

Browse files
authored
Merge pull request #747 from vishnoianil/download-taxonomy-gh
Add support for download taxonomy to github mode
2 parents 4655f92 + b363f80 commit eeebf70

File tree

5 files changed

+139
-12
lines changed

5 files changed

+139
-12
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// src/app/api/github/download/route.ts
2+
'use server';
3+
import { NextRequest, NextResponse } from 'next/server';
4+
import { getToken } from 'next-auth/jwt';
5+
import { GITHUB_API_URL } from '@/types/const';
6+
import { getGitHubUsername } from '@/utils/github';
7+
8+
const UPSTREAM_REPO_NAME = process.env.NEXT_PUBLIC_TAXONOMY_REPO!;
9+
10+
export async function POST(req: NextRequest) {
11+
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET! });
12+
13+
if (!token || !token.accessToken) {
14+
console.error('Unauthorized: Missing or invalid access token');
15+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
16+
}
17+
18+
const githubToken = token.accessToken as string;
19+
const headers = {
20+
'Content-Type': 'application/json',
21+
Authorization: `Bearer ${githubToken}`,
22+
Accept: 'application/vnd.github+json',
23+
'X-GitHub-Api-Version': '2022-11-28'
24+
};
25+
26+
const githubUsername = await getGitHubUsername(headers);
27+
try {
28+
const { branchName } = await req.json();
29+
30+
if (!branchName || typeof branchName !== 'string') {
31+
return NextResponse.json({ error: 'contribution branch does not exist on remote taxonomy.' }, { status: 400 });
32+
}
33+
34+
const tarballUrl = `${GITHUB_API_URL}/repos/${githubUsername}/${UPSTREAM_REPO_NAME}/tarball/${branchName}`;
35+
const tarballRes = await fetch(tarballUrl, {
36+
headers: headers
37+
});
38+
39+
if (!tarballRes.ok) {
40+
return NextResponse.json({ error: 'Failed to download taxonomy for the contribution.' }, { status: 500 });
41+
}
42+
43+
return new NextResponse(tarballRes.body, {
44+
headers: {
45+
'Content-Type': 'application/gzip',
46+
'Content-Disposition': `attachment`,
47+
'Cache-Control': 'no-store'
48+
}
49+
});
50+
} catch (error) {
51+
console.error('failed to download taxonomy for the contribution:', error);
52+
return NextResponse.json({ error: error }, { status: 500 });
53+
}
54+
}

src/app/api/download/route.ts renamed to src/app/api/native/download/route.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,37 @@
1-
// src/app/api/download/route.ts
1+
// src/app/api/native/download/route.ts
22
'use server';
3-
import { NextResponse } from 'next/server';
3+
import { NextRequest, NextResponse } from 'next/server';
44
import { spawn } from 'child_process';
55
import fs from 'fs';
66
import path from 'path';
7+
import * as git from 'isomorphic-git';
78

8-
// GET handler now takes the Request so we can watch for aborts
9-
export async function GET(request: Request) {
10-
const rootDir = process.env.NEXT_PUBLIC_TAXONOMY_ROOT_DIR;
9+
const LOCAL_TAXONOMY_ROOT_DIR = process.env.NEXT_PUBLIC_LOCAL_TAXONOMY_ROOT_DIR || `${process.env.HOME}/.instructlab-ui`;
10+
11+
export async function POST(req: NextRequest) {
12+
const rootDir = LOCAL_TAXONOMY_ROOT_DIR;
1113
if (!rootDir) {
12-
return NextResponse.json({ error: 'NEXT_PUBLIC_TAXONOMY_ROOT_DIR is not configured' }, { status: 500 });
14+
return NextResponse.json({ error: 'Failed to find the local taxonomy that contains the contribution.' }, { status: 500 });
1315
}
16+
const { branchName } = await req.json();
1417

1518
const taxonomyDir = path.join(rootDir, 'taxonomy');
1619
try {
1720
await fs.promises.access(taxonomyDir, fs.constants.R_OK);
1821
} catch {
19-
return NextResponse.json({ error: 'Taxonomy directory not found or not readable' }, { status: 404 });
22+
return NextResponse.json({ error: 'Taxonomy directory not found or not readable' }, { status: 500 });
2023
}
2124

25+
// Checkout the new branch
26+
await git.checkout({ fs, dir: taxonomyDir, ref: branchName });
27+
2228
// Spawn tar to write gzipped archive to stdout
2329
const tar = spawn('tar', ['-czf', '-', '-C', rootDir, 'taxonomy'], {
2430
stdio: ['ignore', 'pipe', 'inherit']
2531
});
2632

2733
// If the client aborts, make sure to kill the tar process
28-
request.signal.addEventListener('abort', () => {
34+
req.signal.addEventListener('abort', () => {
2935
tar.kill('SIGTERM');
3036
});
3137

@@ -51,7 +57,7 @@ export async function GET(request: Request) {
5157
status: 200,
5258
headers: {
5359
'Content-Type': 'application/gzip',
54-
'Content-Disposition': 'attachment; filename="taxonomy.tar.gz"',
60+
'Content-Disposition': `attachment`,
5561
'Cache-Control': 'no-store'
5662
}
5763
});

src/components/Dashboard/Github/dashboard.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import * as React from 'react';
33
import { useSession } from 'next-auth/react';
44
import Image from 'next/image';
55
import { useRouter } from 'next/navigation';
6-
import { fetchPullRequests, getGitHubUsername } from '../../../utils/github';
76
import { DraftEditFormInfo, PullRequest } from '@/types';
87
import { useState } from 'react';
98
import {
@@ -40,6 +39,8 @@ import {
4039
} from '@patternfly/react-core';
4140
import { ExternalLinkAltIcon, OutlinedQuestionCircleIcon, GithubIcon, EllipsisVIcon, PficonTemplateIcon } from '@patternfly/react-icons';
4241
import { deleteDraftData, fetchDraftContributions } from '@/components/Contribute/Utils/autoSaveUtils';
42+
import { handleTaxonomyDownload } from '@/utils/taxonomy';
43+
import { fetchPullRequests, getGitHubUsername } from '@/utils/github';
4344

4445
const InstructLabLogo: React.FC = () => <Image src="/InstructLab-LogoFile-RGB-FullColor.svg" alt="InstructLab Logo" width={256} height={256} />;
4546

@@ -48,6 +49,7 @@ const DashboardGithub: React.FunctionComponent = () => {
4849
const [pullRequests, setPullRequests] = React.useState<PullRequest[]>([]);
4950
const [draftContributions, setDraftContributions] = React.useState<DraftEditFormInfo[]>([]);
5051
const [isFirstPullDone, setIsFirstPullDone] = React.useState<boolean>(false);
52+
const [isDownloadDone, setIsDownloadDone] = React.useState<boolean>(true);
5153
const [isLoading, setIsLoading] = useState<boolean>(true);
5254
//const [error, setError] = React.useState<string | null>(null);
5355
const [isActionMenuOpen, setIsActionMenuOpen] = React.useState<{ [key: number | string]: boolean }>({});
@@ -195,6 +197,16 @@ const DashboardGithub: React.FunctionComponent = () => {
195197
</ModalBody>
196198
</Modal>
197199
)}
200+
{!isDownloadDone && (
201+
<Modal variant={ModalVariant.small} title="Retrieving taxonomy tar file" isOpen onClose={() => setIsDownloadDone(true)}>
202+
<ModalBody>
203+
<div>
204+
<Spinner size="md" />
205+
Retrieving the taxonomy compressed file with the contributed data.
206+
</div>
207+
</ModalBody>
208+
</Modal>
209+
)}
198210
{isFirstPullDone && pullRequests.length === 0 && draftContributions.length === 0 ? (
199211
<EmptyState titleText="Welcome to InstructLab" headingLevel="h4" icon={InstructLabLogo}>
200212
<EmptyStateBody>
@@ -347,6 +359,15 @@ const DashboardGithub: React.FunctionComponent = () => {
347359
</DropdownItem>
348360
)}
349361
</DropdownList>
362+
<DropdownItem
363+
key="download-taxonomy"
364+
onClick={() => {
365+
setIsDownloadDone(false);
366+
handleTaxonomyDownload({ branchName: pr.head.ref, isGithubMode: true, setIsDownloadDone });
367+
}}
368+
>
369+
Download taxonomy
370+
</DropdownItem>
350371
</Dropdown>
351372
)
352373
}}

src/components/Dashboard/Native/dashboard.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { ExpandableSection } from '@patternfly/react-core/dist/esm/components/Ex
4747
import { v4 as uuidv4 } from 'uuid';
4848
import { DraftEditFormInfo } from '@/types';
4949
import { deleteDraftData, fetchDraftContributions } from '@/components/Contribute/Utils/autoSaveUtils';
50+
import { handleTaxonomyDownload } from '@/utils/taxonomy';
5051

5152
const InstructLabLogo: React.FC = () => <Image src="/InstructLab-LogoFile-RGB-FullColor.svg" alt="InstructLab Logo" width={256} height={256} />;
5253

@@ -79,6 +80,7 @@ const DashboardNative: React.FunctionComponent = () => {
7980
const [selectedDraftContribution, setSelectedDraftContribution] = React.useState<string | null>(null);
8081
const [isPublishing, setIsPublishing] = React.useState(false);
8182
const [expandedFiles, setExpandedFiles] = React.useState<Record<string, boolean>>({});
83+
const [isDownloadDone, setIsDownloadDone] = React.useState<boolean>(true);
8284

8385
const router = useRouter();
8486

@@ -393,6 +395,17 @@ const DashboardNative: React.FunctionComponent = () => {
393395
))}
394396
</AlertGroup>
395397

398+
{!isDownloadDone && (
399+
<Modal variant={ModalVariant.small} title="Retrieving taxonomy tar file" isOpen onClose={() => setIsDownloadDone(true)}>
400+
<ModalBody>
401+
<div>
402+
<Spinner size="md" />
403+
Retrieving the taxonomy compressed file with the contributed data.
404+
</div>
405+
</ModalBody>
406+
</Modal>
407+
)}
408+
396409
{isLoading ? (
397410
<Spinner size="lg" />
398411
) : branches.length === 0 && draftContributions.length === 0 ? (
@@ -546,8 +559,8 @@ const DashboardNative: React.FunctionComponent = () => {
546559
<DropdownItem
547560
key="download-taxonomy"
548561
onClick={() => {
549-
// this will trigger the browser download
550-
window.location.href = '/api/download';
562+
setIsDownloadDone(false);
563+
handleTaxonomyDownload({ branchName: branch.name, isGithubMode: false, setIsDownloadDone });
551564
}}
552565
>
553566
Download taxonomy

src/utils/taxonomy.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// src/utils/taxonomy.ts
2+
const GITHUB_TAXONOMY_DOWNLOAD_URL = '/api/github/download';
3+
const NATIVE_TAXONOMY_DOWNLOAD_URL = '/api/native/download';
4+
5+
interface TaxonomyDownloadProp {
6+
branchName: string;
7+
isGithubMode: boolean;
8+
setIsDownloadDone: (isDownloadDone: boolean) => void;
9+
}
10+
export async function handleTaxonomyDownload({ branchName, isGithubMode, setIsDownloadDone }: TaxonomyDownloadProp) {
11+
const res = await fetch(isGithubMode ? GITHUB_TAXONOMY_DOWNLOAD_URL : NATIVE_TAXONOMY_DOWNLOAD_URL, {
12+
method: 'POST',
13+
headers: { 'Content-Type': 'application/json' },
14+
body: JSON.stringify({
15+
branchName: branchName
16+
})
17+
});
18+
19+
if (!res.ok) {
20+
alert('Failed to download the taxonomy');
21+
return { message: res.statusText, status: res.status };
22+
}
23+
24+
setIsDownloadDone(true);
25+
26+
const blob = await res.blob();
27+
const url = window.URL.createObjectURL(blob);
28+
const a = document.createElement('a');
29+
a.href = url;
30+
a.download = `taxonomy-${branchName}.tar.gz`;
31+
a.click();
32+
window.URL.revokeObjectURL(url);
33+
}

0 commit comments

Comments
 (0)