80d78123e5886eec703fd9f5390ab5909135e790
hiram
  Sun Apr 28 15:02:37 2019 -0700
now listing tracks in nested JSON hierarchy refs #18869

diff --git src/hg/hubApi/list.c src/hg/hubApi/list.c
index b73affb..bf276a2 100644
--- src/hg/hubApi/list.c
+++ src/hg/hubApi/list.c
@@ -7,41 +7,30 @@
 /* Print array data for one row from hubPublic table, order here
  * must be same as was stated in the columnName header element
  * This code should be in hg/lib/hubPublic.c (which does not exist)
  */
 {
 int i = 0;
 jsonWriteObjectStart(jw, NULL);
 jsonWriteString(jw, columnNames[i++], el->hubUrl);
 jsonWriteString(jw, columnNames[i++], el->shortLabel);
 jsonWriteString(jw, columnNames[i++], el->longLabel);
 jsonWriteString(jw, columnNames[i++], el->registrationTime);
 jsonWriteNumber(jw, columnNames[i++], (long long)el->dbCount);
 jsonWriteString(jw, columnNames[i++], el->dbList);
 jsonWriteString(jw, columnNames[i++], el->descriptionUrl);
 jsonWriteObjectEnd(jw);
-#ifdef NOT
-jsonWriteListStart(jw, NULL);
-jsonWriteString(jw, NULL, el->hubUrl);
-jsonWriteString(jw, NULL, el->shortLabel);
-jsonWriteString(jw, NULL, el->longLabel);
-jsonWriteString(jw, NULL, el->registrationTime);
-jsonWriteNumber(jw, NULL, (long long)el->dbCount);
-jsonWriteString(jw, NULL, el->dbList);
-jsonWriteString(jw, NULL, el->descriptionUrl);
-jsonWriteListEnd(jw);
-#endif
 }
 
 static void jsonPublicHubs()
 /* output the hubPublic SQL table */
 {
 struct sqlConnection *conn = hConnectCentral();
 char *dataTime = sqlTableUpdate(conn, hubPublicTableName());
 time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
 replaceChar(dataTime, ' ', 'T');	/* ISO 8601 */
 struct hubPublic *el = hubPublicDbLoadAll();
 struct jsonWrite *jw = apiStartOutput();
 jsonWriteString(jw, "dataTime", dataTime);
 jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
 freeMem(dataTime);
 // redundant: jsonWriteString(jw, "tableName", hubPublicTableName());
@@ -73,48 +62,30 @@
 // redundant: jsonWriteString(jw, NULL, el->name);
 jsonWriteString(jw, columnNames[i++], el->description);
 jsonWriteString(jw, columnNames[i++], el->nibPath);
 jsonWriteString(jw, columnNames[i++], el->organism);
 jsonWriteString(jw, columnNames[i++], el->defaultPos);
 jsonWriteNumber(jw, columnNames[i++], (long long)el->active);
 jsonWriteNumber(jw, columnNames[i++], (long long)el->orderKey);
 jsonWriteString(jw, columnNames[i++], el->genome);
 jsonWriteString(jw, columnNames[i++], el->scientificName);
 jsonWriteString(jw, columnNames[i++], el->htmlPath);
 jsonWriteNumber(jw, columnNames[i++], (long long)el->hgNearOk);
 jsonWriteNumber(jw, columnNames[i++], (long long)el->hgPbOk);
 jsonWriteString(jw, columnNames[i++], el->sourceName);
 jsonWriteNumber(jw, columnNames[i++], (long long)el->taxId);
 jsonWriteObjectEnd(jw);
-#ifdef NOT
-jsonWriteListStart(jw, NULL);
-jsonWriteString(jw, NULL, el->name);
-jsonWriteString(jw, NULL, el->description);
-jsonWriteString(jw, NULL, el->nibPath);
-jsonWriteString(jw, NULL, el->organism);
-jsonWriteString(jw, NULL, el->defaultPos);
-jsonWriteNumber(jw, NULL, (long long)el->active);
-jsonWriteNumber(jw, NULL, (long long)el->orderKey);
-jsonWriteString(jw, NULL, el->genome);
-jsonWriteString(jw, NULL, el->scientificName);
-jsonWriteString(jw, NULL, el->htmlPath);
-jsonWriteNumber(jw, NULL, (long long)el->hgNearOk);
-jsonWriteNumber(jw, NULL, (long long)el->hgPbOk);
-jsonWriteString(jw, NULL, el->sourceName);
-jsonWriteNumber(jw, NULL, (long long)el->taxId);
-jsonWriteListEnd(jw);
-#endif
 }
 
 static void jsonDbDb()
 /* output the dbDb SQL table */
 {
 struct sqlConnection *conn = hConnectCentral();
 char *dataTime = sqlTableUpdate(conn, "dbDb");
 time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
 replaceChar(dataTime, ' ', 'T');	/* ISO 8601 */
 struct dbDb *dbList = ucscDbDb();
 struct dbDb *el;
 struct jsonWrite *jw = apiStartOutput();
 jsonWriteString(jw, "dataTime", dataTime);
 jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
 freeMem(dataTime);
@@ -312,60 +283,47 @@
         jsonWriteString(jw, "parent", tdb->parent->track);
     if (tdb->settingsHash)
         {
         struct hashEl *hel;
         struct hashCookie hc = hashFirst(tdb->settingsHash);
         while ((hel = hashNext(&hc)) != NULL)
             {
             if (sameWord("track", hel->name))
                 continue;	// already output in header
             if (isEmpty((char *)hel->val))
                 jsonWriteString(jw, hel->name, "empty");
             else
                 jsonWriteString(jw, hel->name, (char *)hel->val);
             }
         }
-    jsonWriteObjectEnd(jw);
-    }
 
     if (tdb->subtracks)
 	{
 	struct trackDb *el = NULL;
 	for (el = tdb->subtracks; el != NULL; el = el->next )
-	{
 	    recursiveTrackList(jw, el);
 	}
-    }
-}	/*	static void recursiveTrackList()	*/
 
-
-#ifdef NOT
-static int trackDbTrackCmp(const void *va, const void *vb)
-/* Compare to sort based on 'track' name; use shortLabel as secondary sort key.
- * Note: parallel code to hgTracks.c:tgCmpPriority */
-{
-const struct trackDb *a = *((struct trackDb **)va);
-const struct trackDb *b = *((struct trackDb **)vb);
-int dif = strcmp(a->track, b->track);
-if (dif < 0)
-   return -1;
-else if (dif == 0.0)
-   return strcasecmp(a->shortLabel, b->shortLabel);
-else
-   return 1;
+    jsonWriteObjectEnd(jw);
     }
-#endif
+else if (tdb->subtracks)
+    {
+    struct trackDb *el = NULL;
+    for (el = tdb->subtracks; el != NULL; el = el->next )
+	recursiveTrackList(jw, el);
+    }
+}	/*	static void recursiveTrackList()	*/
 
 static void trackDbJsonOutput(char *db, FILE *f)
 /* return track list from specified UCSC database name */
 {
 struct sqlConnection *conn = hAllocConnMaybe(db);
 if (NULL == conn)
     apiErrAbort(err400, err400Msg, "can not find 'genome=%s' for endpoint '/list/tracks", db);
 
 char *dataTime = sqlTableUpdate(conn, "trackDb");
 time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
 replaceChar(dataTime, ' ', 'T');	/* ISO 8601 */
 hFreeConn(&conn);
 struct trackDb *tdbList = obtainTdb(NULL, db);
 struct jsonWrite *jw = apiStartOutput();
 jsonWriteString(jw, "genome", db);
@@ -382,31 +340,31 @@
 
 void apiList(char *words[MAX_PATH_INFO])
 /* 'list' function words[1] is the subCommand */
 {
 if (sameWord("publicHubs", words[1]))
     jsonPublicHubs();
 else if (sameWord("ucscGenomes", words[1]))
     jsonDbDb();
 else if (sameWord("hubGenomes", words[1]))
     {
     char *hubUrl = cgiOptionalString("hubUrl");
     if (isEmpty(hubUrl))
 	apiErrAbort(err400, err400Msg, "must supply hubUrl='http:...' some URL to a hub for /list/hubGenomes");
 
 #ifdef NOT
-    /* this could be done for every function */
+    /* this could be done for every function, beware, cgiSpoof can be here */
     struct cgiVar *varList = cgiVarList();
     struct cgiVar *var = varList;
     for ( ; var; var = var->next)
 	{
 	if (differentStringNullOk("hubUrl", var->name))
 	    fprintf(stderr, "# extraneous CGI variable: '%s'='%s'\n", var->name, var->val);
 	}
 #endif
 
     struct trackHub *hub = errCatchTrackHubOpen(hubUrl);
     if (hub->genomeList)
 	{
 	slNameSort((struct slName **)&hub->genomeList);
         struct jsonWrite *jw = apiStartOutput();
 	jsonWriteString(jw, "hubUrl", hubUrl);