Pass parameters to AWS Lambda function - c#

I have a Lambda function and it supposes to take 3 parameters
public async Task<string> FunctionHandler(string pName, string dictName, ILambdaContext context)
{
//code...
}
I am using Visual Studio 2015, I published this to AWS environment, what do I put in the sample input box to invoke this function?

Personally, I've not experimented with async Task in the Lambda entry point so not able to comment on that.
However, another way to go about it is to change the Lambda function entry point to:
public async Task<string> FunctionHandler(JObject input, ILambdaContext context)
Then pull the two variables out of that like so:
string dictName = input["dictName"].ToString();
string pName = input["pName"].ToString();
Then in the AWS Web Console you enter:
{
"dictName":"hello",
"pName":"kitty"
}
Or instead one could take the JObject value and use it as shown in the following example code:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
namespace SimpleJsonTest
{
[TestClass]
public class JsonObjectTests
{
[TestMethod]
public void ForgiveThisRunOnJsonTestJustShakeYourHeadSayUgghhhAndMoveOn()
{
//Need better names than dictName and pName. Kept it as it is a good approximation of software potty talk.
string json = "{\"dictName\":\"hello\",\"pName\":\"kitty\"}";
JObject jsonObject = JObject.Parse(json);
//Example Zero
string dictName = jsonObject["dictName"].ToString();
string pName = jsonObject["pName"].ToString();
Assert.AreEqual("hello", dictName);
Assert.AreEqual("kitty", pName);
//Example One
MeaningfulName exampleOne = jsonObject.ToObject<MeaningfulName>();
Assert.AreEqual("hello", exampleOne.DictName);
Assert.AreEqual("kitty", exampleOne.PName);
//Example Two (or could just pass in json from above)
MeaningfulName exampleTwo = JsonConvert.DeserializeObject<MeaningfulName>(jsonObject.ToString());
Assert.AreEqual("hello", exampleTwo.DictName);
Assert.AreEqual("kitty", exampleTwo.PName);
}
}
public class MeaningfulName
{
public string PName { get; set; }
[JsonProperty("dictName")] //Change this to suit your needs, or leave it off
public string DictName { get; set; }
}
}
The point is I don't know if you can have two input variables in an AWS Lambda. Odds are you can't. Besides it's probably best if you stick with a json string or object to pass in the multiple variables one needs.

Related

System.Text.Json JsonSerializer: Serialization and deserialization of 'System.Type' instances are not supported

I got an error related with security when I tried to deserialize by using `System.Text.Json JsonSerializer`.
What do I want to achieve?
I want to give the user controle to transalte some records in my database, so use can follow this scenario:
1- User can choose model of my class library.
2- After selecting a class, user will select a property(filed) from this class.
3- User will get list of values of the selected property up.
4- Last step is not here right now, user can edit a certian value.
This my piece of code:
MyPage.razor.cs:
[Inject]
private IGenericHttpClient<Type> HttpClient { get; set; }
private Type SelectedType { get; set; }
// First select a class [Class library] from HTML Select
private void OnTypeChnage(ChangeEventArgs args)
{
string FullName = "My.Models." + args.Value.ToString();
// Create type of selected class
SelectedType = Assemble.GetType(FullName, false);
}
//Call api to get all fields of this class
private async Task OnPropertChange(ChangeEventArgs args)
{
var list = await
HttpClient.GetJsonAsync($"/api/{SelectedType.Name}/all");
}
GenericHttpClient.cs
public async ValueTask<List<T>> GetJsonAsync(string url)
{
using HttpResponseMessage response = await _client.GetAsync(url);
ValidateResponse(response);
var conetnt = await response.Content.ReadAsStringAsync();
//I got the error down
return JsonSerializer.Deserialize<List<T>>(conetnt, new JsonSerializerOptions() { PropertyNameCaseInsensitive=true});
}
System.Text.Json does not support Type class due to security reasons. You send the full assembly name as a string and again try to construct the Type at the client end.
public async ValueTask<List<T>> GetJsonAsync(string url) this wont even compile, due to not specify generic information on method signature.
And also, your problem would come from the content of http response, otherwise, the Deserialize step should work fine.
I copied your code and make a small block that prove it.
// Define somewhere
public class GenericHttpClient
{
public List<T> GetJsonAsync<T>()
{
var content = "[{\"TestProp\": \"This is some test\"}]";
return JsonSerializer.Deserialize<List<T>>(content, new JsonSerializerOptions() { PropertyNameCaseInsensitive=true});
}
}
public class Test
{
public string TestProp { get; set; }
}
// Test it
var test = new GenericHttpClient();
var result = test.GetJsonAsync<Test>();
Like what #Mayur Ekbote mentioned up, "System.Text.Json does not support Type class due to security reasons." I will add a solution but I don't think this solution is very efficient.
Change Type to Dynamic:
[Inject]
private IGenericHttpClient<dynamic> HttpClient { get; set; }
Use JsonElement to get the value as a string:
private async Task OnPropertChange(ChangeEventArgs args)
{
var langCode = CultureInfo.CurrentCulture.Name;
PropertyValueList.Clear();
var list = await HttpClient.GetJsonAsync($"/api/{SelectedType.Name}/all");
List<object> listValue = new List<object>();
SelectedProperty = args.Value.ToString();
string fieldName = char.ToLower(SelectedProperty[0]) + SelectedProperty.Substring(1);
foreach (var item in list)
{
//Convert object to JsonElement
var val = ((JsonElement)item).GetProperty(fieldName).GetString();
PropertyValueList.Add(val);
}
}
Why is it not efficient?
Because I got a list of value String instead of list of selected class.

