/// <summary> /// Reports the status to digital fulfillment system. For each file, if /// => If [titleid!=empty && airingid==* && mediaid==*] /// do not report anything /// => If [titleid==empty && airingid!=empty && mediaid!=empty] /// report by AiringId /// => If [titleid==empty && airingid==empty && mediaid!=empty] /// For each airing/asset for the mediaid, report its status /// => If [titleid==empty && airingid==empty && mediaid==empty] /// do not report anything /// </summary> /// <param name="files">The files.</param> /// <param name="statusMessage">The status message.</param> private void ReportStatusToMonitoringSystem(List <RQModel.FileViewModel> files, String statusMessage) { foreach (var file in files) { if (!String.IsNullOrEmpty(file.AiringId)) { reporter.Report(file.AiringId, airingSvc.IsAiringExists(file.AiringId), statusMessage); } else if (!String.IsNullOrWhiteSpace(file.MediaId)) { // Report to all assets under mediaid List <BLAiringModel.Airing> airings = airingSvc.GetByMediaId(file.MediaId).ToList(); foreach (var airing in airings) { reporter.Report(airing.AssetId, true, statusMessage); } } else if (file.TitleId.HasValue) { // Report to all assets under titleid List <BLAiringModel.Airing> airings = airingSvc.GetNonExpiredBy(file.TitleId.Value, DateTime.MinValue).ToList(); foreach (var airing in airings) { reporter.Report(airing.AssetId, true, statusMessage); } } } }
public EncodingFileContentValidator(IAiringService airingSvc) { _airingSvc = airingSvc; // Verify if the main field: media-id is provided RuleFor(request => request.MediaId). NotEmpty().WithMessage("media-id required"); // Verify that airing id is provided // Verify that the given AiringId exist Func <String, bool> airingIdExistRule = new Func <String, bool>((airingId) => { if (String.IsNullOrEmpty(airingId)) { return(false); } return((_airingSvc.GetBy(airingId) == null) ? false : true); }); RuleFor(c => c.AiringId) .Must(airingIdExistRule) .WithMessage("AiringId {0} doesn't exist", c => c.AiringId); //Verify that required data points are provided RuleFor(request => request.RootId).NotEmpty().WithMessage("root-id required"); // Only apply the remaining validation if mediaid is provided When(x => !String.IsNullOrWhiteSpace(x.MediaId), () => { // Verify that the given MediaId exist Func <String, bool> mediaIdExistRule = new Func <String, bool>((mediaId) => { return((_airingSvc.GetByMediaId(mediaId).IsNullOrEmpty()) ? false : true); }); RuleFor(c => c.MediaId) .Must(mediaIdExistRule) .WithMessage("MediaId {0} doesn't exist", c => c.MediaId); // Verify that outputs property is provided RuleFor(c => c.MediaCollection) .NotEmpty() .WithMessage("Output property required and cannot be empty"); // Apply content segment validation if output is provided When(x => !x.MediaCollection.IsNullOrEmpty(), () => { // Apply content segment validation rule SetContentSegmentsRule(); }); }); }
/// <summary> // Send notifications to all subscribed consumers /// </summary> private void SendNotification(BLFileModel.File file) { List <string> statusList = file.Contents.FirstOrDefault().MediaCollection.Select(c => c.Type.Replace("_", "").Replace("-", "").ToUpper()).ToList(); List <Airing> airingIds = _airingSvc.GetByMediaId(file.MediaId); // For each airing associated with the media id, do the following foreach (Airing airing in airingIds) { // Persist statuses foreach (string status in statusList) { airing.Status[status] = true; } // Finally, persist the airing data _airingSvc.Save(airing, false, true); // Retrieve full list of notification changes List <ChangeNotification> notifications = GetNotificationList(airing, statusList); // Update the status notification _airingSvc.CreateNotificationForStatusChange(airing.AssetId, notifications); } }
public AiringStatusRoutes( IAiringService airingSvc, IStatusSerivce statusService, IQueueService queueService, Serilog.ILogger logger ) : base("v1") { this.RequiresAuthentication(); _statusService = statusService; #region "POST Operations" Post("/airingstatus/{airingid}", _ => { // Verify that the user has permission to POST this.RequiresClaims(c => c.Type == HttpMethod.Post.Verb()); var airingId = (string)_.airingid; try { if (!airingSvc.IsAiringExists(airingId)) { var airingErrorMessage = string.IsNullOrWhiteSpace(airingId) ? "AiringId is required." : "Provided AiringId does not exists or expired."; // Return's NOT found status if airing not exists in current collection. return(Negotiate.WithModel(airingErrorMessage) .WithStatusCode(!string.IsNullOrWhiteSpace(airingId) ? HttpStatusCode.NotFound : HttpStatusCode.BadRequest)); } // Bind POST request to data contract var request = this.Bind <VMAiringRequestModel.AiringStatusRequest>(); //Validates the airing status request var validationResults = ValidateRequest(request); if (validationResults.Any()) { //returns validation results if there is any return(Negotiate.WithModel(validationResults) .WithStatusCode(HttpStatusCode.BadRequest)); } var airing = airingSvc.GetBy(airingId, AiringCollection.CurrentCollection); //Updates the airing status with the POST request foreach (var status in request.Status) { airing.Status[status.Key] = status.Value; } //Set the user name who updates the status airing.UserName = Context.User().UserName; // get all active queues var statusQueues = queueService.GetByStatus(true); // Finally, persist the airing data airingSvc.Save(airing, false, true); //update the status notification airingSvc.CreateNotificationForStatusChange(airing.AssetId, ResetDeliveryQueue(statusQueues, airing)); return(new { Result = "Successfully updated the airing status." }); } catch (Exception e) { logger.Error(e, "Failure ingesting status to airing. Airingid:{@airingId}", airingId); throw; } }); Post("/airingstatus/mediaId/{mediaid}", _ => { // Verify that the user has permission to POST this.RequiresClaims(c => c.Type == HttpMethod.Post.Verb()); var mediaId = (string)_.mediaid; if (string.IsNullOrWhiteSpace(mediaId)) { return(Negotiate.WithModel("MediaId is required.") .WithStatusCode(HttpStatusCode.BadRequest)); } var airingsByMediaId = airingSvc.GetByMediaId(mediaId); try { // Bind POST request to data contract var request = this.Bind <VMAiringRequestModel.AiringStatusRequest>(); //Validates the airing status request var validationResults = ValidateRequest(request); if (validationResults.Any()) { //returns validation results if there is any return(Negotiate.WithModel(validationResults) .WithStatusCode(HttpStatusCode.BadRequest)); } //Updates the airing status with the POST request foreach (var airing in airingsByMediaId) { foreach (var status in request.Status) { airing.Status[status.Key] = status.Value; } //Set the user name who updates the status airing.UserName = Context.User().UserName; // get all active queues var statusQueues = queueService.GetByStatus(true); // Finally, persist the airing data airingSvc.Save(airing, false, true); //update the status notification airingSvc.CreateNotificationForStatusChange(airing.AssetId, ResetDeliveryQueue(statusQueues, airing)); logger.Information("Successfully updated the airing status. AiringId : {AiringId}", airing.AssetId); } return(new { Result = "Successfully updated the airings status." }); } catch (Exception e) { logger.Error(e, "Failure ingesting status to airings by mediaid. Mediaid:{@mediaId}", mediaId); throw; } }); #endregion }
public AiringRoutes( IIdDistributor airingIdDistributorSvc, IAiringIdService airingIdSvc, IAiringService airingSvc, IReportingService reporterSvc, IProductService productSvc, AiringValidator _validator, IQueueService queueSvc, Serilog.ILogger logger, IDestinationService destinationSvc ) : base("v1") { this.RequiresAuthentication(); #region "GET Operations" Get("/airing/{airingId}", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Get.Verb()); var options = GetOptionsFromQueryString(); // These timers are added for instrumentation and will be removed // in the future Dictionary <string, object> routeStats = new Dictionary <string, object>(); long start = Stopwatch.GetTimestamp(); var airing = airingSvc.GetBy((string)_.airingId); routeStats.Add("rtAiring_ElapsedMS", GetElapsedMilliseconds(start, Stopwatch.GetTimestamp())); var user = Context.User(); FilterDestinations(airing, user.Destinations.ToList()); ValidateRequest(airing, user.Brands); var airingLong = airing.ToBusinessModel <BLAiringModel.Airing, BLAiringLongModel.Airing>(); start = Stopwatch.GetTimestamp(); if (options.Contains(Appenders.File.ToString().ToLower())) { airingSvc.AppendFile(ref airingLong); } routeStats.Add("appendFile_ElapsedMS", GetElapsedMilliseconds(start, Stopwatch.GetTimestamp())); start = Stopwatch.GetTimestamp(); if (options.Contains(Appenders.Title.ToString().ToLower())) { airingSvc.AppendTitle(ref airingLong); } routeStats.Add("appendTitle_ElapsedMS", GetElapsedMilliseconds(start, Stopwatch.GetTimestamp())); if (options.Contains(Appenders.Series.ToString().ToLower()) && (options.Contains(Appenders.File.ToString().ToLower()))) { airingSvc.AppendSeries(ref airingLong); } else if (options.Contains(Appenders.Series.ToString().ToLower())) { airingSvc.AppendSeries(ref airingLong); airingSvc.AppendFileBySeriesId(ref airingLong); } start = Stopwatch.GetTimestamp(); if (options.Contains(Appenders.Destination.ToString().ToLower())) { airingSvc.AppendDestinations(ref airingLong); } routeStats.Add("appendDestination_ElapsedMS", GetElapsedMilliseconds(start, Stopwatch.GetTimestamp())); start = Stopwatch.GetTimestamp(); if (options.Contains(Appenders.Change.ToString().ToLower())) { airingSvc.AppendChanges(ref airingLong); } routeStats.Add("appendChanges_ElapsedMS", GetElapsedMilliseconds(start, Stopwatch.GetTimestamp())); // Append status information if requested start = Stopwatch.GetTimestamp(); if (options.Contains(Appenders.Status.ToString().ToLower())) { airingSvc.AppendStatus(ref airingLong); } routeStats.Add("appendStatus_ElapsedMS", GetElapsedMilliseconds(start, Stopwatch.GetTimestamp())); start = Stopwatch.GetTimestamp(); if (options.Contains(Appenders.Package.ToString().ToLower())) { airingSvc.AppendPackage(ref airingLong, Request.Headers.Accept); } routeStats.Add("appendPackage_ElapsedMS", GetElapsedMilliseconds(start, Stopwatch.GetTimestamp())); start = Stopwatch.GetTimestamp(); if (options.Contains(Appenders.Premiere.ToString().ToLower())) { airingSvc.AppendPremiere(ref airingLong); } routeStats.Add("appendPremiere_ElapsedMS", GetElapsedMilliseconds(start, Stopwatch.GetTimestamp())); start = Stopwatch.GetTimestamp(); if (options.Contains(Appenders.Version.ToString().ToLower())) { airingSvc.AppendVersion(ref airingLong); } routeStats.Add("appendVersion_ElapsedMS", GetElapsedMilliseconds(start, Stopwatch.GetTimestamp())); start = Stopwatch.GetTimestamp(); var model = airingLong.ToViewModel <BLAiringLongModel.Airing, VMAiringLongModel.Airing>(); routeStats.Add("BLtoVM_ElapsedMS", GetElapsedMilliseconds(start, Stopwatch.GetTimestamp())); if (!options.Contains(Appenders.Package.ToString().ToLower())) { model.Options.Packages = null; } routeStats.Add("context", "Instrumentation-GET-Airing-Route"); routeStats.Add("requestMethod", this.Request.Method); routeStats.Add("path", this.Request.Path); logger.Information("Request -" + this.Request.Path + "{@info}", routeStats); return(model); }); Get("/airings/titleId/{titleId}", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Get.Verb()); var airings = airingSvc .GetNonExpiredBy((int)_.titleId, DateTime.UtcNow) .ToList(); var user = Context.User(); airings = FilterDestinations(airings, user.Destinations.ToList()); airings = FilterBrands(airings, user.Brands.ToList()); var viewModels = airings.Select(a => a.ToViewModel <BLAiringModel.Airing, VMAiringShortModel.Airing>()).ToList(); return(viewModels); }); Get("/airings/seriesId/{seriesId}", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Get.Verb()); var airings = airingSvc .GetNonExpiredBy((int)_.seriesId, DateTime.UtcNow, true) .ToList(); var user = Context.User(); airings = FilterDestinations(airings, user.Destinations.ToList()); airings = FilterBrands(airings, user.Brands.ToList()); var viewModels = airings.Select(a => a.ToViewModel <BLAiringModel.Airing, VMAiringShortModel.Airing>()).ToList(); return(viewModels); }); Get("/airings/mediaId/{mediaId}", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Get.Verb()); List <BLAiringModel.Airing> airings; DateTime startDate; DateTime endDate; if ((Request.Query.ContainsKey("startDate")) && (Request.Query.ContainsKey("endDate"))) { // validate ValidationResult results = new ValidationResult(); results = validateRequestDate(Request.Query["startDate"], Request.Query["endDate"]); if (results.Errors.Any()) // Verify if there are any validation errors. If so, return error { return(Negotiate.WithModel(results.Errors.Select(c => c.ErrorMessage)) .WithStatusCode(HttpStatusCode.BadRequest)); // Return status } startDate = getDate(Request.Query["startDate"]); endDate = getDate(Request.Query["endDate"]); if (startDate > endDate) { return(Negotiate.WithModel("Start date should be smaller than the end date") .WithStatusCode(HttpStatusCode.PreconditionFailed)); } airings = airingSvc.GetAiringsByMediaId((string)_.mediaId, startDate, endDate); } else { airings = new List <BLAiringModel.Airing>(airingSvc.GetByMediaId((string)_.mediaId)); } var user = Context.User(); airings = FilterDestinations(airings, user.Destinations.ToList()); airings = FilterBrands(airings, user.Brands.ToList()); var viewModels = airings.Select(a => a.ToViewModel <BLAiringModel.Airing, VMAiringShortModel.Airing>()).ToList(); return(viewModels); }); Get("/airings/destination/{destination}", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Get.Verb()); var airings = airingSvc .GetNonExpiredBy((string)_.destination, DateTime.UtcNow) .ToList(); var user = Context.User(); airings = FilterDestinations(airings, user.Destinations.ToList()); airings = FilterBrands(airings, user.Brands.ToList()); var viewModels = airings.Select(a => a.ToViewModel <BLAiringModel.Airing, VMAiringShortModel.Airing>()).ToList(); return(viewModels); }); Get("/airings/brand/{brand}/destination/{destination}", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Get.Verb()); List <BLAiringModel.Airing> airings = airingSvc .GetBy((string)_.brand, (string)_.destination, (DateTime)Request.Query["startDate"], (DateTime)Request.Query["endDate"], Request.Query["airingStatus"]); var user = Context.User(); airings = FilterDestinations(airings, user.Destinations.ToList()); airings = FilterBrands(airings, user.Brands.ToList()); var viewModels = airings.Select(a => a.ToViewModel <BLAiringModel.Airing, VMAiringShortModel.Airing>()).ToList(); return(viewModels); }); #endregion #region "POST Operations" Post("/airing/task", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Post.Verb()); var request = this.Bind <TaskViewModel>(); airingSvc.UpdateTask(request.AiringIds, request.Tasks); return(new { request.AiringIds, Message = "updated successfully" }); }); Post("/airing/send/{airingId}", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Post.Verb()); if (!airingSvc.IsAiringExists((string)_.airingId)) { return(Negotiate.WithModel("airingId not found") .WithStatusCode(HttpStatusCode.NotFound)); } airingSvc.PushToQueues(new List <string> { (string)_.airingId }); return(new { AiringId = (string)_.airingId, Message = "AiringId flagged for delivery for valid queues" }); }); Post("/airing/deliver/{airingId}", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Post.Verb()); if (!airingSvc.IsAiringExists((string)_.airingId)) { return(Negotiate.WithModel("airingId not found") .WithStatusCode(HttpStatusCode.NotFound)); } airingSvc.PushToQueues(new List <string> { (string)_.airingId }); return(new { AiringId = (string)_.airingId, Message = "AiringId flagged for delivery for valid queues" }); }); Post("/airing/send", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Post.Verb()); var request = this.Bind <QueuePushViewModel>(); var queueName = queueSvc.GetByApiKey(request.QueueName); if (queueName == null) { return(Negotiate.WithModel("Queue not found") .WithStatusCode(HttpStatusCode.NotFound)); } if (!queueName.Active) { return(Negotiate.WithModel("Inactive queue") .WithStatusCode(HttpStatusCode.PreconditionFailed)); } var invalidAirings = new List <string>(); var validAiringIds = new List <string>(); foreach (string airingId in request.AiringIds) { if (!airingSvc.IsAiringExists(airingId)) { invalidAirings.Add(airingId); } else { validAiringIds.Add(airingId); } } if (validAiringIds.Any()) { airingSvc.PushToQueue(request.QueueName, request.AiringIds); } return(new { validAiringIds = validAiringIds, invalidAiringsIds = invalidAirings, Message = "validAiringIds are flagged for delivery and delivered based on enabled options of the queue" }); }); Post("/airing/deliver", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Post.Verb()); var request = this.Bind <QueuePushViewModel>(); var queueName = queueSvc.GetByApiKey(request.QueueName); if (queueName == null) { return(Negotiate.WithModel("Queue not found") .WithStatusCode(HttpStatusCode.NotFound)); } if (!queueName.Active) { return(Negotiate.WithModel("Inactive queue") .WithStatusCode(HttpStatusCode.PreconditionFailed)); } var invalidAirings = new List <string>(); var validAiringIds = new List <string>(); foreach (string airingId in request.AiringIds) { if (!airingSvc.IsAiringExists(airingId)) { invalidAirings.Add(airingId); } else { validAiringIds.Add(airingId); } } if (validAiringIds.Any()) { airingSvc.PushToQueue(request.QueueName, request.AiringIds); } return(new { AiringIds = validAiringIds, invalidAiringsIds = invalidAirings, Message = "AiringIds are flagged for delivery and delivered based on enabled options of the queue" }); }); Post("/airing/{prefix}", _ => { // Verify that the user has permission to POST this.RequiresClaims(c => c.Type == HttpMethod.Post.Verb()); var user = Context.User(); // Bind POST request to data contract var request = this.Bind <VMAiringRequestModel.AiringRequest>(); try { // If the Airing provided in the request doesn't have an // AiringId, create new one. CurrentAiringId currentAiringId = null; if (string.IsNullOrEmpty(request.AiringId)) { currentAiringId = airingIdDistributorSvc.Distribute((string)_.prefix); request.AiringId = currentAiringId.AiringId; } else { try { var assetDetails = airingSvc.GetBy(request.AiringId); if (assetDetails.SequenceNumber != 0) { currentAiringId = new CurrentAiringId { SequenceNumber = assetDetails.SequenceNumber, BillingNumber = new BillingNumber { Current = assetDetails.BillingNumber.Current, Lower = assetDetails.BillingNumber.Lower, Upper = assetDetails.BillingNumber.Upper } }; } } catch (AiringNotFoundException e) { logger.Information("Airing released from other systems like turniverse"); } } // Translate data contract to airing business model var airing = Mapper.Map <BLAiringModel.Airing>(request); // validate List <ValidationResult> results = new List <ValidationResult>(); results.Add(_validator.Validate(airing, ruleSet: AiringValidationRuleSet.PostAiring.ToString())); // Verify if there are any validation errors. If so, return error if (results.Where(c => (!c.IsValid)).Count() > 0) { var message = results.Where(c => (!c.IsValid)) .Select(c => c.Errors.Select(d => d.ErrorMessage)); logger.Error("Failure ingesting released asset: {AssetId}", new Dictionary <string, object>() { { "airingid", request.AiringId }, { "mediaid", airing.MediaId }, { "error", message } }); // Return status return(Negotiate.WithModel(message) .WithStatusCode(HttpStatusCode.BadRequest)); } // Now that the validation is succesful, proceed to // persisting the data. But first, populate remaining // properties for the airing business model. if (currentAiringId != null) { airing.SequenceNumber = currentAiringId.SequenceNumber; airing.BillingNumber.Lower = currentAiringId.BillingNumber.Lower; airing.BillingNumber.Current = currentAiringId.BillingNumber.Current; airing.BillingNumber.Upper = currentAiringId.BillingNumber.Upper; } else { airing.BillingNumber = null; } airing.ReleaseOn = DateTime.UtcNow; airing.UserName = user.UserName; //Get the username // If flight information is provided in the airing, create // the corresponding product to destination mapping as defined // in ODT if (airing.Flights.SelectMany(f => f.Products).Any()) { productSvc.ProductDestinationConverter(ref airing); destinationSvc.FilterDestinationPropertiesDeliverablesAndCategoriesAndTransformTokens(ref airing); } else { // Retrieve destination related data - properties, deliverables and categories - lsand // augment it to airing destinationSvc.GetAiringDestinationRelatedData(ref airing); destinationSvc.FilterDestinationPropertiesDeliverablesAndCategoriesAndTransformTokens(ref airing); } // If the versions exist, create a mediaid based on the // provided version informtion,playlists and the network to which this // asset/airing belongs if (airing.Versions.Any()) { airingSvc.AugmentMediaId(ref airing); } // Finally, persist the airing data var savedAiring = airingSvc.Save(airing, request.Instructions.DeliverImmediately, true); // If immediate deliver requested, force push the airing to the // respective queues (based on: product=>destination=>queue relationship) if (request.Instructions.DeliverImmediately) { airingSvc.PushToQueues(new List <string> { savedAiring.AssetId }); } // Report status of this airing to monitoring system (digital fulfillment/logzio) reporterSvc.Report(savedAiring, airingSvc.IsAiringExists(savedAiring.AssetId)); logger.Information("Successfully ingested released asset: {Asset}", GeneratePostAiringpropertiesForLogzIO(savedAiring, user)); // Return airing model return(savedAiring.ToViewModel <BLAiringModel.Airing, VMAiringPostResponseModel.PostResponseAiring>()); } catch (Exception e) { logger.Error(e, "Failure ingesting released asset. AssetId:{@assetId}", request.AiringId); throw e; } }); #endregion #region "DELETE Operations" Delete("/airing", _ => { this.RequiresClaims(c => c.Type == HttpMethod.Delete.Verb()); var request = this.Bind <VMAiringRequestModel.AiringRequest>(); var airing = Mapper.Map <BLAiringModel.Airing>(request); // validate List <ValidationResult> results = new List <ValidationResult>(); results.Add(_validator.Validate(airing, ruleSet: AiringValidationRuleSet.DeleteAiring.ToString())); // Verify if there are any validation errors. If so, return error if (results.Where(c => (!c.IsValid)).Count() > 0) { // Return status return(Negotiate.WithModel(results.Where(c => (!c.IsValid)) .Select(c => c.Errors.Select(d => d.ErrorMessage))) .WithStatusCode(HttpStatusCode.BadRequest)); } var existingAiring = airingSvc.GetBy(airing.AssetId); ValidateRequest(existingAiring, Context.User().Brands); existingAiring.ReleaseBy = airing.ReleaseBy; existingAiring.ReleaseOn = DateTime.UtcNow; existingAiring.UserName = Context.User().UserName; existingAiring.DeliveredTo.Clear(); airingSvc.Delete(existingAiring); //Delete related packages of airing airingSvc.DeleteAiringMappedPackages(airing.AssetId); if (request.Instructions.DeliverImmediately) { airingSvc.PushToQueues(new List <string> { existingAiring.AssetId }); } reporterSvc.Report(existingAiring, false); return(new VMAiringLongModel.AiringMessage { AiringId = airing.AssetId, Message = "deleted successfully" }); }); #endregion }
public FileValidator(IAiringService airingSvc) { this.airingSvc = airingSvc; // Apply video specific file validation RuleSet("Video", () => { /// <summary> /// This validation checks if content segment(s) specified /// in the payload (# of entries in 'contents' array) should match that of the airing. /// That is, # of elements in 'contents' array should match with the # of elements in ‘version’ array for the airing /// </summary> int contentSegmentsCount = default(int); Func <FileViewModel, List <FileContentViewModel>, bool> contentSegmentsMatchRule = new Func <FileViewModel, List <FileContentViewModel>, bool>((request, filec) => { // If the mediaid is provided then do content segment validation using that. If the provided mediaid doesn't exist // then set content segment count to 0 if (!request.MediaId.IsNullOrEmpty()) { var query = this.airingSvc.GetByMediaId(request.MediaId); contentSegmentsCount = (query.IsNullOrEmpty()) ? 0 : query.FirstOrDefault().Versions.Count(); return((filec.Count() != contentSegmentsCount) ? false : true); } else if (!request.AiringId.IsNullOrEmpty()) { try { var airing = this.airingSvc.GetBy(request.AiringId); contentSegmentsCount = (airing == null) ? 0 : airing.Versions.Count(); return((filec.Count() != contentSegmentsCount) ? false : true); } catch (Exception) { //if it throws and exception during Get, throw a validation error return(false); } } else if (request.TitleId.HasValue) { //var airing = airingSvc.GetNonExpiredBy(request.TitleId.Value, DateTime.MinValue); //contentSegmentsCount = (airing == null) ? 0 : airing.FirstOrDefault().Versions.Count(); //return ((filec.Count() != contentSegmentsCount) ? false : true); return(true); } // return false if none of the above criteria matches return(false); }); Func <FileViewModel, List <FileContentViewModel>, object> contentCount = new Func <FileViewModel, List <FileContentViewModel>, object>((request, filec) => { return(contentSegmentsCount); }); // Verify that required data points are provided RuleFor(c => c) .Cascade(CascadeMode.StopOnFirstFailure) .Must(c => !(string.IsNullOrEmpty(c.AiringId) && string.IsNullOrEmpty(c.MediaId) && !c.TitleId.HasValue)) .WithMessage("AiringId, MediaId or TitleId is required") .Must(c => OnlyOneTrue(new bool[] { !string.IsNullOrEmpty(c.AiringId), !string.IsNullOrEmpty(c.MediaId), c.TitleId.HasValue })) .WithMessage("Either provide AiringId, MediaId or TitleId but not more than one") .DependentRules(dr => { // Apply the following validation if AiringId is not empty and MediaId is empty dr.When(x => (!String.IsNullOrWhiteSpace(x.AiringId) && String.IsNullOrWhiteSpace(x.MediaId)), () => { // Verify that the given AiringId exist Func <String, bool> airingIdExistRule = new Func <String, bool>((airingId) => { return(this.airingSvc.IsAiringExists(airingId)); }); dr.RuleFor(c => c.AiringId) .Cascade(CascadeMode.StopOnFirstFailure) .Must(airingIdExistRule) .WithMessage("AiringId {0} doesn't exist/expired.", c => c.MediaId) .DependentRules(o => { o.RuleFor(ox => ox.Contents) .Cascade(CascadeMode.StopOnFirstFailure) .NotEmpty() .WithMessage("Contents property required and cannot be empty when posting video payload by airing-id") .Must(contentSegmentsMatchRule) .WithMessage("Content segments provided in payload doesn't match with that of media/airing. Payload contains {0} content segments (# of items in 'contents' array), while media/airing contains {1} content segments", (request, mediac) => mediac.Count(), contentCount) .DependentRules(ox => { ox.RuleForEach(xo => xo.Contents) .SetValidator(new FileContentValidator()); }); }); }); // Apply the following validation if MediaId is provided dr.When(x => !String.IsNullOrWhiteSpace(x.MediaId), () => { // Verify that the given MediaId exist Func <String, bool> mediaIdExistRule = new Func <String, bool>((mediaId) => { return((airingSvc.GetByMediaId(mediaId).IsNullOrEmpty()) ? false : true); }); dr.RuleFor(c => c.MediaId) .Cascade(CascadeMode.StopOnFirstFailure) .Must(mediaIdExistRule) .WithMessage("MediaId {0} doesn't exist", c => c.MediaId) .DependentRules(oxx => { oxx.RuleFor(ox => ox.Contents) .Cascade(CascadeMode.StopOnFirstFailure) .NotEmpty() .WithMessage("Contents property required and cannot be empty when posting video payload by media-id") .Must(contentSegmentsMatchRule) .WithMessage("Content segments provided in payload doesn't match with that of media/airing. Payload contains {0} content segments (# of items in 'contents' array), while media/airing contains {1} content segments", (request, mediac) => mediac.Count(), contentCount) .DependentRules(o => { o.RuleForEach(xo => xo.Contents) .SetValidator(new FileContentValidator()); }); }); }); // Apply the following validation if TitleId is provided dr.When(x => x.TitleId.HasValue, () => { // Verify that the given MediaId exist Func <int, bool> titleIdExistRule = new Func <int, bool>((titleId) => { //return (airingSvc.GetNonExpiredBy(titleId, DateTime.MinValue).IsNullOrEmpty()) ? false : true; return(true); }); dr.RuleFor(c => c.TitleId.Value) .Cascade(CascadeMode.StopOnFirstFailure) .Must(titleIdExistRule) .WithMessage("TitleId {0} doesn't exist", c => c.TitleId.Value) .DependentRules(oxx => { oxx.RuleFor(ox => ox.Contents) .Cascade(CascadeMode.StopOnFirstFailure) .NotEmpty() .WithMessage("Contents property required and cannot be empty when posting video payload by title-id") .Must(contentSegmentsMatchRule) .WithMessage("Content segments provided in payload doesn't match with that of media/airing. Payload contains {0} content segments (# of items in 'contents' array), while media/airing contains {1} content segments", (request, mediac) => mediac.Count(), contentCount) .DependentRules(o => { o.RuleForEach(xo => xo.Contents) .SetValidator(new FileContentValidator()); }); }); }); }); }); // Apply non-video specific file validation RuleSet("NonVideo", () => { // Verify that TitleId is provided RuleFor(c => c) .Cascade(CascadeMode.StopOnFirstFailure) .Must(c => c.TitleId.HasValue) .WithMessage("Title Id Required") .Must(c => c.TitleId > 0) .WithMessage("Title Id must be greater than zero"); RuleFor(c => c) .Must(c => String.IsNullOrEmpty(c.AiringId) && String.IsNullOrEmpty(c.MediaId)) .WithMessage("Registering non video assets by Airing Id or Media Id not currently supported"); // Verify that other required fileds are provided RuleFor(c => c) .Must(c => !String.IsNullOrEmpty(c.Path)) .WithMessage("Path required"); RuleFor(c => c) .Must(c => !String.IsNullOrEmpty(c.Name)) .WithMessage("Name required"); RuleFor(c => c) .Must(c => !String.IsNullOrEmpty(c.Type)) .WithMessage("Type required"); RuleFor(c => c) .Must(c => !String.IsNullOrEmpty(c.Domain)) .WithMessage("Domain required"); RuleFor(c => c) .Must(c => !String.IsNullOrEmpty(c.AspectRatio)) .WithMessage("Aspect ratio required"); RuleFor(c => c) .Must(c => !String.IsNullOrEmpty(c.Category)) .WithMessage("Category required"); RuleFor(c => c) .Must(c => !String.IsNullOrEmpty(c.Match)) .WithMessage("Match required"); }); }