@@ -2,9 +2,9 @@ import type { OpenAPIV3 } from '@gitbook/openapi-parser';
2
2
import { generateSchemaExample } from './generateSchemaExample' ;
3
3
import type { OpenAPIContextProps , OpenAPIOperationData } from './types' ;
4
4
import { checkIsReference , createStateKey , resolveDescription } from './utils' ;
5
- import { stringifyOpenAPI } from './stringifyOpenAPI' ;
6
5
import { OpenAPITabs , OpenAPITabsList , OpenAPITabsPanels } from './OpenAPITabs' ;
7
6
import { InteractiveSection } from './InteractiveSection' ;
7
+ import { json2xml } from './json2xml' ;
8
8
9
9
/**
10
10
* Display an example of the response content.
@@ -38,91 +38,264 @@ export function OpenAPIResponseExample(props: {
38
38
return Number ( a ) - Number ( b ) ;
39
39
} ) ;
40
40
41
- const examples = responses
42
- . map ( ( [ key , value ] ) => {
43
- const responseObject = value ;
44
- const mediaTypeObject = ( ( ) => {
45
- if ( ! responseObject . content ) {
46
- return null ;
47
- }
48
- const key = Object . keys ( responseObject . content ) [ 0 ] ;
49
- return (
50
- responseObject . content [ 'application/json' ] ??
51
- ( key ? responseObject . content [ key ] : null )
52
- ) ;
53
- } ) ( ) ;
54
-
55
- if ( ! mediaTypeObject ) {
41
+ const tabs = responses
42
+ . map ( ( [ key , responseObject ] ) => {
43
+ const description = resolveDescription ( responseObject ) ;
44
+
45
+ if ( checkIsReference ( responseObject ) ) {
56
46
return {
57
47
key : key ,
58
48
label : key ,
59
- description : resolveDescription ( responseObject ) ,
60
- body : < OpenAPIEmptyResponseExample /> ,
49
+ description,
50
+ body : (
51
+ < OpenAPIExample
52
+ example = { getExampleFromReference ( responseObject ) }
53
+ context = { context }
54
+ syntax = "json"
55
+ />
56
+ ) ,
61
57
} ;
62
58
}
63
59
64
- const example = handleUnresolvedReference (
65
- ( ( ) => {
66
- const { examples, example } = mediaTypeObject ;
67
- if ( examples ) {
68
- const key = Object . keys ( examples ) [ 0 ] ;
69
- if ( key ) {
70
- // @TODO handle multiple examples
71
- const firstExample = examples [ key ] ;
72
- if ( firstExample ) {
73
- return firstExample ;
74
- }
75
- }
76
- }
77
-
78
- if ( example ) {
79
- return { value : example } ;
80
- }
81
-
82
- const schema = mediaTypeObject . schema ;
83
- if ( ! schema ) {
84
- return null ;
85
- }
86
-
87
- return { value : generateSchemaExample ( schema ) } ;
88
- } ) ( ) ,
89
- ) ;
60
+ if ( ! responseObject . content || Object . keys ( responseObject . content ) . length === 0 ) {
61
+ return {
62
+ key : key ,
63
+ label : key ,
64
+ description,
65
+ body : < OpenAPIEmptyResponseExample /> ,
66
+ } ;
67
+ }
90
68
91
69
return {
92
70
key : key ,
93
71
label : key ,
94
72
description : resolveDescription ( responseObject ) ,
95
- body : example ?. value ? (
96
- < context . CodeBlock
97
- code = {
98
- typeof example . value === 'string'
99
- ? example . value
100
- : stringifyOpenAPI ( example . value , null , 2 )
101
- }
102
- syntax = "json"
103
- />
104
- ) : (
105
- < OpenAPIEmptyResponseExample />
106
- ) ,
73
+ body : < OpenAPIResponse context = { context } content = { responseObject . content } /> ,
107
74
} ;
108
75
} )
109
76
. filter ( ( val ) : val is { key : string ; label : string ; body : any ; description : string } =>
110
77
Boolean ( val ) ,
111
78
) ;
112
79
113
- if ( examples . length === 0 ) {
80
+ if ( tabs . length === 0 ) {
114
81
return null ;
115
82
}
116
83
117
84
return (
118
- < OpenAPITabs stateKey = { createStateKey ( 'response-example' ) } items = { examples } >
85
+ < OpenAPITabs stateKey = { createStateKey ( 'response-example' ) } items = { tabs } >
119
86
< InteractiveSection header = { < OpenAPITabsList /> } className = "openapi-response-example" >
120
87
< OpenAPITabsPanels />
121
88
</ InteractiveSection >
122
89
</ OpenAPITabs >
123
90
) ;
124
91
}
125
92
93
+ function OpenAPIResponse ( props : {
94
+ context : OpenAPIContextProps ;
95
+ content : {
96
+ [ media : string ] : OpenAPIV3 . MediaTypeObject ;
97
+ } ;
98
+ } ) {
99
+ const { context, content } = props ;
100
+
101
+ const entries = Object . entries ( content ) ;
102
+ const firstEntry = entries [ 0 ] ;
103
+
104
+ if ( ! firstEntry ) {
105
+ throw new Error ( 'One media type is required' ) ;
106
+ }
107
+
108
+ if ( entries . length === 1 ) {
109
+ const [ mediaType , mediaTypeObject ] = firstEntry ;
110
+ return (
111
+ < OpenAPIResponseMediaType
112
+ context = { context }
113
+ mediaType = { mediaType }
114
+ mediaTypeObject = { mediaTypeObject }
115
+ />
116
+ ) ;
117
+ }
118
+
119
+ const tabs = entries . map ( ( entry ) => {
120
+ const [ mediaType , mediaTypeObject ] = entry ;
121
+ return {
122
+ key : mediaType ,
123
+ label : mediaType ,
124
+ body : (
125
+ < OpenAPIResponseMediaType
126
+ context = { context }
127
+ mediaType = { mediaType }
128
+ mediaTypeObject = { mediaTypeObject }
129
+ />
130
+ ) ,
131
+ } ;
132
+ } ) ;
133
+
134
+ return (
135
+ < OpenAPITabs stateKey = { createStateKey ( 'response-media-types' ) } items = { tabs } >
136
+ < InteractiveSection
137
+ header = { < OpenAPITabsList /> }
138
+ className = "openapi-response-media-types"
139
+ >
140
+ < OpenAPITabsPanels />
141
+ </ InteractiveSection >
142
+ </ OpenAPITabs >
143
+ ) ;
144
+ }
145
+
146
+ function OpenAPIResponseMediaType ( props : {
147
+ mediaTypeObject : OpenAPIV3 . MediaTypeObject ;
148
+ mediaType : string ;
149
+ context : OpenAPIContextProps ;
150
+ } ) {
151
+ const { mediaTypeObject, mediaType } = props ;
152
+ const examples = getExamplesFromMediaTypeObject ( { mediaTypeObject, mediaType } ) ;
153
+ const syntax = getSyntaxFromMediaType ( mediaType ) ;
154
+ const firstExample = examples [ 0 ] ;
155
+
156
+ if ( ! firstExample ) {
157
+ return < OpenAPIEmptyResponseExample /> ;
158
+ }
159
+
160
+ if ( examples . length === 1 ) {
161
+ return (
162
+ < OpenAPIExample
163
+ example = { firstExample . example }
164
+ context = { props . context }
165
+ syntax = { syntax }
166
+ />
167
+ ) ;
168
+ }
169
+
170
+ const tabs = examples . map ( ( example ) => {
171
+ return {
172
+ key : example . key ,
173
+ label : example . example . summary || example . key ,
174
+ body : (
175
+ < OpenAPIExample
176
+ example = { firstExample . example }
177
+ context = { props . context }
178
+ syntax = { syntax }
179
+ />
180
+ ) ,
181
+ } ;
182
+ } ) ;
183
+
184
+ return (
185
+ < OpenAPITabs stateKey = { createStateKey ( 'response-media-type-examples' ) } items = { tabs } >
186
+ < InteractiveSection
187
+ header = { < OpenAPITabsList /> }
188
+ className = "openapi-response-media-type-examples"
189
+ >
190
+ < OpenAPITabsPanels />
191
+ </ InteractiveSection >
192
+ </ OpenAPITabs >
193
+ ) ;
194
+ }
195
+
196
+ /**
197
+ * Display an example.
198
+ */
199
+ function OpenAPIExample ( props : {
200
+ example : OpenAPIV3 . ExampleObject ;
201
+ context : OpenAPIContextProps ;
202
+ syntax : string ;
203
+ } ) {
204
+ const { example, context, syntax } = props ;
205
+ const code = stringifyExample ( { example, xml : syntax === 'xml' } ) ;
206
+
207
+ if ( code === null ) {
208
+ return < OpenAPIEmptyResponseExample /> ;
209
+ }
210
+
211
+ return < context . CodeBlock code = { code } syntax = { syntax } /> ;
212
+ }
213
+
214
+ function stringifyExample ( args : { example : OpenAPIV3 . ExampleObject ; xml : boolean } ) : string | null {
215
+ const { example, xml } = args ;
216
+
217
+ if ( ! example . value ) {
218
+ return null ;
219
+ }
220
+
221
+ if ( typeof example . value === 'string' ) {
222
+ return example . value ;
223
+ }
224
+
225
+ if ( xml ) {
226
+ return json2xml ( example . value ) ;
227
+ }
228
+
229
+ return JSON . stringify ( example . value , null , 2 ) ;
230
+ }
231
+
232
+ /**
233
+ * Get the syntax from a media type.
234
+ */
235
+ function getSyntaxFromMediaType ( mediaType : string ) : string {
236
+ if ( mediaType . includes ( 'json' ) ) {
237
+ return 'json' ;
238
+ }
239
+
240
+ if ( mediaType === 'application/xml' ) {
241
+ return 'xml' ;
242
+ }
243
+
244
+ return 'text' ;
245
+ }
246
+
247
+ /**
248
+ * Get examples from a media type object.
249
+ */
250
+ function getExamplesFromMediaTypeObject ( args : {
251
+ mediaType : string ;
252
+ mediaTypeObject : OpenAPIV3 . MediaTypeObject ;
253
+ } ) : { key : string ; example : OpenAPIV3 . ExampleObject } [ ] {
254
+ const { mediaTypeObject, mediaType } = args ;
255
+ if ( mediaTypeObject . examples ) {
256
+ return Object . entries ( mediaTypeObject . examples ) . map ( ( [ key , example ] ) => {
257
+ return {
258
+ key,
259
+ example : checkIsReference ( example ) ? getExampleFromReference ( example ) : example ,
260
+ } ;
261
+ } ) ;
262
+ }
263
+
264
+ if ( mediaTypeObject . example ) {
265
+ return [ { key : 'default' , example : { value : mediaTypeObject . example } } ] ;
266
+ }
267
+
268
+ if ( mediaTypeObject . schema ) {
269
+ if ( mediaType === 'application/xml' ) {
270
+ // @TODO normally we should use the name of the schema but we don't have it
271
+ // fix it when we got the reference name
272
+ const root = mediaTypeObject . schema . xml ?. name ?? 'object' ;
273
+ return [
274
+ {
275
+ key : 'default' ,
276
+ example : {
277
+ value : {
278
+ [ root ] : generateSchemaExample ( mediaTypeObject . schema , {
279
+ xml : mediaType === 'application/xml' ,
280
+ } ) ,
281
+ } ,
282
+ } ,
283
+ } ,
284
+ ] ;
285
+ }
286
+ return [
287
+ {
288
+ key : 'default' ,
289
+ example : { value : generateSchemaExample ( mediaTypeObject . schema ) } ,
290
+ } ,
291
+ ] ;
292
+ }
293
+ return [ ] ;
294
+ }
295
+
296
+ /**
297
+ * Empty response example.
298
+ */
126
299
function OpenAPIEmptyResponseExample ( ) {
127
300
return (
128
301
< pre className = "openapi-response-example-empty" >
@@ -131,15 +304,9 @@ function OpenAPIEmptyResponseExample() {
131
304
) ;
132
305
}
133
306
134
- function handleUnresolvedReference (
135
- input : OpenAPIV3 . ExampleObject | null ,
136
- ) : OpenAPIV3 . ExampleObject | null {
137
- const isReference = checkIsReference ( input ?. value ) ;
138
-
139
- if ( isReference ) {
140
- // If we find a reference that wasn't resolved or needed to be resolved externally, render out the URL
141
- return { value : input . value . $ref } ;
142
- }
143
-
144
- return input ;
307
+ /**
308
+ * Generate an example from a reference object.
309
+ */
310
+ function getExampleFromReference ( ref : OpenAPIV3 . ReferenceObject ) : OpenAPIV3 . ExampleObject {
311
+ return { summary : 'Unresolved reference' , value : { $ref : ref . $ref } } ;
145
312
}
0 commit comments