Skip to content

Commit

Permalink
merge from main
Browse files Browse the repository at this point in the history
  • Loading branch information
RohinBhargava committed Nov 20, 2024
2 parents b05c439 + 6e86124 commit ce969ce
Show file tree
Hide file tree
Showing 40 changed files with 1,543 additions and 187 deletions.
1 change: 0 additions & 1 deletion .github/workflows/deploy-fdr-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: |
pnpm turbo codegen build test --filter=${{ env.PACKAGE_NAME }}
pnpm --filter "@fern-platform/fdr" sentry:sourcemaps
- name: 💻 Setup AWS CLI
uses: aws-actions/configure-aws-credentials@v1
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/diff-fdr-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: |
pnpm turbo codegen build test --filter=${{ env.PACKAGE_NAME }}
pnpm --filter "@fern-platform/fdr" sentry:sourcemaps
- name: 💻 Setup AWS CLI
uses: aws-actions/configure-aws-credentials@v1
Expand Down
2 changes: 2 additions & 0 deletions fern/apis/fdr/definition/api/v1/read/__package__.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ types:
properties:
endpointId: rootCommons.EndpointId
accessTokenLocator: rootCommons.JqString
headerName: optional<string>
tokenPrefix: optional<string>

errors:
ApiDoesNotExistError:
Expand Down
2 changes: 2 additions & 0 deletions fern/apis/fdr/definition/api/v1/register/__package__.yml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ types:
properties:
endpointId: rootCommons.EndpointId
accessTokenLocator: rootCommons.JqString
headerName: optional<string>
tokenPrefix: optional<string>

EndpointExampleGenerationErrorBody:
properties:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"micromatch": "4.0.8",
"postcss": "8.4.31",
"esbuild": "0.20.2",
"jsonpath-plus": "10.0.0",
"jsonpath-plus": "10.0.7",
"markdown-to-jsx": "7.4.0",
"webpack": "5.94.0",
"clipboardy": "4.0.0",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 52 additions & 12 deletions packages/ui/app/src/atoms/playground.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ import {
type PlaygroundWebSocketRequestFormState,
} from "../playground/types";
import {
getInitialEndpointRequestFormState,
getInitialEndpointRequestFormStateWithExample,
getInitialWebSocketRequestFormState,
} from "../playground/utils";
import { pascalCaseHeaderKeys } from "../playground/utils/header-key-case";
import { FERN_USER_ATOM } from "./auth";
import { FEATURE_FLAGS_ATOM } from "./flags";
import { useAtomEffect } from "./hooks";
import { HEADER_HEIGHT_ATOM } from "./layout";
Expand Down Expand Up @@ -177,7 +178,12 @@ export const PLAYGROUND_AUTH_STATE_ATOM = atomWithStorageValidation<PlaygroundAu
);

export const PLAYGROUND_AUTH_STATE_BEARER_TOKEN_ATOM = atom(
(get) => get(PLAYGROUND_AUTH_STATE_ATOM).bearerAuth ?? PLAYGROUND_AUTH_STATE_BEARER_TOKEN_INITIAL,
(get) => ({
token:
get(PLAYGROUND_AUTH_STATE_ATOM).bearerAuth?.token ??
get(FERN_USER_ATOM)?.playground?.initial_state?.auth?.bearer_token ??
"",
}),
(_get, set, update: SetStateAction<PlaygroundAuthStateBearerToken>) => {
set(PLAYGROUND_AUTH_STATE_ATOM, (prev) => ({
...prev,
Expand All @@ -190,7 +196,12 @@ export const PLAYGROUND_AUTH_STATE_BEARER_TOKEN_ATOM = atom(
);

export const PLAYGROUND_AUTH_STATE_HEADER_ATOM = atom(
(get) => get(PLAYGROUND_AUTH_STATE_ATOM).header ?? PLAYGROUND_AUTH_STATE_HEADER_INITIAL,
(get) => ({
headers: pascalCaseHeaderKeys({
...(get(PLAYGROUND_AUTH_STATE_ATOM).header?.headers ??
get(FERN_USER_ATOM)?.playground?.initial_state?.headers),
}),
}),
(_get, set, update: SetStateAction<PlaygroundAuthStateHeader>) => {
set(PLAYGROUND_AUTH_STATE_ATOM, (prev) => ({
...prev,
Expand All @@ -200,7 +211,16 @@ export const PLAYGROUND_AUTH_STATE_HEADER_ATOM = atom(
);

export const PLAYGROUND_AUTH_STATE_BASIC_AUTH_ATOM = atom(
(get) => get(PLAYGROUND_AUTH_STATE_ATOM).basicAuth ?? PLAYGROUND_AUTH_STATE_BASIC_AUTH_INITIAL,
(get) => ({
username:
get(PLAYGROUND_AUTH_STATE_ATOM).basicAuth?.username ??
get(FERN_USER_ATOM)?.playground?.initial_state?.auth?.basic?.username ??
"",
password:
get(PLAYGROUND_AUTH_STATE_ATOM).basicAuth?.password ??
get(FERN_USER_ATOM)?.playground?.initial_state?.auth?.basic?.password ??
"",
}),
(_get, set, update: SetStateAction<PlaygroundAuthStateBasicAuth>) => {
set(PLAYGROUND_AUTH_STATE_ATOM, (prev) => ({
...prev,
Expand Down Expand Up @@ -265,6 +285,8 @@ export function useSetAndOpenPlayground(): (node: FernNavigation.NavigationNodeA
return;
}

const playgroundInitialState = get(FERN_USER_ATOM)?.playground?.initial_state;

if (node.type === "endpoint") {
const context = createEndpointContext(node, definition);

Expand All @@ -277,7 +299,11 @@ export function useSetAndOpenPlayground(): (node: FernNavigation.NavigationNodeA

set(
formStateAtom,
getInitialEndpointRequestFormStateWithExample(context, context.endpoint.examples?.[0]),
getInitialEndpointRequestFormStateWithExample(
context,
context.endpoint.examples?.[0],
playgroundInitialState,
),
);
} else if (node.type === "webSocket") {
const context = createWebSocketContext(node, definition);
Expand All @@ -290,7 +316,7 @@ export function useSetAndOpenPlayground(): (node: FernNavigation.NavigationNodeA
return;
}

set(formStateAtom, getInitialWebSocketRequestFormState(context));
set(formStateAtom, getInitialWebSocketRequestFormState(context, playgroundInitialState));
}
playgroundFormStateFamily.remove(node.id);
},
Expand All @@ -304,9 +330,16 @@ export function usePlaygroundEndpointFormState(
): [PlaygroundEndpointRequestFormState, Dispatch<SetStateAction<PlaygroundEndpointRequestFormState>>] {
const formStateAtom = playgroundFormStateFamily(ctx.node.id);
const formState = useAtomValue(formStateAtom);
const user = useAtomValue(FERN_USER_ATOM);

return [
formState?.type === "endpoint" ? formState : getInitialEndpointRequestFormState(ctx),
formState?.type === "endpoint"
? formState
: getInitialEndpointRequestFormStateWithExample(
ctx,
ctx.endpoint.examples?.[0],
user?.playground?.initial_state,
),
useAtomCallback(
useCallbackOne(
(get, set, update: SetStateAction<PlaygroundEndpointRequestFormState>) => {
Expand All @@ -316,12 +349,16 @@ export function usePlaygroundEndpointFormState(
? update(
currentFormState?.type === "endpoint"
? currentFormState
: getInitialEndpointRequestFormState(ctx),
: getInitialEndpointRequestFormStateWithExample(
ctx,
ctx.endpoint.examples?.[0],
user?.playground?.initial_state,
),
)
: update;
set(formStateAtom, newFormState);
},
[formStateAtom, ctx],
[formStateAtom, ctx, user?.playground?.initial_state],
),
),
];
Expand All @@ -332,9 +369,12 @@ export function usePlaygroundWebsocketFormState(
): [PlaygroundWebSocketRequestFormState, Dispatch<SetStateAction<PlaygroundWebSocketRequestFormState>>] {
const formStateAtom = playgroundFormStateFamily(context.node.id);
const formState = useAtomValue(playgroundFormStateFamily(context.node.id));
const user = useAtomValue(FERN_USER_ATOM);

return [
formState?.type === "websocket" ? formState : getInitialWebSocketRequestFormState(context),
formState?.type === "websocket"
? formState
: getInitialWebSocketRequestFormState(context, user?.playground?.initial_state),
useAtomCallback(
useCallbackOne(
(get, set, update: SetStateAction<PlaygroundWebSocketRequestFormState>) => {
Expand All @@ -344,12 +384,12 @@ export function usePlaygroundWebsocketFormState(
? update(
currentFormState?.type === "websocket"
? currentFormState
: getInitialWebSocketRequestFormState(context),
: getInitialWebSocketRequestFormState(context, user?.playground?.initial_state),
)
: update;
set(formStateAtom, newFormState);
},
[formStateAtom, context],
[formStateAtom, context, user?.playground?.initial_state],
),
),
];
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/app/src/playground/WithLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const WithLabel: FC<PropsWithChildren<WithLabelProps>> = ({
isRequired={!unwrapped.isOptional}
isList={unwrapped.shape.type === "list"}
isBoolean={unwrapped.shape.type === "primitive" && unwrapped.shape.value.type === "boolean"}
typeShorthand={renderTypeShorthandRoot(unwrapped.shape, types)}
typeShorthand={renderTypeShorthandRoot(property.valueShape, types, false, true)}
>
{children}
</WithLabelInternal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useAtomValue } from "jotai";
import { ReactElement } from "react";
import { PLAYGROUND_AUTH_STATE_ATOM } from "../../atoms";
import { PlaygroundAuthState } from "../types";
import { pascalCaseHeaderKey } from "../utils/header-key-case";

interface PlaygroundCardTriggerManualProps {
auth: APIV1Read.ApiAuth;
Expand Down Expand Up @@ -74,7 +75,7 @@ function isAuthed(auth: APIV1Read.ApiAuth, authState: PlaygroundAuthState): bool
bearerAuth: () => !isEmpty(authState.bearerAuth?.token.trim()),
basicAuth: () =>
!isEmpty(authState.basicAuth?.username.trim()) && !isEmpty(authState.basicAuth?.password.trim()),
header: (header) => !isEmpty(authState.header?.headers[header.headerWireValue]?.trim()),
header: (header) => !isEmpty(authState.header?.headers[pascalCaseHeaderKey(header.headerWireValue)]?.trim()),
oAuth: () => {
const authToken =
authState.oauth?.selectedInputMethod === "credentials"
Expand Down
15 changes: 10 additions & 5 deletions packages/ui/app/src/playground/auth/PlaygroundHeaderAuthForm.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { APIV1Read } from "@fern-api/fdr-sdk/client/types";
import { unknownToString } from "@fern-api/ui-core-utils";
import { atom } from "jotai";
import { useAtom } from "jotai/react";
import type { ReactElement, SetStateAction } from "react";
import { useMemoOne } from "use-memo-one";
import { PLAYGROUND_AUTH_STATE_HEADER_ATOM } from "../../atoms";
import { PasswordInputGroup } from "../PasswordInputGroup";
import { pascalCaseHeaderKey } from "../utils/header-key-case";

export function PlaygroundHeaderAuthForm({
header,
Expand All @@ -17,14 +19,15 @@ export function PlaygroundHeaderAuthForm({
useMemoOne(
() =>
atom(
(get) => get(PLAYGROUND_AUTH_STATE_HEADER_ATOM).headers[header.headerWireValue],
(get) =>
get(PLAYGROUND_AUTH_STATE_HEADER_ATOM).headers[pascalCaseHeaderKey(header.headerWireValue)],
(_get, set, change: SetStateAction<string>) => {
set(PLAYGROUND_AUTH_STATE_HEADER_ATOM, ({ headers }) => ({
headers: {
...headers,
[header.headerWireValue]:
[pascalCaseHeaderKey(header.headerWireValue)]:
typeof change === "function"
? change(headers[header.headerWireValue] ?? "")
? change(headers[pascalCaseHeaderKey(header.headerWireValue)] ?? "")
: change,
},
}));
Expand All @@ -37,12 +40,14 @@ export function PlaygroundHeaderAuthForm({
return (
<li className="-mx-4 space-y-2 p-4">
<label className="inline-flex flex-wrap items-baseline">
<span className="font-mono text-sm">{header.nameOverride ?? header.headerWireValue}</span>
<span className="font-mono text-sm">
{header.nameOverride ?? pascalCaseHeaderKey(header.headerWireValue)}
</span>
</label>
<div>
<PasswordInputGroup
onValueChange={setValue}
value={value}
value={unknownToString(value ?? "")}
autoComplete="off"
data-1p-ignore="true"
disabled={disabled}
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/app/src/playground/code-snippets/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { EndpointContext } from "@fern-api/fdr-sdk/api-definition";
import { toCurlyBraceEndpointPathLiteral } from "@fern-api/fdr-sdk/api-definition";
import { FdrAPI, type APIV1Read } from "@fern-api/fdr-sdk/client/types";
import { SnippetTemplateResolver } from "@fern-api/template-resolver";
import { unknownToString } from "@fern-api/ui-core-utils";
import { UnreachableCaseError } from "ts-essentials";
import { provideRegistryService } from "../../services/registry";
import { PlaygroundAuthState, PlaygroundEndpointRequestFormState } from "../types";
Expand Down Expand Up @@ -182,7 +183,7 @@ export class PlaygroundCodeSnippetResolver {
const headers = { ...this.headers };

// TODO: ensure case insensitivity
if (headers["Content-Type"] === "multipart/form-data") {
if (unknownToString(headers["Content-Type"]).includes("multipart/form-data")) {
delete headers["Content-Type"]; // fetch will set this automatically
}

Expand Down
23 changes: 14 additions & 9 deletions packages/ui/app/src/playground/endpoint/PlaygroundEndpoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { Loadable, failed, loaded, loading, notStartedLoading } from "@fern-ui/l
import { useEventCallback } from "@fern-ui/react-commons";
import { mapValues } from "es-toolkit/object";
import { SendSolid } from "iconoir-react";
import { useSetAtom } from "jotai";
import { useAtomValue, useSetAtom } from "jotai";
import { ReactElement, useCallback, useState } from "react";
import {
FERN_USER_ATOM,
PLAYGROUND_AUTH_STATE_ATOM,
PLAYGROUND_AUTH_STATE_OAUTH_ATOM,
store,
Expand All @@ -25,27 +26,31 @@ import { executeProxyRest } from "../fetch-utils/executeProxyRest";
import { executeProxyStream } from "../fetch-utils/executeProxyStream";
import type { GrpcProxyRequest, ProxyRequest } from "../types";
import { PlaygroundResponse } from "../types/playgroundResponse";
import {
buildAuthHeaders,
getInitialEndpointRequestFormState,
getInitialEndpointRequestFormStateWithExample,
serializeFormStateBody,
} from "../utils";
import { buildAuthHeaders, getInitialEndpointRequestFormStateWithExample, serializeFormStateBody } from "../utils";
import { usePlaygroundBaseUrl } from "../utils/select-environment";
import { PlaygroundEndpointContent } from "./PlaygroundEndpointContent";
import { PlaygroundEndpointPath } from "./PlaygroundEndpointPath";

export const PlaygroundEndpoint = ({ context }: { context: EndpointContext }): ReactElement => {
const user = useAtomValue(FERN_USER_ATOM);
const { node, endpoint, auth } = context;

const [formState, setFormState] = usePlaygroundEndpointFormState(context);

const resetWithExample = useEventCallback(() => {
setFormState(getInitialEndpointRequestFormStateWithExample(context, context.endpoint.examples?.[0]));
setFormState(
getInitialEndpointRequestFormStateWithExample(
context,
context.endpoint.examples?.[0],
user?.playground?.initial_state,
),
);
});

const resetWithoutExample = useEventCallback(() => {
setFormState(getInitialEndpointRequestFormState(context));
setFormState(
getInitialEndpointRequestFormStateWithExample(context, undefined, user?.playground?.initial_state),
);
});

const basePath = useBasePath();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { EndpointContext } from "@fern-api/fdr-sdk/api-definition";
import { PropertyKey, type EndpointContext } from "@fern-api/fdr-sdk/api-definition";
import { EMPTY_ARRAY, visitDiscriminatedUnion } from "@fern-api/ui-core-utils";
import { Dispatch, FC, SetStateAction, useCallback, useMemo } from "react";
import { PlaygroundFileUploadForm } from "../form/PlaygroundFileUploadForm";
import { PlaygroundObjectForm } from "../form/PlaygroundObjectForm";
import { PlaygroundObjectPropertiesForm } from "../form/PlaygroundObjectPropertyForm";
import { PlaygroundEndpointRequestFormState, PlaygroundFormStateBody } from "../types";
import { pascalCaseHeaderKey } from "../utils/header-key-case";
import { PlaygroundEndpointAliasForm } from "./PlaygroundEndpointAliasForm";
import { PlaygroundEndpointFormSection } from "./PlaygroundEndpointFormSection";
import { PlaygroundEndpointMultipartForm } from "./PlaygroundEndpointMultipartForm";
Expand Down Expand Up @@ -104,7 +105,10 @@ export const PlaygroundEndpointForm: FC<PlaygroundEndpointFormProps> = ({
<PlaygroundEndpointFormSection ignoreHeaders={ignoreHeaders} title="Headers">
<PlaygroundObjectPropertiesForm
id="header"
properties={headers}
properties={headers.map((header) => ({
...header,
key: PropertyKey(pascalCaseHeaderKey(header.key)),
}))}
extraProperties={undefined}
onChange={setHeaders}
value={formState?.headers}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,12 @@ export const PlaygroundObjectPropertiesForm = memo<PlaygroundObjectPropertiesFor
type: "value",
value: property.key,
label: property.key,
helperText: renderTypeShorthandRoot(property.valueShape, types),
helperText: renderTypeShorthandRoot(
{ type: "optional", shape: property.valueShape, default: undefined },
types,
false,
true,
),
labelClassName: "font-mono",
tooltip: property.description != null ? <Markdown size="xs" mdx={property.description} /> : undefined,
}),
Expand Down
Loading

0 comments on commit ce969ce

Please sign in to comment.