Skip to content

Commit e705dfd

Browse files
committed
add rate limiting annotations
* ApiLimitMetric defines a metric used for rate limiting * ApiMetricCost defines costs of a metric for an API method * SwaggerGenerator has been cleaned up, removing unused method parameters and extracting generation data into an internal GenerationContext.
1 parent ada3511 commit e705dfd

File tree

17 files changed

+544
-59
lines changed

17 files changed

+544
-59
lines changed

endpoints-framework/src/main/java/com/google/api/server/spi/config/Api.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,9 @@ ApiIssuerAudience[] issuerAudiences() default {
198198
* no effect unless used with endpoints-management-control-appengine.
199199
*/
200200
AnnotationBoolean apiKeyRequired() default AnnotationBoolean.UNSPECIFIED;
201+
202+
/**
203+
* Rate limiting metric definitions that are used in this API.
204+
*/
205+
ApiLimitMetric[] limitDefinitions() default {};
201206
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2017 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.api.server.spi.config;
17+
18+
/**
19+
* A rate limiting metric.
20+
*/
21+
public @interface ApiLimitMetric {
22+
/**
23+
* A limit identifier, consisting of alphanumeric characters and hyphens.
24+
*/
25+
String name();
26+
27+
/**
28+
* A human readable description of the metric, e.g. Bookstore Read API Requests per Minute.
29+
*/
30+
String displayName() default "";
31+
32+
/**
33+
* The default cost limit.
34+
*/
35+
int limit();
36+
}

endpoints-framework/src/main/java/com/google/api/server/spi/config/ApiMethod.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,9 @@ ApiIssuerAudience[] issuerAudiences() default {
128128
* no effect unless used with endpoints-management-control-appengine.
129129
*/
130130
AnnotationBoolean apiKeyRequired() default AnnotationBoolean.UNSPECIFIED;
131+
132+
/**
133+
* A list of metric costs associated with this method.
134+
*/
135+
ApiMetricCost[] metricCosts() default {};
131136
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2017 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.api.server.spi.config;
17+
18+
/**
19+
* Metric cost definition for a method.
20+
*/
21+
public @interface ApiMetricCost {
22+
/**
23+
* The metric name, which must be defined using {@link ApiLimitMetric}.
24+
*/
25+
String name();
26+
27+
/**
28+
* The cost charged to the metric.
29+
*/
30+
int cost();
31+
}

endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiAnnotationConfig.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
package com.google.api.server.spi.config.annotationreader;
1717

1818
import com.google.api.server.spi.config.AnnotationBoolean;
19+
import com.google.api.server.spi.config.ApiLimitMetric;
1920
import com.google.api.server.spi.config.AuthLevel;
2021
import com.google.api.server.spi.config.Authenticator;
2122
import com.google.api.server.spi.config.PeerAuthenticator;
2223
import com.google.api.server.spi.config.model.ApiConfig;
2324
import com.google.api.server.spi.config.model.ApiIssuerAudienceConfig;
2425
import com.google.api.server.spi.config.model.ApiIssuerConfigs;
26+
import com.google.api.server.spi.config.model.ApiLimitMetricConfig;
2527
import com.google.api.server.spi.config.scope.AuthScopeExpressions;
2628
import com.google.common.base.Strings;
29+
import com.google.common.collect.ImmutableList;
2730

2831
import java.util.Arrays;
2932

@@ -186,4 +189,18 @@ public void setApiKeyRequiredIfSpecified(AnnotationBoolean apiKeyRequired) {
186189
config.setApiKeyRequired(false);
187190
}
188191
}
192+
193+
public void setApiLimitMetrics(ApiLimitMetric[] apiLimitMetrics) {
194+
ImmutableList.Builder<ApiLimitMetricConfig> metricConfigs = ImmutableList.builder();
195+
if (apiLimitMetrics != null && apiLimitMetrics.length > 0) {
196+
for (ApiLimitMetric metric : apiLimitMetrics) {
197+
metricConfigs.add(ApiLimitMetricConfig.builder()
198+
.setName(metric.name())
199+
.setDisplayName(metric.displayName())
200+
.setLimit(metric.limit())
201+
.build());
202+
}
203+
}
204+
config.setApiLimitMetrics(metricConfigs.build());
205+
}
189206
}

endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiConfigAnnotationReader.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import com.google.api.server.spi.config.ApiConfigSource;
2525
import com.google.api.server.spi.config.ApiIssuer;
2626
import com.google.api.server.spi.config.ApiIssuerAudience;
27+
import com.google.api.server.spi.config.ApiLimitMetric;
28+
import com.google.api.server.spi.config.ApiMetricCost;
2729
import com.google.api.server.spi.config.AuthLevel;
2830
import com.google.api.server.spi.config.Authenticator;
2931
import com.google.api.server.spi.config.PeerAuthenticator;
@@ -208,6 +210,8 @@ private void readApi(ApiAnnotationConfig config, Annotation api)
208210
.<Class<? extends PeerAuthenticator>[]>getAnnotationProperty(api, "peerAuthenticators"));
209211
config.setApiKeyRequiredIfSpecified(
210212
(AnnotationBoolean) this.getAnnotationProperty(api, "apiKeyRequired"));
213+
config.setApiLimitMetrics(
214+
(ApiLimitMetric[]) this.getAnnotationProperty(api, "limitDefinitions"));
211215
}
212216

213217
private ApiIssuerConfigs getIssuerConfigs(Annotation annotation)
@@ -356,6 +360,8 @@ private void readApiMethodInstance(ApiMethodAnnotationConfig config, Annotation
356360
config.setIgnoredIfSpecified((AnnotationBoolean) getAnnotationProperty(apiMethod, "ignored"));
357361
config.setApiKeyRequiredIfSpecified(
358362
(AnnotationBoolean) this.getAnnotationProperty(apiMethod, "apiKeyRequired"));
363+
config.setMetricCosts(
364+
(ApiMetricCost[]) getAnnotationProperty(apiMethod, "metricCosts"));
359365
}
360366

361367
private void readMethodRequestParameters(EndpointMethod endpointMethod,

endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiMethodAnnotationConfig.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
package com.google.api.server.spi.config.annotationreader;
1717

1818
import com.google.api.server.spi.config.AnnotationBoolean;
19+
import com.google.api.server.spi.config.ApiMetricCost;
1920
import com.google.api.server.spi.config.AuthLevel;
2021
import com.google.api.server.spi.config.Authenticator;
2122
import com.google.api.server.spi.config.PeerAuthenticator;
2223
import com.google.api.server.spi.config.model.ApiIssuerAudienceConfig;
2324
import com.google.api.server.spi.config.model.ApiMethodConfig;
25+
import com.google.api.server.spi.config.model.ApiMetricCostConfig;
2426
import com.google.api.server.spi.config.scope.AuthScopeExpressions;
27+
import com.google.common.collect.ImmutableList;
2528

2629
import java.util.Arrays;
2730

@@ -124,4 +127,17 @@ public void setApiKeyRequiredIfSpecified(AnnotationBoolean apiKeyRequired) {
124127
config.setApiKeyRequired(false);
125128
}
126129
}
130+
131+
public void setMetricCosts(ApiMetricCost[] metricCosts) {
132+
ImmutableList.Builder<ApiMetricCostConfig> costs = ImmutableList.builder();
133+
if (metricCosts != null && metricCosts.length > 0) {
134+
for (ApiMetricCost cost : metricCosts) {
135+
costs.add(ApiMetricCostConfig.builder()
136+
.setName(cost.name())
137+
.setCost(cost.cost())
138+
.build());
139+
}
140+
}
141+
config.setMetricCosts(costs.build());
142+
}
127143
}

endpoints-framework/src/main/java/com/google/api/server/spi/config/model/ApiConfig.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public class ApiConfig {
8484
private final ApiNamespaceConfig namespaceConfig;
8585

8686
private final ApiClassConfig apiClassConfig;
87+
private List<ApiLimitMetricConfig> apiLimitMetrics;
8788

8889
/**
8990
* Simple factory to create {@link ApiConfig} instances.
@@ -142,6 +143,7 @@ protected ApiConfig(ApiConfig original) {
142143
this.authenticators = original.authenticators;
143144
this.peerAuthenticators = original.peerAuthenticators;
144145
this.apiKeyRequired = original.apiKeyRequired;
146+
this.apiLimitMetrics = original.apiLimitMetrics;
145147
this.authConfig = new ApiAuthConfig(original.authConfig);
146148
this.cacheControlConfig = new ApiCacheControlConfig(original.cacheControlConfig);
147149
this.frontendLimitsConfig = new ApiFrontendLimitsConfig(original.frontendLimitsConfig);
@@ -193,6 +195,7 @@ public Iterable<ApiConfigInconsistency<Object>> getConfigurationInconsistencies(
193195
.addIfInconsistent("authenticators", authenticators, config.authenticators)
194196
.addIfInconsistent("peerAuthenticators", peerAuthenticators, config.peerAuthenticators)
195197
.addIfInconsistent("apiKeyRequired", apiKeyRequired, config.apiKeyRequired)
198+
.addIfInconsistent("apiLimitMetrics", apiLimitMetrics, config.apiLimitMetrics)
196199
.addAll(authConfig.getConfigurationInconsistencies(config.authConfig))
197200
.addAll(cacheControlConfig.getConfigurationInconsistencies(config.cacheControlConfig))
198201
.addAll(frontendLimitsConfig.getConfigurationInconsistencies(config.frontendLimitsConfig))
@@ -207,7 +210,8 @@ public int hashCode() {
207210
documentationLink, backendRoot, isAbstract, defaultVersion, discoverable, useDatastore,
208211
resource, authLevel, scopeExpression, audiences, clientIds, authenticators,
209212
peerAuthenticators, authConfig, cacheControlConfig, frontendLimitsConfig,
210-
serializationConfig, apiClassConfig, issuers, issuerAudiences, apiKeyRequired);
213+
serializationConfig, apiClassConfig, issuers, issuerAudiences, apiKeyRequired,
214+
apiLimitMetrics);
211215
}
212216

213217
/**
@@ -277,6 +281,7 @@ protected void setDefaults(ServiceContext serviceContext) {
277281
authenticators = null;
278282
peerAuthenticators = null;
279283
apiKeyRequired = false;
284+
apiLimitMetrics = ImmutableList.of();
280285
}
281286

282287
public ApiKey getApiKey() {
@@ -499,4 +504,12 @@ private String toHttps(String url) {
499504
public ApiClassConfig getApiClassConfig() {
500505
return apiClassConfig;
501506
}
507+
508+
public void setApiLimitMetrics(List<ApiLimitMetricConfig> apiLimitMetrics) {
509+
this.apiLimitMetrics = apiLimitMetrics;
510+
}
511+
512+
public List<ApiLimitMetricConfig> getApiLimitMetrics() {
513+
return apiLimitMetrics;
514+
}
502515
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2017 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.api.server.spi.config.model;
17+
18+
import com.google.auto.value.AutoValue;
19+
20+
@AutoValue
21+
public abstract class ApiLimitMetricConfig {
22+
public abstract String name();
23+
public abstract String displayName();
24+
public abstract int limit();
25+
26+
public static Builder builder() {
27+
return new AutoValue_ApiLimitMetricConfig.Builder();
28+
}
29+
30+
@AutoValue.Builder
31+
public static abstract class Builder {
32+
public abstract Builder setName(String name);
33+
public abstract Builder setDisplayName(String displayName);
34+
public abstract Builder setLimit(int limit);
35+
public abstract ApiLimitMetricConfig build();
36+
}
37+
}

endpoints-framework/src/main/java/com/google/api/server/spi/config/model/ApiMethodConfig.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.api.server.spi.config.model.ApiParameterConfig.Classification;
2525
import com.google.api.server.spi.config.scope.AuthScopeExpression;
2626
import com.google.common.base.Preconditions;
27+
import com.google.common.collect.ImmutableList;
2728
import com.google.common.reflect.TypeToken;
2829

2930
import java.lang.reflect.Method;
@@ -44,6 +45,7 @@
4445
* @author Eric Orth
4546
*/
4647
public class ApiMethodConfig {
48+
4749
private enum RestMethod {
4850
LIST("list", "GET") {
4951
@Override
@@ -157,6 +159,7 @@ public String guessResourceName(
157159
private boolean ignored = false;
158160
private Boolean apiKeyRequired;
159161
private TypeToken<?> returnType;
162+
private List<ApiMetricCostConfig> metricCosts;
160163

161164
private final TypeLoader typeLoader;
162165

@@ -189,6 +192,7 @@ public ApiMethodConfig(ApiMethodConfig original, ApiClassConfig apiClassConfig)
189192
this.apiKeyRequired = original.apiKeyRequired;
190193
this.returnType = original.returnType;
191194
this.typeLoader = original.typeLoader;
195+
this.metricCosts = original.metricCosts;
192196

193197
// Parameter configs are mutable, so we need to do a deep copy.
194198
this.parameterConfigs = new ArrayList<>(original.parameterConfigs.size());
@@ -227,6 +231,7 @@ protected void setDefaults(EndpointMethod endpointMethod, TypeLoader typeLoader,
227231
ignored = false;
228232
apiKeyRequired = null;
229233
returnType = endpointMethod.getReturnType();
234+
metricCosts = ImmutableList.of();
230235
}
231236

232237
private RestMethod getRestMethod(Method method) {
@@ -257,7 +262,8 @@ public boolean equals(Object o) {
257262
Objects.equals(typeLoader, config.typeLoader) &&
258263
ignored == config.ignored &&
259264
apiKeyRequired == config.apiKeyRequired &&
260-
Objects.equals(returnType, config.returnType);
265+
Objects.equals(returnType, config.returnType) &&
266+
Objects.equals(metricCosts, config.metricCosts);
261267
} else {
262268
return false;
263269
}
@@ -267,7 +273,7 @@ public boolean equals(Object o) {
267273
public int hashCode() {
268274
return Objects.hash(endpointMethodName, parameterConfigs, name, path, httpMethod,
269275
scopeExpression, audiences, clientIds, authenticators, peerAuthenticators, typeLoader,
270-
ignored, issuerAudiences, apiKeyRequired, returnType);
276+
ignored, issuerAudiences, apiKeyRequired, returnType, metricCosts);
271277
}
272278

273279
public ApiClassConfig getApiClassConfig() {
@@ -512,4 +518,12 @@ public boolean hasResourceInResponse() {
512518
Type returnType = getReturnType().getRawType();
513519
return returnType != Void.TYPE && returnType != Void.class;
514520
}
521+
522+
public void setMetricCosts(List<ApiMetricCostConfig> metricCosts) {
523+
this.metricCosts = metricCosts;
524+
}
525+
526+
public List<ApiMetricCostConfig> getMetricCosts() {
527+
return metricCosts;
528+
}
515529
}

0 commit comments

Comments
 (0)