diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 0f4394a5ec..a2bf3715e9 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -955,7 +955,7 @@ yargs describe: 'Description file path', type: 'string', }) - .env('REDOCLY_SPOT') + .env('REDOCLY_CLI_RESPECT') .options({ 'output-file': { alias: 'o', diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/auto-with-lint-errors.yaml b/packages/respect-core/src/modules/__tests__/config-parser/auto-with-lint-errors.yaml similarity index 100% rename from packages/respect-core/src/modules/__tests__/config-parcer/auto-with-lint-errors.yaml rename to packages/respect-core/src/modules/__tests__/config-parser/auto-with-lint-errors.yaml diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/auto.yaml b/packages/respect-core/src/modules/__tests__/config-parser/auto.yaml similarity index 100% rename from packages/respect-core/src/modules/__tests__/config-parcer/auto.yaml rename to packages/respect-core/src/modules/__tests__/config-parser/auto.yaml diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/get-value-from-context.test.ts b/packages/respect-core/src/modules/__tests__/config-parser/get-value-from-context.test.ts similarity index 100% rename from packages/respect-core/src/modules/__tests__/config-parcer/get-value-from-context.test.ts rename to packages/respect-core/src/modules/__tests__/config-parser/get-value-from-context.test.ts diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/handle-request-body-replacements.test.ts b/packages/respect-core/src/modules/__tests__/config-parser/handle-request-body-replacements.test.ts similarity index 100% rename from packages/respect-core/src/modules/__tests__/config-parcer/handle-request-body-replacements.test.ts rename to packages/respect-core/src/modules/__tests__/config-parser/handle-request-body-replacements.test.ts diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/parse-parameters.test.ts b/packages/respect-core/src/modules/__tests__/config-parser/parse-parameters.test.ts similarity index 100% rename from packages/respect-core/src/modules/__tests__/config-parcer/parse-parameters.test.ts rename to packages/respect-core/src/modules/__tests__/config-parser/parse-parameters.test.ts diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/parse-request-body.test.ts b/packages/respect-core/src/modules/__tests__/config-parser/parse-request-body.test.ts similarity index 64% rename from packages/respect-core/src/modules/__tests__/config-parcer/parse-request-body.test.ts rename to packages/respect-core/src/modules/__tests__/config-parser/parse-request-body.test.ts index 7f99ca61f1..8d70f9a6f9 100644 --- a/packages/respect-core/src/modules/__tests__/config-parcer/parse-request-body.test.ts +++ b/packages/respect-core/src/modules/__tests__/config-parser/parse-request-body.test.ts @@ -1,24 +1,33 @@ import * as fs from 'node:fs'; import { Buffer } from 'node:buffer'; -import type { RequestBody } from '../../../types'; +import type { RequestBody, TestContext } from '../../../types'; import { parseRequestBody, stripFileDecorator } from '../../config-parser'; jest.mock('node:fs'); describe('parseRequestBody', () => { + const ctx = { + options: { + workflowPath: 'test.yaml', + }, + } as unknown as TestContext; + it('should return empty object if no body', async () => { - expect(await parseRequestBody(undefined)).toEqual({}); + expect(await parseRequestBody(undefined, ctx)).toEqual({}); }); it('should return body with no ctx provided', async () => { expect( - await parseRequestBody({ - payload: { - test: 'test', + await parseRequestBody( + { + payload: { + test: 'test', + }, }, - }) + ctx + ) ).toEqual({ payload: { test: 'test' }, contentType: undefined, @@ -28,16 +37,19 @@ describe('parseRequestBody', () => { it('should return body with ctx provided', async () => { expect( - await parseRequestBody({ - payload: 'clientId={$input.clientID}&grant_type=12', - contentType: 'application/x-www-form-urlencoded', - replacements: [ - { - target: '/clientId', - value: '123', - }, - ], - }) + await parseRequestBody( + { + payload: 'clientId={$input.clientID}&grant_type=12', + contentType: 'application/x-www-form-urlencoded', + replacements: [ + { + target: '/clientId', + value: '123', + }, + ], + }, + ctx + ) ).toEqual({ payload: { clientId: '123', @@ -50,13 +62,16 @@ describe('parseRequestBody', () => { it('should return body with string replacement applied', async () => { expect( - await parseRequestBody({ - payload: { - test: 'test', + await parseRequestBody( + { + payload: { + test: 'test', + }, + contentType: 'application/json', + encoding: 'utf-8', }, - contentType: 'application/json', - encoding: 'utf-8', - }) + ctx + ) ).toEqual({ payload: { test: 'test' }, contentType: 'application/json', @@ -66,13 +81,16 @@ describe('parseRequestBody', () => { it('should handle multipart/form-data', async () => { expect( - await parseRequestBody({ - payload: { - test: 'test', + await parseRequestBody( + { + payload: { + test: 'test', + }, + contentType: 'multipart/form-data', + encoding: 'utf-8', }, - contentType: 'multipart/form-data', - encoding: 'utf-8', - }) + ctx + ) ).toEqual({ payload: expect.any(Object), contentType: expect.stringMatching( @@ -89,14 +107,17 @@ describe('parseRequestBody', () => { callback(); }); expect( - await parseRequestBody({ - payload: { - test: 'test', - file: "$file('file1.txt')", + await parseRequestBody( + { + payload: { + test: 'test', + file: "$file('file1.txt')", + }, + contentType: 'multipart/form-data', + encoding: 'utf-8', }, - contentType: 'multipart/form-data', - encoding: 'utf-8', - }) + ctx + ) ).toEqual({ payload: expect.any(Object), contentType: expect.stringMatching( @@ -109,16 +130,19 @@ describe('parseRequestBody', () => { }); it('should handle multipart/form-data with nested object', async () => { - const { payload, contentType, encoding } = await parseRequestBody({ - payload: { - commit: { - message: 'test', - author: 'John Doe', + const { payload, contentType, encoding } = await parseRequestBody( + { + payload: { + commit: { + message: 'test', + author: 'John Doe', + }, }, + contentType: 'multipart/form-data', + encoding: 'utf-8', }, - contentType: 'multipart/form-data', - encoding: 'utf-8', - }); + ctx + ); expect(contentType).toMatch('multipart/form-data; boundary=--------------------------'); expect(encoding).toBe('utf-8'); expect(typeof payload).toBe('object'); @@ -131,14 +155,17 @@ describe('parseRequestBody', () => { it('should handle multipart/form-data with array', async () => { expect( - await parseRequestBody({ - payload: { - test: 'test', - array: ['test', 'test2'], + await parseRequestBody( + { + payload: { + test: 'test', + array: ['test', 'test2'], + }, + contentType: 'multipart/form-data', + encoding: 'utf-8', }, - contentType: 'multipart/form-data', - encoding: 'utf-8', - }) + ctx + ) ).toEqual({ payload: expect.any(Object), contentType: expect.stringMatching( @@ -155,14 +182,17 @@ describe('parseRequestBody', () => { callback(); }); expect( - await parseRequestBody({ - payload: { - test: 'test', - array: ['test', "$file('file2.txt')"], + await parseRequestBody( + { + payload: { + test: 'test', + array: ['test', "$file('file2.txt')"], + }, + contentType: 'multipart/form-data', + encoding: 'utf-8', }, - contentType: 'multipart/form-data', - encoding: 'utf-8', - }) + ctx + ) ).toEqual({ payload: expect.any(Object), contentType: expect.stringMatching( @@ -180,14 +210,17 @@ describe('parseRequestBody', () => { callback(new Error('error')); }); try { - await parseRequestBody({ - payload: { - test: 'test', - array: ['test', "$file('file2.txt')"], + await parseRequestBody( + { + payload: { + test: 'test', + array: ['test', "$file('file2.txt')"], + }, + contentType: 'multipart/form-data', + encoding: 'utf-8', }, - contentType: 'multipart/form-data', - encoding: 'utf-8', - }); + ctx + ); } catch (error) { expect(error.message).toContain("file2.txt doesn't exist or isn't readable."); } @@ -200,11 +233,14 @@ describe('parseRequestBody', () => { callback(); }); expect( - await parseRequestBody({ - payload: "$file('file3.txt')", - contentType: 'application/octet-stream', - encoding: 'utf-8', - }) + await parseRequestBody( + { + payload: "$file('file3.txt')", + contentType: 'application/octet-stream', + encoding: 'utf-8', + }, + ctx + ) ).toEqual({ payload: 'readStream', contentType: 'application/octet-stream', @@ -215,11 +251,14 @@ describe('parseRequestBody', () => { it('should handle application/octet-stream with string payload', async () => { expect( - await parseRequestBody({ - payload: new Buffer('test') as unknown as RequestBody['payload'], - contentType: 'application/octet-stream', - encoding: 'utf-8', - }) + await parseRequestBody( + { + payload: new Buffer('test') as unknown as RequestBody['payload'], + contentType: 'application/octet-stream', + encoding: 'utf-8', + }, + ctx + ) ).toEqual({ payload: new Buffer('test'), contentType: 'application/octet-stream', @@ -234,11 +273,14 @@ describe('parseRequestBody', () => { callback(new Error('error')); }); await expect( - parseRequestBody({ - payload: "$file('file3.txt')", - contentType: 'application/octet-stream', - encoding: 'utf-8', - }) + parseRequestBody( + { + payload: "$file('file3.txt')", + contentType: 'application/octet-stream', + encoding: 'utf-8', + }, + ctx + ) ).rejects.toThrow("file3.txt doesn't exist or isn't readable."); }); }); diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/resolve-reusable-component-item.test.ts b/packages/respect-core/src/modules/__tests__/config-parser/resolve-reusable-component-item.test.ts similarity index 100% rename from packages/respect-core/src/modules/__tests__/config-parcer/resolve-reusable-component-item.test.ts rename to packages/respect-core/src/modules/__tests__/config-parser/resolve-reusable-component-item.test.ts diff --git a/packages/respect-core/src/modules/__tests__/config-parcer/resolve-reusable-object-reference.test.ts b/packages/respect-core/src/modules/__tests__/config-parser/resolve-reusable-object-reference.test.ts similarity index 100% rename from packages/respect-core/src/modules/__tests__/config-parcer/resolve-reusable-object-reference.test.ts rename to packages/respect-core/src/modules/__tests__/config-parser/resolve-reusable-object-reference.test.ts diff --git a/packages/respect-core/src/modules/config-parser/parse-request-body.ts b/packages/respect-core/src/modules/config-parser/parse-request-body.ts index 55c3d53a32..e16287e238 100644 --- a/packages/respect-core/src/modules/config-parser/parse-request-body.ts +++ b/packages/respect-core/src/modules/config-parser/parse-request-body.ts @@ -3,7 +3,7 @@ import * as path from 'node:path'; import FormData = require('form-data'); import { handlePayloadReplacements } from './handle-request-body-replacements'; import * as querystring from 'node:querystring'; -import { type RequestBody } from '../../types'; +import { type TestContext, type RequestBody } from '../../types'; const KNOWN_BINARY_CONTENT_TYPES_REGEX = /^image\/(png|jpeg|gif|bmp|webp|svg\+xml)|application\/pdf$/; @@ -14,14 +14,19 @@ export function stripFileDecorator(payload: string) { : payload; } -const appendFileToFormData = (formData: FormData, key: string, item: string): Promise => { +const appendFileToFormData = ( + formData: FormData, + key: string, + item: string, + workflowFilePath: string +): Promise => { return new Promise((resolve, reject) => { - const filePath = path.resolve(__dirname, '../', stripFileDecorator(item)); + const currentArazzoFileFolder = path.dirname(workflowFilePath); + const filePath = path.resolve(currentArazzoFileFolder, stripFileDecorator(item)); access(filePath, constants.F_OK | constants.R_OK, (err) => { if (err) { - const relativePath = path.relative(process.cwd(), filePath); - reject(new Error(`File ${relativePath} doesn't exist or isn't readable.`)); + reject(new Error(`File ${filePath} doesn't exist or isn't readable.`)); } else { formData.append(key, createReadStream(filePath)); resolve(); @@ -34,23 +39,24 @@ const appendObjectToFormData = ( promises: Promise[], formData: FormData, payload: Record, + workflowFilePath: string, parentKey?: string ) => { Object.entries(payload).forEach(([key, item]) => { const formKey = parentKey ? `${parentKey}[${key}]` : key; if (typeof item === 'string' && item.startsWith('$file(') && item.endsWith(')')) { - promises.push(appendFileToFormData(formData, formKey, item)); + promises.push(appendFileToFormData(formData, formKey, item, workflowFilePath)); } else if (Array.isArray(item)) { item.forEach((i) => { if (typeof i === 'string' && i.startsWith('$file(') && i.endsWith(')')) { - promises.push(appendFileToFormData(formData, formKey, i)); + promises.push(appendFileToFormData(formData, formKey, i, workflowFilePath)); } else { formData.append(formKey, i.toString()); } }); } else if (typeof item === 'object' && item !== null) { - appendObjectToFormData(promises, formData, item, formKey); + appendObjectToFormData(promises, formData, item, workflowFilePath, formKey); } else if (typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean') { formData.append(formKey, item.toString()); } @@ -59,11 +65,12 @@ const appendObjectToFormData = ( const getRequestBodyMultipartFormData = async ( payload: RequestBody['payload'], - formData: FormData + formData: FormData, + workflowFilePath: string ) => { if (payload && typeof payload === 'object' && !Array.isArray(payload)) { const promises: Promise[] = []; - appendObjectToFormData(promises, formData, payload); + appendObjectToFormData(promises, formData, payload, workflowFilePath); await Promise.all(promises); } }; @@ -88,7 +95,10 @@ const getRequestBodyOctetStream = async (payload: RequestBody['payload']) => { } }; -export async function parseRequestBody(stepRequestBody: RequestBody | undefined): Promise<{ +export async function parseRequestBody( + stepRequestBody: RequestBody | undefined, + ctx: TestContext +): Promise<{ payload: any | undefined; contentType: string | undefined; encoding: string | undefined; @@ -105,8 +115,9 @@ export async function parseRequestBody(stepRequestBody: RequestBody | undefined) if (contentType === 'multipart/form-data') { const formData = new FormData(); + const workflowFilePath = path.resolve(ctx.options.workflowPath); - await getRequestBodyMultipartFormData(payload, formData); + await getRequestBodyMultipartFormData(payload, formData, workflowFilePath); return { payload: formData, diff --git a/packages/respect-core/src/modules/flow-runner/prepare-request.ts b/packages/respect-core/src/modules/flow-runner/prepare-request.ts index 5ff9aaf228..b954053514 100644 --- a/packages/respect-core/src/modules/flow-runner/prepare-request.ts +++ b/packages/respect-core/src/modules/flow-runner/prepare-request.ts @@ -80,7 +80,7 @@ export async function prepareRequest( payload: stepRequestBodyPayload, // encoding: stepRequestBodyEncoding, contentType: stepRequestBodyContentType, - } = await parseRequestBody(step['requestBody']); + } = await parseRequestBody(step['requestBody'], ctx); const requestBody = stepRequestBodyPayload || requestDataFromOpenAPI?.requestBody; const contentType = stepRequestBodyContentType || requestDataFromOpenAPI?.contentType;