private async Task SetValidationStatusAsync(
            PackageValidation packageValidation,
            INuGetValidationResponse validationResponse,
            DateTime now)
        {
            if (validationResponse.Status != ValidationStatus.Incomplete)
            {
                AddValidationIssues(packageValidation, validationResponse.Issues);
            }

            if (validationResponse.Status == ValidationStatus.Succeeded &&
                validationResponse.NupkgUrl != null)
            {
                if (!_validatorProvider.IsNuGetProcessor(packageValidation.Type))
                {
                    throw new InvalidOperationException(
                              $"The validator '{packageValidation.Type}' is not a processor but returned a .nupkg URL as " +
                              $"part of the validation step response.");
                }

                await _packageFileService.CopyPackageUrlForValidationSetAsync(
                    packageValidation.PackageValidationSet,
                    validationResponse.NupkgUrl);
            }

            packageValidation.ValidationStatus          = validationResponse.Status;
            packageValidation.ValidationStatusTimestamp = now;
            await _validationContext.SaveChangesAsync();

            TrackValidationStatus(packageValidation);
        }
        private void CheckForCyclesAndParallelProcessors()
        {
            var processorNames = _configuration
                                 .Validations
                                 .Select(x => x.Name)
                                 .Where(x => _validatorProvider.IsNuGetProcessor(x))
                                 .ToList();

            TopologicalSort.Validate(_configuration.Validations, processorNames);
        }
        public async Task <PackageValidationSet> TryGetOrCreateValidationSetAsync(ProcessValidationSetData message, IValidatingEntity <T> validatingEntity)
        {
            var validationSet = await _validationStorageService.GetValidationSetAsync(message.ValidationTrackingId);

            if (validationSet == null)
            {
                var shouldSkip = await _validationStorageService.OtherRecentValidationSetForPackageExists(
                    validatingEntity,
                    _validationConfiguration.NewValidationRequestDeduplicationWindow,
                    message.ValidationTrackingId);

                if (shouldSkip)
                {
                    return(null);
                }

                validationSet = InitializeValidationSet(message, validatingEntity);

                if (validatingEntity.Status == PackageStatus.Available)
                {
                    var packageETag = await _packageFileService.CopyPackageFileForValidationSetAsync(validationSet);

                    // This indicates that the package in the package container is expected to not change.
                    validationSet.PackageETag = packageETag;
                }
                else
                {
                    await _packageFileService.CopyValidationPackageForValidationSetAsync(validationSet);

                    // A symbols package for the same id and version can be re-submitted.
                    // When this happens a new validation is submitted. After validation the new symbols package will overwrite the old symbols package.
                    // Because of this when a new validation for a symbols package is received it can already exist a symbols package in the public symbols container.
                    if (validatingEntity.ValidatingType == ValidatingType.SymbolPackage)
                    {
                        validationSet.PackageETag = await _packageFileService.GetPublicPackageBlobETagOrNullAsync(validationSet);
                    }
                    else
                    {
                        // This indicates that the package in the packages container is expected to not exist (i.e. it has
                        // has no etag at all).
                        validationSet.PackageETag = null;
                    }
                }

                // If there are any processors in the validation set, back up the original. We back up from the
                // validation set copy to avoid concurrency issues.
                if (validationSet.PackageValidations.Any(x => _validatorProvider.IsNuGetProcessor(x.Type)))
                {
                    await _packageFileService.BackupPackageFileFromValidationSetPackageAsync(validationSet, _sasDefinitionConfiguration.ValidationSetProviderSasDefinition);
                }

                validationSet = await PersistValidationSetAsync(validationSet, validatingEntity);
            }
            else
            {
                var sameKey = validatingEntity.Key == validationSet.PackageKey;

                if (!sameKey)
                {
                    throw new InvalidOperationException($"Validation set  key ({validationSet.PackageKey}) " +
                                                        $"does not match expected {validatingEntity.EntityRecord.GetType().Name} key ({validatingEntity.Key}).");
                }
            }

            return(validationSet);
        }