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

Support for creating Custom Event Types with schemas validation #83

Open
wants to merge 26 commits into
base: main
Choose a base branch
from

Conversation

rjalander
Copy link
Contributor

@rjalander rjalander commented Jul 3, 2024

Changes to add APIs support to create customCDEventAsCloudEvent and customCDEventFromJson
also includes the validation of an official custom schema.json

Note:
Custom event support as per SDK support
Created sample CustomResourceCDEvent.java to test APIs as per example

Jalander Ramagiri added 14 commits April 29, 2024 14:28
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
@rjalander rjalander requested review from aalmiray and afrittoli July 3, 2024 16:27
Jalander Ramagiri added 2 commits July 4, 2024 11:27
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
@rjalander rjalander requested a review from xibz July 4, 2024 13:36
String eventType = getUnVersionedEventTypeFromJson(cdEventJson);
CDEventConstants.CDEventTypes cdEventType = getCDEventTypeEnum(eventType);
public static <T extends CDEvent> CloudEvent customCDEventAsCloudEvent(T customCDEvent, boolean validateContextSchema) {
Optional.of(customCDEvent)
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 not so sure about the use of Optional here instead of a null and validation check

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah make sense

* @return valid cdEvent
*/
public static boolean validateWithContextSchemaUri(CDEvent customCDEvent) {
return Optional.ofNullable(customCDEvent.customSchemaUri())
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as previous comment on Optional

Signed-off-by: Jalander Ramagiri <[email protected]>
Copy link

@xibz xibz left a comment

Choose a reason for hiding this comment

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

@rjalander Overall looks good. Most comments are fairly minor with one suggestion of how validation should return errors to be populated in the exception message (even add a CDEventsValidationException

"customDataContentType"
})
@Generated("jsonschema2pojo")
public class Customresourcecreated {
Copy link

Choose a reason for hiding this comment

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

This is a very odd class name. The lack of camel casing I mean? I know it's a test class, but just seems odd

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah noticed, but this is generated from jsonschema2pojo
Anyway removed this class as per the latest approach.


private static CloudEvent buildCloudEvent(CDEvent cdEvent) throws URISyntaxException {
String cdEventJson = cdEventAsJson(cdEvent);
log.info("CDEvent with type {} as json - {}", cdEvent.currentCDEventType(), cdEventJson);
Copy link

@xibz xibz Jul 8, 2024

Choose a reason for hiding this comment

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

Hmm, I wonder if this should be a debug log with the raw json in there. It seems overkill with it for info. How do you feel about changing this to log.debug for the JSON and having the log.info just having the CDEvent with type {}

String eventType = getUnVersionedEventTypeFromJson(cdEventJson);
CDEventConstants.CDEventTypes cdEventType = getCDEventTypeEnum(eventType);
try {
CDEvent cdEvent = (CDEvent) new ObjectMapper().readValue(cdEventJson, cdEventType.getEventClass());
CDEvent cdEvent = new ObjectMapper().readValue(cdEventJson, cdEventType.getEventClass());
Copy link

Choose a reason for hiding this comment

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

Why are we constructing a new object mapper every time?
Is this method only ever called once? Even so we probably should move it to a field

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed this method using the existing APIs

CDEventConstants.CDEventTypes cdEventType = getCDEventTypeEnum(eventType);
public static <T extends CDEvent> CloudEvent customCDEventAsCloudEvent(T customCDEvent, boolean validateContextSchema) {
if (!validateCustomCDEvent(customCDEvent, validateContextSchema)) {
throw new CDEventsException("Custom CDEvent validation failed.");
Copy link

Choose a reason for hiding this comment

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

Do we not say why something has failed in the exception? We should probably create an issue for that if that's the case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the method with in the condition logs the reason for failures before the exception is thrown.

*/
public static <T extends CDEvent> T customCDEventFromJson(String customCDEventJson, Class<T> eventClass, boolean validateContextSchema) {
try {
T cdEvent = new ObjectMapper().readValue(customCDEventJson, eventClass);
Copy link

Choose a reason for hiding this comment

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

Some comment here with the object mapper. We can just move this to a static field

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed this method now and using it from existing code

public static <T extends CDEvent> T customCDEventFromJson(String customCDEventJson, Class<T> eventClass, boolean validateContextSchema) {
try {
T cdEvent = new ObjectMapper().readValue(customCDEventJson, eventClass);
if (!validateCustomCDEvent(cdEvent, validateContextSchema)) {
Copy link

Choose a reason for hiding this comment

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

Same comment above with the reasons something has failed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using an existing method now, and added more logging for the failure reason

* @param customCDEvent
* @return valid cdEvent
*/
public static boolean validateWithOfficialCustomSchema(CDEvent customCDEvent) {
Copy link

Choose a reason for hiding this comment

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

same here

void testCustomResourceEventFromJsonConformance() throws IOException {

// Use spec/custom/conformance.json to test once Links are implemented for SDK
//File customResourceCreatedExample = new File(CUSTOM_SPEC_FOLDER + "conformance.json");
Copy link

Choose a reason for hiding this comment

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

Suggested change
//File customResourceCreatedExample = new File(CUSTOM_SPEC_FOLDER + "conformance.json");
// File customResourceCreatedExample = new File(CUSTOM_SPEC_FOLDER + "conformance.json");

cdEvent.setSubjectNestedList(Arrays.asList("val1", "val2"));

cdEvent.setCustomSchemaUri(new File("src/test/resources/customresourcecreated.json").toURI());

Copy link

Choose a reason for hiding this comment

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

nit: This spacing seems really odd

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed

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 @rjalander - I've not reviewed the whole code, but I have a comment about downloading the schema provided in an event, please let me know what you think about it.

On golang side, I have not added validation based on schemaUri yet, I'm working on it. I wanted to do it as a separate PR because schemaUri applies to all events, custom and not.

}
log.info("Validate custom CDEvent against context.schemaUri - {}", customCDEvent.customSchemaUri());
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012);
JsonSchema jsonSchema = factory.getSchema(customCDEvent.customSchemaUri());
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 we should not promote a model where for every message the SDK reaches out to some internet location to fetch the schema. The way I was thinking of implementing this for the sdk-go is to let user register a custom schema, so that it's loaded once and re-used from memory, and then give users an option to decide what to do if the custom schemaURI is not in the in-memory schema cache, i.e. fail or try to download the schema, with the former as default.

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 agree, will do the required changes in a separate PR. Removed this implementation from current PR and created issue to work on #84

* @return custom schema URI
*/
@Override
public URI customSchemaUri() {
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: the field name is schemaUri - perhaps the method name should match it for clarity?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changing it to contextSchemaUri()

Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
@rjalander
Copy link
Contributor Author

Thank you @aalmiray @xibz @afrittoli for reviewing this PR
Initially I have created custom CDEvents support using additional APIs customCDEventAsCloudEvent and customCDEventFromJson assuming that various custom events will be created by user of the SDK by extending CDEvent model.
But after looking at the approach from go-sdk, generating a base CustomTypeEvent for all the custom event types and using existing APIs to create CloudEvent and Custom events.

Please review the latest changes.
Will create a doc example to be inline with go-sdk as per https://github.com/cdevents/sdk-go/tree/main/docs#create-a-custom-cdevent

Jalander Ramagiri added 2 commits July 15, 2024 11:50
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
Jalander Ramagiri added 3 commits July 15, 2024 13:36
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
// Code generated by dev.cdevents.generator.CDEventsGenerator. DO NOT EDIT.

/*
Copyright 2023 The CDEvents Authors
Copy link
Contributor

Choose a reason for hiding this comment

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

2024?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually it is generated from a common template for all the existing and new events.

@rjalander rjalander requested review from aalmiray, afrittoli and xibz July 19, 2024 10:14
Copy link

@xibz xibz left a comment

Choose a reason for hiding this comment

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

Most of my comments are nit, but I had one thing that I think we should change. Doesn't have to be this PR, but a follow up one maybe after some discussion about it in CDEvents

Otherwise looks good! Since none of my comments are blocking, approved!

</dependency>
```
### Create a Custom CDEvent
Custom CDEvent can be created using a class `CustomTypeEvent` packaged with in `cdevents-sdk-java`
Copy link

Choose a reason for hiding this comment

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

This reads very similarly to the sentence above:

Custom events follow the CDEvents format and can be defined via the
CustomTypeEvent class, available since v0.4.

How about changing the wording here to be something more like

In this example, we will create a custom event for our tool utilizing the new `CustomTypeEvent`

log.info("Processing event JsonSchema file: {}", file.getAbsolutePath());
try {
JsonNode rootNode = objectMapper.readTree(file);
JsonNode contextNode = rootNode.get("properties").get("context").get("properties");
JsonNode subjectNode = rootNode.get("properties").get("subject").get("properties");
String schemaURL = rootNode.get("$id").asText();
boolean isCustomEvent = schemaURL.endsWith("custom");
Copy link

Choose a reason for hiding this comment

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

This seems really fragile. So any URL that ends with custom is assumed to be custom? Do we define that anywhere? Basically if we have some other even name "some/spec/that-has-custom" would be skipped is it wasn't a custom event

While concerning, and I dont think it needs to be addressed in this PR, can you please add a comment or a TODO to make it less fragile? This may require work in the spec SIG to discuss how to easily identify custom events based on URL.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, not looking for custom event schema to check isCustomEvent here, generating a base CustomTypeEvent.java from an official schema.json, which has "$id": "https://cdevents.dev/0.5.0-draft/schema/custom",.
And this class file will be used to create any custom events

Copy link
Contributor

Choose a reason for hiding this comment

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

We discussed this in the CDEvents WG and agreed that extending the match to schema/custom should be sufficient. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah sounds good to me.

updateSubjectContentProperties(schemaData, subjectContentNode);
schemaData.setCustomEvent(isCustomEvent);

if (!isCustomEvent) {
Copy link

Choose a reason for hiding this comment

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

nit: We can probably just this up some and inverse the if statement

if (isCustomEvent) {
    return schemaData;
}

Jalander Ramagiri added 2 commits July 23, 2024 10:30
Signed-off-by: Jalander Ramagiri <[email protected]>
Signed-off-by: Jalander Ramagiri <[email protected]>
@rjalander
Copy link
Contributor Author

@aalmiray @afrittoli could you please review this PR and approve If there are no further comments.

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.

5 participants