Skip to content

Commit 0e82916

Browse files
Merge pull request #289 from dropbox/json-schema-envelope
Add JSON schema envelope and normalize all output fields
2 parents df0841b + 786a30c commit 0e82916

27 files changed

Lines changed: 669 additions & 139 deletions

README.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,13 @@ JSON error responses use stable `error.code` values:
192192
| `unknown_flag` | Cobra could not resolve a flag. |
193193
| `command_failed` | Fallback for failures without a more specific stable code. |
194194

195-
Successful JSON responses for migrated commands return an `input` object, a `results` array, and a `warnings` array. Result payloads are command-specific. For commands such as `mkdir`, each result reports what happened to the requested path:
195+
Successful JSON responses for migrated commands return `ok: true`, `schema_version: "1"`, `command`, an `input` object, a `results` array, and a `warnings` array. Result payloads are command-specific. Public top-level schemas live under [docs/json-schema/v1](docs/json-schema/v1/). If a multi-target or recursive command fails after some side effects have already happened, dbxcli returns a JSON error envelope and does not include partial success results. For commands such as `mkdir`, each result reports what happened to the requested path:
196196

197197
```json
198198
{
199+
"ok": true,
200+
"schema_version": "1",
201+
"command": "mkdir",
199202
"input": {
200203
"path": "/new-folder",
201204
"parents": false
@@ -224,9 +227,14 @@ For `cp` and `mv`, each result input object uses `from_path` and `to_path`:
224227

225228
```json
226229
{
230+
"ok": true,
231+
"schema_version": "1",
232+
"command": "cp",
227233
"input": {},
228234
"results": [
229235
{
236+
"status": "copied",
237+
"kind": "file",
230238
"input": {
231239
"from_path": "/Reports/old.pdf",
232240
"to_path": "/Reports/copy.pdf"
@@ -249,9 +257,14 @@ For commands such as `rm`, `input` uses command-specific path and flag fields:
249257

250258
```json
251259
{
260+
"ok": true,
261+
"schema_version": "1",
262+
"command": "rm",
252263
"input": {},
253264
"results": [
254265
{
266+
"status": "deleted",
267+
"kind": "file",
255268
"input": {
256269
"path": "/old-file.txt",
257270
"permanent": false,
@@ -276,6 +289,9 @@ For commands such as `rm`, `input` uses command-specific path and flag fields:
276289

277290
```json
278291
{
292+
"ok": true,
293+
"schema_version": "1",
294+
"command": "put",
279295
"input": {
280296
"source": "README.md",
281297
"target": "/README.md",
@@ -311,6 +327,9 @@ Entry-list commands such as `ls`, `search`, and `revs` use the operation-style w
311327

312328
```json
313329
{
330+
"ok": true,
331+
"schema_version": "1",
332+
"command": "ls",
314333
"input": {
315334
"path": "/Reports",
316335
"recursive": false,
@@ -324,6 +343,7 @@ Entry-list commands such as `ls`, `search`, and `revs` use the operation-style w
324343
{
325344
"status": "listed",
326345
"kind": "file",
346+
"input": {},
327347
"result": {
328348
"type": "file",
329349
"path_display": "/Reports/q1.pdf",
@@ -342,9 +362,13 @@ Version, account, and usage commands use the operation-style wrapper with a sing
342362

343363
```json
344364
{
365+
"ok": true,
366+
"schema_version": "1",
367+
"command": "version",
345368
"input": {},
346369
"results": [
347370
{
371+
"status": "reported",
348372
"kind": "version",
349373
"input": {},
350374
"result": {
@@ -360,9 +384,13 @@ Version, account, and usage commands use the operation-style wrapper with a sing
360384

361385
```json
362386
{
387+
"ok": true,
388+
"schema_version": "1",
389+
"command": "account",
363390
"input": {},
364391
"results": [
365392
{
393+
"status": "found",
366394
"kind": "account",
367395
"input": {},
368396
"result": {
@@ -380,9 +408,13 @@ Version, account, and usage commands use the operation-style wrapper with a sing
380408

381409
```json
382410
{
411+
"ok": true,
412+
"schema_version": "1",
413+
"command": "du",
383414
"input": {},
384415
"results": [
385416
{
417+
"status": "reported",
386418
"kind": "space_usage",
387419
"input": {},
388420
"result": {
@@ -402,13 +434,17 @@ Shared-link commands use the same operation-style wrapper. `share-link create`,
402434

403435
```json
404436
{
437+
"ok": true,
438+
"schema_version": "1",
439+
"command": "share-link create",
405440
"input": {
406441
"path": "/Reports/old.pdf"
407442
},
408443
"results": [
409444
{
410445
"status": "created",
411446
"kind": "file",
447+
"input": {},
412448
"result": {
413449
"type": "file",
414450
"url": "https://www.dropbox.com/s/...",
@@ -429,11 +465,15 @@ The legacy `share list folder` command also supports operation-style JSON. It us
429465

430466
```json
431467
{
468+
"ok": true,
469+
"schema_version": "1",
470+
"command": "share list folder",
432471
"input": {},
433472
"results": [
434473
{
435474
"status": "listed",
436475
"kind": "shared_folder",
476+
"input": {},
437477
"result": {
438478
"type": "shared_folder",
439479
"name": "Reports",
@@ -454,11 +494,15 @@ Team commands use the same operation-style wrapper. `team info` returns a single
454494

455495
```json
456496
{
497+
"ok": true,
498+
"schema_version": "1",
499+
"command": "team list-members",
457500
"input": {},
458501
"results": [
459502
{
460503
"status": "listed",
461504
"kind": "team_member",
505+
"input": {},
462506
"result": {
463507
"type": "team_member",
464508
"team_member_id": "dbmid:...",
@@ -480,6 +524,8 @@ In JSON mode, command errors are written to stdout as JSON, including errors fro
480524
```json
481525
{
482526
"ok": false,
527+
"schema_version": "1",
528+
"command": "rm",
483529
"error": {
484530
"message": "path exists and is not a folder: /old-file.txt",
485531
"code": "path_conflict"

cmd/account.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ type jsonAccountTeam struct {
5858
MemberID string `json:"member_id,omitempty"`
5959
}
6060

61-
const accountKindAccount = "account"
61+
const (
62+
accountJSONStatusFound = "found"
63+
accountKindAccount = "account"
64+
)
6265

6366
// renderFullAccount prints the account details returned by GetCurrentAccount.
6467
func renderFullAccount(out io.Writer, fa *users.FullAccount) error {
@@ -116,7 +119,7 @@ func account(cmd *cobra.Command, args []string) error {
116119
input := accountInput{}
117120
return out.Render(func(w io.Writer) error {
118121
return renderFullAccount(w, res)
119-
}, newAccountOperationOutput(input, jsonFullAccount(res)))
122+
}, withJSONCommand(cmd, newAccountOperationOutput(input, jsonFullAccount(res))))
120123
}
121124

122125
// Otherwise look up an account with the provided ID
@@ -130,12 +133,12 @@ func account(cmd *cobra.Command, args []string) error {
130133
}
131134
return out.Render(func(w io.Writer) error {
132135
return renderBasicAccount(w, res)
133-
}, newAccountOperationOutput(input, jsonBasicAccount(res)))
136+
}, withJSONCommand(cmd, newAccountOperationOutput(input, jsonBasicAccount(res))))
134137
}
135138

136139
func newAccountOperationOutput(input accountInput, account jsonAccount) jsonOperationOutput {
137140
return newJSONOperationOutput(input, []jsonOperationResult{
138-
newJSONOperationResult("", accountKindAccount, input, account),
141+
newJSONOperationResult(accountJSONStatusFound, accountKindAccount, input, account),
139142
}, nil)
140143
}
141144

cmd/add-member.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func addMember(cmd *cobra.Command, args []string) (err error) {
4646
}
4747
return commandOutput(cmd).Render(func(w io.Writer) error {
4848
return renderTeamMemberAdd(w, res)
49-
}, teamMemberAddOperationOutput(input, res))
49+
}, withJSONCommand(cmd, teamMemberAddOperationOutput(input, res)))
5050
}
5151

5252
func renderTeamMemberAdd(out io.Writer, res *team.MembersAddLaunch) error {

cmd/cp.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func cp(cmd *cobra.Command, args []string) error {
7171
return fmt.Errorf("cp: %d error(s)", len(cpErrors))
7272
}
7373

74-
return renderJSONOperationOutput(cmd, nil, relocationOperationResults(results))
74+
return renderJSONOperationOutput(cmd, nil, relocationOperationResults(relocationJSONStatusCopied, results))
7575
}
7676

7777
// cpCmd represents the cp command

cmd/du.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ type duAllocation struct {
3939
UserWithinTeamSpaceLimitType string `json:"user_within_team_space_limit_type,omitempty"`
4040
}
4141

42-
const duKindSpaceUsage = "space_usage"
42+
const (
43+
duJSONStatusReported = "reported"
44+
duKindSpaceUsage = "space_usage"
45+
)
4346

4447
func du(cmd *cobra.Command, args []string) (err error) {
4548
dbx := usersNewFunc(config)
@@ -50,7 +53,7 @@ func du(cmd *cobra.Command, args []string) (err error) {
5053

5154
return commandOutput(cmd).Render(func(w io.Writer) error {
5255
return renderUsage(w, usage)
53-
}, newDuOperationOutput(usage))
56+
}, withJSONCommand(cmd, newDuOperationOutput(usage)))
5457
}
5558

5659
func renderUsage(out io.Writer, usage *users.SpaceUsage) error {
@@ -81,7 +84,7 @@ func newDuOutput(usage *users.SpaceUsage) duOutput {
8184
func newDuOperationOutput(usage *users.SpaceUsage) jsonOperationOutput {
8285
input := duInput{}
8386
return newJSONOperationOutput(input, []jsonOperationResult{
84-
newJSONOperationResult("", duKindSpaceUsage, input, newDuOutput(usage)),
87+
newJSONOperationResult(duJSONStatusReported, duKindSpaceUsage, input, newDuOutput(usage)),
8588
}, nil)
8689
}
8790

cmd/info.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func info(cmd *cobra.Command, args []string) (err error) {
3232

3333
return commandOutput(cmd).Render(func(w io.Writer) error {
3434
return renderTeamInfo(w, res)
35-
}, teamInfoOperationOutput(res))
35+
}, withJSONCommand(cmd, teamInfoOperationOutput(res)))
3636
}
3737

3838
func renderTeamInfo(out io.Writer, res *team.TeamGetInfoResult) error {

0 commit comments

Comments
 (0)