ProcessedTemplate.java
// Generated by delombok at Tue Apr 06 14:11:35 UTC 2021
/*
* This file is part of the pl.wrzasq.lambda.
*
* @license http://mit-license.org/ The MIT license
* @copyright 2019 - 2020 © by Rafał Wrzeszcz - Wrzasq.pl.
*/
package pl.wrzasq.lambda.macro.lambda.function.template;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.wrzasq.commons.aws.cloudformation.macro.ResourcesDefinition;
import pl.wrzasq.commons.aws.cloudformation.macro.TemplateDefinition;
import pl.wrzasq.commons.aws.cloudformation.macro.TemplateUtils;
import pl.wrzasq.commons.json.ObjectMapperFactory;
/**
* Contains template structure with handled resources references.
*/
public class ProcessedTemplate implements TemplateDefinition {
/**
* Values converter.
*/
private static ObjectMapper objectMapper = ObjectMapperFactory.createObjectMapper();
/**
* Logger.
*/
private static Logger logger = LoggerFactory.getLogger(ProcessedTemplate.class);
/**
* Mapping of handled resources.
*/
private Map<String, LambdaFunctionResource> resources = new HashMap<>();
/**
* Template structure.
*/
private Map<String, Object> template;
/**
* Template initializer.
*
* @param input Initial template structure.
*/
public ProcessedTemplate(Map<String, Object> input) {
this.template = this.replaceReferences(this.createResources(input));
}
/**
* Finds our custom resources and creates a replacements.
*
* @param input Initial template structure.
* @return Template state after processing.
*/
private Map<String, Object> createResources(Map<String, Object> input) {
var output = new HashMap<>(input);
// re-create resources section
var section = TemplateUtils.asMap(input.get(TemplateUtils.SECTION_RESOURCES));
var resources = new HashMap<>();
for (var entry : section.entrySet()) {
var definition = ProcessedTemplate.objectMapper.convertValue(entry.getValue(), ResourcesDefinition.class);
if (definition.getType().equals(LambdaFunctionResource.LAMBDA_RESOURCE_TYPE)) {
resources.putAll(this.createResource(entry.getKey(), definition.getProperties(), "Lambda"));
} else if (definition.getType().equals(LambdaFunctionResource.SERVERLESS_RESOURCE_TYPE)) {
resources.putAll(this.createResource(entry.getKey(), definition.getProperties(), "Serverless"));
} else {
resources.put(entry.getKey(), section.get(entry.getKey()));
}
}
output.put(TemplateUtils.SECTION_RESOURCES, resources);
return output;
}
/**
* Creates our custom resource in place of virtual one.
*
* @param key Resource logical ID.
* @param properties Resource initial properties.
* @param mode Resource mode (`Lambda` or `Serverless`).
* @return Physical resources definitions.
*/
private Map<String, Object> createResource(String key, Map<String, Object> properties, String mode) {
ProcessedTemplate.logger.info("Creating resources for {}.", key);
var resource = new LambdaFunctionResource(key, mode);
this.resources.put(key, resource);
return resource.buildDefinitions(properties);
}
/**
* Replaces references to virtual resources.
*
* @param input Input template structure.
* @return Template state after processing.
*/
private Map<String, Object> replaceReferences(Map<String, Object> input) {
var output = new HashMap<>(input);
var section = TemplateUtils.asMap(input.get(TemplateUtils.SECTION_RESOURCES));
section = this.replaceDependencies(section);
output.put(TemplateUtils.SECTION_RESOURCES, this.replaceDependencies(section));
return output;
}
/**
* Handles DependsOn clauses.
*
* @param resources Resources section.
* @return New clause value.
*/
private Map<String, Object> replaceDependencies(Map<String, Object> resources) {
var output = new HashMap<String, Object>();
for (var resource : resources.entrySet()) {
var logicalId = resource.getKey();
var config = TemplateUtils.asMap(resource.getValue());
if (config.containsKey(TemplateUtils.PROPERTY_KEY_DEPENDSON)) {
var dependsOn = config.get(TemplateUtils.PROPERTY_KEY_DEPENDSON);
config.put(TemplateUtils.PROPERTY_KEY_DEPENDSON, this.replaceDependenciesIn(dependsOn));
}
output.put(logicalId, config);
}
return output;
}
/**
* Handles DependsOn clause of single resource.
*
* @param dependsOn Depends on clause.
* @return Computed new DependsOn clause.
*/
private Object replaceDependenciesIn(Object dependsOn) {
if (dependsOn instanceof List) {
return ((List<?>) dependsOn).stream().map(Object::toString).map(this::resolveDependency).collect(Collectors.toList());
} else {
return this.resolveDependency(dependsOn.toString());
}
}
/**
* Tries to resolve dependencies against lambda functions.
*
* <p>
* Effective dependency will be LogGroup, to ensure no Lambda execution happens before log group creation.
* </p>
*
* @param dependency Source dependency ID.
* @return Resolved dependency ID.
*/
private String resolveDependency(String dependency) {
return this.resources.containsKey(dependency) ? this.resources.get(dependency).getLogGroupLogicalId() : dependency;
}
/**
* Template structure.
*/
@SuppressWarnings("all")
@lombok.Generated
public Map<String, Object> getTemplate() {
return this.template;
}
}