Consuming SignalR services in c# serverless inside the code - c#

Ivé this code...
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "v1/Sala")]
HttpRequest req, [SignalR(HubName = "{query.HubName}")]
IAsyncCollector<SignalRMessage> signalRMessages) {
await signalRMessages.AddAsync(
new SignalRMessage {
Target = "newMessage",
Arguments = new[] { "Hello" }
});
}
And this code works good and I am happy. But I've a need, in partiular on my EventGridTrigger...
As you could noticed on the code above, the hubname is dinamic and an EventGridTrigger is a sort of special kind of endpoint (Your client app will not call and consume it...SignalR will instead).
But I am capable to identify the hubname on my EventGridTrigger...I can do this:
SignalRDataEvent data =
JsonConvert.DeserializeObject<SignalRDataEvent(eventGridEvent.Data.ToString());
string hubname = data.hubname;
But now...I need to send a signalR message using my variable hubname. Since I can't put [SignalR(HubName = "{query.HubName}")] IAsyncCollector signalRMessages) on my EventGridTrigger, I need to create the object SignalR, probably pass credentials, hubname, etc and then send a message. I can't find any sample for this - At least no samples that can work in serverless c# azure functions. Can someone help me wikth this ?

Try the following:
[SignalR(HubName = "{data.hubName}")]

Related

azure functions server (isolated-process): Invoking SignalR group functions from client

I am trying to add a SignalR client to specific SignalR group on an azure functions server (isolated-process). The server side code that I am trying to invoke is the following:
[Function("SendToGroup")]
[SignalROutput(HubName = "serverless", ConnectionStringSetting = "AzureSignalRConnectionString")]
public static SignalRMessageAction SendToGroup([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
{
using var bodyReader = new StreamReader(req.Body);
return new SignalRMessageAction("newMessage")
{
Arguments = new[] { bodyReader.ReadToEnd() },
GroupName = "groupToSend"
};
}
I have tried invoking the above from the client side in C# with the following code to no avail:
HubConnection _connection = new HubConnectionBuilder().WithUrl("http://localhost:7071/api").Build();
Dispatcher.Dispatch(async () => await _connection.StartAsync());
// This does not work
await HubConnectionExtensions.InvokeAsync(_connection, "SendToGroup");
In my searches, I have not found a single C# client code sample that shows how to connect to and remove oneself from signalR hub groups. I would really appreciate being pointed in the right direction.

Send Azure SignalR message from Azure Function with CosmosDB Trigger

I’m developing a app that used CosmosDB to store data and then when anyone updates the data i want the clients to be updated.
For this i have decided to use the changefeed and then Azure Functions and Azure SignalR.
I have set up 2 functions.
A negotiate function (This one works and the clients connect correctly to the SignalR server)
And a OnDocumentsChanged function, and my problem is getting the function to actually sending the message, when something is changed.
I have the following function:
[FunctionName("OnDocumentsChanged")]
public static async Task Run(
[CosmosDBTrigger(
databaseName: "NewOrder",
collectionName: "NewOrder",
CreateLeaseCollectionIfNotExists = true,
ConnectionStringSetting = "myserver_DOCUMENTDB",
LeaseCollectionName = "leases")]
IReadOnlyList<Document> updatedNewOrder,
[SignalR(ConnectionStringSetting = "AzureSignalRConnectionString", HubName = "NewOrder")] IAsyncCollector<SignalRMessage> signalRMessages,
ILogger log)
{
if (updatedNewOrder != null && updatedNewOrder.Count > 0)
{
foreach (var Orders in updatedNewOrder)
{
await signalRMessages.AddAsync(new SignalRMessage
{
Target = "NewOrderUpdated",
Arguments = new[] { Orders.Id }
});
}
}
}
I can see that it is correctly triggered when a change is made to the database, but no messages are send.
I guess I’m missing a out part that actually send the SignalRMessages I’m just not sure how to implement.
Thanks.

Unit Testing Azure Functions that user property binding

I've created some functions and have used this post https://www.rickvandenbosch.net/blog/azure-functions-binding-to-a-property/ to create them using custom properties
I set up the test project and files using the docs here https://learn.microsoft.com/en-us/azure/azure-functions/functions-test-a-function
My question is, how can functions that use property binding be unit tested? as all the examples I've looked at online use the test factory class to pass in a type of httpRequest.
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] GetStudyDetailsRequest req,
ILogger log)
{
try
{
var client = new ArtenaServiceClient(ArtenaServiceClient.EndpointConfiguration.BasicHttpBinding_IArtenaService);
var result = await client.GetStudyDetailsAsync(req.StudentID, req.Dev);
return CreateActionResult.Create(result);
}
catch (Exception e)
{
log.LogError(e, "", req);
return new OkObjectResult(new { e.Message, e.StackTrace });
}
}
I won't need to include Azure Function request property binding in my "unit" test scope firstly because that's not part of my code I am calling a unit here, secondly that's not my code at all :). That kind of testing might be relevant for end to end integration test where you would send http request to a running Function app. So, here in this case, I would just treat the function Run method as a regular C# method by passing required input directly from unit test.

How to send data to Service Bus Topic with Azure Functions?

I have default C# based HTTP Trigger here and I wish to send data "Hello Name" to Service Bus Topic (already created). I'm coding at portal.
How to do it Service Bus output binding?
This is not working. Any help available?
-Reference missing for handling Service Bus?
-How to define Connection of service bus? Where is Functions.json
-How to send a message to service bus?
//This FunctionApp get triggered by HTTP and send message to Azure Service Bus
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Company.Function
{
public static class HttpTriggerCSharp1
{
[FunctionName("HttpTriggerCSharp1")]
[return: ServiceBus("myqueue", Connection = "ServiceBusConnection")] // I added this for SB Output. Where to define.
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
// I added this for SB Output
return responseMessage;
}
}
}
Firstly, there are two bindings to send data to service bus. Firstly is what you show, using the return binding, after install two packages Microsoft.Azure.WebJobs.Extensions.ServiceBus and WindowsAzure.ServiceBus, then you will be able to send data. And you could not do it cause your function type is IActionResult and you are trying to return string(responseMessage).
So if you want to send the whole responseMessage, just return new OkObjectResult(responseMessage);, it will work. And the result would be like below pic.
And if you want to use return responseMessage; should change your method type to string, it will be public static async Task<string> RunAsync and result will be below.
Another binding you could refer to below code or this sample.
[FunctionName("Function1")]
[return: ServiceBus("myqueue", Connection = "ServiceBusConnection")]
public static async Task RunAsync(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[ServiceBus("myqueue", Connection = "ServiceBusConnection")] MessageSender messagesQueue,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
byte[] bytes = Encoding.ASCII.GetBytes(responseMessage);
Message m1 = new Message(bytes);
await messagesQueue.SendAsync(m1);
}
How to define Connection of service bus? Where is Functions.json
In the local you should define the connection in the local.settings.jon, you could use any name with the connection, then in the binding Connection value should be the name you set in the json file. And cause you are using c#, so you could not modify the function.json file, there will be a function.json file in the debug folder. So you could only change the binding in the code.
Hope this could help you, if you still have other problem , please feel free to let me know.
Make sure you first install Microsoft.Azure.WebJobs.Extensions.ServiceBus NuGet package. Then make sure you are using it in your project:
using Microsoft.Azure.WebJobs.Extensions.ServiceBus;
Make sure you clean and build the project to make sure you have no errors.
Then you need to make sure you have a "ServiceBusConnection" connection string inside your local.settings.json file:
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"ServiceBusConnection": "Endpoint=sb://...",
}
}
Which you can get if you go to Azure portal -> Service bus namespace -> Shared access policies -> RootManageSharedAccessKey -> Primary Connection String. Copy and paste this connection string inside "ServiceBusConnection". You can also use the Secondary Connection String as well.
Note: Service bus queues/topics have shared access policies as well. So if you don't want to use the Service bus namespace level access policies, you can create one at queue/topic level, so you your function app only has access to the queue/topic defined in your namespace.
Also if you decide to publish your function app, you will need to make sure you create a configuration application setting for "ServiceBusConnection", since local.settings.json is only used for local testing.

DocumentDB with Azure Functions

I'm trying to connect an Azure DocumentDB and save documents using Azure Functions but I don't know how to create the connection.
You can do it using the Azure Portal.
After you created the DocumentDB -
Create new Azure Function.
Go to the Integrate Tab.
You can choose Azure Document DB as an output for your function.
Choose your Document DB/Database Name/Collection you want to use.
Document parameter name is the Output of your function.
For example
using System;
public static void Run(string input, out object document, TraceWriter log)
{
log.Info($"C# manually triggered function called with input: {input}");
document = new {
text = $"I'm running in a C# function! {input}"
};
}
you need to provide out object which is the same as you defined in the output tab.
You can just use the document client directly:
var endpoint = "https://XXXXX.documents.azure.com:443/";
var authKey = "XXXXX";
using (var client = new DocumentClient(new Uri(endpoint), authKey))
{
var sqlCountQuery = "select value count(1) from c";
IDocumentQuery<dynamic> query = client.CreateDocumentQuery<dynamic>(UriFactory.CreateDocumentCollectionUri("YOUR_DB_ID", "YOUR_COLLECTON_ID"), sqlCountQuery).AsDocumentQuery();
....
}
Azure Functions supports Document DB (Cosmos DB) out-of-the-box. You can just simply add an environment variable called AzureWebJobsDocumentDBConnectionString in V1 or AzureWebJobsCosmosDBConnectionString in V2.
Then just use a CosmosDBTrigger binding attribute for input binding like (in C# for example):
public static class UpsertProductCosmosDbTrigger
{
[FunctionName("ProductUpsertCosmosDbTrigger")]
public static void Run(
[CosmosDBTrigger(
// Those names come from the application settings.
// Those names can come with both preceding % and trailing %.
databaseName: "CosmosDbDdatabaseName",
collectionName: "CosmosDbCollectionName",
LeaseDatabaseName = "CosmosDbDdatabaseName",
LeaseCollectionName = "CosmosDbLeaseCollectionName")]
IReadOnlyList<Document> input,
TraceWriter log)
...
For output binding use DocumentDB output binding attribute in V1 and CosmosDB in V2 like:
[FunctionName("ProductUpsertHttpTrigger")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "products")]
HttpRequestMessage req,
[DocumentDB(
databaseName: "%CosmosDbDdatabaseName%",
collectionName: "%CosmosDbCollectionName%")] IAsyncCollector<Product> collector,
TraceWriter log)
...
I've written a blog post about this: https://blog.mexia.com.au/cosmos-db-in-azure-functions-v1-and-v2
var EndpointUrl = "EndpointUrl";
var PrimaryKey = "PrimaryKeyValue"
this.client = new DocumentClient(new Uri(EndpointUrl), PrimaryKey);
Database database = await this.client.CreateDatabaseIfNotExistsAsync(new Database { Id = cosmoDbName });
you can get the End-point-URL and Primary-Key value from the azure portal in the keys section.
Assume C# has similar SDK like Java. The below is for Java
There are two ways you can connect to documentDB from an Azure function.
Using SDK
DocumentClient documentClient = new DocumentClient(
"SERVICE_ENDPOINT",
"MASTER_KEY",
ConnectionPolicy.GetDefault(),
ConsistencyLevel.Session);
Refer - [https://learn.microsoft.com/en-us/azure/cosmos-db/sql-api-java-samples][1]. This has .Net Samples too.
Binding
#FunctionName("CosmosDBStore")
#CosmosDBOutput(name = "database",
databaseName = "db_name",
collectionName = "col_name",
connectionStringSetting = "AzureCosmosDBConnection")
Please make sure you have a variable in the name of "AzureCosmosDBConnection" in your application settings and local.settings.json(if you want to test locally)
Refer - [https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-cosmosdb-v2][1]
The above link has C# example too.

Categories

Resources