Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Text;
- using System.Linq;
- using System.Net;
- using log4net;
- using Newtonsoft.Json;
- using PlayFab.AddOnService;
- using PlayFab.DataModel;
- using PlayFab.WebAPIModels.LogicServer;
- using PlayFab.Services;
- using PlayFab.WebAPIModels.Inventory;
- using QueueUtilities;
- namespace ProcessGitHubWebhookEvents
- {
- public class StoreContainer
- {
- public string CatalogVersion;
- public string StoreId;
- public List<StoreItem> StoreItems;
- }
- public class CatalogContainer
- {
- public string CatalogVersion { get; set; }
- public CatalogItem[] Catalog { get; set; }
- }
- public class ProcessGitHubWebhookEvents : IMessageProcessor<GitCommit>
- {
- private enum FileProcessingType
- {
- CloudScript,
- Catalog,
- TitleData,
- Store,
- DropTable,
- Currency,
- TitleNews
- }
- private static readonly ILog Log = LogManager.GetLogger("ProcessGitHubWebHookEvents");
- private const string UpdateStatusMessage = "Updating status for title {0} webook commit {1} from {2} to {3}";
- private const string DeserilizationMessage = "{0} {1} in the repository could not be deserialized";
- private const string ValidationMessage = "{1} {0} Did not pass validation";
- private const string OverlimitMessage = "File is over the size limit for a {0}. {1}";
- private const string CloudScriptFileName = "CloudScript.js";
- private const string CatalogPath = "/Catalogs/";
- private const string TitleDataPath = "/TitleData/";
- private const string StorePath = "/Stores/";
- private const string DropTablePath = "/DropTables/";
- private const string CurrencyPath = "/Currency/";
- private const string TitleNewsPath = "/TitleNews/";
- private const string JsonExt = ".json";
- /// <summary>
- /// Main is for debugging only.
- /// </summary>
- /// <param name="args"></param>
- static void Main(string[] args)
- {
- log4net.Config.XmlConfigurator.Configure();
- Log.Info("Process GitHub Webhook Events startings");
- var processor = new QueueProcessor<ProcessGitHubWebhookEvents, GitCommit>(new ProcessGitHubWebhookEvents());
- processor.ProcessEvents();
- }
- public bool Process(GitCommit gitCommit)
- {
- var cloudScriptFileToUpdate = string.Empty;
- var catalogItemsToUpdate = new List<CatalogContainer>();
- var titleDataToUpdate = new Dictionary<string, string>();
- var storesDataToUpdate = new List<StoreContainer>();
- var dropTableToUpdate = new List<RandomResultTableListing>(); //note that this is a list of List<ResultTableNode>
- var virtualCurrenciesToUpdate = new List<VirtualCurrencyModel>();
- var titleNewsToUpdate = new List<TitleNews>();
- var title = Title.Get(gitCommit.TitleId, false);
- var dev = Developer.Get(title.DeveloperId, false);
- var gitHubAddOn = new GitHubAddOn(title, dev);
- var settings = gitHubAddOn.GetSettings();
- //populate gitCommit object;
- gitCommit.RepositoryName = settings.RepositoryName;
- gitCommit.RepositoryOwner = settings.RepositoryOwner;
- gitCommit.AccessToken = settings.AccessToken;
- try {
- #region validation
- if (gitHubAddOn.GetSettings().UserEnabledCloudScript)
- {
- var cloudScriptReady = ProcessCloudScript(gitHubAddOn, gitCommit, title, out cloudScriptFileToUpdate);
- if (!cloudScriptReady)
- {
- return true;
- }
- }
- if (gitHubAddOn.GetSettings().UserEnabledCatalogs)
- {
- var catalogsReady = ProcessCatalog(gitHubAddOn, gitCommit, title, out catalogItemsToUpdate);
- if (!catalogsReady)
- {
- return true;
- }
- }
- if (gitHubAddOn.GetSettings().UserEnabledTitleData)
- {
- var titleDataReady = ProcessTitleData(gitHubAddOn, gitCommit, title, out titleDataToUpdate);
- if (!titleDataReady)
- {
- return true;
- }
- }
- if (gitHubAddOn.GetSettings().UserEnabledStores)
- {
- var storesReady = ProcessStores(gitHubAddOn, gitCommit, title, out storesDataToUpdate);
- if (!storesReady)
- {
- return true;
- }
- }
- if (gitHubAddOn.GetSettings().UserEnabledDropTables)
- {
- var dropTablesReady = ProcessDropTables(gitHubAddOn, gitCommit, title, out dropTableToUpdate);
- if(!dropTablesReady)
- {
- return true;
- }
- }
- if (gitHubAddOn.GetSettings().UserEnabledCurrency)
- {
- var currencyReady = ProcessCurrency(gitHubAddOn, gitCommit, title, out virtualCurrenciesToUpdate);
- if (!currencyReady)
- {
- return true;
- }
- }
- if (gitHubAddOn.GetSettings().UserEnabledTitleNews)
- {
- var titleNewsReady = ProcessTitleNews(gitHubAddOn, gitCommit, title, out titleNewsToUpdate);
- if (!titleNewsReady)
- {
- return true;
- }
- }
- #endregion
- }
- catch(Exception e)
- {
- Log.Error("Failed to process webhook", e);
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess, "Unable to determine if the commit is valid");
- throw;
- }
- try
- {
- #region save data
- //We are good to save all the data at this point.
- //Save Cloudscript file.
- if (gitHubAddOn.GetSettings().UserEnabledCloudScript)
- {
- SaveOrDeployCloudScript(title, gitCommit, cloudScriptFileToUpdate);
- }
- //Save virtual currency, we should do this before we save the catalog.
- if (gitHubAddOn.GetSettings().UserEnabledCurrency)
- {
- //Find Currencies to remove.
- var currentCurrency = VirtualCurrencyService.GetVirtualCurrencies(title.Id);
- var currencyToDelete = new List<string>();
- var currencyToAdd = new List<VirtualCurrencyModel>();
- foreach (var currency in virtualCurrenciesToUpdate)
- {
- currencyToAdd.Add(currency);
- }
- //Find Currencies to remove
- foreach (var currency in currentCurrency)
- {
- var curr = virtualCurrenciesToUpdate.Find(c => c.CurrencyCode == currency.CurrencyCode);
- if (curr == null)
- {
- currencyToDelete.Add(currency.CurrencyCode);
- }
- }
- //Delete Currencies that need to be deleted.
- if (currencyToDelete.Count > 0)
- {
- VirtualCurrencyService.DeleteCurrencies(title.Id, currencyToDelete);
- }
- foreach (var currency in currencyToAdd)
- {
- VirtualCurrencyService.SetVirtualCurrency(title.Id, new VirtualCurrencyModel()
- {
- TitleId = currency.TitleId,
- CurrencyCode = currency.CurrencyCode,
- DisplayName = currency.DisplayName,
- InitialDeposit = currency.InitialDeposit,
- RechargeMax = currency.RechargeMax,
- RechargeRate = currency.RechargeRate,
- MaxAmount = currency.MaxAmount
- });
- }
- }
- //Save catalog items ( will it fail if the VC doesn't exist? It doesn't in GameManager )
- if (gitHubAddOn.GetSettings().UserEnabledCatalogs)
- {
- //For each catalog, set the catalog data.
- foreach (var catalog in catalogItemsToUpdate)
- {
- CatalogService.SetItems(catalog.Catalog, title.Id, catalog.CatalogVersion);
- }
- }
- if (gitHubAddOn.GetSettings().UserEnabledTitleData)
- {
- if (titleDataToUpdate != null && titleDataToUpdate.Keys.Count > 0)
- {
- TitleDataService.SetTitleData<TitleData>(title.Id, titleDataToUpdate);
- }
- }
- if (gitHubAddOn.GetSettings().UserEnabledStores)
- {
- foreach (var store in storesDataToUpdate)
- {
- StoreItem.SetItems(store.StoreItems, title.Id, store.CatalogVersion, store.StoreId);
- }
- }
- if (gitHubAddOn.GetSettings().UserEnabledDropTables)
- {
- foreach (var droptable in dropTableToUpdate)
- {
- DropTableService.SaveTable(droptable, title.Id, droptable.CatalogVersion);
- }
- }
- if (gitHubAddOn.GetSettings().UserEnabledTitleNews)
- {
- if (titleNewsToUpdate != null && titleNewsToUpdate.Count > 0)
- {
- TitleNewsService.SaveTitleNewsFromRepository(title.Id, titleNewsToUpdate);
- }
- }
- #endregion
- }
- catch (Exception e)
- {
- //If there was a problem saving anywhere along the way, set the commit to a failure.
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess, e.Message);
- //Catch any exception and throw it back to the queue processor.
- throw;
- }
- //we are good to go, set the active commit and save the commit status as available.
- gitHubAddOn.SetActiveCommit(gitCommit.CommitId);
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.Available, string.Empty);
- return true;
- }
- private static void SaveOrDeployCloudScript(Title title, GitCommit gitCommit, string cloudScriptFile)
- {
- var logicVersion = CloudLogicService.GetVersion(title.Id, 1, true);
- if (gitCommit.IsDeployment)
- {
- var revision = CloudLogicService.GetRevision(title.Id, gitCommit.CommitId, true, true);
- var revisionNumber = int.Parse(revision.VersionRevision);
- title.LatestCloudLogicVersion = logicVersion.Version;
- title.Update();
- //Set this revision as the active revision.
- CloudLogicService.SetProdRevision(logicVersion, revisionNumber);
- return;
- }
- var newRevisionNumber = 0;
- //Get existing cloud script revision
- var csVersion = CloudLogicService.GetVersion(title.Id, 1, true);
- //Insert files into the database.
- CloudLogicService.CreateRevision(title.Id, new List<string> { CloudScriptFileName },
- new List<string> { cloudScriptFile }, false, out newRevisionNumber, gitCommit.CommitId);
- //Reacquire the version object, because CreateRevision has incremented invalidating csVersion.
- logicVersion = CloudLogicService.GetVersion(title.Id, csVersion.Version, true);
- title.LatestCloudLogicVersion = logicVersion.Version;
- title.Update();
- }
- private static bool CheckOverLimit(FileProcessingType type, string content, GitCommit gitCommit)
- {
- var limitsResult = new ValidateLimitsResult();
- switch (type)
- {
- case FileProcessingType.CloudScript:
- limitsResult = CloudLogicService.ValidateLimits(content);
- break;
- case FileProcessingType.Catalog:
- limitsResult = CatalogService.ValidateLimits(content);
- break;
- case FileProcessingType.TitleData:
- limitsResult = TitleDataService.ValidateLimits(content);
- break;
- case FileProcessingType.Store:
- limitsResult = StoreService.ValidateLimits(content);
- break;
- case FileProcessingType.DropTable:
- limitsResult = DropTableService.ValidateLimits(content);
- break;
- case FileProcessingType.Currency:
- limitsResult = VirtualCurrencyService.ValidateLimits(content);
- break;
- case FileProcessingType.TitleNews:
- limitsResult = TitleNewsService.ValidateLimits(content);
- break;
- }
- if (!limitsResult.Result)
- {
- if (!gitCommit.IsDeployment)
- {
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- string.Format(OverlimitMessage, limitsResult.Name, limitsResult.ErrorMessage));
- }
- }
- return limitsResult.Result;
- }
- public static bool ProcessCloudScript(GitHubAddOn gitHubAddOn, GitCommit gitCommit, Title title, out string cloudScriptFileToUpdate)
- {
- var cloudScriptFileList = gitHubAddOn.GetCloudScriptFiles(gitCommit.CommitId);
- //Combine all the files they have that are .js into a single concatenated .js file.
- var builder = new StringBuilder();
- foreach (var rf in cloudScriptFileList)
- {
- builder.Append(rf.Content);
- builder.Append("\n");
- }
- cloudScriptFileToUpdate = builder.ToString();
- //Check to make sure that the files are within the limits and rules defined for cloud script files.
- if (!CheckOverLimit(FileProcessingType.CloudScript, builder.ToString(), gitCommit))
- {
- return false;
- }
- var cloudScriptFilesToValidate = new List<CloudScriptFile>()
- {
- new CloudScriptFile()
- {
- Filename = CloudScriptFileName,
- FileContents = builder.ToString()
- }
- };
- //Validate CloudScript Logic to ensure the script is runnable.
- var result = CloudLogicService.ValidateCloudLogic(title, 0, cloudScriptFilesToValidate);
- if (result.Status != HttpStatusCode.OK)
- {
- if (result.Error != null && result.Error.errorDetails != null)
- {
- var sbErrorDetails = new StringBuilder();
- foreach (var error in result.Error.errorDetails)
- {
- sbErrorDetails.Append(error);
- }
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess, result.Error.errorMessage);
- }
- else
- {
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- result.Error != null ? result.Error.errorMessage : string.Empty);
- Log.DebugFormat("Failed to process webhook - {0}",
- result.Error != null ? result.Error.errorMessage : string.Empty);
- }
- return false;
- }
- return true;
- }
- public static bool ProcessCatalog(GitHubAddOn gitHubAddOn, GitCommit gitCommit, Title title, out List<CatalogContainer> catalogItemsToUpdate )
- {
- var catalogFiles = gitHubAddOn.GetDataFiles(gitCommit.CommitId, CatalogPath, JsonExt);
- //Setting to null, so that if we fail this value is null and can be null checked.
- catalogItemsToUpdate = null;
- //Get All catalogs
- if (catalogFiles.Count == 0)
- {
- return false;
- }
- var catalogs = new List<CatalogContainer>();
- //First loop make sure no files have issues.
- foreach (var rf in catalogFiles)
- {
- if (!CheckOverLimit(FileProcessingType.Catalog, rf.Content, gitCommit))
- {
- return false;
- }
- try
- {
- var data = JsonConvert.DeserializeObject<CatalogContainer>(rf.Content);
- if (data == null)
- {
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- string.Format(DeserilizationMessage,"Catalog", rf.Name));
- return false;
- }
- catalogs.Add(data);
- }
- catch (Exception e)
- {
- Log.DebugFormat(string.Format(DeserilizationMessage, "Catalog", rf.Name),e);
- //Not sure if it would be helpful for the user to know the stacktrace of a desearilization failure
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- string.Format(DeserilizationMessage, "Catalog", rf.Name));
- return false;
- }
- }
- catalogItemsToUpdate = catalogs;
- return true;
- }
- public static bool ProcessTitleData(GitHubAddOn gitHubAddOn, GitCommit gitCommit, Title title, out Dictionary<string,string> titleDataToUpdate )
- {
- //Setting to null, so that if we fail this value is null and can be null checked.
- titleDataToUpdate = null;
- var titleDataFiles = gitHubAddOn.GetDataFiles(gitCommit.CommitId, TitleDataPath, JsonExt);
- var titleDataFile = titleDataFiles.Find(tdf => tdf.Name.ToLower().Equals("titledata"));
- if (titleDataFile == null || !CheckOverLimit(FileProcessingType.TitleData, titleDataFile.Content, gitCommit))
- {
- return false;
- }
- //Find only the file named TitleData;
- var titleData = new Dictionary<string, string>();
- try
- {
- titleData = JsonConvert.DeserializeObject<Dictionary<string,string>>(titleDataFile.Content);
- if (titleData == null)
- {
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- string.Format(DeserilizationMessage,"TitleData", titleDataFile.Name));
- return false;
- }
- }
- catch (Exception e)
- {
- Log.DebugFormat(string.Format(DeserilizationMessage, "TitleData", titleDataFile.Name), e);
- //Not sure if it would be helpful for the user to know the stacktrace of a desearilization failure
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- string.Format(DeserilizationMessage,"TitleData", titleDataFile.Name));
- return false;
- }
- titleDataToUpdate = titleData;
- return true;
- }
- public static bool ProcessStores(GitHubAddOn gitHubAddOn, GitCommit gitCommit, Title title, out List<StoreContainer> storesDataToUpdate)
- {
- //Setting to null, so that if we fail this value is null and can be null checked.
- storesDataToUpdate = null;
- var storesFiles = gitHubAddOn.GetDataFiles(gitCommit.CommitId, StorePath, JsonExt);
- //Get All Stores
- if (storesFiles.Count == 0)
- {
- return false;
- }
- //First loop make sure no files have issues.
- foreach (var rf in storesFiles)
- {
- if (!CheckOverLimit(FileProcessingType.Store, rf.Content, gitCommit))
- {
- return false;
- }
- }
- var stores = new List<StoreContainer>();
- //if no files have issues then go ahead and update them.
- foreach (var rf in storesFiles)
- {
- try
- {
- var store = JsonConvert.DeserializeObject<StoreContainer>(rf.Content);
- stores.Add(store);
- }
- catch (Exception e)
- {
- Log.DebugFormat(string.Format(DeserilizationMessage, "Store", rf.Name), e);
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- string.Format(DeserilizationMessage,"Store", rf.Name));
- return false;
- }
- }
- storesDataToUpdate = stores;
- return true;
- }
- public static bool ProcessDropTables(GitHubAddOn gitHubAddOn, GitCommit gitCommit, Title title, out List<RandomResultTableListing> dropTableToUpdate)
- {
- //Setting to null, so that if we fail this value is null and can be null checked.
- dropTableToUpdate = null;
- var dropTables = gitHubAddOn.GetDataFiles(gitCommit.CommitId, DropTablePath, JsonExt);
- //Get All DropTables
- if (dropTables.Count == 0)
- {
- return false;
- }
- //First loop make sure no files have issues and deserialize.
- var droptables = new List<RandomResultTableListing>();
- var alldropTables = new Dictionary<string, RandomResultTable>();
- foreach (var rf in dropTables)
- {
- if (!CheckOverLimit(FileProcessingType.DropTable, rf.Content, gitCommit))
- {
- return false;
- }
- try
- {
- var dropTable = JsonConvert.DeserializeObject<RandomResultTableListing>(rf.Content);
- alldropTables.Add(dropTable.TableId, dropTable);
- }
- catch (Exception e)
- {
- Log.DebugFormat(string.Format(DeserilizationMessage, "DropTable", rf.Name), e);
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- string.Format(DeserilizationMessage,"DropTable", rf.Name));
- return false;
- }
- }
- //all validations must pass in order to continue processing the droptables.
- foreach (var droptable in droptables)
- {
- var catalog = CatalogService.GetCatalogForTitle(title.Id, droptable.CatalogVersion);
- var errors = new StringBuilder();
- if (DropTableService.ValidateTable(droptable, alldropTables ,catalog, errors))
- {
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- string.Format(ValidationMessage, "DropTable", droptable.TableId));
- droptables.Remove(droptable);
- return false;
- }
- }
- dropTableToUpdate = droptables;
- return true;
- }
- public static bool ProcessCurrency(GitHubAddOn gitHubAddOn, GitCommit gitCommit, Title title, out List<VirtualCurrencyModel> virtualCurrenciesToUpdate)
- {
- //Setting to null, so that if we fail this value is null and can be null checked.
- virtualCurrenciesToUpdate = null;
- var currencyFiles = gitHubAddOn.GetDataFiles(gitCommit.CommitId, CurrencyPath, JsonExt);
- //We are only going to support one file in this directroy.
- //It can be named anything .json
- if (currencyFiles.Count == 0 || !CheckOverLimit(FileProcessingType.Currency, currencyFiles[0].Content, gitCommit))
- {
- return false;
- }
- //Only get a file named Currency.
- var currencyDataFile = currencyFiles.Find(cf => cf.Name.ToLower().Contains("currency"));
- var currencyData = new List<VirtualCurrencyModel>();
- try
- {
- currencyData = JsonConvert.DeserializeObject<List<VirtualCurrencyModel>>(currencyDataFile.Content);
- if (currencyData == null)
- {
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- string.Format(DeserilizationMessage,"Currency", currencyDataFile.Name));
- return false;
- }
- }
- catch (Exception e)
- {
- Log.DebugFormat(string.Format(DeserilizationMessage, "Currency", currencyDataFile.Name), e);
- //Not sure if it would be helpful for the user to know the stacktrace of a desearilization failure
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- string.Format(DeserilizationMessage,"Currency", currencyDataFile.Name));
- return false;
- }
- if (currencyData.Count == 0)
- {
- return false;
- }
- virtualCurrenciesToUpdate = currencyData;
- return true;
- }
- public static bool ProcessTitleNews(GitHubAddOn gitHubAddOn, GitCommit gitCommit, Title title, out List<TitleNews> titleNewsToUpdate )
- {
- //Setting to null, so that if we fail this value is null and can be null checked.
- titleNewsToUpdate = null;
- var titleNews = gitHubAddOn.GetDataFiles(gitCommit.CommitId, TitleNewsPath, JsonExt);
- //We are only going to support one file in this directroy.
- //It can be named anything .json
- if (titleNews.Count == 0 || !CheckOverLimit(FileProcessingType.Catalog, titleNews[0].Content, gitCommit))
- {
- return false;
- }
- //Get the file named TitleNews.json
- var titleNewsFile = titleNews.Find(tn => tn.Name.ToLower().Contains("titlenews"));
- if (titleNewsFile == null)
- {
- return false;
- }
- var titlenews = new List<TitleNews>();
- try
- {
- titlenews = JsonConvert.DeserializeObject<List<TitleNews>>(titleNewsFile.Content);
- }
- catch(Exception e)
- {
- Log.DebugFormat(string.Format(DeserilizationMessage, "Title News", titleNewsFile.Name), e);
- //Not sure if it would be helpful for the user to know the stacktrace of a desearilization failure
- UpdateWebHookStatus(gitCommit, GitHubCommitStatus.FailedToprocess,
- string.Format(DeserilizationMessage, "Title News", titleNewsFile.Name));
- return false;
- }
- titleNewsToUpdate = titlenews;
- return true;
- }
- public static void UpdateWebHookStatus(GitCommit gitHubCommit, GitHubCommitStatus status, Exception error = null)
- {
- var errorMessage = error != null ? error.Message : string.Empty;
- Log.DebugFormat(errorMessage, error);
- UpdateWebHookStatus(gitHubCommit, status, errorMessage);
- }
- private static void UpdateWebHookStatus(GitCommit gitHubCommit, GitHubCommitStatus status, string errorMessage)
- {
- Log.DebugFormat(UpdateStatusMessage, gitHubCommit.TitleId, gitHubCommit.CommitId, gitHubCommit.Status, status);
- //TODO: Log playstream Event.
- try
- {
- gitHubCommit.Status = status;
- gitHubCommit.ProcessErrorMessage = errorMessage;
- var gitCommit = GitCommit.Get(gitHubCommit.CommitId, gitHubCommit.TitleId, false);
- if (gitCommit == null)
- {
- GitCommit.Create(gitHubCommit, false);
- }
- else
- {
- gitHubCommit.SetDynamoStore();
- gitHubCommit.Update(true);
- }
- }
- catch
- {
- Log.ErrorFormat(
- "Failed to Update Commit record - GitCommit:{0} for Repo:{1} on Title:{2} ",
- gitHubCommit.CommitId, gitHubCommit.RepositoryName, gitHubCommit.TitleId);
- throw;
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement