Fossil SCM

"Dispatching" json subcommands now produce more useful error messages when no subcommand is specified.

stephan 2011-10-08 12:57 UTC json-multitag-test
Commit a8af09fc166f8a34ff2d327512d84b1b7eaad191
+1 -1
--- ajax/index.html
+++ ajax/index.html
@@ -223,10 +223,11 @@
223223
<input type='button' value='HAI JSONP' onclick='TheApp.cgi.sendCommand("/json/HAI",undefined,{jsonp:"myJsonPCallback"});' />
224224
<input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' />
225225
<input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat?full=0")' />
226226
<input type='button' value='whoami' onclick='TheApp.cgi.whoami()' />
227227
<input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' />
228
+<input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
228229
229230
<br/>
230231
231232
<input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' />
232233
<input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci?files=true")' />
@@ -239,11 +240,10 @@
239240
240241
<br/>
241242
242243
<input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
243244
<input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
244
-<input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
245245
<input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag/list?includeTickets=false&raw=false")' />
246246
<input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/json/tag/list/json?raw=false")' />
247247
<input type='button' value='tag/add'
248248
onclick='TheApp.cgi.sendCommand("/json/tag/add",{name:"json-add-tag-test",checkin:"json",value:"tag test",propagate:false,raw:false})' />
249249
<input type='button' value='tag/cancel'
250250
--- ajax/index.html
+++ ajax/index.html
@@ -223,10 +223,11 @@
223 <input type='button' value='HAI JSONP' onclick='TheApp.cgi.sendCommand("/json/HAI",undefined,{jsonp:"myJsonPCallback"});' />
224 <input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' />
225 <input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat?full=0")' />
226 <input type='button' value='whoami' onclick='TheApp.cgi.whoami()' />
227 <input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' />
 
228
229 <br/>
230
231 <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' />
232 <input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci?files=true")' />
@@ -239,11 +240,10 @@
239
240 <br/>
241
242 <input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
243 <input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
244 <input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
245 <input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag/list?includeTickets=false&raw=false")' />
246 <input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/json/tag/list/json?raw=false")' />
247 <input type='button' value='tag/add'
248 onclick='TheApp.cgi.sendCommand("/json/tag/add",{name:"json-add-tag-test",checkin:"json",value:"tag test",propagate:false,raw:false})' />
249 <input type='button' value='tag/cancel'
250
--- ajax/index.html
+++ ajax/index.html
@@ -223,10 +223,11 @@
223 <input type='button' value='HAI JSONP' onclick='TheApp.cgi.sendCommand("/json/HAI",undefined,{jsonp:"myJsonPCallback"});' />
224 <input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' />
225 <input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat?full=0")' />
226 <input type='button' value='whoami' onclick='TheApp.cgi.whoami()' />
227 <input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' />
228 <input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
229
230 <br/>
231
232 <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' />
233 <input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci?files=true")' />
@@ -239,11 +240,10 @@
240
241 <br/>
242
243 <input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
244 <input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
 
