Graphql SPQR how to represent undefined fields - graphql-spqr

So I was reading this
https://github.com/leangen/graphql-spqr/issues/197
and we have
mutation createUser {
createUser(name: "Test", address: null) {
...
}
}
defined with this code
#GraphQLMutation
public User createUser(String name, Optional<Address> address) { ... }
as the way to say "Create a user with a name of Test and an undefined address" (ie do not overwrite the value of address in the database). Which is backwards, but we can't do anything about that for weird Jackson/Java reasons.
But what if I have
mutation createUser{
createUser(User: {name: "Test", address: null}){
...
}
}
defined with this code
#GraphQLMutation
public User createUser(User user) { ... }
Where a User object has a Name and an Address.
Is there a way to make this work?

Related

What is the proper way to handle callback effects and errors in Compose?

I have a main composable that wraps a NavHost to display different screens. Some of these screens need to navigate to other screens based on state changes in a ViewModel that happen as a result of method calls. Here's a trimmed down example of what I have at the moment:
class ExampleViewModel(application: Application) : AndroidViewModel(application) {
// Used by a
var error: String? by mutableStateOf(null)
private set
var user: User? by mutableStateOf(null)
private set
fun onLogin(email: String, password: String) {
viewModelScope.launch {
doLogin(email, password)
.onSuccess { user = it }
.onFailure { error = it.localizedMessage }
}
}
}
#Composable
fun LoginScreen(
navController: NavController,
exampleViewModel: ExampleViewModel,
) {
DisposableEffect(exampleViewModel.user) {
if (exampleViewModel.user != null) {
navController.navigate("somewhere")
}
onDispose {}
}
var email by rememberSaveable { mutableStateOf("") }
var password by rememberSaveable { mutableStateOf("") }
// Email TextField.
// Password TextField.
Button(onClick = { exampleViewModel.onLogin(email, password) }) {
Text("Login")
}
}
The error is handled like this in a composable up above:
LaunchedEffect(exampleViewModel.error) {
exampleViewModel.error?.let { scaffoldState.snackbarHostState.showSnackbar(it) }
}
Using a DisposableEffect in this way seems kind of dirty, and quite error prone. On top of that, this error handling method makes it difficult to, for example, disable the login form while the login is pending. Would it be better to make onLogin() suspend and handle its success and failures, and corresponding local state, in a callback inside of LoginScreen? The downside to that is that the login screen will no longer automatically redirect if it's navigated to while already logged in. snapshotFlow in a LaunchedEffect(true) is another thing I've considered, but that doesn't really seem to have any particular benefits over DisposableEffect.
What's the correct way to do this? Am I on completely the wrong track here?

Register with unique email OR unique username in ASP.NET Core MVC

I am working on a system that should allow users to register by a unique username, or unique email. Register by both is possible as well.
I am using the default identity pages with some modifications, here is a sample of the register code:
public class InputModel
{
//[Required] //Validated in server side based on role
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
//[Required] //Validated in server side based on role
[Display(Name = "Username")]
public string UserName { get; set; }
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
string username = Input.UserName ?? Input.Email;
if (ModelState.IsValid)
{
var user = new ApplicationUser
{
UserName = username,
Email = Input.Email,
Name = Input.Name,
};
}
}
Basically, if the user entered a username, Username column will be Input.UserName.
if no username (only email), the Username = Input.Email, because obviously it cannot be empty. Now both username and email are equal as the default.
examples:
Username: a#a , Email: a#a , >> no username
Username: xyz , Email: null , >> no email
Username: abc , Email: a#b , >> user entered both username and email
For now, username is always unique and always required (required by identity not the user), but not the case for the email, it can be null as expected but its not unique, I added this line in the startup.cs for the uniqueness:
services.AddIdentity<IdentityUser, IdentityRole>(options => {
options.User.RequireUniqueEmail = true;
})
but now it cannot be null, it give this validation error:
Email '' is invalid.
Please try to implement a setter for Email that converts empty string to null
I added this line in the startup.cs for the uniqueness: options.User.RequireUniqueEmail = true;
but now it cannot be null, it give this validation error: Email '' is invalid.
In source code of UserManager<TUser>.CreateAsync method, we can find that it will call ValidateUserAsync method to valid user before saving the user.
var result = await ValidateUserAsync(user);
in code of ValidateUserAsync method, we can find it call another method ValidateAsync, like below.
var result = await v.ValidateAsync(this, user);
and in ValidateAsync method, if we configured RequireUniqueEmail to true, it will valid Email of current user by calling ValidateEmail method.
if (manager.Options.User.RequireUniqueEmail)
{
await ValidateEmail(manager, user, errors);
}
Go to definition of ValidateEmail method, we can easily find it will check if the email parameter is null or System.String.Empty, or if value consists exclusively of white-space characters.
// make sure email is not empty, valid, and unique
private async Task ValidateEmail(UserManager<TUser> manager, TUser user, List<IdentityError> errors)
{
var email = await manager.GetEmailAsync(user);
if (string.IsNullOrWhiteSpace(email))
{
errors.Add(Describer.InvalidEmail(email));
return;
}
if (!new EmailAddressAttribute().IsValid(email))
{
errors.Add(Describer.InvalidEmail(email));
return;
}
var owner = await manager.FindByEmailAsync(email);
if (owner != null &&
!string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
{
errors.Add(Describer.DuplicateEmail(email));
}
}
you can try to validate if the email or username already exist by using FindByNameAsync or FindByEmailAsync by using the UserManager. If the return is null then it means that the username/email doesn't exist yet.
Here is the sample approach for this:
//private readonly UserManager<IdentityUser> _userManager ;
var isUniqueUserName = await _userManager.FindByNameAsync(UserNameFromInput)==null;
var isUniqueEmail = await _userManager.FindByEmailAsync(EmailFromInput) == null;
if (!isUniqueUserName || !isUniqueEmail)
{
if(!isUniqueUserName)
ModelState.AddModelError("UserName", "UserName already exist.");
if(!isUniqueEmail)
ModelState.AddModelError("Email", "Email already exist.");
return View(registerViewModel);
}

How to use IUserIdProvider

My code:
public string GetUserId(IRequest request) {
var token = request.QueryString.Get("token");
// what is it? request.User.Identity.Name;
if (string.IsNullOrWhiteSpace(token)) {
return token;
}
else {
return JsonConvert.SerializeObject(new UserAbility().GetUserByToken(token));
}
}
I need to map the connection with the user using a different identifier.
So i want to get the custom token from the QueryString in this method, but GetUserId doesn't trigger in every reqeust.
And i always get the request.User.Identity.Name is string empty?
This article explains what you need to do.
http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections#IUserIdProvider

Add additional json structure to all WebApi's responses?

We are integrating with mobile software company to do our application in a mobile device.
Our controller has ( simplification) methods like :
api/users/1
–GetUserById(...)
api/users/changePassword
–ChangePassword(Person p)
Ok.
The ChangePassword can return several applicative error codes ( password has already used , password too short , password bla bla...)
So if ,for example, password has already been used , then the HttpCode should be 200 returned with additional info
We agreed on this convention for every response :( additional to the response data)
{
"Success":0,
"ErrorCode": 6,
"ErrorMessage":"already used"
}
But this structure , as I said - should be in every response.
So till now - for example : api/users/1 returned :
{
"userId":1,
"name":"John"
}
But now - the response should be :
{
"data":
{
"userId":1,
"name":"John"
}
,
"result": //no errors
{
"Success":0,
"ErrorCode": 0,
"ErrorMessage":""
}
}
They always looking for the "result" object to see the applicative response.
Question
I assume that the place which I should do it is in message handler after base.SendAsync ( response part)
But how should I wrap the regular response which I send via Request.CreateResponseMessage with the format + values of :
NB , of course at the Request.CreateResponseMessage phase I already have result object with the appropriate result codes.
By the time message handlers run in Web API pipeline, the result your action method has produced would have been serialized. An action filter would be a better option, since you can deal with objects, and you can do something like this.
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext context)
{
var content = context.Response.Content as ObjectContent;
if (content != null)
{
content.Value =
new MyResponse()
{
Result = new Result() { Success = 0, ErrorCode = 6 },
Data = content.Value
};
}
}
}
public class Result
{
public int Success { get; set; }
public int ErrorCode { get; set; }
}
public class MyResponse
{
public Result Result { get; set; }
public object Data { get; set; }
}
Note: The above code will work only for JSON and not XML.
You can create an ActionFilterAttribute, and on the OnActionExecuted Method you will get HttpActionExecutedContext where you can check the response message.
you can decorate your controlleror action by this attribute and return and create you own ResponseMessage.
Hope that helps.

Update Database Column after X Number of Failed Login Attempts

I have a forms login in my web site using ASP.NET MVC with C#. All of the user profiles are stored in a Customer table that has the following columns:
ID
First_Name
username
password
ActiveWebLog
with ActiveWebLog = 0 meaning the user has not yet activated their account, and ActiveWebLog = 1 meaning the user can login.
This is my Login action in my Controller :
public ActionResult LogOnCustomer()
{
return View();
}
[HttpPost]
public ActionResult LogOnCustomer(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateCustomer(model.UserName, model.Password))
{
this.AuthCustomer = MembershipService.AuthCustomer;
FormsService.SignIn(model.UserName, model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("RedirectPage", "Account");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
What I want is to set ActiveWebLog = -2 when the user inputs an incorrect username or password 5 times.
Does anyone know how to do that?
Add a LoginAttemptCount column to your table.
Check user credentials against the database.
If no user name, fail. It may be advisable to not tell the user that the username does not exist, because that supports grinding attempts to locate user names.
If the user name matches but the LoginAttemptCount => 5 (or any number), fail.
If the user name matches but the password is bad, increment LoginAttemptCount and fail.
If the user name and password are good (hopefully you are using a hashed password) and LoginAttemptCount < 5, reset LoginAttemptCount to zero.
If you use this methodology, you technically don't need to modify the ActiveWebLog column when the user exceeds the bad password limit (but you certainly could).
I would put the business rules for this inside a separate class library. Your controller will probably have the basic logic to invoke this class library and modify your view based on the resulting status codes.
Session["tries"] = 0;
try
{
User.Login();
}
catch (Exception)
{
Session["tries"] = Session["tries"] + 1;
}
if (Session["tries"] > 4)
{
ActiveWebLog = -2;
}

Resources