StateHasChanged() does not reload page - refresh

Issue:
As mentioned in Title, StateHasChanged does not re-render the page
Objective:
I want to Refresh the page when a button is clicked
Current Code
<button #onclick="CreatePlayer">Create User</button>
#functions {
string username;
[CascadingParameter]
Task<AuthenticationState> authenticationStateTask { get; set; }
async Task CreatePlayer()
{
var authState = await authenticationStateTask;
var user = authState.User;
var player = await PlayerData.GetByEmail(user.Identity.Name);
if (player == null)
{
player = new Player()
{
Email = user.Identity.Name,
UserName = username
};
await PlayerData.Create(player);
}
await Task.Delay(50);
StateHasChanged();
}
}

Just for the record, I add my comment in an answer :
StateHasChanged just inform the component that something changes in is state, that doesn't rerender it. The component choose by itself if it has to rerender or not. You can override ShouldRender to force the component to rerender on state changed.
#code {
bool _forceRerender;
async Task CreatePlayer()
{
var authState = await authenticationStateTask;
var user = authState.User;
var player = await PlayerData.GetByEmail(user.Identity.Name);
if (player == null)
{
player = new Player()
{
Email = user.Identity.Name,
UserName = username
};
await PlayerData.Create(player);
}
_forceRerender = true;
StateHasChanged();
}
protected override bool ShouldRender()
{
if (_forceRerender)
{
_forceRerender = false;
return true;
}
return base.ShouldRender();
}
}

On the one hand, you tell the compiler that she should create an event handler for the click event, named CreatePlayer: #onclick="CreatePlayer . This attribute compiler directive, behind the scenes, creates an EventCallback<Task> handler for you, the implication of which is that you do not need to use StateHasChanged in your code at all, as this method ( StateHasChanged ) is automatically called after UI events take place.
On the other hand, you tell the compiler that the type of the button should be set to "submit". This is wrong of course... You can't have it both. Setting the type attribute to "submit", normally submit form data to the server, but In Blazor it is prevented to work that way by code in the JavaScript portion of Blazor. Do you want to submit a form data to the server ? Always recall Blazor is an SPA Application. No submit ?
Your code should be:
<button #onclick="CreatePlayer" >Create User</button>
Just for the records, ordinarily you should inject the AuthenticationStateProvider object into your components, like this:
#inject AuthenticationStateProvider AuthenticationStateProvider
and then retrieve the AuthenticationState object. This is how your code may be rewritten:
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;

Related

nswag generated service has no return logic

I have a asp.net WebAPI service for user login that takes an email and password. The api method has the following signature. LoginDto has two fileds, Email and password.
public async Task<IActionResult> Login(LoginDto dto)
Once the user is authenticated, WebAPI returns an object that has token and Id:
return Ok(new { Token = GenerateJwtTokenFromClaims(claims), Id=user.Id });
On the client side (Blazor app), I used nswag command line tool by running nswag run and it "successfully" generated the Service and Contract files. Everything complies. nswag generated code is pasted below.
When I want to use the login nswag Service, I have the following method (I also have an overloaded method with CancellationToken but I only use this method):
public System.Threading.Tasks.Task Login2Async(LoginDto body)
{
return Login2Async(body, System.Threading.CancellationToken.None);
}
The question that I have is that how do I get the response out of the nswag-generated-code that the WebAPI login sent back to the client? When I try to assign a var to the method, I get Cannot assign void to an implicitly-typed variable which makes sense since I don't see a return type. I also don't see any logic in the nswag generated service file to return the response to the caller. How do I get the response back from the nswag generated API call? Is there an option I have to set in nswag run to get a response object back? Thanks in advance.
public async System.Threading.Tasks.Task Login2Async(LoginDto body, System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Account/Login");
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
request_.Content = content_;
request_.Method = new System.Net.Http.HttpMethod("POST");
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
return;
}
else
if (status_ == 400)
{
var objectResponse_ = await ReadObjectResponseAsync<ProblemDetails>(response_, headers_).ConfigureAwait(false);
throw new ApiException<ProblemDetails>("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
Big thanks to the NSwag team, the issue is resolved. I was returning anonymous object from the WebAPI method. The correct way to do is the following. Notice that IActionResult was changed to ActionResult passing a concrete object to return to the caller.
public async Task<ActionResult<LoginDtoResponse>> Login(LoginDto dto)
then returning
return Ok(new LoginDtoResponse { Token = GenerateJwtTokenFromClaims(claims), Id=user.Id });
After that I did that, the following code was generated:
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<LoginDtoResponse>(response_, headers_).ConfigureAwait(false);
return objectResponse_.Object;
}

How to mock UnitOfWork when using transactions (NUnit and NSubstitute)

I have created a Web API using ASP.NET Core 3.1 and EF Core 5.0. Data is stored in a MSSQL database.
I have implemented UnitOfWork and repository pattern.
The UnitOfWork class has a method used to create transactions and it look like this:
//UnitOfWork
public async Task ExecuteTransaction(Func<Task> insideTransactionFunction)
{
var transaction = await _context.Database.BeginTransactionAsync();
try
{
await insideTransactionFunction();
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
throw;
}
}
Im trying to write tests, for service classses that uses the UnitOfWork class to modify the data in the SQL database. Fx when adding a user using the UserService:
//UserService
public async Task<User> AddUserAsync(User user)
{
await _unitOfWork.ExecuteTransaction(async () =>
{
//Add user locally to generate the local id
_unitOfWork.UserRepository.Add(user);
await _unitOfWork.SaveChangesAsync();
//...Some logic that uses the generated user id...
if(string.IsNullOrWhiteSpace(email.User) == false)
{
//Inviting the user returns the generated Azure User id
user.AzureUserId = _azureService.InviteUser(user.Email);
await _unitOfWork.SaveChangesAsync();
}
}
return user;
}
A test for the above method could look like this:
//UserServiceTests
public async Task AddUserAsync_UserQualifiesForAzureInvite_AzureUserIdSetOnCreatedUser()
{
//Arrange
var unitOfWork = Substitute.For<IUnitOfWork>();
var azureService = Substitute.For<IAzureService>();
var uut = new UserService(unitOfWork, azureService);
var newUser = new User { ... some setup ... };
var azureUserId = Guid.NewGuid();
azureService.InviteUser(null).ReturnsForAnyArgs(Task.FromResult(azureUserId));
//Act
var createdUser = await _uut.AddUserAsync(newUser); //Returns right away
//Assert
Assert.That(createdUser.AzureUserId, Is.EqualTo(azureUserId));
}
The problem is that the transaction created with UnitOfWork is ignored and the user is returned straight away.
public async Task<User> AddUserAsync(User user)
{
//Skipped from here...
await _unitOfWork.ExecuteTransaction(async () =>
{
//
}
//...To here
return user;
}
So my question is: How can i mock the UnitOfWork class and still be able to test code wrapped in the the transaction?
My architecture was fundamentally flawed. Check out the comments and links made by Panagiotis Kanavos.
The solution was to get rid of UnitOfWork and the repositories and it seems testable now.

Changing workflow state in Sitecore

I'm having an issue changing the workflow state for an item programmatically. The state isn't being changed no matter what I do to the field. I've tried using (new SecurityDisabler()){} and putting the item in editing mode then changing the field manually. I've noticed that the item itself has the Lock set to <r />, could this be causing an issue?
Here is some sample code of what I've tried to do:
[HttpPost]
[MultipleButton(Name = "action", Argument = "Submit")]
public ActionResult Submit(LoI model)
{
if (model.Submitted || !model.Signed)
{
return Redirect("/Profile/LoI");
}
ModifyCandidateInfo(model, true);
Session["message"] = Translate.Text("loi-submitted-message");
Session["messageClass"] = "success";
return Redirect("/Profile/LoI");
}
private static void ModifyCandidateInfo(LoI model, bool isSubmission)
{
using (new SecurityDisabler())
{
var candidateFolder = CBUtility.GetCandidateFolder();
var loi= candidateFolder.GetChildren().SingleOrDefault(loi => loi.TemplateID == LoITemplateId);
if (loi == null) return;
loi.Editing.BeginEdit();
EditFields(loi, model);
EditChildren(loi, model);
//Send emails upon submission
if (isSubmission)
{
loi.ExecuteCommand("Submit",
loi.Name + " submitted for " + model.CandidateName);
using (new SecurityDisabler())
{
loi.Editing.BeginEdit();
loi.Fields["__Workflow state"].Value = "{F352B651-341B-4CCF-89FE-BD77F5E4D540}";
loi.Editing.EndEdit();
}
}
loi.Editing.EndEdit();
}
}
I initalized the item's workflow with the following function:
public static void InitializeWorkflow(Item item, ID workflowId)
{
item.Editing.BeginEdit();
var workflow =
item.Database.WorkflowProvider.GetWorkflow(workflowId.ToString());
workflow.Start(item);
item.Editing.EndEdit();
}
The item starts at the default drafting state and executed a "Submit" command that fires off emails. Through the Sitecore UI if I hit submit it'll go to the next workflow state but not programmatically when I fire off the ExecuteCommand function. Below you'll find the ExecuteCommand function.
public static WorkflowResult ExecuteCommand(this Item item, string commandName, string comment)
{
using (new SecurityDisabler())
{
var workflow = item.Database.WorkflowProvider.GetWorkflow(item);
if (workflow == null)
{
return new WorkflowResult(false, "No workflow assigned to item");
}
var command = workflow.GetCommands(item[FieldIDs.WorkflowState])
.FirstOrDefault(c => c.DisplayName == commandName);
return command == null
? new WorkflowResult(false, "Workflow command not found")
: workflow.Execute(command.CommandID, item, comment, false);
}
}
The command fires off fine and the emails are sent but I can't figure out why the state won't change. Could someone provide me with other suggestions or a solution?
Am I reading the workflow state id correctly? I'm using the item ID for the workflow state.
I think your code is really similar to my implementation. This is my code's background.
All items have the same workflow named "WF" and it has three workflow states (Working, Awaiting Approval, and Approved). One page-item having "WF" has some rendering items and those datasource items. Suppose a content editor is ready to submit and approve the item with its related items. By hitting the "Submit" and "Approval" button in the page, all page-item's related items have the same workflow state as the page-item's one.
Most code are from Marek Musielak and this code is perfectly working in my side.
public class UpdateWorkflowState
{
// List all controls in page item
public RenderingReference[] GetListOfSublayouts(string itemId, Item targetItem)
{
RenderingReference[] renderings = null;
if (Sitecore.Data.ID.IsID(itemId))
{
renderings = targetItem.Visualization.GetRenderings(Sitecore.Context.Device, true);
}
return renderings;
}
// Return all datasource defined on one item
public IEnumerable<string> GetDatasourceValue(WorkflowPipelineArgs args, Item targetItem)
{
List<string> uniqueDatasourceValues = new List<string>();
Sitecore.Layouts.RenderingReference[] renderings = GetListOfSublayouts(targetItem.ID.ToString(), targetItem);
LayoutField layoutField = new LayoutField(targetItem.Fields[Sitecore.FieldIDs.FinalLayoutField]);
LayoutDefinition layoutDefinition = LayoutDefinition.Parse(layoutField.Value);
DeviceDefinition deviceDefinition = layoutDefinition.GetDevice(Sitecore.Context.Device.ID.ToString());
foreach (var rendering in renderings)
{
if (!uniqueDatasourceValues.Contains(rendering.Settings.DataSource))
uniqueDatasourceValues.Add(rendering.Settings.DataSource);
}
return uniqueDatasourceValues;
}
// Check workflow state and update state
public WorkflowResult ChangeWorkflowState(Item item, ID workflowStateId)
{
using (new EditContext(item))
{
item[FieldIDs.WorkflowState] = workflowStateId.ToString();
}
Sitecore.Layouts.RenderingReference[] renderings = GetListOfSublayouts(item.ID.ToString(), item);
return new WorkflowResult(true, "OK", workflowStateId);
}
// Verify workflow state and update workflow state
public WorkflowResult ChangeWorkflowState(Item item, string workflowStateName)
{
IWorkflow workflow = item.Database.WorkflowProvider.GetWorkflow(item);
if (workflow == null)
{
return new WorkflowResult(false, "No workflow assigned to item");
}
WorkflowState newState = workflow.GetStates().FirstOrDefault(state => state.DisplayName == workflowStateName);
if (newState == null)
{
return new WorkflowResult(false, "Cannot find workflow state " + workflowStateName);
}
unlockItem(newState, item);
return ChangeWorkflowState(item, ID.Parse(newState.StateID));
}
// Unlock the item when it is on FinalState
public void unlockItem(WorkflowState newState, Item item)
{
if (newState.FinalState && item.Locking.IsLocked())
{
using (new EditContext(item, false, false))
{
item["__lock"] = "<r />";
}
}
}
}

How can i write unit test for this actionfilter

public MyContext _db;
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (_db == null || !_db.ChangeTracker.HasChanges())
{
return;
}
try
{
_db.SaveChanges();
}
catch
{
}
}
This is my action filter for my wep api project. _db context object injected to this filter by per request. My point is here to call SaveChanges() method once after all processing done in service layers. My problem is how can test this filter? How can i mimic exception case that can happen in any controler or service layer and when exception throws saveChanges() never called? How can i setup the case that exception occurred in any place inside application?
I have been doing the same, last week, for my WebAPI 2 action filter.
I have an action filter that validates my ModelState and in case of any error it throws an error list with 200 HTTPcode.
The action looks like this:
public class ModelValidationActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var modelState = actionContext.ModelState;
if (!modelState.IsValid)
{
actionContext.Response = ...
}
}
}
UNIT TEST
var httpControllerContext = new HttpControllerContext
{
Request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/someUri")
{
Content = new ObjectContent(typeof(MyModel),
new MyModel(), new JsonMediaTypeFormatter())
},
RequestContext = new HttpRequestContext()
};
httpControllerContext.Request = new HttpRequestMessage();
httpControllerContext.Request.SetConfiguration(new HttpConfiguration());
var httpActionContext = new HttpActionContext { ControllerContext = httpControllerContext };
var filter = new ModelValidationActionFilterAttribute();
httpActionContext.ModelState.AddModelError("*", "Invalid model state");
// act
filter.OnActionExecuting(httpActionContext);
// assert
httpActionContext.Response.ShouldNotBe(null);
httpActionContext.Response.ShouldBeOfType(typeof (HttpResponseMessage));
var result = httpActionContext.Response.Content.ReadAsStringAsync().Result;
BaseServiceResponse<object> resultResponse =
JsonConvert.DeserializeObject<BaseServiceResponse<object>>(result);
resultResponse.Data.ShouldBe(null);
resultResponse.Messages.Count.ShouldBe(1);
resultResponse.Messages.First().Description.ShouldBe("Invalid model state");
In your case you need to Mock DB context using IDbContext interface - see here: http://aikmeng.com/post/62817541825/how-to-mock-dbcontext-and-dbset-with-moq-for-unit
If an unhandled exception occurs while executing the request then the Exception property on actionExecutedContext will contain the exception. This is part of the framework, and not something you need to test. In your tests you can simple set the Exception property manually and assert that the attribute takes the correct action.
[Fact]
public void Saves_data_on_failure()
{
var mockDbContext = new Mock<IDbContext>();
var myAttribute = new MyAttribute(mockDbContext.Object);
var executionContext = new HttpActionExecutedContext
{
Exception = new Exception("Request failed.")
};
myAttribute.OnActionExecuted(executionContext);
mockDbContext.Verify(d => d.SaveChanges());
}
You might also want to consider whether or not you want to save data for all types of exception. The data might be in an invalid/unknown state.

Get Rendered HTML From Sitecore Item

I need to get the rendered HTML output from a given Sitecore item, assuming it has a layout. I need it to be the latest version of the rendered content whether it's published or not. Using a web request approach like WebClient or HtmlAgility pack will not work because they make the request as an anonymous user which will only render the latest published version (and I need the latest version no matter the state.) Any thoughts? I have everything working I just cant find a way to impersonate or elevate the rights while I execute the page requests.
You could go the WebClient or HtmlAgility pack, but silently login the user based on a token in the query string:
public static class UserExtensions
{
public const string TokenKey = "UserToken";
public const string TokenDateKey = "UserTokenDate";
public static ID CreateUserToken(this User user)
{
if (user.IsAuthenticated)
{
var token = ID.NewID;
user.Profile.SetCustomProperty(TokenKey, token.ToString());
user.Profile.SetCustomProperty(TokenDateKey, DateTime.Now.ToString());
user.Profile.Save();
return token;
}
else
return ID.Null;
}
public static bool IsTokenValid(this User user, string token, TimeSpan maxAge)
{
var tokenId = ID.Null;
if (ID.TryParse(token, out tokenId))
{
var minDate = DateTime.Now.Add(-maxAge);
var tokenDateString = user.Profile.GetCustomProperty(TokenDateKey);
var tokenDate = DateTime.MinValue;
DateTime.TryParse(tokenDateString, out tokenDate);
if (tokenDate < minDate)
return false;
var storedToken = user.Profile.GetCustomProperty(TokenKey);
var storedTokenId = ID.NewID;
if (ID.TryParse(storedToken, out storedTokenId))
return storedTokenId == tokenId;
}
return false;
}
}
Then patch in a HttpRequestProcessor to look for the token:
public class SilentUserLogin : HttpRequestProcessor
{
public TimeSpan MaximumAge
{
get;
set;
}
public override void Process(HttpRequestArgs args)
{
var userValue = args.Context.Request.QueryString["user"];
var tokenValue = args.Context.Request.QueryString["token"];
if (!string.IsNullOrEmpty(userValue) && !string.IsNullOrEmpty(tokenValue))
{
// find user
var user = User.FromName(userValue, AccountType.User);
if (user != null)
{
// Check token is valid
if ((user as User).IsTokenValid(tokenValue, MaximumAge))
{
// log user in
AuthenticationManager.Login(user as User);
}
else
Log.Audit("User token has expired for user: '{0}'".FormatWith(user.Name), this);
}
else
Log.Audit("Failed to locate auto login user " + userValue, this);
}
}
Patch this in with a config file:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<httpRequestBegin>
<processor type="Namespace.SilentUserLogin,Assembly" patch:after="*[#type='Sitecore.Pipelines.HttpRequest.StartMeasurements, Sitecore.Kernel']">
<MaximumAge>00:02:00</MaximumAge>
</processor>
</httpRequestBegin>
</pipelines>
</sitecore>
</configuration>
Finally, call the page via WebClient or HtmlAgility:
var token = Sitecore.Context.User.CreateUserToken();
var url = new UrlString();
url.HostName = HttpContext.Current.Request.Url.Host;
url.Protocol = HttpContext.Current.Request.IsSecureConnection ? "https" : "http";
url.Path = "/";
url["sc_itemid"] = myItem.ID.ToString();
url["sc_lang"] = myItem.Language.ToString();
// Add parameters to allow accessing the master DB
url["user"] = Sitecore.Context.User.Name;
url["token"] = token.ToString();
// Call the url here
This code was cribbed from a similar situation where I needed a URL to feed to a PDF generation library, which behind the scenes fired up IE and hit the site as an anonymous user. This way we could pass a limited time security token via the query string.
You can setup a "preview" site that shows content from the master database as opposed to the public-facing published content. This article will help setting that up: How to Setup a Sitecore Preview Site to Review Content Before Publishing
Once you have this setup on a unique URL, you can then make a WebRequest to pages or use HtmlAgilityPack.