예제 #1
0
        public void ExtractData(INotifier notifier, String soapBody, String mode)
        {
            XmlTextReader xtr  = null;
            XmlDocument   doc  = null;
            XmlNode       node = null;

            if (soapBody != String.Empty)
            {
                if (SettingUtils.IsDebugging(mode))
                {
                    notifier.AddLogEntry("Parsing notification SOAP: " + soapBody);
                }

                xtr  = new XmlTextReader(new System.IO.StringReader(soapBody));
                doc  = new XmlDocument();
                node = doc.ReadNode(xtr);

                while (xtr.Read())
                {
                    if (xtr.IsStartElement())
                    {
                        // Get element name
                        switch (xtr.Name)
                        {
                        //extract session id
                        case "SessionId":
                            if (xtr.Read())
                            {
                                this.SessionID = xtr.Value.Trim();
                                if (SettingUtils.IsDebugging(mode))
                                {
                                    notifier.AddLogEntry("SessionId: " + this.SessionID);
                                }
                            }
                            break;

                        //extract session url
                        case "PartnerUrl":
                            if (xtr.Read())
                            {
                                this.SessionURL = xtr.Value.Trim();
                                if (SettingUtils.IsDebugging(mode))
                                {
                                    notifier.AddLogEntry("SessionURL: " + this.SessionURL);
                                }
                            }
                            break;

                        //extract object's name
                        case "sObject":
                            if (xtr["xsi:type"] != null)
                            {
                                string sObjectName = xtr["xsi:type"];
                                if (sObjectName != null)
                                {
                                    this.ObjectName = sObjectName.Split(new char[] { ':' })[1];
                                    if (SettingUtils.IsDebugging(mode))
                                    {
                                        notifier.AddLogEntry("ObjectName: " + this.ObjectName);
                                    }
                                }
                            }
                            break;

                        //extract notification id [note: salesforce can send a notification multiple times. it is, therefore, a good idea to keep track of this id.]
                        case "Id":
                            if (xtr.Read())
                            {
                                this.NotificationIDs.Add(xtr.Value.Trim());
                            }
                            break;

                        //extract record id
                        case "sf:Id":
                            if (xtr.Read())
                            {
                                this.ObjectIDs.Add(xtr.Value.Trim());
                                if (SettingUtils.IsDebugging(mode))
                                {
                                    notifier.AddLogEntry("ObjectId: " + xtr.Value.Trim());
                                }
                            }
                            break;
                        }
                    }
                }
            }
            else
            {
                if (SettingUtils.IsDebugging(mode))
                {
                    notifier.AddLogEntry("Notification has no data to parse.");
                }
            }
        }
예제 #2
0
        protected override System.Threading.Tasks.Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            WorkflowRuleNotification receivedNotification = null;
            IAuthenticatedWho        authenticatedWho     = null;
            INotifier emailNotifier = null;
            Guid      tenantGuid    = Guid.Empty;
            String    tenantId      = null;
            String    mode          = null;
            String    email         = null;

            try
            {
                // Get the mode from the request uri
                mode = BaseHttpUtils.GetModeFromQuery(request.RequestUri);

                // Get any provided notification email
                email = BaseHttpUtils.GetEmailFromQuery(request.RequestUri);

                // Check to make sure the incoming request has enough segments
                // Deployed we have an extra segment for the deployment sub-directory, hence this is one more than you would have thought
                if (request.RequestUri.Segments.Length < 9)
                {
                    throw new ArgumentNullException("Request.Segments", "The incoming request is not a valid Url for outbound messages.");
                }

                // Get the segments from the call so we know which tenant we're executing against
                tenantId = request.RequestUri.Segments[8].Replace("/", "");

                // Check to make sure we've received valid guids
                if (Guid.TryParse(tenantId, out tenantGuid) == false)
                {
                    throw new ArgumentNullException("Request.Segments", "The incoming request does not contain a valid tenant identifier. You provided: " + tenantId);
                }

                // Create a basic authenticated who for the notifier
                authenticatedWho       = AuthenticationUtils.CreatePublicUser(tenantId);
                authenticatedWho.Email = email;

                // Create the notifier
                emailNotifier = EmailNotifier.GetInstance(tenantId, authenticatedWho, null, "WorkflowRuleListenerMessageHandler");

                // ExtractData would populate notification class' variables, which can be used to get desired data.
                receivedNotification = new WorkflowRuleNotification();
                receivedNotification.ExtractData(emailNotifier, request.Content.ReadAsStringAsync().Result, mode);

                // Now send ManyWho the notification that something has changed on a set of records, but only if ManyWho actually cares about them
                this.Execute(emailNotifier, tenantId, mode, receivedNotification);

                // Send the debug log if the user is running in debug mode
                if (SettingUtils.IsDebugging(mode))
                {
                    ErrorUtils.SendAlert(emailNotifier, null, ErrorUtils.ALERT_TYPE_WARNING, "Debug Log Entries");
                }

                // Send a response back to SFDC
                // Note: since we are not calling base class' SendAsync function, the request will return from here, and will not reach our POST function.
                return(Task.FromResult(receivedNotification.PrepareResponse(request)));
            }
            catch (Exception exception)
            {
                // Send the debug log if the user is running in debug mode
                if (SettingUtils.IsDebugging(mode))
                {
                    ErrorUtils.SendAlert(emailNotifier, null, ErrorUtils.ALERT_TYPE_WARNING, BaseHttpUtils.GetExceptionMessage(exception));
                }

                throw BaseHttpUtils.GetWebException(HttpStatusCode.BadRequest, BaseHttpUtils.GetExceptionMessage(exception));
            }
        }
예제 #3
0
        protected override System.Threading.Tasks.Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            WorkflowRuleNotification receivedNotification = null;
            IAuthenticatedWho        authenticatedWho     = null;
            INotifier emailNotifier = null;
            Guid      tenantGuid    = Guid.Empty;
            Guid      flowGuid      = Guid.Empty;
            String    tenantId      = null;
            String    flowId        = null;
            String    player        = null;
            String    mode          = null;
            String    email         = null;
            String    reportingMode = null;

            try
            {
                // Get the mode from the request uri
                mode = BaseHttpUtils.GetModeFromQuery(request.RequestUri);

                // Get the reporting mode from the request uri
                reportingMode = BaseHttpUtils.GetReportingModeFromQuery(request.RequestUri);

                // Get any provided notification email
                email = BaseHttpUtils.GetEmailFromQuery(request.RequestUri);

                // Check to make sure the incoming request has enough segments
                if (request.RequestUri.Segments.Length < 9)
                {
                    throw new ArgumentNullException("Request.Segments", "The incoming request is not a valid Url for outbound messages.");
                }

                // Get the segments from the call so we know which tenant we're executing against
                tenantId = request.RequestUri.Segments[7].Replace("/", "");
                flowId   = request.RequestUri.Segments[8].Replace("/", "");
                player   = request.RequestUri.Segments[9];

                // Check to make sure we've received valid guids
                if (Guid.TryParse(tenantId, out tenantGuid) == false)
                {
                    throw new ArgumentNullException("Request.Segments", "The incoming request does not contain a valid tenant identifier.");
                }

                if (Guid.TryParse(flowId, out flowGuid) == false)
                {
                    throw new ArgumentNullException("Request.Segments", "The incoming request does not contain a valid flow identifier.");
                }

                // If a player has not been provided, we make it the default player
                if (String.IsNullOrWhiteSpace(player) == true)
                {
                    player = "default";
                }

                // Create a basic authenticated who for the notifier
                authenticatedWho       = AuthenticationUtils.CreatePublicUser(tenantId);
                authenticatedWho.Email = email;

                // Create the notifier
                emailNotifier = EmailNotifier.GetInstance(tenantId, authenticatedWho, null, "WorkflowRuleMessageHandler");

                //ExtractData would populate notification class' variables, which can be used to get desired data.
                receivedNotification = new WorkflowRuleNotification();
                receivedNotification.ExtractData(emailNotifier, request.Content.ReadAsStringAsync().Result, mode);

                if (SettingUtils.IsDebugging(mode))
                {
                    emailNotifier.AddLogEntry("Mode: " + mode);
                }
                if (SettingUtils.IsDebugging(mode))
                {
                    emailNotifier.AddLogEntry("Email: " + email);
                }
                if (SettingUtils.IsDebugging(mode))
                {
                    emailNotifier.AddLogEntry("Reporting Mode: " + reportingMode);
                }

                // Execute the notifications against ManyWho
                this.Execute(emailNotifier, tenantId, flowId, player, mode, reportingMode, receivedNotification);

                // Send the debug log if the user is running in debug mode
                if (SettingUtils.IsDebugging(mode))
                {
                    ErrorUtils.SendAlert(emailNotifier, null, ErrorUtils.ALERT_TYPE_WARNING, "Debug Log Entries");
                }

                //Send a response back to SFDC
                //Note: since we are not calling base class' SendAsync function, the request will return from here, and will not reach our POST function.
                return(Task.FromResult(receivedNotification.PrepareResponse(request)));
            }
            catch (Exception exception)
            {
                // Send the debug log if the user is running in debug mode
                if (SettingUtils.IsDebugging(mode))
                {
                    ErrorUtils.SendAlert(emailNotifier, null, ErrorUtils.ALERT_TYPE_WARNING, BaseHttpUtils.GetExceptionMessage(exception));
                }

                throw BaseHttpUtils.GetWebException(HttpStatusCode.BadRequest, BaseHttpUtils.GetExceptionMessage(exception));
            }
        }
예제 #4
0
        private void Execute(INotifier notifier, String tenantId, String mode, WorkflowRuleNotification workflowRuleNotification)
        {
            Dictionary <String, ListenerServiceRequestAPI> salesforceListenerEntries = null;
            ListenerServiceResponseAPI listenerServiceResponse = null;
            ListenerServiceRequestAPI  listenerServiceRequest  = null;
            SforceService sforceService          = null;
            String        authenticationStrategy = null;
            String        authenticationUrl      = null;
            String        securityToken          = null;
            String        invokeType             = null;
            String        username = null;
            String        password = null;

            if (SettingUtils.IsDebugging(mode))
            {
                notifier.AddLogEntry("Executing listener notification.");
            }

            // Go through each object identifier in the notification
            if (workflowRuleNotification.ObjectIDs != null &&
                workflowRuleNotification.ObjectIDs.Count > 0)
            {
                if (SettingUtils.IsDebugging(mode))
                {
                    notifier.AddLogEntry("Workflow event has object identifiers.");
                }

                foreach (String objectId in workflowRuleNotification.ObjectIDs)
                {
                    if (SettingUtils.IsDebugging(mode))
                    {
                        notifier.AddLogEntry("Processing object identifier: " + objectId);
                    }

                    // Check to see if ManyWho has asked us to listen to any of them
                    salesforceListenerEntries = SalesforceListenerSingleton.GetInstance().GetListenerRequests(tenantId, objectId);

                    // Check to see if we're actually listening
                    if (salesforceListenerEntries != null &&
                        salesforceListenerEntries.Count > 0)
                    {
                        if (SettingUtils.IsDebugging(mode))
                        {
                            notifier.AddLogEntry("Object has listener entries.");
                        }

                        // If it has, send a listen notification back to the workflow engine - one by one at the moment (no bulk!)
                        foreach (KeyValuePair <String, ListenerServiceRequestAPI> pair in salesforceListenerEntries)
                        {
                            // Get the listener request out
                            listenerServiceRequest = pair.Value;

                            if (SettingUtils.IsDebugging(mode))
                            {
                                notifier.AddLogEntry("Executing listener entry.");
                            }

                            // Create the service response
                            listenerServiceResponse             = new ListenerServiceResponseAPI();
                            listenerServiceResponse.annotations = listenerServiceRequest.annotations;
                            listenerServiceResponse.culture     = listenerServiceRequest.culture;
                            listenerServiceResponse.tenantId    = listenerServiceRequest.tenantId;
                            listenerServiceResponse.token       = listenerServiceRequest.token;

                            // Apply the settings for the response from the request so the engine has the full details about the object
                            listenerServiceResponse.listeningEventValue                                  = listenerServiceRequest.valueForListening;
                            listenerServiceResponse.listeningEventValue.contentType                      = listenerServiceRequest.valueForListening.contentType;
                            listenerServiceResponse.listeningEventValue.contentValue                     = listenerServiceRequest.valueForListening.contentValue;
                            listenerServiceResponse.listeningEventValue.developerName                    = listenerServiceRequest.valueForListening.developerName;
                            listenerServiceResponse.listeningEventValue.typeElementDeveloperName         = listenerServiceRequest.valueForListening.typeElementDeveloperName;
                            listenerServiceResponse.listeningEventValue.typeElementId                    = listenerServiceRequest.valueForListening.typeElementId;
                            listenerServiceResponse.listeningEventValue.typeElementPropertyDeveloperName = listenerServiceRequest.valueForListening.typeElementPropertyDeveloperName;
                            listenerServiceResponse.listeningEventValue.typeElementPropertyId            = listenerServiceRequest.valueForListening.typeElementPropertyId;
                            listenerServiceResponse.listeningEventValue.valueElementId                   = listenerServiceRequest.valueForListening.valueElementId;

                            // Get the configuration values out that are needed to check the voting status
                            // TODO: we should smart cache the login info and connection
                            authenticationUrl      = ValueUtils.GetContentValue(SalesforceServiceSingleton.SERVICE_VALUE_AUTHENTICATION_URL, listenerServiceRequest.configurationValues, true);
                            username               = ValueUtils.GetContentValue(SalesforceServiceSingleton.SERVICE_VALUE_USERNAME, listenerServiceRequest.configurationValues, true);
                            password               = ValueUtils.GetContentValue(SalesforceServiceSingleton.SERVICE_VALUE_PASSWORD, listenerServiceRequest.configurationValues, true);
                            securityToken          = ValueUtils.GetContentValue(SalesforceServiceSingleton.SERVICE_VALUE_SECURITY_TOKEN, listenerServiceRequest.configurationValues, false);
                            authenticationStrategy = ValueUtils.GetContentValue(SalesforceServiceSingleton.SERVICE_VALUE_AUTHENTICATION_STRATEGY, listenerServiceRequest.configurationValues, false);

                            if (SettingUtils.IsDebugging(mode))
                            {
                                notifier.AddLogEntry("Logging into salesforce using: " + username);
                            }

                            if (String.IsNullOrWhiteSpace(authenticationStrategy) == true ||
                                authenticationStrategy.Equals(SalesforceServiceSingleton.AUTHENTICATION_STRATEGY_STANDARD, StringComparison.OrdinalIgnoreCase) == true ||
                                authenticationStrategy.Equals(SalesforceServiceSingleton.AUTHENTICATION_STRATEGY_ACTIVE_USER, StringComparison.OrdinalIgnoreCase) == true)
                            {
                                sforceService = SalesforceDataSingleton.GetInstance().LogUserInBasedOnSession(listenerServiceRequest.configurationValues, workflowRuleNotification.SessionID, workflowRuleNotification.SessionURL);
                            }
                            else if (authenticationStrategy.Equals(SalesforceServiceSingleton.AUTHENTICATION_STRATEGY_SUPER_USER, StringComparison.OrdinalIgnoreCase) == true)
                            {
                                // Login to salesforce using the details in the service request
                                sforceService = SalesforceDataSingleton.GetInstance().LoginUsingCredentials(authenticationUrl, username, password, securityToken);
                            }
                            else
                            {
                                throw new ArgumentNullException("ConfigurationValues", String.Format("The provided authentication strategy is not supported: '{0}'", authenticationStrategy));
                            }

                            if (SettingUtils.IsDebugging(mode))
                            {
                                notifier.AddLogEntry("Getting full sObject for: " + objectId);
                            }

                            // Load the latest object from salesforce so we have the data to send back
                            listenerServiceResponse.listeningEventValue.objectData = SalesforceDataSingleton.GetInstance().LoadSObjectByIdentifier(sforceService, workflowRuleNotification.ObjectName, objectId, true);

                            try
                            {
                                if (SettingUtils.IsDebugging(mode))
                                {
                                    notifier.AddLogEntry("Executing event against ManyWho");
                                }

                                // Dispatch a listen response to the engine as an event has occurred
                                invokeType = RunSingleton.GetInstance().Event(notifier, null, tenantId, listenerServiceRequest.callbackUri, listenerServiceResponse);
                            }
                            catch (Exception exception)
                            {
                                if (SettingUtils.IsDebugging(mode))
                                {
                                    notifier.AddLogEntry("Event execution failed with: " + BaseHttpUtils.GetExceptionMessage(exception));
                                }

                                // Something went wrong - but we ignore it for now
                                invokeType = ManyWhoConstants.INVOKE_TYPE_FORWARD;
                            }

                            if (SettingUtils.IsDebugging(mode))
                            {
                                notifier.AddLogEntry("Service returned invoke type of: " + invokeType);
                            }

                            // If the engine returns nothing, errors or returns an response other than a "WAIT", we delete the listener for now
                            // TODO: make this a bit more intelligent so we can handle things like retry
                            if (String.IsNullOrWhiteSpace(invokeType) == false &&
                                invokeType.IndexOf(ManyWhoConstants.INVOKE_TYPE_WAIT, StringComparison.InvariantCultureIgnoreCase) < 0)
                            {
                                if (SettingUtils.IsDebugging(mode))
                                {
                                    notifier.AddLogEntry("Removing entry from listeners for invoke type: " + invokeType);
                                }

                                SalesforceListenerSingleton.GetInstance().UnregisterListener(tenantId, objectId, listenerServiceRequest);
                            }
                        }
                    }
                }
            }
        }
예제 #5
0
        public void Execute(INotifier notifier, String tenantId, String flowId, String player, String mode, String reportingMode, WorkflowRuleNotification workflowRuleNotification)
        {
            FlowResponseAPI                 flowResponse                 = null;
            EngineInvokeRequestAPI          engineInvokeRequest          = null;
            EngineInvokeResponseAPI         engineInvokeResponse         = null;
            EngineInitializationRequestAPI  engineInitializationRequest  = null;
            EngineInitializationResponseAPI engineInitializationResponse = null;
            AuthenticationCredentialsAPI    authenticationCredentials    = null;
            String authenticationToken = null;

            if (SettingUtils.IsDebugging(mode))
            {
                notifier.AddLogEntry("Executing notification.");
            }

            // Check to see if we have object identifiers to process
            if (workflowRuleNotification.ObjectIDs != null &&
                workflowRuleNotification.ObjectIDs.Count > 0)
            {
                if (SettingUtils.IsDebugging(mode))
                {
                    notifier.AddLogEntry("Notification has object identifiers.");
                }
                if (SettingUtils.IsDebugging(mode))
                {
                    notifier.AddLogEntry(String.Format("Loading flow for tenant ({0}) and identifier ({1}).", tenantId, flowId));
                }

                // Now we have the data from the message, we can execute the workflow
                // Load the flow by the unique identifier
                flowResponse = RunSingleton.GetInstance().LoadFlowById(notifier, authenticationToken, tenantId, flowId);

                // Check to make sure we have a flow response
                if (flowResponse == null)
                {
                    throw new ArgumentNullException("FlowResponse", "The flow is null for the provided tenant and flow identifier.");
                }

                foreach (String objectID in workflowRuleNotification.ObjectIDs)
                {
                    if (SettingUtils.IsDebugging(mode))
                    {
                        notifier.AddLogEntry("Sending initialization request to ManyWho.");
                    }

                    // Create an engine initialization request to kick off the flow
                    engineInitializationRequest                  = new EngineInitializationRequestAPI();
                    engineInitializationRequest.flowId           = new FlowIdAPI();
                    engineInitializationRequest.flowId.id        = flowResponse.id.id;
                    engineInitializationRequest.flowId.versionId = flowResponse.id.versionId;
                    engineInitializationRequest.mode             = mode;
                    engineInitializationRequest.reportingMode    = reportingMode;

                    // If we're not using the default player, change the urls
                    if (player != "default")
                    {
                        engineInitializationRequest.joinPlayerUrl = "https://flow.manywho.com/" + tenantId + "/play/" + player;
                        engineInitializationRequest.playerUrl     = "https://flow.manywho.com/" + tenantId + "/play/" + player;
                    }

                    // Initialize the workflow with the values provided
                    engineInitializationRequest.inputs = new List <EngineValueAPI>();
                    engineInitializationRequest.inputs.Add(new EngineValueAPI()
                    {
                        developerName = "SalesforceNotificationRecordId", contentValue = objectID, contentType = ManyWhoConstants.CONTENT_TYPE_STRING
                    });
                    engineInitializationRequest.inputs.Add(new EngineValueAPI()
                    {
                        developerName = "SalesforceNotificationObjectName", contentValue = workflowRuleNotification.ObjectName, contentType = ManyWhoConstants.CONTENT_TYPE_STRING
                    });

                    if (SettingUtils.IsDebugging(mode))
                    {
                        notifier.AddLogEntry("SalesforceNotificationRecordId: " + objectID);
                    }
                    if (SettingUtils.IsDebugging(mode))
                    {
                        notifier.AddLogEntry("SalesforceNotificationObjectName: " + workflowRuleNotification.ObjectName);
                    }

                    // Initialize the engine with the bare basics
                    engineInitializationResponse = RunSingleton.GetInstance().Initialize(notifier, authenticationToken, tenantId, engineInitializationRequest);

                    // Check to see if the workflow is authorized to execute - if not, we need to login using the session
                    if (engineInitializationResponse.statusCode.Equals(ManyWhoConstants.AUTHORIZATION_STATUS_NOT_AUTHORIZED, StringComparison.OrdinalIgnoreCase) == true)
                    {
                        if (SettingUtils.IsDebugging(mode))
                        {
                            notifier.AddLogEntry("Event not authorized, attempting a login using session info.");
                        }
                        if (SettingUtils.IsDebugging(mode))
                        {
                            notifier.AddLogEntry("SessionId: " + workflowRuleNotification.SessionID);
                        }
                        if (SettingUtils.IsDebugging(mode))
                        {
                            notifier.AddLogEntry("SessionURL: " + workflowRuleNotification.SessionURL);
                        }

                        // Create the authentication credentials for the service
                        authenticationCredentials              = new AuthenticationCredentialsAPI();
                        authenticationCredentials.loginUrl     = engineInitializationResponse.authorizationContext.loginUrl;
                        authenticationCredentials.sessionToken = workflowRuleNotification.SessionID;
                        authenticationCredentials.sessionUrl   = workflowRuleNotification.SessionURL;

                        if (SettingUtils.IsDebugging(mode))
                        {
                            notifier.AddLogEntry("Logging into service again using session info.");
                        }

                        // Login to the system
                        authenticationToken = RunSingleton.GetInstance().Login(notifier, tenantId, engineInitializationResponse.stateId, authenticationCredentials);

                        // Apply the state back
                        engineInitializationRequest.stateId = engineInitializationResponse.stateId;

                        if (SettingUtils.IsDebugging(mode))
                        {
                            notifier.AddLogEntry("Initializing engine again for state identifier: " + engineInitializationResponse.stateId);
                        }

                        // Initialize the engine again - re-using the state identifier
                        engineInitializationResponse = RunSingleton.GetInstance().Initialize(notifier, authenticationToken, tenantId, engineInitializationRequest);
                    }

                    // Now create the fist engine invoke request so we can get the content of the first ivr
                    engineInvokeRequest = new EngineInvokeRequestAPI();
                    engineInvokeRequest.currentMapElementId     = engineInitializationResponse.currentMapElementId;
                    engineInvokeRequest.invokeType              = ManyWhoConstants.INVOKE_TYPE_FORWARD;
                    engineInvokeRequest.mapElementInvokeRequest = new MapElementInvokeRequestAPI();
                    engineInvokeRequest.currentMapElementId     = engineInitializationResponse.currentMapElementId;
                    engineInvokeRequest.stateId    = engineInitializationResponse.stateId;
                    engineInvokeRequest.stateToken = engineInitializationResponse.stateToken;
                    engineInvokeRequest.mode       = mode;

                    if (SettingUtils.IsDebugging(mode))
                    {
                        notifier.AddLogEntry("Sending invoke request to ManyWho.");
                    }

                    // Invoke the engine with the first request
                    engineInvokeResponse = RunSingleton.GetInstance().Execute(notifier, authenticationToken, tenantId, engineInvokeRequest);

                    // If we're running in step through mode, we notify the author with the join identifier so they can debug the workflow
                    if (SettingUtils.IsDebugging(engineInvokeRequest.mode) == true &&
                        engineInvokeRequest.mode.Equals(ManyWhoConstants.MODE_DEBUG_STEPTHROUGH, StringComparison.OrdinalIgnoreCase) == true)
                    {
                        notifier.AddLogEntry("Flow is waiting to be joined in order to be debugged.");
                        notifier.AddLogEntry("JoinUrl: " + engineInvokeResponse.joinFlowUri + "&mode=" + engineInvokeRequest.mode);
                    }
                }
            }
            else
            {
                if (SettingUtils.IsDebugging(mode))
                {
                    notifier.AddLogEntry("Notification does not have object identifiers.");
                }
            }
        }