Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes and enhancements for array query param handling #241

Merged
merged 8 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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() {
bourdakos1 marked this conversation as resolved.
Show resolved Hide resolved
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,16 @@ 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>
)) || (
<button className={styles.buttonThin} disabled>
Add item
</button>
)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit hard to follow. Can we do something like:

<button 
  className={styles.buttonThin}
  onClick={handleAddItem}
  disabled={param?.schema?.maxItems != null && param.schema.maxItems <= items.length}
>
  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