Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion QA-CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
- [-] GET with session_id filter returns only messages from that session

#### 1.6 Integration Code Generation
- [-] Generate curl for API execution
- [x] Generate curl for API execution → `flow-functionality/curlApiGeneration.spec.ts`
- [-] Generate Python code for integration
- [-] API access modal

Expand Down
4 changes: 2 additions & 2 deletions QA-SCENARIOS-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,15 +303,15 @@

---

### 5.1 Generate curl for execution `[-]`
### 5.1 Generate curl for execution `[x]`

**Objective:** Verify that Langflow generates a valid `curl` command for flow execution.

**Step by step:**
1. Open a flow in the editor.
2. Click the "API Access" button (api-access-button).
3. Select the `cURL` tab.
4. Verify that the generated code contains the correct flow URL and the `curl -X POST` method.
4. Verify that the generated code targets the correct flow URL and uses the `POST` HTTP method (the actual `curl` invocation may use either `-X POST` or `--request POST` depending on Langflow's snippet generator).

**Validation:** Generated curl code points to the correct flow endpoint.

Expand Down
87 changes: 87 additions & 0 deletions docs/flow-functionality/curlApiGeneration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Flow Functionality — cURL API Generation

**Last validated:** Langflow 1.10.x

---

## What this test validates *(required)*

Validates that Langflow's **API access modal** generates a valid macOS/Linux `curl` command that callers can execute against `/api/v1/run/{flow_id}` to run a flow programmatically. The test asserts the structural shape of the generated command, not just that the clipboard received some text.

If this breaks, integrators copying the snippet will hit malformed requests, missing headers, or a wrong URL — silently breaking the documented integration path that ships with every flow.

---

## Tags *(required)*

`@release` `@workspace` `@stable`

---

## Step by step *(required)*

1. Bootstrap the app and open the Templates page (`side_nav_options_all-templates`)
2. Open the `Basic Prompting` template
3. Click the publish button and open the API access item
4. Switch to the `cURL` tab (`api_tab_curl`)
5. Switch the platform sub-tab to `macOS/Linux` to make the output deterministic
6. Click the Copy icon and read `navigator.clipboard`
7. Assert the structural shape of the curl command (see Validation criterion)

---

## Validation criterion *(required)*

The clipboard content must satisfy **all** of the following:

- Starts with `curl --request POST`
- Contains `--url '<base>/api/v1/run/<UUID>?stream=false'` (UUID matched by `[0-9a-f-]{36}`)
- Contains `--header 'Content-Type: application/json'`
- Contains `x-api-key: YOUR_API_KEY_HERE`
- Contains `--data`
- Contains `"input_value": "Hello"` (the default value carried over from the Basic Prompting template's ChatInput)
- Contains `"session_id"` and `"output_type": "chat"`

A bare "clipboard is non-empty" check is insufficient — the previous version of the test was passing while silently copying the PowerShell variant.

---

## External dependencies *(required)*

- `src/frontend/src/modals/apiModal/utils/get-curl-code.tsx` — `getNewCurlCode` builds the command for both `unix` and `powershell` platforms
- `src/frontend/src/modals/apiModal/codeTabs/code-tabs.tsx` — renders the cURL tab and the macOS/Linux ↔ Windows platform switch (default driven by `getOS()`)
- `src/frontend/src/utils/utils.ts` — `getOS()` reads `navigator.platform` to pick the default platform tab
- `src/backend/base/langflow/api/v1/endpoints.py` — owns `/api/v1/run/{flow_id}`; the URL shape encoded in the curl must keep matching this route

---

## What this test does not cover *(optional)*

- The Windows/PowerShell variant (different syntax: `$jsonData = @'...'@`, `curl.exe`, backtick line-continuation)
- Tweaks payload encoding for flows with file-upload nodes (multi-step curl)
- Actually executing the generated command against the running backend (covered by API tests under `api/flows/`)
- The Python and JavaScript snippets in the same modal (covered by `pythonApiGeneration.spec.ts`)

---

## Preconditions *(optional)*

- Langflow running at `PLAYWRIGHT_BASE_URL`
- No LLM credentials required — only the snippet generator is exercised
- `clipboard-read` permission is granted globally in `playwright.config.ts`

---

## When to review this test *(optional)*

- The cURL tab gains a new platform option (e.g., a `bash`-only sub-tab) — the explicit `macOS/Linux` click may need to change
- `getNewCurlCode` is refactored to drop `--request POST` in favor of `-X POST`, or to switch quoting style
- The `/api/v1/run/{flow_id}` route is renamed or namespaced
- The Basic Prompting template's default ChatInput value changes from `"Hello"` — the assertion `"input_value": "Hello"` will need to track it

---

## Notes *(optional)*

- The platform switch is required because `getOS()` is driven by `navigator.platform`, which can differ between local Chromium runs and CI runners. Without explicitly clicking macOS/Linux, the generated snippet is non-deterministic.
- The previous version of this test asserted only `clipboardContent.length > 0`, which would pass even if the wrong (PowerShell) variant or empty content was copied. The structural assertions guard against that class of false positive.
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,46 @@ import { expect, test } from "../../../fixtures/fixtures";
import { awaitBootstrapTest } from "../../../helpers/other/await-bootstrap-test";

test(
"curl_api_generation",
{ tag: ["@release", "@api", "@workspace"] },
async ({ page, context }) => {
await awaitBootstrapTest(page);
"user can copy a valid macOS/Linux curl command from the API access modal",
{ tag: ["@release", "@workspace", "@stable"] },
async ({ page }) => {
await test.step("Open the Basic Prompting template", async () => {
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
});

await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page.getByTestId("publish-button").click();
await page.getByTestId("api-access-item").click();
await page.getByTestId("api_tab_curl").click();
await page.getByTestId("icon-Copy").last().click();
const handle = await page.evaluateHandle(() =>
await test.step("Open the API access modal on the cURL tab", async () => {
await page.getByTestId("publish-button").click();
await page.getByTestId("api-access-item").click();
await page.getByTestId("api_tab_curl").click();
});

await test.step("Force the macOS/Linux platform variant", async () => {
await page.getByRole("tab", { name: "macOS/Linux" }).click();
});

await test.step("Copy the generated command to the clipboard", async () => {
await page.getByTestId("icon-Copy").last().click();
});

const clipboardContent = await page.evaluate(() =>
navigator.clipboard.readText(),
);
const clipboardContent = await handle.jsonValue();
expect(clipboardContent.length).toBeGreaterThan(0);

await test.step("Validate the curl command structure", async () => {
expect(clipboardContent).toMatch(/^curl --request POST/);
expect(clipboardContent).toMatch(
/--url '[^']*\/api\/v1\/run\/[0-9a-f-]{36}\?stream=false'/,
);
expect(clipboardContent).toContain(
"--header 'Content-Type: application/json'",
);
expect(clipboardContent).toContain("x-api-key: YOUR_API_KEY_HERE");
expect(clipboardContent).toContain("--data");
expect(clipboardContent).toContain('"input_value": "Hello"');
expect(clipboardContent).toContain('"session_id"');
expect(clipboardContent).toContain('"output_type": "chat"');
});
},
);
Loading