Fossil SCM

Started adding /json/timeline support, but this is gonna be a doozie. Breaking it down into separate calls for ci/wiki/ticket, e.g. /json/timeline/ci because the structures will be different for each.

stephan 2011-09-20 22:42 UTC json
Commit eff3f7d92960b0a0af2599bf19bd238afcd4d3b8
2 files changed +136 -5 +12 -12
+136 -5
--- src/json.c
+++ src/json.c
@@ -1289,30 +1289,50 @@
12891289
/* Last entry MUST have a NULL name. */
12901290
{NULL,NULL,0}
12911291
};
12921292
12931293
/*
1294
-** Implements the /json/wiki family of pages/commands. Far from
1295
-** complete.
1294
+** A page/command dispatch helper for fossil_json_f() implementations.
1295
+** depth should be the depth parameter passed to the fossil_json_f().
1296
+** pages must be an array of JsonPageDef commands which we can
1297
+** dispatch. The final item in the array MUST have a NULL name
1298
+** element.
1299
+**
1300
+** This function takes json_comand_arg(1+depth) and searches pages
1301
+** for a matching name. If found then that page's func() is called
1302
+** to fetch the payload, which is returned to the caller.
12961303
**
1304
+** On error, g.json.resultCode is set to one of the FossilJsonCodes
1305
+** values.
12971306
*/
1298
-static cson_value * json_page_wiki(unsigned int depth){
1307
+static cson_value * json_page_dispatch_helper(unsigned int depth,
1308
+ JsonPageDef const * pages){
12991309
JsonPageDef const * def;
13001310
char const * cmd = json_command_arg(1+depth);
1311
+ assert( NULL != pages );
13011312
if( ! cmd ){
13021313
g.json.resultCode = FSL_JSON_E_MISSING_ARGS;
13031314
return NULL;
13041315
}
1305
- def = json_handler_for_name( cmd, &JsonPageDefs_Wiki[0] );
1316
+ def = json_handler_for_name( cmd, pages );
13061317
if(!def){
13071318
g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND;
13081319
return NULL;
13091320
}
13101321
else{
13111322
return (*def->func)(1+depth);
13121323
}
13131324
}
1325
+
1326
+/*
1327
+** Implements the /json/wiki family of pages/commands. Far from
1328
+** complete.
1329
+**
1330
+*/
1331
+static cson_value * json_page_wiki(unsigned int depth){
1332
+ return json_page_dispatch_helper(depth,&JsonPageDefs_Wiki[0]);
1333
+}
13141334
13151335
/*
13161336
** INTERIM implementation of /json/wiki/list
13171337
*/
13181338
static cson_value * json_wiki_list(unsigned int depth){
@@ -1341,10 +1361,121 @@
13411361
listV = NULL;
13421362
end:
13431363
db_finalize(&q);
13441364
return listV;
13451365
}
1366
+
1367
+
1368
+static cson_value * json_timeline_ci(unsigned int depth);
1369
+/*
1370
+** Mapping of /json/timeline/XXX commands/paths to callbacks.
1371
+*/
1372
+static const JsonPageDef JsonPageDefs_Timeline[] = {
1373
+{"ci", json_timeline_ci, 0},
1374
+/* Last entry MUST have a NULL name. */
1375
+{NULL,NULL,0}
1376
+};
1377
+
1378
+/*
1379
+** Implements the /json/timeline family of pages/commands. Far from
1380
+** complete.
1381
+**
1382
+*/
1383
+static cson_value * json_page_timeline(unsigned int depth){
1384
+ return json_page_dispatch_helper(depth,&JsonPageDefs_Timeline[0]);
1385
+}
1386
+
1387
+static int json_getenv_int(char const * pKey, int dflt ){
1388
+ cson_value const * v = json_getenv(pKey);
1389
+ if(!v){
1390
+ return dflt;
1391
+ }else if( cson_value_is_number(v) ){
1392
+ return (int)cson_value_get_integer(v);
1393
+ }else if( cson_value_is_string(v) ){
1394
+ char const * sv = cson_string_cstr(cson_value_get_string(v));
1395
+ assert( (NULL!=sv) && "This is quite unexpected." );
1396
+ return sv ? atoi(sv) : dflt;
1397
+ }else if( cson_value_is_bool(v) ){
1398
+ return cson_value_get_bool(v) ? 1 : 0;
1399
+ }else{
1400
+ /* we should arguably treat JSON null as 0. */
1401
+ return dflt;
1402
+ }
1403
+}
1404
+
1405
+/*
1406
+** /json/timeline/ci
1407
+**
1408
+** Far from complete.
1409
+*/
1410
+static cson_value * json_timeline_ci(unsigned int depth){
1411
+ cson_value * payV = NULL;
1412
+ cson_object * pay = NULL;
1413
+ cson_value * tmp = NULL;
1414
+ int limit = json_getenv_int("n",10);
1415
+ Stmt q;
1416
+ Blob sql = empty_blob;
1417
+ if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){
1418
+ g.json.resultCode = FSL_JSON_E_DENIED;
1419
+ return NULL;
1420
+ }
1421
+ if( limit < 0 ) limit = 10;
1422
+ timeline_temp_table();
1423
+
1424
+ blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
1425
+ blob_append(&sql, timeline_query_for_www(), -1);
1426
+ blob_append(&sql, "AND event.type IN('ci') ", -1);
1427
+ blob_append(&sql, "ORDER BY mtime DESC ", -1);
1428
+ if(limit){
1429
+ blob_appendf(&sql,"LIMIT %d ",limit);
1430
+ }
1431
+ db_multi_exec(blob_buffer(&sql));
1432
+ payV = cson_value_new_object();
1433
+ pay = cson_value_get_object(payV);
1434
+#define SET(K) cson_object_set(pay,K,tmp)
1435
+
1436
+#if 1
1437
+ /* only for testing! */
1438
+ tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
1439
+ SET("timelineSql");
1440
+#endif
1441
+
1442
+ blob_reset(&sql);
1443
+ blob_append(&sql, "SELECT rid AS rid,"
1444
+ " uuid AS uuid,"
1445
+ " timestamp AS timestamp,"
1446
+ " comment AS comment, "
1447
+ " user AS user,"
1448
+ " isleaf AS isLeaf,"
1449
+ " bgcolor AS bgColor,"
1450
+ " etype AS eventType,"
1451
+ " taglist AS tagList," /*FIXME: split this into
1452
+ a JSON array*/
1453
+ " tagid AS tagId,"
1454
+ " short AS shortText"
1455
+ /* ignoring sortby field */
1456
+ " FROM timeline",-1);
1457
+ db_prepare(&q,blob_buffer(&sql));
1458
+ tmp = NULL;
1459
+ cson_sqlite3_stmt_to_json(q.pStmt, &tmp, 1);
1460
+ db_finalize(&q);
1461
+ if(tmp){
1462
+ cson_value * rows = cson_object_take(cson_value_get_object(tmp),"rows");
1463
+ assert(NULL != rows);
1464
+ cson_value_free(tmp);
1465
+ tmp = rows;
1466
+ }
1467
+ SET("timeline");
1468
+#undef SET
1469
+ goto ok;
1470
+ error:
1471
+ cson_value_free(payV);
1472
+ payV = NULL;
1473
+ ok:
1474
+ return payV;
1475
+}
1476
+
13461477
13471478
/*
13481479
** Mapping of names to JSON pages/commands. Each name is a subpath of
13491480
** /json (in CGI mode) or a subcommand of the json command in CLI mode
13501481
*/
@@ -1358,11 +1489,11 @@
13581489
{"login",json_page_login,1},
13591490
{"logout",json_page_logout,1},
13601491
{"stat",json_page_stat,0},
13611492
{"tag", json_page_nyi,0},
13621493
{"ticket", json_page_nyi,0},
1363
-{"timeline", json_page_nyi,0},
1494
+{"timeline", json_page_timeline,0},
13641495
{"user", json_page_nyi,0},
13651496
{"version",json_page_version,0},
13661497
{"wiki",json_page_wiki,0},
13671498
/* Last entry MUST have a NULL name. */
13681499
{NULL,NULL,0}
13691500
--- src/json.c
+++ src/json.c
@@ -1289,30 +1289,50 @@
1289 /* Last entry MUST have a NULL name. */
1290 {NULL,NULL,0}
1291 };
1292
1293 /*
1294 ** Implements the /json/wiki family of pages/commands. Far from
1295 ** complete.
 
 
 
 
 
 
 
1296 **
 
 
1297 */
1298 static cson_value * json_page_wiki(unsigned int depth){
 
1299 JsonPageDef const * def;
1300 char const * cmd = json_command_arg(1+depth);
 
1301 if( ! cmd ){
1302 g.json.resultCode = FSL_JSON_E_MISSING_ARGS;
1303 return NULL;
1304 }
1305 def = json_handler_for_name( cmd, &JsonPageDefs_Wiki[0] );
1306 if(!def){
1307 g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND;
1308 return NULL;
1309 }
1310 else{
1311 return (*def->func)(1+depth);
1312 }
1313 }
 
 
 
 
 
 
 
 
 
1314
1315 /*
1316 ** INTERIM implementation of /json/wiki/list
1317 */
1318 static cson_value * json_wiki_list(unsigned int depth){
@@ -1341,10 +1361,121 @@
1341 listV = NULL;
1342 end:
1343 db_finalize(&q);
1344 return listV;
1345 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1346
1347 /*
1348 ** Mapping of names to JSON pages/commands. Each name is a subpath of
1349 ** /json (in CGI mode) or a subcommand of the json command in CLI mode
1350 */
@@ -1358,11 +1489,11 @@
1358 {"login",json_page_login,1},
1359 {"logout",json_page_logout,1},
1360 {"stat",json_page_stat,0},
1361 {"tag", json_page_nyi,0},
1362 {"ticket", json_page_nyi,0},
1363 {"timeline", json_page_nyi,0},
1364 {"user", json_page_nyi,0},
1365 {"version",json_page_version,0},
1366 {"wiki",json_page_wiki,0},
1367 /* Last entry MUST have a NULL name. */
1368 {NULL,NULL,0}
1369
--- src/json.c
+++ src/json.c
@@ -1289,30 +1289,50 @@
1289 /* Last entry MUST have a NULL name. */
1290 {NULL,NULL,0}
1291 };
1292
1293 /*
1294 ** A page/command dispatch helper for fossil_json_f() implementations.
1295 ** depth should be the depth parameter passed to the fossil_json_f().
1296 ** pages must be an array of JsonPageDef commands which we can
1297 ** dispatch. The final item in the array MUST have a NULL name
1298 ** element.
1299 **
1300 ** This function takes json_comand_arg(1+depth) and searches pages
1301 ** for a matching name. If found then that page's func() is called
1302 ** to fetch the payload, which is returned to the caller.
1303 **
1304 ** On error, g.json.resultCode is set to one of the FossilJsonCodes
1305 ** values.
1306 */
1307 static cson_value * json_page_dispatch_helper(unsigned int depth,
1308 JsonPageDef const * pages){
1309 JsonPageDef const * def;
1310 char const * cmd = json_command_arg(1+depth);
1311 assert( NULL != pages );
1312 if( ! cmd ){
1313 g.json.resultCode = FSL_JSON_E_MISSING_ARGS;
1314 return NULL;
1315 }
1316 def = json_handler_for_name( cmd, pages );
1317 if(!def){
1318 g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND;
1319 return NULL;
1320 }
1321 else{
1322 return (*def->func)(1+depth);
1323 }
1324 }
1325
1326 /*
1327 ** Implements the /json/wiki family of pages/commands. Far from
1328 ** complete.
1329 **
1330 */
1331 static cson_value * json_page_wiki(unsigned int depth){
1332 return json_page_dispatch_helper(depth,&JsonPageDefs_Wiki[0]);
1333 }
1334
1335 /*
1336 ** INTERIM implementation of /json/wiki/list
1337 */
1338 static cson_value * json_wiki_list(unsigned int depth){
@@ -1341,10 +1361,121 @@
1361 listV = NULL;
1362 end:
1363 db_finalize(&q);
1364 return listV;
1365 }
1366
1367
1368 static cson_value * json_timeline_ci(unsigned int depth);
1369 /*
1370 ** Mapping of /json/timeline/XXX commands/paths to callbacks.
1371 */
1372 static const JsonPageDef JsonPageDefs_Timeline[] = {
1373 {"ci", json_timeline_ci, 0},
1374 /* Last entry MUST have a NULL name. */
1375 {NULL,NULL,0}
1376 };
1377
1378 /*
1379 ** Implements the /json/timeline family of pages/commands. Far from
1380 ** complete.
1381 **
1382 */
1383 static cson_value * json_page_timeline(unsigned int depth){
1384 return json_page_dispatch_helper(depth,&JsonPageDefs_Timeline[0]);
1385 }
1386
1387 static int json_getenv_int(char const * pKey, int dflt ){
1388 cson_value const * v = json_getenv(pKey);
1389 if(!v){
1390 return dflt;
1391 }else if( cson_value_is_number(v) ){
1392 return (int)cson_value_get_integer(v);
1393 }else if( cson_value_is_string(v) ){
1394 char const * sv = cson_string_cstr(cson_value_get_string(v));
1395 assert( (NULL!=sv) && "This is quite unexpected." );
1396 return sv ? atoi(sv) : dflt;
1397 }else if( cson_value_is_bool(v) ){
1398 return cson_value_get_bool(v) ? 1 : 0;
1399 }else{
1400 /* we should arguably treat JSON null as 0. */
1401 return dflt;
1402 }
1403 }
1404
1405 /*
1406 ** /json/timeline/ci
1407 **
1408 ** Far from complete.
1409 */
1410 static cson_value * json_timeline_ci(unsigned int depth){
1411 cson_value * payV = NULL;
1412 cson_object * pay = NULL;
1413 cson_value * tmp = NULL;
1414 int limit = json_getenv_int("n",10);
1415 Stmt q;
1416 Blob sql = empty_blob;
1417 if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){
1418 g.json.resultCode = FSL_JSON_E_DENIED;
1419 return NULL;
1420 }
1421 if( limit < 0 ) limit = 10;
1422 timeline_temp_table();
1423
1424 blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
1425 blob_append(&sql, timeline_query_for_www(), -1);
1426 blob_append(&sql, "AND event.type IN('ci') ", -1);
1427 blob_append(&sql, "ORDER BY mtime DESC ", -1);
1428 if(limit){
1429 blob_appendf(&sql,"LIMIT %d ",limit);
1430 }
1431 db_multi_exec(blob_buffer(&sql));
1432 payV = cson_value_new_object();
1433 pay = cson_value_get_object(payV);
1434 #define SET(K) cson_object_set(pay,K,tmp)
1435
1436 #if 1
1437 /* only for testing! */
1438 tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
1439 SET("timelineSql");
1440 #endif
1441
1442 blob_reset(&sql);
1443 blob_append(&sql, "SELECT rid AS rid,"
1444 " uuid AS uuid,"
1445 " timestamp AS timestamp,"
1446 " comment AS comment, "
1447 " user AS user,"
1448 " isleaf AS isLeaf,"
1449 " bgcolor AS bgColor,"
1450 " etype AS eventType,"
1451 " taglist AS tagList," /*FIXME: split this into
1452 a JSON array*/
1453 " tagid AS tagId,"
1454 " short AS shortText"
1455 /* ignoring sortby field */
1456 " FROM timeline",-1);
1457 db_prepare(&q,blob_buffer(&sql));
1458 tmp = NULL;
1459 cson_sqlite3_stmt_to_json(q.pStmt, &tmp, 1);
1460 db_finalize(&q);
1461 if(tmp){
1462 cson_value * rows = cson_object_take(cson_value_get_object(tmp),"rows");
1463 assert(NULL != rows);
1464 cson_value_free(tmp);
1465 tmp = rows;
1466 }
1467 SET("timeline");
1468 #undef SET
1469 goto ok;
1470 error:
1471 cson_value_free(payV);
1472 payV = NULL;
1473 ok:
1474 return payV;
1475 }
1476
1477
1478 /*
1479 ** Mapping of names to JSON pages/commands. Each name is a subpath of
1480 ** /json (in CGI mode) or a subcommand of the json command in CLI mode
1481 */
@@ -1358,11 +1489,11 @@
1489 {"login",json_page_login,1},
1490 {"logout",json_page_logout,1},
1491 {"stat",json_page_stat,0},
1492 {"tag", json_page_nyi,0},
1493 {"ticket", json_page_nyi,0},
1494 {"timeline", json_page_timeline,0},
1495 {"user", json_page_nyi,0},
1496 {"version",json_page_version,0},
1497 {"wiki",json_page_wiki,0},
1498 /* Last entry MUST have a NULL name. */
1499 {NULL,NULL,0}
1500
+12 -12
--- src/timeline.c
+++ src/timeline.c
@@ -738,11 +738,11 @@
738738
}
739739
740740
/*
741741
** Create a temporary table suitable for storing timeline data.
742742
*/
743
-static void timeline_temp_table(void){
743
+void timeline_temp_table(void){
744744
static const char zSql[] =
745745
@ CREATE TEMP TABLE IF NOT EXISTS timeline(
746746
@ rid INTEGER PRIMARY KEY,
747747
@ uuid TEXT,
748748
@ timestamp TEXT,
@@ -766,24 +766,24 @@
766766
*/
767767
const char *timeline_query_for_www(void){
768768
static char *zBase = 0;
769769
static const char zBaseSql[] =
770770
@ SELECT
771
- @ blob.rid,
772
- @ uuid,
771
+ @ blob.rid AS blobRid,
772
+ @ uuid AS uuid,
773773
@ datetime(event.mtime,'localtime') AS timestamp,
774
- @ coalesce(ecomment, comment),
775
- @ coalesce(euser, user),
776
- @ blob.rid IN leaf,
777
- @ bgcolor,
778
- @ event.type,
774
+ @ coalesce(ecomment, comment) AS comment,
775
+ @ coalesce(euser, user) AS user,
776
+ @ blob.rid IN leaf AS leaf,
777
+ @ bgcolor AS bgColor,
778
+ @ event.type AS eventType,
779779
@ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref
780780
@ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
781
- @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0),
782
- @ tagid,
783
- @ brief,
784
- @ event.mtime
781
+ @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0) AS tags,
782
+ @ tagid AS tagid,
783
+ @ brief AS brief,
784
+ @ event.mtime AS mtime
785785
@ FROM event JOIN blob
786786
@ WHERE blob.rid=event.objid
787787
;
788788
if( zBase==0 ){
789789
zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH);
790790
--- src/timeline.c
+++ src/timeline.c
@@ -738,11 +738,11 @@
738 }
739
740 /*
741 ** Create a temporary table suitable for storing timeline data.
742 */
743 static void timeline_temp_table(void){
744 static const char zSql[] =
745 @ CREATE TEMP TABLE IF NOT EXISTS timeline(
746 @ rid INTEGER PRIMARY KEY,
747 @ uuid TEXT,
748 @ timestamp TEXT,
@@ -766,24 +766,24 @@
766 */
767 const char *timeline_query_for_www(void){
768 static char *zBase = 0;
769 static const char zBaseSql[] =
770 @ SELECT
771 @ blob.rid,
772 @ uuid,
773 @ datetime(event.mtime,'localtime') AS timestamp,
774 @ coalesce(ecomment, comment),
775 @ coalesce(euser, user),
776 @ blob.rid IN leaf,
777 @ bgcolor,
778 @ event.type,
779 @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref
780 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
781 @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0),
782 @ tagid,
783 @ brief,
784 @ event.mtime
785 @ FROM event JOIN blob
786 @ WHERE blob.rid=event.objid
787 ;
788 if( zBase==0 ){
789 zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH);
790
--- src/timeline.c
+++ src/timeline.c
@@ -738,11 +738,11 @@
738 }
739
740 /*
741 ** Create a temporary table suitable for storing timeline data.
742 */
743 void timeline_temp_table(void){
744 static const char zSql[] =
745 @ CREATE TEMP TABLE IF NOT EXISTS timeline(
746 @ rid INTEGER PRIMARY KEY,
747 @ uuid TEXT,
748 @ timestamp TEXT,
@@ -766,24 +766,24 @@
766 */
767 const char *timeline_query_for_www(void){
768 static char *zBase = 0;
769 static const char zBaseSql[] =
770 @ SELECT
771 @ blob.rid AS blobRid,
772 @ uuid AS uuid,
773 @ datetime(event.mtime,'localtime') AS timestamp,
774 @ coalesce(ecomment, comment) AS comment,
775 @ coalesce(euser, user) AS user,
776 @ blob.rid IN leaf AS leaf,
777 @ bgcolor AS bgColor,
778 @ event.type AS eventType,
779 @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref
780 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
781 @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0) AS tags,
782 @ tagid AS tagid,
783 @ brief AS brief,
784 @ event.mtime AS mtime
785 @ FROM event JOIN blob
786 @ WHERE blob.rid=event.objid
787 ;
788 if( zBase==0 ){
789 zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH);
790

Keyboard Shortcuts

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