Referencing TargetElements when when authoring Adaptive Cards - c#

I am authoring an Adaptive Card in C# and need to add an Image with a ToggleVisibilityAction. The JSON equivalent is:
{
"type": "Image",
"selectAction": {
"type": "Action.ToggleVisibility",
"title": "expand",
"targetElements": [ "REFERENCE_1", "REFERENCE_2",]
},
"url": "MY_IMAGE_URL",
"altText": "visible"
}
In the above REFERENCE_1 and REFERENCE_2 are the Id fields of the elements I want to target.
When authoring it in C#, I have
new AdaptiveImage()
{
SelectAction = new AdaptiveToggleVisibilityAction()
{
Title = "collapse",
TargetElements = REFERENCE_COLLECTION_HERE,
},
}
My challenge is that the JSON version accepts a string reference with an Id of the TargetElement but the C# version expects a List<AdaptiveTargetElement> where REFERENCE_COLLECTION_HERE is. How do I manage to reference the TargetElement while being able to add it where I want it in my card layout.

You can just use AdaptiveTargetElement objects instead of strings:
new AdaptiveImage()
{
SelectAction = new AdaptiveToggleVisibilityAction()
{
Title = "collapse",
TargetElements = new List<AdaptiveTargetElement>
{
new AdaptiveTargetElement("REFERENCE_1"),
new AdaptiveTargetElement("REFERENCE_2"),
},
},
}
You can see the documentation for them here: https://adaptivecards.io/explorer/TargetElement.html

Related

Adaptive card are not showing in webchat where as showing in MS Teams

We are using the below code to display the adaptive card in Webchat and Teams.
Its working correctly in Teams and in Webchat its displaying as a line.
Adaptive card is showing up in MS teams
Adaptive card is showing up in Webchat
Webchat and msteam are different channels so it's required additional properties for rendering actions in the bot framework.
The MS Teams Adaptive card required special property with the name msteams to the object in an object submit action’s data property in order to access this functionality.
{
"type": "Action.Submit",
"title": "Click me for messageBack",
"data": {
"msteams": {
"type": "messageBack",
"displayText": "I clicked this button",
"text": "text to bots",
"value": "{\"bfKey\": \"bfVal\", \"conflictKey\": \"from value\"}"
},
"extraData": {}
}
}
As per your card "id" property is missing in the "AdaptiveSubmitAction" and need to differentiate both channel property Data values in the implementation.
Replace the "AdaptiveActionSet" code with the below one.
new AdaptiveActionSet() {
Actions = new List < AdaptiveAction > () {
new AdaptiveSubmitAction() {
Title = ResourceString.SubmitText,
Id = ResourceString.SubmitText,
if (turnContext != null && turnContext.Activity.ChannelId == Channels.Msteams) {
Data = new Jobject {
{
"msteams",
new JObject {
{
"type",
"messageBack"
}, {
"displayText",
"Hello"
}, {
"text",
"Hello"
}
}
}
}
}
else {
Data = new JObject {
{
"type",
"messageBack"
}, {
"displayText",
"Hello"
}, {
"text",
"Hello"
}
}
}
}
}
}
Microsoft Docs : MS Team Docs

Azure ARM deployment access a resource from a different subscription when deploying a new resource

