0dbf3efc8d7c425510c124204883708273691ec8
hiram
  Tue Mar 19 13:36:44 2019 -0700
correctly listing full set of tracks for a hub or database with all attributes refs #18869

diff --git src/hg/hubApi/list.c src/hg/hubApi/list.c
index 5ee114d..b464631 100644
--- src/hg/hubApi/list.c
+++ src/hg/hubApi/list.c
@@ -198,49 +198,69 @@
     jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
     freeMem(dataTime);
     jsonWriteNumber(jw, "chromCount", (long long)slCount(ciList));
     jsonWriteObjectStart(jw, "chromosomes");
     for ( ; el != NULL; el = el->next )
 	{
         jsonWriteNumber(jw, el->chrom, (long long)el->size);
 	}
     jsonWriteObjectEnd(jw);	/* chromosomes */
     jsonWriteObjectEnd(jw);	/* top level */
     fputs(jw->dy->string,stdout);
     }
 hFreeConn(&conn);
 }
 
-static void recursiveTrackList(struct jsonWrite *jw, struct trackDb *tdb, char *type)
-/* output trackDb tags, recursive when composite track */
+static void recursiveTrackList(struct jsonWrite *jw, struct trackDb *tdb)
+/* output trackDb tags only for real tracks, not containers,
+ * recursive when subtracks exist
+ */
 {
-jsonWriteListStart(jw, type);
-struct trackDb *el = NULL;
-for (el = tdb; el != NULL; el = el->next )
+if (! ( tdbIsComposite(tdb) || tdbIsCompositeView(tdb) ) )
     {
-    jsonWriteObjectStart(jw, NULL);
-    jsonWriteString(jw, "track", el->track);
-    jsonWriteString(jw, "shortLabel", el->shortLabel);
-    jsonWriteString(jw, "type", el->type);
-    jsonWriteString(jw, "longLabel", el->longLabel);
-    if (el->parent)
-        jsonWriteString(jw, "parent", el->parent->track);
-    if (el->subtracks)
-	recursiveTrackList(jw, el->subtracks, "subtracks");
+    jsonWriteObjectStart(jw, tdb->track);
+    jsonWriteString(jw, "shortLabel", tdb->shortLabel);
+    jsonWriteString(jw, "type", tdb->type);
+    jsonWriteString(jw, "longLabel", tdb->longLabel);
+    if (tdb->parent)
+	jsonWriteString(jw, "parent", tdb->parent->track);
+    if (tdb->settingsHash)
+	{
+	jsonWriteString(jw, "hasSettingsHash", "TRUE");
+	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);
     }
-jsonWriteListEnd(jw);
+
+if (tdb->subtracks)
+    {
+    struct trackDb *el = NULL;
+    for (el = tdb->subtracks; el != NULL; el = el->next )
+	{
+	recursiveTrackList(jw, el);
+	}
+    }
 }	/*	static void recursiveTrackList()	*/
 
 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;
@@ -248,62 +268,73 @@
 
 static void trackDbJsonOutput(char *db, FILE *f)
 /* return track list from specified UCSC database name */
 {
 struct sqlConnection *conn = hAllocConn(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, "db", db);
 jsonWriteString(jw, "dataTime", dataTime);
 jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
 freeMem(dataTime);
-recursiveTrackList(jw, tdbList, "tracks");
+struct trackDb *el = NULL;
+for (el = tdbList; el != NULL; el = el->next )
+    {
+    recursiveTrackList(jw, el);
+    }
 jsonWriteObjectEnd(jw);
 fputs(jw->dy->string,stdout);
 }	/*	static void trackDbJsonOutput(char *db, FILE *f)	*/
 
 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("must supply hubUrl='http:...' some URL to a hub for /list/hubGenomes");
 
     struct trackHub *hub = errCatchTrackHubOpen(hubUrl);
     if (hub->genomeList)
 	{
+	slNameSort((struct slName **)&hub->genomeList);
         struct jsonWrite *jw = apiStartOutput();
 	jsonWriteString(jw, "hubUrl", hubUrl);
-        jsonWriteListStart(jw, "genomes");
-	struct slName *theList = genomeList(hub, NULL, NULL);
-	slNameSort(&theList);
-	struct slName *el = theList;
-	for ( ; el ; el = el->next )
+        jsonWriteObjectStart(jw, "genomes");
+	struct trackHubGenome *el;
+        for ( el = hub->genomeList; el; el = el->next)
 	    {
-	    jsonWriteString(jw, NULL, el->name);
+	    jsonWriteObjectStart(jw, el->name);
+	    jsonWriteString(jw, "organism", el->organism);
+	    jsonWriteString(jw, "description", el->description);
+	    jsonWriteString(jw, "trackDbFile", el->trackDbFile);
+	    jsonWriteString(jw, "twoBitPath", el->twoBitPath);
+	    jsonWriteString(jw, "groups", el->groups);
+	    jsonWriteString(jw, "defaultPos", el->defaultPos);
+	    jsonWriteNumber(jw, "orderKey", el->orderKey);
+	    jsonWriteObjectEnd(jw);
 	    }
-	jsonWriteListEnd(jw);
+	jsonWriteObjectEnd(jw);
 	jsonWriteObjectEnd(jw);
         fputs(jw->dy->string,stdout);
 	}
     }
 else if (sameWord("tracks", words[1]))
     {
     char *hubUrl = cgiOptionalString("hubUrl");
     char *genome = cgiOptionalString("genome");
     char *db = cgiOptionalString("db");
     if (isEmpty(hubUrl) && isEmpty(db))
       apiErrAbort("ERROR: must supply hubUrl or db name to return track list");
     if (isEmpty(hubUrl))	// missing hubUrl implies UCSC database
 	{
         trackDbJsonOutput(db, stdout);	// only need db for this function
 	return;
@@ -311,32 +342,37 @@
     if (isEmpty(genome) || isEmpty(hubUrl))
 	{
         if (isEmpty(genome))
 	    warn("# must supply genome='someName' the name of a genome in a hub for /list/tracks\n");
 	if (isEmpty(hubUrl))
             apiErrAbort("ERROR: must supply hubUrl='http:...' some URL to a hub for /list/genomes");
 	}
     struct trackHub *hub = errCatchTrackHubOpen(hubUrl);
     if (hub->genomeList)
 	{
 	struct trackDb *tdbList = NULL;
 	(void) genomeList(hub, &tdbList, genome);
 	slSort(tdbList, trackDbTrackCmp);
         struct jsonWrite *jw = apiStartOutput();
 	jsonWriteString(jw, "hubUrl", hubUrl);
-	jsonWriteString(jw, "genome", genome);
-        recursiveTrackList(jw, tdbList, "tracks");
+	jsonWriteObjectStart(jw, genome);
+	struct trackDb *el = NULL;
+	for (el = tdbList; el != NULL; el = el->next )
+	    {
+	    recursiveTrackList(jw, el);
+	    }
+	jsonWriteObjectEnd(jw);
 	jsonWriteObjectEnd(jw);
         fputs(jw->dy->string,stdout);
 	}
     }
 else if (sameWord("chromosomes", words[1]))
     {
     char *hubUrl = cgiOptionalString("hubUrl");
 //    char *genome = cgiOptionalString("genome");
     char *db = cgiOptionalString("db");
     if (isEmpty(hubUrl) && isEmpty(db))
         apiErrAbort("ERROR: must supply hubUrl or db name to return chromosome list");
 
     if (isEmpty(hubUrl))	// missing hubUrl implies UCSC database
 	{
         chromInfoJsonOutput(stdout, db);