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
9 changes: 9 additions & 0 deletions package-lock.json

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

7 changes: 4 additions & 3 deletions packages/oas-to-har/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ function isPrimitive(val: unknown) {
}

function stringify(json: Record<string | 'RAW_BODY', unknown>) {
return JSON.stringify(removeUndefinedObjects(typeof json.RAW_BODY !== 'undefined' ? json.RAW_BODY : json));
return JSON.stringify(removeUndefinedObjects(typeof json.RAW_BODY !== 'undefined' ? json.RAW_BODY : json, { preserveNullishArrays: true }));
}

function stringifyParameter(param: any): string {
Expand Down Expand Up @@ -420,7 +420,8 @@ export default function oasToHar(

if (operation.isFormUrlEncoded()) {
if (Object.keys(formData.formData || {}).length) {
const cleanFormData = removeUndefinedObjects(JSON.parse(JSON.stringify(formData.formData)));
const cleanFormData = removeUndefinedObjects(formData.formData, { preserveNullishArrays: true });

if (cleanFormData !== undefined) {
const postData: PostData = { params: [], mimeType: 'application/x-www-form-urlencoded' };

Expand All @@ -444,7 +445,7 @@ export default function oasToHar(

if (isMultipart || isJSON) {
try {
let cleanBody = removeUndefinedObjects(JSON.parse(JSON.stringify(formData.body)));
let cleanBody = removeUndefinedObjects(formData.body, { preserveNullishArrays: true });

if (isMultipart) {
har.postData = { params: [], mimeType: 'multipart/form-data' };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,21 @@ function encodeArray({
escape,
isAllowedReserved = false,
}: Omit<StylizerConfig, 'value'> & { value: string[] }) {
const valueEncoder = (str: string) =>
encodeDisallowedCharacters(str, {
const valueEncoder = (str: string) => {
// Handle null values explicitly to prevent join() from converting to empty string
if (str === null) {
return 'null';
}

const result = encodeDisallowedCharacters(str, {
escape,
returnIfEncoded: location === 'query',
isAllowedReserved,
});

return result;
};

switch (style) {
/**
* @example <caption>`style: simple`</caption>
Expand Down
24 changes: 23 additions & 1 deletion packages/oas-to-har/test/parameters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ describe('parameter handling', () => {
parameters: [{ name: 'id', in: 'query' }],
},
{ query: { id: [null, null] } },
[{ name: 'id', value: '&id=' }],
[{ name: 'id', value: 'null&id=null' }],
),
);

Expand Down Expand Up @@ -207,6 +207,28 @@ describe('parameter handling', () => {
],
},
{ query: {} },
[{ name: 'id', value: 'null&id=null' }],
),
);

it(
'should handle mixed array with null, undefined, and normal values',
assertQueryParams(
{
parameters: [{ name: 'id', in: 'query' }],
},
{ query: { id: [null, undefined, 'normal', null, 'test'] } },
[{ name: 'id', value: 'null&id=&id=normal&id=null&id=test' }],
),
);

it(
'should handle array with only undefined values',
assertQueryParams(
{
parameters: [{ name: 'id', in: 'query' }],
},
{ query: { id: [undefined, undefined] } },
[{ name: 'id', value: '&id=' }],
),
);
Expand Down
46 changes: 46 additions & 0 deletions packages/oas-to-har/test/requestBody.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,52 @@ describe('request body handling', () => {
expect(har.log.entries[0].request.postData).toBeUndefined();
});

it('should preserve null values in arrays & still remove undefined values', () => {
const spec = Oas.init({
paths: {
'/requestBody': {
post: {
requestBody: {
content: {
'application/x-www-form-urlencoded': {
schema: {
type: 'object',
properties: {
foo: {
type: 'array',
items: {
type: 'string',
nullable: true,
},
},
foo2: {
type: 'array',
items: {
type: 'number',
},
},
},
},
},
},
},
},
},
},
});

const har = oasToHar(spec, spec.operation('/requestBody', 'post'), {
formData: { foo: [null, null, undefined], foo2: [1, 2] },
});

expect(har.log.entries[0].request.postData?.params).toStrictEqual([
// Since null is not a primitive, it will be JSON stringified which retains the square brackets
// See line 156-164 of src/index.ts
{ name: 'foo', value: '[null,null]' },
{ name: 'foo2', value: '1,2' },
]);
});

it('should pass in value if one is set and prioritize provided values', () => {
const spec = Oas.init({
paths: {
Expand Down
Loading