Skip to content

Commit

Permalink
refactor: migrate api playground to using atoms (#1156)
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity authored Jul 14, 2024
1 parent 407ae2e commit 9e46cd4
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 370 deletions.
9 changes: 3 additions & 6 deletions packages/ui/app/src/api-playground/PlaygroundButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import { FernNavigation } from "@fern-api/fdr-sdk";
import { FernButton, FernTooltip, FernTooltipProvider } from "@fern-ui/components";
import { useAtomValue } from "jotai";
import { FC } from "react";
import { HAS_PLAYGROUND_ATOM } from "../atoms";
import { useSetAndOpenPlayground } from "./PlaygroundContext";
import { HAS_PLAYGROUND_ATOM, useSetAndOpenPlayground } from "../atoms";

export const PlaygroundButton: FC<{ state: FernNavigation.NavigationNodeApiLeaf }> = ({ state }) => {
const setSelectionStateAndOpen = useSetAndOpenPlayground();
const openPlayground = useSetAndOpenPlayground();
const hasPlayground = useAtomValue(HAS_PLAYGROUND_ATOM);

if (!hasPlayground) {
Expand All @@ -23,9 +22,7 @@ export const PlaygroundButton: FC<{ state: FernNavigation.NavigationNodeApiLeaf
}
>
<FernButton
onClick={() => {
setSelectionStateAndOpen(state);
}}
onClick={() => openPlayground(state)}
rightIcon="play"
variant="outlined"
intent="primary"
Expand Down
92 changes: 7 additions & 85 deletions packages/ui/app/src/api-playground/PlaygroundContext.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,22 @@
import { FernNavigation } from "@fern-api/fdr-sdk";
import * as Sentry from "@sentry/nextjs";
import { useAtom, useAtomValue } from "jotai";
import { useAtomValue } from "jotai";
import dynamic from "next/dynamic";
import { FC, PropsWithChildren, createContext, useCallback, useContext, useEffect } from "react";
import { FC, useEffect } from "react";
import useSWR from "swr";
import { noop } from "ts-essentials";
import urljoin from "url-join";
import { capturePosthogEvent } from "../analytics/posthog";
import { APIS_ATOM, FLATTENED_APIS_ATOM, store, useBasePath } from "../atoms";
import {
HAS_PLAYGROUND_ATOM,
PLAYGROUND_FORM_STATE_ATOM,
useInitPlaygroundRouter,
useOpenPlayground,
} from "../atoms/playground";
import { ResolvedApiDefinition, ResolvedRootPackage, isEndpoint, isWebSocket } from "../resolver/types";
import { getInitialEndpointRequestFormStateWithExample } from "./PlaygroundDrawer";
import { APIS_ATOM, store, useBasePath } from "../atoms";
import { HAS_PLAYGROUND_ATOM, useInitPlaygroundRouter } from "../atoms/playground";
import { ResolvedRootPackage } from "../resolver/types";

const PlaygroundDrawer = dynamic(() => import("./PlaygroundDrawer").then((m) => m.PlaygroundDrawer), {
ssr: false,
});

const PlaygroundContext = createContext<(state: FernNavigation.NavigationNodeApiLeaf) => void>(noop);

const fetcher = async (url: string) => {
const res = await fetch(url);
return res.json();
};

export const PlaygroundContextProvider: FC<PropsWithChildren> = ({ children }) => {
export const PlaygroundContextProvider: FC = () => {
const basePath = useBasePath();
const key = urljoin(basePath ?? "", "/api/fern-docs/resolve-api");

Expand All @@ -41,74 +29,8 @@ export const PlaygroundContextProvider: FC<PropsWithChildren> = ({ children }) =
}
}, [data]);

const flattenedApis = useAtomValue(FLATTENED_APIS_ATOM);

const [globalFormState, setGlobalFormState] = useAtom(PLAYGROUND_FORM_STATE_ATOM);

useInitPlaygroundRouter();

const openPlayground = useOpenPlayground();

const setSelectionStateAndOpen = useCallback(
async (newSelectionState: FernNavigation.NavigationNodeApiLeaf) => {
const matchedPackage = flattenedApis[newSelectionState.apiDefinitionId];
if (matchedPackage == null) {
Sentry.captureMessage("Could not find package for API playground selection state", "fatal");
return;
}

if (newSelectionState.type === "endpoint") {
const matchedEndpoint = matchedPackage.apiDefinitions.find(
(definition) => isEndpoint(definition) && definition.id === newSelectionState.endpointId,
) as ResolvedApiDefinition.Endpoint | undefined;
if (matchedEndpoint == null) {
Sentry.captureMessage("Could not find endpoint for API playground selection state", "fatal");
}
openPlayground(newSelectionState.id);
capturePosthogEvent("api_playground_opened", {
endpointId: newSelectionState.endpointId,
endpointName: matchedEndpoint?.title,
});
if (matchedEndpoint != null && globalFormState[newSelectionState.id] == null) {
setGlobalFormState((currentFormState) => {
return {
...currentFormState,
[newSelectionState.id]: getInitialEndpointRequestFormStateWithExample(
matchedPackage?.auth,
matchedEndpoint,
matchedEndpoint?.examples[0],
matchedPackage?.types ?? {},
),
};
});
}
} else if (newSelectionState.type === "webSocket") {
const matchedWebSocket = matchedPackage.apiDefinitions.find(
(definition) => isWebSocket(definition) && definition.id === newSelectionState.webSocketId,
) as ResolvedApiDefinition.Endpoint | undefined;
if (matchedWebSocket == null) {
Sentry.captureMessage("Could not find websocket for API playground selection state", "fatal");
}
openPlayground(newSelectionState.id);
capturePosthogEvent("api_playground_opened", {
webSocketId: newSelectionState.webSocketId,
webSocketName: matchedWebSocket?.title,
});
}
},
[flattenedApis, globalFormState, openPlayground, setGlobalFormState],
);

const hasPlayground = useAtomValue(HAS_PLAYGROUND_ATOM);

return (
<PlaygroundContext.Provider value={setSelectionStateAndOpen}>
{children}
{hasPlayground && <PlaygroundDrawer />}
</PlaygroundContext.Provider>
);
return hasPlayground ? <PlaygroundDrawer /> : null;
};

export function useSetAndOpenPlayground(): (state: FernNavigation.NavigationNodeApiLeaf) => void {
return useContext(PlaygroundContext);
}
Loading

0 comments on commit 9e46cd4

Please sign in to comment.