Commit 16227608 authored by Martti Käärik's avatar Martti Käärik
Browse files

Create HTTP request instances based on API operations with relevant members...

Create HTTP request instances based on API operations with relevant members assigned (method, uri) + create Java mappings for enums + make names unique (e.g. enum literals in package scope)
parent bed7ec29
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
package org.etsi.mts.tdl.transform;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.TreeMap;
@@ -22,9 +21,9 @@ import org.etsi.mts.tdl.NamedElement;
import org.etsi.mts.tdl.Package;
import org.etsi.mts.tdl.PackageableElement;
import org.etsi.mts.tdl.Parameter;
import org.etsi.mts.tdl.ParameterBinding;
import org.etsi.mts.tdl.ParameterMapping;
import org.etsi.mts.tdl.SimpleDataType;
import org.etsi.mts.tdl.StructuredDataInstance;
import org.etsi.mts.tdl.StructuredDataType;
import org.etsi.mts.tdl.tdlFactory;
import org.etsi.mts.tdl.tdlPackage;
@@ -79,6 +78,10 @@ public abstract class AbstractTranslator {
		return getTypeFor(getCleanName(name), tdlPackage.Literals.STRUCTURED_DATA_TYPE);
	}
	
	protected StructuredDataInstance getStructuredDataInstanceFor(String name) {
		return getTypeFor(getCleanName(name), tdlPackage.Literals.STRUCTURED_DATA_INSTANCE);
	}

	protected String idStartDigitRegex = "\\A\\d";
	protected String idInvalidCharRegex = "\\W";
	public static String cleanName(String name) {
+241 −47
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ package org.etsi.mts.tdl.openapi2tdl.next;

import java.io.File;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
@@ -14,15 +15,23 @@ import org.etsi.mts.tdl.Annotation;
import org.etsi.mts.tdl.AnnotationType;
import org.etsi.mts.tdl.CollectionDataType;
import org.etsi.mts.tdl.DataElementMapping;
import org.etsi.mts.tdl.DataElementUse;
import org.etsi.mts.tdl.DataInstance;
import org.etsi.mts.tdl.DataResourceMapping;
import org.etsi.mts.tdl.DataType;
import org.etsi.mts.tdl.ElementImport;
import org.etsi.mts.tdl.EnumDataType;
import org.etsi.mts.tdl.Extension;
import org.etsi.mts.tdl.LiteralValueUse;
import org.etsi.mts.tdl.Member;
import org.etsi.mts.tdl.MemberAssignment;
import org.etsi.mts.tdl.NamedElement;
import org.etsi.mts.tdl.Package;
import org.etsi.mts.tdl.PackageableElement;
import org.etsi.mts.tdl.ParameterMapping;
import org.etsi.mts.tdl.SimpleDataInstance;
import org.etsi.mts.tdl.SimpleDataType;
import org.etsi.mts.tdl.StructuredDataInstance;
import org.etsi.mts.tdl.StructuredDataType;
import org.etsi.mts.tdl.tdlFactory;
import org.etsi.mts.tdl.tdlPackage;
@@ -41,7 +50,13 @@ import org.osgi.service.prefs.Preferences;

import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.PathItem.HttpMethod;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.parser.core.models.ParseOptions;
import io.swagger.v3.parser.util.InlineModelResolver;

@@ -53,8 +68,6 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
	private boolean isXtext = false;
	private Set<String> xtextKyewords;

	private Map<String, String> nameReplacements = new Hashtable();

	private Package theTdlPackage;

	public void translate(String filename) throws Exception {
@@ -99,27 +112,27 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
			preferences = ps.getNode(PropertyPage.PREFERENCE_SCOPE);
		}

		Package httpPackage = TDLHelper.getHttpPackage(getTargetResource());
		Package httpMessageBasedPackage = null;
		boolean useMessageBasedApi = true;
		if (preferences != null)
			useMessageBasedApi = !preferences.getBoolean(PropertyPage.PROCEDURE_BASED, false);
		StructuredDataType messageBody = null;
		if (useMessageBasedApi) {
			Package httpPackage = TDLHelper.getHttpPackage(getTargetResource());
			if (httpPackage != null) {
				Package messageBased = null;
				packages: for (Package p : httpPackage.getNestedPackage()) {
					for (PackageableElement e : p.getPackagedElement()) {
						if (e.eClass().equals(tdlPackage.eINSTANCE.getStructuredDataType())
								&& e.getName().equals("Body")) {
							messageBody = (StructuredDataType) e;
							messageBased = p;
							httpMessageBasedPackage = p;
							break packages;
						}
					}
				}
				if (messageBased != null) {
				if (httpMessageBasedPackage != null) {
					ElementImport importHttp = tdlFactory.eINSTANCE.createElementImport();
					importHttp.setImportedPackage(messageBased);
					importHttp.setImportedPackage(httpMessageBasedPackage);
					getGeneratedPackage().getImport().add(importHttp);
				}
			}
@@ -165,16 +178,110 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
			}
		}
		

		// Map operations to request body schemas so we can create wrappers later
		Map<String, Set<PathOperation>> schemaOperations = new Hashtable<>();
		Paths paths = model.getPaths();
		for (String pathUri: paths.keySet()) {
			PathItem path = paths.get(pathUri);
			Map<HttpMethod, Operation> ops = path.readOperationsMap();
			for (HttpMethod method : ops.keySet()) {
				Operation op = ops.get(method);
				RequestBody req = op.getRequestBody();
				if (req != null) {
					MediaType jsonContent = req.getContent().get("application/json");
					if (jsonContent != null) {
						Schema schema = jsonContent.getSchema();
						boolean array = false;
						if (schema.getType() != null && schema.getType().equals("array")) {
							schema = schema.getItems();
							array = true;
						}
						String schemaName = schema.get$ref();
						schemaName = schemaName.replace("#/components/schemas/", "");
						Set<PathOperation> set = schemaOperations.get(schemaName);
						if (set == null) {
							set = new HashSet<>();
							schemaOperations.put(schemaName, set);
						}
						set.add(new PathOperation(pathUri, method, op, array));
					}
				}
			}
		}
		

		StructuredDataType httpRequest = null;
		EnumDataType httpMethodEnum = null;
		if (httpMessageBasedPackage != null) {
			for (PackageableElement e : httpMessageBasedPackage.getPackagedElement()) {
				if (e.eClass().equals(tdlPackage.eINSTANCE.getStructuredDataType())) {
					if (e.getName().equals("Request"))
						httpRequest = (StructuredDataType) e;
				} else if (e.eClass().equals(tdlPackage.eINSTANCE.getEnumDataType())) {
						if (e.getName().equals("Method"))
							httpMethodEnum = (EnumDataType) e;
					}
			}
		}
		for (String schemaName : model.getComponents().getSchemas().keySet()) {
			Schema<?> schema = model.getComponents().getSchemas().get(schemaName);
			schema.setName(schemaName);
			DataType dataType = translate(schema, "", messageBody);
			//addMapping(schema, dataType, sourceMappingTag, targetMappingTag);
			
			//Operation wrappers

			if (useMessageBasedApi) {
				Set<PathOperation> ops = schemaOperations.get(schemaName);
				if (ops != null) {
					for (PathOperation pathOperation : ops) {
						Operation op = pathOperation.operation;
						
						String name = op.getOperationId();
						if (name == null)
							name = pathOperation.path;
						
						StructuredDataInstance requestData = getStructuredDataInstanceFor(name);
						requestData.setDataType(httpRequest);
						
						setMemberValue(requestData, "uri", pathOperation.path);
						
						SimpleDataInstance methodValue = findContentWithName(pathOperation.method.name(), httpMethodEnum, tdlPackage.eINSTANCE.getSimpleDataInstance());
						setMemberValue(requestData, "method", methodValue);
						
//						for (Parameter par: op.getParameters()) {
//							System.out.println(par);
//						}
//						Member headersMember = findContentWithName("headers", httpRequest, tdlPackage.eINSTANCE.getMember());
//						Member parametersMember = findContentWithName("parameters", httpRequest, tdlPackage.eINSTANCE.getMember());
					}
				}
			}
		}

		if (generateJavaMappings)
			addMappings(filename, mappingsPackage, drmTarget, javaPackage);

		ensureUniqueNames(getGeneratedPackage());
	}
	
	private void setMemberValue(StructuredDataInstance data, String memberName, Object value) {
		
		Member uriMember = findContentWithName(memberName, data.getDataType(), tdlPackage.eINSTANCE.getMember());
		MemberAssignment assignment = tdlFactory.eINSTANCE.createMemberAssignment();
		assignment.setMember(uriMember);
		if (value instanceof String) {
			LiteralValueUse dataUse = tdlFactory.eINSTANCE.createLiteralValueUse();
			dataUse.setValue((String) value);
			assignment.setMemberSpec(dataUse);
			
		} else if (value instanceof DataInstance) {
			DataElementUse dataUse = tdlFactory.eINSTANCE.createDataElementUse();
			dataUse.setDataElement((DataInstance) value);
			assignment.setMemberSpec(dataUse);
		}
		data.getMemberAssignment().add(assignment);
		
	}

	@Override
