Skip to content

Commit

Permalink
feat(open-api-gateway): support singular tags on operations
Browse files Browse the repository at this point in the history
This change adds support for tagging operations. Previously, tagging would cause broken lambda
handler wrappers etc to be generated since the CDK construct assumes a single OperationConfig, and
tagging APIs caused multiple to be generated. In this change, we always generate a single
OperationConfig, but iterate over all apis to  make sure all operations for all tags are included.
Note that this change only supports tagging each operation with zero or one tags, multiple tags will
cause duplicated code to be generated which will not build - support for multiple tags requires an
upstream change in openapi-generator.

re #269
  • Loading branch information
cogwirrel committed Feb 1, 2023
1 parent 982cff5 commit 5d07c9c
Show file tree
Hide file tree
Showing 31 changed files with 17,591 additions and 47 deletions.
7 changes: 6 additions & 1 deletion packages/open-api-gateway/scripts/generators/generate
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ output_path=''
generator=''
generator_dir=''
additional_properties=''
src_dir='src'
while [[ "$#" -gt 0 ]]; do case $1 in
--spec-path) spec_path="$2"; shift;;
--output-path) output_path="$2"; shift;;
--generator) generator="$2"; shift;;
--generator-dir) generator_dir="$2"; shift;;
--additional-properties) additional_properties="$2"; shift;;
--src-dir) src_dir="$2"; shift;;
esac; shift; done

echo "Generating OpenAPI $generator_dir ($generator)..."
Expand All @@ -38,6 +40,9 @@ fi
# Install dependencies
$pkg_manager install --silent --save-dev @openapitools/openapi-generator-cli@2.5.1

# Support a special placeholder of {{src}} in config.yaml to ensure our custom templates get written to the correct folder
sed 's|{{src}}|'"$src_dir"'|g' config.yaml > config.final.yaml

# Generate the client
npx openapi-generator-cli generate \
--log-to-stderr \
Expand All @@ -46,7 +51,7 @@ npx openapi-generator-cli generate \
--generate-alias-as-model \
--minimal-update \
--template-dir templates \
--config config.yaml \
--config config.final.yaml \
--additional-properties="$additional_properties" \
--input-spec $spec_path \
--output $output_path
Expand Down
16 changes: 8 additions & 8 deletions packages/open-api-gateway/scripts/generators/java/config.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
files:
operationConfig.mustache:
destinationFilename: /OperationConfig.java
templateType: API
destinationFilename: {{src}}/api/DefaultApi/OperationConfig.java
templateType: SupportingFiles
operations.mustache:
destinationFilename: /Operations.java
templateType: API
destinationFilename: {{src}}/api/DefaultApi/Operations.java
templateType: SupportingFiles
operationLookup.mustache:
destinationFilename: /OperationLookup.java
templateType: API
destinationFilename: {{src}}/api/DefaultApi/OperationLookup.java
templateType: SupportingFiles
handlers.mustache:
destinationFilename: /Handlers.java
templateType: API
destinationFilename: {{src}}/api/DefaultApi/Handlers.java
templateType: SupportingFiles
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
{{#apiInfo}}
{{#apis.0}}
package {{package}};
{{/apis.0}}
{{/apiInfo}}

{{#apiInfo}}
{{#apis.0}}
import {{modelPackage}}.*;
{{/apis.0}}
{{/apiInfo}}

import java.util.Arrays;
import java.util.Optional;
Expand All @@ -25,17 +33,25 @@ import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;

{{#apiInfo}}
{{#apis}}
{{#imports}}import {{import}};
{{/imports}}
{{/apis}}
{{/apiInfo}}

{{#apiInfo}}
{{#apis.0}}
import {{invokerPackage}}.JSON;
{{/apis.0}}
{{/apiInfo}}

{{>generatedAnnotation}}
public class Handlers {
static {
// JSON has a static instance of Gson which is instantiated lazily the first time it is initialised.
// Create an instance here to insure that the static Gson instance is always available.
// Create an instance here to ensure that the static Gson instance is always available.
new JSON();
}

Expand Down Expand Up @@ -221,6 +237,8 @@ public class Handlers {
}
}

{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
/**
Expand Down Expand Up @@ -513,19 +531,31 @@ public class Handlers {
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
public static abstract class HandlerRouter implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
private static final String {{nickname}}MethodAndPath = concatMethodAndPath("{{httpMethod}}", "{{path}}");
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
private final {{operationIdCamelCase}} constructed{{operationIdCamelCase}};
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
/**
Expand All @@ -534,13 +564,19 @@ public class Handlers {
public abstract {{operationIdCamelCase}} {{nickname}}();
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
private static enum Route {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{nickname}}Route,
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
}
/**
Expand All @@ -549,19 +585,27 @@ public class Handlers {
private final Map<String, Route> routes = new HashMap<>();
public HandlerRouter() {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
this.routes.put({{nickname}}MethodAndPath, Route.{{nickname}}Route);
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
// Handlers are all constructed in the router's constructor such that lambda behaviour remains consistent;
// ie resources created in the constructor remain in memory between invocations.
// https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
this.constructed{{operationIdCamelCase}} = this.{{nickname}}();
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
}

/**
Expand All @@ -581,6 +625,8 @@ public class Handlers {
Route route = this.routes.get(methodAndPath);
switch (route) {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
case {{nickname}}Route:
Expand All @@ -589,6 +635,8 @@ public class Handlers {
return this.constructed{{operationIdCamelCase}}.handleRequestWithAdditionalInterceptors(event, context, {{nickname}}Interceptors);
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
default:
throw new RuntimeException(String.format("No registered handler for method {} and path {}", method, path));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
{{#apiInfo}}
{{#apis.0}}
package {{package}};
{{/apis.0}}
{{/apiInfo}}

{{#apiInfo}}
{{#apis.0}}
import {{modelPackage}}.*;
{{/apis.0}}
{{/apiInfo}}

{{#apiInfo}}
{{#apis}}
{{#imports}}import {{import}};
{{/imports}}
{{/apis}}
{{/apiInfo}}

import java.util.HashMap;
import java.util.Map;
Expand All @@ -12,19 +24,27 @@ import java.util.Map;
{{>generatedAnnotation}}
@lombok.Builder @lombok.Getter
public class OperationConfig<T> {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
private T {{nickname}};
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}

public Map<String, T> asMap() {
Map<String, T> map = new HashMap<>();
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
map.put("{{nickname}}", this.{{nickname}});
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
return map;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
{{#apiInfo}}
{{#apis.0}}
package {{package}};
{{/apis.0}}
{{/apiInfo}}

{{#apiInfo}}
{{#apis.0}}
import {{modelPackage}}.*;
{{/apis.0}}
{{/apiInfo}}

{{#apiInfo}}
{{#apis}}
{{#imports}}import {{import}};
{{/imports}}
{{/apis}}
{{/apiInfo}}

import java.util.HashMap;
import java.util.Map;
Expand All @@ -18,11 +30,15 @@ public class OperationLookup {
public static Map<String, Map<String, String>> getOperationLookup() {
final Map<String, Map<String, String>> config = new HashMap<>();
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
config.put("{{nickname}}", new HashMap<String, String>() { { put("path", "{{path}}"); put("method", "{{httpMethod}}"); } });
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}

return config;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{{#apiInfo}}
{{#apis.0}}
package {{package}};
{{/apis.0}}
{{/apiInfo}}

{{>generatedAnnotation}}
public class Operations {
Expand All @@ -9,11 +13,15 @@ public class Operations {
*/
public static <T> OperationConfig.OperationConfigBuilder<T> all(final T value) {
return OperationConfig.<T>builder()
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
.{{nickname}}(value)
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
files:
operationConfig.mustache:
destinationFilename: _operation_config.py
templateType: API
destinationFilename: {{src}}/apis/tags/default_api_operation_config.py
templateType: SupportingFiles
Loading

0 comments on commit 5d07c9c

Please sign in to comment.