@f3liz/rescript-autogen-openapi 0.3.1

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 (72) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +111 -0
  3. package/lib/es6/src/Codegen.d.ts +28 -0
  4. package/lib/es6/src/Codegen.mjs +423 -0
  5. package/lib/es6/src/Types.d.ts +286 -0
  6. package/lib/es6/src/Types.mjs +20 -0
  7. package/lib/es6/src/bindings/Toposort.mjs +12 -0
  8. package/lib/es6/src/core/CodegenUtils.mjs +261 -0
  9. package/lib/es6/src/core/DocOverride.mjs +399 -0
  10. package/lib/es6/src/core/FileSystem.d.ts +4 -0
  11. package/lib/es6/src/core/FileSystem.mjs +78 -0
  12. package/lib/es6/src/core/IRBuilder.mjs +201 -0
  13. package/lib/es6/src/core/OpenAPIParser.mjs +168 -0
  14. package/lib/es6/src/core/Pipeline.d.ts +6 -0
  15. package/lib/es6/src/core/Pipeline.mjs +150 -0
  16. package/lib/es6/src/core/ReferenceResolver.mjs +41 -0
  17. package/lib/es6/src/core/Result.mjs +378 -0
  18. package/lib/es6/src/core/SchemaIR.mjs +425 -0
  19. package/lib/es6/src/core/SchemaIRParser.mjs +683 -0
  20. package/lib/es6/src/core/SchemaRefResolver.mjs +146 -0
  21. package/lib/es6/src/core/SchemaRegistry.mjs +92 -0
  22. package/lib/es6/src/core/SpecDiffer.mjs +251 -0
  23. package/lib/es6/src/core/SpecMerger.mjs +237 -0
  24. package/lib/es6/src/generators/ComponentSchemaGenerator.mjs +207 -0
  25. package/lib/es6/src/generators/DiffReportGenerator.mjs +155 -0
  26. package/lib/es6/src/generators/EndpointGenerator.mjs +173 -0
  27. package/lib/es6/src/generators/IRToSuryGenerator.mjs +543 -0
  28. package/lib/es6/src/generators/IRToTypeGenerator.mjs +592 -0
  29. package/lib/es6/src/generators/IRToTypeScriptGenerator.mjs +143 -0
  30. package/lib/es6/src/generators/ModuleGenerator.mjs +285 -0
  31. package/lib/es6/src/generators/SchemaCodeGenerator.mjs +77 -0
  32. package/lib/es6/src/generators/ThinWrapperGenerator.mjs +97 -0
  33. package/lib/es6/src/generators/TypeScriptDtsGenerator.mjs +172 -0
  34. package/lib/es6/src/generators/TypeScriptWrapperGenerator.mjs +145 -0
  35. package/lib/es6/src/types/CodegenError.d.ts +66 -0
  36. package/lib/es6/src/types/CodegenError.mjs +79 -0
  37. package/lib/es6/src/types/Config.d.ts +31 -0
  38. package/lib/es6/src/types/Config.mjs +42 -0
  39. package/lib/es6/src/types/GenerationContext.mjs +47 -0
  40. package/package.json +53 -0
  41. package/rescript.json +26 -0
  42. package/src/Codegen.res +231 -0
  43. package/src/Types.res +222 -0
  44. package/src/bindings/Toposort.res +16 -0
  45. package/src/core/CodegenUtils.res +180 -0
  46. package/src/core/DocOverride.res +504 -0
  47. package/src/core/FileSystem.res +63 -0
  48. package/src/core/IRBuilder.res +66 -0
  49. package/src/core/OpenAPIParser.res +144 -0
  50. package/src/core/Pipeline.res +52 -0
  51. package/src/core/ReferenceResolver.res +41 -0
  52. package/src/core/Result.res +187 -0
  53. package/src/core/SchemaIR.res +291 -0
  54. package/src/core/SchemaIRParser.res +454 -0
  55. package/src/core/SchemaRefResolver.res +143 -0
  56. package/src/core/SchemaRegistry.res +107 -0
  57. package/src/core/SpecDiffer.res +270 -0
  58. package/src/core/SpecMerger.res +245 -0
  59. package/src/generators/ComponentSchemaGenerator.res +210 -0
  60. package/src/generators/DiffReportGenerator.res +152 -0
  61. package/src/generators/EndpointGenerator.res +176 -0
  62. package/src/generators/IRToSuryGenerator.res +386 -0
  63. package/src/generators/IRToTypeGenerator.res +423 -0
  64. package/src/generators/IRToTypeScriptGenerator.res +77 -0
  65. package/src/generators/ModuleGenerator.res +363 -0
  66. package/src/generators/SchemaCodeGenerator.res +84 -0
  67. package/src/generators/ThinWrapperGenerator.res +124 -0
  68. package/src/generators/TypeScriptDtsGenerator.res +193 -0
  69. package/src/generators/TypeScriptWrapperGenerator.res +166 -0
  70. package/src/types/CodegenError.res +85 -0
  71. package/src/types/Config.res +95 -0
  72. package/src/types/GenerationContext.res +56 -0
