◐ Shell
clean mode source ↗

Caching GraphQL queries by grokys · Pull Request #2181 · github/VisualStudio

Functionality to get a page of a list of pull requests

  • Handled by: Caching for default 8 hours and clear on refresh
    ICompiledQuery<Page<PullRequestListItemModel>> query;
    if (address.IsGitHubDotCom())
    {
    if (readPullRequests == null)
    {
    readPullRequests = new Query()
    .Repository(owner: Var(nameof(owner)), name: Var(nameof(name)))
    .PullRequests(
    first: 100,
    after: Var(nameof(after)),
    orderBy: new IssueOrder { Direction = OrderDirection.Desc, Field = IssueOrderField.CreatedAt },
    states: Var(nameof(states)))
    .Select(page => new Page<PullRequestListItemModel>
    {
    EndCursor = page.PageInfo.EndCursor,
    HasNextPage = page.PageInfo.HasNextPage,
    TotalCount = page.TotalCount,
    Items = page.Nodes.Select(pr => new ListItemAdapter
    {
    Id = pr.Id.Value,
    LastCommit = pr.Commits(null, null, 1, null).Nodes.Select(commit =>
    new LastCommitSummaryAdapter
    {
    CheckSuites = commit.Commit.CheckSuites(null, null, null, null, null).AllPages(10)
    .Select(suite => new CheckSuiteSummaryModel
    {
    CheckRuns = suite.CheckRuns(null, null, null, null, null).AllPages(10)
    .Select(run => new CheckRunSummaryModel
    {
    Conclusion = run.Conclusion.FromGraphQl(),
    Status = run.Status.FromGraphQl()
    }).ToList(),
    }).ToList(),
    Statuses = commit.Commit.Status
    .Select(context =>
    context.Contexts.Select(statusContext => new StatusSummaryModel
    {
    State = statusContext.State.FromGraphQl(),
    }).ToList()
    ).SingleOrDefault()
    }).ToList().FirstOrDefault(),
    Author = new ActorModel
    {
    Login = pr.Author.Login,
    AvatarUrl = pr.Author.AvatarUrl(null),
    },
    CommentCount = pr.Comments(0, null, null, null).TotalCount,
    Number = pr.Number,
    Reviews = pr.Reviews(null, null, null, null, null, null).AllPages().Select(review => new ReviewAdapter
    {
    Body = review.Body,
    CommentCount = review.Comments(null, null, null, null).TotalCount,
    }).ToList(),
    State = pr.State.FromGraphQl(),
    Title = pr.Title,
    UpdatedAt = pr.UpdatedAt,
    }).ToList(),
    }).Compile();
    }
    query = readPullRequests;
    }
    else
    {
    if (readPullRequestsEnterprise == null)
    {
    readPullRequestsEnterprise = new Query()
    .Repository(owner: Var(nameof(owner)), name: Var(nameof(name)))
    .PullRequests(
    first: 100,
    after: Var(nameof(after)),
    orderBy: new IssueOrder { Direction = OrderDirection.Desc, Field = IssueOrderField.CreatedAt },
    states: Var(nameof(states)))
    .Select(page => new Page<PullRequestListItemModel>
    {
    EndCursor = page.PageInfo.EndCursor,
    HasNextPage = page.PageInfo.HasNextPage,
    TotalCount = page.TotalCount,
    Items = page.Nodes.Select(pr => new ListItemAdapter
    {
    Id = pr.Id.Value,
    LastCommit = pr.Commits(null, null, 1, null).Nodes.Select(commit =>
    new LastCommitSummaryAdapter
    {
    Statuses = commit.Commit.Status.Select(context =>
    context == null
    ? null
    : context.Contexts
    .Select(statusContext => new StatusSummaryModel
    {
    State = statusContext.State.FromGraphQl()
    }).ToList()
    ).SingleOrDefault()
    }).ToList().FirstOrDefault(),
    Author = new ActorModel
    {
    Login = pr.Author.Login,
    AvatarUrl = pr.Author.AvatarUrl(null),
    },
    CommentCount = pr.Comments(0, null, null, null).TotalCount,
    Number = pr.Number,
    Reviews = pr.Reviews(null, null, null, null, null, null).AllPages().Select(review => new ReviewAdapter
    {
    Body = review.Body,
    CommentCount = review.Comments(null, null, null, null).TotalCount,
    }).ToList(),
    State = pr.State.FromGraphQl(),
    Title = pr.Title,
    UpdatedAt = pr.UpdatedAt,
    }).ToList(),
    }).Compile();
    }
    query = readPullRequestsEnterprise;
    }
    var graphql = await graphqlFactory.CreateConnection(address);
    var vars = new Dictionary<string, object>
    {
    { nameof(owner), owner },
    { nameof(name), name },
    { nameof(after), after },
    { nameof(states), states.Select(x => (Octokit.GraphQL.Model.PullRequestState)x).ToList() },
    };
    var region = owner + '/' + name + "/pr-list";
    var result = await graphql.Run(query, vars, regionName: region);

Functionality to get the pull request detail

  • Handled by: Caching for default 8 hours and clear on item refresh
    if (readPullRequest == null)
    {
    readPullRequest = new Query()
    .Repository(owner: Var(nameof(owner)), name: Var(nameof(name)))
    .PullRequest(number: Var(nameof(number)))
    .Select(pr => new PullRequestDetailModel
    {
    Id = pr.Id.Value,
    Number = pr.Number,
    Author = new ActorModel
    {
    Login = pr.Author.Login,
    AvatarUrl = pr.Author.AvatarUrl(null),
    },
    Title = pr.Title,
    Body = pr.Body,
    BaseRefSha = pr.BaseRefOid,
    BaseRefName = pr.BaseRefName,
    BaseRepositoryOwner = pr.Repository.Owner.Login,
    HeadRefName = pr.HeadRefName,
    HeadRefSha = pr.HeadRefOid,
    HeadRepositoryOwner = pr.HeadRepositoryOwner != null ? pr.HeadRepositoryOwner.Login : null,
    State = pr.State.FromGraphQl(),
    UpdatedAt = pr.UpdatedAt,
    CommentCount = pr.Comments(0, null, null, null).TotalCount,
    Comments = pr.Comments(null, null, null, null).AllPages().Select(comment => new CommentModel
    {
    Id = comment.Id.Value,
    Author = new ActorModel
    {
    Login = comment.Author.Login,
    AvatarUrl = comment.Author.AvatarUrl(null),
    },
    Body = comment.Body,
    CreatedAt = comment.CreatedAt,
    DatabaseId = comment.DatabaseId.Value,
    Url = comment.Url,
    }).ToList(),
    Reviews = pr.Reviews(null, null, null, null, null, null).AllPages().Select(review => new PullRequestReviewModel
    {
    Id = review.Id.Value,
    Body = review.Body,
    CommitId = review.Commit.Oid,
    State = review.State.FromGraphQl(),
    SubmittedAt = review.SubmittedAt,
    Author = new ActorModel
    {
    Login = review.Author.Login,
    AvatarUrl = review.Author.AvatarUrl(null),
    },
    Comments = review.Comments(null, null, null, null).AllPages().Select(comment => new CommentAdapter
    {
    Id = comment.Id.Value,
    PullRequestId = comment.PullRequest.Number,
    DatabaseId = comment.DatabaseId.Value,
    Author = new ActorModel
    {
    Login = comment.Author.Login,
    AvatarUrl = comment.Author.AvatarUrl(null),
    },
    Body = comment.Body,
    Path = comment.Path,
    CommitSha = comment.Commit.Oid,
    DiffHunk = comment.DiffHunk,
    Position = comment.Position,
    OriginalPosition = comment.OriginalPosition,
    OriginalCommitId = comment.OriginalCommit.Oid,
    ReplyTo = comment.ReplyTo != null ? comment.ReplyTo.Id.Value : null,
    CreatedAt = comment.CreatedAt,
    Url = comment.Url,
    }).ToList(),
    }).ToList(),
    Timeline = pr.Timeline(null, null, null, null, null).AllPages().Select(item => item.Switch<object>(when =>
    when.Commit(commit => new CommitModel
    {
    AbbreviatedOid = commit.AbbreviatedOid,
    // TODO: commit.Author.User can be null
    Author = new ActorModel
    {
    Login = commit.Author.User.Login,
    AvatarUrl = commit.Author.User.AvatarUrl(null),
    },
    MessageHeadline = commit.MessageHeadline,
    Oid = commit.Oid,
    }).IssueComment(comment => new CommentModel
    {
    Author = new ActorModel
    {
    Login = comment.Author.Login,
    AvatarUrl = comment.Author.AvatarUrl(null),
    },
    Body = comment.Body,
    CreatedAt = comment.CreatedAt,
    DatabaseId = comment.DatabaseId.Value,
    Id = comment.Id.Value,
    Url = comment.Url,
    }))).ToList()
    }).Compile();
    }
    var vars = new Dictionary<string, object>
    {
    { nameof(owner), owner },
    { nameof(name), name },
    { nameof(number), number },
    };
    var connection = await graphqlFactory.CreateConnection(address);
    var result = await connection.Run(readPullRequest, vars, refresh: refresh);

Functionality to get the last commit of a pull request for statuses

  • Handled by: Caching for default 8 hours and clear on item refresh
    async Task<LastCommitAdapter> GetPullRequestLastCommitAdapter(HostAddress address, string owner, string name, int number, bool refresh)
    {
    ICompiledQuery<IEnumerable<LastCommitAdapter>> query;
    if (address.IsGitHubDotCom())
    {
    if (readCommitStatuses == null)
    {
    readCommitStatuses = new Query()
    .Repository(owner: Var(nameof(owner)), name: Var(nameof(name)))
    .PullRequest(number: Var(nameof(number))).Commits(last: 1).Nodes.Select(
    commit => new LastCommitAdapter
    {
    HeadSha = commit.Commit.Oid,
    CheckSuites = commit.Commit.CheckSuites(null, null, null, null, null).AllPages(10)
    .Select(suite => new CheckSuiteModel
    {
    CheckRuns = suite.CheckRuns(null, null, null, null, null).AllPages(10)
    .Select(run => new CheckRunModel
    {
    Id = run.Id.Value,
    Conclusion = run.Conclusion.FromGraphQl(),
    Status = run.Status.FromGraphQl(),
    Name = run.Name,
    DetailsUrl = run.Permalink,
    Summary = run.Summary,
    Text = run.Text,
    Annotations = run.Annotations(null, null, null, null).AllPages()
    .Select(annotation => new CheckRunAnnotationModel
    {
    Title = annotation.Title,
    Message = annotation.Message,
    Path = annotation.Path,
    AnnotationLevel = annotation.AnnotationLevel.Value.FromGraphQl(),
    StartLine = annotation.Location.Start.Line,
    EndLine = annotation.Location.End.Line,
    }).ToList()
    }).ToList(),
    ApplicationName = suite.App != null ? suite.App.Name : "Private App"
    }).ToList(),
    Statuses = commit.Commit.Status
    .Select(context =>
    context.Contexts.Select(statusContext => new StatusModel
    {
    State = statusContext.State.FromGraphQl(),
    Context = statusContext.Context,
    TargetUrl = statusContext.TargetUrl,
    Description = statusContext.Description
    }).ToList()
    ).SingleOrDefault()
    }
    ).Compile();
    }
    query = readCommitStatuses;
    }
    else
    {
    if (readCommitStatusesEnterprise == null)
    {
    readCommitStatusesEnterprise = new Query()
    .Repository(owner: Var(nameof(owner)), name: Var(nameof(name)))
    .PullRequest(number: Var(nameof(number))).Commits(last: 1).Nodes.Select(
    commit => new LastCommitAdapter
    {
    Statuses = commit.Commit.Status == null ? null : commit.Commit.Status
    .Select(context => context == null
    ? null
    : context.Contexts
    .Select(statusContext => new StatusModel
    {
    State = statusContext.State.FromGraphQl(),
    Context = statusContext.Context,
    TargetUrl = statusContext.TargetUrl,
    Description = statusContext.Description,
    }).ToList()
    ).SingleOrDefault()
    }
    ).Compile();
    }
    query = readCommitStatusesEnterprise;
    }
    var vars = new Dictionary<string, object>
    {
    { nameof(owner), owner },
    { nameof(name), name },
    { nameof(number), number },
    };
    var connection = await graphqlFactory.CreateConnection(address);
    var result = await connection.Run(query, vars, refresh);
    return result.First();
    }

Functionality to get a list of viewer repositories for cloning

  • Handled by: Caching for default 8 hours and clear on refresh
    public async Task<ViewerRepositoriesModel> ReadViewerRepositories(HostAddress address)
    {
    if (readViewerRepositories == null)
    {
    var order = new RepositoryOrder
    {
    Field = RepositoryOrderField.PushedAt,
    Direction = OrderDirection.Desc
    };
    var repositorySelection = new Fragment<Repository, RepositoryListItemModel>(
    "repository",
    repo => new RepositoryListItemModel
    {
    IsFork = repo.IsFork,
    IsPrivate = repo.IsPrivate,
    Name = repo.Name,
    Owner = repo.Owner.Login,
    Url = new Uri(repo.Url),
    });
    readViewerRepositories = new Query()
    .Viewer
    .Select(viewer => new ViewerRepositoriesModel
    {
    Owner = viewer.Login,
    Repositories = viewer.Repositories(null, null, null, null, null, null, null, order, null, null)
    .AllPages()
    .Select(repositorySelection).ToList(),
    ContributedToRepositories = viewer.RepositoriesContributedTo(100, null, null, null, null, null, null, order, null)
    .Nodes
    .Select(repositorySelection).ToList(),
    Organizations = viewer.Organizations(null, null, null, null).AllPages().Select(org => new
    {
    org.Login,
    Repositories = org.Repositories(100, null, null, null, null, null, null, order, null, null)
    .Nodes
    .Select(repositorySelection).ToList()
    }).ToDictionary(x => x.Login, x => (IReadOnlyList<RepositoryListItemModel>)x.Repositories),
    }).Compile();
    }
    var graphql = await graphqlFactory.CreateConnection(address).ConfigureAwait(false);
    var result = await graphql.Run(readViewerRepositories).ConfigureAwait(false);
    return result;
    }

Functionality to get a pull request node id

  • Handled by: Caching for default 8 hours
    public async Task<string> GetGraphQLPullRequestId(
    LocalRepositoryModel localRepository,
    string repositoryOwner,
    int number)
    {
    var address = HostAddress.Create(localRepository.CloneUrl.Host);
    var graphql = await graphqlFactory.CreateConnection(address);
    var query = new Query()
    .Repository(owner: repositoryOwner, name: localRepository.Name)
    .PullRequest(number)
    .Select(x => x.Id);
    return (await graphql.Run(query)).Value;
    }

Functionality to get assignable users

  • Handled by: Caching for 1 hour
    public async Task<Page<ActorModel>> ReadAssignableUsers(
    HostAddress address,
    string owner,
    string name,
    string after)
    {
    if (readAssignableUsers == null)
    {
    readAssignableUsers = new Query()
    .Repository(owner: Var(nameof(owner)), name: Var(nameof(name)))
    .AssignableUsers(first: 100, after: Var(nameof(after)))
    .Select(connection => new Page<ActorModel>
    {
    EndCursor = connection.PageInfo.EndCursor,
    HasNextPage = connection.PageInfo.HasNextPage,
    TotalCount = connection.TotalCount,
    Items = connection.Nodes.Select(user => new ActorModel
    {
    AvatarUrl = user.AvatarUrl(30),
    Login = user.Login,
    }).ToList(),
    }).Compile();
    }
    var graphql = await graphqlFactory.CreateConnection(address);
    var vars = new Dictionary<string, object>
    {
    { nameof(owner), owner },
    { nameof(name), name },
    { nameof(after), after },
    };
    return await graphql.Run(readAssignableUsers, vars);
    }

Functionality to get the viewer details

  • Handled by: Caching for 10 minutes
    public virtual async Task<ActorModel> ReadViewer(HostAddress address)
    {
    if (readViewer == null)
    {
    readViewer = new Query()
    .Viewer
    .Select(x => new ActorModel
    {
    Login = x.Login,
    AvatarUrl = x.AvatarUrl(null),
    }).Compile();
    }
    var connection = await graphqlFactory.CreateConnection(address);
    return await connection.Run(readViewer);
    }

Functionality to get a parent repo if one exists

  • Handled by: Caching for default 8 hours
    public async Task<(string owner, string name)?> FindParent(HostAddress address, string owner, string name)
    {
    Guard.ArgumentNotNull(address, nameof(address));
    Guard.ArgumentNotEmptyString(owner, nameof(owner));
    Guard.ArgumentNotEmptyString(name, nameof(name));
    if (readParentOwnerLogin == null)
    {
    readParentOwnerLogin = new Query()
    .Repository(owner: Var(nameof(owner)), name: Var(nameof(name)))
    .Select(r => r.Parent != null ? Tuple.Create(r.Parent.Owner.Login, r.Parent.Name) : null)
    .Compile();
    }
    var vars = new Dictionary<string, object>
    {
    { nameof(owner), owner },
    { nameof(name), name },
    };
    var graphql = await graphqlFactory.CreateConnection(address);
    var result = await graphql.Run(readParentOwnerLogin, vars);
    return result != null ? (result.Item1, result.Item2) : ((string, string)?)null;
    }