ilscipio/scipio-erp

View on GitHub
framework/common/webcommon/WEB-INF/actions/includes/runServiceLikeEvent.groovy

Summary

Maintainability
Test Coverage
/*******************************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *******************************************************************************/

/**
 * SCIPIO: This script emulates the execution of an event service call
 * from controller event, but using groovy so it can be run from screen.
 * 
 * It will run the service in a separate transaction to avoid having errors
 * halt the screen render transaction.
 * 
 * There are differences, such as the context variables used for error messages
 * instead of request attributes. It appends to existing event/error list,
 * not replace.
 * 
 * Derived from:
 * org.ofbiz.widget.renderer.ScreenRenderer.populateContextForRequest
 * org.ofbiz.webapp.event.ServiceEventHandler.invoke
 * 
 * FIXME:
 * * Does not support multi-part-map or string-map-prefix!
 * 
 * KNOWN ISSUES:
 * * Does not check for GET params on HTTPS requests; usually we want to allow here
 *   anyway because this is mainly intended for queries.
 */

import java.util.*;
import org.ofbiz.entity.*;
import org.ofbiz.base.util.*;
import org.ofbiz.entity.util.*;
import org.ofbiz.service.*;
import org.ofbiz.webapp.event.EventHandlerException;
import org.ofbiz.webapp.event.ServiceEventHandler;

final module = "runServiceLikeEvent.groovy";

doExec = context.rsleArgs.doExec; // boolean for easy conditional execution
if (doExec == null) {
    doExec = true;
}

serviceName = context.rsleArgs.serviceName;

serviceCtxOverrides = context.rsleArgs.serviceCtxOverrides;

serviceParams = context.rsleArgs.serviceParams;

serviceExtraParams = context.rsleArgs.serviceExtraParams;

updateCtx = context.rsleArgs.updateCtx;
if (updateCtx == null) {
    updateCtx = true;
}

updateReqAttr = context.rsleArgs.updateReqAttr;
if (updateReqAttr == null) {
    updateReqAttr = true;
}

updateParamsMap = context.rsleArgs.updateParamsMap;
if (updateParamsMap == null) {
    updateParamsMap = true;
}

context.remove("rsleArgs");

eventMessageList = [];
errorMessageList = [];

rsleRes = [:];
rsleRes.errorType = null;
// convenience bools
rsleRes.isExec = false;
rsleRes.isError = false;
rsleRes.isServiceFailure = false;
rsleRes.isServiceError = false;
rsleRes.isServiceSuccess = false;

if (doExec) {
    rsleRes.isExec = true;
    try {
        DispatchContext dctx = dispatcher.getDispatchContext();
        if (dctx == null) {
            throw new EventHandlerException("Dispatch context cannot be found");
        }
        
        // get the service model to generate context
        ModelService model = null;

        try {
            model = dctx.getModelService(serviceName);
        } catch (GenericServiceException e) {
            throw new EventHandlerException("Problems getting the service model", e);
        }

        if (model == null) {
            throw new EventHandlerException("Problems getting the service model");
        }
        
        if (serviceParams == null) {
        
            Map<String, Object> rawParametersMap = UtilHttp.getCombinedMap(request);
            Set<String> urlOnlyParameterNames = UtilHttp.getUrlOnlyParameterMap(request).keySet();
    
            // we have a service and the model; build the context
            Map<String, Object> serviceContext = new HashMap<String, Object>();
            for (ModelParam modelParam: model.getInModelParamList()) {
                String name = modelParam.name;
    
                // don't include userLogin, that's taken care of below
                if ("userLogin".equals(name)) continue;
                // don't include locale, that is also taken care of below
                if ("locale".equals(name)) continue;
                // don't include timeZone, that is also taken care of below
                if ("timeZone".equals(name)) continue;
    
                Object value = null;
                //if (UtilValidate.isNotEmpty(modelParam.stringMapPrefix)) {
                //    Map<String, Object> paramMap = UtilHttp.makeParamMapWithPrefix(request, multiPartMap, modelParam.stringMapPrefix, null);
                //    value = paramMap;
                //    if (Debug.verboseOn()) Debug.logVerbose("Set [" + modelParam.name + "]: " + paramMap, module);
                //} else if (UtilValidate.isNotEmpty(modelParam.stringListSuffix)) {
                //    List<Object> paramList = UtilHttp.makeParamListWithSuffix(request, multiPartMap, modelParam.stringListSuffix, null);
                //    value = paramList;
                //} else {
                if (true) {
                    // first check the multi-part map
                    //value = multiPartMap.get(name); // SCIPIO: TODO
    
                    // next check attributes; do this before parameters so that attribute which can be changed by code can override parameters which can't
                    if (UtilValidate.isEmpty(value)) {
                        Object tempVal = request.getAttribute(UtilValidate.isEmpty(modelParam.requestAttributeName) ? name : modelParam.requestAttributeName);
                        if (tempVal != null) {
                            value = tempVal;
                        }
                    }
    
                    // check the request parameters
                    if (UtilValidate.isEmpty(value)) {
                        // SCIPIO: WARN: this skips the standard POST security check, but almost no other screens check for this
                        //ServiceEventHandler.checkSecureParameter(requestMap, urlOnlyParameterNames, name, session, serviceName, dctx.getDelegator());
    
                        // if the service modelParam has allow-html="any" then get this direct from the request instead of in the parameters Map so there will be no canonicalization possibly messing things up
                        if ("any".equals(modelParam.allowHtml)) {
                            value = request.getParameter(name);
                        } else {
                            // use the rawParametersMap from UtilHttp in order to also get pathInfo parameters, do canonicalization, etc
                            value = rawParametersMap.get(name);
                        }
    
                        // make any composite parameter data (e.g., from a set of parameters {name_c_date, name_c_hour, name_c_minutes})
                        if (value == null) {
                            value = UtilHttp.makeParamValueFromComposite(request, name, locale);
                        }
                    }
    
                    // then session
                    if (UtilValidate.isEmpty(value)) {
                        Object tempVal = request.getSession().getAttribute(UtilValidate.isEmpty(modelParam.sessionAttributeName) ? name : modelParam.sessionAttributeName);
                        if (tempVal != null) {
                            value = tempVal;
                        }
                    }
    
                    // no field found
                    if (value == null) {
                        //still null, give up for this one
                        continue;
                    }
    
                    if (value instanceof String && ((String) value).length() == 0) {
                        // interpreting empty fields as null values for each in back end handling...
                        value = null;
                    }
                }
                // set even if null so that values will get nulled in the db later on
                serviceContext.put(name, value);
            }
            
            serviceParams = serviceContext;
        }
        
        if (serviceExtraParams) {
            serviceParams.putAll(serviceExtraParams);
        }
        
        servCtx = model.makeValid(serviceParams, ModelService.IN_PARAM);
        servCtx.put("userLogin", context.userLogin);
        servCtx.put("locale", context.locale);
        servCtx.put("timeZone", context.timeZone);
        if (serviceCtxOverrides != null) {
            servCtx.putAll(serviceCtxOverrides);
        }
        // NOTE: ALWAYS require a new transaction so errors don't mess up the screen render transaction
        servRes = dispatcher.runSync(serviceName, servCtx, -1, true);
        rsleRes.serviceResult = servRes;
        
        // set the results in the request AND parameters map
        for (Map.Entry<String, Object> rme: servRes.entrySet()) {
            String resultKey = rme.getKey();
            Object resultValue = rme.getValue();
    
            if (resultKey != null && !ModelService.OUT_SYS_PARAMS.contains(resultKey)) { // SCIPIO: simplified with isSysResponseField
                if (updateReqAttr) {
                    request.setAttribute(resultKey, resultValue);
                }
                if (updateParamsMap) {
                    parameters.put(resultKey, resultValue);
                }
            }
        }
        
        // DON'T put these in request attributes; they would have been removed by the ScreenRenderer
        servMsgs = [:];
        org.ofbiz.webapp.event.EventUtil.copyEventErrorMessages(servRes, servMsgs); // SCIPIO: refactored

        if (servMsgs._EVENT_MESSAGE_) {
            eventMessageList.add(servMsgs._EVENT_MESSAGE_);
        }
        if (servMsgs._EVENT_MESSAGE_LIST_) {
            eventMessageList.addAll(servMsgs._EVENT_MESSAGE_LIST_);
        }
        if (servMsgs._ERROR_MESSAGE_) {
            errorMessageList.add(servMsgs._ERROR_MESSAGE_);
        }
        if (servMsgs._ERROR_MESSAGE_LIST_) {
            errorMessageList.addAll(servMsgs._ERROR_MESSAGE_LIST_);
        }
        
        rsleRes.isServiceFailure = ServiceUtil.isFailure(servRes);
        rsleRes.isServiceError = ServiceUtil.isError(servRes);
        rsleRes.isServiceSuccess = ServiceUtil.isSuccess(servRes);
    
        if (rsleRes.isServiceFailure) {
            rsleRes.errorType = "failure";
            rsleRes.isError = true;
        } else if (rsleRes.isServiceError) {
            rsleRes.errorType = "error";
            rsleRes.isError = true;
        }
    } catch (ServiceAuthException e) {
        errorMessageList.add(e.getNonNestedMessage());
        rsleRes.isError = true;
        rsleRes.errorType = "auth";
    } catch (ServiceValidationException e) {
        if (e.getMessageList() != null) {
            errorMessageList.addAll(e.getMessageList());
        } else {
            errorMessageList.add(e.getNonNestedMessage());
        }
        rsleRes.isError = true;
        rsleRes.errorType = "validate";
    } catch(GenericServiceException e) {
        Debug.logError(e, "Exception trying to run " + serviceName + " service from groovy", module);
        errorMessageList.add(UtilProperties.getMessage("CommonUiLabels", "CommonError", locale)); // TODO: better message
        rsleRes.isError = true;
        rsleRes.errorType = "general";
    }
    
    if (updateCtx) {
        if (context.eventMessageList == null) {
            context.eventMessageList = eventMessageList;
        } else {
            context.eventMessageList.addAll(eventMessageList);
        }
        
        if (context.errorMessageList == null) {
            context.errorMessageList = errorMessageList;
        } else {
            context.errorMessageList.addAll(errorMessageList);
        }
        
        if (errorMessageList) {
            context.isError = true;
        }
    }
}

rsleRes.eventMessageList = eventMessageList;
rsleRes.errorMessageList = errorMessageList;

context.rsleRes = rsleRes;