How to run the same Nunit test twice programmatically?

I have using RestSharp to test APIs and I have a delete call that I want to run twice in the same method.
The delete call will delete with two different query params. It only can take one query param at a time so I want to call it twice with the two different query params. What is the best optimized solution to this.
The example below I am deleting with the user id 1 and I want to also delete with user id 2
[Test]
public void DeletedUser()
{
response = HttpDelete("url");
QueryParam.Add("id", 1);
Assert.statusCode(200, response);
}
I have used Andy solution to use TestCase attribute but I get an syntax error when trying to not hard code the data being used.
Error Message: "An attribute argument must be a constant expression , typeof expression or array creation expression of an attribute parameter type"
ex..
public static string data = "1"
[TestCase(data)] //Getting the error here
[Test]
public void DeletedUser(string id)
{
response = HttpDelete("url");
QueryParam.Add("id", id);
Assert.statusCode(200, response);
}
I need to run the call using two dynamic test data. The data gets generated from a Post call before the Delete call and it gets saved and serialized into a class where I have the data variables..
Here is an example of the class where the test data is stored
public class Data
{
public class UserData
{
public string id1;
public string id2;
public UserData()
{
id1 = "";
id2 = "";
}
}
}
This is the Post call and how the data is being saved.
[Test]
public void AddUser()
{
response = HttpPost("url", modle);
Data data = new Data()
data.UserData.id1 = response.content;
}
How can I now use this data.UserData.id1 in my TestCase attribute
You can make use of NUnit's [TestCase] attribute to run a test multiple times with different parameters.
Example
[TestCase(1)]
[TestCase(2)]
public void DeletedUser(int id)
{
response = HttpDelete("url");
QueryParam.Add("id", id);
Assert.statusCode(200, response);
}
You can extend this with as many parameters as needed to complete a test. For example, if you expect different responses for different IDs:
[TestCase(1, 200)]
[TestCase(2, 404)]
public void DeletedUser(int id, int expectedResponseCode)
{
response = HttpDelete("url");
QueryParam.Add("id", id);
Assert.statusCode(expectedResponseCode, response);
}
Full documentation is available here.
Update
In response to your further question about testing with dynamic data, as Charlie said you can only reference literals or literal constants from an attribute so you won't be able to use [TestCase] with dynamic data.
Instead you could use the [TestCaseSource] attribute. Create a static method that retrieves your test data and returns an array, and then quote the name of this method in the attribute.
private static int[] GetIdsToDelete() {
// UserData userData = ... read in data
return new int[] {
userData.id1,
userData.id2
}
}
[TestCaseSource(nameof(GetIdsToDelete))]
public void DeletedUser(int id)
{
response = HttpDelete("url");
QueryParam.Add("id", id);
Assert.statusCode(200, response);
}
You can find the full documentation for TestCaseSource here.

How to deserialize luis entity in IRecognizerConvert

