docusaurus-theme-openapi-docs 0.0.0-1160 → 0.0.0-1161

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.d.ts +2 -1
  2. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.js +47 -0
  3. package/lib/theme/ApiExplorer/Body/index.js +4 -0
  4. package/lib/theme/ApiExplorer/CodeSnippets/index.d.ts +2 -1
  5. package/lib/theme/ApiExplorer/CodeSnippets/index.js +4 -0
  6. package/lib/theme/ApiExplorer/ContentType/index.js +5 -1
  7. package/lib/theme/ApiExplorer/EncodingSelection/slice.d.ts +17 -0
  8. package/lib/theme/ApiExplorer/EncodingSelection/slice.js +29 -0
  9. package/lib/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.d.ts +12 -0
  10. package/lib/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.js +39 -0
  11. package/lib/theme/ApiExplorer/Request/index.js +7 -1
  12. package/lib/theme/ApiExplorer/Request/makeRequest.d.ts +3 -1
  13. package/lib/theme/ApiExplorer/Request/makeRequest.js +17 -3
  14. package/lib/theme/ApiExplorer/buildPostmanRequest.d.ts +4 -1
  15. package/lib/theme/ApiExplorer/buildPostmanRequest.js +46 -5
  16. package/lib/theme/ApiExplorer/index.js +1 -0
  17. package/lib/theme/ApiExplorer/persistenceMiddleware.d.ts +2 -0
  18. package/lib/theme/ApiItem/hooks.d.ts +1 -0
  19. package/lib/theme/ApiItem/index.js +1 -0
  20. package/lib/theme/ApiItem/store.d.ts +6 -0
  21. package/lib/theme/ApiItem/store.js +11 -7
  22. package/package.json +3 -3
  23. package/src/theme/ApiExplorer/Body/FormBodyItem/index.tsx +55 -10
  24. package/src/theme/ApiExplorer/Body/index.tsx +5 -0
  25. package/src/theme/ApiExplorer/CodeSnippets/index.tsx +9 -1
  26. package/src/theme/ApiExplorer/ContentType/index.tsx +5 -3
  27. package/src/theme/ApiExplorer/EncodingSelection/slice.ts +31 -0
  28. package/src/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.ts +43 -0
  29. package/src/theme/ApiExplorer/Request/index.tsx +6 -1
  30. package/src/theme/ApiExplorer/Request/makeRequest.ts +17 -3
  31. package/src/theme/ApiExplorer/buildPostmanRequest.ts +52 -5
  32. package/src/theme/ApiExplorer/index.tsx +1 -0
  33. package/src/theme/ApiItem/index.tsx +1 -0
  34. package/src/theme/ApiItem/store.ts +2 -0
  35. package/tsconfig.tsbuildinfo +1 -1
@@ -7,6 +7,7 @@ interface FormBodyItemProps {
7
7
  label?: string;
8
8
  required?: boolean;
9
9
  exampleValue?: SchemaObject["example"];
10
+ fieldEncoding?: string;
10
11
  }
11
- export default function FormBodyItem({ schemaObject, id, schema, label, required, exampleValue, }: FormBodyItemProps): React.JSX.Element;
12
+ export default function FormBodyItem({ schemaObject, id, schema, label, required, exampleValue, fieldEncoding, }: FormBodyItemProps): React.JSX.Element;
12
13
  export {};
@@ -83,6 +83,7 @@ const FileArrayFormBodyItem_1 = __importDefault(
83
83
  require("../FileArrayFormBodyItem")
84
84
  );
85
85
  const slice_1 = require("../slice");
