Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define CAMARA Event using cloudevents in OAS notification #43

Merged
merged 28 commits into from
Dec 4, 2023
Merged
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3dd803c
feat(cloud-event): First try to define CAMARA Event using cloudevents…
patrice-conil Jul 26, 2023
8a11ac5
Rewrite sample to include reverse DNS prefix into 'type' and design '…
patrice-conil Jul 27, 2023
339a8e2
Add new response codes to be cloudevents compliant and rename endpoin…
patrice-conil Jul 31, 2023
e2418d3
Integrates remarks from @PedroDiez
patrice-conil Aug 2, 2023
0c6c11f
Update artifacts/notification-as-cloud-event.yaml
patrice-conil Sep 5, 2023
a97bb67
Update artifacts/notification-as-cloud-event.yaml
patrice-conil Sep 5, 2023
9226178
Integrates @PedroDiez comments.
patrice-conil Sep 5, 2023
234cfdb
Integrates comments from reviews.
patrice-conil Sep 18, 2023
8922567
Integrate comments from @jlurien review.
patrice-conil Sep 19, 2023
950efcd
Change org.camara into org.camaraproject to be aligned with #56
patrice-conil Sep 19, 2023
7904ec2
Remove subject field to be aligned with DG
patrice-conil Sep 19, 2023
fd986cd
Add a comment about the security system
patrice-conil Oct 20, 2023
2420cf3
fix(security): Add {} to support no security.
patrice-conil Oct 30, 2023
2eb7b7d
Update artifacts/notification-as-cloud-event.yaml
patrice-conil Nov 10, 2023
b7eb630
Update artifacts/notification-as-cloud-event.yaml
patrice-conil Nov 10, 2023
248e442
style(cloudevent): Add missing quote in Source description
patrice-conil Nov 13, 2023
14b8650
Update artifacts/notification-as-cloud-event.yaml
patrice-conil Nov 13, 2023
441360c
style(cloudevent): Fix example in source
patrice-conil Nov 13, 2023
21711ac
style(cloudevent): Fix examples
patrice-conil Nov 13, 2023
e90cdd7
style(cloudevent): Revert datacontenttype to application/json
patrice-conil Nov 13, 2023
338a4a2
style(cloudevent): Close backtick in comment
patrice-conil Nov 15, 2023
3b19bae
style(cloudevent): Change source uri in sample
patrice-conil Nov 16, 2023
fec372a
style(cloudevent): Add notificationsBearerAuth security scheme
patrice-conil Nov 16, 2023
430f4ca
style(cloudevent): Remove multi-example to comply with OAS 3.0
patrice-conil Nov 16, 2023
984794b
Add time as mandatory attribute
patrice-conil Nov 20, 2023
2d4683a
style(cloudevent): Remove data field from CloudEvent
patrice-conil Nov 24, 2023
e5ced0d
Merge remote-tracking branch 'patrice/cloud_events' into cloud_events
patrice-conil Nov 24, 2023
429a7e3
style(cloudevent): Revert commit to keep data field as optional in Cl…
patrice-conil Nov 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 287 additions & 0 deletions artifacts/notification-as-cloud-event.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
openapi: 3.0.3
info:
title: Event Notification using CloudEvents specifications
description: |
The event notification endpoint is used by the API server to notify the API consumer that an event occurred. The notification is the message posted on listener side.

# Introduction

A lot of CAMARA APIs offer the capability to API consumer to receive events.
Event data are defined in each API definition but in order to provide consistency across CAMARA APIs and to increase
interoperability we will use [cloudevents](https://cloudevents.io/) specifications. In particular, every CAMARA Event will
be defined using [cloudevents-json-format](https://github.com/cloudevents/spec/blob/main/cloudevents/formats/json-format.md)

# Relevant terms and definitions

* **Occurrence** : An "occurrence" is the capture of a statement of fact during the operation of a software system.

* **Event**: An "event" is a data record expressing an occurrence and its context. Events are routed from an
event producer (the source) to interested event consumers.

* **Producer**: The "producer" is a specific instance, process or device that creates the data structure
describing the CloudEvent.

* **Source**: The "source" is the context in which the occurrence happened. In a distributed system it might
consist of multiple producers. If a source is not aware of CloudEvents, an external producer creates
the CloudEvent on behalf of the source.

* **Consumer**: A "consumer" receives the event and acts upon it. It uses the context and data to execute some
logic, which might lead to the occurrence of new events.

* **Data**: Domain-specific information about the occurrence (i.e. the payload). This might
include information about the occurrence, details about the data that was changed, or more.

# API Functionality

Only one endpoint/operation is provided: `POST /your_webhook_notification_url`
This endpoint describes the event notification received on subscription listener side when the event occurred. A detailed description of the event notification is provided in the CAMARA API design guideline document.

termsOfService: http://swagger.io/terms/
contact:
email: [email protected]
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
version: 0.1.0
externalDocs:
description: Product documentation at CAMARA
url: https://github.com/camaraproject/
security:
- notificationsBearerAuth: []
- {}

servers:
- url: '{apiRoot}'
variables:
apiRoot:
default: https://localhost:8080
description: Can be any notification server address sent by the client application
tags:
- name: Cloud Event
description: |
Events received on subscription listener side.
paths:
/your_webhook_notification_url:
post:
tags:
- CAMARA Cloud Event notification endpoint
summary: "Cloud Event notification endpoint to notify consumer that statement of fact had occurred"
description: |
INFORMATIVE ENDPOINT: The value of this endpoint is freely declared by each client app by means of resource-based
rartych marked this conversation as resolved.
Show resolved Hide resolved
subscription or instance-based subscription. /your_webhook_notification_url is
just a convention naming referring to an absolute URL, indeed the one indicated by API client
in the triggering of the procedure (resource-based or instance-based). In this way, it represents an absolute
URL, i.e.: notifications won't be sent to /event-notification/vX/your_webhook_notification_url.
operationId: sendEvent
requestBody:
required: true
content:
application/cloudevents+json:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is correct but I think someone complained about lack of support by certain code generators. We could stick to just application/json if it is safer

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to CloudEvents discussion format of "+json" is defined in RFC6839, so it should be supported by code generators.

I propose to follow the CloudEvents requirement for JSON format:

Such a representation MUST use the media type application/cloudevents+json

schema:
$ref: "#/components/schemas/CloudEvent"
examples:
QOS_STATUS_CHANGED_EXAMPLE:
$ref: '#/components/examples/QOS_STATUS_CHANGED_EXAMPLE'

responses:
201:
description: Created
202:
description: Accepted
204:
description: No Content
200:
description: Ok
content:
application/json:
schema:
type: object
400:
$ref: "#/components/responses/Generic400"
401:
$ref: "#/components/responses/Generic401"
403:
$ref: "#/components/responses/Generic403"
410:
$ref: "#/components/responses/Generic410"
415:
$ref: "#/components/responses/Generic415"
429:
$ref: "#/components/responses/Generic429"
500:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic500 can be used

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

$ref: "#/components/responses/Generic500"
503:
$ref: "#/components/responses/Generic503"
rartych marked this conversation as resolved.
Show resolved Hide resolved
components:
securitySchemes:
notificationsBearerAuth:
type: http
scheme: bearer
bearerFormat: "{$request.body#/webhook/notificationAuthToken}"
schemas:
ErrorInfo:
type: object
required:
- status
- code
- message
properties:
status:
type: integer
description: HTTP response status code
code:
type: string
description: Code given to this error
message:
type: string
description: Detailed error description

DateTime:
type: string
format: date-time
description: |
Timestamp of when the occurrence happened. Must adhere to RFC 3339.
WARN: This optional field in CloudEvents specification is required in CAMARA APIs implementation.
example: '2018-04-05T17:31:00Z'

Source:
type: string
format: uri-reference
minLength: 1
description: |
Identifies the context in which an event happened - be a non-empty `URI-reference` like:
- URI with a DNS authority:
* https://github.com/cloudevents
* mailto:[email protected]
- Universally-unique URN with a UUID:
* urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66
- Application-specific identifier:
* /cloudevents/spec/pull/123
* 1-555-123-4567
example: "https://notificationSendServer12.supertelco.com"

CloudEvent:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also add a discriminator explicitly to this base class schema

      discriminator:
        propertyName: type
        mapping:
          CHILD_EVENT_NAME: "#/components/schemas/ChildEventWithData"
`` 

description: The notification callback
required:
- id
- source
rartych marked this conversation as resolved.
Show resolved Hide resolved
- specversion
- type
patrice-conil marked this conversation as resolved.
Show resolved Hide resolved
- time
properties:
rartych marked this conversation as resolved.
Show resolved Hide resolved
id:
type: string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as long the id MUST be a non-empty string - I also recommend the constraints "minLength" here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the format agreed is org.camaraproject...
recommend to increase to "25" -> org.camaraproject.qod.v0.

description: identifier of this event, that must be unique in the source context.
minLength: 1
source:
$ref: '#/components/schemas/Source'
type:
type: string
rartych marked this conversation as resolved.
Show resolved Hide resolved
description: 'type of event as defined in each CAMARA API (e.g.: org.camaraproject.qod.qos-status-changed-event)'
example: 'org.camaraproject.qod.qos-status-changed-event'
minLength: 25
specversion:
type: string
rartych marked this conversation as resolved.
Show resolved Hide resolved
description: Version of the specification to which this event conforms (must be 1.0 if it conforms to cloudevents 1.0.2 version)
minLength: 3
datacontenttype:
type: string
description: 'media-type that describes the event payload encoding, must be "application/json" for CAMARA APIs'
data:
rartych marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a discussion in camaraproject/QualityOnDemand#224 (comment), about whether 1) to define data in the base class, as optional and with a generic type: object, or 2) to define data only in the child events which has data. Both would be correct, but not sure about which one would work better with code generators.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not have experience with code generators, but currently we do not have any example of subproject using events without data object. So for simplicity I prefer approach 1) where we do not have to define child object ChildEventWithData in each subproject using eventing, but data is in base class as optional.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed for that reason we have to define "a ChildEventWithData" schema, including data for each Child event. The name is just a templated one, real names we already have, among others:

QoD:

 EventQosStatusChanged:
      allOf:
        - $ref: "#/components/schemas/CloudEvent"
        - type: object
          properties:
            data: {}

DeviceStatus:

    EventRoamingStatus:
      description: event structure for roaming status change
      allOf:
        - $ref: "#/components/schemas/CloudEvent"
        - type: object
          properties:
            data:
              $ref: "#/components/schemas/RoamingStatus"

    EventRoamingOn:
      description: event structure for roaming on change
      allOf:
        - $ref: "#/components/schemas/CloudEvent"
        - type: object
          properties:
            data:
              $ref: "#/components/schemas/BasicDeviceEventData"

so we have to define the data per child event mandatorily always. The question was whether we should have to define it also in the base schema as optional.

Copy link
Collaborator Author

@patrice-conil patrice-conil Nov 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jlurien, I agree with you, from an OO perspective we don't need to define it in the base schema.
I'm undecided because optional data field can also be considered a placeholder for what to extend in child classes.
But as you are the first that use this template in QoD and don't find it so useful, I will remove it from base class.
@rartych, both versions works with code generators.
I think defining ChildEventWithData add unnecessary layer in model, I just added a comment in CloudEvent description. WDYT about?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far, it is kept as optional in the CloudEvent base class. I think this is what get more consensus. If both work well with code generators, they are totally equivalent and the final schema once the composition is applied is the same.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with both approaches. API Design Guidelines are "normative" for CAMARA, here we provide example that can be used by subprojects and API Consumers, but other definitions can be used if they conform to API Design Guidelines and CloudEvents specification.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see: #43 (comment)

type: object
description: Event details payload described in each CAMARA API and referenced by its type
time:
$ref: "#/components/schemas/DateTime"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make the template even more useful, I would add also a sample schema for children events something like:

    ChildEventWithData:
      allOf:
        - $ref: "#/components/schemas/CloudEvent"
        - type: object
          properties:
            data:
              type: object
              description: Event details depending on the event type
              properties: {}
              required: {}
          required:
            - data

responses:
Generic400:
description: Problem with the client request
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorInfo"
example:
status: 400
code: "INVALID_ARGUMENT"
message: "Client specified an invalid argument, request body or query param"
Generic401:
description: Authentication problem with the client request
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorInfo"
example:
status: 401
code: "UNAUTHENTICATED"
message: "Request not authenticated due to missing, invalid, or expired credentials"
Generic403:
description: Client does not have sufficient permission
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorInfo"
example:
status: 403
code: "PERMISSION_DENIED"
message: "Client does not have sufficient permissions to perform this action"
Generic410:
description: Gone
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorInfo"
example:
code: GONE
message: "Access to the target resource is no longer available."
Generic415:
description: Unsupported Media Type
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorInfo"
example:
code: UNSUPPORTED_MEDIA_TYPE
message: "The specified media type is not supported"
Generic429:
rartych marked this conversation as resolved.
Show resolved Hide resolved
description: Too Many Requests
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorInfo"
example:
code: TOO_MANY_REQUESTS
message: "Endpoint does not support as many requests per time slot"
Generic500:
rartych marked this conversation as resolved.
Show resolved Hide resolved
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorInfo"
example:
status: 500
code: "INTERNAL"
message: "Server error"
Generic503:
description: Service unavailable. Typically the server is down.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorInfo"
example:
status: 503
code: "UNAVAILABLE"
message: "Service unavailable"
examples:
QOS_STATUS_CHANGED_EXAMPLE:
value:
id: "123e4567-e89b-12d3-a456-426655440000"
source: "https://notificationSendServer12.supertelco.com"
type: "org.camaraproject.qod.qos-status-changed-event"
specversion: "1.0"
time: "2023-01-17T13:18:23.682Z"
datacontenttype: "application/json"
data:
sessionId: "6e8bc430-9c3a-11d9-9669-0800200c9a66"
qosStatus: UNAVAILABLE
statusInfo: DURATION_EXPIRED