In Luis, I created a simple pattern with a simple entity like this:
list bots {Name}
where "Name" is my entity that I would like to get in C#. The pattern and intent works fine and I am getting that correctly.
I follow the official example and built a IRecognizerConvert class so I can deserialize the result. It deserialize the intent just fine but fail to deserialize the entity.
In the _Entities sub-class, I only have the "Name" variable that I am trying to deserialize and nothing else. I don't have any other partial class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Newtonsoft.Json;
using Microsoft.Bot.Builder.AI.Luis;
namespace EmptyBot1.Models
{
public class ChatbotIntent : IRecognizerConvert
{
public string Text;
public string AlteredText;
public enum Intent
{
CreateBot,
ListBots,
ListAllBots,
RunBot,
Cancel,
Greet,
None
};
public Dictionary<Intent, IntentScore> Intents;
public class _Entities
{
public string Name;
}
public _Entities Entities;
[JsonExtensionData(ReadData = true, WriteData = true)]
public IDictionary<string, object> Properties { get; set; }
public void Convert(dynamic result)
{
var _result = JsonConvert.DeserializeObject<ChatbotIntent>(JsonConvert.SerializeObject(result, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));
Text = _result.Text;
AlteredText = _result.AlteredText;
Intents = _result.Intents;
Entities = _result.Entities;
Properties = _result.Properties;
}
public (Intent intent, double score) TopIntent()
{
Intent maxIntent = Intent.None;
var max = 0.0;
foreach (var entry in Intents)
{
if (entry.Value.Score > max)
{
maxIntent = entry.Key;
max = entry.Value.Score.Value;
}
}
return (maxIntent, max);
}
}
}
In the previous snippet, the important part is the _Entities class which define how the entities look from coming back from Luis. Since I only have 1 simple string entity "Name", I thought this is sufficient.
public class _Entities
{
public string Name;
}
However when I run it and I give it an utterance like:
"list bots mybots"
Luis would correctly assign Name="mybots" and get the correct intent, but it crash on the JsonConvert.DeserializeObject line saying the json format is incorrect. I assume this is complaining about the class I made? And not the actual JSON result from luis?
What do I need to add to the _Entities class so the luis entity can be successfully deserialzied?
I know this is an old question but I'm facing the same situation now so I want to contribute with the step-by-step that worked for me.
As #ranusharao and Bill said, you need to download LUISGen from GitHub.
Start a CMD, go to your solution's directory
cd C:\MySolutionFolder
and run
luis init
if you haven't done yet.
It will ask you for your App ID and information that you get in luis.ai.
After that, go to luis.ai / Manage / Versions, click on your current version and click Export as Json.
Place your JSON file in your solution's folder.
Once you have done that, run the following command in your console:
LUISGen C:\MyJSONPath\MyBot.json -cs MyClassName -o C:\MySolutionFolder
That -cs stands for C#, but if you are usign Typescript then change it for "-ts".
So there you have it, you can access your class with something like:
var result = await _luisRecognizerService._recognizer.RecognizeAsync<MyClassName>(turnContext, cancellationToken);
Console.WriteLine(result.Entities.Producto);
_luisRecognizerService is my instance of LuisRecognizer (dependency injection)
As recommended by #ranusharao, using LUISGen tool, a class will automatically be generated that works with the bot framework.

Parse Json_encoded array from PHP in C# UWP

I developing a UWP app using C#, this conect a PHP WebService, this web service return a array encoded using json_encode function, i cant parse this json string in my UWP app, please help
1: PHP CODE
$aCli[]= array("CAT"=>"OK","MSG"=>"SESION-OK","EXTRA"=>array("ID"=>"$spID","NOM"=>"$spNom"));
echo json_encode($aCli);
2: The result using postman is:
[{"CAT":"OK","MSG":"SESION-OK","EXTRA":{"ID":"3","NOM":"CHARLS"}}]
3. The result using Async Task from C# is:
"[{\"CAT\":\"OK\",\"MSG\":\"SESION-OK\",\"EXTRA\":{\"ID\":\"3\",\"NOM\":\"CHARLS\"}}]"
4. How to deserialize this string?, i am trying it using
using Windows.Data.Json;
5. This is the code using in this time
sJSON= await IniciarSesion();//this use async class to connect with webservice
JsonObject objJson = JsonObject.Parse(sJSON);//error is raised in this line
/*Json String is invalid*/
sCat = objJson["CAT"].GetString();
sMsg = objJson["MSG"].GetString();
Important comment above:
your JSON is a representation of an array, not an object.
IF you can use Newtonsoft (JSON.Net), here's one way, with dependency on JSON.Net, not Windows.Data.Json. Trivial sample only, improve as necessary (null checks, etc.)
using System;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
var str = "[{\"CAT\":\"OK\",\"MSG\":\"SESION-OK\",\"EXTRA\":{\"ID\":\"3\",\"NOM\":\"CHARLS\"}}]";
var j = JArray.Parse(str);
var token = j[0];
//using dynamic to simplify sample, create/use your own type
var obj = token.ToObject<dynamic>();
Console.WriteLine(obj.CAT);
Console.WriteLine(obj.MSG);
Console.WriteLine(obj.EXTRA);
Console.WriteLine(obj.EXTRA.ID);
Console.WriteLine(obj.EXTRA.NOM);
}
}
Hth..
You could use Newtonsoft to deserialize JSON string directly. For your requirement, you need to make the data model fist.
[{"CAT":"OK","MSG":"SESION-OK","EXTRA":{"ID":"3","NOM":"CHARLS"}}]
public class Pet
{
public string Cat { get; set; }
public string Msg { get; set; }
public Extra Extra { get; set; }
}
public class Extra
{
public string Id { get; set; }
public string Nom { get; set; }
}
Usage
private void Button_Click(object sender, RoutedEventArgs e)
{
var str = "[{\"CAT\":\"OK\",\"MSG\":\"SESION-OK\",\"EXTRA\":{\"ID\":\"3\",\"NOM\":\"CHARLS\"}}]";
var items = JsonConvert.DeserializeObject<List<Pet>>(str);
}
For more you could refer Serializing and Deserializing JSON documentation.
The problem is with the brackets ' [] ' at the beginning and end of the string,
You should trim them like:
' YOUR_JSON_STRING = YOUR_JSON_STRING.trim('[',']');
and then parse the json string below:
' dynamic result = JsonConvert.DeserializeObject(YOUR_JSON_STRING)'

