373b5c4187422a9584814dcfa324028fd2e50f14
chmalee
  Tue Aug 4 15:00:30 2026 -0700
Have the pre-finish hook return the rows it wrote, and fix the table display bugs that the real row data now lets us resolve, refs #37999

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

diff --git src/hg/hgHubConnect/hooks/hooklib.c src/hg/hgHubConnect/hooks/hooklib.c
index 23d5bc3bcc7..c8ae6eb96fa 100644
--- src/hg/hgHubConnect/hooks/hooklib.c
+++ src/hg/hgHubConnect/hooks/hooklib.c
@@ -1,29 +1,30 @@
 /* hooklib.c - functions common to all the different tusd hooks */
 
 /* Copyright (C) 2014 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "linefile.h"
 #include "hash.h"
 #include "options.h"
 #include "wikiLink.h"
 #include "customTrack.h"
 #include "userdata.h"
 #include "jsonQuery.h"
 #include "jsHelper.h"
+#include "jsonWrite.h"
 #include "errCatch.h"
 #include "obscure.h"
 #include "cheapcgi.h"
 #include "hooklib.h"
 
 char *prettyFileSize(long size)
 /* Return a string representing the size of a file */
 {
 char buf[32];
 sprintWithGreekByte(buf, sizeof(buf), size);
 return cloneString(buf);
 }
 
 char *encodePath(char *path)
 /* Return a string where each individual component of a '/' separated
@@ -147,30 +148,43 @@
 return NULL;
 }
 
 void fillOutHttpResponseError(struct jsonElement *response)
 {
 fprintf(stderr, "http response error!\n");
 }
 
 void fillOutHttpResponseSuccess(struct jsonElement *response)
 {
 fprintf(stderr, "http response success!\n");
 // DEBUG: comment out after a few releases
 jsonPrintToFile(response, NULL, stderr, 0);
 }
 
+void setUploadedFileList(struct jsonElement *response, char *userName, struct hubSpace *fileList)
+/* Put the hubSpace rows this upload created or changed into the response body. tusd
+ * forwards the body to the client, which shows the rows the server actually holds */
+{
+struct jsonWrite *jw = jsonWriteNew();
+jsonWriteObjectStart(jw, NULL);
+hubSpaceWriteFileList(jw, userName, fileList);
+jsonWriteObjectEnd(jw);
+struct jsonElement *httpResponse = jsonMustFindNamedField(response, "", HTTP_NAME);
+jsonObjectAdd(httpResponse, HTTP_BODY, newJsonString(dyStringContents(jw->dy)));
+jsonWriteFree(&jw);
+}
+
 struct jsonElement *makeDefaultResponse()
 /* Create the default response json with some fields pre-filled */
 {
 struct hash *defHash = hashNew(0);
 struct jsonElement *response = newJsonObject(defHash);
 // only the HTTP Response object is important to have by default, the other
 // fields will be created as needed
 struct jsonElement *httpResponse = newJsonObject(hashNew(0));
 jsonObjectAdd(httpResponse, HTTP_STATUS, newJsonNumber(200)); // default to a successful response 
 jsonObjectAdd(httpResponse, HTTP_BODY, newJsonString(""));
 struct jsonElement *header = newJsonObject(hashNew(0));
 jsonObjectAdd(header, HTTP_CONTENT_TYPE, newJsonString(HTTP_CONTENT_TYPE_STR));
 jsonObjectAdd(httpResponse, HTTP_HEADER, header);
 jsonObjectAdd(response, HTTP_NAME, httpResponse);
 return response;
@@ -178,26 +192,26 @@
 
 void rejectUpload(struct jsonElement *response, char *msg, ...)
 /* Set the keys for stopping an upload */
 {
 // first set the necessary keys to reject the request
 jsonObjectAdd(response, REJECT_SETTING, newJsonBoolean(TRUE));
 jsonObjectAdd(response, STOP_SETTING, newJsonBoolean(TRUE));
 
 // now format the message
 va_list args;
 va_start(args, msg);
 struct dyString *ds = dyStringNew(0);
 dyStringVaPrintf(ds, msg, args);
 va_end(args);
 // find the HTTPResponse object and fill it out with msg:
-struct jsonElement *httpResponse = jsonFindNamedField(response, "", HTTP_NAME);
+struct jsonElement *httpResponse = jsonMustFindNamedField(response, "", HTTP_NAME);
 jsonObjectAdd(httpResponse, HTTP_STATUS, newJsonNumber(500));
 jsonObjectAdd(httpResponse, HTTP_BODY, newJsonString(dyStringCannibalize(&ds)));
 }
 
 boolean isFileTypeRecognized(char *fileName)
 /* Return true if this file one of our recognized types */
 {
 return TRUE;
 }