I am using an Azure service principal and C# Azure SDK to deploy this ARM template. The publicIPPrefixResourceId I am passing as a parameter is the ARM Id of the resource that is in a different subscription from where I am currently deploying to. However, the service principal has access to both subscriptions. But I get an error saying it was not able to find the publicIPPrefixResourceId in the current subscription.
I am wondering is there a way to specify that also includes other subscriptions while looking for a resource? I don't want to use a custom-managed service identity because my service principle has access to both subscriptions.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"publicIpName": {
"type": "string"
},
"publicIpSku": {
"type": "string",
"defaultValue": "Standard"
},
"publicIPPrefixResourceId": {
"type": "string",
"metadata": {
"description": "Resource Id of the PublicIpPrefix to create VM VIP"
}
}
},
"resources": [
{
"apiVersion": "2019-02-01",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[parameters('publicIpName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('publicIpSku')]"
},
"properties": {
"publicIPAllocationMethod": "Static",
"publicIPPrefix": {
"Id": "[parameters('publicIPPrefixResourceId')]"
}
}
}
]
}
C# Azure SDK that is deploying above ARM template:
var deploymentTask = azure.Deployments.Define(parameters.DeploymentName)
.WithExistingResourceGroup(resourceGroupName)
.WithTemplate(vmTemplate)
.WithParameters(deploymentParameters)
.WithMode(DeploymentMode.Incremental)
.Create();
I don't think this is doable, while looking for resource group, you are looking under a specific subscription.
The SDK you are using is our old Azure SDK for .net, I'll suggest that you try our new SDK Azure.ResourceManager and Azure.ResourceManager.Resource, the new SDK integrates with the latest Azure.Identity, you can directly use azure resource identifier to look for resources
var client = new ArmClient(new DefaultAzureCredential());
var resourceGroup = client.GetResourceGroupResource(new Azure.Core.ResourceIdentifier("/subscriptions/<Subscriptionid>/resourceGroups/<resourcegroupname>"));
ArmDeploymentCollection ArmDeploymentCollection = resourceGroup.GetArmDeployments();
string deploymentName = "myDeployment";
var input = new ArmDeploymentContent(new ArmDeploymentProperties(ArmDeploymentMode.Incremental)
{
TemplateLink = new ArmDeploymentTemplateLink()
{
Uri = new Uri("https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.storage/storage-account-create/azuredeploy.json")
},
Parameters = BinaryData.FromObjectAsJson(new JsonObject()
{
{"storageAccountType", new JsonObject()
{
{"value", "Standard_GRS" }
}
}
})
});
ArmOperation<ArmDeploymentResource> lro = await ArmDeploymentCollection.CreateOrUpdateAsync(WaitUntil.Completed, deploymentName, input);
ArmDeploymentResource deployment = lro.Value;
Or hierarchy look for the resources like below
var client = new ArmClient(new DefaultAzureCredential());
var subscriptions =client.GetSubscriptions();
foreach(var sub in subscriptions)
{
Console.WriteLine("SubID:"+sub.Data.DisplayName);
var rgs = sub.GetResourceGroups();
foreach(var rg in rgs)
{
Console.WriteLine("RgName:"+rg.Data.Name);
ArmDeploymentCollection ArmDeploymentCollection = rg.GetArmDeployments();
string deploymentName = "myDeployment";
var input = new ArmDeploymentContent(new ArmDeploymentProperties(ArmDeploymentMode.Incremental)
{
TemplateLink = new ArmDeploymentTemplateLink()
{
Uri = new Uri("https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.storage/storage-account-create/azuredeploy.json")
},
Parameters = BinaryData.FromObjectAsJson(new JsonObject()
{
{"storageAccountType", new JsonObject()
{
{"value", "Standard_GRS" }
}
}
})
});
ArmOperation<ArmDeploymentResource> lro = await ArmDeploymentCollection.CreateOrUpdateAsync(WaitUntil.Completed, deploymentName, input);
ArmDeploymentResource deployment = lro.Value;
}
}
Console.ReadLine();
The new version SDK is going to cover more Azure services very soon
It is possible to reference a resource the way you are trying to do. There are caveats however:
You need to provide the resourceId correctly, for example: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-tmp/providers/Microsoft.Network/publicIPPrefixes/prefixtemp" - see also resourceId function
You cannot reference a public IP prefix in another subscription. This is not a limitation of ARM but rather of the specific service.
all public IP addresses created from the prefix must exist in the same Azure region and subscription as the prefix.
Full "working" example:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"publicIpName": {
"type": "string"
},
"publicIpSku": {
"type": "string",
"defaultValue": "Standard"
},
"publicIPPrefixResourceId": {
"type": "string",
"metadata": {
"description": "Resource Id of the PublicIpPrefix to create VM VIP"
},
"defaultValue": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-tmp/providers/Microsoft.Network/publicIPPrefixes/prefixtemp"
}
},
"resources": [
],
"outputs": {
"ipPrefixInOtherSubscription": {
"type": "string",
"value": "[reference(parameters('publicIPPrefixResourceId'), '2021-08-01').ipPrefix]"
}
}
}
If you try to use it like you intended, you get this error:
{
"code": "ResourceReferenceUsesWrongSubscriptionId",
"message": "Property publicIPPrefix of resource /subscriptions/11111111-0000-0000-0000-000000000000/resourceGroups/rg-temp/providers/Microsoft.Network/publicIPAddresses/test can only reference resources in subscription(s) 11111111-0000-0000-0000-000000000000. Value /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-tmp/providers/Microsoft.Network/publicIPPrefixes/prefixtemp references wrong subscription.",
"details": []
}
And there is no way around that.

Converting a string to a JSON for a HTTP POST request

I'm trying to return a json file from a HTTP POST request from Slack. I am using netcoreapp3.1 along with the Newtonsoft.Json NuGet. Right now my HTTP POST function looks like this.
public async Task<ActionResult> SlashCommand([FromForm] SlackSlashCommand request)
{
var retVal = new JsonResult(GetBlock());
return retVal;
}
GetBlock() is a function that returns a class that I created. This works currently, but everytime I want to modify the json that it returns, I have to modify that class. I would really love to just have a json in string format that I can copy and paste into my code and then return to Slack in json format.
Is there a way to do this? I've been trying to use JsonConvert.DeserializeObject(str); but I'm using it incorrectly. From what I understand, that function takes in an string and converts it to an object. I need to take in a string and convert it to a Microsoft.AspNetCore.Mvc.ActionResult json.
Any help? Thanks.
An alternative option is to use an anonymous type, which will be less vulnerable to becoming invalid JSON (a simple typo in your JSON string could render the entire block of JSON unreadable):
var data = new
{
blocks = new object[] {
new {
type = "section",
text = new {
type = "plain_text",
text = "Hello!",
emoji = true
}
},
new {
type = "divider"
},
new {
type = "actions",
elements = new object[] {
new {
type = "button",
text = new {
type = "plain_text",
text = "Help"
},
value = "helpButton"
}
}
}
}
};
return new JsonResult(data);
Produces:
{
"blocks": [
{
"type": "section",
"text":
{
"type": "plain_text",
"text": "Hello!",
"emoji": true
}
},
{
"type": "divider"
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text":
{
"type": "plain_text",
"text": "help"
},
"value": "helpButton"
}
]
}
]
}
Try it online
I found an answer.
This is my JSON in string format:
string str = "{\"blocks\": [{\"type\": \"section\",\"text\": {\"type\": \"plain_text\",\"text\": \"Hello!\",\"emoji\": true}},{\"type\": \"divider\"},{\"type\": \"actions\",\"elements\": [{\"type\": \"button\",\"text\": {\"type\": \"plain_text\",\"text\": \"Help\"},\"value\": \"helpButton\"}]}]}";
And then this is my function:
public async Task<ActionResult> SlashCommand([FromForm] SlackSlashCommand request)
{
var retVal = new JsonResult(JsonConvert.DeserializeObject<object>(str));
return retVal;
}
This works quite well. Sorry if I didn't give too much info.

Alexa Skill Works in Website, returns "Something went wrong" on device

