LUIS ActionBinding Param Library doesnt display if enum - botframework

If I specify as enum then it doesn't get displayed in the dialog. Can anyone help to point out if am missing some thing?
[LuisActionBinding("CollPay", FriendlyName = "Reminder")]
public class CollPayAction : BaseLuisAction
{
public enum PaymentAmtOptions
{
[Terms(new string[] { "Full Payment", "Entire Amount", "Full Due Amount" })]
FullPayment = 1,
[Terms(new string[] { "Clubbed Payment", "Combined Payment" })]
CombinedPayment
};
[Required(ErrorMessage = "Are you planning to make a separate payment or combined one?")]
[LuisActionBindingParam(CustomType = "BOPYMTOPTION", Order = 2)]
[Template(TemplateUsage.EnumSelectOne, "Are you planning to make a separate payment or combined one? {||}",
"How would you like to make the payment - separate for each Invoice(or) clubbed with other pending dues? {||}")]
public PaymentAmtOptions PaymentAmount { get; set; }
public override Task<object> FulfillAsync()
{
var result = string.Format("Hello! You have reached the CollPay intent");
return Task.FromResult((object)result);
}
}

Thanks for reporting this, it was certainly an issue. The good news is that a PR with a patch was already created.
Once the PR is approved, you will have to update your code:
-To use the updated library
-To validate the enum value. Below you will find how the code could look like:
[LuisActionBinding("CollPay", FriendlyName = "Reminder")]
public class CollPayAction : BaseLuisAction
{
public enum PaymentAmtOptions
{
None = 0, // default - no option selected
FullPayment = 1,
CombinedPayment = 2
};
// custom validator for my enum value
public class ValidPaymentAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
return value is PaymentAmtOptions && ((PaymentAmtOptions)value) != PaymentAmtOptions.None;
}
}
[ValidPayment(ErrorMessage = "Are you planning to make a separate payment [FullPayment] or combined one [CombinedPayment]?")]
[LuisActionBindingParam(CustomType = "BOPYMTOPTION", Order = 2)]
public PaymentAmtOptions PaymentAmount { get; set; }
public override Task<object> FulfillAsync()
{
var result = string.Format("Hello! You have reached the CollPay intent");
return Task.FromResult((object)result);
}
}

Related

Xunit.net: Validating model returned by an async ASP.NET method

I have been writing unit testing for the API. I need to check whether values inside the model returned by the action method are expected or not.
[Fact]
public async Task MerchantController_GetMGetProductByMerchantId_Merchant()
{
//Arrange
var merchantId = 1000;
var merchant = new Merchant
{
MerchantId = merchantId,
MerchantEmail = "akhil#gmail.com",
MerchantName="akhil",
MerchantPassword="12345",
ConfirmPassword="12345",
};
A.CallTo(() => _merchantRepository.GetMerchantByID(1000)).Returns(merchant);
var MerchantController = new MerchantController(_merchantRepository);
//Act
var result = MerchantController.GetMerchant(merchantId);
//Assert
result.Should()
.BeOfType<Task<ActionResult<Merchant>>>();
Assert.True(merchant.Equals(result));
}
How can I check result.MerchantEmail and akhil#gmail.com are equal in the assert.
controller with the action method GetMerchant
[HttpGet("{id}")]
public async Task<ActionResult<Merchant>> GetMerchant(int id)
{
try
{
return await _repository.GetMerchantByID(id);
}
catch
{
return NotFound();
}
}
Repository for the 'GetMerchantByID'
public async Task<Merchant> GetMerchantByID(int MerchantId)
{
return await _context.Merchants.FindAsync(MerchantId);
}
I need to check the value contained in the merchant model and how I can do it.
Merchant model
public class Merchant
{
[Key]
public int MerchantId { get; set; }
[Required(ErrorMessage = "Field can't be empty")]
[DataType(DataType.EmailAddress, ErrorMessage = "E-mail is not valid")]
public string? MerchantEmail { get; set; }
public string? MerchantName { get; set; }
[DataType(DataType.PhoneNumber)]
[Display(Name = "Phone Number")]
public string? MerchantPhoneNumber { get; set; }
[Display(Name = "Please enter password"), MaxLength(20)]
public string? MerchantPassword { get; set; }
[NotMapped]
[Display(Name = "ConfirmPassword")]
[Compare("MerchantPassword", ErrorMessage = "Passwords don not match")]
public string? ConfirmPassword { get; set; }
}
can someone help me to find a way if it is possible or suggest some resources
Some notes:
GetMerchant returns a Task<T>, so you need to await this call.
ActionResult<T> supports an implicit cast to T
Since Merchant is a class, using Equals will use reference equality. In your particular case, you're assuming that the same instance of Merchant that was passed into the controller is returned. Then you also don't need to use BeOfType.
You're mixing FluentAssertions and xUnit assertions.
So, you can rewrite the last part as:
// Act
Merchant result = await MerchantController.GetMerchant(merchantId);
// Assert
result.Should().BeSameAs(merchant);
One final design-related comment. A controller is an implementation detail of an HTTP API. So you really should be testing the HTTP by either using OWIN (in .NET 4.x) or the HostBuilder in .NET Core and later such as is done here.
The changes I made are given, to get the value from the return type from the instance of the ActionResult use (ActionResultinstance).Value it will separate the value from return type.
[Fact]
public async Task MerchantController_GetMGetProductByMerchantId_Merchant()
{
//Arrange
var merchantId = 1000;
Merchant merchant = new Merchant
{
MerchantId = merchantId,
MerchantEmail = "akhil#gmail.com",
MerchantName = "akhil",
MerchantPassword = "12345",
ConfirmPassword = "12345",
};
A.CallTo(() => _merchantRepository.GetMerchantByID(1000)).Returns(merchant);
var MerchantController = new MerchantController(_merchantRepository);
//Act
ActionResult<Merchant> TempResult =await MerchantController.GetMerchant(merchantId);
var result = TempResult.Value;
//Assert
result.Should().BeOfType<Merchant>();
}

Validating selection field

I have a property that has an enumeration, in one of the values of the enumeration I have "help", if the user selects that option, I would like to do two things: 1. Send the user a text with help. 2. Ask the user if he wants to continue, or if he wants to leave. I do not know how to do it.
thank you very much.
public enum ContentClassification
{
Confidential_Restricted = 1 ,
Confidential_Secret = 2,
Public = 3,
Strictly_Confidential = 4,
help = 5
};
public ContentClassification ContentClassification { get; set; }
return new FormBuilder()
.Field(nameof(ContentClassification))
You may launch the Form many times, means if you get the "Help" choice from the first Form, launch another form for confirmation.
For example:
public enum ContentClassification
{
Confidential_Restricted = 1,
Confidential_Secret = 2,
Public = 3,
Strictly_Confidential = 4,
help = 5
};
public enum Validating
{
Continue,
Leave
};
[Serializable]
public class Classification
{
public ContentClassification? Choice;
public static IForm<Classification> BuildForm()
{
return new FormBuilder<Classification>()
.Message("You want to")
.Field(nameof(Choice))
.Build();
}
public Validating? Confirmation;
public static IForm<Classification> BuildConfirmForm()
{
return new FormBuilder<Classification>()
.Message("Send your message here")
.Field(nameof(Confirmation))
.Build();
}
}
And then create your RootDialog for example like this:
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
var form = new FormDialog<Classification>(new Classification(), Classification.BuildForm, FormOptions.PromptInStart, null);
context.Call(form, this.GetResultAsync);
return Task.CompletedTask;
}
private async Task GetResultAsync(IDialogContext context, IAwaitable<Classification> result)
{
var state = await result;
if (state.Choice == ContentClassification.help)
{
var form = new FormDialog<Classification>(new Classification(), Classification.BuildConfirmForm, FormOptions.PromptInStart, null);
context.Call(form, null); //change null to your result task here to handle the result.
}
}
}
You still need to implement the logic codes for other options in GetResultAsync method together with the logic codes to handle the result of second form BuildConfirmForm.

