Consuming WEB API Xamarin Forms - xamarin

I have a WEB API hosted on a server, there I have a Products table with Name and Description.I already checked for the postman and this is ok, when I try to implement the method in xamarin by visual studio to bring a record by its name and display in a listview I receiving the following message
Can not implicitly convert type "void"in
"System.Collections.Generic.List"
public async void GetProductByName(string Name)
{
var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(120);
txtTest.Text =
"http://www.ProdutosAPITest6.hostname.com/api/products";
var URI = txtTest.Text + "/" + Name.ToString();
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(URI)
};
var response = await client.SendAsync(requestMessage);
if (response.IsSuccessStatusCode == true)
{
var products = await response.Content.ReadAsStringAsync();
var resultModel = JsonConvert.DeserializeObject<Product>
(products);
return resultModel;
}
}
MY CLASS
namespace XF_ConsumingWebAPI.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
MY LISTVIEW
public ListView myListView { get { return ProductsList; }}
protected override async void OnAppearing()
{
var name = //pass name;
List<ProductModel> products = await GetProductByName(Name);
if (products.Count() > 0)
{
myListView.ItemsSource = products;
}
base.OnAppearing();
}
in the line List<Produto> products = await LoadData(Name);
I'm receiving the following message
Can not implicitly convert type "void" in
"System.Collections.Generic.List<XF_ConsumingWebAPI.Models.Product>"

Try to set ItemSource in GetproductByName function instead of returning resultModel i.e.
myListView.ItemSource=resultModel

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>();
}

Accessing elements of types with runtime indexers using expression trees

