0
votes

I'm new to Azure and am trying to create a simple CRUD web application with Azure functions and Cosmos DB, using C#. So far, I've successfully implemented two functions, one to write a document to the database and another to read all documents from the database. I've also implemented a function to delete a document but I cannot get it to work, even when using the Azure portal's function test utility. The problem occurs with the call to DeleteDocumentAsync, which causes an exception to be thrown.

The complete run.csx code (simplified by using a hard-coded document SelfLink) is shown below:

#r "Microsoft.Azure.Documents.Client"
using System.Net;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;

private static bool success;

public static HttpResponseMessage Run(HttpRequestMessage req, out object deletionDocument, TraceWriter log)
{
    string endpointUrl = "https://blahblah.documents.azure.com:443/"; // ** Copied from 'URI' in Read-Write Keys screen.
    string authorizationKey = "blahblahblah"; // ** Copied from 'PRIMARY KEY' in Read-Write Keys screen.
    ConnectionPolicy connectionPolicy = new ConnectionPolicy();
    connectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests = 3;
    connectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds = 60;
    connectionPolicy.RequestTimeout = new TimeSpan(0, 0, 30);
    deletionDocument = null;

    using (DocumentClient client = new DocumentClient(new Uri(endpointUrl), authorizationKey, connectionPolicy))
    {
        success = true;
        Task t = DeleteDocument(client, log);
    }

    return success
        ? req.CreateResponse(HttpStatusCode.OK, "Deletion succeeded")
        : req.CreateResponse(HttpStatusCode.BadRequest, "Deletion failed");
}

private static async Task DeleteDocument(DocumentClient client, TraceWriter log)
{
    try
    {
        await client.DeleteDocumentAsync("dbs/p3wOAA==/colls/p3wOAPsBIwA=/docs/p3wOAPsBIwAEAAAAAAAAAA==/");
    }
    catch (Exception ex)
    {
        success = false;
        log.Info("ex: " + ex.StackTrace);
    }
}

The function.json file is shown below:

{
  "bindings": [
    {
      "authLevel": "anonymous",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in",
      "methods": [
        "delete"
      ]
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    },
    {
      "type": "documentDB",
      "name": "deletionDocument",
      "databaseName": "taskDatabase",
      "collectionName": "MsgCollection",
      "createIfNotExists": false,
      "connection": "apw-messages-id_DOCUMENTDB",
      "direction": "out"
    }
  ],
  "disabled": false
}

The exeption's stack trace is shown below. The top of the stack trace refers to 'GenerateKeyAuthorizationSignature'. Is the root cause a permissions issue? I would appreciate any help in solving this.

at Microsoft.Azure.Documents.AuthorizationHelper.GenerateKeyAuthorizationSignature(String verb, Uri uri, NameValueCollection headers, IComputeHash stringHMACSHA256Helper, String clientVersion) at Microsoft.Azure.Documents.Client.GatewayServiceConfigurationReader.d__0.MoveNext()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Documents.Routing.GlobalEndpointManager.d__0.MoveNext()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Documents.Client.GatewayServiceConfigurationReader.d__b.MoveNext()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Documents.Client.DocumentClient.d__35d.MoveNext()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Documents.Client.DocumentClient.d__29.MoveNext()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Documents.Client.DocumentClient.d__44.MoveNext()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Documents.Client.DocumentClient.d__cf.MoveNext()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Documents.BackoffRetryUtility1.<>c__DisplayClass2.<<ExecuteAsync>b__0>d__4.MoveNext()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Documents.BackoffRetryUtility1.d__1b.MoveNext()--- End of stack trace from previous location where exception was thrown --- at Microsoft.Azure.Documents.BackoffRetryUtility1.<ExecuteRetry>d__1b.MoveNext()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Documents.BackoffRetryUtility1.d__a.MoveNext()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Submission#0.d__3.MoveNext() in D:\home\blah\run.csx:line 34

1

1 Answers

2
votes

First, when using Azure Functions, keep the DocumentClient static so the instance is shared across executions, this is a performance improvement.

With that in mind, you can create this Function:

#r "Microsoft.Azure.Documents.Client"
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using System.Net;

private static string endpointUrl = "https://blahblah.documents.azure.com:443/"; // ** Copied from 'URI' in Read-Write Keys screen.
private static string authorizationKey = "blahblahblah"; // ** Copied from 'PRIMARY KEY' in Read-Write Keys screen.
private static DocumentClient client = new DocumentClient(new Uri(endpointUrl), authorizationKey, new ConnectionPolicy() {
    RequestTimeout = new TimeSpan(0, 0, 30),
    RetryOptions = new RetryOptions() {
        MaxRetryAttemptsOnThrottledRequests = 3,
        MaxRetryWaitTimeInSeconds = 60
    }
});

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    bool success = true;

    try {
        await client.DeleteDocumentAsync("dbs/p3wOAA==/colls/p3wOAPsBIwA=/docs/p3wOAPsBIwAEAAAAAAAAAA==/");
        // or you could use the UriFactory if you have the document id
        //Uri documentUri = UriFactory.CreateDocumentUri("name of database","name of collection","document id");
        //await client.DeleteDocumentAsync(documentUri);
    }
    catch(Exception ex){
        success = false;
        log.Info("ex: " + ex.StackTrace);
    }

    return success
        ? req.CreateResponse(HttpStatusCode.OK, "Deletion succeeded")
        : req.CreateResponse(HttpStatusCode.BadRequest, "Deletion failed");
}

functions.json:

{
  "bindings": [
    {
      "authLevel": "anonymous",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in",
      "methods": [
        "delete"
      ]
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    }
  ],
  "disabled": false
}