protected List <CustomJobFailedTarget> GetRetryTargets(int?page = null, int?count = null)
        {
            var job =
                new CustomJob
            {
                Id = Job.Id
            };

            if (page != null && count != null)
            {
                Log.Log($"Loading retry targets with page {page.Value} and count {count.Value} ...");
                job.LoadRelation(CustomJob.RelationNames.CustomJobFailedTargetsOfCustomJob, Service, false,
                                 count.Value, page.Value, CustomJobFailedTarget.Fields.ID);
                Log.Log($"Loaded retry targets with page {page.Value} and count {count.Value}.");
            }
            else
            {
                Log.Log($"Loading retry targets ...");
                job.LoadRelation(CustomJob.RelationNames.CustomJobFailedTargetsOfCustomJob, Service,
                                 CustomJobFailedTarget.Fields.ID);
                Log.Log($"Loaded retry targets.");
            }

            var targets = job.CustomJobFailedTargetsOfCustomJob ?? new CustomJobFailedTarget[0];

            Log.Log($"Retry targets count: {targets.Length}.");

            return(targets.ToList());
        }
        private void PostRunActions(JobRunStatus runStatus)
        {
            if (runStatus.IsClose)
            {
                var finalStatus = runStatus.IsSuccess
                                        ? CustomJob.StatusReasonEnum.Success
                                        : CustomJob.StatusReasonEnum.Failure;

                Close(Service, finalStatus, Job.Id);
            }

            if (Job.DeleteOnSuccess == true &&
                runStatus.IsSuccess && runStatus.IsClose && runStatus.RunTargetFailures?.Any() != true)
            {
                log.Log("Checking target failures ...");
                var job = new CustomJob {
                    Id = Job.Id
                };
                job.LoadRelation(CustomJob.RelationNames.CustomJobFailedTargetsOfCustomJob, Service, false, 1);
                var isFailedExist = job.CustomJobFailedTargetsOfCustomJob?.Any() == true;
                log.Log($"Failed exist: {isFailedExist}.");

                if (!isFailedExist)
                {
                    log.Log("Deleting job ...", LogLevel.Debug);
                    Service.Delete(CustomJob.EntityLogicalName, Job.Id);
                    log.Log("Deleted job.");
                }
            }
        }
        private void PerformSyncActions(Entity targetGeneric)
        {
            targetGeneric.Require(nameof(targetGeneric));

            var customJob = new CustomJob();

            // post
            if ((Context as IPluginExecutionContext)?.Stage == 40)
            {
                Log.Log("Processing post-operation actions ...");

                var postImage = Context.PostEntityImages.FirstOrDefault().Value?.ToEntity <CustomJob>();

                if (postImage == null && Context.MessageName == "Update")
                {
                    throw new InvalidPluginExecutionException("A full post image must be registered on this plugin step.");
                }

                postImage = postImage ?? targetGeneric.ToEntity <CustomJob>();

                foreach (var attribute in postImage.Attributes)
                {
                    customJob[attribute.Key] = attribute.Value;
                }

                ValidateJobParams(customJob);

                if (string.IsNullOrEmpty(customJob.Name))
                {
                    Log.Log("Name is empty, updating ...");
                    Service.Update(
                        new CustomJob
                    {
                        Id   = customJob.Id,
                        Name = BuildJobName(customJob)
                    });
                    Log.Log("Name was empty, updated.");
                }

                return;
            }

            if (Context.MessageName == "Update")
            {
                Log.Log("Update message, fetching target job from CRM ...");
                customJob = Service.Retrieve(targetGeneric.LogicalName, targetGeneric.Id, new ColumnSet(true))
                            .ToEntity <CustomJob>();
                Log.Log("Fetched target job from CRM.");
            }

            foreach (var attribute in targetGeneric.Attributes)
            {
                customJob[attribute.Key] = attribute.Value;
            }

            // fill page if empty
            if (customJob.RecordsPerPage != null && customJob.PageNumber == null)
            {
                Log.Log("Records per page set, but not the page number; setting to '1'.");
                targetGeneric[CustomJob.Fields.PageNumber] = 1;
            }

            // clear failed targets as we are starting over and reset retry runs
            if (customJob.StatusReason == CustomJob.StatusReasonEnum.Draft)
            {
                Log.Log("Draft status.");
                Log.Log("Clearing retry run, page number, cookie, and last message.");
                targetGeneric[CustomJob.Fields.CurrentRetryRun]  = null;
                targetGeneric[CustomJob.Fields.PageNumber]       = null;
                targetGeneric[CustomJob.Fields.PagingCookie]     = null;
                targetGeneric[CustomJob.Fields.LatestRunMessage] = null;

                Log.Log("Loading failed targets for this job ...");
                var tempJob =
                    new CustomJob
                {
                    Id = customJob.Id
                };
                tempJob.LoadRelation(CustomJob.RelationNames.CustomJobFailedTargetsOfCustomJob, Service);
                Log.Log("Loaded failed targets for this job.");

                if (tempJob.CustomJobFailedTargetsOfCustomJob != null)
                {
                    Log.Log($"Failed targets: {tempJob.CustomJobFailedTargetsOfCustomJob.Length}.");
                    var failures = tempJob.CustomJobFailedTargetsOfCustomJob;

                    var request =
                        new ExecuteMultipleRequest
                    {
                        Requests = new OrganizationRequestCollection(),
                        Settings =
                            new ExecuteMultipleSettings
                        {
                            ContinueOnError = true,
                            ReturnResponses = false
                        }
                    };

                    Log.Log($"Deleting failed targets ...");
                    for (var i = 0; i < Math.Ceiling(failures.Length / 1000d); i++)
                    {
                        request.Requests.Clear();
                        request.Requests.AddRange(failures.Skip(i * 1000).Take(1000)
                                                  .Select(failure =>
                                                          new DeleteRequest
                        {
                            Target = new EntityReference(CustomJobFailedTarget.EntityLogicalName,
                                                         failure.Id)
                        }));

                        Service.Execute(request);
                    }
                    Log.Log($"Deleted failed targets.");
                }
            }

            // cancel active sub-jobs
            if (customJob.StatusReason == CustomJob.StatusReasonEnum.Draft ||
                customJob.StatusReason == CustomJob.StatusReasonEnum.Cancelled)
            {
                Log.Log($"{customJob.StatusReason} status.");
                var tempJob =
                    new CustomJob
                {
                    Id = customJob.Id
                };

                Log.Log("Loading active children of this job ...");
                var filter = new FilterExpression();
                filter.AddCondition(CustomJob.Fields.Status, ConditionOperator.Equal, (int)CustomJob.StatusEnum.Active);
                tempJob.LoadRelation(CustomJob.RelationNames.CustomJobsOfParentJob, Service, false, filter);
                Log.Log("Loaded active children of this job.");

                if (tempJob.CustomJobsOfParentJob != null)
                {
                    Log.Log($"Active children: {tempJob.CustomJobsOfParentJob.Length}.");
                    foreach (var job in tempJob.CustomJobsOfParentJob)
                    {
                        Log.Log($"Setting sub job '{job.Id}' to cancelled ...");
                        Service.Update(
                            new CustomJob
                        {
                            Id           = job.Id,
                            Status       = CustomJob.StatusEnum.Inactive,
                            StatusReason = CustomJob.StatusReasonEnum.Cancelled
                        });
                        Log.Log($"Set sub job '{job.Id}' to cancelled.");
                    }
                }
            }

            var isNamingFieldsUpdated = targetGeneric.Attributes.Keys
                                        .Any(key => new[]
            {
                CustomJob.Fields.TargetLogicalName,
                CustomJob.Fields.ActionName,
                CustomJob.Fields.Workflow,
                CustomJob.Fields.TargetID,
                CustomJob.Fields.TargetXML
            }.Contains(key));

            if (string.IsNullOrEmpty(customJob.Name) || isNamingFieldsUpdated)
            {
                targetGeneric[CustomJob.Fields.Name] = BuildJobName(customJob);
                Log.Log($"Set job name to '{targetGeneric[CustomJob.Fields.Name]}'.");
            }

            if (customJob.StatusReason == CustomJob.StatusReasonEnum.Draft &&
                customJob.MarkForWaiting == true && customJob.TargetDate != null &&
                Context.MessageName != "Create")
            {
                Log.Log("Setting job to waiting because of flag ...");
                targetGeneric[CustomJob.Fields.MarkForWaiting] = false;
                targetGeneric[CustomJob.Fields.StatusReason]   = CustomJob.StatusReasonEnum.Waiting.ToOptionSetValue();
                Log.Log("Set job to waiting because of flag.");
            }
        }
Example #4
0
        protected override void ExecuteLogic()
        {
            var target = ((Entity)context.InputParameters["Target"]).ToEntity <CustomJob>();

            if (target.RecurrenceUpdatedTrigger == null)
            {
                log.Log("Recurrence update not triggered. Exiting ...");
                return;
            }

            var postImage = context.PostEntityImages.FirstOrDefault().Value?.ToEntity <CustomJob>();

            if (postImage == null)
            {
                throw new InvalidPluginExecutionException("A full post image must be registered on this plugin step.");
            }

            var customJob =
                new CustomJob
            {
                Id = target.Id
            };

            log.Log($"Loading related recurrences.", LogLevel.Debug);
            customJob.LoadRelation(CustomJob.RelationNames.Recurrences, service);

            if (customJob.Recurrences == null)
            {
                if (postImage.RecurrentJob == true)
                {
                    log.Log("Setting to non-recurrent ...", LogLevel.Debug);
                    service.Update(
                        new CustomJob
                    {
                        Id           = customJob.Id,
                        RecurrentJob = false
                    });
                    log.Log("Set to non-recurrent.");
                }

                log.Log($"No recurrences set. Exiting ...");
                return;
            }

            DateTime?targetDate = null;

            foreach (var recurrenceRule in customJob.Recurrences)
            {
                log.Log($"Getting next recurrence for {recurrenceRule.Id}.", LogLevel.Debug);
                var action         = new GlobalActions.ldv_CustomJobGetNextRecurrenceDate(recurrenceRule.ToEntityReference(), service);
                var nextRecurrence = action.Execute().NextTargetDate;
                log.Log($"Next recurrence: '{nextRecurrence}'.");

                if (nextRecurrence > new DateTime(1900) && (targetDate == null || nextRecurrence < targetDate))
                {
                    targetDate = nextRecurrence;
                }
            }

            if (targetDate == null)
            {
                log.Log("Updating latest run message and date.", LogLevel.Debug);
                service.Update(
                    new CustomJob
                {
                    Id = target.Id,
                    LatestRunMessage   = "Recurrence reached its cycle end.",
                    TargetDate         = null,
                    PreviousTargetDate = DateTime.UtcNow,
                    Status             = CustomJob.StatusEnum.Inactive,
                    StatusReason       = CustomJob.StatusReasonEnum.Success
                });
                log.Log("Updated.");

                return;
            }

            log.Log("Updating target date.", LogLevel.Debug);
            service.Update(
                new CustomJob
            {
                Id           = customJob.Id,
                RecurrentJob = customJob.Recurrences.Any(),
                TargetDate   = targetDate
            });
        }