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

fix(OpenAPI): Fix array syntax ambiguities #13711

Closed
wants to merge 1 commit into from

Conversation

provokateurin
Copy link
Member

Fixes for nextcloud/openapi-extractor#168.
No real issues found, Talk code quality is great as always 🚀

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not possible
  • 📘 API documentation in docs/ has been updated or is not required
  • 🔖 Capability is added or not needed

Signed-off-by: provokateurin <kate@provokateurin.de>
@provokateurin provokateurin force-pushed the fix/openapi/array-syntax-ambiguities branch from 034f376 to 7ecf40c Compare November 6, 2024 13:21
@@ -95,7 +95,7 @@ public function listBans(): DataResponse {
* Required capability: `ban-v1`
*
* @param int $banId ID of the ban to be removed
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>
* @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really not a fan of this... it will never be a list. But we might introduce an 'error' key like we have on many other APIs, and then the response type changes from [] to {}
I guess it's hard to know which case is preferred technically.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but even then this is a breaking change.
If you'd document it as \stdClass for now and later as array{error: string} it would be fine.
I don't think this change makes it worse as it is the same JSON data in the end.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the cleanest way is to have it return null as then you can later add any data and you are not limited to either objects or lists.
Unfortunately I don't think we can change this as it technically is a breaking change, though no client should really be looking at this data (and openapi-extractor also skips it automatically).
Same goes for the default value in the constructor of DataResponse which should be null as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but even then this is a breaking change.

well form array<empty> to array{error? string} it should be okay?
But anyway, we will see.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the former is [] in JSON and the later is {} or {"error": ""} in JSON and those are not compatible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah lol, nvm. the list<empty> on root is not added to the OpenAPI definition as a list:

                                                "meta": {
                                                    "$ref": "#/components/schemas/OCSMeta"
                                                },
                                                "data": {}
                                            }
                                        }
                                    }

So I guess we are fine to start using it later either way

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any disadvantage of using array{} 🤔

It does:

diff --git a/openapi-full.json b/openapi-full.json
index 0fd153e89..206856fca 100644
--- a/openapi-full.json
+++ b/openapi-full.json
@@ -2433,7 +2433,9 @@
                                                 "meta": {
                                                     "$ref": "#/components/schemas/OCSMeta"
                                                 },
-                                                "data": {}
+                                                "data": {
+                                                    "type": "object"
+                                                }
                                             }
                                         }
                                     }

I guess the real problem is that the API does not return like this, so it actually creates invalid code-docs combination?

Copy link
Member

@nickvergessen nickvergessen Nov 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my favorite solution for now is:

diff --git a/lib/Controller/AEnvironmentAwareController.php b/lib/Controller/AEnvironmentAwareController.php
index 25cfa8083..238f0af5a 100644
--- a/lib/Controller/AEnvironmentAwareController.php
+++ b/lib/Controller/AEnvironmentAwareController.php
@@ -73,4 +73,16 @@ abstract class AEnvironmentAwareController extends OCSController {
 
                return $format;
        }
+
+       /**
+        * @psalm-suppress InvalidReturnStatement,InvalidReturnType
+        * @psalm-return array{}
+        */
+       public function getEmptyObjectData(): array {
+               if ($this->getResponseFormat() === 'json') {
+                       // Cheating here to make sure the array is always a JSON object on the API.
+                       return new \stdClass();
+               }
+               return [];
+       }
 }
diff --git a/lib/Controller/BanController.php b/lib/Controller/BanController.php
index a38b9fdbe..3b5d5b690 100644
--- a/lib/Controller/BanController.php
+++ b/lib/Controller/BanController.php
@@ -95,7 +95,7 @@ class BanController extends AEnvironmentAwareController {
         * Required capability: `ban-v1`
         *
         * @param int $banId ID of the ban to be removed
-        * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
+        * @return DataResponse<Http::STATUS_OK, array{}, array{}>
         *
         * 200: Unban successfully or not found
         */
@@ -103,6 +103,6 @@ class BanController extends AEnvironmentAwareController {
        #[RequireModeratorParticipant]
        public function unbanActor(int $banId): DataResponse {
                $this->banService->findAndDeleteBanByIdForRoom($banId, $this->room->getId());
-               return new DataResponse([], Http::STATUS_OK);
+               return new DataResponse($this->getEmptyObjectData(), Http::STATUS_OK);
        }
 }
diff --git a/openapi-full.json b/openapi-full.json
index 0fd153e89..206856fca 100644
--- a/openapi-full.json
+++ b/openapi-full.json
@@ -2433,7 +2433,9 @@
                                                 "meta": {
                                                     "$ref": "#/components/schemas/OCSMeta"
                                                 },
-                                                "data": {}
+                                                "data": {
+                                                    "type": "object"
+                                                }
                                             }
                                         }
                                     }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any disadvantage of using array{}

You can not switch to list<> later without a breaking change.

I guess the real problem is that the API does not return like this, so it actually creates invalid code-docs combination?

Yeah this should not be done, I don't think psalm or openapi-extractor can catch it.

I think my favorite solution for now is:

Looks ok to me, even though I think null is still better as it allows you to use any type later without a breaking change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can not switch to list<> later without a breaking change.

Well those things never should return lists directly anyway.

I made a poll for the team to give feedback if toggling to null and later on to array{error?: string} is problematic. If it turns out to be, we can also annotate array{error?: string} now already to indicate the correct way going forward.

@@ -120,7 +120,7 @@ protected function getBotFromHeaders(string $token, string $message): Bot {
* @param string $referenceId For the message to be able to later identify it again
* @param int $replyTo Parent id which this message is a reply to
* @param bool $silent If sent silent the chat message will not create any notifications
* @return DataResponse<Http::STATUS_CREATED|Http::STATUS_BAD_REQUEST|Http::STATUS_UNAUTHORIZED|Http::STATUS_REQUEST_ENTITY_TOO_LARGE, array<empty>, array{}>
* @return DataResponse<Http::STATUS_CREATED|Http::STATUS_BAD_REQUEST|Http::STATUS_UNAUTHORIZED|Http::STATUS_REQUEST_ENTITY_TOO_LARGE, list<empty>, array{}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here 🙈

@nickvergessen
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants