Skip to content

Commit

Permalink
feat: Customizing Cloudevents validation (cloudevents#594)
Browse files Browse the repository at this point in the history
Add SPI for custom CloudEvent validation.

Signed-off-by: vbhat6 <vinayas_bhat@intuit.com>
  • Loading branch information
touchkey authored and lyca committed Feb 17, 2024
1 parent 55fddb3 commit f87a0dc
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2018-Present The CloudEvents Authors
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.cloudevents.core.provider;

import io.cloudevents.CloudEvent;
import io.cloudevents.core.validator.CloudEventValidator;

import java.util.Iterator;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;

/**
* CloudEventValidatorProvider is a singleton class which loads and access CE Validator service providers on behalf of service clients.
*/
public class CloudEventValidatorProvider {

private static final CloudEventValidatorProvider cloudEventValidatorProvider = new CloudEventValidatorProvider();

private final ServiceLoader<CloudEventValidator> loader;

private CloudEventValidatorProvider(){
loader = ServiceLoader.load(CloudEventValidator.class);
}

public static CloudEventValidatorProvider getInstance() {
return cloudEventValidatorProvider;
}

/**
* iterates through available Cloudevent validators.
* @param cloudEvent
*/
public void validate(CloudEvent cloudEvent){
for (final CloudEventValidator validator : loader) {
validator.validate(cloudEvent);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
*/
package io.cloudevents.core.v03;

import io.cloudevents.CloudEvent;
import io.cloudevents.SpecVersion;
import io.cloudevents.core.CloudEventUtils;
import io.cloudevents.core.impl.BaseCloudEventBuilder;
import io.cloudevents.core.provider.CloudEventValidatorProvider;
import io.cloudevents.core.v1.CloudEventV1;
import io.cloudevents.rw.CloudEventContextReader;
import io.cloudevents.rw.CloudEventContextWriter;
import io.cloudevents.rw.CloudEventRWException;
Expand Down Expand Up @@ -122,7 +125,11 @@ public CloudEventV03 build() {
throw createMissingAttributeException("type");
}

return new CloudEventV03(id, source, type, time, schemaurl, datacontenttype, subject, this.data, this.extensions);
CloudEventV03 cloudEvent = new CloudEventV03(id, source, type, time, schemaurl, datacontenttype, subject, this.data, this.extensions);
final CloudEventValidatorProvider validator = CloudEventValidatorProvider.getInstance();
validator.validate(cloudEvent);

return cloudEvent;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import io.cloudevents.SpecVersion;
import io.cloudevents.core.CloudEventUtils;
import io.cloudevents.core.impl.BaseCloudEventBuilder;
import io.cloudevents.core.provider.CloudEventValidatorProvider;
import io.cloudevents.core.validator.CloudEventValidator;
import io.cloudevents.rw.CloudEventContextReader;
import io.cloudevents.rw.CloudEventContextWriter;
import io.cloudevents.rw.CloudEventRWException;
Expand Down Expand Up @@ -119,7 +121,12 @@ public CloudEvent build() {
throw createMissingAttributeException(TYPE);
}

return new CloudEventV1(id, source, type, datacontenttype, dataschema, subject, time, this.data, this.extensions);
CloudEvent cloudEvent = new CloudEventV1(id, source, type, datacontenttype, dataschema, subject, time, this.data, this.extensions);

final CloudEventValidatorProvider validator = CloudEventValidatorProvider.getInstance();
validator.validate(cloudEvent);

return cloudEvent;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2018-Present The CloudEvents Authors
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.cloudevents.core.validator;

import io.cloudevents.CloudEvent;

/**
* @author Vinay Bhat
* Interface which defines validation for CloudEvents attributes and extensions.
*/
public interface CloudEventValidator {

/**
* Validate the attributes of a CloudEvent.
*
* @param cloudEvent the CloudEvent to validate
*/
void validate(CloudEvent cloudEvent);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.cloudevents.core.test;

import io.cloudevents.CloudEvent;
import io.cloudevents.core.validator.CloudEventValidator;

public class CloudEventCustomValidator implements CloudEventValidator {

@Override
public void validate(CloudEvent cloudEvent) {
String namespace = null;
if ((namespace = (String) cloudEvent.getExtension("namespace")) != null &&
!namespace.equals("sales")){
throw new IllegalStateException("Expecting sales in namespace extension");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,16 @@ void testMissingType() {
).hasMessageContaining("Attribute 'type' cannot be null");
}

@Test
void testValidatorProvider(){
assertThatCode(() -> CloudEventBuilder
.v1()
.withId("000")
.withSource(URI.create("http://localhost"))
.withType(TYPE)
.withExtension("namespace", "order")
.build()
).hasMessageContaining("Expecting sales in namespace extension");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.cloudevents.core.test.CloudEventCustomValidator

0 comments on commit f87a0dc

Please sign in to comment.