cloudcreativity / laravel-json-api

JSON API (jsonapi.org) package for Laravel applications.
http://laravel-json-api.readthedocs.io/en/latest/
Apache License 2.0
778 stars 110 forks source link

Validate ID of a relationship #588

Closed lo00l closed 3 years ago

lo00l commented 3 years ago

There's a resource commissions which has a currency relationship. In my Validators class I have something like this:

protected function rules($record, array $data): array
{
    return [
        ...

        'currency' => new HasOne('currencies'),
    ];
}

My Currency model has a primary key of UUID type. I also set an ID constraint when registering a currencies resource.

Everything works fine unless I try to pass an invalid currency ID when creating a commissions resource:

POST /api/v1/commissions

{
    "type": "commissions",
    "attributes": {},
    "relationships": {
        "currency": {
            "data": {
                "type": "currencies",
                "id": "123"
            }
        }
    }
}

I expect to get a 422 response but get 500 with the following error: testing.ERROR: SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for type uuid: "123" (SQL: select exists(select * from "currencies" where "currencies"."id" = 123 and "currencies"."deleted_at" is null and "is_disabled" = 0) as "exists")

Well, as I can see, the currency is fetched first and then validation is applied. How can I validate raw data first and fetch the row from DB after this only?

I tried to add

'currency.id' => 'uuid',

to my validator, but it didn't work.

Thanks in advance!

lindyhopchris commented 3 years ago

Hi!

This is because the id is checked when parsing the document for compliance with the JSON:API specification. The currency adapter's exists() method is called: https://github.com/cloudcreativity/laravel-json-api/blob/develop/src/Eloquent/AbstractAdapter.php#L208-L211

So to fix this, you need to overload the method on your currencies adapter to check the format of the id, for example:

public function exists(string $resourceId): bool
{
    if (1 === preg_match(/** */, $resourceId) {
        return parent::exists($resourceId);
    }

    return false;
}

As a side note, we've actually fix this in the rewrite of the package. In the new package it's dealt with here: https://laraveljsonapi.io/docs/1.0/schemas/identifier.html#pattern