Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d2527c9
Rename HTTP Client Extension to Hurl Client Extension in launch confi…
tharindulak Aug 6, 2026
2db5041
Enhance Hurl Client functionality with new options and tests
tharindulak Aug 6, 2026
cdd082c
Add composeHurlDocumentWithBoundaries function and update related tes…
tharindulak Aug 6, 2026
83a9a0f
Bump version to 0.9.5 and remove deprecated activation event
tharindulak Aug 7, 2026
0255956
Refactor HurlRunnerImpl to prioritize --variables-file entries before…
tharindulak Aug 7, 2026
d1241f8
Update README.md to enhance documentation on chaining requests and va…
tharindulak Aug 7, 2026
b0f4166
Refactor HurlNotebookController to streamline execution logic and enh…
tharindulak Aug 10, 2026
9b7aa05
Refactor HurlNotebookController to improve execution timing and handl…
tharindulak Aug 10, 2026
419d689
Merge branch 'main' of https://github.com/wso2/vscode-extensions into…
tharindulak Aug 10, 2026
e760466
Address CodeRabbit and Copilot review feedback on the Hurl Client PR
tharindulak Aug 10, 2026
a87fde7
Merge branch 'main' into fix-hurl-client
tharindulak Aug 13, 2026
b7a2aa1
Merge branch 'main' of https://github.com/wso2/vscode-extensions into…
tharindulak Aug 17, 2026
453ee3c
Merge branch 'fix-hurl-client' of https://github.com/tharindulak/vsco…
tharindulak Aug 17, 2026
9745974
Resolve a relative hurl-client.fileRoot against the workspace folder
tharindulak Aug 17, 2026
cd091f7
Merge branch 'main' of https://github.com/wso2/vscode-extensions into…
tharindulak Aug 20, 2026
db23661
Merge branch 'main' into fix-hurl-client
tharindulak Sep 7, 2026
bcaf757
Bump fast-uri and qs overrides to clear Trivy findings
tharindulak Sep 7, 2026
ad1238c
Merge remote-tracking branch 'origin/fix-hurl-client' into fix-hurl-c…
tharindulak Sep 7, 2026
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 .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@
"preLaunchTask": "npm: watch-apk"
},
{
"name": "HTTP Client Extension",
"name": "Hurl Client Extension",
"type": "extensionHost",
"request": "launch",
"debugWebviews": true,
Expand Down
3 changes: 2 additions & 1 deletion common/config/rush/pnpm-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@
"@codemirror/view": "6.38.8",
"@codemirror/lint": "6.8.5",
"@codemirror/state": "6.5.2",
"qs": ">=6.15.2",
"qs": ">=6.16.0",
"fast-uri": "^3.1.6",
"@babel/core": ">=7.29.7",
"dompurify": ">=3.4.12",
"js-yaml": "4.3.1",
Expand Down
33 changes: 17 additions & 16 deletions common/config/rush/pnpm-lock.yaml

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

52 changes: 40 additions & 12 deletions workspaces/api-tryit/hurl-parser/src/hurl-collection-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,23 +158,51 @@ export function splitHurlRequestBlocks(content: string): string[] {
return parseHurlDocument(content).blocks.map(block => block.text);
}

export interface HurlDocumentBoundary {
/** Index into the `blocks` array passed to composeHurlDocumentWithBoundaries. */
sourceIndex: number;
startLine: number;
endLine: number;
}

/**
* Like composeHurlDocument, but also reports the line range each surviving
* block occupies in the combined document. Blocks that are empty after
* trimming are dropped, exactly as composeHurlDocument drops them, and are
* simply absent from `boundaries` - callers that need to correlate back to
* their original block list should use `sourceIndex`, not array position.
*/
export function composeHurlDocumentWithBoundaries(blocks: string[]): { document: string; boundaries: HurlDocumentBoundary[] } {
const boundaries: HurlDocumentBoundary[] = [];
const survivingBlocks: string[] = [];
let currentLine = 1;

blocks.forEach((block, sourceIndex) => {
const normalized = normalizeHurlLineEndings(block).trim();
if (!normalized) {
return;
}
const lineCount = normalized.split('\n').length;
boundaries.push({ sourceIndex, startLine: currentLine, endLine: currentLine + lineCount - 1 });
survivingBlocks.push(normalized);
currentLine += lineCount + 1; // account for the blank separator line
});

const document = survivingBlocks.length > 0 ? `${survivingBlocks.join('\n\n').trimEnd()}\n` : '';
return { document, boundaries };
}

export function composeHurlDocument(header: string, blocks: string[]): string {
const normalizedHeader = normalizeHurlLineEndings(header).trim();
const normalizedBlocks = blocks
.map(block => normalizeHurlLineEndings(block).trim())
.filter(block => block.length > 0);
const { document: blocksDocument } = composeHurlDocumentWithBoundaries(blocks);

const sections: string[] = [];
if (normalizedHeader) {
sections.push(normalizedHeader);
if (!normalizedHeader) {
return blocksDocument;
}
sections.push(...normalizedBlocks);

if (sections.length === 0) {
return '';
if (!blocksDocument) {
return `${normalizedHeader}\n`;
}

return `${sections.join('\n\n').trimEnd()}\n`;
return `${normalizedHeader}\n\n${blocksDocument}`;
}

export function upsertCollectionNameInHurl(content: string, collectionName: string): string {
Expand Down
93 changes: 92 additions & 1 deletion workspaces/api-tryit/hurl-parser/tests/hurl-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
parseHurlCollection,
} from '../src';
import type { ApiCollection } from '@wso2/api-tryit-core';
import { parseHurlDocument } from '../src';
import { composeHurlDocument, composeHurlDocumentWithBoundaries, parseHurlDocument } from '../src';

// verify that the utility for splitting Hurl documents is available

Expand Down Expand Up @@ -53,6 +53,97 @@ describe('parseHurlDocument helper', () => {
});
});

describe('composeHurlDocument for combining notebook cells into one Chained Run', () => {
it('combines separately-authored entry sources into one document that reparses in the same order', () => {
// Mirrors the marketplace review's exact scenario: a GET that captures
// api_key, followed by an entry that uses {{api_key}} - each string here
// stands in for one notebook cell's own text.
const capturingCell = [
'GET http://localhost:3333',
'HTTP 200',
'[Captures]',
'api_key: jsonpath "$.api_key"',
].join('\n');
const dependentCell = [
'GET http://localhost:3333/protected',
'Authorization: Bearer {{api_key}}',
'HTTP 200',
].join('\n');

const combined = composeHurlDocument('', [capturingCell, dependentCell]);
const { blocks } = parseHurlDocument(combined);

expect(blocks).toHaveLength(2);
expect(blocks[0].text).toContain('[Captures]');
expect(blocks[0].text).toContain('api_key: jsonpath "$.api_key"');
expect(blocks[1].text).toContain('{{api_key}}');
expect(blocks[1].method).toBe('GET');
expect(blocks[1].url).toBe('http://localhost:3333/protected');
});

it('preserves cell order for more than two combined cells', () => {
const cells = [
'GET https://example.com/one\nHTTP 200',
'GET https://example.com/two\nHTTP 200',
'GET https://example.com/three\nHTTP 200',
];

const combined = composeHurlDocument('', cells);
const { blocks } = parseHurlDocument(combined);

expect(blocks.map(b => b.url)).toEqual([
'https://example.com/one',
'https://example.com/two',
'https://example.com/three',
]);
});

it('drops cells that are blank after trimming rather than producing an empty block', () => {
const combined = composeHurlDocument('', ['GET https://example.com/one\nHTTP 200', ' ', '']);
const { blocks } = parseHurlDocument(combined);

expect(blocks).toHaveLength(1);
});
});

describe('composeHurlDocumentWithBoundaries', () => {
it('reports the line range each block occupies in the combined document', () => {
const blocks = ['GET https://example.com/one\nHTTP 200', 'GET https://example.com/two\nHTTP 200'];
const { document, boundaries } = composeHurlDocumentWithBoundaries(blocks);

expect(document.split('\n')).toEqual([
'GET https://example.com/one', 'HTTP 200', '', 'GET https://example.com/two', 'HTTP 200', ''
]);
expect(boundaries).toEqual([
{ sourceIndex: 0, startLine: 1, endLine: 2 },
{ sourceIndex: 1, startLine: 4, endLine: 5 }
]);
});

it('leaves a gap in source indices for blocks dropped as blank, without shifting later boundaries', () => {
// Mirrors the notebook header-cell bug: a comment-only block that
// survives (it's non-blank, just has no request) still gets its own
// boundary and line range - callers correlate back to their original
// cell list via sourceIndex, not array position.
const blocks = [
'GET https://example.com/one\nHTTP 200',
'# just a comment, no request',
'GET https://example.com/two\nHTTP 200'
];
const { boundaries } = composeHurlDocumentWithBoundaries(blocks);

expect(boundaries.map(b => b.sourceIndex)).toEqual([0, 1, 2]);
expect(boundaries[1].startLine).toBe(4);
expect(boundaries[2].startLine).toBe(6);
});

it('drops genuinely blank blocks entirely, leaving a gap in sourceIndex', () => {
const blocks = ['GET https://example.com/one\nHTTP 200', ' ', 'GET https://example.com/two\nHTTP 200'];
const { boundaries } = composeHurlDocumentWithBoundaries(blocks);

expect(boundaries.map(b => b.sourceIndex)).toEqual([0, 2]);
});
});

describe('parseHurlCollection', () => {
it('parses a single Hurl request into an ApiCollection model', () => {
Expand Down
9 changes: 9 additions & 0 deletions workspaces/api-tryit/hurl-runner/src/hurl-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,12 @@ export class HurlRunnerImpl implements HurlRunner {
if (options.includeResponseOutput) {
args.push('-i');
}
// Order matters: later --variables-file entries override earlier ones for
// values they both define, so callers pass the shared file before any
// per-file override.
for (const variablesFilePath of options.variablesFilePaths || []) {
args.push('--variables-file', variablesFilePath);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if (options.insecure) {
args.push('-k');
}
Expand All @@ -416,6 +422,9 @@ export class HurlRunnerImpl implements HurlRunner {
for (const [key, value] of Object.entries(options.variables || {})) {
args.push('--variable', `${key}=${value}`);
}
if (options.extraArgs && options.extraArgs.length > 0) {
args.push(...options.extraArgs);
}
return args;
}

Expand Down
Loading
Loading