86
+ const slice_2 = require("../../EncodingSelection/slice");
86
87
  function FormBodyItem({
87
88
  schemaObject,
88
89
  id,
@@ -90,8 +91,37 @@ function FormBodyItem({
90
91
  label,
91
92
  required,
92
93
  exampleValue,
94
+ fieldEncoding,
93
95
  }) {
94
96
  const dispatch = (0, hooks_1.useTypedDispatch)();
97
+ // Parse comma-separated encoding contentType into selectable options
98
+ const encodingOptions = fieldEncoding
99
+ ? fieldEncoding
100
+ .split(",")
101
+ .map((t) => t.trim())
102
+ .filter(Boolean)
103
+ : [];
104
+ const hasMultipleEncodings = encodingOptions.length > 1;
105
+ // Initialize with the first declared content type
106
+ const [selectedEncoding, setSelectedEncoding] = (0, react_1.useState)(
107
+ encodingOptions[0] ?? ""
108
+ );
109
+ // Seed Redux with the first declared encoding on mount so the code snippet
110
+ // reflects a content type immediately, even before the user interacts.
111
+ // The empty dep array is intentional: `fieldEncoding` comes from a static
112
+ // spec value that never changes for the lifetime of this component instance,
113
+ // and re-seeding on every render would fight user selections.
114
+ (0, react_1.useEffect)(() => {
115
+ if (encodingOptions[0]) {
116
+ dispatch(
117
+ (0, slice_2.setFieldEncoding)({
118
+ field: id,
119
+ contentType: encodingOptions[0],
120
+ })
121
+ );
122
+ }
123
+ // eslint-disable-next-line react-hooks/exhaustive-deps
124
+ }, []);
95
125
  const [value, setValue] = (0, react_1.useState)(() => {
96
126
  let initialValue = exampleValue ?? "";
97
127
  if (schemaObject.type === "object" && exampleValue) {
@@ -134,6 +164,23 @@ function FormBodyItem({
134
164
  label: label,
135
165
  required: required,
136
166
  }),
167
+ hasMultipleEncodings &&
168
+ react_1.default.createElement(
169
+ "div",
170
+ { style: { marginTop: "0.5rem" } },
171
+ react_1.default.createElement(FormSelect_1.default, {
172
+ label: "Content-Type",
173
+ options: encodingOptions,
174
+ value: selectedEncoding,
175
+ onChange: (e) => {
176
+ const ct = e.target.value;
177
+ setSelectedEncoding(ct);
178
+ dispatch(
179
+ (0, slice_2.setFieldEncoding)({ field: id, contentType: ct })
180
+ );
181
+ },
182
+ })
183
+ ),
137
184
  react_1.default.createElement(FormFileUpload_1.default, {
138
185
  placeholder: schemaObject.description || id,
139
186
  onChange: (file) => {
@@ -140,6 +140,7 @@ function Body({
140
140
  const rawSchema = requestBodyMetadata?.content?.[contentType]?.schema;
141
141
  const example = requestBodyMetadata?.content?.[contentType]?.example;
142
142
  const examples = requestBodyMetadata?.content?.[contentType]?.examples;
143
+ const encoding = requestBodyMetadata?.content?.[contentType]?.encoding;
143
144
  // Resolve the schema based on user's anyOf/oneOf tab selections
144
145
  const schema = (0, react_1.useMemo)(() => {
145
146
  if (!rawSchema) return rawSchema;
@@ -391,6 +392,7 @@ function Body({
391
392
  required:
392
393
  Array.isArray(schema.required) &&
393
394
  schema.required.includes(key),
395
+ fieldEncoding: encoding?.[key]?.contentType,
394
396
  })
395
397
  );
396
398
  })
@@ -414,6 +416,7 @@ function Body({
414
416
  required:
415
417
  Array.isArray(schema.required) &&
416
418
  schema.required.includes(schemaKey),
419
+ fieldEncoding: encoding?.[schemaKey]?.contentType,
417
420
  })
418
421
  );
419
422
  }
@@ -440,6 +443,7 @@ function Body({
440
443
  required:
441
444
  Array.isArray(schema.required) &&
442
445
  schema.required.includes(schemaKey),
446
+ fieldEncoding: encoding?.[schemaKey]?.contentType,
443
447
  })
444
448
  );
445
449
  }
@@ -6,6 +6,7 @@ export interface Props {
6
6
  postman: sdk.Request;
7
7
  codeSamples: CodeSample[];
8
8
  maskCredentials?: boolean;
9
+ requestBody?: import("docusaurus-plugin-openapi-docs/src/openapi/types").RequestBodyObject;
9
10
  }
10
- declare function CodeSnippets({ postman, codeSamples, maskCredentials: propMaskCredentials, }: Props): React.JSX.Element | null;
11
+ declare function CodeSnippets({ postman, codeSamples, maskCredentials: propMaskCredentials, requestBody, }: Props): React.JSX.Element | null;
11
12
  export default CodeSnippets;
@@ -79,6 +79,7 @@ const buildPostmanRequest_1 = __importDefault(
79
79
  require("@theme/ApiExplorer/buildPostmanRequest")
80
80
  );
81
81
  const CodeTabs_1 = __importDefault(require("@theme/ApiExplorer/CodeTabs"));
82
+ const useResolvedEncoding_1 = require("@theme/ApiExplorer/EncodingSelection/useResolvedEncoding");
82
83
  const hooks_1 = require("@theme/ApiItem/hooks");
83
84
  const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
84
85
  const postman_code_generators_1 = __importDefault(
@@ -97,6 +98,7 @@ function CodeSnippets({
97
98
  postman,
98
99
  codeSamples,
99
100
  maskCredentials: propMaskCredentials,
101
+ requestBody,
100
102
  }) {
101
103
  const { siteConfig } = (0, useDocusaurusContext_1.default)();
102
104
  const contentType = (0, hooks_1.useTypedSelector)(
@@ -147,6 +149,7 @@ function CodeSnippets({
147
149
  };
148
150
  })()
149
151
  : auth;
152
+ const encoding = (0, useResolvedEncoding_1.useResolvedEncoding)(requestBody);
150
153
  // Create a Postman request object using cleanedAuth or original auth
151
154
  const cleanedPostmanRequest = (0, buildPostmanRequest_1.default)(postman, {
152
155
  queryParams,
@@ -158,6 +161,7 @@ function CodeSnippets({
158
161
  body,
159
162
  server,
160
163
  auth: cleanedAuth,
164
+ encoding,
161
165
  });
162
166
  // User-defined languages array
163
167
  // Can override languageSet, change order of langs, override options and variants
@@ -16,6 +16,7 @@ const FormItem_1 = __importDefault(require("@theme/ApiExplorer/FormItem"));
16
16
  const FormSelect_1 = __importDefault(require("@theme/ApiExplorer/FormSelect"));
17
17
  const hooks_1 = require("@theme/ApiItem/hooks");
18
18
  const slice_1 = require("./slice");
19
+ const slice_2 = require("@theme/ApiExplorer/EncodingSelection/slice");
19
20
  function ContentType() {
20
21
  const value = (0, hooks_1.useTypedSelector)(
21
22
  (state) => state.contentType.value
@@ -34,7 +35,10 @@ function ContentType() {
34
35
  label: "Content-Type",
35
36
  value: value,
36
37
  options: options,
37
- onChange: (e) => dispatch((0, slice_1.setContentType)(e.target.value)),
38
+ onChange: (e) => {
39
+ dispatch((0, slice_1.setContentType)(e.target.value));
40
+ dispatch((0, slice_2.clearEncodingSelection)());
41
+ },
38
42
  })
39
43
  );
40
44
  }
@@ -0,0 +1,17 @@
1
+ import { PayloadAction } from "@reduxjs/toolkit";
2
+ export type State = Record<string, string>;
3
+ export declare const slice: import("@reduxjs/toolkit").Slice<State, {
4
+ setFieldEncoding: (state: {
5
+ [x: string]: string;
6
+ }, action: PayloadAction<{
7
+ field: string;
8
+ contentType: string;
9
+ }>) => void;
10
+ clearEncodingSelection: () => State;
11
+ }, "encodingSelection", "encodingSelection", import("@reduxjs/toolkit").SliceSelectors<State>>;
12
+ export declare const setFieldEncoding: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
13
+ field: string;
14
+ contentType: string;
15
+ }, "encodingSelection/setFieldEncoding">, clearEncodingSelection: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"encodingSelection/clearEncodingSelection">;
16
+ declare const _default: import("redux").Reducer<State>;
17
+ export default _default;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ /* ============================================================================
3
+ * Copyright (c) Palo Alto Networks
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ * ========================================================================== */
8
+ var _a;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.clearEncodingSelection =
11
+ exports.setFieldEncoding =
12
+ exports.slice =
13
+ void 0;
14
+ const toolkit_1 = require("@reduxjs/toolkit");
15
+ const initialState = {};
16
+ exports.slice = (0, toolkit_1.createSlice)({
17
+ name: "encodingSelection",
18
+ initialState,
19
+ reducers: {
20
+ setFieldEncoding: (state, action) => {
21
+ state[action.payload.field] = action.payload.contentType;
22
+ },
23
+ clearEncodingSelection: () => initialState,
24
+ },
25
+ });
26
+ ((_a = exports.slice.actions),
27
+ (exports.setFieldEncoding = _a.setFieldEncoding),
28
+ (exports.clearEncodingSelection = _a.clearEncodingSelection));
29
+ exports.default = exports.slice.reducer;
@@ -0,0 +1,12 @@
1
+ import type { RequestBodyObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
2
+ /**
3
+ * Merges the spec-declared `encoding` for the active content type with any
4
+ * per-field content-type selections the user has made in the UI. User picks
5
+ * take precedence over the spec default.
6
+ *
7
+ * Returns `undefined` when no encoding is declared for the current content
8
+ * type so callers can skip the encoding path entirely.
9
+ */
10
+ export declare function useResolvedEncoding(requestBody: RequestBodyObject | undefined): Record<string, {
11
+ contentType?: string;
12
+ }> | undefined;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /* ============================================================================
3
+ * Copyright (c) Palo Alto Networks
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ * ========================================================================== */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.useResolvedEncoding = useResolvedEncoding;
10
+ const hooks_1 = require("@theme/ApiItem/hooks");
11
+ /**
12
+ * Merges the spec-declared `encoding` for the active content type with any
13
+ * per-field content-type selections the user has made in the UI. User picks
14
+ * take precedence over the spec default.
15
+ *
16
+ * Returns `undefined` when no encoding is declared for the current content
17
+ * type so callers can skip the encoding path entirely.
18
+ */
19
+ function useResolvedEncoding(requestBody) {
20
+ const contentType = (0, hooks_1.useTypedSelector)(
21
+ (state) => state.contentType.value
22
+ );
23
+ const encodingSelection = (0, hooks_1.useTypedSelector)(
24
+ (state) => state.encodingSelection
25
+ );
26
+ const specEncoding = requestBody?.content?.[contentType]?.encoding ?? {};
27
+ if (!Object.keys(specEncoding).length) {
28
+ return undefined;
29
+ }
30
+ return Object.fromEntries(
31
+ Object.entries(specEncoding).map(([field, enc]) => [
32
+ field,
33
+ {
34
+ ...enc,
35
+ contentType: encodingSelection[field] ?? enc.contentType,
36
+ },
37
+ ])
38
+ );
39
+ }
@@ -90,6 +90,7 @@ const ParamOptions_1 = __importDefault(
90
90
  );
91
91
  const slice_1 = require("@theme/ApiExplorer/Response/slice");
92
92
  const Server_1 = __importDefault(require("@theme/ApiExplorer/Server"));
93
+ const useResolvedEncoding_1 = require("@theme/ApiExplorer/EncodingSelection/useResolvedEncoding");
93
94
  const hooks_1 = require("@theme/ApiItem/hooks");
94
95
  const translationIds_1 = require("@theme/translationIds");
95
96
  const sdk = __importStar(require("postman-collection"));
@@ -147,6 +148,9 @@ function Request({ item }) {
147
148
  ...cookieParams,
148
149
  ...headerParams,
149
150
  ];
151
+ const encoding = (0, useResolvedEncoding_1.useResolvedEncoding)(
152
+ item.requestBody
153
+ );
150
154
  const postmanRequest = (0, buildPostmanRequest_1.default)(postman, {
151
155
  queryParams,
152
156
  pathParams,
@@ -157,6 +161,7 @@ function Request({ item }) {
157
161
  body,
158
162
  server,
159
163
  auth,
164
+ encoding,
160
165
  });
161
166
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
162
167
  const paramsObject = {
@@ -236,7 +241,8 @@ function Request({ item }) {
236
241
  proxy,
237
242
  body,
238
243
  requestTimeout,
239
- requestCredentials
244
+ requestCredentials,
245
+ encoding
240
246
  );
241
247
  if (res.headers.get("content-type")?.includes("text/event-stream")) {
242
248
  await handleEventStream(res);
@@ -6,5 +6,7 @@ export declare class RequestError extends Error {
6
6
  originalError?: Error;
7
7
  constructor(type: RequestErrorType, message: string, originalError?: Error);
8
8
  }
9
- declare function makeRequest(request: sdk.Request, proxy: string | undefined, _body: Body, timeout?: number, credentials?: RequestCredentials): Promise<Response>;
9
+ declare function makeRequest(request: sdk.Request, proxy: string | undefined, _body: Body, timeout?: number, credentials?: RequestCredentials, encoding?: Record<string, {
10
+ contentType?: string;
11
+ }>): Promise<Response>;
10
12
  export default makeRequest;
@@ -90,7 +90,8 @@ async function makeRequest(
90
90
  proxy,
91
91
  _body,
92
92
  timeout = DEFAULT_REQUEST_TIMEOUT,
93
- credentials
93
+ credentials,
94
+ encoding
94
95
  ) {
95
96
  const headers = request.toJSON().header;
96
97
  let myHeaders = new Headers();
@@ -197,12 +198,25 @@ async function makeRequest(
197
198
  const members = request.body?.formdata?.members;
198
199
  if (Array.isArray(members)) {
199
200
  for (const data of members) {
201
+ const partContentType = encoding?.[data.key]?.contentType
202
+ ?.split(",")[0]
203
+ .trim();
200
204
  if (data.key && data.value.content) {
201
- myBody.append(data.key, data.value.content);
205
+ const blob = partContentType
206
+ ? new Blob([data.value.content], { type: partContentType })
207
+ : data.value.content;
208
+ myBody.append(data.key, blob);
202
209
  }
203
210
  // handle generic key-value payload
204
211
  if (data.key && typeof data.value === "string") {
205
- myBody.append(data.key, data.value);
212
+ if (partContentType) {
213
+ myBody.append(
214
+ data.key,
215
+ new Blob([data.value], { type: partContentType })
216
+ );
217
+ } else {
218
+ myBody.append(data.key, data.value);
219
+ }
206
220
  }
207
221
  }
208
222
  }
@@ -15,6 +15,9 @@ interface Options {
15
15
  accept: string;
16
16
  body: Body;
17
17
  auth: AuthState;
18
+ encoding?: Record<string, {
19
+ contentType?: string;
20
+ }>;
18
21
  }
19
- declare function buildPostmanRequest(postman: sdk.Request, { queryParams, pathParams, cookieParams, contentType, accept, headerParams, body, server, auth, }: Options): sdk.Request;
22
+ declare function buildPostmanRequest(postman: sdk.Request, { queryParams, pathParams, cookieParams, contentType, accept, headerParams, body, server, auth, encoding, }: Options): sdk.Request;
20
23
  export default buildPostmanRequest;
@@ -314,11 +314,30 @@ function tryDecodeJsonParam(value) {
314
314
  }
315
315
  }
316
316
  // TODO: this is all a bit hacky
317
- function setBody(clonedPostman, body) {
317
+ function setBody(clonedPostman, body, encoding) {
318
318
  if (clonedPostman.body === undefined) {
319
319
  return;
320
320
  }
321
321
  if (body.type === "empty") {
322
+ // When the original request has formdata and encoding is declared, keep the
323
+ // placeholder params so the code snippet reflects the selected encoding even
324
+ // before the user has uploaded a file.
325
+ if (
326
+ clonedPostman.body?.mode === "formdata" &&
327
+ encoding &&
328
+ Object.keys(encoding).length > 0
329
+ ) {
330
+ const members = clonedPostman.body.formdata?.members ?? [];
331
+ members.forEach((param) => {
332
+ const partContentType = encoding[param.key]?.contentType
333
+ ?.split(",")[0]
334
+ .trim();
335
+ if (partContentType) {
336
+ param.contentType = partContentType;
337
+ }
338
+ });
339
+ return;
340
+ }
322
341
  clonedPostman.body = undefined;
323
342
  return;
324
343
  }
@@ -354,14 +373,35 @@ function setBody(clonedPostman, body) {
354
373
  Object.entries(body.content)
355
374
  .filter((entry) => !!entry[1])
356
375
  .forEach(([key, content]) => {
376
+ const partContentType = encoding?.[key]?.contentType
377
+ ?.split(",")[0]
378
+ .trim();
357
379
  if (content.type === "file") {
358
- params.push(new sdk.FormParam({ key: key, ...content }));
380
+ params.push(
381
+ new sdk.FormParam({
382
+ key: key,
383
+ ...content,
384
+ ...(partContentType && { contentType: partContentType }),
385
+ })
386
+ );
359
387
  } else if (content.type === "file[]") {
360
388
  content.value.forEach((file) =>
361
- params.push(new sdk.FormParam({ key, value: file }))
389
+ params.push(
390
+ new sdk.FormParam({
391
+ key,
392
+ value: file,
393
+ ...(partContentType && { contentType: partContentType }),
394
+ })
395
+ )
362
396
  );
363
397
  } else {
364
- params.push(new sdk.FormParam({ key: key, value: content.value }));
398
+ params.push(
399
+ new sdk.FormParam({
400
+ key: key,
401
+ value: content.value,
402
+ ...(partContentType && { contentType: partContentType }),
403
+ })
404
+ );
365
405
  }
366
406
  });
367
407
  params.forEach((param) => {
@@ -409,6 +449,7 @@ function buildPostmanRequest(
409
449
  body,
410
450
  server,
411
451
  auth,
452
+ encoding,
412
453
  }
413
454
  ) {
414
455
  const clonedPostman = (0, cloneDeep_1.default)(postman);
@@ -521,7 +562,7 @@ function buildPostmanRequest(
521
562
  headerParams,
522
563
  otherHeaders
523
564
  );
524
- setBody(clonedPostman, body);
565
+ setBody(clonedPostman, body, encoding);
525
566
  return clonedPostman;
526
567
  }
527
568
  exports.default = buildPostmanRequest;
@@ -99,6 +99,7 @@ function ApiExplorer({ item, infoPath }) {
99
99
  postman: postman,
100
100
  codeSamples: item["x-codeSamples"] ?? [],
101
101
  maskCredentials: mask_credentials,
102
+ requestBody: item.requestBody,
102
103
  }),
103
104
  react_1.default.createElement(Request_1.default, { item: item }),
104
105
  react_1.default.createElement(Response_1.default, { item: item })
@@ -3,6 +3,7 @@ import type { ThemeConfig } from "docusaurus-theme-openapi-docs/src/types";
3
3
  export declare function createPersistenceMiddleware(options: ThemeConfig["api"]): Middleware<{}, {
4
4
  accept: import("./Accept/slice").State;
5
5
  contentType: import("./ContentType/slice").State;
6
+ encodingSelection: import("./EncodingSelection/slice").State;
6
7
  response: import("./Response/slice").State;
7
8
  server: import("./Server/slice").State;
8
9
  body: import("./Body/slice").FormBody | import("./Body/slice").RawBody | import("./Body/slice").EmptyBody;
@@ -12,6 +13,7 @@ export declare function createPersistenceMiddleware(options: ThemeConfig["api"])
12
13
  }, import("redux-thunk").ThunkDispatch<{
13
14
  accept: import("./Accept/slice").State;
14
15
  contentType: import("./ContentType/slice").State;
16
+ encodingSelection: import("./EncodingSelection/slice").State;
15
17
  response: import("./Response/slice").State;
16
18
  server: import("./Server/slice").State;
17
19
  body: import("./Body/slice").FormBody | import("./Body/slice").RawBody | import("./Body/slice").EmptyBody;
@@ -3,6 +3,7 @@ import type { RootState } from "./store";
3
3
  export declare const useTypedDispatch: () => import("redux-thunk").ThunkDispatch<{
4
4
  accept: import("../ApiExplorer/Accept/slice").State;
5
5
  contentType: import("../ApiExplorer/ContentType/slice").State;
6
+ encodingSelection: import("../ApiExplorer/EncodingSelection/slice").State;
6
7
  response: import("../ApiExplorer/Response/slice").State;
7
8
  server: import("../ApiExplorer/Server/slice").State;
8
9
  body: import("../ApiExplorer/Body/slice").FormBody | import("../ApiExplorer/Body/slice").RawBody | import("../ApiExplorer/Body/slice").EmptyBody;
@@ -134,6 +134,7 @@ function ApiItem(props) {
134
134
  params,
135
135
  auth,
136
136
  schemaSelection: { selections: {} },
137
+ encodingSelection: {},
137
138
  },
138
139
  [persistenceMiddleware]
139
140
  );
@@ -1,6 +1,7 @@
1
1
  declare const rootReducer: import("redux").Reducer<{
2
2
  accept: import("@theme/ApiExplorer/Accept/slice").State;
3
3
  contentType: import("@theme/ApiExplorer/ContentType/slice").State;
4
+ encodingSelection: import("@theme/ApiExplorer/EncodingSelection/slice").State;
4
5
  response: import("@theme/ApiExplorer/Response/slice").State;
5
6
  server: import("@theme/ApiExplorer/Server/slice").State;
6
7
  body: import("@theme/ApiExplorer/Body/slice").FormBody | import("@theme/ApiExplorer/Body/slice").RawBody | import("@theme/ApiExplorer/Body/slice").EmptyBody;
@@ -10,6 +11,7 @@ declare const rootReducer: import("redux").Reducer<{
10
11
  }, import("redux").UnknownAction, Partial<{
11
12
  accept: import("@theme/ApiExplorer/Accept/slice").State | undefined;
12
13
  contentType: import("@theme/ApiExplorer/ContentType/slice").State | undefined;
14
+ encodingSelection: import("@theme/ApiExplorer/EncodingSelection/slice").State | undefined;
13
15
  response: import("@theme/ApiExplorer/Response/slice").State | undefined;
14
16
  server: import("@theme/ApiExplorer/Server/slice").State | undefined;
15
17
  body: import("@theme/ApiExplorer/Body/slice").FormBody | import("@theme/ApiExplorer/Body/slice").RawBody | import("@theme/ApiExplorer/Body/slice").EmptyBody | undefined;
@@ -21,6 +23,7 @@ export type RootState = ReturnType<typeof rootReducer>;
21
23
  export declare const createStoreWithState: (preloadedState: RootState, middlewares: any[]) => import("@reduxjs/toolkit").EnhancedStore<{
22
24
  accept: import("@theme/ApiExplorer/Accept/slice").State;
23
25
  contentType: import("@theme/ApiExplorer/ContentType/slice").State;
26
+ encodingSelection: import("@theme/ApiExplorer/EncodingSelection/slice").State;
24
27
  response: import("@theme/ApiExplorer/Response/slice").State;
25
28
  server: import("@theme/ApiExplorer/Server/slice").State;
26
29
  body: import("@theme/ApiExplorer/Body/slice").FormBody | import("@theme/ApiExplorer/Body/slice").RawBody | import("@theme/ApiExplorer/Body/slice").EmptyBody;
@@ -31,6 +34,7 @@ export declare const createStoreWithState: (preloadedState: RootState, middlewar
31
34
  dispatch: import("redux-thunk").ThunkDispatch<{
32
35
  accept: import("@theme/ApiExplorer/Accept/slice").State;
33
36
  contentType: import("@theme/ApiExplorer/ContentType/slice").State;
37
+ encodingSelection: import("@theme/ApiExplorer/EncodingSelection/slice").State;
34
38
  response: import("@theme/ApiExplorer/Response/slice").State;
35
39
  server: import("@theme/ApiExplorer/Server/slice").State;
36
40
  body: import("@theme/ApiExplorer/Body/slice").FormBody | import("@theme/ApiExplorer/Body/slice").RawBody | import("@theme/ApiExplorer/Body/slice").EmptyBody;
@@ -42,6 +46,7 @@ export declare const createStoreWithState: (preloadedState: RootState, middlewar
42
46
  export declare const createStoreWithoutState: (preloadedState: {}, middlewares: any[]) => import("@reduxjs/toolkit").EnhancedStore<{
43
47
  accept: import("@theme/ApiExplorer/Accept/slice").State;
44
48
  contentType: import("@theme/ApiExplorer/ContentType/slice").State;
49
+ encodingSelection: import("@theme/ApiExplorer/EncodingSelection/slice").State;
45
50
  response: import("@theme/ApiExplorer/Response/slice").State;
46
51
  server: import("@theme/ApiExplorer/Server/slice").State;
47
52
  body: import("@theme/ApiExplorer/Body/slice").FormBody | import("@theme/ApiExplorer/Body/slice").RawBody | import("@theme/ApiExplorer/Body/slice").EmptyBody;
@@ -52,6 +57,7 @@ export declare const createStoreWithoutState: (preloadedState: {}, middlewares:
52
57
  dispatch: import("redux-thunk").ThunkDispatch<{
53
58
  accept: import("@theme/ApiExplorer/Accept/slice").State;
54
59
  contentType: import("@theme/ApiExplorer/ContentType/slice").State;
60
+ encodingSelection: import("@theme/ApiExplorer/EncodingSelection/slice").State;
55
61
  response: import("@theme/ApiExplorer/Response/slice").State;
56
62
  server: import("@theme/ApiExplorer/Server/slice").State;
57
63
  body: import("@theme/ApiExplorer/Body/slice").FormBody | import("@theme/ApiExplorer/Body/slice").RawBody | import("@theme/ApiExplorer/Body/slice").EmptyBody;
@@ -22,22 +22,26 @@ const slice_4 = __importDefault(
22
22
  require("@theme/ApiExplorer/ContentType/slice")
23
23
  );
24
24
  const slice_5 = __importDefault(
25
+ require("@theme/ApiExplorer/EncodingSelection/slice")
26
+ );
27
+ const slice_6 = __importDefault(
25
28
  require("@theme/ApiExplorer/ParamOptions/slice")
26
29
  );
27
- const slice_6 = __importDefault(require("@theme/ApiExplorer/Response/slice"));
28
- const slice_7 = __importDefault(
30
+ const slice_7 = __importDefault(require("@theme/ApiExplorer/Response/slice"));
31
+ const slice_8 = __importDefault(
29
32
  require("@theme/ApiExplorer/SchemaSelection/slice")
30
33
  );
31
- const slice_8 = __importDefault(require("@theme/ApiExplorer/Server/slice"));
34
+ const slice_9 = __importDefault(require("@theme/ApiExplorer/Server/slice"));
32
35
  const rootReducer = (0, toolkit_1.combineReducers)({
33
36
  accept: slice_1.default,
34
37
  contentType: slice_4.default,
35
- response: slice_6.default,
36
- server: slice_8.default,
38
+ encodingSelection: slice_5.default,
39
+ response: slice_7.default,
40
+ server: slice_9.default,
37
41
  body: slice_3.default,
38
- params: slice_5.default,
42
+ params: slice_6.default,
39
43
  auth: slice_2.default,
40
- schemaSelection: slice_7.default,
44
+ schemaSelection: slice_8.default,
41
45
  });
42
46
  const createStoreWithState = (preloadedState, middlewares) =>
43
47
  (0, toolkit_1.configureStore)({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "docusaurus-theme-openapi-docs",
3
3
  "description": "OpenAPI theme for Docusaurus.",
4
- "version": "0.0.0-1160",
4
+ "version": "0.0.0-1161",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "openapi",
@@ -38,7 +38,7 @@
38
38
  "@types/postman-collection": "^3.5.11",
39
39
  "@types/react-modal": "^3.16.3",
40
40
  "concurrently": "^9.2.0",
41
- "docusaurus-plugin-openapi-docs": "0.0.0-1160",
41
+ "docusaurus-plugin-openapi-docs": "0.0.0-1161",
42
42
  "docusaurus-plugin-sass": "^0.2.6",
43
43
  "eslint-plugin-prettier": "^5.5.1"
44
44
  },
@@ -82,5 +82,5 @@
82
82
  "engines": {
83
83
  "node": ">=14"
84
84
  },
85
- "gitHead": "9590f36ccddb212cb31943e14654c4a9a6884ec5"
85
+ "gitHead": "43f0aa571d28347f05806d74d5d1af1e23ecc0e0"
86
86
  }
@@ -17,6 +17,7 @@ import type { SchemaObject } from "docusaurus-plugin-openapi-docs/src/openapi/ty
17
17
 
18
18
  import FileArrayFormBodyItem from "../FileArrayFormBodyItem";
19
19
  import { clearFormBodyKey, setFileFormBody, setStringFormBody } from "../slice";
20
+ import { setFieldEncoding } from "../../EncodingSelection/slice";
20
21
 
21
22
  interface FormBodyItemProps {
22
23
  schemaObject: SchemaObject;
@@ -25,6 +26,7 @@ interface FormBodyItemProps {
25
26
  label?: string;
26
27
  required?: boolean;
27
28
  exampleValue?: SchemaObject["example"];
29
+ fieldEncoding?: string;
28
30
  }
29
31
 
30
32
  export default function FormBodyItem({
@@ -34,8 +36,37 @@ export default function FormBodyItem({
34
36
  label,
35
37
  required,
36
38
  exampleValue,
39
+ fieldEncoding,
37
40
  }: FormBodyItemProps): React.JSX.Element {
38
41
  const dispatch = useTypedDispatch();
42
+
43
+ // Parse comma-separated encoding contentType into selectable options
44
+ const encodingOptions = fieldEncoding
45
+ ? fieldEncoding
46
+ .split(",")
47
+ .map((t) => t.trim())
48
+ .filter(Boolean)
49
+ : [];
50
+ const hasMultipleEncodings = encodingOptions.length > 1;
51
+
52
+ // Initialize with the first declared content type
53
+ const [selectedEncoding, setSelectedEncoding] = useState<string>(
54
+ encodingOptions[0] ?? ""
55
+ );
56
+
57
+ // Seed Redux with the first declared encoding on mount so the code snippet
58
+ // reflects a content type immediately, even before the user interacts.
59
+ // The empty dep array is intentional: `fieldEncoding` comes from a static
60
+ // spec value that never changes for the lifetime of this component instance,
61
+ // and re-seeding on every render would fight user selections.
62
+ useEffect(() => {
63
+ if (encodingOptions[0]) {
64
+ dispatch(
65
+ setFieldEncoding({ field: id, contentType: encodingOptions[0] })
66
+ );
67
+ }
68
+ // eslint-disable-next-line react-hooks/exhaustive-deps
69
+ }, []);
39
70
  const [value, setValue] = useState(() => {
40
71
  let initialValue = exampleValue ?? "";
41
72
 
@@ -71,6 +102,20 @@ export default function FormBodyItem({
71
102
  return (
72
103
  <>
73
104
  {label && <FormLabel label={label} required={required} />}
105
+ {hasMultipleEncodings && (
106
+ <div style={{ marginTop: "0.5rem" }}>
107
+ <FormSelect
108
+ label="Content-Type"
109
+ options={encodingOptions}
110
+ value={selectedEncoding}
111
+ onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
112
+ const ct = e.target.value;
113
+ setSelectedEncoding(ct);
114
+ dispatch(setFieldEncoding({ field: id, contentType: ct }));
115
+ }}
116
+ />
117
+ </div>
118
+ )}
74
119
  <FormFileUpload
75
120
  placeholder={schemaObject.description || id}
76
121
  onChange={(file: any) => {
@@ -95,16 +140,16 @@ export default function FormBodyItem({
95
140
 
96
141
  if (schemaObject.type === "object") {
97
142
  return (
98
- <>
99
- {label && <FormLabel label={label} required={required} />}
100
- <LiveApp
101
- action={(code: string) =>
102
- dispatch(setStringFormBody({ key: id, value: code }))
103
- }
104
- >
105
- {value}
106
- </LiveApp>
107
- </>
143
+ <>
144
+ {label && <FormLabel label={label} required={required} />}
145
+ <LiveApp
146
+ action={(code: string) =>
147
+ dispatch(setStringFormBody({ key: id, value: code }))
148
+ }
149
+ >
150
+ {value}
151
+ </LiveApp>
152
+ </>
108
153
  );
109
154
  }
110
155
 
@@ -93,6 +93,8 @@ function Body({
93
93
  const rawSchema = requestBodyMetadata?.content?.[contentType]?.schema;
94
94
  const example = requestBodyMetadata?.content?.[contentType]?.example;
95
95
  const examples = requestBodyMetadata?.content?.[contentType]?.examples;
96
+ const encoding: Record<string, { contentType?: string }> | undefined =
97
+ requestBodyMetadata?.content?.[contentType]?.encoding;
96
98
 
97
99
  // Resolve the schema based on user's anyOf/oneOf tab selections
98
100
  const schema = useMemo(() => {
@@ -339,6 +341,7 @@ function Body({
339
341
  Array.isArray(schema.required) &&
340
342
  schema.required.includes(key)
341
343
  }
344
+ fieldEncoding={encoding?.[key]?.contentType}
342
345
  ></FormBodyItem>
343
346
  </FormItem>
344
347
  );
@@ -361,6 +364,7 @@ function Body({
361
364
  Array.isArray(schema.required) &&
362
365
  schema.required.includes(schemaKey)
363
366
  }
367
+ fieldEncoding={encoding?.[schemaKey]?.contentType}
364
368
  ></FormBodyItem>
365
369
  </FormItem>
366
370
  );
@@ -387,6 +391,7 @@ function Body({
387
391
  Array.isArray(schema.required) &&
388
392
  schema.required.includes(schemaKey)
389
393
  }
394
+ fieldEncoding={encoding?.[schemaKey]?.contentType}
390
395
  ></FormBodyItem>
391
396
  </FormItem>
392
397
  );
@@ -11,6 +11,7 @@ import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
11
11
  import ApiCodeBlock from "@theme/ApiExplorer/ApiCodeBlock";
12
12
  import buildPostmanRequest from "@theme/ApiExplorer/buildPostmanRequest";
13
13
  import CodeTabs from "@theme/ApiExplorer/CodeTabs";
14
+ import { useResolvedEncoding } from "@theme/ApiExplorer/EncodingSelection/useResolvedEncoding";
14
15
  import { useTypedSelector } from "@theme/ApiItem/hooks";
15
16
  import cloneDeep from "lodash/cloneDeep";
16
17
  import codegen from "postman-code-generators";
@@ -30,6 +31,7 @@ export interface Props {
30
31
  postman: sdk.Request;
31
32
  codeSamples: CodeSample[];
32
33
  maskCredentials?: boolean;
34
+ requestBody?: import("docusaurus-plugin-openapi-docs/src/openapi/types").RequestBodyObject;
33
35
  }
34
36
 
35
37
  function CodeTab({ children, hidden, className }: any): React.JSX.Element {
@@ -44,6 +46,7 @@ function CodeSnippets({
44
46
  postman,
45
47
  codeSamples,
46
48
  maskCredentials: propMaskCredentials,
49
+ requestBody,
47
50
  }: Props) {
48
51
  const { siteConfig } = useDocusaurusContext();
49
52
 
@@ -76,7 +79,9 @@ function CodeSnippets({
76
79
  const authOptions =
77
80
  clonedAuth?.options?.[key] ??
78
81
  clonedAuth?.options?.[comboAuthId];
79
- placeholder = authOptions?.find((opt: any) => opt.key === key)?.name;
82
+ placeholder = authOptions?.find(
83
+ (opt: any) => opt.key === key
84
+ )?.name;
80
85
  obj[key] = cleanCredentials(obj[key]);
81
86
  } else {
82
87
  obj[key] = `<${placeholder ?? key}>`;
@@ -93,6 +98,8 @@ function CodeSnippets({
93
98
  })()
94
99
  : auth;
95
100
 
101
+ const encoding = useResolvedEncoding(requestBody);
102
+
96
103
  // Create a Postman request object using cleanedAuth or original auth
97
104
  const cleanedPostmanRequest = buildPostmanRequest(postman, {
98
105
  queryParams,
@@ -104,6 +111,7 @@ function CodeSnippets({
104
111
  body,
105
112
  server,
106
113
  auth: cleanedAuth,
114
+ encoding,
107
115
  });
108
116
 
109
117
  // User-defined languages array
@@ -12,6 +12,7 @@ import FormSelect from "@theme/ApiExplorer/FormSelect";
12
12
  import { useTypedDispatch, useTypedSelector } from "@theme/ApiItem/hooks";
13
13
 
14
14
  import { setContentType } from "./slice";
15
+ import { clearEncodingSelection } from "@theme/ApiExplorer/EncodingSelection/slice";
15
16
 
16
17
  function ContentType() {
17
18
  const value = useTypedSelector((state: any) => state.contentType.value);
@@ -28,9 +29,10 @@ function ContentType() {
28
29
  label="Content-Type"
29
30
  value={value}
30
31
  options={options}
31
- onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
32
- dispatch(setContentType(e.target.value))
33
- }
32
+ onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
33
+ dispatch(setContentType(e.target.value));
34
+ dispatch(clearEncodingSelection());
35
+ }}
34
36
  />
35
37
  </FormItem>
36
38
  );
@@ -0,0 +1,31 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
9
+
10
+ // Maps form field name → user-selected content type
11
+ export type State = Record<string, string>;
12
+
13
+ const initialState: State = {};
14
+
15
+ export const slice = createSlice({
16
+ name: "encodingSelection",
17
+ initialState,
18
+ reducers: {
19
+ setFieldEncoding: (
20
+ state,
21
+ action: PayloadAction<{ field: string; contentType: string }>
22
+ ) => {
23
+ state[action.payload.field] = action.payload.contentType;
24
+ },
25
+ clearEncodingSelection: () => initialState,
26
+ },
27
+ });
28
+
29
+ export const { setFieldEncoding, clearEncodingSelection } = slice.actions;
30
+
31
+ export default slice.reducer;
@@ -0,0 +1,43 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ import { useTypedSelector } from "@theme/ApiItem/hooks";
9
+ import type { RequestBodyObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
10
+
11
+ /**
12
+ * Merges the spec-declared `encoding` for the active content type with any
13
+ * per-field content-type selections the user has made in the UI. User picks
14
+ * take precedence over the spec default.
15
+ *
16
+ * Returns `undefined` when no encoding is declared for the current content
17
+ * type so callers can skip the encoding path entirely.
18
+ */
19
+ export function useResolvedEncoding(
20
+ requestBody: RequestBodyObject | undefined
21
+ ): Record<string, { contentType?: string }> | undefined {
22
+ const contentType = useTypedSelector((state: any) => state.contentType.value);
23
+ const encodingSelection = useTypedSelector(
24
+ (state: any) => state.encodingSelection
25
+ );
26
+
27
+ const specEncoding: Record<string, { contentType?: string }> =
28
+ requestBody?.content?.[contentType]?.encoding ?? {};
29
+
30
+ if (!Object.keys(specEncoding).length) {
31
+ return undefined;
32
+ }
33
+
34
+ return Object.fromEntries(
35
+ Object.entries(specEncoding).map(([field, enc]) => [
36
+ field,
37
+ {
38
+ ...enc,
39
+ contentType: encodingSelection[field] ?? enc.contentType,
40
+ },
41
+ ])
42
+ );
43
+ }
@@ -25,6 +25,7 @@ import {
25
25
  clearHeaders,
26
26
  } from "@theme/ApiExplorer/Response/slice";
27
27
  import Server from "@theme/ApiExplorer/Server";
28
+ import { useResolvedEncoding } from "@theme/ApiExplorer/EncodingSelection/useResolvedEncoding";
28
29
  import { useTypedDispatch, useTypedSelector } from "@theme/ApiItem/hooks";
29
30
  import { OPENAPI_REQUEST } from "@theme/translationIds";
30
31
  import type { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
@@ -76,6 +77,8 @@ function Request({ item }: { item: ApiItem }) {
76
77
  ...headerParams,
77
78
  ];
78
79
 
80
+ const encoding = useResolvedEncoding(item.requestBody);
81
+
79
82
  const postmanRequest = buildPostmanRequest(postman, {
80
83
  queryParams,
81
84
  pathParams,
@@ -86,6 +89,7 @@ function Request({ item }: { item: ApiItem }) {
86
89
  body,
87
90
  server,
88
91
  auth,
92
+ encoding,
89
93
  });
90
94
 
91
95
  const delay = (ms: number) =>
@@ -175,7 +179,8 @@ function Request({ item }: { item: ApiItem }) {
175
179
  proxy,
176
180
  body,
177
181
  requestTimeout,
178
- requestCredentials
182
+ requestCredentials,
183
+ encoding
179
184
  );
180
185
  if (res.headers.get("content-type")?.includes("text/event-stream")) {
181
186
  await handleEventStream(res);
@@ -115,7 +115,8 @@ async function makeRequest(
115
115
  proxy: string | undefined,
116
116
  _body: Body,
117
117
  timeout: number = DEFAULT_REQUEST_TIMEOUT,
118
- credentials?: RequestCredentials
118
+ credentials?: RequestCredentials,
119
+ encoding?: Record<string, { contentType?: string }>
119
120
  ) {
120
121
  const headers = request.toJSON().header;
121
122
 
@@ -227,12 +228,25 @@ async function makeRequest(
227
228
  const members = (request.body as any)?.formdata?.members;
228
229
  if (Array.isArray(members)) {
229
230
  for (const data of members) {
231
+ const partContentType = encoding?.[data.key]?.contentType
232
+ ?.split(",")[0]
233
+ .trim();
230
234
  if (data.key && data.value.content) {
231
- myBody.append(data.key, data.value.content);
235
+ const blob = partContentType
236
+ ? new Blob([data.value.content], { type: partContentType })
237
+ : data.value.content;
238
+ myBody.append(data.key, blob);
232
239
  }
233
240
  // handle generic key-value payload
234
241
  if (data.key && typeof data.value === "string") {
235
- myBody.append(data.key, data.value);
242
+ if (partContentType) {
243
+ myBody.append(
244
+ data.key,
245
+ new Blob([data.value], { type: partContentType })
246
+ );
247
+ } else {
248
+ myBody.append(data.key, data.value);
249
+ }
236
250
  }
237
251
  }
238
252
  }
@@ -293,12 +293,36 @@ function tryDecodeJsonParam(value: string): any {
293
293
  }
294
294
 
295
295
  // TODO: this is all a bit hacky
296
- function setBody(clonedPostman: sdk.Request, body: Body) {
296
+ function setBody(
297
+ clonedPostman: sdk.Request,
298
+ body: Body,
299
+ encoding?: Record<string, { contentType?: string }>
300
+ ) {
297
301
  if (clonedPostman.body === undefined) {
298
302
  return;
299
303
  }
300
304
 
301
305
  if (body.type === "empty") {
306
+ // When the original request has formdata and encoding is declared, keep the
307
+ // placeholder params so the code snippet reflects the selected encoding even
308
+ // before the user has uploaded a file.
309
+ if (
310
+ clonedPostman.body?.mode === "formdata" &&
311
+ encoding &&
312
+ Object.keys(encoding).length > 0
313
+ ) {
314
+ const members: any[] =
315
+ (clonedPostman.body.formdata as any)?.members ?? [];
316
+ members.forEach((param: any) => {
317
+ const partContentType = encoding[param.key]?.contentType
318
+ ?.split(",")[0]
319
+ .trim();
320
+ if (partContentType) {
321
+ param.contentType = partContentType;
322
+ }
323
+ });
324
+ return;
325
+ }
302
326
  clonedPostman.body = undefined;
303
327
  return;
304
328
  }
@@ -336,14 +360,35 @@ function setBody(clonedPostman: sdk.Request, body: Body) {
336
360
  Object.entries(body.content)
337
361
  .filter((entry): entry is [string, NonNullable<Content>] => !!entry[1])
338
362
  .forEach(([key, content]) => {
363
+ const partContentType = encoding?.[key]?.contentType
364
+ ?.split(",")[0]
365
+ .trim();
339
366
  if (content.type === "file") {
340
- params.push(new sdk.FormParam({ key: key, ...content }));
367
+ params.push(
368
+ new sdk.FormParam({
369
+ key: key,
370
+ ...content,
371
+ ...(partContentType && { contentType: partContentType }),
372
+ })
373
+ );
341
374
  } else if (content.type === "file[]") {
342
375
  content.value.forEach((file) =>
343
- params.push(new sdk.FormParam({ key, value: file }))
376
+ params.push(
377
+ new sdk.FormParam({
378
+ key,
379
+ value: file,
380
+ ...(partContentType && { contentType: partContentType }),
381
+ })
382
+ )
344
383
  );
345
384
  } else {
346
- params.push(new sdk.FormParam({ key: key, value: content.value }));
385
+ params.push(
386
+ new sdk.FormParam({
387
+ key: key,
388
+ value: content.value,
389
+ ...(partContentType && { contentType: partContentType }),
390
+ })
391
+ );
347
392
  }
348
393
  });
349
394
  params.forEach((param) => {
@@ -391,6 +436,7 @@ interface Options {
391
436
  accept: string;
392
437
  body: Body;
393
438
  auth: AuthState;
439
+ encoding?: Record<string, { contentType?: string }>;
394
440
  }
395
441
 
396
442
  function buildPostmanRequest(
@@ -405,6 +451,7 @@ function buildPostmanRequest(
405
451
  body,
406
452
  server,
407
453
  auth,
454
+ encoding,
408
455
  }: Options
409
456
  ) {
410
457
  const clonedPostman = cloneDeep(postman);
@@ -532,7 +579,7 @@ function buildPostmanRequest(
532
579
  otherHeaders
533
580
  );
534
581
 
535
- setBody(clonedPostman, body);
582
+ setBody(clonedPostman, body, encoding);
536
583
 
537
584
  return clonedPostman;
538
585
  }
@@ -41,6 +41,7 @@ function ApiExplorer({
41
41
  postman={postman}
42
42
  codeSamples={(item as any)["x-codeSamples"] ?? []}
43
43
  maskCredentials={mask_credentials}
44
+ requestBody={item.requestBody}
44
45
  />
45
46
  )}
46
47
  <Request item={item} />
@@ -159,6 +159,7 @@ export default function ApiItem(props: Props): JSX.Element {
159
159
  params,
160
160
  auth,
161
161
  schemaSelection: { selections: {} },
162
+ encodingSelection: {},
162
163
  },
163
164
  [persistenceMiddleware]
164
165
  );
@@ -10,6 +10,7 @@ import accept from "@theme/ApiExplorer/Accept/slice";
10
10
  import auth from "@theme/ApiExplorer/Authorization/slice";
11
11
  import body from "@theme/ApiExplorer/Body/slice";
12
12
  import contentType from "@theme/ApiExplorer/ContentType/slice";
13
+ import encodingSelection from "@theme/ApiExplorer/EncodingSelection/slice";
13
14
  import params from "@theme/ApiExplorer/ParamOptions/slice";
14
15
  import response from "@theme/ApiExplorer/Response/slice";
15
16
  import schemaSelection from "@theme/ApiExplorer/SchemaSelection/slice";
@@ -18,6 +19,7 @@ import server from "@theme/ApiExplorer/Server/slice";
18
19
  const rootReducer = combineReducers({
19
20
  accept,
20
21
  contentType,
22
+ encodingSelection,
21
23
  response,
22
24
  server,
23
25
  body,
@@ -1 +1 @@
1
- {"root":["./src/index.ts","./src/plugin-content-docs.d.ts","./src/postman-code-generators.d.ts","./src/react-magic-dropzone.d.ts","./src/theme-classic.d.ts","./src/theme-openapi.d.ts","./src/types.d.ts","./src/markdown/createDescription.ts","./src/markdown/schema.ts","./src/markdown/utils.test.ts","./src/markdown/utils.ts","./src/theme/translationIds.ts","./src/theme/ApiExplorer/buildPostmanRequest.ts","./src/theme/ApiExplorer/index.tsx","./src/theme/ApiExplorer/persistenceMiddleware.ts","./src/theme/ApiExplorer/storage-utils.ts","./src/theme/ApiExplorer/Accept/index.tsx","./src/theme/ApiExplorer/Accept/slice.ts","./src/theme/ApiExplorer/ApiCodeBlock/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/Container/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/Content/Element.tsx","./src/theme/ApiExplorer/ApiCodeBlock/Content/String.tsx","./src/theme/ApiExplorer/ApiCodeBlock/CopyButton/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/ExitButton/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/ExpandButton/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/Line/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/WordWrapButton/index.tsx","./src/theme/ApiExplorer/Authorization/auth-types.ts","./src/theme/ApiExplorer/Authorization/index.tsx","./src/theme/ApiExplorer/Authorization/slice.ts","./src/theme/ApiExplorer/Body/index.tsx","./src/theme/ApiExplorer/Body/json2xml.d.ts","./src/theme/ApiExplorer/Body/resolveSchemaWithSelections.ts","./src/theme/ApiExplorer/Body/slice.ts","./src/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.tsx","./src/theme/ApiExplorer/Body/FormBodyItem/index.tsx","./src/theme/ApiExplorer/CodeSnippets/code-snippets-types.ts","./src/theme/ApiExplorer/CodeSnippets/index.tsx","./src/theme/ApiExplorer/CodeSnippets/languages.ts","./src/theme/ApiExplorer/CodeTabs/index.tsx","./src/theme/ApiExplorer/ContentType/index.tsx","./src/theme/ApiExplorer/ContentType/slice.ts","./src/theme/ApiExplorer/Export/index.tsx","./src/theme/ApiExplorer/FloatingButton/index.tsx","./src/theme/ApiExplorer/FormFileUpload/index.tsx","./src/theme/ApiExplorer/FormItem/index.tsx","./src/theme/ApiExplorer/FormLabel/index.tsx","./src/theme/ApiExplorer/FormMultiSelect/index.tsx","./src/theme/ApiExplorer/FormSelect/index.tsx","./src/theme/ApiExplorer/FormTextInput/index.tsx","./src/theme/ApiExplorer/LiveEditor/index.tsx","./src/theme/ApiExplorer/MethodEndpoint/index.tsx","./src/theme/ApiExplorer/ParamOptions/index.tsx","./src/theme/ApiExplorer/ParamOptions/slice.ts","./src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.tsx","./src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.tsx","./src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.tsx","./src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.tsx","./src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.tsx","./src/theme/ApiExplorer/Request/index.tsx","./src/theme/ApiExplorer/Request/makeRequest.ts","./src/theme/ApiExplorer/Response/index.tsx","./src/theme/ApiExplorer/Response/slice.ts","./src/theme/ApiExplorer/SchemaSelection/index.ts","./src/theme/ApiExplorer/SchemaSelection/slice.ts","./src/theme/ApiExplorer/SecuritySchemes/index.tsx","./src/theme/ApiExplorer/Server/index.tsx","./src/theme/ApiExplorer/Server/slice.ts","./src/theme/ApiItem/hooks.ts","./src/theme/ApiItem/index.tsx","./src/theme/ApiItem/store.ts","./src/theme/ApiItem/Layout/index.tsx","./src/theme/ApiLogo/index.tsx","./src/theme/ApiTabs/index.tsx","./src/theme/ArrayBrackets/index.tsx","./src/theme/CodeSamples/index.tsx","./src/theme/DiscriminatorTabs/index.tsx","./src/theme/Example/index.tsx","./src/theme/Markdown/index.d.ts","./src/theme/MimeTabs/index.tsx","./src/theme/OperationTabs/index.tsx","./src/theme/ParamsDetails/index.tsx","./src/theme/ParamsItem/index.tsx","./src/theme/RequestSchema/index.tsx","./src/theme/ResponseExamples/index.tsx","./src/theme/ResponseHeaders/index.tsx","./src/theme/ResponseSchema/index.tsx","./src/theme/Schema/index.tsx","./src/theme/SchemaItem/index.tsx","./src/theme/SchemaTabs/index.tsx","./src/theme/SkeletonLoader/index.tsx","./src/theme/StatusCodes/index.tsx"],"version":"5.9.3"}
1
+ {"root":["./src/index.ts","./src/plugin-content-docs.d.ts","./src/postman-code-generators.d.ts","./src/react-magic-dropzone.d.ts","./src/theme-classic.d.ts","./src/theme-openapi.d.ts","./src/types.d.ts","./src/markdown/createDescription.ts","./src/markdown/schema.ts","./src/markdown/utils.test.ts","./src/markdown/utils.ts","./src/theme/translationIds.ts","./src/theme/ApiExplorer/buildPostmanRequest.ts","./src/theme/ApiExplorer/index.tsx","./src/theme/ApiExplorer/persistenceMiddleware.ts","./src/theme/ApiExplorer/storage-utils.ts","./src/theme/ApiExplorer/Accept/index.tsx","./src/theme/ApiExplorer/Accept/slice.ts","./src/theme/ApiExplorer/ApiCodeBlock/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/Container/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/Content/Element.tsx","./src/theme/ApiExplorer/ApiCodeBlock/Content/String.tsx","./src/theme/ApiExplorer/ApiCodeBlock/CopyButton/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/ExitButton/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/ExpandButton/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/Line/index.tsx","./src/theme/ApiExplorer/ApiCodeBlock/WordWrapButton/index.tsx","./src/theme/ApiExplorer/Authorization/auth-types.ts","./src/theme/ApiExplorer/Authorization/index.tsx","./src/theme/ApiExplorer/Authorization/slice.ts","./src/theme/ApiExplorer/Body/index.tsx","./src/theme/ApiExplorer/Body/json2xml.d.ts","./src/theme/ApiExplorer/Body/resolveSchemaWithSelections.ts","./src/theme/ApiExplorer/Body/slice.ts","./src/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.tsx","./src/theme/ApiExplorer/Body/FormBodyItem/index.tsx","./src/theme/ApiExplorer/CodeSnippets/code-snippets-types.ts","./src/theme/ApiExplorer/CodeSnippets/index.tsx","./src/theme/ApiExplorer/CodeSnippets/languages.ts","./src/theme/ApiExplorer/CodeTabs/index.tsx","./src/theme/ApiExplorer/ContentType/index.tsx","./src/theme/ApiExplorer/ContentType/slice.ts","./src/theme/ApiExplorer/EncodingSelection/slice.ts","./src/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.ts","./src/theme/ApiExplorer/Export/index.tsx","./src/theme/ApiExplorer/FloatingButton/index.tsx","./src/theme/ApiExplorer/FormFileUpload/index.tsx","./src/theme/ApiExplorer/FormItem/index.tsx","./src/theme/ApiExplorer/FormLabel/index.tsx","./src/theme/ApiExplorer/FormMultiSelect/index.tsx","./src/theme/ApiExplorer/FormSelect/index.tsx","./src/theme/ApiExplorer/FormTextInput/index.tsx","./src/theme/ApiExplorer/LiveEditor/index.tsx","./src/theme/ApiExplorer/MethodEndpoint/index.tsx","./src/theme/ApiExplorer/ParamOptions/index.tsx","./src/theme/ApiExplorer/ParamOptions/slice.ts","./src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.tsx","./src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.tsx","./src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.tsx","./src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.tsx","./src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.tsx","./src/theme/ApiExplorer/Request/index.tsx","./src/theme/ApiExplorer/Request/makeRequest.ts","./src/theme/ApiExplorer/Response/index.tsx","./src/theme/ApiExplorer/Response/slice.ts","./src/theme/ApiExplorer/SchemaSelection/index.ts","./src/theme/ApiExplorer/SchemaSelection/slice.ts","./src/theme/ApiExplorer/SecuritySchemes/index.tsx","./src/theme/ApiExplorer/Server/index.tsx","./src/theme/ApiExplorer/Server/slice.ts","./src/theme/ApiItem/hooks.ts","./src/theme/ApiItem/index.tsx","./src/theme/ApiItem/store.ts","./src/theme/ApiItem/Layout/index.tsx","./src/theme/ApiLogo/index.tsx","./src/theme/ApiTabs/index.tsx","./src/theme/ArrayBrackets/index.tsx","./src/theme/CodeSamples/index.tsx","./src/theme/DiscriminatorTabs/index.tsx","./src/theme/Example/index.tsx","./src/theme/Markdown/index.d.ts","./src/theme/MimeTabs/index.tsx","./src/theme/OperationTabs/index.tsx","./src/theme/ParamsDetails/index.tsx","./src/theme/ParamsItem/index.tsx","./src/theme/RequestSchema/index.tsx","./src/theme/ResponseExamples/index.tsx","./src/theme/ResponseHeaders/index.tsx","./src/theme/ResponseSchema/index.tsx","./src/theme/Schema/index.tsx","./src/theme/SchemaItem/index.tsx","./src/theme/SchemaTabs/index.tsx","./src/theme/SkeletonLoader/index.tsx","./src/theme/StatusCodes/index.tsx"],"version":"5.9.3"}