SignalHubPushInProtocolRequestHandler.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2025 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.protocol.modipa.handlers;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import org.openspcoop2.core.commons.CoreException;
import org.openspcoop2.core.constants.CostantiDB;
import org.openspcoop2.core.constants.TipoPdD;
import org.openspcoop2.core.id.IDAccordo;
import org.openspcoop2.core.id.IDServizio;
import org.openspcoop2.core.id.IDSoggetto;
import org.openspcoop2.core.registry.AccordoServizioParteSpecifica;
import org.openspcoop2.core.registry.Fruitore;
import org.openspcoop2.core.registry.ProtocolProperty;
import org.openspcoop2.core.registry.driver.DriverRegistroServiziException;
import org.openspcoop2.core.registry.driver.IDAccordoFactory;
import org.openspcoop2.core.registry.driver.IDServizioFactory;
import org.openspcoop2.core.registry.driver.IDriverRegistroServiziGet;
import org.openspcoop2.pdd.core.dynamic.DynamicException;
import org.openspcoop2.pdd.core.dynamic.DynamicUtils;
import org.openspcoop2.pdd.core.handlers.HandlerException;
import org.openspcoop2.pdd.core.handlers.InRequestContext;
import org.openspcoop2.pdd.core.handlers.InRequestHandler;
import org.openspcoop2.protocol.basic.registry.RegistryReader;
import org.openspcoop2.protocol.modipa.config.ModIProperties;
import org.openspcoop2.protocol.modipa.config.ModISignalHubConfig;
import org.openspcoop2.protocol.modipa.config.ModISignalHubParamConfig;
import org.openspcoop2.protocol.modipa.constants.ModICostanti;
import org.openspcoop2.protocol.modipa.constants.ModISignalHubOperation;
import org.openspcoop2.protocol.modipa.properties.ModIDynamicConfigurationAccordiParteComuneUtilities;
import org.openspcoop2.protocol.modipa.utils.ModIUtilities;
import org.openspcoop2.protocol.registry.RegistroServiziReader;
import org.openspcoop2.protocol.sdk.Context;
import org.openspcoop2.protocol.sdk.ProtocolException;
import org.openspcoop2.protocol.sdk.constants.IntegrationFunctionError;
import org.openspcoop2.protocol.sdk.properties.ProtocolProperties;
import org.openspcoop2.protocol.sdk.properties.ProtocolPropertiesUtils;
import org.openspcoop2.protocol.sdk.registry.IRegistryReader;
import org.openspcoop2.protocol.sdk.registry.ProtocolFiltroRicercaServizi;
import org.openspcoop2.protocol.sdk.registry.RegistryException;
import org.openspcoop2.protocol.sdk.registry.RegistryNotFound;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.utils.BooleanNullable;
import org.openspcoop2.utils.service.beans.ProfiloEnum;
import org.openspcoop2.utils.service.beans.utils.ProfiloUtils;
import org.slf4j.Logger;

