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

OpenAPI: option to generate typed requests/responses #136 #133

parent b6fff796
Loading
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ import java.util.TreeMap;
import java.util.function.Predicate;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.EcoreUtil2;
import org.etsi.mts.tdl.Annotation;
@@ -205,6 +206,13 @@ public abstract class AbstractTranslator {
		T content = null;
		if (optional.isPresent()) {
			content = (T) optional.get();
		} else {
			EClass extClass = tdlPackage.eINSTANCE.getExtension();
			Optional<EObject> extension =  container.eContents().stream()
				.filter(e->extClass.isInstance(e))
				.findFirst();
			if (extension.isPresent())
				return findContentWithName(name, ((Extension)extension.get()).getExtending(), targetType);
		}
		return content;
	}
+140 −33
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.parser.core.models.ParseOptions;
import io.swagger.v3.parser.util.InlineModelResolver;

@@ -72,6 +73,13 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {

	private Package theTdlPackage;

	private StructuredDataType messageBody;
	private StructuredDataType httpRequest;
	private StructuredDataType httpResponse;
	private CollectionDataType httpRequestParameters;
	private EnumDataType httpParameterLocationEnum;
	private EnumDataType httpMethodEnum;

	public void translate(String filename) throws Exception {
		translate(filename, sourceMappingTag, targetMappingTag);
	}
@@ -119,12 +127,13 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
		Package httpPackage = ResourceHandler.getHttpPackage(getTargetResource());
		Package httpMessageBasedPackage = null;
		boolean useMessageBasedApi = true;
		boolean useSpecializedRequest = false;
		String javaApiPackage = "generated.java";
		if (preferences != null) {
			useMessageBasedApi = !preferences.getBoolean(PropertyPage.PROCEDURE_BASED, false);
			useSpecializedRequest = preferences.getBoolean(PropertyPage.TYPED_REQUESTS, false);
			javaApiPackage= preferences.get(PropertyPage.JAVA_API_PACKAGE, "generated.java");
		}
		StructuredDataType messageBody = null;
		if (useMessageBasedApi) {
			if (httpPackage != null) {
				packages: for (Package p : httpPackage.getNestedPackage()) {
@@ -141,6 +150,23 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
					ElementImport importHttp = tdlFactory.eINSTANCE.createElementImport();
					importHttp.setImportedPackage(httpMessageBasedPackage);
					getGeneratedPackage().getImport().add(importHttp);
					
					for (PackageableElement e : httpMessageBasedPackage.getPackagedElement()) {
						if (e.eClass().equals(tdlPackage.eINSTANCE.getStructuredDataType())) {
							if (e.getName().equals("Request"))
								httpRequest = (StructuredDataType) e;
							if (e.getName().equals("Response"))
								httpResponse = (StructuredDataType) e;
						} else if (e.eClass().equals(tdlPackage.eINSTANCE.getEnumDataType())) {
							if (e.getName().equals("Method"))
								httpMethodEnum = (EnumDataType) e;
							else if (e.getName().equals("Location"))
								httpParameterLocationEnum = (EnumDataType) e;
						} else if (e.eClass().equals(tdlPackage.eINSTANCE.getCollectionDataType())) {
							if (e.getName().equals("Parameters"))
								httpRequestParameters = (CollectionDataType) e;
						}
					}
				}
			}
		}
@@ -239,36 +265,15 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
		}
		

		StructuredDataType httpRequest = null;
		CollectionDataType httpRequestParameters = null;
		EnumDataType httpParameterLocationEnum = 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;
					else if (e.getName().equals("Location"))
						httpParameterLocationEnum = (EnumDataType) e;
				} else if (e.eClass().equals(tdlPackage.eINSTANCE.getCollectionDataType())) {
					if (e.getName().equals("Parameters"))
						httpRequestParameters = (CollectionDataType) e;
				}
			}
		}
		
		if (useMessageBasedApi) {
			createOperationWrapper(noContentOperations, httpRequest, httpMethodEnum, httpRequestParameters, httpParameterLocationEnum);
			createOperationWrapper(noContentOperations, null, useSpecializedRequest);
		}
		for (PathOperation op : inlineSchemaOperations) {
			Schema<?> schema = op.inlineSchema;
			schema.setName(op.path.substring(1) + "_body");
			DataType dataType = translate(schema, "", messageBody);
			if (useMessageBasedApi) {
				createOperationWrapper(Collections.singleton(op), httpRequest, httpMethodEnum, httpRequestParameters, httpParameterLocationEnum);
				createOperationWrapper(Collections.singleton(op), dataType, useSpecializedRequest);
			}
		}
		for (String schemaName : model.getComponents().getSchemas().keySet()) {
@@ -279,7 +284,7 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
			if (useMessageBasedApi) {
				Set<PathOperation> ops = schemaOperations.get(schemaName);
				if (ops != null)
					createOperationWrapper(ops, httpRequest, httpMethodEnum, httpRequestParameters, httpParameterLocationEnum);
					createOperationWrapper(ops, dataType, useSpecializedRequest);
			}
		}

