Skip to content

Commit

Permalink
Update versions for openapi launch (#391)
Browse files Browse the repository at this point in the history
* Update source anlaysis to 0.3.0

* Bump studio version

* Format

* Update the changelog

* Retroactively add changelogs

* Rename changelog files

* Update changelog entry for openapi

* Rename files with underscores not dots

* First pass at a fix for the automatic body selection after history entry is loaded

* Add quickfix to support url params
  • Loading branch information
brettimus authored Dec 5, 2024
1 parent 98d7bab commit ef33428
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 13 deletions.
2 changes: 1 addition & 1 deletion api/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.12.3",
"version": "0.12.4",
"name": "@fiberplane/studio",
"description": "Local development debugging interface for Hono apps",
"author": "Fiberplane<info@fiberplane.com>",
Expand Down
2 changes: 1 addition & 1 deletion packages/source-analysis/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fiberplane/source-analysis",
"version": "0.2.0",
"version": "0.3.0",
"type": "module",
"types": "./dist/index.d.ts",
"author": "Fiberplane<info@fiberplane.com>",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ test("zod-openapi", async () => {
expect(factory.getHistoryLength()).toBe(2);
expect(factory.hasVisited(factory.rootId)).toBeTruthy();
expect(factory.getFilesForHistory()).toMatchSnapshot();

} finally {
monitor.stop();
}
Expand Down
2 changes: 1 addition & 1 deletion studio/src/pages/RequestorPage/store/request-body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const RequestorBodyTypeSchema = z.union([
RequestorBodyFileTypeSchema,
]);

type RequestorBodyType = z.infer<typeof RequestorBodyTypeSchema>;
export type RequestorBodyType = z.infer<typeof RequestorBodyTypeSchema>;

export const isRequestorBodyType = (
bodyType: unknown,
Expand Down
2 changes: 1 addition & 1 deletion studio/src/pages/RequestorPage/store/set-body-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export function setBodyTypeInState(
): void {
const oldBodyValue = state.body.value;
const oldBodyType = state.body.type;

// Handle the case where the body type is the same, but the multipart flag is different
if (oldBodyType === newBodyType) {
// HACK - Refactor
Expand Down Expand Up @@ -58,6 +59,5 @@ export function setBodyTypeInState(
const isNonTextOldBody =
Array.isArray(oldBodyValue) || oldBodyValue instanceof File;
const newBodyValue = isNonTextOldBody ? "" : oldBodyValue;

state.body = { type: newBodyType, value: newBodyValue }; //,
}
158 changes: 155 additions & 3 deletions studio/src/pages/RequestorPage/useRequestorHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@ import { removeQueryParams } from "@/utils";
import type { TraceListResponse } from "@fiberplane/fpx-types";
import { useHandler } from "@fiberplane/hooks";
import { useMemo } from "react";
import { createKeyValueParameters } from "./KeyValueForm";
import {
type KeyValueParameter,
createKeyValueParameters,
} from "./KeyValueForm";
import {
type ProxiedRequestResponse,
useFetchRequestorRequests,
} from "./queries";
import { findMatchedRoute } from "./routes";
import { useRequestorStore } from "./store";
import { type RequestorBody, useRequestorStore } from "./store";
import type { RequestorBodyType } from "./store/request-body";
import { isRequestMethod, isWsRequest } from "./types";
import {
sortProxiedRequestResponsesDescending,
traceToProxiedRequestResponse,
} from "./utils";

const EMPTY_TRACES: TraceListResponse = [];

export function useRequestorHistory() {
const {
routes,
Expand Down Expand Up @@ -146,7 +151,9 @@ export function useRequestorHistory() {
} else {
const safeBody =
typeof body !== "string" ? JSON.stringify(body) : body;
setBody(safeBody);
const bodyType = determineBodyType(headers);
const transformedBody = transformBodyValue(bodyType, safeBody);
setBody(transformedBody);
}
} else {
// HACK - move this logic into the reducer
Expand Down Expand Up @@ -197,3 +204,148 @@ export function useRequestorHistory() {
loadHistoricalRequest,
};
}

type BodyType = {
type: RequestorBodyType;
isMultipart?: boolean;
};

/**
* Transforms the body value based on the body type into something that can be displayed in the UI
*
* @NOTE - This is a temporary solution. Currently does not work for form data.
*/
function transformBodyValue(
bodyType: BodyType,
bodyValue: string | undefined | null,
): RequestorBody {
switch (bodyType.type) {
case "json": {
try {
const parsed = JSON.parse(bodyValue || "");
return { type: "json", value: JSON.stringify(parsed, null, 2) }; // Pretty-print JSON
} catch {
if (!bodyValue) {
return { type: "json", value: undefined };
}
if (typeof bodyValue === "string") {
return { type: "json", value: bodyValue };
}
return { type: "json", value: JSON.stringify(bodyValue) };
}
}
case "text": {
return { type: "text", value: bodyValue ?? undefined };
}

case "form-data": {
/**
* NOTE - Handling form bodies is tricky because of how the middleware might serialize them
* E.g., this is a multipart form data request body as the trace shows it
*
* ```
* {"avatar":{"name":"IMG_5635.png","type":"image/png","size":3141659}}
* ```
*
* E.g., this is a urlencoded form data request body as the trace shows it
*
* ```
* {"name":"Samwise the Brave"}
* ```
*/
if (bodyType.isMultipart) {
// Handle multipart form-data
// const formattedValue = parseUrlEncodedFormBody(bodyValue ?? "");
return {
type: "form-data",
isMultipart: true,
value: [],
};
}
// Handle urlencoded form-data
const formattedValue = parseUrlEncodedFormBody(bodyValue ?? "");
return {
type: "form-data",
isMultipart: false,
value: formattedValue.map((param) => ({
...param,
value: {
value: param.value,
type: "text",
},
})),
};
}
case "file":
return { type: "file", value: undefined };

default:
return { type: "text", value: bodyValue ?? undefined };
}
}

function determineBodyType(headers: Record<string, string>): BodyType {
const contentType = headers["Content-Type"] || headers["content-type"];
if (!contentType) {
return { type: "text" };
}

if (contentType.includes("application/json")) {
return { type: "json" };
}
if (
contentType.includes("application/xml") ||
contentType.includes("text/xml")
) {
return { type: "text" };
}
if (contentType.includes("text/plain")) {
return { type: "text" };
}
if (contentType.includes("application/x-www-form-urlencoded")) {
return { type: "form-data", isMultipart: false };
}
if (contentType.includes("multipart/form-data")) {
return { type: "form-data", isMultipart: true };
}
if (contentType.includes("application/octet-stream")) {
return { type: "file" };
}

return { type: "text" };
}

function parseUrlEncodedFormBody(body: string): KeyValueParameter[] {
if (isStringifiedRecordWithKeys(body)) {
return createKeyValueParameters(
Object.entries(JSON.parse(body)).map(([key, value]) => ({
key,
value: String(value),
enabled: true,
})),
);
}

// Split the body by '&' to get key-value pairs
const pairs = body.split("&");

// Map each pair to a KeyValueParameter
const keyValueParameters = pairs.map((pair) => {
const [key, value] = pair.split("=").map(decodeURIComponent);
return { key, value };
});

// Use createKeyValueParameters to generate the final structure
return createKeyValueParameters(keyValueParameters);
}

function isStringifiedRecordWithKeys(
obj: unknown,
): obj is Record<string, string> {
try {
const parsed = JSON.parse(obj as string);
return typeof parsed === "object" && parsed !== null;
} catch {
return false;
}
}
6 changes: 1 addition & 5 deletions www/src/content/changelog/!canary.mdx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
---
date: 2024-10-19
date: 2024-12-09
version: canary
draft: true
---

### Features
* Improved code analysis. Through the new @fiberplane/source-analysis package a more flexible and thorough source code implementation is included
* OpenAPI integration. You can now fetch the OpenAPI spec for your api, and Studio will use it to map spec definitions to your api routes. Expected query parameters, headers, and body fields are then surfaced in Studio.
* Basic Command bar. You can now toggle the timeline or logs with the command bar (`cmd+k`), as well as switch between different tabs (Body, Headers, Params, Docs) in the request fields.
* Natural language request generation. You can now generate requests with an instruction prompt. Press `cmd+shift+g` to open the prompt input.

### Bug fixes
9 changes: 9 additions & 0 deletions www/src/content/changelog/2024-10-25-v0_10_0.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
date: 2024-10-25
version: 0.10.0
draft: false
---

### Features

- Fiberplane Services. Log in with GitHub to get access to 100 free AI-generated requests every day!
11 changes: 11 additions & 0 deletions www/src/content/changelog/2024-12-02-v0_12_1.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
date: 2024-12-02
version: 0.12.1
draft: false
---

### Features
* Improved code analysis. Through the new @fiberplane/source-analysis package a more flexible and thorough source code implementation is included

### Bug fixes
* Fixed an issue where the request body type would switch to "text" even when you selected "JSON"
8 changes: 8 additions & 0 deletions www/src/content/changelog/2024-12-04-v0_12_3.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
date: 2024-12-04
version: 0.12.3
draft: false
---

### Features
* Routes file tree view. You can now view your routes as they're organized in your file system.
14 changes: 14 additions & 0 deletions www/src/content/changelog/2024-12-05-v0_12_4.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
date: 2024-12-05
version: 0.12.4
draft: false
---

### Features
* OpenAPI integration. Studio will use the OpenAPI spec from Hono-Zod-OpenAPI apps to map spec definitions to your api routes. Docs for a route are shown in a new "Docs" tab.
* Basic Command bar. You can now toggle the timeline or logs with the command bar (`cmd+k`), as well as switch between different tabs (Body, Headers, Params, Docs) in the request fields.
* Natural language request generation. You can now generate requests with an instruction prompt. Press `cmd+shift+g` to open the prompt input.

### Bug fixes
* Added a tooltip to the sidebar panel toggle to make it clearer what the keyboard shortcut is (`cmd+b`)
* Fixed an issue where the route side bar would not highlight the currently selected route after you switched the method manually

0 comments on commit ef33428

Please sign in to comment.