fork of hey-api/openapi-ts because I need some additional things
1import type { Context } from '../../../ir/context';
2import type { IR } from '../../../ir/types';
3import { refToName } from '../../../utils/ref';
4import type { ParameterObject, ReferenceObject, SchemaObject } from '../types/spec';
5import { mediaTypeObjects } from './mediaType';
6import { paginationField } from './pagination';
7import { parseExtensions, schemaToIrSchema } from './schema';
8
9/**
10 * Returns default parameter `allowReserved` based on value of `in`.
11 */
12const defaultAllowReserved = (_in: ParameterObject['in']): boolean | undefined => {
13 switch (_in) {
14 // this keyword only applies to parameters with an `in` value of `query`
15 case 'query':
16 return false;
17 default:
18 return;
19 }
20};
21
22/**
23 * Returns default parameter `explode` based on value of `style`.
24 */
25const defaultExplode = (style: Required<ParameterObject>['style']): boolean => {
26 switch (style) {
27 // default value for `deepObject` is `false`, but that behavior is undefined
28 // so we use `true` to make this work with the `client-fetch` package
29 case 'deepObject':
30 case 'form':
31 return true;
32 default:
33 return false;
34 }
35};
36
37/**
38 * Returns default parameter `style` based on value of `in`.
39 */
40const defaultStyle = (_in: ParameterObject['in']): Required<ParameterObject>['style'] => {
41 switch (_in) {
42 case 'header':
43 case 'path':
44 return 'simple';
45 case 'cookie':
46 case 'query':
47 return 'form';
48 }
49};
50
51export const parametersArrayToObject = ({
52 context,
53 parameters,
54}: {
55 context: Context;
56 parameters?: ReadonlyArray<ParameterObject | ReferenceObject>;
57}): IR.ParametersObject | undefined => {
58 if (!parameters || !Object.keys(parameters).length) {
59 return;
60 }
61
62 const parametersObject: IR.ParametersObject = {};
63
64 for (const parameterOrReference of parameters) {
65 const parameter =
66 '$ref' in parameterOrReference
67 ? context.dereference<ParameterObject>(parameterOrReference)
68 : parameterOrReference;
69
70 if (!parametersObject[parameter.in]) {
71 parametersObject[parameter.in] = {};
72 }
73
74 // lowercase keys for case insensitive access
75 parametersObject[parameter.in]![parameter.name.toLocaleLowerCase()] = parameterToIrParameter({
76 $ref: `#/todo/real/path/to/parameter/${parameter.name}`,
77 context,
78 parameter,
79 });
80 }
81
82 return parametersObject;
83};
84
85const parameterToIrParameter = ({
86 $ref,
87 context,
88 parameter,
89}: {
90 $ref: string;
91 context: Context;
92 parameter: ParameterObject;
93}): IR.ParameterObject => {
94 // TODO: parser - fix
95 let schema = parameter.schema;
96
97 if (!schema) {
98 const contents = mediaTypeObjects({ content: parameter.content });
99 // TODO: add support for multiple content types, for now prefer JSON
100 const content = contents.find((content) => content.type === 'json') || contents[0];
101 if (content) {
102 schema = content.schema;
103 }
104 }
105
106 const finalSchema: SchemaObject = {
107 deprecated: parameter.deprecated,
108 description: parameter.description,
109 ...schema,
110 };
111
112 const pagination = paginationField({
113 context,
114 name: parameter.name,
115 schema: finalSchema,
116 });
117
118 const style = parameter.style || defaultStyle(parameter.in);
119 const explode = parameter.explode !== undefined ? parameter.explode : defaultExplode(style);
120 const allowReserved =
121 parameter.allowReserved !== undefined
122 ? parameter.allowReserved
123 : defaultAllowReserved(parameter.in);
124
125 const irParameter: IR.ParameterObject = {
126 allowReserved,
127 explode,
128 location: parameter.in,
129 name: parameter.name,
130 schema: schemaToIrSchema({
131 context,
132 schema: finalSchema,
133 state: {
134 $ref,
135 circularReferenceTracker: new Set(),
136 },
137 }),
138 style,
139 };
140
141 if (parameter.deprecated) {
142 irParameter.deprecated = parameter.deprecated;
143 }
144
145 if (parameter.description) {
146 irParameter.description = parameter.description;
147 }
148
149 if (pagination) {
150 irParameter.pagination = pagination;
151 }
152
153 if (parameter.required) {
154 irParameter.required = parameter.required;
155 }
156
157 parseExtensions({
158 source: parameter,
159 target: irParameter,
160 });
161
162 return irParameter;
163};
164
165export const parseParameter = ({
166 $ref,
167 context,
168 parameter,
169}: {
170 $ref: string;
171 context: Context;
172 parameter: ParameterObject;
173}) => {
174 if (!context.ir.components) {
175 context.ir.components = {};
176 }
177
178 if (!context.ir.components.parameters) {
179 context.ir.components.parameters = {};
180 }
181
182 context.ir.components.parameters[refToName($ref)] = parameterToIrParameter({
183 $ref,
184 context,
185 parameter,
186 });
187};