complex object is null in Web API method using Httpclient

I have Web API service deployed and and consuming in another web application. Web API method take complex object (List object) and results also complex object.
So I created local models for Input parameter and results model to match with Web API complex objects in web application. then I passed JsonConvert.SerializeObject for that parameter. But when I debug in Web API that parameter value showing null.
Web application
[Serializable]
public class PreferencesInput
{
public string ShortName { get; set; }
public string ShortNameDescription { get; set; }
.....
}
[Serializable]
public class PreferencesOuput
{
public bool Status { get; set; }
public string Error { get; set; }
}
public class HomeController : Controller
{
public ActionResult Index()
{
RunAsync().Wait();
return View();
}
private static async Task RunAsync()
{
var inputs = new List<PreferencesInput>();
var input = new PreferencesInput
{
ShortName = "REGION",
ShortNameDescription = "Geographical regions",
OptedInFlag = true
};
inputs.Add(input);
....
...
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:8585/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
HttpResponseMessage response = await client.GetAsync("preferences/updatepreferences/?id='3016523'
&optInInterestAreas=" + JsonConvert.SerializeObject(inputs) +
"&solicitationFlag=false").ConfigureAwait(false);;
if (response.IsSuccessStatusCode)
{
string results = await response.Content.ReadAsStringAsync();
var myList = JsonConvert.DeserializeObject<List<PreferencesOuput>>(results);
}
web API
[Route("preferences/updatepreferences")]
[HttpGet]
public PreferencesOuput UpdatePreferences(string id, IEnumerable<PreferencesInput> optInInterestAreas, bool solicitationFlag)
{
.....
}
Only difference is Web application Input model has less parameters than the Web API model.
What I am doing wrong here?
IEnumerable<PreferencesInput> optInInterestAreas is null
update
I can see serialization date like below before sending to Web API call, In Web API method it is showing null, rest of the parameters are showing correct.
[{"ShortName":"REGION","ShortNameDescription":"Geographical regions","ShortSubName":null,"Description":null,"OptedInFlag":true},
{"ShortName":"REGION","ShortNameDescription":"Asia Pacific","ShortSubName":"ASIA_PACIFIC","Description":null,"OptedInFlag":true},
{"ShortName":"REGION","ShortNameDescription":"Canada","ShortSubName":"CANADA","Description":null,"OptedInFlag":true}]
You could try to specify the route with parameters. Something like:
[Route("preferences/updatepreferences/{id}/{optInInterestAreas}/{solicitationFlag:bool}")]
Your optInInterestAreas parameter is null because in Web API, the parameter binding rules specify that anything other than a "simple" parameter type (string, int, etc) is assumed to be passed in the body, not the route or query string as you're doing. You could get this to work by using the [FromUri] attribute on that parameter or by defining a custom type converter, but I would highly recommend changing your API as it does not follow generally accepted best practices.
By convention, GET is assumed to be side-effect-free, but I'm guessing something called UpdatePreferences almost certainly changes data. I would consider using a different verb and passing the updated preferences in the body. POST is better, but if you want it to be truly RESTful, you should ensure that the URI uniquely identifies the resource and use PUT.
I would start by changing your input model to something like this:
public class PreferencesInput
{
public IList<InterestArea> InterestAreas { get; set; }
public bool SolicitationFlag { get; set; }
}
public class InterestArea
{
public string ShortName { get; set; }
public string ShortNameDescription { get; set; }
...
}
Then define your API action like this:
[Route("preferences/{id}")]
[HttpPut]
public PreferencesOuput UpdatePreferences(string id, PreferencesInput preferences)
{
...
}
As you can see, the URI now uniquely identifies the thing, and the verb specifies what you want to "do"; in this case, completely replace whatever is at that URI (if anything) with the thing you are passing.
Side-note:
On the MVC side, calling Wait() in your Index action is blocking a thread while waiting for your async method to complete. That's a serious invitation for deadlocks. Async only works properly if you go "all the way" with it. In this case it's incredibly easy - just change the Index action to:
public async Task<ActionResult> Index()
{
await RunAsync();
return View();
}

Categories