Permalink
Browse files

Added Swagger spec generation and Swagger UI interface to display API…

… instead of home-grown page
  • Loading branch information...
1 parent bea228c commit 062e9cced576c29b32004d1bf7848e42722d1756 @sergeychernyshev sergeychernyshev committed Aug 26, 2016
View
@@ -37,3 +37,6 @@
[submodule "phptidy"]
path = phptidy
url = https://github.com/cmrcx/phptidy.git
+[submodule "swagger-ui"]
+ path = swagger-ui
+ url = https://github.com/swagger-api/swagger-ui.git
View
@@ -1,147 +1,100 @@
<?php
require_once(__DIR__ . '/global.php');
-if (!array_key_exists('call', $_GET)) {
- header('HTTP/1.0 400 Bad Request');
- ?>
- <style>
- code {
- font-family: monospace,"Courier New";
- background-color: #f9f9f9;
- padding: 0.2em;
- }
-
- dl {
- margin-bottom: 2em;
- }
+if (!array_key_exists('call', $_GET) && array_key_exists('swagger-spec', $_GET)) {
+ $swagger_spec = array(
+ 'swagger' => '2.0',
+ 'info' => array(
+ 'contact' => array(
+ 'email' => UserConfig::$supportEmailFromEmail
+ ),
+ 'version' => UserConfig::$apiSpecVersion
+ ),
+ 'basePath' => UserConfig::$USERSROOTURL . '/api.php'
+ );
- dt {
- margin-bottom: 0.3em;
- }
+ if (UserConfig::$appName) {
+ $swagger_spec['info']['title'] = UserConfig::$appName;
+ }
- dd {
- margin-bottom: 0.5em;
- }
+ if (UserConfig::$termsOfServiceFullURL) {
+ $swagger_spec['info']['termsOfService'] = UserConfig::$termsOfServiceFullURL;
+ }
- b.call {
- color: green;
- }
+ // Swagger tags, e.g. API namespaces
+ foreach (\StartupAPI\API\Endpoint::getNamespaces() as $namespace) {
+ $swagger_spec['tags'][] = array(
+ 'name' => $namespace->getSlug(),
+ 'description' => $namespace->getName()
+ );
+ }
- b.param {
- color: blue;
- }
- </style>
- <h1>400 Bad Request</h1>
- <p>Required parameter: <b>call</b></p>
- <?php
- $user = StartupAPI::getUser();
- if (!is_null($user) && $user->isAdmin()) {
- ?>
- <p>
- Available endpoints:
- <?php
- $all_endpoints = \StartupAPI\API\Endpoint::getAllEndpointsBySlug();
- foreach (\StartupAPI\API\Endpoint::getNamespaces() as $namespace) {
- ?>
- <h2><?php echo $namespace->getName(); ?></h2>
- <ul>
- <?php
- $namespace_slug = $namespace->getSlug();
- foreach ($all_endpoints[$namespace_slug] as $endpoint_slug => $endpoints) {
- ?>
- <li>
- <h3><?php echo $endpoint_slug ?></h3>
- <dl>
- <?php
- foreach ($endpoints as $method => $endpoint) {
- $call = "/$namespace_slug$endpoint_slug";
-
- $params = $endpoint->getParams();
-
- $sample_params_urlencoded = '';
- if (count($params) > 0) {
- foreach ($params as $name => $param) {
- if (!$param->isOptional()) {
- $sample_params_urlencoded .= '<b class="param">' . $name . '</b>=';
- $sample_params_urlencoded .= urldecode($param->getSampleValue());
- }
- }
-
- if (!empty($sample_params_urlencoded)) {
- $sample_params_urlencoded = '&amp;' . $sample_params_urlencoded;
- }
- }
- ?>
- <dt>
- <code>
- <?php echo $method; ?>
- <?php
- if ($method == 'GET') {
- ?>
- <a href="?call=<?php echo $call . strip_tags($sample_params_urlencoded) ?>"><?php echo UserConfig::$USERSROOTFULLURL ?>/api.php?call=<b class="call"><?php echo $call ?></b><?php echo $sample_params_urlencoded ?></a>
- <?php
- } else {
- echo UserConfig::$USERSROOTFULLURL;
- ?>/api.php?call=<b class="call"><?php echo $call; ?></b>
- <?php
- }
- ?>
- </code>
- </dt>
- <dd>
- <?php echo $endpoint->getDescription(); ?>
- <?php
- if (count($params) > 0) {
- ?>
- <h4>Parameters:</h4>
- <dl>
- <?php
- foreach ($params as $name => $param) {
- ?>
- <dt>
- <code><?php echo $name; ?></code>
- <?php
- if ($param->isOptional()) {
- ?> (optional)<?php
- } else {
- ?> (required)<?php
- }
- ?>
- </dt>
- <dd>
- <?php
- echo $param->getDescription();
-
- if ($param->allowsMultipleValues()) {
- ?>
- (allows multiple values)
- <?php
- }
- ?>
- </dd>
- <?php
- }
- ?>
- </dl>
- <?php
- }
- ?>
- </dd>
- <?php
- }
- ?>
- </dl>
- </li>
- <?php
+ // Swagger paths, e.g. API Endpoints groupped by path
+ $all_endpoints = \StartupAPI\API\Endpoint::getAllEndpointsBySlug();
+ foreach ($all_endpoints as $namespace_slug => $namespace_endpoints) {
+ foreach ($namespace_endpoints as $endpoint_slug => $endpoints) {
+ foreach ($endpoints as $method => $endpoint) {
+ $operation = array(
+ 'tags' => array(
+ $namespace_slug
+ ),
+ 'summary' => $endpoint->getDescription(),
+ 'description' => $endpoint->getDescription(),
+ 'operationId' => get_class($endpoint),
+ 'responses' => array(
+ '200' => array(
+ 'description' => 'success'
+ ),
+ '400' => array(
+ 'description' => 'invalid input'
+ )
+ )
+ );
+
+ $params = $endpoint->getParams();
+
+ foreach ($params as $name => $param) {
+ $param_spec = array(
+ 'name' => $name,
+ 'description' => $param->getDescription(),
+ 'required' => !$param->isOptional()
+ );
+
+ if ($method === 'GET') {
+ $param_spec['in'] = 'query';
+ } else {
+ $param_spec['in'] = 'formData';
+ }
+
+ if ($param->allowsMultipleValues()) {
+ $param_spec['type'] = 'array';
+ $param_spec['collectionFormat'] = 'multi';
+ }
+ $operation['parameters'][] = $param_spec;
}
- ?>
- </ul>
- <?php
+
+ $swagger_spec['paths']["/api.php?call=/$namespace_slug$endpoint_slug"] = array(
+ strtolower($method) => $operation
+ );
+ }
}
- ?>
- </p>
- <?php
}
+
+ header('Content-type: application/json');
+ echo json_encode($swagger_spec);
+
+ if (json_last_error() !== JSON_ERROR_NONE) {
+ header('HTTP/1.1 400 Bad Request');
+ header('Content-type: text/plain');
+ echo "Error encoding JSON result";
+ }
+
+ exit;
+}
+
+if (!array_key_exists('call', $_GET)) {
+ $template_info = StartupAPI::getTemplateInfo();
+ StartupAPI::$template->display('@startupapi/swagger-ui.html.twig', $template_info);
exit;
}
@@ -172,7 +125,7 @@
'result' => $endpoint->call($params, $raw_request_body)
);
} catch (\StartupAPI\API\NotFoundException $ex) {
- header('HTTP/1.0 404 Not Found');
+ header('HTTP/1.1 404 Not Found');
$response = array(
'meta' => array(
'success' => false,
@@ -181,7 +134,7 @@
)
);
} catch (\StartupAPI\API\MethodNotAllowedException $ex) {
- header('HTTP/1.0 405 Method not allowed');
+ header('HTTP/1.1 405 Method not allowed');
$response = array(
'meta' => array(
'success' => false,
@@ -190,7 +143,7 @@
)
);
} catch (\StartupAPI\API\UnauthenticatedException $ex) {
- header('HTTP/1.0 401 Authentication Required');
+ header('HTTP/1.1 401 Authentication Required');
header('WWW-Authenticate: FormBased');
$response = array(
'meta' => array(
@@ -200,7 +153,7 @@
)
);
} catch (\StartupAPI\API\UnauthorizedException $ex) {
- header('HTTP/1.0 403 Forbidden');
+ header('HTTP/1.1 403 Forbidden');
$response = array(
'meta' => array(
'success' => false,
@@ -209,7 +162,7 @@
)
);
} catch (\StartupAPI\API\BadParameterException $ex) {
- header('HTTP/1.0 400 Bad Parameter');
+ header('HTTP/1.1 400 Bad Parameter');
$response = array(
'meta' => array(
'success' => false,
@@ -218,7 +171,7 @@
)
);
} catch (\StartupAPI\API\APIException $ex) {
- header('HTTP/1.0 500 Server Error');
+ header('HTTP/1.1 500 Server Error');
$response = array(
'meta' => array(
'success' => false,
View
@@ -888,6 +888,8 @@ class UserConfig {
*/
public static $enable_startupapi_apis = true;
+ public static $apiSpecVersion = '1.0.0';
+
/* ========================================================================
*
* Some global functions and default hooks, as well as static initializer
Submodule swagger-ui added at 48e7bc
@@ -4,6 +4,7 @@
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>{% block title %}Users - {{UserConfig.appName}}{% endblock %}</title>
{% include '@startupapi/head_tag.html.twig' %}
+ {% block head %}{% endblock %}
</head>
<body>
{% include '@startupapi/power_strip.html.twig' %}
@@ -0,0 +1,72 @@
+{% extends '@startupapi/page.html.twig' %}
+
+{% block head %}
+<link href='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
+<link href='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
+<link href='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
+<link href='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/css/reset.css' media='print' rel='stylesheet' type='text/css'/>
+<link href='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/css/print.css' media='print' rel='stylesheet' type='text/css'/>
+
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/object-assign-pollyfill.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/jquery.slideto.min.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/jquery.wiggle.min.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/handlebars-4.0.5.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/lodash.min.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/backbone-min.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/swagger-ui.min.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/highlight.9.1.0.pack_extended.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/jsoneditor.min.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/marked.js' type='text/javascript'></script>
+<script src='{{ UserConfig.USERSROOTURL }}/swagger-ui/dist/lib/swagger-oauth.js' type='text/javascript'></script>
+
+<!-- Some basic translations -->
+<!-- <script src='lang/translator.js' type='text/javascript'></script> -->
+<!-- <script src='lang/ru.js' type='text/javascript'></script> -->
+<!-- <script src='lang/en.js' type='text/javascript'></script> -->
+
+<script type="text/javascript">
+$(function () {
+ hljs.configure({
+ highlightSizeThreshold: 5000
+ });
+ // Pre load translate...
+ if(window.SwaggerTranslator) {
+ window.SwaggerTranslator.translate();
+ }
+ window.swaggerUi = new SwaggerUi({
+ url: '{{ UserConfig.USERSROOTURL }}/api.php?swagger-spec',
+ dom_id: "swagger-ui-container",
+ supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
+ onComplete: function(swaggerApi, swaggerUi){
+ if(window.SwaggerTranslator) {
+ window.SwaggerTranslator.translate();
+ }
+ },
+ onFailure: function(data) {
+ log("Unable to Load SwaggerUI");
+ },
+ docExpansion: "list",
+ jsonEditor: false,
+ defaultModelRendering: 'schema',
+ showRequestHeaders: false
+ });
+ window.swaggerUi.load();
+ function log() {
+ if ('console' in window) {
+ console.log.apply(console, arguments);
+ }
+ }
+});
+</script>
+{% endblock %}
+
+{% block content %}
+<div class="container-fluid" style="margin-bottom: 75px">
+ <div class="swagger-section">
+ <div id="message-bar" class="swagger-ui-wrap" data-sw-translate>&nbsp;</div>
+ <div id="swagger-ui-container" class="swagger-ui-wrap"></div>
+ </div>
+</div>
+{% endblock %}
@@ -4,6 +4,7 @@
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>{% block title %}Users - {{UserConfig.appName}}{% endblock %}</title>
{% include '@startupapi/head_tag.html.twig' %}
+ {% block head %}{% endblock %}
</head>
<body>
Oops, something went wrong.

0 comments on commit 062e9cc

Please sign in to comment.