Skip to content

Commit

Permalink
#71 add post-execute-condition (#243)
Browse files Browse the repository at this point in the history
* #71 add post-execute-condition

* #71 add post-execute-condition

* #71 add post-execute-condition

reduce code duplication for tests (not optimal but it improves maintainability)

* #71 add post-execute-condition

prepare snapshot release

* #71 add post-execute-condition

fix wrong directory for general-tests

* #71 add post-execute-condition

* github actions test report

* #71 add post-execute-condition

minor refactoring
add empty response body test

* #71 add post-execute-condition

tests:
- change response status code
- additional response headers

* #71 add post-execute-condition

tests:
- skip condition

* #71 add post-execute-condition

tests:
- execute condition
  • Loading branch information
MarcGiffing authored Mar 2, 2024
1 parent 545c8af commit 9bf6cd0
Show file tree
Hide file tree
Showing 108 changed files with 2,563 additions and 2,358 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ on: [push]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- name: Set up JDK 17
Expand All @@ -15,3 +13,8 @@ jobs:
java-version: 17
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: Publish Test Report
uses: mikepenz/action-junit-report@v4
if: success() || failure() # always run even if the previous step fails
with:
report_paths: '**/target/surefire-reports/TEST-*.xml'
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
*.iml
.factorypath
.apt_generated
.springBeans
.springBeans
.flattened-pom.xml
1 change: 1 addition & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ bucket4j.filters[0].strategy=first # [first, all] if multiple rate limits config
bucket4j.filters[0].rate-limits[0].cache-key=getRemoteAddr() # defines the cache key. It will be evaluated with the Spring Expression Language
bucket4j.filters[0].rate-limits[0].num-tokens=1 # The number of tokens to consume
bucket4j.filters[0].rate-limits[0].execute-condition=1==1 # an optional SpEl expression to decide to execute the rate limit or not
bucket4j.filters[1].rate-limits[0].post-execute-condition= # an optional SpEl expression to decide if the token consumption should only estimated for the incoming request and the returning response used to check if the token must be consumed: getStatus() eq 401
bucket4j.filters[0].rate-limits[0].execute-predicates[0]=PATH=/hello,/world # On the HTTP Path as a list
bucket4j.filters[0].rate-limits[0].execute-predicates[1]=METHOD=GET,POST # On the HTTP Method
bucket4j.filters[0].rate-limits[0].execute-predicates[2]=QUERY=HELLO # Checks for the existence of a Query Parameter
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.giffing.bucket4j.spring.boot.starter.context;



/**
* Used to check if the rate limit should be performed independently from the servlet|webflux|gateway request filter
*
*/
@FunctionalInterface
public interface PostRateLimitCheck<R, P> {

/**
* @param request the request information object
* @param response the response information object
*
* @return null if no rate limit should be performed. (maybe skipped or shouldn't be executed).
*/
RateLimitResultWrapper rateLimit(R request, P response);

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ public interface RateLimitCheck<R> {
*
* @return null if no rate limit should be performed. (maybe skipped or shouldn't be executed).
*/
ConsumptionProbeHolder rateLimit(R request);
RateLimitResultWrapper rateLimit(R request);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.giffing.bucket4j.spring.boot.starter.context;

import lombok.Builder;
import lombok.Data;
import lombok.NonNull;

@Data
@Builder
public class RateLimitResult {

@NonNull
private final boolean estimation;

@NonNull
private final boolean consumed;

@NonNull
private final long remainingTokens;

@NonNull
private final long nanosToWaitForRefill;

@NonNull
private final long nanosToWaitForReset;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.giffing.bucket4j.spring.boot.starter.context;

import lombok.Data;

import java.util.concurrent.CompletableFuture;


@Data
public class RateLimitResultWrapper {

private RateLimitResult rateLimitResult;

private CompletableFuture<RateLimitResult> rateLimitResultCompletableFuture;

public RateLimitResultWrapper(RateLimitResult rateLimitResult) {
this.rateLimitResult = rateLimitResult;
}

public RateLimitResultWrapper(CompletableFuture<RateLimitResult> rateLimitResultCompletableFuture) {
this.rateLimitResultCompletableFuture = rateLimitResultCompletableFuture;
}

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
package com.giffing.bucket4j.spring.boot.starter.context.properties;

import java.io.Serializable;
import java.time.temporal.ChronoUnit;

import jakarta.validation.constraints.AssertTrue;
import com.giffing.bucket4j.spring.boot.starter.context.RefillSpeed;
import com.giffing.bucket4j.spring.boot.starter.context.constraintvalidations.ValidDurationChronoUnit;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;

import lombok.Data;
import org.springframework.util.StringUtils;

import com.giffing.bucket4j.spring.boot.starter.context.RefillSpeed;
import com.giffing.bucket4j.spring.boot.starter.context.constraintvalidations.ValidDurationChronoUnit;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.io.Serializable;
import java.time.temporal.ChronoUnit;

/**
* Configures the rate of data which should be transfered
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
package com.giffing.bucket4j.spring.boot.starter.context.properties;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.http.HttpStatus;

import com.giffing.bucket4j.spring.boot.starter.context.PostRateLimitCheck;
import com.giffing.bucket4j.spring.boot.starter.context.RateLimitCheck;
import com.giffing.bucket4j.spring.boot.starter.context.RateLimitConditionMatchingStrategy;

import lombok.Data;
import lombok.ToString;
import org.springframework.http.HttpStatus;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* This class is the main configuration class which is used to build the servlet|webflux|gateway request filter
*
*/
@Data
@ToString
public class FilterConfiguration<R> {
public class FilterConfiguration<R, P> {

private RateLimitConditionMatchingStrategy strategy = RateLimitConditionMatchingStrategy.FIRST;

Expand All @@ -32,7 +31,7 @@ public class FilterConfiguration<R> {
* The order of the filter depending on other filters independently from the Bucket4j filters.
*/
private int order;

/**
* Hides the HTTP response headers
* x-rate-limit-remaining
Expand All @@ -58,11 +57,16 @@ public class FilterConfiguration<R> {
private Map<String, String> httpResponseHeaders = new HashMap<>();

private List<RateLimitCheck<R>> rateLimitChecks = new ArrayList<>();

private List<PostRateLimitCheck<R, P>> postRateLimitChecks = new ArrayList<>();

public void addRateLimitCheck(RateLimitCheck<R> rateLimitCheck) {
this.rateLimitChecks.add(rateLimitCheck);
}

private Metrics metrics;

public void addPostRateLimitCheck(PostRateLimitCheck<R, P> prlc) {
getPostRateLimitChecks().add(prlc);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ 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<>();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.giffing.bucket4j.spring.boot.starter.context.qualifier;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier(Gateway.VALUE)
public @interface Gateway {
String VALUE = "GATEWAY";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.giffing.bucket4j.spring.boot.starter.context.qualifier;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier(Servlet.VALUE)
public @interface Servlet {
String VALUE = "SERVLET";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.giffing.bucket4j.spring.boot.starter.context.qualifier;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier(Webflux.VALUE)
public @interface Webflux {
String VALUE = "WEBFLUX";
}
Loading

0 comments on commit 9bf6cd0

Please sign in to comment.