9d54db29580e26c13688de88701c61eae1aca9ce
hiram
  Wed Jul 3 10:55:21 2019 -0700
add trackDb output to the list schema and allow tables without trackDb refs #23589

diff --git src/hg/hubApi/list.c src/hg/hubApi/list.c
index 02b5113..9708dde 100644
--- src/hg/hubApi/list.c
+++ src/hg/hubApi/list.c
@@ -281,30 +281,70 @@
         {
         itemCount = bamFileItemCount(bigDataUrl, indexFileOrUrl);
         }
     else if (sameString("vcfTabix", type))
         {
         itemCount = vcfTabixItemCount(bigDataUrl, indexFileOrUrl);
         }
     }
 errCatchEnd(errCatch);
 if (isNotEmpty(errCatch->message->string))
     fprintf(stderr, "%s", errCatch->message->string);
 errCatchFree(&errCatch);
 return itemCount;
 }
 
+static void outputTrackDbVars(struct jsonWrite *jw, struct trackDb *tdb,
+    long long itemCount)
+/* JSON output the fundamental trackDb variables */
+{
+if (NULL == tdb)	/* might not be any trackDb */
+    return;
+
+boolean protectedData = FALSE;
+if (trackDbSetting(tdb, "tableBrowser"))
+    protectedData = TRUE;
+jsonWriteString(jw, "shortLabel", tdb->shortLabel);
+jsonWriteString(jw, "type", tdb->type);
+jsonWriteString(jw, "longLabel", tdb->longLabel);
+jsonWriteNumber(jw, "itemCount", itemCount);
+if (tdb->parent)
+    {
+    jsonWriteString(jw, "parent", tdb->parent->track);
+    if (tdb->parent->parent)
+        jsonWriteString(jw, "parentParent", tdb->parent->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 (sameWord("tableBrowser", hel->name))
+            jsonWriteBoolean(jw, "protectedData", TRUE);
+        else if (isEmpty((char *)hel->val))
+            jsonWriteString(jw, hel->name, "empty");
+        else if (protectedData && sameWord(hel->name, "bigDataUrl"))
+            jsonWriteString(jw, hel->name, "protectedData");
+        else
+            jsonWriteString(jw, hel->name, (char *)hel->val);
+        }
+    }
+}
+
 static void hubSchemaJsonOutput(FILE *f, char *hubUrl, char *genome, char *track)
 /* for given hubUrl and track, output the schema for the hub track */
 {
 struct trackHub *hub = errCatchTrackHubOpen(hubUrl);
 struct trackHubGenome *ge = NULL;
 
 if (isEmpty(genome))
     apiErrAbort(err400, err400Msg, "must specify a 'genome=name' with hubUrl for endpoint: /list/schema?hubUrl=%s;genome=<empty>", hubUrl);
 
 struct trackHubGenome *foundGenome = NULL;
 
 for (ge = hub->genomeList; ge; ge = ge->next)
     {
     if (sameOk(genome, ge->name))
 	{
@@ -324,55 +364,58 @@
 struct trackDb *tdb = obtainTdb(foundGenome, NULL);
 if (NULL == tdb)
     apiErrAbort(err400, err400Msg, "failed to find a track hub definition in genome=%s track=%s for endpoint '/list/schema' given hubUrl=%s'", genome, track, hubUrl);
 
 struct trackDb *thisTrack = findTrackDb(track, tdb);
 if (NULL == thisTrack)
     apiErrAbort(err400, err400Msg, "failed to find specified track=%s in genome=%s for endpoint '/list/schema'  given hubUrl='%s'", track, genome, hubUrl);
 
 char *bigDataUrl = hReplaceGbdb(trackDbSetting(thisTrack, "bigDataUrl"));
 if (NULL == bigDataUrl)
     apiErrAbort(err400, err400Msg, "failed to find bigDataUrl for specified track=%s in genome=%s for endpoint '/list/schema'  given hubUrl='%s'", track, genome, hubUrl);
 char *indexFileOrUrl = hReplaceGbdb(trackDbSetting(tdb, "bigDataIndex"));
 struct bbiFile *bbi = bigFileOpen(thisTrack->type, bigDataUrl);
 long long itemCount = bbiItemCount(bigDataUrl, thisTrack->type, indexFileOrUrl);
 
-jsonWriteNumber(jw, "itemCount", itemCount);
+outputTrackDbVars(jw, thisTrack, itemCount);
 
 struct asObject *as = bigBedAsOrDefault(bbi);
 struct sqlFieldType *fiList = sqlFieldTypesFromAs(as);
 bigColumnTypes(jw, fiList, as);
 
 apiFinishOutput(0, NULL, jw);
 }	/* static void hubSchemaJsonOutput(FILE *f, char *hubUrl,
 	 *	char *genome, char *track) */
 
 static void schemaJsonOutput(FILE *f, char *db, char *track)
 /* for given db and track, output the schema for the associated table */
 {
 struct sqlConnection *conn = hAllocConnMaybe(db);
 if (NULL == conn)
     apiErrAbort(err400, err400Msg, "can not find 'genome=%s' for endpoint '/list/schema", db);
 
 struct trackDb *tdb = obtainTdb(NULL, db);
 struct trackDb *thisTrack = findTrackDb(track, tdb);
-if (NULL == thisTrack)
+if (NULL == thisTrack)	/* OK to work with tables without trackDb definitions */
+    {
+    if (! sqlTableExists(conn, track))
 	apiErrAbort(err400, err400Msg, "failed to find specified track=%s in genome=%s for endpoint '/list/schema'", track, db);
+    }
+/* in case of no trackDb, be wary of trying to use it */
 if (thisTrack && (tdbIsComposite(thisTrack) || tdbIsCompositeView(thisTrack)))
     apiErrAbort(err400, err400Msg, "container track '%s' does not contain data, use the children of this container for data access", track);
 
-
 char *sqlTableName = cloneString(track);
 /* the trackDb might have a specific table defined instead */
 char *tableName = trackDbSetting(thisTrack, "table");
 if (isNotEmpty(tableName))
     {
     freeMem(sqlTableName);
     sqlTableName = cloneString(tableName);
     }
 
 /* this function knows how to deal with split chromosomes, the NULL
  * here for the chrom name means to use the first chrom name in chromInfo
  */
 struct hTableInfo *hti = hFindTableInfoWithConn(conn, NULL, sqlTableName);
 /* check if table name needs to be modified */
 char *splitTableName = NULL;
@@ -398,42 +441,46 @@
 struct asColumn *columnEl = as->columnList;
 int asColumnCount = slCount(columnEl);
 
 char *dataTime = sqlTableUpdate(conn, splitTableName);
 
 time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
 replaceChar(dataTime, ' ', 'T');	/* ISO 8601 */
 struct jsonWrite *jw = apiStartOutput();
 jsonWriteString(jw, "genome", db);
 jsonWriteString(jw, "track", track);
 jsonWriteString(jw, "dataTime", dataTime);
 jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
 freeMem(dataTime);
 
 long long itemCount = 0;
+/* do not show counts for protected data */
+if (! trackDbSetting(thisTrack, "tableBrowser"))
+    {
     char query[2048];
     sqlSafef(query, sizeof(query), "select count(*) from %s", splitTableName);
-itemCount = sqlQuickNum(conn, query);
     if (hti && hti->isSplit)	/* punting on split table item count */
 	itemCount = 0;
     else
 	{
 	itemCount = sqlQuickNum(conn, query);
-    jsonWriteNumber(jw, "itemCount", itemCount);
+	}
     }
 hFreeConn(&conn);
 
+outputTrackDbVars(jw, thisTrack, itemCount);
+
 if (hti && (hti->isSplit || debug))
     jsonWriteBoolean(jw, "splitTable", hti->isSplit);
 
 outputSchema(thisTrack, jw, columnNames, columnTypes, jsonTypes, hti,
   columnCount, asColumnCount, columnEl);
 
 apiFinishOutput(0, NULL, jw);
 
 }	/*	static void schemaJsonOutput(FILE *f, char *db, char *track) */
 
 static void chromInfoJsonOutput(FILE *f, char *db)
 /* for given db, if there is a track, list the chromosomes in that track,
  * for no track, simply list the chromosomes in the sequence
  */
 {
@@ -594,69 +641,40 @@
     }
 return itemCount;
 }
 
 static void recursiveTrackList(struct jsonWrite *jw, struct trackDb *tdb,
     char *db)
 /* output trackDb tags only for real tracks, not containers,
  * recursive when subtracks exist
  */
 {
 boolean isContainer = tdbIsComposite(tdb) || tdbIsCompositeView(tdb);
 
 /* do *NOT* print containers when 'trackLeavesOnly' requested */
 if (! (trackLeavesOnly && isContainer) )
     {
-    boolean protectedData = FALSE;
     long long itemCount = 0;
-    if (trackDbSetting(tdb, "tableBrowser"))
-	protectedData = TRUE;
-    else
+    /* do not show counts for protected data */
+    if (! trackDbSetting(tdb, "tableBrowser"))
 	itemCount = dataItemCount(db, tdb);
     jsonWriteObjectStart(jw, tdb->track);
     if (tdbIsComposite(tdb))
         jsonWriteString(jw, "compositeContainer", "TRUE");
     if (tdbIsCompositeView(tdb))
         jsonWriteString(jw, "compositeViewContainer", "TRUE");
-    jsonWriteString(jw, "shortLabel", tdb->shortLabel);
-    jsonWriteString(jw, "type", tdb->type);
-    jsonWriteString(jw, "longLabel", tdb->longLabel);
-    jsonWriteNumber(jw, "itemCount", itemCount);
-    if (tdb->parent)
-        {
-        jsonWriteString(jw, "parent", tdb->parent->track);
-	if (tdb->parent->parent)
-	    jsonWriteString(jw, "parentParent", tdb->parent->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 (sameWord("tableBrowser", hel->name))
-		jsonWriteBoolean(jw, "protectedData", TRUE);
-            else if (isEmpty((char *)hel->val))
-		jsonWriteString(jw, hel->name, "empty");
-            else if (protectedData && sameWord(hel->name, "bigDataUrl"))
-		jsonWriteString(jw, hel->name, "protectedData");
-	    else
-		jsonWriteString(jw, hel->name, (char *)hel->val);
-            }
-        }
+    outputTrackDbVars(jw, tdb, itemCount);
 
     if (tdb->subtracks)
 	{
 	struct trackDb *el = NULL;
 	for (el = tdb->subtracks; el != NULL; el = el->next )
 	    recursiveTrackList(jw, el, db);
 	}
 
     jsonWriteObjectEnd(jw);
     }
 else if (tdb->subtracks)
     {
     struct trackDb *el = NULL;
     for (el = tdb->subtracks; el != NULL; el = el->next )
 	recursiveTrackList(jw, el, db);