Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
43 changes: 43 additions & 0 deletions demo/examples/openapi-array-query-params.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
openapi: 3.0.3
info:
version: 1.0.0
title: ""
servers:
- url: https://example.com
paths:
/Things:
get:
summary: View Things
parameters:
- name: "arrayParam"
in: "query"
required: false
description: "You can pass 0, 1 or 2 occurrences of this in the query string"
style: "form"
explode: true
schema:
type: "array"
maxItems: 2
items:
type: "string"
responses:
"200":
description: OK
/Stuff:
get:
summary: View Stuff
parameters:
- name: "arrayParam"
in: "query"
required: false
description: "You can pass 0, 1 or 2 occurrences of this in the query string"
style: "pipeDelimited"
explode: false
schema:
type: "array"
maxItems: 2
items:
type: "string"
responses:
"200":
description: OK
2 changes: 1 addition & 1 deletion packages/docusaurus-plugin-openapi/src/openapi/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export interface ParameterObject {
allowEmptyValue?: boolean;
//
style?: string;
explode?: string;
explode?: boolean;
allowReserved?: boolean;
schema?: SchemaObject;
example?: any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
* ========================================================================== */

import React, { useState, useEffect } from "react";
import React, { useState } from "react";

import { nanoid } from "@reduxjs/toolkit";

Expand All @@ -21,6 +21,11 @@ interface ParamProps {
param: Param;
}

interface Item {
id: string;
value?: string;
}

function ParamOption({ param }: ParamProps) {
if (param.schema?.type === "array" && param.schema.items?.enum) {
return <ParamMultiSelectFormItem param={param} />;
Expand Down Expand Up @@ -161,10 +166,16 @@ function ArrayItem({
}

function ParamArrayFormItem({ param }: ParamProps) {
const [items, setItems] = useState<{ id: string; value?: string }[]>([]);
const [items, setItems] = useState<Item[]>([]);
const dispatch = useTypedDispatch();

function handleAddItem() {
if (
param?.schema?.maxItems !== undefined &&
items.length >= param.schema.maxItems
) {
return;
}
setItems((i) => [
...i,
{
Expand All @@ -173,7 +184,7 @@ function ParamArrayFormItem({ param }: ParamProps) {
]);
}

useEffect(() => {
function updateItems(items: Array<Item>) {
const values = items
.map((item) => item.value)
.filter((item): item is string => !!item);
Expand All @@ -184,12 +195,13 @@ function ParamArrayFormItem({ param }: ParamProps) {
value: values.length > 0 ? values : undefined,
})
);
}, [dispatch, items, param]);
}

function handleDeleteItem(itemToDelete: { id: string }) {
return () => {
const newItems = items.filter((i) => i.id !== itemToDelete.id);
setItems(newItems);
updateItems(newItems);
};
}

Expand All @@ -202,6 +214,7 @@ function ParamArrayFormItem({ param }: ParamProps) {
return i;
});
setItems(newItems);
updateItems(newItems);
};
}

Expand Down Expand Up @@ -230,9 +243,12 @@ function ParamArrayFormItem({ param }: ParamProps) {
</button>
</div>
))}
<button className={styles.buttonThin} onClick={handleAddItem}>
Add item
</button>
{(param?.schema?.maxItems == null ||
items.length < param.schema.maxItems) && (
<button className={styles.buttonThin} onClick={handleAddItem}>
Add item
</button>
)}
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* ============================================================================
* Copyright (c) Cloud Annotations
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* ========================================================================== */

import sdk from "postman-collection";

import { openApiQueryParams2PostmanQueryParams } from "./buildPostmanRequest";

describe("openApiQueryParams2PostmanQueryParams", () => {
it("should transform empty array to empty array", () => {
const expected: sdk.QueryParam[] = [];
const actual = openApiQueryParams2PostmanQueryParams([]);
expect(actual).toStrictEqual(expected);
});

it("default to comma delimited", () => {
const expected: sdk.QueryParam[] = [
new sdk.QueryParam({ key: "arrayParam", value: "abc,def" }),
];
const actual = openApiQueryParams2PostmanQueryParams([
{
name: "arrayParam",
in: "query",
value: ["abc", "def"],
},
]);
expect(actual).toStrictEqual(expected);
});

it("should expand params if explode=true", () => {
const expected: sdk.QueryParam[] = [
new sdk.QueryParam({ key: "arrayParam", value: "abc" }),
new sdk.QueryParam({ key: "arrayParam", value: "def" }),
];
const actual = openApiQueryParams2PostmanQueryParams([
{
name: "arrayParam",
in: "query",
style: "form",
explode: true,
value: ["abc", "def"],
},
]);
expect(actual).toStrictEqual(expected);
});

it("should respect style=pipeDelimited", () => {
const expected: sdk.QueryParam[] = [
new sdk.QueryParam({ key: "arrayParam", value: "abc|def" }),
];
const actual = openApiQueryParams2PostmanQueryParams([
{
name: "arrayParam",
in: "query",
style: "pipeDelimited",
value: ["abc", "def"],
},
]);
expect(actual).toStrictEqual(expected);
});

it("should respect style=spaceDelimited", () => {
const expected: sdk.QueryParam[] = [
new sdk.QueryParam({ key: "arrayParam", value: "abc%20def" }),
];
const actual = openApiQueryParams2PostmanQueryParams([
{
name: "arrayParam",
in: "query",
style: "spaceDelimited",
value: ["abc", "def"],
},
]);
expect(actual).toStrictEqual(expected);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,36 @@ type Param = {
value?: string | string[];
} & ParameterObject;

function setQueryParams(postman: sdk.Request, queryParams: Param[]) {
postman.url.query.clear();
export function openApiQueryParams2PostmanQueryParams(
queryParams: Param[]
): sdk.QueryParam[] {
let qp = [];
for (const param of queryParams) {
if (Array.isArray(param.value) && param?.explode === true) {
for (const value of param.value) {
qp.push({ ...param, value });
}
} else {
qp.push(param);
}
}

const qp = queryParams
return qp
.map((param) => {
if (!param.value) {
return undefined;
}

let delimiter = ",";
if (param?.style === "spaceDelimited") {
delimiter = "%20";
} else if (param?.style === "pipeDelimited") {
delimiter = "|";
}
if (Array.isArray(param.value)) {
return new sdk.QueryParam({
key: param.name,
value: param.value.map(encodeURIComponent).join(","),
value: param.value.map(encodeURIComponent).join(delimiter),
});
}

Expand All @@ -50,7 +67,11 @@ function setQueryParams(postman: sdk.Request, queryParams: Param[]) {
});
})
.filter((item): item is sdk.QueryParam => item !== undefined);
}

function setQueryParams(postman: sdk.Request, queryParams: Param[]) {
postman.url.query.clear();
const qp = openApiQueryParams2PostmanQueryParams(queryParams);
if (qp.length > 0) {
postman.addQueryParams(qp);
}
Expand Down