PHPSTAN: Laravel 9 mutators producing "Access to undefined property" error - laravel-9

I am trying to store a key into my database and I want it to be encrypted and decrypted.
So I use Laravel 9 mutator: https://laravel.com/docs/9.x/eloquent-mutators
protected function privateKey(): Attribute
{
return Attribute::make(
get: fn ($value) => Crypt::decryptString($value),
set: fn ($value) => Crypt::encryptString($value),
);
}
and I accessed it to one of my service class:
$provider = Provider::findOrFail($id);
$privateKey = $provider->private_key;
However, phpstan throws error saying:
Access to an undefined property App\Models\Method|Illuminate\Database\Eloquent\Collection<int,
App\Models\Provider>::$private_key
However, when I tried using the old way of mutating and accessing attributes, it worked:
public function setPrivateKeyAttribute(string $value): void
{
if (!empty($value)) {
$this->attributes['private_key'] = Crypt::encryptString($value);
}
}
public function getPrivateKeyAttribute(string $value): string
{
return Crypt::decryptString($value);
}
and at this point, I don't have any idea why. is this a bug in the side of phpstan? if not, how can I resolve the issue?

Related

How to get a custom ModelState error message in ASP.NET Core when a wrong enum value is passed in?

I'm passing a model to an API action with a property called eventType which is a nullable custom enum.
If I pass a random value for eventType, such as 'h', it fails to serialise which is correct.
However, the error I get from the ModelState is not something I would want a public caller to see. It includes the line number and position (see below).
I've tried a number of options including a custom data annotation with no success.
Does anyone know how I could define a nicer custom message?
"Error converting value \"h\" to type
'System.Nullable`1[Custom.EventTypes]'. Path 'eventType', line 1,
position 80."
Most times the first error is usually the most important error or rather one that describes the situation properly. You can use this way to manipulate to get the first error message from the first key or change it to whatever you want if you wish to get all the error messages.
public ActionResult GetMyMoney(MyModel myModel)
{
string nss = ModelState.First().Key;
ModelError[] ern = ModelState[nss].Errors.ToArray();
string ndd = ern.First().ErrorMessage;
}
public class CustomFilter: IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (!context.ModelState.IsValid)
{
// You can pass custom object to BadRequestObjectResult method
context.Result = new BadRequestObjectResult(customObject);
}
}
}
You can write a custom filter like above mentioned and pass a custom object with your message.
Ref: this
IF you just want the error messages you can simply create a custom class of response and then
var response = new ResponseApi{
StatusCode = HttpStatusCode.BadRequest,
Message = "Validation Error",
Response = ModelState.Values.SelectMany(x => x.Errors).Select(x =>
x.ErrorMessage)
};
then just return the response or create a validation filter to handle validations globally.
/// <summary>
/// Validatation filter to validate all the models.
/// </summary>
public class ValidationActionFilter : ActionFilterAttribute
{
/// <inheritdoc/>
public override void OnActionExecuting(HttpActionContext actionContext)
{
ModelStateDictionary modelState = actionContext.ModelState;
if (!modelState.IsValid)
{
actionContext.Response = SendResponse(new ResponseApi
{
StatusCode= 400,
Message = "Validation Error",
Response = modelState.Values.SelectMany(x =>
x.Errors).Select(x => x.ErrorMessage)
});
}
}
private HttpResponseMessage SendResponse(ResponseApiresponse)
{
var responseMessage = new HttpResponseMessage
{
StatusCode = (HttpStatusCode)response.StatusCode,
Content = new StringContent(JsonConvert.SerializeObject(response)),
};
responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return responseMessage;
}
}

PHP Youshido GraphQL issue with nested fields

I am using version v1.4.2.18. The library can be found here: https://github.com/Youshido/GraphQL
I am trying to accomplish the following:
query {
articleSummary(id:1) {
title,
body,
article {
id
}
}
}
I have an ArticleSummaryField.php:
class ArticleSummaryField extends AbstractField
{
public function build(FieldConfig $config)
{
$config->addArgument('id', new NonNullType(new StringType()));
}
public function getType()
{
return new ArticleSummaryType();
}
public function resolve($value, array $args, ResolveInfo $info)
{
return [
'title' => 'test title',
'body' => 'test body',
'article' => $args['id']
];
}
}
Then the ArticleSummaryType.php:
class ArticleSummaryType extends AbstractObjectType
{
public function build($config)
{
$config
->addField('title', new StringType());
->addField('body', new StringType());
->addField('article', new ArticleField());
}
}
Then the ArticleField.php has the getType method return the ArticleType which has the id field.
However what i am getting is an error:
Fatal error: Uncaught Error: Call to undefined method ArticleField::getNullableType() in .../vendor/youshido/graphql/src/Execution/Processor.php on line 135
What seems to be happening is that when $targetField->getType() on line 135 in src/Execution/Processor.php is called its returning the ArticleField class, not the ArticleType class.
I would expect that to return the class as declared in the 'getType' method on the ArticleField class.
Am i going about this wrong for nesting fields? Or is there a bug in the library?
To accomplish this you only pass the Field class as the first argument.
class ArticleSummaryType extends AbstractObjectType
{
public function build($config)
{
$config
->addField('title', new StringType());
->addField('body', new StringType());
->addField(new ArticleField());
}
}
Then in the field class you can override getName to set the name for the field as needed or it will use the class name as the field name.

Invalid ModelState error message for Nullable types

I validate the input using ModelState.IsValid:
[HttpGet]
[Route("subjects")]
[ValidateAttribute]
public IHttpActionResult GetSubjects(bool? isActive = null)
{
//get subjects
}
If I pass in the uri ~/subjects/?isActive=abcdef, I get the error message:
The value 'abcdef' is not valid for Nullable`1.
If the input parameter is not nullable
public IHttpActionResult GetSubjects(bool isActive){
//get subjects
}
I get the error message:
The value 'abcdef' is not valid for Boolean.
I want to override the message if nullable type so I can maintain the message ("The value 'abcdef' is not valid for Boolean."). How can I do this since in the ModelState error I don't get the data type. I am implementing the validation as a custom ActionFilterAttribute (ValidationAttribute).
You can change callback that formats type conversion error messages. For example, let's define it right into Global.asax.cs:
public class WebApiApplication : HttpApplication
{
protected void Application_Start()
{
ModelBinderConfig.TypeConversionErrorMessageProvider = this.NullableAwareTypeConversionErrorMessageProvider;
// rest of your initialization code
}
private string NullableAwareTypeConversionErrorMessageProvider(HttpActionContext actionContext, ModelMetadata modelMetadata, object incomingValue)
{
var target = modelMetadata.PropertyName;
if (target == null)
{
var type = Nullable.GetUnderlyingType(modelMetadata.ModelType) ?? modelMetadata.ModelType;
target = type.Name;
}
return string.Format("The value '{0}' is not valid for {1}", incomingValue, target);
}
}
For not nullable types Nullable.GetUnderlyingType will return null, in this case we will use original type.
Unfortunately you cannot access default string resources and if you need to localize error message you must do it on your own.
Another way is to implement your own IModelBinder, but this is not a good idea for your particular problem.
Lorond's answer highlights how flexible asp.net web api is in terms of letting a programmer customize many parts of the API. When I looked at this question, my thought process was to handle it in an action filter rather than overriding something in the configuration.
public class ValidateTypeAttribute : ActionFilterAttribute
{
public ValidateTypeAttribute() { }
public override void OnActionExecuting(HttpActionContext actionContext)
{
string somebool = actionContext.Request.GetQueryNameValuePairs().Where(x => x.Key.ToString() == "somebool").Select(x => x.Value).FirstOrDefault();
bool outBool;
//do something if somebool is empty string
if (!bool.TryParse(somebool, out outBool))
{
HttpResponseMessage response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
response.ReasonPhrase = "The value " + somebool + " is not valid for Boolean.";
actionContext.Response = response;
}
else
{
base.OnActionExecuting(actionContext);
}
}
Then decorate the action method in the controller with the action filter attribute

Cast route parameter in Nancy is always null

I have a Nancy module which uses a function which expects as parameters a string (a captured pattern from a route) and a method group. When trying to pass the parameter directly it will not compile as I "cannot use a method group as an argument to a dynamically dispatched operation".
I have created a second route which attempts to cast the dynamic to a string, but this always returns null.
using System;
using Nancy;
public class MyModule : NancyModule
{
public MyModule()
{
//Get["/path/{Name}/action"] = parameters =>
// {
// return MyMethod(parameters.Name, methodToBeCalled); // this does not compile
// };
Get["/path/{Name}/anotherAction"] = parameters =>
{
return MyMethod(parameters.Name as string, anotherMethodToBeCalled);
};
}
public Response MyMethod(string name, Func<int> doSomething)
{
doSomething();
return Response.AsText(string.Format("Hello {0}", name));
}
public int methodToBeCalled()
{
return -1;
}
public int anotherMethodToBeCalled()
{
return 1;
}
}
Tested with the following class in a separate project:
using System;
using Nancy;
using Nancy.Testing;
using NUnit.Framework;
[TestFixture]
public class MyModuleTest
{
Browser browser;
[SetUp]
public void SetUp()
{
browser = new Browser(with =>
{
with.Module<MyModule>();
with.EnableAutoRegistration();
});
}
[Test]
public void Can_Get_View()
{
// When
var result = browser.Get("/path/foobar/anotherAction", with => with.HttpRequest());
// Then
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
Assert.AreEqual("Hello foobar", result.Body.AsString()); //fails as parameters.Name is always null when cast to a string
}
}
You can find the whole test over on github
I've had similar issues when using 'as' so I tend to use explicitly cast it:
return MyMethod((string)parameters.Name, anotherMethodToBeCalled);
Also I think there was a bug raised with the casing on parameters, but I think it's better to keep them lowercase:
Get["/path/{name}/anotherAction"]
(string)parameters.name
Your code works for me with upper case and lowercase, using the explicit cast.

Struts2 more than one action in one class

I'm using Struts2. I have two web forms that have the same code. I would like to eliminate one form. Here is the structure of my Struts project.
\Web Pages
form.jsp
\WEB-INF
\Content
error.jsp
form.jsp
success.jsp
\Source Packages
\action
MyAction.java
MyAction.java
package action;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.*;
public class MyAction extends ActionSupport {
#Action(value = "foo", results = {
#Result(name = "input", location = "form.jsp"),
#Result(name = "success", location = "success.jsp"),
#Result(name = "error", location = "error.jsp")
})
public String execute() throws Exception {
if (user.length() == 1) {
return "success";
} else {
return "error";
}
}
private String user = "";
public void validate() {
if (user.length() == 0) {
addFieldError("user", getText("user required"));
}
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
}
I tried to eliminate form.jsp under \Web Pages by adding a new action method to MyAction.java.
#Action(value="bar", results = {
#Result(name = "success", location = "form.jsp"),
})
public String another() {
return "success";
}
But I got the following error when I go to http : //localhost .../bar.action
HTTP Status 404 - No result defined for action action.MyAction and result input
Your MyAction has an implementation of validate(), which means it is validation aware.
What's happening is that you're calling another, but validate() is kicking in (as it's in the interceptor stack). Validation is failing, and therefore sending to INPUT result, which is not defined in another.
You should
Add #SkipValidation to the another method if you don't want validation there
Add the INPUT result to another() if you want a default input result
On a more general note, when you get that kind of error (No result defined for action X and result input) it usually means you're either having validation errors, parameter population errors (eg: an exception in preparable).