Skip to content

Commit 16acfdd

Browse files
authored
Merge pull request #19 from godon-dev/aling_with_new_api_to_cli_contract
cli: add --output option for formatted responses
2 parents 29c1e2f + 2f0aae7 commit 16acfdd

2 files changed

Lines changed: 125 additions & 76 deletions

File tree

src/godon/credential.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import std/[httpclient, json, strutils, uri, tables]
1+
import std/[httpclient, json, uri, tables]
22
import yaml
33
import client, types
44

src/godon_cli.nim

Lines changed: 124 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import std/[parseopt, strutils, os, json]
2+
import yaml, yaml/presenter, yaml/dumping
23
import godon/[client, breeder, credential, types]
34

5+
type
6+
OutputFormat* = enum
7+
Text = "text"
8+
Json = "json"
9+
Yaml = "yaml"
10+
411
proc writeHelp() =
512
echo """Godon CLI - Command line interface for Godon API
613
@@ -23,8 +30,9 @@ Global Options:
2330
--hostname, -h <host> Godon hostname (default: localhost)
2431
--port, -p <port> Godon port (default: 8080)
2532
--api-version, -v <ver> API version (default: v0)
33+
--output, -o <format> Output format: text, json, or yaml (default: text)
2634
--insecure Skip SSL certificate verification (HTTPS only)
27-
--help, -h Show this help message
35+
--help Show this help message
2836
2937
Examples:
3038
godon_cli breeder list
@@ -40,12 +48,13 @@ proc writeError(message: string) =
4048
stderr.writeLine("Error: " & message)
4149
quit(1)
4250

43-
proc parseArgs(): (string, string, int, string, bool, seq[string]) =
51+
proc parseArgs(): (string, string, int, string, bool, OutputFormat, seq[string]) =
4452
var command = ""
4553
var hostname = "localhost"
4654
var port = 8080
4755
var apiVersion = "v0"
4856
var insecure = false
57+
var outputFormat = OutputFormat.Text
4958
var args: seq[string] = @[]
5059

5160
var p = initOptParser(commandLineParams())
@@ -82,7 +91,19 @@ proc parseArgs(): (string, string, int, string, bool, seq[string]) =
8291
args.add("--id=" & val)
8392
of "insecure":
8493
insecure = true
85-
of "help", "h":
94+
of "output", "o":
95+
if val.len == 0:
96+
writeError("Output option requires a value (text, json, or yaml)")
97+
case val.normalize()
98+
of "text":
99+
outputFormat = OutputFormat.Text
100+
of "json":
101+
outputFormat = OutputFormat.Json
102+
of "yaml":
103+
outputFormat = OutputFormat.Yaml
104+
else:
105+
writeError("Unknown output format: " & val & ". Use text, json, or yaml")
106+
of "help":
86107
writeHelp()
87108
quit(0)
88109
else:
@@ -95,23 +116,39 @@ proc parseArgs(): (string, string, int, string, bool, seq[string]) =
95116
writeHelp()
96117
quit(0)
97118

98-
(command, hostname, port, apiVersion, insecure, args)
119+
(command, hostname, port, apiVersion, insecure, outputFormat, args)
120+
121+
proc formatOutput*[T](data: T, outputFormat: OutputFormat) =
122+
## Format data according to output format preference
123+
case outputFormat
124+
of OutputFormat.Text:
125+
# Text mode is handled by caller with custom formatting
126+
discard
127+
of OutputFormat.Json:
128+
echo pretty(%*data)
129+
of OutputFormat.Yaml:
130+
# Convert to YAML by dumping JSON as text and transforming it
131+
let jsonString = pretty(%*data)
132+
var dumper = blockOnlyDumper()
133+
echo dumper.transform(jsonString)
99134

100-
proc handleBreederCommand(client: GodonClient, command: string, args: seq[string]) =
135+
proc handleBreederCommand(client: GodonClient, command: string, args: seq[string], outputFormat: OutputFormat) =
101136
let subCommand = if args.len > 0: args[0] else: ""
102137

103138
case subCommand:
104139
of "list":
105-
echo "Listing breeders..."
106140
let response = client.listBreeders()
107141
if response.success:
108-
echo "Breeders:"
109-
for breeder in response.data:
110-
echo " ID: ", breeder.id
111-
echo " Name: ", breeder.name
112-
echo " Status: ", breeder.status
113-
echo " Created: ", breeder.createdAt
114-
echo " ---"
142+
if outputFormat == OutputFormat.Text:
143+
echo "Breeders:"
144+
for breeder in response.data:
145+
echo " ID: ", breeder.id
146+
echo " Name: ", breeder.name
147+
echo " Status: ", breeder.status
148+
echo " Created: ", breeder.createdAt
149+
echo " ---"
150+
else:
151+
formatOutput(response.data, outputFormat)
115152
else:
116153
writeError(response.error)
117154

@@ -133,14 +170,16 @@ proc handleBreederCommand(client: GodonClient, command: string, args: seq[string
133170
if not fileExists(file):
134171
writeError("File not found: " & file)
135172

136-
echo "Creating breeder '", name, "' from file: ", file
137173
let content = readFile(file)
138174
let response = client.createBreederFromYamlWithName(content, name)
139175
if response.success:
140-
echo "Breeder created successfully:"
141-
echo " ID: ", response.data.id
142-
echo " Name: ", response.data.name
143-
echo " Status: ", response.data.status
176+
if outputFormat == OutputFormat.Text:
177+
echo "Breeder created successfully:"
178+
echo " ID: ", response.data.id
179+
echo " Name: ", response.data.name
180+
echo " Status: ", response.data.status
181+
else:
182+
formatOutput(response.data, outputFormat)
144183
else:
145184
writeError(response.error)
146185

@@ -153,16 +192,18 @@ proc handleBreederCommand(client: GodonClient, command: string, args: seq[string
153192

154193
if id.len == 0:
155194
writeError("breeder show requires --id <id>")
156-
157-
echo "Getting breeder details for ID: ", id
195+
158196
let response = client.getBreeder(id)
159197
if response.success:
160-
echo "Breeder Details:"
161-
echo " ID: ", response.data.id
162-
echo " Name: ", response.data.name
163-
echo " Status: ", response.data.status
164-
echo " Config: ", pretty(response.data.config)
165-
echo " Created: ", response.data.createdAt
198+
if outputFormat == OutputFormat.Text:
199+
echo "Breeder Details:"
200+
echo " ID: ", response.data.id
201+
echo " Name: ", response.data.name
202+
echo " Status: ", response.data.status
203+
echo " Config: ", pretty(response.data.config)
204+
echo " Created: ", response.data.createdAt
205+
else:
206+
formatOutput(response.data, outputFormat)
166207
else:
167208
writeError(response.error)
168209

@@ -178,15 +219,17 @@ proc handleBreederCommand(client: GodonClient, command: string, args: seq[string
178219

179220
if not fileExists(file):
180221
writeError("File not found: " & file)
181-
182-
echo "Updating breeder from file: ", file
222+
183223
let content = readFile(file)
184224
let response = client.updateBreederFromYaml(content)
185225
if response.success:
186-
echo "Breeder updated successfully:"
187-
echo " ID: ", response.data.id
188-
echo " Name: ", response.data.name
189-
echo " Status: ", response.data.status
226+
if outputFormat == OutputFormat.Text:
227+
echo "Breeder updated successfully:"
228+
echo " ID: ", response.data.id
229+
echo " Name: ", response.data.name
230+
echo " Status: ", response.data.status
231+
else:
232+
formatOutput(response.data, outputFormat)
190233
else:
191234
writeError(response.error)
192235

@@ -199,36 +242,38 @@ proc handleBreederCommand(client: GodonClient, command: string, args: seq[string
199242

200243
if id.len == 0:
201244
writeError("breeder purge requires --id <id>")
202-
203-
echo "Deleting breeder with ID: ", id
245+
204246
let response = client.deleteBreeder(id)
205247
if response.success:
206-
echo "Breeder deleted successfully"
207-
if response.data != nil:
208-
echo "Response: ", pretty(response.data)
248+
if outputFormat == OutputFormat.Text:
249+
echo "Breeder deleted successfully: ", id
250+
else:
251+
formatOutput(response.data, outputFormat)
209252
else:
210253
writeError(response.error)
211254

212255
else:
213256
writeError("Unknown breeder command: " & subCommand)
214257

215-
proc handleCredentialCommand(client: GodonClient, command: string, args: seq[string]) =
258+
proc handleCredentialCommand(client: GodonClient, command: string, args: seq[string], outputFormat: OutputFormat) =
216259
let subCommand = if args.len > 0: args[0] else: ""
217260

218261
case subCommand:
219262
of "list":
220-
echo "Listing credentials..."
221263
let response = client.listCredentials()
222264
if response.success:
223-
echo "Credentials:"
224-
for credential in response.data:
225-
echo " ID: ", credential.id
226-
echo " Name: ", credential.name
227-
echo " Type: ", credential.credentialType
228-
echo " Description: ", credential.description
229-
echo " Windmill Variable: ", credential.windmillVariable
230-
echo " Created: ", credential.createdAt
231-
echo " ---"
265+
if outputFormat == OutputFormat.Text:
266+
echo "Credentials:"
267+
for credential in response.data:
268+
echo " ID: ", credential.id
269+
echo " Name: ", credential.name
270+
echo " Type: ", credential.credentialType
271+
echo " Description: ", credential.description
272+
echo " windmillVariable: ", credential.windmillVariable
273+
echo " Created: ", credential.createdAt
274+
echo " ---"
275+
else:
276+
formatOutput(response.data, outputFormat)
232277
else:
233278
writeError(response.error)
234279

@@ -244,16 +289,18 @@ proc handleCredentialCommand(client: GodonClient, command: string, args: seq[str
244289

245290
if not fileExists(file):
246291
writeError("File not found: " & file)
247-
248-
echo "Creating credential from file: ", file
292+
249293
let content = readFile(file)
250294
let response = client.createCredentialFromYaml(content)
251295
if response.success:
252-
echo "Credential created successfully:"
253-
echo " ID: ", response.data.id
254-
echo " Name: ", response.data.name
255-
echo " Type: ", response.data.credentialType
256-
echo " Windmill Variable: ", response.data.windmillVariable
296+
if outputFormat == OutputFormat.Text:
297+
echo "Credential created successfully:"
298+
echo " ID: ", response.data.id
299+
echo " Name: ", response.data.name
300+
echo " Type: ", response.data.credentialType
301+
echo " windmillVariable: ", response.data.windmillVariable
302+
else:
303+
formatOutput(response.data, outputFormat)
257304
else:
258305
writeError(response.error)
259306

@@ -266,20 +313,22 @@ proc handleCredentialCommand(client: GodonClient, command: string, args: seq[str
266313

267314
if id.len == 0:
268315
writeError("credential show requires --id <id>")
269-
270-
echo "Getting credential details for ID: ", id
316+
271317
let response = client.getCredential(id)
272318
if response.success:
273-
echo "Credential Details:"
274-
echo " ID: ", response.data.id
275-
echo " Name: ", response.data.name
276-
echo " Type: ", response.data.credentialType
277-
echo " Description: ", response.data.description
278-
echo " Windmill Variable: ", response.data.windmillVariable
279-
echo " Created: ", response.data.createdAt
280-
echo " Last Used: ", response.data.lastUsedAt
281-
echo " Content:"
282-
echo " ", response.data.content # Show actual credential content
319+
if outputFormat == OutputFormat.Text:
320+
echo "Credential Details:"
321+
echo " ID: ", response.data.id
322+
echo " Name: ", response.data.name
323+
echo " Type: ", response.data.credentialType
324+
echo " Description: ", response.data.description
325+
echo " windmillVariable: ", response.data.windmillVariable
326+
echo " Created: ", response.data.createdAt
327+
echo " Last Used: ", response.data.lastUsedAt
328+
echo " Content:"
329+
echo " ", response.data.content
330+
else:
331+
formatOutput(response.data, outputFormat)
283332
else:
284333
writeError(response.error)
285334

@@ -292,33 +341,33 @@ proc handleCredentialCommand(client: GodonClient, command: string, args: seq[str
292341

293342
if id.len == 0:
294343
writeError("credential delete requires --id <id>")
295-
296-
echo "Deleting credential with ID: ", id
344+
297345
let response = client.deleteCredential(id)
298346
if response.success:
299-
echo "Credential deleted successfully"
300-
if response.data != nil:
301-
echo "Response: ", pretty(response.data)
347+
if outputFormat == OutputFormat.Text:
348+
echo "Credential deleted successfully: ", id
349+
else:
350+
formatOutput(response.data, outputFormat)
302351
else:
303352
writeError(response.error)
304353

305354
else:
306355
writeError("Unknown credential command: " & subCommand)
307356

308-
let (command, hostname, port, apiVersion, insecure, args) = parseArgs()
357+
let (command, hostname, port, apiVersion, insecure, outputFormat, args) = parseArgs()
309358

310359
let godonClient = newGodonClient(hostname, port, apiVersion, insecure)
311360

312361
case command:
313362
of "breeder":
314363
if args.len == 0:
315364
writeError("breeder command requires a subcommand (list, create, show, update, purge)")
316-
handleBreederCommand(godonClient, command, args)
365+
handleBreederCommand(godonClient, command, args, outputFormat)
317366

318367
of "credential":
319368
if args.len == 0:
320369
writeError("credential command requires a subcommand (list, create, show, delete)")
321-
handleCredentialCommand(godonClient, command, args)
370+
handleCredentialCommand(godonClient, command, args, outputFormat)
322371

323372
else:
324373
writeError("Unknown command: " & command)

0 commit comments

Comments
 (0)