Skip to content

feat(gax): implement startUploadCallable for resumable uploads - #14139

Open
whowes wants to merge 1 commit into
mainfrom
whowes/resumable-upload-start
Open

feat(gax): implement startUploadCallable for resumable uploads#14139
whowes wants to merge 1 commit into
mainfrom
whowes/resumable-upload-start

Conversation

@whowes

@whowes whowes commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

The implementation uses a custom HttpJsonClientCall.Listener to extract X-Goog-Upload-URL and X-Goog-Upload-Chunk-Granularity headers to be returned in a ResumableUploadSession.

gemini-code-assist[bot]

This comment was marked as outdated.

@whowes whowes changed the title whowes/resumable upload start feat(gax): implement startUpload in HttpJsonResumableUploadClient Aug 19, 2026
@whowes
whowes force-pushed the whowes/resumable-upload-start branch 2 times, most recently from 8d52acf to 7df9201 Compare August 20, 2026 00:06
@whowes

whowes commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 7df9201 to 4253855 Compare August 20, 2026 00:10

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces HttpJsonResumableUploadClient to support resumable uploads over HTTP/JSON, along with a comprehensive suite of unit tests. The feedback suggests validating that the parsed chunk granularity is strictly positive to prevent potential arithmetic errors or infinite loops.

@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 4253855 to 798876d Compare August 20, 2026 01:38
@whowes whowes changed the title feat(gax): implement startUpload in HttpJsonResumableUploadClient feat(gax): implement startUploadCallable for resumable uploads Aug 21, 2026
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 798876d to a071113 Compare August 21, 2026 00:37
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from a071113 to e43a3fa Compare August 24, 2026 19:40
@whowes
whowes force-pushed the whowes/resumable-upload-start branch 2 times, most recently from 3039c34 to 56d1462 Compare August 25, 2026 16:34
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 56d1462 to 22569f9 Compare August 25, 2026 21:44
@whowes
whowes force-pushed the whowes/resumable-upload-start branch 2 times, most recently from 7072e2b to 0b792ed Compare August 26, 2026 17:56
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 0b792ed to 5b6cbd7 Compare August 26, 2026 18:12
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 5b6cbd7 to a139b72 Compare August 26, 2026 18:28
@whowes
whowes force-pushed the whowes/resumable-upload-start branch 2 times, most recently from 826210c to 8ebae80 Compare August 27, 2026 20:58
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 8ebae80 to 295f162 Compare August 27, 2026 23:52
@whowes
whowes force-pushed the whowes/resumable-upload-start branch 2 times, most recently from 2e01dcb to d41b2ef Compare August 28, 2026 01:43
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from d41b2ef to 64defcf Compare August 28, 2026 16:49
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 64defcf to 04f09c2 Compare August 28, 2026 17:02
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 04f09c2 to 7602831 Compare August 28, 2026 17:23
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 7602831 to 9eab629 Compare August 28, 2026 19:11
.setFullMethodName(methodDescriptor.getFullMethodName())
.setHttpMethod(HttpMethods.POST)
.setType(ApiMethodDescriptor.MethodType.UNARY)
.setRequestFormatter(methodDescriptor.getRequestFormatter())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the requestFormatter generated in the stub so we can use it to parse a proto message to Json request?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, this will come from the stub.

}

@Override
public void onHeaders(HttpJsonMetadata responseHeaders) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, we need a custom listener because it is the only place we can get info from response headers?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right - this is directly related to #14136

if (!Strings.isNullOrEmpty(granularityStr)) {
try {
this.chunkGranularity = Long.parseLong(granularityStr);
} catch (NumberFormatException ignored) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we want to ignore it because it may indicate a bigger problem in the server. We may want to recreate an ApiException.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/* retryable= */ false);
future.setException(apiException);
}
} catch (Throwable t) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are any unknown exceptions happen in onClose(), I think it's OK to let it bubble up without recreating an ApiException.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, switched to just setting it on the Future directly.

.build());
} else {
future.setException(
ApiExceptionFactory.createException(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are going to use HttpJsonCallableFactory (which provides other features like retry and tracing) to wrap this callable later, we don't need to recreate an ApiException because it is already supported by HttpJsonExceptionCallable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a helper method to wrap the startUpload callable in the HttpJsonExceptionCallable (and others in next PRs) so that we don't have to translate exceptions manually. We do still need to create ApiExceptions for occasions where a response code or missing/malformed header value signifies an error condition (where there's not an exception present already).

We should discuss more about Callable wrapping with later PRs - I'm not sure that the callables returned by this client should be wrapped for tracing and retries? Definitely I think the higher-level ResumableUploadCallables should be (in HttpJsonCallableFactory), but with these calls being managed by the state machine layer it's less clear to me how that will work (and I was having some inconsistent results in my local testing when wrapping in the tracing callable in particular.)

@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 9eab629 to 7ec0238 Compare August 28, 2026 20:51
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 7ec0238 to 33af57f Compare August 28, 2026 21:40
Base automatically changed from whowes/resumable-upload-client to main August 28, 2026 22:42
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 33af57f to 0f24dae Compare August 28, 2026 22:42
renovate-bot pushed a commit to renovate-bot/google-cloud-java that referenced this pull request Aug 28, 2026
@whowes
whowes force-pushed the whowes/resumable-upload-start branch from 0f24dae to 1dc0716 Compare August 28, 2026 22:47
@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed for 'gapic-generator-java-root'

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants