Add 429 (Too Many Requests) error, as option

This commit is contained in:
2023-03-30 11:56:29 -05:00
parent a9e793dfb8
commit adcddff440
2 changed files with 69 additions and 32 deletions

View File

@ -115,14 +115,11 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withTitle(apiInstanceMetaData.getName()) .withTitle(apiInstanceMetaData.getName())
.withDescription(apiInstanceMetaData.getDescription()) .withDescription(apiInstanceMetaData.getDescription())
.withContact(new Contact() .withContact(new Contact()
.withEmail(apiInstanceMetaData.getContactEmail()) .withEmail(apiInstanceMetaData.getContactEmail()))
) .withVersion(version))
.withVersion(version)
)
.withServers(ListBuilder.of(new Server() .withServers(ListBuilder.of(new Server()
.withDescription("This server") .withDescription("This server")
.withUrl("/api/" + version) .withUrl("/api/" + version)));
));
openAPI.setTags(new ArrayList<>()); openAPI.setTags(new ArrayList<>());
openAPI.setPaths(new LinkedHashMap<>()); openAPI.setPaths(new LinkedHashMap<>());
@ -134,27 +131,23 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withSchemas(componentSchemas) .withSchemas(componentSchemas)
.withResponses(componentResponses) .withResponses(componentResponses)
.withSecuritySchemes(securitySchemes) .withSecuritySchemes(securitySchemes)
.withExamples(getComponentExamples()) .withExamples(getComponentExamples()));
);
securitySchemes.put("basicAuth", new SecurityScheme()
.withType("http")
.withScheme("basic"));
securitySchemes.put("bearerAuth", new SecurityScheme() securitySchemes.put("bearerAuth", new SecurityScheme()
.withType("http") .withType("http")
.withScheme("bearer") .withScheme("bearer")
.withBearerFormat("JWT") .withBearerFormat("JWT"));
);
securitySchemes.put("basicAuth", new SecurityScheme()
.withType("http")
.withScheme("basic")
);
LinkedHashMap<String, String> scopes = new LinkedHashMap<>(); LinkedHashMap<String, String> scopes = new LinkedHashMap<>();
// todo, or not todo? .withScopes(scopes) // todo, or not todo? .withScopes(scopes)
securitySchemes.put("OAuth2", new OAuth2() securitySchemes.put("OAuth2", new OAuth2()
.withFlows(MapBuilder.of("clientCredentials", new OAuth2Flow() .withFlows(MapBuilder.of("clientCredentials", new OAuth2Flow()
.withTokenUrl("/api/oauth/token") .withTokenUrl("/api/oauth/token"))));
))
);
componentSchemas.put("baseSearchResultFields", new Schema() componentSchemas.put("baseSearchResultFields", new Schema()
.withType("object") .withType("object")
.withProperties(MapBuilder.of( .withProperties(MapBuilder.of(
@ -167,8 +160,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
"pageSize", new Schema() "pageSize", new Schema()
.withType("integer") .withType("integer")
.withDescription("Requested result page size") .withDescription("Requested result page size")
)) )));
);
/////////////////// ///////////////////
// foreach table // // foreach table //
@ -363,7 +355,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withDescription("Whether to combine query field as an AND or an OR. Default is AND.") .withDescription("Whether to combine query field as an AND or an OR. Default is AND.")
.withIn("query") .withIn("query")
.withSchema(new Schema().withType("string").withEnumValues(ListBuilder.of("AND", "OR"))))) .withSchema(new Schema().withType("string").withEnumValues(ListBuilder.of("AND", "OR")))))
.withResponses(buildStandardErrorResponses()) .withResponses(buildStandardErrorResponses(apiInstanceMetaData))
.withResponse(HttpStatus.OK.getCode(), new Response() .withResponse(HttpStatus.OK.getCode(), new Response()
.withDescription("Successfully searched the " + tableLabel + " table (though may have found 0 records).") .withDescription("Successfully searched the " + tableLabel + " table (though may have found 0 records).")
.withContent(MapBuilder.of("application/json", new Content() .withContent(MapBuilder.of("application/json", new Content()
@ -410,7 +402,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withIn("path") .withIn("path")
.withRequired(true) .withRequired(true)
.withSchema(new Schema().withType(getFieldType(primaryKeyField))))) .withSchema(new Schema().withType(getFieldType(primaryKeyField)))))
.withResponses(buildStandardErrorResponses()) .withResponses(buildStandardErrorResponses(apiInstanceMetaData))
.withResponse(HttpStatus.NOT_FOUND.getCode(), buildStandardErrorResponse("The requested " + tableLabel + " record was not found.", "Could not find " + tableLabel + " with " + primaryKeyLabel + " of 47.")) .withResponse(HttpStatus.NOT_FOUND.getCode(), buildStandardErrorResponse("The requested " + tableLabel + " record was not found.", "Could not find " + tableLabel + " with " + primaryKeyLabel + " of 47."))
.withResponse(HttpStatus.OK.getCode(), new Response() .withResponse(HttpStatus.OK.getCode(), new Response()
.withDescription("Successfully got the requested " + tableLabel) .withDescription("Successfully got the requested " + tableLabel)
@ -444,7 +436,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withDescription("Field values to update in the " + tableLabel + " record.") .withDescription("Field values to update in the " + tableLabel + " record.")
.withContent(MapBuilder.of("application/json", new Content() .withContent(MapBuilder.of("application/json", new Content()
.withSchema(new Schema().withRef("#/components/schemas/" + tableApiName))))) .withSchema(new Schema().withRef("#/components/schemas/" + tableApiName)))))
.withResponses(buildStandardErrorResponses()) .withResponses(buildStandardErrorResponses(apiInstanceMetaData))
.withResponse(HttpStatus.NOT_FOUND.getCode(), buildStandardErrorResponse("The requested " + tableLabel + " record was not found.", "Could not find " + tableLabel + " with " + primaryKeyLabel + " of 47.")) .withResponse(HttpStatus.NOT_FOUND.getCode(), buildStandardErrorResponse("The requested " + tableLabel + " record was not found.", "Could not find " + tableLabel + " with " + primaryKeyLabel + " of 47."))
.withResponse(HttpStatus.NO_CONTENT.getCode(), new Response().withDescription("Successfully updated the requested " + tableLabel)) .withResponse(HttpStatus.NO_CONTENT.getCode(), new Response().withDescription("Successfully updated the requested " + tableLabel))
.withSecurity(getSecurity(tableUpdatePermissionName)); .withSecurity(getSecurity(tableUpdatePermissionName));
@ -465,7 +457,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withIn("path") .withIn("path")
.withRequired(true) .withRequired(true)
.withSchema(new Schema().withType(getFieldType(primaryKeyField))))) .withSchema(new Schema().withType(getFieldType(primaryKeyField)))))
.withResponses(buildStandardErrorResponses()) .withResponses(buildStandardErrorResponses(apiInstanceMetaData))
.withResponse(HttpStatus.NOT_FOUND.getCode(), buildStandardErrorResponse("The requested " + tableLabel + " record was not found.", "Could not find " + tableLabel + " with " + primaryKeyLabel + " of 47.")) .withResponse(HttpStatus.NOT_FOUND.getCode(), buildStandardErrorResponse("The requested " + tableLabel + " record was not found.", "Could not find " + tableLabel + " with " + primaryKeyLabel + " of 47."))
.withResponse(HttpStatus.NO_CONTENT.getCode(), new Response().withDescription("Successfully deleted the requested " + tableLabel)) .withResponse(HttpStatus.NO_CONTENT.getCode(), new Response().withDescription("Successfully deleted the requested " + tableLabel))
.withSecurity(getSecurity(tableDeletePermissionName)); .withSecurity(getSecurity(tableDeletePermissionName));
@ -493,7 +485,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withContent(MapBuilder.of("application/json", new Content() .withContent(MapBuilder.of("application/json", new Content()
.withSchema(new Schema().withRef("#/components/schemas/" + tableApiName)) .withSchema(new Schema().withRef("#/components/schemas/" + tableApiName))
))) )))
.withResponses(buildStandardErrorResponses()) .withResponses(buildStandardErrorResponses(apiInstanceMetaData))
.withResponse(HttpStatus.CREATED.getCode(), new Response() .withResponse(HttpStatus.CREATED.getCode(), new Response()
.withDescription("Successfully created the requested " + tableLabel) .withDescription("Successfully created the requested " + tableLabel)
.withContent(MapBuilder.of("application/json", new Content() .withContent(MapBuilder.of("application/json", new Content()
@ -508,8 +500,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
if(insertCapability) if(insertCapability)
{ {
openAPI.getPaths().put("/" + tableApiName + "/", new Path() openAPI.getPaths().put("/" + tableApiName + "/", new Path()
.withPost(slashPost) .withPost(slashPost));
);
} }
//////////////// ////////////////
@ -532,7 +523,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withSchema(new Schema() .withSchema(new Schema()
.withType("array") .withType("array")
.withItems(new Schema().withRef("#/components/schemas/" + tableApiName)))))) .withItems(new Schema().withRef("#/components/schemas/" + tableApiName))))))
.withResponses(buildStandardErrorResponses()) .withResponses(buildStandardErrorResponses(apiInstanceMetaData))
.withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyApiName, primaryKeyField, "post")) .withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyApiName, primaryKeyField, "post"))
.withTags(ListBuilder.of(tableLabel)) .withTags(ListBuilder.of(tableLabel))
.withSecurity(getSecurity(tableInsertPermissionName)); .withSecurity(getSecurity(tableInsertPermissionName));
@ -561,7 +552,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withReadOnly(false) .withReadOnly(false)
.withNullable(false) .withNullable(false)
.withExample("47")))))))) .withExample("47"))))))))
.withResponses(buildStandardErrorResponses()) .withResponses(buildStandardErrorResponses(apiInstanceMetaData))
.withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyApiName, primaryKeyField, "patch")) .withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyApiName, primaryKeyField, "patch"))
.withTags(ListBuilder.of(tableLabel)) .withTags(ListBuilder.of(tableLabel))
.withSecurity(getSecurity(tableUpdatePermissionName)); .withSecurity(getSecurity(tableUpdatePermissionName));
@ -582,7 +573,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
.withType("array") .withType("array")
.withItems(new Schema().withType(getFieldType(primaryKeyField))) .withItems(new Schema().withType(getFieldType(primaryKeyField)))
.withExample(List.of(42, 47)))))) .withExample(List.of(42, 47))))))
.withResponses(buildStandardErrorResponses()) .withResponses(buildStandardErrorResponses(apiInstanceMetaData))
.withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyApiName, primaryKeyField, "delete")) .withResponse(HttpStatus.MULTI_STATUS.getCode(), buildMultiStatusResponse(tableLabel, primaryKeyApiName, primaryKeyField, "delete"))
.withTags(ListBuilder.of(tableLabel)) .withTags(ListBuilder.of(tableLabel))
.withSecurity(getSecurity(tableDeletePermissionName)); .withSecurity(getSecurity(tableDeletePermissionName));
@ -601,6 +592,11 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
componentResponses.put("error" + HttpStatus.FORBIDDEN.getCode(), buildStandardErrorResponse("Forbidden. You do not have permission to access the requested resource.", "You do not have permission to access the requested resource.")); componentResponses.put("error" + HttpStatus.FORBIDDEN.getCode(), buildStandardErrorResponse("Forbidden. You do not have permission to access the requested resource.", "You do not have permission to access the requested resource."));
componentResponses.put("error" + HttpStatus.INTERNAL_SERVER_ERROR.getCode(), buildStandardErrorResponse("Internal Server Error. An error occurred in the server while processing the request.", "Database connection error. Try again later.")); componentResponses.put("error" + HttpStatus.INTERNAL_SERVER_ERROR.getCode(), buildStandardErrorResponse("Internal Server Error. An error occurred in the server while processing the request.", "Database connection error. Try again later."));
if(apiInstanceMetaData.getIncludeErrorTooManyRequests())
{
componentResponses.put("error" + HttpStatus.TOO_MANY_REQUESTS.getCode(), buildStandardErrorResponse("Too Many Requests. Your application has issued too many API requests in too short of a time frame."));
}
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecOutput(); GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecOutput();
output.setOpenAPI(openAPI); output.setOpenAPI(openAPI);
output.setYaml(YamlUtils.toYaml(openAPI)); output.setYaml(YamlUtils.toYaml(openAPI));
@ -1016,14 +1012,21 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
private static Map<Integer, Response> buildStandardErrorResponses() private static Map<Integer, Response> buildStandardErrorResponses(ApiInstanceMetaData apiInstanceMetaData)
{ {
return MapBuilder.of( Map<Integer, Response> rs = MapBuilder.of(
HttpStatus.BAD_REQUEST.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.BAD_REQUEST.getCode()), HttpStatus.BAD_REQUEST.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.BAD_REQUEST.getCode()),
HttpStatus.UNAUTHORIZED.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.UNAUTHORIZED.getCode()), HttpStatus.UNAUTHORIZED.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.UNAUTHORIZED.getCode()),
HttpStatus.FORBIDDEN.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.FORBIDDEN.getCode()), HttpStatus.FORBIDDEN.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.FORBIDDEN.getCode()),
HttpStatus.INTERNAL_SERVER_ERROR.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.INTERNAL_SERVER_ERROR.getCode()) HttpStatus.INTERNAL_SERVER_ERROR.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.INTERNAL_SERVER_ERROR.getCode())
); );
if(apiInstanceMetaData.getIncludeErrorTooManyRequests())
{
rs.put(HttpStatus.TOO_MANY_REQUESTS.getCode(), new Response().withRef("#/components/responses/error" + HttpStatus.TOO_MANY_REQUESTS.getCode()));
}
return (rs);
} }