@@ -0,0 +1,363 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+
3
+ // ModuleGenerator.res - Generate API modules organized by tags or in a flat structure
4
+ open Types
5
+
6
+ let generateSchemaCodeForDict = (schemaDict: dict<jsonSchema>) =>
7
+ Dict.toArray(schemaDict)
8
+ ->Array.toSorted(((nameA, _), (nameB, _)) => String.compare(nameA, nameB))
9
+ ->Array.flatMap(((name, schema)) => {
10
+ let (ir, _) = SchemaIRParser.parseJsonSchema(schema)
11
+ let (typeCode, _, extractedTypes) = IRToTypeGenerator.generateNamedType(
12
+ ~namedSchema={name: name, description: schema.description, type_: ir},
13
+ )
14
+ let (schemaCode, _) = IRToSuryGenerator.generateNamedSchema(
15
+ ~namedSchema={name: `${name}Schema`, description: schema.description, type_: ir},
16
+ ~extractedTypes,
17
+ )
18
+ [typeCode->CodegenUtils.indent(2), schemaCode->CodegenUtils.indent(2), ""]
19
+ })
20
+
21
+ let generateTagModulesCode = (endpoints: array<endpoint>, ~overrideDir=?, ~indent=2) => {
22
+ let groupedByTag = OpenAPIParser.groupByTag(endpoints)
23
+ let indentStr = " "->String.repeat(indent)
24
+
25
+ Dict.keysToArray(groupedByTag)
26
+ ->Array.toSorted(String.compare)
27
+ ->Array.filterMap(tag =>
28
+ groupedByTag
29
+ ->Dict.get(tag)
30
+ ->Option.map(tagEndpoints => {
31
+ let moduleName = CodegenUtils.toPascalCase(tag)
32
+ let endpointLines = tagEndpoints->Array.flatMap(endpoint => [
33
+ EndpointGenerator.generateEndpointCode(
34
+ endpoint,
35
+ ~overrideDir?,
36
+ ~moduleName,
37
+ )->CodegenUtils.indent(indent + 2),
38
+ "",
39
+ ])
40
+ Array.concat(
41
+ [`${indentStr}module ${moduleName} = {`],
42
+ Array.concat(endpointLines, [`${indentStr}}`, ""]),
43
+ )
44
+ })
45
+ )
46
+ ->Array.flatMap(lines => lines)
47
+ }
48
+
49
+ let generateTagModuleFile = (
50
+ ~tag,
51
+ ~endpoints,
52
+ ~includeSchemas as _: bool=true,
53
+ ~wrapInModule=false,
54
+ ~overrideDir=?,
55
+ ) => {
56
+ let moduleName = CodegenUtils.toPascalCase(tag)
57
+ let header = CodegenUtils.generateFileHeader(~description=`API endpoints for ${tag}`)
58
+ let body =
59
+ endpoints
60
+ ->Array.map(endpoint =>
61
+ EndpointGenerator.generateEndpointCode(endpoint, ~overrideDir?, ~moduleName)
62
+ )
63
+ ->Array.join("\n\n")
64
+
65
+ if wrapInModule {
66
+ `
67
+ |${header->String.trimEnd}
68
+ |
69
+ |module ${moduleName} = {
70
+ |${body->CodegenUtils.indent(2)}
71
+ |}
72
+ |`->CodegenUtils.trimMargin
73
+ } else {
74
+ `
75
+ |${header->String.trimEnd}
76
+ |
77
+ |${body}
78
+ |`->CodegenUtils.trimMargin
79
+ }
80
+ }
81
+
82
+ let generateAllTagModules = (
83
+ ~endpoints,
84
+ ~includeSchemas=true,
85
+ ~wrapInModule=false,
86
+ ~overrideDir=?,
87
+ ) => {
88
+ let groupedByTag = OpenAPIParser.groupByTag(endpoints)
89
+ Dict.toArray(groupedByTag)
90
+ ->Array.toSorted(((tagA, _), (tagB, _)) => String.compare(tagA, tagB))
91
+ ->Array.map(((tag, tagEndpoints)) => (
92
+ tag,
93
+ generateTagModuleFile(~tag, ~endpoints=tagEndpoints, ~includeSchemas, ~wrapInModule, ~overrideDir?),
94
+ ))
95
+ }
96
+
97
+ let generateIndexModule = (~tags, ~moduleName="API") => {
98
+ let header = CodegenUtils.generateFileHeader(~description="Main API module index")
99
+ let modules =
100
+ tags
101
+ ->Array.toSorted(String.compare)
102
+ ->Array.map(tag => {
103
+ let m = CodegenUtils.toPascalCase(tag)
104
+ ` module ${m} = ${m}`
105
+ })
106
+ ->Array.join("\n")
107
+
108
+ `
109
+ |${header->String.trimEnd}
110
+ |
111
+ |module ${moduleName} = {
112
+ |${modules}
113
+ |}
114
+ |`->CodegenUtils.trimMargin
115
+ }
116
+
117
+ let generateFlatModuleCode = (~moduleName, ~endpoints, ~overrideDir=?) => {
118
+ let header = CodegenUtils.generateFileHeader(~description=`All API endpoints in ${moduleName}`)
119
+ let body =
120
+ endpoints
121
+ ->Array.map(endpoint =>
122
+ EndpointGenerator.generateEndpointCode(
123
+ endpoint,
124
+ ~overrideDir?,
125
+ ~moduleName,
126
+ )->CodegenUtils.indent(2)
127
+ )
128
+ ->Array.join("\n\n")
129
+
130
+ `
131
+ |${header->String.trimEnd}
132
+ |
133
+ |module ${moduleName} = {
134
+ |${body}
135
+ |}
136
+ |`->CodegenUtils.trimMargin
137
+ }
138
+
139
+ let internalGenerateIntegratedModule = (
140
+ ~name,
141
+ ~description,
142
+ ~endpoints,
143
+ ~schemas,
144
+ ~overrideDir=?,
145
+ ~isExtension=false,
146
+ ~includeHeader=true,
147
+ ) => {
148
+ let lines = []
149
+
150
+ if includeHeader {
151
+ lines->Array.pushMany([CodegenUtils.generateFileHeader(~description), ""])
152
+ }
153
+
154
+ lines->Array.push(`module ${name} = {`)
155
+
156
+ schemas->Option.forEach(schemaDict =>
157
+ if Dict.keysToArray(schemaDict)->Array.length > 0 {
158
+ lines->Array.pushMany([
159
+ ` // ${isExtension ? "Extension" : "Component"} Schemas`,
160
+ "",
161
+ ...generateSchemaCodeForDict(schemaDict),
162
+ ])
163
+ }
164
+ )
165
+
166
+ if Array.length(endpoints) > 0 {
167
+ if isExtension {
168
+ lines->Array.pushMany([" // Extension Endpoints", ""])
169
+ }
170
+ lines->Array.pushMany(generateTagModulesCode(endpoints, ~overrideDir?))
171
+ }
172
+
173
+ lines->Array.push("}")
174
+ Array.join(lines, "\n")
175
+ }
176
+
177
+ let generateSharedModule = (~endpoints, ~schemas, ~overrideDir=?, ~includeHeader=true) =>
178
+ internalGenerateIntegratedModule(
179
+ ~name="Shared",
180
+ ~description="Shared API code",
181
+ ~endpoints,
182
+ ~schemas,
183
+ ~overrideDir?,
184
+ ~includeHeader,
185
+ )
186
+
187
+ let generateExtensionModule = (~forkName, ~endpoints, ~schemas, ~overrideDir=?, ~includeHeader=true) =>
188
+ internalGenerateIntegratedModule(
189
+ ~name=`${CodegenUtils.toPascalCase(forkName)}Extensions`,
190
+ ~description=`${forkName} extensions`,
191
+ ~endpoints,
192
+ ~schemas,
193
+ ~overrideDir?,
194
+ ~isExtension=true,
195
+ ~includeHeader,
196
+ )
197
+
198
+ let generateCombinedModule = (
199
+ ~forkName,
200
+ ~sharedEndpoints,
201
+ ~extensionEndpoints,
202
+ ~sharedSchemas,
203
+ ~extensionSchemas,
204
+ ~overrideDir=?,
205
+ ) => {
206
+ let header = CodegenUtils.generateFileHeader(~description=`Combined Shared and ${forkName} extensions`)
207
+
208
+ let shared = generateSharedModule(
209
+ ~endpoints=sharedEndpoints,
210
+ ~schemas=sharedSchemas,
211
+ ~overrideDir?,
212
+ ~includeHeader=false,
213
+ )
214
+
215
+ let extension = generateExtensionModule(
216
+ ~forkName,
217
+ ~endpoints=extensionEndpoints,
218
+ ~schemas=extensionSchemas,
219
+ ~overrideDir?,
220
+ ~includeHeader=false,
221
+ )
222
+
223
+ `
224
+ |${header->String.trimEnd}
225
+ |
226
+ |${shared}
227
+ |
228
+ |${extension}
229
+ |`->CodegenUtils.trimMargin
230
+ }
231
+
232
+ let generateTagModuleFiles = (~endpoints, ~outputDir, ~wrapInModule=false, ~overrideDir=?) => {
233
+ let files =
234
+ generateAllTagModules(~endpoints, ~includeSchemas=true, ~wrapInModule, ~overrideDir?)->Array.map(((
235
+ tag,
236
+ content,
237
+ )) => {
238
+ let path = FileSystem.makePath(outputDir, `${CodegenUtils.toPascalCase(tag)}.res`)
239
+ ({path, content}: FileSystem.fileToWrite)
240
+ })
241
+ Pipeline.fromFilesAndWarnings(files, [])
242
+ }
243
+
244
+ let generateFlatModuleFile = (~moduleName, ~endpoints, ~outputDir, ~overrideDir=?) => {
245
+ let path = FileSystem.makePath(outputDir, `${moduleName}.res`)
246
+ let content = generateFlatModuleCode(~moduleName, ~endpoints, ~overrideDir?)
247
+ Pipeline.fromFile(({path, content}: FileSystem.fileToWrite))
248
+ }
249
+
250
+ let generateInstanceTagModules = (
251
+ ~instanceName,
252
+ ~modulePrefix,
253
+ ~endpoints,
254
+ ~schemas,
255
+ ~outputDir,
256
+ ~overrideDir=?,
257
+ ) => {
258
+ let apiDir = FileSystem.makePath(FileSystem.makePath(outputDir, instanceName), "api")
259
+
260
+ let schemaFiles = schemas->Option.mapOr([], schemaDict =>
261
+ if Dict.keysToArray(schemaDict)->Array.length == 0 {
262
+ []
263
+ } else {
264
+ let result = ComponentSchemaGenerator.generate(
265
+ ~spec={
266
+ openapi: "3.1.0",
267
+ info: {title: instanceName, version: "1.0.0", description: None},
268
+ paths: Dict.make(),
269
+ components: Some({schemas: Some(schemaDict)}),
270
+ },
271
+ ~outputDir=apiDir,
272
+ )
273
+ result.files->Array.map(file =>
274
+ if file.path->String.endsWith("ComponentSchemas.res") {
275
+ {
276
+ ...file,
277
+ path: file.path->String.replace(
278
+ "ComponentSchemas.res",
279
+ `${modulePrefix}ComponentSchemas.res`,
280
+ ),
281
+ }
282
+ } else {
283
+ file
284
+ }
285
+ )
286
+ }
287
+ )
288
+
289
+ let groupedByTag = OpenAPIParser.groupByTag(endpoints)
290
+ let endpointFiles =
291
+ Dict.toArray(groupedByTag)
292
+ ->Array.toSorted(((tagA, _), (tagB, _)) => String.compare(tagA, tagB))
293
+ ->Array.filterMap(((tag, tagEndpoints)) => {
294
+ let moduleName = `${modulePrefix}${CodegenUtils.toPascalCase(tag)}`
295
+ let endpointCodes = tagEndpoints->Array.flatMap(endpoint => [
296
+ EndpointGenerator.generateEndpointCode(endpoint, ~overrideDir?, ~moduleName, ~modulePrefix),
297
+ "",
298
+ ])
299
+ let content = Array.join(
300
+ [
301
+ CodegenUtils.generateFileHeader(~description=`${instanceName} API for ${tag}`),
302
+ "",
303
+ ...endpointCodes,
304
+ ],
305
+ "\n",
306
+ )
307
+ let path = FileSystem.makePath(apiDir, `${moduleName}.res`)
308
+ Some(({path, content}: FileSystem.fileToWrite))
309
+ })
310
+
311
+ Pipeline.fromFilesAndWarnings(Array.concat(schemaFiles, endpointFiles), [])
312
+ }
313
+
314
+ let generateBaseTagModules = (~baseName, ~basePrefix, ~endpoints, ~schemas, ~outputDir, ~overrideDir=?) =>
315
+ generateInstanceTagModules(
316
+ ~instanceName=baseName,
317
+ ~modulePrefix=basePrefix,
318
+ ~endpoints,
319
+ ~schemas,
320
+ ~outputDir,
321
+ ~overrideDir?,
322
+ )
323
+
324
+ let generateForkTagModules = (~forkName, ~forkPrefix, ~endpoints, ~schemas, ~outputDir, ~overrideDir=?) =>
325
+ generateInstanceTagModules(
326
+ ~instanceName=forkName,
327
+ ~modulePrefix=forkPrefix,
328
+ ~endpoints,
329
+ ~schemas,
330
+ ~outputDir,
331
+ ~overrideDir?,
332
+ )
333
+
334
+ let generateSeparatePerTagModules = (
335
+ ~baseName,
336
+ ~basePrefix,
337
+ ~forkName,
338
+ ~forkPrefix=None,
339
+ ~sharedEndpoints,
340
+ ~extensionEndpoints,
341
+ ~sharedSchemas,
342
+ ~extensionSchemas,
343
+ ~outputDir,
344
+ ~overrideDir=?,
345
+ ) =>
346
+ Pipeline.combine([
347
+ generateBaseTagModules(
348
+ ~baseName,
349
+ ~basePrefix,
350
+ ~endpoints=sharedEndpoints,
351
+ ~schemas=sharedSchemas,
352
+ ~outputDir,
353
+ ~overrideDir?,
354
+ ),
355
+ generateForkTagModules(
356
+ ~forkName,
357
+ ~forkPrefix=Option.getOr(forkPrefix, CodegenUtils.toPascalCase(forkName)),
358
+ ~endpoints=extensionEndpoints,
359
+ ~schemas=extensionSchemas,
360
+ ~outputDir,
361
+ ~overrideDir?,
362
+ ),
363
+ ])
@@ -0,0 +1,84 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+
3
+ // SchemaCodeGenerator.res - Generate complete Sury schema code with types
4
+
5
+ let generateTypeCodeAndSchemaCode = (name, schema: Types.jsonSchema) => {
6
+ let (ir, _) = SchemaIRParser.parseJsonSchema(schema)
7
+ let (typeCode, _, extractedTypes) = IRToTypeGenerator.generateNamedType(
8
+ ~namedSchema={name: name, description: schema.description, type_: ir},
9
+ )
10
+ let (schemaCode, _) = IRToSuryGenerator.generateNamedSchema(
11
+ ~namedSchema={name: `${name}Schema`, description: schema.description, type_: ir},
12
+ ~extractedTypes,
13
+ )
14
+ `${typeCode}\n\n${schemaCode}`
15
+ }
16
+
17
+ let generateTypeAndSchema = (name, schema) => generateTypeCodeAndSchemaCode(name, schema)
18
+
19
+ let generateComponentSchemas = (components: option<Types.components>) =>
20
+ components
21
+ ->Option.flatMap(c => c.schemas)
22
+ ->Option.mapOr("// No component schemas\n", schemas => {
23
+ let sections =
24
+ schemas
25
+ ->Dict.toArray
26
+ ->Array.map(((name, schema)) => generateTypeCodeAndSchemaCode(name, schema))
27
+ ->Array.join("\n\n")
28
+ `// Component Schemas\n\n${sections}`
29
+ })
30
+
31
+ let generateOperationSchemas = (operationId, operation: Types.operation) => {
32
+ let generatePart = (suffix, schemaOpt) =>
33
+ schemaOpt->Option.mapOr("", schema =>
34
+ generateTypeCodeAndSchemaCode(`${CodegenUtils.toPascalCase(operationId)}${suffix}`, schema)
35
+ )
36
+
37
+ let requestBodySchema =
38
+ operation.requestBody
39
+ ->Option.flatMap(body => body.content->Dict.get("application/json"))
40
+ ->Option.flatMap(mediaType => mediaType.schema)
41
+
42
+ let successResponseSchema =
43
+ operation.responses
44
+ ->(
45
+ responses =>
46
+ Dict.get(responses, "200")->Option.orElse(Dict.get(responses, "201"))
47
+ )
48
+ ->Option.flatMap(response => response.content)
49
+ ->Option.flatMap(content => content->Dict.get("application/json"))
50
+ ->Option.flatMap(mediaType => mediaType.schema)
51
+
52
+ [
53
+ generatePart("Request", requestBodySchema),
54
+ generatePart("Response", successResponseSchema),
55
+ ]
56
+ ->Array.filter(code => code != "")
57
+ ->Array.join("\n\n")
58
+ }
59
+
60
+ let generateEndpointModule = (path, method, operation: Types.operation) => {
61
+ let operationId = OpenAPIParser.getOperationId(path, method, operation)
62
+ let docComment = CodegenUtils.generateDocComment(
63
+ ~summary=?operation.summary,
64
+ ~description=?operation.description,
65
+ (),
66
+ )
67
+ let methodStr = switch method {
68
+ | #GET => "GET"
69
+ | #POST => "POST"
70
+ | #PUT => "PUT"
71
+ | #PATCH => "PATCH"
72
+ | #DELETE => "DELETE"
73
+ | #HEAD => "HEAD"
74
+ | #OPTIONS => "OPTIONS"
75
+ }
76
+ let schemasCode = generateOperationSchemas(operationId, operation)
77
+
78
+ `${docComment}module ${CodegenUtils.toPascalCase(operationId)} = {
79
+ ${schemasCode->CodegenUtils.indent(2)}
80
+
81
+ let endpoint = "${path}"
82
+ let method = #${methodStr}
83
+ }`
84
+ }
@@ -0,0 +1,124 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+
3
+ // ThinWrapperGenerator.res - Generate ReScript thin wrappers with pipe-first ergonomics
4
+ open Types
5
+
6
+ let clientTypeCode = `
7
+ |type client = {
8
+ | baseUrl: string,
9
+ | token: option<string>,
10
+ | fetch: ${CodegenUtils.fetchTypeSignature},
11
+ |}
12
+ |`->CodegenUtils.trimMargin
13
+
14
+ let generateConnectFunction = (title) =>
15
+ `
16
+ |/** Create a client for ${title} */
17
+ |let connect = (~baseUrl: string, ~token: option<string>=?, ~fetch: ${CodegenUtils.fetchTypeSignature}, ()): client => {
18
+ | baseUrl,
19
+ | token,
20
+ | fetch,
21
+ |}
22
+ |`->CodegenUtils.trimMargin
23
+
24
+ let generateWrapperFunction = (~endpoint: endpoint, ~generatedModuleName: string) => {
25
+ let operationName = CodegenUtils.generateOperationName(
26
+ endpoint.operationId,
27
+ endpoint.path,
28
+ endpoint.method,
29
+ )
30
+ let hasRequestBody = endpoint.requestBody->Option.isSome
31
+ let docComment = endpoint.summary->Option.mapOr("", summary => {
32
+ let descriptionPart = endpoint.description->Option.mapOr("", description =>
33
+ description == summary ? "" : " - " ++ description
34
+ )
35
+ ` /** ${summary}${descriptionPart} */\n`
36
+ })
37
+
38
+ let signature = hasRequestBody
39
+ ? `let ${operationName} = (request: ${generatedModuleName}.${operationName}Request, ~client: client)`
40
+ : `let ${operationName} = (~client: client)`
41
+
42
+ let callArguments = hasRequestBody ? "~body=request, " : ""
43
+
44
+ `${docComment} ${signature}: promise<${generatedModuleName}.${operationName}Response> =>
45
+ ${generatedModuleName}.${operationName}(${callArguments}~fetch=client.fetch)`
46
+ }
47
+
48
+ let generateWrapper = (
49
+ ~spec: openAPISpec,
50
+ ~endpoints,
51
+ ~extensionEndpoints=[],
52
+ ~outputDir,
53
+ ~wrapperModuleName="Wrapper",
54
+ ~generatedModulePrefix="",
55
+ ~baseModulePrefix="",
56
+ ) => {
57
+ let extensionOperationIds =
58
+ extensionEndpoints->Array.reduce(Dict.make(), (acc, endpoint) => {
59
+ let name = CodegenUtils.generateOperationName(
60
+ endpoint.operationId,
61
+ endpoint.path,
62
+ endpoint.method,
63
+ )
64
+ Dict.set(acc, name, true)
65
+ acc
66
+ })
67
+
68
+ let hasExtensions = Array.length(extensionEndpoints) > 0
69
+
70
+ let allEndpoints = Array.concat(
71
+ hasExtensions
72
+ ? endpoints->Array.filter(endpoint => {
73
+ let name = CodegenUtils.generateOperationName(
74
+ endpoint.operationId,
75
+ endpoint.path,
76
+ endpoint.method,
77
+ )
78
+ !Dict.has(extensionOperationIds, name)
79
+ })
80
+ : endpoints,
81
+ extensionEndpoints,
82
+ )
83
+
84
+ let endpointsByTag = OpenAPIParser.groupByTag(allEndpoints)
85
+
86
+ let modulesCode =
87
+ Dict.keysToArray(endpointsByTag)
88
+ ->Array.map(tag => {
89
+ let moduleName = CodegenUtils.toPascalCase(tag)
90
+ let wrapperFunctions =
91
+ Dict.get(endpointsByTag, tag)
92
+ ->Option.getOr([])
93
+ ->Array.map(endpoint => {
94
+ let operationName = CodegenUtils.generateOperationName(
95
+ endpoint.operationId,
96
+ endpoint.path,
97
+ endpoint.method,
98
+ )
99
+ let isExtension = hasExtensions && Dict.has(extensionOperationIds, operationName)
100
+ let prefix =
101
+ (!isExtension && baseModulePrefix != "") ? baseModulePrefix : generatedModulePrefix
102
+
103
+ let targetModuleName = prefix != "" ? `${prefix}${moduleName}` : moduleName
104
+ generateWrapperFunction(~endpoint, ~generatedModuleName=targetModuleName)
105
+ })
106
+ ->Array.join("\n\n")
107
+
108
+ `module ${moduleName} = {\n${wrapperFunctions}\n}`
109
+ })
110
+ ->Array.join("\n\n")
111
+
112
+ let fileContent = `// Generated thin wrapper
113
+
114
+ ${clientTypeCode}
115
+
116
+ ${generateConnectFunction(spec.info.title)}
117
+
118
+ ${modulesCode}`
119
+
120
+ Pipeline.fromFilesAndWarnings(
121
+ [{path: FileSystem.makePath(outputDir, `${wrapperModuleName}.res`), content: fileContent}],
122
+ [],
123
+ )
124
+ }