Пример #1
0
        /// <summary>
        /// Gets the list of updates matching the query filter from an upstream update server.
        /// <para>
        /// If the destinatin metadata sink also implements <see cref="IMetadataSource"/>, a delta query for changed categories is performed./>
        /// </para>
        /// </summary>
        /// <param name="updatesFilter">Updates filter. See <see cref="QueryFilter"/> for details.</param>
        /// <param name="destination">Metadata collection where to write the result. If the destination implements IMetadataSource, a delta retrieve is performed</param>
        /// <remarks>
        /// </remarks>
        public async Task GetUpdates(QueryFilter updatesFilter, IMetadataSink destination)
        {
            var deltaMetadataSource = destination as IMetadataSource;

            var progress = new MetadataQueryProgress();

            if (updatesFilter == null || updatesFilter.ProductsFilter.Count == 0 || updatesFilter.ClassificationsFilter.Count == 0)
            {
                throw new Exception("The filter cannot be null of empty");
            }

            if (AccessToken == null || AccessToken.ExpiresIn(TimeSpan.FromMinutes(2)))
            {
                await RefreshAccessToken(deltaMetadataSource?.UpstreamAccountName, deltaMetadataSource?.UpstreamAccountGuid);
            }

            // If no configuration is known, query it now
            if (ConfigData == null)
            {
                await RefreshServerConfigData();
            }

            progress.CurrentTask = MetadataQueryStage.GetRevisionIdsStart;
            MetadataQueryProgress?.Invoke(this, progress);

            var updatesAnchor = deltaMetadataSource?.GetAnchorForFilter(updatesFilter);

            updatesFilter.Anchor = updatesAnchor;
            var updatesQueryResult = await GetUpdateIds(updatesFilter);

            progress.CurrentTask = MetadataQueryStage.GetRevisionIdsEnd;
            MetadataQueryProgress?.Invoke(this, progress);

            // Find all updates that did not change
            var unchangedUpdates = GetUnchangedUpdates(deltaMetadataSource?.UpdatesIndex, updatesQueryResult.identities);

            // Create the list of updates to query data for. Remove those updates that were reported as "new"
            // but for which we already have metadata
            var updatesToRetrieveDataFor = updatesQueryResult.identities.Where(
                newUpdateId => !unchangedUpdates.ContainsKey(newUpdateId));

            progress.CurrentTask = MetadataQueryStage.GetUpdateMetadataStart;
            progress.Maximum     = updatesToRetrieveDataFor.Count();
            progress.Current     = 0;
            MetadataQueryProgress?.Invoke(this, progress);

            GetUpdateDataForIds(
                updatesToRetrieveDataFor.Select(id => id.Raw).ToList(), destination);

            // Update the QueryResult filter and anchor
            updatesFilter.Anchor = updatesQueryResult.anchor;
            destination.SetQueryFilter(updatesFilter);

            progress.CurrentTask = MetadataQueryStage.GetUpdateMetadataEnd;
            MetadataQueryProgress?.Invoke(this, progress);
        }
Пример #2
0
        /// <summary>
        /// Gets the list of categories from the upstream server and adds them to the specified metadata collection.
        /// </summary>
        /// <param name="destination">Metadata collection where to add the results. If the collection implements <see cref="IMetadataSource"/>, only delta changes are retrieved and added to the destination.</param>
        public async Task GetCategories(IMetadataSink destination)
        {
            var deltaMetadataSource = destination as IMetadataSource;
            var progress            = new MetadataQueryProgress();

            if (AccessToken == null || AccessToken.ExpiresIn(TimeSpan.FromMinutes(2)))
            {
                await RefreshAccessToken(deltaMetadataSource?.UpstreamAccountName, deltaMetadataSource?.UpstreamAccountGuid);
            }

            // If no configuration is known, query it now
            if (ConfigData == null)
            {
                await RefreshServerConfigData();
            }

            // Query IDs for all categories known to the upstream server
            progress.CurrentTask = MetadataQueryStage.GetRevisionIdsStart;
            MetadataQueryProgress?.Invoke(this, progress);

            var categoriesAnchor    = deltaMetadataSource?.CategoriesAnchor;
            var categoryQueryResult = await GetCategoryIds(categoriesAnchor);

            progress.CurrentTask = MetadataQueryStage.GetRevisionIdsEnd;
            MetadataQueryProgress?.Invoke(this, progress);

            var cachedCategories = deltaMetadataSource?.CategoriesIndex;

            // Find all updates that did not change
            var unchangedUpdates = GetUnchangedUpdates(cachedCategories, categoryQueryResult.identities);

            // Create the list of updates to query data for. Remove those updates that were reported as "new"
            // but for which we already have metadata
            var updatesToRetrieveDataFor = categoryQueryResult.identities.Where(
                newUpdateId => !unchangedUpdates.ContainsKey(newUpdateId));

            // Retrieve metadata for all new categories
            progress.CurrentTask = MetadataQueryStage.GetUpdateMetadataStart;
            progress.Maximum     = updatesToRetrieveDataFor.Count();
            progress.Current     = 0;
            MetadataQueryProgress?.Invoke(this, progress);

            destination.SetCategoriesAnchor(categoryQueryResult.anchor);

            GetUpdateDataForIds(
                updatesToRetrieveDataFor.Select(id => id.Raw).ToList(), destination);

            progress.CurrentTask = MetadataQueryStage.GetUpdateMetadataEnd;
            MetadataQueryProgress?.Invoke(this, progress);
        }
Пример #3
0
        /// <summary>
        /// Retrieves configuration data from the upstream server.
        /// </summary>
        /// <returns>Server configuration data</returns>
        public async Task <ServerSyncConfigData> GetServerConfigData()
        {
            await RefreshAccessToken(Guid.NewGuid().ToString(), Guid.NewGuid());

            var progress = new MetadataQueryProgress();

            progress.CurrentTask = MetadataQueryStage.GetServerConfigStart;
            MetadataQueryProgress?.Invoke(this, progress);

            var result = await QueryConfigData();

            progress.CurrentTask = MetadataQueryStage.GetServerConfigEnd;
            MetadataQueryProgress?.Invoke(this, progress);

            return(result);
        }
Пример #4
0
        /// <summary>
        /// Retrieves update data for the list of update ids
        /// </summary>
        /// <param name="updateIds">The ids to retrieve data for</param>
        /// <param name="destination">The metadata destination to write update metadata to</param>
        private void GetUpdateDataForIds(List <UpdateIdentity> updateIds, IMetadataSink destination)
        {
            // Data retrieval is done is done in batches of upto MaxNumberOfUpdatesPerRequest
            var retrieveBatches = CreateBatchedListFromFlatList(updateIds, ConfigData.MaxNumberOfUpdatesPerRequest);

            // Progress tracking and reporting
            int batchesDone = 0;
            var progress    = new MetadataQueryProgress()
            {
                CurrentTask = MetadataQueryStage.GetUpdateMetadataProgress, Maximum = updateIds.Count, Current = 0
            };

            MetadataQueryProgress?.Invoke(this, progress);

            // Run batches in parallel
            retrieveBatches.AsParallel().ForAll(batch =>
            {
                var updateDataRequest                     = new GetUpdateDataRequest();
                updateDataRequest.GetUpdateData           = new GetUpdateDataRequestBody();
                updateDataRequest.GetUpdateData.cookie    = AccessToken.AccessCookie;
                updateDataRequest.GetUpdateData.updateIds = batch;

                GetUpdateDataResponse updateDataReply;
                int retryCount = 0;
                do
                {
                    try
                    {
                        updateDataReply = ServerSyncClient.GetUpdateDataAsync(updateDataRequest).GetAwaiter().GetResult();
                    }
                    catch (System.TimeoutException)
                    {
                        updateDataReply = null;
                    }
                    retryCount++;
                } while (updateDataReply == null && retryCount < 10);

                if (updateDataReply == null || updateDataReply.GetUpdateDataResponse1 == null || updateDataReply.GetUpdateDataResponse1.GetUpdateDataResult == null)
                {
                    throw new Exception("Failed to get update metadata");
                }

                // Parse the list of raw files into a more usable format
                var filesList = new List <UpdateFileUrl>(updateDataReply.GetUpdateDataResponse1.GetUpdateDataResult.fileUrls.Select(rawFile => new UpdateFileUrl(rawFile)));

                // First add the files information to the store; it will be used to link update files with urls later
                filesList.ForEach(file => destination.AddFile(file));

                // Add the updates to the result, converting them to a higher level representation
                destination.AddUpdates(updateDataReply.GetUpdateDataResponse1.GetUpdateDataResult.updates);

                lock (destination)
                {
                    // Track progress
                    batchesDone++;
                    progress.PercentDone = ((double)batchesDone * 100) / retrieveBatches.Count;
                    progress.Current    += batch.Count();
                    MetadataQueryProgress?.Invoke(this, progress);
                }
            });
        }