Azure Function crashes upon missing url query - azure

I have an Azure-Function with the following configuration
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
},
{
"type": "cosmosDB",
"name": "inputDocument",
"databaseName": "MeasureInputData",
"collectionName": "hubs",
"connectionStringSetting": "measure-cosmosdb_DOCUMENTDB",
"direction": "in",
"sqlQuery": "SELECT * FROM c WHERE c.uuid = {hub}"
}
],
"disabled": false
}
As you can see above, I have the {hub} in the sqlQuery passed as a url parameter.
Doing a request like
https://<my-function-url>?code=<my-function-key>&hub=<my-hub-id>
returns the matching hub.
If leaving the value <my-hub-id> empty
https://<my-function-url>?code=<my-function-key>&hub=
returns an empty object.
The issue is when I donĀ“t provide the hub parameter in the URL the function crashes.
https://<my-function-url>?code=<my-function-key>
requesting the above returns http status code 500
Shouldn't missing query parameters be handled in some way?
Thanks

It seems the sql will be
SELECT * FROM c WHERE c.uuid =
but not
SELECT * FROM c WHERE c.uuid = "" or SELECT * FROM c WHERE c.uuid = null
when the parameter "hub" missing.
Azure function cosmos db binding will not handle the missing parameter in query. If you want to handle this error, I think we can create another api or function and put(call) the original function url in this new api/function. Then we can handle this error in the new api/function.
If you hope azure function to add this feature, you can raise your idea to the azure function feedback page.

