spring-cloud-aws icon indicating copy to clipboard operation
spring-cloud-aws copied to clipboard

Make AwsClientCustomizer Flexible

Open akefirad opened this issue 2 years ago • 9 comments

Type: Feature

Is your feature request related to a problem? Please describe. Imagine a developer wants to add a single request interceptor to a specific client. If I understand the whole thing correctly, the only way to do this is to create an AwsClientCustomizer bean that returns a ClientOverrideConfiguration instance in which the interceptor is added. This is a bit sub-optimal. The ClientOverrideConfiguration (COC) is huge one and I don't feel comfortable overriding the original instance (considering the fact that COC instance returned by the customizer replaces the original instance). Looking at the implementation, at least in AWSpring case, the COC instance is not an empty one, which means if I inject my bean, I have to duplicate the logic in SpringCloudClientConfiguration.

Describe the solution you'd like I think it makes sense to have more granular customizers, instead of a big fat one providing COC. (I'm fine with the rest of the existing customizer which are merely providers.) Another alternative is to expect a Consumer<ClientOverrideConfiguration.Builder> instead of a ready-to-use instance of COC. A less optimal solution is to considering user-defined beans of type request interceptors. Another totally different approach would be to expose the client builder in the customizer.

Describe alternatives you've considered Right now I override the SesClient to get access to the client builder to inject my interceptor.

Let me know if you need more information.

akefirad avatar Dec 02 '22 19:12 akefirad

Sounds reasonable. Can you provide some code samples how this API would be used (without implementing it yet)?

maciejwalkowiak avatar Jan 04 '23 13:01 maciejwalkowiak

Random ideas:

  1. Add overrideConfigurationBuilder method to AwsClientCustomizer interface. The question is what to do with overrideConfiguration method?
  2. Accept a Consumer<ClientOverrideConfiguration.Builder> bean while building the configuration instance.

I think I'd go with the first one since it's consistent with what we already have in AwsClientCustomizer.

akefirad avatar Jan 12 '23 11:01 akefirad

Hey @akefirad ,

I am not sure I am following the question. By AWS docs:

Configuration values for which the client already provides sensible defaults. All values are optional, and not specifying them will use optimal values defined by the service itself. Use builder() to create a set of options.

Meaning if you add an interceptor and do not touch other values they should be a default, just like when you just do Client.build().

Inside of SpringCloudClientConfiguration we have the method clientOverrideConfiguration. We can make this method return ClientOverrideConfiguration.Builder instead of ClientOverrideConfiguration.

Then you could use something like:

	@Bean
	AwsClientCustomizer<SnsClientBuilder> snsClientBuilderAwsClientConfigurer() {
		return new CustomAwsClientConfig.SnsAwsClientConfigurer();
	}

	class SnsAwsClientConfigurer implements AwsClientCustomizer<SnsClientBuilder> {
		@Override
		@Nullable
		public ClientOverrideConfiguration overrideConfiguration() {
			return new SpringCloudClientConfiguration().clientOverrideConfiguration().apiCallTimeout(Duration.ofMillis(1999)).build();
		}
	}
}

MatejNedic avatar Mar 16 '23 20:03 MatejNedic

Almost there 🙂, it's should be like this:

    @Bean
    fun sesClientCustomizer(): AwsClientCustomizer<SesClientBuilder> = object : AwsClientCustomizer<SesClientBuilder> {
        // Refactor when awspring/spring-cloud-aws/#562 is fixed!
        override fun overrideConfiguration(): ClientOverrideConfiguration {
            return SpringCloudClientConfiguration()
                .clientOverrideConfiguration()
                .toBuilder() // COC is immutable!
                .apiCallTimeout(Duration.ofMillis(1999))
                .build()
        }
    }

In fact, that's what actually I'm doing right now. So yes, it's not a blocker right now. Apart from "being a bit hacky (to my eyes)" and code duplication, the fact that I need to instantiate a third party class to just get an instance of an AWS class feels a bit sub-optimal. The developer needs to just add an interface, why does she need to know about SpringCloudClientConfiguration at all? Does that make sense?

akefirad avatar Mar 19 '23 14:03 akefirad

Makes sense, maybe we should apply:

 SpringCloudClientConfiguration 
				.putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, getUserAgent());

inside of AwsClientCustomizer and with your approach then we can combine it. This of course means it will be ClientOverrideConfiguration.Builder instead of ClientOverrideConfiguration. This way it is hidden from the user but it is always applied.

MatejNedic avatar Mar 19 '23 20:03 MatejNedic

This issue should simplify #199 .

MatejNedic avatar Mar 31 '23 23:03 MatejNedic

Let me know if you need help with the implementation.

akefirad avatar Apr 02 '23 06:04 akefirad

Hey @akefirad , the contribution is welcome! :) Do you want me to assign you?

MatejNedic avatar Apr 02 '23 07:04 MatejNedic

Sure.

akefirad avatar May 01 '23 13:05 akefirad