Skip to content

Commit 3f35e72

Browse files
start adding unit tests and fixing code
1 parent 8382b41 commit 3f35e72

3 files changed

Lines changed: 153 additions & 21 deletions

File tree

plugins/iac/nimble/src/main/java/org/apache/cloudstack/tosca/parser/ToscaParsingErrorsContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@ public boolean hasErrors() {
4747
}
4848

4949
public String buildErrorMessages() {
50-
return messages.stream().map(Message::toString).collect(Collectors.joining("; "));
50+
return messages.stream().map(Message::toString).collect(Collectors.joining("\n"));
5151
}
5252
}

plugins/iac/nimble/src/main/java/org/apache/cloudstack/tosca/parser/ToscaServiceTemplateParser.java

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.apache.cloudstack.tosca.model.ToscaTypeDefinition;
3030
import org.apache.commons.collections.CollectionUtils;
3131
import org.apache.commons.collections.MapUtils;
32+
import org.apache.logging.log4j.LogManager;
33+
import org.apache.logging.log4j.Logger;
3234

3335
import javax.inject.Inject;
3436
import java.util.HashMap;
@@ -39,8 +41,14 @@
3941
import java.util.stream.Collectors;
4042

4143
public class ToscaServiceTemplateParser {
44+
private final Logger logger = LogManager.getLogger(ToscaServiceTemplateParser.class);
45+
46+
private final ToscaFieldParser toscaFieldParser;
47+
4248
@Inject
43-
private ToscaFieldParser toscaFieldParser;
49+
public ToscaServiceTemplateParser(ToscaFieldParser toscaFieldParser) {
50+
this.toscaFieldParser = toscaFieldParser;
51+
}
4452

4553
public ToscaServiceTemplate parseServiceTemplate(String content, Map<String, ToscaNodeType> toscaProfile, Account caller) {
4654
Map<String, Object> rawToscaTemplate = ToscaYamlHelper.asMap(ToscaYamlHelper.loadYaml(content));
@@ -62,6 +70,28 @@ public ToscaServiceTemplate parseServiceTemplate(String content, Map<String, Tos
6270
return new ToscaServiceTemplate();
6371
}
6472

73+
/**
74+
* Checks whether all the YAML keys present in the root and service_template levels of
75+
* TOSCA service templates are known and whether all the required ones were provided.
76+
* @param template The TOSCA service template to be validated.
77+
* @param context The service template parsing context ({@link ToscaServiceTemplateParsingContext}) for error handling purposes.
78+
* @throws InvalidParameterValueException When unknown keys are present or required keys are missing.
79+
*/
80+
private void checkRootServiceTemplateYamlKeys(Map<String, Object> template, ToscaServiceTemplateParsingContext context) {
81+
validateKnownToscaKeys(template, Set.of(ToscaConstants.SERVICE_TEMPLATE_TOSCA_VERSION_KEY, ToscaConstants.SERVICE_TEMPLATE_DESCRIPTION_KEY, ToscaConstants.SERVICE_TEMPLATE_SERVICE_TEMPLATE_KEY), "Root level", context);
82+
validateRequiredToscaKeys(template, Set.of(ToscaConstants.SERVICE_TEMPLATE_SERVICE_TEMPLATE_KEY), "Root level", context);
83+
if (context.hasErrors()) {
84+
throw new InvalidParameterValueException(context.buildErrorMessages());
85+
}
86+
87+
Map<String, Object> serviceTemplateSection = ToscaYamlHelper.asMap(template.get(ToscaConstants.SERVICE_TEMPLATE_SERVICE_TEMPLATE_KEY));
88+
validateKnownToscaKeys(serviceTemplateSection, Set.of(ToscaConstants.SERVICE_TEMPLATE_NODE_TEMPLATES_KEY, ToscaConstants.SERVICE_TEMPLATE_INPUTS_KEY, ToscaConstants.SERVICE_TEMPLATE_SERVICE_TEMPLATE_KEY), "Service template level", context);
89+
validateRequiredToscaKeys(serviceTemplateSection, Set.of(ToscaConstants.SERVICE_TEMPLATE_NODE_TEMPLATES_KEY), "Service template level", context);
90+
if (context.hasErrors()) {
91+
throw new InvalidParameterValueException(context.buildErrorMessages());
92+
}
93+
}
94+
6595
private void checkUnresolvedProperties(Map<String, ToscaNodeTemplate> nodeTemplates, String function, ToscaServiceTemplateParsingContext context) {
6696
Map<String, Set<ToscaProperty>> unresolvedProperties = "$get_property".equals(function) ? context.getUnresolvedByGetProperty() : context.getUnresolvedByGetAttribute();
6797
for (Map.Entry<String, Set<ToscaProperty>> entry : unresolvedProperties.entrySet()) {
@@ -129,20 +159,6 @@ private Map<String, Set<ToscaNodeTemplate>> buildGraphDependencies(Map<String, T
129159
return serviceTemplateDependencies;
130160
}
131161

132-
private void checkRootServiceTemplateYamlKeys(Map<String, Object> serviceTemplate, ToscaServiceTemplateParsingContext context) {
133-
validateKnownToscaKeys(serviceTemplate, Set.of(ToscaConstants.SERVICE_TEMPLATE_TOSCA_VERSION_KEY, ToscaConstants.SERVICE_TEMPLATE_DESCRIPTION_KEY, ToscaConstants.SERVICE_TEMPLATE_SERVICE_TEMPLATE_KEY), "YAML's root level", context);
134-
validateRequiredToscaKeys(serviceTemplate, Set.of(ToscaConstants.SERVICE_TEMPLATE_SERVICE_TEMPLATE_KEY), "YAML's root level", context);
135-
if (context.hasErrors()) {
136-
throw new InvalidParameterValueException(context.buildErrorMessages());
137-
}
138-
139-
validateKnownToscaKeys(serviceTemplate, Set.of(ToscaConstants.SERVICE_TEMPLATE_TOSCA_VERSION_KEY, ToscaConstants.SERVICE_TEMPLATE_DESCRIPTION_KEY, ToscaConstants.SERVICE_TEMPLATE_SERVICE_TEMPLATE_KEY), "YAML's root level", context);
140-
validateRequiredToscaKeys(serviceTemplate, Set.of(ToscaConstants.SERVICE_TEMPLATE_NODE_TEMPLATES_KEY, ToscaConstants.SERVICE_TEMPLATE_INPUTS_KEY), "Service template level", context);
141-
if (context.hasErrors()) {
142-
throw new InvalidParameterValueException(context.buildErrorMessages());
143-
}
144-
}
145-
146162
private Map<String, ToscaNodeTemplate> parseNodeTemplates(Map<String, Object> nodeTemplates, ToscaServiceTemplateParsingContext context) {
147163
Map<String, ToscaNodeTemplate> nodeTemplateDefinitions = new HashMap<>();
148164
for (Map.Entry<String, Object> nodeTemplate : nodeTemplates.entrySet()) {
@@ -317,15 +333,29 @@ private ToscaInputDefinition parseInput(String name, Map<String, Object> body, T
317333
return new ToscaInputDefinition(name, description, type, defaultValue, validation);
318334
}
319335

320-
private void validateRequiredToscaKeys(Map<String, Object> content, Set<String> requiredKeys, String templateSection, ToscaServiceTemplateParsingContext context) {
336+
/**
337+
* Validate whether all the required TOSCA keys are present in {@param content}.
338+
* @param content The YAML content to be validated.
339+
* @param requiredKeys The TOSCA keys that must be present in {@param content}.
340+
* @param templateSection The section of the service template being validated. Only used for building the error message.
341+
* @param context The service template parsing context ({@link ToscaServiceTemplateParsingContext}) to which error messages might be added.
342+
*/
343+
protected void validateRequiredToscaKeys(Map<String, Object> content, Set<String> requiredKeys, String templateSection, ToscaServiceTemplateParsingContext context) {
321344
requiredKeys.stream()
322345
.filter(key -> !content.containsKey(key))
323-
.forEach(key -> context.addError(String.format("The key [%s] is required in the [%s] section.", key, templateSection), templateSection));
346+
.forEach(key -> context.addError(String.format("The key [%s] is required.", key), templateSection));
324347
}
325348

326-
private void validateKnownToscaKeys(Map<String, Object> content, Set<String> validKeys, String templateSection, ToscaServiceTemplateParsingContext context) {
349+
/**
350+
* Validate whether all the YAML keys present in {@param content} are known TOSCA keys.
351+
* @param content The YAML content to be validated.
352+
* @param knownToscaKeys The known TOSCA keys that can be present in{@param content} .
353+
* @param templateSection The section of the service template being validated. Only used for building the error message.
354+
* @param context The service template parsing context ({@link ToscaServiceTemplateParsingContext}) to which error messages might be added.
355+
*/
356+
protected void validateKnownToscaKeys(Map<String, Object> content, Set<String> knownToscaKeys, String templateSection, ToscaServiceTemplateParsingContext context) {
327357
content.keySet().stream()
328-
.filter(key -> !validKeys.contains(key))
329-
.forEach(key -> context.addError(String.format("Unknown key [%s] in the [%s] section.", key, templateSection), templateSection));
358+
.filter(key -> !knownToscaKeys.contains(key))
359+
.forEach(key -> context.addError(String.format("Unknown key [%s].", key), templateSection));
330360
}
331361
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.tosca.parser;
18+
19+
import com.cloud.exception.InvalidParameterValueException;
20+
import org.junit.Before;
21+
import org.junit.Test;
22+
import org.junit.runner.RunWith;
23+
import org.mockito.Mock;
24+
import org.mockito.Mockito;
25+
import org.mockito.junit.MockitoJUnitRunner;
26+
27+
import java.util.Map;
28+
import java.util.Set;
29+
30+
@RunWith(MockitoJUnitRunner.class)
31+
public class ToscaServiceTemplateParserTest {
32+
private ToscaServiceTemplateParser toscaServiceTemplateParserSpy;
33+
34+
@Mock
35+
private ToscaServiceTemplateParsingContext parsingContextMock;
36+
37+
@Before
38+
public void setUp() {
39+
ToscaFieldParser toscaFieldParser = new ToscaFieldParser();
40+
toscaServiceTemplateParserSpy = Mockito.spy(new ToscaServiceTemplateParser(toscaFieldParser));
41+
}
42+
43+
@Test(expected = InvalidParameterValueException.class)
44+
public void parseServiceTemplateTestThrowExceptionWhenRequiredKeysOfTheRootSectionAreMissing() {
45+
String content = "{tosca_definitions_version: tosca_2_0, description: null}";
46+
toscaServiceTemplateParserSpy.parseServiceTemplate(content, null, null);
47+
}
48+
49+
@Test(expected = InvalidParameterValueException.class)
50+
public void parseServiceTemplateTestThrowExceptionWhenRequiredKeysOfTheServiceTemplateSectionAreMissing() {
51+
String content = "{tosca_definitions_version: tosca_2_0, description: null, service_template: {inputs: null}}";
52+
toscaServiceTemplateParserSpy.parseServiceTemplate(content, null, null);
53+
}
54+
55+
@Test(expected = InvalidParameterValueException.class)
56+
public void parseServiceTemplateTestThrowExceptionWhenUnknownKeysOfTheRootSectionArePresent() {
57+
String content = "{tosca_definitions_version: tosca_2_0, description: null, unknown-key: null, service_template: null}";
58+
toscaServiceTemplateParserSpy.parseServiceTemplate(content, null, null);
59+
}
60+
61+
@Test(expected = InvalidParameterValueException.class)
62+
public void parseServiceTemplateTestThrowExceptionWhenUnknownKeysOfTheServiceTemplateSectionArePresent() {
63+
String content = "{tosca_definitions_version: tosca_2_0, description: null, service_template: {inputs: null, node_templates: null, unknown-key: null}}";
64+
toscaServiceTemplateParserSpy.parseServiceTemplate(content, null, null);
65+
}
66+
67+
@Test
68+
public void validateRequiredToscaKeysTestAddErrorToTheParsingContextWhenRequiredKeysAreMissing() {
69+
Map<String, Object> content = ToscaYamlHelper.asMap(ToscaYamlHelper.loadYaml("{not-required-key: value}"));
70+
Set<String> requiredKeys = Set.of("required-key-1", "required-key-2");
71+
72+
toscaServiceTemplateParserSpy.validateRequiredToscaKeys(content, requiredKeys, "Template Section", parsingContextMock);
73+
Mockito.verify(parsingContextMock, Mockito.times(2)).addError(Mockito.anyString(), Mockito.anyString());
74+
}
75+
76+
@Test
77+
public void validateRequiredToscaKeysTestNotAddErrorToTheParsingContextWhenAllRequiredKeysArePresent() {
78+
Map<String, Object> content = ToscaYamlHelper.asMap(ToscaYamlHelper.loadYaml("{required-key-1: value-1, required-key-2: value-2}"));
79+
Set<String> requiredKeys = Set.of("required-key-1", "required-key-2");
80+
81+
toscaServiceTemplateParserSpy.validateRequiredToscaKeys(content, requiredKeys, "Template Section", parsingContextMock);
82+
Mockito.verify(parsingContextMock, Mockito.times(0)).addError(Mockito.anyString(), Mockito.anyString());
83+
}
84+
85+
@Test
86+
public void validateKnownToscaKeysTestAddErrorToTheParsingContextWhenAnUnknownKeyIsPresent() {
87+
Map<String, Object> content = ToscaYamlHelper.asMap(ToscaYamlHelper.loadYaml("{not-required-key: value}"));
88+
Set<String> knownKeys = Set.of("known-key-1", "known-key-2");
89+
90+
toscaServiceTemplateParserSpy.validateKnownToscaKeys(content, knownKeys, "Template Section", parsingContextMock);
91+
Mockito.verify(parsingContextMock, Mockito.times(1)).addError(Mockito.anyString(), Mockito.anyString());
92+
}
93+
94+
@Test
95+
public void validateKnownToscaKeysTestNotAddErrorToTheParsingContextWhenAllKeysAreKnown() {
96+
Map<String, Object> content = ToscaYamlHelper.asMap(ToscaYamlHelper.loadYaml("{known-key-2: value}"));
97+
Set<String> knownKeys = Set.of("known-key-1", "known-key-2");
98+
99+
toscaServiceTemplateParserSpy.validateKnownToscaKeys(content, knownKeys, "Template Section", parsingContextMock);
100+
Mockito.verify(parsingContextMock, Mockito.times(0)).addError(Mockito.anyString(), Mockito.anyString());
101+
}
102+
}

0 commit comments

Comments
 (0)