-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for Method level @ratelimiting annoation #250
- Loading branch information
1 parent
ae2a8a4
commit 206c6cf
Showing
30 changed files
with
888 additions
and
555 deletions.
There are no files selected for viewing
18 changes: 9 additions & 9 deletions
18
...ontext/src/main/java/com/giffing/bucket4j/spring/boot/starter/context/RateLimitCheck.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,19 @@ | ||
package com.giffing.bucket4j.spring.boot.starter.context; | ||
|
||
|
||
import com.giffing.bucket4j.spring.boot.starter.context.properties.RateLimit; | ||
|
||
/** | ||
* Used to check if the rate limit should be performed independently from the servlet|webflux|gateway request filter | ||
* | ||
* Used to check if the rate limit should be performed independently from the servlet|webflux|gateway request filter | ||
*/ | ||
@FunctionalInterface | ||
public interface RateLimitCheck<R> { | ||
|
||
/** | ||
* @param request the request information object | ||
* | ||
* @return null if no rate limit should be performed. (maybe skipped or shouldn't be executed). | ||
*/ | ||
RateLimitResultWrapper rateLimit(R request); | ||
/** | ||
* @param request the request information object | ||
* @param mainRateLimit overwrites the rate limit configuration from the properties | ||
* @return null if no rate limit should be performed. (maybe skipped or shouldn't be executed). | ||
*/ | ||
RateLimitResultWrapper rateLimit(R request, RateLimit mainRateLimit); | ||
|
||
} |
21 changes: 21 additions & 0 deletions
21
...-context/src/main/java/com/giffing/bucket4j/spring/boot/starter/context/RateLimiting.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.giffing.bucket4j.spring.boot.starter.context; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target(ElementType.METHOD) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface RateLimiting { | ||
|
||
String name(); | ||
|
||
String cacheKey() default ""; | ||
|
||
String executeCondition() default ""; | ||
|
||
String skipCondition() default ""; | ||
|
||
String fallbackMethodName() default ""; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
...in/java/com/giffing/bucket4j/spring/boot/starter/context/properties/MethodProperties.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.giffing.bucket4j.spring.boot.starter.context.properties; | ||
|
||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.NotNull; | ||
import lombok.Data; | ||
import lombok.ToString; | ||
|
||
@Data | ||
@ToString | ||
public class MethodProperties { | ||
|
||
@NotBlank | ||
private String name; | ||
|
||
@NotBlank | ||
private String cacheName; | ||
|
||
@NotNull | ||
private RateLimit rateLimit; | ||
|
||
} |
158 changes: 102 additions & 56 deletions
158
.../src/main/java/com/giffing/bucket4j/spring/boot/starter/context/properties/RateLimit.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,113 @@ | ||
package com.giffing.bucket4j.spring.boot.starter.context.properties; | ||
|
||
import java.io.Serializable; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import com.giffing.bucket4j.spring.boot.starter.context.ExecutePredicateDefinition; | ||
import com.giffing.bucket4j.spring.boot.starter.context.constraintvalidations.ValidBandWidthIds; | ||
|
||
import jakarta.validation.Valid; | ||
import jakarta.validation.constraints.*; | ||
|
||
import io.github.bucket4j.TokensInheritanceStrategy; | ||
import jakarta.validation.Valid; | ||
import jakarta.validation.constraints.Min; | ||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.NotEmpty; | ||
import jakarta.validation.constraints.NotNull; | ||
import lombok.Data; | ||
|
||
import java.io.Serializable; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
@Data | ||
@ValidBandWidthIds | ||
public class RateLimit implements Serializable { | ||
|
||
/** | ||
* SpEl condition to check if the rate limit should be executed. If null there is no check. | ||
*/ | ||
private String executeCondition; | ||
|
||
/** | ||
* TODO comment | ||
*/ | ||
private String postExecuteCondition; | ||
|
||
@Valid | ||
private List<ExecutePredicateDefinition> executePredicates = new ArrayList<>(); | ||
|
||
/** | ||
* SpEl condition to check if the rate-limit should apply. If null there is no check. | ||
*/ | ||
private String skipCondition; | ||
|
||
@Valid | ||
private List<ExecutePredicateDefinition> skipPredicates = new ArrayList<>(); | ||
|
||
/** | ||
* SPEL expression to dynamic evaluate filter key | ||
*/ | ||
@NotBlank | ||
private String cacheKey = "1"; | ||
|
||
@Null(message = "The expression is depcreated since 0.8. Please use cache-key instead") | ||
@Deprecated | ||
private String expression; | ||
|
||
/** | ||
* The number of tokens that should be consumed | ||
*/ | ||
@NotNull | ||
@Min(1) | ||
private Integer numTokens = 1; | ||
|
||
@NotEmpty | ||
@Valid | ||
private List<BandWidth> bandwidths = new ArrayList<>(); | ||
|
||
/** | ||
* The token inheritance strategy to use when replacing the configuration of a bucket | ||
*/ | ||
@NotNull | ||
private TokensInheritanceStrategy tokensInheritanceStrategy = TokensInheritanceStrategy.RESET; | ||
|
||
/** | ||
* SpEl condition to check if the rate limit should be executed. If null there is no check. | ||
*/ | ||
private String executeCondition; | ||
|
||
/** | ||
* TODO comment | ||
*/ | ||
private String postExecuteCondition; | ||
|
||
@Valid | ||
private List<ExecutePredicateDefinition> executePredicates = new ArrayList<>(); | ||
|
||
/** | ||
* SpEl condition to check if the rate-limit should apply. If null there is no check. | ||
*/ | ||
private String skipCondition; | ||
|
||
@Valid | ||
private List<ExecutePredicateDefinition> skipPredicates = new ArrayList<>(); | ||
|
||
/** | ||
* SPEL expression to dynamic evaluate filter key | ||
*/ | ||
@NotBlank | ||
private String cacheKey = "1"; | ||
|
||
/** | ||
* The number of tokens that should be consumed | ||
*/ | ||
@NotNull | ||
@Min(1) | ||
private Integer numTokens = 1; | ||
|
||
@NotEmpty | ||
@Valid | ||
private List<BandWidth> bandwidths = new ArrayList<>(); | ||
|
||
/** | ||
* The token inheritance strategy to use when replacing the configuration of a bucket | ||
*/ | ||
@NotNull | ||
private TokensInheritanceStrategy tokensInheritanceStrategy = TokensInheritanceStrategy.RESET; | ||
|
||
public RateLimit copy() { | ||
var copy = new RateLimit(); | ||
copy.setExecuteCondition(this.executeCondition); | ||
copy.setPostExecuteCondition(this.postExecuteCondition); | ||
copy.setExecutePredicates(this.executePredicates); | ||
copy.setSkipCondition(this.skipCondition); | ||
copy.setSkipPredicates(this.skipPredicates); | ||
copy.setCacheKey(this.cacheKey); | ||
copy.setNumTokens(this.numTokens); | ||
copy.setBandwidths(this.bandwidths); | ||
copy.setTokensInheritanceStrategy(this.tokensInheritanceStrategy); | ||
return copy; | ||
} | ||
|
||
public void consumeNotNullValues(RateLimit toConsume) { | ||
if(toConsume == null) { | ||
return; | ||
} | ||
|
||
if (toConsume.getExecuteCondition() != null && !toConsume.getExecuteCondition().isEmpty()) { | ||
this.setExecuteCondition(toConsume.getExecuteCondition()); | ||
} | ||
if (toConsume.getPostExecuteCondition() != null && !toConsume.getPostExecuteCondition().isEmpty()) { | ||
this.setPostExecuteCondition(toConsume.getPostExecuteCondition()); | ||
} | ||
if (toConsume.getExecutePredicates() != null && !toConsume.getExecutePredicates().isEmpty()) { | ||
this.setExecutePredicates(toConsume.getExecutePredicates()); | ||
} | ||
if (toConsume.getSkipCondition() != null && !toConsume.getSkipCondition().isEmpty()) { | ||
this.setSkipCondition(toConsume.getSkipCondition()); | ||
} | ||
if (toConsume.getSkipPredicates() != null && !toConsume.getSkipPredicates().isEmpty()) { | ||
this.setSkipPredicates(toConsume.getSkipPredicates()); | ||
} | ||
if (toConsume.getCacheKey() != null && !toConsume.getCacheKey().equals("1") && !toConsume.getCacheKey().isEmpty()) { | ||
this.setCacheKey(toConsume.getCacheKey()); | ||
} | ||
if(toConsume.getNumTokens() != null && toConsume.getNumTokens() != 1) { | ||
this.setNumTokens(toConsume.getNumTokens()); | ||
} | ||
if(toConsume.getBandwidths() != null && !toConsume.getBandwidths().isEmpty()) { | ||
this.setBandwidths(toConsume.getBandwidths()); | ||
} | ||
if(toConsume.getTokensInheritanceStrategy() != null) { | ||
this.setTokensInheritanceStrategy(toConsume.getTokensInheritanceStrategy()); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
...arter/src/main/java/com/giffing/bucket4j/spring/boot/starter/config/aspect/AopConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.giffing.bucket4j.spring.boot.starter.config.aspect; | ||
|
||
import com.giffing.bucket4j.spring.boot.starter.config.cache.Bucket4jCacheConfiguration; | ||
import com.giffing.bucket4j.spring.boot.starter.config.cache.SyncCacheResolver; | ||
import com.giffing.bucket4j.spring.boot.starter.config.metrics.actuator.SpringBootActuatorConfig; | ||
import com.giffing.bucket4j.spring.boot.starter.config.service.ServiceConfiguration; | ||
import com.giffing.bucket4j.spring.boot.starter.context.properties.Bucket4JBootProperties; | ||
import com.giffing.bucket4j.spring.boot.starter.service.RateLimitService; | ||
import org.aspectj.lang.annotation.Aspect; | ||
import org.springframework.boot.autoconfigure.AutoConfigureAfter; | ||
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Import; | ||
|
||
@Configuration | ||
@ConditionalOnClass(Aspect.class) | ||
@ConditionalOnProperty(prefix = Bucket4JBootProperties.PROPERTY_PREFIX, value = {"enabled"}, matchIfMissing = true) | ||
@EnableConfigurationProperties({Bucket4JBootProperties.class}) | ||
@AutoConfigureAfter(value = { CacheAutoConfiguration.class, Bucket4jCacheConfiguration.class }) | ||
@ConditionalOnBean(value = SyncCacheResolver.class) | ||
@Import(value = {ServiceConfiguration.class, Bucket4jCacheConfiguration.class, SpringBootActuatorConfig.class}) | ||
public class AopConfig { | ||
|
||
@Bean | ||
public RateLimitAspect rateLimitAspect(RateLimitService rateLimitService, Bucket4JBootProperties bucket4JBootProperties, SyncCacheResolver syncCacheResolver) { | ||
return new RateLimitAspect(rateLimitService, bucket4JBootProperties.getMethods(), syncCacheResolver); | ||
} | ||
|
||
} |
Oops, something went wrong.