1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 12:12:48 +01:00

Added Swagger

This commit is contained in:
Hillel Coren 2015-11-08 22:34:26 +02:00
parent ed021347cf
commit 57245211d8
13 changed files with 395 additions and 11 deletions

View File

@ -1,5 +1,5 @@
Attribution Assurance License Attribution Assurance License
Copyright (c) 2014 by Hillel Coren Copyright (c) 2015 by Hillel Coren
http://www.hillelcoren.com http://www.hillelcoren.com
All Rights Reserved All Rights Reserved

View File

@ -15,6 +15,7 @@ use App\Ninja\Serializers\ArraySerializer;
use App\Ninja\Transformers\AccountTransformer; use App\Ninja\Transformers\AccountTransformer;
use App\Ninja\Transformers\UserAccountTransformer; use App\Ninja\Transformers\UserAccountTransformer;
use App\Http\Controllers\BaseAPIController; use App\Http\Controllers\BaseAPIController;
use Swagger\Annotations as SWG;
class AccountApiController extends BaseAPIController class AccountApiController extends BaseAPIController
{ {

View File

@ -8,6 +8,36 @@ use League\Fractal\Resource\Item;
use League\Fractal\Resource\Collection; use League\Fractal\Resource\Collection;
use App\Ninja\Serializers\ArraySerializer; use App\Ninja\Serializers\ArraySerializer;
/**
* @SWG\Swagger(
* schemes={"http","https"},
* host="ninja.dev",
* basePath="/api/v1",
* @SWG\Info(
* version="1.0.0",
* title="Invoice Ninja API",
* description="An open-source invoicing and time-tracking app built with Laravel",
* termsOfService="",
* @SWG\Contact(
* email="contact@invoiceninja.com"
* ),
* @SWG\License(
* name="Attribution Assurance License",
* url="https://raw.githubusercontent.com/invoiceninja/invoiceninja/master/LICENSE"
* )
* ),
* @SWG\ExternalDocumentation(
* description="Find out more about Invoice Ninja",
* url="https://www.invoiceninja.com"
* ),
* @SWG\SecurityScheme(
* securityDefinition="api_key",
* type="apiKey",
* in="header",
* name="TOKEN"
* )
* )
*/
class BaseAPIController extends Controller class BaseAPIController extends Controller
{ {
protected $manager; protected $manager;

View File

@ -24,6 +24,21 @@ class InvoiceApiController extends Controller
$this->mailer = $mailer; $this->mailer = $mailer;
} }
/**
* @SWG\Get(
* path="/invoices",
* summary="List of invoices",
* @SWG\Response(
* response=200,
* description="A list with invoices",
* @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/Invoice"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
public function index($clientPublicId = false) public function index($clientPublicId = false)
{ {
$invoices = Invoice::scope() $invoices = Invoice::scope()

View File

@ -35,7 +35,7 @@ class ApiCheck {
Session::set('token_id', $token->id); Session::set('token_id', $token->id);
} else { } else {
sleep(3); sleep(3);
return Response::make('Invalid token', 403, $headers); return Response::json('Invalid token', 403, $headers);
} }
} }
@ -44,7 +44,7 @@ class ApiCheck {
} }
if (!Utils::isPro() && !$loggingIn) { if (!Utils::isPro() && !$loggingIn) {
return Response::make('API requires pro plan', 403, $headers); return Response::json('API requires pro plan', 403, $headers);
} else { } else {
$key = Auth::check() ? Auth::user()->account->id : $request->getClientIp(); $key = Auth::check() ? Auth::user()->account->id : $request->getClientIp();
@ -68,7 +68,7 @@ class ApiCheck {
if ($new_hour_throttle > $hour) { if ($new_hour_throttle > $hour) {
$wait = ceil($new_hour_throttle - $hour); $wait = ceil($new_hour_throttle - $hour);
sleep(1); sleep(1);
return Response::make("Please wait {$wait} second(s)", 403, $headers); return Response::json("Please wait {$wait} second(s)", 403, $headers);
} }
Cache::put("hour_throttle:{$key}", $new_hour_throttle, 10); Cache::put("hour_throttle:{$key}", $new_hour_throttle, 10);

View File

@ -4,8 +4,21 @@ use App\Models\Invoice;
use League\Fractal; use League\Fractal;
use League\Fractal\TransformerAbstract; use League\Fractal\TransformerAbstract;
/**
* @SWG\Definition(definition="Invoice",required={"invoice_number"}, @SWG\Xml(name="Invoice"))
*/
class InvoiceTransformer extends TransformerAbstract class InvoiceTransformer extends TransformerAbstract
{ {
/**
* @SWG\Property(property="id", type="integer", example=1)
* @SWG\Property(property="invoice_number", type="string", example="0001")
* @SWG\Property(property="amount", type="float", example=10)
* @SWG\Property(property="balance", type="float", example=10)
* @SWG\Property(property="client_id", type="integer", example=1)
* @SWG\Property(property="invoice_status_id", type="integer", example=1)
*/
protected $defaultIncludes = [ protected $defaultIncludes = [
'invoice_items', 'invoice_items',
]; ];
@ -18,11 +31,11 @@ class InvoiceTransformer extends TransformerAbstract
public function transform(Invoice $invoice) public function transform(Invoice $invoice)
{ {
return [ return [
'public_id' => (int) $invoice->public_id, 'id' => (int) $invoice->public_id,
'invoice_number' => $invoice->invoice_number, 'invoice_number' => $invoice->invoice_number,
'amount' => (float) $invoice->amount, 'amount' => (float) $invoice->amount,
'balance' => (float) $invoice->balance, 'balance' => (float) $invoice->balance,
'client_id' => (int) $invoice->client_id, 'client_id' => (int) $invoice->client->public_id,
'invoice_status_id' => (int) $invoice->invoice_status_id, 'invoice_status_id' => (int) $invoice->invoice_status_id,
'updated_at' => $invoice->updated_at, 'updated_at' => $invoice->updated_at,
'deleted_at' => $invoice->deleted_at, 'deleted_at' => $invoice->deleted_at,

View File

@ -66,7 +66,8 @@
"phpspec/phpspec": "~2.1", "phpspec/phpspec": "~2.1",
"codeception/codeception": "~2.0", "codeception/codeception": "~2.0",
"codeception/c3": "~2.0", "codeception/c3": "~2.0",
"fzaninotto/faker": "^1.5" "fzaninotto/faker": "^1.5",
"jlapp/swaggervel": "master-dev"
}, },
"autoload": { "autoload": {
"classmap": [ "classmap": [

110
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "4c2d5d48a648e380f3acf2943b91e6bc", "hash": "fb15622e77287d516219e55ebb01ea3e",
"packages": [ "packages": [
{ {
"name": "agmscode/omnipay-agms", "name": "agmscode/omnipay-agms",
@ -2464,6 +2464,51 @@
], ],
"time": "2015-03-11 20:06:43" "time": "2015-03-11 20:06:43"
}, },
{
"name": "jlapp/swaggervel",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/slampenny/Swaggervel.git",
"reference": "ea47fafde4984278e27a8044a1b1b0bcfd79130c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slampenny/Swaggervel/zipball/ea47fafde4984278e27a8044a1b1b0bcfd79130c",
"reference": "ea47fafde4984278e27a8044a1b1b0bcfd79130c",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"zircote/swagger-php": "*"
},
"type": "library",
"autoload": {
"psr-0": {
"Jlapp\\Swaggervel": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "jlapp",
"email": "jordan@jordanlapp.com"
}
],
"description": "A great way to integrate Swagger into Laravel",
"keywords": [
"L4",
"api",
"documentation",
"l5",
"laravel",
"swagger"
],
"time": "2015-08-18 15:33:39"
},
{ {
"name": "jsanc623/phpbenchtime", "name": "jsanc623/phpbenchtime",
"version": "2.1.0", "version": "2.1.0",
@ -6968,6 +7013,66 @@
], ],
"description": "A Swiftmailer Transport for Postmark.", "description": "A Swiftmailer Transport for Postmark.",
"time": "2015-03-19 13:06:11" "time": "2015-03-19 13:06:11"
},
{
"name": "zircote/swagger-php",
"version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/zircote/swagger-php.git",
"reference": "f6624cc067d7894ec32943f5b94cf282c683f7c7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/f6624cc067d7894ec32943f5b94cf282c683f7c7",
"reference": "f6624cc067d7894ec32943f5b94cf282c683f7c7",
"shasum": ""
},
"require": {
"doctrine/annotations": "*",
"php": ">=5.4.0",
"symfony/finder": "*"
},
"require-dev": {
"zendframework/zend-form": "*"
},
"bin": [
"bin/swagger"
],
"type": "library",
"autoload": {
"psr-4": {
"Swagger\\": "src"
},
"files": [
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache2"
],
"authors": [
{
"name": "Robert Allen",
"email": "zircote@gmail.com",
"homepage": "http://www.zircote.com"
},
{
"name": "Bob Fanger",
"email": "bfanger@gmail.com",
"homepage": "http://bfanger.nl"
}
],
"description": "Swagger-PHP - Generate interactive documentation for your RESTful API using phpdoc annotations",
"homepage": "https://github.com/zircote/swagger-php/",
"keywords": [
"api",
"json",
"rest",
"service discovery"
],
"time": "2015-10-18 13:05:54"
} }
], ],
"packages-dev": [ "packages-dev": [
@ -8377,7 +8482,8 @@
"dercoder/omnipay-paysafecard": 20, "dercoder/omnipay-paysafecard": 20,
"meebio/omnipay-secure-trading": 20, "meebio/omnipay-secure-trading": 20,
"labs7in0/omnipay-wechat": 20, "labs7in0/omnipay-wechat": 20,
"laracasts/presenter": 20 "laracasts/presenter": 20,
"jlapp/swaggervel": 20
}, },
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,

View File

@ -150,7 +150,7 @@ return [
'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider', 'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider',
'Illuminate\Html\HtmlServiceProvider', 'Illuminate\Html\HtmlServiceProvider',
'Laravel\Socialite\SocialiteServiceProvider', 'Laravel\Socialite\SocialiteServiceProvider',
'Jlapp\Swaggervel\SwaggervelServiceProvider',
/* /*
* Application Service Providers... * Application Service Providers...

97
config/swaggervel.php Normal file
View File

@ -0,0 +1,97 @@
<?php
/**
* Created by PhpStorm.
* User: Jordan
* Date: 04/07/14
* Time: 3:54 PM
*/
return array(
/*
|--------------------------------------------------------------------------
| Absolute path to location where parsed swagger annotations will be stored
|--------------------------------------------------------------------------
*/
'doc-dir' => storage_path() . '/docs',
/*
|--------------------------------------------------------------------------
| Relative path to access parsed swagger annotations.
|--------------------------------------------------------------------------
*/
'doc-route' => 'docs',
/*
|--------------------------------------------------------------------------
| Absolute path to directory containing the swagger annotations are stored.
|--------------------------------------------------------------------------
*/
"app-dir" => "app",
/*
|--------------------------------------------------------------------------
| Absolute path to directories that you would like to exclude from swagger generation
|--------------------------------------------------------------------------
*/
"excludes" => array(
storage_path(),
base_path()."/tests",
base_path()."/resources/views",
base_path()."/config",
base_path()."/vendor"
),
/*
|--------------------------------------------------------------------------
| Turn this off to remove swagger generation on production
|--------------------------------------------------------------------------
*/
"generateAlways" => env('APP_DEBUG'),
"api-key" => "auth_token",
/*
|--------------------------------------------------------------------------
| Edit to set the api's version number
|--------------------------------------------------------------------------
*/
"default-api-version" => "v1",
/*
|--------------------------------------------------------------------------
| Edit to set the swagger version number
|--------------------------------------------------------------------------
*/
"default-swagger-version" => "2.0",
/*
|--------------------------------------------------------------------------
| Edit to set the api's base path
|--------------------------------------------------------------------------
*/
"default-base-path" => "",
/*
|--------------------------------------------------------------------------
| Edit to trust the proxy's ip address - needed for AWS Load Balancer
|--------------------------------------------------------------------------
*/
"behind-reverse-proxy" => false,
/*
|--------------------------------------------------------------------------
| Uncomment to add response headers when swagger is generated
|--------------------------------------------------------------------------
*/
/*"viewHeaders" => array(
'Content-Type' => 'text/plain'
),*/
/*
|--------------------------------------------------------------------------
| Uncomment to add request headers when swagger performs requests
|--------------------------------------------------------------------------
*/
/*"requestHeaders" => array(
'TestMe' => 'testValue'
),*/
);

View File

@ -838,7 +838,7 @@
function createInvoiceModel() { function createInvoiceModel() {
var invoice = ko.toJS(window.model).invoice; var invoice = ko.toJS(window.model).invoice;
invoice.is_pro = {{ Auth::user()->isPro() ? 'true' : 'false' }}; invoice.is_pro = {{ Auth::user()->isPro() ? 'true' : 'false' }};
//invoice.is_quote = {{ $entityType == ENTITY_QUOTE ? 'true' : 'false' }}; invoice.is_quote = {{ $entityType == ENTITY_QUOTE ? 'true' : 'false' }};
invoice.contact = _.findWhere(invoice.client.contacts, {send_invoice: true}); invoice.contact = _.findWhere(invoice.client.contacts, {send_invoice: true});
if (invoice.is_recurring) { if (invoice.is_recurring) {

View File

View File

@ -0,0 +1,121 @@
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header("Access-Control-Allow-Headers: X-Requested-With");
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="icon" type="image/png" href="vendor/swaggervel/images/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="vendor/swaggervel/images/favicon-16x16.png" sizes="16x16" />
<link href='vendor/swaggervel/css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='vendor/swaggervel/css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='vendor/swaggervel/css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='vendor/swaggervel/css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<link href='vendor/swaggervel/css/print.css' media='print' rel='stylesheet' type='text/css'/>
<script src='vendor/swaggervel/lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='vendor/swaggervel/lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='vendor/swaggervel/lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='vendor/swaggervel/lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='vendor/swaggervel/lib/handlebars-2.0.0.js' type='text/javascript'></script>
<script src='vendor/swaggervel/lib/underscore-min.js' type='text/javascript'></script>
<script src='vendor/swaggervel/lib/backbone-min.js' type='text/javascript'></script>
<script src='vendor/swaggervel/swagger-ui.js' type='text/javascript'></script>
<script src='vendor/swaggervel/lib/highlight.7.3.pack.js' type='text/javascript'></script>
<script src='vendor/swaggervel/lib/marked.js' type='text/javascript'></script>
<script src='vendor/swaggervel/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 log() {
if ('console' in window) {
console.log.apply(console, arguments);
}
}
$(function () {
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
url = decodeURIComponent(url[1]);
} else {
url = "{!! $urlToDocs !!}";
}
// Pre load translate...
if (window.SwaggerTranslator) {
window.SwaggerTranslator.translate();
}
window.swaggerUi = new SwaggerUi({
url: url,
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
onComplete: function (swaggerApi, swaggerUi) {
log("Loaded SwaggerUI");
@if (isset($requestHeaders))
@foreach($requestHeaders as $requestKey => $requestValue)
window.authorizations.add("{!!$requestKey!!}", new ApiKeyAuthorization("{!!$requestKey!!}", "{!!$requestValue!!}", "header"));
@endforeach
@endif
if (window.SwaggerTranslator) {
window.SwaggerTranslator.translate();
}
$('pre code').each(function (i, e) {
hljs.highlightBlock(e)
});
addApiKeyAuthorization();
},
onFailure: function (data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none",
apisSorter: "alpha",
showRequestHeaders: false
});
function addApiKeyAuthorization() {
var key = encodeURIComponent($('#input_apiKey')[0].value);
if (key && key.trim() != "") {
var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("X-Ninja-Token", key, "header");
window.swaggerUi.api.clientAuthorizations.add("api_key", apiKeyAuth);
log("added key " + key);
}
}
$('#input_apiKey').change(addApiKeyAuthorization);
window.swaggerUi.load();
});
</script>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.io">swagger</a>
<form id='api_selector'>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div>
<div class='input'><input placeholder="API Token" id="input_apiKey" name="apiKey" type="text" value="{{ env('TEST_API_TOKEN') }}"/></div>
<div class='input'><a id="explore" href="#" data-sw-translate>Explore</a></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap" data-sw-translate>&nbsp;</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body>
</html>