public async Task <ApiValidationResult> ResumeJob(string tenantId, string jobId)
        {
            var jobData = await JobStore.Load(tenantId, jobId);

            if (jobData == null)
            {
                return(ApiValidationResult.Failure(ErrorKeys.InvalidJobId));
            }

            if (jobData.Status.State == JobState.InProgress)
            {
                return(ApiValidationResult.Ok());
            }

            var changeableStates = new[] { JobState.Draining, JobState.Paused };

            if (changeableStates.Any(s => s == jobData.Status.State))
            {
                var updated = await JobStore.UpdateState(tenantId, jobId, jobData.Status.State, JobState.InProgress);

                if (updated)
                {
                    await JobNotification.NotifyJobUpdated(jobId);

                    return(ApiValidationResult.Ok());
                }
            }

            return(ApiValidationResult.Failure(ErrorKeys.InvalidJobState));
        }
        public async Task <ApiValidationResult> StopJob(string tenantId, string jobId)
        {
            var jobData = await JobStore.Load(tenantId, jobId);

            if (jobData == null)
            {
                return(ApiValidationResult.Failure(ErrorKeys.InvalidJobId));
            }

            if (jobData.Status.State == JobState.Stopped)
            {
                return(ApiValidationResult.Ok());
            }

            if (jobData.Configuration.IsIndefinite)
            {
                return(ApiValidationResult.Failure(ErrorKeys.InvalidJobAction));
            }

            if (jobData.Configuration.PreprocessorJobIds.SafeAny())
            {
                foreach (var preprocessorJobId in jobData.Configuration.PreprocessorJobIds)
                {
                    var preprocessorJobStatus = await JobStore.LoadStatus(tenantId, preprocessorJobId);

                    if (preprocessorJobStatus.State < JobState.Completed)
                    {
                        return(ApiValidationResult.Failure(ErrorKeys.JobActionHasPreprocessorDependency,
                                                           new[] { preprocessorJobId }));
                    }
                }
            }

            var changeableStates = new[] { JobState.InProgress, JobState.Draining, JobState.Paused };

            if (changeableStates.Any(s => s == jobData.Status.State))
            {
                var updated = await JobStore.UpdateState(tenantId, jobId, jobData.Status.State, JobState.Stopped);

                if (updated)
                {
                    await JobNotification.NotifyJobUpdated(jobId);

                    var jobStepSource = GetJobQueue(jobData);
                    if (jobStepSource != null)
                    {
                        await jobStepSource.Purge();
                    }

                    return(ApiValidationResult.Ok());
                }
            }

            return(ApiValidationResult.Failure(ErrorKeys.InvalidJobState));
        }
        public async Task StartJobIfNotStarted(string tenantId, string jobId)
        {
            var jobData = await JobStore.Load(tenantId, jobId);

            if (jobData == null)
            {
                throw new InvalidOperationException($"JobId {jobId} does not exist.");
            }

            if (jobData.Status.State != JobState.Initializing)
            {
                return;
            }

            await JobStore.UpdateState(tenantId, jobId, JobState.Initializing, JobState.InProgress);

            await JobNotification.NotifyJobUpdated(jobId);
        }
        public async Task StartJob(string tenantId, string jobId)
        {
            var jobData = await JobStore.Load(tenantId, jobId);

            if (jobData == null)
            {
                throw new InvalidOperationException($"JobId {jobId} does not exist.");
            }

            if (jobData.Status.State != JobState.Initializing)
            {
                throw new InvalidOperationException($"JobId {jobId} cannot be started due to its state.");
            }

            if (!await JobStore.UpdateState(tenantId, jobId, JobState.Initializing, JobState.InProgress))
            {
                throw new InvalidOperationException($"JobId {jobId} could not be updated to start.");
            }

            await JobNotification.NotifyJobUpdated(jobId);
        }