1fab075a58052c812dc69b9609cc987b400985cc
hiram
  Mon Apr 22 22:05:40 2019 -0700
getting to api functions immediately no cart involved refs #18869

diff --git src/hg/hubApi/hubApi.c src/hg/hubApi/hubApi.c
index 43826cd..ff5c858 100644
--- src/hg/hubApi/hubApi.c
+++ src/hg/hubApi/hubApi.c
@@ -195,46 +195,42 @@
     {
     hashIncInt(countTracks, "superTrack child");
     hashIncInt(countTracks, stripType);
     hashIncInt(countTracks, "track count");
     }
 else if (isEmpty(tdb->type))
     hashIncInt(countTracks, "no type specified");
 else
     {
     hashIncInt(countTracks, stripType);
     hashIncInt(countTracks, "track count");
     }
 freeMem(stripType);
 }
 
-static void sampleUrl(struct trackHub *hub, char *db, struct trackDb *tdb, char *chrom, unsigned chromSize, char *errorString)
+static void sampleUrl(struct trackHub *hub, char *db, struct trackDb *tdb, char *errorString)
 /* print out a sample getData URL */
 {
 char errorPrint[2048];
 errorPrint[0] = 0;
 
 if (isNotEmpty(errorString))
     {
     safef(errorPrint, sizeof(errorPrint), " <font color='red'>ERROR: %s</font>", errorString);
     }
 
 boolean superChild = tdbIsSuperTrackChild(tdb);
-unsigned start = chromSize / 4;
-unsigned end = start + 10000;
-if (end > chromSize)
-    end = chromSize;
 char *genome = NULL;
 if (hub)
     genome = hub->genomeList->name;
 
 struct dyString *extraDyFlags = newDyString(128);
 if (debug)
     dyStringAppend(extraDyFlags, ";debug=1");
 if (jsonOutputArrays)
     dyStringAppend(extraDyFlags, ";jsonOutputArrays=1");
 char *extraFlags = dyStringCannibalize(&extraDyFlags);
 
 if (db)
     {
     if (hub)
 	{
@@ -262,39 +258,32 @@
 else if (hub)
     {
     char urlReference[2048];
     safef(urlReference, sizeof(urlReference), " <a href='%s/getData/track?hubUrl=%s;genome=%s;track=%s;maxItemsOutput=5%s' target=_blank>(sample data)%s</a>\n", urlPrefix, hub->url, genome, tdb->track, extraFlags, errorPrint);
 
     if (tdb->parent)
 	hPrintf("<li><b>%s</b>: %s subtrack of parent: %s%s</li>\n", tdb->track, tdb->type, tdb->parent->track, urlReference);
     else
 	hPrintf("<li><b>%s</b>: %s%s</li>\n", tdb->track, tdb->type, urlReference);
     }
 else
     hPrintf("<li>%s : %s not db hub track ?</li>\n", tdb->track, tdb->type);
 }
 
 static void hubSampleUrl(struct trackHub *hub, struct trackDb *tdb,
-    long chromCount, long itemCount, char *chromName, unsigned chromSize,
-      char *genome, char *errorString)
+    long chromCount, long itemCount, char *genome, char *errorString)
 {
-unsigned start = chromSize / 4;
-unsigned end = start + 10000;
-if (end > chromSize)
-    end = chromSize;
-
-
 struct dyString *extraDyFlags = newDyString(128);
 if (debug)
     dyStringAppend(extraDyFlags, ";debug=1");
 if (jsonOutputArrays)
     dyStringAppend(extraDyFlags, ";jsonOutputArrays=1");
 char *extraFlags = dyStringCannibalize(&extraDyFlags);
 
 char errorPrint[2048];
 errorPrint[0] = 0;
 
 if (isNotEmpty(errorString))
     {
     safef(errorPrint, sizeof(errorPrint), " : <font color='red'>ERROR: %s</font>", errorString);
     }
 
@@ -320,32 +309,31 @@
         else if (startsWithWord("bigWig", tdb->type))
             hPrintf("    <li><b>%s</b>: %s%s%s</li>\n", tdb->track, tdb->type, countsMessage, urlReference);
         else
             hPrintf("    <li><b>%s</b>: %s%s%s</li>\n", tdb->track, tdb->type, countsMessage, urlReference);
     }
 else
     {
         if (allowedBigBedType(tdb->type))
             hPrintf("    <li><b>%s</b>: %s%s</li>\n", tdb->track, tdb->type, countsMessage);
         else if (startsWithWord("bigWig", tdb->type))
             hPrintf("    <li><b>%s</b>: %s%s</li>\n", tdb->track, tdb->type, countsMessage);
         else
             hPrintf("    <li><b>%s</b>: %s%s</li>\n", tdb->track, tdb->type, countsMessage);
     }
 }	/* static void hubSampleUrl(struct trackHub *hub, struct trackDb *tdb,
-	 * long chromCount, long itemCount, char *chromName, unsigned chromSize,
-	 *   char *genome)
+	 * long chromCount, long itemCount, char *genome)
 	 */
 
 static void bbiLargestChrom(struct bbiChromInfo *chromList, char **chromName,
     unsigned *chromSize)
 /* find largest chromosome name and size in the chromList */
 {
 if (chromName && chromSize)
     {
     *chromSize = 0;
     char *returnName = NULL;
     struct bbiChromInfo *el;
     for (el = chromList; el; el = el->next)
 	{
 	if (el->size > *chromSize)
 	    {
@@ -487,60 +475,60 @@
 		    bigDataIndex = trackHubRelativeUrl(hub->genomeList->trackDbFile, relIdxUrl);
 		char *bigDataUrl = trackDbSetting(tdbEl, "bigDataUrl");
 		char *longName = NULL;
 		unsigned longSize = 0;
 		struct dyString *errors = newDyString(1024);
 		(void) bbiBriefMeasure(tdbEl->type, bigDataUrl, bigDataIndex, &chromCount, &itemCount, errors, &longName, &longSize);
 		chromSize = longSize;
 		chromName = longName;
 		}
 	    }
         if (tdbIsCompositeView(tdbEl))
 	    hPrintf("<li><b>%s</b>: %s : composite view of parent: %s</li>\n", tdbEl->track, tdbEl->type, tdbEl->parent->track);
 	else
 	    {
 	    if (isSupportedType(tdbEl->type))
-		hubSampleUrl(hub, tdbEl, chromCount, itemCount, chromName, chromSize, genome, errorString);
+		hubSampleUrl(hub, tdbEl, chromCount, itemCount, genome, errorString);
 	    else
 		hPrintf("<li><b>%s</b>: %s : subtrack of parent: %s</li>\n", tdbEl->track, tdbEl->type, tdbEl->parent->track);
 	    }
 	hashCountTrack(tdbEl, countTracks);
         if (tdbEl->subtracks)
 	    hubSubTracks(hub, db, tdbEl, countTracks, chromCount, itemCount, chromName, chromSize, genome, errorString);
 	}
     }
 hPrintf("    </ul></li>\n");
 }	/* hubSubTracks() */
 
 static void showSubTracks(struct trackHub *hub, char *db, struct trackDb *tdb, struct hash *countTracks,
     char *chromName, unsigned chromSize, char *errorString)
 /* tdb has subtracks, show only subTracks, no details */
 {
 hPrintf("    <li><ul>\n");
 if (debug)
     hPrintf("    <li>subtracks for '%s' db: '%s'</li>\n", tdb->track, db);
 if (tdb->subtracks)
     {
     struct trackDb *tdbEl = NULL;
     for (tdbEl = tdb->subtracks; tdbEl; tdbEl = tdbEl->next)
 	{
         if (tdbIsCompositeView(tdbEl))
 	    hPrintf("<li><b>%s</b>: %s : composite view of parent: %s</li>\n", tdbEl->track, tdbEl->type, tdbEl->parent->track);
 	else
 	    {
 	    if (isSupportedType(tdbEl->type))
-		sampleUrl(hub, db, tdbEl, chromName, chromSize, errorString);
+		sampleUrl(hub, db, tdbEl, errorString);
 	    else
 		hPrintf("<li><b>%s</b>: %s : subtrack of parent: %s</li>\n", tdbEl->track, tdbEl->type, tdbEl->parent->track);
 	    }
 	hashCountTrack(tdbEl, countTracks);
         if (tdbEl->subtracks)
 	    showSubTracks(hub, db, tdbEl, countTracks, chromName, chromSize, errorString);
 	}
     }
 hPrintf("    </ul></li>\n");
 }
 
 static void trackSettings(struct trackDb *tdb, struct hash *countTracks)
 /* process the settingsHash for a trackDb, recursive when subtracks */
 {
 hPrintf("    <li><ul>\n");
@@ -592,57 +580,57 @@
 if (! (compositeContainer || compositeView) )
     {
     if (chromSize < 1 || depthSearch)
 	{
 	char *longName = NULL;
 	unsigned longSize = 0;
         (void) bbiBriefMeasure(tdb->type, bigDataUrl, bigDataIndex, &chromCount, &itemCount, errors, &longName, &longSize);
 	chromSize = longSize;
 	chromName = longName;
 	}
     }
 
 if (depthSearch && bigDataUrl)
     {
     if (isSupportedType(tdb->type))
-	    hubSampleUrl(hub, tdb, chromCount, itemCount, chromName, chromSize, genome, errors->string);
+	    hubSampleUrl(hub, tdb, chromCount, itemCount, genome, errors->string);
     }
 else
     {
     if (compositeContainer)
         hPrintf("    <li><b>%s</b>: %s : composite track container has %d subtracks</li>\n", tdb->track, tdb->type, slCount(tdb->subtracks));
     else if (compositeView)
         hPrintf("    <li><b>%s</b>: %s : composite view of parent: %s</li>\n", tdb->track, tdb->type, tdb->parent->track);
     else if (superChild)
 	{
 	if (isSupportedType(tdb->type))
-	    hubSampleUrl(hub, tdb, chromCount, itemCount, chromName, chromSize, genome,  errors->string);
+	    hubSampleUrl(hub, tdb, chromCount, itemCount, genome,  errors->string);
 	else
 	    hPrintf("    <li><b>%s</b>: %s : superTrack child of parent: %s</li>\n", tdb->track, tdb->type, tdb->parent->track);
 	}
     else if (! depthSearch && bigDataUrl)
 	{
         if (isSupportedType(tdb->type))
 	    {
-	    hubSampleUrl(hub, tdb, chromCount, itemCount, chromName, chromSize, genome, errors->string);
+	    hubSampleUrl(hub, tdb, chromCount, itemCount, genome, errors->string);
 	    }
 	}
     else
 	{
         if (isSupportedType(tdb->type))
 	    {
-	    hubSampleUrl(hub, tdb, chromCount, itemCount, chromName, chromSize, genome, errors->string);
+	    hubSampleUrl(hub, tdb, chromCount, itemCount, genome, errors->string);
 	    }
 	else
 	    hPrintf("    <li><b>%s</b>: %s (what is this)</li>\n", tdb->track, tdb->type);
         }
     }
 if (allTrackSettings)
     {
     hPrintf("    <li><ul>\n");
     trackSettings(tdb, countTracks); /* show all settings */
     hPrintf("    </ul></li>\n");
     }
 else if (tdb->subtracks)
     {
     hubSubTracks(hub, db, tdb, countTracks, chromCount, itemCount, chromName, chromSize, genome, errors->string);
     }
@@ -660,40 +648,40 @@
 {
 char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
 boolean compositeContainer = tdbIsComposite(tdb);
 boolean compositeView = tdbIsCompositeView(tdb);
 boolean superChild = tdbIsSuperTrackChild(tdb);
 boolean depthSearch = cartUsualBoolean(cart, "depthSearch", FALSE);
 hashCountTrack(tdb, countTracks);
 
 if (compositeContainer)
     hPrintf("    <li><b>%s</b>: %s : composite track container has %d subtracks</li>\n", tdb->track, tdb->type, slCount(tdb->subtracks));
 else if (compositeView)
     hPrintf("    <li><b>%s</b>: %s : composite view of parent: %s</li>\n", tdb->track, tdb->type, tdb->parent->track);
 else if (superChild)
     {
     if (isSupportedType(tdb->type))
-        sampleUrl(NULL, db, tdb, chromName, chromSize, errorString);
+        sampleUrl(NULL, db, tdb, errorString);
     else
 	hPrintf("    <li><b>%s</b>: %s : superTrack child of parent: %s</li>\n", tdb->track, tdb->type, tdb->parent->track);
     }
 else if (! depthSearch && bigDataUrl)
     hPrintf("    <li><b>%s</b>: %s : %s</li>\n", tdb->track, tdb->type, bigDataUrl);
 else
     {
     if (isSupportedType(tdb->type))
-        sampleUrl(NULL, db, tdb, chromName, chromSize, errorString);
+        sampleUrl(NULL, db, tdb, errorString);
     else
         hPrintf("    <li><b>%s</b>: %s</li>\n", tdb->track, tdb->type);
     }
 
 if (allTrackSettings)
     {
     hPrintf("    <li><ul>\n");
     trackSettings(tdb, countTracks); /* show all settings */
     hPrintf("    </ul></li>\n");
     }
 else if (tdb->subtracks)
     {
     showSubTracks(NULL, db, tdb, countTracks, chromName, chromSize, NULL);
     }
 return;
@@ -1226,81 +1214,35 @@
 hButton("sourceSelected", "go");
 hWrites("</td><td>press 'go' after selections made</td></tr>\n");
 
 hPrintf("</form>\n");
 
 hPrintf("<tr><th colspan=3>(example JSON list output: <a href='/list/publicHubs' target=_blank>Public hubs</a>, and <a href='/list/ucscGenomes' target=_blank>UCSC database genomes</a>)</th></tr>\n");
 
 hPrintf("</table>\n");
 hPrintf("</td></tr></table>\n");
 
 /* how does debug carry forward ? */
 // if (debug)
 //    cgiMakeHiddenVar("debug", "1");
 }
 
-static void doMiddle(struct cart *theCart)
-/* Set up globals and make web page */
+static void apiRequest(char *pathInfo)
 {
-cart = theCart;
-measureTiming = isNotEmpty(cartOptionalString(cart, "measureTiming"));
-char *database = NULL;
-char *genome = NULL;
-
-if (measureTiming)
-    startProcessTiming();
-
-cgiVarSet("ignoreCookie", "1");
-
-getDbAndGenome(cart, &database, &genome, oldVars);
-initGenbankTableNames(database);
-initSupportedTypes();
-initUrlPrefix();
-
-trackLeavesOnly = cartUsualBoolean(cart, "trackLeavesOnly", trackLeavesOnly);
-jsonOutputArrays = cartUsualBoolean(cart, "jsonOutputArrays", jsonOutputArrays);
-
-/* global variable for all workers to honor this limit */
-maxItemsOutput = cartUsualInt(cart, "maxItemsOutput", maxItemsOutput);
-if (maxItemsOutput < 0)	/* can use -1 to indicate as much as allowed */
-    maxItemsOutput = maxItemLimit;
-/* maxItemsOutput of 0 might be useful, to be seen, let it go through */
-// if (maxItemsOutput < 1)	/* safety check */
-//     maxItemsOutput = 1;
-
-if (maxItemsOutput > maxItemLimit)	/* safety check */
-    maxItemsOutput = maxItemLimit;
-
-debug = cartUsualBoolean(cart, "debug", debug);
-// debug = TRUE;
-
-int timeout = cartUsualInt(cart, "udcTimeout", 300);
-if (udcCacheTimeout() < timeout)
-    udcSetCacheTimeout(timeout);
-knetUdcInstall();
-
-char *pathInfo = getenv("PATH_INFO");
-/* nothing on incoming path, then display the WEB page instead */
-if (sameOk("/",pathInfo))
-    pathInfo = NULL;
-
-boolean commandError = FALSE;
+hPrintDisable();
 /*expect no more than MAX_PATH_INFO number of words*/
 char *words[MAX_PATH_INFO];
-
-if (isNotEmpty(pathInfo))
-    {
 /* can immediately verify valid parameters right here right now */
 char *start = cgiOptionalString("start");
 char *end = cgiOptionalString("end");
 char *db = cgiOptionalString("db");
 struct dyString *errorMsg = newDyString(128);
 
 if (isNotEmpty(db))
     {
     struct sqlConnection *conn = hAllocConnMaybe(db);
     if (NULL == conn)
         dyStringPrintf(errorMsg, "can not find database db='%s' for endpoint '%s'", db, pathInfo);
     else
         hFreeConn(&conn);
     }
 if (isNotEmpty(start) || isNotEmpty(end))
@@ -1341,31 +1283,57 @@
 if (isNotEmpty(errorMsg->string))
     apiErrAbort(err400, err400Msg, "%s", errorMsg->string);
 
 setupFunctionHash();
 struct hashEl *hel = parsePathInfo(pathInfo, words);
 /* verify valid API command */
 if (hel)	/* have valid command */
     {
     hPrintDisable();
     void (*apiFunction)(char **) = hel->val;
     (*apiFunction)(words);
     return;
     }
  else
     apiErrAbort(err400, err400Msg, "no such command: '/%s/%s for endpoint '%s'", words[0], words[1], pathInfo);
-    }
+}	/*	static void apiRequest(char *pathInfo)	*/
+
+static void doMiddle(struct cart *theCart)
+/* Set up globals and make web page */
+{
+cart = theCart;
+// measureTiming = isNotEmpty(cartOptionalString(cart, "measureTiming"));
+char *database = NULL;
+char *genome = NULL;
+
+if (measureTiming)
+    startProcessTiming();
+
+cgiVarSet("ignoreCookie", "1");
+
+getDbAndGenome(cart, &database, &genome, oldVars);
+initGenbankTableNames(database);
+initSupportedTypes();
+initUrlPrefix();
+
+trackLeavesOnly = cartUsualBoolean(cart, "trackLeavesOnly", trackLeavesOnly);
+jsonOutputArrays = cartUsualBoolean(cart, "jsonOutputArrays", jsonOutputArrays);
+
+char *pathInfo = getenv("PATH_INFO");
+/* nothing on incoming path, then display the WEB page instead */
+if (sameOk("/",pathInfo))
+    pathInfo = NULL;
 
 (void) hubPublicDbLoadAll();
 
 webStartJWest(cart, database, "UCSC JSON API interface");
 // webStartGbNoBanner(cart, database, "UCSC JSON API interface");
 // webStartGbOptionalBanner(cart, database, "UCSC JSON API interface", TRUE, FALSE);
 
 hPrintf("<div class='container-fluid gbPage'>\n");
 /* these style mentions need to go into custom css file */
 hPrintf("<div style='border:10px solid white'>\n");
 
 if (debug)
     {
     hPrintf("<ul>\n");
     hPrintf("<li>hgBotDelay: %d</li>\n", botDelay);
@@ -1409,35 +1377,30 @@
     {
     hPrintf("<ul>\n");
     hPrintf("<li>otherHubUrl: '%s'</li>\n", otherHubUrl);
     hPrintf("<li>hubDropDown: '%s'</li>\n", hubDropDown);
     hPrintf("<li>urlDropDown: '%s'</li>\n", urlDropDown);
     hPrintf("<li>ucscDb: '%s'</li>\n", ucscDb);
     hPrintf("<li>urlInput: '%s'</li>\n", urlInput);
     hPrintf("</ul>\n");
     }
 if (isEmpty(otherHubUrl))
     otherHubUrl = urlInput;
 
 if (sameWord(RADIO_OTHERHUB, selectRadio))	/* requested other hub URL */
     urlInput = otherHubUrl;
 
-if (commandError)
-  {
-  hPrintf("<h3>ERROR: no such command: '%s/%s' for endpoint '%s'</h3>", words[0], words[1], pathInfo);
-  }
-
 long lastTime = clock1000();
 struct trackHub *hub = errCatchTrackHubOpen(urlInput);
 if (measureTiming)
     {
     long thisTime = clock1000();
     hPrintf("<em>hub open time: %ld millis</em><br>\n", thisTime - lastTime);
     }
 
 hPrintf("<h3>Documentation: <a href='../../goldenPath/help/api.html'>API definitions/help</a>, and <a href='../../goldenPath/help/trackDb/trackDbHub.html' target=_blank>Track definition document</a> for definitions of track settings.</h3>\n");
 
 if (debug)
     showCartDump();
 
 hPrintf("<h2>Explore hub or database assemblies and tracks</h2>\n");
 
@@ -1473,42 +1436,99 @@
 
 if (timedOut)
     hPrintf("<h1>Reached time out %ld seconds</h1>", timeOutSeconds);
 if (measureTiming)
     hPrintf("<em>Overall total time: %ld millis</em><br>\n", clock1000() - enteredMainTime);
 
 hPrintf("</div> <!-- end of text analysis output -->\n");
 hPrintf("</div> <!-- end of surrounding border-->\n");
 hPrintf("</div> <!-- end this page contents -->\n");
 
 webIncludeFile("inc/jWestFooter.html");
 webEndJWest();
 // cartWebEnd();
 }	/*	void doMiddle(struct cart *theCart)	*/
 
+static void setGlobalCgiVars()
+/* check for CGI variables and set global flags */
+{
+char *trackLeaves = cgiOptionalString("trackLeavesOnly");
+if (sameOk("1", trackLeaves))
+    trackLeavesOnly = TRUE;
+
+char *jsonArray = cgiOptionalString("jsonOutputArrays");
+if (sameOk("1", jsonArray))
+    jsonOutputArrays = TRUE;
+
+int maybeDebug = cgiOptionalInt("debug", 0);
+if (1 == maybeDebug)
+    debug = TRUE;
+
+char *measTime = cgiOptionalString("measureTiming");
+if (isNotEmpty(measTime) && sameWord("1", measTime))
+    measureTiming = TRUE;
+char *maxOut = cgiOptionalString("maxItemsOutput");
+if (isNotEmpty(maxOut))
+    {
+    long long n = -2;
+    struct errCatch *errCatch = errCatchNew();
+    if (errCatchStart(errCatch))
+        {
+	n = sqlLongLong(maxOut);
+        }
+    errCatchEnd(errCatch);
+    if (errCatch->gotError)
+	apiErrAbort(err400, err400Msg, "can not recognize maxItemsOutput '%s' as a number", maxOut);
+    else
+	{
+	if (n == -1)	/* can use -1 to indicate as much as allowed */
+	    maxItemsOutput = maxItemLimit;
+	else if (n > maxItemLimit)	/* safety check */
+	    apiErrAbort(err400, err400Msg, "requested maxItemsOutput '%s' greater than maximum limit allowed: %d", maxOut, maxItemLimit);
+	else if (n < 1)
+	    apiErrAbort(err400, err400Msg, "requested maxItemsOutput '%s' can not be less than one", maxOut, maxItemLimit);
+	else
+	    maxItemsOutput = n;
+	}
+    }
+}	/*	static void setGlobalCgiVars()	*/
+
 /* Null terminated list of CGI Variables we don't want to save
  * permanently. */
 static char *excludeVars[] = {"Submit", "submit", "sourceSelected", "selectRadio", "ucscGenome", "publicHubs", "clade", NULL,};
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 enteredMainTime = clock1000();
 cgiSpoof(&argc, argv);
 verboseTimeInit();
 /* similar delay system as in DAS server */
 botDelay = hgBotDelayTimeFrac(delayFraction);
 if (botDelay > 0)
     {
     if (botDelay > 2000)
         {
 	hogExit();
         return 0;
         }
     sleep1000(botDelay);
     }
 
+setGlobalCgiVars();
+
+int timeout = cgiOptionalInt("udcTimeout", 300);
+if (udcCacheTimeout() < timeout)
+    udcSetCacheTimeout(timeout);
+knetUdcInstall();
+
+char *pathInfo = getenv("PATH_INFO");
+if (isNotEmpty(pathInfo)) /* can get to this immediately, no cart needed */
+    apiRequest(pathInfo);
+else
+    {
     trackCounter = hashNew(0);
     cartEmptyShellNoContent(doMiddle, hUserCookie(), excludeVars, oldVars);
+    }
 cgiExitTime("hubApi", enteredMainTime);
 return 0;
 }