@@ -299,9 +304,7 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
		return type;
	}
	
	private void createOperationWrapper(Set<PathOperation> ops, StructuredDataType httpRequest,
			EnumDataType httpMethodEnum, CollectionDataType httpRequestParameters,
			EnumDataType httpParameterLocationEnum) {
	private void createOperationWrapper(Set<PathOperation> ops, DataType bodyType, boolean useSpecializedRequest) {

		for (PathOperation pathOperation : ops) {
			Operation op = pathOperation.operation;
@@ -311,7 +314,19 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
				name = pathOperation.path;
			
			StructuredDataInstance requestData = getStructuredDataInstanceFor(name);
			if (useSpecializedRequest && bodyType != null) {
				StructuredDataType specializedRequest = getStructuredDataTypeFor(bodyType.getName() + "_Request");
				String bodyMemberName = "body_t";
				if (specializedRequest.getExtension().isEmpty()) {
					addSuperType(specializedRequest, httpRequest);
					Member bodyMember = getContentWithName(bodyMemberName, specializedRequest, tdlPackage.Literals.MEMBER);
					bodyMember.setDataType(bodyType);
					specializedRequest.getMember().add(bodyMember);
				}
				requestData.setDataType(specializedRequest);
			} else {
				requestData.setDataType(httpRequest);
			}
			
			setMemberValue(requestData, "uri", pathOperation.path);
			
@@ -364,8 +379,42 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
						System.err.println("Unsupported parameter type: " + in + " at " + par.get$ref());
				}
			}
//			Member headersMember = findContentWithName("headers", httpRequest, tdlPackage.eINSTANCE.getMember());
//			Member parametersMember = findContentWithName("parameters", httpRequest, tdlPackage.eINSTANCE.getMember());
			
			for (String responseCode : op.getResponses().keySet()) {
				ApiResponse response = op.getResponses().get(responseCode);
				StructuredDataInstance responseData = getStructuredDataInstanceFor(name + "_" + responseCode);
				
				DataType responseBodyType = null;
				if (response.getContent() != null && response.getContent().get("application/json") != null) {
					Schema<?> responseSchema = response.getContent().get("application/json").getSchema();
					if (responseSchema != null) {
						if (responseSchema.getName() == null) {
							responseSchema.setName(name + "_" + responseCode + "_body");
						}
						responseBodyType = translate(responseSchema, "", messageBody);
					}
				}
				
				if (useSpecializedRequest && responseBodyType != null) {
					StructuredDataType specializedResponse = getStructuredDataTypeFor(responseBodyType.getName() + "_Response");
					String bodyMemberName = "body_t";
					if (specializedResponse.getExtension().isEmpty()) {
						addSuperType(specializedResponse, httpResponse);
						Member bodyMember = getContentWithName(bodyMemberName, specializedResponse, tdlPackage.Literals.MEMBER);
						bodyMember.setDataType(responseBodyType);
						specializedResponse.getMember().add(bodyMember);
					}
					responseData.setDataType(specializedResponse);
				} else {
					responseData.setDataType(httpResponse);
				}
				
				if (responseCode.matches("\\d+")) {
					setMemberValue(responseData, "status", responseCode);
				} else if (responseCode.equals("default")) {
					// TODO
				}
			}
		}
	}
	
