How to obtain a user ID and OAuth access token in Dialogflow - dialogflow-es

In a webhook (written in Java), as part of an action configured for account linking with the following topology:
Actions-on-Google->Dialogflow->Webhook,
I am trying to extract the userId and OAuth accessToken from the incoming requests.
Both initial request (before the linking flow is started):
[java] 02-25-2019 16:41:33 [qtp2056234595-232] INFO domain.lola.user.utils.http.ControllerUtils [toString:30] - Received AoG Request {
[java] "responseId": "8da3a8c2-2d60-4675-b249-a39bbc1840c9",
[java] "queryResult": {
[java] "queryText": "GOOGLE_ASSISTANT_WELCOME",
[java] "parameters": {
[java] "any": ""
[java] },
[java] "allRequiredParamsPresent": true,
[java] "fulfillmentMessages": [{
[java] "text": {
[java] "text": [""]
[java] }
[java] }],
[java] "outputContexts": [{
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/google_assistant_welcome",
[java] "parameters": {
[java] "any.original": "",
[java] "any": ""
[java] }
[java] }, {
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/actions_capability_screen_output",
[java] "parameters": {
[java] "any.original": "",
[java] "any": ""
[java] }
[java] }, {
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/google_assistant_input_type_voice",
[java] "parameters": {
[java] "any.original": "",
[java] "any": ""
[java] }
[java] }, {
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/actions_capability_audio_output",
[java] "parameters": {
[java] "any.original": "",
[java] "any": ""
[java] }
[java] }, {
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/actions_capability_media_response_audio",
[java] "parameters": {
[java] "any.original": "",
[java] "any": ""
[java] }
[java] }, {
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/actions_capability_web_browser",
[java] "parameters": {
[java] "any.original": "",
[java] "any": ""
[java] }
[java] }],
[java] "intent": {
[java] "name": "projects/speechbank-e8a15/agent/intents/f645f492-f6dc-4e7e-8da6-45711c654ad0",
[java] "displayName": "RawText"
[java] },
[java] "intentDetectionConfidence": 1.0,
[java] "languageCode": "en-us"
[java] },
[java] "originalDetectIntentRequest": {
[java] "source": "google",
[java] "version": "2",
[java] "payload": {
[java] "isInSandbox": true,
[java] "surface": {
[java] "capabilities": [{
[java] "name": "actions.capability.AUDIO_OUTPUT"
[java] }, {
[java] "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
[java] }, {
[java] "name": "actions.capability.WEB_BROWSER"
[java] }, {
[java] "name": "actions.capability.SCREEN_OUTPUT"
[java] }]
[java] },
[java] "inputs": [{
[java] "rawInputs": [{
[java] "query": "open speech Bank",
[java] "inputType": "VOICE"
[java] }],
[java] "intent": "actions.intent.MAIN"
[java] }],
[java] "user": {
[java] "userStorage": "{\"data\":{}}",
[java] "lastSeen": "2019-02-25T16:40:39Z",
[java] "locale": "en-US",
[java] "userId": "ABwppHFQHUBr0RrWA_OuL-kK2sxTPUvQtL3D-x2Ydr-7uxLt9zzEFzJrGB-X96d9XY8k9XTJj-RUg9WpzGB9jg"
[java] },
[java] "conversation": {
[java] "conversationId": "ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg",
[java] "type": "NEW"
[java] },
[java] "availableSurfaces": [{
[java] "capabilities": [{
[java] "name": "actions.capability.AUDIO_OUTPUT"
[java] }, {
[java] "name": "actions.capability.WEB_BROWSER"
[java] }, {
[java] "name": "actions.capability.SCREEN_OUTPUT"
[java] }]
[java] }]
[java] }
[java] },
[java] "session": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg"
[java] }
and the post-linking one:
[java] "responseId": "2aa05bec-9fd3-4387-8e87-ef70509395db",
[java] "queryResult": {
[java] "queryText": "actions_intent_SIGN_IN",
[java] "parameters": {
[java] },
[java] "allRequiredParamsPresent": true,
[java] "fulfillmentMessages": [{
[java] "text": {
[java] "text": [""]
[java] }
[java] }],
[java] "outputContexts": [{
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/actions_capability_screen_output"
[java] }, {
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/actions_intent_sign_in",
[java] "parameters": {
[java] "SIGN_IN": {
[java] "#type": "type.googleapis.com/google.actions.v2.SignInValue",
[java] "status": "OK"
[java] },
[java] "text": ""
[java] }
[java] }, {
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/_actions_on_google",
[java] "lifespanCount": 98,
[java] "parameters": {
[java] "data": "{}"
[java] }
[java] }, {
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/actions_capability_audio_output"
[java] }, {
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/actions_capability_media_response_audio"
[java] }, {
[java] "name": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg/contexts/actions_capability_web_browser"
[java] }],
[java] "intent": {
[java] "name": "projects/speechbank-e8a15/agent/intents/88bbeb61-b612-47b1-b0e5-8d0de392a0c3",
[java] "displayName": "SignInIntent"
[java] },
[java] "intentDetectionConfidence": 1.0,
[java] "languageCode": "en-us"
[java] },
[java] "originalDetectIntentRequest": {
[java] "source": "google",
[java] "version": "2",
[java] "payload": {
[java] "isInSandbox": true,
[java] "surface": {
[java] "capabilities": [{
[java] "name": "actions.capability.WEB_BROWSER"
[java] }, {
[java] "name": "actions.capability.AUDIO_OUTPUT"
[java] }, {
[java] "name": "actions.capability.SCREEN_OUTPUT"
[java] }, {
[java] "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
[java] }]
[java] },
[java] "inputs": [{
[java] "rawInputs": [{
[java] }],
[java] "arguments": [{
[java] "extension": {
[java] "#type": "type.googleapis.com/google.actions.v2.SignInValue",
[java] "status": "OK"
[java] },
[java] "name": "SIGN_IN"
[java] }, {
[java] "name": "text"
[java] }],
[java] "intent": "actions.intent.SIGN_IN"
[java] }],
[java] "user": {
[java] "userStorage": "{\"data\":{}}",
[java] "lastSeen": "2019-02-25T16:40:39Z",
[java] "accessToken": "token1",
[java] "locale": "en-US",
[java] "userId": "ABwppHFQHUBr0RrWA_OuL-kK2sxTPUvQtL3D-x2Ydr-7uxLt9zzEFzJrGB-X96d9XY8k9XTJj-RUg9WpzGB9jg"
[java] },
[java] "conversation": {
[java] "conversationId": "ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg",
[java] "type": "ACTIVE",
[java] "conversationToken": "[\"_actions_on_google\"]"
[java] },
[java] "availableSurfaces": [{
[java] "capabilities": [{
[java] "name": "actions.capability.WEB_BROWSER"
[java] }, {
[java] "name": "actions.capability.AUDIO_OUTPUT"
[java] }, {
[java] "name": "actions.capability.SCREEN_OUTPUT"
[java] }]
[java] }]
[java] }
[java] },
[java] "session": "projects/speechbank-e8a15/agent/sessions/ABwppHEEjeH_tnJt20xAXWhtglLPVUHLgTuf1WVfDzN_FGr46jKppLq3P--zPd3g-3890FJu7hErWAdOZAQeBg"
[java] }
seem to contain the userId field. The accessToken only starts appearing after the linking, which is correct.
However, in my webhook, in both cases I'm using the following to capture the userId and accessToken (aside from other things):
private void logUserDetails(ActionRequest request) {
String userId = request.getAppRequest().getUser().getUserId();
log.info("request.getAppRequest().getUser().getUserId()={}",userId);
String idToken = request.getUser().getIdToken();
log.info("idToken={}",idToken);
String usrId = request.getUser().getUserId();
log.info("request.getUser().getUserId()={}",usrId);
String queryText = request.getWebhookRequest().getQueryResult().getQueryText();
log.info("queryText={}", queryText);
}
and in both cases I'm seeing them both returned as null:
pre account linking:
[java] 02-25-2019 16:41:33 INFO AoGApp [logUserDetails:23] - request.getAppRequest().getUser().getUserId()=null
[java] 02-25-2019 16:41:33 INFO AoGApp [logUserDetails:26] - idToken=null
[java] 02-25-2019 16:41:33 INFO AoGApp [logUserDetails:29] - request.getUser().getUserId()=null
[java] 02-25-2019 16:41:33 INFO AoGApp [logUserDetails:32] - queryText=GOOGLE_ASSISTANT_WELCOME
post account linking:
[java] 02-25-2019 16:43:25 INFO AoGApp [logUserDetails:23] - request.getAppRequest().getUser().getUserId()=null
[java] 02-25-2019 16:43:25 INFO AoGApp [logUserDetails:26] - idToken=null
[java] 02-25-2019 16:43:25 INFO AoGApp [logUserDetails:29] - request.getUser().getUserId()=null
[java] 02-25-2019 16:43:25 INFO AoGApp [logUserDetails:32] - queryText=actions_intent_SIGN_IN
Via the Java API, queryText seems to be the only one extracting the actual value, but not so for the other fields from the incoming requests, where they are all null.
I have a couple of questions:
At what point does the userId get created by Google and what triggers its creation? In other words, would there be a case where the userId in the request is actually null, before its value has been generated, will I ever be able to see such situation in the request coming into my webhook?
How can I extract other fields from the request coming into my webhook? What am I doing wrong such that parsing the request via Java API returns different results than the values seen on a raw request?

Related

Error:ManagedEnvResourceDisallowedByPolicy,Message:Fail to create managed environment because creation of required resources was disallowed by policy

While creating Azure Container app with Networking options via Azure ARM automation script I am getting the below error:
Error Details:
{    "id": "/subscriptions/xxxx/resourceGroups/yyyy/providers/Microsoft.App/managedEnvironments/xxxxx",
    "name": "testcontainerappsenv",
    "type": "Microsoft.App/managedEnvironments",
    "location": "westeurope",
    "tags": {},
    "systemData": {
        "createdBy": "test#gmail.com",
        "createdByType": "User",
        "createdAt": "xxxx",
        "lastModifiedBy": "test#gmail.com",
        "lastModifiedByType": "User",
        "lastModifiedAt": "xxxx"
    },
    "properties": {
        "provisioningState": "Failed",
        "vnetConfiguration": {
            "internal": false,            "infrastructureSubnetId": "/subscriptions/xxxx/resourceGroups/yyy/providers/Microsoft.Network/virtualNetworks/testvnt/subnets/aaaa",
            "dockerBridgeCidr": "10.1.0.1/16",
            "platformReservedCidr": "10.0.0.0/16",
            "platformReservedDnsIP": "10.0.0.2"
        },
        "deploymentErrors": "**ErrorCode: ManagedEnvironmentResourceDisallowedByPolicy, Message: Fail to create managed environment because creation of required resources was disallowed by policy, refer to https://go.microsoft.com/fwlink/?linkid=2198255 for more detail.**",
        "defaultDomain": "xxxxxx.westeurope.azurecontainerapps.io",
        "appLogsConfiguration": {
            "destination": "log-analytics",
            "logAnalyticsConfiguration": {
                "customerId": "xxxxxxxxxx"
            }
        }
    }
}
Here goes the policy which is blocking us in this case: https://portal.azure.com/#blade/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F783ea2a8-b8fd-46be-896a-9ae79643a0b1
Can anyone help us here by providing their guidance
After troubleshooting for few hours and going deep we found that the policy : https://portal.azure.com/#blade/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F783ea2a8-b8fd-46be-896a-9ae79643a0b1 is not allowing us. In order to cross check we did a deployment without Networking and found it got created successfully.
This was not a mandatory/default policy when creating container apps. If the policy "Container apps should disable external network access" is enabled at the tenant or organizational level, so it's not allowing.
Azure Built in Policy for Azure Container Apps and know issues on this tutorial
I reproduced the problem by enabling this policy on the tenant and attempting to create a container app.
Running below ARM templates should not be permitted because traffic from the Container App was made public.
Basic templates from the portal
Template json file
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"subscriptionId": {
"type": "string"
},
"name": {
"type": "string"
},
"location": {
"type": "string"
},
"environmentId": {
"type": "string"
},
"containers": {
"type": "array"
},
"secrets": {
"type": "secureObject",
"defaultValue": {
"arrayValue": []
}
},
"registries": {
"type": "array"
},
"ingress": {
"type": "object"
}
},
"resources": [
{
"apiVersion": "2022-06-01-preview",
"name": "[parameters('name')]",
"type": "Microsoft.App/containerapps",
"kind": "containerapps",
"location": "[parameters('location')]",
"dependsOn": [],
"properties": {
"environmentId": "[parameters('environmentId')]",
"configuration": {
"secrets": "[parameters('secrets').arrayValue]",
"registries": "[parameters('registries')]",
"activeRevisionsMode": "Single",
"ingress": "[parameters('ingress')]"
},
"template": {
"containers": "[parameters('containers')]",
"scale": {
"minReplicas": 0
}
}
}
}
]
}
parameters json file
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"subscriptionId": {
"value": "********************"
},
"name": {
"value": "demoswarnaapp"
},
"location": {
"value": "westeurope"
},
"environmentId": {
"value": "/subscriptions/****************resourceGroups/Compute-Resources/providers/Microsoft.App/managedEnvironments/Testca"
},
"containers": {
"value": [
{
"name": "simple-hello-world-container",
"image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest",
"command": [],
"resources": {
"cpu": 0.25,
"memory": ".5Gi"
}
}
]
},
"registries": {
"value": []
},
"secrets": {
"value": {
"arrayValue": []
}
},
"ingress": {
"value": {
"external": true,
"targetPort": 80
}
}
}
}
Upon disable that policy its allowed and deployed successfully.

Managed application botService channel, CHANNEL_NOT_SUPPORTED error

I'm working on managed application that should contain a bot for the teams application.
At the moment the bot resources look like this:
{
"type": "Microsoft.BotService/botServices",
"name": "[concat(parameters('webAppName'), '-bs')]",
"kind": "azurebot",
"apiVersion": "2022-06-15-preview",
"location": "global",
"sku": {
"name": "F0"
},
"dependsOn": [
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('userAssignedIdentityName'))]",
"[resourceId('Microsoft.Web/sites', variables('webAppName'))]"
],
"properties": {
"displayName": "[parameters('webAppName')]",
"msaAppType": "MultiTenant",
"msaAppId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('userAssignedIdentityName')), '2018-11-30').clientId]",
"endpoint": "[concat('https://', parameters('webAppName'), '.azurewebsites.net', '/api/v1/messages')]"
}
},
{
"type": "Microsoft.BotService/botServices/channels",
"apiVersion": "2022-06-15-preview",
"name": "[concat(parameters('webAppName'), '-bs/', '-tc')]",
"location": "global",
"sku": {
"name": "F0"
},
"kind": "azurebot",
"properties": {
"channelName": "MsTeamsChannel",
"properties": {
"isEnabled": true
}
},
"dependsOn": [
"[resourceId('Microsoft.BotService/botServices', concat(parameters('webAppName'), '-bs'))]"
]
}
The Microsoft.BotService/botServices/channels part is causing the issue:
{
"status": "Failed",
"error": {
"code": "ApplianceDeploymentFailed",
"message": "The operation to create appliance failed. Please check operations of deployment 'olwa11119' under resource group '/subscriptions/19...truncated...14/resourceGroups/mrg-test_managed_medx_app-previ-20220924201448'. Error message: 'At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.'",
"details": [
{
"code": "BadRequest",
"message": "{\r\n \"error\": {\r\n \"code\": \"CHANNEL_NOT_SUPPORTED\",\r\n \"message\": \"Channel is not supported\"\r\n }\r\n}"
}
]
}
}
Any tips how can i fix this? Thanks in advance.
Eventually with the million tries and fails i have found out what's wrong with the template. To fix this you have to change the "name" of the "Microsoft.BotService/botServices/channels" to "MsTeamsChannel", same as "channelName" in "properties".
It's not documented anywhere, even more, documentation is extremely misleading:
string (required)
Character limit: 2-64
Valid characters:
Alphanumerics, underscores, periods, and hyphens.
Start with alphanumeric.
So my working template now looks like this:
{
"type": "Microsoft.BotService/botServices/channels",
"apiVersion": "2022-06-15-preview",
"name": "[concat(parameters('webAppName'), '-bs', '/', 'MsTeamsChannel')]",
"location": "global",
"sku": {
"name": "F0"
},
"kind": "azurebot",
"properties": {
"channelName": "MsTeamsChannel",
"properties": {
"isEnabled": true,
"acceptedTerms": true
}
},
"dependsOn": [
"[resourceId('Microsoft.BotService/botServices', concat(parameters('webAppName'), '-bs'))]"
]
}
Hope this is going to save lots of time to someone.

How can I transform the JSON in app logic?

I got to as far as a json result from log analytic query API HTTP action call:
{
"tables": [
{
"name": "PrimaryResult",
"columns": [
{
"name": "TimeGenerated",
"type": "datetime"
},
{
"name": "_queue",
"type": "string"
},
{
"name": "_messages",
"type": "real"
}
],
"rows": [
[
"2022-06-03T03:20:00Z",
"queue1",
8073
],
[
"2022-06-03T03:20:00Z",
"queue2",
570
]
]
}
]
}
I need transform it to this following format, which essentially an adaptivecards.io card
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "**TimeGenerated**" //Columns[0].Name
},
{
"type": "TextBlock",
"text": "2022-06-03T03:20:00Z" //Rows[0][0]
},
{
"type": "TextBlock",
"text": "2022-06-03T03:20:00Z" //Rows[1][0]
}
]
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "**_queue**" //Columns[1].Name
},
{
"type": "TextBlock",
"text": "queue1" //Rows[0][1]
},
{
"type": "TextBlock",
"text": "queue2" //Rows[1][1]
}
]
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "**_messages**"
},
{
"type": "TextBlock",
"text": "8073"
},
{
"type": "TextBlock",
"text": "570"
}
]
}
]
}
How can I do this? I tried with nested for-loop action, however I'm stuck in composing the final variable that can be inserted in the right place.
It would be ideal to hold a transformed variable ahead of time so that I can just include it in the later stage as POST to another webhook.
Edit: Whilst the column is static, the number rows are dynamic.
Create a LogicApp and load in this definition to see how to do it ...
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Initialize_AdaptiveCard_JSON": {
"inputs": {
"variables": [
{
"name": "XML",
"type": "string",
"value": "{\n \"type\": \"ColumnSet\",\n \"columns\": [\n {\n \"type\": \"Column\",\n \"items\": [\n {\n \"type\": \"TextBlock\",\n \"text\": \"**#{variables('Object Variable')['tables'][0]['columns'][0]['Name']}**\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][0][0]}\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][1][0]}\"\n }\n ]\n },\n {\n \"type\": \"Column\",\n \"items\": [\n {\n \"type\": \"TextBlock\",\n \"text\": \"**#{variables('Object Variable')['tables'][0]['columns'][1]['Name']}**\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][0][1]}\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][1][1]}\"\n }\n ]\n },\n {\n \"type\": \"Column\",\n \"items\": [\n {\n \"type\": \"TextBlock\",\n \"text\": \"**#{variables('Object Variable')['tables'][0]['columns'][2]['Name']}**\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][0][2]}\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"#{variables('Object Variable')['tables'][0]['rows'][1][2]}\"\n }\n ]\n }\n ]\n}"
}
]
},
"runAfter": {
"Initialize_Object": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_Object": {
"inputs": {
"variables": [
{
"name": "Object Variable",
"type": "object",
"value": {
"tables": [
{
"columns": [
{
"name": "TimeGenerated",
"type": "datetime"
},
{
"name": "_queue",
"type": "string"
},
{
"name": "_messages",
"type": "real"
}
],
"name": "PrimaryResult",
"rows": [
[
"2022-06-03T03:20:00Z",
"queue1",
8073
],
[
"2022-06-03T03:20:00Z",
"queue2",
570
]
]
}
]
}
}
]
},
"runAfter": {},
"type": "InitializeVariable"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"Recurrence": {
"evaluatedRecurrence": {
"frequency": "Month",
"interval": 12
},
"recurrence": {
"frequency": "Month",
"interval": 12
},
"type": "Recurrence"
}
}
},
"parameters": {}
}
Basically, you just need to navigate to the specific section of the JSON to retrieve what you need for each individual value.
This is an example of what I'm talking about ...
variables('Object Variable')['tables'][0]['columns'][1]['Name']
... that will get you to the name property for the second column in the first table.
My own solution. Essentially it's broken down into two stages: Initialize the column header and then append the rows at 2nd stage.
Pseudo code
var columns = [];
// initialize column header
for (i=0; i<input.tables[0].columns.length; i++) {
columns.add({
"type": "Column",
"items": [{
"type": "TextBlock",
"text": "**input.tables[0].columns[i].name**",
}]
});
}
// populate row
for (j=0; j<input.tables[0].rows.length; j++) {
for (i=0; i<input.tables[0].columns.length; i++) {
var thisColumn = columns[i];
thisColumn.items.add({
"type": "TextBlock",
"text": "input.tables[0].row[j][i]"
});
}
}
Full solution:
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose_Final": {
"inputs": {
"columns": "#variables('columns')",
"type:": "ColumnSet"
},
"runAfter": {
"Until": [
"Succeeded"
]
},
"type": "Compose"
},
"Initialize_Input": {
"inputs": {
"variables": [
{
"name": "input",
"type": "object",
"value": {
"tables": [
{
"columns": [
{
"name": "_queue",
"type": "string"
},
{
"name": "_messages",
"type": "real"
}
],
"name": "PrimaryResult",
"rows": [
[
"queue1",
8073
],
[
"queue2",
570
],
[
"queue3",
666
]
]
}
]
}
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Initialize_variable_columnIdx": {
"inputs": {
"variables": [
{
"name": "columnIdx",
"type": "integer",
"value": 0
}
]
},
"runAfter": {
"Initialize_variable_columns": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Initialize_variable_columns": {
"inputs": {
"variables": [
{
"name": "columns",
"type": "array",
"value": []
}
]
},
"runAfter": {
"Initialize_Input": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Select_Column_Header": {
"inputs": {
"from": "#variables('input')?['tables'][0]['columns']",
"select": {
"items": [
{
"text": "**#{item()['name']}**",
"type": "TextBlock"
}
],
"type": "Column"
}
},
"runAfter": {
"Initialize_variable_columnIdx": [
"Succeeded"
]
},
"type": "Select"
},
"Until": {
"actions": {
"Append_to_array_variable": {
"inputs": {
"name": "columns",
"value": {
"items": "#outputs('Compose')",
"type": "Column"
}
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "AppendToArrayVariable"
},
"Compose": {
"inputs": "#union(body('Select_Column_Header')[variables('columnIdx')]['items'], body('Select'))",
"runAfter": {
"Select": [
"Succeeded"
]
},
"type": "Compose"
},
"Increment_variable": {
"inputs": {
"name": "columnIdx",
"value": 1
},
"runAfter": {
"Append_to_array_variable": [
"Succeeded"
]
},
"type": "IncrementVariable"
},
"Select": {
"inputs": {
"from": "#variables('input')?['tables'][0]['rows']",
"select": {
"text": "#item()[variables('columnIdx')]",
"type": "TextBlock"
}
},
"runAfter": {},
"type": "Select"
}
},
"expression": "#greaterOrEquals(variables('columnIdx'), length(variables('input')?['tables'][0]['columns']))",
"limit": {
"count": 60,
"timeout": "PT1H"
},
"runAfter": {
"Select_Column_Header": [
"Succeeded"
]
},
"type": "Until"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"Recurrence": {
"evaluatedRecurrence": {
"frequency": "Month",
"interval": 12
},
"recurrence": {
"frequency": "Month",
"interval": 12
},
"type": "Recurrence"
}
}
},
"parameters": {}
}

InvalidResourceNamespace error when attempting to deploy Event Grid Subscription for Azure Function

I have an existing Event Grid Topic and want to add an Event Subscription to it, with an existing Azure Function endpoint.
To achieve this I am using Linked Templates. ARM Template validation passes, but deployment fails, with quite a peculiar error:
"InvalidResourceNamespace: "The resource namespace 'subscriptions' is invalid. (Code: InvalidResourceNamespace)"
Here's the raw error:
{
"code": "DeploymentFailed",
"message": "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.",
"details": [
{
"code": "InvalidContentLink",
"message": "Unable to download deployment content from 'https://storagearmtpl.blob.core.windows.net/arm-tpl-service-cd-11111-artifacts/nestedtemplates/eventGridSubscriptionTemplate.json?sv=sasartifactsstring'. The tracking Id is '11111111'. Please see https://aka.ms/arm-deploy for usage details."
},
{
"code": "InvalidResourceNamespace",
"message": "The resource namespace 'subscriptions' is invalid."
}
]
}
Where sasartifactsstring is a valid artifacts locations sas token. (I'm assuming, it looks correct)
I understand that this error stems from the "type" of the resource in the template being invalid, but as you can see below, it's simply Microsoft.EventGrid/topics/providers/eventSubscriptions.
/nestedtemplates/eventGridSubscriptionTemplate.json
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"subscriptionName": {
"type": "string",
"metadata": {
"description": "Name of the event grid subscription"
}
},
"topicName": {
"type": "string",
"metadata": {
"description": "Event grid Topic Name to Subscribe."
}
},
"functionResourceGroupName": {
"type": "string",
"metadata": {
"description": "Resource group name for functionapp"
}
},
"functionAppName": {
"type": "string",
"metadata": {
"description": "function app name"
}
},
"subscriptionId": {
"type": "string",
"metadata": {
"description": "The id string of the Azure subscription"
}
},
"topicResourceGroupName": {
"type": "string",
"metadata": {
"description": "The name of the topic resource group"
}
}
},
"variables": {},
"resources": [
{
"name": "[concat(parameters('topicName'), '/Microsoft.EventGrid/', parameters('subscriptionName'))]",
"type": "Microsoft.EventGrid/topics/providers/eventSubscriptions",
"location": "[resourceGroup().location]",
"apiVersion": "2020-06-01",
"properties": {
"topic": "[concat('/subscriptions/', parameters('subscriptionId'), '/resourceGroups/', parameters('topicResourceGroupName'), 'providers/Microsoft.EventGrid/topics/', parameters('topicName'))]",
"destination": {
"endpointType": "AzureFunction",
"properties": {
"resourceId": "[concat('/subscriptions/', parameters('subscriptionId'), '/resourceGroups/', parameters('functionResourceGroupName'), '/providers/Microsoft.Web/sites/', parameters('functionAppName'), '/functions/HelloWorld')]",
"maxEventsPerBatch": 1,
"preferredBatchSizeInKilobytes": 64
}
},
}
},
"dependsOn": [
]
}
],
"outputs": {}
}
/azuredeploy.json
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"_artifactsLocation": {
"type": "string"
},
"_artifactsLocationSasToken": {
"type": "securestring"
},
"functionResourceGroupName": {
"type": "string"
},
"functionAppName": {
"type": "string"
},
"topicName": {
"type": "string"
},
"topicResourceGroupName": {
"type": "string"
},
"subscriptionId": {
"type": "string"
}
},
"variables": {
"templateFolder": "nestedtemplates",
"subscriptionTemplateFileName": "eventGridSubscriptionTemplate.json"
},
"resources": [
{
"name": "functionsubscription"
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10"
"resourceGroup": "[parameters('topicResourceGroupName')]",
"dependsOn": [ ],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('_artifactsLocation'), '/', variables('templateFolder'), '/', variables('subscriptionTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"subscriptionName": {
"value": "FunctionSubscription"
},
"topicName": {
"value": "[parameters('topicName')]"
},
"functionResourceGroupName": {
"value": "[parameters('functionResourceGroupName')]"
},
"functionAppName": {
"value": "[parameters('functionAppName')]"
},
"topicResourceGroupName": {
"value": "[parameters('topicResourceGroupName')]"
},
"subscriptionId": {
"value": "[parameters('subscriptionId')]"
}
}
}
}
]
}
azure deploy parameters file
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
,
"functionResourceGroupName": {
"value": "functionResourceGroup"
},
"functionAppName": {
"value": "functionAppName"
},
"subscriptionId": {
"value": "1111111111"
},
"topicName": {
"value": "topicName"
},
"topicResourceGroupName": {
"value": "topicResourceGroup"
}
}
Really not sure what I'm doing wrong here. Worth noting that the first error is annoying as well and I'm not sure why it can't download the deployment content...
update/edit:
It's worth noting that the release is using Classic Azure Release Pipelines. And the error comes up during the release.
Looking into the deployment logs for the resource group, I was able to see that the deployment was trying to deploy an invalid resource, with the type starting "subscriptions/.....", so at least I know where the error is coming from. Still investigating what is causing this misread...
The cause of the odd 'subscriptions' error was due to the fact that I inserted a new resource into my resources list in my deployment (parent) ARM template, but not at the end of the list.
Once I put the deployment resource that references the new event grid subscription at the end of the resources list, the error did not show up.
azuredeploy.json
resources: [
{all other existing deployment resources ....},
{
"name": "functionsubscription"
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10"
"resourceGroup": "[parameters('topicResourceGroupName')]",
"dependsOn": [ ],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('_artifactsLocation'), '/', variables('templateFolder'), '/', variables('subscriptionTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"subscriptionName": {
"value": "FunctionSubscription"
},
"topicName": {
"value": "[parameters('topicName')]"
},
"functionResourceGroupName": {
"value": "[parameters('functionResourceGroupName')]"
},
"functionAppName": {
"value": "[parameters('functionAppName')]"
},
"topicResourceGroupName": {
"value": "[parameters('topicResourceGroupName')]"
},
"subscriptionId": {
"value": "[parameters('subscriptionId')]"
}
}
}
}
]
However, I am still dealing with the InvalidContentLink error, the main problem of the question has been resolved.

