diff --git a/.github/workflows/pr-ci.yaml b/.github/workflows/pr-ci.yaml index d7bab279..207da233 100644 --- a/.github/workflows/pr-ci.yaml +++ b/.github/workflows/pr-ci.yaml @@ -41,6 +41,9 @@ jobs: - name: Verify java package run: mvn verify + - name: Install java package + run: + mvn install - name: Integration standard e2e run: ./e2e_test.sh 1 diff --git a/python/rpdk/java/codegen.py b/python/rpdk/java/codegen.py index 0762109c..e6ede443 100644 --- a/python/rpdk/java/codegen.py +++ b/python/rpdk/java/codegen.py @@ -360,6 +360,7 @@ def generate(self, project): contents = template.render( package_name=self.package_name, operations=project.schema.get("handlers", {}).keys(), + contains_type_configuration=project.configuration_schema, pojo_name="ResourceModel", wrapper_parent="LambdaWrapper", ) @@ -384,12 +385,25 @@ def generate(self, project): contents = template.render( package_name=self.package_name, operations=OPERATIONS, + contains_type_configuration=project.configuration_schema, pojo_name="ResourceModel", ) project.overwrite(path, contents) # generate POJOs models = resolve_models(project.schema) + if project.configuration_schema: + configuration_schema_path = ( + self._get_generated_root(project) + / project.configuration_schema_filename + ) + project.write_configuration_schema(configuration_schema_path) + configuration_models = resolve_models( + project.configuration_schema, "TypeConfigurationModel" + ) + else: + configuration_models = {"TypeConfigurationModel": {}} + models.update(configuration_models) LOG.debug("Writing %d POJOs", len(models)) @@ -416,6 +430,9 @@ def generate(self, project): package_name=self.package_name, model_name=model_name, properties=properties, + no_args_constructor_required=( + model_name != "TypeConfigurationModel" or len(properties) != 0 + ), ) project.overwrite(path, contents) @@ -439,6 +456,7 @@ def _write_executable_wrapper_class(self, src, project): package_name=self.package_name, operations=project.schema.get("handlers", {}).keys(), pojo_name="ResourceModel", + contains_type_configuration=project.configuration_schema, wrapper_parent="ExecutableWrapper", ) project.overwrite(path, contents) @@ -495,7 +513,7 @@ def _find_jar(project): (project.root / "target").glob("{}-*.jar".format(project.hypenated_name)) ) if not jar_glob: - LOG.debug("No Java ARchives match") + LOG.debug("No Java Archives matched at %s", str(project.root / "target")) raise JavaArchiveNotFoundError( "No JAR artifact was found.\n" "Please run 'mvn package' or the equivalent command " diff --git a/python/rpdk/java/templates/generate/BaseHandler.java b/python/rpdk/java/templates/generate/BaseHandler.java index 910e4bbf..154c3434 100644 --- a/python/rpdk/java/templates/generate/BaseHandler.java +++ b/python/rpdk/java/templates/generate/BaseHandler.java @@ -6,12 +6,11 @@ import software.amazon.cloudformation.proxy.ProgressEvent; import software.amazon.cloudformation.proxy.ResourceHandlerRequest; -public abstract class BaseHandler { +public abstract class BaseHandler { public abstract ProgressEvent<{{ pojo_name }}, CallbackT> handleRequest( final AmazonWebServicesClientProxy proxy, final ResourceHandlerRequest<{{ pojo_name }}> request, final CallbackT callbackContext, - final Logger logger); - + final Logger logger{{ ',\n final ConfigurationT typeConfiguration' if contains_type_configuration }}); } diff --git a/python/rpdk/java/templates/generate/HandlerWrapper.java b/python/rpdk/java/templates/generate/HandlerWrapper.java index 977ac0b0..8b40bbb4 100644 --- a/python/rpdk/java/templates/generate/HandlerWrapper.java +++ b/python/rpdk/java/templates/generate/HandlerWrapper.java @@ -42,17 +42,17 @@ import org.json.JSONObject; -public class {{ "HandlerWrapper" if wrapper_parent == "LambdaWrapper" else "HandlerWrapperExecutable" }} extends {{ wrapper_parent }}<{{ pojo_name }}, CallbackContext> { +public class {{ "HandlerWrapper" if wrapper_parent == "LambdaWrapper" else "HandlerWrapperExecutable" }} extends {{ wrapper_parent }}<{{ pojo_name }}, CallbackContext, TypeConfigurationModel> { private final Configuration configuration = new Configuration(); private JSONObject resourceSchema; - private final Map> handlers = new HashMap<>(); - private final static TypeReference> REQUEST_REFERENCE = - new TypeReference>() {}; + private final Map> handlers = new HashMap<>(); + private final static TypeReference> REQUEST_REFERENCE = + new TypeReference>() {}; private final static TypeReference<{{ pojo_name }}> TYPE_REFERENCE = new TypeReference<{{ pojo_name }}>() {}; - private final static TypeReference> TEST_ENTRY_TYPE_REFERENCE = - new TypeReference>() {}; + private final static TypeReference> TEST_ENTRY_TYPE_REFERENCE = + new TypeReference>() {}; public {{ "HandlerWrapper" if wrapper_parent == "LambdaWrapper" else "HandlerWrapperExecutable" }}() { @@ -67,18 +67,22 @@ private void initialiseHandlers() { @Override public ProgressEvent<{{ pojo_name }}, CallbackContext> invokeHandler( - final AmazonWebServicesClientProxy proxy, - final ResourceHandlerRequest<{{ pojo_name }}> request, - final Action action, - final CallbackContext callbackContext) { + final AmazonWebServicesClientProxy proxy, + final ResourceHandlerRequest<{{ pojo_name }}> request, + final Action action, + final CallbackContext callbackContext, + final TypeConfigurationModel typeConfiguration) { + final String actionName = (action == null) ? "" : action.toString(); // paranoia if (!handlers.containsKey(action)) throw new RuntimeException("Unknown action " + actionName); - final BaseHandler handler = handlers.get(action); + + final BaseHandler handler = handlers.get(action); loggerProxy.log(String.format("[%s] invoking handler...", actionName)); - final ProgressEvent<{{ pojo_name }}, CallbackContext> result = handler.handleRequest(proxy, request, callbackContext, loggerProxy); + final ProgressEvent<{{ pojo_name }}, CallbackContext> result = handler.handleRequest(proxy, request, + callbackContext, loggerProxy{{ ', typeConfiguration' if contains_type_configuration }}); loggerProxy.log(String.format("[%s] handler invoked", actionName)); return result; } @@ -94,10 +98,11 @@ public void testEntrypoint( this.loggerProxy = new LoggerProxy(); this.loggerProxy.addLogPublisher(new LambdaLogPublisher(context.getLogger())); - ProgressEvent<{{ pojo_name }}, CallbackContext> response = ProgressEvent.failed(null, null, HandlerErrorCode.InternalFailure, "Uninitialized"); + ProgressEvent<{{ pojo_name }}, CallbackContext> response = ProgressEvent.failed(null, null, + HandlerErrorCode.InternalFailure, "Uninitialized"); try { final String input = IOUtils.toString(inputStream, "UTF-8"); - final ResourceHandlerTestPayload<{{ pojo_name }}, CallbackContext> payload = + final ResourceHandlerTestPayload<{{ pojo_name }}, CallbackContext, TypeConfigurationModel> payload = this.serializer.deserialize( input, TEST_ENTRY_TYPE_REFERENCE); @@ -105,7 +110,8 @@ public void testEntrypoint( final AmazonWebServicesClientProxy proxy = new AmazonWebServicesClientProxy( loggerProxy, payload.getCredentials(), () -> (long) context.getRemainingTimeInMillis()); - response = invokeHandler(proxy, payload.getRequest(), payload.getAction(), payload.getCallbackContext()); + response = invokeHandler(proxy, payload.getRequest(), payload.getAction(), payload.getCallbackContext(), + payload.getTypeConfiguration()); } catch (final BaseHandlerException e) { response = ProgressEvent.defaultFailureHandler(e, e.getErrorCode()); } catch (final AmazonServiceException | AwsServiceException e) { @@ -135,7 +141,7 @@ public static void main(String[] args) throws IOException { System.out.println("__CFN_RESOURCE_END_RESPONSE__"); } -private static void readFileToSystemOut(final String fileName) throws IOException { + private static void readFileToSystemOut(final String fileName) throws IOException { //Create object of FileReader final FileReader inputFile = new FileReader(fileName); try(BufferedReader bufferReader = new BufferedReader(inputFile)) { @@ -161,8 +167,10 @@ public Map provideResourceDefinedTags(final {{ pojo_name}} resou } @Override - protected ResourceHandlerRequest<{{ pojo_name }}> transform(final HandlerRequest<{{ pojo_name }}, CallbackContext> request) throws IOException { - final RequestData<{{ pojo_name }}> requestData = request.getRequestData(); + protected ResourceHandlerRequest<{{ pojo_name }}> transform( + final HandlerRequest<{{ pojo_name }}, CallbackContext,TypeConfigurationModel> request) throws IOException { + + final RequestData<{{ pojo_name }}, TypeConfigurationModel> requestData = request.getRequestData(); return ResourceHandlerRequest.<{{ pojo_name }}>builder() .clientRequestToken(request.getBearerToken()) @@ -179,7 +187,7 @@ public Map provideResourceDefinedTags(final {{ pojo_name}} resou } @Override - protected TypeReference> getTypeReference() { + protected TypeReference> getTypeReference() { return REQUEST_REFERENCE; } diff --git a/python/rpdk/java/templates/generate/POJO.java b/python/rpdk/java/templates/generate/POJO.java index 4d16d5a4..33073f6e 100644 --- a/python/rpdk/java/templates/generate/POJO.java +++ b/python/rpdk/java/templates/generate/POJO.java @@ -8,16 +8,13 @@ import java.util.Map; import java.util.List; import java.util.Set; -import lombok.AllArgsConstructor; +import lombok.AllArgsConstructor;{{ '\nimport lombok.NoArgsConstructor;' if no_args_constructor_required }} import lombok.Builder; import lombok.Data; -import lombok.NoArgsConstructor; - @Data -@Builder(toBuilder = true) -@AllArgsConstructor -@NoArgsConstructor +@Builder +@AllArgsConstructor{{ '\n@NoArgsConstructor' if no_args_constructor_required }} @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE) public class {{ model_name|uppercase_first_letter }} { {% for name, type in properties.items() %} diff --git a/src/main/java/software/amazon/cloudformation/AbstractWrapper.java b/src/main/java/software/amazon/cloudformation/AbstractWrapper.java index db49af5d..53c963a0 100644 --- a/src/main/java/software/amazon/cloudformation/AbstractWrapper.java +++ b/src/main/java/software/amazon/cloudformation/AbstractWrapper.java @@ -70,7 +70,7 @@ import software.amazon.cloudformation.resource.Validator; import software.amazon.cloudformation.resource.exceptions.ValidationException; -public abstract class AbstractWrapper { +public abstract class AbstractWrapper { public static final SdkHttpClient HTTP_CLIENT = ApacheHttpClient.builder().build(); @@ -90,7 +90,7 @@ public abstract class AbstractWrapper { protected final CloudWatchProvider providerCloudWatchProvider; protected final CloudWatchLogsProvider cloudWatchLogsProvider; protected final SchemaValidator validator; - protected final TypeReference> typeReference; + protected final TypeReference> typeReference; protected MetricsPublisher providerMetricsPublisher; @@ -179,7 +179,7 @@ public void processRequest(final InputStream inputStream, final OutputStream out TerminalException { ProgressEvent handlerResponse = null; - HandlerRequest request = null; + HandlerRequest request = null; scrubFiles(); try { if (inputStream == null) { @@ -239,7 +239,8 @@ public void processRequest(final InputStream inputStream, final OutputStream out } private ProgressEvent - processInvocation(final JSONObject rawRequest, final HandlerRequest request) throws IOException, + processInvocation(final JSONObject rawRequest, final HandlerRequest request) + throws IOException, TerminalException { assert request != null : "Invalid request object received"; @@ -260,6 +261,7 @@ public void processRequest(final InputStream inputStream, final OutputStream out // transform the request object to pass to caller ResourceHandlerRequest resourceHandlerRequest = transform(request); + ConfigurationT typeConfiguration = request.getRequestData().getTypeConfiguration(); if (resourceHandlerRequest != null) { resourceHandlerRequest.setPreviousResourceTags(getPreviousResourceTags(request)); @@ -325,7 +327,7 @@ public void processRequest(final InputStream inputStream, final OutputStream out } ProgressEvent handlerResponse = wrapInvocationAndHandleErrors(awsClientProxy, - resourceHandlerRequest, request, callbackContext); + resourceHandlerRequest, request, callbackContext, typeConfiguration); if (handlerResponse.getStatus() == OperationStatus.IN_PROGRESS && !isMutatingAction) { throw new TerminalException("READ and LIST handlers must return synchronously."); @@ -334,8 +336,9 @@ public void processRequest(final InputStream inputStream, final OutputStream out return handlerResponse; } - private void - logUnhandledError(final String errorDescription, final HandlerRequest request, final Throwable e) { + private void logUnhandledError(final String errorDescription, + final HandlerRequest request, + final Throwable e) { log(String.format("%s in a %s action on a %s: %s%n%s", errorDescription, request.getAction(), request.getResourceType(), e.toString(), ExceptionUtils.getStackTrace(e))); } @@ -349,13 +352,14 @@ public void processRequest(final InputStream inputStream, final OutputStream out private ProgressEvent wrapInvocationAndHandleErrors(final AmazonWebServicesClientProxy awsClientProxy, final ResourceHandlerRequest resourceHandlerRequest, - final HandlerRequest request, - final CallbackT callbackContext) { + final HandlerRequest request, + final CallbackT callbackContext, + final ConfigurationT typeConfiguration) { Date startTime = Date.from(Instant.now()); try { ProgressEvent handlerResponse = invokeHandler(awsClientProxy, resourceHandlerRequest, - request.getAction(), callbackContext); + request.getAction(), callbackContext, typeConfiguration); if (handlerResponse != null) { this.log(String.format("Handler returned %s", handlerResponse.getStatus())); } else { @@ -384,7 +388,8 @@ public void processRequest(final InputStream inputStream, final OutputStream out } - protected void writeResponse(final OutputStream outputStream, final ProgressEvent response) + protected void writeResponse(final OutputStream outputStream, + final ProgressEvent response) throws IOException { if (response.getResourceModel() != null) { // strip write only properties on final results, we will need the intact model @@ -437,7 +442,7 @@ private void validateModel(final JSONObject modelObject) throws ValidationExcept * and is not needed by the handler implementations * @return A converted ResourceHandlerRequest model */ - protected abstract ResourceHandlerRequest transform(HandlerRequest request) + protected abstract ResourceHandlerRequest transform(HandlerRequest request) throws IOException; /** @@ -465,6 +470,7 @@ protected abstract ResourceHandlerRequest transform(HandlerRequest transform(HandlerRequest invokeHandler(AmazonWebServicesClientProxy proxy, ResourceHandlerRequest request, Action action, - CallbackT callbackContext) + CallbackT callbackContext, + ConfigurationT typeConfiguration) throws Exception; /* @@ -512,7 +519,7 @@ private void log(final String message) { } } - protected abstract TypeReference> getTypeReference(); + protected abstract TypeReference> getTypeReference(); protected abstract TypeReference getModelTypeReference(); @@ -536,7 +543,7 @@ protected void scrubFiles() { * @return a Map of Tag names to Tag values */ @VisibleForTesting - protected Map getDesiredResourceTags(final HandlerRequest request) { + protected Map getDesiredResourceTags(final HandlerRequest request) { Map desiredResourceTags = new HashMap<>(); JSONObject object; @@ -559,7 +566,7 @@ protected Map getDesiredResourceTags(final HandlerRequest getPreviousResourceTags(final HandlerRequest request) { + protected Map getPreviousResourceTags(final HandlerRequest request) { Map previousResourceTags = new HashMap<>(); if (request != null && request.getRequestData() != null) { @@ -574,7 +581,7 @@ protected Map getPreviousResourceTags(final HandlerRequest request) { + protected String getStackId(final HandlerRequest request) { if (request != null) { return request.getStackId(); } diff --git a/src/main/java/software/amazon/cloudformation/ExecutableWrapper.java b/src/main/java/software/amazon/cloudformation/ExecutableWrapper.java index f427de20..aee6b922 100644 --- a/src/main/java/software/amazon/cloudformation/ExecutableWrapper.java +++ b/src/main/java/software/amazon/cloudformation/ExecutableWrapper.java @@ -29,7 +29,8 @@ import software.amazon.cloudformation.resource.SchemaValidator; import software.amazon.cloudformation.resource.Serializer; -public abstract class ExecutableWrapper extends AbstractWrapper { +public abstract class ExecutableWrapper + extends AbstractWrapper { private Logger platformLogger = LoggerFactory.getLogger("GLOBAL"); public ExecutableWrapper() { diff --git a/src/main/java/software/amazon/cloudformation/LambdaWrapper.java b/src/main/java/software/amazon/cloudformation/LambdaWrapper.java index dc49c0c4..6565c4ec 100644 --- a/src/main/java/software/amazon/cloudformation/LambdaWrapper.java +++ b/src/main/java/software/amazon/cloudformation/LambdaWrapper.java @@ -29,8 +29,8 @@ import software.amazon.cloudformation.resource.SchemaValidator; import software.amazon.cloudformation.resource.Serializer; -public abstract class LambdaWrapper extends AbstractWrapper - implements RequestStreamHandler { +public abstract class LambdaWrapper + extends AbstractWrapper implements RequestStreamHandler { public LambdaWrapper() { super(); diff --git a/src/main/java/software/amazon/cloudformation/exceptions/CfnInvalidTypeConfigurationException.java b/src/main/java/software/amazon/cloudformation/exceptions/CfnInvalidTypeConfigurationException.java new file mode 100644 index 00000000..0dcb9308 --- /dev/null +++ b/src/main/java/software/amazon/cloudformation/exceptions/CfnInvalidTypeConfigurationException.java @@ -0,0 +1,13 @@ +package software.amazon.cloudformation.exceptions; + +import software.amazon.cloudformation.proxy.HandlerErrorCode; + +public class CfnInvalidTypeConfigurationException extends BaseHandlerException { + + private static final long serialVersionUID = -1646136434112354328L; + + public CfnInvalidTypeConfigurationException(String resourceTypeName, String message) { + super(String.format(HandlerErrorCode.InvalidTypeConfiguration.getMessage(), resourceTypeName, message), + null, HandlerErrorCode.InvalidTypeConfiguration); + } +} diff --git a/src/main/java/software/amazon/cloudformation/proxy/ExceptionMessages.java b/src/main/java/software/amazon/cloudformation/proxy/ExceptionMessages.java index f9d0d101..ccf5af9a 100644 --- a/src/main/java/software/amazon/cloudformation/proxy/ExceptionMessages.java +++ b/src/main/java/software/amazon/cloudformation/proxy/ExceptionMessages.java @@ -29,6 +29,7 @@ final class ExceptionMessages { static final String SERVICE_INTERNAL_ERROR = "Internal error reported from downstream service during operation '%s'."; static final String SERVICE_LIMIT_EXCEEDED = "Limit exceeded for resource of type '%s'. Reason: %s"; static final String THROTTLING = "Rate exceeded for operation '%s'."; + static final String INVALID_TYPECONFIGURATION = "Invalid TypeConfiguration provided for type '%s'. Reason: %s"; private ExceptionMessages() { } diff --git a/src/main/java/software/amazon/cloudformation/proxy/HandlerErrorCode.java b/src/main/java/software/amazon/cloudformation/proxy/HandlerErrorCode.java index 58590481..f9c45f3e 100644 --- a/src/main/java/software/amazon/cloudformation/proxy/HandlerErrorCode.java +++ b/src/main/java/software/amazon/cloudformation/proxy/HandlerErrorCode.java @@ -99,7 +99,12 @@ public enum HandlerErrorCode { * an unexpected error occurred within the handler, such as an NPE, etc. * (Terminal) */ - InternalFailure(ExceptionMessages.INTERNAL_FAILURE); + InternalFailure(ExceptionMessages.INTERNAL_FAILURE), + + /** + * typeConfiguration is null or required typeConfiguration property is null + */ + InvalidTypeConfiguration(ExceptionMessages.INVALID_TYPECONFIGURATION); @Getter private String message; diff --git a/src/main/java/software/amazon/cloudformation/proxy/HandlerRequest.java b/src/main/java/software/amazon/cloudformation/proxy/HandlerRequest.java index cad91d4a..c42feec9 100644 --- a/src/main/java/software/amazon/cloudformation/proxy/HandlerRequest.java +++ b/src/main/java/software/amazon/cloudformation/proxy/HandlerRequest.java @@ -24,7 +24,7 @@ */ @Data @NoArgsConstructor -public class HandlerRequest { +public class HandlerRequest { private Action action; private String awsAccountId; private String bearerToken; @@ -32,7 +32,7 @@ public class HandlerRequest { private String region; private String resourceType; private String resourceTypeVersion; - private RequestData requestData; + private RequestData requestData; private String stackId; private CallbackT callbackContext; private Boolean snapshotRequested; diff --git a/src/main/java/software/amazon/cloudformation/proxy/RequestData.java b/src/main/java/software/amazon/cloudformation/proxy/RequestData.java index 3587298e..41bbde6f 100644 --- a/src/main/java/software/amazon/cloudformation/proxy/RequestData.java +++ b/src/main/java/software/amazon/cloudformation/proxy/RequestData.java @@ -20,13 +20,14 @@ @Data @NoArgsConstructor -public class RequestData { +public class RequestData { private Credentials callerCredentials; private Credentials providerCredentials; private String providerLogGroupName; private String logicalResourceId; private ResourceT resourceProperties; private ResourceT previousResourceProperties; + private ConfigurationT typeConfiguration; private Map systemTags; private Map previousSystemTags; private Map stackTags; diff --git a/src/main/java/software/amazon/cloudformation/proxy/ResourceHandlerTestPayload.java b/src/main/java/software/amazon/cloudformation/proxy/ResourceHandlerTestPayload.java index cf79fb8f..77d00d31 100644 --- a/src/main/java/software/amazon/cloudformation/proxy/ResourceHandlerTestPayload.java +++ b/src/main/java/software/amazon/cloudformation/proxy/ResourceHandlerTestPayload.java @@ -31,9 +31,10 @@ @AllArgsConstructor @NoArgsConstructor @Builder(toBuilder = true) -public class ResourceHandlerTestPayload { +public class ResourceHandlerTestPayload { private Credentials credentials; private Action action; private ResourceHandlerRequest request; private CallbackT callbackContext; + private ConfigurationT typeConfiguration; } diff --git a/src/main/java/software/amazon/cloudformation/scheduler/CloudWatchScheduler.java b/src/main/java/software/amazon/cloudformation/scheduler/CloudWatchScheduler.java index 55b18263..29bf393d 100644 --- a/src/main/java/software/amazon/cloudformation/scheduler/CloudWatchScheduler.java +++ b/src/main/java/software/amazon/cloudformation/scheduler/CloudWatchScheduler.java @@ -81,9 +81,10 @@ public void refreshClient() { * @param resource model state configuration to apply * @param callback context associated with reschedule context */ - public void rescheduleAfterMinutes(final String functionArn, - final int minutesFromNow, - final HandlerRequest handlerRequest) { + public void rescheduleAfterMinutes(final String functionArn, + final int minutesFromNow, + final HandlerRequest handlerRequest) { assert client != null : "CloudWatchEventsClient was not initialised. You must call refreshClient() first."; // generate a cron expression; minutes must be a positive integer diff --git a/src/test/java/software/amazon/cloudformation/ExecutableWrapperOverride.java b/src/test/java/software/amazon/cloudformation/ExecutableWrapperOverride.java index cf366d23..ae38052a 100644 --- a/src/test/java/software/amazon/cloudformation/ExecutableWrapperOverride.java +++ b/src/test/java/software/amazon/cloudformation/ExecutableWrapperOverride.java @@ -37,7 +37,7 @@ @Data @EqualsAndHashCode(callSuper = true) -public class ExecutableWrapperOverride extends ExecutableWrapper { +public class ExecutableWrapperOverride extends ExecutableWrapper { /** * This .ctor provided for testing @@ -61,7 +61,8 @@ protected JSONObject provideResourceSchemaJSONObject() { public ProgressEvent invokeHandler(final AmazonWebServicesClientProxy awsClientProxy, final ResourceHandlerRequest request, final Action action, - final TestContext callbackContext) + final TestContext callbackContext, + final TestConfigurationModel testConfigurationModel) throws Exception { this.awsClientProxy = awsClientProxy; this.request = request; @@ -102,7 +103,8 @@ public void enqueueResponses(final List> r } @Override - protected ResourceHandlerRequest transform(final HandlerRequest request) { + protected ResourceHandlerRequest + transform(final HandlerRequest request) { return transformResponse; } @@ -114,8 +116,8 @@ protected Map provideResourceDefinedTags(final TestModel resourc public ResourceHandlerRequest transformResponse; @Override - protected TypeReference> getTypeReference() { - return new TypeReference>() { + protected TypeReference> getTypeReference() { + return new TypeReference>() { }; } diff --git a/src/test/java/software/amazon/cloudformation/LambdaWrapperOverride.java b/src/test/java/software/amazon/cloudformation/LambdaWrapperOverride.java index ef08da1a..8366febc 100644 --- a/src/test/java/software/amazon/cloudformation/LambdaWrapperOverride.java +++ b/src/test/java/software/amazon/cloudformation/LambdaWrapperOverride.java @@ -37,7 +37,7 @@ @Data @EqualsAndHashCode(callSuper = true) -public class LambdaWrapperOverride extends LambdaWrapper { +public class LambdaWrapperOverride extends LambdaWrapper { /** * This .ctor provided for testing */ @@ -60,7 +60,8 @@ protected JSONObject provideResourceSchemaJSONObject() { public ProgressEvent invokeHandler(final AmazonWebServicesClientProxy awsClientProxy, final ResourceHandlerRequest request, final Action action, - final TestContext callbackContext) + final TestContext callbackContext, + final TestConfigurationModel testConfigurationModel) throws Exception { this.awsClientProxy = awsClientProxy; this.request = request; @@ -101,7 +102,8 @@ public void enqueueResponses(final List> r } @Override - protected ResourceHandlerRequest transform(final HandlerRequest request) { + protected ResourceHandlerRequest + transform(final HandlerRequest request) { return transformResponse; } @@ -113,8 +115,8 @@ protected Map provideResourceDefinedTags(final TestModel resourc public ResourceHandlerRequest transformResponse; @Override - protected TypeReference> getTypeReference() { - return new TypeReference>() { + protected TypeReference> getTypeReference() { + return new TypeReference>() { }; } diff --git a/src/test/java/software/amazon/cloudformation/TestConfigurationModel.java b/src/test/java/software/amazon/cloudformation/TestConfigurationModel.java new file mode 100644 index 00000000..3077b4c9 --- /dev/null +++ b/src/test/java/software/amazon/cloudformation/TestConfigurationModel.java @@ -0,0 +1,32 @@ +/* +* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ +package software.amazon.cloudformation; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class TestConfigurationModel { + + private String property1; + + private Integer property2; + +} diff --git a/src/test/java/software/amazon/cloudformation/WrapperOverride.java b/src/test/java/software/amazon/cloudformation/WrapperOverride.java index c2ffcbf9..bcb7a6ce 100644 --- a/src/test/java/software/amazon/cloudformation/WrapperOverride.java +++ b/src/test/java/software/amazon/cloudformation/WrapperOverride.java @@ -40,7 +40,7 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class WrapperOverride extends AbstractWrapper { +public class WrapperOverride extends AbstractWrapper { /** * Invoked to test normal initialization flows @@ -71,7 +71,8 @@ protected JSONObject provideResourceSchemaJSONObject() { public ProgressEvent invokeHandler(final AmazonWebServicesClientProxy awsClientProxy, final ResourceHandlerRequest request, final Action action, - final TestContext callbackContext) + final TestContext callbackContext, + final TestConfigurationModel testConfigurationModel) throws Exception { this.awsClientProxy = awsClientProxy; this.request = request; @@ -85,7 +86,6 @@ public ProgressEvent invokeHandler(final AmazonWebServic } else { return invokeHandlerResponses.remove(); } - } // lets tests assert on the passed in arguments @@ -93,6 +93,7 @@ public ProgressEvent invokeHandler(final AmazonWebServic public ResourceHandlerRequest request; public Action action; public TestContext callbackContext; + public TestConfigurationModel typeConfiguration; // allows test to have the invoke throw an exception public Exception invokeHandlerException; @@ -112,7 +113,8 @@ public void enqueueResponses(final List> r } @Override - protected ResourceHandlerRequest transform(final HandlerRequest request) { + protected ResourceHandlerRequest + transform(final HandlerRequest request) { return transformResponse; } @@ -124,8 +126,8 @@ protected Map provideResourceDefinedTags(final TestModel resourc public ResourceHandlerRequest transformResponse; @Override - protected TypeReference> getTypeReference() { - return new TypeReference>() { + protected TypeReference> getTypeReference() { + return new TypeReference>() { }; } diff --git a/src/test/java/software/amazon/cloudformation/WrapperTest.java b/src/test/java/software/amazon/cloudformation/WrapperTest.java index d83f96cd..35a0bca7 100755 --- a/src/test/java/software/amazon/cloudformation/WrapperTest.java +++ b/src/test/java/software/amazon/cloudformation/WrapperTest.java @@ -944,8 +944,8 @@ public void getDesiredResourceTags_oneStackTagAndOneResourceTag() { resourceTags.put("Tag2", "Value2"); final TestModel model = TestModel.builder().tags(resourceTags).build(); - final HandlerRequest request = new HandlerRequest<>(); - final RequestData requestData = new RequestData<>(); + final HandlerRequest request = new HandlerRequest<>(); + final RequestData requestData = new RequestData<>(); requestData.setResourceProperties(model); requestData.setStackTags(stackTags); request.setRequestData(requestData); @@ -966,8 +966,8 @@ public void getDesiredResourceTags_resourceTagOverridesStackTag() { resourceTags.put("Tag1", "Value2"); final TestModel model = TestModel.builder().tags(resourceTags).build(); - final HandlerRequest request = new HandlerRequest<>(); - final RequestData requestData = new RequestData<>(); + final HandlerRequest request = new HandlerRequest<>(); + final RequestData requestData = new RequestData<>(); requestData.setResourceProperties(model); requestData.setStackTags(stackTags); request.setRequestData(requestData); @@ -987,8 +987,8 @@ public void getPreviousResourceTags_oneStackTagAndOneResourceTag() { resourceTags.put("Tag2", "Value2"); final TestModel model = TestModel.builder().tags(resourceTags).build(); - final HandlerRequest request = new HandlerRequest<>(); - final RequestData requestData = new RequestData<>(); + final HandlerRequest request = new HandlerRequest<>(); + final RequestData requestData = new RequestData<>(); requestData.setPreviousResourceProperties(model); requestData.setPreviousStackTags(stackTags); request.setRequestData(requestData); @@ -1009,8 +1009,8 @@ public void getPreviousResourceTags_resourceTagOverridesStackTag() { resourceTags.put("Tag1", "Value2"); final TestModel model = TestModel.builder().tags(resourceTags).build(); - final HandlerRequest request = new HandlerRequest<>(); - final RequestData requestData = new RequestData<>(); + final HandlerRequest request = new HandlerRequest<>(); + final RequestData requestData = new RequestData<>(); requestData.setPreviousResourceProperties(model); requestData.setPreviousStackTags(stackTags); request.setRequestData(requestData); @@ -1023,7 +1023,7 @@ public void getPreviousResourceTags_resourceTagOverridesStackTag() { @Test public void getStackId_setAndGetStackId() { - final HandlerRequest request = new HandlerRequest<>(); + final HandlerRequest request = new HandlerRequest<>(); request.setStackId("AWSStackId"); final String stackId = wrapper.getStackId(request); diff --git a/src/test/java/software/amazon/cloudformation/exceptions/CfnInvalidTypeConfigurationExceptionTests.java b/src/test/java/software/amazon/cloudformation/exceptions/CfnInvalidTypeConfigurationExceptionTests.java new file mode 100644 index 00000000..8b01250d --- /dev/null +++ b/src/test/java/software/amazon/cloudformation/exceptions/CfnInvalidTypeConfigurationExceptionTests.java @@ -0,0 +1,24 @@ +package software.amazon.cloudformation.exceptions; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import org.junit.jupiter.api.Test; + +public class CfnInvalidTypeConfigurationExceptionTests { + + @Test + public void cfnInvalidTypeConfigurationException_isBaseHandlerException() { + assertThatExceptionOfType(BaseHandlerException.class).isThrownBy(() -> { + throw new CfnInvalidTypeConfigurationException("AWS::Type::Resource", ""); + }).withNoCause().withMessageContaining("") + .withMessageContaining("AWS::Type::Resource") + .withMessageContaining("Invalid TypeConfiguration"); + } + + @Test + public void cfnInvalidTypeConfigurationException_noCauseGiven() { + assertThatExceptionOfType(CfnInvalidTypeConfigurationException.class).isThrownBy(() -> { + throw new CfnInvalidTypeConfigurationException("AWS::Type::Resource", ""); + }).withNoCause().withMessageContaining("").withMessageContaining("AWS::Type::Resource") + .withMessageContaining("Invalid TypeConfiguration"); + } +} diff --git a/src/test/java/software/amazon/cloudformation/proxy/End2EndCallChainTest.java b/src/test/java/software/amazon/cloudformation/proxy/End2EndCallChainTest.java index d1f48925..623e2483 100644 --- a/src/test/java/software/amazon/cloudformation/proxy/End2EndCallChainTest.java +++ b/src/test/java/software/amazon/cloudformation/proxy/End2EndCallChainTest.java @@ -46,6 +46,7 @@ import software.amazon.cloudformation.metrics.MetricsPublisher; import software.amazon.cloudformation.proxy.handler.Model; import software.amazon.cloudformation.proxy.handler.ServiceHandlerWrapper; +import software.amazon.cloudformation.proxy.handler.TypeConfigurationModel; import software.amazon.cloudformation.proxy.service.AccessDenied; import software.amazon.cloudformation.proxy.service.CreateRequest; import software.amazon.cloudformation.proxy.service.CreateResponse; @@ -127,8 +128,8 @@ public AwsErrorDetails awsErrorDetails() { assertThat(event.getMessage()).contains("Repo already exists"); } - private HandlerRequest prepareRequest(Model model) throws Exception { - HandlerRequest request = new HandlerRequest<>(); + private HandlerRequest prepareRequest(Model model) throws Exception { + HandlerRequest request = new HandlerRequest<>(); request.setAction(Action.CREATE); request.setAwsAccountId("1234567891234"); request.setBearerToken("dwezxdfgfgh"); @@ -136,18 +137,20 @@ private HandlerRequest prepareRequest(Model model) th request.setRegion("us-east-2"); request.setResourceType("AWS::Code::Repository"); request.setStackId(UUID.randomUUID().toString()); - RequestData data = new RequestData<>(); + RequestData data = new RequestData<>(); data.setResourceProperties(model); data.setCallerCredentials(credentials); request.setRequestData(data); return request; } - private HandlerRequest prepareRequest() throws Exception { + private HandlerRequest prepareRequest() throws Exception { return prepareRequest(Model.builder().repoName("repository").build()); } - private InputStream prepareStream(Serializer serializer, HandlerRequest request) throws Exception { + private InputStream prepareStream(Serializer serializer, + HandlerRequest request) + throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(2048); Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8); @@ -177,7 +180,8 @@ public void setCredentials(Credentials credentials) { @Order(5) @Test public void notFound() throws Exception { - final HandlerRequest request = prepareRequest(Model.builder().repoName("repository").build()); + final HandlerRequest request = prepareRequest(Model.builder().repoName("repository").build()); request.setAction(Action.READ); final Model model = request.getRequestData().getResourceProperties(); final Serializer serializer = new Serializer(); @@ -228,7 +232,7 @@ public AwsErrorDetails awsErrorDetails() { @Order(10) @Test public void createHandler() throws Exception { - final HandlerRequest request = prepareRequest(); + final HandlerRequest request = prepareRequest(); final Serializer serializer = new Serializer(); final InputStream stream = prepareStream(serializer, request); final ByteArrayOutputStream output = new ByteArrayOutputStream(2048); @@ -280,7 +284,7 @@ public void createHandler() throws Exception { @Order(20) @Test public void createHandlerAlreadyExists() throws Exception { - final HandlerRequest request = prepareRequest(); + final HandlerRequest request = prepareRequest(); final Serializer serializer = new Serializer(); final InputStream stream = prepareStream(serializer, request); final ByteArrayOutputStream output = new ByteArrayOutputStream(2048); @@ -333,7 +337,8 @@ public AwsErrorDetails awsErrorDetails() { @Order(30) @Test public void createHandlerThrottleException() throws Exception { - HandlerRequest request = prepareRequest(Model.builder().repoName("repository").build()); + HandlerRequest request = prepareRequest(Model.builder().repoName("repository").build()); request.setAction(Action.CREATE); final Serializer serializer = new Serializer(); final InputStream stream = prepareStream(serializer, request); @@ -397,7 +402,8 @@ public AwsErrorDetails awsErrorDetails() { @Order(40) @Test public void createHandlerThottleExceptionEarlyInProgressBailout() throws Exception { - final HandlerRequest request = prepareRequest(Model.builder().repoName("repository").build()); + final HandlerRequest request = prepareRequest(Model.builder().repoName("repository").build()); request.setAction(Action.CREATE); final Serializer serializer = new Serializer(); final InputStream stream = prepareStream(serializer, request); @@ -449,7 +455,8 @@ public AwsErrorDetails awsErrorDetails() { @Order(40) @Test public void accessDenied() throws Exception { - final HandlerRequest request = prepareRequest(Model.builder().repoName("repository").build()); + final HandlerRequest request = prepareRequest(Model.builder().repoName("repository").build()); request.setAction(Action.READ); final Serializer serializer = new Serializer(); final InputStream stream = prepareStream(serializer, request); diff --git a/src/test/java/software/amazon/cloudformation/proxy/handler/ServiceHandlerWrapper.java b/src/test/java/software/amazon/cloudformation/proxy/handler/ServiceHandlerWrapper.java index 7b1661f6..6825597b 100644 --- a/src/test/java/software/amazon/cloudformation/proxy/handler/ServiceHandlerWrapper.java +++ b/src/test/java/software/amazon/cloudformation/proxy/handler/ServiceHandlerWrapper.java @@ -37,7 +37,7 @@ import software.amazon.cloudformation.resource.SchemaValidator; import software.amazon.cloudformation.resource.Serializer; -public class ServiceHandlerWrapper extends AbstractWrapper { +public class ServiceHandlerWrapper extends AbstractWrapper { private final ServiceClient serviceClient; @@ -55,7 +55,8 @@ public ServiceHandlerWrapper(final CredentialsProvider providerLoggingCredential } @Override - protected ResourceHandlerRequest transform(final HandlerRequest request) { + protected ResourceHandlerRequest + transform(final HandlerRequest request) { final Model desiredResourceState; final Model previousResourceState; final Map systemTags; @@ -99,7 +100,8 @@ public Map provideResourceDefinedTags(final Model resourceModel) public ProgressEvent invokeHandler(final AmazonWebServicesClientProxy proxy, final ResourceHandlerRequest request, final Action action, - final StdCallbackContext callbackContext) { + final StdCallbackContext callbackContext, + final TypeConfigurationModel typeConfigurationModel) { switch (action) { case CREATE: return new CreateHandler(serviceClient).handleRequest(proxy, request, callbackContext, @@ -116,8 +118,8 @@ public ProgressEvent invokeHandler(final AmazonWebSer } @Override - protected TypeReference> getTypeReference() { - return new TypeReference>() { + protected TypeReference> getTypeReference() { + return new TypeReference>() { }; } diff --git a/src/test/java/software/amazon/cloudformation/proxy/handler/TypeConfigurationModel.java b/src/test/java/software/amazon/cloudformation/proxy/handler/TypeConfigurationModel.java new file mode 100644 index 00000000..e663d717 --- /dev/null +++ b/src/test/java/software/amazon/cloudformation/proxy/handler/TypeConfigurationModel.java @@ -0,0 +1,30 @@ +/* +* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ +package software.amazon.cloudformation.proxy.handler; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@AllArgsConstructor +@Builder +public class TypeConfigurationModel { + + private String property1; + + private Integer property2; + +} diff --git a/src/test/java/software/amazon/cloudformation/resource/SerializerTest.java b/src/test/java/software/amazon/cloudformation/resource/SerializerTest.java index d4f7f6a7..619fb110 100644 --- a/src/test/java/software/amazon/cloudformation/resource/SerializerTest.java +++ b/src/test/java/software/amazon/cloudformation/resource/SerializerTest.java @@ -26,6 +26,7 @@ import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import software.amazon.cloudformation.Action; +import software.amazon.cloudformation.TestConfigurationModel; import software.amazon.cloudformation.TestContext; import software.amazon.cloudformation.TestModel; import software.amazon.cloudformation.proxy.HandlerRequest; @@ -35,9 +36,9 @@ public class SerializerTest { private static final String TEST_DATA_BASE_PATH = "src/test/java/software/amazon/cloudformation/data/%s"; - private final TypeReference< - HandlerRequest> typeReference = new TypeReference>() { - }; + private final TypeReference> typeReference = new TypeReference< + HandlerRequest>() { + }; public static String loadRequestJson(final String fileName) throws IOException { final File file = new File(String.format(TEST_DATA_BASE_PATH, fileName)); @@ -63,7 +64,7 @@ public void testDeserialize_AccuratePayload() throws IOException { final String in = loadRequestJson("create.request.json"); - final HandlerRequest r = s.deserialize(in, typeReference); + final HandlerRequest r = s.deserialize(in, typeReference); assertThat(r).isNotNull(); assertThat(r.getAction()).isEqualTo(Action.CREATE); @@ -78,7 +79,7 @@ public void testDeserialize_AccuratePayload() throws IOException { .isEqualTo("arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack/e722ae60-fe62-11e8-9a0e-0ae8cc519968"); assertThat(r.getCallbackContext()).isNull(); - final RequestData requestData = r.getRequestData(); + final RequestData requestData = r.getRequestData(); assertThat(requestData.getCallerCredentials()).isNotNull(); assertThat(requestData.getCallerCredentials().getAccessKeyId()).isEqualTo("IASAYK835GAIFHAHEI23"); assertThat(requestData.getCallerCredentials().getSecretAccessKey()).isEqualTo("66iOGPN5LnpZorcLr8Kh25u8AbjHVllv5/poh2O0"); @@ -100,7 +101,7 @@ public void testDeserialize_ExtranousRequestFields_AreIncluded() throws IOExcept // however but model validation will consider raw payload final String in = loadRequestJson("create.request.with-extraneous-request-fields.json"); - final HandlerRequest r = s.deserialize(in, typeReference); + final HandlerRequest r = s.deserialize(in, typeReference); assertThat(r).isNotNull(); assertThat(r.getAction()).isEqualTo(Action.CREATE); @@ -115,7 +116,7 @@ public void testDeserialize_ExtranousRequestFields_AreIncluded() throws IOExcept .isEqualTo("arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack/e722ae60-fe62-11e8-9a0e-0ae8cc519968"); assertThat(r.getCallbackContext()).isNull(); - final RequestData requestData = r.getRequestData(); + final RequestData requestData = r.getRequestData(); assertThat(requestData.getCallerCredentials()).isNotNull(); assertThat(requestData.getCallerCredentials().getAccessKeyId()).isEqualTo("IASAYK835GAIFHAHEI23"); assertThat(requestData.getCallerCredentials().getSecretAccessKey()).isEqualTo("66iOGPN5LnpZorcLr8Kh25u8AbjHVllv5/poh2O0"); @@ -139,7 +140,7 @@ public void testDeserialize_ExtranousModelFields_AreAllowed() throws IOException // however but model validation will consider raw payload final String in = loadRequestJson("create.request.with-extraneous-model-fields.json"); - final HandlerRequest r = s.deserialize(in, typeReference); + final HandlerRequest r = s.deserialize(in, typeReference); assertThat(r).isNotNull(); assertThat(r.getAction()).isEqualTo(Action.CREATE); @@ -154,7 +155,7 @@ public void testDeserialize_ExtranousModelFields_AreAllowed() throws IOException assertThat(r.getStackId()) .isEqualTo("arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack/e722ae60-fe62-11e8-9a0e-0ae8cc519968"); - final RequestData requestData = r.getRequestData(); + final RequestData requestData = r.getRequestData(); assertThat(requestData.getCallerCredentials()).isNotNull(); assertThat(requestData.getCallerCredentials().getAccessKeyId()).isEqualTo("IASAYK835GAIFHAHEI23"); assertThat(requestData.getCallerCredentials().getSecretAccessKey()).isEqualTo("66iOGPN5LnpZorcLr8Kh25u8AbjHVllv5/poh2O0"); diff --git a/src/test/java/software/amazon/cloudformation/scheduler/CloudWatchSchedulerTest.java b/src/test/java/software/amazon/cloudformation/scheduler/CloudWatchSchedulerTest.java index 7ace62ea..a08454f9 100644 --- a/src/test/java/software/amazon/cloudformation/scheduler/CloudWatchSchedulerTest.java +++ b/src/test/java/software/amazon/cloudformation/scheduler/CloudWatchSchedulerTest.java @@ -33,6 +33,7 @@ import software.amazon.awssdk.services.cloudwatchevents.model.DeleteRuleRequest; import software.amazon.awssdk.services.cloudwatchevents.model.RemoveTargetsRequest; import software.amazon.awssdk.services.cloudwatchevents.model.RuleState; +import software.amazon.cloudformation.TestConfigurationModel; import software.amazon.cloudformation.TestContext; import software.amazon.cloudformation.TestModel; import software.amazon.cloudformation.injection.CloudWatchEventsProvider; @@ -156,7 +157,7 @@ public void test_rescheduleAfterMinutes_1MinuteFloor() throws IOException { when(cronHelper.generateOneTimeCronExpression(1)).thenReturn("cron(41 14 31 10 ? 2019)"); final CloudWatchScheduler scheduler = new CloudWatchScheduler(provider, loggerProxy, cronHelper, serializer); scheduler.refreshClient(); - final HandlerRequest request = new HandlerRequest<>(); + final HandlerRequest request = new HandlerRequest<>(); request.setRequestContext(requestContext); diff --git a/src/test/resources/software/amazon/cloudformation/wrapper-override.json b/src/test/resources/software/amazon/cloudformation/wrapper-override.json index 2a40593b..2ffbfe07 100644 --- a/src/test/resources/software/amazon/cloudformation/wrapper-override.json +++ b/src/test/resources/software/amazon/cloudformation/wrapper-override.json @@ -11,7 +11,8 @@ "type": "string" } } - } + }, + "additionalProperties": false } }, "properties": { @@ -27,7 +28,8 @@ "subProperty": { "$ref": "#/definitions/subProperty" } - } + }, + "additionalProperties": false } }, "additionalProperties": false, diff --git a/tests/data/schema-with-typeconfiguration.json b/tests/data/schema-with-typeconfiguration.json new file mode 100644 index 00000000..ef3c15b5 --- /dev/null +++ b/tests/data/schema-with-typeconfiguration.json @@ -0,0 +1,50 @@ +{ + "typeName": "Company::Test::Type", + "description": "Test type", + "typeConfiguration": { + "properties": { + "Credentials": { + "$ref": "#/definitions/Credentials" + } + }, + "additionalProperties": false, + "required": [ + "Credentials" + ] + }, + "definitions": { + "Credentials": { + "type": "object", + "properties": { + "ApiKey": { + "description": "API key", + "type": "string" + }, + "ApplicationKey": { + "description": "application key", + "type": "string" + }, + "CountryCode": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "properties": { + "Type": { + "type": "string", + "description": "The type of the monitor", + "enum": [ + "composite" + ] + } + }, + "required": [ + "Type" + ], + "primaryIdentifier": [ + "/properties/Type" + ], + "additionalProperties": false +} diff --git a/tests/data/schema-without-typeconfiguration.json b/tests/data/schema-without-typeconfiguration.json new file mode 100644 index 00000000..6a054c38 --- /dev/null +++ b/tests/data/schema-without-typeconfiguration.json @@ -0,0 +1,39 @@ +{ + "typeName": "Company::Test::Type", + "description": "Test type", + "definitions": { + "Credentials": { + "type": "object", + "properties": { + "ApiKey": { + "description": "API key", + "type": "string" + }, + "ApplicationKey": { + "description": "application key", + "type": "string" + }, + "CountryCode": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "properties": { + "Type": { + "type": "string", + "description": "The type of the monitor", + "enum": [ + "composite" + ] + } + }, + "required": [ + "Type" + ], + "primaryIdentifier": [ + "/properties/Type" + ], + "additionalProperties": false +} diff --git a/tests/test_codegen.py b/tests/test_codegen.py index d30891f1..cadd3e0e 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -1,6 +1,8 @@ # fixture and parameter have the same name # pylint: disable=redefined-outer-name,protected-access import xml.etree.ElementTree as ET +from pathlib import Path +from shutil import copyfile from unittest.mock import MagicMock, Mock, patch import yaml @@ -86,11 +88,78 @@ def test_generate(project): project.generate() + # assert TypeConfigurationModel is added to generated directory + type_configuration_model_file = ( + generated_root + / "software" + / "amazon" + / "foo" + / RESOURCE.lower() + / "TypeConfigurationModel.java" + ) + assert type_configuration_model_file.is_file() + # asserts we remove existing files in the tree assert not src_file.is_file() assert not test_file.is_file() +def test_generate_with_type_configuration(project, tmpdir): + copyfile( + str(Path.cwd() / "tests/data/schema-with-typeconfiguration.json"), + str(tmpdir / "schema-with-typeconfiguration.json"), + ) + project.type_info = ("schema", "with", "typeconfiguration") + project.load_schema() + project.load_configuration_schema() + project.generate() + generated_root = project._plugin._get_generated_root(project) + + # assert TypeConfigurationModel is added to generated directory + type_configuration_model_file = ( + generated_root + / "software" + / "amazon" + / "foo" + / RESOURCE.lower() + / "TypeConfigurationModel.java" + ) + type_configuration_schema_file = ( + generated_root / "schema-with-typeconfiguration-configuration.json" + ) + + assert type_configuration_model_file.is_file() + assert type_configuration_schema_file.is_file() + + +def test_generate_with_out_type_configuration(project, tmpdir): + copyfile( + str(Path.cwd() / "tests/data/schema-without-typeconfiguration.json"), + str(tmpdir / "schema-without-typeconfiguration.json"), + ) + project.type_info = ("schema", "without", "typeconfiguration") + project.load_schema() + project.load_configuration_schema() + project.generate() + generated_root = project._plugin._get_generated_root(project) + + # assert TypeConfigurationModel is added to generated directory + type_configuration_model_file = ( + generated_root + / "software" + / "amazon" + / "foo" + / RESOURCE.lower() + / "TypeConfigurationModel.java" + ) + type_configuration_schema_file = ( + generated_root / "schema-without-typeconfiguration-configuration.json" + ) + + assert type_configuration_model_file.is_file() + assert not type_configuration_schema_file.is_file() + + def test_protocol_version_is_set(project): assert project.settings["protocolVersion"] == "2.0.0"