MT#3925 Extend API documentation (Auth, styling).

agranig/rest
Andreas Granig 12 years ago
parent 2e9e52711f
commit df6bfea155

@ -0,0 +1,102 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "user preferences for a subscriber",
"required": [],
"properties": {
"attribute": { "type": "string", "description": "The preference name." },
"value": { "type": "string", "description": "The preference name." },
},
"title": "subscriberpreference",
"type": "object"
}
[% #
GET /api/subscriberpreferences/?subscriber_id=10
[
"cli": "12345",
"block_in_list": [ "12345", "12346", "12347" ]
]
POST /api/subscriberpreferences/?subscriber_id=10
[
"cli": "12345",
"block_in_list": [ "12345", "12346", "12347" ]
]
GET /api/subscribers/?id=77
{
"username": "foo",
"_links": [
"callforwards": {
"href": "/api/callforwards/?subscriber_id=987"
},
"cf_destinationsets": {
"href": "/api/destinationsets/?subscriber_id=987"
}
],
}
GET /api/callforwards/?subscriber_id=77
{
# should be separated by cf types
"_links": [
"cfu": {
"href": "/api/callforwards/?id=555"
}
"cfb": {
"href": "/api/callforwards/?id=556"
}
]
}
GET /api/callforwards/?id=555
{
"_links": [
"destinationset": {
"href": "/api/cf_destinationsets/?id=555"
},
"timeset": {
"href": "/api/cf_timesets/?id=556"
},
]
}
GET /api/destinationsets/?subscriber_id=77
PATCH /api/subscribers/?id=77
[
{ "op":
]
$res = $ua->get("/api/subscribers/?id=77")->from_json;
$cf = $res->{_links}->{callforwards}; # "/api/callforwards/?subscriber_id=77
$dsets = $res->{_links}->{cf_destinationsets}; # "/api/cf_destinationsets/?subscriber_id=77
$res = $ua->get($cf->{href})->from_json;
$cfu = $res->{_links}->{cfu}; # /api/callforwards/?id=555
if($cfu) {
$res = $ua->get($cfu)->from_json;
$res = $ua->get($res->{_links}->{collection}->{href})->from_json;
$dset = $res->{_links}->{destinationsets}->[0];
$ua->put($cfu->{href}, "{ \"_links\": [ \"destinationset\": { \"href\": \"$dset->{href}\" }, \"timeset\": null] }");
} else {
$res = $ua->get($dsets->{href})->from_json;
$dset = $res->{_links}->{destinationsets}->[0];
$ua->put($cf->{href}, "{ \"_links\": [ \"destinationset\": { \"href\": \"$dset->{href}\" }, \"timeset\": null] }");
}
-%]

@ -12,18 +12,34 @@
.content { margin-left: 2em }
h1 { color: white; background-color: #54893B; padding: 1.7em; }
h6 { font-size: 1.1em; }
code { white-space: pre-wrap; background-color: transparent; border: none; color: #000; font-size: 0.9em; padding: 0; }
code { white-space: pre-wrap; background-color: transparent; border: none; color: #000; font-size: 0.9em; display: block; margin: 0 0 20px 0;}
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #aaa; width: 50%; padding: 20px; }
td { vertical-align: top; }
span { font-family: monospace; }
nav ol { counter-reset: item }
nav li { display: block }
nav li:before { content: counters(item, ".") " "; counter-increment: item }
a { color: #54893B; border-bottom: 1px dotted #54893B; }
a:hover { color: #54893B; text-decoration: underline; border: none; }
.ui-state-active { border: 1px solid #54893B; color: #54893B; }
.ui-state-default { color: #54893B; }
.ui-widget { font-family: inherit; }
.ui-widget-content { background: none; }
/* toc numbering */
nav ol { counter-reset: item }
nav li { display: block }
nav li:before { content: counters(item, ".") " "; counter-increment: item }
/* header numbering */
body { counter-reset: h2counter; counter-reset: h3counter; }
h1 { counter-reset: h2counter; }
h2:before { content: counter(h2counter) "\0000a0\0000a0"; counter-increment: h2counter; counter-reset: h3counter; }
h2.nocount:before { content: none; counter-increment: none; }
h2 { counter-reset: h3counter; }
h3:before { counter-increment: h3counter; content: counter(h2counter) "." counter(h3counter) "\0000a0\0000a0"; }
/*h3:before { content: counter(h2counter) "." counter(h3counter) "\0000a0\0000a0"; counter-increment: h3counter; }*/
/* print properties */
@media print { .ui-accordion > *{display:block !important;} }
.pagebreak { page-break-after: always; }
.chapter { page-break-inside: avoid; }
@ -34,17 +50,24 @@
<body>
[%
chapters = [
{ level = 2, id = 'intro', title = 'Introduction', },
{ level = 2, id = 'relations', title = 'Relations', },
{ level = 3, id = 'std-relations', title = 'Standard Link Relations', },
{ level = 3, id = 'rel-contacts', title = 'Contacts Relation', uri = '/api/contacts/' },
{ level = 2, id = 'definitions', title = 'Definitions', },
{ level = 2, id = 'intro', title = 'Introduction', },
{ level = 2, id = 'auth', title = 'Authentication', },
{ level = 2, id = 'relations', title = 'Resources', },
{ level = 3, id = 'rel-contacts', title = 'Contacts', uri = '/api/contacts/' },
# { level = 3, id = 'rel-resellers', title = 'Resellers', uri = '/api/resellers/' },
# { level = 3, id = 'rel-admins', title = 'Admins', uri = '/api/admins/' },
# { level = 3, id = 'rel-customers', title = 'Customers', uri = '/api/customers/' },
# { level = 3, id = 'rel-billingprofiles', title = 'Billing Profiles', uri = '/api/billing/' },
{ level = 2, id = 'definitions', title = 'Definitions', },
];
-%]
<h1>Sipwise NGCP HTTP API Documentation</h1>
<div class="content">
<div class="chapter">
<h2>Table of Contents</h2>
<h2 class="nocount">Table of Contents</h2>
<nav>
<ol>
[% FOR chapter IN chapters %]

@ -0,0 +1,83 @@
<h[% level %] id="[% id %]">
[% IF uri -%]
<a href="[% uri %]" rel="collection">
[% END -%]
[% title %]
[% IF uri -%]
</a>
[% END -%]
</h[% level %]>
Authentication and authorization on the Sipwise NGCP HTTP API is performed via <b>SSL Client Certificates</b>. You can generate and download certificates for administrators and resellers via the <b>NGCP Panel</b> in the <b>Administrators</b> view.
You will need two files:
<ol>
<li>The client certificate generated via the NGCP Panel. This is usually labelled <span>NGCP-API-client-certificate-xxxxx.pem</span>.</li>
<li>The CA certificate used to sign the server certificate, in case it as been self-signed or the CA is not recognized by the client host environment.</li>
</ol>
<h[% level + 1 %]>Examples</h[% level + 1 %]>
<div class="examples">
<h5>Using cURL on the Shell</h5>
<p>
With cURL, use <span>--cert /path/to/NGCP-API-client-certificate-xxxxx.pem</span> to specify the client certificate, and <span>--cacert /path/to/ca-cert.pem</span> to specify the CA certificate in case of a self-signed server certificate.
<code>
curl -i -X GET --cert /path/to/NGCP-API-client-certificate-1385650532.pem --cacert /path/to/ca-cert.pem https://example.org:1443/api/something/
</code>
</p>
<h5>Using Perl LWP::UserAgent</h5>
<p>
With LWP::UserAgent, set up the SSL client certificates using the <span>ssl_opts()</span> function. Since the key file downloaded from the NGCP Panel combines both the client key and the certificate into one single file, use the same filename for the <span>SSL_cert_file</span> and <span>SSL_key_file</span> option.
<code>
#!/usr/bin/perl -w
use strict;
use LWP::UserAgent;
my $ua = LWP::UserAgent->new();
$ua->ssl_opts(
SSL_cert_file => '/path/to/NGCP-API-client-certificate-1385650532.pem',
SSL_key_file => '/path/to/NGCP-API-client-certificate-1385650532.pem',
SSL_ca_file => '/path/to/ca-cert.pem',
);
my $res = $ua->get('https://example.org:1443/api/something/');
if($res->is_success) {
print $res->as_string;
} else {
print STDERR $res->status_line, "\n";
}
</code>
</p>
<h5>Using PHP cURL</h5>
<p>
Same as with Perl's LWP::UserAgent described above, you have to set the key and certificate paths using <span>curl_setopt_array()</span>, with the parameters <span>CURLOPT_SSLCERT</span> and <span>CURLOPT_SSLKEY</span> pointing to your client certificate.
<code>
$ua = curl_init();
$options = array(
CURLOPT_SSLCERT => '/path/to/NGCP-API-client-certificate-1385650532.pem',
CURLOPT_SSLKEY => '/path/to/NGCP-API-client-certificate-1385650532.pem',
CURLOPT_CAINFO => '/path/to/ca-cert.pem',
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_RETURNTRANSFER => true,
);
curl_setopt_array($ua , $options);
curl_setopt($ua, CURLOPT_URL, 'https://example.org:1443/api/something/');
$res = curl_exec($ua);
if(!$res) {
echo "Curl Error : " . curl_error($ua);
}
else {
echo $res;
}
</code>
</p>
</div>
[% # vim: set tabstop=4 syntax=html expandtab: -%]

@ -1,4 +1,4 @@
<h2 id="[% id %]">
<h[% level %] id="[% id %]">
[% IF uri -%]
<a href="[% uri %]" rel="collection">
[% END -%]
@ -6,11 +6,67 @@
[% IF uri -%]
</a>
[% END -%]
</h2>
</h[% level %]>
<p>This documentation is also a machine-readable service document. The resource processing model is implied by
the <a href="#draft-kelly-json-hal">JSON Hypertext Application Language</a>, draft version 06. This document
specifies a <a href="#rfc6906">profile</a> by defining <a href="#relations">link relations</a> and their
implied restrictions on the media type.</p>
<p>
This documentation describes the Sipwise NGCP HTTP API, which is used to control the NGCP platform via 3rd party
applications. It is also a machine-readable service document and serves as entry point for API clients.
</p>
<p>
The Sipwise NGCP HTTP API strictly follows the REST principle and uses the <a href="http://tools.ietf.org/html/draft-kelly-json-hal-06">JSON Hypertext Application Language (JSON-HAL)</a> to express resources and their relations. This means, compared to typical plain JSON APIs, that relations between resources are defined as hyperlinks within the JSON objects, so a client can and should navigate between related objects without hard-coding any URIs.
</p>
<h[% level + 1%]>Brief Overview of JSON-HAL</h[% level + 1%]>
HAL is a generic media type with which Web APIs can be developed and exposed as series of links. Clients of these APIs can select links by their link relation type and traverse them in order to progress through the application.
A HAL document uses the format described in <a href="http://tools.ietf.org/html/rfc4627">RFC4627 - The application/json Media Type for JavaScript Object Notation (JSON)</a> and has the media type <span>application/hal+json</span>.
An example request to fetch a <i>contact</i> via the API looks like this:
<code>
GET /api/contacts/?id=1 HTTP/1.1
Accept: application/hal+json
</code>
The corresponding response is as follows:
<code>
HTTP/1.1 200 OK
Content-Type: application/hal+json
{
"_links": {
"self": { "href": "/api/contacts/?id=1" },
"ngcp:reseller": { "href": "/api/resellers/?id=5" },
},
"firstname": "John",
"lastname": "Doe",
"email": "john.doe@example.org"
}
</code>
Here, we have a HAL document representing a contact resource with the URI <span>/api/contacts/?id=1</span>. It has a link to a <span>reseller</span> it belongs to, and its own state in the form of <span>firstname</span>, <span>lastname</span> and <span>email</span> properties.
To fetch the reseller of this contact, an API client only has to follow the link provided in <span>_links.reseller.href</span>.
A simple code example might look like this:
<code>
$ua = LWP::UserAgent->new;
# fetch the contact (URI hardcoded for simplicity only!)
$contact = from_json($ua->get('https://example.org/api/contacts/?id=1'));
# follow the reseller link to fetch the reseller of this contact
$reseller = from_json($ua->get($contact->{'_links'}->{'ngcp:reseller'}->{'href'});
</code>
<h[% level + 1%]>API Versioning</h[% level + 1%]>
Due to the JSON-HAL structure, all related resources are hyperlinked, which implies that no strict API versioning is required. If URIs change between NGCP versions, the hyperlinks in the JSON-HAL documents are updated accordingly.
As a consequence, this means that a client implemented against the API <b>should not hardcode URIs</b>, rather than using the hyperlinks provided in the resources.
[% # vim: set tabstop=4 syntax=html expandtab: -%]

@ -8,6 +8,15 @@
[% END -%]
</h[% level %]>
<h[% level + 1 %]>Description</h[% level + 1%]>
<p>
The <a href="#rel-contacts">contacts</a> item is used to specify the contact information of <a href="#rel-contracts">contracts</a> for customers, resellers and peering groups.
</p>
<p>
For customer contacts, the item MUST link to a <a href="#rel-resellers">reseller</a>, otherwise it MUST be unset.
</p>
<h[% level + 1 %]>Properties</h[% level + 1%]>
<code>
[% INSERT 'js/api/properties/contacts-item.json' -%]
@ -18,13 +27,15 @@
[% INSERT 'js/api/links/contacts-item.json' -%]
</code>
<p class="alert alert-error">If the contact is going to be used for identifying a reseller or peering (that is, when you create an <a href="#rel-contracts">contracts</a> item), you <b>MUST NOT</b> set the <i>ngcp:resellers</i> relation.</p>
<h[% level + 1 %]>Examples</h[% level + 1 %]>
<div class="examples">
<h[% level + 2 %]>Request available HTTP methods on the URI</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X OPTIONS -H \'Connection: close\' -E NGCP-API-client-certificate-1385650532.pem --cacert ca-cert.pem https://example.org:1443/api/contacts/';
cmd = 'curl -i -X OPTIONS -H \'Connection: close\' --cert NGCP-API-client-certificate-1385650532.pem --cacert ca-cert.pem https://example.org:1443/api/contacts/';
INCLUDE helpers/api_command.tt cmd=cmd level=level+3;
request = [
@ -42,7 +53,7 @@
<h[% level + 2 %]>Request the entire <i>contacts</i> collection</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X GET -H \'Connection: close\' -E NGCP-API-client-certificate-1385650532.pem --cacert ca-cert.pem https://example.org:1443/api/contacts/';
cmd = 'curl -i -X GET -H \'Connection: close\' --cert NGCP-API-client-certificate-1385650532.pem --cacert ca-cert.pem https://example.org:1443/api/contacts/';
INCLUDE helpers/api_command.tt cmd=cmd level=level+3;
request = [
@ -150,7 +161,7 @@ Link: &lt;/api/resellers/?id=2&gt;; rel="http://example.com/#rel-contacts"
<h[% level + 2 %]>Create a new <i>contacts</i> entry</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X POST -H \'Connection: close\' -E NGCP-API-client-certificate-1385650532.pem --cacert ca-cert.pem https://example.org:1443/api/contacts/ --data-binary \'{ "firstname": "John", "lastname": "Doe", "email": "john.doe@example.org", "city": null, "company": null, "country": null, "phonenumber": null, "postcode": null, "street": null, "_links" : { "http://purl.org/sipwise/ngcp-api/#rel-resellers": { "href": "/api/resellers/?id=1" } } }\' -H \'Content-Type: application/hal+json\'';
cmd = 'curl -i -X POST -H \'Connection: close\' --cert NGCP-API-client-certificate-1385650532.pem --cacert ca-cert.pem https://example.org:1443/api/contacts/ --data-binary \'{ "firstname": "John", "lastname": "Doe", "email": "john.doe@example.org", "city": null, "company": null, "country": null, "phonenumber": null, "postcode": null, "street": null, "_links" : { "http://purl.org/sipwise/ngcp-api/#rel-resellers": { "href": "/api/resellers/?id=1" } } }\' -H \'Content-Type: application/hal+json\'';
INCLUDE helpers/api_command.tt cmd=cmd level=level+3;
request = [
@ -186,7 +197,7 @@ Link: &lt;/api/resellers/?id=2&gt;; rel="http://example.com/#rel-contacts"
<h[% level + 2 %]>Update an existing <i>contacts</i> entry</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X PUT -H \'Connection: close\' -E NGCP-API-client-certificate-1385650532.pem --cacert ca-cert.pem https://example.org:1443/api/contacts/?id=10 --data-binary \'{ "firstname": "John", "lastname": "Doe", "email": "john.doe@example.org", "city": null, "company": null, "country": null, "phonenumber": null, "postcode": null, "street": null, "_links" : { "http://purl.org/sipwise/ngcp-api/#rel-resellers": { "href": "/api/resellers/?id=1" } } }\' -H \'Content-Type: application/hal+json\' -H \'If-Match: *\' -H \'Prefer: return=minimal\'';
cmd = 'curl -i -X PUT -H \'Connection: close\' --cert NGCP-API-client-certificate-1385650532.pem --cacert ca-cert.pem https://example.org:1443/api/contacts/?id=10 --data-binary \'{ "firstname": "John", "lastname": "Doe", "email": "john.doe@example.org", "city": null, "company": null, "country": null, "phonenumber": null, "postcode": null, "street": null, "_links" : { "http://purl.org/sipwise/ngcp-api/#rel-resellers": { "href": "/api/resellers/?id=1" } } }\' -H \'Content-Type: application/hal+json\' -H \'If-Match: *\' -H \'Prefer: return=minimal\'';
INCLUDE helpers/api_command.tt cmd=cmd extended=1 level=level+3;
request = [
@ -224,7 +235,7 @@ Link: &lt;/api/resellers/?id=2&gt;; rel="http://example.com/#rel-contacts"
<h[% level + 2 %]>Update specific fields of an existing <i>contacts</i> entry</h[% level + 2 %]>
<p>
[%
cmd = 'curl -i -X PATCH -H \'Connection: close\' -E NGCP-API-client-certificate-1385650532.pem --cacert ca-cert.pem https://example.org:1443/api/contacts/?id=10 --data-binary \'[ { "op": "replace", "path": "/email", "value": "other.john.doe@example.org" } ]\' -H \'Content-Type: application/json-patch+json\' -H \'If-Match: *\' -H \'Prefer: return=minimal\'';
cmd = 'curl -i -X PATCH -H \'Connection: close\' --cert NGCP-API-client-certificate-1385650532.pem --cacert ca-cert.pem https://example.org:1443/api/contacts/?id=10 --data-binary \'[ { "op": "replace", "path": "/email", "value": "other.john.doe@example.org" } ]\' -H \'Content-Type: application/json-patch+json\' -H \'If-Match: *\' -H \'Prefer: return=minimal\'';
INCLUDE helpers/api_command.tt cmd=cmd extended=1 level=level+3;
request = [

@ -1,7 +0,0 @@
<h[% level %]><a href="#iana-relations">Standard Link Relations</a></h[% level %]>
<ul>
<li id="self">self</li>
<li id="collection">collection</li>
<li id="item">item</li>
</ul>
[% # vim: set tabstop=4 syntax=html expandtab: -%]
Loading…
Cancel
Save