Skip to content

Commit 5267c73

Browse files
committed
chore: incremental overlay generation in playground
1 parent 10e9799 commit 5267c73

File tree

7 files changed

+87
-48
lines changed

7 files changed

+87
-48
lines changed

cmd/wasm/functions.go

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"syscall/js"
1111
)
1212

13-
func CalculateOverlay(originalYAML, targetYAML string) (string, error) {
13+
func CalculateOverlay(originalYAML, targetYAML, existingOverlay string) (string, error) {
1414
var orig yaml.Node
1515
err := yaml.Unmarshal([]byte(originalYAML), &orig)
1616
if err != nil {
@@ -22,11 +22,27 @@ func CalculateOverlay(originalYAML, targetYAML string) (string, error) {
2222
return "", fmt.Errorf("failed to parse target schema: %w", err)
2323
}
2424

25-
overlay, err := overlay.Compare("example overlay", &orig, target)
25+
// we go from the original to a new version, then look at the extra overlays on top
26+
// of that, then add that to the existing overlay
27+
var overlayDocument overlay.Overlay
28+
err = yaml.Unmarshal([]byte(existingOverlay), &overlayDocument)
29+
if err != nil {
30+
return "", fmt.Errorf("failed to parse overlay schema: %w", err)
31+
}
32+
// now modify the original using the existing overlay
33+
err = overlayDocument.ApplyTo(&orig)
34+
if err != nil {
35+
return "", fmt.Errorf("failed to apply existing overlay: %w", err)
36+
}
37+
38+
newOverlay, err := overlay.Compare("example overlay", &orig, target)
2639
if err != nil {
2740
return "", fmt.Errorf("failed to compare schemas: %w", err)
2841
}
29-
out, err := yaml.Marshal(overlay)
42+
// Now we take those actions, add them onto the end of the existing overlay
43+
overlayDocument.Actions = append(overlayDocument.Actions, newOverlay.Actions...)
44+
45+
out, err := yaml.Marshal(overlayDocument)
3046
if err != nil {
3147
return "", fmt.Errorf("failed to marshal schema: %w", err)
3248
}
@@ -130,11 +146,11 @@ func promisify(fn func(args []js.Value) (string, error)) js.Func {
130146

131147
func main() {
132148
js.Global().Set("CalculateOverlay", promisify(func(args []js.Value) (string, error) {
133-
if len(args) != 2 {
134-
return "", fmt.Errorf("CalculateOverlay: expected 2 args, got %v", len(args))
149+
if len(args) != 3 {
150+
return "", fmt.Errorf("CalculateOverlay: expected 3 args, got %v", len(args))
135151
}
136152

137-
return CalculateOverlay(args[0].String(), args[1].String())
153+
return CalculateOverlay(args[0].String(), args[1].String(), args[2].String())
138154
}))
139155

140156
js.Global().Set("ApplyOverlay", promisify(func(args []js.Value) (string, error) {

web/api/share.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,15 @@ async function processData(chunks: Uint8Array[]): Promise<Response> {
5959
offset += chunk.length;
6060
}
6161

62-
const hash = createHash("sha256").update(data).digest("hex");
62+
const hash = createHash("sha256").update(data).digest("base64").slice(0, 7);
6363
const key = `share-urls/${hash}`;
6464

6565
const result = await put(key, data, {
6666
addRandomSuffix: false,
6767
access: "public",
6868
});
6969
const downloadURL = result.downloadUrl;
70-
const encodedDownloadURL = Buffer.from(downloadURL).toString("base64");
70+
const encodedDownloadURL = Buffer.from(downloadURL.trim()).toString("base64");
7171

7272
return new Response(JSON.stringify(encodedDownloadURL), {
7373
status: 200,

web/src/App.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
display: flex;
7070
margin: 0 auto;
7171
text-align: center;
72+
overflow: hidden;
7273
width: 100vw;
7374
justify-content: space-between;
7475
}

web/src/Playground.tsx

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,20 @@ import { editor } from "monaco-editor";
1212
import { ApplyOverlay, CalculateOverlay, GetInfo } from "./bridge";
1313
import { Alert } from "@speakeasy-api/moonshine";
1414
import { blankOverlay, petstore } from "./defaults";
15-
import { useAtom } from "jotai";
1615
import speakeasyWhiteLogo from "./assets/speakeasy-white.svg";
1716
import openapiLogo from "./assets/openapi.svg";
18-
import { atom } from "jotai";
1917
import { compress, decompress } from "@/compress";
2018
import { CopyButton } from "@/components/CopyButton";
2119
import { Button } from "@/components/ui/button";
2220
import {
23-
PanelGroup,
21+
ImperativePanelGroupHandle,
2422
Panel,
23+
PanelGroup,
2524
PanelResizeHandle,
26-
ImperativePanelGroupHandle,
2725
} from "react-resizable-panels";
2826
import posthog from "posthog-js";
2927
import { useDebounceCallback, useMediaQuery } from "usehooks-ts";
3028

31-
const originalOpenAPI = atom(petstore);
32-
const changedOpenAPI = atom<string>("");
33-
const overlay = atom(blankOverlay);
3429
const Link = ({ children, href }: { children: ReactNode; href: string }) => (
3530
<a
3631
className="border-b border-transparent pb-[2px] transition-all duration-200 hover:border-current "
@@ -46,10 +41,10 @@ const Link = ({ children, href }: { children: ReactNode; href: string }) => (
4641
function Playground() {
4742
const [ready, setReady] = useState(false);
4843

49-
const [original, setOriginal] = useAtom(originalOpenAPI);
50-
const [changed, setChanged] = useAtom(changedOpenAPI);
44+
const original = useRef(petstore);
45+
const changed = useRef("");
5146
const [changedLoading, setChangedLoading] = useState(false);
52-
const [result, setResult] = useAtom(overlay);
47+
const result = useRef(blankOverlay);
5348
const [resultLoading, setResultLoading] = useState(false);
5449
const [error, setError] = useState("");
5550
const [shareUrl, setShareUrl] = useState("");
@@ -66,8 +61,12 @@ function Playground() {
6661
const getShareUrl = useCallback(async () => {
6762
try {
6863
setShareUrlLoading(true);
69-
const info = await GetInfo(original, false);
70-
const start = JSON.stringify({ result, original, info });
64+
const info = await GetInfo(original.current, false);
65+
const start = JSON.stringify({
66+
result: result.current,
67+
original: original.current,
68+
info: info,
69+
});
7170
const blob = await compress(start);
7271

7372
const response = await fetch("/api/share", {
@@ -114,24 +113,33 @@ function Playground() {
114113
throw new Error("No body");
115114
}
116115
const decompressedData = await decompress(blob.body);
117-
const { result, original } = JSON.parse(decompressedData);
116+
const decompressed: { original: string; result: string } =
117+
JSON.parse(decompressedData);
118118

119-
setOriginal(original);
120-
setResult(result);
119+
original.current = decompressed.original;
120+
result.current = decompressed.result;
121121

122-
const changed = await ApplyOverlay(original, result, false);
123-
const info = await GetInfo(original, false);
122+
const changedNew = await ApplyOverlay(
123+
original.current,
124+
result.current,
125+
false,
126+
);
127+
const info = await GetInfo(original.current, false);
124128
posthog.capture("overlay.speakeasy.com:load-shared", {
125129
openapi: JSON.parse(info),
126130
});
127131

128-
setChanged(changed);
132+
changed.current = changedNew;
129133
} catch (error: any) {
130134
console.error("invalid share url:", error.message);
131135
}
132136
} else {
133-
const changed = await ApplyOverlay(original, result, false);
134-
setChanged(changed);
137+
const changedNew = await ApplyOverlay(
138+
original.current,
139+
result.current,
140+
false,
141+
);
142+
changed.current = changedNew;
135143
}
136144
setReady(true);
137145
})();
@@ -141,9 +149,14 @@ function Playground() {
141149
async (value: string | undefined, _: editor.IModelContentChangedEvent) => {
142150
try {
143151
setResultLoading(true);
144-
setOriginal(value || "");
145-
const res = await CalculateOverlay(value || "", changed, true);
146-
setResult(res);
152+
original.current = value || "";
153+
const res = await CalculateOverlay(
154+
value || "",
155+
changed.current,
156+
result.current,
157+
true,
158+
);
159+
result.current = res;
147160
setError("");
148161
} catch (e: unknown) {
149162
if (e instanceof Error) {
@@ -153,7 +166,7 @@ function Playground() {
153166
setResultLoading(false);
154167
}
155168
},
156-
[changed, original],
169+
[],
157170
);
158171

159172
const onChangeADebounced = useDebounceCallback(onChangeA, 500);
@@ -162,9 +175,13 @@ function Playground() {
162175
async (value: string | undefined, _: editor.IModelContentChangedEvent) => {
163176
try {
164177
setResultLoading(true);
165-
setChanged(value || "");
166-
const res = await CalculateOverlay(original, value || "", true);
167-
setResult(res);
178+
changed.current = value || "";
179+
result.current = await CalculateOverlay(
180+
original.current,
181+
value || "",
182+
result.current,
183+
true,
184+
);
168185
setError("");
169186
} catch (e: unknown) {
170187
if (e instanceof Error) {
@@ -174,7 +191,7 @@ function Playground() {
174191
setResultLoading(false);
175192
}
176193
},
177-
[changed, original],
194+
[],
178195
);
179196

180197
const onChangeBDebounced = useDebounceCallback(onChangeB, 500);
@@ -183,9 +200,12 @@ function Playground() {
183200
async (value: string | undefined, _: editor.IModelContentChangedEvent) => {
184201
try {
185202
setChangedLoading(true);
186-
setResult(value || "");
187-
const res = await ApplyOverlay(original, value || "", true);
188-
setChanged(res);
203+
result.current = value || "";
204+
changed.current = await ApplyOverlay(
205+
original.current,
206+
value || "",
207+
true,
208+
);
189209
setError("");
190210
} catch (e: unknown) {
191211
if (e instanceof Error) {
@@ -195,15 +215,15 @@ function Playground() {
195215
setChangedLoading(false);
196216
}
197217
},
198-
[changed, original],
218+
[],
199219
);
200220

201221
const onChangeCDebounced = useDebounceCallback(onChangeC, 500);
202222

203223
useEffect(() => {
204224
const tryHandlePageTitle = async () => {
205225
try {
206-
const info = await GetInfo(original);
226+
const info = await GetInfo(original.current);
207227
const { title, version } = JSON.parse(info);
208228
const pageTitle = `${title} ${version} | Speakeasy OpenAPI Overlay Playground`;
209229
if (document.title !== pageTitle) {
@@ -312,7 +332,7 @@ function Playground() {
312332
onClick={getShareUrl}
313333
disabled={shareUrlLoading}
314334
>
315-
Short URL
335+
Share
316336
</Button>
317337
<div className="flex items-center gap-x-2 grow">
318338
{shareUrl ? <CopyButton value={shareUrl} /> : null}
@@ -354,7 +374,7 @@ function Playground() {
354374
<div style={{ height: "calc(100vh - 50px)" }}>
355375
<Editor
356376
readonly={false}
357-
value={original}
377+
value={original.current}
358378
onChange={onChangeADebounced}
359379
title="Original"
360380
index={0}
@@ -367,8 +387,8 @@ function Playground() {
367387
<div style={{ height: "calc(100vh - 50px)" }}>
368388
<Editor
369389
readonly={false}
370-
original={original}
371-
value={changed}
390+
original={original.current}
391+
value={changed.current}
372392
onChange={onChangeBDebounced}
373393
loading={changedLoading}
374394
title={"Original + Overlay"}
@@ -382,7 +402,7 @@ function Playground() {
382402
<div style={{ height: "calc(100vh - 50px)" }}>
383403
<Editor
384404
readonly={false}
385-
value={result}
405+
value={result.current}
386406
onChange={onChangeCDebounced}
387407
loading={resultLoading}
388408
title={"Overlay"}

web/src/assets/wasm/lib.wasm

1.98 KB
Binary file not shown.

web/src/bridge.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export type CalculateOverlayMessage = {
4747
payload: {
4848
from: string;
4949
to: string;
50+
existing: string;
5051
};
5152
};
5253
Response:
@@ -100,12 +101,13 @@ export type ApplyOverlayMessage = {
100101
export function CalculateOverlay(
101102
from: string,
102103
to: string,
104+
existing: string,
103105
supercede = false,
104106
): Promise<any> {
105107
return sendMessage(
106108
{
107109
type: "CalculateOverlay",
108-
payload: { from, to },
110+
payload: { from, to, existing },
109111
} satisfies CalculateOverlayMessage["Request"],
110112
supercede,
111113
);

web/src/openapi.web.worker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const messageHandlers: MessageHandlers = {
2121
CalculateOverlay: async (
2222
payload: CalculateOverlayMessage["Request"]["payload"],
2323
) => {
24-
return exec("CalculateOverlay", payload.from, payload.to);
24+
return exec("CalculateOverlay", payload.from, payload.to, payload.existing);
2525
},
2626
ApplyOverlay: async (payload: ApplyOverlayMessage["Request"]["payload"]) => {
2727
return exec("ApplyOverlay", payload.source, payload.overlay);

0 commit comments

Comments
 (0)