Custom Message Properties on Azure Queue/Topic Message from Azure Function - azure

I would like to be able to add custom properties to a queue/topic message as I place it in a queue from and Azure Function. The custom properties are for filtering the messages into different topics. I must be missing something because this working example doesn't seem to have anywhere to put custom properties.
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req,
TraceWriter log,
ICollector<Contact> outputSbMsg)
{
var contactList = await req.Content.ReadAsAsync<ContactList>();
foreach(var contact in contactList.Contacts)
{
if (contact.ContactId == -1)
{
continue;
}
contact.State = contactList.State;
outputSbMsg.Add(contact);
}
}
I'm coding the function through the Azure Portal. The contact list comes into the function through in the body of an http request. The functions parses out each contact, adds modifies some properties and submits each contact to the queue topic. Additionally I pull other data from the request headers and the contact list and I would like to use that data in the queue topic to filter the requests into different subscriptions.
Edit:
As per #Sean Feldman's suggestion below, the data is added to a BrokeredMessage before adding the BrokeredMessage to the output collection. The key part is to serialize the contact object before adding it to the BrokeredMessage.
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req,
TraceWriter log,
ICollector<BrokeredMessage> outputSbMsg)
{
var contactList = await req.Content.ReadAsAsync<ContactList>();
foreach(var contact in contactList.Contacts)
{
if (contact.ContactId == -1)
{
continue;
}
string jsonData = JsonConvert.SerializeObject(contact);
BrokeredMessage message = new BrokeredMessage(jsonData);
message.Properties.Add("State", contactList.State);
outputSbMsg.Add(message);
}
}
Thank you

To be able to set custom/user properties, the output collector should be of a native Azure Service Bus message type, BrokeredMessage.
In your case, you'll have to change ICollector<Contact> to ICollector<BrokeredMessage>.

Related

In Azure function that returns ServiceBus message, how do I conditionally returning message?

I have an azure function which returns a service bus message. However, I want to conditionally return a service bus message, instead of being forced to return the message every time.
here is an example
[FunctionName("ServiceBusOutput")]
[return: ServiceBus("myqueue", Connection = "ServiceBusConnection")]
public static string ServiceBusOutput([HttpTrigger] dynamic input, ILogger log)
{
log.LogInformation($"C# function processed: {input.Text}");
// check condition here, abort return completely
// Otherwise, return
return input.Text;
}
Said another way, I want to return a message on a service bus when certain conditions apply within the function code block. Is this possible?
One idea that does not work is to throw an exception. However, this just results in the message being placed into the DL queue. I want to completely abort the operation of returning the message on the service bus, and avoid DL.
Another idea that does not work is to simply execute
return;
But this results in compile-time error, which is sort of expected
"An object of a type convertible to 'MyReturnType1' is required"
I can think of a hack which I dont like, which is to return null, and handle the null later in the chain. But this is sort of dirty to me.
You could just bind ServiceBus as MessageSender type, then use the SendAsync() method to send the message.
The below is my test code, if the request name equals "george", it will send the name to the message queue.
public static class Function1
{
[FunctionName("Function1")]
public static async System.Threading.Tasks.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;
if (name.Equals("george")) {
byte[] bytes = Encoding.ASCII.GetBytes(name);
Message m1 = new Message();
m1.Body = bytes;
await messagesQueue.SendAsync(m1);
}
}
}
Suppose this is what you want, hope this could help you, if you still have other problem please feel free to let me know.
Instead of using the output binding available in Azure Function, you can send a message to the queue from a custom queue client created inside the function.
Posting the message based on a condition is not possible with the bindings.

Configuring Twilio SMS from Azure Functions v2

I have some code where I'm reading messages off of an Azure Event Hub that I want to either send an email or send an SMS.
The email is working through send grid, but I'm not sure how to configure the SMS part though.
I think I'd want to use Twilio and here's a sample of what my code's like. The "messageCollector" works for sending Email since there's some configuration for SendGrid in the local json. How do I configure Twilio?
[FunctionName("SendAlert")]
public static async Task Run(
[EventHubTrigger("v1-email-hub", Connection = "EventHubConnection")] EventData[] events,
[SendGrid] IAsyncCollector<SendGridMessage> messageCollector,
[TwilioSms] IAsyncCollector<CreateMessageOptions> smsCollector,
[Inject] NotificationEventLogic eventLogic,
ILogger log)
{
foreach (EventData eventData in events)
{
string messageBody = Encoding.UTF8.GetString(eventData.Body.Array, eventData.Body.Offset, eventData.Body.Count);
var notificationEvents = JsonConvert.DeserializeObject<List<NotificationEvent>>(messageBody);
foreach (var ev in notificationEvents)
{
if (ev.NotificationEventType == NotificationEventType.Email)
{
var message = new SendGridMessage();
// ... ... make message and add it
await messageCollector.AddAsync(message);
}
else if (ev.NotificationEventType == NotificationEventType.SMS)
{
// Not sure how to get this to work
var mobileMessage = new CreateMessageOptions(new PhoneNumber(ev.Data))
{
Body = $"Notification {ev.NotificationId}"
};
await smsCollector.AddAsync(mobileMessage);
}
// await smsCollector.AddAsync()
await eventLogic.CreateEventAsync(ev);
}
}
}
You will need to configure it in attribute
[TwilioSms(AccountSidSetting = "TwilioAccountSid", AuthTokenSetting = "TwilioAuthToken", From = "+1425XXXXXXX")]
as it mentioned in documentation
TwilioAccountSid This value must be set
to the name of an app setting that holds your Twilio Account Sid e.g.
TwilioAccountSid. If not set, the default app setting name is
"AzureWebJobsTwilioAccountSid".
TwilioAuthToken This value must be set to
the name of an app setting that holds your Twilio authentication token
e.g. TwilioAccountAuthToken. If not set, the default app setting name
is "AzureWebJobsTwilioAuthToken".

Azure function inserting but not updating cosmosDB

I have an azure function taking in messages from an Azure service bus queue and sending documents to cosmosDB. I'm using Azure functions 1.x:
public static class Function1
{
[FunctionName("Function1")]
public static void Run([ServiceBusTrigger("ServiceBusQueue", AccessRights.Manage, Connection = "ServiceBusQueueConnection")]BrokeredMessage current, [DocumentDB(
databaseName: "DBname",
collectionName: "Colname",
ConnectionStringSetting = "CosmosDBConnection")]out dynamic document, TraceWriter log)
{
document = current.GetBody<MyObject>();
log.Info($"C# ServiceBus queue triggered function processed the message and sent to cosmos");
}
}
This inserts to cosmos successfully, but when updating I get errors:
Microsoft.Azure.Documents.DocumentClintException: Entity with the specified id already exists in the system.
They key I'm trying to update on is the partition key of that collection.
I saw this question: Azure function C#: Create or replace document in cosmos db on HTTP request
But It seems like my usage is similar to the one in Matias Quarantas answer. Also he mentioned that using an out parameter causes an upsert on cosmos.
How can I create this "upsert" function, while still using azure function 1.x?
The binding does indeed do an Upsert operation.
I created this sample Function that takes an Http payload (JSON) and stores it in Cosmos DB as-is:
[FunctionName("Function1")]
public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req,
[DocumentDB("MyDb", "MyCollection", ConnectionStringSetting = "MyCosmosConnectionString")] out dynamic document,
TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
dynamic data = req.Content.ReadAsAsync<object>().GetAwaiter().GetResult();
document = data;
return req.CreateResponse(HttpStatusCode.OK);
}
If I send a JSON payload to the Http endpoint, the output binding works as expected:
When I check the Data Explorer, I see:
If I send a second payload, this time, adding a property (same id):
The Data Explorer shows the document was updated, with the same Function code:
Can you add the full Exception/Error trace? Is your Service Bus Message including an "id"? Is your collection partitioned?
If your collection is partitioned and you are changing the value of the Partition key property, then the binding won't update the existing document, it will create a new one because the Upsert operation won't find an existing document (based on the id/partition key). But it won't throw an exception.

How to add more entries in customDimensions in to Application Insights Telemetry in Azure Function

