Esempio n. 1
0
        private BeholdEmailer CreateBeholdEmailerFromConfig(string site)
        {
            var tabrep = new TableauRepository(
                Configurator.GetConfig("tableau_server"),
                Configurator.GetConfig("repository_pw"),
                "readonly"
                )
            {
                logger = this.Logger
            };
            var tabcmd = new Tabcmd(
                Configurator.GetConfig("tabcmd_program_location"),
                Configurator.GetConfig("tableau_server"),
                Configurator.GetConfig("server_admin_username"),
                Configurator.GetConfig("server_admin_password"),
                site,
                Configurator.GetConfig("tabcmd_config_location"),
                tabrep,
                this.Logger
                );

            BeholdEmailer tabemailer = new BeholdEmailer(tabcmd, this.CreateSmtpClientFromConfig());

            tabemailer.Logger = this.Logger;
            tabemailer.HtmlEmailTemplateFilename = Configurator.GetConfig("html_email_template_filename");
            tabemailer.TextEmailTemplateFilename = Configurator.GetConfig("text_email_template_filename");
            return(tabemailer);
        }
Esempio n. 2
0
        private void loadSubscriptionsFromRepository()
        {
            try
            {
                TableauRepository rep = new TableauRepository(Configurator.GetConfig("tableau_server"),
                                                              Configurator.GetConfig("repository_pw"), "readonly");
                rep.logger = this.logger;
                NpgsqlDataReader         dr = rep.query_inactive_subscription_schedules();
                Dictionary <string, int> input_box_schedules = new Dictionary <string, int>();
                int rowCount = 0;
                availableSchedulesList.Items.Clear();
                if (dr.HasRows == true)
                {
                    while (dr.Read())
                    {
                        availableSchedulesList.Items.Add(dr.GetString(1));
                        input_box_schedules[dr.GetString(1)] = rowCount;
                        rowCount++;
                    }
                }
                dr.Close();

                // Check the selected schedules from the configs
                StringCollection checked_schedules = Configurator.GetConfigCollection("selected_schedule_names");
                if (checked_schedules != null)
                {
                    foreach (String sched_name in checked_schedules)
                    {
                        availableSchedulesList.SetItemChecked(input_box_schedules[sched_name], true);
                    }
                }
            }
            catch (NpgsqlException ne)
            {
                this.logger.Log("Error with repository while loading the available schedules. Press reload button to try again. Going with existing checked schedules for now");
                this.logger.Log(ne.Message);

                // Fill in the checkbox list based on the existing selected schedules
                StringCollection checked_schedules = Configurator.GetConfigCollection("selected_schedule_names");
                if (checked_schedules != null)
                {
                    var i = 0;
                    Dictionary <string, int> input_box_schedules = new Dictionary <string, int>();
                    availableSchedulesList.Items.Clear();
                    foreach (String sched_name in checked_schedules)
                    {
                        availableSchedulesList.Items.Add(sched_name);
                        input_box_schedules[sched_name] = i;
                        availableSchedulesList.SetItemChecked(input_box_schedules[sched_name], true);
                        i++;
                    }
                }
            }

            catch (ConfigurationException ce)
            {
                this.logger.Log("Incorrect credentials for repository readonly user. Cannot connect to repository.");
                MessageBox.Show("Credentials were not correct for the Repository \"readonly\" user. Please fix");
            }
        }
Esempio n. 3
0
        private string GenerateSingleExport(string exportSite, string exportUsername, string exportViewLocation, string exportAttachmentType, string exportFilename, Dictionary <string, string> viewFilterDictionary)
        {
            // Generate Single File
            try
            {
                TableauRepository tabrep = new TableauRepository(
                    Configurator.GetConfig("tableau_server"),
                    Configurator.GetConfig("repository_pw"),
                    "readonly"
                    );
                tabrep.logger = this.Logger;

                Tabcmd tabcmd = new Tabcmd(
                    Configurator.GetConfig("tabcmd_program_location"),
                    Configurator.GetConfig("tableau_server"),
                    Configurator.GetConfig("server_admin_username"),
                    Configurator.GetConfig("server_admin_password"),
                    exportSite,
                    Configurator.GetConfig("tabcmd_config_location"),
                    tabrep,
                    this.Logger
                    );
                // Emailer here is used because the Watermarking is built in there. Would it make more sense to move it to Tabcmd eventually, or its own class?
                BeholdEmailer tabemailer    = new BeholdEmailer(tabcmd, Configurator.GetConfig("smtp_server"));
                Watermarker   wm            = new Watermarker();
                string[]      pageLocations = { "top_left", "top_center", "top_right", "bottom_left", "bottom_center", "bottom_right" };
                foreach (string pageLocation in pageLocations)
                {
                    string settingsPageLocation = pageLocation.Split('_')[0] + pageLocation.Split('_')[1].First() + pageLocation.Split('_')[1].Substring(1);
                    wm.SetPageLocationWatermarkFromConfig(pageLocation, Configurator.GetConfigSerializableStringDict(settingsPageLocation));
                }

                string filename = tabemailer.GenerateExportAndWatermark(exportUsername, exportViewLocation,
                                                                        exportAttachmentType, viewFilterDictionary, wm);
                string[] fileEnding = filename.Split('.');

                string finalFilename = String.Format("{0}{1}.{2}", Configurator.GetConfig("export_archive_folder"), exportFilename, fileEnding[fileEnding.Length - 1]);

                this.Logger.Log(String.Format("Finalizing file and putting it here: {0}", finalFilename));
                File.Copy(filename, finalFilename, true);
                this.Logger.Log(String.Format("Removing original file {0}", filename));
                File.Delete(filename);

                return("Finished single export file creation. Saved to: " + finalFilename);
            }
            catch (ConfigurationException ce)
            {
                //progress.finish_progress_bar(33);
                // progress.update_status("Export failed for some reason, most likely bad settings.\nCheck logs for more info");
                this.Logger.Log(ce.Message);
                return("Single export failed. Please see logs for more information.");
            }
        }
Esempio n. 4
0
        /*
         * Test Page Methods
         */


        private void create_test_export(object sender, EventArgs e)
        {
            // Needs validation here
            TextBox[] elements_to_validate = { testFileSaveLocation, testFilename, testSite, testUsernameForImpersonation, testViewLocation };
            bool      validation           = this.validate_set_of_elements(elements_to_validate);

            if (validation == false)
            {
                return;
            }
            Progress progress = new Progress(0, "Creating test file");

            progress.Show(this);
            progress.Update();
            try
            {
                TableauRepository tabrep = new TableauRepository(tableau_server_url.Text, repositoryPW.Text, "readonly");
                tabrep.logger = this.logger;
                Tabcmd tabcmd = new Tabcmd(tabcmdProgramLocation.Text, tableau_server_url.Text, server_admin_username.Text, server_password.Text, testSite.Text,
                                           tabcmdConfigLocation.Text, tabrep, this.logger);

                // Emailer here is used because the Watermarking is built in there. Would it make more sense to move it to Tabcmd eventually, or its own class?
                BeholdEmailer tabemailer     = new BeholdEmailer(tabcmd, emailServer.Text);
                Watermarker   wm             = new Watermarker();
                string[]      page_locations = { "top_left", "top_center", "top_right", "bottom_left", "bottom_center", "bottom_right" };
                foreach (string page_location in page_locations)
                {
                    string settingsPageLocation = page_location.Split('_')[0] + page_location.Split('_')[1].First() + page_location.Split('_')[1].Substring(1);
                    wm.setPageLocationWatermarkFromConfig(page_location, Configurator.GetConfigSerializableStringDict(settingsPageLocation));
                }

                string   filename    = tabemailer.generate_export_and_watermark(testUsernameForImpersonation.Text, testViewLocation.Text, "fullpdf", new Dictionary <string, string>(), wm);
                string[] file_ending = filename.Split('.');

                string final_filename = String.Format("{0}{1}.{2}", testFileSaveLocation.Text, testFilename.Text, file_ending[file_ending.Length - 1]);

                this.logger.Log(String.Format("Finalizing file and putting it here: {0}", final_filename));
                File.Copy(filename, final_filename, true);
                this.logger.Log(String.Format("Removing original file {0}", filename));
                File.Delete(filename);
                progress.finish_progress_bar(100);
                progress.update_status("Export created successfully!");
            }
            catch (ConfigurationException ce)
            {
                progress.finish_progress_bar(33);
                progress.update_status("Export failed for some reason, most likely bad settings.\nCheck logs for more info");
                this.logger.Log(ce.Message);
            }
        }
Esempio n. 5
0
        private BeholdEmailer create_behold_emailer_from_config(string site)
        {
            TableauRepository tabrep = new TableauRepository(tableau_server_url.Text, repositoryPW.Text, "readonly");

            tabrep.logger = this.logger;
            Tabcmd tabcmd = new Tabcmd(tabcmdProgramLocation.Text, tableau_server_url.Text, server_admin_username.Text, server_password.Text, site,
                                       tabcmdConfigLocation.Text, tabrep, this.logger);


            BeholdEmailer tabemailer = new BeholdEmailer(tabcmd, this.create_smtp_client_from_config());

            tabemailer.logger = this.logger;
            tabemailer.html_email_template_filename = htmlEmailFilename.Text;
            tabemailer.text_email_template_filename = textEmailFilename.Text;
            return(tabemailer);
        }
Esempio n. 6
0
        public Tabcmd(string tabcmd_folder, string tableau_server_url, string username, string password,
                      string site, string tabcmd_config_location, TableauRepository TableauRepository)
        {
            this.tabcmd_folder          = tabcmd_folder;
            this.username               = username;
            this.password               = password;
            this.site                   = site;
            this.tableau_server_url     = tableau_server_url;
            this.tabcmd_config_filename = "tabcmd-session.xml";
            this.tabcmd_config_location = tabcmd_config_location;

            this.repository = TableauRepository;

            // This preps tabcmd for subsequent calls
            this.log("Preping the tabcmd admin session");
            this.create_tabcmd_admin_session();
            this.logger = null;
        }
Esempio n. 7
0
        public Tabcmd(string tabcmd_folder, string tableau_server_url, string username, string password,
                      string site, string tabcmd_config_location, TableauRepository TableauRepository)
        {
            this.tabcmdFolderLocation = tabcmd_folder;
            this.username             = username;
            this.password             = password;
            this.Site                 = site;
            this.TableauServerUrl     = tableau_server_url;
            this.tabcmdConfigFilename = "tabcmd-session.xml";
            this.tabcmdConfigLocation = tabcmd_config_location;

            this.Repository = TableauRepository;

            // This preps tabcmd for subsequent calls
            this.Log("Preping the tabcmd admin session");
            this.CreateTabcmdAdminSession();
            this.Logger = null;
        }
Esempio n. 8
0
        public Tabcmd(string tabcmdFolderLocation, string tableauServerUrl,
                      string username, string password, string site, string repositoryPassword,
                      string tabcmdConfigurationFileLocation)
        {
            // tabcmd program configurations are stored within an XML file
            this.tabcmdFolderLocation = tabcmdFolderLocation;
            this.tabcmdConfigFilename = "tabcmd-session.xml";
            this.tabcmdConfigLocation = tabcmdConfigurationFileLocation;
            // Open the configuration file and test whether it resembles the real file.
            // Should really replace with XML reader to make this better, but in the end this part needs to be deprecated for the APIs
            try
            {
                StreamReader tabcmdConfigurationFile = new StreamReader(tabcmdConfigurationFileLocation + tabcmdConfigFilename);
                // Read first line
                tabcmdConfigurationFile.ReadLine();
                // Read second line, should be <session>. May come up with better test
                string second_line = tabcmdConfigurationFile.ReadLine();
                if (second_line.Contains("<session>") == false)
                {
                    throw new ConfigurationException("tabcmd-session.xml file information is incorrect. File is not tabcmd-session.xml");
                }
                tabcmdConfigurationFile.Close();
            }
            catch (IOException)
            {
                throw new ConfigurationException("tabcmd-config file information is incorrect. Config file could not be opened");
            }

            this.username         = username;
            this.password         = password;
            this.Site             = site;
            this.TableauServerUrl = tableauServerUrl;

            this.repositoryPassword = repositoryPassword;
            this.Repository         = new TableauRepository(this.TableauServerUrl, this.repositoryPassword, "readonly");

            // This preps tabcmd for subsequent calls
            this.Log("Preping the tabcmd admin session");
            this.CreateTabcmdAdminSession();
            this.Logger = null;
        }
Esempio n. 9
0
        public Tabcmd(string tabcmd_folder, string tableau_server_url,
                      string username, string password, string site, string repository_password,
                      string tabcmd_config_location)
        {
            // Tabcmd program configurations
            this.tabcmd_folder          = tabcmd_folder;
            this.tabcmd_config_filename = "tabcmd-session.xml";
            this.tabcmd_config_location = tabcmd_config_location;
            // Open the configuration file and test whether it resembles the real file
            try {
                StreamReader tabcmd_config = new StreamReader(tabcmd_config_location + tabcmd_config_filename);
                // Read first line
                tabcmd_config.ReadLine();
                // Read second line, should be <session>. May come up with better test
                string second_line = tabcmd_config.ReadLine();
                if (second_line.Contains("<session>") == false)
                {
                    throw new ConfigurationException("tabcmd-session.xml file information is incorrect. File is not tabcmd-session.xml");
                }
                tabcmd_config.Close();
            }
            catch (IOException) {
                throw new ConfigurationException("tabcmd-config file information is incorrect. Config file could not be opened");
            }

            this.username           = username;
            this.password           = password;
            this.site               = site;
            this.tableau_server_url = tableau_server_url;

            this.repository_pw = repository_password;
            this.repository    = new TableauRepository(this.tableau_server_url, this.repository_pw, "readonly");

            // This preps tabcmd for subsequent calls
            this.log("Preping the tabcmd admin session");
            this.create_tabcmd_admin_session();
            this.logger = null;
        }
Esempio n. 10
0
        /*
         * Schedules Tab
         * */
        private void runSchedules_TimedEvent(Object source, EventArgs e)
        {
            // Only run on one minute after every 15
            if (DateTime.Now.Minute == 1 || DateTime.Now.Minute == 16 || DateTime.Now.Minute == 31 || DateTime.Now.Minute == 46)
            {
                this.logger.Log("Schedule checks begin");
                this.scheduledEmailsStatus.Text = "Scheduled Emails are running";
                try
                {
                    // Read the schedules that are currently in the repository, to update the active schedule queue
                    TableauRepository rep = new TableauRepository(Configurator.GetConfig("tableau_server"),
                                                                  Configurator.GetConfig("repository_pw"), "readonly");
                    rep.logger = this.logger;

                    NpgsqlDataReader dr = rep.query_inactive_subscription_schedules_for_next_run_time();
                    if (dr.HasRows == true)
                    {
                        using (StreamWriter active_schedules_file = new StreamWriter(this.active_schedules_queue_filename, false, new UTF8Encoding()))
                        {
                            while (dr.Read())
                            {
                                string sched_name = dr.GetString(0);
                                string sched_next_run_time;

                                //Although server time is usually UTC, the time is later compared with current time converted to UTC too.
                                DateTime serverTime = dr.GetDateTime(1);
                                sched_next_run_time = serverTime.ToString();

                                this.logger.Log(String.Format("Next scheduled run time (UTC) is {0}", sched_next_run_time));

                                // Only add if the sched_name is in the checked schedules
                                int      num_checked_items      = availableSchedulesList.CheckedItems.Count;
                                string[] checked_schedule_names = new string[num_checked_items];
                                var      k = 0;
                                foreach (object itemChecked in availableSchedulesList.CheckedItems)
                                {
                                    checked_schedule_names[k] = itemChecked.ToString();
                                    k++;
                                }

                                string[] sched = new string[2] {
                                    sched_name, sched_next_run_time
                                };
                                bool schedule_exists_in_queue = false;
                                foreach (string[] queued_sched in this.active_schedules)
                                {
                                    if (queued_sched[0] == sched_name && queued_sched[1] == sched_next_run_time)
                                    {
                                        schedule_exists_in_queue = true;
                                        this.logger.Log(String.Format("Schedule {0} at {1} already exists in queue", sched_name, sched_next_run_time));
                                    }
                                }
                                if (schedule_exists_in_queue == false)
                                {
                                    // Only add checked schedule names
                                    if (checked_schedule_names.Any(sched.Contains) == true)
                                    {
                                        this.active_schedules.Add(sched);
                                        this.logger.Log(String.Format("Schedule {0} at {1} added to the queue", sched_name, sched_next_run_time));
                                        active_schedules_file.WriteLine(String.Format("{0}|{1}", sched_name, sched_next_run_time));
                                    }
                                }
                            }
                        }
                    }
                    dr.Close();
                }
                catch (NpgsqlException ne)
                {
                    this.logger.Log("Connecting to repository to update the active queue failed. Ignoring for now, will update at next interval");
                    this.logger.Log(ne.Message);
                }

                // Look at the Active Queue and determine if anything is past it's due date
                foreach (string[] queued_sched in this.active_schedules.Reverse <string[]>())
                {
                    if (DateTime.Now.ToUniversalTime() > DateTime.Parse(queued_sched[1]))
                    {
                        this.logger.Log(String.Format("Schedule {0} at {1} is now eligible to be run.", queued_sched[0], queued_sched[1]));
                        bool result = send_email(queued_sched[0]);
                        if (result == true)
                        {
                            this.logger.Log(String.Format("Schedule {0} at {1} has run successfully. Removing from queue", queued_sched[0], queued_sched[1]));
                        }
                        else
                        {
                            //Suppose a schedule contains multiple subscriptions and one of them fails, the original app will send pdf to everyone else every 15 minutes.
                            //This prevents it by removing unsuccessful sessions.
                            this.logger.Log(String.Format("Schedule {0} at {1} failed (maybe partially). Removing from queue", queued_sched[0], queued_sched[1]));
                        }
                        // Write completed schedules into the schedule_run log
                        this.completed_schedules_file.WriteLine(String.Format("{0}|{1}", queued_sched[0], queued_sched[1]));
                        this.active_schedules.Remove(queued_sched);
                    }
                }
                // Write the queues to the active_schedules files
                using (StreamWriter active_schedules_file = new StreamWriter(this.active_schedules_queue_filename, false, new UTF8Encoding()))
                {
                    foreach (string[] queued_sched in this.active_schedules)
                    {
                        active_schedules_file.WriteLine(String.Format("{0}|{1}", queued_sched[0], queued_sched[1]));
                    }
                }

                this.logger.Log("Schedule check completed successfully");
                this.scheduledEmailsStatus.Text = "Waiting to Run Next Scheduled Emails";
            }
        }
Esempio n. 11
0
        private void updateSchedulesQueue()
        {
            var activityId = this.nextActivityId;

            this.nextActivityId++;
            WriteToActivityGrid("Checking for schedules to run in the repository", activityId);
            this.Logger.Log("Schedule checks begin");
            try
            {
                // Read the schedules that are currently in the repository, to update the active schedule queue
                TableauRepository rep = new TableauRepository(Configurator.GetConfig("tableau_server"),
                                                              Configurator.GetConfig("repository_pw"), "readonly");
                rep.logger = this.Logger;
                //this.Logger.Log("Starting to read from the repository");
                NpgsqlDataReader dr = rep.QueryInactiveSubscriptionSchedulesForNextRunTime();
                if (dr.HasRows == true)
                {
                    //this.Logger.Log("Opening the active schedules queue file");
                    using (StreamWriter activeSchedulesQueueFileWriter = new StreamWriter(this.ActiveSchedulesQueueFilename, false, new UTF8Encoding()))
                    {
                        //this.Logger.Log("Reading from the result set ");
                        while (dr.Read())
                        {
                            // this.Logger.Log("Working inside the result set ");
                            string scheduleName        = dr.GetString(0);
                            string scheduleNextRunTime = dr.GetDateTime(1).ToString();
                            //this.Logger.Log(String.Format("Schedule here {0} {1}", scheduleName, scheduleNextRunTime));

                            // Because the selections are stored instantly, load from the saved collectionr rather than the control
                            // It is possible the control has not been redrawn at the time
                            List <string> selectedScheduleNames = new List <string>()
                            {
                            };
                            StringCollection savedSchedules = Configurator.GetConfigCollection("selected_schedule_names");
                            if (savedSchedules != null)
                            {
                                foreach (String savedScheduleName in savedSchedules)
                                {
                                    selectedScheduleNames.Add(savedScheduleName);
                                }
                            }

                            string[] scheduleInfo = new string[2] {
                                scheduleName, scheduleNextRunTime
                            };
                            bool doesScheduleExistInQueue = false;
                            // See if what is in the activeSchedulesQueue file is a checked schedule still
                            foreach (string[] activeSchedule in this.ActiveSchedules)
                            {
                                if (activeSchedule[0] == scheduleName && activeSchedule[1] == scheduleNextRunTime)
                                {
                                    this.Logger.Log(String.Format("Schedule {0} at {1} already in the schedules queue", scheduleName, scheduleNextRunTime));
                                    doesScheduleExistInQueue = true;
                                }
                            }
                            if (doesScheduleExistInQueue == false)
                            {
                                // Only add checked schedule names
                                if (selectedScheduleNames.Any(scheduleInfo.Contains) == true)
                                {
                                    this.ActiveSchedules.Add(scheduleInfo);
                                    WriteToActivityGrid(String.Format("Schedule {0} at {1} added to the schedules queue", scheduleName, scheduleNextRunTime), activityId);
                                    this.Logger.Log(String.Format("Schedule {0} at {1} added to the schedules queue", scheduleName, scheduleNextRunTime));
                                    activeSchedulesQueueFileWriter.WriteLine(String.Format("{0}|{1}", scheduleName, scheduleNextRunTime));
                                }
                                else
                                {
                                    this.Logger.Log(String.Format("Schedule {0} at {1} not added to schedules queue because it is not selected", scheduleName, scheduleNextRunTime));
                                }
                            }
                        }
                    }
                }
                else
                {
                    this.Logger.Log("No rows found in the query for new schedules");
                }
                dr.Close();
            }
            catch (NpgsqlException ne)
            {
                this.Logger.Log("Connecting to repository to update the active queue failed. Ignoring for now, will update at next interval");
                this.Logger.Log(ne.Message);
            }
            WriteToActivityGrid("Completed checking for new schedules", activityId);

            // Look at the Active Queue and determine if anything is past it's due date
            foreach (string[] queuedSchedule in this.ActiveSchedules.Reverse <string[]>())
            {
                if (DateTime.Now.ToUniversalTime() > DateTime.Parse(queuedSchedule[1]))
                {
                    activityId = this.nextActivityId;
                    this.nextActivityId++;
                    this.Logger.Log(String.Format("Schedule {0} at {1} is now eligible to be run.", queuedSchedule[0], queuedSchedule[1]));
                    WriteToActivityGrid(String.Format("Schedule {0} at {1} is now eligible to be run. Queuing to run.", queuedSchedule[0], queuedSchedule[1]), activityId);
                    // Add to the actionQueue at the top
                    asyncActionQueue.Insert(0, () =>
                    {
                        var queuedScheduleName    = queuedSchedule[0];
                        var queuedScheduleRunTime = queuedSchedule[1];
                        WriteToActivityGrid(String.Format("Running Schedule {0} at {1}.", queuedSchedule[0], queuedSchedule[1]), activityId);
                        bool result = SendEmail(queuedScheduleName);

                        // Even if failure, remove from active schedules queue
                        this.Logger.Log(String.Format("Removing {0} at {1} from active schedules queue", queuedSchedule[0], queuedSchedule[1]));
                        this.ActiveSchedules.Remove(queuedSchedule);
                        // Write the remaining queues.
                        using (StreamWriter activeSchedulesQueueFileWriter = new StreamWriter(this.ActiveSchedulesQueueFilename, false, new UTF8Encoding()))
                        {
                            foreach (string[] currentlyQueuedSchedules in this.ActiveSchedules)
                            {
                                activeSchedulesQueueFileWriter.WriteLine(String.Format("{0}|{1}", currentlyQueuedSchedules[0], currentlyQueuedSchedules[1]));
                            }
                        }

                        if (result)
                        {
                            WriteToActivityGrid(String.Format("Completed Schedule {0} at {1}.", queuedScheduleName, queuedScheduleRunTime), activityId);
                            return(String.Format("Completed Schedule {0} at {1}.", queuedScheduleName, queuedScheduleRunTime));
                        }
                        else
                        {
                            this.ActiveSchedules.Remove(queuedSchedule);
                            WriteToActivityGrid(String.Format("Schedule {0} at {1} failed, see log.", queuedScheduleName, queuedScheduleRunTime), activityId);
                            return(String.Format("Schedule {0} at {1} failed, see log.", queuedScheduleName, queuedScheduleRunTime));
                        }
                    }
                                            );

                    // Launch the next action in the queue, if possible
                    try
                    {
                        actionQueueBackgroundWorker.RunWorkerAsync(asyncActionQueue[0]);
                    }
                    // The queue response will launch the next action once it is finished
                    catch (InvalidOperationException)
                    {
                        this.Logger.Log("Action queued but other action currently running");
                    }
                }
            }
        }