Skip to content

Commit b908dd8

Browse files
OpenAPI stuff
1 parent 133262d commit b908dd8

12 files changed

Lines changed: 1451 additions & 2293 deletions

File tree

src/Nap.Cli/Program.fs

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,36 @@ open Nap.Core
44

55
/// Parse CLI arguments into a structured form
66
type CliArgs = {
7-
Command : string // "run", "check", "help"
8-
File : string option
9-
Env : string option
10-
Vars : Map<string, string>
11-
Output : string // "pretty", "junit", "json", "ndjson"
12-
Verbose : bool
7+
Command : string // "run", "check", "generate", "help"
8+
SubCommand : string option // e.g. "openapi" for "generate openapi"
9+
File : string option
10+
Env : string option
11+
Vars : Map<string, string>
12+
Output : string // "pretty", "junit", "json", "ndjson"
13+
OutputDir : string option // --output-dir for generate command
14+
Verbose : bool
1315
}
1416

1517
let parseArgs (argv: string array) : CliArgs =
1618
let mutable command = "help"
19+
let mutable subCommand = None
1720
let mutable file = None
1821
let mutable env = None
1922
let mutable vars = Map.empty
2023
let mutable output = "pretty"
24+
let mutable outputDir = None
2125
let mutable verbose = false
2226
let mutable i = 0
2327

2428
if argv.Length > 0 then
2529
command <- argv[0]
2630
i <- 1
2731

32+
// For "generate openapi", consume the subcommand
33+
if command = "generate" && i < argv.Length && not (argv[i].StartsWith "--") then
34+
subCommand <- Some argv[i]
35+
i <- i + 1
36+
2837
while i < argv.Length do
2938
match argv[i] with
3039
| "--env" when i + 1 < argv.Length ->
@@ -38,6 +47,9 @@ let parseArgs (argv: string array) : CliArgs =
3847
| "--output" when i + 1 < argv.Length ->
3948
output <- argv[i + 1]
4049
i <- i + 2
50+
| "--output-dir" when i + 1 < argv.Length ->
51+
outputDir <- Some argv[i + 1]
52+
i <- i + 2
4153
| "--verbose" ->
4254
verbose <- true
4355
i <- i + 1
@@ -47,20 +59,23 @@ let parseArgs (argv: string array) : CliArgs =
4759
| _ ->
4860
i <- i + 1
4961

50-
{ Command = command; File = file; Env = env; Vars = vars; Output = output; Verbose = verbose }
62+
{ Command = command; SubCommand = subCommand; File = file; Env = env
63+
Vars = vars; Output = output; OutputDir = outputDir; Verbose = verbose }
5164

5265
let printHelp () =
5366
printfn "Nap — API testing tool"
5467
printfn ""
5568
printfn "Usage:"
56-
printfn " nap run <file|folder> Run a .nap file, .naplist playlist, or folder"
57-
printfn " nap check <file> Validate a .nap or .naplist file"
58-
printfn " nap help Show this help"
69+
printfn " nap run <file|folder> Run a .nap file, .naplist playlist, or folder"
70+
printfn " nap check <file> Validate a .nap or .naplist file"
71+
printfn " nap generate openapi <spec> --output-dir <dir> Generate .nap files from OpenAPI spec"
72+
printfn " nap help Show this help"
5973
printfn ""
6074
printfn "Options:"
6175
printfn " --env <name> Environment name (loads .napenv.<name>)"
6276
printfn " --var <key=value> Variable override (repeatable)"
6377
printfn " --output <format> Output: pretty (default), junit, json, ndjson"
78+
printfn " --output-dir <dir> Output directory for generate command"
6479
printfn " --verbose Enable debug-level logging"
6580

6681
let runFile (args: CliArgs) : int =
@@ -208,6 +223,53 @@ let runFile (args: CliArgs) : int =
208223

209224
if result.Passed then 0 else 1
210225

226+
let private writeGenerated (outDir: string) (result: OpenApiGenerator.GenerationResult) : unit =
227+
let writeFile (f: OpenApiGenerator.GeneratedFile) =
228+
let fullPath = Path.Combine(outDir, f.FileName)
229+
let dir = Path.GetDirectoryName(fullPath)
230+
if not (Directory.Exists dir) then
231+
Directory.CreateDirectory(dir) |> ignore
232+
File.WriteAllText(fullPath, f.Content)
233+
writeFile result.Environment
234+
for nap in result.NapFiles do
235+
writeFile nap
236+
writeFile result.Playlist
237+
238+
let generateOpenApi (args: CliArgs) : int =
239+
match args.File with
240+
| None ->
241+
eprintfn "Error: no spec file specified"
242+
eprintfn "Usage: nap generate openapi <spec.json> --output-dir <dir>"
243+
2
244+
| Some specPath ->
245+
let specPath = Path.GetFullPath(specPath)
246+
if not (File.Exists specPath) then
247+
eprintfn "Error: %s not found" specPath
248+
2
249+
else
250+
let outDir =
251+
match args.OutputDir with
252+
| Some dir -> Path.GetFullPath(dir)
253+
| None -> Path.GetDirectoryName(specPath)
254+
let specContent = File.ReadAllText(specPath)
255+
match OpenApiGenerator.generate specContent with
256+
| Error msg ->
257+
eprintfn "Error: %s" msg
258+
1
259+
| Ok generated ->
260+
if not (Directory.Exists outDir) then
261+
Directory.CreateDirectory(outDir) |> ignore
262+
writeGenerated outDir generated
263+
match args.Output with
264+
| "json" ->
265+
printfn "{\"files\":%d,\"playlist\":\"%s\"}" generated.NapFiles.Length generated.Playlist.FileName
266+
| _ ->
267+
printfn "Generated %d .nap files from OpenAPI spec" generated.NapFiles.Length
268+
printfn " Playlist: %s" generated.Playlist.FileName
269+
printfn " Environment: %s" generated.Environment.FileName
270+
printfn " Output: %s" outDir
271+
0
272+
211273
let checkFile (args: CliArgs) : int =
212274
match args.File with
213275
| None ->
@@ -243,6 +305,15 @@ let main argv =
243305
match args.Command with
244306
| "run" -> runFile args
245307
| "check" -> checkFile args
308+
| "generate" ->
309+
match args.SubCommand with
310+
| Some "openapi" -> generateOpenApi args
311+
| Some other ->
312+
eprintfn "Unknown generate target: %s" other
313+
2
314+
| None ->
315+
eprintfn "Usage: nap generate openapi <spec.json> --output-dir <dir>"
316+
2
246317
| "help" | "--help" | "-h" ->
247318
printHelp ()
248319
0

src/Nap.Core/Nap.Core.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<Compile Include="Environment.fs" />
1313
<Compile Include="Runner.fs" />
1414
<Compile Include="Output.fs" />
15+
<Compile Include="OpenApiGenerator.fs" />
1516
</ItemGroup>
1617

1718
<ItemGroup>

0 commit comments

Comments
 (0)