Skip to content

Commit 908d7dd

Browse files
finish adding tests and adjusting
1 parent dbce556 commit 908d7dd

4 files changed

Lines changed: 157 additions & 80 deletions

File tree

plugins/iac/nimble/src/main/java/org/apache/cloudstack/tosca/functions/ToscaBooleanFunctions.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,9 @@ public ValidValues(List<Object> validValues) {
3030
public boolean evaluate(Object value) {
3131
return validValues.contains(value);
3232
}
33+
34+
public List<Object> getValidValues() {
35+
return validValues;
36+
}
3337
}
3438
}

plugins/iac/nimble/src/main/java/org/apache/cloudstack/tosca/model/ToscaPropertyDefinition.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ public ToscaPropertyDefinition(String name, String description, ToscaTypeDefinit
3131
public boolean isRequired() {
3232
return required;
3333
}
34+
35+
public ToscaFunction.ToscaBooleanFunction getValidation() {
36+
return validation;
37+
}
3438
}

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

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,79 +38,108 @@
3838
public class ToscaParser {
3939
private final Logger logger = LogManager.getLogger(ToscaParser.class);
4040

41-
private enum FieldDefinitionType {
41+
protected enum TypeOfToscaField {
4242
ATTRIBUTE, PROPERTY
4343
}
4444

45-
private static final String NODE_TYPES_KEY = "node_types";
4645
private static final String DATA_TYPES_KEY = "data_types";
46+
private static final String NODE_TYPES_KEY = "node_types";
4747

48-
private static final String NODE_TYPES_PROPERTIES_KEY = "properties";
4948
private static final String NODE_TYPES_ATTRIBUTES_KEY = "attributes";
49+
private static final String NODE_TYPES_PROPERTIES_KEY = "properties";
5050

51-
private static final String FIELDS_DESCRIPTION_KEY = "description";
5251
private static final String FIELDS_TYPE_KEY = "type";
53-
private static final String FIELDS_VALIDATION_KEY = "validation";
5452
private static final String FIELDS_REQUIRED_KEY = "required";
53+
private static final String FIELDS_VALIDATION_KEY = "validation";
54+
private static final String FIELDS_DESCRIPTION_KEY = "description";
5555
private static final String FIELDS_ENTRY_SCHEMA_KEY = "entry_schema";
5656

57+
/**
58+
* Parses a node type definition file.
59+
* @param nodeTypeContent The YAML content of the node type definition file.
60+
* @return a {@link ToscaNodeType} representing the node type.
61+
*/
5762
public ToscaNodeType parseNodeTypeDefinitionFile(String nodeTypeContent) {
5863
Object rawYaml = ToscaYamlHelper.loadYaml(nodeTypeContent);
5964
Map<String, Object> yamlRoot = ToscaYamlHelper.asMap(rawYaml);
6065
Map<String, ToscaDataTypeDefinition> dataTypes = parseDataTypes(yamlRoot);
6166
return parseNodeType(yamlRoot, dataTypes);
6267
}
6368

69+
/**
70+
* Parses all data types defined in the TOSCA file.
71+
* @param yamlRoot The root of the YAML file.
72+
* @return a map of {@link ToscaDataTypeDefinition} representing the data types, whose key is the name of the data type and whose value is the data type itself.
73+
*/
6474
protected Map<String, ToscaDataTypeDefinition> parseDataTypes(Map<String, Object> yamlRoot) {
6575
Map<String, Object> dataTypesRaw = ToscaYamlHelper.asMap(yamlRoot.get(DATA_TYPES_KEY));
6676
logger.info("Parsing the following data types: {}.", dataTypesRaw::keySet);
6777
return dataTypesRaw.entrySet().stream().map(dataTypeEntry -> {
6878
String dataTypeName = dataTypeEntry.getKey();
6979
Map<String, Object> dataTypeBody = ToscaYamlHelper.asMap(dataTypeEntry.getValue());
70-
Map<String, ToscaPropertyDefinition> propertyDefinitions = (Map<String, ToscaPropertyDefinition>) parseFieldDefinition(dataTypeBody.get(NODE_TYPES_PROPERTIES_KEY), FieldDefinitionType.PROPERTY, null);
80+
Map<String, ToscaPropertyDefinition> propertyDefinitions = (Map<String, ToscaPropertyDefinition>) parseField(dataTypeBody.get(NODE_TYPES_PROPERTIES_KEY), TypeOfToscaField.PROPERTY, null);
7181
return new ToscaDataTypeDefinition(dataTypeName, propertyDefinitions);
7282
}).collect(Collectors.toMap(ToscaDataTypeDefinition::getName, (dataType) -> dataType));
7383
}
7484

75-
private ToscaNodeType parseNodeType(Map<String, Object> yamlRoot, Map<String, ToscaDataTypeDefinition> dataTypes) {
85+
/**
86+
* Parses a node type definition.
87+
* @param yamlRoot The root of the YAML node type definition file.
88+
* @param dataTypes Available data types.
89+
* @return a {@link ToscaNodeType} representing the node type.
90+
*/
91+
protected ToscaNodeType parseNodeType(Map<String, Object> yamlRoot, Map<String, ToscaDataTypeDefinition> dataTypes) {
7692
Map.Entry<String, Object> nodeTypeRaw = ToscaYamlHelper.asMap(yamlRoot.get(NODE_TYPES_KEY)).entrySet().iterator().next();
7793
String nodeTypeName = nodeTypeRaw.getKey();
7894
logger.info("Parsing the following node type: [{}].", nodeTypeName);
7995
Map<String, Object> nodeTypeBody = ToscaYamlHelper.asMap(nodeTypeRaw.getValue());
80-
Map<String, ToscaPropertyDefinition> propertyDefinitions = (Map<String, ToscaPropertyDefinition>) parseFieldDefinition(nodeTypeBody.get(NODE_TYPES_PROPERTIES_KEY), FieldDefinitionType.PROPERTY, dataTypes);
81-
Map<String, ToscaAttributeDefinition> attributeDefinitions = (Map<String, ToscaAttributeDefinition>) parseFieldDefinition(nodeTypeBody.get(NODE_TYPES_ATTRIBUTES_KEY), FieldDefinitionType.ATTRIBUTE, dataTypes);
96+
Map<String, ToscaPropertyDefinition> propertyDefinitions = (Map<String, ToscaPropertyDefinition>) parseField(nodeTypeBody.get(NODE_TYPES_PROPERTIES_KEY), TypeOfToscaField.PROPERTY, dataTypes);
97+
Map<String, ToscaAttributeDefinition> attributeDefinitions = (Map<String, ToscaAttributeDefinition>) parseField(nodeTypeBody.get(NODE_TYPES_ATTRIBUTES_KEY), TypeOfToscaField.ATTRIBUTE, dataTypes);
8298
ToscaNodeType nodeType = new ToscaNodeType(nodeTypeName, propertyDefinitions, attributeDefinitions);
8399
logger.info("Successfully parsed the following node type: [{}].", nodeType::toString);
84100
return nodeType;
85101
}
86102

87-
private Map<String, ? extends ToscaFieldDefinition> parseFieldDefinition(Object rawFields, FieldDefinitionType fieldDefinitionType, Map<String, ToscaDataTypeDefinition> dataTypes) {
103+
/**
104+
* Parses a field (property or attribute) of a TOSCA resource.
105+
* @param rawFields The body of the field. Example: "{ name: { type: string, description: Name } }"
106+
* @param typeOfToscaField The type of the field that will be parsed.
107+
* @param dataTypes Available data types. If null, then the type of the fields must be a primitive or a collection.
108+
* @return a map of {@link ToscaFieldDefinition} representing the fields, whose key is the name of the field and whose value is the field itself.
109+
*/
110+
protected Map<String, ? extends ToscaFieldDefinition> parseField(Object rawFields, TypeOfToscaField typeOfToscaField, Map<String, ToscaDataTypeDefinition> dataTypes) {
88111
Map<String, Object> fields = ToscaYamlHelper.asMap(rawFields);
89112
logger.debug("Parsing the following {}: {}.",
90-
() -> fieldDefinitionType == FieldDefinitionType.ATTRIBUTE ? "attributes" : "properties", fields::keySet);
113+
() -> typeOfToscaField == TypeOfToscaField.ATTRIBUTE ? "attributes" : "properties", fields::keySet);
91114

92115
return fields.entrySet().stream().map((field) -> {
93-
String fieldName = field.getKey();
116+
String name = field.getKey();
94117
Map<String, Object> fieldBody = ToscaYamlHelper.asMap(field.getValue());
95-
String fieldDescription = ToscaYamlHelper.asString(fieldBody.get(FIELDS_DESCRIPTION_KEY));
96-
ToscaTypeDefinition fieldType = parseFieldType(fieldBody, dataTypes);
118+
String description = ToscaYamlHelper.asString(fieldBody.get(FIELDS_DESCRIPTION_KEY));
119+
ToscaTypeDefinition type = parseType(fieldBody, dataTypes);
97120

98-
if (fieldDefinitionType == FieldDefinitionType.ATTRIBUTE) {
99-
ToscaAttributeDefinition attributeDefinition = new ToscaAttributeDefinition(fieldName, fieldDescription, fieldType);
121+
if (typeOfToscaField == TypeOfToscaField.ATTRIBUTE) {
122+
ToscaAttributeDefinition attributeDefinition = new ToscaAttributeDefinition(name, description, type);
100123
logger.debug("Successfully parsed the following attribute: [{}].", attributeDefinition::toString);
101124
return attributeDefinition;
102125
}
103126

104127
boolean required = ToscaYamlHelper.asBoolean(fieldBody.get(FIELDS_REQUIRED_KEY));
105128
ToscaFunction.ToscaBooleanFunction validation = parseToscaBooleanFunction(ToscaYamlHelper.asMap(fieldBody.get(FIELDS_VALIDATION_KEY)));
106-
ToscaPropertyDefinition propertyDefinition = new ToscaPropertyDefinition(fieldName, fieldDescription, fieldType, required, validation);
129+
ToscaPropertyDefinition propertyDefinition = new ToscaPropertyDefinition(name, description, type, required, validation);
107130
logger.debug("Successfully parsed the following property: [{}].", propertyDefinition::toString);
108131
return propertyDefinition;
109132
}).collect(Collectors.toMap(ToscaFieldDefinition::getName, (field) -> field));
110133
}
111134

112-
protected ToscaTypeDefinition parseFieldType(Map<String, Object> fieldBody, Map<String, ToscaDataTypeDefinition> dataTypes) {
113-
String rawType = ToscaYamlHelper.asString(fieldBody.get(FIELDS_TYPE_KEY));
135+
/**
136+
* Parses the type of TOSCA resource. Types can be primitive, collection, or data type, and are represented by the {@link ToscaTypeDefinition} class.
137+
* @param typeBody The body of the type. Example: "{ type: list, entry_schema: { type: NodeOffering } }"
138+
* @param dataTypes The available data types. If null, then the type being parsed must be either a primitive or a collection.
139+
* @return a {@link ToscaTypeDefinition} representing the type of the field. Null if the type is not recognized.
140+
*/
141+
protected ToscaTypeDefinition parseType(Map<String, Object> typeBody, Map<String, ToscaDataTypeDefinition> dataTypes) {
142+
String rawType = ToscaYamlHelper.asString(typeBody.get(FIELDS_TYPE_KEY));
114143
logger.debug("Parsing the following type: [{}].", rawType);
115144
ToscaPrimitiveType primitiveType = EnumUtils.getEnumIgnoreCase(ToscaPrimitiveType.class, rawType);
116145
if (primitiveType != null) {
@@ -121,32 +150,47 @@ protected ToscaTypeDefinition parseFieldType(Map<String, Object> fieldBody, Map<
121150
ToscaCollectionType collectionType = EnumUtils.getEnumIgnoreCase(ToscaCollectionType.class, rawType);
122151
if (collectionType != null) {
123152
logger.debug("The type is a collection, returning its corresponding ToscaTypeDefinition.");
124-
return ToscaTypeDefinition.ofCollection(collectionType, parseFieldType(ToscaYamlHelper.asMap(fieldBody.get(FIELDS_ENTRY_SCHEMA_KEY)), dataTypes));
153+
return ToscaTypeDefinition.ofCollection(collectionType, parseType(ToscaYamlHelper.asMap(typeBody.get(FIELDS_ENTRY_SCHEMA_KEY)), dataTypes));
154+
}
155+
156+
if (MapUtils.isEmpty(dataTypes) || !dataTypes.containsKey(rawType)) {
157+
logger.debug("The [{}] type is not recognized, returning null.", rawType);
158+
return null;
125159
}
126160

127161
logger.debug("The type is a data type, returning its corresponding ToscaTypeDefinition based in the declared data types: {}.", dataTypes::keySet);
128162
return ToscaTypeDefinition.ofDataType(dataTypes.get(rawType));
129163
}
130164

165+
/**
166+
* Parses a TOSCA boolean ({@link ToscaFunction.ToscaBooleanFunction}) function.
167+
* @param validationBody The body of the validation field. Example: "{ $valid_values: [ $value, [ CloudManaged, ExternalManaged ] ] }")
168+
* @return a {@link ToscaFunction.ToscaBooleanFunction} representing the boolean function. If the function is not recognized, returns null.
169+
*/
131170
protected ToscaFunction.ToscaBooleanFunction parseToscaBooleanFunction(Map<String, Object> validationBody) {
132171
if (MapUtils.isEmpty(validationBody)) {
133172
return null;
134173
}
135174

136175
Map.Entry<String, Object> function = validationBody.entrySet().iterator().next();
137176
String name = function.getKey();
138-
Object body = function.getValue();
177+
Object arguments = function.getValue();
139178
logger.debug("Parsing the following TOSCA boolean function: [{}].", name);
140179
switch (name) {
141180
case "$valid_values":
142-
return parseValidValuesFunction(body);
181+
return parseValidValuesFunction(arguments);
143182
}
144183
return null;
145184
}
146185

147-
private ToscaFunction.ToscaBooleanFunction parseValidValuesFunction(Object functionBody) {
148-
List<Object> arguments = (List<Object>) functionBody;
149-
List<Object> validValues = (List<Object>) arguments.get(1);
186+
/**
187+
* Parses the $valid_values TOSCA function.
188+
* @param arguments The arguments of the $valid_values function. Example: "{ $valid_values: [ $value, [ CloudManaged, ExternalManaged ] ] }"
189+
* @return a {@link ToscaBooleanFunctions.ValidValues} (typed as {@link ToscaFunction.ToscaBooleanFunction}) representing the $valid_values function.
190+
*/
191+
private ToscaFunction.ToscaBooleanFunction parseValidValuesFunction(Object arguments) {
192+
List<Object> args = (List<Object>) arguments;
193+
List<Object> validValues = (List<Object>) args.get(1);
150194
return new ToscaBooleanFunctions.ValidValues(validValues);
151195
}
152196
}

0 commit comments

Comments
 (0)