mirror of
https://github.com/adidas/api-guidelines.git
synced 2025-10-25 15:19:19 +00:00
Creates rest/core-principles/quality.md
Auto commit by GitBook Editor
This commit is contained in:
2
rest/execution/README.md
Normal file
2
rest/execution/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# Execution
|
||||
Performance qualities, such as security and usability, which are observable at run time.
|
||||
5
rest/execution/api-keys.md
Normal file
5
rest/execution/api-keys.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Client Application Access
|
||||
> It is imperative to provide a consistent, preferably self-service, way to obtain API access tokens and register new client applications.
|
||||
|
||||
Every API that is publicly available **MUST** be listed in the adidas API developer portal provided by the API Management platform – Mashery. A client developer **MUST** be able to register new client application using the adidas developers portal and required the application key throughout the portal.
|
||||
|
||||
26
rest/execution/asynchronous-tasks.md
Normal file
26
rest/execution/asynchronous-tasks.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Asynchronous Tasks
|
||||
If an API operation is asynchronous, but a client could track its progress, the response to such an asynchronous operation **MUST** return, in the case of success, the **202 Accepted** status code together with an `application/hal+json` representation of a new **task-tracking resource**.
|
||||
|
||||
## Task Tracking Resource
|
||||
The task-tracking resource **SHOULD** convey the information about the status of an asynchronous task.
|
||||
|
||||
Retrieval of such a resource using the HTTP GET Request Method **SHOULD** be designed as follows:
|
||||
|
||||
1. Task is Still Processing
|
||||
|
||||
Return **200 OK** and representation of the current status.
|
||||
|
||||
2. Task Successfully Completed
|
||||
|
||||
Return **303 See Other** together with [HTTP Location Header](https://tools.ietf.org/html/rfc7231#section-7.1.2) with URI or a outcome resource.
|
||||
|
||||
3. Task Failed
|
||||
|
||||
Return **200 OK** and `application/problem+json` with the problem detail information on the task has failed.
|
||||
|
||||
|
||||
## Design Note
|
||||
The asynchronous operation task-tracking resource can be either **polled** by client or the client might initially provide a **callback** to be executed when the operation finishes.
|
||||
|
||||
In the case of callback, the API and its client MUST agree on what HTTP method and request format is used for the callback invitation. If built within adidas, the "client" API is also the subject of the adidas API guidelines.
|
||||
|
||||
25
rest/execution/authentication.md
Normal file
25
rest/execution/authentication.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Authentication
|
||||
Every API exposed outside of the adidas network **MUST** be available to authenticated clients only. Every unauthenticated HTTP request to exposed API **MUST** result in the **403 – Forbidden** HTTP Status code.
|
||||
|
||||
Based on whether user authorization is required an API call can be authenticated in two ways:
|
||||
|
||||
1. OAuth2 token
|
||||
1. API key
|
||||
|
||||
# OAuth 2 Token
|
||||
Every API that requires user authentication or authorization **MUST** use OAuth 2 tokens to authenticate the client.
|
||||
|
||||
## API Key
|
||||
An API **MAY** use simple the API token instead of the OAuth 2 token if it doesn't need to authorize the user . The key **MUST** be provided in the `Adidas-API-Key` HTTP header.
|
||||
|
||||
#### Example
|
||||
|
||||
Request:
|
||||
|
||||
```
|
||||
GET /demo-approval-api/ HTTP/1.1
|
||||
Adidas-API-Key: 9kfapap6612jkfd3ja9323q
|
||||
Host: adidas.api.mashery.com
|
||||
```
|
||||
|
||||
> NOTE: See more details in the [[Demo] Approval API](http://docs.demoapprovalapi.apiary.io) example.
|
||||
136
rest/execution/batch-operations.md
Normal file
136
rest/execution/batch-operations.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# Batch Operations
|
||||
|
||||
## Processing Similar Resources
|
||||
An operation that needs to process several related resources in bulk **SHOULD** use a collection resource with the appropriate HTTP Request Method. When processing existing resource the request message body **MUST** contain the URLs of the respective resources being processed.
|
||||
|
||||
#### Example
|
||||
##### Create Multiple Orders at Once
|
||||
|
||||
```
|
||||
POST /orders
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"order": [
|
||||
{
|
||||
"itemCount": 42
|
||||
},
|
||||
{
|
||||
"itemCount": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
##### Update Multiple Orders at Once
|
||||
> NOTE: The self-link relation identifies the existing resource being edited.
|
||||
|
||||
```
|
||||
PATCH /orders
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"order": [
|
||||
{
|
||||
"_links": {
|
||||
"self": { "href": "/order/1"}
|
||||
},
|
||||
"itemCount": 42
|
||||
},
|
||||
{
|
||||
"_links": {
|
||||
"self": { "href": "/order/2"}
|
||||
},
|
||||
"itemCount": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Results of Bulk Operation
|
||||
Every bulk operation **MUST** be atomic and treated as any other operation.
|
||||
|
||||
> _The server must implement bulk requests as atomic. If the request is for creating ten addresses, the server should create all ten addresses before returning a successful response code. The server should not commit changes partially in the case of failures._
|
||||
|
||||
|
||||
## DO NOT USE "POST Tunneling."
|
||||
Every API **MUST** avoid tunneling multiple HTTP Request using one POST request. Instead, provide an application-specific resource to process the batch request.
|
||||
|
||||
|
||||
## Non-atomic Bulk Operations
|
||||
Non-atomic bulk operations are **strongly discouraged** as they bring additional burden and confusion to the client and are difficult to consume, debug, maintain and evolve over the time.
|
||||
|
||||
The suggestion is to **split** a non-atomic operation into several atomic operations. The cost of few more calls will be greatly outweighed but the cleaner design, clarity and easier maintainability.
|
||||
|
||||
However, in such an operation has to be provided such a non-atomic bulk operation **MUST** conform to the following guidelines.
|
||||
|
||||
1. Non-atomic bulk operation **MUST** return a success status code (e.g. **200 OK**) only if every and all sub-operation succeeded.
|
||||
|
||||
1. If any single one sub-operation fails the whole non-atomic bulk operation **MUST** return the respective **4xx** or **5xx** status code.
|
||||
|
||||
1. In the case of a failure the response **MUST** contain the [problem detail](https://adidas-group.gitbooks.io/api-guidelines/content/message/error-reporting.html) information about every sub-operation that has failed.
|
||||
|
||||
1. **The client MUST be aware that the operation is non-atomic and the even the operation might have failed some sub-operations were processed successfully.**
|
||||
|
||||
|
||||
#### Example
|
||||
|
||||
Non-atomic request for creating four orders:
|
||||
|
||||
```
|
||||
POST /orders
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"order": [
|
||||
{
|
||||
"itemCount": 42
|
||||
},
|
||||
{
|
||||
"itemCount": -100
|
||||
},
|
||||
{
|
||||
"itemCount": 42
|
||||
},
|
||||
{
|
||||
"itemCount": 1.3232
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
And the error response:
|
||||
|
||||
```
|
||||
HTTP/1.1 400 Bad Request
|
||||
Content-Type: application/problem+json
|
||||
|
||||
{
|
||||
"type": "https://example.net/partial_operation_failure",
|
||||
"title": "Partial Failure",
|
||||
"detail": "Some orders couldn't be created, other orders were created.",
|
||||
|
||||
"errors": [
|
||||
{
|
||||
"type": "https://example.net/invalid-params",
|
||||
"instance": "/orders/1",
|
||||
"title": "Invalid Parameter",
|
||||
"detail": "itemCount must be a positive integer",
|
||||
"status": 400
|
||||
},
|
||||
{
|
||||
"type": "https://example.net/invalid-params",
|
||||
"instance": "/orders/3",
|
||||
"title": "Invalid Parameter",
|
||||
"detail": "itemCount must be a positive integer",
|
||||
"status": 400
|
||||
}
|
||||
],
|
||||
|
||||
"processed": ...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The `processed` field should contain the result of processed sub-operations as if they were returned in a 200 OK.
|
||||
45
rest/execution/caching.md
Normal file
45
rest/execution/caching.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Caching
|
||||
Every API implementation **SHOULD** return both the cache expiry information ([`Cache-Control` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control)) and specific resource version information ([`ETag` HTTP Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag)).
|
||||
|
||||
## Cache-Control
|
||||
Every API implementation's response **SHOULD** include information about cache-ability and cache expiration of the response. For HTTP 1.1 this is achieved using the `Cache-Control` header.
|
||||
|
||||
|
||||
### Common Cache-Control Scenarios
|
||||
Two, most common scenarios for controlling the cache-ability of a response includes (1) Setting expiration and revalidation and (2) disabling the caching of a response. Refer to the [Cache-Control Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) for additional controls.
|
||||
|
||||
#### 1. Cache Expiration & Revalidation
|
||||
The common scenario to set cache expiration and revalidation policy is to use the `max-age` and `must-revalidate` directives:
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Date: Mon, 19 Aug 2017 00:00:00 CEST
|
||||
Last-Modified: Mon, 19 Aug 2017 00:00:00 CEST
|
||||
Cache-Control: max-age=3600,must-revalidate
|
||||
Content-Type: application/hal+json; charset=UTF-8
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### 2. Disabling Cache
|
||||
To disable caching completely API implementation **SHOULD** use the `no-cache` and `no-store` directives:
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Date: Mon, 19 Aug 2017 00:00:00 CEST
|
||||
Last-Modified: Mon, 19 Aug 2017 00:00:00 CEST
|
||||
Cache-Control: no-cache, no-store, must-revalidate
|
||||
Content-Type: application/hal+json; charset=UTF-8
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
## ETag
|
||||
Every API implementation's response to a [cacheable request](https://github.com/for-GET/know-your-http-well/blob/master/methods.md#cacheable) **SHOULD** include the [`ETag` HTTP Header](https://tools.ietf.org/html/rfc7232#section-2.3) to identify the specific version of the returned resource.
|
||||
|
||||
Every API client **SHOULD** use [`If-None-Match` HTTP header](https://tools.ietf.org/html/rfc7232#section-3.2) whenever it's performing a cacheable request. The value of `If-None-Match` should be the value of the `ETag` header stored from a previous request. The client **MUST** be ready to handle the **304 Not Modified** response from the server to use the legal copy.
|
||||
|
||||
#### How ETag works
|
||||
ETags are unique identifiers for a particular version of a resource found by a URL. They are used for cache validation, to check for modifications quickly.
|
||||
|
||||
A client requests a resource from the server at a particular URI. The server responds with the specific ETag value in the HTTP ETag header field. ETag and the resource will be stored locally by the client. Subsequent requests from the client are done with the If-None-Match header, which now contains the ETag value from the previous request. The server now compares the values. If they are the same, it responds with HTTP Status Code 304 Not Modified. If not, the resource is sent.
|
||||
80
rest/execution/choosing-fields-and-embedded-resoruces.md
Normal file
80
rest/execution/choosing-fields-and-embedded-resoruces.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Choosing Fields & Embedded Resources
|
||||
|
||||
|
||||
## Response Message Fields
|
||||
Every request that may result in a response with a non-trivial body **SHOULD** implement the `fields` query parameter. If provided, the value of this parameter must be a comma-separated list of top-level response message fields.
|
||||
|
||||
Upon receiving such a request, in the event of a 2xx response, the service **SHOULD** respond with a message that includes only top-level fields specified in the `fields` parameter. That includes HAL-specific fields (`_links` and `_embedded`).
|
||||
|
||||
#### Example
|
||||
Retrieve only some details of an Order resource:
|
||||
|
||||
HTTP Request
|
||||
```
|
||||
GET /order/1234?fields=_links,orderNumber,status HTTP/1.1
|
||||
Accept: application/hal+json
|
||||
```
|
||||
|
||||
HTTP Response
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/hal+json
|
||||
|
||||
{
|
||||
"_links": {
|
||||
"self": { "href": "/orders/1234" },
|
||||
"author": { "href": "/users/john" },
|
||||
"items": [ ... ]
|
||||
},
|
||||
"orderNumber": 1234,
|
||||
"status": "pending"
|
||||
}
|
||||
```
|
||||
|
||||
## Embedded Resources
|
||||
Similarly to the `fields` query parameter, every request that might result in a response containing related embedded resource representations **SHOULD** implement the `embedded` query parameter. If, provided the value of the `embedded` query parameter **MUST** be a comma-separated list of relation identifiers.
|
||||
|
||||
Upon receiving such a request, in the event of a 2xx response, the service **SHOULD** respond with a message that "embeds" (see HAL `_embedded` field) only the related resources specified in the `embedded` parameter.
|
||||
|
||||
|
||||
#### Example
|
||||
Embed only information about the Author of an Order resource. We are not interested in Items that are in this order.
|
||||
|
||||
HTTP Request
|
||||
|
||||
```
|
||||
GET /order/1234?embed=author HTTP/1.1
|
||||
Accept: application/hal+json
|
||||
```
|
||||
|
||||
HTTP Response
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/hal+json
|
||||
|
||||
{
|
||||
"_links": {
|
||||
"self": { "href": "/orders/1234" },
|
||||
"author": { "href": "/users/john" },
|
||||
"items": [ ... ]
|
||||
},
|
||||
"orderNumber": 1234,
|
||||
"itemCount": 42,
|
||||
"status": "pending",
|
||||
"_embedded": {
|
||||
"author": {
|
||||
"_links": { "self": "/users/john" },
|
||||
"name": "John Appleseed",
|
||||
"email": "john@apple.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Reasonable Defaults
|
||||
When `fields` and `embedded` parameters are not provided or not implemented the server **SHOULD** return reasonable default field and/or embedded resources. The defaults **MUST** always contain the `_links` field, where available.
|
||||
|
||||
## Resource Variants
|
||||
The facility of `fields` and `embedded` parameters doesn't impose any restriction of creating new resource variants.
|
||||
|
||||
It is OK to create a new resource just as a compound resource only to provide selected fields or blend-embed some existing resources together.
|
||||
37
rest/execution/localization.md
Normal file
37
rest/execution/localization.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Localization
|
||||
|
||||
## Language Variants
|
||||
If a resource has multiple language variants and the difference between variants is only in the language of human-readable fields, then the [`Accept-Language`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language) request HTTP header **SHOULD** be used to select the desired language variant.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
GET /article HTTP/1.1
|
||||
Accept-Language: en,en-US,fr;q=0.6
|
||||
```
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/hal+json;charset=UTF-8
|
||||
Content-Language: en
|
||||
Vary: Accept-Language
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
## Language or Country-specific Data Structure
|
||||
If the difference between language or country specific variants of a resource is bigger than just in the content of human readable strings, for example, the data structure of the resource representation is different, then a query parameter **SHOULD** be used to communicate the requested variant.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
GET /article?market=en_US HTTP/1.1
|
||||
```
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/hal+json;charset=UTF-8
|
||||
Content-Language: en
|
||||
Vary: Accept-Language
|
||||
|
||||
...
|
||||
```
|
||||
27
rest/execution/pagination.md
Normal file
27
rest/execution/pagination.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Pagination
|
||||
A collection resource **SHOULD** provide the [`first`, `last`, `next` and `prev` link](https://tools.ietf.org/html/rfc5988#section-6.2.2)s for navigation within the collection.
|
||||
|
||||
#### Example
|
||||
The Collection of Orders using the collection navigation link and `offset` and `limit` query parameters:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"_links": {
|
||||
"self": { "href": "/orders?offset=100&limit=10" },
|
||||
"prev": { "href": "/orders?offset=90&limit=10" },
|
||||
"next": { "href": "/orders?offset=110&limit=10" },
|
||||
"first": { "href": "/orders?limit=10" },
|
||||
"last": { "href": "/orders?offset=900&limit=10" }
|
||||
},
|
||||
"total_count": 910,
|
||||
"_embedded": {
|
||||
"order": [
|
||||
{ ... },
|
||||
{ ... },
|
||||
|
||||
...
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
17
rest/execution/query-requests-with-large-inputs.md
Normal file
17
rest/execution/query-requests-with-large-inputs.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Query Requests with Large Inputs
|
||||
While HTTP doesn't impose any limit on the length of a URI, some implementation of server or client might have difficulties handling long URIs (usually URIs with many or large query parameters).
|
||||
|
||||
Every endpoint with such a URI **MUST** use the HTTP **POST** Request Method and send the query string in HTTP Request Message body using the `application/x-www-form-urlencoded` media type.
|
||||
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
POST /orders HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
search=attributes&color=white&size=56&...
|
||||
```
|
||||
|
||||
|
||||
> _NOTE: Since this operation is safe and idempotent, using the POST method violates the HTTP protocol semantics and results in loss of cache-ability._
|
||||
63
rest/execution/rate-limiting.md
Normal file
63
rest/execution/rate-limiting.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Rate Limiting
|
||||
The API rate limiting is provided by the selected adidas API management platform – Mashery.
|
||||
|
||||
Rate limit information is provided in the for of HTTP headers. There are two types of rate limits: **Quota** and **Throttle**. The quota is a limit enforced per a longer period (typically a day). The throttle is the limit of calls per second.
|
||||
|
||||
## Quota Limit
|
||||
The limit on the number of calls per a period (day). The default quota limit is 5000 calls per day.
|
||||
|
||||
#### Example
|
||||
Example response to a request over the quota limit:
|
||||
|
||||
```
|
||||
HTTP/1.1 403 Forbidden
|
||||
Content-Type: application/problem+json
|
||||
|
||||
X-Error-Detail-Header: Account Over Rate Limit
|
||||
X-Mashery-Error-Code: ERR_403_DEVELOPER_OVER_RATE
|
||||
|
||||
{
|
||||
"title": "Rate Limit Exceeded",
|
||||
"detail": "Account Over Rate Limit"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Throttle Limit
|
||||
The limit on the number of calls per second. The default throttle limit is two calls per second.
|
||||
|
||||
#### Example
|
||||
Example response to a request over the throttle limit:
|
||||
|
||||
```
|
||||
HTTP/1.1 403 Forbidden
|
||||
Content-Type: application/problem+json
|
||||
|
||||
Retry-After: 1
|
||||
X-Error-Detail-Header: Account Over Queries Per Second Limit
|
||||
X-Mashery-Error-Code: ERR_403_DEVELOPER_OVER_QPS
|
||||
|
||||
{
|
||||
"title": "Quota Limit Exceeded",
|
||||
"detail": "Account Over Queries Per Second Limit"
|
||||
}
|
||||
```
|
||||
|
||||
> NOTE: The `Retry-After` gives a hint how long before the same request should be repeated (in seconds).
|
||||
|
||||
|
||||
## Detail Information
|
||||
By default, the headers do not contain details about the current usage and quotas. The default can be changed in the API management.
|
||||
|
||||
#### Example
|
||||
A successful response with the details about throttle (`X-Plan-QPS`) and quota (`X-Plan-Quota`) rate limits:
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
X-Plan-QPS-Allotted: 10
|
||||
X-Plan-QPS-Current: 1
|
||||
X-Plan-Quota-Allotted: 1000
|
||||
X-Plan-Quota-Current: 2
|
||||
X-Plan-Quota-Reset: Tuesday, June 6, 2017 12:00:00 AM GMT
|
||||
```
|
||||
61
rest/execution/search-requests.md
Normal file
61
rest/execution/search-requests.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Search Requests
|
||||
A search (filter) operation on a collection resource **SHOULD** be defined as safe, idempotent and cacheable, therefore using the [GET HTTP request method](https://adidas-group.gitbooks.io/api-guidelines/content/protocol/use-appropriate-methods.html).
|
||||
|
||||
Every search parameter **SHOULD** be provided in the form of a query parameter. In the case of search parameters being mutually exclusive or require the presence of another parameter, the explanation **MUST** be part of operation's description.
|
||||
|
||||
|
||||
#### Example
|
||||
A collection of orders can be filtered by either article id it includes or by article manufacturer id. The two parameters are mutually exclusive and cannot be used together. The API description for such a design should look as follows:
|
||||
|
||||
```yaml
|
||||
paths:
|
||||
/orders:
|
||||
x-summary: Collection of Orders
|
||||
|
||||
get:
|
||||
summary: Retrieve or Search in the Collection of Orders
|
||||
description: |
|
||||
|
||||
This operation allows to retrieve a filtered list of orders based on multiple criteria:
|
||||
|
||||
1. **Filter Orders by Article Id**
|
||||
2. **Filter Orders by Manufacturer Id**
|
||||
|
||||
parameters:
|
||||
- name: article_id
|
||||
in: query
|
||||
description: |
|
||||
Article Id. Denotes the id of an article that must be in the order.
|
||||
|
||||
**Mutually exclusive** with `manufacturer_id`.
|
||||
|
||||
required: false
|
||||
type: string
|
||||
x-example: article_id_1
|
||||
|
||||
- name: manufacturer_id
|
||||
in: query
|
||||
description: |
|
||||
Manufacturer Id. Denotes an id of a manufacturer of an article that must be in the order.
|
||||
|
||||
**Mutually exclusive** with `article_id`.
|
||||
|
||||
required: false
|
||||
type: string
|
||||
x-example: manufacturer_id_1
|
||||
```
|
||||
|
||||
## Alternative Design
|
||||
When it would be beneficial (e.g. one of the filter queries is more common than another one) a separate resource for the particular query **SHOULD** be provided. In such a case, the pivotal search parameter **MAY** be in the form of a path variable.
|
||||
|
||||
#### Example
|
||||
Building on top of the example mentioned above, we will provide the filtering of orders by article id as a separate resource.
|
||||
|
||||
```yaml
|
||||
paths:
|
||||
/articles/{article_id}/orders:
|
||||
x-summary: Collection of Orders for given Article
|
||||
|
||||
get:
|
||||
summary: Retrieve the collection of Orders that contain given article.
|
||||
```
|
||||
0
rest/execution/security.md
Normal file
0
rest/execution/security.md
Normal file
3
rest/execution/testing-enviroments.md
Normal file
3
rest/execution/testing-enviroments.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Testing Environments
|
||||
Every API implementation **SHOULD** aim to provide a consistent, seamless way to create **test accounts** and access **test fixtures** to facilitate the development of API client applications.
|
||||
|
||||
Reference in New Issue
Block a user