Skip to content

Fix not to render optional fields when unset #55

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

Merged
merged 8 commits into from
May 31, 2023

Conversation

rjalander
Copy link
Contributor

This PR has the changes to fix the issue #51, not to render the optional fields when null during serialization using an ObjectMapper.

@rjalander rjalander requested a review from afrittoli May 24, 2023 09:59
Comment on lines 53 to 61
String cdEventJson = CDEvents.cdEventAsJson(cdEvent);

CloudEvent ceEvent = CDEvents.cdEventAsCloudEvent(cdEvent);

String ceDataJson = new String(ceEvent.getData().toBytes(), StandardCharsets.UTF_8);

assertThat(ceEvent.getType()).isEqualTo(cdEvent.getContext().getType());
assertThat(ceEvent.getSource()).isEqualTo(cdEvent.getContext().getSource());
assertThat(ceDataJson).isEqualTo(cdEventJson);
Copy link
Contributor

Choose a reason for hiding this comment

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

If I understand the test correctly, what it does it:

  • create a CDEvent object, renders it to json
  • create a CloudEvent out of the CDEvent
  • extract the payload from the CloudEvent and matches is against the CDEvent as json
  • compare type and source

This looks good to test check CloudEvent binding, but how does it help to test the unset fields?
Don't we need JSON documents as test data for that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, added test data to compare optional fields with the actual CDEvent.
To keep this PR smaller, added the optional field validation only for Core events for now, will create separate PRs to handle tests for other type of events.

@Test
void createTaskRunFinishedEventWithOptionalFieldsUnset() throws IOException {
InputStream inputStream = getClass().getResourceAsStream("/taskrun_finished_optional.json");
TaskRunFinishedCDEvent expectedCDEvent = objectMapper.readValue(inputStream, TaskRunFinishedCDEvent.class);
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 can be ok, it guarantees consistent behaviour, however, I'm not 100% it guarantees that optional fields are not included in the rendered JSON.

At this level, I know that:

  • if I create an object without optional fields, those optional fields are not set in the object.
  • if I render that object without optional fields and parse it back, those optional fields are not set in the object.
  • If a load an object from JSON that does not specify optional fields, those optional fields are not set in the object

I'm still not 100% sure that the rendered JSON will not contain the optional fields set as well, but perhaps that's covered already by other tests - wdyt?

I see two possible ways we could test that further - possibly not for every event, just once might be enough.

  • either read bytes from inputStream and compare it with cdEventJson converted to bytes. This can be tricky since you may need to compact the json first (to avoid issues with spaces, indentations etc) and also ordering in dicts may be an issue unless the json rendered implementation always renders in the same order
  • or add one resource file with the optional fields set to empty value. Load it and check that the optional fields are in the object, with empty or null value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it compares the Subject's content object as whole but not field to filed comparison.
Or I can compare from both the objects that all optional fields are set to null and they are equal, something like;

assertThat(cdEvent.getSubject().getContent().getTaskName()).isEqualTo(null);
assertThat(expectedCDEvent.getSubject().getContent().getTaskName()).isEqualTo(null);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now updated the test validation by comparing the JsonNodes created from inputStream and cdEventJson
This way I can compare actual and expected optional and mandatory fields from the test data.

cdEvent.setSubjectPipelineRunId("/dev/pipeline/run/subject");

String cdEventJson = CDEvents.cdEventAsJson(cdEvent);
CloudEvent ceEvent = CDEvents.cdEventAsCloudEvent(cdEvent);
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really need to go through the cloudevent binding in this test?
It does not harm but I thought that would be covered already by previous tests.
It's fine to keep it if you deem it relevant here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes to validate the CDEvent against schema, I need to invoke CDEvents.cdEventAsCloudEvent(cdEvent);

But I think I can move validation part to the CDEvents.cdEventAsJson(cdEvent); method itself as cdEventAsJson() is invoked from cdEventAsCloudEvent() to set the data in the CloudEvent. So that I can remove cloudevent binding from this test also.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the clarification!

@rjalander rjalander requested a review from afrittoli May 30, 2023 12:48

PipelineRunFinishedSubjectContent that = (PipelineRunFinishedSubjectContent) o;

if (getPipelineName() != null ? !getPipelineName().equals(that.getPipelineName()) : that.getPipelineName() != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: Since pipeline name is a string, this could be if (getPipelineName() == that.getPipelineName()) which would return true also in case they're both null

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now updated the test validation by comparing the JsonNodes, equals method is no longer needed.

@rjalander rjalander requested a review from afrittoli May 31, 2023 14:36
@@ -36,6 +36,10 @@ private CDEvents() {
*/
public static String cdEventAsJson(CDEvent cdEvent) {
try {
if (!validateCDEvent(cdEvent)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks - I think this is fine, perhaps in future, we might add a flag to make validation optional.
In the golang SDK I did not include validation in the JSON rendering, I made a validate method available instead, so that it is possible to render and invalid CDEvent to JSON, and the producer can decide whether to validate or not.

I did include a call to validate in the cloudevents binding to enforce sending of valid events thought (similar to what you had before).

I wonder whether instead of moving the validation from cdEventAsCloudEvent to cdEventAsJson, it would be better to add a call to validate in the test?

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 also ok with keeping it as it is today.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree, even this keeps the behavior consistent.
Reverting to keep the validation only for CloudEvents binding, removed for JSON render and added test assertion to validate CDEvent against schema.

Copy link
Contributor

@afrittoli afrittoli left a comment

Choose a reason for hiding this comment

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

Thanks for the updates!

Copy link
Contributor

@afrittoli afrittoli left a comment

Choose a reason for hiding this comment

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

Thanks for the updates!

@afrittoli
Copy link
Contributor

@rjalander is it ok if squash and merge now, or would you like more reviews from other Java SDK maintainers?

@rjalander
Copy link
Contributor Author

I think we are good to merge, If you have no other comments.

@afrittoli afrittoli merged commit 1d42acc into cdevents:main May 31, 2023
@cdevents-bot cdevents-bot added the released Issue has been released label Jul 24, 2023
@cdevents-bot
Copy link
Collaborator

🎉 This issue has been resolved in v0.1.2 (Release Notes)

@cdevents-bot cdevents-bot added this to the v0.1.2 milestone Jul 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
released Issue has been released
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants