Difference between revisions of "stoney core: REST API"

From stoney cloud
Jump to: navigation, search
[unchecked revision][checked revision]
(Filtering, sorting and searching)
(Mandatory headers)
 
(102 intermediate revisions by 3 users not shown)
Line 1: Line 1:
== REST API ==
+
= Overview =
* The [http://en.wikipedia.org/wiki/REST#RESTful_web_APIs REST API] will be implemented as a first-class citizen
+
** It provides all the available functions and data to its clients
+
** Serves as a data and business logic abstraction layer
+
* The REST API will be implemented using HTTPS and REST principles
+
** Clients are required to validate the certificate (at least via CA)
+
* The REST API uses JSON as the primary data interchange format (serialization of data structures should be abstracted), other formats should be possible in the future.
+
* Authentication via Basic HTTP-Auth
+
* Multiple authentication methods can be added in the future (possibly Web-Server assisted):
+
** X509 Certificate based authentication
+
** Kerberos
+
** API key with shared secret
+
** Access tokens
+
** OAuth
+
* versioned API:
+
** starting with one version number in the URI, for example: https://api.selfcare.com/v1/customer , corresponding  to the major version in SemVer
+
** minor version will be added via Request-Header-Field in future (as-needed)
+
* All API calls need to be fully nonblocking. If an expensive call has to be made to a backend system, the client needs to be provided with a status URI which can be checked for the current status or preferably be notified via [http://en.wikipedia.org/wiki/WebSocket WebSockets].
+
* Input validation must be performed for all data (validation of data happens twice: in the API and the client)
+
** JSON (or XML) validation has to be done before everything else and the client needs to be informed if he passed invalid syntax (see [http://www.php.net/manual/de/function.json-last-error.php function.json-last-error] and [http://www.php.net/manual/de/function.json-last-error-msg.php function.json-last-error-msg])
+
* Meaningful error message will be presented to the client
+
* All API functions are to be documented using an accepted documentation standard (doxygen (preferred), phpDocumentor or Sami)
+
* The API will be based on existing, proven and tested open source modules and components, coming either from a framework are as stand alone implementations,
+
  
 +
= Entities, Roles and Relationships =
 +
== Overview ==
 +
Description, how everything works together.Entitlement, Access and Roles
  
Why a REST API?
+
== Entities ==
* Separation and abstraction of presentation and business logic
+
An Entity is always a company or a person.
** Faster development/test cycles for business logic
+
** Smaller development packages
+
* Support for multiple clients with the same code base
+
** HTML/JS/CSS for selfcare Web GUI
+
** Command line interface for easy scripting
+
** Integration into third party provisioning systems for resellers
+
* Automatic testing of functionality
+
* Base for [http://en.wikipedia.org/wiki/Responsive_web_design responsive] resp. [http://www.abookapart.com/products/mobile-first Mobile First] Web-Applications/-Design
+
  
 +
=== Service Provider ===
 +
A service provider is the owner of the stoney cloud installation. One or more employees of the the service provider must be [[#Super User | super users]].
  
=== Yii related API modules ===
+
=== Reseller(s) ===
* On the Yii PHP Framework Homepage: [http://www.yiiframework.com/extensions/?tag=rest Extensions tagged with "rest"]
+
A reseller sells services of the service provider as his own. He is responsible for the administration of his customers and their services.
** On the Yii PHP Framework Homepage: [http://www.yiiframework.com/extension/restfullyii/ RestfullYii] or on GitHub: [https://github.com/evan108108/RESTFullYii RestfullYii]
+
 
* On the Yii PHP Framework Homepage: [http://www.yiiframework.com/extensions/?tag=api Extensions tagged with "api"]
+
=== Customers(s) ===
* [http://www.yiiframework.com/extension/yii-apiauth/ yii-apiauth]
+
A customer subscribes to services that the reseller sells. The customer can administrate his own people, employees and services.
 +
 
 +
=== People ===
 +
==== Person ====
 +
A person belonging one or more resellers and one or more customers. Normally, a person would belong to one reseller and one customer.
 +
 
 +
==== Employee ====
 +
A person, which is employed by one or more resellers and/or one or more customers.
 +
 
 +
==== Super User ====
 +
A super user is a person which has the right to complete the REST API functionality. The stoney cloud is delivered with one active super user. See the  [[stoney_core:_OpenLDAP_directory_data_organisation#People_.28Superuser.29 | People (Superuser)]] description in the [[stoney core: OpenLDAP directory data organisation]].
 +
 
 +
== Roles ==
 +
The roles are not finalized yet.
 +
 
 +
== Relationships ==
 +
The relationships are hierarchical:
 +
 
 +
'''Service Provider Person''' > '''Reseller Employee''' > '''Customer Employee''' > '''Reseller Person''' and '''Customer Person'''
 +
 
 +
The further down you go, the less rights a person has (this is currently independent from any existing roles, as these have not been finalized yet).
 +
 
 +
=== Service Provider Person (SPP) ===
 +
Definition of a Service Provider Person:
 +
{| border="1" class="wikitable sortable"
 +
! API Attribute
 +
! LDAP Attribute
 +
! Example
 +
! Description
 +
|-
 +
 
 +
 
 +
| id
 +
| uid
 +
| 4000002
 +
| Unique id of the person.
 +
|-
 +
 
 +
| -
 +
| sstBelongsToUID
 +
| 1
 +
| Hidden (internal) attribute, that will lose it's meaning, when the roles are finalized. Makes a person a super user.
 +
|-
 +
 
 +
| isActive
 +
| sstIsActive
 +
| true
 +
| Is the person active or not. Only active people are allowed to authenticate themselves.
 +
|-
 +
 
 +
| belongsToResellerId
 +
| sstBelongsToResellerUID
 +
| 4000000
 +
| Shows the reseller, that the person belongs to (only one reseller possible, N:1 relation).
 +
|-
 +
 
 +
| belongsToCustomerId
 +
| sstBelongsToCustomerUID
 +
| 4000001
 +
| Shows the customer, that the person belongs to (only one customer possible, N:1 relation).
 +
|-
 +
 
 +
| employeeOfId
 +
| sstEmployeeOfUID
 +
| 4000000
 +
| Shows the reseller (in this case), that the person is an employee of (multiple resellers possible, N:M relation).
 +
|-
 +
 
 +
| employeeOfId
 +
| sstEmployeeOfUID
 +
| 4000001
 +
| Shows the customer (in this case), that the person is an employee of (multiple customers possible, N:M relation).
 +
|-
 +
 
 +
|}
 +
 
 +
==== Service Provider Person (SPP) Rights ====
 +
A Service Provider Person can just about do anything. The following rights are unique for a Service Provider Person (SPP):
 +
* SPP.00: '''Create a reseller''': Can create a reseller.
 +
* SPP.01: '''Retrieve all resellers''': Can retrieve all resellers.
 +
* SPP.02: '''Retrieve a reseller''': Can retrieve a reseller.
 +
* SPP.03: '''Update a reseller:''' Can update a reseller.
 +
* SPP.04: '''Partly Update a reseller''': Can partly update a reseller.
 +
* SPP.05: '''Delete reseller''': Can delete a reseller (if no customers, people or services are linked to the reseller).
 +
* SPP.06: '''Create a person''': Can create a person (and make them a super user, an employee of one or more reseller(s) and an employee of one or more reseller(s)).
 +
* SPP.07: '''Retrieve all people''': Can retrieve all people.
 +
* SPP.08: '''Retrieve a person''': Can retrieve a person.
 +
* SPP.09: '''Update a person:''' Can update a person (including making them a super user, an employee of one or more reseller(s) and an employee of one or more reseller(s)).
 +
* SPP.10: '''Partly Update a person''': Can partly update a person.
 +
* SPP.11: '''Delete person''': Can delete a person (if no services are linked to the person).
 +
 
 +
=== Reseller Employee (RE) ===
 +
Definition of a Reseller Employee (RE):
 +
{| border="1" class="wikitable sortable"
 +
! API Attribute
 +
! LDAP Attribute
 +
! Example
 +
! Description
 +
|-
 +
 
 +
 
 +
| id
 +
| uid
 +
| 4000003
 +
| Unique id of the person.
 +
|-
 +
 
 +
| isActive
 +
| sstIsActive
 +
| true
 +
| Is the person active or not. Only active people are allowed to authenticate themselves.
 +
|-
 +
 
 +
| belongsToResellerId
 +
| sstBelongsToResellerUID
 +
| 4000000
 +
| Shows the reseller, that the person belongs to (only one reseller possible, N:1 relation).
 +
|-
 +
 
 +
| belongsToCustomerId
 +
| sstBelongsToCustomerUID
 +
| 4000001
 +
| Shows the customer, that the person belongs to (only one customer possible, N:1 relation).
 +
|-
 +
 
 +
| employeeOfId
 +
| sstEmployeeOfUID
 +
| 4000000
 +
| Shows the reseller (in this case), that the person is an employee of (multiple resellers possible, N:M relation).
 +
|-
 +
 
 +
| employeeOfId
 +
| sstEmployeeOfUID
 +
| 4000001
 +
| Shows the customer (in this case), that the person is an employee of (multiple customers possible, N:M relation).
 +
|-
 +
 
 +
|}
 +
 
 +
==== Reseller Employee (RE) Rights ====
 +
Sees the reseller they belong to, themselves, their own customers, people and their services. The following rights exist for a Reseller Employee (RE) and a Service Provider Person (SPP):
 +
* RE.00: '''Retrieve some resellers''': Can retrieve the reseller(s) they are an employee of.
 +
* RE.01: '''Retrieve a reseller''': Can retrieve one of the reseller(s) they are an employee of.
 +
* RE.02: '''Update a reseller:''' Can update one of the reseller(s) they are an employee of.
 +
* RE.03: '''Partly Update a reseller''': Can partly update one of the reseller(s) they are an employee of.
 +
 
 +
* RE.04: '''Create a customer''': Can create a customer.
 +
* RE.05: '''Retrieve all customers''': Can retrieve all their customers.
 +
* RE.06: '''Retrieve a customer''': Can retrieve one of their customers.
 +
* RE.07: '''Update a customer:''' Can update one of their customers.
 +
* RE.08: '''Partly Update a customer''': Can partly update one of their customers.
 +
* RE.09: '''Delete customer''': Can delete one of their customers (if no people or services are linked to the customer).
 +
 
 +
* RE.10: '''Create a person''': Can create a person (including making them an employee of their reseller(s) and of their customer(s)).
 +
* RE.11: '''Retrieve all people''': Can retrieve all their people (of their reseller(s) and their customer(s)).
 +
* RE.12: '''Retrieve a person''': Can retrieve one of their people (of their reseller(s) and their customer(s)).
 +
* RE.13: '''Update a person:''' Can update a person (including making them an employee of their reseller(s) and of their customer(s)).
 +
* RE.14: '''Partly Update a person''': Can partly update a person.
 +
* RE.15: '''Delete person''': Can delete a person (if no services are linked to the person).
 +
 
 +
=== Customer Employee (CE) ===
 +
Definition of a Customer Employee (CE):
 +
{| border="1" class="wikitable sortable"
 +
! API Attribute
 +
! LDAP Attribute
 +
! Example
 +
! Description
 +
|-
 +
 
 +
 
 +
| id
 +
| uid
 +
| 4000004
 +
| Unique id of the person.
 +
|-
 +
 
 +
| isActive
 +
| sstIsActive
 +
| true
 +
| Is the person active or not. Only active people are allowed to authenticate themselves.
 +
|-
 +
 
 +
| belongsToResellerId
 +
| sstBelongsToResellerUID
 +
| 4000000
 +
| Shows the reseller, that the person belongs to (only one reseller possible, N:1 relation).
 +
|-
 +
 
 +
| belongsToCustomerId
 +
| sstBelongsToCustomerUID
 +
| 4000002
 +
| Shows the customer, that the person belongs to (only one customer possible, N:1 relation).
 +
|-
 +
 
 +
| employeeOfId
 +
| sstEmployeeOfUID
 +
| 4000002
 +
| Shows the customer (in this case), that the person is an employee of (multiple customers possible, N:M relation).
 +
|-
 +
 
 +
|}
 +
 
 +
==== Customer Employee (CE) Rights ====
 +
Sees the customer they belong to, themselves, their own people and their services. The following rights exist for a Customer Employee (CE), a Reseller Employee (RE) and a Service Provider Person (SPP):
 +
* CE.00: '''Retrieve some customers''': Can retrieve the customer(s) they are an employee of.
 +
* CE.01: '''Retrieve a customer''': Can retrieve one of the customer(s) they are an employee of.
 +
* CE.02: '''Update a customer:''' Can update one of the customer(s) they are an employee of.
 +
* CE.03: '''Partly Update a customer''': Can partly update one of the customer(s) they are an employee of.
 +
 
 +
* CE.04: '''Create a person''': Can create a person (including making them an employee of their customer(s)).
 +
* CE.05: '''Retrieve all people''': Can retrieve all their people (of their customer(s)).
 +
* CE.06: '''Retrieve a person''': Can retrieve one of their people (of their customer(s)).
 +
* CE.07: '''Update a person:''' Can update a person (including making them an employee of their customer(s)).
 +
* CE.08: '''Partly Update a person''': Can partly update a person.
 +
* CE.09: '''Delete person''': Can delete a person (if no services are linked to the person).
 +
 
 +
=== Reseller Person (P) and Customer Person (P) ===
 +
Definition of a Reseller Person (P) and Customer Person (P):
 +
{| border="1" class="wikitable sortable"
 +
! API Attribute
 +
! LDAP Attribute
 +
! Example
 +
! Description
 +
|-
 +
 
 +
 
 +
| id
 +
| uid
 +
| 4000005
 +
| Unique id of the person.
 +
|-
 +
 
 +
| isActive
 +
| sstIsActive
 +
| true
 +
| Is the person active or not. Only active people are allowed to authenticate themselves.
 +
|-
 +
 
 +
| belongsToResellerId
 +
| sstBelongsToResellerUID
 +
| 4000000
 +
| Shows the reseller, that the person belongs to (only one reseller possible, N:1 relation).
 +
|-
 +
 
 +
| belongsToCustomerId
 +
| sstBelongsToCustomerUID
 +
| 4000002
 +
| Shows the customer, that the person belongs to (only one customer possible, N:1 relation).
 +
|-
 +
 
 +
|}
 +
 
 +
==== Reseller Person (P) and Customer Person (P) Rights ====
 +
A Reseller Person (P) and Customer Person (P) act exactly the same. They see themselves and their own services.
 +
Sees the customer they belong to, themselves, their own people and their services. The following rights exist for a Reseller Person (P), a Customer Person (P), a Customer Employee (CE), a Reseller Employee (RE) and a Service Provider Person (SPP):
 +
* P.00: '''Retrieve a person''': Can retrieve themselves.
 +
* P.01: '''Update a person:''' Can update themselves.
 +
* P.02: '''Partly Update a person''': Can partly update themselves.
 +
 
 +
= REST API documentation =
 +
=== Reserved Keywords ===
 +
We have some special reserved keywords, which can no be used by the REST API:
 +
* sort: Used for sorting.
 +
* q: Full text search.
 +
* page: Pagination.
  
== Service implementation details ==
 
 
=== Base URI ===
 
=== Base URI ===
 
The RESTful web service has to be accessible via a secure HTTP (HTTPS) base URI, for instance <code>https://api.example.com/v1</code>.
 
The RESTful web service has to be accessible via a secure HTTP (HTTPS) base URI, for instance <code>https://api.example.com/v1</code>.
Line 51: Line 289:
  
 
Furthermore the service must retrieve the authenticated users role and object ownership and respect their respective value when returning collections and elements and acting on HTTP methods. If a client tries to get, modify or delete an element for which it is not authorized, the services will response with a <code>403</code> (Forbidden) HTTP [[#Error_codes_and_responses|error code]] and includes a descriptive authorization validation message within the JSON error object.
 
Furthermore the service must retrieve the authenticated users role and object ownership and respect their respective value when returning collections and elements and acting on HTTP methods. If a client tries to get, modify or delete an element for which it is not authorized, the services will response with a <code>403</code> (Forbidden) HTTP [[#Error_codes_and_responses|error code]] and includes a descriptive authorization validation message within the JSON error object.
 +
 +
To solely authentication a person, use the resource described under [[stoney core: Authentication Resource - REST API]].
  
 
=== Data interchange format ===
 
=== Data interchange format ===
 
The service needs to accept and send all data in the JSON data interchange format via HTTP, encoded as UTF-8. Thus a client needs to accept and use the [http://tools.ietf.org/html/rfc4627 <code>application/json</code>] media type. Further media types might be supported in the future.
 
The service needs to accept and send all data in the JSON data interchange format via HTTP, encoded as UTF-8. Thus a client needs to accept and use the [http://tools.ietf.org/html/rfc4627 <code>application/json</code>] media type. Further media types might be supported in the future.
  
This results in the following required request and respons headers:
+
This results in the following required request and response headers:
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
 
|-
 
|-
Line 62: Line 302:
  
 
|-
 
|-
| <code>Accept: application/json</code>
+
|<pre>
| <code>Content-Type: application/json; charset=UTF-8</code>
+
Accept: application/json
 +
Accept-Charset: utf-8
 +
</pre>
 +
| <pre>Content-Type: application/json;charset=utf-8</pre>
  
 
|}
 
|}
  
 
* If the client sends an <code>Accept</code> header with an unsupported value (at the moment only <code>application/json</code> is supported), the service will respond with a <code>406</code> (Not Acceptable)  [[#Error codes and responses|error code]].
 
* If the client sends an <code>Accept</code> header with an unsupported value (at the moment only <code>application/json</code> is supported), the service will respond with a <code>406</code> (Not Acceptable)  [[#Error codes and responses|error code]].
 +
* If the client sends an <code>Accept-Charset</code> header with an unsupported value (at the moment only <code>utf-8</code> is supported), the service will respond with a <code>406</code> (Not Acceptable)  [[#Error codes and responses|error code]].
 
* If no <code>Accept</code> header is sent, the server will use JSON, possibly pretty-printed and annotated.
 
* If no <code>Accept</code> header is sent, the server will use JSON, possibly pretty-printed and annotated.
 +
* If no <code>Accept-Charset</code> header is sent, the server send the response with <code>utf-8</code> as charset.
 
* If the client sends a <code>Content-Type</code> other than <code>application/json</code> on a POST, PUT or PATCH request, the service will respond with a <code>415</code> (Unsupported Media Type) [[#Error codes and responses|error code]].
 
* If the client sends a <code>Content-Type</code> other than <code>application/json</code> on a POST, PUT or PATCH request, the service will respond with a <code>415</code> (Unsupported Media Type) [[#Error codes and responses|error code]].
 
* If the client sends invalid JSON, the service will response with a <code>400</code> (Bad Request) HTTP  [[#Error codes and responses|error code]] and a descriptive error message within the error object.
 
* If the client sends invalid JSON, the service will response with a <code>400</code> (Bad Request) HTTP  [[#Error codes and responses|error code]] and a descriptive error message within the error object.
 +
* When returning a <code>401</code>, a proper <code>WWW-Authenticate</code> header must be sent as well as documented in [http://tools.ietf.org/html/rfc7235#page-7 RFC7235 (or earlier HTTP 1.1/1.0 RFCs)].
 +
 +
Unless otherwise noted, the [http://tools.ietf.org/wg/httpbis/ HTTPbis RFCs] always apply.
  
 
Future extension:
 
Future extension:
Line 98: Line 346:
 
| Bad Request
 
| Bad Request
 
| The request was invalid. A descriptive error message will be sent within the response body.
 
| The request was invalid. A descriptive error message will be sent within the response body.
 +
 +
|-
 +
| 401
 +
| Unauthorized
 +
| The client has failed or not yet tried to authenticate.
 +
 +
|-
 +
| 403
 +
| Forbidden
 +
| The client is not allowed to access the requested resource.
  
 
|-
 
|-
Line 110: Line 368:
  
 
|-
 
|-
| 401
+
| 409
| Unauthorized
+
| Resource Conflict
| The client has failed or not yet tried to authenticate.
+
| State of the resource doesn't permit request.
 
+
|-
+
| 403
+
| Forbidden
+
| The client is not allowed to access the requested resource.
+
  
 
|-
 
|-
Line 128: Line 381:
 
| Unprocessable Entity
 
| Unprocessable Entity
 
| The request was well-formed but was unable to be followed due to semantic errors. For example, a client sends a invalid field value (numbers instead of characters) in a JSON object.
 
| The request was well-formed but was unable to be followed due to semantic errors. For example, a client sends a invalid field value (numbers instead of characters) in a JSON object.
 +
 +
 +
|-
 +
| 428
 +
| Precondition Required
 +
| The client did not provide a proper ETag and/or Last-modification header when updating an object via PUT, see http://tools.ietf.org/html/rfc6585#section-3
 +
 +
 +
|-
 +
| 429
 +
| Too Many Requests
 +
| There were too many requests within a given time-period, see http://tools.ietf.org/html/rfc6585#section-4
 +
  
 
|-
 
|-
Line 146: Line 412:
 
{  
 
{  
 
   "error": {  
 
   "error": {  
     "code": 123,
+
    "module": "core",
     "message": "Validation failed"
+
     "code": 422,
 +
     "message": "The request was well-formed but was unable to be followed due to semantic errors."
 
     "details" : [
 
     "details" : [
 
       {
 
       {
         "code" : 5432,
+
        "module": "core",
         "field" : "firstName",
+
         "code" : 1006,
         "message" : "First name cannot be longer than 35 characters"
+
         "field" : "isCompany",
 +
         "message" : "is invalid, true or false will be accepted"
 
       },
 
       },
 
       {
 
       {
 +
        "module": "core",
 
         "code" : 5123,
 
         "code" : 5123,
 
         "field" : "password",
 
         "field" : "password",
Line 168: Line 437:
 
Besides the above mentioned headers, the following headers are mandatory.
 
Besides the above mentioned headers, the following headers are mandatory.
  
* every answer to a GET reqest should always include <code>ETag</code> and <code>Last-Modified</code> header. This allows a proxy to cache requests and a client to revalidate already fetched data.
+
* every answer to a GET reqest should always include <code>[http://en.wikipedia.org/wiki/HTTP_ETag ETag]</code> and <code>Last-Modified</code> header. This allows a proxy to cache requests and a client to revalidate already fetched data.
 
* the service must recognize <code>ETag</code>, <code>Last-Modified</code> and <code>Cache-Control: none</code> provided by the client and act accordingly.
 
* the service must recognize <code>ETag</code>, <code>Last-Modified</code> and <code>Cache-Control: none</code> provided by the client and act accordingly.
 
* every answer to a GET request must include proper <code>Cache-Control</code> headers
 
* every answer to a GET request must include proper <code>Cache-Control</code> headers
 +
* every PUT and PATCH request to update an object must include the <code>ETag</code> provided by the <code>GET</code> request to fetch the object initially. The API must respond with an <code>428 (Precondition Required)</code> if the ETag is missing.
 +
 +
''Implementation notes'':
 +
* one could use the internal LDAP attributes <code>entryCSN</code> and/or <code>modifyTimestamp</code> to generate an ETag via a hash function. In the case of business objects where multiple LDAP objects are aggregated for one exposed object, the hash can be generated over all constituent objects
 +
* the <code>Last-Modified</code> header can be used to directly limit the search results when hitting the LDAP via the <code>modifyTimestamp</code> internal attribute. Ex. <code>modifytimestamp>=20060301000000Z</code>
  
 
=== Resources and HTTP methods ===
 
=== Resources and HTTP methods ===
Line 250: Line 524:
  
 
=== Relations ===
 
=== Relations ===
If a relation can only exist within another resource, it will be represent by its URI, for example: <code>/threads/123/messages/45</code>. This URI represents the message with ID #45 of the forum thread with ID #123.
+
Resources which stand in relation with each other are represented as URIs (scoping):
 +
* <code>/v1/resellers/4000001/customers</code> -> collection resource (all customers of reseller with uid=4000001)
 +
* <code>/v1/resellers/4000001/customers/4000002</code> -> resource (the customer with uid=4000002 of reseller with uid=4000001)
 +
 
 +
The following queries have the same effect as the URIs from above:
 +
* <code>/v1/customers?belongsToResellerUID=4000001</code> -> collection resource (all customers of reseller with uid=4000001)
 +
* <code>/v1/customers/4000002</code> -> resource (the customer with uid=4000002 of reseller with uid=4000001)
  
If a resources can stand by its own, such as users it won't be added as a sub-resource.
 
 
Relations are always returned as URIs, which the client can hit.
 
Relations are always returned as URIs, which the client can hit.
  
@TBD: Shall there be a functionality to request the embedding of elements, for relations that are commonly requested alongside the resource (to save requests)?
+
Future possibilities (sub objects) which aren't currently implemented:
 +
* If a relation can only exist within another resource, it will be represent by its URI, for example: <code>/threads/123/messages/45</code>. This URI represents the message with ID #45 of the forum thread with ID #123.
  
 
=== Filtering, sorting and searching ===
 
=== Filtering, sorting and searching ===
 
Filter, sort and search requests are added as query parameters to the resource URI.
 
Filter, sort and search requests are added as query parameters to the resource URI.
  
For '''filtering''' the objects returned by a resource URI, the name of an object's attribute is added as a query parameter with the required value.
+
For '''filtering''' the objects returned by a resource URI, the name of an object's attribute is added as a query parameter with the required value. For example, get all active user elements:
 +
GET /v1/users?status=active
  
For example, get all active user elements <code>GET /v1/users?status=active</code>
 
  
 +
For '''sorting''' the objects returned by a resource URI, the query parameter <code>sort</code> is added with the object's sort attribute(s) as the value. The following example will sort users by lastname and firstname in lexicographic ascending order:
 +
GET /v1/users?sort=lastname,firstname
  
For '''sorting''' the objects returned by a resource URI, the query parameter <code>sort</code> is added with the object's sort attribute(s) as the value.
+
The following will sort users by lastname in lexicographic descending and firstname in lexicographic ascending order:
 +
GET /v1/users?sort=-lastname,firstname
  
For example, sort all users by their last and first name <code>GET /v1/users?sort=lastname,firstname</code>
+
The following will sort users by lastname and by firstname in lexicographic descending order:
 +
GET /v1/users?sort=-lastname,-firstname
  
  
For '''full text search''' the objects returned by a resource URI, the query parameter <code>q</code> is added with the value to search for.
+
For '''full text search''' the objects returned by a resource URI, the query parameter <code>q</code> is added with the value to search for. For example
 +
GET /v1/users?q=Muell
 +
will return users named <code>Mueller</code> as well as the ones living at <code>Muellhaldenstrasse</code>.
  
For example, <code>GET /v1/users?q=Muell</code> will return users named <code>Mueller</code> as well as the ones living at <code>Muellhaldenstrasse</code>.
+
For full text search over all the available resources visit [[stoney core: Search Resource - REST API]].
  
 
=== Pagination ===
 
=== Pagination ===
Responses with multiple items will be '''limited and paginated to 30 items''' per default. Further items can be accessed by appending the <code>page</code> query parameter. The number of items to be returned can be '''raised to a maximum of 100''', by specifying the <code>per_page</code> query parameter.
+
Responses with multiple items will be '''limited and paginated to 30 items''' per default (defined on server-side). Further items can be accessed by appending the <code>page</code> query parameter. The number of items to be returned can be '''raised to a maximum of 100''' (defined on server-side), by specifying the <code>per_page</code> query parameter.
  
 
For example, to request page number 3 with 40 items per page, a client would send the following GET request:
 
For example, to request page number 3 with 40 items per page, a client would send the following GET request:
 
<pre>
 
<pre>
GET https://api.example.com/v1/users?page=3&per_page=40
+
GET /v1/people?page=3&per_page=40
 
</pre>
 
</pre>
  
The service returns [http://tools.ietf.org/html/rfc5988#section-6.2.2 official registered link relation types] (<code>next</code>, <code>prev</code>, <code>first</code>, <code>last</code>) within the HTTP [http://tools.ietf.org/html/rfc5988#section-5 Link header field] for pagination use:
+
If pagination is requested by the client and/or enforced by the server (e.g. if the number of available records is larger than the default count and no pagination requested), the service returns [http://tools.ietf.org/html/rfc5988#section-6.2.2 official registered link relation types] (<code>next</code>, <code>prev</code>, <code>first</code>, <code>last</code>) within the HTTP [http://tools.ietf.org/html/rfc5988#section-5 Link header field] for pagination use:
 
<pre>
 
<pre>
Link: <https://api.example.com/v1/users?page=1&per_page=40>; rel="first",
+
Content-Type: Content-Type: application/json; charset=UTF-8
   <https://api.example.com/v1/users?page=2&per_page=40>; rel="prev",
+
Link: <https://api.example.com/v1/resellers?page=1&per_page=30>; rel="first",
   <https://api.example.com/v1/users?page=4&per_page=40>; rel="next",
+
   <https://api.example.com/v1/resellers?page=2&per_page=30>; rel="prev",
   <https://api.example.com/v1/users?page=10&per_page=40>; rel="last"
+
   <https://api.example.com/v1/resellers?page=4&per_page=30>; rel="next",
 +
   <https://api.example.com/v1/resellers?page=10&per_page=30>; rel="last"
 +
X-Total-Count: 295
 
</pre>
 
</pre>
  
 
The client MUST use those pagination links, rather than constructing the URLs by itself.
 
The client MUST use those pagination links, rather than constructing the URLs by itself.
 +
 +
Furthermore the service sets a custom header <code>X-Total-Count</code> containing the (estimation of) total number of records.
  
 
=== Field specifications and limitations ===
 
=== Field specifications and limitations ===
 
@TBD: Do we want a possibility to specify which fields should be return on a GET request? This could either be used to save further requests to element URIs, if one fetches items from a collection URL, or to reduce the required amount of data to be transferred if one only uses a few attributes from a response. If yes, a <code>fields</code> query parameter should be added to a resource which takes a comma separated list of field names, such as <code>https://api.example.com/v1/users?fields=firstname,lastname</code>.
 
@TBD: Do we want a possibility to specify which fields should be return on a GET request? This could either be used to save further requests to element URIs, if one fetches items from a collection URL, or to reduce the required amount of data to be transferred if one only uses a few attributes from a response. If yes, a <code>fields</code> query parameter should be added to a resource which takes a comma separated list of field names, such as <code>https://api.example.com/v1/users?fields=firstname,lastname</code>.
 +
 +
Maybe. In a collection there shouldn't be as many elements returned such that this may be come a problem. On the other hand, if we return large sets, we should rather use caching properly. Making it possible to specify fields makes cachign even harder. See for example https://blog.apigee.com/detail/restful_api_design_can_your_api_give_developers_just_the_information/ --[[User:Tiziano|Tiziano]] ([[User talk:Tiziano|talk]]) 11:47, 11 December 2013 (CET)
  
 
=== Input validations ===
 
=== Input validations ===
The service validates all input it receives from a client and returns a <code>422</code> (Unprocessable Entity) HTTP [[#Error_codes_and_responses|error code with a descriptive error object]].
+
The service validates all input it receives from a client and returns a <code>422</code> (Unprocessable Entity) HTTP [[#Error_codes_and_responses|status code together with a descriptive error object]] in case of an input violation (but well-formed JSON).
  
== REST API documentation ==
+
In case a client passes invalid JSON (a JSON parser is unable to parse the JSON string), the service returns a <code>400</code> (Bad Request) HTTP [[#Error_codes_and_responses|status code together with a descriptive error object]] containing the JSON parser error message.
=== Reseller resource ===
+
Resource representing a collection of resellers or a specific reseller element.
+
  
==== Reseller creation (POST) ====
+
= Notes =
To create a new reseller the client needs to send a HTTP <code>POST</code> request on the reseller collection resource URI <code>https://api.example.com/v1/resellers</code>, including the associated reseller informations.
+
* Resource for modules => which modules are available for a given role and belong to which category
The service will generate a new reseller and responds with a HTTP status code <code>201</code> (Created) on success. The newly created reseller URI is returned within the HTTP location header, which can be used by the client to gather informations about the new reseller.
+
* Nested URLs vs. filter via get parameter => <code>/users/<UID>/products</code> vs. <code>/products/?userId=<UID></code>, (choose a better word for product?)
 
+
* Sudo mechanism, via custom HTTP header, for example <code>X-USER</code>
TBD: Table of all attributes (including optional ones)
+
 
+
===== Reseller creation (POST) example =====
+
 
+
'''Request''':
+
<pre>
+
POST /v1/resellers/ HTTP 1.1
+
HOST: api.example.com
+
</pre>
+
<pre>
+
Accept: application/json
+
Content-Type: application/json
+
</pre>
+
<source lang='javascript'>
+
{
+
  "isCompany": true,
+
  "billingAddress":
+
  {
+
    "organizationName": "Reseller Ltd.",
+
    "gender": 'm',
+
    "givenName": "Name",
+
    "surname": "Surname",
+
    "postalAddress": "Street Number",
+
    "countryCode": "CH",
+
    "postalCode": "Postal Code",
+
    "localityName": "Locality",
+
    "preferredLanguage": "en-GB",
+
    "mail": "name.surname@example.com",
+
    "telephoneNumber": "+41 00 000 00 00",
+
    "mobileTelephoneNumber": "+41 00 000 00 00",
+
    "websiteURL": "https://www.example.com/"
+
  }
+
}
+
</source>
+
 
+
'''Answer''':
+
<pre>
+
HTTP/1.1 201 Created
+
</pre>
+
<pre>
+
Content-Type: application/json; charset=UTF-8
+
Location: https://api.example.com/v1/resellers/4000001
+
</pre>
+
<source lang='javascript'>
+
{
+
  "id": 4000001,
+
  "location": "https://api.example.com/v1/resellers/4000001",
+
}
+
</source>
+
 
+
==== Reseller retrieval (GET) ====
+
 
+
===== Reseller collection retrieval (GET) example =====
+
To retrieve existing resellers, the client needs to send a HTTP <code>GET</code> request on the reseller's collection resource URI <code>https://api.example.com/v1/resellers</code>.
+
The service responds with a HTTP status code 200 (OK) on success and returns the various resellers.
+
 
+
'''Request''':
+
<pre>
+
GET /v1/resellers/ HTTP 1.1
+
HOST: api.example.com
+
</pre>
+
<pre>
+
Accept: application/json
+
</pre>
+
 
+
'''Answer''':
+
<pre>
+
HTTP/1.1 200 OK
+
</pre>
+
<pre>
+
Content-Type: application/json; charset=UTF-8
+
</pre>
+
<source lang='javascript'>
+
[
+
  {
+
    "id": 4000000,
+
    "location": "https://api.example.com/v1/resellers/4000000",
+
    "isCompany": true,
+
    "descriptiveName": "stepping stone GmbH"
+
  },
+
  {
+
    "id": 4000001,
+
    "location": "https://api.example.com/v1/resellers/4000001",
+
    "isCompany": true,
+
    "descriptiveName": "Company Name or Givenname Surname"
+
  }
+
]
+
</source>
+
 
+
===== Reseller element retrieval (GET) example =====
+
To retrieve an existing reseller and fetch the informations associated with it, the client needs to send a HTTP <code>GET</code> request on the reseller's element resource URI (such as <code>https://api.example.com//v1/resellers/4000001</code>.
+
The service responds with a HTTP status code 200 (OK) on success and returns the associated reseller informations.
+
 
+
'''Request''':
+
<pre>
+
GET /v1/resellers/4000001 HTTP 1.1
+
HOST: api.example.com
+
</pre>
+
<pre>
+
Accept: application/json
+
</pre>
+
 
+
'''Answer''':
+
<pre>
+
HTTP/1.1 200 OK
+
</pre>
+
<pre>
+
Content-Type: application/json; charset=UTF-8
+
</pre>
+
<source lang='javascript'>
+
{
+
  "id": 4000001,
+
  "isCompany": true,
+
  "billingAddress":
+
  {
+
    "organizationName": "Reseller Ltd.",
+
    "gender": 'm',
+
    "givenName": "Name",
+
    "surname": "Surname",
+
    "postalAddress": "Street Number",
+
    "countryCode": "CH",
+
    "postalCode": "Postal Code",
+
    "localityName": "Locality",
+
    "preferredLanguage": "en-GB",
+
    "mail": "name.surname@example.com",
+
    "telephoneNumber": "+41 00 000 00 00",
+
    "mobileTelephoneNumber": "+41 00 000 00 00",
+
    "websiteURL": "https://www.example.com/"
+
  }
+
  "shippingAddresses":
+
  [
+
    TBD,
+
  ]
+
}
+
</source>
+
 
+
==== Reseller update (PUT) ====
+
To updates an existing reseller, the client needs to send a HTTP <code>PUT</code> request on the reseller's element resource URI (such as, <code>https://api.example.com//v1/resellers/4000001</code>).
+
The service responds with a HTTP status code 200 (OK) on success and returns an empty body.
+
 
+
The <code>PUT</code> method requires one to sent the complete record, thus the work-flow is normally as follows:
+
# A [[#Reseller_retrieval_.28GET.29|GET]] request on the Reseller element URI will be made to fetch the whole document.
+
# Update the fields which content has changed
+
# Send a <code>PUT</code> request with all the reseller data
+
'''Request''':
+
<pre>
+
PUT /v1/resellers/4000001 HTTP 1.1
+
HOST: api.example.com
+
</pre>
+
<pre>
+
Accept: application/json
+
Content-Type: application/json
+
</pre>
+
<source lang='javascript'>
+
{
+
  "id": 4000001,
+
  "isCompany": true,
+
  "billingAddress":
+
  {
+
    "organizationName": "Reseller Ltd.",
+
    "gender": 'm',
+
    "givenName": "Name",
+
    "surname": "Surname",
+
    "postalAddress": "Street Number",
+
    "countryCode": "CH",
+
    "postalCode": "Postal Code",
+
    "localityName": "Locality",
+
    "preferredLanguage": "en-GB",
+
    "mail": "name.surname@example.com",
+
    "telephoneNumber": "+41 00 000 00 00",
+
    "mobileTelephoneNumber": "+41 00 000 00 00",
+
    "websiteURL": "https://www.example.com/"
+
  }
+
  "shippingAddresses":
+
  [
+
    TBD,
+
  ]
+
}
+
</source>
+
 
+
'''Answer''':
+
<pre>
+
HTTP/1.1 200 OK
+
</pre>
+
<pre>
+
Content-Type: application/json; charset=UTF-8
+
</pre>
+
<source lang='javascript'>
+
</source>
+
 
+
==== Reseller partly update (PATCH) ====
+
To update fields of an existing reseller, the client needs to send a HTTP <code>PATCH</code> request on the reseller's element resource URI (such as, <code>https://api.example.com//v1/resellers/4000001</code>).
+
The service responds with a HTTP status code 200 (OK) on success and returns an empty body.
+
 
+
In contrast to the [[#Reseller_update_.28PUT.29|<code>PUT</code>]] method, only the changed fields are to be included in the request.
+
 
+
'''Request''':
+
<pre>
+
PATCH /v1/resellers/4000001 HTTP 1.1
+
HOST: api.example.com
+
</pre>
+
<pre>
+
Accept: application/json
+
Content-Type: application/json
+
</pre>
+
<source lang='javascript'>
+
{
+
  "billingAddress":
+
  {
+
    "postalAddress": "New Street Number",
+
    "preferredLanguage": "de-CH"
+
  }
+
}
+
</source>
+
 
+
'''Answer''':
+
<pre>
+
HTTP/1.1 200 OK
+
</pre>
+
<pre>
+
Content-Type: application/json; charset=UTF-8
+
</pre>
+
<source lang='javascript'>
+
</source>
+
 
+
==== Reseller deletion (DELETE) ====
+
To delete an existing reseller, the client needs to send a HTTP <code>DELETE</code> request on the reseller's element resource URI (such as, <code>https://api.example.com//v1/resellers/4000001</code>). The service responds with a HTTP status code <code>200</code> (OK) on success and returns an empty body.
+
 
+
'''Request''':
+
<pre>
+
DELETE /v1/resellers/4000001 HTTP 1.1
+
HOST: api.example.com
+
</pre>
+
<pre>
+
Accept: application/json
+
</pre>
+
<source lang='javascript'>
+
</source>
+
 
+
'''Answer''':
+
<pre>
+
HTTP/1.1 200 OK
+
</pre>
+
<pre>
+
Content-Type: application/json; charset=UTF-8
+
</pre>
+
<source lang='javascript'>
+
</source>
+
  
[[Category:Development]]
+
[[Category:REST API]][[Category:stoney core]]

Latest revision as of 15:15, 7 September 2014

Overview

Entities, Roles and Relationships

Overview

Description, how everything works together.Entitlement, Access and Roles

Entities

An Entity is always a company or a person.

Service Provider

A service provider is the owner of the stoney cloud installation. One or more employees of the the service provider must be super users.

Reseller(s)

A reseller sells services of the service provider as his own. He is responsible for the administration of his customers and their services.

Customers(s)

A customer subscribes to services that the reseller sells. The customer can administrate his own people, employees and services.

People

Person

A person belonging one or more resellers and one or more customers. Normally, a person would belong to one reseller and one customer.

Employee

A person, which is employed by one or more resellers and/or one or more customers.

Super User

A super user is a person which has the right to complete the REST API functionality. The stoney cloud is delivered with one active super user. See the People (Superuser) description in the stoney core: OpenLDAP directory data organisation.

Roles

The roles are not finalized yet.

Relationships

The relationships are hierarchical:

Service Provider Person > Reseller Employee > Customer Employee > Reseller Person and Customer Person

The further down you go, the less rights a person has (this is currently independent from any existing roles, as these have not been finalized yet).

Service Provider Person (SPP)

Definition of a Service Provider Person:

API Attribute LDAP Attribute Example Description
id uid 4000002 Unique id of the person.
- sstBelongsToUID 1 Hidden (internal) attribute, that will lose it's meaning, when the roles are finalized. Makes a person a super user.
isActive sstIsActive true Is the person active or not. Only active people are allowed to authenticate themselves.
belongsToResellerId sstBelongsToResellerUID 4000000 Shows the reseller, that the person belongs to (only one reseller possible, N:1 relation).
belongsToCustomerId sstBelongsToCustomerUID 4000001 Shows the customer, that the person belongs to (only one customer possible, N:1 relation).
employeeOfId sstEmployeeOfUID 4000000 Shows the reseller (in this case), that the person is an employee of (multiple resellers possible, N:M relation).
employeeOfId sstEmployeeOfUID 4000001 Shows the customer (in this case), that the person is an employee of (multiple customers possible, N:M relation).

Service Provider Person (SPP) Rights

A Service Provider Person can just about do anything. The following rights are unique for a Service Provider Person (SPP):

  • SPP.00: Create a reseller: Can create a reseller.
  • SPP.01: Retrieve all resellers: Can retrieve all resellers.
  • SPP.02: Retrieve a reseller: Can retrieve a reseller.
  • SPP.03: Update a reseller: Can update a reseller.
  • SPP.04: Partly Update a reseller: Can partly update a reseller.
  • SPP.05: Delete reseller: Can delete a reseller (if no customers, people or services are linked to the reseller).
  • SPP.06: Create a person: Can create a person (and make them a super user, an employee of one or more reseller(s) and an employee of one or more reseller(s)).
  • SPP.07: Retrieve all people: Can retrieve all people.
  • SPP.08: Retrieve a person: Can retrieve a person.
  • SPP.09: Update a person: Can update a person (including making them a super user, an employee of one or more reseller(s) and an employee of one or more reseller(s)).
  • SPP.10: Partly Update a person: Can partly update a person.
  • SPP.11: Delete person: Can delete a person (if no services are linked to the person).

Reseller Employee (RE)

Definition of a Reseller Employee (RE):

API Attribute LDAP Attribute Example Description
id uid 4000003 Unique id of the person.
isActive sstIsActive true Is the person active or not. Only active people are allowed to authenticate themselves.
belongsToResellerId sstBelongsToResellerUID 4000000 Shows the reseller, that the person belongs to (only one reseller possible, N:1 relation).
belongsToCustomerId sstBelongsToCustomerUID 4000001 Shows the customer, that the person belongs to (only one customer possible, N:1 relation).
employeeOfId sstEmployeeOfUID 4000000 Shows the reseller (in this case), that the person is an employee of (multiple resellers possible, N:M relation).
employeeOfId sstEmployeeOfUID 4000001 Shows the customer (in this case), that the person is an employee of (multiple customers possible, N:M relation).

Reseller Employee (RE) Rights

Sees the reseller they belong to, themselves, their own customers, people and their services. The following rights exist for a Reseller Employee (RE) and a Service Provider Person (SPP):

  • RE.00: Retrieve some resellers: Can retrieve the reseller(s) they are an employee of.
  • RE.01: Retrieve a reseller: Can retrieve one of the reseller(s) they are an employee of.
  • RE.02: Update a reseller: Can update one of the reseller(s) they are an employee of.
  • RE.03: Partly Update a reseller: Can partly update one of the reseller(s) they are an employee of.
  • RE.04: Create a customer: Can create a customer.
  • RE.05: Retrieve all customers: Can retrieve all their customers.
  • RE.06: Retrieve a customer: Can retrieve one of their customers.
  • RE.07: Update a customer: Can update one of their customers.
  • RE.08: Partly Update a customer: Can partly update one of their customers.
  • RE.09: Delete customer: Can delete one of their customers (if no people or services are linked to the customer).
  • RE.10: Create a person: Can create a person (including making them an employee of their reseller(s) and of their customer(s)).
  • RE.11: Retrieve all people: Can retrieve all their people (of their reseller(s) and their customer(s)).
  • RE.12: Retrieve a person: Can retrieve one of their people (of their reseller(s) and their customer(s)).
  • RE.13: Update a person: Can update a person (including making them an employee of their reseller(s) and of their customer(s)).
  • RE.14: Partly Update a person: Can partly update a person.
  • RE.15: Delete person: Can delete a person (if no services are linked to the person).

Customer Employee (CE)

Definition of a Customer Employee (CE):

API Attribute LDAP Attribute Example Description
id uid 4000004 Unique id of the person.
isActive sstIsActive true Is the person active or not. Only active people are allowed to authenticate themselves.
belongsToResellerId sstBelongsToResellerUID 4000000 Shows the reseller, that the person belongs to (only one reseller possible, N:1 relation).
belongsToCustomerId sstBelongsToCustomerUID 4000002 Shows the customer, that the person belongs to (only one customer possible, N:1 relation).
employeeOfId sstEmployeeOfUID 4000002 Shows the customer (in this case), that the person is an employee of (multiple customers possible, N:M relation).

Customer Employee (CE) Rights

Sees the customer they belong to, themselves, their own people and their services. The following rights exist for a Customer Employee (CE), a Reseller Employee (RE) and a Service Provider Person (SPP):

  • CE.00: Retrieve some customers: Can retrieve the customer(s) they are an employee of.
  • CE.01: Retrieve a customer: Can retrieve one of the customer(s) they are an employee of.
  • CE.02: Update a customer: Can update one of the customer(s) they are an employee of.
  • CE.03: Partly Update a customer: Can partly update one of the customer(s) they are an employee of.
  • CE.04: Create a person: Can create a person (including making them an employee of their customer(s)).
  • CE.05: Retrieve all people: Can retrieve all their people (of their customer(s)).
  • CE.06: Retrieve a person: Can retrieve one of their people (of their customer(s)).
  • CE.07: Update a person: Can update a person (including making them an employee of their customer(s)).
  • CE.08: Partly Update a person: Can partly update a person.
  • CE.09: Delete person: Can delete a person (if no services are linked to the person).

Reseller Person (P) and Customer Person (P)

Definition of a Reseller Person (P) and Customer Person (P):

API Attribute LDAP Attribute Example Description
id uid 4000005 Unique id of the person.
isActive sstIsActive true Is the person active or not. Only active people are allowed to authenticate themselves.
belongsToResellerId sstBelongsToResellerUID 4000000 Shows the reseller, that the person belongs to (only one reseller possible, N:1 relation).
belongsToCustomerId sstBelongsToCustomerUID 4000002 Shows the customer, that the person belongs to (only one customer possible, N:1 relation).

Reseller Person (P) and Customer Person (P) Rights

A Reseller Person (P) and Customer Person (P) act exactly the same. They see themselves and their own services. Sees the customer they belong to, themselves, their own people and their services. The following rights exist for a Reseller Person (P), a Customer Person (P), a Customer Employee (CE), a Reseller Employee (RE) and a Service Provider Person (SPP):

  • P.00: Retrieve a person: Can retrieve themselves.
  • P.01: Update a person: Can update themselves.
  • P.02: Partly Update a person: Can partly update themselves.

REST API documentation

Reserved Keywords

We have some special reserved keywords, which can no be used by the REST API:

  • sort: Used for sorting.
  • q: Full text search.
  • page: Pagination.

Base URI

The RESTful web service has to be accessible via a secure HTTP (HTTPS) base URI, for instance https://api.example.com/v1. The definition of the base URI is up to the provider of the service. The only requirements are the use of HTTPS and the presence of the service's version information, so that further changes are possible without breaking existing clients.

Client authentication and authorization

The service needs to authenticate each client via HTTP basic authentication by a user name and a corresponding password. If a unauthenticated client tries to access the service, it will response with a 401 (Unauthorized) HTTP error code.

Furthermore the service must retrieve the authenticated users role and object ownership and respect their respective value when returning collections and elements and acting on HTTP methods. If a client tries to get, modify or delete an element for which it is not authorized, the services will response with a 403 (Forbidden) HTTP error code and includes a descriptive authorization validation message within the JSON error object.

To solely authentication a person, use the resource described under stoney core: Authentication Resource - REST API.

Data interchange format

The service needs to accept and send all data in the JSON data interchange format via HTTP, encoded as UTF-8. Thus a client needs to accept and use the application/json media type. Further media types might be supported in the future.

This results in the following required request and response headers:

Request header Response header
Accept: application/json
Accept-Charset: utf-8
Content-Type: application/json;charset=utf-8
  • If the client sends an Accept header with an unsupported value (at the moment only application/json is supported), the service will respond with a 406 (Not Acceptable) error code.
  • If the client sends an Accept-Charset header with an unsupported value (at the moment only utf-8 is supported), the service will respond with a 406 (Not Acceptable) error code.
  • If no Accept header is sent, the server will use JSON, possibly pretty-printed and annotated.
  • If no Accept-Charset header is sent, the server send the response with utf-8 as charset.
  • If the client sends a Content-Type other than application/json on a POST, PUT or PATCH request, the service will respond with a 415 (Unsupported Media Type) error code.
  • If the client sends invalid JSON, the service will response with a 400 (Bad Request) HTTP error code and a descriptive error message within the error object.
  • When returning a 401, a proper WWW-Authenticate header must be sent as well as documented in RFC7235 (or earlier HTTP 1.1/1.0 RFCs).

Unless otherwise noted, the HTTPbis RFCs always apply.

Future extension: Client may supply application/vnd.org.stoney-cloud.api+json as Content-Type to declare the requested schema/format of the data. This can then also be used to introduce additional versioning.

Error codes and responses

The service returns appropriate HTTP status codes for every request, the following table lists the commonly used codes:

HTTP status code Text Description
200 OK Success.
201 Created A new resource was successfully created.
400 Bad Request The request was invalid. A descriptive error message will be sent within the response body.
401 Unauthorized The client has failed or not yet tried to authenticate.
403 Forbidden The client is not allowed to access the requested resource.
404 Not Found The requested resource could not be found but may be available again in the future.
406 Not Acceptable The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.
409 Resource Conflict State of the resource doesn't permit request.
415 Unsupported Media Type The request entity has a media type which the server or resource does not support. For example, sending XML instead of JSON in a POST, PUT or PATCH method.
422 Unprocessable Entity The request was well-formed but was unable to be followed due to semantic errors. For example, a client sends a invalid field value (numbers instead of characters) in a JSON object.


428 Precondition Required The client did not provide a proper ETag and/or Last-modification header when updating an object via PUT, see http://tools.ietf.org/html/rfc6585#section-3


429 Too Many Requests There were too many requests within a given time-period, see http://tools.ietf.org/html/rfc6585#section-4


500 Internal Server Error An internal error succoured. A descriptive error message will be sent within the response body.
503 Service Unavailable The service is temporary unavailable, because it is overloaded or down for maintenance

Additionally the service returns a descriptive error object in case a HTTP error was returned (4xx) within the message body of the response. An error object consists of an error code and a human readable error message, with further detailed error messages if applicable.

{ 
  "error": { 
    "module": "core",
    "code": 422,
    "message": "The request was well-formed but was unable to be followed due to semantic errors."
    "details" : [
      {
        "module": "core",
        "code" : 1006,
        "field" : "isCompany",
        "message" : "is invalid, true or false will be accepted"
      },
      {
        "module": "core",
        "code" : 5123,
        "field" : "password",
        "message" : "Password cannot be blank"
      }
    ]
  }
}

Mandatory headers

Besides the above mentioned headers, the following headers are mandatory.

  • every answer to a GET reqest should always include ETag and Last-Modified header. This allows a proxy to cache requests and a client to revalidate already fetched data.
  • the service must recognize ETag, Last-Modified and Cache-Control: none provided by the client and act accordingly.
  • every answer to a GET request must include proper Cache-Control headers
  • every PUT and PATCH request to update an object must include the ETag provided by the GET request to fetch the object initially. The API must respond with an 428 (Precondition Required) if the ETag is missing.

Implementation notes:

  • one could use the internal LDAP attributes entryCSN and/or modifyTimestamp to generate an ETag via a hash function. In the case of business objects where multiple LDAP objects are aggregated for one exposed object, the hash can be generated over all constituent objects
  • the Last-Modified header can be used to directly limit the search results when hitting the LDAP via the modifyTimestamp internal attribute. Ex. modifytimestamp>=20060301000000Z

Resources and HTTP methods

Resources are always nouns, and specified in plural (such as resellers, customers, users etc.), this prevents one from dealing with irregular pluralizations such as person/people.

The manipulation of resources happens via the HTTP request methods such as GET, POST, PUT, DELETE and PATCH. The following example illustrates the concept with a fictive user resource:

HTTP request Description
GET /users Retrieves a list of users
GET /users/12345678 Retrieves a specific user with user ID 12345678
POST /users Creates a new user
PUT /users/12345678 Updates the user with user ID 12345678
PATCH /users/12345678 Partly updates the user with user ID 12345678
DELETE /users/12345678 Deletes the user with user ID 12345678
DELETE /users Deletes all users

POST

On successful creation of an element, the service must return an URI string to the newly created element.

Example:

Request:

POST /v1/users/ HTTP 1.1
HOST: api.example.com
Accept: application/json
Content-Type: application/json
{
  "usersName": "Mueller",
  "usersType": "reseller",
}

Answer:

HTTP/1.1 201 Created
Content-Type: application/json; charset=UTF-8 
Location: https://api.example.com/v1/users/67890
{
  "id": 67890,
  "location": "https://api.example.com/v1/users/67890",
}

Relations

Resources which stand in relation with each other are represented as URIs (scoping):

  • /v1/resellers/4000001/customers -> collection resource (all customers of reseller with uid=4000001)
  • /v1/resellers/4000001/customers/4000002 -> resource (the customer with uid=4000002 of reseller with uid=4000001)

The following queries have the same effect as the URIs from above:

  • /v1/customers?belongsToResellerUID=4000001 -> collection resource (all customers of reseller with uid=4000001)
  • /v1/customers/4000002 -> resource (the customer with uid=4000002 of reseller with uid=4000001)

Relations are always returned as URIs, which the client can hit.

Future possibilities (sub objects) which aren't currently implemented:

  • If a relation can only exist within another resource, it will be represent by its URI, for example: /threads/123/messages/45. This URI represents the message with ID #45 of the forum thread with ID #123.

Filtering, sorting and searching

Filter, sort and search requests are added as query parameters to the resource URI.

For filtering the objects returned by a resource URI, the name of an object's attribute is added as a query parameter with the required value. For example, get all active user elements:

GET /v1/users?status=active


For sorting the objects returned by a resource URI, the query parameter sort is added with the object's sort attribute(s) as the value. The following example will sort users by lastname and firstname in lexicographic ascending order:

GET /v1/users?sort=lastname,firstname

The following will sort users by lastname in lexicographic descending and firstname in lexicographic ascending order:

GET /v1/users?sort=-lastname,firstname

The following will sort users by lastname and by firstname in lexicographic descending order:

GET /v1/users?sort=-lastname,-firstname


For full text search the objects returned by a resource URI, the query parameter q is added with the value to search for. For example

GET /v1/users?q=Muell

will return users named Mueller as well as the ones living at Muellhaldenstrasse.

For full text search over all the available resources visit stoney core: Search Resource - REST API.

Pagination

Responses with multiple items will be limited and paginated to 30 items per default (defined on server-side). Further items can be accessed by appending the page query parameter. The number of items to be returned can be raised to a maximum of 100 (defined on server-side), by specifying the per_page query parameter.

For example, to request page number 3 with 40 items per page, a client would send the following GET request:

GET /v1/people?page=3&per_page=40

If pagination is requested by the client and/or enforced by the server (e.g. if the number of available records is larger than the default count and no pagination requested), the service returns official registered link relation types (next, prev, first, last) within the HTTP Link header field for pagination use:

Content-Type: Content-Type: application/json; charset=UTF-8
Link: <https://api.example.com/v1/resellers?page=1&per_page=30>; rel="first",
  <https://api.example.com/v1/resellers?page=2&per_page=30>; rel="prev",
  <https://api.example.com/v1/resellers?page=4&per_page=30>; rel="next",
  <https://api.example.com/v1/resellers?page=10&per_page=30>; rel="last"
X-Total-Count: 295

The client MUST use those pagination links, rather than constructing the URLs by itself.

Furthermore the service sets a custom header X-Total-Count containing the (estimation of) total number of records.

Field specifications and limitations

@TBD: Do we want a possibility to specify which fields should be return on a GET request? This could either be used to save further requests to element URIs, if one fetches items from a collection URL, or to reduce the required amount of data to be transferred if one only uses a few attributes from a response. If yes, a fields query parameter should be added to a resource which takes a comma separated list of field names, such as https://api.example.com/v1/users?fields=firstname,lastname.

Maybe. In a collection there shouldn't be as many elements returned such that this may be come a problem. On the other hand, if we return large sets, we should rather use caching properly. Making it possible to specify fields makes cachign even harder. See for example https://blog.apigee.com/detail/restful_api_design_can_your_api_give_developers_just_the_information/ --Tiziano (talk) 11:47, 11 December 2013 (CET)

Input validations

The service validates all input it receives from a client and returns a 422 (Unprocessable Entity) HTTP status code together with a descriptive error object in case of an input violation (but well-formed JSON).

In case a client passes invalid JSON (a JSON parser is unable to parse the JSON string), the service returns a 400 (Bad Request) HTTP status code together with a descriptive error object containing the JSON parser error message.

Notes

  • Resource for modules => which modules are available for a given role and belong to which category
  • Nested URLs vs. filter via get parameter => /users/<UID>/products vs. /products/?userId=<UID>, (choose a better word for product?)
  • Sudo mechanism, via custom HTTP header, for example X-USER