summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMateu Aguiló Bosch2017-04-23 08:04:08 (GMT)
committerMateu Aguiló Bosch2017-04-23 08:04:08 (GMT)
commitc865b7e8cdb77307b9f202950193846372acf052 (patch)
tree18ffbcd86e96f5bff6359a383966ef4c5aac76d8
feat(Misc): Initial commit
-rw-r--r--README.md12
-rw-r--r--config/schema/jsonapi_extras.schema.yml43
-rw-r--r--jsonapi_extras.info.yml6
-rw-r--r--jsonapi_extras.links.action.yml5
-rw-r--r--jsonapi_extras.links.menu.yml5
-rw-r--r--src/Entity/ResourceConfig.php118
-rw-r--r--src/Form/ResourceConfigDeleteForm.php58
-rw-r--r--src/Form/ResourceConfigForm.php187
-rw-r--r--src/ResourceConfigHtmlRouteProvider.php101
-rw-r--r--src/ResourceConfigListBuilder.php37
10 files changed, 572 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d830267
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+# JSON API Extras
+
+This module provides extra functionality on top of JSON API. You should not need
+this module to get an spec compliant JSON API.
+
+This module adds the following features:
+
+ - Allows you to customize the URL for your resource under the `/jsonapi`
+ prefix.
+ - Allows you to customize the resource type to other than
+ `${entityTypeId}--${bundle}`.
+ - Lets you remove fields from the JSON API output.
diff --git a/config/schema/jsonapi_extras.schema.yml b/config/schema/jsonapi_extras.schema.yml
new file mode 100644
index 0000000..6144bc6
--- /dev/null
+++ b/config/schema/jsonapi_extras.schema.yml
@@ -0,0 +1,43 @@
+jsonapi_extras.resource_config.*:
+ type: config_entity
+ label: 'Resource Config config'
+ mapping:
+ id:
+ type: string
+ label: 'Original Resource plugin ID.'
+ path:
+ type: string
+ label: 'Path'
+ description: 'The path for the resource.'
+ resourceType:
+ type: string
+ label: 'Type'
+ description: 'The value for the resource type.'
+ resourceFields:
+ type: sequence
+ label: 'Fields'
+ sequence:
+ type: jsonapi_extras.resource_field
+
+jsonapi_extras.resource_field:
+ type: mapping
+ mapping:
+ publicName:
+ type: string
+ label: 'Public attribute name'
+ id:
+ type: string
+ label: 'Resource field type'
+ class:
+ type: string
+ label: 'Wrapper class'
+ postNormalizationCallbacks:
+ type: sequence
+ label: 'Post-normalization callbacks'
+ description: 'Set of static callbacks to be executed in sequence after normalization.'
+ sequence:
+ type: callable
+
+# TODO: Probably worth creating a data type to validate that it's a callable.
+callable:
+ type: string
diff --git a/jsonapi_extras.info.yml b/jsonapi_extras.info.yml
new file mode 100644
index 0000000..6413d5f
--- /dev/null
+++ b/jsonapi_extras.info.yml
@@ -0,0 +1,6 @@
+name: JSON API Extras
+type: module
+description: Builds on top of JSON API to deliver extra functionality.
+core: 8.x
+dependencies:
+ - jsonapi
diff --git a/jsonapi_extras.links.action.yml b/jsonapi_extras.links.action.yml
new file mode 100644
index 0000000..049f6a5
--- /dev/null
+++ b/jsonapi_extras.links.action.yml
@@ -0,0 +1,5 @@
+entity.resource_config.add_form:
+ route_name: 'entity.resource_config.add_form'
+ title: 'Add Resource Config'
+ appears_on:
+ - entity.resource_config.collection
diff --git a/jsonapi_extras.links.menu.yml b/jsonapi_extras.links.menu.yml
new file mode 100644
index 0000000..253af37
--- /dev/null
+++ b/jsonapi_extras.links.menu.yml
@@ -0,0 +1,5 @@
+entity.resource_config.collection:
+ title: JSON API
+ description: 'Configure JSON API extras.'
+ parent: system.admin_config_services
+ route_name: entity.resource_config.collection
diff --git a/src/Entity/ResourceConfig.php b/src/Entity/ResourceConfig.php
new file mode 100644
index 0000000..70b3270
--- /dev/null
+++ b/src/Entity/ResourceConfig.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\jsonapi_extras\Entity\ResourceConfig.
+ */
+
+namespace Drupal\jsonapi_extras\Entity;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+
+/**
+ * Defines the Resource Config entity.
+ *
+ * @ConfigEntityType(
+ * id = "resource_config",
+ * label = @Translation("Resource Config"),
+ * handlers = {
+ * "list_builder" = "Drupal\jsonapi_extras\ResourceConfigListBuilder",
+ * "form" = {
+ * "add" = "Drupal\jsonapi_extras\Form\ResourceConfigForm",
+ * "edit" = "Drupal\jsonapi_extras\Form\ResourceConfigForm",
+ * "delete" = "Drupal\jsonapi_extras\Form\ResourceConfigDeleteForm"
+ * },
+ * "route_provider" = {
+ * "html" = "Drupal\jsonapi_extras\ResourceConfigHtmlRouteProvider",
+ * },
+ * },
+ * config_prefix = "resource_config",
+ * admin_permission = "administer site configuration",
+ * entity_keys = {
+ * "id" = "id",
+ * "label" = "label",
+ * "uuid" = "uuid"
+ * },
+ * links = {
+ * "canonical" = "/admin/config/services/resource_config/{resource_config}",
+ * "add-form" = "/admin/config/services/resource_config/add",
+ * "edit-form" = "/admin/config/services/resource_config/{resource_config}/edit",
+ * "delete-form" = "/admin/config/services/resource_config/{resource_config}/delete",
+ * "collection" = "/admin/config/services/resource_config"
+ * }
+ * )
+ */
+class ResourceConfig extends ConfigEntityBase {
+
+ /**
+ * The Resource Config ID.
+ *
+ * @var string
+ */
+ protected $id;
+
+ /**
+ * The path for the resource.
+ *
+ * @var string
+ */
+ protected $path;
+
+ /**
+ * The type for the resource.
+ *
+ * @var string
+ */
+ protected $resourceType;
+
+ /**
+ * Resource fields.
+ *
+ * @var array
+ */
+ protected $resourceFields = [];
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __construct(array $values, $entity_type) {
+ parent::__construct(static::addDefaults($values), $entity_type);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function addDefaults(array $values) {
+ $default_field = [
+ 'data' => [
+ 'column' => 'value',
+ ],
+ 'id' => 'field',
+ ];
+ if (!empty($values['resourceFields'])) {
+ $values['resourceFields'] = array_map(function ($value) use ($default_field) {
+ if (!is_array($value)) {
+ // Avoid PHP blowing up.
+ return $value;
+ }
+ $defaults = NestedArray::mergeDeep($default_field, $value);
+
+ // If the field has a callback, that makes it a callback resource field.
+ if (!empty($defaults['callback'])) {
+ $defaults['id'] = 'callback';
+ unset($defaults['data']);
+ }
+
+ // If the process callbacks are empty, remove them.
+ if (isset($defaults['processCallbacks']) && !$defaults['processCallbacks']) {
+ unset($defaults['processCallbacks']);
+ }
+
+ return $defaults;
+ }, $values['resourceFields']);
+ }
+ return $values;
+ }
+
+}
diff --git a/src/Form/ResourceConfigDeleteForm.php b/src/Form/ResourceConfigDeleteForm.php
new file mode 100644
index 0000000..3eb20d4
--- /dev/null
+++ b/src/Form/ResourceConfigDeleteForm.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\jsonapi_extras\Form\ResourceConfigDeleteForm.
+ */
+
+namespace Drupal\jsonapi_extras\Form;
+
+use Drupal\Core\Entity\EntityConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+/**
+ * Builds the form to delete Resource Config entities.
+ */
+class ResourceConfigDeleteForm extends EntityConfirmFormBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getQuestion() {
+ return $this->t('Are you sure you want to delete %name?', array('%name' => $this->entity->label()));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCancelUrl() {
+ return new Url('entity.resource_config.collection');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConfirmText() {
+ return $this->t('Delete');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, FormStateInterface $form_state) {
+ $this->entity->delete();
+
+ drupal_set_message(
+ $this->t('content @type: deleted @label.',
+ [
+ '@type' => $this->entity->bundle(),
+ '@label' => $this->entity->label(),
+ ]
+ )
+ );
+
+ $form_state->setRedirectUrl($this->getCancelUrl());
+ }
+
+}
diff --git a/src/Form/ResourceConfigForm.php b/src/Form/ResourceConfigForm.php
new file mode 100644
index 0000000..0513fba
--- /dev/null
+++ b/src/Form/ResourceConfigForm.php
@@ -0,0 +1,187 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\jsonapi_extras\Form\ResourceConfigForm.
+ */
+
+namespace Drupal\jsonapi_extras\Form;
+
+use Drupal\Core\Entity\EntityForm;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\jsonapi\ResourceType\ResourceTypeRepository;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class ResourceConfigForm.
+ *
+ * @package Drupal\jsonapi_extras\Form
+ */
+class ResourceConfigForm extends EntityForm {
+
+ /**
+ * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
+ */
+ protected $bundleInfo;
+
+ /**
+ * @var \Drupal\jsonapi\ResourceType\ResourceTypeRepository
+ */
+ protected $resourceTypeRepository;
+
+ /**
+ * EntityBundlePicker constructor.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+ * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info
+ */
+ public function __construct(EntityTypeBundleInfoInterface $bundle_info, ResourceTypeRepository $resource_type_repository) {
+ $this->bundleInfo = $bundle_info;
+ $this->resourceTypeRepository = $resource_type_repository;
+ }
+
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('entity_type.bundle.info'),
+ $container->get('jsonapi.resource_type.repository')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function form(array $form, FormStateInterface $form_state) {
+ $form = parent::form($form, $form_state);
+
+ $entity_types = $this->entityTypeManager->getDefinitions();
+ $entity_type_options = array_reduce($entity_types, function ($carry, EntityTypeInterface $entity_type) {
+ $carry[$entity_type->id()] = $entity_type->getLabel();
+
+ return $carry;
+ }, []);
+
+ $form['entity_type_id'] = [
+ '#title' => $this->t('Entity Type'),
+ '#type' => 'select',
+ '#options' => $entity_type_options,
+ '#empty_option' => $this->t('- Select -'),
+ '#required' => TRUE,
+ '#ajax' => [
+ 'callback' => '::bundleCallback',
+ 'wrapper' => 'bundle-wrapper',
+ ],
+ ];
+
+ // Disable caching on this form.
+ $form_state->setCached(FALSE);
+
+ $form['actions'] = [
+ '#type' => 'actions',
+ ];
+
+ // Add a submit button that handles the submission of the form.
+ $form['actions']['submit'] = [
+ '#type' => 'submit',
+ '#value' => $this->t('Submit'),
+ ];
+
+ $form['bundle_wrapper'] = [
+ '#type' => 'container',
+ '#attributes' => ['id' => 'bundle-wrapper'],
+ ];
+ $form['fields_wrapper'] = [
+ '#type' => 'container',
+ '#attributes' => ['id' => 'fields-wrapper'],
+ ];
+ if (!$entity_type_id = $form_state->getValue('entity_type_id')) {
+ return $form;
+ }
+ $has_bundles = (bool) $this->entityTypeManager
+ ->getDefinition($entity_type_id)->getBundleEntityType();
+ if ($has_bundles) {
+ $bundles = [];
+ $bundle_info = $this->bundleInfo->getBundleInfo($entity_type_id);
+ foreach ($bundle_info as $bundle_id => $info) {
+ $bundles[$bundle_id] = $info['translatable']
+ ? $this->t($info['label'])
+ : $info['label'];
+ }
+ $form['bundle_wrapper']['bundle_id'] = [
+ '#type' => 'select',
+ '#empty_option' => $this->t('- Select -'),
+ '#title' => $this->t('Bundle'),
+ '#options' => $bundles,
+ '#required' => TRUE,
+ '#ajax' => [
+ 'callback' => '::bundleCallback',
+ 'wrapper' => 'bundle-wrapper',
+ ],
+ ];
+ }
+ else {
+ $form['bundle_wrapper']['bundle_id'] = [
+ '#type' => 'hidden',
+ '#value' => $entity_type_id,
+ ];
+ }
+ $bundle = $has_bundles
+ ? $form_state->getValue('bundle_id')
+ : $entity_type_id;
+ if ($resource_type = $this->resourceTypeRepository->get($entity_type_id, $bundle)) {
+ // Get the JSON API resource type.
+ $form['bundle_wrapper']['fields_wrapper']['header'] = [
+ '#type' => 'html_tag',
+ '#tag' => 'h2',
+ '#value' => $this->t('Override @type', [
+ '@type' => $resource_type->getTypeName(),
+ '@entity_type' => $entity_type_id,
+ '@bundle' => $bundle,
+ ]),
+ ];
+ }
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save(array $form, FormStateInterface $form_state) {
+ $resource_config = $this->entity;
+ $status = $resource_config->save();
+
+ switch ($status) {
+ case SAVED_NEW:
+ drupal_set_message($this->t('Created the %label Resource Config.', [
+ '%label' => $resource_config->label(),
+ ]));
+ break;
+
+ default:
+ drupal_set_message($this->t('Saved the %label Resource Config.', [
+ '%label' => $resource_config->label(),
+ ]));
+ }
+ $form_state->setRedirectUrl($resource_config->urlInfo('collection'));
+ }
+
+ /**
+ * Implements callback for Ajax event on entity type or bundle selection.
+ *
+ * @param array $form
+ * From render array.
+ *
+ * @return array
+ * Color selection section of the form.
+ */
+ public function bundleCallback(array &$form) {
+ return $form['bundle_wrapper'];
+ }
+
+}
diff --git a/src/ResourceConfigHtmlRouteProvider.php b/src/ResourceConfigHtmlRouteProvider.php
new file mode 100644
index 0000000..4d71545
--- /dev/null
+++ b/src/ResourceConfigHtmlRouteProvider.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\jsonapi_extras\ResourceConfigHtmlRouteProvider.
+ */
+
+namespace Drupal\jsonapi_extras;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Provides routes for Resource Config entities.
+ *
+ * @see \Drupal\Core\Entity\Routing\AdminHtmlRouteProvider
+ * @see \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider
+ */
+class ResourceConfigHtmlRouteProvider extends AdminHtmlRouteProvider {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getRoutes(EntityTypeInterface $entity_type) {
+ $collection = parent::getRoutes($entity_type);
+
+ $entity_type_id = $entity_type->id();
+
+ if ($collection_route = $this->getCollectionRoute($entity_type)) {
+ $collection->add("entity.{$entity_type_id}.collection", $collection_route);
+ }
+
+ if ($add_form_route = $this->getAddFormRoute($entity_type)) {
+ $collection->add("entity.{$entity_type_id}.add_form", $add_form_route);
+ }
+
+ return $collection;
+ }
+
+ /**
+ * Gets the collection route.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+ * The entity type.
+ *
+ * @return \Symfony\Component\Routing\Route|null
+ * The generated route, if available.
+ */
+ protected function getCollectionRoute(EntityTypeInterface $entity_type) {
+ if ($entity_type->hasLinkTemplate('collection') && $entity_type->hasListBuilderClass()) {
+ $entity_type_id = $entity_type->id();
+ $route = new Route($entity_type->getLinkTemplate('collection'));
+ $route
+ ->setDefaults([
+ '_entity_list' => $entity_type_id,
+ // Make sure this is not a TranslatableMarkup object as the
+ // TitleResolver translates this string again.
+ '_title' => (string) $entity_type->getLabel(),
+ ])
+ ->setRequirement('_permission', $entity_type->getAdminPermission())
+ ->setOption('_admin_route', TRUE);
+
+ return $route;
+ }
+ }
+
+ /**
+ * Gets the add-form route.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+ * The entity type.
+ *
+ * @return \Symfony\Component\Routing\Route|null
+ * The generated route, if available.
+ */
+ protected function getAddFormRoute(EntityTypeInterface $entity_type) {
+ if ($entity_type->hasLinkTemplate('add-form')) {
+ $entity_type_id = $entity_type->id();
+ $route = new Route($entity_type->getLinkTemplate('add-form'));
+ // Use the add form handler, if available, otherwise default.
+ $operation = 'default';
+ if ($entity_type->getFormClass('add')) {
+ $operation = 'add';
+ }
+ $route
+ ->setDefaults([
+ '_entity_form' => "{$entity_type_id}.{$operation}",
+ '_title' => "Add {$entity_type->getLabel()}",
+ ])
+ ->setRequirement('_entity_create_access', $entity_type_id)
+ ->setOption('parameters', [
+ $entity_type_id => ['type' => 'entity:' . $entity_type_id],
+ ])
+ ->setOption('_admin_route', TRUE);
+
+ return $route;
+ }
+ }
+
+}
diff --git a/src/ResourceConfigListBuilder.php b/src/ResourceConfigListBuilder.php
new file mode 100644
index 0000000..62854ec
--- /dev/null
+++ b/src/ResourceConfigListBuilder.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\jsonapi_extras\ResourceConfigListBuilder.
+ */
+
+namespace Drupal\jsonapi_extras;
+
+use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides a listing of Resource Config entities.
+ */
+class ResourceConfigListBuilder extends ConfigEntityListBuilder {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildHeader() {
+ $header['label'] = $this->t('Resource Config');
+ $header['id'] = $this->t('Machine name');
+ return $header + parent::buildHeader();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildRow(EntityInterface $entity) {
+ $row['label'] = $entity->label();
+ $row['id'] = $entity->id();
+ // You probably want a few more properties here...
+ return $row + parent::buildRow($entity);
+ }
+
+}