Google Dialogflow: sentiment analysis results not present for google assistant

I am working on a dialogflow chatbot, I am on the "essential enterprise" plan and therefore I have access to sentiment analysis which works fine from the simulator panel on the left of dialogflow console, however when I switch to testing my bot on google assistant, dialogflow fulfillment code stops extracting sentiment score from the request sent by dialogflow.
Looking into the request sent in both cases, google assistant and plain dialogflow, I see that "sentiment analysis" results are in fact missing from the request object once you switch to testing on google assistant.
The two request objects are as the following:
Here is the first dialogflow request, not using google assistant (notice queryTextSentiment node near the bottom )
{
"responseId": "b76b18c6-7640-4322-b8e5-2db74cc22656-b55300fa",
"queryResult": {
"queryText": "Very difficult",
"parameters": {},
"allRequiredParamsPresent": true,
"fulfillmentText": "how do you describe the assignments of this course?",
"fulfillmentMessages": [
{
"text": {
"text": [
"how do you describe the assignments of this course?"
]
}
}
],
"outputContexts": [
{
"name": "projects/labeeb-nlddnb/agent/sessions/d66952d9-05c9-bf8a-e083-fa7ccc0bebdd/contexts/assignments_ctx",
"lifespanCount": 5
},
{
"name": "projects/labeeb-nlddnb/agent/sessions/d66952d9-05c9-bf8a-e083-fa7ccc0bebdd/contexts/difficulty_ctx",
"lifespanCount": 4,
"parameters": {
"course_name": "Machine Learning",
"course_name.original": "Machine Learning"
}
}
],
"intent": {
"name": "projects/labeeb-nlddnb/agent/intents/d2d12691-624a-43cd-8b88-b3c7116831dd",
"displayName": "difficulty"
},
"intentDetectionConfidence": 1,
"languageCode": "en",
"sentimentAnalysisResult": {
"queryTextSentiment": {
"score": -0.5,
"magnitude": 0.5
}
}
},
"originalDetectIntentRequest": {
"payload": {}
},
"session": "projects/labeeb-nlddnb/agent/sessions/d66952d9-05c9-bf8a-e083-fa7ccc0bebdd"
}
and here is the request dialogflow sends when I test from google assistant, and dialogflow doesn't set any sentiment analysis results in this case:
{
"responseId": "ce8600dc-4364-48c2-a85b-3acb4cab589e-b55300fa",
"queryResult": {
"queryText": "Very difficult",
"parameters": {},
"allRequiredParamsPresent": true,
"fulfillmentText": "how do you describe the assignments of this course?",
"fulfillmentMessages": [
{
"text": {
"text": [
"how do you describe the assignments of this course?"
]
}
}
],
"outputContexts": [
{
"name": "projects/labeeb-nlddnb/agent/sessions/ABwppHHtMXYQddZeGRTOy0mDfanYvokXr8s72lTD9omqiMy73G3B0JaA0DwvErTRc0HkvhPTmA-CIcAXuQ/contexts/assignments_ctx",
"lifespanCount": 5
},
{
"name": "projects/labeeb-nlddnb/agent/sessions/ABwppHHtMXYQddZeGRTOy0mDfanYvokXr8s72lTD9omqiMy73G3B0JaA0DwvErTRc0HkvhPTmA-CIcAXuQ/contexts/actions_capability_account_linking"
},
{
"name": "projects/labeeb-nlddnb/agent/sessions/ABwppHHtMXYQddZeGRTOy0mDfanYvokXr8s72lTD9omqiMy73G3B0JaA0DwvErTRc0HkvhPTmA-CIcAXuQ/contexts/actions_capability_media_response_audio"
},
{
"name": "projects/labeeb-nlddnb/agent/sessions/ABwppHHtMXYQddZeGRTOy0mDfanYvokXr8s72lTD9omqiMy73G3B0JaA0DwvErTRc0HkvhPTmA-CIcAXuQ/contexts/actions_capability_audio_output"
},
{
"name": "projects/labeeb-nlddnb/agent/sessions/ABwppHHtMXYQddZeGRTOy0mDfanYvokXr8s72lTD9omqiMy73G3B0JaA0DwvErTRc0HkvhPTmA-CIcAXuQ/contexts/actions_capability_web_browser"
},
{
"name": "projects/labeeb-nlddnb/agent/sessions/ABwppHHtMXYQddZeGRTOy0mDfanYvokXr8s72lTD9omqiMy73G3B0JaA0DwvErTRc0HkvhPTmA-CIcAXuQ/contexts/actions_capability_screen_output"
},
{
"name": "projects/labeeb-nlddnb/agent/sessions/ABwppHHtMXYQddZeGRTOy0mDfanYvokXr8s72lTD9omqiMy73G3B0JaA0DwvErTRc0HkvhPTmA-CIcAXuQ/contexts/google_assistant_input_type_keyboard"
},
{
"name": "projects/labeeb-nlddnb/agent/sessions/ABwppHHtMXYQddZeGRTOy0mDfanYvokXr8s72lTD9omqiMy73G3B0JaA0DwvErTRc0HkvhPTmA-CIcAXuQ/contexts/difficulty_ctx",
"lifespanCount": 4,
"parameters": {
"course_name": "Machine Learning",
"course_name.original": "Machine Learning"
}
}
],
"intent": {
"name": "projects/labeeb-nlddnb/agent/intents/d2d12691-624a-43cd-8b88-b3c7116831dd",
"displayName": "difficulty"
},
"intentDetectionConfidence": 1,
"languageCode": "en"
},
"originalDetectIntentRequest": {
"source": "google",
"version": "2",
"payload": {
"user": {
"locale": "en-US",
"lastSeen": "2019-07-24T16:47:07Z",
"userVerificationStatus": "VERIFIED"
},
"conversation": {
"conversationId": "ABwppHHtMXYQddZeGRTOy0mDfanYvokXr8s72lTD9omqiMy73G3B0JaA0DwvErTRc0HkvhPTmA-CIcAXuQ",
"type": "ACTIVE",
"conversationToken": "[\"difficulty_ctx\"]"
},
"inputs": [
{
"intent": "actions.intent.TEXT",
"rawInputs": [
{
"inputType": "KEYBOARD",
"query": "Very difficult"
}
],
"arguments": [
{
"name": "text",
"rawText": "Very difficult",
"textValue": "Very difficult"
}
]
}
],
"surface": {
"capabilities": [
{
"name": "actions.capability.ACCOUNT_LINKING"
},
{
"name": "actions.capability.MEDIA_RESPONSE_AUDIO"
},
{
"name": "actions.capability.AUDIO_OUTPUT"
},
{
"name": "actions.capability.WEB_BROWSER"
},
{
"name": "actions.capability.SCREEN_OUTPUT"
}
]
},
"isInSandbox": true,
"availableSurfaces": [
{
"capabilities": [
{
"name": "actions.capability.AUDIO_OUTPUT"
},
{
"name": "actions.capability.SCREEN_OUTPUT"
},
{
"name": "actions.capability.WEB_BROWSER"
}
]
}
],
"requestType": "SIMULATOR"
}
},
"session": "projects/labeeb-nlddnb/agent/sessions/ABwppHHtMXYQddZeGRTOy0mDfanYvokXr8s72lTD9omqiMy73G3B0JaA0DwvErTRc0HkvhPTmA-CIcAXuQ"
}
Does anyone have an explanation for that?
Sentiment Analysis is not supported for queries originating from Google Assistant.
You can find this statement in the Settings for your Dialogflow Chatbot on Dialogflow.com in the "Advanced Tab" (just below the Toggle to enable Sentiment Analysis).
In fact, sentiment is not logged either, even outside of G Assistant. Here is the log from Operations->Logging in GCP, which is where Dialogflow interactions are logged when you turn on logging. The log there is not the same as the "Raw Interaction Log" that you get from the Dialogflow UI, unfortunately. It's less detailed.
Note that the interesting stuff is all packed in textPayload - in a very hard to parse text string. All that's there is a "Score" variable, which is the confidence score. Thanks goodness that's in there at the least!
Only other way to grab the sentiment score would be to create a custom fulfillment that runs on every intent detect (which is a bummer b/c it'll slow everything down a bit) - and that logs the score separately.
:(
{
"textPayload": "Dialogflow Response : id: \"100aea48-26f8-46b5-ab3a-e48febb0dd1f-e13762d2\"\nlang: \"en\"\nsession_id: \"2e63bd78-79f3-7b02-6efd-5d9f96e1c382\"\ntimestamp: \"2020-06-28T14:08:49.563Z\"\nresult {\n source: \"agent\"\n resolved_query: \"thank you bye\"\n score: 0.68635064\n parameters {\n }\n contexts {\n name: \"__system_counters__\"\n lifespan: 1\n parameters {\n fields {\n key: \"no-input\"\n value {\n number_value: 0.0\n }\n }\n fields {\n key: \"no-match\"\n value {\n number_value: 0.0\n }\n }\n }\n }\n metadata {\n intent_id: \"e5b0d316-d0a0-40c4-b0c3-b0565712aee4\"\n intent_name: \"Closing\"\n webhook_used: \"false\"\n webhook_for_slot_filling_used: \"false\"\n is_fallback_intent: \"false\"\n }\n fulfillment {\n speech: \"closing intent matched\"\n messages {\n lang: \"en\"\n type {\n number_value: 0.0\n }\n speech {\n string_value: \"closing intent matched\"\n }\n }\n }\n}\nstatus {\n code: 200\n error_type: \"success\"\n}\n",
"insertId": "1k1ucxsfhckbar",
"resource": {
"type": "global",
"labels": {
"project_id": "qualitymanagement-eooojp"
}
},
"timestamp": "2020-06-28T14:08:49.574Z",
"severity": "INFO",
"labels": {
"protocol": "V2",
"request_id": "100aea48-26f8-46b5-ab3a-e48febb0dd1f-e13762d2",
"type": "dialogflow_response"
},
"logName": "projects/qualitymanagement-eooojp/logs/dialogflow_agent",
"trace": "2e63bd78-79f3-7b02-6efd-5d9f96e1c382",
"receiveTimestamp": "2020-06-28T14:08:49.589705622Z"
}

Resources