From 9255e73a21c2ab44b6f728e0fc4f87de6e86a1a2 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Sukumar Date: Tue, 11 Jun 2024 21:12:56 +0200 Subject: [PATCH] Save actual request files natively --- .gitignore | 1 + src/plugins/core/parse/index.ts | 1 + .../parse/inputRedirectionHttpRegionParser.ts | 116 ++++++++++++++++++ .../core/parse/requestBodyHttpRegionParser.ts | 82 ++++++++++++- src/plugins/core/registerCorePlugin.ts | 1 - src/utils/parserUtils.ts | 7 +- 6 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 src/plugins/core/parse/inputRedirectionHttpRegionParser.ts diff --git a/.gitignore b/.gitignore index d673870a..8b0b8836 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ node_modules *.vsix *.env .eslintcache +.idea \ No newline at end of file diff --git a/src/plugins/core/parse/index.ts b/src/plugins/core/parse/index.ts index eb992e32..8d0759f0 100644 --- a/src/plugins/core/parse/index.ts +++ b/src/plugins/core/parse/index.ts @@ -2,6 +2,7 @@ export * from './commentHttpRegionParser'; export * from './metaHttpRegionParser'; export * from './multipartMixedInterceptor'; export * from './outputRedirectionHttpRegionParser'; +export * from './inputRedirectionHttpRegionParser'; export * from './requestBodyHttpRegionParser'; export * from './requestHttpRegionParser'; export * from './responseHttpRegionParser'; diff --git a/src/plugins/core/parse/inputRedirectionHttpRegionParser.ts b/src/plugins/core/parse/inputRedirectionHttpRegionParser.ts new file mode 100644 index 00000000..d2e95810 --- /dev/null +++ b/src/plugins/core/parse/inputRedirectionHttpRegionParser.ts @@ -0,0 +1,116 @@ +import { fileProvider, log } from '../../../io'; +import * as models from '../../../models'; +import * as utils from '../../../utils'; +import { transformToBufferOrString } from '../request'; + +export async function parseInputRedirection( + getLineReader: models.getHttpLineGenerator, + context: models.ParserContext +): Promise { + if (context.httpRegion.request) { + const lineReader = getLineReader(); + + const next = lineReader.next(); + if (!next.done) { + const textLine = next.value.textLine; + + console.log(`file Calling input redirection parser${textLine}`); + + const match = /^<(?:(?@)(?\w+)?)?\s+(?.+?)\s*$/u.exec(textLine); + + if (match && match.groups?.fileName) { + console.log(match.groups.fileName.trim()); + const fileName = `redirect-actual-${match.groups.fileName.trim()}`; + const force: boolean = true; + + context.httpRegion.hooks.onRequest.addHook('inputRedirection', async (request, context) => { + try { + console.log(`file request body is ${request.body}`); + if (request.body) { + console.log('file Request body is present'); + const body = await transformToBufferOrString(request.body, context); + const fileNameReplaced = utils.toString( + await utils.replaceVariables(fileName, models.VariableType.variable, context) + ); + console.log(`File name replaced is ${fileNameReplaced}`); + + if (fileNameReplaced) { + const file = await getInputRedirectionFileName(fileNameReplaced, force, context.httpFile.fileName); + console.log(`File name is ${file}`); + if (file) { + if (typeof body === 'string') { + await fileProvider.writeBuffer(file, Buffer.from(body)); + } else { + await fileProvider.writeBuffer(file, body); + } + } else { + log.debug(`file ${fileName} not found`); + } + } else { + log.debug(`file ${fileName} not found`); + } + } + } catch (err) { + log.error(`input redirection failed for ${fileName}`, err); + } + }); + return { + nextParserLine: next.value.line, + symbols: [ + new models.HttpSymbol({ + name: match.groups.key, + description: match.groups.value, + kind: models.HttpSymbolKind.response, + startLine: next.value.line, + startOffset: 0, + endLine: next.value.line, + endOffset: next.value.textLine.length, + children: [ + new models.HttpSymbol({ + name: 'filename', + description: fileName, + kind: models.HttpSymbolKind.path, + startLine: next.value.line, + startOffset: next.value.textLine.indexOf(fileName), + endLine: next.value.line, + endOffset: next.value.textLine.indexOf(fileName) + fileName.length, + }), + ], + }), + ], + }; + } + } + } + return false; +} + +async function getInputRedirectionFileName(fileName: string, force: boolean, baseName: models.PathLike) { + let file = await toAbsoluteFileName(fileName, baseName); + + if (!force) { + if (await fileProvider.exists(file)) { + const dotIndex = fileName.lastIndexOf('.'); + if (dotIndex > 0 && dotIndex < fileName.length - 2) { + const path = fileName.slice(0, dotIndex); + const extension = fileName.slice(dotIndex + 1); + let index = 1; + + file = await toAbsoluteFileName(`${path}-${index}.${extension}`, baseName); + while (await fileProvider.exists(file)) { + file = await toAbsoluteFileName(`${path}-${index++}.${extension}`, baseName); + } + } + } + } + return file; +} +async function toAbsoluteFileName(fileName: string, baseName: models.PathLike) { + if (!(await fileProvider.isAbsolute(fileName))) { + const dirName = fileProvider.dirname(baseName); + if (dirName) { + return fileProvider.joinPath(dirName, fileName); + } + } + return fileName; +} diff --git a/src/plugins/core/parse/requestBodyHttpRegionParser.ts b/src/plugins/core/parse/requestBodyHttpRegionParser.ts index 5ef0e288..71a55075 100644 --- a/src/plugins/core/parse/requestBodyHttpRegionParser.ts +++ b/src/plugins/core/parse/requestBodyHttpRegionParser.ts @@ -1,5 +1,7 @@ import * as models from '../../../models'; import * as utils from '../../../utils'; +import { fileProvider, log } from '../../../io'; +import { transformToBufferOrString } from '../request'; export async function parseRequestBody( getLineReader: models.getHttpLineGenerator, @@ -10,8 +12,16 @@ export async function parseRequestBody( if (context.httpRegion.request) { const requestBody = getRequestBody(context); const next = lineReader.next(); + if (!next.done) { - if (requestBody.rawBody.length > 0 || !utils.isStringEmpty(next.value.textLine)) { + const textLine = next.value.textLine; + + const match = + /^<(?:(?@)(?\w+)?)?\s+(?.+?)\s+>>(?!)?\s+(?.+?)\s*$/u.exec( + textLine + ); + + if (requestBody.rawBody.length > 0 || !utils.isStringEmpty(textLine)) { const symbols: Array = []; if (!requestBody.symbol || requestBody.symbol.endLine !== next.value.line - 1) { @@ -23,7 +33,7 @@ export async function parseRequestBody( startOffset: 0, endLine: next.value.line, endOffset: next.value.textLine.length, - children: utils.parseHandlebarsSymbols(next.value.textLine, next.value.line), + children: utils.parseHandlebarsSymbols(textLine, next.value.line), }); symbols.push(requestBody.symbol); } else { @@ -32,6 +42,44 @@ export async function parseRequestBody( requestBody.symbol.children?.push?.(...utils.parseHandlebarsSymbols(next.value.textLine, next.value.line)); } + if (match && match.groups?.outputFile) { + console.log(match.groups.outputFile.trim()); + const fileName = match.groups.outputFile.trim(); + const force: boolean = !!match.groups.force; + + context.httpRegion.hooks.onRequest.addHook('inputRedirection', async (request, context) => { + try { + console.log(`request body is ${request.body}`); + if (request.body) { + console.log('Request body is present'); + const body = await transformToBufferOrString(request.body, context); + const fileNameReplaced = utils.toString( + await utils.replaceVariables(fileName, models.VariableType.variable, context) + ); + console.log(`File name replaced is ${fileNameReplaced}`); + + if (fileNameReplaced) { + const file = await getInputRedirectionFileName(fileNameReplaced, force, context.httpFile.fileName); + console.log(`File name is ${file}`); + if (file) { + if (typeof body === 'string') { + await fileProvider.writeBuffer(file, Buffer.from(body)); + } else { + await fileProvider.writeBuffer(file, body); + } + } else { + log.debug(`file ${fileName} not found`); + } + } else { + log.debug(`file ${fileName} not found`); + } + } + } catch (err) { + log.error(`input redirection failed for ${fileName}`, err); + } + }); + } + const fileImport = utils.parseFileImport(next.value.textLine); if (fileImport) { requestBody.rawBody.push(fileImport); @@ -70,3 +118,33 @@ export function getRequestBody(context: models.ParserContext) { } return result; } + +async function getInputRedirectionFileName(fileName: string, force: boolean, baseName: models.PathLike) { + let file = await toAbsoluteFileName(fileName, baseName); + + if (!force) { + if (await fileProvider.exists(file)) { + const dotIndex = fileName.lastIndexOf('.'); + if (dotIndex > 0 && dotIndex < fileName.length - 2) { + const path = fileName.slice(0, dotIndex); + const extension = fileName.slice(dotIndex + 1); + let index = 1; + + file = await toAbsoluteFileName(`${path}-${index}.${extension}`, baseName); + while (await fileProvider.exists(file)) { + file = await toAbsoluteFileName(`${path}-${index++}.${extension}`, baseName); + } + } + } + } + return file; +} +async function toAbsoluteFileName(fileName: string, baseName: models.PathLike) { + if (!(await fileProvider.isAbsolute(fileName))) { + const dirName = fileProvider.dirname(baseName); + if (dirName) { + return fileProvider.joinPath(dirName, fileName); + } + } + return fileName; +} diff --git a/src/plugins/core/registerCorePlugin.ts b/src/plugins/core/registerCorePlugin.ts index ccbfea79..b0248655 100644 --- a/src/plugins/core/registerCorePlugin.ts +++ b/src/plugins/core/registerCorePlugin.ts @@ -50,7 +50,6 @@ function initParseHook(api: models.HttpyacHooksApi) { api.hooks.parse.addHook('responseRef', parse.parseResponseRef); api.hooks.parse.addHook('response', parse.parseResponse); api.hooks.parse.addHook('requestBody', parse.parseRequestBody); - api.hooks.parse.addInterceptor(new parse.MultipartMixedInterceptor()); } function initParseEndHook(api: models.HttpyacHooksApi) { diff --git a/src/utils/parserUtils.ts b/src/utils/parserUtils.ts index 5fb6e5f1..69a60dc2 100644 --- a/src/utils/parserUtils.ts +++ b/src/utils/parserUtils.ts @@ -542,8 +542,11 @@ export async function parseInlineResponse( } export function parseFileImport(text: string) { - const fileImport = /^<(?:(?@)(?\w+)?)?\s+(?.+?)\s*$/u.exec(text); - if (fileImport && fileImport.length === 4 && fileImport.groups) { + const fileImport = + /^<(?:(?@)(?\w+)?)?\s+(?.+?)\s+>>(?!)?\s+(?.+?)\s*$/u.exec( + text + ); + if (fileImport && fileImport.length === 6 && fileImport.groups) { return { fileName: fileImport.groups.fileName.trim(), injectVariables: !!fileImport.groups.injectVariables,