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

feat(cloud-stream): search configuration classes for channels and operations #690

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ChannelBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.bindings.messages.MessageBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.channels.ChannelMerger;
import io.github.springwolf.core.asyncapi.scanners.classes.spring.ComponentClassScanner;
import io.github.springwolf.core.asyncapi.scanners.classes.SpringwolfClassScanner;
import io.github.springwolf.core.asyncapi.scanners.common.utils.AsyncAnnotationUtil;
import io.github.springwolf.core.configuration.docket.AsyncApiDocket;
import io.github.springwolf.core.configuration.docket.AsyncApiDocketService;
Expand All @@ -42,7 +42,7 @@ public class CloudStreamFunctionChannelsScanner implements ChannelsScanner {

private final AsyncApiDocketService asyncApiDocketService;
private final BeanMethodsScanner beanMethodsScanner;
private final ComponentClassScanner componentClassScanner;
private final SpringwolfClassScanner classScanner;
Copy link
Member

Choose a reason for hiding this comment

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

The use of ComponentClassScanner and not SpringwolfClassScanner is intentional.
SpringwolfClassScanner will return classes annotated with @Component which is needed here, but also classes returned by bean methods. In the case of spring cloud stream functions, the bean method always has generics, which can't be retrieved (to my knowledge) from the Class object.

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed. Therefore both scanners are needed.
My intention is to support Configuration annotations as well. And overall to align the cloudstream with the other plugins - if possible.

Will have a closer look, whether Configurations are Components already

private final ComponentsService componentsService;
private final BindingServiceProperties cloudStreamBindingsProperties;
private final FunctionalChannelBeanBuilder functionalChannelBeanBuilder;
Expand All @@ -52,7 +52,7 @@ public class CloudStreamFunctionChannelsScanner implements ChannelsScanner {
@Override
public Map<String, ChannelObject> scan() {
Set<AnnotatedElement> elements = new HashSet<>();
elements.addAll(componentClassScanner.scan());
elements.addAll(classScanner.scan());
elements.addAll(beanMethodsScanner.getBeanMethods());

List<Map.Entry<String, ChannelObject>> channels = elements.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,20 @@ public class FunctionalChannelBeanBuilder {
public Set<FunctionalChannelBeanData> build(AnnotatedElement element) {
Class<?> type = getRawType(element);

if (Consumer.class.isAssignableFrom(type)) {
Class<?> payloadType = getTypeGenerics(element).get(0);
List<Class<?>> typeGenerics = getTypeGenerics(element);
if (Consumer.class.isAssignableFrom(type) && typeGenerics.size() >= 1) {
Class<?> payloadType = typeGenerics.get(0);
return Set.of(ofConsumer(element, payloadType));
}

if (Supplier.class.isAssignableFrom(type)) {
Class<?> payloadType = getTypeGenerics(element).get(0);
if (Supplier.class.isAssignableFrom(type) && typeGenerics.size() >= 1) {
Class<?> payloadType = typeGenerics.get(0);
return Set.of(ofSupplier(element, payloadType));
}

if (Function.class.isAssignableFrom(type)) {
Class<?> inputType = getTypeGenerics(element).get(0);
Class<?> outputType = getTypeGenerics(element).get(1);
if (Function.class.isAssignableFrom(type) && typeGenerics.size() >= 2) {
Copy link
Member

Choose a reason for hiding this comment

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

Might be too defensive, Consumer and Supplier always have exactly one generic type and Function always has exactly 2, but that wouldn't hurt.

Class<?> inputType = typeGenerics.get(0);
Class<?> outputType = typeGenerics.get(1);

return Set.of(ofConsumer(element, inputType), ofSupplier(element, outputType));
}
Expand Down Expand Up @@ -89,8 +90,11 @@ private static String getElementName(AnnotatedElement element) {

private List<Class<?>> getTypeGenerics(AnnotatedElement element) {
if (element instanceof Method m) {
ParameterizedType genericReturnType = (ParameterizedType) m.getGenericReturnType();
return getTypeGenerics(genericReturnType);
if (m.getGenericReturnType() instanceof ParameterizedType) {
Copy link
Member

Choose a reason for hiding this comment

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

I did not add the type check because we are always dealing here with methods that return Consumer, Supplier or Function for which getGenericReturnType is always ParameterizedType. But that's probably good practice.
Maybe use smart casting though?

if (m.getGenericReturnType() instanceof ParameterizedType parameterizedType) {
    return getTypeGenerics(parameterizedType);
}

Copy link
Member Author

Choose a reason for hiding this comment

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

I did run into an exception, but could have been an intermediate state.

ParameterizedType genericReturnType = (ParameterizedType) m.getGenericReturnType();
return getTypeGenerics(genericReturnType);
}
return Collections.emptyList();
}

if (element instanceof Class<?> c) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersNotDocumented;
import io.github.springwolf.core.asyncapi.scanners.OperationsScanner;
import io.github.springwolf.core.asyncapi.scanners.beans.BeanMethodsScanner;
import io.github.springwolf.core.asyncapi.scanners.classes.spring.ComponentClassScanner;
import io.github.springwolf.core.asyncapi.scanners.classes.SpringwolfClassScanner;
import io.github.springwolf.core.asyncapi.scanners.operations.OperationMerger;
import io.github.springwolf.core.configuration.docket.AsyncApiDocket;
import io.github.springwolf.core.configuration.docket.AsyncApiDocketService;
Expand All @@ -39,15 +39,15 @@ public class CloudStreamFunctionOperationsScanner implements OperationsScanner {

private final AsyncApiDocketService asyncApiDocketService;
private final BeanMethodsScanner beanMethodsScanner;
private final ComponentClassScanner componentClassScanner;
Copy link
Member

Choose a reason for hiding this comment

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

Same as first comment, the use of ComponentClassScanner here is intentional.

private final SpringwolfClassScanner classScanner;
private final ComponentsService componentsService;
private final BindingServiceProperties cloudStreamBindingsProperties;
private final FunctionalChannelBeanBuilder functionalChannelBeanBuilder;

@Override
public Map<String, Operation> scan() {
Set<AnnotatedElement> elements = new HashSet<>();
elements.addAll(componentClassScanner.scan());
elements.addAll(classScanner.scan());
elements.addAll(beanMethodsScanner.getBeanMethods());

List<Map.Entry<String, Operation>> operations = elements.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import io.github.springwolf.core.asyncapi.scanners.beans.BeanMethodsScanner;
import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ChannelBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.bindings.messages.MessageBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.classes.spring.ComponentClassScanner;
import io.github.springwolf.core.asyncapi.scanners.classes.SpringwolfClassScanner;
import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
import io.github.springwolf.core.configuration.docket.AsyncApiDocketService;
import io.github.springwolf.core.configuration.properties.SpringwolfConfigConstants;
Expand All @@ -30,7 +30,7 @@ public class SpringwolfCloudStreamAutoConfiguration {
public CloudStreamFunctionChannelsScanner cloudStreamFunctionChannelsScanner(
AsyncApiDocketService asyncApiDocketService,
BeanMethodsScanner beanMethodsScanner,
ComponentClassScanner componentClassScanner,
SpringwolfClassScanner classScanner,
ComponentsService componentsService,
BindingServiceProperties cloudstreamBindingServiceProperties,
FunctionalChannelBeanBuilder functionalChannelBeanBuilder,
Expand All @@ -39,7 +39,7 @@ public CloudStreamFunctionChannelsScanner cloudStreamFunctionChannelsScanner(
return new CloudStreamFunctionChannelsScanner(
asyncApiDocketService,
beanMethodsScanner,
componentClassScanner,
classScanner,
componentsService,
cloudstreamBindingServiceProperties,
functionalChannelBeanBuilder,
Expand All @@ -51,14 +51,14 @@ public CloudStreamFunctionChannelsScanner cloudStreamFunctionChannelsScanner(
public CloudStreamFunctionOperationsScanner cloudStreamFunctionOperationsScanner(
AsyncApiDocketService asyncApiDocketService,
BeanMethodsScanner beanMethodsScanner,
ComponentClassScanner componentClassScanner,
SpringwolfClassScanner classScanner,
ComponentsService componentsService,
BindingServiceProperties cloudstreamBindingServiceProperties,
FunctionalChannelBeanBuilder functionalChannelBeanBuilder) {
return new CloudStreamFunctionOperationsScanner(
asyncApiDocketService,
beanMethodsScanner,
componentClassScanner,
classScanner,
componentsService,
cloudstreamBindingServiceProperties,
functionalChannelBeanBuilder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.github.springwolf.core.asyncapi.components.examples.walkers.json.ExampleJsonValueGenerator;
import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersNotDocumented;
import io.github.springwolf.core.asyncapi.scanners.beans.DefaultBeanMethodsScanner;
import io.github.springwolf.core.asyncapi.scanners.classes.SpringwolfClassScanner;
import io.github.springwolf.core.asyncapi.scanners.classes.spring.ComponentClassScanner;
import io.github.springwolf.core.asyncapi.scanners.classes.spring.ConfigurationClassScanner;
import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
Expand Down Expand Up @@ -60,7 +61,9 @@
@ContextConfiguration(
classes = {
ConfigurationClassScanner.class,
SpringwolfClassScanner.class,
ComponentClassScanner.class,
ConfigurationClassScanner.class,
DefaultBeanMethodsScanner.class,
DefaultComponentsService.class,
SwaggerSchemaUtil.class,
Expand Down