From 16afca805824011227e701627f1a76dd2e4b1256 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Wed, 3 Jun 2020 10:28:37 +0200 Subject: [PATCH 1/5] Create GreenField Api Development Docs --- docs/greenfield-development.md | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 docs/greenfield-development.md diff --git a/docs/greenfield-development.md b/docs/greenfield-development.md new file mode 100644 index 000000000..57e9e79e7 --- /dev/null +++ b/docs/greenfield-development.md @@ -0,0 +1,42 @@ +# GreenField API Development Documentation + + +## Updating existing API endpoints + +### Scenario 1: Changing a property type on the model +Changing a property on a model is a breaking change unless the server starts handling both versions. + +#### Solutions +* Bump the version of the endpoint. + +#### Alternatives considered +* Create a `JsonConverter` that allows conversion between the original type and the new type. However, if this option is used, you will need to ensure that the response model returns the same format. In the case of the `GET` endpoint, you will break clients expecting the original type. + +### Scenario 2: Removing a property on the model +Removing a property on a model is a breaking change. + +#### Solutions +* Bump the version of the endpoint. + +#### Alternatives considered +* Create a default value (one that is not useful) to be sent back in the model. Ignore the property being sent on the model to the server. + +### Scenario 3: Adding a property on the model +Adding a property on a model can potentially be a breaking change. It is a breaking change if: +* the property is required +* the property has no default value + +#### Solutions +* Check if the payload has the property present. If not, either set to the default value (in the case of a`POST`) or set to the model's current value. See [Detecting missing properties in a JSON model](#missing-properties-detect) for how to achieve this. + +#### Alternatives considered +* Bump the version of the endpoint +* Assume the property is always sent and let the value be set to the default if not ( in the case of nullable types, this may be problematic when calling update endpoints). +* Use [`[JsonExtensionData]AdditionalData`](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonExtensionDataAttribute.htm) so that clients receive the full payload even after updating only the server. This is problematic as it only fixes clients which implement this opinionated flow (this is not a standard or common way of doing API calls) . + +## Technical specifics + +### Detecting missing properties in a JSON model. +Possible solutions: +* Read the raw JSON object in the controller action and and search for the lack of a specific property. +* Use [`JSON.NET Serialization Callabacks`](https://www.newtonsoft.com/json/help/html/SerializationCallbacks.htm) to set a `List MissingProperties;` variable From 6af3b4a51d2e72e58e270ae9a9bda7a3370fb962 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Wed, 3 Jun 2020 11:12:26 +0200 Subject: [PATCH 2/5] Update greenfield-development.md --- docs/greenfield-development.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/docs/greenfield-development.md b/docs/greenfield-development.md index 57e9e79e7..894d5052d 100644 --- a/docs/greenfield-development.md +++ b/docs/greenfield-development.md @@ -1,5 +1,18 @@ # GreenField API Development Documentation +## Adding new API endpoints +* Always document all endpoints and model schemas in swagger. OpenAPI 3.0 is used as a specification, in JSON formatting, and is written manually. The specification is split to a file per controller and then merged by the server through a controller action at `/swagger/v1/swagger.json`. +* All `JsonConverter` usage should be registered through attributes within the model itself. +* `decimal` and `long` and other similar types should be serialized to a string and able to deserialize from the original type and a string. +* Ensure that the correct security permissions are set on the endpoint. Create a new permission if none of the existing ones are suitable. +* Use HTTP methods according to REST principles when possible. This means: + * `POST` - Create or custom action + * `PUT` - Update full model + * `PATCH` - Update partially + * `DELETE` - Delete or Archive +* When returning an error response, we should differentiate from 2 possible scenarios: + * Model validation - an error on the request was found - [Status Code 422](https://httpstatuses.com/422) with the model [ValidationProblemDetails](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.validationproblemdetails?view=aspnetcore-3.1). + * Generic request error - an error resulting from the business logic unable to handle the specified request - [Status Code 400](https://httpstatuses.com/400) with the model [ProblemDetails](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails?view=aspnetcore-3.1). ## Updating existing API endpoints @@ -23,20 +36,22 @@ Removing a property on a model is a breaking change. ### Scenario 3: Adding a property on the model Adding a property on a model can potentially be a breaking change. It is a breaking change if: -* the property is required -* the property has no default value +* the property is required. +* the property has no default value. #### Solutions * Check if the payload has the property present. If not, either set to the default value (in the case of a`POST`) or set to the model's current value. See [Detecting missing properties in a JSON model](#missing-properties-detect) for how to achieve this. #### Alternatives considered -* Bump the version of the endpoint +* Bump the version of the endpoint. * Assume the property is always sent and let the value be set to the default if not ( in the case of nullable types, this may be problematic when calling update endpoints). * Use [`[JsonExtensionData]AdditionalData`](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonExtensionDataAttribute.htm) so that clients receive the full payload even after updating only the server. This is problematic as it only fixes clients which implement this opinionated flow (this is not a standard or common way of doing API calls) . + + ## Technical specifics ### Detecting missing properties in a JSON model. Possible solutions: * Read the raw JSON object in the controller action and and search for the lack of a specific property. -* Use [`JSON.NET Serialization Callabacks`](https://www.newtonsoft.com/json/help/html/SerializationCallbacks.htm) to set a `List MissingProperties;` variable +* Use [`JSON.NET Serialization Callabacks`](https://www.newtonsoft.com/json/help/html/SerializationCallbacks.htm) to set a `List MissingProperties;` variable. From 34239dc3839187853f4d7b7f0f55950828a308d9 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Thu, 4 Jun 2020 12:03:56 +0200 Subject: [PATCH 3/5] Update docs/greenfield-development.md Co-authored-by: Dennis Reimann --- docs/greenfield-development.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/greenfield-development.md b/docs/greenfield-development.md index 894d5052d..444c4831d 100644 --- a/docs/greenfield-development.md +++ b/docs/greenfield-development.md @@ -4,7 +4,7 @@ * Always document all endpoints and model schemas in swagger. OpenAPI 3.0 is used as a specification, in JSON formatting, and is written manually. The specification is split to a file per controller and then merged by the server through a controller action at `/swagger/v1/swagger.json`. * All `JsonConverter` usage should be registered through attributes within the model itself. * `decimal` and `long` and other similar types should be serialized to a string and able to deserialize from the original type and a string. -* Ensure that the correct security permissions are set on the endpoint. Create a new permission if none of the existing ones are suitable. +* Ensure that the correct security permissions are set on the endpoint. Create a new permission if none of the existing ones are suitable. * Use HTTP methods according to REST principles when possible. This means: * `POST` - Create or custom action * `PUT` - Update full model From 6c828a29ec1066693dd94517ab59531f25d46bde Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Fri, 12 Jun 2020 14:10:43 +0200 Subject: [PATCH 4/5] Update greenfield-development.md --- docs/greenfield-development.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/greenfield-development.md b/docs/greenfield-development.md index 444c4831d..dde987916 100644 --- a/docs/greenfield-development.md +++ b/docs/greenfield-development.md @@ -1,3 +1,4 @@ + # GreenField API Development Documentation ## Adding new API endpoints @@ -11,8 +12,22 @@ * `PATCH` - Update partially * `DELETE` - Delete or Archive * When returning an error response, we should differentiate from 2 possible scenarios: - * Model validation - an error on the request was found - [Status Code 422](https://httpstatuses.com/422) with the model [ValidationProblemDetails](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.validationproblemdetails?view=aspnetcore-3.1). - * Generic request error - an error resulting from the business logic unable to handle the specified request - [Status Code 400](https://httpstatuses.com/400) with the model [ProblemDetails](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails?view=aspnetcore-3.1). + * Model validation - an error or errors on the request was found - [Status Code 422](https://httpstatuses.com/422) with the model: + ```json + [ + { + "path": "prop-name", + "message": "human readable message" + } + ] + ``` + * Generic request error - an error resulting from the business logic unable to handle the specified request - [Status Code 400](https://httpstatuses.com/400) with the model: + ```json + { + "code": "unique-error-code", + "message":"a human readable message" + } + ``` ## Updating existing API endpoints From 6729827645d401e129276385a0aacd0f08880979 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Mon, 15 Jun 2020 12:45:05 +0200 Subject: [PATCH 5/5] Update greenfield-development.md --- docs/greenfield-development.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/greenfield-development.md b/docs/greenfield-development.md index dde987916..51e291b0b 100644 --- a/docs/greenfield-development.md +++ b/docs/greenfield-development.md @@ -4,7 +4,7 @@ * Always document all endpoints and model schemas in swagger. OpenAPI 3.0 is used as a specification, in JSON formatting, and is written manually. The specification is split to a file per controller and then merged by the server through a controller action at `/swagger/v1/swagger.json`. * All `JsonConverter` usage should be registered through attributes within the model itself. -* `decimal` and `long` and other similar types should be serialized to a string and able to deserialize from the original type and a string. +* `decimal` and `long` and other similar types, if there is a need for decimal precision or has the possibility of an overflow issue, should be serialized to a string and able to deserialize from the original type and a string. * Ensure that the correct security permissions are set on the endpoint. Create a new permission if none of the existing ones are suitable. * Use HTTP methods according to REST principles when possible. This means: * `POST` - Create or custom action