WADLApiReader.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it). 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */


package org.openspcoop2.utils.wadl;

import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.ws.rs.core.MultivaluedMap;

import org.jvnet.ws.wadl.Param;
import org.jvnet.ws.wadl.ast.ApplicationNode;
import org.jvnet.ws.wadl.ast.FaultNode;
import org.jvnet.ws.wadl.ast.MethodNode;
import org.jvnet.ws.wadl.ast.PathSegment;
import org.jvnet.ws.wadl.ast.RepresentationNode;
import org.jvnet.ws.wadl.ast.ResourceNode;
import org.openspcoop2.utils.rest.ApiReaderConfig;
import org.openspcoop2.utils.rest.IApiReader;
import org.openspcoop2.utils.rest.ProcessingException;
import org.openspcoop2.utils.rest.api.Api;
import org.openspcoop2.utils.rest.api.ApiOperation;
import org.openspcoop2.utils.rest.api.ApiParameterSchema;
import org.openspcoop2.utils.rest.api.ApiRequest;
import org.openspcoop2.utils.rest.api.ApiBodyParameter;
import org.openspcoop2.utils.rest.api.ApiRequestDynamicPathParameter;
import org.openspcoop2.utils.rest.api.ApiHeaderParameter;
import org.openspcoop2.utils.rest.api.ApiRequestQueryParameter;
import org.openspcoop2.utils.rest.api.ApiResponse;
import org.openspcoop2.utils.rest.api.ApiSchema;
import org.openspcoop2.utils.rest.api.ApiSchemaType;
import org.openspcoop2.utils.transport.http.HttpRequestMethod;
import org.slf4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;


/**
 * WADLApiReader
 * 
 * @author Andrea Poli (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 *
 */
public class WADLApiReader implements IApiReader {

	private ApplicationWrapper applicationWadlWrapper;
	private ApiSchema[] schemas;
	
	private Map<String, byte[]> convert(ApiSchema ... schema) {
		Map<String, byte[]> map = null;
		if(schema!=null && schema.length>0) {
			for (ApiSchema apiSchema : schema) {
				if(ApiSchemaType.XSD.equals(apiSchema.getType())) {
					if(map==null) {
						map = new HashMap<String, byte[]>();
					}
					map.put(apiSchema.getName(), apiSchema.getContent());
				}
			}
		}
		return map;
	}
	
	
	
	@Override
	public void init(Logger log, String content, ApiReaderConfig config) throws ProcessingException {
		this._init(log, content, config);
	}
	@Override
	public void init(Logger log, String content, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		this._init(log, content, config, schema);
	}
	private void _init(Logger log, String content, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		try{
			this.applicationWadlWrapper = WADLUtilities.getInstance(config.getXmlUtils()).readWADLFromBytes(log, content.getBytes(config.getCharset().getValue()),
					config.isVerbose(),config.isProcessInclude(),config.isProcessInlineSchema(),
					this.convert(schema));
			this.schemas = schema;
		}catch(Exception e){
			throw new ProcessingException(e.getMessage(),e);
		}
	}
	
	
	
	@Override
	public void init(Logger log, byte[] content, ApiReaderConfig config) throws ProcessingException {
		this._init(log, content, config);
	}
	@Override
	public void init(Logger log, byte[] content, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		this._init(log, content, config, schema);
	}
	private void _init(Logger log, byte[] content, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		try{
			this.applicationWadlWrapper = WADLUtilities.getInstance(config.getXmlUtils()).readWADLFromBytes(log, content,
					config.isVerbose(),config.isProcessInclude(),config.isProcessInlineSchema(),
					this.convert(schema));
			this.schemas = schema;
		}catch(Exception e){
			throw new ProcessingException(e.getMessage(),e);
		}
	}
	
	
	
	
	@Override
	public void init(Logger log, File file, ApiReaderConfig config) throws ProcessingException {
		this._init(log, file, config);
	}
	@Override
	public void init(Logger log, File file, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		this._init(log, file, config, schema);
	}
	private void _init(Logger log, File file, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		try{
			this.applicationWadlWrapper = WADLUtilities.getInstance(config.getXmlUtils()).readWADLFromFile(log, file,
					config.isVerbose(),config.isProcessInclude(),config.isProcessInlineSchema(),
					this.convert(schema));
			this.schemas = schema;
		}catch(Exception e){
			throw new ProcessingException(e.getMessage(),e);
		}
	}
	
	
	
	
	@Override
	public void init(Logger log, URI uri, ApiReaderConfig config) throws ProcessingException {
		this._init(log, uri, config);
	}
	@Override
	public void init(Logger log, URI uri, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		this._init(log, uri, config, schema);
	}
	private void _init(Logger log, URI uri, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		try{
			this.applicationWadlWrapper = WADLUtilities.getInstance(config.getXmlUtils()).readWADLFromURI(log, uri,
					config.isVerbose(),config.isProcessInclude(),config.isProcessInlineSchema(),
					this.convert(schema));
			this.schemas = schema;
		}catch(Exception e){
			throw new ProcessingException(e.getMessage(),e);
		}
	}