@@ -251,17 +358,36 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
				}
			}
		}
		if (schema.getEnum() != null) {
			EnumDataType t = getEnumDataTypeFor(name);
			if (t.getValue().isEmpty())
				for (Object literal : schema.getEnum()) {
					if (literal instanceof String) {
						SimpleDataInstance literalData = tdlFactory.eINSTANCE.createSimpleDataInstance();
						literalData.setName(getCleanName((String) literal));
						literalData.setDataType(t);
						t.getValue().add(literalData);
					}
					// TODO what if enum literals are not Strings?
				}
			return t;

		} else {
			SimpleDataType t = getSimpleDataTypeFor(name);
			addSuperType(t, superType);
			return t;
		}
	}

	private <T extends DataType> void addSuperType(T type, T superType) {
		if (superType == null)
			return;
		List<Extension> ext = null;
		if (type instanceof SimpleDataType)
			ext = Collections.singletonList(((SimpleDataType) type).getExtension());
		if (type instanceof SimpleDataType) {
			Extension extension = ((SimpleDataType) type).getExtension();
			if (extension != null)
				ext = Collections.singletonList(extension);
		}
		if (type instanceof StructuredDataType)
			ext = ((StructuredDataType) type).getExtension();
		if (ext == null)
@@ -321,10 +447,8 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
	}

	public String getCleanName(String name) {
		String replacement = nameReplacements.get(name);
		if (replacement != null)
			return replacement;
		replacement = name;
		
		String replacement = name;
		if (isXtext) {
			if (replacement.matches(idStartDigitRegex))
				replacement = "_" + replacement;
@@ -334,11 +458,36 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
			 * if (xtextKyewords.contains(replacement)) { replacement = "^" + replacement; }
			 */
		}
		// TODO check for duplicates
		nameReplacements.put(name, replacement);
		
		return replacement;
	}
	
	public void ensureUniqueNames(Package p) {
		Set<String> names = new HashSet<>();
		for (PackageableElement pe: p.getPackagedElement()) {
			makeUnique(pe, names);
			if (pe instanceof EnumDataType)
				for (PackageableElement l: ((EnumDataType) pe).getValue()) 
					makeUnique(l, names);
			else if (pe instanceof Package) {
				ensureUniqueNames((Package) pe);
			}
		}
		
	}
	
	private void makeUnique(NamedElement e, Set<String> names) {
		String name = e.getName();
		int i = 1;
		String newName = name;
		while (names.contains(newName)) {
			newName = name + "_" + i++;
		}
		if (!newName.equals(name))
			e.setName(newName);
		names.add(newName);
	}

	/**
	 * Helper method for parsing specs into an intermediary OpenAPI structure for
	 * pre-processing.
@@ -368,6 +517,19 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
		return openAPI;
	}
	
	class PathOperation {
		String path;
		HttpMethod method;
		Operation operation;
		boolean array;
		public PathOperation(String path, HttpMethod method, Operation operation, boolean array) {
			this.path = path;
			this.method = method;
			this.operation = operation;
			this.array = array;
		}
	}

	public void addMappings(String filename, Package mappingsPackage, DataResourceMapping resourceMapping, Package javaPackage) {
		
		CodegenConfigurator configurator = new CodegenConfigurator();
@@ -397,21 +559,53 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
				
				List<ModelMap> allModels = (List<ModelMap>) objs.get("models");
				for (ModelMap modelMap : allModels) {
					CodegenModel codegen = modelMap.getModel();
					String name = codegen.getName();
					StructuredDataType type = getStructuredDataTypeFor(name);
					CodegenModel codeGen = modelMap.getModel();
					String name = codeGen.getName();
					DataType type = null;
					if (codeGen.isEnum)
						type = getEnumDataTypeFor(name);
					else
						type = getStructuredDataTypeFor(name);
					
					DataElementMapping eMapping = tdlFactory.eINSTANCE.createDataElementMapping();
					mappingsPackage.getPackagedElement().add(eMapping);
					eMapping.setName(getCleanName(name) + "_Mapping");
					eMapping.setMappableDataElement(type);
					eMapping.setElementURI(codegen.getClassname());
					eMapping.setElementURI(codeGen.getClassname());
					eMapping.setDataResourceMapping(resourceMapping);
					
					for (CodegenProperty var: codegen.getVars()) {
					if (type instanceof EnumDataType) {
						
						List<Map<String, String>> enumVars = (List<Map<String, String>>) codeGen.getAllowableValues().get("enumVars");
						for (Map<String, String> enumVar : enumVars) {
							String javaName = enumVar.get("name");
							String modelName = enumVar.get("value");
							// Wrapped in ""
							modelName = modelName.substring(1, modelName.length() - 1);
							
							SimpleDataInstance literal = null;
							for (SimpleDataInstance l: ((EnumDataType) type).getValue()) {
								if (l.getName().equals(getCleanName(modelName))) {
									 literal = l;
									 break;
								}
							}
							if (literal == null)
								continue;

							DataElementMapping lMapping = tdlFactory.eINSTANCE.createDataElementMapping();
							mappingsPackage.getPackagedElement().add(lMapping);
							lMapping.setName(getCleanName(name) + "_" + getCleanName(modelName) + "_Mapping");
							lMapping.setMappableDataElement(literal);
							lMapping.setElementURI(javaName);
							lMapping.setDataResourceMapping(resourceMapping);
						}
						
					} else  if (type instanceof StructuredDataType) {
						for (CodegenProperty var: codeGen.getVars()) {
							String vName = var.getBaseName();
							Member member = null;
						for (Member m: type.allMembers()) {
							for (Member m: ((StructuredDataType) type).allMembers()) {
								if (m.getName().equals(getCleanName(vName))) {
									 member = m;
									 break;
@@ -435,7 +629,7 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
								sa.setValue(var.getSetter());
								pMapping.getAnnotation().add(sa);
							}
						
						}
					}
				}
				//return super.postProcessSupportingFileData(objs);