CosmosDB Azure Function binding - azure

I am trying to use COSMOS DB with the Azure function
My function looks like
[FunctionName("DeleteAVFeedAuditData")]
public static async Task Run([TimerTrigger("0 0/1 * * * *")]TimerInfo myTimer, [DocumentDB]DocumentClient client,
TraceWriter log)
{
var c = client;
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
var value=ConfigurationManager.AppSettings["AVAuditFlushAfterDays"];
var collectionUri = UriFactory.CreateDocumentCollectionUri("AVFeedAudit", "AuditRecords");
//var documents = client.CreateDocumentQuery(collectionUri,"Select * from c where c.EndedAt");
//foreach (Document d in documents)
//{
// await client.DeleteDocumentAsync(d.SelfLink);
//}
}
}
and local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "
DefaultEndpointsProtocol=xxxxx/xxxxx==;EndpointSuffix=core.windows.net",
"AzureWebJobsDashboard": "",
"AzureWebJobsDocumentDBConnectionString":
"AccountEndpoint=xxxxx/;AccountKey=xxxx==;",
}
}
I had configured the connection
"AzureWebJobsDocumentDBConnectionString" with the cosmosdb connection string which contains the #"endpointurl+key"
When trying to run the application . its saying ID of the document is required, whereas on google it says it will create the document client object based on the connection string.
Please advise me what wrong i am doing in the binding. As my object is to create the document client through which i can query the document and delete the document.

Got the answer.
install-package microsoft.azure.documentdb -version 1.13 [ Note - not version 1.17]
go to local.settings.json and add ""AzureWebJobsDocumentDBConnectionString":
go to host.json and add the assembly reference
{
"frameworks": {
"net46": {
"dependencies": {
"Dynamitey": "1.0.2",
"Microsoft.Azure.DocumentDB": "1.13.0",
"Microsoft.Azure.WebJobs.Extensions.DocumentDB": "1.0.0"
}
}
}
and it will create the DocumentClient object by which you can perform any CRUD operation on it.
Thanks

Related

Cosmos DB - Change Feed Trigger: The Listener for 'my-function-name' was unable to start

I am getting below error while running my azure function app application:
[7/15/2020 8:26:08 AM] The listener for function
'NotificationChangeFeed' was unable to start. [7/15/2020 8:26:08 AM]
The listener for function 'NotificationChangeFeed' was unable to
start. Microsoft.Azure.DocumentDB.Core: Object reference not set to an
instance of an object.
Error Screen shot:
Here is my Change Feed Trigger Azure Function:
public static class NotificationChangeFeed
{
[FunctionName("NotificationChangeFeed")]
public static async Task Run([CosmosDBTrigger(
databaseName: "FleetHubNotifications",
collectionName: "Notification",
ConnectionStringSetting = "CosmosDBConnection",
LeaseCollectionName = "leases", CreateLeaseCollectionIfNotExists = true)]IReadOnlyList<Document> input,
[Inject] ILoggingService loggingService,
[Inject] IEmailProcessor emailProcessor)
{
var logger = new Logger(loggingService);
try
{
if (input != null && input.Count > 0)
{
foreach (Document document in input)
{
string requestBody = document.ToString();
var notification = requestBody.AsPoco<Notification>();
var result = await emailProcessor.HandleEmailAsync(notification, logger);
if (result)
{
logger.Info($"Email Notification sent successfully for file name: {document.Id}");
}
else
{
logger.Warning($"Unable to process document for Email Notification for file with name: {document.Id}");
}
}
}
}
catch (Exception ex)
{
logger.Error($"Unable to process Documents for Email Notification for Files: {input.Count}", ex,
nameof(NotificationChangeFeed));
}
}
}
local.settings.json:
{
"IsEncrypted": "false",
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"CosmosDbId": "FleetHubNotifications",
//Localhost
"CosmoDbAuthKey": "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
"CosmoDbEndpoint": "https://localhost:8081/",
"CosmosDBConnection": "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
}
}
Connecting String of local Storage Emulator is correct.
If your firewall restricts func from accessing the Storage Account, then this error may be reported. The firewall is one of the reasons that the listener cannot access the virtual Storage Emulator.
When running the function locally, all triggers except httptrigger need to use the Storage Emulator. If the firewall restricts the listener's access to virtual storage, problems can occur when performing functions.
Try disabling the firewall and see if that resolves the issue.
Of course, it is also possible that the Storage Emulator service is not open. Try typing
"%programfiles(x86)%\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe" status
in cmd to check the status.
If it returns false, enter the following command to start the Storage Emulator:
"%programfiles(x86)%\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe" init
"%programfiles(x86)%\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe" start
To sum up:
This listener can not start is generally for three reasons.
1.Connection string error prevents connection,
2.firewall is set
3.some services are not turned on.

Azure Queue Trigger Function - Local Integration

I am creating a simple queue triggered azure function using Visual Studio. I am connecting it with my storage account, but for some reason its not working. Any help is appreciated.
This is my code: (auto-generated by VS)
[FunctionName("QueueTest")]
public static void Run([QueueTrigger("my-queue", Connection = "")]string myQueueItem, TraceWriter log)
{
log.Info($"C# Queue trigger function processed: {myQueueItem}");
}
This is my local.settings.json
{
"IsEncrypted": false,
"Values":{
"AzureWebJobsStorage":"DefaultEndpointsProtocol=https;AccountName=accountname;AccountKey=accountkey"
}
}
Queue trigger by default use a AzureWebJobsStorage account. All you need to do is just remove Connection parameter from the attribute:
[FunctionName("QueueTest")]
public static void Run([QueueTrigger("my-queue")]string myQueueItem, TraceWriter log)
{
log.Info($"C# Queue trigger function processed: {myQueueItem}");
}
Ideally if you are a Windows user, use Azure Storage Emulator to connect to local queues. Afterwards change the connection string in your local.settings.json file:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"AzureWebJobsDashboard": "UseDevelopmentStorage=true",
}
}
If you are not a Windows user, you must connect to queues hosted on the Azure platform. To do this, find the storage account linked to your functions and copy the connection string from his settings (Storage Account -> Access Keys -> Connection string)
So I figured out the issue. All the configurations were fine. The issue was, Azure Function Version of my function app was 1 but for some reason, probably because of latest SDK/WebJobs, version 1 was not working correctly. So I had to create another function app with AzureFunctionVersion 2 and all worked fine.
You need to add the connection string of your queue storage account to the local.settings.json and then supply the name of the connection string as the Connection parameter of the QueueTrigger, e.g. in local.settings.json
"Values": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=accountname;AccountKey=accountkey",
"MyStorage": "DefaultEndpointsProtocol=https;AccountName=accountname2;AccountKey=accountkey2;EndpointSuffix=core.windows.net"
}
and in your code
[QueueTrigger("my-queue", Connection = "MyStorage")]string myQueueItem

Azure function is giving not a valid Base-64 string error

I have a http-trigger with a CosmosDB output binding and a simplest of a function as below.
public static class AddRequest
{
[FunctionName("AddRequest")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
ILogger log, [CosmosDB(
databaseName: "haveThatDB",
collectionName: "Requests",
ConnectionStringSetting = "MongoDBEndPoint",CreateIfNotExists =true)] IAsyncCollector<Request> requestOutput
)
{
string jsonContent = await req.ReadAsStringAsync();
dynamic data = JsonConvert.DeserializeObject(jsonContent);
await requestOutput.AddAsync(data);
return req != null
? (ActionResult)new OkObjectResult($"Hello, ras")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
}
when i executed i get an error
Exception binding parameter 'requestOutput'. System.Private.CoreLib: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters
i am using V2 of azure functions.
i have observed that removing the output binding works. so looks like something is up with this output binding.
local.settings contents are as below
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"MongoDBEndPoint": "AccountEndpoint=https://abc.documents.azure.com:10255;AccountKey=xxxxxxxxxxxxyyyyyyyyzzzzzzz",
"MongoDBName": "haveThatDB"
}
}
any help will be appreciated.
Azure Cosmos DB bindings are only supported for use with the SQL API. For all other Azure Cosmos DB APIs, you should access the database from your function by using the static client for your API, including MongoDB API, Cassandra API, Gremlin API, and Table API.
Supported APIs
Azure Cosmos DB bindings for Azure Functions 2.x

Azure Function DocumentClient Binding

I am trying to upgrade my DocumentDB nuget package from 1.13 to 1.18
I am facing issue while upgrading my azure function which is having a DocumentClient binding.
In DocumentDB 1.13 the binding sections does not take :{Id} as an binding parameter and was creating the DocumentClient object perfectly . Whereas the DocumentDB 1.18 needs {Id} as an binding parameter [ Which i dont want , as I want to iterate through entire documents in the collection ]
my host.json binding before 1.18 was
{
"frameworks": {
"net46": {
"dependencies": {
"Dynamitey": "1.0.2",
"Microsoft.Azure.DocumentDB": "1.13.0",
"Microsoft.Azure.WebJobs.Extensions.DocumentDB": "1.0.0"
}
}
}
my local.settings.json had only
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "
DefaultEndpointsProtocol=xxxxx/xxxxx==;EndpointSuffix=core.windows.net",
"AzureWebJobsDashboard": "",
"AzureWebJobsDocumentDBConnectionString":
"AccountEndpoint=xxxxx/;AccountKey=xxxx==;",
}
}
and my azure function looks like
[FunctionName("DeleteAVFeedAuditData")]
public static async Task Run([TimerTrigger("0 0/1 * * * *")]TimerInfo myTimer, [DocumentDB]DocumentClient client,
TraceWriter log)
{
var c = client;
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
var value=ConfigurationManager.AppSettings["AVAuditFlushAfterDays"];
var collectionUri = UriFactory.CreateDocumentCollectionUri("AVFeedAudit", "AuditRecords");
//var documents = client.CreateDocumentQuery(collectionUri,"Select * from c where c.EndedAt");
//foreach (Document d in documents)
//{
// await client.DeleteDocumentAsync(d.SelfLink);
//}
}
}
Now when running the azure function with updated package of documentDB 1.18 it says to bind the {Id} which will give only the single document with the specified id . Whereas my requirment is same as the previous version of DocumentDB 1.13.
Please tell how can i get the entire documents binded with my DocumentClient with the new updated package.
According to your description, I checked this issue and reproduced this issue as follows:
Please tell how can i get the entire documents binded with my DocumentClient with the new updated package.
Based on your scenario, I would recommend you construct the DocumentClient by yourself instead of using the binding to DocumentClient for a workaround to achieve your purpose.
DocumentClient client = new DocumentClient(new Uri("https://<your-account-name>.documents.azure.com:443/"), "<your-account-key>");
And you could configure the serviceEndpoint and accountKey under your local.settings.json file just as the app setting AzureWebJobsStorage. Then you could use the following code for retrieving your setting value:
ConfigurationManager.AppSettings["your-appsetting-key"];
Moreover, here is a issue about constructing the DocumentClient from the connection string, you could refer to it.
UPDATE:
For 1.18, the following code could work as expected:
[FunctionName("Function1")]
public static void Run([TimerTrigger("*/10 * * * * *")]TimerInfo myTimer, [DocumentDB("brucedb01", "brucecoll01",ConnectionStringSetting = "AzureWebJobsDocumentDBConnectionString")] IEnumerable<dynamic> documents, TraceWriter log)
{
foreach (JObject doc in documents)
{
//doc.SelectToken("_self").Value<string>();
log.Info(doc.ToString());
}
}

Azure Functions Update DocumentDb document with Binder

