Skip to content

Commit 26b08a1

Browse files
Implement sidebar and stub out page for imported repository to show the charts
The sidebar queries the DDN from the client-side with `useSql` from `@madatdata/react`, using the default anonymous (thus read-only) credential to query the "metadata table" that includes the list of repositories that have had a succesful import, and it links to a page for each one, which is currently a stub but where we will show the chart(s) with Observable Plot.
1 parent 0451f1b commit 26b08a1

11 files changed

Lines changed: 184 additions & 73 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# This file contains public environment variables and is therefore checked into the repo
2+
# For secret environment variables, see `.env.local` which is _not_ checked into the repo
3+
# Read env-vars.d.ts for expected variable names
4+
# See more: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables
5+
6+
NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY=github-analytics-metadata
7+
NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_COMPLETED_TABLE=completed_repositories

examples/nextjs-import-airbyte-github-export-seafowl/.env.test.local

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ SPLITGRAPH_API_KEY="********************************"
88
SPLITGRAPH_API_SECRET="********************************"
99

1010
# This should match the username associated with the API key
11-
SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE="*****"
11+
NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE="*****"
1212

1313
# Create a GitHub token that can query the repositories you want to connect
1414
# For example, a token with read-only access to public repos is sufficient
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.charts {
2+
background: inherit;
3+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import style from "./Charts.module.css";
2+
3+
import type { ImportedRepository } from "../../types";
4+
5+
export interface ChartsProps {
6+
importedRepository: ImportedRepository;
7+
}
8+
9+
export const Charts = ({
10+
importedRepository: {
11+
githubNamespace,
12+
githubRepository,
13+
splitgraphNamespace,
14+
splitgraphRepository,
15+
},
16+
}: ChartsProps) => {
17+
return (
18+
<div className={style.charts}>
19+
Chart for{" "}
20+
<a href={`https://github.com/${githubNamespace}/${githubRepository}`}>
21+
github.com/{githubNamespace}/{githubRepository}
22+
</a>
23+
, based on{" "}
24+
<a
25+
href={`https://www.splitgraph.com/${splitgraphNamespace}/${splitgraphRepository}`}
26+
>
27+
splitgraph.com/{splitgraphNamespace}/{splitgraphRepository}
28+
</a>
29+
</div>
30+
);
31+
};

examples/nextjs-import-airbyte-github-export-seafowl/components/Sidebar.tsx

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,78 @@
1-
import React from "react";
1+
import React, { useMemo } from "react";
22
import Link from "next/link";
33
import styles from "./Sidebar.module.css";
4+
import { useSql } from "@madatdata/react";
45

5-
export interface GitHubRepository {
6-
namespace: string;
7-
repository: string;
8-
}
6+
import type { ImportedRepository } from "../types";
97

10-
interface SidebarProps {
11-
repositories: GitHubRepository[];
12-
}
8+
const META_REPOSITORY =
9+
process.env.NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY;
10+
const META_NAMESPACE =
11+
process.env.NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE;
12+
const META_TABLE =
13+
process.env.NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_COMPLETED_TABLE;
14+
15+
const useImportedRepositories = (): ImportedRepository[] => {
16+
const { response, error } = useSql<{
17+
githubNamespace: string;
18+
githubRepository: string;
19+
splitgraphNamespace: string;
20+
splitgraphRepository: string;
21+
}>(
22+
`
23+
WITH ordered_repos AS (
24+
SELECT
25+
github_namespace,
26+
github_repository,
27+
splitgraph_namespace,
28+
splitgraph_repository,
29+
completed_at
30+
FROM "${META_NAMESPACE}/${META_REPOSITORY}"."${META_TABLE}"
31+
ORDER BY completed_at DESC
32+
)
33+
SELECT DISTINCT
34+
github_namespace AS "githubNamespace",
35+
github_repository AS "githubRepository",
36+
splitgraph_namespace AS "splitgraphNamespace",
37+
splitgraph_repository AS "splitgraphRepository"
38+
FROM ordered_repos;
39+
`
40+
);
41+
42+
const repositories = useMemo(() => {
43+
if (error) {
44+
console.warn("Error fetching repositories:", error);
45+
return [];
46+
}
47+
48+
if (!response) {
49+
console.warn("No response received");
50+
return [];
51+
}
52+
53+
return response.rows ?? [];
54+
}, [error, response]);
55+
56+
return repositories;
57+
};
58+
59+
export const Sidebar = () => {
60+
const repositories = useImportedRepositories();
1361

14-
export const Sidebar = ({ repositories }: React.PropsWithRef<SidebarProps>) => {
1562
return (
1663
<aside className={styles.sidebar}>
1764
<div className={styles.importButtonContainer}>
18-
<Link href="/start-import" className={styles.importButton}>
65+
<Link href="/" className={styles.importButton}>
1966
Import Your Repository
2067
</Link>
2168
</div>
2269
<ul className={styles.repoList}>
2370
{repositories.map((repo, index) => (
2471
<li key={index}>
25-
<Link href={`/${repo.namespace}/${repo.repository}`}>
26-
{repo.namespace}/{repo.repository}
72+
<Link
73+
href={`/${repo.githubNamespace}/${repo.githubRepository}?splitgraphNamespace=${repo.splitgraphNamespace}&splitgraphRepository=${repo.splitgraphRepository}`}
74+
>
75+
{repo.githubNamespace}/{repo.githubRepository}
2776
</Link>
2877
</li>
2978
))}

examples/nextjs-import-airbyte-github-export-seafowl/env-vars.d.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace NodeJS {
4141
/**
4242
* The namespace of the repository in Splitgraph where metadata is stored
4343
* containing the state of imported GitHub repositories, which should contain
44-
* the repository `SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY`.
44+
* the repository `NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY`.
4545
*
4646
* This should be defined in `.env.local`, since it's not checked into Git
4747
* and can vary between users. It should match the username associated with
@@ -52,15 +52,15 @@ namespace NodeJS {
5252
* ```
5353
* miles/splitgraph-github-analytics.completed_repositories
5454
* ^^^^^
55-
* SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE=miles
55+
* NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE=miles
5656
* ```
5757
*/
58-
SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE: string;
58+
NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE: string;
5959

6060
/**
6161
* The repository (no namespace) in Splitgraph where metadata is stored
6262
* containing the state of imported GitHub repositories, which should be a
63-
* repository contained inside `SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE`.
63+
* repository contained inside `NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE`.
6464
*
6565
* This is defined by default in `.env` which is checked into Git.
6666
*
@@ -69,10 +69,10 @@ namespace NodeJS {
6969
* ```
7070
* miles/splitgraph-github-analytics.completed_repositories
7171
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
72-
* SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY=splitgraph-github-analytics
72+
* NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY=splitgraph-github-analytics
7373
* ```
7474
*/
75-
SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY: string;
75+
NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY: string;
7676

7777
/**
7878
* The name of the table containing completed repositories, which are inserted
@@ -86,9 +86,9 @@ namespace NodeJS {
8686
* ```
8787
* miles/splitgraph-github-analytics.completed_repositories
8888
* ^^^^^^^^^^^^^^^^^^^^^^
89-
* SPLITGRAPH_GITHUB_ANALYTICS_META_COMPLETED_TABLE=completed_repositories
89+
* NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_COMPLETED_TABLE=completed_repositories
9090
* ```
9191
*/
92-
SPLITGRAPH_GITHUB_ANALYTICS_META_COMPLETED_TABLE: string;
92+
NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_COMPLETED_TABLE: string;
9393
}
9494
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { BaseLayout } from "../../components/BaseLayout";
2+
3+
import { Sidebar } from "../../components/Sidebar";
4+
import { Charts } from "../../components/RepositoryAnalytics/Charts";
5+
import { useRouter } from "next/router";
6+
7+
import type { ImportedRepository } from "../../types";
8+
9+
const useImportedRepoFromURL = () => {
10+
const { query } = useRouter();
11+
12+
return (
13+
[
14+
["github_namespace", "githubNamespace"],
15+
["github_repository", "githubRepository"],
16+
["splitgraphNamespace", "splitgraphNamespace"],
17+
["splitgraphRepository", "splitgraphRepository"],
18+
] as [string, keyof ImportedRepository][]
19+
).reduce((acc, [queryParam, repoKey]) => {
20+
if (!query[queryParam] || Array.isArray(query[queryParam])) {
21+
throw new Error(`Invalid query params: unexpected type of ${queryParam}`);
22+
}
23+
24+
return {
25+
...acc,
26+
[repoKey]: query[queryParam] as string,
27+
};
28+
}, {} as ImportedRepository);
29+
};
30+
31+
const RepositoryAnalyticsPage = () => {
32+
return (
33+
<BaseLayout sidebar={<Sidebar />}>
34+
<Charts importedRepository={useImportedRepoFromURL()} />
35+
</BaseLayout>
36+
);
37+
};
38+
39+
export default RepositoryAnalyticsPage;
Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
import type { AppProps } from "next/app";
2+
import { SqlProvider, makeSplitgraphHTTPContext } from "@madatdata/react";
3+
import { useMemo } from "react";
4+
25
import "../components/global-styles/reset.css";
36
import "../components/global-styles/theme.css";
47

58
export default function GitHubAnalyticsApp({ Component, pageProps }: AppProps) {
6-
return <Component {...pageProps} />;
9+
const splitgraphDataContext = useMemo(
10+
() => makeSplitgraphHTTPContext({ credential: null }),
11+
[]
12+
);
13+
14+
return (
15+
<SqlProvider dataContext={splitgraphDataContext}>
16+
<Component {...pageProps} />
17+
</SqlProvider>
18+
);
719
}

examples/nextjs-import-airbyte-github-export-seafowl/pages/api/mark-import-export-complete.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ export type MarkImportExportCompleteResponseData =
1818
| MarkImportExportCompleteSuccessResponse
1919
| { error: string };
2020

21-
const META_NAMESPACE = process.env.SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE;
22-
const META_REPOSITORY = process.env.SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY;
23-
const META_TABLE = process.env.SPLITGRAPH_GITHUB_ANALYTICS_META_COMPLETED_TABLE;
21+
const META_NAMESPACE =
22+
process.env.NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE;
23+
const META_REPOSITORY =
24+
process.env.NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY;
25+
const META_TABLE =
26+
process.env.NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_COMPLETED_TABLE;
2427

2528
/**
2629
* To manually send a request, example:
@@ -42,7 +45,7 @@ export default async function handler(
4245
if (!META_NAMESPACE) {
4346
res.status(400).json({
4447
error:
45-
"Missing env var: SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE " +
48+
"Missing env var: NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE " +
4649
"Is it in .env.local or Vercel secrets?",
4750
});
4851
return;
@@ -51,7 +54,7 @@ export default async function handler(
5154
if (!META_REPOSITORY) {
5255
res.status(400).json({
5356
error:
54-
"Missing env var: SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY " +
57+
"Missing env var: NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY " +
5558
"Is it in .env or Vercel environment variables?",
5659
});
5760
return;
@@ -125,7 +128,7 @@ const markImportExportAsComplete = async ({
125128
// NOTE: We also assume that META_NAMESPACE is the same as destination namespace
126129
if (!username || username !== META_NAMESPACE) {
127130
throw new Error(
128-
"Authenticated user does not match SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE"
131+
"Authenticated user does not match NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE"
129132
);
130133
}
131134

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,14 @@
1-
import { SqlProvider, makeSplitgraphHTTPContext } from "@madatdata/react";
2-
import { useMemo } from "react";
3-
41
import { BaseLayout } from "../components/BaseLayout";
52

6-
import { Sidebar, type GitHubRepository } from "../components/Sidebar";
3+
import { Sidebar } from "../components/Sidebar";
74
import { Stepper } from "../components/ImportExportStepper/Stepper";
85

9-
const SplitgraphSampleQuery = () => {
10-
const splitgraphDataContext = useMemo(
11-
() => makeSplitgraphHTTPContext({ credential: null }),
12-
[]
13-
);
14-
15-
// Uses splitgraph.com by default (anon access supported for public data)
6+
const ImportPage = () => {
167
return (
17-
<SqlProvider dataContext={splitgraphDataContext}>
18-
<BaseLayout sidebar={<Sidebar repositories={sampleRepositories} />}>
19-
<Stepper />
20-
</BaseLayout>
21-
</SqlProvider>
8+
<BaseLayout sidebar={<Sidebar />}>
9+
<Stepper />
10+
</BaseLayout>
2211
);
2312
};
2413

25-
export default SplitgraphSampleQuery;
26-
27-
const sampleRepositories: GitHubRepository[] = [
28-
{ namespace: "OpenTech", repository: "data-structures" },
29-
{ namespace: "AiSolutions", repository: "machine-learning-api" },
30-
{ namespace: "DevToolsInc", repository: "react-components" },
31-
{ namespace: "QuantumComputing", repository: "quantum-algorithms" },
32-
{ namespace: "GlobalNetworks", repository: "network-optimization" },
33-
{ namespace: "CyberSec", repository: "firewall-config" },
34-
{ namespace: "DataSci", repository: "data-analysis" },
35-
{ namespace: "WebDevCo", repository: "responsive-templates" },
36-
{ namespace: "CloudNet", repository: "cloud-infrastructure" },
37-
{ namespace: "AiData", repository: "neural-networks" },
38-
{ namespace: "DistributedSys", repository: "microservices-arch" },
39-
{ namespace: "KernelDev", repository: "os-development" },
40-
{ namespace: "FrontEndMagic", repository: "vue-utilities" },
41-
{ namespace: "BackEndLogix", repository: "nodejs-server" },
42-
{ namespace: "Securitech", repository: "encryption-utils" },
43-
{ namespace: "FullStack", repository: "end-to-end-app" },
44-
{ namespace: "DBMasters", repository: "database-design" },
45-
{ namespace: "MobileApps", repository: "android-development" },
46-
{ namespace: "GameFactory", repository: "game-engine" },
47-
{ namespace: "WebAssembly", repository: "wasm-runtime" },
48-
{ namespace: "RoboLogic", repository: "robot-navigation" },
49-
{ namespace: "IoTDesign", repository: "iot-devices" },
50-
{ namespace: "BlockchainTech", repository: "blockchain-network" },
51-
{ namespace: "CryptoCoins", repository: "cryptocurrency" },
52-
{ namespace: "VRWorld", repository: "vr-applications" },
53-
];
14+
export default ImportPage;

0 commit comments

Comments
 (0)