@@ -414,7 +463,6 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
	@Override
	public void setTargetResource(Resource targetResource) {
		super.setTargetResource(targetResource);

		Class resourceClass = targetResource.getClass();
		do {
			// Flaky
@@ -463,6 +511,23 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
			properties.putAll(schema.getProperties());
	}

	private void getAllRequired(Schema<?> schema, Set<String> required) {
		String reference = schema.get$ref();
		if (reference != null)
			schema = ModelUtils.getReferencedSchema(model, schema);
		List<Schema> xOf = schema.getAllOf();
		if (xOf == null)
			xOf = schema.getAnyOf();
		if (xOf == null)
			xOf = schema.getOneOf();
		if (xOf != null) {
			for (Schema ofSchema : xOf)
				this.getAllRequired(ofSchema, required);
		}
		if (schema.getRequired() != null)
			required.addAll(schema.getRequired());
	}

	private DataType translate(Schema<?> schema, String prefix, DataType superType) {
		Map<String, Schema> properties = new Hashtable<>();
		this.getAllProperties(schema, properties);
@@ -534,6 +599,8 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
	private DataType translateObject(Schema<?> schema, String name) {
		Map<String, Schema> properties = new Hashtable<>();
		this.getAllProperties(schema, properties);
		Set<String> required = new HashSet<>();
		this.getAllRequired(schema, required);
		StructuredDataType dataType = getStructuredDataTypeFor(name);
		if (properties.isEmpty() || !dataType.getMember().isEmpty()) {
			return dataType;
@@ -551,6 +618,9 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
			}
			Member m = getContentWithName((String) propertyName, dataType, tdlPackage.Literals.MEMBER);
			m.setDataType(memberType);
			if (!required.contains(propertyName)) {
				m.setIsOptional(true);
			}
			if (m.container() == null) {
				dataType.getMember().add(m);
			}
@@ -762,6 +832,43 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
						}
					}
				}
				
				for (PackageableElement pe : getGeneratedPackage().getPackagedElement()) {
					if (pe instanceof StructuredDataType) {
						StructuredDataType type = (StructuredDataType) pe;
						
						boolean isRequest = false;
						boolean isResponse = false;
						for (org.etsi.mts.tdl.Extension ext : type.getExtension()) {
							if (ext.getExtending() == httpRequest) isRequest = true;
							if (ext.getExtending() == httpResponse) isResponse = true;
						}
						
						if (isRequest || isResponse) {
							String javaClass = isRequest ? "Request" : "Response";
							
							DataElementMapping eMapping = tdlFactory.eINSTANCE.createDataElementMapping();
							mappingsPackage.getPackagedElement().add(eMapping);
							eMapping.setName(getCleanName(type.getName()) + "_Mapping");
							eMapping.setMappableDataElement(type);
							eMapping.setElementURI(javaClass);
							eMapping.setDataResourceMapping(resourceMapping);
							Annotation ca = tdlFactory.eINSTANCE.createAnnotation();
							ca.setKey(classAnnotation);
							eMapping.getAnnotation().add(ca);
							
							for (Member m : type.getMember()) {
								if (m.getName().equals("body_t")) {
									ParameterMapping pMapping = tdlFactory.eINSTANCE.createParameterMapping();
									eMapping.getParameterMapping().add(pMapping);
									pMapping.setParameter(m);
									pMapping.setParameterURI("body");
								}
							}
						}
					}
				}
				
				//return super.postProcessSupportingFileData(objs);
				return objs;
			}
+6 −1
Original line number Diff line number Diff line
@@ -23,7 +23,8 @@ public class PropertyPage extends FieldEditorPreferencePage implements IWorkbenc
	public static final String PROCEDURE_BASED = "ProcedureBased",
			GENERATE_JAVA_MAPPING = "GenerateJavaMapping",
			JAVA_GENERATOR = "JavaMappingGenerator",
			JAVA_API_PACKAGE = "JavaApiPackage";
			JAVA_API_PACKAGE = "JavaApiPackage",
			TYPED_REQUESTS = "TypedRequests";

	public static final String PREFERENCE_SCOPE = "org.etsi.mts.tdl.openapi2tdl";

@@ -112,6 +113,10 @@ public class PropertyPage extends FieldEditorPreferencePage implements IWorkbenc
		BooleanFieldEditor procBased = new BooleanFieldEditor(PROCEDURE_BASED, "Procedure-based API", parent);
		addField(procBased);
		
		parent = getFieldEditorParent();
		BooleanFieldEditor specializedRequest = new BooleanFieldEditor(TYPED_REQUESTS, "Generate typed requests/responses", parent);
		addField(specializedRequest);
		
		parent = getFieldEditorParent();
		BooleanFieldEditor genJavaMappings = new BooleanFieldEditor(GENERATE_JAVA_MAPPING, "Generate type mappings for Java", parent);
		addField(genJavaMappings);