No, you should handled that. If you check the hello world sample:
[FunctionName("HttpTriggerCSharp")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "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;
return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
It tries to read value from query string and from body. In case it's null, it will return
new BadRequestObjectResult("Please pass a name on the query string or in the request body")
so you should do the same, in case there's no hub provided in the querystring, return 400 (bad request)

Related

Setting Azure Function Service Bus Topic and Subscription Output Binding via C#

I have a simple HTTP trigger Azure Function with multiple Service Bus output bindings. All the bindings are pointing to the same Topic, but they have different Subscriptions. If I was to set this function app through function.json it is pretty straightforward:
{
"bindings": [
{
"authLevel": "function",
"name": "req",
"type": "httpTrigger",
"direction": "in",
"methods": [
"get",
"post"
]
},
{
"name": "$return",
"type": "http",
"direction": "out"
},
{
"type": "serviceBus",
"connection": "SERVICEBUS",
"name": "output",
"topicName": "outtopic",
"subscriptionName": "sub",
"direction": "out"
},
{
"type": "serviceBus",
"connection": "SERVICEBUS",
"name": "output",
"topicName": "outtopic",
"subscriptionName": "sub2",
"direction": "out"
}
],
"disabled": false
}
But I publish my functions via Visual Studio and therefore my Azure Functions are read only in the portal and function.json is automatically generated by VS upon publishing.
The problem is that I cannot figure out how do I setup multiple output bindings pointing to different subscriptions. Currently I have something like this:
[FunctionName("Function2")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[ServiceBus("outtopic", entityType:EntityType.Topic)] IAsyncCollector<string> output,
[ServiceBus("outtopic", entityType: EntityType.Topic)] IAsyncCollector<string> output2,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
await output.AddAsync(requestBody);
return new OkObjectResult("OK");
}
As you can see output and output2 are pointing to the same Topic, but there is no option to specify Subscription.
At this point I am pretty confident that this has not been implemented yet. But I hope there is a workaround maybe?
Try this, add Connection property in the definition, per this example - if by subscription you mean on the azure subscription:
public static void Run([BlobTrigger("inputvideo/{customername}/{date}/{filename}", Connection = "AzureWebJobsStorage")]Stream myBlob,
string customername,
string date,
string filename,
[ServiceBus("detectobjectsqueue",EntityType.Queue, Connection="ServiceBusConnectionString")] IAsyncCollector<string> output,
ILogger log)
Update
Per your comment, I understood that by subscription you mean on topic subscription. In that case, the idea of topic is that all subscriptions receive the message. So you have one publisher, and whoever subscribes to the topic it will receive the message. If you would like to make sure that a specific subscriber receives the message, either implement message filtering(per type for example) on the receiving endpoint or use dedicated queue per subscriber.
Also, conceptually, the publisher should not know who are the subscribers, and the subscriber should not know who is the publisher. If you know who is the subscriber, why not use the REST call for example to the receiving endpoint?
It is not possible to directly put messages into a Topic Subscription, rather every message has to come through a Topic.
To make sure only a particular subscription receives a message, you need to configure the Topic Subscription rule. You can read more about rules in the blog post here.

Azure Cosmos DB Input Binding - cannot pass OFFSET and LIMIT values as params?

I'm experiencing an issue with trying to dynamically pass the values for OFFSET and LIMIT as query params to my CosmosDB trigger.
If I hardcode the values for these two into the query, it works as expected.
However, with this code:
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"route": "v1/query/properties",
"methods": [
"post"
]
},
{
"name": "propertiesInquiryInput",
"type": "cosmosDB",
"databaseName": "property",
"collectionName": "discovery",
"connectionStringSetting": "CosmosDBConnectionString",
"direction": "in",
"leaseCollectionName": "leases",
"sqlQuery": "SELECT * FROM c WHERE c.country={country} OFFSET {pageNo} LIMIT {perPage}"
},
I get the following failure upon execution:
System.Private.CoreLib: Exception while executing function: Functions.queryProperties. Microsoft.Azure.DocumentDB.Core: Message: {"errors":[{"severity":"Error","location":
{"start":48,"end":55},"code":"SC2062","message":"The OFFSET count value exceeds the maximum allowed value."},
{"severity":"Error","location":{"start":62,"end":70},"code":"SC2061","message":"The LIMIT count value exceeds the maximum allowed value."}]}
I'm relatively new to Azure services and their patterns, so perhaps I'm missing something obvious here. I've tried sending these values as a JSON object via POST and as query params via a GET request. Nothing seems to work.
I'm also not aware of a way to view what the SQL query that IS being fired off, so as to perhaps debug it from that angle. Any guidance in the right direction would be appreciated.
Update:
Adding function body for clarity:
module.exports = async function (context, req) {
const results = context.bindings.propertiesInquiryInput;
!results.length && context.done(null, { status: 404, body: "[]", });
const body = JSON.stringify(results.map(data => reshapeResponse(data));
return context.done(null, { status: 200, body });
}
In addition to the original question:
Got the same exception message with parametrised query and azure.cosmos=4.2.0 pypi package for python.
Case to reproduce:
query = f"""
SELECT *
FROM c
WHERE ...
AND c.run.submittedBy = #author
OFFSET 0 LIMIT #exp_limit
"""
items = list(cdb_container.query_items(
query=query,
parameters=[
{"name":"#exp_limit", "value": f"{num_of_experiments}"}, # can't pass as param due to `The LIMIT count value exceeds the maximum allowed value.`
{"name":"#author", "value": f"{submitter}"},
],
enable_cross_partition_query=True
))
Workaround (with formatted string for the query):
num_of_experiments = 10
query = f"""
SELECT *
FROM c
WHERE ...
AND c.run.submittedBy = #author
OFFSET 0 LIMIT {num_of_experiments}
"""
Omitted not-related parameters in WHERE statement for example purpose. Might be helpful for someone who is struggling with a similar case.

Defining a return for a very simple manual Azure function

I have an Azure function that does this:
public static int Run(int myvalue, TraceWriter log)
{
log.Info($"C# manually triggered function called with input: {myvalue}");
if (myvalue == 1) return 1;
return (myvalue + 1);
}
I created it directly in the portal; however, my attempt at the bindings are probably wrong:
{
"bindings": [
{
"type": "manualTrigger",
"direction": "in",
"name": "myvalue"
},
{
"type": "int",
"direction": "out",
"name": "$return"
}
],
"disabled": false
}
When I run it inside the portal, it gives me a 202 (accepted), but doesn't output the return value from the function.
My question is, basically, why am I not getting any output; however, I suppose my first question should be (is), should I be getting output; and if so, what's wrong with my binding?
This is a simplified version of a slightly more complex function, that I intend to use as a condition inside a logic app (hence why I need a return value).
int is not a supported type for manual trigger. You should be seeing an error like this in the logs:
[Error] Microsoft.Azure.WebJobs.Host: Error indexing method 'Functions.ManualTriggerCSharp1'. Microsoft.Azure.WebJobs.Script: Can't bind ManualTriggerAttribute to type 'System.Int32'.
If you change the parameter type to string, logging should work:
public static void Run(string myvalue, TraceWriter log)
{
log.Info($"C# manually triggered function called with input: {myvalue}");
}
Now, there is no point in returning anything from manual trigger. You can keep returning an int but it will be ignored. But remove the second binding:
{
"bindings": [
{
"type": "manualTrigger",
"direction": "in",
"name": "myvalue"
}
],
"disabled": false
}
To return a value, you probably need to switch to HTTP trigger, read the data from HTTP request and return HTTP response.

Azure functions: how to bind query string parameters of http trigger function to SQL query of Cosmos DB

I am trying to run an http trigger Azure function with Cosmos DB input binding. I would like the url of the http trigger to include several parameters on the query string that become bound to the SQL query of the input Cosmos DB binding. I am trying the following bindings in function.json, but it does not work (the function does not even get triggered):
{
"direction": "in",
"type": "httpTrigger",
"authLevel": "anonymous",
"name": "req",
"methods": [ "get" ],
"route": "users/{age=age?}/{gender=gender?}"
},
{
"direction": "in",
"type": "documentDB",
"name": "users",
"databaseName": "Database",
"collectionName": "Users",
"sqlQuery": "SELECT * FROM x where x.age = {age} and x.gender = {gender}",
"connection": "COSMOSDB_CONNECTION_STRING"
},
According to this answer the route constraint users/{age=age?}/{gender=gender?} is valid for Web API, and according to the documentation you can use any Web API Route Constraint with your parameters. Ultimately I would to like make a GET request to the Azure function that looks like api/users?age=30&gender=male. How should this be done then?
I don't think you can configure a Cosmos DB binding to values defined in query parameters e.g. ?age=30. At least I haven't seen any examples like that in the functions documentation.
But you can bind them to the route parameters to achieve the same outcome, which you have pretty much done already.
Keeping that route of users/{age}/{gender}, your Cosmos SqlQuery will then pick up those route parameters when invoking a GET on http://yourfunctionhost/yourfunction/users/30/male
GET and POST parameter will be bound, so they can be used inside the sqlQuery without any additional configuration. Just give it a try, this has definitely changed in the past
You can bind the Query-Values to CosmosDB Input Binding:
Azure Cosmos DB input binding for Azure Functions 2.x and higher
[FunctionName("DocByIdFromQueryString")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]
HttpRequest req,
[CosmosDB(
databaseName: "ToDoItems",
collectionName: "Items",
ConnectionStringSetting = "CosmosDBConnection",
Id = "{Query.id}",
PartitionKey = "{Query.partitionKey}")] ToDoItem toDoItem,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
if (toDoItem == null)
{
log.LogInformation($"ToDo item not found");
}
else
{
log.LogInformation($"Found ToDo item, Description={toDoItem.Description}");
}
return new OkResult();
}

How to call azure function with params

I am trying to use Azure function with to invoke the same function with different time and different param(url).
I didn't find any good example that shows how to pass some data. I want to pass link to function.
My code is:
var rp = require('request-promise');
var request = require('request');
module.exports = function (context //need to get the url) {
and the function
{
"bindings": [
{
"name": "myTimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 0 */1 * * *"
}
],
"disabled": false
}
If your settings are relatively static (but you still don't want to hard code them), you may use app settings to store them, and then read e.g.
let url = process.env['MyUrl'];
If URL should be determined per request, you may use HTTP trigger and read the URL from query parameters:
let url = req.query.myurl;
I'm not sure what exactly you are trying to achieve with parameterized timer-triggered function.
Another possibility is if your parameters are stored somewhere in e.g. Azure Document DB (Cosmos).
You could still use a TimerTrigger to invoke the function, and include a DocumentDB input binding allowing you to query for the specific parameter values needed to execute the function.
Here's a C# example triggered by Timer, with a DocumentDB input binding. Note: I'm using the latest VS2017 Preview tooling for Azure functions.
[FunctionName("TimerTriggerCSharp")]
public static void Run(
[TimerTrigger("0 */1 * * * *")]TimerInfo myTimer, TraceWriter log,
[DocumentDB("test-db-dev", "TestingCollection", SqlQuery = "select * from c where c.doc = \"Test\"")] IEnumerable<dynamic> incomingDocuments)
{..}
With the following binding json:
{
"bindings": [
{
"name": "myTimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 */5 * * * *"
},
{
"type": "documentDB",
"name": "incomingDocuments",
"databaseName": "test-db-dev",
"collectionName": "TestingCollection",
"sqlQuery": "select * from c where c.docType = \"Test\"",
"connection": "my-testing_DOCUMENTDB",
"direction": "in"
}
],
"disabled": false
}

Resources