View File

@ -50,6 +50,8 @@ public class ApiInstanceMetaData extends QMiddlewareInstanceMetaData
private List<APIVersion> pastVersions; private List<APIVersion> pastVersions;
private List<APIVersion> futureVersions; private List<APIVersion> futureVersions;
private boolean includeErrorTooManyRequests = true;
/******************************************************************************* /*******************************************************************************
@ -120,6 +122,7 @@ public class ApiInstanceMetaData extends QMiddlewareInstanceMetaData
} }
} }
// todo - find duplicate tableApiNames!!
} }
@ -339,4 +342,35 @@ public class ApiInstanceMetaData extends QMiddlewareInstanceMetaData
return (this); return (this);
} }
/*******************************************************************************
** Getter for includeErrorTooManyRequests
*******************************************************************************/
public boolean getIncludeErrorTooManyRequests()
{
return (this.includeErrorTooManyRequests);
}
/*******************************************************************************
** Setter for includeErrorTooManyRequests
*******************************************************************************/
public void setIncludeErrorTooManyRequests(boolean includeErrorTooManyRequests)
{
this.includeErrorTooManyRequests = includeErrorTooManyRequests;
}
/*******************************************************************************
** Fluent setter for includeErrorTooManyRequests
*******************************************************************************/
public ApiInstanceMetaData withIncludeErrorTooManyRequests(boolean includeErrorTooManyRequests)
{
this.includeErrorTooManyRequests = includeErrorTooManyRequests;
return (this);
}
} }