/** * Returns the swagger spec as array * * @return array Swagger spec */ public function getSwaggerSpec() { $ret = $this->getBasicStructure(); $routingMap = $this->restUtils->getServiceRoutingMap(); $paths = array(); foreach ($routingMap as $contName => $routes) { list(, $bundle, , $document) = explode('.', $contName); foreach ($routes as $routeName => $route) { $routeMethod = strtolower($route->getMethods()[0]); // skip /schema/ stuff if (strpos($route->getPath(), '/schema/') !== false) { continue; } $thisModel = $this->restUtils->getModelFromRoute($route); if ($thisModel === false) { throw new \LogicException(sprintf('Could not resolve route "%s" to model', $routeName)); } $entityClassName = str_replace('\\', '', get_class($thisModel)); $schema = $this->schemaUtils->getModelSchema($entityClassName, $thisModel); $ret['definitions'][$entityClassName] = json_decode($this->restUtils->serializeContent($schema), true); $isCollectionRequest = true; if (in_array('id', array_keys($route->getRequirements())) === true) { $isCollectionRequest = false; } $thisPattern = $route->getPattern(); $entityName = ucfirst($document); $thisPath = $this->getBasicPathStructure($isCollectionRequest, $entityName, $entityClassName, $schema->getProperty('id')->getType()); $thisPath['tags'] = $this->getPathTags($route); $thisPath['operationId'] = $routeName; $thisPath['summary'] = $this->getSummary($routeMethod, $isCollectionRequest, $entityName); // post body stuff if ($routeMethod == 'put' || $routeMethod == 'post') { // special handling for POST/PUT.. we need to have 2 schemas, one for response, one for request.. // we don't want to have ID in the request body within those requests do we.. // an exception is when id is required.. $incomingEntitySchema = $entityClassName; if (is_null($schema->getRequired()) || !in_array('id', $schema->getRequired())) { $incomingEntitySchema = $incomingEntitySchema . 'Incoming'; $incomingSchema = clone $schema; $incomingSchema->removeProperty('id'); $ret['definitions'][$incomingEntitySchema] = json_decode($this->restUtils->serializeContent($incomingSchema), true); } $thisPath['parameters'][] = array('name' => $bundle, 'in' => 'body', 'description' => 'Post', 'required' => true, 'schema' => array('$ref' => '#/definitions/' . $incomingEntitySchema)); // add error responses.. $thisPath['responses'][400] = array('description' => 'Bad request', 'schema' => array('type' => 'object')); } if ($routeMethod == 'options') { $thisPath['responses'][200] = array('description' => 'Schema response', 'schema' => array('$ref' => '#/definitions/SchemaModel')); } $paths[$thisPattern][$routeMethod] = $thisPath; } } $ret['definitions']['SchemaModel'] = $this->schemaModel->getSchema(); ksort($paths); $ret['paths'] = $paths; return $ret; }
/** * Return schema GET results. * * @param Request $request Current http request * @param string $id ID of record * * @throws SerializationException * @return \Symfony\Component\HttpFoundation\Response $response Result of the action */ public function schemaAction(Request $request, $id = null) { $request->attributes->set('schemaRequest', true); list($app, $module, , $modelName, $schemaType) = explode('.', $request->attributes->get('_route')); $response = $this->response; $response->setStatusCode(Response::HTTP_OK); $response->setPublic(); if (!$id && $schemaType != 'canonicalIdSchema') { $schema = $this->schemaUtils->getCollectionSchema($modelName, $this->getModel()); } else { $schema = $this->schemaUtils->getModelSchema($modelName, $this->getModel()); } // enabled methods for CorsListener $corsMethods = 'GET, POST, PUT, PATCH, DELETE, OPTIONS'; try { $router = $this->getRouter(); // if post route is available we assume everything is readable $router->generate(implode('.', array($app, $module, 'rest', $modelName, 'post'))); } catch (RouteNotFoundException $exception) { // only allow read methods $corsMethods = 'GET, OPTIONS'; } $request->attributes->set('corsMethods', $corsMethods); return $this->render('GravitonRestBundle:Main:index.json.twig', ['response' => $this->serialize($schema)], $response); }
/** * Add rel=schema Link header for most routes * * This does not add a link to routes used by the schema bundle * itself. * * @param FilterResponseEvent $event response event * * @return void */ public function onKernelResponse(FilterResponseEvent $event) { $request = $event->getRequest(); $response = $event->getResponse(); $type = $response->headers->get('Content-Type'); if ($type !== null && substr(strtolower($type), 0, 16) !== 'application/json') { return; } // build content-type string $contentType = 'application/json; charset=UTF-8'; if ($request->get('_route') != 'graviton.core.static.main.all') { try { $schemaRoute = SchemaUtils::getSchemaRouteName($request->get('_route')); $contentType .= sprintf('; profile=%s', $this->router->generate($schemaRoute, array(), true)); } catch (\Exception $e) { return true; } } // replace content-type if a schema was requested if ($request->attributes->get('schemaRequest')) { $contentType = 'application/schema+json'; } $response->headers->set('Content-Type', $contentType); $event->setResponse($response); }
/** * add a rel=self Link header to the response * * @param FilterResponseEvent $event response listener event * * @return void */ public function onKernelResponse(FilterResponseEvent $event) { $request = $event->getRequest(); if ($request->attributes->get('schemaRequest', false)) { $response = $event->getResponse(); $linkHeader = LinkHeader::fromResponse($response); $routeName = SchemaUtils::getSchemaRouteName($request->get('_route')); $url = $this->router->generate($routeName, array(), true); // append rel=canonical link to link headers $linkHeader->add(new LinkHeaderItem($url, array('rel' => 'canonical'))); // overwrite link headers with new headers $response->headers->set('Link', (string) $linkHeader); $event->setResponse($response); } }