RebelCode/rcmod-eddbk-rest-api

View on GitHub
src/Module/EddBkRestApiModule.php

Summary

Maintainability
F
3 days
Test Coverage
<?php

namespace RebelCode\EddBookings\RestApi\Module;

use ArrayIterator;
use DateTime;
use DateTimeZone;
use Dhii\Data\Container\ContainerFactoryInterface;
use Dhii\Event\EventFactoryInterface;
use Dhii\Exception\InternalException;
use Dhii\Factory\GenericCallbackFactory;
use Dhii\Iterator\NormalizeIteratorCapableTrait;
use Dhii\Util\String\StringableInterface as Stringable;
use IteratorIterator;
use Psr\Container\ContainerInterface;
use Psr\EventManager\EventManagerInterface;
use RebelCode\EddBookings\RestApi\Auth\FilterAuthValidator;
use RebelCode\EddBookings\RestApi\Auth\TolerantCompositeValidator;
use RebelCode\EddBookings\RestApi\Auth\UserIsAdminAuthValidator;
use RebelCode\EddBookings\RestApi\Controller\BookingsController;
use RebelCode\EddBookings\RestApi\Controller\ClientsController;
use RebelCode\EddBookings\RestApi\Controller\ResourcesController;
use RebelCode\EddBookings\RestApi\Controller\ServicesController;
use RebelCode\EddBookings\RestApi\Controller\SessionsController;
use RebelCode\EddBookings\RestApi\Handlers\Bookings\BookingInfoHandler;
use RebelCode\EddBookings\RestApi\Handlers\Bookings\CreateBookingHandler;
use RebelCode\EddBookings\RestApi\Handlers\Bookings\DeleteBookingHandler;
use RebelCode\EddBookings\RestApi\Handlers\Bookings\QueryBookingsHandler;
use RebelCode\EddBookings\RestApi\Handlers\Bookings\UpdateBookingHandler;
use RebelCode\EddBookings\RestApi\Handlers\Clients\ClientInfoHandler;
use RebelCode\EddBookings\RestApi\Handlers\Clients\CreateClientHandler;
use RebelCode\EddBookings\RestApi\Handlers\Clients\QueryClientsHandler;
use RebelCode\EddBookings\RestApi\Handlers\Resources\CreateResourceHandler;
use RebelCode\EddBookings\RestApi\Handlers\Resources\DeleteResourceHandler;
use RebelCode\EddBookings\RestApi\Handlers\Resources\QueryResourcesHandler;
use RebelCode\EddBookings\RestApi\Handlers\Resources\ResourceInfoHandler;
use RebelCode\EddBookings\RestApi\Handlers\Resources\UpdateResourceHandler;
use RebelCode\EddBookings\RestApi\Handlers\Services\CreateServiceHandler;
use RebelCode\EddBookings\RestApi\Handlers\Services\DeleteServiceHandler;
use RebelCode\EddBookings\RestApi\Handlers\Services\QueryServicesHandler;
use RebelCode\EddBookings\RestApi\Handlers\Services\ServiceInfoHandler;
use RebelCode\EddBookings\RestApi\Handlers\Services\UpdateServiceHandler;
use RebelCode\EddBookings\RestApi\Handlers\Sessions\QuerySessionsHandler;
use RebelCode\EddBookings\RestApi\Transformer\AvailabilityRuleTransformer;
use RebelCode\EddBookings\RestApi\Transformer\AvailabilityTransformer;
use RebelCode\EddBookings\RestApi\Transformer\BookingTransformer;
use RebelCode\EddBookings\RestApi\Transformer\CoreInfoServiceTransformer;
use RebelCode\EddBookings\RestApi\Transformer\FullInfoServiceTransformer;
use RebelCode\EddBookings\RestApi\Transformer\ResourceIdTransformer;
use RebelCode\EddBookings\RestApi\Transformer\ResourceTransformer;
use RebelCode\EddBookings\RestApi\Transformer\ServiceAvailabilityTransformer;
use RebelCode\EddBookings\RestApi\Transformer\SessionTransformer;
use RebelCode\EddBookings\RestApi\Transformer\SessionTypeDataTransformer;
use RebelCode\Modular\Module\AbstractBaseModule;
use RebelCode\Transformers\CallbackTransformer;
use RebelCode\Transformers\MapTransformer;
use RebelCode\Transformers\NoOpTransformer;
use RebelCode\Transformers\TransformerIterator;
use RebelCode\WordPress\Nonce\Factory\NonceFactory;
use RebelCode\WordPress\Nonce\Factory\NonceFactoryInterface;
use RebelCode\WordPress\Nonce\NonceInterface;
use Traversable;
use WP_Post;

/**
 * The REST API module class.
 *
 * @since [*next-version*]
 */
class EddBkRestApiModule extends AbstractBaseModule
{
    /* @since [*next-version*] */
    use NormalizeIteratorCapableTrait;

    /**
     * Constructor.
     *
     * @since [*next-version*]
     *
     * @param string|Stringable         $key                  The module key.
     * @param string[]|Stringable[]     $dependencies         The module dependencies.
     * @param ContainerFactoryInterface $configFactory        The config factory.
     * @param ContainerFactoryInterface $containerFactory     The container factory.
     * @param ContainerFactoryInterface $compContainerFactory The composite container factory.
     * @param EventManagerInterface     $eventManager         The event manager.
     * @param EventFactoryInterface     $eventFactory         The event factory.
     */
    public function __construct(
        $key,
        $dependencies,
        $configFactory,
        $containerFactory,
        $compContainerFactory,
        $eventManager,
        $eventFactory
    ) {
        $this->_initModule($key, $dependencies, $configFactory, $containerFactory, $compContainerFactory);
        $this->_initModuleEvents($eventManager, $eventFactory);
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     *
     * @throws InternalException If an error occurred while trying to read from the config file.
     */
    public function setup()
    {
        return $this->_setupContainer(
            $this->_loadPhpConfigFile(RCMOD_EDDBK_REST_API_CONFIG_FILE),
            [
                /*-------------------------------------------------------------*\
                 * Resource Controllers                                        *
                \*-------------------------------------------------------------*/

                /*
                 * The bookings REST API resource controller.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_bookings_controller' => function (ContainerInterface $c) {
                    return new BookingsController(
                        $c->get('eddbk_rest_api_bookings_iterator_factory'),
                        $c->get('booking_factory'),
                        $c->get('booking_transitioner'),
                        $c->get('bookings_select_rm'),
                        $c->get('bookings_entity_manager'),
                        $c->get('sql_expression_builder'),
                        $c->get('eddbk_clients_controller')
                    );
                },

                /*
                 * The resources REST API controller.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_resources_controller' => function (ContainerInterface $c) {
                    return new ResourcesController(
                        $c->get('eddbk_rest_api_resources_iterator_factory'),
                        $c->get('resources_entity_manager'),
                        $c->get('event_manager'),
                        $c->get('event_factory')
                    );
                },

                /*
                 * The services REST API resource controller.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_services_controller' => function (ContainerInterface $c) {
                    return new ServicesController(
                        $c->get('eddbk_services_manager'),
                        $c->get('eddbk_rest_api_services_iterator_factory'),
                        $c->get('eddbk_rest_api_user_is_admin_auth_validator'),
                        $c->get('eddbk_rest_api_user_is_admin_auth_validator'),
                        $c->get('event_factory')
                    );
                },

                /*
                 * The clients REST API resource controller.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_clients_controller' => function (ContainerInterface $c) {
                    return new ClientsController($c->get('eddbk_rest_api_clients_iterator_factory'), EDD()->customers);
                },

                /*
                 * The sessions REST API resource controller.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_sessions_controller' => function (ContainerInterface $c) {
                    return new SessionsController(
                        $c->get('eddbk_rest_api_sessions_iterator_factory'),
                        $c->get('unbooked_sessions_select_rm'),
                        $c->get('eddbk_rest_api_sessions_ordering'),
                        $c->get('sql_expression_builder'),
                        $c->get('eddbk_rest_api/controllers/sessions/default_num_sessions_per_page'),
                        $c->get('eddbk_rest_api/controllers/sessions/max_num_sessions_per_page')
                    );
                },

                /**
                 * The order to use for sessions in the REST API.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_sessions_ordering' => function (ContainerInterface $c) {
                    return [
                        $c->get('sql_order_factory')->make([
                            'entity'    => 'session',
                            'field'     => 'start',
                            'ascending' => true,
                        ]),
                    ];
                },

                /*-------------------------------------------------------------*\
                 * Controller Result Iterator Factories                        *
                \*-------------------------------------------------------------*/

                /*
                 * The iterator factory for the bookings controller.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_bookings_iterator_factory' => function (ContainerInterface $c) {
                    return new GenericCallbackFactory(function ($config) use ($c) {
                        $items = $this->_containerGet($config, 'items');
                        $iterator = $this->_normalizeIterator($items);

                        return new TransformerIterator($iterator, $c->get('eddbk_rest_api_bookings_transformer'));
                    });
                },

                /*
                 * The iterator factory for the resources controller.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_resources_iterator_factory' => function (ContainerInterface $c) {
                    return new GenericCallbackFactory(function ($config) use ($c) {
                        $items    = $this->_containerGet($config, 'items');
                        $iterator = $this->_normalizeIterator($items);

                        return new TransformerIterator($iterator, $c->get('eddbk_rest_api_resource_transformer'));
                    });
                },

                /*
                 * The iterator factory for the services controller.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_services_iterator_factory' => function (ContainerInterface $c) {
                    return new GenericCallbackFactory(function ($config) use ($c) {
                        $items    = $this->_containerGet($config, 'items');
                        $coreOnly = $this->_containerGet($config, 'core_only');

                        // Iterator of results
                        $iterator = $this->_normalizeIterator($items);
                        // Transformer to apply to each
                        $transformer = ($coreOnly)
                            ? $c->get('eddbk_rest_api_core_info_service_transformer')
                            : $c->get('eddbk_rest_api_full_info_service_transformer');

                        return new TransformerIterator($iterator, $transformer);
                    });
                },

                /*
                 * The iterator factory for the clients controller.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_clients_iterator_factory' => function (ContainerInterface $c) {
                    return new GenericCallbackFactory(function ($config) use ($c) {
                        $items = $this->_containerGet($config, 'items');
                        $iterator = $this->_normalizeIterator($items);

                        return new TransformerIterator($iterator, $c->get('eddbk_rest_api_clients_transformer'));
                    });
                },

                /*
                 * The iterator factory for the session controller.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_sessions_iterator_factory' => function (ContainerInterface $c) {
                    return new GenericCallbackFactory(function ($config) use ($c) {
                        $items = $this->_containerGet($config, 'items');
                        $iterator = $this->_normalizeIterator($items);

                        return new TransformerIterator($iterator, $c->get('eddbk_rest_api_sessions_transformer'));
                    });
                },

                /*-------------------------------------------------------------*\
                 * REST API route handlers - Bookings                          *
                \*-------------------------------------------------------------*/

                /*
                 * Handles the bookings route that receives generic booking queries.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_query_bookings_handler' => function (ContainerInterface $c) {
                    return new QueryBookingsHandler(
                        $c->get('eddbk_bookings_controller'),
                        $c->get('booking_status_select_rm'),
                        $c->get('booking_logic/statuses')
                    );
                },

                /*
                 * Handles the bookings route that provides information about a single booking.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_get_booking_info_handler' => function (ContainerInterface $c) {
                    return new BookingInfoHandler($c->get('eddbk_bookings_controller'));
                },

                /*
                 * Handles the bookings route for creating new bookings.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_create_booking_handler' => function (ContainerInterface $c) {
                    return new CreateBookingHandler($c->get('eddbk_bookings_controller'), $c->get('eddbk_rest_api'));
                },

                /*
                 * Handles the bookings route for updating bookings.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_update_booking_handler' => function (ContainerInterface $c) {
                    return new UpdateBookingHandler($c->get('eddbk_bookings_controller'));
                },

                /*
                 * Handles the bookings route for deleting bookings.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_delete_booking_handler' => function (ContainerInterface $c) {
                    return new DeleteBookingHandler($c->get('eddbk_bookings_controller'));
                },

                /*-------------------------------------------------------------*\
                 * REST API route handlers - Resources                         *
                \*-------------------------------------------------------------*/

                /*
                 * Handles the resources route that receives generic resource queries.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_query_resources_handler' => function (ContainerInterface $c) {
                    return new QueryResourcesHandler($c->get('eddbk_resources_controller'));
                },

                /*
                 * Handles the resources route that provides information about a single resource.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_get_resource_info_handler' => function (ContainerInterface $c) {
                    return new ResourceInfoHandler($c->get('eddbk_resources_controller'));
                },

                /*
                 * Handles the resources route for creating new resources.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_create_resource_handler' => function (ContainerInterface $c) {
                    return new CreateResourceHandler($c->get('eddbk_resources_controller'), $c->get('eddbk_rest_api'));
                },

                /*
                 * Handles the resources route for updating resources.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_update_resource_handler' => function (ContainerInterface $c) {
                    return new UpdateResourceHandler($c->get('eddbk_resources_controller'));
                },

                /*
                 * Handles the resources route for deleting resources.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_delete_resource_handler' => function (ContainerInterface $c) {
                    return new DeleteResourceHandler($c->get('eddbk_resources_controller'));
                },

                /*-------------------------------------------------------------*\
                 * REST API route handlers - Clients                           *
                \*-------------------------------------------------------------*/

                /*
                 * Handles the clients route that receives generic client queries.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_query_clients_handler' => function (ContainerInterface $c) {
                    return new QueryClientsHandler($c->get('eddbk_clients_controller'));
                },

                /*
                 * Handles the clients route that provides information about a single client.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_get_client_info_handler' => function (ContainerInterface $c) {
                    return new ClientInfoHandler($c->get('eddbk_clients_controller'));
                },

                /*
                 * Handles the clients route for creating new clients.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_create_client_handler' => function (ContainerInterface $c) {
                    return new CreateClientHandler($c->get('eddbk_clients_controller'), $c->get('eddbk_rest_api'));
                },

                /*-------------------------------------------------------------*\
                 * REST API route handlers - Sessions                          *
                \*-------------------------------------------------------------*/

                /*
                 * Handles the sessions route that receives generic session queries.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_query_sessions_handler' => function (ContainerInterface $c) {
                    return new QuerySessionsHandler($c->get('eddbk_sessions_controller'));
                },

                /*-------------------------------------------------------------*\
                 * REST API route handlers - Services                          *
                \*-------------------------------------------------------------*/

                /*
                 * Handles the services route that receives generic service queries.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_query_services_handler' => function (ContainerInterface $c) {
                    return new QueryServicesHandler($c->get('eddbk_services_controller'));
                },

                /*
                 * Handles the services route that provides information about a single service.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_get_service_info_handler' => function (ContainerInterface $c) {
                    return new ServiceInfoHandler($c->get('eddbk_services_controller'));
                },

                /*
                 * Handles the services route for creating new services.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_create_service_handler' => function (ContainerInterface $c) {
                    return new CreateServiceHandler($c->get('eddbk_services_controller'), $c->get('eddbk_rest_api'));
                },

                /*
                 * Handles the services route for updating services.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_update_service_handler' => function (ContainerInterface $c) {
                    return new UpdateServiceHandler($c->get('eddbk_services_controller'));
                },

                /*
                 * Handles the services route for deleting services.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_delete_service_handler' => function (ContainerInterface $c) {
                    return new DeleteServiceHandler($c->get('eddbk_services_controller'));
                },

                /*-------------------------------------------------------------*\
                 * Transformers - for data, not robots in disguise             *
                \*-------------------------------------------------------------*/

                /*
                 * A no-operation transformer.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_noop_transformer' => function (ContainerInterface $c = null) {
                    return new NoOpTransformer();
                },

                /*
                 * The transformer that transforms bookings into the result that is sent in REST API responses.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_bookings_transformer' => function (ContainerInterface $c) {
                    return new BookingTransformer(
                        $c->get('eddbk_timestamp_datetime_transformer'),
                        $c->get('eddbk_rest_api_service_id_transformer'),
                        $c->get('eddbk_rest_api_bookings_resource_id_transformer'),
                        $c->get('eddbk_rest_api_client_id_transformer')
                    );
                },

                /*
                 * The transformer used by the bookings transformer to change resource IDs into resource data.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_bookings_resource_id_transformer' => function (ContainerInterface $c) {
                    return new ResourceIdTransformer(
                        $c->get('eddbk_rest_api_simple_resource_transformer'),
                        $c->get('resources_entity_manager')
                    );
                },

                /*
                 * The transformer that transforms resources into the result that is sent in REST API responses.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_resource_transformer' => function (ContainerInterface $c) {
                    return new ResourceTransformer($c->get('eddbk_rest_api_availability_transformer'));
                },

                /*
                 * The transformer that partially transforms resources into the result that is sent in REST API
                 * responses. This excludes resource availability from the result.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_simple_resource_transformer' => function (ContainerInterface $c) {
                    return new ResourceTransformer();
                },

                /*
                 * The availability transformer for transforming availabilities.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_availability_transformer' => function (ContainerInterface $c) {
                    return new AvailabilityTransformer(
                        $c->get('eddbk_rest_api_availability_rule_transformer')
                    );
                },

                /*
                 * The transformer that transforms services into the results that only contain core info.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_core_info_service_transformer' => function (ContainerInterface $c) {
                    return new CoreInfoServiceTransformer(
                        $c->get('eddbk_rest_api_service_session_type_list_transformer')
                    );
                },

                /*
                 * The transformer that transforms services into the results that contain full info.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_full_info_service_transformer' => function (ContainerInterface $c) {
                    return new FullInfoServiceTransformer(
                        $c->get('eddbk_rest_api_service_session_type_list_transformer'),
                        $c->get('eddbk_rest_api_service_availability_transformer'),
                        $c->get('eddbk_boolean_transformer')
                    );
                },

                /*
                 * The transformer that transformers service availabilities into the correct response format.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_service_availability_transformer' => function (ContainerInterface $c) {
                    return new ServiceAvailabilityTransformer(
                        $c->get('eddbk_rest_api_availability_rule_transformer')
                    );
                },

                /*
                 * The transformer that transformers service availability rules into the correct response format.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_availability_rule_transformer' => function (ContainerInterface $c) {
                    return new AvailabilityRuleTransformer(
                        $c->get('eddbk_timestamp_datetime_transformer'),
                        $c->get('eddbk_boolean_transformer'),
                        $c->get('eddbk_comma_list_array_transformer'),
                        $c->get('eddbk_rest_api_service_exclude_dates_transformer')
                    );
                },

                /*
                 * The transformer for transforming a list of session type configs.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_service_session_type_list_transformer'    => function (ContainerInterface $c) {
                    return new CallbackTransformer(function ($sessionLengths) use ($c) {
                        $iterator    = $this->_normalizeIterator($sessionLengths);
                        $transformer = $c->get('eddbk_rest_api_session_type_transformer');
                        $result      = new TransformerIterator($iterator, $transformer);

                        return iterator_to_array($result);
                    });
                },

                /*
                 * The transformer for transforming a session type config.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_session_type_transformer'              => function (ContainerInterface $c) {
                    return new MapTransformer([
                        [
                            MapTransformer::K_SOURCE => 'id',
                        ],
                        [
                            MapTransformer::K_SOURCE => 'type',
                        ],
                        [
                            MapTransformer::K_SOURCE => 'label',
                        ],
                        [
                            MapTransformer::K_SOURCE      => 'price',
                            MapTransformer::K_TRANSFORMER => $c->get('eddbk_rest_api_session_type_price_transformer'),
                        ],
                        [
                            MapTransformer::K_SOURCE      => 'data',
                            MapTransformer::K_TRANSFORMER => $c->get('eddbk_rest_api_session_type_data_transformer')
                        ],
                    ]);
                },

                /*
                 * The transformer that transforms session type data.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_session_type_data_transformer' => function (ContainerInterface $c) {
                    return new SessionTypeDataTransformer(
                        $c->get('eddbk_rest_api_resource_id_transformer')
                    );
                },

                /*
                 * The transformer that transforms resource IDs into full resource data.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_resource_id_transformer' => function (ContainerInterface $c) {
                    return new ResourceIdTransformer(
                        $c->get('eddbk_rest_api_resource_transformer'),
                        $c->get('resources_entity_manager')
                    );
                },

                /*
                 * The transformer for transforming a session type's price.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_session_type_price_transformer'     => function (ContainerInterface $c) {
                    return new CallbackTransformer(function ($price) use ($c) {
                        return [
                            'amount'    => $price,
                            'currency'  => \edd_get_currency(),
                            'formatted' => $c->get('eddbk_rest_api_price_transformer')->transform($price),
                        ];
                    });
                },

                /*
                 * The transformer for transforming a price amount into its formatted counterpart.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_price_transformer'                          => function (ContainerInterface $c) {
                    return new CallbackTransformer(function ($price) use ($c) {
                        return html_entity_decode(\edd_currency_filter(\edd_format_amount($price)));
                    });
                },

                /*
                 * The transformer for transforming the session rule excluded dates for services.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_service_exclude_dates_transformer'       => function (ContainerInterface $c) {
                    $commaListTransformer = $c->get('eddbk_comma_list_array_transformer');
                    $datetimeTransformer  = $c->get('eddbk_timestamp_datetime_transformer');

                    return new CallbackTransformer(function ($value) use ($commaListTransformer, $datetimeTransformer) {
                        // Transform comma list to an iterator
                        $array    = $commaListTransformer->transform($value);
                        $iterator = $this->_normalizeIterator($array);
                        // Create the transformer iterator, to transform each timestamp into a datetime string
                        $transformIterator = new TransformerIterator($iterator, $datetimeTransformer);

                        // Reduce to an array and return
                        return iterator_to_array($transformIterator);
                    });
                },

                /*
                 * The transformer that transforms clients into the result that is sent in REST API responses.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_clients_transformer' => function (ContainerInterface $c) {
                    return new MapTransformer([
                        [
                            MapTransformer::K_SOURCE => 'id',
                        ],
                        [
                            MapTransformer::K_SOURCE => 'name',
                        ],
                        [
                            MapTransformer::K_SOURCE => 'email',
                        ],

                    ]);
                },

                /*
                 * The transformer that transformers sessions into the result that is sent in the REST API response.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_sessions_transformer' => function (ContainerInterface $c) {
                    return new SessionTransformer($c->get('eddbk_timestamp_datetime_transformer'));
                },

                /*
                 * The transformer that transforms a service ID into service data.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_service_id_transformer' => function (ContainerInterface $c) {
                    return new CallbackTransformer(function ($serviceId) use ($c) {
                        if (empty($serviceId)) {
                            return;
                        }

                        $controller = $c->get('eddbk_services_controller');
                        $services = $controller->get(['id' => $serviceId]);
                        $service = null;

                        foreach ($services as $service) {
                            break;
                        }

                        return $service;
                    });
                },

                /*
                 * The transformer that transforms a client ID into client data.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_client_id_transformer' => function (ContainerInterface $c) {
                    return new CallbackTransformer(function ($clientId) use ($c) {
                        if (empty($clientId)) {
                            return;
                        }

                        $controller = $c->get('eddbk_clients_controller');
                        $clients = $controller->get(['id' => $clientId]);
                        $client = null;

                        foreach ($clients as $client) {
                            break;
                        }

                        return $client;
                    });
                },

                /*
                 * Transformer that transforms timestamps into formatted datetime strings.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_timestamp_datetime_transformer' => function (ContainerInterface $c) {
                    return new CallbackTransformer(function ($timestamp) use ($c) {
                        // Get WordPress timezone, defaulting to UTC
                        $tzName = get_option('timezone_string');
                        $tzName = empty($tzName) ? 'UTC' : $tzName;
                        // Create date time object with the timezone
                        $timezone = new DateTimeZone($tzName);
                        $dateTime = new DateTime('@' . $timestamp, $timezone);

                        // Return formatted
                        return $dateTime->format($c->get('eddbk_rest_api/datetime_format'));
                    });
                },

                /*
                 * Transformer that transforms WP_Post instances into arrays.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_post_array_transformer' => function (ContainerInterface $c) {
                    return new CallbackTransformer(function ($post) {
                        if (empty($post)) {
                            return;
                        }

                        if (!($post instanceof WP_Post)) {
                            throw $this->_createInvalidArgumentException(
                                $this->__('Argument is not a WP_Post instance'), null, null, $post
                            );
                        }

                        return $post->to_array();
                    });
                },

                /*
                 * The transformer for transforming values into booleans.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_boolean_transformer'                        => function (ContainerInterface $c) {
                    return new CallbackTransformer(function ($value) {
                        return (bool) $value;
                    });
                },

                /*
                 * The transformer for transforming comma separating strings into arrays.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_comma_list_array_transformer'               => function (ContainerInterface $c) {
                    return new CallbackTransformer(function ($commaList) {
                        return (strlen($commaList) > 0)
                            ? explode(',', $commaList)
                            : [];
                    });
                },

                /*
                 * The transformer for transforming un-serializing strings.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_unserialize_transformer' => function (ContainerInterface $c) {
                    return new CallbackTransformer(function ($string) {
                        return unserialize($string);
                    });
                },

                /*-------------------------------------------------------------*\
                 * REST API route handlers - Auth Validators                   *
                \*-------------------------------------------------------------*/

                /*
                 * The authorization validator that only authorizes administrator users.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_user_is_admin_auth_validator' => function (ContainerInterface $c) {
                    return new UserIsAdminAuthValidator($c->get('eddbk_rest_api/admin_capability'));
                },

                /*
                 * The authorization validator that authorizes client apps via filter event.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_filter_event_auth_validator' => function (ContainerInterface $c) {
                    return new FilterAuthValidator(
                        $c->get('event_manager'),
                        $c->get('event_factory'),
                        $c->get('eddbk_rest_api/auth/transient_nonce_filter_validator/event_name'),
                        $c->get('eddbk_rest_api/auth/transient_nonce_filter_validator/event_param_key'),
                        $c->get('eddbk_rest_api/auth/transient_nonce_filter_validator/event_param_default')
                    );
                },

                /*
                 * The authorization validator that authorizes WordPress client apps.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_wp_client_app_auth_validator' => function (ContainerInterface $c) {
                    return new TolerantCompositeValidator([
                        $c->get('eddbk_rest_api_user_is_admin_auth_validator'),
                        $c->get('eddbk_rest_api_filter_event_auth_validator')
                    ]);
                },

                /*-------------------------------------------------------------*\
                 * Misc. REST API services                                     *
                \*-------------------------------------------------------------*/

                /**
                 * The factory that creates WordPress nonce instances for the REST API.
                 *
                 * @since [*next-version*]
                 *
                 * @return NonceFactoryInterface
                 */
                'eddbk_rest_api_nonce_factory' => function (ContainerInterface $c) {
                    return new NonceFactory();
                },

                /**
                 * The factory that creates the WordPress client app auth nonce instance.
                 *
                 * @since [*next-version*]
                 *
                 * @return NonceFactoryInterface
                 */
                'eddbk_rest_api_wp_client_app_nonce_factory' => function (ContainerInterface $c) {
                    return new TransientNonceFactory(
                        $c->get('eddbk_rest_api/auth/transient_nonce_filter_validator/transient_name'),
                        $c->get('eddbk_rest_api/auth/transient_nonce_filter_validator/transient_expiry')
                    );
                },

                /**
                 * The nonce used to authorize WordPress client apps.
                 *
                 * @return NonceInterface
                 */
                'eddbk_rest_api_wp_client_app_nonce' => function (ContainerInterface $c) {
                    $factory = $c->get('eddbk_rest_api_wp_client_app_nonce_factory');
                    $nonceId = $c->get('eddbk_rest_api/auth/transient_nonce_filter_validator/handler/nonce');

                    return $factory->make([
                        NonceFactoryInterface::K_CONFIG_ID => $nonceId
                    ]);
                },

                /*
                 * The handler that checks and verifies a nonce to authorize WordPress client apps.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_wp_client_app_auth_nonce_handler' => function (ContainerInterface $c) {
                    /* @var $nonce NonceInterface */
                    $nonce = $c->get('eddbk_rest_api_wp_client_app_nonce');

                    return new TransientNonceAuthHandler(
                        $c->get('eddbk_rest_api/auth/transient_nonce_filter_validator/handler/header'),
                        $nonce->getId(),
                        $c->get('eddbk_rest_api/auth/transient_nonce_filter_validator/event_param_key'),
                        $c->get('eddbk_rest_api/auth/transient_nonce_filter_validator/transient_name')
                    );
                },

                /*
                 * The REST API initializer - initializes the routes and handlers when invoked.
                 *
                 * @since [*next-version*]
                 */
                'eddbk_rest_api_initializer' => function (ContainerInterface $c) {
                    return new RestApiInitializer($c->get('eddbk_rest_api'), $c);
                },
            ]
        );
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function run(ContainerInterface $c = null)
    {
        if ($c === null) {
            return;
        }

        $this->_attach('rest_api_init', $c->get('eddbk_rest_api_initializer'));

        // Attach handler for WP client apps to be authorized by nonce
        $this->_attach(
            $c->get('eddbk_rest_api/auth/transient_nonce_filter_validator/event_name'),
            $c->get('eddbk_rest_api_wp_client_app_auth_nonce_handler')
        );
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    protected function _createArrayIterator(array $array)
    {
        return new ArrayIterator($array);
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    protected function _createTraversableIterator(Traversable $traversable)
    {
        return new IteratorIterator($traversable);
    }
}