245 <input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag/list?includeTickets=false&raw=false")' />
246 <input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/json/tag/list/json?raw=false")' />
247 <input type='button' value='tag/add'
248 onclick='TheApp.cgi.sendCommand("/json/tag/add",{name:"json-add-tag-test",checkin:"json",value:"tag test",propagate:false,raw:false})' />
249 <input type='button' value='tag/cancel'
250
+66 -7
--- src/json.c
+++ src/json.c
@@ -1320,16 +1320,21 @@
13201320
if(!part){
13211321
fossil_warning("Iterating further than expected in %s.",
13221322
__FILE__);
13231323
break;
13241324
}
1325
- blob_append(&path,part,-1);
1326
- if(i < (aLen-1)){
1327
- blob_append(&path,"/",1);
1328
- }
1325
+ blob_appendf(&path,"%s%s", (i>1 ? "/": ""), part);
13291326
}
1330
- rc = json_new_string(blob_buffer(&path));
1327
+ rc = json_new_string((blob_size(&path)>0)
1328
+ ? blob_buffer(&path)
1329
+ : "")
1330
+ /* reminder; we need an empty string instead of NULL
1331
+ in this case, to avoid what outwardly looks like
1332
+ (but is not) an allocation error in
1333
+ json_create_response().
1334
+ */
1335
+ ;
13311336
blob_reset(&path);
13321337
return rc;
13331338
}
13341339
}
13351340
@@ -1953,10 +1958,33 @@
19531958
static cson_value * json_user_get();
19541959
#if 0
19551960
static cson_value * json_user_create();
19561961
static cson_value * json_user_edit();
19571962
#endif
1963
+
1964
+/*
1965
+** Creates a comma-separated list of command names
1966
+** taken from zPages. zPages must be an array of objects
1967
+** whose final entry MUST have a NULL name value or results
1968
+** are undefined.
1969
+**
1970
+** The list is appended to pOut. The number of items (not bytes)
1971
+** appended are returned.
1972
+*/
1973
+static int json_pagedefs_to_string(JsonPageDef const * zPages,
1974
+ Blob * pOut){
1975
+ int i = 0;
1976
+ for( ; zPages->name; ++zPages, ++i ){
1977
+ if(g.isHTTP && zPages->runMode < 0) continue;
1978
+ else if(zPages->runMode > 0) continue;
1979
+ blob_appendf(pOut, zPages->name, -1);
1980
+ if((zPages+1)->name){
1981
+ blob_append(pOut, ", ",2);
1982
+ }
1983
+ }
1984
+ return i;
1985
+}
19581986
19591987
/*
19601988
** Mapping of /json/user/XXX commands/paths to callbacks.
19611989
*/
19621990
static const JsonPageDef JsonPageDefs_User[] = {
@@ -1972,11 +2000,16 @@
19722000
cson_value * json_page_dispatch_helper(JsonPageDef const * pages){
19732001
JsonPageDef const * def;
19742002
char const * cmd = json_command_arg(1+g.json.dispatchDepth);
19752003
assert( NULL != pages );
19762004
if( ! cmd ){
1977
- json_set_err(FSL_JSON_E_MISSING_ARGS, "No subcommand specified.");
2005
+ Blob cmdNames = empty_blob;
2006
+ json_pagedefs_to_string(pages, &cmdNames);
2007
+ json_set_err(FSL_JSON_E_MISSING_ARGS,
2008
+ "No subcommand specified. Try one of (%s).",
2009
+ blob_str(&cmdNames));
2010
+ blob_reset(&cmdNames);
19782011
return NULL;
19792012
}
19802013
def = json_handler_for_name( cmd, pages );
19812014
if(!def){
19822015
g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND;
@@ -2188,10 +2221,13 @@
21882221
cson_value * payload = NULL;
21892222
JsonPageDef const * pageDef = NULL;
21902223
BEGIN_TIMER;
21912224
json_mode_bootstrap();
21922225
cmd = json_command_arg(1);
2226
+ if(!cmd || !*cmd){
2227
+ goto usage;
2228
+ }
21932229
/*cgi_printf("{\"cmd\":\"%s\"}\n",cmd); return;*/
21942230
pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
21952231
if( ! pageDef ){
21962232
json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 0 );
21972233
return;
@@ -2207,10 +2243,24 @@
22072243
}else{
22082244
cson_value * root = json_create_response(rc, NULL, payload);
22092245
json_send_response(root);
22102246
cson_value_free(root);
22112247
}
2248
+
2249
+ return;
2250
+ usage:
2251
+ {
2252
+ Blob cmdNames = empty_blob;
2253
+ blob_init(&cmdNames,
2254
+ "No command (sub-path) specified. Try one of: ",
2255
+ -1);
2256
+ json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames);
2257
+ json_err(FSL_JSON_E_MISSING_ARGS,
2258
+ blob_str(&cmdNames), 0);
2259
+ blob_reset(&cmdNames);
2260
+ }
2261
+
22122262
}
22132263
22142264
/*
22152265
** This function dispatches json commands and is the CLI equivalent of
22162266
** json_page_top().
@@ -2290,13 +2340,22 @@
22902340
fossil_exit(1);
22912341
}
22922342
}
22932343
return;
22942344
usage:
2295
- usage("subcommand");
2345
+ {
2346
+ Blob cmdNames = empty_blob;
2347
+ blob_init(&cmdNames,
2348
+ "No subcommand specified. Try one of: ", -1);
2349
+ json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames);
2350
+ json_err(FSL_JSON_E_MISSING_ARGS,
2351
+ blob_str(&cmdNames), 1);
2352
+ blob_reset(&cmdNames);
2353
+ fossil_exit(1);
2354
+ }
22962355
}
22972356
22982357
#undef BITSET_BYTEFOR
22992358
#undef BITSET_SET
23002359
#undef BITSET_UNSET
23012360
#undef BITSET_GET
23022361
#undef BITSET_TOGGLE
23032362
--- src/json.c
+++ src/json.c
@@ -1320,16 +1320,21 @@
1320 if(!part){
1321 fossil_warning("Iterating further than expected in %s.",
1322 __FILE__);
1323 break;
1324 }
1325 blob_append(&path,part,-1);
1326 if(i < (aLen-1)){
1327 blob_append(&path,"/",1);
1328 }
1329 }
1330 rc = json_new_string(blob_buffer(&path));
 
 
 
 
 
 
 
 
1331 blob_reset(&path);
1332 return rc;
1333 }
1334 }
1335
@@ -1953,10 +1958,33 @@
1953 static cson_value * json_user_get();
1954 #if 0
1955 static cson_value * json_user_create();
1956 static cson_value * json_user_edit();
1957 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1958
1959 /*
1960 ** Mapping of /json/user/XXX commands/paths to callbacks.
1961 */
1962 static const JsonPageDef JsonPageDefs_User[] = {
@@ -1972,11 +2000,16 @@
1972 cson_value * json_page_dispatch_helper(JsonPageDef const * pages){
1973 JsonPageDef const * def;
1974 char const * cmd = json_command_arg(1+g.json.dispatchDepth);
1975 assert( NULL != pages );
1976 if( ! cmd ){
1977 json_set_err(FSL_JSON_E_MISSING_ARGS, "No subcommand specified.");
 
 
 
 
 
1978 return NULL;
1979 }
1980 def = json_handler_for_name( cmd, pages );
1981 if(!def){
1982 g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND;
@@ -2188,10 +2221,13 @@
2188 cson_value * payload = NULL;
2189 JsonPageDef const * pageDef = NULL;
2190 BEGIN_TIMER;
2191 json_mode_bootstrap();
2192 cmd = json_command_arg(1);
 
 
 
2193 /*cgi_printf("{\"cmd\":\"%s\"}\n",cmd); return;*/
2194 pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
2195 if( ! pageDef ){
2196 json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 0 );
2197 return;
@@ -2207,10 +2243,24 @@
2207 }else{
2208 cson_value * root = json_create_response(rc, NULL, payload);
2209 json_send_response(root);
2210 cson_value_free(root);
2211 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2212 }
2213
2214 /*
2215 ** This function dispatches json commands and is the CLI equivalent of
2216 ** json_page_top().
@@ -2290,13 +2340,22 @@
2290 fossil_exit(1);
2291 }
2292 }
2293 return;
2294 usage:
2295 usage("subcommand");
 
 
 
 
 
 
 
 
 
2296 }
2297
2298 #undef BITSET_BYTEFOR
2299 #undef BITSET_SET
2300 #undef BITSET_UNSET
2301 #undef BITSET_GET
2302 #undef BITSET_TOGGLE
2303
--- src/json.c
+++ src/json.c
@@ -1320,16 +1320,21 @@
1320 if(!part){
1321 fossil_warning("Iterating further than expected in %s.",
1322 __FILE__);
1323 break;
1324 }
1325 blob_appendf(&path,"%s%s", (i>1 ? "/": ""), part);
 
 
 
1326 }
1327 rc = json_new_string((blob_size(&path)>0)
1328 ? blob_buffer(&path)
1329 : "")
1330 /* reminder; we need an empty string instead of NULL
1331 in this case, to avoid what outwardly looks like
1332 (but is not) an allocation error in
1333 json_create_response().
1334 */
1335 ;
1336 blob_reset(&path);
1337 return rc;
1338 }
1339 }
1340
@@ -1953,10 +1958,33 @@
1958 static cson_value * json_user_get();
1959 #if 0
1960 static cson_value * json_user_create();
1961 static cson_value * json_user_edit();
1962 #endif
1963
1964 /*
1965 ** Creates a comma-separated list of command names
1966 ** taken from zPages. zPages must be an array of objects
1967 ** whose final entry MUST have a NULL name value or results
1968 ** are undefined.
1969 **
1970 ** The list is appended to pOut. The number of items (not bytes)
1971 ** appended are returned.
1972 */
1973 static int json_pagedefs_to_string(JsonPageDef const * zPages,
1974 Blob * pOut){
1975 int i = 0;
1976 for( ; zPages->name; ++zPages, ++i ){
1977 if(g.isHTTP && zPages->runMode < 0) continue;
1978 else if(zPages->runMode > 0) continue;
1979 blob_appendf(pOut, zPages->name, -1);
1980 if((zPages+1)->name){
1981 blob_append(pOut, ", ",2);
1982 }
1983 }
1984 return i;
1985 }
1986
1987 /*
1988 ** Mapping of /json/user/XXX commands/paths to callbacks.
1989 */
1990 static const JsonPageDef JsonPageDefs_User[] = {
@@ -1972,11 +2000,16 @@
2000 cson_value * json_page_dispatch_helper(JsonPageDef const * pages){
2001 JsonPageDef const * def;
2002 char const * cmd = json_command_arg(1+g.json.dispatchDepth);
2003 assert( NULL != pages );
2004 if( ! cmd ){
2005 Blob cmdNames = empty_blob;
2006 json_pagedefs_to_string(pages, &cmdNames);
2007 json_set_err(FSL_JSON_E_MISSING_ARGS,
2008 "No subcommand specified. Try one of (%s).",
2009 blob_str(&cmdNames));
2010 blob_reset(&cmdNames);
2011 return NULL;
2012 }
2013 def = json_handler_for_name( cmd, pages );
2014 if(!def){
2015 g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND;
@@ -2188,10 +2221,13 @@
2221 cson_value * payload = NULL;
2222 JsonPageDef const * pageDef = NULL;
2223 BEGIN_TIMER;
2224 json_mode_bootstrap();
2225 cmd = json_command_arg(1);
2226 if(!cmd || !*cmd){
2227 goto usage;
2228 }
2229 /*cgi_printf("{\"cmd\":\"%s\"}\n",cmd); return;*/
2230 pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
2231 if( ! pageDef ){
2232 json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 0 );
2233 return;
@@ -2207,10 +2243,24 @@
2243 }else{
2244 cson_value * root = json_create_response(rc, NULL, payload);
2245 json_send_response(root);
2246 cson_value_free(root);
2247 }
2248
2249 return;
2250 usage:
2251 {
2252 Blob cmdNames = empty_blob;
2253 blob_init(&cmdNames,
2254 "No command (sub-path) specified. Try one of: ",
2255 -1);
2256 json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames);
2257 json_err(FSL_JSON_E_MISSING_ARGS,
2258 blob_str(&cmdNames), 0);
2259 blob_reset(&cmdNames);
2260 }
2261
2262 }
2263
2264 /*
2265 ** This function dispatches json commands and is the CLI equivalent of
2266 ** json_page_top().
@@ -2290,13 +2340,22 @@
2340 fossil_exit(1);
2341 }
2342 }
2343 return;
2344 usage:
2345 {
2346 Blob cmdNames = empty_blob;
2347 blob_init(&cmdNames,
2348 "No subcommand specified. Try one of: ", -1);
2349 json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames);
2350 json_err(FSL_JSON_E_MISSING_ARGS,
2351 blob_str(&cmdNames), 1);
2352 blob_reset(&cmdNames);
2353 fossil_exit(1);
2354 }
2355 }
2356
2357 #undef BITSET_BYTEFOR
2358 #undef BITSET_SET
2359 #undef BITSET_UNSET
2360 #undef BITSET_GET
2361 #undef BITSET_TOGGLE
2362
--- src/json_timeline.c
+++ src/json_timeline.c
@@ -33,20 +33,15 @@
3333
/* the short forms are only enabled in CLI mode, to avoid
3434
that we end up with HTTP clients using 3 different names
3535
for the same requests.
3636
*/
3737
{"branch", json_timeline_branch, 0},
38
-{"c", json_timeline_ci, -1},
3938
{"checkin", json_timeline_ci, 0},
4039
{"ci", json_timeline_ci, -1},
41
-{"com", json_timeline_ci, -1},
42
-{"commit", json_timeline_ci, 0},
4340
{"t", json_timeline_ticket, -1},
44
-{"tkt", json_timeline_ticket, -1},
4541
{"ticket", json_timeline_ticket, 0},
4642
{"w", json_timeline_wiki, -1},
47
-{"wi", json_timeline_wiki, -1},
4843
{"wiki", json_timeline_wiki, 0},
4944
/* Last entry MUST have a NULL name. */
5045
{NULL,NULL,0}
5146
};
5247
5348
--- src/json_timeline.c
+++ src/json_timeline.c
@@ -33,20 +33,15 @@
33 /* the short forms are only enabled in CLI mode, to avoid
34 that we end up with HTTP clients using 3 different names
35 for the same requests.
36 */
37 {"branch", json_timeline_branch, 0},
38 {"c", json_timeline_ci, -1},
39 {"checkin", json_timeline_ci, 0},
40 {"ci", json_timeline_ci, -1},
41 {"com", json_timeline_ci, -1},
42 {"commit", json_timeline_ci, 0},
43 {"t", json_timeline_ticket, -1},
44 {"tkt", json_timeline_ticket, -1},
45 {"ticket", json_timeline_ticket, 0},
46 {"w", json_timeline_wiki, -1},
47 {"wi", json_timeline_wiki, -1},
48 {"wiki", json_timeline_wiki, 0},
49 /* Last entry MUST have a NULL name. */
50 {NULL,NULL,0}
51 };
52
53
--- src/json_timeline.c
+++ src/json_timeline.c
@@ -33,20 +33,15 @@
33 /* the short forms are only enabled in CLI mode, to avoid
34 that we end up with HTTP clients using 3 different names
35 for the same requests.
36 */
37 {"branch", json_timeline_branch, 0},
 
38 {"checkin", json_timeline_ci, 0},
39 {"ci", json_timeline_ci, -1},
 
 
40 {"t", json_timeline_ticket, -1},
 
41 {"ticket", json_timeline_ticket, 0},
42 {"w", json_timeline_wiki, -1},
 
43 {"wiki", json_timeline_wiki, 0},
44 /* Last entry MUST have a NULL name. */
45 {NULL,NULL,0}
46 };
47
48

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button