private void Merge(EntityContext context, CatalogPageEntity existing, CatalogPageEntity latest)
        {
            if (existing == null)
            {
                context.CatalogPages.Add(latest);
                return;
            }

            var commitIdToCommit = existing
                                   .CatalogCommits
                                   .ToDictionary(x => x.CommitId);

            foreach (var latestCommit in latest.CatalogCommits)
            {
                if (!commitIdToCommit.TryGetValue(latestCommit.CommitId, out var existingCommit))
                {
                    latestCommit.CatalogPage = existing;
                    context.CatalogCommits.Add(latestCommit);
                    continue;
                }

                if (latestCommit.Count != existingCommit.Count ||
                    latestCommit.CatalogLeaves.Count != existingCommit.CatalogLeaves.Count)
                {
                    throw new InvalidOperationException("The number of catalog leaves cannot change in a commit.");
                }

                var packageKeyToLeaf = existingCommit
                                       .CatalogLeaves
                                       .ToDictionary(x => x.PackageKey);

                foreach (var latestLeaf in latestCommit.CatalogLeaves)
                {
                    if (!packageKeyToLeaf.TryGetValue(latestLeaf.PackageKey, out var existingLeaf))
                    {
                        throw new InvalidOperationException("The packages in a commit cannot change.");
                    }

                    if (latestLeaf.Type != existingLeaf.Type)
                    {
                        throw new InvalidOperationException("The type of a catalog leaf cannot change.");
                    }

                    existingLeaf.RelativePath = latestLeaf.RelativePath;
                }
            }
        }
        private async Task <CatalogPageEntity> InitializeAsync(
            string pageUrl,
            IReadOnlyList <CatalogEntry> leaves,
            IReadOnlyDictionary <string, long> identityToPackageKey)
        {
            await VerifyExpectedPageUrlAsync(pageUrl);

            var pageEntity = new CatalogPageEntity
            {
                Url            = pageUrl,
                CatalogCommits = new List <CatalogCommitEntity>(),
            };

            var commits = leaves
                          .GroupBy(x => x.CommitTimeStamp.ToUniversalTime());

            foreach (var commit in commits)
            {
                var commitLeaves = commit.ToList();

                // This really only every be one, but there is an oddball:
                // https://api.nuget.org/v3/catalog0/page868.json, timestamp: 2015-04-17T23:24:26.0796162Z
                var commitId = string.Join(" ", commit
                                           .Select(x => Guid.Parse(x.CommitId))
                                           .OrderBy(x => x)
                                           .Distinct()
                                           .ToList());

                var commitEntity = new CatalogCommitEntity
                {
                    CatalogPage     = pageEntity,
                    CommitId        = commitId,
                    CommitTimestamp = commit.Key.UtcTicks,
                    CatalogLeaves   = new List <CatalogLeafEntity>(),
                    Count           = commitLeaves.Count,
                };

                pageEntity.CatalogCommits.Add(commitEntity);

                foreach (var leaf in commitLeaves)
                {
                    var identity   = $"{leaf.Id}/{leaf.Version.ToNormalizedString()}";
                    var packageKey = identityToPackageKey[identity];

                    if (leaf.Types.Count != 1)
                    {
                        throw new InvalidOperationException($"Found a catalog leaf with {leaf.Types.Count} types instead of 1.");
                    }

                    CatalogLeafType type;
                    if (leaf.IsAddOrUpdate)
                    {
                        type = CatalogLeafType.PackageDetails;
                    }
                    else if (leaf.IsDelete)
                    {
                        type = CatalogLeafType.PackageDelete;
                    }
                    else
                    {
                        throw new InvalidOperationException("Unexpected catalog leaf type.");
                    }

                    var leafEntity = new CatalogLeafEntity
                    {
                        CatalogCommit = commitEntity,
                        PackageKey    = packageKey,
                        Type          = type,
                    };

                    await VerifyExpectedLeafUrlAsync(leaf, leafEntity);

                    commitEntity.CatalogLeaves.Add(leafEntity);
                }
            }

            return(pageEntity);
        }