How to over-write the pydantic model's validation message - fastapi

I want to change the validation message from pydantic model class, code for model class is below:
class Input(BaseModel):
ip: IPvAnyAddress
#validator("ip", always=True)
def not_valid_ip(cls, v):
"""To validate ip-address."""
if str(v) == "":
raise ValueError(f'Invalid IP-Address:: {v}')
if not isinstance(v, IPvAnyAddress):
raise ValueError(f'Invalid IP format:: {v}')
return v
currently, it does not update the message written above. I am using fastAPI for API development.
{
"detail": [
{
"loc": [
"body",
"input",
"ip"
],
"msg": "value is not a valid IPv4 or IPv6 address",
"type": "value_error.ipvanyaddress"
}
]
}

That's because the standard validation is failing before your validators is called.
All you need to do is add pre=True to your validator decorator, e.g.
#validator("ip", always=True, pre=True)

Related

FastAPI pydantic response_model

FastAPI with uvicorn. So far i have everything working fine, now wanted to add extra validation with pydantic and response_model validation. My challange is, the response is a nested object, example:
{
"name": "always the same",
"value": ["ACL1", "ACL2", "ACL3"]
}
I have tried to:
class ACL(BaseModel):
name: str
#app.post(
"get/acls",
response_model=List[ACL]
)
def get_acls(credentials: HTTPBasicCredentials = Depends(security)):
But obviously it will fail. How to define correctly the response model so that it's correctly validated ? (and also visible in openapi documentation page ?)
Thanks,
You can use nested models in Pydantic and by by extension also in FastAPI. Like so:
class ACL(BaseModel):
name: str
class YourResponseModel(BaseModel):
name: str
value: list[ACL]
#app.post("/get/acls", response_model=YourResponseModel)
def get_acl():
return {"name": "always the same", "value": [{"name":"ACL1"}, {"name":"ACL2"}, {"name":"ACL3"}]}
If you don't want nested objects in your value list, then you have to build some logic to parse it. Below is an example of this:
class ACL(BaseModel):
name: str
class AnotherResponseModel(BaseModel):
name: str
value: list[str]
#app.post("/get/acls2", response_model=AnotherResponseModel)
def get_acl():
acls = [ACL(name="ACL1"), ACL(name="ACL2"), ACL(name="ACL3")]
return {"name": "always the same", "value": [acl.name for acl in acls] }
Please note, it is bad practice to mix up POST and GET requests (you are defining a POST endpoint, that will 'get' some ACLs for the end user). That is however unrelated to this question.

How to test pact with null data

I am aware of that PACT expects provider data need to be in our control, but I am facing following situation
I have pact contract for multiple consumers, all have some mandatory attribute and some are optional attribute, but business logic suppress all the attribute which are having null value, but according to contract I would still be needing that value as null,
what should I do for it?
Edit 1:
i.e let's say below my contract looks
consumer sent request with below params:
{ "method": "GET", "path" : "/pathOfApi", "headers":{ "accept": "json" } }
Provider responds with below data:
{ "Status": 200,
"body" :[
{"country" : "string",
"countryId" :"string",
"postalcode": "string",
"addressLine1" :"string",
"addressLine2" : "string"
"customerName" : "string",
"customerId" : "string"
}
]
now not all customer has address line 2, now in production if addressLine 2 is null it won't be present in output of api, but for our contract field should be present with null
If your provider does not return a field, but the consumer is expecting null, then either the consumer needs to change their expectation (because it's incorrect) or the provider should update its implementation to return null values.
Just because a consumer asks for something doesn't mean you need to do it!
If in some instances the field is present and other it is not, you need to write two tests to cover each case. I'd suggest covering one case with all of the fields, and another with the minimum set of fields (see https://docs.pact.io/faq/#why-is-there-no-support-for-specifying-optional-attributes).

Using FastAPI & Pydantic, how do I define an Optional field with a description

For a FastAPI Pydanctic class I have these values
class ErrorReportRequest(BaseModel):
sender: Optional[str] = Field(..., description="Who sends the error message.")
error_message_displayed_to_client: str = Field(..., description="The error message displayed to the client.")
I use the class as an input model
router = APIRouter()
#router.post(
"/error_report",
response_model=None,
include_in_schema=True,
)
def error_report(err: ErrorReportRequest):
pass
When I run this, sender is a required field. If it's not included in the incoming JSON, I get a validation error.
Input:
{
"error_message_displayed_to_client": "string"
}
Results in:
{
"detail": [
{
"loc": [
"body",
"sender"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
If I remove the Field description like this:
class ErrorReportRequest(BaseModel):
sender: Optional[str]
error_message_displayed_to_client: str = Field(..., description="The error message displayed to the client.")
the request passes.
How can I add a Field description to an optional field so that it's still allowed to omit the field name?
You'll need to provide an explicit default value to Field, for example None instead of ...:
class ErrorReportRequest(BaseModel):
sender: Optional[str] = Field(None, description="Who sends the error message.")
error_message_displayed_to_client: str = Field(..., description="The error message displayed to the client.")
... indicates that the value is required. This is of course in conflict with the Optional, but it looks like pydantic gives higher priority to ....
From the documentation of Field:
default: (a positional argument) the default value of the field. Since the Field replaces the field's default, this first argument can be used to set the default. Use ellipsis (...) to indicate the field is required.

MassTransit. Edit MessageType property for remove unnecessary items

First of all, excuse my English, it's very bad. I am using MassTransit with Azure Service Bus for asynchronous communication between microservices.
Due to project design requirements, the objects sent and / or published in masstransit are objects (class instances) that implement interfaces and have inheritance, which implies that the "MessageType" property is an array of multiple values, which results in a sending/publication of "a copy" of the object for each of the base classes and interfaces of the object.
Since the application really only needs to process the final class, the result of this behavior is that queues and topics of message types are created that are never processed.
Is there a way to modify the MessageType array to remove all unnecessary items?
Example:
This is a current message:
{
"messageId": "c93e0000-eab0-3663-c954-08d89e1f87cb",
"correlationId": "42fc2237-77b1-4994-821d-87790b6caf06",
"conversationId": "c93e0000-eab0-3663-f342-08d89e1f87cd",
"sourceAddress": "sb://bus.servicebus.windows.net/tmspayments7dc9d65778jfh75_dotnet_bus_3r9yyy8ksy5g84tdbdcjasmhg9",
"destinationAddress": "sb://bus.servicebus.windows.net/queue",
"messageType": [
"urn:message:Payments.Application.Integration.Outcoming.Commands.Sales:NotifySalePaymentHasCanceledCommand",
"urn:message:Payments.Application.Integration.Outcoming.Commands:BaseIntegrationOutcomingCommand",
"urn:message:Tms.Frameworks.CqrsEs.Cqrs.Commands:IdentifiedCommandBase",
"urn:message:Tms.Frameworks.CqrsEs.Cqrs.Messages:MessageBase",
"urn:message:Tms.Frameworks.CqrsEs.Cqrs.Messages:IIdentifiedMessage",
"urn:message:Tms.Frameworks.CqrsEs.Cqrs.Messages:IMessage",
"urn:message:Tms.Frameworks.CqrsEs.Cqrs.Commands:IIdentifiedCommand",
"urn:message:Tms.Frameworks.CqrsEs.Cqrs.Messages:ICommand",
"urn:message:Tms.Frameworks.CqrsEs.Cqrs.Commands.Outcoming:IOutcomingCommand",
"urn:message:Tms.Frameworks.CqrsEs.Cqrs.Messages:IOutcomingMessage"
],
"message": {
"commandId": "42fc2237-77b1-4994-821d-87790b6caf06",
"terminalId": 71,
"paymentId": "a28a23e9-3ba5-464f-9a20-d25d1991ab46"
},
"sentTime": "2020-12-11T21:55:53.252599Z",
"headers": {},
"host": {
"machineName": "tms-payments-7dc9d65778-jfh75",
"processName": "dotnet",
"processId": 1,
"assembly": "Payments.Api",
"assemblyVersion": "1.0.0.0",
"frameworkVersion": "3.1.8",
"massTransitVersion": "5.5.2.0",
"operatingSystemVersion": "Unix 4.15.0.1098"
}
}
And this is my goal message (with only one item in messageType property):
{
"messageId": "c93e0000-eab0-3663-c954-08d89e1f87cb",
"correlationId": "42fc2237-77b1-4994-821d-87790b6caf06",
"conversationId": "c93e0000-eab0-3663-f342-08d89e1f87cd",
"sourceAddress": "sb://bus.servicebus.windows.net/tmspayments7dc9d65778jfh75_dotnet_bus_3r9yyy8ksy5g84tdbdcjasmhg9",
"destinationAddress": "sb://bus.servicebus.windows.net/queue",
"messageType": [
"urn:message:Payments.Application.Integration.Outcoming.Commands.Sales:NotifySalePaymentHasCanceledCommand"
],
"message": {
"commandId": "42fc2237-77b1-4994-821d-87790b6caf06",
"terminalId": 71,
"paymentId": "a28a23e9-3ba5-464f-9a20-d25d1991ab46"
},
"sentTime": "2020-12-11T21:55:53.252599Z",
"headers": {},
"host": {
"machineName": "tms-payments-7dc9d65778-jfh75",
"processName": "dotnet",
"processId": 1,
"assembly": "Payments.Api",
"assemblyVersion": "1.0.0.0",
"frameworkVersion": "3.1.8",
"massTransitVersion": "5.5.2.0",
"operatingSystemVersion": "Unix 4.15.0.1098"
}
}
Thank you very much.
Regards
Borja
Short Answer: No
MassTransit will reflect all supported types and include them in the message envelope to support polymorphic message delivery and deserialization.
You can exclude message types from being created as exchanges/topics on the message broker, but that does not exclude them from the message type array during serialization.

How can I get Mandrill to parse the Handlebars tags in my template?

I'm using Mandrill's send-template API method to send an email. It shouldn't matter, but just in case, I'm using PHP.
The email is delivered using the correct template, but none of the Handlebars variables are replaced with their values. Instead, they're merely removed.
Here's a full request as seen in the API Logs after reducing my code:
{
"template_name": "my-template-slug",
"template_content": [
{
"name": "price",
"content": "$25"
}
],
"message": {
"subject": "My Subject",
"from_email": "my#email.com",
"from_name": "My Name",
"to": [
{
"name": "Jimmy Crackcorn",
"type": "to",
"email": "jimmy#crackcorn.com"
}
]
},
"async": false,
"ip_pool": null,
"send_at": null,
"key": "my_api_key"
}
I also tried simplifying my template to just {{price}}, which just sends a blank email.
I've also verified that Sending Defaults > Merge Language is set to Handlebars.
In case it makes any difference, I created the template in MailChimp and sent it to Mandrill, then removed the escaping back-slashes from the variables.
Handlebars should be passed in the merge_vars or global_merge_vars parameters in your API request, not in template_content—which is for mc:edit regions for the MailChimp template language.

Resources