From ef3975a7003b37fc7d4eb7aa5e1bfbe545d5994d Mon Sep 17 00:00:00 2001 From: I522722 Date: Thu, 29 Apr 2021 16:17:55 +0200 Subject: [PATCH 1/7] Add section on trait inheritance and refer to it from trait objects --- spec/asyncapi.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/asyncapi.md b/spec/asyncapi.md index f61827c21..577ca786a 100644 --- a/spec/asyncapi.md +++ b/spec/asyncapi.md @@ -139,6 +139,10 @@ This is applicable for `$ref` fields in the specification as follows from the [J By convention, the AsyncAPI Specification (A2S) file is named `asyncapi.json` or `asyncapi.yaml`. +### Trait Inheritance + + + ### Schema #### AsyncAPI Object @@ -661,7 +665,7 @@ Field Name | Type | Description tags | [Tags Object](#tagsObject) | A list of tags for API documentation control. Tags can be used for logical grouping of operations. externalDocs | [External Documentation Object](#externalDocumentationObject) | Additional external documentation for this operation. bindings | [Operation Bindings Object](#operationBindingsObject) \| [Reference Object](#referenceObject) | A map where the keys describe the name of the protocol and the values describe protocol-specific definitions for the operation. -traits | [[Operation Trait Object](#operationTraitObject) | [Reference Object](#referenceObject) ] | A list of traits to apply to the operation object. Traits MUST be merged into the operation object using the [JSON Merge Patch](https://tools.ietf.org/html/rfc7386) algorithm in the same order they are defined here. +traits | [[Operation Trait Object](#operationTraitObject) | [Reference Object](#referenceObject) ] | A list of traits to apply to the operation object. Traits MUST be merged into the operation object as defined in the [trait inheritance section](#traitInheritance). The resulting object MUST be a valid [Operation Object](#operationObject). message | [[Message Object](#messageObject) | [Reference Object](#referenceObject)] | A definition of the message that will be published or received on this channel. `oneOf` is allowed here to specify multiple messages, however, **a message MUST be valid only against one of the referenced message objects.** This object can be extended with [Specification Extensions](#specificationExtensions). @@ -1014,7 +1018,7 @@ Field Name | Type | Description externalDocs | [External Documentation Object](#externalDocumentationObject) | Additional external documentation for this message. bindings | [Message Bindings Object](#messageBindingsObject) \| [Reference Object](#referenceObject) | A map where the keys describe the name of the protocol and the values describe protocol-specific definitions for the message. examples | [Map[`string`, `any`]] | An array of key/value pairs where keys MUST be either **headers** and/or **payload**. Values MUST contain examples that validate against the [headers](#messageObjectHeaders) or [payload](#messageObjectPayload) fields, respectively. -traits | [[Message Trait Object](#messageTraitObject) | [Reference Object](#referenceObject)] | A list of traits to apply to the message object. Traits MUST be merged into the message object using the [JSON Merge Patch](https://tools.ietf.org/html/rfc7386) algorithm in the same order they are defined here. The resulting object MUST be a valid [Message Object](#messageObject). +traits | [[Message Trait Object](#messageTraitObject) | [Reference Object](#referenceObject)] | A list of traits to apply to the message object. Traits MUST be merged into the message object as defined in the [trait inheritance section](#traitInheritance). The resulting object MUST be a valid [Message Object](#messageObject). This object can be extended with [Specification Extensions](#specificationExtensions). From 07ba1726fb17e8d4bb97e61825731d4ea5fa972d Mon Sep 17 00:00:00 2001 From: I522722 Date: Thu, 29 Apr 2021 18:17:45 +0200 Subject: [PATCH 2/7] First draft how to formalize the trait inheritance rules --- spec/asyncapi.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/spec/asyncapi.md b/spec/asyncapi.md index 577ca786a..71e48fe6d 100644 --- a/spec/asyncapi.md +++ b/spec/asyncapi.md @@ -141,7 +141,15 @@ By convention, the AsyncAPI Specification (A2S) file is named `asyncapi.json` or ### Trait Inheritance - +When traits ([Operation Trait Object](#operationTraitObject) and [Message Trait Object](#messageTraitObject)) are defined, they MUST be merged into the target object according to the following rules: + +* Traits are merged with a recursive merge algorithm: + * Objects are merged recursively + * All other types (including arrays) are overwritten +* The target object that the trait is applied is the most specific and therefore is never overwritten, only extended. +* The order of the trait objects defines its specificity. Subsequent trait objects in the trait array are more specific. +* The resulting merge order is the following, with the most specific object to the right: + * `merge(trait1, trait2, trait3, ..., targetObject)` ### Schema From 89322f8d7cc3a85c1946632c56afb82ffacff7fc Mon Sep 17 00:00:00 2001 From: I522722 Date: Fri, 30 Apr 2021 08:23:49 +0200 Subject: [PATCH 3/7] Explicitly define behavior for undefined and null values --- spec/asyncapi.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/asyncapi.md b/spec/asyncapi.md index 71e48fe6d..147a037d9 100644 --- a/spec/asyncapi.md +++ b/spec/asyncapi.md @@ -143,13 +143,15 @@ By convention, the AsyncAPI Specification (A2S) file is named `asyncapi.json` or When traits ([Operation Trait Object](#operationTraitObject) and [Message Trait Object](#messageTraitObject)) are defined, they MUST be merged into the target object according to the following rules: -* Traits are merged with a recursive merge algorithm: +* Traits are merged with a recursive merge algorithm, similar to [JSON Merge Patch](https://tools.ietf.org/html/rfc7386): * Objects are merged recursively - * All other types (including arrays) are overwritten + * All other types (including arrays) are overwritten, with the following exceptions + * If the value of the source object is `undefined` it is not merged + * If the value of the source object is `null`, the target object property gets removed or set to `null` * The target object that the trait is applied is the most specific and therefore is never overwritten, only extended. * The order of the trait objects defines its specificity. Subsequent trait objects in the trait array are more specific. * The resulting merge order is the following, with the most specific object to the right: - * `merge(trait1, trait2, trait3, ..., targetObject)` + * `trait1`, `trait2`, `trait3`, ..., `target object` ### Schema From f305df8a251d2d3b142bf9d65f111257da719852 Mon Sep 17 00:00:00 2001 From: I522722 Date: Fri, 30 Apr 2021 08:29:50 +0200 Subject: [PATCH 4/7] Define what happens with undefined and null --- spec/asyncapi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/asyncapi.md b/spec/asyncapi.md index 147a037d9..42262d2c2 100644 --- a/spec/asyncapi.md +++ b/spec/asyncapi.md @@ -146,7 +146,7 @@ When traits ([Operation Trait Object](#operationTraitObject) and [Message Trait * Traits are merged with a recursive merge algorithm, similar to [JSON Merge Patch](https://tools.ietf.org/html/rfc7386): * Objects are merged recursively * All other types (including arrays) are overwritten, with the following exceptions - * If the value of the source object is `undefined` it is not merged + * If the value of the source object is `undefined` it is ignored * If the value of the source object is `null`, the target object property gets removed or set to `null` * The target object that the trait is applied is the most specific and therefore is never overwritten, only extended. * The order of the trait objects defines its specificity. Subsequent trait objects in the trait array are more specific. From 06db0434992755a45fcc0f9f7fd0bddde62d661a Mon Sep 17 00:00:00 2001 From: I522722 Date: Fri, 30 Apr 2021 10:46:56 +0200 Subject: [PATCH 5/7] Updated description of trait inheritance merge behavior Now it aims to be identical to JSON Merge patch --- spec/asyncapi.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/asyncapi.md b/spec/asyncapi.md index 42262d2c2..8c8aeee3b 100644 --- a/spec/asyncapi.md +++ b/spec/asyncapi.md @@ -143,13 +143,13 @@ By convention, the AsyncAPI Specification (A2S) file is named `asyncapi.json` or When traits ([Operation Trait Object](#operationTraitObject) and [Message Trait Object](#messageTraitObject)) are defined, they MUST be merged into the target object according to the following rules: -* Traits are merged with a recursive merge algorithm, similar to [JSON Merge Patch](https://tools.ietf.org/html/rfc7386): - * Objects are merged recursively - * All other types (including arrays) are overwritten, with the following exceptions - * If the value of the source object is `undefined` it is ignored - * If the value of the source object is `null`, the target object property gets removed or set to `null` -* The target object that the trait is applied is the most specific and therefore is never overwritten, only extended. -* The order of the trait objects defines its specificity. Subsequent trait objects in the trait array are more specific. +* Traits are merged with a recursive merge algorithm, as defined in the [JSON Merge Patch](https://tools.ietf.org/html/rfc7386) RFC. +* To summarize the main merge rules: + * Objects are merged recursively (if the source and the target property are both objects). + * If the value of the source object property is `null`, the target object property gets removed. + * In all other cases (including arrays and `undefined`) the source object property overwrites the target object property. +* The target object that the trait is applied to is the most specific and is therefore never overwritten, only extended. +* The order of the traits defines their specificity. Subsequent traits in the trait array are more specific. * The resulting merge order is the following, with the most specific object to the right: * `trait1`, `trait2`, `trait3`, ..., `target object` From c74707275838ba59e399be14ee434b4205accfa4 Mon Sep 17 00:00:00 2001 From: I522722 Date: Sat, 1 May 2021 07:15:23 +0200 Subject: [PATCH 6/7] Excplicitly state that must be dereferenced before merging Added some further clarifications to avoid target object naming confusion --- spec/asyncapi.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/spec/asyncapi.md b/spec/asyncapi.md index 8c8aeee3b..5a5fb3de1 100644 --- a/spec/asyncapi.md +++ b/spec/asyncapi.md @@ -143,15 +143,16 @@ By convention, the AsyncAPI Specification (A2S) file is named `asyncapi.json` or When traits ([Operation Trait Object](#operationTraitObject) and [Message Trait Object](#messageTraitObject)) are defined, they MUST be merged into the target object according to the following rules: -* Traits are merged with a recursive merge algorithm, as defined in the [JSON Merge Patch](https://tools.ietf.org/html/rfc7386) RFC. -* To summarize the main merge rules: +* Traits are merged with a recursive merge algorithm, as defined in the [JSON Merge Patch](https://tools.ietf.org/html/rfc7386) RFC. To summarize the main merge rules as a merge from source object into a target object: * Objects are merged recursively (if the source and the target property are both objects). * If the value of the source object property is `null`, the target object property gets removed. * In all other cases (including arrays and `undefined`) the source object property overwrites the target object property. -* The target object that the trait is applied to is the most specific and is therefore never overwritten, only extended. -* The order of the traits defines their specificity. Subsequent traits in the trait array are more specific. -* The resulting merge order is the following, with the most specific object to the right: - * `trait1`, `trait2`, `trait3`, ..., `target object` +* The sequence of merging is the following + * Before applying merges, JSON Schema `$ref` MUST be dereferenced + * The [Message Object](#messageObject) and the [Operation Object](#operationObject) that the trait is applied to is the most specific and is therefore never overwritten, only extended. + * The order of the traits defines their specificity. Subsequent traits in the trait array are more specific. + * The resulting merge order is the following, with the most specific object to the right: + * `trait1`, `trait2`, `trait3`, ..., `message/operation object` ### Schema From 6f57e01d24ae06d5c72cb975bad616853ee28c7c Mon Sep 17 00:00:00 2001 From: Simon Heimler Date: Wed, 10 Aug 2022 06:39:55 +0200 Subject: [PATCH 7/7] Added trait explanation as suggested by @patrickp-at-work --- spec/asyncapi.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/asyncapi.md b/spec/asyncapi.md index 5a5fb3de1..33651ed7c 100644 --- a/spec/asyncapi.md +++ b/spec/asyncapi.md @@ -139,6 +139,10 @@ This is applicable for `$ref` fields in the specification as follows from the [J By convention, the AsyncAPI Specification (A2S) file is named `asyncapi.json` or `asyncapi.yaml`. +### Traits + +Traits are pieces of information that will be merged into the referencing object. Using traits reduces the amount of repetitive information within an AsyncAPI document. + ### Trait Inheritance When traits ([Operation Trait Object](#operationTraitObject) and [Message Trait Object](#messageTraitObject)) are defined, they MUST be merged into the target object according to the following rules: