Skip to content

Prototype of a delegating CloudEventFormatter #322

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

jskeet
Copy link
Contributor

@jskeet jskeet commented May 13, 2025

This is the proposed (but certainly not finalized) approach for #321.

This is the proposed (but certainly not finalized) approach for cloudevents#321.
@jskeet jskeet requested a review from captainsafia May 13, 2025 10:02
@jskeet
Copy link
Contributor Author

jskeet commented May 13, 2025

(I'd forgotten about signing-off the commit, but I haven't updated it, because we definitely don't want to merge it in the current state...)

// - All documentation
// - We can't call original.InferContentType, because it's protected. Is that an issue? (Users can always override GetOrInferContentType instead.)
// - Are we happy with this being public? With the extension methods being public, this class doesn't have to be.
// - Are we happy with the extension methods being extension methods?
Copy link
Contributor

Choose a reason for hiding this comment

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

Public extension methods + internal class seems like a good conservative base to start from. We can always expand the API surface later as needed.

// TODO:
// - Naming:
// - Is the namespace appropriate? We have very few types in this root namespace
// - Delegating or Decorating?
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm torn between the two but I think Delegating is the right way to go here. Decorating implies that data is modified/enhanced in some way whereas delegating does a better job of describing it's strictly behavioral, IMO.

@lailabougria
Copy link

@jskeet First of all, my apologies for taking so long. I basically dissapeared for a couple of weeks of back-to-back conferences.

I do have a couple of questions as I'm new to the code base and there aren't any tests that show how this API is to be used, so apologies if they're straightforward:

  • Based on the fact that the CloudEventFormatters is public, my assumption is that this API is to be used by users to register validation actions?
  • Does this mean the CreateExtension method would change, removing the validationAction from the current API?
  • Currently it looks like a user would call WithValidation only once, and the action should contain all validations for that specific type of CloudEvent. Given that currently, validation actions are registered per extension, I was wondering what the underlying thought process was. As a user, I liked the seperation of validation action per extension as it keeps that code smaller and focussed, although the downside is that if there are cross-extension validations (validation applying to multiple extensions at a time) it becomes trickier.

@jskeet
Copy link
Contributor Author

jskeet commented Jun 12, 2025

Based on the fact that the CloudEventFormatters is public, my assumption is that this API is to be used by users to register validation actions?

Well, to create new CloudEventFormatter instances which do perform validation. (It's not used to modify any existing instances.)

@captainsafia's suggestion is that the extension methods there should be the only public API surface, at least for the moment.

Does this mean the CreateExtension method would change, removing the validationAction from the current API?

No, they're performing different kinds of validation. CloudEventAttribute.CreateExtension can specify validation that is just at the scope of "is this value valid for this extension" whereas CloudEventFormatters.WithValidation is intended to validate an overall CloudEvent being formatted/parsed by a CloudEventFormatter.

Currently it looks like a user would call WithValidation only once, and the action should contain all validations for that specific type of CloudEvent.

Yes. The intention is there are two different type of validation:

  • CloudEventAttribute-specific: "Is the given value valid for this CloudEvent"
  • CloudEvent-wide: "Does this CloudEvent have everything I expect it to" - which would include whether "required" extensions are all present

If an extension attribute is required, that can only be validated if some validator knows about it - so if a CloudEventFormatter is provided with a CloudEvent which doesn't include the attribute, and asked to encode it, how could it know that the extension attribute which is missing should be present? I think that has to be known by the CloudEventFormatter.

For the very narrow situation of "I only care about decoding, and I only care about independently-required attributes" it would be possible to let CloudEventFormatter look at the extension attributes provided on the decode call - but that wouldn't help with cases such as "Either both X and Y must be specified, or neither" which are easliy handled by the CloudEvent-wide approach in this PR.

@lailabougria
Copy link

@jskeet Thanks for clarifying, that makes sense.

I appreciate keeping these types of validations seperate, that's ideal.
I look forward to see this being included as it would help to address the use cases I had in mind.

Thank you!

@jskeet
Copy link
Contributor Author

jskeet commented Jun 19, 2025

@lailabougria Great, thanks. I'll turn this into a proper PR with documentation and tests when I have the time... but at the moment, that could be in quite a while.

Until I've got time to put this into a submittable state (unless you have the time to work on it yourself, of course) - you could potentially copy this code into your own codebase, and then just drop it when it's part of the SDK. It's only additive, and only uses public members as far as I can remember.

@lailabougria
Copy link

@jskeet My summer vacation is about to start, but I will look at it when I return if you haven't had the chance to start yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants