Skip to content

Commit 10e0e8c

Browse files
committed
cli: add --output option for formatted responses
Add --output option supporting text/json/yaml formats. Text mode shows human-readable messages (default), while json/yaml modes output raw structured data. Fix windmillVariable label and add UUID to delete confirmations.
1 parent 29c1e2f commit 10e0e8c

2 files changed

Lines changed: 123 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: 122 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
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,37 @@ 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
131+
echo yaml.dump(data)
99132

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

103136
case subCommand:
104137
of "list":
105-
echo "Listing breeders..."
106138
let response = client.listBreeders()
107139
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 " ---"
140+
if outputFormat == OutputFormat.Text:
141+
echo "Breeders:"
142+
for breeder in response.data:
143+
echo " ID: ", breeder.id
144+
echo " Name: ", breeder.name
145+
echo " Status: ", breeder.status
146+
echo " Created: ", breeder.createdAt
147+
echo " ---"
148+
else:
149+
formatOutput(response.data, outputFormat)
115150
else:
116151
writeError(response.error)
117152

@@ -133,14 +168,16 @@ proc handleBreederCommand(client: GodonClient, command: string, args: seq[string
133168
if not fileExists(file):
134169
writeError("File not found: " & file)
135170

136-
echo "Creating breeder '", name, "' from file: ", file
137171
let content = readFile(file)
138172
let response = client.createBreederFromYamlWithName(content, name)
139173
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
174+
if outputFormat == OutputFormat.Text:
175+
echo "Breeder created successfully:"
176+
echo " ID: ", response.data.id
177+
echo " Name: ", response.data.name
178+
echo " Status: ", response.data.status
179+
else:
180+
formatOutput(response.data, outputFormat)
144181
else:
145182
writeError(response.error)
146183

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

154191
if id.len == 0:
155192
writeError("breeder show requires --id <id>")
156-
157-
echo "Getting breeder details for ID: ", id
193+
158194
let response = client.getBreeder(id)
159195
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
196+
if outputFormat == OutputFormat.Text:
197+
echo "Breeder Details:"
198+
echo " ID: ", response.data.id
199+
echo " Name: ", response.data.name
200+
echo " Status: ", response.data.status
201+
echo " Config: ", pretty(response.data.config)
202+
echo " Created: ", response.data.createdAt
203+
else:
204+
formatOutput(response.data, outputFormat)
166205
else:
167206
writeError(response.error)
168207

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

179218
if not fileExists(file):
180219
writeError("File not found: " & file)
181-
182-
echo "Updating breeder from file: ", file
220+
183221
let content = readFile(file)
184222
let response = client.updateBreederFromYaml(content)
185223
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
224+
if outputFormat == OutputFormat.Text:
225+
echo "Breeder updated successfully:"
226+
echo " ID: ", response.data.id
227+
echo " Name: ", response.data.name
228+
echo " Status: ", response.data.status
229+
else:
230+
formatOutput(response.data, outputFormat)
190231
else:
191232
writeError(response.error)
192233

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

200241
if id.len == 0:
201242
writeError("breeder purge requires --id <id>")
202-
203-
echo "Deleting breeder with ID: ", id
243+
204244
let response = client.deleteBreeder(id)
205245
if response.success:
206-
echo "Breeder deleted successfully"
207-
if response.data != nil:
208-
echo "Response: ", pretty(response.data)
246+
if outputFormat == OutputFormat.Text:
247+
echo "Breeder deleted successfully: ", id
248+
else:
249+
formatOutput(response.data, outputFormat)
209250
else:
210251
writeError(response.error)
211252

212253
else:
213254
writeError("Unknown breeder command: " & subCommand)
214255

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

218259
case subCommand:
219260
of "list":
220-
echo "Listing credentials..."
221261
let response = client.listCredentials()
222262
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 " ---"
263+
if outputFormat == OutputFormat.Text:
264+
echo "Credentials:"
265+
for credential in response.data:
266+
echo " ID: ", credential.id
267+
echo " Name: ", credential.name
268+
echo " Type: ", credential.credentialType
269+
echo " Description: ", credential.description
270+
echo " windmillVariable: ", credential.windmillVariable
271+
echo " Created: ", credential.createdAt
272+
echo " ---"
273+
else:
274+
formatOutput(response.data, outputFormat)
232275
else:
233276
writeError(response.error)
234277

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

245288
if not fileExists(file):
246289
writeError("File not found: " & file)
247-
248-
echo "Creating credential from file: ", file
290+
249291
let content = readFile(file)
250292
let response = client.createCredentialFromYaml(content)
251293
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
294+
if outputFormat == OutputFormat.Text:
295+
echo "Credential created successfully:"
296+
echo " ID: ", response.data.id
297+
echo " Name: ", response.data.name
298+
echo " Type: ", response.data.credentialType
299+
echo " windmillVariable: ", response.data.windmillVariable
300+
else:
301+
formatOutput(response.data, outputFormat)
257302
else:
258303
writeError(response.error)
259304

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

267312
if id.len == 0:
268313
writeError("credential show requires --id <id>")
269-
270-
echo "Getting credential details for ID: ", id
314+
271315
let response = client.getCredential(id)
272316
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
317+
if outputFormat == OutputFormat.Text:
318+
echo "Credential Details:"
319+
echo " ID: ", response.data.id
320+
echo " Name: ", response.data.name
321+
echo " Type: ", response.data.credentialType
322+
echo " Description: ", response.data.description
323+
echo " windmillVariable: ", response.data.windmillVariable
324+
echo " Created: ", response.data.createdAt
325+
echo " Last Used: ", response.data.lastUsedAt
326+
echo " Content:"
327+
echo " ", response.data.content
328+
else:
329+
formatOutput(response.data, outputFormat)
283330
else:
284331
writeError(response.error)
285332

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

293340
if id.len == 0:
294341
writeError("credential delete requires --id <id>")
295-
296-
echo "Deleting credential with ID: ", id
342+
297343
let response = client.deleteCredential(id)
298344
if response.success:
299-
echo "Credential deleted successfully"
300-
if response.data != nil:
301-
echo "Response: ", pretty(response.data)
345+
if outputFormat == OutputFormat.Text:
346+
echo "Credential deleted successfully: ", id
347+
else:
348+
formatOutput(response.data, outputFormat)
302349
else:
303350
writeError(response.error)
304351

305352
else:
306353
writeError("Unknown credential command: " & subCommand)
307354

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

310357
let godonClient = newGodonClient(hostname, port, apiVersion, insecure)
311358

312359
case command:
313360
of "breeder":
314361
if args.len == 0:
315362
writeError("breeder command requires a subcommand (list, create, show, update, purge)")
316-
handleBreederCommand(godonClient, command, args)
363+
handleBreederCommand(godonClient, command, args, outputFormat)
317364

318365
of "credential":
319366
if args.len == 0:
320367
writeError("credential command requires a subcommand (list, create, show, delete)")
321-
handleCredentialCommand(godonClient, command, args)
368+
handleCredentialCommand(godonClient, command, args, outputFormat)
322369

323370
else:
324371
writeError("Unknown command: " & command)

0 commit comments

Comments
 (0)