From dc3eb2c17cb62965799b097cb148138a71d79833 Mon Sep 17 00:00:00 2001 From: Phil Kedy Date: Wed, 15 Sep 2021 18:22:46 -0400 Subject: [PATCH 1/8] Adding attributes from the CloudEvents spec and a slightly better routing example --- .../pubsub/howto-route-messages.md | 294 ++++++++++++++++-- 1 file changed, 264 insertions(+), 30 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md index f860036de..51967caa0 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md +++ b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md @@ -42,14 +42,14 @@ metadata: name: myevent-subscription spec: pubsubname: pubsub - topic: deathStarStatus + topic: transactions routes: rules: - - match: event.type == "rebels.attacking.v3" - path: /dsstatus.v3 - - match: event.type == "rebels.attacking.v2" - path: /dsstatus.v2 - default: /dsstatus + - match: event.type == "withdraw.v3" + path: /withdraw.v3 + - match: event.type == "withdraw.v2" + path: /withdraw.v2 + default: /withdraw scopes: - app1 - app2 @@ -59,7 +59,7 @@ scopes: Alternatively, the programattic approach varies slightly in that the `routes` structure is returned instead of `route`. The JSON structure matches the declarative YAML. -{{< tabs Python Node Go PHP>}} +{{< tabs Python Node "C#" Go PHP>}} {{% codetab %}} ```python @@ -77,19 +77,19 @@ def subscribe(): subscriptions = [ { 'pubsubname': 'pubsub', - 'topic': 'deathStarStatus', + 'topic': 'transactions', 'routes': { 'rules': [ { - 'match': 'event.type == "rebels.attacking.v3"', - 'path': '/dsstatus.v3' + 'match': 'event.type == "withdraw.v3"', + 'path': '/withdraw.v3' }, { - 'match': 'event.type == "rebels.attacking.v2"', - 'path': '/dsstatus.v2' + 'match': 'event.type == "withdraw.v2"', + 'path': '/withdraw.v2' }, ], - 'default': '/dsstatus' + 'default': '/withdraw' } }] return jsonify(subscriptions) @@ -116,19 +116,19 @@ app.get('/dapr/subscribe', (req, res) => { res.json([ { pubsubname: "pubsub", - topic: "deathStarStatus", + topic: "transactions", routes: { rules: [ { - match: 'event.type == "rebels.attacking.v3"', - path: '/dsstatus.v3' + match: 'event.type == "withdraw.v3"', + path: '/withdraw.v3' }, { - match: 'event.type == "rebels.attacking.v2"', - path: '/dsstatus.v2' + match: 'event.type == "withdraw.v2"', + path: '/withdraw.v2' }, ], - default: '/dsstatus' + default: '/withdraw' } } ]); @@ -143,6 +143,34 @@ app.listen(port, () => console.log(`consumer app listening on port ${port}!`)) ``` {{% /codetab %}} +{{% codetab %}} +```csharp + [Topic("pubsub", "transactions", "event.type ==\"withdraw.v3\"", 1)] + [HttpPost("withdraw.v3")] + public async Task> WithdrawV2(TransactionV3 transaction, [FromServices] DaprClient daprClient) + { + // Logic + return account; + } + + [Topic("pubsub", "transactions", "event.type ==\"withdraw.v2\"", 2)] + [HttpPost("withdraw.v2")] + public async Task> WithdrawV2(TransactionV2 transaction, [FromServices] DaprClient daprClient) + { + // Logic + return account; + } + + [Topic("pubsub", "transactions")] + [HttpPost("withdraw")] + public async Task> WithdrawV2(Transaction transaction, [FromServices] DaprClient daprClient) + { + // Logic + return account; + } +``` +{{% /codetab %}} + {{% codetab %}} ```golang package main @@ -180,19 +208,19 @@ func configureSubscribeHandler(w http.ResponseWriter, _ *http.Request) { t := []subscription{ { PubsubName: "pubsub", - Topic: "deathStarStatus", + Topic: "transactions", Routes: routes{ Rules: []rule{ { - Match: `event.type == "rebels.attacking.v3"`, - Path: "/dsstatus.v3", + Match: `event.type == "withdraw.v3"`, + Path: "/withdraw.v3", }, { - Match: `event.type == "rebels.attacking.v2"`, - Path: "/dsstatus.v2", + Match: `event.type == "withdraw.v2"`, + Path: "/withdraw.v2", }, }, - Default: "/dsstatus", + Default: "/withdraw", }, }, } @@ -216,12 +244,12 @@ func main() { require_once __DIR__.'/vendor/autoload.php'; $app = \Dapr\App::create(configure: fn(\DI\ContainerBuilder $builder) => $builder->addDefinitions(['dapr.subscriptions' => [ - new \Dapr\PubSub\Subscription(pubsubname: 'pubsub', topic: 'deathStarStatus', routes: ( + new \Dapr\PubSub\Subscription(pubsubname: 'pubsub', topic: 'transactions', routes: ( rules: => [ - ('match': 'event.type == "rebels.attacking.v3"', path: '/dsstatus.v3'), - ('match': 'event.type == "rebels.attacking.v2"', path: '/dsstatus.v2'), + ('match': 'event.type == "withdraw.v3"', path: '/withdraw.v3'), + ('match': 'event.type == "withdraw.v2"', path: '/withdraw.v2'), ] - default: '/dsstatus')), + default: '/withdraw')), ]])); $app->post('/dsstatus', function( #[\Dapr\Attributes\FromBody] @@ -238,7 +266,213 @@ $app->start(); {{< /tabs >}} -In these examples, depending on the type of the event (`event.type`), the application will be called on `/dsstatus.v3`, `/dsstatus.v2` or `/dsstatus`. The expressions are written as [Common Expression Language (CEL)](https://opensource.google/projects/cel) where `event` represents the cloud event. Any of the attributes from the [CloudEvents core specification](https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#required-attributes) can be referenced in the expression. One caveat is that it is only possible to access the attributes inside `event.data` if it is nested JSON +In these examples, depending on the type of the event (`event.type`), the application will be called on `/dsstatus.v3`, `/dsstatus.v2` or `/dsstatus`. The expressions are written as [Common Expression Language (CEL)](https://opensource.google/projects/cel) where `event` represents the cloud event. Any of the attributes from the [CloudEvents core specification](https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#required-attributes) can be referenced in the expression. + +For reference, the following attributes are from the CloudEvents specification. + +### Event Data + +#### data + +As defined by the term Data, CloudEvents MAY include domain-specific information about the occurrence. When present, this information will be encapsulated within `data`. + +- Description: The event payload. This specification does not place any restriction on the type of this information. It is encoded into a media format which is specified by the `datacontenttype` attribute (e.g. application/json), and adheres to the `dataschema` format when those respective attributes are present. +- Constraints: + - OPTIONAL + +{{% alert title="Limitation" color="warning" %}} +Currently, it is only possible to access the attributes inside data if it is nested JSON values and not JSON escaped in a string. +{{% /alert %}} + +### REQUIRED Attributes + +The following attributes are REQUIRED to be present in all CloudEvents: + +#### id + +- Type: `String` +- Description: Identifies the event. Producers MUST ensure that `source` + `id` + is unique for each distinct event. If a duplicate event is re-sent (e.g. due + to a network error) it MAY have the same `id`. Consumers MAY assume that + Events with identical `source` and `id` are duplicates. +- Constraints: + - REQUIRED + - MUST be a non-empty string + - MUST be unique within the scope of the producer +- Examples: + - An event counter maintained by the producer + - A UUID + +#### source + +- Type: `URI-reference` +- Description: Identifies the context in which an event happened. Often this + will include information such as the type of the event source, the + organization publishing the event or the process that produced the event. The + exact syntax and semantics behind the data encoded in the URI is defined by + the event producer. + + Producers MUST ensure that `source` + `id` is unique for each distinct event. + + An application MAY assign a unique `source` to each distinct producer, which + makes it easy to produce unique IDs since no other producer will have the same + source. The application MAY use UUIDs, URNs, DNS authorities or an + application-specific scheme to create unique `source` identifiers. + + A source MAY include more than one producer. In that case the producers MUST + collaborate to ensure that `source` + `id` is unique for each distinct event. + +- Constraints: + - REQUIRED + - MUST be a non-empty URI-reference + - An absolute URI is RECOMMENDED +- Examples + - Internet-wide unique URI with a DNS authority. + - https://github.com/cloudevents + - mailto:cncf-wg-serverless@lists.cncf.io + - Universally-unique URN with a UUID: + - urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66 + - Application-specific identifiers + - /cloudevents/spec/pull/123 + - /sensors/tn-1234567/alerts + - 1-555-123-4567 + +#### specversion + +- Type: `String` +- Description: The version of the CloudEvents specification which the event + uses. This enables the interpretation of the context. Compliant event + producers MUST use a value of `1.0` when referring to this version of the + specification. + + Currently, this attribute will only have the 'major' and 'minor' version + numbers included in it. This allows for 'patch' changes to the specification + to be made without changing this property's value in the serialization. + Note: for 'release candidate' releases a suffix might be used for testing + purposes. + +- Constraints: + - REQUIRED + - MUST be a non-empty string + +#### type + +- Type: `String` +- Description: This attribute contains a value describing the type of event + related to the originating occurrence. Often this attribute is used for + routing, observability, policy enforcement, etc. The format of this is + producer defined and might include information such as the version of the + `type` - see + [Versioning of CloudEvents in the Primer](primer.md#versioning-of-cloudevents) + for more information. +- Constraints: + - REQUIRED + - MUST be a non-empty string + - SHOULD be prefixed with a reverse-DNS name. The prefixed domain dictates the + organization which defines the semantics of this event type. +- Examples + - com.github.pull_request.opened + - com.example.object.deleted.v2 + +### OPTIONAL Attributes + +The following attributes are OPTIONAL to appear in CloudEvents. See the +[Notational Conventions](#notational-conventions) section for more information +on the definition of OPTIONAL. + +#### datacontenttype + +- Type: `String` per [RFC 2046](https://tools.ietf.org/html/rfc2046) +- Description: Content type of `data` value. This attribute enables `data` to + carry any type of content, whereby format and encoding might differ from that + of the chosen event format. For example, an event rendered using the + [JSON envelope](./json-format.md#3-envelope) format might carry an XML payload + in `data`, and the consumer is informed by this attribute being set to + "application/xml". The rules for how `data` content is rendered for different + `datacontenttype` values are defined in the event format specifications; for + example, the JSON event format defines the relationship in + [section 3.1](./json-format.md#31-handling-of-data). + + For some binary mode protocol bindings, this field is directly mapped to the + respective protocol's content-type metadata property. Normative rules for the + binary mode and the content-type metadata mapping can be found in the + respective protocol + + In some event formats the `datacontenttype` attribute MAY be omitted. For + example, if a JSON format event has no `datacontenttype` attribute, then it is + implied that the `data` is a JSON value conforming to the "application/json" + media type. In other words: a JSON-format event with no `datacontenttype` is + exactly equivalent to one with `datacontenttype="application/json"`. + + When translating an event message with no `datacontenttype` attribute to a + different format or protocol binding, the target `datacontenttype` SHOULD be + set explicitly to the implied `datacontenttype` of the source. + +- Constraints: + - OPTIONAL + - If present, MUST adhere to the format specified in + [RFC 2046](https://tools.ietf.org/html/rfc2046) +- For Media Type examples see + [IANA Media Types](http://www.iana.org/assignments/media-types/media-types.xhtml) + +#### dataschema + +- Type: `URI` +- Description: Identifies the schema that `data` adheres to. Incompatible + changes to the schema SHOULD be reflected by a different URI. See + [Versioning of CloudEvents in the Primer](primer.md#versioning-of-cloudevents) + for more information. +- Constraints: + - OPTIONAL + - If present, MUST be a non-empty URI + +#### subject + +- Type: `String` +- Description: This describes the subject of the event in the context of the + event producer (identified by `source`). In publish-subscribe scenarios, a + subscriber will typically subscribe to events emitted by a `source`, but the + `source` identifier alone might not be sufficient as a qualifier for any + specific event if the `source` context has internal sub-structure. + + Identifying the subject of the event in context metadata (opposed to only in + the `data` payload) is particularly helpful in generic subscription filtering + scenarios where middleware is unable to interpret the `data` content. In the + above example, the subscriber might only be interested in blobs with names + ending with '.jpg' or '.jpeg' and the `subject` attribute allows for + constructing a simple and efficient string-suffix filter for that subset of + events. + +- Constraints: + - OPTIONAL + - If present, MUST be a non-empty string +- Example: + - A subscriber might register interest for when new blobs are created inside a + blob-storage container. In this case, the event `source` identifies the + subscription scope (storage container), the `type` identifies the "blob + created" event, and the `id` uniquely identifies the event instance to + distinguish separate occurrences of a same-named blob having been created; + the name of the newly created blob is carried in `subject`: + - `source`: https://example.com/storage/tenant/container + - `subject`: mynewfile.jpg + +#### time + +- Type: `Timestamp` +- Description: Timestamp of when the occurrence happened. If the time of the + occurrence cannot be determined then this attribute MAY be set to some other + time (such as the current time) by the CloudEvents producer, however all + producers for the same `source` MUST be consistent in this respect. In other + words, either they all use the actual time of the occurrence or they all use + the same algorithm to determine the value used. +- Constraints: + - OPTIONAL + - If present, MUST adhere to the format specified in + [RFC 3339](https://tools.ietf.org/html/rfc3339) + +{{% alert title="Limitation" color="warning" %}} +Currently, comparisons to time (e.g. before or after "now") are not supported. +{{% /alert %}} ## Next steps From cebe13ff747898175ff7371e3b16bbae7b081d50 Mon Sep 17 00:00:00 2001 From: Phil Kedy Date: Wed, 15 Sep 2021 19:08:45 -0400 Subject: [PATCH 2/8] Tweaks per PR --- .../building-blocks/pubsub/howto-route-messages.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md index 51967caa0..b69926997 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md +++ b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md @@ -94,7 +94,7 @@ def subscribe(): }] return jsonify(subscriptions) -@app.route('/dsstatus', methods=['POST']) +@app.route('/withdraw', methods=['POST']) def ds_subscriber(): print(request.json, flush=True) return json.dumps({'success':True}), 200, {'ContentType':'application/json'} @@ -134,7 +134,7 @@ app.get('/dapr/subscribe', (req, res) => { ]); }) -app.post('/dsstatus', (req, res) => { +app.post('/withdraw', (req, res) => { console.log(req.body); res.sendStatus(200); }); @@ -147,7 +147,7 @@ app.listen(port, () => console.log(`consumer app listening on port ${port}!`)) ```csharp [Topic("pubsub", "transactions", "event.type ==\"withdraw.v3\"", 1)] [HttpPost("withdraw.v3")] - public async Task> WithdrawV2(TransactionV3 transaction, [FromServices] DaprClient daprClient) + public async Task> WithdrawV3(TransactionV3 transaction, [FromServices] DaprClient daprClient) { // Logic return account; @@ -163,7 +163,7 @@ app.listen(port, () => console.log(`consumer app listening on port ${port}!`)) [Topic("pubsub", "transactions")] [HttpPost("withdraw")] - public async Task> WithdrawV2(Transaction transaction, [FromServices] DaprClient daprClient) + public async Task> Withdraw(Transaction transaction, [FromServices] DaprClient daprClient) { // Logic return account; @@ -251,7 +251,7 @@ $app = \Dapr\App::create(configure: fn(\DI\ContainerBuilder $builder) => $builde ] default: '/withdraw')), ]])); -$app->post('/dsstatus', function( +$app->post('/withdraw', function( #[\Dapr\Attributes\FromBody] \Dapr\PubSub\CloudEvent $cloudEvent, \Psr\Log\LoggerInterface $logger @@ -266,7 +266,7 @@ $app->start(); {{< /tabs >}} -In these examples, depending on the type of the event (`event.type`), the application will be called on `/dsstatus.v3`, `/dsstatus.v2` or `/dsstatus`. The expressions are written as [Common Expression Language (CEL)](https://opensource.google/projects/cel) where `event` represents the cloud event. Any of the attributes from the [CloudEvents core specification](https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#required-attributes) can be referenced in the expression. +In these examples, depending on the type of the event (`event.type`), the application will be called on `/withdraw.v3`, `/withdraw.v2` or `/withdraw`. The expressions are written as [Common Expression Language (CEL)](https://opensource.google/projects/cel) where `event` represents the cloud event. Any of the attributes from the [CloudEvents core specification](https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#required-attributes) can be referenced in the expression. For reference, the following attributes are from the CloudEvents specification. @@ -396,7 +396,7 @@ on the definition of OPTIONAL. For some binary mode protocol bindings, this field is directly mapped to the respective protocol's content-type metadata property. Normative rules for the binary mode and the content-type metadata mapping can be found in the - respective protocol + respective protocol. In some event formats the `datacontenttype` attribute MAY be omitted. For example, if a JSON format event has no `datacontenttype` attribute, then it is From df1d8e7d661f51c478681da98faaf0c5ff632a37 Mon Sep 17 00:00:00 2001 From: Javier Vela Date: Tue, 21 Sep 2021 18:14:28 +0200 Subject: [PATCH 3/8] azure queue add missing param decodeBase64 --- .../components-reference/supported-bindings/storagequeues.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/daprdocs/content/en/reference/components-reference/supported-bindings/storagequeues.md b/daprdocs/content/en/reference/components-reference/supported-bindings/storagequeues.md index 2c6f79d75..502eb78d0 100644 --- a/daprdocs/content/en/reference/components-reference/supported-bindings/storagequeues.md +++ b/daprdocs/content/en/reference/components-reference/supported-bindings/storagequeues.md @@ -30,6 +30,8 @@ spec: value: "myqueue" - name: ttlInSeconds value: "60" + - name: decodeBase64 + value: "false" ``` {{% alert title="Warning" color="warning" %}} @@ -44,6 +46,7 @@ The above example uses secrets as plain strings. It is recommended to use a secr | storageAccessKey | Y | Input/Output | The Azure Storage access key | `"accessKey"` | | queue | Y | Input/Output | The name of the Azure Storage queue | `"myqueue"` | | ttlInSeconds | N | Output | Parameter to set the default message time to live. If this parameter is omitted, messages will expire after 10 minutes. See [also](#specifying-a-ttl-per-message) | `"60"` | +| decodeBase64 | N | Output | Configuration to decode base64 file content before saving to Blob Storage. (In case of saving a file with binary content). `true` is the only allowed positive value. Other positive variations like `"True", "1"` are not acceptable. Defaults to `false` | `true`, `false` | ## Binding support From a927dd10286ce404c65481fc49aae88b2c76f02c Mon Sep 17 00:00:00 2001 From: Phil Kedy Date: Fri, 24 Sep 2021 18:10:00 -0400 Subject: [PATCH 4/8] * Tweaking the example to match what was demoed during the 9/21/2021 community call * Added example CEL expressions * Added link to 9/21/2021 community call youtube video * Added section headers for the right nav --- .../pubsub/howto-route-messages.md | 147 +++++++++++------- 1 file changed, 90 insertions(+), 57 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md index b69926997..d0dd2c702 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md +++ b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md @@ -42,14 +42,14 @@ metadata: name: myevent-subscription spec: pubsubname: pubsub - topic: transactions + topic: inventory routes: rules: - - match: event.type == "withdraw.v3" - path: /withdraw.v3 - - match: event.type == "withdraw.v2" - path: /withdraw.v2 - default: /withdraw + - match: event.type == "widget" + path: /widgets + - match: event.type == "gadget" + path: /gadgets + default: /products scopes: - app1 - app2 @@ -77,24 +77,24 @@ def subscribe(): subscriptions = [ { 'pubsubname': 'pubsub', - 'topic': 'transactions', + 'topic': 'inventory', 'routes': { 'rules': [ { - 'match': 'event.type == "withdraw.v3"', - 'path': '/withdraw.v3' + 'match': 'event.type == "widget"', + 'path': '/widgets' }, { - 'match': 'event.type == "withdraw.v2"', - 'path': '/withdraw.v2' + 'match': 'event.type == "gadget"', + 'path': '/gadgets' }, ], - 'default': '/withdraw' + 'default': '/products' } }] return jsonify(subscriptions) -@app.route('/withdraw', methods=['POST']) +@app.route('/products', methods=['POST']) def ds_subscriber(): print(request.json, flush=True) return json.dumps({'success':True}), 200, {'ContentType':'application/json'} @@ -113,30 +113,30 @@ app.use(bodyParser.json({ type: 'application/*+json' })); const port = 3000 app.get('/dapr/subscribe', (req, res) => { - res.json([ - { - pubsubname: "pubsub", - topic: "transactions", - routes: { - rules: [ - { - match: 'event.type == "withdraw.v3"', - path: '/withdraw.v3' - }, - { - match: 'event.type == "withdraw.v2"', - path: '/withdraw.v2' - }, - ], - default: '/withdraw' - } - } - ]); + res.json([ + { + pubsubname: "pubsub", + topic: "inventory", + routes: { + rules: [ + { + match: 'event.type == "widget"', + path: '/widgets' + }, + { + match: 'event.type == "gadget"', + path: '/gadgets' + }, + ], + default: '/products' + } + } + ]); }) -app.post('/withdraw', (req, res) => { - console.log(req.body); - res.sendStatus(200); +app.post('/products', (req, res) => { + console.log(req.body); + res.sendStatus(200); }); app.listen(port, () => console.log(`consumer app listening on port ${port}!`)) @@ -145,25 +145,25 @@ app.listen(port, () => console.log(`consumer app listening on port ${port}!`)) {{% codetab %}} ```csharp - [Topic("pubsub", "transactions", "event.type ==\"withdraw.v3\"", 1)] - [HttpPost("withdraw.v3")] - public async Task> WithdrawV3(TransactionV3 transaction, [FromServices] DaprClient daprClient) + [Topic("pubsub", "inventory", "event.type ==\"widget\"", 1)] + [HttpPost("widgets")] + public async Task> HandleWidget(Widget transaction, [FromServices] DaprClient daprClient) { // Logic return account; } - [Topic("pubsub", "transactions", "event.type ==\"withdraw.v2\"", 2)] - [HttpPost("withdraw.v2")] - public async Task> WithdrawV2(TransactionV2 transaction, [FromServices] DaprClient daprClient) + [Topic("pubsub", "inventory", "event.type ==\"gadget\"", 2)] + [HttpPost("gadgets")] + public async Task> HandleGadget(Gadget transaction, [FromServices] DaprClient daprClient) { // Logic return account; } - [Topic("pubsub", "transactions")] - [HttpPost("withdraw")] - public async Task> Withdraw(Transaction transaction, [FromServices] DaprClient daprClient) + [Topic("pubsub", "inventory")] + [HttpPost("products")] + public async Task> HandleProduct(Product transaction, [FromServices] DaprClient daprClient) { // Logic return account; @@ -208,19 +208,19 @@ func configureSubscribeHandler(w http.ResponseWriter, _ *http.Request) { t := []subscription{ { PubsubName: "pubsub", - Topic: "transactions", + Topic: "inventory", Routes: routes{ Rules: []rule{ { - Match: `event.type == "withdraw.v3"`, - Path: "/withdraw.v3", + Match: `event.type == "widget"`, + Path: "/widgets", }, { - Match: `event.type == "withdraw.v2"`, - Path: "/withdraw.v2", + Match: `event.type == "gadget"`, + Path: "/gadgets", }, }, - Default: "/withdraw", + Default: "/products", }, }, } @@ -244,14 +244,14 @@ func main() { require_once __DIR__.'/vendor/autoload.php'; $app = \Dapr\App::create(configure: fn(\DI\ContainerBuilder $builder) => $builder->addDefinitions(['dapr.subscriptions' => [ - new \Dapr\PubSub\Subscription(pubsubname: 'pubsub', topic: 'transactions', routes: ( + new \Dapr\PubSub\Subscription(pubsubname: 'pubsub', topic: 'inventory', routes: ( rules: => [ - ('match': 'event.type == "withdraw.v3"', path: '/withdraw.v3'), - ('match': 'event.type == "withdraw.v2"', path: '/withdraw.v2'), + ('match': 'event.type == "widget"', path: '/widgets'), + ('match': 'event.type == "gadget"', path: '/gadgets'), ] - default: '/withdraw')), + default: '/products')), ]])); -$app->post('/withdraw', function( +$app->post('/products', function( #[\Dapr\Attributes\FromBody] \Dapr\PubSub\CloudEvent $cloudEvent, \Psr\Log\LoggerInterface $logger @@ -266,7 +266,34 @@ $app->start(); {{< /tabs >}} -In these examples, depending on the type of the event (`event.type`), the application will be called on `/withdraw.v3`, `/withdraw.v2` or `/withdraw`. The expressions are written as [Common Expression Language (CEL)](https://opensource.google/projects/cel) where `event` represents the cloud event. Any of the attributes from the [CloudEvents core specification](https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#required-attributes) can be referenced in the expression. +## Common Expression Language (CEL) + +In these examples, depending on the type of the event (`event.type`), the application will be called on `/widgets`, `/gadgets` or `/products`. The expressions are written as [Common Expression Language (CEL)](https://github.com/google/cel-spec) where `event` represents the cloud event. Any of the attributes from the [CloudEvents core specification](https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#required-attributes) can be referenced in the expression. + +### Example expressions + +Match "important" messages + +```javascript +has(event.data.important) && event.data.important == true +``` + +Match deposits greater than $10000 + +```javascript +event.type == "deposit" && event.data.amount > 10000 +``` + +Match multiple versions of a message + +```javascript +event.type == "mymessage.v1" +``` +```javascript +event.type == "mymessage.v2" +``` + +## CloudEvent attributes For reference, the following attributes are from the CloudEvents specification. @@ -476,7 +503,13 @@ Currently, comparisons to time (e.g. before or after "now") are not supported. ## Next steps -- Try the [Pub/Sub quickstart sample](https://github.com/dapr/quickstarts/tree/master/pub-sub) +Watch [this video](https://www.youtube.com/watch?v=QqJgRmbH82I&t=1063s) on how to use message routing with pub/sub. + +

+ +

+ +- Try the [Pub/Sub routing sample](https://github.com/dapr/samples/tree/master/pub-sub-routing) - Learn about [topic scoping]({{< ref pubsub-scopes.md >}}) - Learn about [message time-to-live]({{< ref pubsub-message-ttl.md >}}) - Learn [how to configure Pub/Sub components with multiple namespaces]({{< ref pubsub-namespaces.md >}}) From c44a9406041ae69d677e9a69c2531b288104ba38 Mon Sep 17 00:00:00 2001 From: Phil Kedy Date: Sat, 25 Sep 2021 10:59:07 -0400 Subject: [PATCH 5/8] Moving the community call demo video to its own section --- .../building-blocks/pubsub/howto-route-messages.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md index d0dd2c702..7c8d28a77 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md +++ b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md @@ -501,14 +501,16 @@ on the definition of OPTIONAL. Currently, comparisons to time (e.g. before or after "now") are not supported. {{% /alert %}} -## Next steps +## Community call demo -Watch [this video](https://www.youtube.com/watch?v=QqJgRmbH82I&t=1063s) on how to use message routing with pub/sub. +Watch [this video](https://www.youtube.com/watch?v=QqJgRmbH82I&t=1063s) on how to use message routing with pub/sub:

+## Next steps + - Try the [Pub/Sub routing sample](https://github.com/dapr/samples/tree/master/pub-sub-routing) - Learn about [topic scoping]({{< ref pubsub-scopes.md >}}) - Learn about [message time-to-live]({{< ref pubsub-message-ttl.md >}}) From ca8887a83aa50760126632488635d65798762e9d Mon Sep 17 00:00:00 2001 From: Phil Kedy Date: Sat, 25 Sep 2021 11:10:55 -0400 Subject: [PATCH 6/8] Fixing broken cloudevents links --- .../building-blocks/pubsub/howto-route-messages.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md index 7c8d28a77..75433a8b1 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md +++ b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md @@ -390,7 +390,7 @@ The following attributes are REQUIRED to be present in all CloudEvents: routing, observability, policy enforcement, etc. The format of this is producer defined and might include information such as the version of the `type` - see - [Versioning of CloudEvents in the Primer](primer.md#versioning-of-cloudevents) + [Versioning of CloudEvents in the Primer](https://github.com/cloudevents/spec/blob/v1.0.1/primer.md#versioning-of-cloudevents) for more information. - Constraints: - REQUIRED @@ -404,7 +404,7 @@ The following attributes are REQUIRED to be present in all CloudEvents: ### OPTIONAL Attributes The following attributes are OPTIONAL to appear in CloudEvents. See the -[Notational Conventions](#notational-conventions) section for more information +[Notational Conventions](https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#notational-conventions) section for more information on the definition of OPTIONAL. #### datacontenttype @@ -413,12 +413,12 @@ on the definition of OPTIONAL. - Description: Content type of `data` value. This attribute enables `data` to carry any type of content, whereby format and encoding might differ from that of the chosen event format. For example, an event rendered using the - [JSON envelope](./json-format.md#3-envelope) format might carry an XML payload + [JSON envelope](https://github.com/cloudevents/spec/blob/v1.0.1/json-format.md#3-envelope) format might carry an XML payload in `data`, and the consumer is informed by this attribute being set to "application/xml". The rules for how `data` content is rendered for different `datacontenttype` values are defined in the event format specifications; for example, the JSON event format defines the relationship in - [section 3.1](./json-format.md#31-handling-of-data). + [section 3.1](https://github.com/cloudevents/spec/blob/v1.0.1/json-format.md#31-handling-of-data). For some binary mode protocol bindings, this field is directly mapped to the respective protocol's content-type metadata property. Normative rules for the @@ -447,7 +447,7 @@ on the definition of OPTIONAL. - Type: `URI` - Description: Identifies the schema that `data` adheres to. Incompatible changes to the schema SHOULD be reflected by a different URI. See - [Versioning of CloudEvents in the Primer](primer.md#versioning-of-cloudevents) + [Versioning of CloudEvents in the Primer](https://github.com/cloudevents/spec/blob/v1.0.1/primer.md#versioning-of-cloudevents) for more information. - Constraints: - OPTIONAL From 8f43af5199d1dc9e16a502cc39148847da51f3f4 Mon Sep 17 00:00:00 2001 From: Phil Kedy Date: Sat, 25 Sep 2021 11:16:05 -0400 Subject: [PATCH 7/8] Updating C# example --- .../building-blocks/pubsub/howto-route-messages.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md index 75433a8b1..5fac6c647 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md +++ b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-route-messages.md @@ -147,26 +147,26 @@ app.listen(port, () => console.log(`consumer app listening on port ${port}!`)) ```csharp [Topic("pubsub", "inventory", "event.type ==\"widget\"", 1)] [HttpPost("widgets")] - public async Task> HandleWidget(Widget transaction, [FromServices] DaprClient daprClient) + public async Task> HandleWidget(Widget widget, [FromServices] DaprClient daprClient) { // Logic - return account; + return stock; } [Topic("pubsub", "inventory", "event.type ==\"gadget\"", 2)] [HttpPost("gadgets")] - public async Task> HandleGadget(Gadget transaction, [FromServices] DaprClient daprClient) + public async Task> HandleGadget(Gadget gadget, [FromServices] DaprClient daprClient) { // Logic - return account; + return stock; } [Topic("pubsub", "inventory")] [HttpPost("products")] - public async Task> HandleProduct(Product transaction, [FromServices] DaprClient daprClient) + public async Task> HandleProduct(Product product, [FromServices] DaprClient daprClient) { // Logic - return account; + return stock; } ``` {{% /codetab %}} From 019ad846ea664f4cd8a42c925d1caec46a742e22 Mon Sep 17 00:00:00 2001 From: Lorenzo Montanari Date: Mon, 27 Sep 2021 14:52:36 +0200 Subject: [PATCH 8/8] Some fixes in secrets_api docs - Fixed some formattation issues - Removed erroneous backslashes from command snippets --- daprdocs/content/en/reference/api/secrets_api.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/daprdocs/content/en/reference/api/secrets_api.md b/daprdocs/content/en/reference/api/secrets_api.md index e0a447535..84c39467d 100644 --- a/daprdocs/content/en/reference/api/secrets_api.md +++ b/daprdocs/content/en/reference/api/secrets_api.md @@ -95,14 +95,14 @@ Code | Description ### Examples ```shell -curl http://localhost:3500/v1.0/secrets/vault/db-secret \ +curl http://localhost:3500/v1.0/secrets/vault/db-secret ``` ```shell -curl http://localhost:3500/v1.0/secrets/vault/db-secret?metadata.version_id=15&metadata.version_stage=AAA \ +curl http://localhost:3500/v1.0/secrets/vault/db-secret?metadata.version_id=15&metadata.version_stage=AAA ``` -> Note, in case of deploying into namespace other than default`, the above query will also have to include the namespace metadata (e.g. `production` below) +> Note, in case of deploying into namespace other than default, the above query will also have to include the namespace metadata (e.g. `production` below) ```shell curl http://localhost:3500/v1.0/secrets/vault/db-secret?metadata.version_id=15&?metadata.namespace=production @@ -165,7 +165,7 @@ Code | Description ### Examples ```shell -curl http://localhost:3500/v1.0/secrets/vault/bulk \ +curl http://localhost:3500/v1.0/secrets/vault/bulk ``` ```json