diff --git a/READMES/jsonapi.md b/READMES/jsonapi.md index 6e6aa8e82c..1bf3ab92ee 100644 --- a/READMES/jsonapi.md +++ b/READMES/jsonapi.md @@ -21,5 +21,27 @@ If you expect to see a field's data included in the response and it is not there See the config at `admin/config/services/jsonapi/resource_types/node--news_story/edit?destination=/admin/config/services/jsonapi/resource_types` for an example. **Breadcrumbs and JSON:API** -See in [CMS Breadcrumb documentation](https://prod.cms.va.gov/admin/structure/cm_document/note/126/breadcrumbs) +See in [CMS Breadcrumb documentation](https://prod.cms.va.gov/admin/structure/cm_document/note/126/breadcrumbs) +## UI Explorer + +You can explore the JSON:API endpoints via a Swagger UI to see what's available and to test out requests and responses. + +1. Login to the site as a user with an "administrator" or "content_api_consumer" role. +2. Go to "/admin/config/services/openapi/swagger/jsonapi" +3. Read a [Swagger UI tutorial](https://idratherbewriting.com/learnapidoc/pubapis_swagger.html) to familiarize yourself with the UI features. + +Via this UI, you can: + +1. View resource information - Each endpoint has a description, list of parameters, and response codes. +2. Try it out - You can test each endpoint with a "Try it out" button in the top right section of "Parameters". +3. Filter by tag - Use the search to narrow down the endpoints displayed. +4. Deep Link - As you click to gather information about the API, the URL automatically updates so you can share that + specific documentation with others. + +## Tests + +You can find JSON:API tests in the following places: + +- 'tests/phpunit/API/JsonApiRequestTest' - Tests GET requests and associated configuration. +- 'tests/phpunit/API/JsonApiExplorerUITest' - Tests Swagger UI for OpenAPI documentation. diff --git a/composer.json b/composer.json index dd0eae0b8c..d8053f9c2f 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "require": { "bower-asset/cropper": "^4.1", "caxy/php-htmldiff": "^0.1.14", - "composer/installers": "^2.2.0", + "composer/installers": "^2.2", "consolidation/site-process": "^5.2", "cweagans/composer-patches": "^1.7", "datadog/dd-trace": "^0.95.0", @@ -143,6 +143,8 @@ "drupal/office_hours": "^1.9", "drupal/openapi": "^2.0@RC", "drupal/openapi_jsonapi": "^3.0", + "drupal/openapi_ui": "^1.0@RC", + "drupal/openapi_ui_swagger": "^1.0", "drupal/override_node_options": "^2.4", "drupal/paragraphs": "^1.5", "drupal/paragraphs_browser": "^1.1", @@ -202,6 +204,7 @@ "mglaman/phpstan-drupal": "^1.0", "michelf/php-markdown": "^2.0", "mikey179/vfsstream": "^1.6", + "mnsami/composer-custom-directory-installer": "^2.0", "npm-asset/dropzone": "^5.5", "npm-asset/yarn": "1.19.1", "oomphinc/composer-installers-extender": "^2.0", @@ -305,7 +308,8 @@ "php-http/discovery": true, "cweagans/composer-patches": true, "orakili/composer-drupal-info-file-patch-helper": true, - "digitalrevolution/php-codesniffer-baseline": true + "digitalrevolution/php-codesniffer-baseline": true, + "mnsami/composer-custom-directory-installer": false } }, "extra": { @@ -336,6 +340,7 @@ "type:drupal-core" ], "docroot/libraries/{$name}": [ + "swagger-api/swagger-ui", "type:drupal-library", "type:bower-asset", "type:npm-asset" @@ -467,6 +472,9 @@ "3110109 - Support renamed resource types Part 2": "https://git.drupalcode.org/project/openapi_jsonapi/-/commit/9b02c70521c499152c915e481aaf4925992bc8f9.patch", "3185778 - Fix TypeError: Cannot read property 'anyOf' of undefined": "https://www.drupal.org/files/issues/2020-12-21/add_required_keyword_8.x_2.x-3185778-5.patch" }, + "drupal/openapi_ui_swagger": { + "3259746 - Add more UI config defaults": "https://www.drupal.org/files/issues/2023-12-13/add-more-config-defaults-3259746-3.patch" + }, "drupal/paragraphs": { "3138609 - Fix broken fieldsets on forms": "https://www.drupal.org/files/issues/2020-06-03/3138609-24.patch", "2919186 - Remove button should not be shown on required field": "https://www.drupal.org/files/issues/2020-05-13/hide_field_required_paragraphs_remove_button_1.patch" diff --git a/composer.lock b/composer.lock index 594e3f1031..d4e493c5fe 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "58b8d1517a64990d5e5c614a54eabc98", + "content-hash": "0cba3a7279e9faa20cc7a2d6cb05cf96", "packages": [ { "name": "asm89/stack-cors", @@ -10541,6 +10541,100 @@ "source": "https://git.drupalcode.org/project/openapi_jsonapi" } }, + { + "name": "drupal/openapi_ui", + "version": "1.0.0-rc4", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/openapi_ui.git", + "reference": "8.x-1.0-rc4" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/openapi_ui-8.x-1.0-rc4.zip", + "reference": "8.x-1.0-rc4", + "shasum": "11f8cc945a2e6eb45dad512077a42ea9024fb4fc" + }, + "require": { + "drupal/core": "^8.8 || ^9 || ^10" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.0-rc4", + "datestamp": "1670512625", + "security-coverage": { + "status": "not-covered", + "message": "RC releases are not covered by Drupal security advisories." + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "richgerdes", + "homepage": "https://www.drupal.org/user/3437973" + } + ], + "description": "Provides plugin system for OpenAPI/Swagger Interface libraries.", + "homepage": "http://drupal.org/project/openapi_ui", + "support": { + "source": "https://git.drupalcode.org/project/openapi_ui" + } + }, + { + "name": "drupal/openapi_ui_swagger", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/openapi_ui_swagger.git", + "reference": "8.x-1.0" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/openapi_ui_swagger-8.x-1.0.zip", + "reference": "8.x-1.0", + "shasum": "32478822e27fc02193f2179c752fbbfd092cf366" + }, + "require": { + "drupal/core": "^8 || ^9 || ^10", + "drupal/openapi_ui": "^1.0", + "swagger-api/swagger-ui": "^3.0.17" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.0", + "datestamp": "1696427728", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "richgerdes", + "homepage": "https://www.drupal.org/user/3437973" + } + ], + "description": "Creates OpenAPI specification for Drupal REST resources.", + "homepage": "https://www.drupal.org/project/openapi_ui_swagger", + "keywords": [ + "Drupal" + ], + "support": { + "source": "http://cgit.drupalcode.org/openapi_ui_swagger", + "issues": "http://drupal.org/project/issues/openapi_ui_swagger" + } + }, { "name": "drupal/override_node_options", "version": "2.7.0", @@ -16848,6 +16942,62 @@ }, "time": "2016-07-25T17:07:32+00:00" }, + { + "name": "mnsami/composer-custom-directory-installer", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/mnsami/composer-custom-directory-installer.git", + "reference": "85f66323978d0b1cb0e6acc7f69b3e7b912f82d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mnsami/composer-custom-directory-installer/zipball/85f66323978d0b1cb0e6acc7f69b3e7b912f82d9", + "reference": "85f66323978d0b1cb0e6acc7f69b3e7b912f82d9", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3" + }, + "type": "composer-plugin", + "extra": { + "class": [ + "Composer\\CustomDirectoryInstaller\\LibraryPlugin", + "Composer\\CustomDirectoryInstaller\\PearPlugin", + "Composer\\CustomDirectoryInstaller\\PluginPlugin" + ], + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Composer\\CustomDirectoryInstaller": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mina Nabil Sami", + "email": "mina.nsami@gmail.com" + } + ], + "description": "A composer plugin, to help install packages of different types in custom paths.", + "keywords": [ + "composer", + "composer-installer", + "composer-plugin" + ], + "support": { + "issues": "https://github.com/mnsami/composer-custom-directory-installer/issues", + "source": "https://github.com/mnsami/composer-custom-directory-installer/tree/2.0.0" + }, + "time": "2020-08-18T11:00:11+00:00" + }, { "name": "monolog/monolog", "version": "2.9.2", @@ -21835,6 +21985,67 @@ }, "time": "2023-09-08T16:15:47+00:00" }, + { + "name": "swagger-api/swagger-ui", + "version": "v3.52.5", + "source": { + "type": "git", + "url": "https://github.com/swagger-api/swagger-ui.git", + "reference": "f1ad60dc92e7edb0898583e16c3e66fe3e9eada2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/f1ad60dc92e7edb0898583e16c3e66fe3e9eada2", + "reference": "f1ad60dc92e7edb0898583e16c3e66fe3e9eada2", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Anna Bodnia", + "email": "anna.bodnia@gmail.com" + }, + { + "name": "Buu Nguyen", + "email": "buunguyen@gmail.com" + }, + { + "name": "Josh Ponelat", + "email": "jponelat@gmail.com" + }, + { + "name": "Kyle Shockey", + "email": "kyleshockey1@gmail.com" + }, + { + "name": "Robert Barnwell", + "email": "robert@robertismy.name" + }, + { + "name": "Sahar Jafari", + "email": "shr.jafari@gmail.com" + } + ], + "description": " Swagger UI is a collection of HTML, Javascript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.", + "homepage": "http://swagger.io", + "keywords": [ + "api", + "documentation", + "openapi", + "specification", + "swagger", + "ui" + ], + "support": { + "issues": "https://github.com/swagger-api/swagger-ui/issues", + "source": "https://github.com/swagger-api/swagger-ui/tree/v3.52.5" + }, + "time": "2021-10-14T14:25:14+00:00" + }, { "name": "symfony/browser-kit", "version": "v6.4.0", @@ -26947,6 +27158,7 @@ "drupal/mimemail": 15, "drupal/no_table_drag": 15, "drupal/openapi": 5, + "drupal/openapi_ui": 5, "drupal/paragraphs_features": 10, "drupal/password_strength": 20, "drupal/pathologic": 15, diff --git a/config/sync/core.extension.yml b/config/sync/core.extension.yml index e0f6987dbf..93ef439b0f 100644 --- a/config/sync/core.extension.yml +++ b/config/sync/core.extension.yml @@ -177,6 +177,8 @@ module: office_hours: 0 openapi: 0 openapi_jsonapi: 0 + openapi_ui: 0 + openapi_ui_swagger: 0 options: 0 override_node_options: 0 page_cache: 0 diff --git a/tests/phpunit/API/JsonApiExplorerUiTest.php b/tests/phpunit/API/JsonApiExplorerUiTest.php new file mode 100644 index 0000000000..491a0d2050 --- /dev/null +++ b/tests/phpunit/API/JsonApiExplorerUiTest.php @@ -0,0 +1,44 @@ +createUser(); + $user->addRole('content_api_consumer'); + $user->save(); + + $this->drupalLogin($user); + $this->drupalGet('/admin/config/services/openapi/swagger/jsonapi'); + + // Confirm that the page is accessible. + $this->assertSession()->statusCodeEquals(200); + + // Assert that ".page-title" is "OpenAPI Documentation". + $this->assertSession()->elementTextContains( + 'css', + '.page-title', + 'OpenAPI Documentation' + ); + + // Any further assertions would need to be done with JavaScript test bases, + // such as Nightwatch. Or this test could be moved to Cypress. + // But this is a good start... + } + +}