Dynamic LINQ: Comparing Nested Data With Parent Property

I've a class with following structure:
public class BestWayContext
{
public Preference Preference { get; set; }
public DateTime DueDate { get; set; }
public List<ServiceRate> ServiceRate { get; set; }
}
public class ServiceRate
{
public int Id { get; set; }
public string Carrier { get; set; }
public string Service { get; set; }
public decimal Rate { get; set; }
public DateTime DeliveryDate { get; set; }
}
and I've dynamic linq expression string
"Preference != null && ServiceRate.Any(Carrier == Preference.Carrier)"
and I want to convert above string in Dynamic LINQ as follows:
var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda<BestWayContext, bool>(condition, null).Compile();
But it showing following error:
Please correct me what am I doing wrong?
It looks like you wanted to do something like this:
var bwc = new BestWayContext
{
Preference = new Preference { Carrier = "test" },
DueDate = DateTime.Now,
ServiceRate = new List<ServiceRate>
{
new ServiceRate
{
Carrier = "test",
DeliveryDate = DateTime.Now,
Id = 2,
Rate = 100,
Service = "testService"
}
}
};
string condition = "Preference != null && ServiceRate.Any(Carrier == #0)";
var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda<BestWayContext, bool>(condition, bwc.Preference.Carrier).Compile();
bool res = expression(bwc); // true
bwc.ServiceRate.First().Carrier = "test1"; // just for testing this -> there is only one so I've used first
res = expression(bwc); // false
You want to use Preference which belong to BestWayContext but you didn't tell the compiler about that. If i write your expression on Linq i will do as follows:
[List of BestWayContext].Where(f => f.Preference != null && f.ServiceRate.Where(g => g.Carrier == f.Preference.Carrier)
);
As you see i specified to use Preference of BestWayContext.

How to get ASP.Net Web API and OData to bind a string value as a key?

I'm going through a short Web Api + OData tutorial from asp.net: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/getting-started-with-odata-in-web-api/create-a-read-only-odata-endpoint.
I downloaded the example project, and it works. But then I started playing around with the Product model that they use in the example. I added a new property to act as a key of type string instead of an integer key.
The new Product.cs:
public class Product
{
public string stringKey { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
The modified controller:
public class ProductsController : EntitySetController<Product, string>
{
static List<Product> products = new List<Product>()
{
new Product() { stringKey = "one", ID = 1, Name = "Hat", Price = 15, Category = "Apparel" },
new Product() { stringKey = "two", ID = 2, Name = "Socks", Price = 5, Category = "Apparel" },
new Product() { stringKey = "three", ID = 3, Name = "Scarf", Price = 12, Category = "Apparel" },
new Product() { stringKey = "four", ID = 4, Name = "Yo-yo", Price = 4.95M, Category = "Toys" },
new Product() { stringKey = "five", ID = 5, Name = "Puzzle", Price = 8, Category = "Toys" },
};
[Queryable]
public override IQueryable<Product> Get()
{
return products.AsQueryable();
}
protected override Product GetEntityByKey(string key)
{
return products.FirstOrDefault(p => p.stringKey == key);
}
}
The trouble is that when I go to /odata/Products(one) the string "one" is not bound to the key argument in the GetEntityByKey(string key) action. However, when I browse to odata/Products(1) then "1" does get bound to the key argument.
How can I get a string with text values to bind correctly, instead of just binding strings with numerical values?
Update
I forgot to include the WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Product>("Products");
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);
}
}
I noticed that the path /odata/Products(0011-1100) would only bind "0011" as the string key.
After some playing around with it, I've found that the following path works as I had hoped:
/odata/Products('one')
It appears the single quotes are required to read the entire string within the parentheses.

Getting an Enum to display on client side

I'm having hard time understanding how to convert an Enum value to it's corresponding name. My model is as follows:
public class CatalogRule
{
public int ID { get; set; }
[Display(Name = "Catalog"), Required]
public int CatalogID { get; set; }
[Display(Name = "Item Rule"), Required]
public ItemType ItemRule { get; set; }
public string Items { get; set; }
[Display(Name = "Price Rule"), Required]
public PriceType PriceRule { get; set; }
[Display(Name = "Value"), Column(TypeName = "MONEY")]
public decimal PriceValue { get; set; }
[Display(Name = "Exclusive?")]
public bool Exclude { get; set; }
}
public enum ItemType
{
Catalog,
Category,
Group,
Item
}
public enum PriceType
{
Catalog,
Price_A,
Price_B,
Price_C
}
A sample result from .net API:
[
{
$id: "1",
$type: "XYZ.CMgr.Models.CatalogRule, XYZ.CMgr",
ID: 1,
CatalogID: 501981,
ItemRule: 0,
Items: "198",
PriceRule: 1,
PriceValue: 0.5,
Exclude: false
},
{
$id: "2",
$type: "XYZ.CMgr.Models.CatalogRule, XYZ.CMgr",
ID: 2,
CatalogID: 501981,
ItemRule: 2,
Items: "9899",
PriceRule: 2,
PriceValue: 10.45,
Exclude: false
}
]
So in this example, I need to get Catalog for results[0].ItemRule & Price A for results[0].PriceRule. How can I accomplish this in BreezeJS??
This is easy to do in ASP.NET Web API, because it is an out-of-box feature in the default JSON serializer (Json.NET).
To see strings instead of enum numbers in JSON, just add an instance of StringEnumConverter to JSON serializer settings during app init:
var jsonFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
UPDATE: Yep, you right, this is not help with Breeze.js. Ok, you can anyway do a little magic to make enums work like strings (while new version with fix is not released).
Create a custom ContextProvider which updates all integer enum values in metadata to strings. Here it is:
public class StringEnumEFContextProvider<T> : EFContextProvider<T>
where T : class, new()
{
protected override string BuildJsonMetadata()
{
XDocument xDoc;
if (Context is DbContext)
{
xDoc = GetCsdlFromDbContext(Context);
}
else
{
xDoc = GetCsdlFromObjectContext(Context);
}
var schemaNs = "http://schemas.microsoft.com/ado/2009/11/edm";
foreach (var enumType in xDoc.Descendants(XName.Get("EnumType", schemaNs)))
{
foreach (var member in enumType.Elements(XName.Get("Member", schemaNs)))
{
member.Attribute("Value").Value = member.Attribute("Name").Value;
}
}
return CsdlToJson(xDoc);
}
}
And use it instead of EFContextProvider in your Web API controllers:
private EFContextProvider<BreezeSampleContext> _contextProvider =
new StringEnumEFContextProvider<BreezeSampleContext>();
This works well for me with current Breeze.js version (1.1.3), although I haven't checked other scenarios, like validation...
UPDATE: To fix validation, change data type for enums in breeze.[min|debug].js, manually (DataType.fromEdmDataType function, dt = DataType.String; for enum) or replace default function during app init:
breeze.DataType.fromEdmDataType = function (typeName) {
var dt = null;
var parts = typeName.split(".");
if (parts.length > 1) {
var simpleName = parts[1];
if (simpleName === "image") {
// hack
dt = DataType.Byte;
} else if (parts.length == 2) {
dt = DataType.fromName(simpleName);
if (!dt) {
if (simpleName === "DateTimeOffset") {
dt = DataType.DateTime;
} else {
dt = DataType.Undefined;
}
}
} else {
// enum
dt = DataType.String; // THIS IS A FIX!
}
}
return dt;
};
Dirty, dirty hacks, I know... But that's the solution I found
There will be a new release out in the next few days where we "change" breeze's enum behavior ( i.e. break existing code with regards to enums). In the new release enums are serialized and queried by their .NET names instead of as integers. I will post back here when the new release is out.

Resources