/** * @covers Imbo\Model\Error::getData */ public function testGetData() { $date = new DateTime(); $this->model->setHttpCode(404); $this->model->setErrorMessage('message'); $this->model->setDate($date); $this->model->setImboErrorCode(100); $this->model->setImageIdentifier('identifier'); $this->assertSame(['httpCode' => 404, 'errorMessage' => 'message', 'date' => $date, 'imboErrorCode' => 100, 'imageIdentifier' => 'identifier'], $this->model->getData()); }
/** * Set an error model and update some parts of the response object * * @param Model\Error $error An error model instance * @return Response */ public function setError(Model\Error $error) { $errorMessage = $error->getErrorMessage(); $this->headers->add(array('X-Imbo-Error-Message' => $errorMessage, 'X-Imbo-Error-InternalCode' => $error->getImboErrorCode(), 'X-Imbo-Error-Date' => $error->getDate()->format('D, d M Y H:i:s') . ' GMT')); $this->setStatusCode($error->getHttpCode(), $errorMessage)->setEtag(null)->setLastModified(null); $this->setModel($error); return $this; }
/** * {@inheritdoc} */ public function formatError(Model\Error $model) { $data = array('error' => array('code' => $model->getHttpCode(), 'message' => $model->getErrorMessage(), 'date' => $this->dateFormatter->formatDate($model->getDate()), 'imboErrorCode' => $model->getImboErrorCode())); if ($imageIdentifier = $model->getImageIdentifier()) { $data['imageIdentifier'] = $imageIdentifier; } return $this->encode($data); }
/** * {@inheritdoc} */ public function formatError(Model\Error $model) { $imageIdentifierXml = ''; if ($imageIdentifier = $model->getImageIdentifier()) { $imageIdentifierXml = '<imageIdentifier>' . $imageIdentifier . '</imageIdentifier>'; } return <<<ERROR <?xml version="1.0" encoding="UTF-8"?> <imbo> <error> <code>{$model->getHttpCode()}</code> <message>{$model->getErrorMessage()}</message> <date>{$this->dateFormatter->formatDate($model->getDate())}</date> <imboErrorCode>{$model->getImboErrorCode()}</imboErrorCode> </error> {$imageIdentifierXml} </imbo> ERROR; }
/** * @covers Imbo\Model\Error::createFromException */ public function testWillUseImageChecksumAsImageIdentifierIfRequestHasAnImageWhenCreatingError() { $exception = new RuntimeException('You wronged', 400); $exception->setImboErrorCode(123); $request = $this->getMock('Imbo\\Http\\Request\\Request'); $image = $this->getMock('Imbo\\Model\\Image'); $image->expects($this->once())->method('getChecksum')->will($this->returnValue('checksum')); $request->expects($this->once())->method('getImage')->will($this->returnValue($image)); $request->expects($this->never())->method('checksum'); $model = Error::createFromException($exception, $request); $this->assertSame(123, $model->getImboErrorCode()); $this->assertSame('checksum', $model->getImageIdentifier()); }
/** * Run the application */ public function run(array $config) { // Request and response objects $request = Request::createFromGlobals(); Request::setTrustedProxies($config['trustedProxies']); $response = new Response(); $response->setPublic(); $response->headers->set('X-Imbo-Version', Version::VERSION); // Database and storage adapters $database = $config['database']; if (is_callable($database) && !$database instanceof DatabaseInterface) { $database = $database(); } if (!$database instanceof DatabaseInterface) { throw new InvalidArgumentException('Invalid database adapter', 500); } $storage = $config['storage']; if (is_callable($storage) && !$storage instanceof StorageInterface) { $storage = $storage(); } if (!$storage instanceof StorageInterface) { throw new InvalidArgumentException('Invalid storage adapter', 500); } // User lookup adapters $userLookup = $config['auth']; // Construct an ArrayStorage instance if the auth details is an array if (is_array($userLookup)) { $userLookup = new Auth\ArrayStorage($userLookup); } // Make sure the "auth" part of the configuration is an instance of the user lookup // interface if (!$userLookup instanceof Auth\UserLookupInterface) { throw new InvalidArgumentException('Invalid auth configuration', 500); } // Create a router based on the routes in the configuration and internal routes $router = new Router($config['routes']); // Create the event manager and the event template $eventManager = new EventManager(); $event = new Event(); $event->setArguments(array('request' => $request, 'response' => $response, 'database' => $database, 'storage' => $storage, 'userLookup' => $userLookup, 'config' => $config, 'manager' => $eventManager)); $eventManager->setEventTemplate($event); // A date formatter helper $dateFormatter = new Helpers\DateFormatter(); // Response formatters $formatters = array('json' => new Formatter\JSON($dateFormatter), 'xml' => new Formatter\XML($dateFormatter)); $contentNegotiation = new Http\ContentNegotiation(); // Collect event listener data $eventListeners = array('Imbo\\Resource\\Index', 'Imbo\\Resource\\Status', 'Imbo\\Resource\\Stats', 'Imbo\\Resource\\GlobalShortUrl', 'Imbo\\Resource\\ShortUrls', 'Imbo\\Resource\\ShortUrl', 'Imbo\\Resource\\User', 'Imbo\\Resource\\Images', 'Imbo\\Resource\\Image', 'Imbo\\Resource\\Metadata', 'Imbo\\Http\\Response\\ResponseFormatter' => array('formatters' => $formatters, 'contentNegotiation' => $contentNegotiation), 'Imbo\\EventListener\\DatabaseOperations', 'Imbo\\EventListener\\StorageOperations', 'Imbo\\Image\\ImagePreparation', 'Imbo\\EventListener\\ImageTransformer', 'Imbo\\EventListener\\ResponseSender', 'Imbo\\EventListener\\ResponseETag'); foreach ($eventListeners as $listener => $params) { if (is_string($params)) { $listener = $params; $params = array(); } $eventManager->addEventHandler($listener, $listener, $params)->addCallbacks($listener, $listener::getSubscribedEvents()); } // Event listener initializers foreach ($config['eventListenerInitializers'] as $name => $initializer) { if (!$initializer) { // The initializer has been disabled via config continue; } if (is_string($initializer)) { // The initializer has been specified as a string, representing a class name. Create // an instance $initializer = new $initializer(); } if (!$initializer instanceof InitializerInterface) { throw new InvalidArgumentException('Invalid event listener initializer: ' . $name, 500); } $eventManager->addInitializer($initializer); } // Listeners from configuration foreach ($config['eventListeners'] as $name => $definition) { if (!$definition) { // This occurs when a user disables a default event listener continue; } if (is_string($definition)) { // Class name $eventManager->addEventHandler($name, $definition)->addCallbacks($name, $definition::getSubscribedEvents()); continue; } if (is_callable($definition) && !$definition instanceof ListenerInterface) { // Callable piece of code which is not an implementation of the listener interface $definition = $definition(); } if ($definition instanceof ListenerInterface) { $eventManager->addEventHandler($name, $definition)->addCallbacks($name, $definition::getSubscribedEvents()); continue; } if (is_array($definition) && !empty($definition['listener'])) { $listener = $definition['listener']; $params = is_string($listener) && isset($definition['params']) ? $definition['params'] : array(); $publicKeys = isset($definition['publicKeys']) ? $definition['publicKeys'] : array(); if (is_callable($listener) && !$listener instanceof ListenerInterface) { $listener = $listener(); } if (!is_string($listener) && !$listener instanceof ListenerInterface) { throw new InvalidArgumentException('Invalid event listener definition', 500); } $eventManager->addEventHandler($name, $listener, $params)->addCallbacks($name, $listener::getSubscribedEvents(), $publicKeys); } else { if (is_array($definition) && !empty($definition['callback']) && !empty($definition['events'])) { $priority = 0; $events = array(); $publicKeys = array(); if (isset($definition['priority'])) { $priority = (int) $definition['priority']; } if (isset($definition['publicKeys'])) { $publicKeys = $definition['publicKeys']; } foreach ($definition['events'] as $event => $p) { if (is_int($event)) { $event = $p; $p = $priority; } $events[$event] = $p; } $eventManager->addEventHandler($name, $definition['callback'])->addCallbacks($name, $events, $publicKeys); } else { throw new InvalidArgumentException('Invalid event listener definition', 500); } } } // Custom resources foreach ($config['resources'] as $name => $resource) { if (is_callable($resource)) { $resource = $resource(); } $eventManager->addEventHandler($name, $resource)->addCallbacks($name, $resource::getSubscribedEvents()); } try { // Route the request $router->route($request); $eventManager->trigger('route.match'); // Create the resource $routeName = (string) $request->getRoute(); if (isset($config['resources'][$routeName])) { $resource = $config['resources'][$routeName]; if (is_callable($resource)) { $resource = $resource(); } if (is_string($resource)) { $resource = new $resource(); } if (!$resource instanceof ResourceInterface) { throw new InvalidArgumentException('Invalid resource class for route: ' . $routeName, 500); } } else { $className = 'Imbo\\Resource\\' . ucfirst($routeName); $resource = new $className(); } // Inform the user agent of which methods are allowed against this resource $response->headers->set('Allow', $resource->getAllowedMethods(), false); if ($publicKey = $request->getPublicKey()) { // Ensure that the public key actually exists if (!$userLookup->publicKeyExists($publicKey)) { $e = new RuntimeException('Public key not found', 404); $e->setImboErrorCode(Exception::AUTH_UNKNOWN_PUBLIC_KEY); throw $e; } } $methodName = strtolower($request->getMethod()); // Generate the event name based on the accessed resource and the HTTP method $eventName = $routeName . '.' . $methodName; if (!$eventManager->hasListenersForEvent($eventName)) { throw new RuntimeException('Method not allowed', 405); } $eventManager->trigger($eventName)->trigger('response.negotiate'); } catch (Exception $exception) { $negotiated = false; $error = Error::createFromException($exception, $request); $response->setError($error); // If the error is not from the previous attempt at doing content negotiation, force // another round since the model has changed into an error model. if ($exception->getCode() !== 406) { try { $eventManager->trigger('response.negotiate'); $negotiated = true; } catch (Exception $exception) { // The client does not accept any of the content types. Generate a new error $error = Error::createFromException($exception, $request); $response->setError($error); } } // Try to negotiate in a non-strict manner if the response format still has not been // chosen if (!$negotiated) { $eventManager->trigger('response.negotiate', array('noStrict' => true)); } } // Send the response $eventManager->trigger('response.send'); }