public async Task DownloadProductFileAsync(long id, string title, string sourceUri, string destination, IStatus status)
        {
            if (string.IsNullOrEmpty(sourceUri))
            {
                return;
            }

            var sourceUriSansSession = formatUriRemoveSessionDelegate.Format(sourceUri);
            var destinationUri       = formatValidationFileDelegate.Format(sourceUriSansSession);

            // return early if validation is not expected for this file
            if (!confirmValidationExpectedDelegate.Confirm(sourceUriSansSession))
            {
                return;
            }

            if (fileController.Exists(destinationUri))
            {
                await statusController.InformAsync(status, "Validation file already exists, will not be redownloading");

                return;
            }

            var validationSourceUri = formatValidationUriDelegate.Format(sourceUri);

            var downloadValidationFileTask = await statusController.CreateAsync(status, "Download validation file");

            await downloadFromUriAsyncDelegate.DownloadFromUriAsync(
                validationSourceUri,
                validationDirectoryDelegate.GetDirectory(string.Empty),
                downloadValidationFileTask);

            await statusController.CompleteAsync(downloadValidationFileTask);
        }
コード例 #2
0
        public override async Task ProcessActivityAsync(IStatus status)
        {
            // This activity will centralize processing and marking updated account products.
            // Currently the process is the following:
            // 1) Itemize account products that were created after last updateData-accountProducts
            // Please note, that every updateData-accountProducts that results in new products will
            // overwrite this timestamp, so it's expected that updateData-updated is run between the
            // sessions that produce new account products.
            // 2) Itemize all account products and confirm is isNew or Updates passes the condition
            // ...
            // In the future additional heuristics can be employed - such as using products, not just
            // account products and other. Currently they are considered as YAGNI

            var updateDataUpdatedStatus = await statusController.CreateAsync(status, "Process updated account products");

            var addNewlyCreatedStatus = await statusController.CreateAsync(updateDataUpdatedStatus, "Add account products created since last data update");

            var accountProductsNewOrUpdated = new List <long>();

            //var lastUpdatedAccountProductsData = await activityContextCreatedIndexController.GetCreatedAsync(
            //    activityContextController.ToString((A.UpdateData, Context.AccountProducts)), addNewlyCreatedStatus);

            //var newlyCreatedAccountProducts = await accountProductDataController.ItemizeAsync(lastUpdatedAccountProductsData, addNewlyCreatedStatus);

            //accountProductsNewOrUpdated.AddRange(newlyCreatedAccountProducts);

            await statusController.CompleteAsync(addNewlyCreatedStatus);

            var addUpdatedAccountProductsStatus = await statusController.CreateAsync(updateDataUpdatedStatus, "Add updated account products");

            var current = 0;

            foreach (var id in await accountProductDataController.ItemizeAllAsync(addUpdatedAccountProductsStatus))
            {
                await statusController.UpdateProgressAsync(
                    addUpdatedAccountProductsStatus,
                    ++current,
                    await accountProductDataController.CountAsync(addUpdatedAccountProductsStatus),
                    id.ToString());

                var accountProduct = await accountProductDataController.GetByIdAsync(id, status);

                if (confirmAccountProductUpdatedDelegate.Confirm(accountProduct))
                {
                    accountProductsNewOrUpdated.Add(id);
                }
            }

            foreach (var accountProduct in accountProductsNewOrUpdated)
            {
                await updatedIndexController.CreateAsync(accountProduct, addUpdatedAccountProductsStatus);
            }

            await statusController.CompleteAsync(addUpdatedAccountProductsStatus);

            await updatedIndexController.CommitAsync(updateDataUpdatedStatus);

            await statusController.CompleteAsync(updateDataUpdatedStatus);
        }
コード例 #3
0
        public async Task <IFileValidationResult> ValidateFileAsync(string productFileUri, string validationUri, IStatus status)
        {
            var fileValidation = new FileValidation
            {
                Filename = productFileUri
            };

            if (string.IsNullOrEmpty(productFileUri))
            {
                throw new ArgumentNullException();
            }

            if (string.IsNullOrEmpty(validationUri))
            {
                throw new ArgumentNullException();
            }

            fileValidation.ValidationExpected = confirmValidationExpectedDelegate.Confirm(productFileUri);

            if (!fileValidation.ValidationExpected)
            {
                return(fileValidation);
            }

            fileValidation.ValidationFileExists = VerifyValidationFileExists(validationUri);
            fileValidation.ProductFileExists    = VerifyProductFileExists(productFileUri);

            if (!fileValidation.ValidationFileExists ||
                !fileValidation.ProductFileExists)
            {
                return(fileValidation);
            }

            try
            {
                using (var xmlStream = streamController.OpenReadable(validationUri))
                    validationXml.Load(xmlStream);

                fileValidation.ValidationFileIsValid = true;
            }
            catch
            {
                fileValidation.ValidationFileIsValid = false;
                return(fileValidation);
            }

            var fileElement = validationXml.GetElementsByTagName("file");

            if (fileElement == null ||
                fileElement.Count < 1 ||
                fileElement[0] == null ||
                fileElement[0].Attributes == null)
            {
                fileValidation.ValidationFileIsValid = false;
                return(fileValidation);
            }

            long   expectedSize;
            string expectedName;
            int    chunks;
            bool   available;

            try
            {
                expectedSize = long.Parse(fileElement[0].Attributes["total_size"]?.Value);
                expectedName = fileElement[0].Attributes["name"]?.Value;
                chunks       = int.Parse(fileElement[0].Attributes["chunks"]?.Value);
                available    = fileElement[0].Attributes["available"]?.Value == "1";

                if (!available)
                {
                    var notAvailableMessage = fileElement[0].Attributes["notavailablemsg"]?.Value;
                    throw new Exception(notAvailableMessage);
                }
            }
            catch
            {
                fileValidation.ValidationFileIsValid = false;
                return(fileValidation);
            }

            fileValidation.FilenameVerified = VerifyFilename(productFileUri, expectedName);
            fileValidation.SizeVerified     = VerifySize(productFileUri, expectedSize);

            var chunksValidation = new List <IChunkValidation>();

            using (var fileStream = streamController.OpenReadable(productFileUri))
            {
                long length = 0;

                foreach (XmlNode chunkElement in fileElement[0].ChildNodes)
                {
                    if (chunkElement.Name != "chunk")
                    {
                        continue;
                    }

                    long   from, to = 0;
                    string expectedMd5 = string.Empty;

                    from        = long.Parse(chunkElement.Attributes["from"]?.Value);
                    to          = long.Parse(chunkElement.Attributes["to"]?.Value);
                    length     += (to - from);
                    expectedMd5 = chunkElement.FirstChild.Value;

                    chunksValidation.Add(await VerifyChunkAsync(fileStream, from, to, expectedMd5, status));

                    await statusController.UpdateProgressAsync(
                        status,
                        length,
                        expectedSize,
                        productFileUri,
                        DataUnits.Bytes);
                }

                fileValidation.Chunks = chunksValidation.ToArray();

                await statusController.UpdateProgressAsync(status, length, expectedSize, productFileUri);
            }

            await statusController.InformAsync(status, $"Validation result: {productFileUri} is valid: " +
                                               $"{validationResultController.ProductFileIsValid(fileValidation)}");

            return(fileValidation);
        }
コード例 #4
0
        public async Task <GameDetails> GetDeserializedAsync(IStatus status, string uri, IDictionary <string, string> parameters = null)
        {
            // GOG.com quirk
            // GameDetails as sent by GOG.com servers have an intersting data structure for downloads:
            // it's represented as an array, where first element is a string with the language,
            // followed by actual download details, something like:
            // [ "English", { // download1 }, { // download2 } ]
            // Which of course is not a problem for JavaScript, but is a problem for
            // deserializing into strongly typed C# data structures.
            // To work around this we wrapped encapsulated usual network requests,
            // data transformation and desearialization in a sinlge package.
            // To process downloads we do the following:
            // - if response contains language downloads, signified by [[
            // - extract actual language information and remove it from the string
            // - deserialize downloads into OperatingSystemsDownloads collection
            // - assign languages, since we know we should have as many downloads array as languages

            var data = await getResourceAsyncDelegate.GetResourceAsync(status, uri, parameters);

            var gameDetails = serializationController.Deserialize <GameDetails>(data);

            if (gameDetails == null)
            {
                return(null);
            }

            var gameDetailsLanguageDownloads = new List <OperatingSystemsDownloads>();

            if (!confirmStringContainsLanguageDownloadsDelegate.Confirm(data))
            {
                return(gameDetails);
            }
            var downloadStrings = itemizeGameDetailsDownloadsDelegate.Itemize(data);

            foreach (var downloadString in downloadStrings)
            {
                var downloadLanguages = itemizeDownloadLanguagesDelegate.Itemize(downloadString);
                if (downloadLanguages == null)
                {
                    throw new InvalidOperationException("Cannot find any download languages or download language format changed.");
                }

                // ... and remove download lanugage strings from downloads
                var downloadsStringSansLanguages = replaceMultipleStringsDelegate.ReplaceMultiple(
                    downloadString,
                    string.Empty,
                    downloadLanguages.ToArray());

                // now it should be safe to deserialize langugage downloads
                var downloads =
                    serializationController.Deserialize <OperatingSystemsDownloads[][]>(
                        downloadsStringSansLanguages);

                // and convert GOG two-dimensional array of downloads to single-dimensional array
                var languageDownloads = convert2DArrayToArrayDelegate.Convert(downloads);

                if (languageDownloads.Count() != downloadLanguages.Count())
                {
                    throw new InvalidOperationException("Number of extracted language downloads doesn't match number of languages.");
                }

                // map language downloads with the language code we extracted earlier
                var languageDownloadIndex = 0;

                collectionController.Map(downloadLanguages, language =>
                {
                    var formattedLanguage = formatDownloadLanguageDelegate.Format(language);
                    var languageCode      = languageController.GetLanguageCode(formattedLanguage);

                    languageDownloads[languageDownloadIndex++].Language = languageCode;
                });

                gameDetailsLanguageDownloads.AddRange(languageDownloads);
            }

            gameDetails.LanguageDownloads = gameDetailsLanguageDownloads.ToArray();

            return(gameDetails);
        }