From df869bd7f8f84bf5246050538d5124ae1e3da4af Mon Sep 17 00:00:00 2001 From: "e.baranov" Date: Thu, 4 May 2017 16:21:08 +0300 Subject: [PATCH 1/7] add common data description --- rfcs/common-data.md | 159 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 rfcs/common-data.md diff --git a/rfcs/common-data.md b/rfcs/common-data.md new file mode 100644 index 0000000..e5fa986 --- /dev/null +++ b/rfcs/common-data.md @@ -0,0 +1,159 @@ +--- +RFC: XXXX +Author: Egor Baranov +Status: Draft +Created: 2017-05-03 +Last Modified: 2017-05-03 +--- + +# API Blueprint RFC XXXX: Common Data + +## Table of Contents + +- [Abstract](#abstract) +- [Motivation](#motivation) +- [Rationale](#rationale) +- [Backwards Compatibility](#backwards-compatibility) + +## Abstract + +This RFC proposes an ability to add common responses and to multiple actions. + +## Motivation + +When you describe big API several actions can have identical responses. +For example you have 10 actions allowed for authorized user only, so each +of such actions can respond with 401. That is why you have to write same code +10 times. If you want to change this code you have to change it in 10 places. +This problem can be cause of misspellings and logical errors. +So ability to add common responses in one place for multiple actions is required. + +## Rationale + +Common responses can be defined in an API Blueprint under an "Common Data" header. +Inside this header one or several Response sections are allowed. +Here is an example: + +```apib +# Group Authorized resources + +# Common Data + ++ Response 401 + +## Posts [GET /posts] + ++ Response 200 + +## Comments [GET /comments] + ++ Response 200 +``` + +This code equals to: + +```apib +# Group Authorized resources + +## Posts [GET /posts] + ++ Response 200 + ++ Response 401 + +## Comments [GET /comments] + ++ Response 200 + ++ Response 401 +``` + +It is possible to have several "Common Data" sections: + +```apib +# Group Authorized resources + +# Common Data + ++ Response 401 + +# Common Data + ++ Response 500 + +## Posts [GET /posts] + ++ Response 200 + +## Comments [GET /comments] + ++ Response 200 +``` + +This code equals to: + +```apib +# Group Authorized resources + +## Posts [GET /posts] + ++ Response 200 + ++ Response 401 + ++ Response 500 + +## Comments [GET /comments] ++ Response 200 + ++ Response 401 + ++ Response 500 +``` + +Common Data section influences to current actions group and all internal actions +groups, but not neighbors: + +```apib +# Group Authorized resources + +# Common Data + ++ Response 401 + +## Posts [GET /posts] + ++ Response 200 + +# Group Authorization + +## Auth [POST /auth] + ++ Response 200 +``` + +This code equals to: + +```apib +# Group Authorized resources + +## Posts [GET /posts] + ++ Response 200 + ++ Response 401 + +# Group Authorization + +## Auth [POST /auth] + ++ Response 200 +``` + +`/auth` action has no 401 response, because it is located in separate group. + +## Backwards Compatibility + +If anyone use "Common Data" header in the documentation after this RFC it will +treat as special section instead of regular markdown header. If there is no +such section in the documention there will be no backward compatibility problems. From 8df44bf06ac09d07f41e752846e8f020d8509527 Mon Sep 17 00:00:00 2001 From: "e.baranov" Date: Thu, 4 May 2017 18:50:50 +0300 Subject: [PATCH 2/7] fix headers levels --- rfcs/common-data.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rfcs/common-data.md b/rfcs/common-data.md index e5fa986..eb41790 100644 --- a/rfcs/common-data.md +++ b/rfcs/common-data.md @@ -37,7 +37,7 @@ Here is an example: ```apib # Group Authorized resources -# Common Data +## Common Data + Response 401 @@ -73,11 +73,11 @@ It is possible to have several "Common Data" sections: ```apib # Group Authorized resources -# Common Data +## Common Data + Response 401 -# Common Data +## Common Data + Response 500 @@ -117,7 +117,7 @@ groups, but not neighbors: ```apib # Group Authorized resources -# Common Data +## Common Data + Response 401 From 6816f8a7cebc10e9d909636106dca1f1203f4137 Mon Sep 17 00:00:00 2001 From: "e.baranov" Date: Fri, 5 May 2017 19:30:07 +0300 Subject: [PATCH 3/7] add variant with action prototypes --- rfcs/common-data.md | 101 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 7 deletions(-) diff --git a/rfcs/common-data.md b/rfcs/common-data.md index eb41790..a0d30b9 100644 --- a/rfcs/common-data.md +++ b/rfcs/common-data.md @@ -23,13 +23,19 @@ This RFC proposes an ability to add common responses and to multiple actions. When you describe big API several actions can have identical responses. For example you have 10 actions allowed for authorized user only, so each -of such actions can respond with 401. That is why you have to write same code -10 times. If you want to change this code you have to change it in 10 places. -This problem can be cause of misspellings and logical errors. -So ability to add common responses in one place for multiple actions is required. +of such actions can respond with 401. If you have some complex response you can +create named type and use it 10 times but even in this situation you have to +write same code 10 times. This problem connected not only authorization but +with other tasks. If you have common error in several actions you will have +exatly the same situation. If you want to change this code you have to change it +in 10 places. This problem can be cause of misspellings and logical errors. +So ability to add common responses in one place for multiple actions is +required. ## Rationale +### Common Data + Common responses can be defined in an API Blueprint under an "Common Data" header. Inside this header one or several Response sections are allowed. Here is an example: @@ -152,8 +158,89 @@ This code equals to: `/auth` action has no 401 response, because it is located in separate group. +### Action prototypes + +Another variant of solving problem is using actions inheritance. In this +situation we creates base action and all actions of group inherites its +responses: + +```apib +# Group Authorized resources + +# Prototype + ++ Response 401 + +## Posts [GET /posts] + ++ Response 200 +``` + +In this example we have created unnamed prototype for actions group Authorized +resources. In this groups and all its subgroups all actions will inherit +responses from the prototype. + +Code above equals to: + +```apib +# Group Authorized resources + +## Posts [GET /posts] + ++ Response 200 + ++ Response 401 +``` + +We can set several prototypes for one group: + +```apib +# Group Authorized resources + +# Prototype + ++ Response 401 + +## Prototype + ++ Response 500 + +## Posts [GET /posts] + ++ Response 200 +``` + +We can created named prototype. Advantage of this variant is an ability to +disable prototype for some actions group: + +```apib +## Prototype Authorized + ++ Response 401 + +After previous line all actions will have 401 response + +# Group Authorized resources + +## Posts [GET /posts] + ++ Response 200 + +# Group Authorization + +## Unuse Prototype Authorized + +In this section there will be no 401 response because we have diabled prototype +Authorized + +## Auth [POST /auth] + ++ Response 200 +``` + ## Backwards Compatibility -If anyone use "Common Data" header in the documentation after this RFC it will -treat as special section instead of regular markdown header. If there is no -such section in the documention there will be no backward compatibility problems. +If anyone use "Common Data" or "Prototype" headers in the documentation after +this RFC it will treat as special section instead of regular markdown header. +If there is no such section in the documention there will be no backward +compatibility problems. From c7cd3b87b6e4a8c2e232522819e38555bec8d080 Mon Sep 17 00:00:00 2001 From: "e.baranov" Date: Fri, 5 May 2017 19:40:56 +0300 Subject: [PATCH 4/7] add info about disabling prototype for one action --- rfcs/common-data.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/rfcs/common-data.md b/rfcs/common-data.md index a0d30b9..d4be149 100644 --- a/rfcs/common-data.md +++ b/rfcs/common-data.md @@ -238,6 +238,32 @@ Authorized + Response 200 ``` +It is possible to disable prototype for one particular action: + +```apib +## Prototype Authorized + ++ Response 401 + +After previous line all actions will have 401 response + +# Group Authorized resources + +## Posts [GET /posts] + ++ Response 200 + +# Group Authorization + +## Auth [POST /auth] + +Disable prototype Authorized only for this action + ++ Unuse Prototype Authorized + ++ Response 200 +``` + ## Backwards Compatibility If anyone use "Common Data" or "Prototype" headers in the documentation after From 719bab767ef84e810435d318e3c87ed60226d32d Mon Sep 17 00:00:00 2001 From: "e.baranov" Date: Thu, 13 Jul 2017 19:59:38 +0300 Subject: [PATCH 5/7] rewrite rfc using MSON inheritance syntax --- rfcs/common-data.md | 272 ------------------------------------ rfcs/resource-prototypes.md | 237 +++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+), 272 deletions(-) delete mode 100644 rfcs/common-data.md create mode 100644 rfcs/resource-prototypes.md diff --git a/rfcs/common-data.md b/rfcs/common-data.md deleted file mode 100644 index d4be149..0000000 --- a/rfcs/common-data.md +++ /dev/null @@ -1,272 +0,0 @@ ---- -RFC: XXXX -Author: Egor Baranov -Status: Draft -Created: 2017-05-03 -Last Modified: 2017-05-03 ---- - -# API Blueprint RFC XXXX: Common Data - -## Table of Contents - -- [Abstract](#abstract) -- [Motivation](#motivation) -- [Rationale](#rationale) -- [Backwards Compatibility](#backwards-compatibility) - -## Abstract - -This RFC proposes an ability to add common responses and to multiple actions. - -## Motivation - -When you describe big API several actions can have identical responses. -For example you have 10 actions allowed for authorized user only, so each -of such actions can respond with 401. If you have some complex response you can -create named type and use it 10 times but even in this situation you have to -write same code 10 times. This problem connected not only authorization but -with other tasks. If you have common error in several actions you will have -exatly the same situation. If you want to change this code you have to change it -in 10 places. This problem can be cause of misspellings and logical errors. -So ability to add common responses in one place for multiple actions is -required. - -## Rationale - -### Common Data - -Common responses can be defined in an API Blueprint under an "Common Data" header. -Inside this header one or several Response sections are allowed. -Here is an example: - -```apib -# Group Authorized resources - -## Common Data - -+ Response 401 - -## Posts [GET /posts] - -+ Response 200 - -## Comments [GET /comments] - -+ Response 200 -``` - -This code equals to: - -```apib -# Group Authorized resources - -## Posts [GET /posts] - -+ Response 200 - -+ Response 401 - -## Comments [GET /comments] - -+ Response 200 - -+ Response 401 -``` - -It is possible to have several "Common Data" sections: - -```apib -# Group Authorized resources - -## Common Data - -+ Response 401 - -## Common Data - -+ Response 500 - -## Posts [GET /posts] - -+ Response 200 - -## Comments [GET /comments] - -+ Response 200 -``` - -This code equals to: - -```apib -# Group Authorized resources - -## Posts [GET /posts] - -+ Response 200 - -+ Response 401 - -+ Response 500 - -## Comments [GET /comments] -+ Response 200 - -+ Response 401 - -+ Response 500 -``` - -Common Data section influences to current actions group and all internal actions -groups, but not neighbors: - -```apib -# Group Authorized resources - -## Common Data - -+ Response 401 - -## Posts [GET /posts] - -+ Response 200 - -# Group Authorization - -## Auth [POST /auth] - -+ Response 200 -``` - -This code equals to: - -```apib -# Group Authorized resources - -## Posts [GET /posts] - -+ Response 200 - -+ Response 401 - -# Group Authorization - -## Auth [POST /auth] - -+ Response 200 -``` - -`/auth` action has no 401 response, because it is located in separate group. - -### Action prototypes - -Another variant of solving problem is using actions inheritance. In this -situation we creates base action and all actions of group inherites its -responses: - -```apib -# Group Authorized resources - -# Prototype - -+ Response 401 - -## Posts [GET /posts] - -+ Response 200 -``` - -In this example we have created unnamed prototype for actions group Authorized -resources. In this groups and all its subgroups all actions will inherit -responses from the prototype. - -Code above equals to: - -```apib -# Group Authorized resources - -## Posts [GET /posts] - -+ Response 200 - -+ Response 401 -``` - -We can set several prototypes for one group: - -```apib -# Group Authorized resources - -# Prototype - -+ Response 401 - -## Prototype - -+ Response 500 - -## Posts [GET /posts] - -+ Response 200 -``` - -We can created named prototype. Advantage of this variant is an ability to -disable prototype for some actions group: - -```apib -## Prototype Authorized - -+ Response 401 - -After previous line all actions will have 401 response - -# Group Authorized resources - -## Posts [GET /posts] - -+ Response 200 - -# Group Authorization - -## Unuse Prototype Authorized - -In this section there will be no 401 response because we have diabled prototype -Authorized - -## Auth [POST /auth] - -+ Response 200 -``` - -It is possible to disable prototype for one particular action: - -```apib -## Prototype Authorized - -+ Response 401 - -After previous line all actions will have 401 response - -# Group Authorized resources - -## Posts [GET /posts] - -+ Response 200 - -# Group Authorization - -## Auth [POST /auth] - -Disable prototype Authorized only for this action - -+ Unuse Prototype Authorized - -+ Response 200 -``` - -## Backwards Compatibility - -If anyone use "Common Data" or "Prototype" headers in the documentation after -this RFC it will treat as special section instead of regular markdown header. -If there is no such section in the documention there will be no backward -compatibility problems. diff --git a/rfcs/resource-prototypes.md b/rfcs/resource-prototypes.md new file mode 100644 index 0000000..edf2019 --- /dev/null +++ b/rfcs/resource-prototypes.md @@ -0,0 +1,237 @@ +--- +RFC: XXXX +Author: Egor Baranov +Status: Draft +Created: 2017-05-03 +Last Modified: 2017-05-03 +--- + +# API Blueprint RFC XXXX: Resource prototypes + +## Table of Contents + +- [Abstract](#abstract) +- [Motivation](#motivation) +- [Rationale](#rationale) +- [Backwards Compatibility](#backwards-compatibility) + +## Abstract + +This RFC proposes an ability to craete resource prototypes and use it to reduce +code duplication. + +## Motivation + +When you describe big API several resources can have identical responses. +Good examples of such problem are: + +* request format checks - when several requests can return common error like + `WrongFormat` +* limit rate - when API checks how many requests have user done during some + period of time and any request can return error like `LimitExceeded` +* authorization checking - some part of API can require authorization so several + actions can return HTTP 403 forbidden +* deferred actions - when API request don't perform its job synchronously but + put them into queue and return an identifier back to user; such actions will + have same response format + +All these cases leads to code duplication: you have to write the same response +again and again. This problem can be cause of misspellings and logical errors. +So ability to add common responses and headers in one place for multiple actions +is required. + +## Rationale + +### Resource prototypes + +One of the possible solutions to reduce duplication is creating resource +prototype and setting it to +[resource](https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md#resource-section) +or +[resource group](https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md#def-resourcegroup-section) + +We have complicated API with several duplicate responses: + +```apib +# Data Structures + +## Post + ++ title: `test` (string, required) ++ body: `hello world` (string, required) + +## User + ++ name: `John Smith` (string, required) ++ email: `user@example.com` (string, required) + +## Posts [/posts] + +### List posts [GET] + ++ Response 200 + + Attributes (array[Post], required, fixed-type) + ++ Response 429 (application/json) + + Attributes + + status: tooManyRequests (string, required, fixed) + + waitFor: `10` (number, required) - wait before next request + ++ Response 400 (application/json) + + Attributes + + status: badRequest (string, required, fixed) + + errors (array[string], required, fixed-type) - list of errors in request + +### Show post [GET] + ++ Response 200 + + Attributes (Post, required) + ++ Response 429 (application/json) + + Attributes + + status: tooManyRequests (string, required, fixed) + + waitFor: `10` (number, required) - wait before next request + ++ Response 400 (application/json) + + Attributes + + status: badRequest (string, required, fixed) + + errors (array[string], required, fixed-type) - list of errors in request + +## Users [/users] + +### List users [GET] + ++ Response 200 + + Attributes (array[User], required, fixed-type) + ++ Response 429 (application/json) + + Attributes + + status: tooManyRequests (string, required, fixed) + + waitFor: `10` (number, required) - wait before next request + ++ Response 400 (application/json) + + Attributes + + status: badRequest (string, required, fixed) + + errors (array[string], required, fixed-type) - list of errors in request + ++ Response 403 (application/json) + + Attributes + + status: forbidden (string, required, fixed) + +### Create user [POST] + ++ Response 200 + + Attributes (User, required) + ++ Response 429 (application/json) + + Attributes + + status: tooManyRequests (string, required, fixed) + + waitFor: `10` (number, required) - wait before next request + ++ Response 400 (application/json) + + Attributes + + status: badRequest (string, required, fixed) + + errors (array[string], required, fixed-type) - list of errors in request + ++ Response 403 (application/json) + + Attributes + + status: forbidden (string, required, fixed) +``` + +As you can see responses 429, 400 and 403 are duplicated several times. It is +possible to extract response description into +[Data Structures](https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md#def-data-structures) +section and it will help to reduce duplication but you have to write something +like + +```apib ++ Response 429 (application/json) + + Attributes (TooManyRequestsError) +``` + +for every resource. It is possible to remove duplication completely using new +proposed syntax: + +```apib +# Data Structures + +## Post + ++ title: `test` (string, required) ++ body: `hello world` (string, required) + +## User + ++ name: `John Smith` (string, required) ++ email: `user@example.com` (string, required) + +## Authorized + ++ Response 403 (application/json) + + Attributes + + status: forbidden (string, required, fixed) + +## Common Resource + ++ Response 429 (application/json) + + Attributes + + status: tooManyRequests (string, required, fixed) + + waitFor: `10` (number, required) - wait before next request + ++ Response 400 (application/json) + + Attributes + + status: badRequest (string, required, fixed) + + errors (array[string], required, fixed-type) - list of errors in request + +# Group Blog (Common Resource) + +## Posts [/posts] + +### List posts [GET] + ++ Response 200 + + Attributes (array[Post], required, fixed-type) + +### Show post [GET] + ++ Response 200 + + Attributes (Post, required) + +## Users (Authorized) [/users] + +### List users [GET] + ++ Response 200 + + Attributes (array[User], required, fixed-type) + +### Create user [POST] + ++ Response 200 + + Attributes (User, required) +``` + +In `Data Structures` section two prototypes were defined: `Common Resource` +and `Authorized`. Both of them have some responses. To assign prototype for all +resources of resource group you need to use such syntax: `Section name +(Prototype Name)`. If section has nested section then all resources of nested +section will have responses defined in prototype. `Users` resource group will +have responces from `Common Resource` prototype (because it's a prototype of its +parent group) and from `Authorized` prototype. + +It is possible to assign several prototypes for one resource or resource group: + +```apib +# Group Blog (Common Resource, Authorized) +``` + +It is possible when one prototype has its own prototype: + +```apib +# Data Structures + +## Common Resource (Rate, Format) +``` + +## Backwards Compatibility + +There will be no backward compatibility problems. From 1874b8510e41214496ab97520b27ab0bd7a606d3 Mon Sep 17 00:00:00 2001 From: "e.baranov" Date: Tue, 18 Jul 2017 12:35:47 +0300 Subject: [PATCH 6/7] add base type for resource prototypes --- rfcs/resource-prototypes.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rfcs/resource-prototypes.md b/rfcs/resource-prototypes.md index edf2019..1b4ce46 100644 --- a/rfcs/resource-prototypes.md +++ b/rfcs/resource-prototypes.md @@ -165,13 +165,13 @@ proposed syntax: + name: `John Smith` (string, required) + email: `user@example.com` (string, required) -## Authorized +## Authorized (Resource Prototype) + Response 403 (application/json) + Attributes + status: forbidden (string, required, fixed) -## Common Resource +## Common Resource (Resource Prototype) + Response 429 (application/json) + Attributes @@ -211,12 +211,12 @@ proposed syntax: ``` In `Data Structures` section two prototypes were defined: `Common Resource` -and `Authorized`. Both of them have some responses. To assign prototype for all -resources of resource group you need to use such syntax: `Section name -(Prototype Name)`. If section has nested section then all resources of nested -section will have responses defined in prototype. `Users` resource group will -have responces from `Common Resource` prototype (because it's a prototype of its -parent group) and from `Authorized` prototype. +and `Authorized` with base type `Resource Prototype`. Both of them have some +responses. To assign prototype for all resources of resource group you need to +use such syntax: `Section name (Prototype Name)`. If section has nested section +then all resources of nested section will have responses defined in prototype. +`Users` resource group will have responces from `Common Resource` prototype +(because it's a prototype of its parent group) and from `Authorized` prototype. It is possible to assign several prototypes for one resource or resource group: From 215e716c0976c3b0b20170696d203b92ce669555 Mon Sep 17 00:00:00 2001 From: "e.baranov" Date: Mon, 21 Aug 2017 13:23:49 +0300 Subject: [PATCH 7/7] make separate section for resource prototypes --- rfcs/resource-prototypes.md | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/rfcs/resource-prototypes.md b/rfcs/resource-prototypes.md index 1b4ce46..caea0fc 100644 --- a/rfcs/resource-prototypes.md +++ b/rfcs/resource-prototypes.md @@ -165,13 +165,15 @@ proposed syntax: + name: `John Smith` (string, required) + email: `user@example.com` (string, required) -## Authorized (Resource Prototype) +# Resource Prototypes + +## Authorized + Response 403 (application/json) + Attributes + status: forbidden (string, required, fixed) -## Common Resource (Resource Prototype) +## Common Resource + Response 429 (application/json) + Attributes @@ -210,13 +212,20 @@ proposed syntax: + Attributes (User, required) ``` -In `Data Structures` section two prototypes were defined: `Common Resource` -and `Authorized` with base type `Resource Prototype`. Both of them have some -responses. To assign prototype for all resources of resource group you need to -use such syntax: `Section name (Prototype Name)`. If section has nested section -then all resources of nested section will have responses defined in prototype. -`Users` resource group will have responces from `Common Resource` prototype -(because it's a prototype of its parent group) and from `Authorized` prototype. +New section `Resource Prototypes` proposed. It is simmilar to +[Data Structures](https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md#def-data-structures) +but contains resource prototypes instead of MSON data structures. It's important +to create new separate section for resource prototypes, because data format of +MSON data structures and resource prototypes differes a lot so separate section +will help users to read API description easier. + +In `Resource Prototypes` section two prototypes were defined: `Common Resource` +and `Authorized`. Both of them have some responses. To assign prototype for all +resources of resource group you need to use such syntax: +`Section name (Prototype Name)`. If section has nested section then all +resources of nested section will have responses defined in prototype. `Users` +resource group will have responces from `Common Resource` prototype (because +it's a prototype of its parent group) and from `Authorized` prototype. It is possible to assign several prototypes for one resource or resource group: @@ -227,7 +236,7 @@ It is possible to assign several prototypes for one resource or resource group: It is possible when one prototype has its own prototype: ```apib -# Data Structures +# Resource Prototypes ## Common Resource (Rate, Format) ```