Azure / azure-cosmosdb-java

Java Async SDK for SQL API of Azure Cosmos DB
MIT License
54 stars 61 forks source link

Replace Document in Async client is throwing an authorization error. #180

Open ribsthakkar opened 5 years ago

ribsthakkar commented 5 years ago

Describe the bug Calling the client.ReplaceDocument is apparently throwing a authorization error When I call execute the following statement: To Reproduce Observable<ResourceResponse<Document>> obs = client.replaceDocument( collectionLink, item, ro);

The console output throws the following error: DocumentClientException{error={"code":"Unauthorized","message":"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'put\ncolls\ndbs//colls/\nfri, 07 jun 2019 22:00:25 gmt\n\n'\r\nActivityId: fae077cc-8092-4f8d-939a-09c935de4462, Microsoft.Azure.Documents.Common/2.4.0.0, StatusCode: Unauthorized","additionalErrorInfo":null}, resourceAddress='null', statusCode=401, message=The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'put colls dbs//colls/ fri, 07 jun 2019 22:00:25 gmt

' ActivityId: fae077cc-8092-4f8d-939a-09c935de4462, Microsoft.Azure.Documents.Common/2.4.0.0, StatusCode: Unauthorized, getCauseInfo=null, responseHeaders={content-length=407, Strict-Transport-Security=max-age=31536000, Server=Microsoft-HTTPAPI/2.0, Content-Location=https://dbname-eastus2.documents.azure.com/dbs//colls/, x-ms-gatewayversion=version=2.4.0.0, x-ms-activity-id=fae077cc-8092-4f8d-939a-09c935de4462, Date=Fri, 07 Jun 2019 22:00:24 GMT, Content-Type=application/json}, requestHeaders={authorization=type%3Dmaster%26ver%3D1.0%26sig%3DV%2Fn4viFWXX02TS6uCUVzndfwf73SgpOh2H39B7pnBno%3D, x-ms-cosmos-allow-tentative-writes=true, Accept=application/json, x-ms-date=Fri, 07 Jun 2019 22:00:25 GMT, x-ms-documentdb-partitionkey=[], x-ms-documentdb-pre-trigger-include=, x-ms-consistency-level=Session, Content-Type=application/json}} at com.microsoft.azure.cosmosdb.rx.internal.RxGatewayStoreModel.validateOrThrow(RxGatewayStoreModel.java:445) at com.microsoft.azure.cosmosdb.rx.internal.RxGatewayStoreModel.lambda$null$8(RxGatewayStoreModel.java:378) at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69) at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:77) at rx.internal.operators.DeferredScalarSubscriber.complete(DeferredScalarSubscriber.java:100) at rx.internal.operators.DeferredScalarSubscriber.onCompleted(DeferredScalarSubscriber.java:73) at io.reactivex.netty.protocol.http.UnicastContentSubject$AutoReleaseByteBufOperator$1.onCompleted(UnicastContentSubject.java:260) at rx.internal.operators.BufferUntilSubscriber.onCompleted(BufferUntilSubscriber.java:156) at io.reactivex.netty.protocol.http.UnicastContentSubject.onCompleted(UnicastContentSubject.java:282) at io.reactivex.netty.protocol.http.client.ClientRequestResponseConverter$ResponseState.sendOnComplete(ClientRequestResponseConverter.java:413) at io.reactivex.netty.protocol.http.client.ClientRequestResponseConverter$ResponseState.access$500(ClientRequestResponseConverter.java:350) at io.reactivex.netty.protocol.http.client.ClientRequestResponseConverter.channelRead(ClientRequestResponseConverter.java:168) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:438) at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:323) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:297) at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1436) at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1203) at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1247) at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:502) at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:441) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:278) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86) at io.reactivex.netty.metrics.BytesInspector.channelRead(BytesInspector.java:59) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) at io.reactivex.netty.pipeline.InternalReadTimeoutHandler.channelRead(InternalReadTimeoutHandler.java:108) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:656) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:591) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:508) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:470) at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:909) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:834)

Expected behavior I have not changed my keys, and I expect the basic request to authorize correctly.

Actual behavior The request seems to be unauthorized even though all of the other client functions seem to be working correctly. I am able to create documents, upsert documents, create collections, read the collections, create db etc without issue.

Environment summary SDK Version: 2.4.3 Java JDK version: Java 11 OS Version (e.g. Windows, Linux, MacOSX) MacOSX Mojave 10.14.5

christopheranderson commented 5 years ago

(Sorry for delay, we're really busy with an upcoming release and missed this issue)

@ribsthakkar does this always repro?

Do you have a code snippet you can share for how you're calling replaceDocument

ribsthakkar commented 5 years ago

No worries. Releases can be hectic times.

All the times I have tried it, it seems to be doing so. I have been relying on upserting documents in the meantime because I've just been experimenting with the tools. Here is a larger snippet of how I was planning on using the replace document function:

private Double replaceInventoryItems(List<InventoryItem> inventory) {
        String collectionLink = String.format("/dbs/%s/colls/%s", databaseName, collectionName);

        List<Observable<ResourceResponse<Document>>> createDocumentsOBs = new ArrayList<>();
        for (InventoryItem bank : inventory) {
            Observable<ResourceResponse<Document>> obs = masterClient.replaceDocument(
                    collectionLink, bank, new RequestOptions());

            createDocumentsOBs.add(obs);
        }

        return Observable.merge(createDocumentsOBs)
                .map(ResourceResponse::getRequestCharge)
                .observeOn(scheduler) // the scheduler will be used for the following work
                .map(charge -> {
                    // as we don't want to run heavyWork() on netty IO thread, we provide the custom scheduler
                    // for switching from netty IO thread to user thread.
                    return charge;
                })
                .reduce((sum, value) -> sum + value)
                .toBlocking().single();
    }

The code is closely inspired by the Sample Family Code that is provided in the Azure documentation. I have a feeling that it might be an issue with the API request. Maybe it should be something other than PUT?

ribsthakkar commented 5 years ago

Any updates? @christopheranderson

christopheranderson commented 5 years ago

Looking at this just now, it looks like you didn't pass a valid URI:

'put
colls
dbs//colls/
fri, 07 jun 2019 22:00:25 gmt

'

notice how dbs//colls is missing info. it should be something like dbs/foo/colls/bar/docs/baz. If you can repro this again, can you log the string you're passing in to confirm? With upsertDoc, the collection path works fine, which explains why upsert would work.

ribsthakkar commented 5 years ago

Don't think I can reproduce it myself since I no longer have access to the codebase after my internship ended. However, I was just asking so my teammates could reference in the future.