A follow-question to my previous post about Azure Functions. I need to update a document in DocumentDB using the imperative binder (Binder). I don't really understand the documentation and I can't find any examples (I more or less find one kind of example which is the TextWriter one). The documentation says I can bind to "out T" by I find no examples of this.
Say that the document looks like this before running the function:
{
child: {
value: 0
}
}
And the functions looks like this:
var document = await binder.BindAsync<dynamic>(new DocumentDBAttribute("myDB", "myCollection")
{
ConnectionStringSetting = "my_DOCUMENTDB",
Id = deviceId
});
log.Info($"C# Event Hub trigger function processed a message: document: { document }");
document.value = 100;
document.child.value = 200;
log.Info($"Updated document: { document }");
According to the second logging row, the document isn't properly updated. The child is not updated (which existed when read from the store) and value is added. Either way, nothing is persisted. I've tried adding an Output in the function.json, but the compiler complains about it and the documentation states that you shouldn't have any.
What am I missing?
Mathew's sample (using DocumentClient) works, but I wanted to clarify the other way you can do it with Binder and an output binding.
You're bumping into two issues:
The Document implementation of dynamic appears to return a new object instance every time you request a child object. This isn't related to Functions, but explains why document.child.value = 200 doesn't work. You are updating one instance of child that is not actually attached to the document. I'll try to double-check this with DocumentDb folks, but that is confusing. One way around this is to request a JObject instead of a dynamic. My code below does that.
As #mathewc pointed out, Binder does not auto-update the document. We'll track that in the issue he filed. Instead, you can use an output binding with IAsyncCollector<dynamic> to update the document. Behind-the-scenes we'll call InsertOrReplaceDocumentAsync, which will update the document.
Here's a full sample that worked for me:
Code:
#r "Microsoft.Azure.WebJobs.Extensions.DocumentDB"
#r "Newtonsoft.Json"
using System;
using Newtonsoft.Json.Linq;
public static async Task Run(string input, Binder binder, IAsyncCollector<dynamic> collector, TraceWriter log)
{
string deviceId = "0a3aa1ff-fc76-4bc9-9fe5-32871d5f451b";
dynamic document = await binder.BindAsync<JObject>(new DocumentDBAttribute("ItemDb", "ItemCollection")
{
ConnectionStringSetting = "brettsamfunc_DOCUMENTDB",
Id = deviceId
});
log.Info($"C# Event Hub trigger function processed a message: document: { document }");
document.value = 100;
document.child.value = 200;
await collector.AddAsync(document);
log.Info($"Updated document: { document }");
}
binding:
{
"type": "documentDB",
"name": "collector",
"connection": "brettsamfunc_DOCUMENTDB",
"direction": "out",
"databaseName": "ItemDb",
"collectionName": "ItemCollection",
"createIfNotExists": false
}
Yes, I do believe there is an issue here, and I've logged a bug here in our repo to track it.
To work around this until we fix it, you can bind to and use the DocumentClient directly to perform the update, e.g.:
public static async Task Run(
string input, Binder binder, DocumentClient client, TraceWriter log)
{
var docId = "c31d48aa-d74b-46a3-8ba6-0d4c6f288559";
var document = await binder.BindAsync<JObject>(
new DocumentDBAttribute("ItemDb", "ItemCollection")
{
ConnectionStringSetting = "<mydb>",
Id = docId
});
log.Info("Item before: " + document.ToString());
document["text"] = "Modified!";
var docUri = UriFactory.CreateDocumentUri("ItemDb", "ItemCollection", docId);
await client.ReplaceDocumentAsync(docUri, document);
}
However, once you're using DocumentClient directly like this, it might turn out that you can just use it directly for all of your operations, your call. For example:
public static async Task Run(
string input, DocumentClient client, TraceWriter log)
{
var docId = "c31d48aa-d74b-46a3-8ba6-0d4c6f288559";
var docUri = UriFactory.CreateDocumentUri("ItemDb", "ItemCollection", docId);
var response = await client.ReadDocumentAsync(docUri);
dynamic document = response.Resource;
log.Info("Value: " + dynamic.text);
document.text = "Modified!";
await client.ReplaceDocumentAsync(docUri, document);
}

Resources