Im trying to implement nunit tests with fluentvalidations, but it shows me a failed test and the message: FluentValidation.AsyncValidatorInvokedSynchronouslyException : Validator "AddVehicleCommandValidator" contains asynchronous rules but was invoked synchronously. Please call ValidateAsync rather than Validate.
In my test class I have a test method:
[Test]
public async Task ShouldRequireUniqueId()
{
var vehicle = await SendAsync(new AddVehicleCommand
{
Vehicle = new VehicleDto
{
Maker = "BMW",
Model = "M4",
UniqueId = "C1"
}
});
await SendAsync(new AddVehicleCommand
{
Vehicle = new VehicleDto
{
Maker = "BMW",
Model = "M4",
UniqueId = "C2"
}
});
var command = new AddVehicleCommand
{
Vehicle = new VehicleDto
{
Maker = "BMW",
Model = "M4",
UniqueId = vehicle.Data.UniqueId
}
};
var exception = FluentActions.Invoking(() => SendAsync(command))
.Should().ThrowAsync<ValidationException>();
}
Method SendAsync is a generic method for using MediatR and IPipelineBehavior:
public static async Task<TResponse> SendAsync<TResponse>(IRequest<TResponse> request)
{
using var scope = _scopeFactory.CreateScope();
var mediator = scope.ServiceProvider.GetService<IMediator>();
return await mediator.Send(request);
}
also I setup nessery things for registering services at onetimesetup method:
[OneTimeSetUp]
public static void RunBeforeAnyTests()
{
var builder = WebApplication.CreateBuilder();
var services = new ServiceCollection();
var startup = new Startup(_configuration);
startup.ConfigureServices(services);
services.AddSingleton(Mock.Of<IWebHostEnvironment>(w => w.ApplicationName == "VehicleReservation.WebApi" && w.EnvironmentName == "Development"));
_scopeFactory = services.BuildServiceProvider().GetService<IServiceScopeFactory>();
//_checkpoint = new Checkpoint();
}
So why this test fails? Tnx
UPDATE#1:
[Test]
public async Task ShouldRequireUniqueId()
{
var vehicle = await SendAsync(new AddVehicleCommand
{
Vehicle = new VehicleDto
{
Maker = "BMW",
Model = "M4",
UniqueId = "C1"
}
});
await SendAsync(new AddVehicleCommand
{
Vehicle = new VehicleDto
{
Maker = "BMW",
Model = "M4",
UniqueId = "C2"
}
});
var command = new AddVehicleCommand
{
Vehicle = new VehicleDto
{
Maker = "BMW",
Model = "M4",
UniqueId = vehicle.Data.UniqueId
}
};
var exception = await FluentActions.Awaiting(() => SendAsync(command))
.Should().ThrowAsync<ValidationException>();
}
The error message you describes an asynchronous validator is synchronously invoked. See https://docs.fluentvalidation.net/en/latest/async.html
So somewhere in your code you're blocking the validation.
FluentActions.Invoke takes an Action/Func<T>.
For async execution, use Awaiting
await FluentActions.Awaiting(() => SendAsync(command))
.Should().ThrowAsync<ValidationException>();
Related
I'm currently working on a project that has to rely heavily on MQTT - one of the parts that needs to utilize MQTT is a ASP Net API, but I'm having difficulties receiving messages.
Here is my MQTTHandler:
public MQTTHandler()
{
_mqttUrl = Properties.Resources.mqttURL ?? "";
_mqttPort = Properties.Resources.mqttPort ?? "";
_mqttUsername = Properties.Resources.mqttUsername ?? "";
_mqttPassword = Properties.Resources.mqttUsername ?? "";
_mqttFactory = new MqttFactory();
_tls = false;
}
public async Task<IManagedMqttClient> ConnectClientAsync()
{
var clientID = Guid.NewGuid().ToString();
var messageBuilder = new MqttClientOptionsBuilder()
.WithClientId(clientID)
.WithCredentials(_mqttUsername, _mqttPassword)
.WithTcpServer(_mqttUrl, Convert.ToInt32(_mqttPort));
var options = _tls ? messageBuilder.WithTls().Build() : messageBuilder.Build();
var managedOptions = new ManagedMqttClientOptionsBuilder()
.WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
.WithClientOptions(options)
.Build();
_mqttClient = new MqttFactory().CreateManagedMqttClient();
await _mqttClient.StartAsync(managedOptions);
Console.WriteLine("Klient startet");
return _mqttClient;
}
public async Task PublishAsync(string topic, string payload, bool retainFlag = true, int qos = 1)
{
await _mqttClient.EnqueueAsync(new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(payload)
.WithQualityOfServiceLevel((MQTTnet.Protocol.MqttQualityOfServiceLevel)qos)
.WithRetainFlag(retainFlag)
.Build());
Console.WriteLine("Besked published");
}
public async Task SubscribeAsync(string topic, int qos = 1)
{
var topicFilters = new List<MQTTnet.Packets.MqttTopicFilter>
{
new MqttTopicFilterBuilder()
.WithTopic(topic)
.WithQualityOfServiceLevel((MQTTnet.Protocol.MqttQualityOfServiceLevel)(qos))
.Build()
};
await _mqttClient.SubscribeAsync(topicFilters);
}
public Status GetSystemStatus(MqttApplicationMessageReceivedEventArgs e)
{
try
{
var json = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
var status = JsonSerializer.Deserialize<Status>(json);
if (status != null)
{
return status;
}
else
{
return null;
}
}
catch (Exception)
{
throw;
}
}
The above has been tested with a console app and works as it should.
The reason I need MQTT in the APi is that a POST method has to act on the value of a topic;
In particular I need to check a systems status before allowing the post;
[HttpPost]
public async Task<ActionResult<Order>> PostOrder(Order order)
{
if (_lastStatus != null)
{
if (_lastStatus.OpStatus)
{
return StatusCode(400, "System is busy!");
}
else
{
var response = await _orderManager.AddOrder(order);
return StatusCode(response.StatusCode, response.Message);
}
}
return StatusCode(400, "Something went wrong");
}
So I will need to set up a subscriber for this controller, and set the value of _lastStatus on received messages:
private readonly MQTTHandler _mqttHandler;
private IManagedMqttClient _mqttClient;
private Status _lastStatus;
public OrdersController(OrderManager orderManager)
{
_orderManager = orderManager;
_mqttHandler = new MQTTHandler();
_mqttClient = _mqttHandler.ConnectClientAsync().Result;
_mqttHandler.SubscribeAsync("JSON/Status");
_mqttClient.ApplicationMessageReceivedAsync += e =>
{
_lastStatus = _mqttHandler.GetSystemStatus(e);
return Task.CompletedTask;
};
}
However, it's behaving a little odd and I'm not experienced enough to know why.
The first time I make a POST request, _lastStatus is null - every following POST request seem to have the last retained message.
I'm guessing that I am struggling due to stuff being asynchronous, but not sure, and every attempt I've attempted to make it synchronous have failed.
Anyone have a clue about what I'm doing wrong?
I am using .net core 2.2 with SignalR version 1.1.0. When I test the app, messages are being received by member who are NOT in the group. My groups are being dynamically created at run time based on relevant criteria, as in : var TheHub = CurrUser.Hubname; I cannot work out why group members who are NOT in the group are also receiving the messages. I am sending to GROUP and not ALL.
Please see code. Any help greatly appreciated, I am ready to pull my hair out.
My hub class
public class Chathub : Microsoft.AspNetCore.SignalR.Hub
{
public override async Task OnConnectedAsync()
{
var TheHub = CurrUser.Hubname;
await Groups.AddToGroupAsync(Context.ConnectionId, TheHub.ToString());
await base.OnConnectedAsync();
}
public Task SendMessageGroup(string user, string message)
{
var TheHub = CurrUser.Hubname;
return Clients.Group(TheHub.ToString()).SendAsync("ReceiveMessage", user, message);
}
}
My Javascript
"use strict";
document.getElementById("sendgroupButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessageGroup", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
playAudio();
});
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
document.getElementById("sendgroupButton").disabled = true;
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
connection.start().then(function () {
document.getElementById("sendgroupButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
This is how I get the current value for curruser.hubname, please see below.
#inject SignInManager<ApplicationUser> SignInManager
#inject UserManager<ApplicationUser> UserManager
#if (SignInManager.IsSignedIn(User))
{
CurrUser.CurrentUsertId = UserManager.GetUserId(User);
var ctx = new WebookContext();
var LoggedInGuestHouseName = (from Ghouse in ctx.Guesthouse
where Ghouse.UserId == CurrUser.CurrentUsertId
select Ghouse).SingleOrDefault();
//check to see if guesthouse details have been completed, if not skip this next line of code.
if( LoggedInGuestHouseName != null)
{
CurrUser.GuestHouseName = LoggedInGuestHouseName.GuestHouseName;
// add the hub to current user
CurrUser.HubId = (int) LoggedInGuestHouseName.HubId;
var Ghname = LoggedInGuestHouseName.GuestHouseName;
var GhUserEmailaddress = LoggedInGuestHouseName.Emailaddress;
var GhHuId = LoggedInGuestHouseName.HubId;
CurrUser.GuestHouseName = Ghname;
CurrUser.GuestHouseEmailaddress = GhUserEmailaddress;
var q = (from gh in ctx.Hub
where gh.HubId == GhHuId
select gh).SingleOrDefault();
var myhubname = q.HubName;
CurrUser.Hubname = myhubname;
};
}
Looks like SignalR core is not for the feint hearted. Until a authoritative book comes out, one is really walking blind. I have researched this topic blue, but alas have now given up on SignalR for now.
I'm attempting to get to grips with SignalR, to do so I'm trying to extend the functionality of the simple chat room tutorial that Microsoft provide in their documentation.
I'm now trying to add a second hub, which will allow the user to do send in integers and receive the value multiplied by 10. The hub itself is almost identical to the normal ChatHub, except with an extra step that checks the input is a number and does the multiplication.
ChatHub
public class ChatHub : Hub
{
public async Task SendMessage(string group,string user, string message)
{
await Clients.Group(group).SendAsync("ReceiveMessage", user, message);
}
public async Task AddToGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has joined the group {groupName}.");
}
}
CalcHub
public class CalcHub : Hub
{
public async Task SendMessage(string group, string user, string message)
{
var value = MultiplyByTen(message);
await Clients.Group(group).SendAsync("ReceiveMessage", user, value);
}
public async Task AddToGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has joined the group {groupName}.");
}
public string MultiplyByTen(string input)
{
bool isANumber = Int32.TryParse(input, out int value);
if (isANumber)
{
return (value * 10).ToString();
}
return "Not a number";
}
}
I have Javascript set up for my front-end, which works perfectly fine when I try to connect to the ChatHub and send messages, however when I attempt to use the connection to CalcHub, I get the Cannot send data if the connection is not in the 'Connected' State error message.
Here is how the two connections are established.
var calcConnection = new signalR.HubConnectionBuilder().withUrl("https://localhost:44309/calcHub").build();
var chatConnection = new signalR.HubConnectionBuilder().withUrl("https://localhost:44308/chatHub").build();
var activeConnection;
setConnection();
$("#hubSelector").on("change",
function(data) {
setConnection();
});
I have a simple select element that will swap the connection based on its value. SetConnection is the method that controls this, which is used at DOM ready to set the initial connection.
Both of the hubs are registered in my startup class too.
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chatHub");
routes.MapHub<CalcHub>("/calcHub");
});
If I navigate to the two addresses of the hubs https://localhost:44309/calcHub and https://localhost:44309/chatHub, I can also see that they are valid addresses as I get the Connection ID required message.
Why is my calcHub not working?
Site.js
// Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
// for details on configuring this project to bundle and minify static web assets.
// Write your JavaScript code.
$(function() {
var calcConnection = new signalR.HubConnectionBuilder().withUrl("https://localhost:44309/calcHub").build();
var chatConnection = new signalR.HubConnectionBuilder().withUrl("https://localhost:44308/chatHub").build();
var activeConnection;
//setConnection();
//$("#hubSelector").on("change",
// function(data) {
// setConnection();
// });
activeConnection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
activeConnection.start().catch(function (err) {
return console.error(err.toString());
});
$("#addgroup").on("click", function () {
var group = document.getElementById("group").value;
activeConnection.invoke("AddToGroup", group).catch(function (err) {
return console.error(err.toString());
});
$("#group-list").append("<p>" + group + "</p>");
event.preventDefault();
});
$("#sendButton").on("click", function () {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
var group = document.getElementById("group").value;
activeConnection.invoke("SendMessage", group, user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
function setConnection() {
var selectValue = $("#hubSelector").val();
if (selectValue === "chat") {
$("#activeHub").html("<span>Active Hub: Chat</span>");
activeConnection = chatConnection;
}
if (selectValue === "calc") {
$("#activeHub").html("<span>Active Hub: Calc</span>");
activeConnection = calcConnection;
}
console.log(activeConnection);
}
});
I'm trying to send list of objects from MVC to WEBAPI using below methods. API is able to able receive the list from controller but, value of each item in the list is either empty/null on API side.
Can anyone please help me to fix this?
Controller Method:
private List<FCM.Models.Facility> GetFacilityDetails()
{
var url = "http://localhost:64664/";
var facilies = new List<Facility>();
facilies.Add( new Facility{ FCLT_ID = 100, FCLT_NM = "Facility 100" });
facilies.Add( new Facility{ FCLT_ID = 200, FCLT_NM = "Facility 200" });
facilies.Add( new Facility{ FCLT_ID = 300, FCLT_NM = "Facility 300" });
var json = JsonConvert.SerializeObject(facilies);
var _client = new RestClient(url);
var request = new RestRequest("api/facility/details", Method.GET) { RequestFormat = DataFormat.Json };
facilies.ForEach(fclt =>
request.AddParameter("facilites", fclt, ParameterType.GetOrPost));
var response = _client.Execute<List<FCM.Models.Facility>>(request);
if (response.Data == null)
{
throw new Exception(response.ErrorMessage);
}
return response.Data;
}
WebAPI method:
[Route("api/facility/details")]
public IEnumerable<Facility> GetFullAddress([FromUri] IEnumerable<Facility> facilities)
{
return null;
}
Like the comment suggested you maybe want to issue a POST request instead, but if you would like to send an array with a GETrequest you could do it like this (with System.Net.Http.HttpClient):
Add a Format method to you Facility class:
public class Facility
{
public int FCLT_ID { get; set; }
public string FCLT_NM { get; set; }
public string Format(int index)
{
return $"[{index}].FCLT_ID={FCLT_ID}&[{index}].FCLT_NM={FCLT_NM}";
}
}
Define a class which can format the array values:
public class FacilityList : List<Facility>
{
public string Format()
{
var builder = new StringBuilder();
for (var i = 0; i < Count; i++)
{
builder.Append(this[i].Format(i));
if(i != Count -1)
{
builder.Append("&");
}
}
return builder.ToString();
}
}
And then issue the request:
var client = new HttpClient()
{
BaseAddress = new Uri("http://localhost:64664/"),
DefaultRequestHeaders = {Accept = {new MediaTypeWithQualityHeaderValue("application/json")}}
};
var facilities = new FacilityList
{
new Facility {FCLT_ID = 100, FCLT_NM = "Facility 100"},
new Facility {FCLT_ID = 200, FCLT_NM = "Facility 200"},
new Facility {FCLT_ID = 300, FCLT_NM = "Facility 300"}
};
var format = facilities.Format();
var response = client.GetAsync("api/facility/details?" + format).GetAwaiter().GetResult();
var result = JsonConvert.DeserializeObject<IEnumerable<Facility>>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());
This will bind to your controller action:
[Route("api/facility/details")]
public IHttpActionResult Get([FromUri] IEnumerable<Facility> facilities)
{
// Do stuff..
return Ok(facilities);
}
I have a requirement where I need to update a user's google calendar when an event is inserted in the application's fullcalendar. I am using the peleyal's example for ASP.Net MVC and OAuth for Google API (using GoogleAuthorizationCodeFlow and AuthorizationCodeMvcApp) to handle this. I believe I have set up the credentials right in the google developer console as well.
I am able to create events on the google calendar locally without a problem. But from the azure deployed site I am not able to create the event. There are no exceptions/errors either. Is there anything that needs to be done from azure side to be able to create events and to use Google API?
var finalREsult = System.Threading.Tasks.Task.Run(async () =>
{
try
{
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetaData()).AuthorizeAsync(cancellationToken);
if (result.Credential != null)
{
var service = new CalendarService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "****"
});
var startDate = patientappointment.DateScheduled.Value.ToUniversalTime();
var endDate = patientappointment.DateScheduled.Value.AddMinutes(patientappointment.AppointmentLengthMinutes).ToUniversalTime();
var myEvent = new Event
{
Summary = string.Format("{0}/{1}/{2} - {3}", patientappointment.CurrentUserName, patientappointment.LocationName, patientappointment.RoomName, patientappointment.Notes),
Location = "Ireland",
Start = new EventDateTime
{
DateTime = new DateTime(startDate.Year, startDate.Month, startDate.Day, startDate.Hour, startDate.Minute, 0),
TimeZone = "(GMT+01:00) Dublin"
},
End = new EventDateTime
{
DateTime = new DateTime(endDate.Year, endDate.Month, endDate.Day, endDate.Hour, endDate.Minute, 0),
TimeZone = "(GMT+01:00) Dublin"
},
Recurrence = new String[] { "RRULE:FREQ=WEEKLY;BYDAY=MO" }
// Attendees = new List<EventAttendee> { new EventAttendee { Email = "**0#gmail.com" } },
};
EventsResource.InsertRequest request = service.Events.Insert(myEvent, "******#group.calendar.google.com");
request.Execute();
}
}
catch (Exception ex)
{
throw ex;
}
Looking at the requests information, I dont see the request being sent to first autheticate and get the auth code from the deployed site.
internal class ForceOfflineGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
public ForceOfflineGoogleAuthorizationCodeFlow
(AuthorizationCodeFlow.Initializer initializer)
: base((GoogleAuthorizationCodeFlow.Initializer)initializer) { }
public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(string redirectUri)
{
return new GoogleAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl))
{
ResponseType = "code",
ClientId = ClientSecrets.ClientId,
Scope = string.Join(" ", Scopes),
RedirectUri = redirectUri,
AccessType = "offline",
ApprovalPrompt = "force",
State = ""
};
}
};
public class AppFlowMetaData : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow = new ForceOfflineGoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = ConfigurationManager.AppSettings["ClientId"],
ClientSecret = ConfigurationManager.AppSettings["ClientSecret"]
},
Scopes = new[] { CalendarService.Scope.Calendar },
DataStore = null
});
public override string GetUserId(System.Web.Mvc.Controller controller)
{
return "user1";
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}