I want to validate rule against input array data with runtime indexer not with some fixed zero index value.
It works if i insert data one by one in session.Insert()
It does't work if i use sesion.InsertAll() with array data
I tried providing Expression.Constant value to indexer but no action triggers
class Program
{
static void Main(string[] args)
{
RuleTestWithSingleInsertData();
// RuleTestWithInsertDataAll();
Console.ReadKey();
}
public static void RuleTestWithSingleInsertData()
{
try
{
CustomRuleRepository repository = new CustomRuleRepository();
List<RuleEngineEntity> rules = new List<RuleEngineEntity>();
rules.Add(new RuleEngineEntity { FieldName = "Age", Name = "CustomerCheck", Value = 25 });
repository.LoadRuleForTest1(rules.FirstOrDefault());
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
RuleEngineRequestModel ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = new List<Customer>() { new Customer { Name = "A", Age = 19 },
new Customer { Name = "B", Age = 26 } }.ToArray()
};
session.InsertAll(ruleEngineRequestModel.customerData);
var IspassedorNot = session.Fire();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public static void RuleTestWithInsertDataAll()
{
try
{
CustomRuleRepository repository = new CustomRuleRepository();
List<RuleEngineEntity> rules = new List<RuleEngineEntity>();
rules.Add(new RuleEngineEntity { FieldName = "Age", Name = "CustomerCheck", Value = 25 });
repository.LoadRuleForTest2(rules.FirstOrDefault());
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
RuleEngineRequestModel ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = new List<Customer>() { new Customer { Name = "A", Age = 28 },
new Customer { Name = "B", Age = 26 } }.ToArray()
};
session.InsertAll(ruleEngineRequestModel.customerData);
var IspassedorNot = session.Fire();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
public class RuleEngineRequestModel
{
public List<RuleEngineEntity> ruleList { get; set; }
public Customer[] customerData { get; set; }
public List<Customer> customerDataList { get; set; }
}
public class RuleEngineEntity
{
public string Name { get; set; }
public int Value { get; set; }
public string Operator { get; set; }
public string FieldName { get; set; }
public bool SendEmail { get; set; }
}
public class Customer
{
public string Name { get; set; }
public int Age { get; set; }
}
public class CustomRuleRepository : IRuleRepository
{
private readonly IRuleSet _ruleSet = new RuleSet("customerRule");
public IEnumerable<IRuleSet> GetRuleSets()
{
return new[] { _ruleSet };
}
public void LoadRuleForTest1(RuleEngineEntity rule)
{
_ruleSet.Add(BuildRuleForTest1(rule));
}
public void LoadRuleForTest2(RuleEngineEntity rule)
{
_ruleSet.Add(BuildRuleForTest2(rule));
}
public List<IRuleDefinition> BuildRuleForTest1(RuleEngineEntity rule)
{
return Test1(rule);
}
public List<IRuleDefinition> BuildRuleForTest2(RuleEngineEntity rule)
{
return Test2(rule);
}
public List<IRuleDefinition> Test1(RuleEngineEntity rule)
{
RuleBuilder builder = new RuleBuilder();
builder.Name("DefaultRules");
try
{
var modelPattern = builder.LeftHandSide().Pattern(typeof(Customer), "CustomerCheck");
var modelParameter = modelPattern.Declaration.ToParameterExpression();
var expres = Expression.Property(modelParameter, rule.FieldName);
var binaryExpression = Expression.GreaterThan(expres, Expression.Constant(rule.Value));
LambdaExpression expressionCondition = Expression.Lambda(binaryExpression,
modelParameter);
modelPattern.Condition(expressionCondition);
Expression<Action<IContext, Customer, RuleEngineEntity>> action =
(ctx, CustomerCheck, rules) => FireActionAsync(ctx, CustomerCheck, rules);
builder.RightHandSide().Action(action);
}
catch (Exception e)
{
// throw new Exception(e.Message);
}
var buildRule = builder.Build();
return new List<IRuleDefinition> { buildRule };
}
public List<IRuleDefinition> Test2(RuleEngineEntity rule)
{
RuleBuilder builder = new RuleBuilder();
builder.Name("DefaultRules");
try
{
var modelPattern = builder.LeftHandSide().Pattern(typeof(RuleEngineRequestModel), "CustomerCheck");
var modelParameter = modelPattern.Declaration.ToParameterExpression();
var customerDataInArray = Expression.Property(modelParameter, nameof(RuleEngineRequestModel.customerData));
var indx = Expression.Parameter(typeof(int), "index");
var customerData = Expression.ArrayIndex(customerDataInArray, indx);
var expres = Expression.Property(customerData, rule.FieldName);
var binaryExpression = Expression.GreaterThan(expres, Expression.Constant(rule.Value));
LambdaExpression expressionCondition = Expression.Lambda(binaryExpression,
modelParameter);
modelPattern.Condition(expressionCondition);
Expression<Action<IContext, Customer>> action =
(ctx, CustomerCheck) => FireActionAsync(ctx, CustomerCheck, null);
builder.RightHandSide().Action(action);
}
catch (Exception e)
{
// throw new Exception(e.Message);
}
var buildRule = builder.Build();
return new List<IRuleDefinition> { buildRule };
}
public void FireActionAsync(IContext ctx, Customer customer, RuleEngineEntity rule=null)
{
Console.WriteLine($"{rule.Name} Triggered");
}
}
Error: variable 'index' of type 'System.Int32' referenced from scope '', but it is not defined
Expected: want to validate rule against array data with dynamic indexer.
At a quick glance, it appears that you are passing in an array of Customers when inserting the facts into the session, but the rule is looking for the RuleEngineRequestModel.
Also, side note - why are you initialising an array as a List, then converting to an array, rather than just initialising as an array? i.e.
var ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = {
new Customer { Name = "A", Age = 28 },
new Customer { Name = "B", Age = 26 }
}
};
Finally, why are you inserting the rules at the same time as the data? That seems like it would cause more headaches than benefits, especially seeing as your runtime rule builder ignores them entirely.
EDIT: Having had a chance to see the updated code, this confirms my suspicions - if you use Rule 1 for both tests, it should work (rather, it will fire for each individual customer). The reason why Rule 2 never works is because it's expecting a RuleEngineRequestModel, but you are passing the IEnumerable in directly. Either pass in the request model directly and continue to use Rule 2, or scrap Rule 2 entirely.

How can I find out the value of an Id from a Tapped view cell event

I have code to call an event when a ViewCell is tapped. In the event handler I need to know the Id value from the cell that called the event. can someone help suggest how I can get this value.
Also I would like to pass this value to the categoriesPage that is being opened. How can I do this?
public class CategoryGroupWordCountVM
{
bool isToggled;
public int Id { get; set; }
public string Name { get; set; }
public bool IsToggled { get; set; }
public int TotalWordCount { get; set; }
}
List<CategoryGroupWordCountVM> categoryGroups;
foreach (var category in categoryGroups) {
var cell = new CategoryGroupTextCell { BindingContext = category };
cell.Tapped += openCategoriesPage;
section.Add(cell);
}
async void openCategoriesPage(object sender, EventArgs e)
{
var ctg = (CategoryGroupTextCell)sender;
var Id = ??
await Navigation.PushAsync(categoriesPage);
}
You can use BindingContext
For example: (assuming CategoryPageVM is viewmodel-type for the page you are navigating to on tapped event).
async void openCategoriesPage(object sender, EventArgs e)
{
var ctg = (CategoryGroupTextCell)sender;
// get id from binding-context
var id = (ctg.BindingContext as CategoryGroupWordCountVM)?.Id;
// construct or get viewmodel for the page you are navigating to
var newPageVM = new CategoryPageVM { CategoryId = id };
// assign viewmodel to page
var categoriesPage = new CategoryPage { BindingContext = newPageVM };
await Navigation.PushAsync(categoriesPage);
}
Also, this link offers more details regarding passing data during navigation.

Manually build View Model from Request Data

I have a page that currently submits via Ajax to a controller method. MVC automatically converts the request data into my View Model type, and that's great.
Now I'm trying to change it so instead of an Ajax post, it makes a SignalR call instead. I want to submit the same data via SignalR (via $('form').serialize()), and then parse the data into my view model type.
Example:
//controller
public ActionResult MyMethod(MyViewModel vm){
//vm is automatically created from form data
}
//SignalR Hub
public void MyMethodViaSignalR(string formData){
//how can I turn formData or Context.Request into a MyViewModel?
}
//Make the SignalR call
myHub($('form').serialize());
I found a generic solution. I put this code in my Base Controller (you could copy it to any one controller as well), and it will build whatever view model you need.
protected class SignalRRequestJSon
{
public string H { get; set; }
public string M { get; set; }
public List<string> A { get; set; }
public int I { get; set; }
}
public T GetModel<T>(HttpRequest req) where T : class, new()
{
var obj = JsonConvert.DeserializeObject<SignalRRequestJSon>(req["data"]);
var stringWriter = new System.IO.StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var url = req.UrlReferrer.ToString(); // this value doesn't matter, but needs to be a valid url.
var queryStringData = obj.A[0];
var httpRequest = new HttpRequest("", url, queryStringData);
var httpContext = new HttpContext(httpRequest, httpResponse);
var routeData = new RouteData();
routeData.Values.Add("controller", this.GetType().Name.ToLower().Replace("controller", ""));
routeData.Values.Add("action", "Arbitrary");
this.ControllerContext = new ControllerContext(new HttpContextWrapper(httpContext), routeData, this);
var valueProvider = new QueryStringValueProvider(this.ControllerContext);
this.ValueProvider = valueProvider;
var vm = new T();
UpdateModel(vm, valueProvider);
return vm;
}
public ActionResult Arbitrary<T>(T model)
{
return View();
}
Called like this from my SignalR hub:
var controller = DependencyResolver.Current.GetService<MyController>();
var vm = controller.GetModel<MyViewModel>(HttpContext.Current.Request);

Unit test WebApi2 passing header values

I am working on a project using WebApi2. With my test project I am using Moq and XUnit.
So far testing an api has been pretty straight forward to do a GET like
[Fact()]
public void GetCustomer()
{
var id = 2;
_customerMock.Setup(c => c.FindSingle(id))
.Returns(FakeCustomers()
.Single(cust => cust.Id == id));
var result = new CustomersController(_customerMock.Object).Get(id);
var negotiatedResult = result as OkContentActionResult<Customer>;
Assert.NotNull(negotiatedResult);
Assert.IsType<OkNegotiatedContentResult<Customer>>(negotiatedResult);
Assert.Equal(negotiatedResult.Content.Id,id);
}
Now I am moving onto something a little complicated where I need to access value from the request header.
I have created my own Ok() result by extending the IHttpActionResult
public OkContentActionResult(T content,HttpRequestMessage request)
{
_request = request;
_content = content;
}
This allows me to have a small helper that reads the header value from the request.
public virtual IHttpActionResult Post(Customer customer)
{
var header = RequestHeader.GetHeaderValue("customerId", this.Request);
if (header != "1234")
How am I meant to setup Moq with a dummy Request?
I have spent the last hour or so hunting for an example that allows me to do this with webapi however I cant seem to find anything.
So far.....and I am pretty sure its wrong for the api but I have
// arrange
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var headers = new NameValueCollection
{
{ "customerId", "111111" }
};
request.Setup(x => x.Headers).Returns(headers);
request.Setup(x => x.HttpMethod).Returns("GET");
request.Setup(x => x.Url).Returns(new Uri("http://foo.com"));
request.Setup(x => x.RawUrl).Returns("/foo");
context.Setup(x => x.Request).Returns(request.Object);
var controller = new Mock<ControllerBase>();
_customerController = new CustomerController()
{
// Request = request,
};
I am not really sure what next I need to do as I havent needed to setup a mock HttpRequestBase in the past.
Can anyone suggest a good article or point me in the right direction?
Thank you!!!
I believe that you should avoid reading the headers in your controller for better separation of concerns (you don't need to read the Customer from request body in the controller right?) and testability.
How I will do it is create a CustomerId class (this is optional. see note below) and CustomerIdParameterBinding
public class CustomerId
{
public string Value { get; set; }
}
public class CustomerIdParameterBinding : HttpParameterBinding
{
public CustomerIdParameterBinding(HttpParameterDescriptor parameter)
: base(parameter)
{
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
{
actionContext.ActionArguments[Descriptor.ParameterName] = new CustomerId { Value = GetIdOrNull(actionContext) };
return Task.FromResult(0);
}
private string GetIdOrNull(HttpActionContext actionContext)
{
IEnumerable<string> idValues;
if(actionContext.Request.Headers.TryGetValues("customerId", out idValues))
{
return idValues.First();
}
return null;
}
}
Writing up the CustomerIdParameterBinding
config.ParameterBindingRules.Add(p =>
{
return p.ParameterType == typeof(CustomerId) ? new CustomerIdParameterBinding(p) : null;
});
Then in my controller
public void Post(CustomerId id, Customer customer)
Testing the Parameter Binding
public void TestMethod()
{
var parameterName = "TestParam";
var expectedCustomerIdValue = "Yehey!";
//Arrange
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost/someUri");
requestMessage.Headers.Add("customerId", expectedCustomerIdValue );
var httpActionContext = new HttpActionContext
{
ControllerContext = new HttpControllerContext
{
Request = requestMessage
}
};
var stubParameterDescriptor = new Mock<HttpParameterDescriptor>();
stubParameterDescriptor.SetupGet(i => i.ParameterName).Returns(parameterName);
//Act
var customerIdParameterBinding = new CustomerIdParameterBinding(stubParameterDescriptor.Object);
customerIdParameterBinding.ExecuteBindingAsync(null, httpActionContext, (new CancellationTokenSource()).Token).Wait();
//Assert here
//httpActionContext.ActionArguments[parameterName] contains the CustomerId
}
Note: If you don't want to create a CustomerId class, you can annotate your parameter with a custom ParameterBindingAttribute. Like so
public void Post([CustomerId] string customerId, Customer customer)
See here on how to create a ParameterBindingAttribute

Resources