-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LDT-49: API Endpoint for Exporting Site Configurations solution.
- Loading branch information
Showing
6 changed files
with
394 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# API Endpoint for Exporting Site Configurations | ||
|
||
## Description: | ||
To develop a secure REST API endpoint that enables administrators to export crucial site configurations, including content types and field settings, thereby facilitating site management and configuration versioning. | ||
|
||
## Acceptance Criteria: | ||
- The endpoint must be secured with appropriate authentication mechanisms to ensure that only users with administrative privileges can access the export functionality. | ||
- The API must provide a JSON export of selected configuration settings. | ||
- Proper error responses should be implemented to handle unauthorized access, unsupported configuration requests, and other potential API errors. | ||
|
||
## Solution | ||
Course Link: | ||
Troubleshoot: | ||
Raise Issue: https://github.com/axelerant-trainings/project-usecases/issues/new |
8 changes: 8 additions & 0 deletions
8
Drupal/modules/custom/config_exporter/config_exporter.info.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
name: 'Configuration Exporter' | ||
type: module | ||
description: 'Provides a REST endpoint for exporting site configurations.' | ||
core_version_requirement: ^10 | ||
package: Custom | ||
dependencies: | ||
- drupal:rest | ||
- drupal:serialization |
7 changes: 7 additions & 0 deletions
7
Drupal/modules/custom/config_exporter/config_exporter.routing.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
config_exporter.admin_settings: | ||
path: '/admin/config/system/config-manager' | ||
defaults: | ||
_form: '\Drupal\config_exporter\Form\ConfigManagerForm' | ||
_title: 'Configuration Manager' | ||
requirements: | ||
_permission: 'administer site configuration' |
143 changes: 143 additions & 0 deletions
143
Drupal/modules/custom/config_exporter/src/Form/ConfigManagerForm.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
<?php | ||
|
||
namespace Drupal\config_exporter\Form; | ||
|
||
use Drupal\Core\Config\Entity\ConfigEntityInterface; | ||
use Drupal\Core\Form\ConfigFormBase; | ||
use Drupal\Core\Form\FormStateInterface; | ||
use Drupal\Core\Entity\EntityTypeInterface; | ||
use Drupal\Core\Config\StorageInterface; | ||
use Drupal\Core\Entity\EntityTypeManagerInterface; | ||
use Symfony\Component\DependencyInjection\ContainerInterface; | ||
|
||
class ConfigManagerForm extends ConfigFormBase { | ||
|
||
/** | ||
* The entity type manager. | ||
* | ||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface | ||
*/ | ||
protected $entityTypeManager; | ||
|
||
/** | ||
* The config storage. | ||
* | ||
* @var \Drupal\Core\Config\StorageInterface | ||
*/ | ||
protected $configStorage; | ||
|
||
/** | ||
* Tracks the valid config entity type definitions. | ||
* | ||
* @var \Drupal\Core\Entity\EntityTypeInterface[] | ||
*/ | ||
protected $definitions = []; | ||
|
||
/** | ||
* Constructs a new ConfigSingleImportForm. | ||
* | ||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager | ||
* The entity type manager. | ||
* @param \Drupal\Core\Config\StorageInterface $config_storage | ||
* The config storage. | ||
*/ | ||
public function __construct(EntityTypeManagerInterface $entity_type_manager, StorageInterface $config_storage) { | ||
$this->entityTypeManager = $entity_type_manager; | ||
$this->configStorage = $config_storage; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public static function create(ContainerInterface $container) { | ||
return new static( | ||
$container->get('entity_type.manager'), | ||
$container->get('config.storage') | ||
); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getEditableConfigNames() { | ||
return [ | ||
'config_exporter.settings', | ||
]; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getFormId() { | ||
return 'config_exporter_admin_settings'; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function buildForm(array $form, FormStateInterface $form_state) { | ||
$options = $this->getListOfConfigOptions(); | ||
$selected = $this->config('config_exporter.settings')->get('selected_configurations') ?: []; | ||
|
||
$form['configurations'] = [ | ||
'#type' => 'checkboxes', | ||
'#title' => $this->t('Choose configurations to expose via API'), | ||
'#options' => $options, | ||
'#default_value' => $selected, | ||
]; | ||
|
||
return parent::buildForm($form, $form_state); | ||
} | ||
|
||
/** | ||
* Returns a list of general configurations. | ||
* | ||
* @return array | ||
* A list of general conficgurations. | ||
*/ | ||
private function getListOfConfigOptions() { | ||
|
||
// Entity related configurations. | ||
foreach ($this->entityTypeManager->getDefinitions() as $entity_type => $definition) { | ||
if ($definition->entityClassImplements(ConfigEntityInterface::class)) { | ||
$this->definitions[$entity_type] = $definition; | ||
} | ||
} | ||
|
||
// Gather the config entity prefixes. | ||
$config_prefixes = array_map(function (EntityTypeInterface $definition) { | ||
return $definition->getConfigPrefix() . '.'; | ||
}, $this->definitions); | ||
|
||
// Find all config, and then filter out entity configurations. | ||
$names = $this->configStorage->listAll(); | ||
$names = array_combine($names, $names); | ||
foreach ($names as $config_name) { | ||
foreach ($config_prefixes as $config_prefix) { | ||
if (str_starts_with($config_name, $config_prefix)) { | ||
unset($names[$config_name]); | ||
} | ||
} | ||
} | ||
|
||
return $names; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function submitForm(array &$form, FormStateInterface $form_state) | ||
{ | ||
$selected = array_filter($form_state->getValue('configurations')); | ||
|
||
// Remove keys and reset to sequential numeric keys | ||
// to avoid, 'key contains a dot which is not supported' error. | ||
$selected = array_values($selected); | ||
|
||
$this->config('config_exporter.settings') | ||
->set('selected_configurations', $selected) | ||
->save(); | ||
|
||
parent::submitForm($form, $form_state); | ||
} | ||
} |
120 changes: 120 additions & 0 deletions
120
Drupal/modules/custom/config_exporter/src/Plugin/rest/resource/ConfigExportResource.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
<?php | ||
|
||
namespace Drupal\config_exporter\Plugin\rest\resource; | ||
|
||
use Drupal\rest\Plugin\ResourceBase; | ||
use Drupal\rest\ResourceResponse; | ||
use Drupal\Core\Session\AccountProxyInterface; | ||
use Drupal\Core\Config\ConfigFactoryInterface; | ||
use Psr\Log\LoggerInterface; | ||
use Symfony\Component\DependencyInjection\ContainerInterface; | ||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; | ||
|
||
/** | ||
* Provides a resource for exporting configurations. | ||
* | ||
* @RestResource( | ||
* id = "config_export_resource", | ||
* label = @Translation("Config Export Resource"), | ||
* uri_paths = { | ||
* "canonical" = "/api/config-export/{config_name}" | ||
* } | ||
* ) | ||
*/ | ||
class ConfigExportResource extends ResourceBase { | ||
/** | ||
* The currently authenticated user. | ||
* | ||
* @var \Drupal\Core\Session\AccountInterface | ||
*/ | ||
protected $currentUser; | ||
|
||
/** | ||
* The configuration factory. | ||
* | ||
* @var \Drupal\Core\Config\ConfigFactoryInterface | ||
*/ | ||
protected $configFactory; | ||
|
||
/** | ||
* Constructs a ConfigExportResource instance. | ||
* | ||
* @param array $configuration | ||
* A configuration array containing information about the plugin instance. | ||
* @param string $plugin_id | ||
* The plugin_id for the plugin instance. | ||
* @param mixed $plugin_definition | ||
* The plugin implementation definition. | ||
* @param array $serializer_formats | ||
* The available serialization formats. | ||
* @param \Psr\Log\LoggerInterface $logger | ||
* A logger instance. | ||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager | ||
* The entity type manager. | ||
* @param \Drupal\Core\Session\AccountProxyInterface $current_user | ||
* The currently authenticated user. | ||
* @param \Drupal\contact\MailHandlerInterface $mail_handler | ||
* The contact mail handler service. | ||
*/ | ||
public function __construct(array $configuration, $plugin_id, $plugin_definition, $serializer_formats, LoggerInterface $logger, AccountProxyInterface $current_user, ConfigFactoryInterface $config_factory) { | ||
parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger); | ||
$this->currentUser = $current_user; | ||
$this->configFactory = $config_factory; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | ||
return new static( | ||
$configuration, | ||
$plugin_id, | ||
$plugin_definition, | ||
$container->getParameter('serializer.formats'), | ||
$container->get('logger.factory')->get('rest'), | ||
$container->get('current_user'), | ||
$container->get('config.factory') | ||
); | ||
} | ||
|
||
/** | ||
* Responds to GET requests. | ||
* | ||
* Returns details for the specified configuration. | ||
* | ||
* @param string $config_name | ||
* The name of the configuration. | ||
* | ||
* @return \Drupal\rest\ResourceResponse | ||
* The response containing the configuration detail. | ||
* | ||
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException; | ||
* When configuration does not exists or not allowed to view. | ||
*/ | ||
public function get($config_name = NULL) { | ||
|
||
// Get the list of allowed configurations. | ||
$config = $this->configFactory | ||
->get('config_exporter.settings') | ||
->get('selected_configurations') ?? []; | ||
|
||
// When configuration is not allowed to view. | ||
if (!in_array($config_name, $config)) { | ||
throw new BadRequestHttpException(sprintf('Configuration (%s) yet not exposed to view.', $config_name)); | ||
} | ||
|
||
$config = $this->configFactory->get($config_name); | ||
|
||
// When config does not exists and only has an empty new config object. | ||
if ($config->isNew()) { | ||
throw new BadRequestHttpException(sprintf('Configuration (%s) does not exists.', $config_name)); | ||
} | ||
else { | ||
$data[$config_name] = $config->getRawData(); | ||
|
||
$response = new ResourceResponse($data); | ||
$response->getCacheableMetadata()->addCacheTags(['config:config_exporter.settings']); | ||
return $response; | ||
} | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
Drupal/modules/custom/config_exporter/src/Plugin/rest/resource/ConfigListResource.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
<?php | ||
|
||
namespace Drupal\config_exporter\Plugin\rest\resource; | ||
|
||
use Drupal\rest\Plugin\ResourceBase; | ||
use Drupal\rest\ResourceResponse; | ||
use Drupal\Core\Config\ConfigFactoryInterface; | ||
use Psr\Log\LoggerInterface; | ||
use Symfony\Component\DependencyInjection\ContainerInterface; | ||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; | ||
|
||
/** | ||
* Provides a resource for fetching allowed config list. | ||
* | ||
* @RestResource( | ||
* id = "config_list", | ||
* label = @Translation("Allowed Config List"), | ||
* uri_paths = { | ||
* "canonical" = "/api/config-list" | ||
* } | ||
* ) | ||
*/ | ||
|
||
class ConfigListResource extends ResourceBase { | ||
|
||
/** | ||
* The configuration factory. | ||
* | ||
* @var \Drupal\Core\Config\ConfigFactoryInterface | ||
*/ | ||
protected $configFactory; | ||
|
||
/** | ||
* Constructs a ConfigExportResource instance. | ||
* | ||
* @param array $configuration | ||
* A configuration array containing information about the plugin instance. | ||
* @param string $plugin_id | ||
* The plugin_id for the plugin instance. | ||
* @param mixed $plugin_definition | ||
* The plugin implementation definition. | ||
* @param array $serializer_formats | ||
* The available serialization formats. | ||
* @param \Psr\Log\LoggerInterface $logger | ||
* A logger instance. | ||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager | ||
* The entity type manager. | ||
* @param \Drupal\Core\Session\AccountProxyInterface $current_user | ||
* The currently authenticated user. | ||
* @param \Drupal\contact\MailHandlerInterface $mail_handler | ||
* The contact mail handler service. | ||
*/ | ||
public function __construct(array $configuration, $plugin_id, $plugin_definition, $serializer_formats, LoggerInterface $logger, ConfigFactoryInterface $config_factory) { | ||
parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger); | ||
$this->configFactory = $config_factory; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | ||
return new static( | ||
$configuration, | ||
$plugin_id, | ||
$plugin_definition, | ||
$container->getParameter('serializer.formats'), | ||
$container->get('logger.factory')->get('rest'), | ||
$container->get('config.factory') | ||
); | ||
} | ||
|
||
/** | ||
* Responds to GET requests. | ||
* | ||
* Returns list of allowed configurations. | ||
* | ||
* @param string $config_name | ||
* The name of the configuration. | ||
* | ||
* @return \Drupal\rest\ResourceResponse | ||
* The response containing the list of allowed configurations. | ||
* | ||
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException; | ||
* When admin did not allowed any configuration to view. | ||
*/ | ||
public function get() { | ||
|
||
$config = $this->configFactory | ||
->get('config_exporter.settings') | ||
->get('selected_configurations') ?? []; | ||
|
||
// When config does not exists and only has an empty new config object. | ||
if (empty($config)) { | ||
throw new BadRequestHttpException('Currently, site admin did not allow any configuration to view.'); | ||
} | ||
else { | ||
$response = new ResourceResponse($config); | ||
$response->getCacheableMetadata()->addCacheTags(['config:config_exporter.settings']); | ||
return $response; | ||
} | ||
} | ||
} |