Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Example nextjs site that imports repositories from GitHub to Splitgraph, then exports them to Seafowl, then renders a chart of stargazers #21

Open
wants to merge 36 commits into
base: generated-import-plugins
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
0f64a81
Add stub `examples/nextjs-import-airbyte-github-export-seafowl/`
milesrichardson May 23, 2023
776e11f
Stub out layout and sidebar of GitHub analytics example
milesrichardson May 23, 2023
1f3b6d1
Add backend config and API routes for starting, awaiting import task
milesrichardson May 24, 2023
82db03b
Add Stepper component with ImportPanel and ExportPanel
milesrichardson May 24, 2023
0975075
Implement backend API routes `start-export-to-seafowl` and `await-exp…
milesrichardson May 25, 2023
5cd10be
Implement the components for the "Export Panel"
milesrichardson May 25, 2023
8ba4151
Move lib-backend to lib/backend
milesrichardson May 26, 2023
2dcec66
Hardcode list of "relevant" table names for ingestion from GitHub
milesrichardson May 26, 2023
65ca23e
Serialize the stepper state into the URL, so that awaiting can be res…
milesrichardson May 26, 2023
9e47b5a
Move `lib/config.ts` -> `lib/config/github-tables.ts`
milesrichardson May 26, 2023
d60cd4d
Export analytics queries to Seafowl in addition to tables
milesrichardson May 26, 2023
17d8bc9
Track completed import/exports in a meta repository on Splitgraph
milesrichardson May 29, 2023
3ba9e97
Support ?debug=1 parameter in URL of stepper to render DebugPane
milesrichardson May 29, 2023
e6c592e
Implement sidebar and stub out page for imported repository to show t…
milesrichardson May 29, 2023
613a774
Install `@observablehq/plot` in `example-nextjs-import-airbyte-github…
milesrichardson May 29, 2023
309203d
Render stargazers line chart with Observable Plot querying Seafowl
milesrichardson May 30, 2023
9b58189
Refactor Observable plot: Add `useSqlPlot` hook and make file per plot
milesrichardson Jun 1, 2023
5f4d6b5
Refactor: use better name than `acc` in reduce function
milesrichardson Jun 8, 2023
b362bc2
Add styling, bells and whistles to stepper (text, buttons, loading ba…
milesrichardson Jun 17, 2023
4454199
Refactor
milesrichardson Jun 19, 2023
cb71604
Display preview table for each loading/completed: either embed Splitg…
milesrichardson Jun 19, 2023
cc8cde1
Allow toggling between Splitgraph/Seafowl embeds (once export has com…
milesrichardson Jun 19, 2023
2b51141
Use `<a>` tag for "Import Your Repository" link, to completely reset …
milesrichardson Jun 20, 2023
f43d0e8
Add `formatTimeElapsed` function prop to `LoadingBar` for custom form…
milesrichardson Jun 20, 2023
cf97067
Productionize the export step: add lots of nice styling, bells and wh…
milesrichardson Jun 20, 2023
ecd1e43
Refactor
milesrichardson Jun 20, 2023
418ecfd
Only poll unique export taskIds, and do the polling from one hook ins…
milesrichardson Jun 21, 2023
9835599
Add workaround for failed exports of queries: create a fallback table
milesrichardson Jun 23, 2023
c260e10
When embedding Seafowl query of exported queries, select from the des…
milesrichardson Jun 23, 2023
4cc3e41
Move embedded preview components to be shared with export panel and r…
milesrichardson Jun 28, 2023
ab57659
Add `reduceRows` method to `useSqlPlot` for case where mapping isn't …
milesrichardson Jun 28, 2023
15d14e2
Add stacked bar chart of issue reactions by month with bars broken do…
milesrichardson Jun 28, 2023
06c57b9
Make repo page have three tabs: tables, queries and charts
milesrichardson Jun 28, 2023
93022b1
Rename chart to IssueReactsByMonth
milesrichardson Jun 29, 2023
afa4c07
Bump GitHub import page size to 100, start date to 2023-01-01
milesrichardson Jun 29, 2023
06d94f7
Add scatter plot of user comment length vs. lines of code
milesrichardson Jun 29, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions examples/nextjs-import-airbyte-github-export-seafowl/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This file contains public environment variables and is therefore checked into the repo
# For secret environment variables, see `.env.local` which is _not_ checked into the repo
# Read env-vars.d.ts for expected variable names
# See more: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables

NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_REPOSITORY=github-analytics-metadata
NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_COMPLETED_TABLE=completed_repositories
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# IMPORTANT: Put your own values in `.env.local` (a git-ignored file) when running this locally
# Configure them in Vercel settings when running in production
# This file is mostly to show which variables exist, since it's the only one checked into the repo.
# SEE: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables

# Create your own API key and secret: https://www.splitgraph.com/connect
SPLITGRAPH_API_KEY="********************************"
SPLITGRAPH_API_SECRET="********************************"

# This should match the username associated with the API key
NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE="*****"

# Create a GitHub token that can query the repositories you want to connect
# For example, a token with read-only access to public repos is sufficient
# CREATE ONE HERE: https://github.com/settings/personal-access-tokens/new
GITHUB_PAT_SECRET="github_pat_**********************_***********************************************************"

# OPTIONAL: Set this environment variable to a proxy address to capture requests from API routes
# e.g. To intercept requests to Splitgraph API sent from madatdata libraries in API routes
# You can also set this by running: yarn dev-mitm (see package.json)
# MITMPROXY_ADDRESS="http://localhost:7979"

# OPTIONAL: Set Seafowl environment variables to use for creating fallback tables when exports fail
# NOTE 1: At the moment the instance URL must be https://demo.seafowl.cloud because that's where
# the Splitgraph export API exports tables to when no instance URL is specified, and we are
# currently not specifying the instance URL when starting exports, and only use it when creating fallback tables.
# NOTE 2: The dbname (SEAFOWL_INSTANCE_DATABASE) MUST match NEXT_PUBLIC_SPLITGRAPH_GITHUB_ANALYTICS_META_NAMESPACE
#
# SEAFOWL_INSTANCE_URL="https://demo.seafowl.cloud"
# SEAFOWL_INSTANCE_SECRET="********************************"
# SEAFOWL_INSTANCE_DATABASE="**********"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.next
.env.local
.env.*.local
!.env.test.local
26 changes: 26 additions & 0 deletions examples/nextjs-import-airbyte-github-export-seafowl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# End-to-End Example: Use `airbyte-github` to import GitHub repository into Splitgraph, then export it to Seafowl, via Next.js API routes

This is a full end-to-end example demonstrating importing data to Splitgraph
(using the `airbyte-github` plugin), exporting it to Seafowl (using the
`export-to-seafowl` plugin), and then querying it (with `DbSeafowl` and React
hooks from `@madatdata/react`). The importers and exporting of data is triggered
by backend API routes (e.g. the Vecel runtime), which execute in an environment
with secrets (an `API_SECRET` for Splitgraph, and a GitHub access token for
`airbyte-github`). The client side queries Seafowl directly by sending raw SQL
queries in HTP requests, which is what Seafowl is ultimately designed for.

## Try Now

### Preview Immediately

_No signup required, just click the button!_

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/splitgraph/madatdata/tree/main/examples/nextjs-import-airbyte-github-export-seafowl?file=pages/index.tsx)

[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/splitgraph/madatdata/main/examples/nextjs-import-airbyte-github-export-seafowl?file=pages/index.tsx&hardReloadOnChange=true&startScript=dev&node=16&port=3000)

### Or, deploy to Vercel (signup required)

_Signup, fork the repo, and import it_

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/splitgraph/madatdata/tree/main/examples/nextjs-import-airbyte-github-export-seafowl&project-name=madatdata-basic-hooks&repository-name=madatdata-nextjs-basic-hooks)
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.container {
display: flex;
flex-direction: column;
height: 100vh;
background-color: var(--background);
}

.header {
width: 100%;
position: sticky;
top: 0;
z-index: 100;
background-color: var(--header);
color: var(--text);
}

.main {
display: flex;
flex-grow: 1;
overflow: hidden;
}

.sidebar {
width: 20%; /* adjust as per your needs */
overflow-y: auto;
/* add additional styles for your sidebar */
color: var(--text);
}

.content {
width: 80%; /* adjust as per your needs */
overflow-y: auto;
position: relative;
/* add additional styles for your content area */
color: var(--text);
background-color: var(--background);
padding: 24px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import styles from "./BaseLayout.module.css";
import { Header } from "./Header";

export const BaseLayout = ({
children,
sidebar,
}: React.PropsWithChildren<{
sidebar: React.ReactNode;
}>) => {
return (
<div className={styles.container}>
<div className={styles.header}>
<Header />
</div>
<div className={styles.main}>
<div className={styles.sidebar}>{sidebar}</div>
<div className={styles.content}>{children}</div>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.embeddedPreviewHeading {
margin-bottom: 0;
}

.embeddedPreviewDescription {
margin-bottom: 1rem;
}

.note {
font-size: small;
/* color: red !important; */
display: block;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import styles from "./EmbeddedPreviews.module.css";
import type {
ExportTable,
ExportQueryInput,
ExportTableInput,
} from "../../types";

import { EmbedPreviewTableOrQuery } from "../EmbeddedQuery/EmbeddedQuery";
import { ComponentProps } from "react";

export const EmbeddedTablePreviewHeadingAndDescription = ({
exportComplete,
}: {
exportComplete: boolean;
}) => {
return (
<>
{!exportComplete ? (
<>
<h2 className={styles.embeddedPreviewHeading}>Tables to Export</h2>
<p className={styles.embeddedPreviewDescription}>
These are the tables that we'll export from Splitgraph to Seafowl.
You can query them in Splitgraph now, and then when the export is
complete, you'll be able to query them in Seafowl too.
</p>
</>
) : (
<>
<h2 className={styles.embeddedPreviewHeading}>Exported Tables</h2>
<p className={styles.embeddedPreviewDescription}>
We successfully exported the tables to Seafowl, so now you can query
them in Seafowl too.
</p>
</>
)}
</>
);
};

export const EmbeddedTablePreviews = ({
tablesToExport,
splitgraphRepository,
splitgraphNamespace,
useLoadingOrCompleted,
}: {
tablesToExport: ExportTableInput[];
splitgraphRepository: string;
splitgraphNamespace: string;
useLoadingOrCompleted: ComponentProps<
typeof EmbedPreviewTableOrQuery
>["useLoadingOrCompleted"];
}) => {
return (
<>
{tablesToExport.map((exportTable) => (
<EmbedPreviewTableOrQuery
key={`export-table-preview-${exportTable.table}`}
exportInput={exportTable}
importedRepository={{ splitgraphNamespace, splitgraphRepository }}
makeQuery={({ splitgraphNamespace, splitgraphRepository, table }) =>
`SELECT * FROM "${splitgraphNamespace}/${splitgraphRepository}"."${table}";`
}
useLoadingOrCompleted={useLoadingOrCompleted}
makeMatchInputToExported={(exportTableInput) => (exportTable) => {
return (
exportTable.destinationSchema === exportTableInput.repository &&
exportTable.destinationTable === exportTableInput.table
);
}}
/>
))}
</>
);
};

export const EmbeddedQueryPreviewHeadingAndDescription = ({
exportComplete,
}: {
exportComplete: boolean;
}) => {
return (
<>
{" "}
{!exportComplete ? (
<>
<h2 className={styles.embeddedPreviewHeading}>Queries to Export</h2>
<p className={styles.embeddedPreviewDescription}>
We've prepared a few queries to export from Splitgraph to Seafowl,
so that we can use them to render the charts that we want.
Splitgraph will execute the query and insert its result into
Seafowl. You can query them in Splitgraph now, and then when the
export is complete, you'll be able to query them in Seafowl too.
</p>
</>
) : (
<>
<h2 className={styles.embeddedPreviewHeading}>Exported Queries</h2>
<p className={styles.embeddedPreviewDescription}>
We successfully exported these queries from Splitgraph to Seafowl,
so now you can query them in Seafowl too.{" "}
<em className={styles.note}>
Note: If some queries failed to export, it's probably because they
had empty result sets (e.g. the table of issue reactions)
</em>
</p>
</>
)}
</>
);
};

export const EmbeddedQueryPreviews = ({
queriesToExport,
splitgraphRepository,
splitgraphNamespace,
useLoadingOrCompleted,
}: {
queriesToExport: ExportQueryInput[];
splitgraphRepository: string;
splitgraphNamespace: string;
useLoadingOrCompleted: ComponentProps<
typeof EmbedPreviewTableOrQuery
>["useLoadingOrCompleted"];
}) => {
return (
<>
{queriesToExport.map((exportQuery) => (
<EmbedPreviewTableOrQuery
key={`export-query-preview-${exportQuery.destinationTable}-${exportQuery.destinationSchema}`}
exportInput={exportQuery}
importedRepository={{ splitgraphNamespace, splitgraphRepository }}
// This is the query we run on Splitgraph that we exported to Seafowl
makeQuery={({ sourceQuery }) => sourceQuery}
// But once it's exported, we can just select from its table in Seafowl (and
// besides, the sourceQuery might not be compatible with Seafowl anyway)
makeSeafowlQuery={({ destinationSchema, destinationTable }) =>
`SELECT * FROM "${destinationSchema}"."${destinationTable}";`
}
useLoadingOrCompleted={useLoadingOrCompleted}
makeMatchInputToExported={(exportQueryInput) =>
(exportTable: ExportTable) => {
return (
exportTable.destinationSchema ===
exportQueryInput.destinationSchema &&
exportTable.destinationTable ===
exportQueryInput.destinationTable
);
}}
/>
))}
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
.embeddedQuery {
border-left: 4px solid var(--muted);
padding-left: 10px;
margin-bottom: 2rem;
}

.topBar {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}

.consoleFlavorButtonsAndLoadingBar {
display: inline-flex;
flex-direction: row;
}

.embeddedQuery iframe {
width: 100%;
}

.heading {
font-size: 1.5rem;
font-weight: bold;
display: inline-flex;
align-items: center;
padding-top: 10px;
padding-bottom: 10px;
}

.embedControls {
background: inherit;
}

.openInConsoleLink {
display: inline-flex;
align-items: center;
font-size: small;
}

.openInConsoleLink svg {
margin-right: 4px;
}
Loading