Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store/Update requests aren't working with JSONAPI payload format #562

Open
maicol07 opened this issue May 8, 2023 · 3 comments · May be fixed by #582
Open

Store/Update requests aren't working with JSONAPI payload format #562

maicol07 opened this issue May 8, 2023 · 3 comments · May be fixed by #582
Labels
enhancement New feature or request

Comments

@maicol07
Copy link
Contributor

maicol07 commented May 8, 2023

Hi,

if I try to send to the POST endpoint (store) the following payload (compliant with JSONAPI specs):

{
  "type": "users",
  "attributes": {
    "username": "test",
    "email": "test@test.tl"
  },
  "relationships": {}
}

I get the following error:

{
  "message": "username is required. (and 1 more error)",
  "errors": {
    "username": [
      "username is required."
    ],
    "email": [
      "email is required."
    ]
  }
}

I've found out Restify only accepts a plain payload of type [attribute => value], like this:

{
    "username": "test",
    "email": "test@test.tl"
}

By the way, this works but due to this Restify isn't fully compliant with JSONAPI (the docs home page says "JSON:API consistency" under features).

Can you support the JSONAPI payload schema?

Thanks

@maicol07
Copy link
Contributor Author

maicol07 commented May 11, 2023

As a workaround, I've added methods on my repository to support JSON API payloads, translating them into Restify ones:

/**
     * @psalm-suppress MissingParamType
     */
    public function allowToStore(RestifyRequest $request, $payload = null): RestifyRepository
    {
        $this->adaptJsonApiRequest($request);

        return parent::allowToStore($request, $payload);
    }

    /**
     * @psalm-suppress MissingParamType
     */
    public function allowToPatch(RestifyRequest $request, $payload = null): RestifyRepository
    {
        $this->adaptJsonApiRequest($request, true);

        return parent::allowToPatch($request, $payload);
    }

    public function getStoringRules(RestifyRequest $request): array
    {
        return $this->collectFields($request)->mapWithKeys(static fn (Field $k) => [
            ($k->label ?? $k->attribute) => $k->getStoringRules(),
        ])->toArray();
    }

    public function collectFields(RestifyRequest $request): FieldCollection
    {
        $fields = parent::collectFields($request);
        if ($request->isUpdateRequest()) {
            return $fields->map(static function (Field $field) {
                if (!($field instanceof BelongsTo)) {
                    // Fix to allow updating fields with custom labels
                    $field->label = $field->attribute;
                }

                return $field;
            });
        }

        return $fields;
    }

    /**
     * Adapt JSON:API request to Restify request.
     */
    protected function adaptJsonApiRequest(RestifyRequest $request, bool $snake_attributes = false): void
    {
        /** @var array<string, mixed> $attributes */
        $attributes = $request->input('data.attributes') ?? [];
        // Convert all keys to snake_case using Collections
        if ($snake_attributes) {
            $attributes = collect($attributes)
                ->mapWithKeys(static fn ($value, $key) => [Str::snake($key) => $value])
                ->toArray();
        }
        $relationships = $request->input('data.relationships') ?? [];

        // Get relationships in form of "relationshipName" → relationship_id
        $relationships = array_map(static fn (array $relationship): int => Arr::get($relationship, 'data.id'), $relationships);

        $request->replace([
            ...$attributes,
            ...$relationships,
        ]);
    }

Actually, it supports:

  • Post (Store)
  • Patch/update
  • Labels
  • Different attribute casing
  • One to one/HasOne relationships (TBC)

@binaryk
Copy link
Collaborator

binaryk commented May 22, 2023

That's a good idea to include support. Thanks @maicol07 , will make sure we prioritize this.

@binaryk binaryk added the enhancement New feature or request label May 22, 2023
@maicol07
Copy link
Contributor Author

That's a good idea to include support. Thanks @maicol07 , will make sure we prioritize this.

By the way, we'll also how to figure out where to put pivot fields (I've read something on discuss.jsonapi.org where they indicate meta inside the the relationship)

@maicol07 maicol07 linked a pull request Sep 4, 2023 that will close this issue
10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants