zudoku 0.25.1 → 0.25.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/demo.js +0 -1
- package/dist/app/demo.js.map +1 -1
- package/dist/app/standalone.js +0 -1
- package/dist/app/standalone.js.map +1 -1
- package/dist/config/validators/InputSidebarSchema.d.ts +2 -2
- package/dist/config/validators/validate.d.ts +4 -4
- package/dist/lib/authentication/providers/openid.js +1 -1
- package/dist/lib/authentication/providers/openid.js.map +1 -1
- package/dist/lib/oas/graphql/index.d.ts +3 -0
- package/dist/lib/oas/graphql/index.js +12 -13
- package/dist/lib/oas/graphql/index.js.map +1 -1
- package/dist/lib/plugins/openapi/ColorizedParam.d.ts +10 -2
- package/dist/lib/plugins/openapi/ColorizedParam.js +16 -7
- package/dist/lib/plugins/openapi/ColorizedParam.js.map +1 -1
- package/dist/lib/plugins/openapi/ParameterListItem.js +3 -2
- package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
- package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js +2 -0
- package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -1
- package/dist/lib/plugins/openapi/RequestBodySidecarBox.d.ts +1 -1
- package/dist/lib/plugins/openapi/RequestBodySidecarBox.js +2 -5
- package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
- package/dist/lib/plugins/openapi/ResponsesSidecarBox.js +2 -6
- package/dist/lib/plugins/openapi/ResponsesSidecarBox.js.map +1 -1
- package/dist/lib/plugins/openapi/{ExampleDisplay.d.ts → SidecarExamples.d.ts} +2 -6
- package/dist/lib/plugins/openapi/SidecarExamples.js +62 -0
- package/dist/lib/plugins/openapi/SidecarExamples.js.map +1 -0
- package/dist/lib/plugins/openapi/client/GraphQLClient.d.ts +1 -1
- package/dist/lib/plugins/openapi/client/GraphQLClient.js +22 -93
- package/dist/lib/plugins/openapi/client/GraphQLClient.js.map +1 -1
- package/dist/lib/plugins/openapi/client/createServer.d.ts +2 -1
- package/dist/lib/plugins/openapi/client/createServer.js +5 -2
- package/dist/lib/plugins/openapi/client/createServer.js.map +1 -1
- package/dist/lib/plugins/openapi/client/useCreateQuery.d.ts +1 -1
- package/dist/lib/plugins/openapi/client/useCreateQuery.js +2 -13
- package/dist/lib/plugins/openapi/client/useCreateQuery.js.map +1 -1
- package/dist/lib/plugins/openapi/index.d.ts +2 -1
- package/dist/lib/plugins/openapi/index.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/EnumSelector.d.ts +8 -0
- package/dist/lib/plugins/openapi/playground/EnumSelector.js +21 -0
- package/dist/lib/plugins/openapi/playground/EnumSelector.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/PathParams.js +9 -4
- package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/Playground.d.ts +3 -0
- package/dist/lib/plugins/openapi/playground/Playground.js +5 -2
- package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/QueryParams.js +23 -8
- package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
- package/dist/lib/plugins/openapi/schema/SchemaComponents.js +2 -1
- package/dist/lib/plugins/openapi/schema/SchemaComponents.js.map +1 -1
- package/dist/lib/plugins/openapi/util/generateSchemaExample.js +19 -11
- package/dist/lib/plugins/openapi/util/generateSchemaExample.js.map +1 -1
- package/dist/lib/ui/Badge.js +1 -1
- package/dist/lib/ui/Badge.js.map +1 -1
- package/dist/lib/ui/Button.d.ts +1 -1
- package/dist/lib/ui/Checkbox.d.ts +8 -2
- package/dist/lib/ui/Checkbox.js +13 -1
- package/dist/lib/ui/Checkbox.js.map +1 -1
- package/dist/lib/util/traverse.d.ts +8 -1
- package/dist/lib/util/traverse.js +7 -3
- package/dist/lib/util/traverse.js.map +1 -1
- package/dist/vite/api/schema-codegen.d.ts +12 -0
- package/dist/vite/api/schema-codegen.js +62 -0
- package/dist/vite/api/schema-codegen.js.map +1 -0
- package/dist/vite/api/schema-codegen.test.d.ts +1 -0
- package/dist/vite/api/schema-codegen.test.js +247 -0
- package/dist/vite/api/schema-codegen.test.js.map +1 -0
- package/dist/vite/config.js +0 -7
- package/dist/vite/config.js.map +1 -1
- package/dist/vite/plugin-api.js +110 -82
- package/dist/vite/plugin-api.js.map +1 -1
- package/dist/vite/plugin-component.js +0 -1
- package/dist/vite/plugin-component.js.map +1 -1
- package/lib/Command-9x_kZHr4.js +611 -0
- package/lib/Command-9x_kZHr4.js.map +1 -0
- package/lib/{OperationList-BLdHAQ39.js → OperationList-B8bHMKme.js} +1440 -1434
- package/lib/OperationList-B8bHMKme.js.map +1 -0
- package/lib/{createServer-Bf5_6o6G.js → createServer-BznDkeSA.js} +4227 -5154
- package/lib/createServer-BznDkeSA.js.map +1 -0
- package/lib/index-TaRXY2w1.js +43 -0
- package/lib/index-TaRXY2w1.js.map +1 -0
- package/lib/index-sD8L1_Dl.js +1292 -0
- package/lib/index-sD8L1_Dl.js.map +1 -0
- package/lib/post-processors/traverse.js +11 -8
- package/lib/post-processors/traverse.js.map +1 -1
- package/lib/ui/Badge.js +1 -1
- package/lib/ui/Badge.js.map +1 -1
- package/lib/ui/Checkbox.js +25 -14
- package/lib/ui/Checkbox.js.map +1 -1
- package/lib/ui/Command.js +14 -550
- package/lib/ui/Command.js.map +1 -1
- package/lib/zudoku.auth-openid.js +36 -36
- package/lib/zudoku.auth-openid.js.map +1 -1
- package/lib/zudoku.plugin-openapi.js +1 -1
- package/package.json +1 -6
- package/src/app/demo.tsx +0 -1
- package/src/app/standalone.tsx +0 -1
- package/src/lib/authentication/providers/openid.tsx +1 -1
- package/src/lib/oas/graphql/index.ts +19 -15
- package/src/lib/plugins/openapi/ColorizedParam.tsx +29 -12
- package/src/lib/plugins/openapi/ParameterListItem.tsx +9 -7
- package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +2 -0
- package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +2 -7
- package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +5 -8
- package/src/lib/plugins/openapi/SidecarExamples.tsx +155 -0
- package/src/lib/plugins/openapi/client/GraphQLClient.tsx +28 -120
- package/src/lib/plugins/openapi/client/createServer.ts +6 -2
- package/src/lib/plugins/openapi/client/useCreateQuery.ts +2 -17
- package/src/lib/plugins/openapi/index.tsx +2 -1
- package/src/lib/plugins/openapi/playground/EnumSelector.tsx +86 -0
- package/src/lib/plugins/openapi/playground/PathParams.tsx +72 -64
- package/src/lib/plugins/openapi/playground/Playground.tsx +26 -13
- package/src/lib/plugins/openapi/playground/QueryParams.tsx +102 -73
- package/src/lib/plugins/openapi/schema/SchemaComponents.tsx +4 -7
- package/src/lib/plugins/openapi/util/generateSchemaExample.ts +26 -11
- package/src/lib/ui/Badge.tsx +1 -1
- package/src/lib/ui/Checkbox.tsx +24 -7
- package/src/lib/util/traverse.ts +15 -5
- package/dist/lib/plugins/openapi/ExampleDisplay.js +0 -78
- package/dist/lib/plugins/openapi/ExampleDisplay.js.map +0 -1
- package/dist/lib/plugins/openapi/client/worker.d.ts +0 -4
- package/dist/lib/plugins/openapi/client/worker.js +0 -29
- package/dist/lib/plugins/openapi/client/worker.js.map +0 -1
- package/dist/lib/plugins/openapi-worker.d.ts +0 -1
- package/dist/lib/plugins/openapi-worker.js +0 -8
- package/dist/lib/plugins/openapi-worker.js.map +0 -1
- package/lib/Dialog-Bxv1yEIg.js +0 -67
- package/lib/Dialog-Bxv1yEIg.js.map +0 -1
- package/lib/OperationList-BLdHAQ39.js.map +0 -1
- package/lib/assets/index-C7jnHK4b.js +0 -4841
- package/lib/assets/index-C7jnHK4b.js.map +0 -1
- package/lib/assets/worker-Cbp2r2BQ.js +0 -18592
- package/lib/assets/worker-Cbp2r2BQ.js.map +0 -1
- package/lib/createServer-Bf5_6o6G.js.map +0 -1
- package/lib/index-BNx95gkf.js +0 -1284
- package/lib/index-BNx95gkf.js.map +0 -1
- package/lib/index-DyBL--Kz.js +0 -826
- package/lib/index-DyBL--Kz.js.map +0 -1
- package/lib/zudoku.openapi-worker.js +0 -15
- package/lib/zudoku.openapi-worker.js.map +0 -1
- package/src/lib/plugins/openapi/ExampleDisplay.tsx +0 -163
- package/src/lib/plugins/openapi/client/worker.ts +0 -44
- package/src/lib/plugins/openapi-worker.ts +0 -11
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { useContext, useMemo } from "react";
|
|
1
|
+
import { useContext } from "react";
|
|
3
2
|
import type { TypedDocumentString } from "../graphql/graphql.js";
|
|
4
3
|
import { GraphQLContext } from "./GraphQLContext.js";
|
|
5
4
|
|
|
@@ -12,22 +11,8 @@ export const useCreateQuery = <TResult, TVariables>(
|
|
|
12
11
|
throw new Error("useGraphQL must be used within a GraphQLProvider");
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
const hash = useMemo(() => {
|
|
16
|
-
if (
|
|
17
|
-
typeof variables[0] === "object" &&
|
|
18
|
-
variables[0] != null &&
|
|
19
|
-
"input" in variables[0] &&
|
|
20
|
-
typeof variables[0].input === "function"
|
|
21
|
-
) {
|
|
22
|
-
// This is a pre-hashed name to ensure that the query key is consistent across server and client
|
|
23
|
-
return variables[0].input.name;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return hashit(variables[0] ?? {});
|
|
27
|
-
}, [variables]);
|
|
28
|
-
|
|
29
14
|
return {
|
|
30
15
|
queryFn: () => graphQLClient.fetch(query, ...variables),
|
|
31
|
-
queryKey: [query,
|
|
16
|
+
queryKey: [query, variables[0]],
|
|
32
17
|
} as const;
|
|
33
18
|
};
|
|
@@ -7,6 +7,7 @@ import { CirclePlayIcon, LogInIcon } from "lucide-react";
|
|
|
7
7
|
import type { SidebarItem } from "../../../config/validators/SidebarSchema.js";
|
|
8
8
|
import { useAuth } from "../../authentication/hook.js";
|
|
9
9
|
import { ColorMap } from "../../components/navigation/SidebarBadge.js";
|
|
10
|
+
import type { SchemaImports } from "../../oas/graphql/index.js";
|
|
10
11
|
import { Button } from "../../ui/Button.js";
|
|
11
12
|
import { joinPath } from "../../util/joinPath.js";
|
|
12
13
|
import { GraphQLClient } from "./client/GraphQLClient.js";
|
|
@@ -35,7 +36,7 @@ const GetCategoriesQuery = graphql(`
|
|
|
35
36
|
}
|
|
36
37
|
`);
|
|
37
38
|
|
|
38
|
-
type InternalOasPluginConfig = {
|
|
39
|
+
type InternalOasPluginConfig = { schemaImports?: SchemaImports };
|
|
39
40
|
|
|
40
41
|
const MethodColorMap: Record<string, keyof typeof ColorMap> = {
|
|
41
42
|
get: "green",
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Command,
|
|
4
|
+
CommandEmpty,
|
|
5
|
+
CommandInput,
|
|
6
|
+
CommandItem,
|
|
7
|
+
CommandList,
|
|
8
|
+
} from "../../../ui/Command.js";
|
|
9
|
+
import {
|
|
10
|
+
Popover,
|
|
11
|
+
PopoverContent,
|
|
12
|
+
PopoverTrigger,
|
|
13
|
+
} from "../../../ui/Popover.js";
|
|
14
|
+
import { cn } from "../../../util/cn.js";
|
|
15
|
+
|
|
16
|
+
interface EnumSelectorProps {
|
|
17
|
+
value: string;
|
|
18
|
+
enumValues: string[];
|
|
19
|
+
onChange: (value: string) => void;
|
|
20
|
+
onValueSelected: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const EnumSelector = ({
|
|
24
|
+
value,
|
|
25
|
+
enumValues,
|
|
26
|
+
onChange,
|
|
27
|
+
onValueSelected,
|
|
28
|
+
}: EnumSelectorProps) => {
|
|
29
|
+
const [searchValue, setSearchValue] = useState("");
|
|
30
|
+
const [open, setOpen] = useState(false);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
34
|
+
<PopoverTrigger asChild>
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
role="combobox"
|
|
38
|
+
className={cn(
|
|
39
|
+
"px-3 py-2 w-full border-0 shadow-none text-xs font-mono text-start hover:bg-accent/40 rounded border-transparent hover:bg-accent",
|
|
40
|
+
!value && "text-muted-foreground",
|
|
41
|
+
)}
|
|
42
|
+
>
|
|
43
|
+
{value || "Select value"}
|
|
44
|
+
</button>
|
|
45
|
+
</PopoverTrigger>
|
|
46
|
+
<PopoverContent
|
|
47
|
+
className="p-0 w-[--radix-popover-trigger-width] "
|
|
48
|
+
align="start"
|
|
49
|
+
sideOffset={3}
|
|
50
|
+
alignOffset={-3}
|
|
51
|
+
side="bottom"
|
|
52
|
+
>
|
|
53
|
+
<Command className="max-h-[180px]">
|
|
54
|
+
<CommandInput
|
|
55
|
+
placeholder="Enter value"
|
|
56
|
+
className="h-9 bg-transparent "
|
|
57
|
+
onValueChange={setSearchValue}
|
|
58
|
+
onKeyDown={(e) => {
|
|
59
|
+
if (e.key === "Enter") {
|
|
60
|
+
onChange(searchValue);
|
|
61
|
+
onValueSelected();
|
|
62
|
+
setOpen(false);
|
|
63
|
+
}
|
|
64
|
+
}}
|
|
65
|
+
/>
|
|
66
|
+
<CommandList>
|
|
67
|
+
<CommandEmpty>Use "{searchValue}"</CommandEmpty>
|
|
68
|
+
{enumValues.map((enumValue) => (
|
|
69
|
+
<CommandItem
|
|
70
|
+
key={enumValue}
|
|
71
|
+
value={enumValue}
|
|
72
|
+
onSelect={(selected) => {
|
|
73
|
+
onChange(selected);
|
|
74
|
+
onValueSelected();
|
|
75
|
+
setOpen(false);
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
{enumValue}
|
|
79
|
+
</CommandItem>
|
|
80
|
+
))}
|
|
81
|
+
</CommandList>
|
|
82
|
+
</Command>
|
|
83
|
+
</PopoverContent>
|
|
84
|
+
</Popover>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import { EraserIcon } from "lucide-react";
|
|
2
2
|
import { Control, Controller, useFieldArray } from "react-hook-form";
|
|
3
|
+
import { Card } from "zudoku/ui/Card.js";
|
|
3
4
|
import { Button } from "../../../ui/Button.js";
|
|
4
5
|
import { Input } from "../../../ui/Input.js";
|
|
5
6
|
import { cn } from "../../../util/cn.js";
|
|
6
|
-
import { ColorizedParam } from "../ColorizedParam.js";
|
|
7
|
+
import { ColorizedParam, useParamColor } from "../ColorizedParam.js";
|
|
7
8
|
import type { PlaygroundForm } from "./Playground.js";
|
|
8
9
|
|
|
10
|
+
const PathParamLabel = ({ name }: { name: string }) => {
|
|
11
|
+
const color = useParamColor(name);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="flex items-center">
|
|
15
|
+
<div
|
|
16
|
+
className="w-2 h-2 rounded-full"
|
|
17
|
+
style={{ backgroundColor: `hsl(${color})` }}
|
|
18
|
+
/>
|
|
19
|
+
|
|
20
|
+
<ColorizedParam
|
|
21
|
+
slug={name}
|
|
22
|
+
name={name}
|
|
23
|
+
className="font-mono text-xs m-2 px-1"
|
|
24
|
+
/>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
9
29
|
export const PathParams = ({
|
|
10
30
|
control,
|
|
11
31
|
}: {
|
|
@@ -17,72 +37,60 @@ export const PathParams = ({
|
|
|
17
37
|
});
|
|
18
38
|
|
|
19
39
|
return (
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
control={control}
|
|
27
|
-
name={`pathParams.${i}.value`}
|
|
28
|
-
render={({ field }) => (
|
|
29
|
-
<div>
|
|
30
|
-
<ColorizedParam
|
|
31
|
-
slug={part.name}
|
|
32
|
-
name={part.name}
|
|
33
|
-
backgroundOpacity="25%"
|
|
34
|
-
borderOpacity={field.value ? "100%" : "0"}
|
|
35
|
-
className={cn(
|
|
36
|
-
"font-mono text-xs m-2",
|
|
37
|
-
!field.value && "opacity-60",
|
|
38
|
-
)}
|
|
39
|
-
/>
|
|
40
|
-
*
|
|
41
|
-
</div>
|
|
42
|
-
)}
|
|
43
|
-
/>
|
|
44
|
-
</td>
|
|
45
|
-
<td>
|
|
46
|
-
<div className="flex justify-between items-center">
|
|
47
|
-
<Controller
|
|
48
|
-
control={control}
|
|
49
|
-
name={`pathParams.${i}.value`}
|
|
50
|
-
render={({ field }) => (
|
|
51
|
-
<Input
|
|
52
|
-
{...field}
|
|
53
|
-
required
|
|
54
|
-
placeholder="Enter value"
|
|
55
|
-
className="border-0 shadow-none ring-0 font-mono text-xs"
|
|
56
|
-
/>
|
|
57
|
-
)}
|
|
58
|
-
/>
|
|
40
|
+
<Card className="rounded-lg">
|
|
41
|
+
<table className="w-full">
|
|
42
|
+
<tbody>
|
|
43
|
+
{fields.map((part, i) => (
|
|
44
|
+
<tr key={part.id} className="hover:bg-accent/40">
|
|
45
|
+
<td className="w-5/12">
|
|
59
46
|
<Controller
|
|
60
47
|
control={control}
|
|
61
48
|
name={`pathParams.${i}.value`}
|
|
62
|
-
render={(
|
|
63
|
-
<Button
|
|
64
|
-
size="icon"
|
|
65
|
-
type="button"
|
|
66
|
-
variant="ghost"
|
|
67
|
-
aria-label="Clear value"
|
|
68
|
-
className={cn(
|
|
69
|
-
"ms-2",
|
|
70
|
-
field.value.length === 0
|
|
71
|
-
? "opacity-0 pointer-events-none"
|
|
72
|
-
: "opacity-100",
|
|
73
|
-
)}
|
|
74
|
-
title="Clear value"
|
|
75
|
-
onClick={() => field.onChange("")}
|
|
76
|
-
>
|
|
77
|
-
<EraserIcon size={16} />
|
|
78
|
-
</Button>
|
|
79
|
-
)}
|
|
49
|
+
render={() => <PathParamLabel name={part.name} />}
|
|
80
50
|
/>
|
|
81
|
-
</
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
51
|
+
</td>
|
|
52
|
+
<td className="w-7/12">
|
|
53
|
+
<div className="flex justify-between items-center">
|
|
54
|
+
<Controller
|
|
55
|
+
control={control}
|
|
56
|
+
name={`pathParams.${i}.value`}
|
|
57
|
+
render={({ field }) => (
|
|
58
|
+
<Input
|
|
59
|
+
{...field}
|
|
60
|
+
required
|
|
61
|
+
placeholder="Enter value"
|
|
62
|
+
className="w-full border-0 shadow-none text-xs font-mono hover:bg-accent"
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
/>
|
|
66
|
+
<Controller
|
|
67
|
+
control={control}
|
|
68
|
+
name={`pathParams.${i}.value`}
|
|
69
|
+
render={({ field }) => (
|
|
70
|
+
<Button
|
|
71
|
+
size="icon"
|
|
72
|
+
type="button"
|
|
73
|
+
variant="ghost"
|
|
74
|
+
aria-label="Clear value"
|
|
75
|
+
className={cn(
|
|
76
|
+
"ms-2 mr-1",
|
|
77
|
+
field.value.length === 0
|
|
78
|
+
? "opacity-0 pointer-events-none"
|
|
79
|
+
: "opacity-100",
|
|
80
|
+
)}
|
|
81
|
+
title="Clear value"
|
|
82
|
+
onClick={() => field.onChange("")}
|
|
83
|
+
>
|
|
84
|
+
<EraserIcon size={16} />
|
|
85
|
+
</Button>
|
|
86
|
+
)}
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
</td>
|
|
90
|
+
</tr>
|
|
91
|
+
))}
|
|
92
|
+
</tbody>
|
|
93
|
+
</table>
|
|
94
|
+
</Card>
|
|
87
95
|
);
|
|
88
96
|
};
|
|
@@ -46,6 +46,8 @@ export type QueryParam = {
|
|
|
46
46
|
defaultValue?: string;
|
|
47
47
|
defaultActive?: boolean;
|
|
48
48
|
isRequired?: boolean;
|
|
49
|
+
enum?: string[];
|
|
50
|
+
type?: string;
|
|
49
51
|
};
|
|
50
52
|
export type PathParam = {
|
|
51
53
|
name: string;
|
|
@@ -55,9 +57,17 @@ export type PathParam = {
|
|
|
55
57
|
|
|
56
58
|
export type PlaygroundForm = {
|
|
57
59
|
body: string;
|
|
58
|
-
queryParams: Array<{
|
|
60
|
+
queryParams: Array<{
|
|
61
|
+
name: string;
|
|
62
|
+
value: string;
|
|
63
|
+
active: boolean;
|
|
64
|
+
enum?: string[];
|
|
65
|
+
}>;
|
|
59
66
|
pathParams: Array<{ name: string; value: string }>;
|
|
60
|
-
headers: Array<{
|
|
67
|
+
headers: Array<{
|
|
68
|
+
name: string;
|
|
69
|
+
value: string;
|
|
70
|
+
}>;
|
|
61
71
|
identity?: string;
|
|
62
72
|
};
|
|
63
73
|
|
|
@@ -92,6 +102,7 @@ export const Playground = ({
|
|
|
92
102
|
name: param.name,
|
|
93
103
|
value: param.defaultValue ?? "",
|
|
94
104
|
active: param.defaultActive ?? false,
|
|
105
|
+
enum: param.enum ?? [],
|
|
95
106
|
})),
|
|
96
107
|
pathParams: pathParams.map((param) => ({
|
|
97
108
|
name: param.name,
|
|
@@ -171,17 +182,19 @@ export const Playground = ({
|
|
|
171
182
|
const replaced = part.replace(/[:{}]/g, "");
|
|
172
183
|
const value = formState.pathParams.find((p) => p.name === replaced)?.value;
|
|
173
184
|
|
|
174
|
-
const pathParamValue =
|
|
175
|
-
<ColorizedParam
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
185
|
+
const pathParamValue = (
|
|
186
|
+
<ColorizedParam
|
|
187
|
+
backgroundOpacity="25%"
|
|
188
|
+
name={part}
|
|
189
|
+
slug={part}
|
|
190
|
+
title={
|
|
191
|
+
!value
|
|
192
|
+
? `Missing value for path parameter \`${replaced}\``
|
|
193
|
+
: undefined
|
|
194
|
+
}
|
|
182
195
|
>
|
|
183
|
-
{part}
|
|
184
|
-
</
|
|
196
|
+
{value ? encodeURIComponent(value) : part}
|
|
197
|
+
</ColorizedParam>
|
|
185
198
|
);
|
|
186
199
|
|
|
187
200
|
return (
|
|
@@ -240,7 +253,7 @@ export const Playground = ({
|
|
|
240
253
|
>
|
|
241
254
|
<form onSubmit={handleSubmit((data) => queryMutation.mutateAsync(data))}>
|
|
242
255
|
<div className="grid grid-cols-[8fr_7fr] text-sm h-full">
|
|
243
|
-
<div className="flex flex-col gap-4 p-
|
|
256
|
+
<div className="flex flex-col gap-4 p-4 after:bg-muted-foreground/20 relative after:absolute after:w-px after:inset-0 after:left-auto">
|
|
244
257
|
<div className="flex gap-2 items-stretch">
|
|
245
258
|
<div className="flex flex-1 items-center w-full border rounded-md">
|
|
246
259
|
<div className="border-r p-2 bg-muted rounded-l-md self-stretch font-semibold font-mono">
|
|
@@ -5,9 +5,12 @@ import {
|
|
|
5
5
|
useFieldArray,
|
|
6
6
|
useFormContext,
|
|
7
7
|
} from "react-hook-form";
|
|
8
|
+
import { Card } from "zudoku/ui/Card.js";
|
|
9
|
+
import { Checkbox } from "zudoku/ui/Checkbox.js";
|
|
8
10
|
import { Button } from "../../../ui/Button.js";
|
|
9
11
|
import { Input } from "../../../ui/Input.js";
|
|
10
12
|
import { cn } from "../../../util/cn.js";
|
|
13
|
+
import { EnumSelector } from "./EnumSelector.js";
|
|
11
14
|
import { InlineInput } from "./InlineInput.js";
|
|
12
15
|
import {
|
|
13
16
|
NO_IDENTITY,
|
|
@@ -34,97 +37,123 @@ export const QueryParams = ({
|
|
|
34
37
|
const hasSelectedIdentity = selectedIdentity !== NO_IDENTITY;
|
|
35
38
|
|
|
36
39
|
return (
|
|
37
|
-
<
|
|
38
|
-
<table className="w-full
|
|
40
|
+
<Card className="rounded-lg">
|
|
41
|
+
<table className="w-full">
|
|
39
42
|
<tbody>
|
|
40
43
|
{fields
|
|
41
44
|
.filter(
|
|
42
45
|
// TODO remove this hack for Accu or make it more generic
|
|
43
46
|
(field) => !(hasSelectedIdentity && field.name === "apikey"),
|
|
44
47
|
)
|
|
45
|
-
.map((field, i) =>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<input
|
|
53
|
-
type="checkbox"
|
|
54
|
-
id={`queryParams.${i}.active`}
|
|
55
|
-
checked={field.value}
|
|
56
|
-
onChange={field.onChange}
|
|
57
|
-
/>
|
|
58
|
-
)}
|
|
59
|
-
/>
|
|
60
|
-
</td>
|
|
61
|
-
<td>
|
|
62
|
-
<Controller
|
|
63
|
-
control={control}
|
|
64
|
-
render={({ field }) => (
|
|
65
|
-
<InlineInput asChild>
|
|
66
|
-
<label
|
|
67
|
-
className="flex items-center cursor-pointer"
|
|
68
|
-
htmlFor={`queryParams.${i}.active`}
|
|
69
|
-
title={
|
|
70
|
-
requiredFields[i] ? "Required field" : undefined
|
|
71
|
-
}
|
|
72
|
-
>
|
|
73
|
-
{field.value}
|
|
74
|
-
{requiredFields[i] && <sup> *</sup>}
|
|
75
|
-
</label>
|
|
76
|
-
</InlineInput>
|
|
77
|
-
)}
|
|
78
|
-
name={`queryParams.${i}.name`}
|
|
79
|
-
/>
|
|
80
|
-
</td>
|
|
81
|
-
<td>
|
|
82
|
-
<div className="flex justify-between items-center">
|
|
48
|
+
.map((field, i) => {
|
|
49
|
+
const currentParam = queryParams.find(
|
|
50
|
+
(param) => param.name === field.name,
|
|
51
|
+
);
|
|
52
|
+
return (
|
|
53
|
+
<tr key={field.id} className="hover:bg-accent/40">
|
|
54
|
+
<td className="w-5/12 flex items-center ps-3">
|
|
83
55
|
<Controller
|
|
84
56
|
control={control}
|
|
57
|
+
name={`queryParams.${i}.active`}
|
|
85
58
|
render={({ field }) => (
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
form.setValue(`queryParams.${i}.active`, true);
|
|
92
|
-
}
|
|
93
|
-
}}
|
|
94
|
-
placeholder="Enter value"
|
|
95
|
-
className="w-full border-0 shadow-none text-xs font-mono"
|
|
59
|
+
<Checkbox
|
|
60
|
+
variant="outline"
|
|
61
|
+
id={`queryParams.${i}.active`}
|
|
62
|
+
checked={field.value}
|
|
63
|
+
onCheckedChange={field.onChange}
|
|
96
64
|
/>
|
|
97
65
|
)}
|
|
98
|
-
name={`queryParams.${i}.value`}
|
|
99
66
|
/>
|
|
100
67
|
<Controller
|
|
101
68
|
control={control}
|
|
102
69
|
render={({ field }) => (
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
onClick={() => field.onChange("")}
|
|
116
|
-
>
|
|
117
|
-
<EraserIcon size={16} />
|
|
118
|
-
</Button>
|
|
70
|
+
<InlineInput asChild>
|
|
71
|
+
<label
|
|
72
|
+
className="flex items-center cursor-pointer gap-1"
|
|
73
|
+
htmlFor={`queryParams.${i}.active`}
|
|
74
|
+
title={
|
|
75
|
+
requiredFields[i] ? "Required field" : undefined
|
|
76
|
+
}
|
|
77
|
+
>
|
|
78
|
+
{field.value}
|
|
79
|
+
{requiredFields[i] && <sup> *</sup>}
|
|
80
|
+
</label>
|
|
81
|
+
</InlineInput>
|
|
119
82
|
)}
|
|
120
|
-
name={`queryParams.${i}.
|
|
83
|
+
name={`queryParams.${i}.name`}
|
|
121
84
|
/>
|
|
122
|
-
</
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
85
|
+
</td>
|
|
86
|
+
<td className="w-7/12">
|
|
87
|
+
<div className="flex justify-between items-center">
|
|
88
|
+
<Controller
|
|
89
|
+
control={control}
|
|
90
|
+
render={({ field }) => {
|
|
91
|
+
const hasEnum =
|
|
92
|
+
currentParam?.enum && currentParam.enum.length > 0;
|
|
93
|
+
|
|
94
|
+
if (!hasEnum) {
|
|
95
|
+
return (
|
|
96
|
+
<Input
|
|
97
|
+
{...field}
|
|
98
|
+
onChange={(e) => {
|
|
99
|
+
field.onChange(e.target.value);
|
|
100
|
+
if (e.target.value.length > 0) {
|
|
101
|
+
form.setValue(
|
|
102
|
+
`queryParams.${i}.active`,
|
|
103
|
+
true,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}}
|
|
107
|
+
placeholder="Enter value"
|
|
108
|
+
className="w-full border-0 shadow-none text-xs font-mono hover:bg-accent"
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const enumValues = currentParam.enum ?? [];
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<EnumSelector
|
|
117
|
+
value={field.value}
|
|
118
|
+
enumValues={enumValues}
|
|
119
|
+
onChange={field.onChange}
|
|
120
|
+
onValueSelected={() => {
|
|
121
|
+
form.setValue(`queryParams.${i}.active`, true);
|
|
122
|
+
}}
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
125
|
+
}}
|
|
126
|
+
name={`queryParams.${i}.value`}
|
|
127
|
+
/>
|
|
128
|
+
<Controller
|
|
129
|
+
control={control}
|
|
130
|
+
render={({ field }) => (
|
|
131
|
+
<Button
|
|
132
|
+
size="icon"
|
|
133
|
+
type="button"
|
|
134
|
+
variant="ghost"
|
|
135
|
+
aria-label="Clear value"
|
|
136
|
+
className={cn(
|
|
137
|
+
"ms-2 mr-1",
|
|
138
|
+
field.value.length === 0
|
|
139
|
+
? "opacity-0 pointer-events-none"
|
|
140
|
+
: "opacity-100",
|
|
141
|
+
)}
|
|
142
|
+
title="Clear value"
|
|
143
|
+
onClick={() => field.onChange("")}
|
|
144
|
+
>
|
|
145
|
+
<EraserIcon size={16} />
|
|
146
|
+
</Button>
|
|
147
|
+
)}
|
|
148
|
+
name={`queryParams.${i}.value`}
|
|
149
|
+
/>
|
|
150
|
+
</div>
|
|
151
|
+
</td>
|
|
152
|
+
</tr>
|
|
153
|
+
);
|
|
154
|
+
})}
|
|
126
155
|
</tbody>
|
|
127
156
|
</table>
|
|
128
|
-
</
|
|
157
|
+
</Card>
|
|
129
158
|
);
|
|
130
159
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as Collapsible from "@radix-ui/react-collapsible";
|
|
2
2
|
import { ListPlusIcon, RefreshCcwDotIcon } from "lucide-react";
|
|
3
3
|
import { useCallback, useState } from "react";
|
|
4
|
+
import { Badge } from "zudoku/ui/Badge.js";
|
|
4
5
|
import { Markdown, ProseClasses } from "../../../components/Markdown.js";
|
|
5
6
|
import { CIRCULAR_REF } from "../../../oas/parser/dereference/index.js";
|
|
6
7
|
import type { SchemaObject } from "../../../oas/parser/index.js";
|
|
@@ -85,7 +86,7 @@ export const SchemaPropertyItem = ({
|
|
|
85
86
|
<div className="flex flex-col gap-1 justify-between text-sm">
|
|
86
87
|
<div className="flex gap-2 items-center">
|
|
87
88
|
<code>{name}</code>
|
|
88
|
-
<
|
|
89
|
+
<Badge variant="secondary">
|
|
89
90
|
{schema.type === "array" && schema.items.type ? (
|
|
90
91
|
<span>{schema.items.type}[]</span>
|
|
91
92
|
) : Array.isArray(schema.type) ? (
|
|
@@ -93,12 +94,8 @@ export const SchemaPropertyItem = ({
|
|
|
93
94
|
) : (
|
|
94
95
|
<span>{schema.type}</span>
|
|
95
96
|
)}
|
|
96
|
-
</
|
|
97
|
-
{group === "optional" &&
|
|
98
|
-
<span className="py-px px-1.5 font-medium border rounded-lg">
|
|
99
|
-
optional
|
|
100
|
-
</span>
|
|
101
|
-
)}
|
|
97
|
+
</Badge>
|
|
98
|
+
{group === "optional" && <Badge variant="outline">optional</Badge>}
|
|
102
99
|
</div>
|
|
103
100
|
|
|
104
101
|
{schema.description && (
|
|
@@ -5,24 +5,35 @@ export const generateSchemaExample = (
|
|
|
5
5
|
name?: string,
|
|
6
6
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
7
|
): any => {
|
|
8
|
-
//
|
|
8
|
+
// Check for schema-level example first
|
|
9
9
|
if (schema.example !== undefined) {
|
|
10
10
|
return schema.example;
|
|
11
|
-
} else if (schema.examples) {
|
|
12
|
-
return Object.values(schema.examples)[0];
|
|
13
|
-
} else if (schema.default !== undefined) {
|
|
14
|
-
return schema.default;
|
|
15
11
|
}
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
// Then check for schema-level examples
|
|
14
|
+
if (
|
|
15
|
+
schema.examples &&
|
|
16
|
+
typeof schema.examples === "object" &&
|
|
17
|
+
"default" in schema.examples
|
|
18
|
+
) {
|
|
19
|
+
const defaultExample = schema.examples.default;
|
|
20
|
+
if (defaultExample !== null) {
|
|
21
|
+
return typeof defaultExample === "object" && "value" in defaultExample
|
|
22
|
+
? defaultExample.value
|
|
23
|
+
: defaultExample;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// For object schemas with properties
|
|
28
|
+
if (schema.type === "object" && schema.properties) {
|
|
29
|
+
const example: Record<string, any> = {};
|
|
20
30
|
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
32
|
+
if (typeof propSchema === "object") {
|
|
23
33
|
example[key] = generateSchemaExample(propSchema as SchemaObject, key);
|
|
24
34
|
}
|
|
25
35
|
}
|
|
36
|
+
|
|
26
37
|
return example;
|
|
27
38
|
}
|
|
28
39
|
|
|
@@ -31,7 +42,9 @@ export const generateSchemaExample = (
|
|
|
31
42
|
return schema.items.map((itemSchema) =>
|
|
32
43
|
generateSchemaExample(itemSchema as SchemaObject),
|
|
33
44
|
);
|
|
34
|
-
}
|
|
45
|
+
}
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- OpenAPI schemas don't always adhere to spec
|
|
47
|
+
if (schema.items) {
|
|
35
48
|
return [generateSchemaExample(schema.items as SchemaObject)];
|
|
36
49
|
}
|
|
37
50
|
return [];
|
|
@@ -51,6 +64,8 @@ export const generateSchemaExample = (
|
|
|
51
64
|
return true;
|
|
52
65
|
case "null":
|
|
53
66
|
return null;
|
|
67
|
+
case "object":
|
|
68
|
+
return {};
|
|
54
69
|
case undefined:
|
|
55
70
|
default:
|
|
56
71
|
return {};
|