/**
 * SignalHubPushInRequestHandler
 *
 * @author Tommaso Burlon (tommaso.burlon@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class SignalHubPushInProtocolRequestHandler implements InRequestHandler {
	
	@Override
	public void invoke(InRequestContext context) throws HandlerException {
		try {
			if (checkSignalHubPushConditions(context)) {
				List<ProtocolProperty> props = getSignalHubPushProperty(context);
				parseSignalHubPushProperty(context, props);
			}
		} catch (ProtocolException 
				| DriverRegistroServiziException 
				| DynamicException 
				| RegistryNotFound 
				| RegistryException e) {
			throw new HandlerException(e);
		} 
	}
	
	private boolean checkSignalHubPushConditions(InRequestContext context) throws ProtocolException, DriverRegistroServiziException, RegistryNotFound, RegistryException {
		Logger logger = context.getLogCore();
		
		
		// controllo che signalhub sia abilitato
		ModIProperties modiProperties = ModIProperties.getInstance();
		if(!modiProperties.isSignalHubEnabled()) { 
			return false;
		}
		
		// controllo che sia una fruizione ModI
		if (context.getTipoPorta() == null
				|| !context.getTipoPorta().equals(TipoPdD.DELEGATA)
				|| context.getPddContext() == null)
			return false;
		
		IRegistryReader registryReader = this.getIRegistryReader(context);

		
		// controllo che l'id dell'accordo sia raggiungibile
		AccordoServizioParteSpecifica asps = registryReader.getAccordoServizioParteSpecifica(this.getRequestInfo(context).getIdServizio());
		IDAccordo idAccordo = IDAccordoFactory.getInstance().getIDAccordoFromUri(asps.getAccordoServizioParteComune());
		if (idAccordo == null)
			return false;
		
		
		// controllo che l'accordo sia l'accordo built-in per la registrazione dei segnali
		boolean isApiSignalHubPushAPI = false;
			
		Map<String, IDriverRegistroServiziGet> readers = RegistroServiziReader.getDriverRegistroServizi();

		for (Map.Entry<String, IDriverRegistroServiziGet> entry : readers.entrySet()) {
			IRegistryReader reader;
			try {
				reader = new RegistryReader(entry.getValue(), logger);
				isApiSignalHubPushAPI |= ModIDynamicConfigurationAccordiParteComuneUtilities.isApiSignalHubPushAPI(idAccordo, reader, ModIProperties.getInstance(), logger);
			} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException
					| InvocationTargetException | NoSuchMethodException | SecurityException e) {
				// ignore
			}
		}
		
		if (!isApiSignalHubPushAPI)
			return false;
		
		logger.debug("Nuova richiesta di deposito di segnali SignalHub");
		
		return true;
	}
	
	
	private RequestInfo getRequestInfo(InRequestContext context) {
		return (RequestInfo) context.getPddContext().getObject(org.openspcoop2.core.constants.Costanti.REQUEST_INFO);
	}
	
	private IRegistryReader getIRegistryReader(InRequestContext context) throws ProtocolException {
		return context.getProtocolFactory().getCachedRegistryReader(context.getStato(), this.getRequestInfo(context));
	}
	
	private IDSoggetto getFruitore(InRequestContext context) {
		return this.getRequestInfo(context).getFruitore();
	}
	
	private List<ProtocolProperty> getSignalHubPushProperty(InRequestContext context) throws ProtocolException, RegistryNotFound, RegistryException {
		IRegistryReader reader = this.getIRegistryReader(context);
		IDServizio servizio = this.getRequestInfo(context).getIdServizio();
		IDSoggetto idFruitore = this.getFruitore(context);
		
		AccordoServizioParteSpecifica asps = reader.getAccordoServizioParteSpecifica(servizio, false);
		List<Fruitore> fruitori = Objects.requireNonNullElseGet(asps.getFruitoreList(), ArrayList::new);
		
		Optional<Fruitore> fruitore = fruitori
				.stream().filter(f -> f.getNome().equals(idFruitore.getNome()) && f.getTipo().equals(idFruitore.getTipo()))
				.findAny();
				
		if (fruitore.isEmpty())
			return List.of();
		
		List<ProtocolProperty> props = fruitore.get().getProtocolPropertyList();
		if (props != null && !props.isEmpty())
			return props;
			
		return List.of();
	}

	private String getDynamicProperty(List<ProtocolProperty> props, ModISignalHubParamConfig param, Context context, Map<String, Object> dynamicMap, boolean useDefault) throws ProtocolException {
		try {
			String key = CostantiDB.MODIPA_API_IMPL_PUSH_SIGNAL_HUB_PARAM_VALUE_ID_PREFIX + param.getPropertyId();
			String mode = ProtocolPropertiesUtils.getRequiredStringValuePropertyRegistry(props, CostantiDB.MODIPA_API_IMPL_PUSH_SIGNAL_HUB_PARAM_MODE_ID_PREFIX + param.getPropertyId());
			String ridefinedValue = ProtocolPropertiesUtils.getOptionalStringValuePropertyRegistry(props, CostantiDB.MODIPA_API_IMPL_PUSH_SIGNAL_HUB_PARAM_VALUE_ID_PREFIX + param.getPropertyId());
			List<String> values = null;
					
			// se il campo e' stato ridefinito uso il template ridefinito altrimenti quello di default
			if (!useDefault && mode.equals(CostantiDB.MODIPA_PROFILO_RIDEFINISCI)) {
				values = List.of(ridefinedValue);
			} else {
				values = param.getRules();
			}
	
			return ModIUtilities.getDynamicValue(key, values, dynamicMap, context);
		} catch (ProtocolException e) {
			return null;
		}
	}
	
	private void parseSignalHubPushProperty(InRequestContext context, List<ProtocolProperty> props) throws ProtocolException, DynamicException, HandlerException, RegistryNotFound, RegistryException, NumberFormatException, DriverRegistroServiziException { 
		ModIProperties modiProperties = ModIProperties.getInstance();
		ModISignalHubConfig signalHubConfig = modiProperties.getSignalHubConfig();
		Logger logger = context.getLogCore();

		Map<String, ModISignalHubParamConfig> claims = signalHubConfig.getClaims()
				.stream()
				.collect(Collectors.toMap(v -> v.getPropertyId(), v -> v));
		
		Map<String, Object> dynamicMap = DynamicUtils.buildDynamicMap(context.getMessaggio(), context.getPddContext(), null, logger, modiProperties.isReadByPathBufferEnabled());

		// controllo se il segnale e' un segnale di SEEDUPDATE in tal caso le informazioni saranne messe da govway negli header di default
		boolean isSeedUpdate = false;
		String signalTypeRaw = getDynamicProperty(props, claims.get(ModICostanti.MODIPA_SIGNAL_HUB_ID_SIGNAL_TYPE), context.getPddContext(), dynamicMap, true);
		ModISignalHubOperation signalType = ModISignalHubOperation.fromString(signalTypeRaw);
		
		if (signalType != null && signalType.equals(ModISignalHubOperation.SEEDUPDATE))
			isSeedUpdate = true;
		
		// ottengo le varie informaizoni di deposito del segnale (nel caso di seedupdate usero i template di default)
		String objectId = getDynamicProperty(props, claims.get(ModICostanti.MODIPA_SIGNAL_HUB_ID_OBJECT_ID), context.getPddContext(), dynamicMap, isSeedUpdate);
		String objectType = getDynamicProperty(props, claims.get(ModICostanti.MODIPA_SIGNAL_HUB_ID_OBJECT_TYPE), context.getPddContext(), dynamicMap, isSeedUpdate);
		signalTypeRaw = getDynamicProperty(props, claims.get(ModICostanti.MODIPA_SIGNAL_HUB_ID_SIGNAL_TYPE), context.getPddContext(), dynamicMap, isSeedUpdate);

		if (objectId == null) {
			throw newHandlerException("Parametro obbligatorio 'objectId' non rilevato", IntegrationFunctionError.BAD_REQUEST);
		}
		if (objectType == null) {
			throw newHandlerException("Parametro obbligatorio 'objectType' non rilevato", IntegrationFunctionError.BAD_REQUEST);
		}
		if (signalTypeRaw == null) {
			throw newHandlerException("Parametro obbligatorio 'signalType' non rilevato", IntegrationFunctionError.BAD_REQUEST);
		}
		
		signalType = ModISignalHubOperation.fromString(signalTypeRaw);
		if (signalType == null) {
			throw newHandlerException("Parametro 'signalType' con valore errato '"+signalTypeRaw+"'; valori supportati: UPDATE, CREATE, DELETE", IntegrationFunctionError.BAD_REQUEST);
		}
		
		String service = null;
		String serviceVersion = null;
		String serviceId = null;
		
		service = getDynamicProperty(props, claims.get("service"), context.getPddContext(), dynamicMap, isSeedUpdate);
		serviceVersion = getDynamicProperty(props, claims.get("serviceVersion"), context.getPddContext(), dynamicMap, isSeedUpdate);
		serviceId = getDynamicProperty(props, claims.get("serviceId"), context.getPddContext(), dynamicMap, isSeedUpdate);
		
		
		IRegistryReader reader = this.getIRegistryReader(context);
		
		IDServizio idServizio = null;
		List<ProtocolProperty> eServiceProperties = null;
		
		if (serviceId == null && (service == null || serviceVersion == null)) {
			throw newHandlerException("Un serviceId o in alternativa il nome e la versione devono essere obbligatoriamente indicati per poter individuare il servizio", IntegrationFunctionError.BAD_REQUEST);
		}
		
		// se tutte le informazioni sono presenti ottengo il serviceId a partire dall'id del servizio
		if (serviceId == null) {
			
			int versioneServizio = -1;
			try {
				versioneServizio = Integer.valueOf(serviceVersion);
				if(versioneServizio<=0) {
					throw new CoreException("Valore deve essere maggiore di zero");
				}
			}catch(Exception e) {
				String msg = "Parametro 'serviceVersion' presenta un valore '"+serviceVersion+"' non valido; deve essere un numero intero maggiore di zero";
				logger.error(msg+": "+e.getMessage(), e);
				throw newHandlerException(msg, IntegrationFunctionError.BAD_REQUEST);
			}
			
			try {
				idServizio = IDServizioFactory.getInstance().getIDServizioFromValues(
						ProfiloUtils.toProtocollo(ProfiloEnum.MODIPA), 
						service,
						this.getFruitore(context), 
						versioneServizio);
						AccordoServizioParteSpecifica asps = reader.getAccordoServizioParteSpecifica(idServizio);
						eServiceProperties = asps.getProtocolPropertyList();
			}catch(RegistryNotFound notFound) {
				String nomeSoggetto = idServizio!=null && idServizio.getSoggettoErogatore()!=null && idServizio.getSoggettoErogatore().getNome()!=null ? idServizio.getSoggettoErogatore().getNome() : "Non identificato";
				throw newHandlerException("Il soggetto '"+nomeSoggetto+"' non risulta erogare un servizio con nome '"+service+"'  e versione '"+versioneServizio+"'", IntegrationFunctionError.BAD_REQUEST);
			}
		} else {
			// altrimenti ottengo l'id sdel servizio partendo dal serviceId
			ProtocolFiltroRicercaServizi filter = new ProtocolFiltroRicercaServizi();
			ProtocolProperties filterProps = new ProtocolProperties();
			filterProps.addProperty(ModICostanti.MODIPA_API_IMPL_INFO_ESERVICE_ID, serviceId);
			filter.setProtocolPropertiesServizi(filterProps);
			
			List<IDServizio> idServices = null;
			try {
				idServices = reader.findIdAccordiServizioParteSpecifica(filter);
			}catch(RegistryNotFound notFound) {
				// ignore
			}
			logger.debug("id servizi: {}", idServices);
			
			if (idServices==null || idServices.isEmpty() || idServices.get(0)==null) {
				throw newHandlerException("Non esiste una erogazione di servizio registrato con l'id servizio '"+serviceId+"' indicato", IntegrationFunctionError.BAD_REQUEST);
			}
			
			idServizio = idServices.get(0);
			
			IDSoggetto soggettoFruitore = this.getFruitore(context);
			if (!idServizio.getSoggettoErogatore().equals(soggettoFruitore)) {
				context.getPddContext().addObject(org.openspcoop2.core.constants.Costanti.ERRORE_AUTORIZZAZIONE, "true");
				throw newHandlerException("L'erogazione di servizio individuata con l'id servizio '"+serviceId+"' appartiene ad un soggetto '"+idServizio.getSoggettoErogatore().getNome()+"' differente dal soggetto pubblicatore '"+soggettoFruitore.getNome()+"'", 
						IntegrationFunctionError.AUTHORIZATION_DENY);
			}

			AccordoServizioParteSpecifica asps = reader.getAccordoServizioParteSpecifica(idServizio);
			eServiceProperties = asps.getProtocolPropertyList();
		}
		
		try {
			serviceId = ProtocolPropertiesUtils.getRequiredStringValuePropertyRegistry(eServiceProperties, ModICostanti.MODIPA_API_IMPL_INFO_ESERVICE_ID);
		}catch(Exception e) {
			// succede nel caso di name e version
			throw newHandlerException("L'erogazione del servizio indicato non contiene la configurazione relativa al serviceId", IntegrationFunctionError.BAD_REQUEST);
		}
		
		try {
			BooleanNullable signalHub = ProtocolPropertiesUtils.getOptionalBooleanValuePropertyRegistry(eServiceProperties, ModICostanti.MODIPA_API_IMPL_INFO_SIGNAL_HUB_ID);
			if(signalHub==null || signalHub.getValue()==null || !signalHub.getValue().booleanValue()) {
				throw new CoreException("Non Attiva");
			}
		}catch(Exception e) {
			// succede nel caso non sia stata attuata la configurazione
			String msg = "Nell'erogazione del servizio indicato non risulta attiva la funzionalità Signal Hub";
			logger.error(msg+": "+e.getMessage(), e);
			throw newHandlerException(msg, IntegrationFunctionError.BAD_REQUEST);
		}
		
		context.getPddContext().addObject(ModICostanti.MODIPA_KEY_INFO_SIGNAL_HUB_OBJECT_ID, objectId);
		context.getPddContext().addObject(ModICostanti.MODIPA_KEY_INFO_SIGNAL_HUB_ESERVICE_ID, serviceId);
		context.getPddContext().addObject(ModICostanti.MODIPA_KEY_INFO_SIGNAL_HUB_SERVICE, idServizio);
		context.getPddContext().addObject(ModICostanti.MODIPA_KEY_INFO_SIGNAL_HUB_OBJECT_TYPE, objectType);
		context.getPddContext().addObject(ModICostanti.MODIPA_KEY_INFO_SIGNAL_HUB_SIGNAL_TYPE, signalType);
		context.getPddContext().addObject(ModICostanti.MODIPA_KEY_INFO_SIGNAL_HUB_PROPERTIES, eServiceProperties);
	}

	private HandlerException newHandlerException(String msg, IntegrationFunctionError error) {
		HandlerException he = new HandlerException(msg);
		he.setIntegrationFunctionError(error);
		return he;
	}
}