diff --git a/README.md b/README.md index 0edeb5c..a1b7dc8 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ There are some fantastic examples of [configurable rules](https://redocly.com/do - [String schemas length defined](configurable-rules/string-schemas-length-defined/) - [JSON Schema misconfigurations](configurable-rules/json-schema-misconfigurations/) - [Azure APIM unsupported keywords](configurable-rules/azure-apim-unsupported-keyword/) +- [Operation-deprecated-response-headers](configurable-rules/operation-deprecated-response-headers/) ### Custom plugins diff --git a/configurable-rules/operation-deprecated-response-headers/README.md b/configurable-rules/operation-deprecated-response-headers/README.md new file mode 100644 index 0000000..e7c4a26 --- /dev/null +++ b/configurable-rules/operation-deprecated-response-headers/README.md @@ -0,0 +1,244 @@ +# `Deprecated` services SHOULD define a standard set of headers to communicate `Deprecation` and `Sunset` information. There should also exist a `Link` header to inform the consumer where the service location has been moved to, if any + +Authors: + +- `@jeremyfiel` Jeremy Fiel (ADP) + +## What this does and why + +There are three parts to a deprecation strategy. Defining _when_ (`deprecation`) the service will be deprecated, the `sunset` date of the service for it to be completely turned off, and a `link` to where a replacement service may exist. We use a number of RFC standards to define these header fields. + +- [RFC7231][4] +- [RFC8288][1] +- [RFC8594][3] +- [RFC9651][5] +- [RFC9745][2] + +### 2XX responses + +_while the service remains active_ + +* We must define the requirement for the `headers` map to exist in a `2XX` response +* We define the required headers: `deprecation`, `sunset`, `link` + +### 410 Gone response + +_the service is no longer available_ + +* A deprecated service must define a `410` response +* We must define the requirement for the `headers` map to exist +* After a service has been deprecated, the `deprecation` header must not be provided. +* A `sunset` header to indicate when the service was removed and a `link` header to indicate where a new service can be found + +## Code + +Add this to the `rules` section of your `redocly.yaml`: + +```yaml +rules: + rule/response-2XX-deprecated-must-define-headers: + severity: warn + message: Deprecated endpoints MUST respond with standard headers + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + subject: + type: Response + matchParentKeys: '2([\d]+){2}' + assertions: + required: + - headers + +rule/response-2XX-deprecated-must-use-standard-headers: + severity: warn + message: 'Deprecated endpoints MUST respond with "Sunset", "Deprecation", and "Link" standard headers' + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + - subject: + type: Response + matchParentKeys: '2([\d]+){2}' + assertions: + required: + - headers + subject: + type: HeadersMap + assertions: + required: + - Deprecation + - Sunset + - Link + +rule/operation-deprecated-must-define-410-response: + severity: warn + message: Deprecated endpoints MUST define a 410 response + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + subject: + type: Responses + assertions: + required: + - '410' + +rule/response-410-deprecated-must-define-headers: + severity: warn + message: Deprecated endpoints MUST respond with standard headers + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + subject: + type: Response + matchParentKeys: '410' + assertions: + required: + - headers + +rule/response-410-deprecated-must-define-standard-headers: + severity: warn + message: 'Deprecated endpoints MUST respond with "Sunset" and "Link" standard headers after "Deprecation" date value has been exceeded' + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + - subject: + type: Response + matchParentKeys: '410' + assertions: + required: + - headers + subject: + type: HeadersMap + assertions: + required: + - 'Sunset' + - 'Link' + +rule/response-410-deprecated-must-not-define-deprecation-header: + severity: warn + message: 'Deprecated endpoints 410 response MUST NOT define "Deprecation" header' + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + - subject: + type: Response + matchParentKeys: '410' + assertions: + required: + - headers + subject: + type: HeadersMap + assertions: + disallowed: + - 'Deprecation' +``` + +## Examples + +```yaml +#invalid +openapi: 3.1.0 +info: + title: Redocly Configurable Rule - Deprecated Endpoints + version: 0.0.0 +paths: + '/things': + summary: my deprecated endpoint + deprecated: true + responses: + '200': + description: OK + content: + 'application/json': + schema: {} +``` + +```yaml +# valid +openapi: 3.1.0 +info: + title: Redocly Configurable Rule - Deprecated Endpoints + version: 0.0.0 +paths: + '/things': + summary: my deprecated endpoint + deprecated: true + parameters: [] + responses: + '200': + description: OK + headers: + deprecation: + schema: + type: string + examples: + deprecation_date: + summary: A Structured Field Value as defined in Section 3.3.7 of RFC9651[5]. + value: '@1688169599' + sunset: + description: Please note that for historical reasons the Sunset HTTP header field uses a different data type for date than the Deprecation header field + schema: + type: string + examples: + sunset_date: + summary: An HTTP-date timestamp, as defined in Section 7.1.1.1 of [RFC7231][4], and SHOULD be a timestamp in the future. + value: Sun, 30 Jun 2024 23:59:59 GMT + link: + schema: + type: string + examples: + link_value: + value: ; rel=alternate; title='the more-things api' + content: + 'application/json': + schema: {} + '410': + description: Gone + headers: + sunset: + description: Please note that for historical reasons the Sunset HTTP header field uses a different data type for date than the Deprecation header field + schema: + type: string + examples: + sunset_date: + summary: An HTTP-date timestamp, as defined in Section 7.1.1.1 of [RFC7231][4], and SHOULD be a timestamp in the future. + value: Sun, 30 Jun 2024 23:59:59 GMT + link: + schema: + type: string + examples: + link_value: + value: ; rel=alternate; title='the more-things api' + content: + 'application/json': + schema: {} +``` + +[1]: https://datatracker.ietf.org/doc/html/rfc8288 "RFC8288 - Web Linking" +[2]: https://datatracker.ietf.org/doc/html/rfc9745/ "RFC 9745 - HTTP Deprecation Header Field" +[3]: https://datatracker.ietf.org/doc/html/rfc8594/ "RFC8594 - HTTP Sunset Header Field" +[4]: https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.1.1 "RFC7231 - Date/Time Formats" +[5]: https://datatracker.ietf.org/doc/html/rfc9651/#section-3.3.7 "RFC9651 - Dates" diff --git a/configurable-rules/operation-deprecated-response-headers/openapi.yaml b/configurable-rules/operation-deprecated-response-headers/openapi.yaml new file mode 100644 index 0000000..701ab8d --- /dev/null +++ b/configurable-rules/operation-deprecated-response-headers/openapi.yaml @@ -0,0 +1,57 @@ +openapi: 3.1.0 +info: + title: Redocly Configurable Rule - Deprecated Endpoints + version: 0.0.0 +paths: + '/things': + summary: my deprecated endpoint + deprecated: true + parameters: [] + responses: + '200': + description: OK + headers: + deprecation: + schema: + type: string + examples: + deprecation_date: + summary: an Item Structured Header RFC8941. Refer to Section 3.3.7 of [SFBIS] for ABNF of `sf-date` + value: '@1688169599' + sunset: + description: Please note that for historical reasons the Sunset HTTP header field uses a different data type for date than the Deprecation header field + schema: + type: string + examples: + sunset_date: + summary: An HTTP-date timestamp, as defined in Section 7.1.1.1 of [RFC7231], and SHOULD be a timestamp in the future. + value: Sun, 30 Jun 2024 23:59:59 GMT + link: + schema: + type: string + examples: + link_value: + value: ; rel=alternate; title='the more-things api' + content: + 'application/json': + schema: {} + '410': + description: Gone + headers: + sunset: + description: Please note that for historical reasons the Sunset HTTP header field uses a different data type for date than the Deprecation header field + schema: + type: string + examples: + sunset_date: + summary: An HTTP-date timestamp, as defined in Section 7.1.1.1 of [RFC7231], and SHOULD be a timestamp in the future. + value: Sun, 30 Jun 2024 23:59:59 GMT + link: + schema: + type: string + examples: + link_value: + value: ; rel=alternate; title='the more-things api' + content: + 'application/json': + schema: {} diff --git a/configurable-rules/operation-deprecated-response-headers/redocly.yaml b/configurable-rules/operation-deprecated-response-headers/redocly.yaml new file mode 100644 index 0000000..983cc5e --- /dev/null +++ b/configurable-rules/operation-deprecated-response-headers/redocly.yaml @@ -0,0 +1,119 @@ +rules: + rule/response-2XX-deprecated-must-define-headers: + severity: error + message: Deprecated endpoints MUST respond with standard headers + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + subject: + type: Response + matchParentKeys: '2([\d]+){2}' + assertions: + required: + - headers + +rule/response-2XX-deprecated-must-use-standard-headers: + severity: error + message: 'Deprecated endpoints MUST respond with "Sunset", "Deprecation", and "Link" standard headers' + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + - subject: + type: Response + matchParentKeys: '2([\d]+){2}' + assertions: + required: + - headers + subject: + type: HeadersMap + assertions: + required: + - Deprecation + - Sunset + - Link + +rule/operation-deprecated-must-define-410-response: + severity: error + message: Deprecated endpoints MUST define a 410 response + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + subject: + type: Responses + assertions: + required: + - '410' + +rule/response-410-deprecated-must-define-headers: + severity: error + message: Deprecated endpoints MUST respond with standard headers + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + subject: + type: Response + matchParentKeys: '410' + assertions: + required: + - headers + +rule/response-410-deprecated-must-define-standard-headers: + severity: error + message: 'Deprecated endpoints MUST respond with "Sunset" and "Link" standard headers after "Deprecation" date value has been exceeded' + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + - subject: + type: Response + matchParentKeys: '410' + assertions: + required: + - headers + subject: + type: HeadersMap + assertions: + required: + - 'Sunset' + - 'Link' + +rule/response-410-deprecated-must-not-define-deprecation-header: + severity: error + message: 'Deprecated endpoints 410 response MUST NOT define "Deprecation" header' + where: + - subject: + type: Operation + property: deprecated + assertions: + defined: true + const: true + - subject: + type: Response + matchParentKeys: '410' + assertions: + required: + - headers + subject: + type: HeadersMap + assertions: + disallowed: + - 'Deprecation'