I am new to developing Alexa skills but I have created a very simple skill that is written in C# and hosted in Azure. At the moment it simply returns a JSON message.
public class AlexaController : ApiController
{
[HttpPost, Route("api/alexa/demo")]
public dynamic Pluralsight(dynamic request)
{
string version;
version = "5";
return new
{
version = "1.0",
sessionAttributes = new { },
response = new
{
outputSpeech = new
{
type = "PlainText",
text = $"Finance build is version, {version}"
},
card = new
{
type = "Simple",
title = "TeamCity",
content = "Version identifier"
},
shouldEndSession = true
}
};
}
My utterances are:
HelloWorldIntent hello
HelloWorldIntent hi
AMAZON.HelpIntent assistance needed please
AMAZON.HelpIntent i need assistance
Intent schema is:
{
"intents": [
{
"intent": "HelloWorldIntent"
},
{
"intent": "AMAZON.HelpIntent"
}
]
}
When tested with the website it works fine:
{
"version": "1.0",
"response": {
"outputSpeech": {
"text": "Finance build is version, 5",
"type": "PlainText"
},
"card": {
"content": "Version identifier",
"title": "TeamCity"
},
"speechletResponse": {
"outputSpeech": {
"text": "Finance build is version, 5"
},
"card": {
"content": "Version identifier",
"title": "TeamCity"
},
"shouldEndSession": true
}
},
"sessionAttributes": {}
}
However when I test it on my Echo Dot it says "Something went wrong".
I have checked and the skill is enabled and that it is using the correct account.
Is there anyway to get more detail to what is happening?
The problem was due to the language I had specified for the skill.
I am based in the UK and had sent the language to English (U.S.), this meant that the skill would be in the skill store for the United States (but not in the UK skill store).
Adding a new language (set to English U.K.) allowed the skill to be tested on my device in the UK.

Create WPF UI from JSON schema

Is there a way to create a WPF UI using a JSON Schema? I am aware that it is possible to convert it into a HTML form with the help of AngularJS and others. But looking for a way to create WPF out of the same has not been fruitful.
There exists a Source by Rico Suter
on how to create a Visual Json Editor. My requirement is slightly different from what is given here. In my case, I want to create WPF controls based on the schema and the properties mentioned within the schema. And, with the help of the UI, I want to be able to create as many JSON objects by entering the values into the UI controls.
For example, let's consider the below JSON schema as a sample.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {},
"id": "http://example.com/example.json",
"properties": {
"checked": {
"default": false,
"description": "An explanation about the purpose of this instance.",
"id": "/properties/checked",
"title": "The Checked Schema",
"type": "boolean"
},
"dimensions": {
"id": "/properties/dimensions",
"properties": {
"height": {
"default": 10,
"description": "An explanation about the purpose of this instance.",
"id": "/properties/dimensions/properties/height",
"title": "The Height Schema",
"type": "integer"
},
"width": {
"default": 5,
"description": "An explanation about the purpose of this instance.",
"id": "/properties/dimensions/properties/width",
"title": "The Width Schema",
"type": "integer"
}
},
"type": "object"
},
"id": {
"default": 1,
"description": "An explanation about the purpose of this instance.",
"id": "/properties/id",
"title": "The Id Schema",
"type": "integer"
},
"name": {
"default": "A green door",
"description": "An explanation about the purpose of this instance.",
"id": "/properties/name",
"title": "The Name Schema",
"type": "string"
},
"price": {
"default": 12.5,
"description": "An explanation about the purpose of this instance.",
"id": "/properties/price",
"title": "The Price Schema",
"type": "number"
},
"tags": {
"id": "/properties/tags",
"items": {
"default": "home",
"description": "An explanation about the purpose of this instance.",
"id": "/properties/tags/items",
"title": "The Empty Schema",
"type": "string"
},
"type": "array"
}
},
"type": "object"
}
I want to be able to display a checkbox for the checked property. Similarly, a GroupBox or something with 2 TextBox controls within to enter the dimensions(height and width). This UI should enable the user to enter desired values based on which a JSON object can be generated. Something like,
{
"checked": false,
"dimensions": {
"width": 5,
"height": 10
},
"id": 1,
"name": "A green door",
"price": 12.5,
"tags": [
"home",
"green"
]
}
Currently, I am creating a list of JSchema objects and and deserializing every property into type JSchema and then adding it to the list. And thereafter, I am trying to create controls for the same. This is just messy and I have not completely reached my goal. Yet I do not feel that I will be satisfied with the end result. If you could suggest a way to achieve the same it would be of great help. Thanks.
Samples taken from
here.
So, it is certainly possible. What you would need to do is define the deserialization routine to make List<T>/ObservableCollection<T> objects that implement INotifyPropeertyChanged. You could do this via Newtonsoft Json or write a JSchema to ViewModel converter
Next, you can make a ContentControl or even a Listbox/StackPanel bound to this enumerable, like a Master Detail view, and the details view can implement a Property Grid on a selected object. Example of Property grid.
Make sure all your bindings are TwoWay to preserve changes you make.
Additionally, you could implement the OnSelectionChanged event on your StackPanel to serialize the changes.
Resources
Master detail view
Property grid source
I wanted to do same thing for UWP, but didn't find a working solution that I could use.
Besides PropertyGrid mentioned above I also found
DataGrid from Windows Community Toolkit and DataForm from Telerik.
Using those still would require converting Json to Object model and back.
As it turned out Newtonsoft.Json is built with databinding in mind, so it is pretty easy to generate controls from Json that would bind to Json properties.
Here is a code snippet to do that:
private void RenderForm(JArray jArray)
{
StackPanel stackPanel = new StackPanel() { Orientation = Orientation.Vertical };
this.Content = stackPanel;
stackPanel.Height = this.Height;
stackPanel.Width = this.Width;
stackPanel.Children.Add(button);
foreach (JObject element in jArray)
{
String type = element["type"].ToString();
TextBlock textBlock = new TextBlock() { Text = element["name"].ToString() };
textBlock.Padding = new Thickness() { Top = 5 };
switch (type)
{
case "hiddendata":
break;
case "bool":
CheckBox checkBox = new CheckBox();
checkBox.DataContext = element;
Binding checkBoxBinding = new Binding() { Path = new PropertyPath("[value].Value"), Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
checkBoxBinding.Source = element;
checkBox.SetBinding(CheckBox.IsCheckedProperty, checkBoxBinding);
stackPanel.Children.Add(textBlock);
stackPanel.Children.Add(checkBox);
break;
case "image":
if (!String.IsNullOrEmpty(element["value"].Value<String>()))
{
Image image = new Image();
image.MaxHeight = 200;
image.MaxWidth = 200;
var ignore = SetImageSource(element["value"].Value<String>(), image);
stackPanel.Children.Add(textBlock);
stackPanel.Children.Add(image);
}
break;
case "info":
if (!String.IsNullOrEmpty(element["value"].Value<String>()))
{
TextBlock displayTextBlock = new TextBlock();
displayTextBlock.DataContext = element;
Binding displayTextBlockBinding = new Binding() { Path = new PropertyPath("[value].Value"), Mode = BindingMode.OneWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
displayTextBlockBinding.Source = element;
displayTextBlock.SetBinding(TextBlock.TextProperty, displayTextBlockBinding);
stackPanel.Children.Add(textBlock);
stackPanel.Children.Add(displayTextBlock);
}
break;
case "password":
PasswordBox passwordBox = new PasswordBox();
passwordBox.DataContext = element;
Binding passwordBoxBinding = new Binding() { Path = new PropertyPath("[value].Value"), Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
passwordBoxBinding.Source = element;
passwordBox.SetBinding(PasswordBox.PasswordProperty, passwordBoxBinding);
stackPanel.Children.Add(textBlock);
stackPanel.Children.Add(passwordBox);
break;
case "string":
default:
TextBox textBox = new TextBox();
textBox.DataContext = element;
Binding textBoxBinding = new Binding() { Path = new PropertyPath("[value].Value"), Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
textBoxBinding.Source = element;
textBox.SetBinding(TextBox.TextProperty, textBoxBinding);
stackPanel.Children.Add(textBlock);
stackPanel.Children.Add(textBox);
break;
}
}
}

Categories

Resources