	@Override
	public void init(Logger log, Document doc, ApiReaderConfig config) throws ProcessingException {
		this._init(log, doc, config);
	}
	@Override
	public void init(Logger log, Document doc, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		this._init(log, doc, config, schema);
	}
	private void _init(Logger log, Document doc, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		try{
			this.applicationWadlWrapper = WADLUtilities.getInstance(config.getXmlUtils()).readWADLFromDocument(log, doc,
					config.isVerbose(),config.isProcessInclude(),config.isProcessInlineSchema(),
					this.convert(schema));
			this.schemas = schema;
		}catch(Exception e){
			throw new ProcessingException(e.getMessage(),e);
		}
	}

	
	
	
	@Override
	public void init(Logger log, Element element, ApiReaderConfig config) throws ProcessingException {
		this._init(log, element, config);
	}
	@Override
	public void init(Logger log, Element element, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		this._init(log, element, config, schema);
	}
	private void _init(Logger log, Element element, ApiReaderConfig config, ApiSchema ... schema) throws ProcessingException {
		try{
			this.applicationWadlWrapper = WADLUtilities.getInstance(config.getXmlUtils()).readWADLFromDocument(log, element,
					config.isVerbose(),config.isProcessInclude(),config.isProcessInlineSchema(),
					this.convert(schema));
			this.schemas = schema;
		}catch(Exception e){
			throw new ProcessingException(e.getMessage(),e);
		}
	}




	@Override
	public Api read() throws ProcessingException {
		
		Api api = new WADLApi(this.applicationWadlWrapper);
		
		if(this.schemas!=null && this.schemas.length>0) {
			for (ApiSchema apiSchema : this.schemas) {
				api.addSchema(apiSchema);
			}
		}
		
		ApplicationNode an = this.applicationWadlWrapper.getApplicationNode();
		List<ResourceNode> rs = an.getResources();
		if(rs.size()<=0){
			throw new ProcessingException("Non sono state trovate risorse registrate nel documento");
		}
		if(rs.size()>1){
			throw new ProcessingException("Trovato piĆ¹ di un elemento 'resources' nella radice del documento");
		}
		try{
			api.setBaseURL(new URL(rs.get(0).getUriTemplate()));
		}catch(Exception e){
			throw new ProcessingException("Trovata resources base url malformata: "+e.getMessage(),e);
		}
		
		this.readOperations(rs, api);
		
		return api;
	}
	private void readOperations(List<ResourceNode> rs,Api api) throws ProcessingException{
		try{	
			if(rs!=null && rs.size()>0){
				for (ResourceNode resourceNode : rs) {
					List<MethodNode> method = resourceNode.getMethods();
					if(method!=null && method.size()>0){
						for (MethodNode methodNode : method) {
							ApiOperation operation = new ApiOperation(HttpRequestMethod.valueOf(methodNode.getName().toUpperCase()), resourceNode.getAllResourceUriTemplate());
							
							List<Param> lHeaders = methodNode.getHeaderParameters();
							if(lHeaders!=null && lHeaders.size()>0){
								if(operation.getRequest()==null){
									operation.setRequest(new ApiRequest());
								}
								for (Param param : lHeaders) {
									ApiParameterSchema apiParameterSchema = new ApiParameterSchema();
									apiParameterSchema.addType(param.getType().toString(), null);
									ApiHeaderParameter header = new ApiHeaderParameter(param.getName(),apiParameterSchema);
									header.setRequired(param.isRequired());
									operation.getRequest().addHeaderParameter(header);
								}
							}
							
							List<Param> lQuery = methodNode.getQueryParameters();
							if(lQuery!=null && lQuery.size()>0){
								if(operation.getRequest()==null){
									operation.setRequest(new ApiRequest());
								}
								for (Param param : lQuery) {
									ApiParameterSchema apiParameterSchema = new ApiParameterSchema();
									apiParameterSchema.addType(param.getType().toString(), null);
									ApiRequestQueryParameter query = new ApiRequestQueryParameter(param.getName(),apiParameterSchema);
									query.setRequired(param.isRequired());
									operation.getRequest().addQueryParameter(query);
								}
							}

							List<PathSegment> lSegment = resourceNode.getPathSegments();
							if(lSegment!=null && lSegment.size()>0){
								for (PathSegment segment : lSegment) {
									List<Param> lDynamicPath = segment.getTemplateParameters();
									if(lDynamicPath!=null && lDynamicPath.size()>0){
										for (Param param : lDynamicPath) {
											if(operation.getRequest()==null){
												operation.setRequest(new ApiRequest());
											}
											ApiParameterSchema apiParameterSchema = new ApiParameterSchema();
											apiParameterSchema.addType(param.getType().toString(), null);
											ApiRequestDynamicPathParameter query = new ApiRequestDynamicPathParameter(param.getName(),apiParameterSchema);
											//query.setRequired(param.isRequired());
											query.setRequired(true);
											operation.getRequest().addDynamicPathParameter(query);
										}
									}
								}
							}
							
							List<RepresentationNode> lInput = methodNode.getSupportedInputs();
							if(lInput!=null && lInput.size()>0){
					    		for (RepresentationNode representationNode : lInput) {
					    			if(operation.getRequest()==null){
										operation.setRequest(new ApiRequest());
									}
					    			ApiBodyParameter query = new ApiBodyParameter(null);
					    			query.setMediaType(representationNode.getMediaType());
					    			query.setElement(representationNode.getElement());
									operation.getRequest().addBodyParameter(query);
								}
							}
							
				    		MultivaluedMap<List<Long>, RepresentationNode> mapOutput = methodNode.getSupportedOutputs();
				    		if(mapOutput!=null && mapOutput.size()>0){
					    		Iterator<List<Long>> itOutput = mapOutput.keySet().iterator();
					    		while (itOutput.hasNext()) {
									List<java.lang.Long> listLong = (List<java.lang.Long>) itOutput.next();
									List<RepresentationNode> representationNode = mapOutput.get(listLong);
									
									for (int i = 0; i < listLong.size(); i++) {
										int httpStatus = listLong.get(i).intValue();
										RepresentationNode rNodeHttpStatus = representationNode.get(i);
										
										ApiResponse apiResponse = null;
										for (ApiResponse responseExists : operation.getResponses()) {
											if(responseExists.getHttpReturnCode() == httpStatus) {
												apiResponse = responseExists;
												break;
											}
										}
										
										if(apiResponse==null) {
											apiResponse=new ApiResponse();
											apiResponse.setHttpReturnCode(httpStatus);
											
											if(rNodeHttpStatus.getParam()!=null){
												for (Param param : rNodeHttpStatus.getParam()) {
													ApiParameterSchema apiParameterSchema = new ApiParameterSchema();
													apiParameterSchema.addType(param.getType().toString(), null);
													ApiHeaderParameter header = new ApiHeaderParameter(param.getName(),apiParameterSchema);
													header.setRequired(param.isRequired());
													apiResponse.addHeaderParameter(header);
												}
							    			}
											
											operation.addResponse(apiResponse);
										}
										
										if(rNodeHttpStatus.getMediaType()!=null) {
											String name = null;
											if(rNodeHttpStatus.getElement()!=null) {
												name = rNodeHttpStatus.getElement().getLocalPart();
											}
											ApiBodyParameter bodyParameter = new ApiBodyParameter(name);
											bodyParameter.setMediaType(rNodeHttpStatus.getMediaType());
											bodyParameter.setElement(rNodeHttpStatus.getElement());
											apiResponse.addBodyParameter(bodyParameter);
										}
									}
									
								}
				    		}
				    		
				    		MultivaluedMap<List<Long>, FaultNode> mapFault = methodNode.getFaults();
				    		if(mapFault!=null && mapFault.size()>0){
					    		Iterator<List<Long>> itFault = mapFault.keySet().iterator();
					    		while (itFault.hasNext()) {
									List<java.lang.Long> listLong = (List<java.lang.Long>) itFault.next();
									List<FaultNode> representationNode = mapFault.get(listLong);
									
									for (int i = 0; i < listLong.size(); i++) {
										int httpStatus = listLong.get(i).intValue();
										FaultNode rNodeHttpStatus = representationNode.get(i);
										
										ApiResponse apiResponse = null;
										for (ApiResponse responseExists : operation.getResponses()) {
											if(responseExists.getHttpReturnCode() == httpStatus) {
												apiResponse = responseExists;
												break;
											}
										}
										
										if(apiResponse==null) {
											apiResponse=new ApiResponse();
											apiResponse.setHttpReturnCode(httpStatus);
											
											if(rNodeHttpStatus.getParam()!=null){
												for (Param param : rNodeHttpStatus.getParam()) {
													ApiParameterSchema apiParameterSchema = new ApiParameterSchema();
													apiParameterSchema.addType(param.getType().toString(), null);
													ApiHeaderParameter header = new ApiHeaderParameter(param.getName(),apiParameterSchema);
													header.setRequired(param.isRequired());
													apiResponse.addHeaderParameter(header);
												}
							    			}
											
											operation.addResponse(apiResponse);
										}
										
										if(rNodeHttpStatus.getMediaType()!=null) {
											String name = null;
											if(rNodeHttpStatus.getElement()!=null) {
												name = rNodeHttpStatus.getElement().getLocalPart();
											}
											ApiBodyParameter bodyParameter = new ApiBodyParameter(name);
											bodyParameter.setMediaType(rNodeHttpStatus.getMediaType());
											bodyParameter.setElement(rNodeHttpStatus.getElement());
											apiResponse.addBodyParameter(bodyParameter);
										}
									}
								}
				    		}
				    		
							api.addOperation(operation);
						}
					}
					this.readOperations(resourceNode.getChildResources(), api);
				}
			}
		}catch(Exception e){
			throw new ProcessingException(e.getMessage(),e);
		}
	}

}