I am trying to integrate Azure App Insights with an Azure Function App (HttpTriggered). I want to add my own keys and values in the "customDimensions" object of the requests table. Right now it only shows the following:
On query
requests
| where iKey == "449470fb-****" and id == "5e17e23e-****"
I get this:
LogLevel: Information
Category: Host.Results
FullName: Functions.FTAID
StartTime: 2017-07-14T14:24:10.9410000Z
param__context: ****
HttpMethod: POST
param__req: Method: POST, Uri: ****
Succeeded: True
TriggerReason: This function was programmatically called via the host APIs.
EndTime: 2017-07-14T14:24:11.6080000Z
I want to add more key values such as:
EnvironmentName: Development
ServiceLine: Business
Based on this answer, I implemented the ITelemetryInitializer interface as follows:
public class CustomTelemetry : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry == null) return;
requestTelemetry.Context.Properties.Add("EnvironmentName", "Development");
}
}
Here is how the run.csx code for the Azure Function App looks like:
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, ExecutionContext context, TraceWriter log)
{
// Initialize the App Insights Telemetry
TelemetryConfiguration.Active.InstrumentationKey = System.Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process);
TelemetryConfiguration.Active.TelemetryInitializers.Add(new CustomTelemetry());
TelemetryClient telemetry = new TelemetryClient();
var jsonBody = await req.Content.ReadAsStringAsync();
GetIoItemID obj = new GetIoItemID();
JArray output = obj.GetResponseJson(jsonBody, log, telemetry);
var response = req.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(output.ToString(), System.Text.Encoding.UTF8, "application/json");
return response;
}
But this did not work...
I believe, since you're creating the TelemetryClient yourself in this example, you don't need to bother with the telemetry initializer, you could just do
var telemetry = new TelemetryClient();
telemetry.Context.Properties["EnvironmentName"] = "Development";
directly, and everything sent by that instance of that telemetry client will have those properties set.
You'd need that telemetry initializer if you don't have control over who's creating the telemetry client and want to touch every item of telemetry created wherever?
I don't know how that TelemetryClient instance gets used downstream in azure functions though, so i'm not entirely positive, though.
Edit: from azure functions post about this, it says:
We’ll be working hard to get Application Insights ready for production
workloads. We’re also listening for any feedback you have. Please file
it on our GitHub. We’ll be adding some new features like better
sampling controls and automatic dependency tracking soon. We hope
you’ll give it a try and start to gain more insight into how your
Functions are behaving. You can read more about how it works at
https://aka.ms/func-ai
and the example from that func-ai link has a couple things:
1) it creates the telemetry client statically up front once (instead of in each call to the function)
private static TelemetryClient telemetry = new TelemetryClient();
private static string key = TelemetryConfiguration.Active.InstrumentationKey = System.Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process);
and inside the function it is doing:
telemetry.Context.Operation.Id = context.InvocationId.ToString();
to properly do correlation with events you might create with your telemetry client so you might want to do that too.
2) it appears that the telemetry client you create you can use, but they create their own telemetry client and send data there, so anything you touch in your telemetry client's context isn't seen by azure functions itself.
so, to me that leads me to something you can try:
add a static constructor in your class, and in that static constructor, do the telemetry initializer thing you were doing above. possibly this gets your telemetry initializer added to the context before azure functions starts creating its request and calling your method?
If that doesn't work, you might need to post on their GitHub or email the person listed in the article for more details on how to do this?

Azure function CRUD on Table Storage

How can we do CRUD with table storage in Azure functions:
I have insert working, but would like to know how to return entities and do updates and deletes too.
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log, IAsyncCollector<User> outputTable)
{
log.Info($"C# HTTP trigger function processed a request. RequestUri={req.RequestUri}");
var user = new User();
user.PartitionKey = "Users";
user.RowKey = DateTime.Now.Ticks.ToString();
user.UserId = "aaaa";
user.Country = "uk";
await outputTable.AddAsync(user);
....
You can bind your function to an instance of CloudTable class, and then you get all its API at your hands.
I think you should be able to just replace IAsyncCollector<User> with CloudTable in your function definition and adjust the usage (provided you have a valid output binding).
See "Output usage" under Azure Functions Storage table bindings.

Resources