azure-functions-java-worker
azure-functions-java-worker copied to clipboard
Inconsistent behavior between OutputBinding<String> and OutputBinding<List<String>>
The following code results in 2 rows in the cosmos DB
@FunctionName("CosmosDBTriggerJava1")
public void run(
@CosmosDBTrigger(
name = "items",
databaseName = "db1",
collectionName = "col3",
leaseCollectionName="leases",
connectionStringSetting = "juzhucos_DOCUMENTDB",
createLeaseCollectionIfNotExists = true
)
Object[] items,
@CosmosDBOutput(name = "database",
databaseName = "db1",
collectionName = "col2",
connectionStringSetting = "juzhucos_DOCUMENTDB")
OutputBinding<String> outputItem, //this line caused error. It works with OutputBinding<List<Object>> and OutputBinding<String>
final ExecutionContext context
) {
outputItem.setValue("[{\"name\":\"value 0\"},{\"name\":\"value 1\"}]");
}
While the following code results in error
@FunctionName("CosmosDBTriggerJava")
public void run(
@CosmosDBTrigger(
name = "items",
databaseName = "db1",
collectionName = "col1",
leaseCollectionName="leases",
connectionStringSetting = "juzhucos_DOCUMENTDB",
createLeaseCollectionIfNotExists = true
)
Object[] input,
@CosmosDBOutput(name = "database",
databaseName = "db1",
collectionName = "col2",
connectionStringSetting = "juzhucos_DOCUMENTDB")
OutputBinding<List<String>> outputItem,
final ExecutionContext context
) {
List<String> items = new ArrayList<String>();
for (int i = 0; i < 2; i ++) {
items.add("{\"name\":\"value 0\"}");
}
outputItem.setValue(items);
}
[1/19/2020 8:47:13 AM] Executed 'Functions.CosmosDBTriggerJava' (Failed, Id=8e0edee5-49f5-4afc-82ff-fde57a43b67b)
[1/19/2020 8:47:13 AM] System.Private.CoreLib: Exception while executing function: Functions.CosmosDBTriggerJava. Microsoft.Azure.DocumentDB.Core: Value cannot be null.
[1/19/2020 8:47:13 AM] Parameter name: document.
This is because the first one is serialized to an array of 2 JSON objects, when doing RPC from the Java worker to the function host.
[{"name":"value 0"},{"name":"value 1"}]
While the later one is serialized to an array of 2 strings
["{\"name\":\"value 0\"}","{\"name\":\"value 1\"}"]
Can we make the later one support adding JSON string objects to the list, since the first one supports that.
Thanks for reporting this @JuntaoZhu I had exactly the problem and it was hard to debug that in Azure functions. For all others, here's a complete "Input/Output" example for multiple documents (it even works with List<Map<String, Object>>):
@FunctionName("MyFunction")
@CosmosDBOutput(
name = "cosmosDbOutput",
databaseName = "myDatabase",
collectionName = "container1",
connectionStringSetting = "cosmosDbConnection"
)
public List<Map<String, Object>> run(
@CosmosDBTrigger(
name = "cosmosDbTrigger",
databaseName = "myDatabase",
collectionName = "container2",
leaseCollectionName = "leases",
createLeaseCollectionIfNotExists = true,
connectionStringSetting = "cosmosDbConnection"
) List<String> itemStrings,
ExecutionContext context
) {
context.getLogger().log(INFO, () -> "Received " + itemStrings.size() + " items");
List<Map<String, Object>> items = itemStrings.stream()
.map(item -> deserializeItem(context, item))
.filter(Objects::nonNull)
.collect(Collectors.toList());
Set<String> itemKeys = items.stream().flatMap(item -> item.keySet().stream()).collect(Collectors.toSet());
context.getLogger().log(INFO, () -> "Discovered item keys " + itemKeys);
return items;
}
private Map<String, Object> deserializeItem(ExecutionContext context, String item) {
try {
return OBJECT_MAPPER.readValue(item, new TypeReference<Map<String, Object>>() {
});
} catch (JsonProcessingException e) {
context.getLogger().log(WARNING, () -> "Cannot deserialize '" + item + "': " + ExceptionUtils.getStackTrace(e));
return null;
}
}