Conversation
- downgrade blobserviceversion to 2026_04_06 - change AZURE_LIVE_TEST_SERVICE_VERSION to V2026_04_06 in ci.system.properties in azure-storage-common - create both sync and async
…p instead of ContainersImpl
…ationPolicy + AccessTokenCache pattern
…eAuthorizationPolicy
| use: '@autorest/java@4.1.63' | ||
| input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/15d7f54a5389d5906ffb4e56bb2f38fe5525c0d3/specification/storage/data-plane/Microsoft.BlobStorage/stable/2026-06-06/blob.json | ||
| input-file: https://raw.githubusercontent.com/nickliu-msft/azure-rest-api-specs/013866b01623e6f2cc6c313b44c9c6460de3e91e/specification/storage/data-plane/Microsoft.BlobStorage/stable/2026-10-06/blob.json | ||
| java: true |
There was a problem hiding this comment.
The input-file points to a personal GitHub fork (nickliu-msft/azure-rest-api-specs) and references a future-dated spec folder (2026-10-06). This makes generation non-reproducible and likely breaks for others/CI; please point to an official Azure/azure-rest-api-specs commit and a spec version that exists there.
…/blob/implementation/util/BlobSessionClient.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
| AZURE_LIVE_TEST_SERVICE_VERSION=V2026_04_06 | ||
| AZURE_STORAGE_SAS_SERVICE_VERSION=2026-06-06 |
There was a problem hiding this comment.
AZURE_LIVE_TEST_SERVICE_VERSION was downgraded to V2026_04_06, but AZURE_STORAGE_SAS_SERVICE_VERSION remains 2026-06-06. Given this PR also removes 2026-06-06 from BlobServiceVersion, keeping the SAS service version on 2026-06-06 risks tests using a version that the client no longer recognizes/supports. Consider aligning AZURE_STORAGE_SAS_SERVICE_VERSION with the live test version (or documenting why it must remain newer).
| } | ||
|
|
||
| return new HttpPipelineBuilder().policies(policies.toArray(new HttpPipelinePolicy[0])) | ||
| .httpClient(basePipeline.getHttpClient()) |
There was a problem hiding this comment.
wrapWithSessionPolicy rebuilds a new HttpPipeline but only carries over the HttpClient and policies. This drops pipeline-level configuration like the existing Tracer (and any initialization that depended on ClientOptions), and will reinitialize InstrumentationPolicy with default settings. Consider preserving basePipeline.getTracer() (and, if possible, the original ClientOptions), or avoid rebuilding the pipeline by delegating to the existing basePipeline after inserting the session auth behavior.
| .httpClient(basePipeline.getHttpClient()) | |
| .httpClient(basePipeline.getHttpClient()) | |
| .tracer(basePipeline.getTracer()) |
| public void policyRefreshesNearExpiryWithoutBlockingSyncRequests() { | ||
| StorageSessionCredential nearExpiry = credentialWithToken(FIRST_TOKEN, OffsetDateTime.now().plusSeconds(2)); | ||
| StorageSessionCredential refreshed = credentialWithToken(SECOND_TOKEN); | ||
|
|
||
| when(sessionClient.createSessionSync()).thenReturn(nearExpiry); | ||
| when(sessionClient.createSessionAsync()).thenReturn(Mono.just(refreshed)); | ||
|
|
||
| StorageSessionCredential initial = policy.getValidSessionSync(); | ||
| StorageSessionCredential duringRefresh = policy.getValidSessionSync(); | ||
| StorageSessionCredential afterRefresh = policy.getValidSessionSync(); | ||
|
|
||
| assertEquals(FIRST_TOKEN, initial.getSessionToken()); | ||
| assertEquals(FIRST_TOKEN, duringRefresh.getSessionToken()); | ||
| assertEquals(SECOND_TOKEN, afterRefresh.getSessionToken()); | ||
| verify(sessionClient, times(1)).createSessionSync(); |
There was a problem hiding this comment.
policyRefreshesNearExpiryWithoutBlockingSyncRequests asserts that the second getValidSessionSync() call returns the pre-refresh token, but refreshSessionInBackground() subscribes immediately. With a fast/inline createSessionAsync() (as in this mock), the refresh can complete before the second call, making the assertion timing-dependent and potentially flaky. Consider making the test tolerant to either outcome or explicitly controlling scheduling/completion of the refresh Mono.
| HttpPipelinePolicy capturePolicy = (context, next) -> { | ||
| String auth = context.getHttpRequest().getHeaders().getValue(HttpHeaderName.AUTHORIZATION); | ||
| if (auth != null) { | ||
| capturedAuthHeaders.add(auth); | ||
| } | ||
| return next.process(); | ||
| }; |
There was a problem hiding this comment.
The capturePolicy is added via builder.addPolicy(...), which defaults to a PER_CALL policy. In the pipeline, PER_CALL policies run before auth policies, so context.getHttpRequest().getHeaders().getValue(AUTHORIZATION) will still be null here and capturedAuthHeaders will remain empty, making the assertions below fail. To reliably capture the final Authorization header, capture it after next.process() (or set the policy position to PER_RETRY / after-auth).
| HttpPipelinePolicy capturePolicy = (context, next) -> { | |
| String auth = context.getHttpRequest().getHeaders().getValue(HttpHeaderName.AUTHORIZATION); | |
| if (auth != null) { | |
| capturedAuthHeaders.add(auth); | |
| } | |
| return next.process(); | |
| }; | |
| HttpPipelinePolicy capturePolicy = (context, next) -> next.process().doOnSuccess(response -> { | |
| String auth = context.getHttpRequest().getHeaders().getValue(HttpHeaderName.AUTHORIZATION); | |
| if (auth != null) { | |
| capturedAuthHeaders.add(auth); | |
| } | |
| }); |
| List<HttpPipelinePolicy> bearerPolicies = new ArrayList<>(policies); | ||
| httpsValidation(tokenCredential, "bearer token", endpoint, logger); | ||
| String scope = audience != null | ||
| ? ((audience.toString().endsWith("/") ? audience + ".default" : audience + "/.default")) | ||
| : Constants.STORAGE_SCOPE; | ||
| bearerPolicies.add(new StorageBearerTokenChallengeAuthorizationPolicy(tokenCredential, scope)); | ||
|
|
There was a problem hiding this comment.
In addSessionPolicyIfEnabled, the bearerPipeline used for CreateSession is built from the current policies list before later pipeline policies are appended (per-retry policies, after-retry providers like InstrumentationPolicy, response validation, logging, etc.). This means CreateSession requests may behave differently (missing retries/logging/validation) than normal requests. Consider building the bearer-only pipeline from the final policy list (minus the session policy) so CreateSession calls get the same cross-cutting behavior as other requests.
| @ServiceMethod(returns = ReturnType.SINGLE) | ||
| CreateSessionResponse createSession() { | ||
| return createSessionWithResponse(null, Context.NONE).getValue(); |
There was a problem hiding this comment.
These createSession* methods are package-private even though they’re annotated with @ServiceMethod and return a generated implementation.models.* type. If Create Session is intended to be a customer-facing API, these should be public and ideally return a public model type; otherwise, consider removing @ServiceMethod to avoid implying a supported public surface.
| HttpPipelinePolicy capturePolicy = (context, next) -> { | ||
| String auth = context.getHttpRequest().getHeaders().getValue(HttpHeaderName.AUTHORIZATION); | ||
| if (auth != null) { | ||
| capturedAuthHeaders.add(auth); | ||
| } | ||
| return next.process(); | ||
| }; |
There was a problem hiding this comment.
The capturePolicy is added via builder.addPolicy(...), which defaults to a PER_CALL policy. In the pipeline, PER_CALL policies run before auth policies, so context.getHttpRequest().getHeaders().getValue(AUTHORIZATION) will still be null here and capturedAuthHeaders will remain empty, making the assertions below fail. To reliably capture the final Authorization header, capture it after next.process() (or set the policy position to PER_RETRY / after-auth).
| HttpPipelinePolicy capturePolicy = (context, next) -> { | |
| String auth = context.getHttpRequest().getHeaders().getValue(HttpHeaderName.AUTHORIZATION); | |
| if (auth != null) { | |
| capturedAuthHeaders.add(auth); | |
| } | |
| return next.process(); | |
| }; | |
| HttpPipelinePolicy capturePolicy = (context, next) -> next.process().doOnSuccess(ignored -> { | |
| String auth = context.getHttpRequest().getHeaders().getValue(HttpHeaderName.AUTHORIZATION); | |
| if (auth != null) { | |
| capturedAuthHeaders.add(auth); | |
| } | |
| }); |
|
/azp run java - pullrequest |
|
Azure Pipelines could not run because the pipeline triggers exclude this branch/path. |
|
/azp run java - storage - ci |
|
No pipelines are associated with this pull request. |
|
/azp run java - storage - tests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
No description provided.