3c4b5182841d84bcff734f0f8edee48280957a6c larrym Thu Mar 22 14:35:49 2012 -0700 follow-up on code review by Jim; added some doc and use warnAbortHandler diff --git src/hg/hgApi/hgApi.c src/hg/hgApi/hgApi.c index 8c3c9b6..2154345 100644 --- src/hg/hgApi/hgApi.c +++ src/hg/hgApi/hgApi.c @@ -1,42 +1,56 @@ -/* hgApi - provide a JSON based API to the browser. */ +/* hgApi - provide a JSON based API to the browser. + +Required CGI parameters: + +db: assembly +cmd: command (see below) + +Optional CGI parameters: + +jsonp: if present, the returned json is wrapped in a call to the value of the jsonp parameter (e.g. "jsonp=parseResponse"). + +Supported commands: + +defaultPos: default position for this assembly + +metaDb: return list of values for metaDb parameter + +hgt_mdbVal: return metaDb value control - see code for details + +tableMetadata: returns an html table with metadata for track parameter + +codonToPos: returns genomic position for given codon; parameters: codon, table and name (which is gene name). + +codonToPos: returns genomic position for given exon; parameters: exon, table and name (which is gene name). + +cv: Return list of CV terms for the specified term type; just supporting cellType, dataType, and antibody initially + e.g. http://genome.ucsc.edu/cgi-bin/hgApi?db=hg19&cmd=cv&file=cv.ra&type=dataType + +*/ #include "common.h" #include "hdb.h" #include "mdb.h" #include "cheapcgi.h" #include "hPrint.h" #include "dystring.h" #include "hui.h" #include "search.h" #include "encode/encodeExp.h" #include "cv.h" -static void fail(char *msg) -{ -puts("Status: 400\n\n"); -puts(msg); -exit(-1); -} - -void makeIndent(char *buf, int bufLen, int indent) -{ -indent = min(indent, bufLen - 2); -memset(buf, '\t', indent); -buf[indent] = 0; -} - static void encodeExpJson(struct dyString *json, struct encodeExp *el) /* Print out encodeExp in JSON format. Manually converted from autoSql which outputs * to file pointer. */ // TODO: move to lib/encode/encodeExp.c, extend autoSql to support, and use json*() functions { dyStringPrintf(json, "{"); dyStringPrintf(json, "\"ix\":%u", el->ix); dyStringPrintf(json, ", "); dyStringPrintf(json, "\"organism\":\"%s\"", el->organism); dyStringPrintf(json, ", "); dyStringPrintf(json, "\"lab\":\"%s\"", el->lab); dyStringPrintf(json, ", "); dyStringPrintf(json, "\"dataType\":\"%s\"", el->dataType); dyStringPrintf(json, ", "); @@ -107,105 +121,116 @@ dyStringPrintf(json, "\"vendorId\":\"%s\"", (char *)hashOptionalVal(termHash, "vendorId", "unknown")); dyStringPrintf(json, ","); dyStringPrintf(json, "\"lab\":\"%s\"", (char *)hashOptionalVal(termHash, "lab", "unknown")); dyStringPrintf(json, ","); dyStringPrintf(json, "\"targetId\":\"%s\"", (char *)hashOptionalVal(termHash, "targetId", "unknown")); dyStringPrintf(json, ","); dyStringPrintf(json, "\"targetUrl\":\"%s\"", (char *)hashOptionalVal(termHash, "targetUrl", "unknown")); dyStringPrintf(json, ","); dyStringPrintf(json, "\"orderUrl\":\"%s\"", (char *)hashOptionalVal(termHash, "orderUrl", "unknown")); // TODO: add validation file(s) ? } dyStringPrintf(json, "}\n"); } +static void warnAbortHandler(char *format, va_list args) +/* warnAbort handler that aborts with an HTTP 400 status code. */ +{ +puts("Status: 400\n\n"); +vfprintf(stdout, format, args); +exit(-1); +} int main(int argc, char *argv[]) { struct dyString *output = newDyString(10000); -// add cgiSpoof -char *database = cgiOptionalString("db"); + +cgiSpoof(&argc,argv); +pushWarnHandler(warnAbortHandler); +pushAbortHandler(warnAbortHandler); + +char *database = cgiString("db"); char *cmd = cgiOptionalString("cmd"); char *jsonp = cgiOptionalString("jsonp"); if(database) { database = sqlEscapeString(database); if(!hDbExists(database)) - fail("Invalid database"); + errAbort("Invalid database"); } else - fail("Missing 'db' parameter"); + errAbort("Missing 'db' parameter"); if(!cmd) - fail("Missing 'cmd' parameter"); + errAbort("Missing 'cmd' parameter"); if(!strcmp(cmd, "defaultPos")) { dyStringPrintf(output, "{\"pos\": \"%s\"}", hDefaultPos(database)); } else if(!strcmp(cmd, "metaDb")) { // Return list of values for given metaDb var // e.g. http://genome.ucsc.edu/hgApi?db=hg18&cmd=metaDb&var=cell struct sqlConnection *conn = hAllocConn(database); boolean metaDbExists = sqlTableExists(conn, "metaDb"); if(metaDbExists) { char *var = cgiOptionalString("var"); if(var) var = sqlEscapeString(var); else - fail("Missing var parameter"); + errAbort("Missing var parameter"); boolean fileSearch = (cgiOptionalInt("fileSearch",0) == 1); struct slPair *pairs = mdbValLabelSearch(conn, var, MDB_VAL_STD_TRUNCATION, FALSE, !fileSearch, fileSearch); // not tags, either a file or table search struct slPair *pair; dyStringPrintf(output, "[\n"); for (pair = pairs; pair != NULL; pair = pair->next) { if(pair != pairs) dyStringPrintf(output, ",\n"); dyStringPrintf(output, "['%s','%s']", javaScriptLiteralEncode(mdbPairLabel(pair)), javaScriptLiteralEncode(mdbPairVal(pair))); } dyStringPrintf(output, "\n]\n"); } else - fail("Assembly does not support metaDb"); + errAbort("Assembly does not support metaDb"); } // TODO: move to lib since hgTracks and hgApi share #define METADATA_VALUE_PREFIX "hgt_mdbVal" else if(startsWith(METADATA_VALUE_PREFIX, cmd)) { // Returns metaDb value control: drop down or free text, with or without help link. // e.g. http://genome.ucsc.edu/hgApi?db=hg18&cmd=hgt_mdbVal3&var=cell // TODO: Move guts to lib, so that hgTracks::searchTracks.c and hgApi.c can share struct sqlConnection *conn = hAllocConn(database); boolean metaDbExists = sqlTableExists(conn, "metaDb"); if(metaDbExists) { char *var = cgiOptionalString("var"); if(var) var = sqlEscapeString(var); else - fail("Missing var parameter"); + errAbort("Missing var parameter"); int ix = atoi(cmd+strlen(METADATA_VALUE_PREFIX)); // 1 based index if(ix == 0) // - fail("Unsupported 'cmd' parameter"); + errAbort("Unsupported 'cmd' parameter"); enum cvSearchable searchBy = cvSearchMethod(var); char name[128]; safef(name,sizeof name,"%s%i",METADATA_VALUE_PREFIX,ix); if (searchBy == cvSearchBySingleSelect || searchBy == cvSearchByMultiSelect) { boolean fileSearch = (cgiOptionalInt("fileSearch",0) == 1); struct slPair *pairs = mdbValLabelSearch(conn, var, MDB_VAL_STD_TRUNCATION, FALSE, !fileSearch, fileSearch); // not tags, either a file or table search if (slCount(pairs) > 0) { char *dropDownHtml = cgiMakeSelectDropList((searchBy == cvSearchByMultiSelect), name, pairs,NULL, ANYLABEL,"mdbVal", "style='min-width: 200px; font-size: .9em;' onchange='findTracksMdbValChanged(this);'"); if (dropDownHtml) { dyStringAppend(output,dropDownHtml); @@ -217,36 +242,36 @@ else if (searchBy == cvSearchByFreeText) { dyStringPrintf(output,"<input type='text' name='%s' value='' class='mdbVal freeText' onchange='findTracksMdbValChanged(this);' style='max-width:310px; width:310px; font-size:.9em;'>", name); } else if (searchBy == cvSearchByWildList) { dyStringPrintf(output,"<input type='text' name='%s' value='' class='mdbVal wildList' title='enter comma separated list of values' onchange='findTracksMdbValChanged(this);' style='max-width:310px; width:310px; font-size:.9em;'>", name); } else if (searchBy == cvSearchByDateRange || searchBy == cvSearchByIntegerRange) { // TO BE IMPLEMENTED } else - fail("Metadata variable not searchable"); + errAbort("Metadata variable not searchable"); dyStringPrintf(output,"<span id='helpLink%i'> </span>",ix); } else - fail("Assembly does not support metaDb"); + errAbort("Assembly does not support metaDb"); } else if(!strcmp(cmd, "tableMetadata")) { // returns an html table with metadata for a given track char *trackName = cgiOptionalString("track"); boolean showLonglabel = (NULL != cgiOptionalString("showLonglabel")); boolean showShortLabel = (NULL != cgiOptionalString("showShortLabel")); if (trackName != NULL) { struct trackDb *tdb = hTrackDbForTrackAndAncestors(database, trackName); // Doesn't get whole track list if (tdb != NULL) { char * html = metadataAsHtmlTable(database,tdb,showLonglabel,showShortLabel,NULL); if (html) { dyStringAppend(output,html); @@ -335,48 +360,48 @@ else if (!strcmp(cmd, "cv")) { // Return list of CV terms for the specified term type // Just supporting cellType, dataType, and antibody initially // TODO: retire db= // e.g. http://genome.ucsc.edu/cgi-bin/hgApi?db=hg19&cmd=cv&file=cv.ra&type=dataType char *type = cgiString("type"); char *cvFile = cgiOptionalString("file"); if (cvFile != NULL) cvFileDeclare(cvFile); if (differentString(type, "dataType") && differentString(type, "cellType") && differentString(type, "antibody")) { warn("Unsupported CV type %s (must be dataType, cellType, antibody)", type); - fail("Unsupported 'cmd' parameter"); + errAbort("Unsupported 'cmd' parameter"); } dyStringPrintf(output, "[\n"); struct hash *typeHash = (struct hash *)cvTermHash(cvTermNormalized(type)); struct hashCookie hc = hashFirst(typeHash); struct hashEl *hel; while ((hel = hashNext(&hc)) != NULL) { cvTermJson(output, type, hel->val); dyStringAppend(output,","); } output->string[dyStringLen(output)-1] = 0; output->stringSize--; dyStringPrintf(output, "\n]\n"); } else { warn("unknown cmd: %s",cmd); - fail("Unsupported 'cmd' parameter"); + errAbort("Unsupported 'cmd' parameter"); } -// It's debatable whether the type should be text/plain, text/javascript or application/javascript; I think -// any of the types containing "javascript" don't work with IE6, so I'm using text/plain - +// It's debatable whether the type should be text/plain, text/javascript or application/javascript; +// text/javascript works with all our supported browsers, so we are using that one. puts("Content-Type:text/javascript\n"); + //puts("\n"); if(jsonp) printf("%s(%s)", jsonp, dyStringContents(output)); else puts(dyStringContents(output)); return 0; }