From 54bf7ed659843aa06918261cfd216e03fcd44302 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Mon, 5 Jul 2021 23:23:59 +0200 Subject: [PATCH] Produce a schema-valid OpenAPI specification (closes #1457) --- README.md | 2 +- controllers/FilesApiController.php | 8 +- controllers/GenericEntityApiController.php | 10 +- controllers/OpenApiController.php | 36 ++-- grocy.openapi.json | 204 ++++++++++----------- localization/strings.pot | 2 +- public/viewjs/openapiui.js | 3 +- services/UserfieldsService.php | 2 +- views/layout/default.blade.php | 2 +- views/openapiui.blade.php | 2 +- 10 files changed, 136 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index 7ce8a3a8..3015fbf2 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ _RTL languages are unfortunately not yet supported._ ## Things worth to know -### REST API & data model documentation +### REST API See the integrated Swagger UI instance on [/api](https://demo.grocy.info/api). ### Barcode readers & camera scanning diff --git a/controllers/FilesApiController.php b/controllers/FilesApiController.php index 66b8d934..5dbca1ec 100644 --- a/controllers/FilesApiController.php +++ b/controllers/FilesApiController.php @@ -11,7 +11,7 @@ class FilesApiController extends BaseApiController { try { - if (!in_array($args['group'], $this->getOpenApiSpec()->components->internalSchemas->FileGroups->enum)) + if (!in_array($args['group'], $this->getOpenApiSpec()->components->schemas->FileGroups->enum)) { throw new \Exception('Invalid file group'); } @@ -39,7 +39,7 @@ class FilesApiController extends BaseApiController { try { - if (!in_array($args['group'], $this->getOpenApiSpec()->components->internalSchemas->FileGroups->enum)) + if (!in_array($args['group'], $this->getOpenApiSpec()->components->schemas->FileGroups->enum)) { throw new \Exception('Invalid file group'); } @@ -69,7 +69,7 @@ class FilesApiController extends BaseApiController { try { - if (!in_array($args['group'], $this->getOpenApiSpec()->components->internalSchemas->FileGroups->enum)) + if (!in_array($args['group'], $this->getOpenApiSpec()->components->schemas->FileGroups->enum)) { throw new \Exception('Invalid file group'); } @@ -100,7 +100,7 @@ class FilesApiController extends BaseApiController { try { - if (!in_array($args['group'], $this->getOpenApiSpec()->components->internalSchemas->FileGroups->enum)) + if (!in_array($args['group'], $this->getOpenApiSpec()->components->schemas->FileGroups->enum)) { throw new \Exception('Invalid file group'); } diff --git a/controllers/GenericEntityApiController.php b/controllers/GenericEntityApiController.php index f9a5da40..5270a4d9 100644 --- a/controllers/GenericEntityApiController.php +++ b/controllers/GenericEntityApiController.php @@ -216,26 +216,26 @@ class GenericEntityApiController extends BaseApiController private function IsEntityWithEditRequiresAdmin($entity) { - return in_array($entity, $this->getOpenApiSpec()->components->internalSchemas->ExposedEntityEditRequiresAdmin->enum); + return in_array($entity, $this->getOpenApiSpec()->components->schemas->ExposedEntityEditRequiresAdmin->enum); } private function IsEntityWithNoListing($entity) { - return in_array($entity, $this->getOpenApiSpec()->components->internalSchemas->ExposedEntityNoListing->enum); + return in_array($entity, $this->getOpenApiSpec()->components->schemas->ExposedEntityNoListing->enum); } private function IsEntityWithNoEdit($entity) { - return in_array($entity, $this->getOpenApiSpec()->components->internalSchemas->ExposedEntityNoEdit->enum); + return in_array($entity, $this->getOpenApiSpec()->components->schemas->ExposedEntityNoEdit->enum); } private function IsEntityWithNoDelete($entity) { - return in_array($entity, $this->getOpenApiSpec()->components->internalSchemas->ExposedEntityNoDelete->enum); + return in_array($entity, $this->getOpenApiSpec()->components->schemas->ExposedEntityNoDelete->enum); } private function IsValidExposedEntity($entity) { - return in_array($entity, $this->getOpenApiSpec()->components->internalSchemas->ExposedEntity->enum); + return in_array($entity, $this->getOpenApiSpec()->components->schemas->ExposedEntity->enum); } } diff --git a/controllers/OpenApiController.php b/controllers/OpenApiController.php index 45ea0a38..14aa8194 100644 --- a/controllers/OpenApiController.php +++ b/controllers/OpenApiController.php @@ -36,45 +36,45 @@ class OpenApiController extends BaseApiController $spec->info->description = str_replace('PlaceHolderManageApiKeysUrl', $this->AppContainer->get('UrlManager')->ConstructUrl('/manageapikeys'), $spec->info->description); $spec->servers[0]->url = $this->AppContainer->get('UrlManager')->ConstructUrl('/api'); - $spec->components->internalSchemas->ExposedEntity_IncludingUserEntities = clone $spec->components->internalSchemas->ExposedEntity; + $spec->components->schemas->ExposedEntity_IncludingUserEntities = clone $spec->components->schemas->ExposedEntity; foreach ($this->getUserfieldsService()->GetEntities() as $userEntity) { - array_push($spec->components->internalSchemas->ExposedEntity_IncludingUserEntities->enum, $userEntity); + array_push($spec->components->schemas->ExposedEntity_IncludingUserEntities->enum, $userEntity); } - $spec->components->internalSchemas->ExposedEntity_NotIncludingNotEditable = clone $spec->components->internalSchemas->StringEnumTemplate; - foreach ($spec->components->internalSchemas->ExposedEntity->enum as $value) + $spec->components->schemas->ExposedEntity_NotIncludingNotEditable = clone $spec->components->schemas->StringEnumTemplate; + foreach ($spec->components->schemas->ExposedEntity->enum as $value) { - if (!in_array($value, $spec->components->internalSchemas->ExposedEntityNoEdit->enum)) + if (!in_array($value, $spec->components->schemas->ExposedEntityNoEdit->enum)) { - array_push($spec->components->internalSchemas->ExposedEntity_NotIncludingNotEditable->enum, $value); + array_push($spec->components->schemas->ExposedEntity_NotIncludingNotEditable->enum, $value); } } - $spec->components->internalSchemas->ExposedEntity_IncludingUserEntities_NotIncludingNotEditable = clone $spec->components->internalSchemas->StringEnumTemplate; - foreach ($spec->components->internalSchemas->ExposedEntity_IncludingUserEntities->enum as $value) + $spec->components->schemas->ExposedEntity_IncludingUserEntities_NotIncludingNotEditable = clone $spec->components->schemas->StringEnumTemplate; + foreach ($spec->components->schemas->ExposedEntity_IncludingUserEntities->enum as $value) { - if (!in_array($value, $spec->components->internalSchemas->ExposedEntityNoEdit->enum)) + if (!in_array($value, $spec->components->schemas->ExposedEntityNoEdit->enum)) { - array_push($spec->components->internalSchemas->ExposedEntity_IncludingUserEntities_NotIncludingNotEditable->enum, $value); + array_push($spec->components->schemas->ExposedEntity_IncludingUserEntities_NotIncludingNotEditable->enum, $value); } } - $spec->components->internalSchemas->ExposedEntity_NotIncludingNotDeletable = clone $spec->components->internalSchemas->StringEnumTemplate; - foreach ($spec->components->internalSchemas->ExposedEntity->enum as $value) + $spec->components->schemas->ExposedEntity_NotIncludingNotDeletable = clone $spec->components->schemas->StringEnumTemplate; + foreach ($spec->components->schemas->ExposedEntity->enum as $value) { - if (!in_array($value, $spec->components->internalSchemas->ExposedEntityNoDelete->enum)) + if (!in_array($value, $spec->components->schemas->ExposedEntityNoDelete->enum)) { - array_push($spec->components->internalSchemas->ExposedEntity_NotIncludingNotDeletable->enum, $value); + array_push($spec->components->schemas->ExposedEntity_NotIncludingNotDeletable->enum, $value); } } - $spec->components->internalSchemas->ExposedEntity_NotIncludingNotListable = clone $spec->components->internalSchemas->StringEnumTemplate; - foreach ($spec->components->internalSchemas->ExposedEntity->enum as $value) + $spec->components->schemas->ExposedEntity_NotIncludingNotListable = clone $spec->components->schemas->StringEnumTemplate; + foreach ($spec->components->schemas->ExposedEntity->enum as $value) { - if (!in_array($value, $spec->components->internalSchemas->ExposedEntityNoListing->enum)) + if (!in_array($value, $spec->components->schemas->ExposedEntityNoListing->enum)) { - array_push($spec->components->internalSchemas->ExposedEntity_NotIncludingNotListable->enum, $value); + array_push($spec->components->schemas->ExposedEntity_NotIncludingNotListable->enum, $value); } } diff --git a/grocy.openapi.json b/grocy.openapi.json index 255ec003..1c7a7977 100644 --- a/grocy.openapi.json +++ b/grocy.openapi.json @@ -253,7 +253,7 @@ "required": true, "description": "A valid entity name", "schema": { - "$ref": "#/components/internalSchemas/ExposedEntity_NotIncludingNotListable" + "$ref": "#/components/schemas/ExposedEntity_NotIncludingNotListable" } }, { @@ -342,7 +342,7 @@ "required": true, "description": "A valid entity name", "schema": { - "$ref": "#/components/internalSchemas/ExposedEntity_NotIncludingNotEditable" + "$ref": "#/components/schemas/ExposedEntity_NotIncludingNotEditable" } } ], @@ -426,7 +426,7 @@ "required": true, "description": "A valid entity name", "schema": { - "$ref": "#/components/internalSchemas/ExposedEntity_NotIncludingNotListable" + "$ref": "#/components/schemas/ExposedEntity_NotIncludingNotListable" } }, { @@ -510,7 +510,7 @@ "required": true, "description": "A valid entity name", "schema": { - "$ref": "#/components/internalSchemas/ExposedEntity_NotIncludingNotEditable" + "$ref": "#/components/schemas/ExposedEntity_NotIncludingNotEditable" } }, { @@ -587,7 +587,7 @@ "required": true, "description": "A valid entity name", "schema": { - "$ref": "#/components/internalSchemas/ExposedEntity_NotIncludingNotDeletable" + "$ref": "#/components/schemas/ExposedEntity_NotIncludingNotDeletable" } }, { @@ -630,7 +630,7 @@ "required": true, "description": "A valid entity name", "schema": { - "$ref": "#/components/internalSchemas/ExposedEntity_IncludingUserEntities" + "$ref": "#/components/schemas/ExposedEntity_IncludingUserEntities" } }, { @@ -679,7 +679,7 @@ "required": true, "description": "A valid entity name", "schema": { - "$ref": "#/components/internalSchemas/ExposedEntity_IncludingUserEntities_NotIncludingNotEditable" + "$ref": "#/components/schemas/ExposedEntity_IncludingUserEntities_NotIncludingNotEditable" } }, { @@ -734,7 +734,7 @@ "required": true, "description": "The file group", "schema": { - "$ref": "#/components/internalSchemas/FileGroups" + "$ref": "#/components/schemas/FileGroups" } }, { @@ -814,7 +814,7 @@ "required": true, "description": "The file group", "schema": { - "$ref": "#/components/internalSchemas/FileGroups" + "$ref": "#/components/schemas/FileGroups" } }, { @@ -865,7 +865,7 @@ "required": true, "description": "The file group", "schema": { - "$ref": "#/components/internalSchemas/FileGroups" + "$ref": "#/components/schemas/FileGroups" } }, { @@ -1900,7 +1900,7 @@ "description": "The due date of the product to add, when omitted, the current date is used" }, "transaction_type": { - "$ref": "#/components/internalSchemas/StockTransactionType" + "$ref": "#/components/schemas/StockTransactionType" }, "price": { "type": "number", @@ -1988,7 +1988,7 @@ "description": "The amount to remove - please note that when tare weight handling for the product is enabled, this needs to be the amount including the container weight (gross), the amount to be posted will be automatically calculated based on what is in stock and the defined tare weight" }, "transaction_type": { - "$ref": "#/components/internalSchemas/StockTransactionType" + "$ref": "#/components/schemas/StockTransactionType" }, "spoiled": { "type": "boolean", @@ -2444,7 +2444,7 @@ "description": "The due date of the product to add, when omitted, the current date is used" }, "transaction_type": { - "$ref": "#/components/internalSchemas/StockTransactionType" + "$ref": "#/components/schemas/StockTransactionType" }, "price": { "type": "number", @@ -2523,7 +2523,7 @@ "description": "The amount to remove - please note that when tare weight handling for the product is enabled, this needs to be the amount including the container weight (gross), the amount to be posted will be automatically calculated based on what is in stock and the defined tare weight" }, "transaction_type": { - "$ref": "#/components/internalSchemas/StockTransactionType" + "$ref": "#/components/schemas/StockTransactionType" }, "spoiled": { "type": "boolean", @@ -4114,91 +4114,6 @@ } }, "components": { - "internalSchemas": { - "ExposedEntity": { - "type": "string", - "enum": [ - "products", - "chores", - "product_barcodes", - "batteries", - "locations", - "quantity_units", - "quantity_unit_conversions", - "shopping_list", - "shopping_lists", - "shopping_locations", - "recipes", - "recipes_pos", - "recipes_nestings", - "tasks", - "task_categories", - "product_groups", - "equipment", - "api_keys", - "userfields", - "userentities", - "userobjects", - "meal_plan", - "stock_log", - "stock", - "stock_current_locations", - "api_keys" - ] - }, - "ExposedEntityNoListing": { - "type": "string", - "enum": [ - "api_keys" - ] - }, - "ExposedEntityNoEdit": { - "type": "string", - "enum": [ - "stock_log", - "api_keys", - "stock", - "stock_current_locations" - ] - }, - "ExposedEntityNoDelete": { - "type": "string", - "enum": [ - "stock_log", - "stock", - "stock_current_locations" - ] - }, - "ExposedEntityEditRequiresAdmin": { - "type": "string", - "enum": [ - "api_keys" - ] - }, - "StockTransactionType": { - "type": "string", - "enum": [ - "purchase", - "consume", - "inventory-correction", - "product-opened" - ] - }, - "FileGroups": { - "type": "string", - "enum": [ - "equipmentmanuals", - "recipepictures", - "productpictures", - "userfiles", - "userpictures" - ] - }, - "StringEnumTemplate": { - "type": "string", - "enum": [] - } - }, "schemas": { "Product": { "type": "object", @@ -5082,7 +4997,7 @@ "type": "string" }, "transaction_type": { - "$ref": "#/components/internalSchemas/StockTransactionType" + "$ref": "#/components/schemas/StockTransactionType" }, "row_created_timestamp": { "type": "string", @@ -5130,7 +5045,7 @@ "default": false }, "transaction_type": { - "$ref": "#/components/internalSchemas/StockTransactionType" + "$ref": "#/components/schemas/StockTransactionType" }, "row_created_timestamp": { "type": "string", @@ -5180,7 +5095,7 @@ "type": "string" }, "transaction_type": { - "$ref": "#/components/internalSchemas/StockTransactionType" + "$ref": "#/components/schemas/StockTransactionType" } }, "example": { @@ -5428,6 +5343,91 @@ "type": "string" } } + }, + "ExposedEntity": { + "type": "string", + "enum": [ + "products", + "chores", + "product_barcodes", + "batteries", + "locations", + "quantity_units", + "quantity_unit_conversions", + "shopping_list", + "shopping_lists", + "shopping_locations", + "recipes", + "recipes_pos", + "recipes_nestings", + "tasks", + "task_categories", + "product_groups", + "equipment", + "api_keys", + "userfields", + "userentities", + "userobjects", + "meal_plan", + "stock_log", + "stock", + "stock_current_locations", + "api_keys" + ] + }, + "ExposedEntityNoListing": { + "type": "string", + "enum": [ + "api_keys" + ] + }, + "ExposedEntityNoEdit": { + "type": "string", + "enum": [ + "stock_log", + "api_keys", + "stock", + "stock_current_locations" + ] + }, + "ExposedEntityNoDelete": { + "type": "string", + "enum": [ + "stock_log", + "stock", + "stock_current_locations" + ] + }, + "ExposedEntityEditRequiresAdmin": { + "type": "string", + "enum": [ + "api_keys" + ] + }, + "StockTransactionType": { + "type": "string", + "enum": [ + "purchase", + "consume", + "inventory-correction", + "product-opened" + ] + }, + "FileGroups": { + "type": "string", + "enum": [ + "equipmentmanuals", + "recipepictures", + "productpictures", + "userfiles", + "userpictures" + ] + }, + "StringEnumTemplate": { + "type": "string", + "enum": [ + "" + ] } }, "securitySchemes": { diff --git a/localization/strings.pot b/localization/strings.pot index 40cb5fa4..5984e96b 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -324,7 +324,7 @@ msgstr "" msgid "Manage API keys" msgstr "" -msgid "REST API & data model documentation" +msgid "REST API browser" msgstr "" msgid "API keys" diff --git a/public/viewjs/openapiui.js b/public/viewjs/openapiui.js index d4f77c11..78315306 100644 --- a/public/viewjs/openapiui.js +++ b/public/viewjs/openapiui.js @@ -20,7 +20,8 @@ const swaggerUi = SwaggerUIBundle({ HideTopbarPlugin ], layout: 'StandaloneLayout', - docExpansion: "list" + docExpansion: "list", + defaultModelsExpandDepth: -1 }); window.ui = swaggerUi; diff --git a/services/UserfieldsService.php b/services/UserfieldsService.php index 53ac2ac0..492944e7 100644 --- a/services/UserfieldsService.php +++ b/services/UserfieldsService.php @@ -49,7 +49,7 @@ class UserfieldsService extends BaseService public function GetEntities() { - $exposedDefaultEntities = $this->getOpenApiSpec()->components->internalSchemas->ExposedEntity->enum; + $exposedDefaultEntities = $this->getOpenApiSpec()->components->schemas->ExposedEntity->enum; $userEntities = []; $specialEntities = ['users']; diff --git a/views/layout/default.blade.php b/views/layout/default.blade.php index b786f41d..29abe35b 100644 --- a/views/layout/default.blade.php +++ b/views/layout/default.blade.php @@ -652,7 +652,7 @@ @endif  {{ $__t('REST API & data model documentation') }} + href="{{ $U('/api') }}"> {{ $__t('REST API browser') }}  {{ $__t('Barcode scanner testing') }} diff --git a/views/openapiui.blade.php b/views/openapiui.blade.php index 3ef40d56..a43b8177 100644 --- a/views/openapiui.blade.php +++ b/views/openapiui.blade.php @@ -18,7 +18,7 @@ - {{ $__t('REST API & data model documentation') }} | grocy + {{ $__t('REST API browser') }} | grocy