Skip to content

Commit

Permalink
Merge pull request #98 from dolthub/taylor/branch-pagination
Browse files Browse the repository at this point in the history
Add pagination to branch list page
  • Loading branch information
tbantle22 authored Jan 5, 2024
2 parents f85b6e9 + 938869c commit 0067668
Show file tree
Hide file tree
Showing 21 changed files with 229 additions and 109 deletions.
6 changes: 4 additions & 2 deletions graphql-server/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ type Branch {
"""
scalar Timestamp

type BranchNamesList {
type BranchList {
nextOffset: Int
list: [Branch!]!
}

Expand Down Expand Up @@ -282,7 +283,8 @@ type TagList {
type Query {
branch(databaseName: String!, branchName: String!): Branch
branchOrDefault(databaseName: String!, branchName: String): Branch
branches(databaseName: String!, sortBy: SortBranchesBy): BranchNamesList!
branches(offset: Int, databaseName: String!, sortBy: SortBranchesBy): BranchList!
allBranches(offset: Int, databaseName: String!, sortBy: SortBranchesBy): [Branch!]!
defaultBranch(databaseName: String!): Branch
commits(offset: Int, databaseName: String!, refName: String, afterCommitId: String, twoDot: Boolean, excludingCommitsFromRefName: String): CommitList!
currentDatabase: String
Expand Down
3 changes: 2 additions & 1 deletion graphql-server/src/branches/branch.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Field, GraphQLTimestamp, ID, ObjectType } from "@nestjs/graphql";
import { RawRow } from "../queryFactory/types";
import * as table from "../tables/table.model";
import { convertToUTCDate } from "../utils";
import { ListOffsetRes } from "../utils/commonTypes";

@ObjectType()
export class Branch {
Expand Down Expand Up @@ -31,7 +32,7 @@ export class Branch {
}

@ObjectType()
export class BranchNamesList {
export class BranchList extends ListOffsetRes {
@Field(_type => [Branch])
list: Branch[];
}
Expand Down
37 changes: 28 additions & 9 deletions graphql-server/src/branches/branch.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import {
Resolver,
} from "@nestjs/graphql";
import { ConnectionResolver } from "../connections/connection.resolver";
import { RawRow } from "../queryFactory/types";
import { Table } from "../tables/table.model";
import { TableResolver } from "../tables/table.resolver";
import { BranchArgs, DBArgs } from "../utils/commonTypes";
import { ROW_LIMIT, getNextOffset } from "../utils";
import { BranchArgs, DBArgs, DBArgsWithOffset } from "../utils/commonTypes";
import { SortBranchesBy } from "./branch.enum";
import { Branch, BranchNamesList, fromDoltBranchesRow } from "./branch.model";
import { Branch, BranchList, fromDoltBranchesRow } from "./branch.model";

@ArgsType()
export class GetBranchOrDefaultArgs extends DBArgs {
Expand Down Expand Up @@ -43,7 +45,7 @@ export class CreateBranchArgs extends DBArgs {
}

@ArgsType()
class ListBranchesArgs extends DBArgs {
class ListBranchesArgs extends DBArgsWithOffset {
@Field(_type => SortBranchesBy, { nullable: true })
sortBy?: SortBranchesBy;
}
Expand Down Expand Up @@ -75,13 +77,18 @@ export class BranchResolver {
return branch;
}

@Query(_returns => BranchNamesList)
async branches(@Args() args: ListBranchesArgs): Promise<BranchNamesList> {
@Query(_returns => BranchList)
async branches(@Args() args: ListBranchesArgs): Promise<BranchList> {
const conn = this.conn.connection();
const res = await conn.getBranches(args);
return {
list: res.map(b => fromDoltBranchesRow(args.databaseName, b)),
};
const res = await conn.getBranches({ ...args, offset: args.offset ?? 0 });
return fromBranchListRes(res, args);
}

@Query(_returns => [Branch])
async allBranches(@Args() args: ListBranchesArgs): Promise<Branch[]> {
const conn = this.conn.connection();
const res = await conn.getAllBranches(args);
return res.map(b => fromDoltBranchesRow(args.databaseName, b));
}

@Query(_returns => Branch, { nullable: true })
Expand Down Expand Up @@ -162,3 +169,15 @@ export function getDefaultBranchFromBranchesList(

return options[0];
}

function fromBranchListRes(
branches: RawRow[],
args: ListBranchesArgs,
): BranchList {
return {
list: branches
.slice(0, ROW_LIMIT)
.map(b => fromDoltBranchesRow(args.databaseName, b)),
nextOffset: getNextOffset(branches.length, args.offset ?? 0),
};
}
31 changes: 25 additions & 6 deletions graphql-server/src/queryFactory/dolt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,16 +151,35 @@ export class DoltQueryFactory
);
}

async getBranches(args: t.DBArgs & { sortBy?: SortBranchesBy }): t.PR {
async getBranches(
args: t.DBArgs & { sortBy?: SortBranchesBy; offset: number },
): t.PR {
return this.queryForBuilder(async em => {
let sel = em.createQueryBuilder().select("*").from("dolt_branches", "");
if (args.sortBy) {
sel = sel.addOrderBy(qh.getOrderByColForBranches(args.sortBy), "DESC");
}
return sel.getRawMany();
const [orderBy, dir] = qh.getOrderByColForBranches(args.sortBy);
return em
.createQueryBuilder()
.select("*")
.from("dolt_branches", "")
.addOrderBy(orderBy, dir)
.limit(ROW_LIMIT + 1)
.offset(args.offset)
.getRawMany();
}, args.databaseName);
}

async getAllBranches(args: t.DBArgs): t.PR {
return this.queryForBuilder(
async em =>
em
.createQueryBuilder()
.select("*")
.from("dolt_branches", "")
.limit(1000)
.getRawMany(),
args.databaseName,
);
}

async createNewBranch(args: t.BranchArgs & { fromRefName: string }): t.PR {
return this.query(
qh.callNewBranch,
Expand Down
8 changes: 5 additions & 3 deletions graphql-server/src/queryFactory/dolt/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ export const callNewBranch = `CALL DOLT_BRANCH(?, ?)`;

export const callDeleteBranch = `CALL DOLT_BRANCH("-D", ?)`;

export function getOrderByColForBranches(sortBy?: SortBranchesBy): string {
export function getOrderByColForBranches(
sortBy?: SortBranchesBy,
): [string, "ASC" | "DESC"] {
switch (sortBy) {
case SortBranchesBy.LastUpdated:
return "latest_commit_date";
return ["latest_commit_date", "DESC"];
default:
return "";
return ["name", "ASC"];
}
}

Expand Down
6 changes: 5 additions & 1 deletion graphql-server/src/queryFactory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ export declare class QueryFactory {

getBranch(args: t.BranchArgs): t.UPR;

getBranches(args: t.DBArgs & { sortBy?: SortBranchesBy }): t.PR;
getBranches(
args: t.DBArgs & { sortBy?: SortBranchesBy; offset: number },
): t.PR;

getAllBranches(args: t.DBArgs): t.PR;

createNewBranch(args: t.BranchArgs & { fromRefName: string }): t.PR;

Expand Down
6 changes: 5 additions & 1 deletion graphql-server/src/queryFactory/mysql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,11 @@ export class MySQLQueryFactory
];
}

async getBranches(args: t.DBArgs): t.PR {
async getBranches(args: t.DBArgs & { offset: number }): t.PR {
return this.getAllBranches(args);
}

async getAllBranches(args: t.DBArgs): t.PR {
const branch = await this.getBranch({ ...args, branchName: "main" });
return branch ?? [];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function BranchSelectorQuery(props: BranchSelectorForRepoProps) {
<Inner
{...props}
onChangeValue={handleChangeBranch}
branches={data.branches.list}
branches={data.allBranches}
defaultName={props.defaultName ?? defaultBranchName}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default function BranchSelectorWithTableQuery(
<Inner
{...props}
onChangeValue={handleChangeBranch}
branches={data.branches.list}
branches={data.allBranches}
defaultName={props.defaultName ?? defaultBranchName}
/>
)}
Expand Down
2 changes: 1 addition & 1 deletion web/components/CustomFormSelect/BranchSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function CustomBranchSelector(props: CustomProps): JSX.Element {
render={data => (
<Inner
{...props}
branches={data.branches.list}
branches={data.allBranches}
defaultName={props.defaultName ?? defaultBranchName}
/>
)}
Expand Down
15 changes: 3 additions & 12 deletions web/components/CustomFormSelect/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,7 @@ export const branchSelectorMock = (
},
result: {
data: {
branches: {
__typename: "BranchNamesList",
list: empty ? [] : [testBranch(params), mainBranch(params)],
},
allBranches: empty ? [] : [testBranch(params), mainBranch(params)],
},
},
};
Expand All @@ -95,10 +92,7 @@ export const noBranchMock = (params: DatabaseParams): MockedResponse => {
},
result: {
data: {
branches: {
__typename: "BranchNamesList",
list: [],
},
allBranches: [],
},
},
};
Expand All @@ -112,10 +106,7 @@ export const oneBranchMock = (params: DatabaseParams): MockedResponse => {
},
result: {
data: {
branches: {
__typename: "BranchNamesList",
list: [mainBranch(params)],
},
allBranches: [mainBranch(params)],
},
},
};
Expand Down
6 changes: 2 additions & 4 deletions web/components/CustomFormSelect/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ export const BRANCH_SELECTOR_QUERY = gql`
databaseName
}
query BranchesForSelector($databaseName: String!) {
branches(databaseName: $databaseName) {
list {
...BranchForBranchSelector
}
allBranches(databaseName: $databaseName) {
...BranchForBranchSelector
}
}
`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,46 @@
import { BranchFragment } from "@gen/graphql-types";
import InfiniteScroll from "react-infinite-scroller";
import BranchRow from "./BranchRow";
import css from "./index.module.css";

type Props = {
branches: BranchFragment[];
onDeleteClicked: (b: BranchFragment) => void;
loadMore: () => Promise<void>;
hasMore: boolean;
};

export default function BranchList(props: Props): JSX.Element {
return (
<table className={css.table} data-cy="branch-list">
<thead>
<tr>
<th>Branch name</th>
<th>Last updated by</th>
<th>Last updated</th>
<th aria-hidden="true" />
</tr>
</thead>
<tbody>
{props.branches.map(b => (
<BranchRow
onDeleteClicked={() => props.onDeleteClicked(b)}
key={b._id}
branch={b}
/>
))}
</tbody>
</table>
<InfiniteScroll
// Passing extra props to infinite scroll seems to apply those props to the top level div,
// so we can't spread props here.
loadMore={props.loadMore}
hasMore={props.hasMore}
loader={<div className={css.loader}>Loading branches ...</div>}
useWindow={false}
initialLoad={false}
getScrollParent={() => document.getElementById("main-content")}
>
<table className={css.table} data-cy="branch-list">
<thead>
<tr>
<th>Branch name</th>
<th>Last updated by</th>
<th>Last updated</th>
<th aria-hidden="true" />
</tr>
</thead>
<tbody>
{props.branches.map(b => (
<BranchRow
onDeleteClicked={() => props.onDeleteClicked(b)}
key={b._id}
branch={b}
/>
))}
</tbody>
</table>
</InfiniteScroll>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ type Props = {

type InnerProps = {
branches: BranchFragment[];
loadMore: () => Promise<void>;
hasMore: boolean;
sortBranches: (sortBy?: SortBranchesBy) => Promise<void>;
sortBy?: SortBranchesBy;
} & Props;
Expand Down Expand Up @@ -119,6 +121,8 @@ export default function BranchesPage({ params }: Props): JSX.Element {
<Inner
params={params}
branches={data}
loadMore={res.loadMore}
hasMore={res.hasMore}
sortBranches={res.sortBranches}
sortBy={res.sortBy}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ export const BRANCHES_FOR_BRANCHES_PAGE_QUERY = gql`
lastUpdated
lastCommitter
}
query BranchList($databaseName: String!, $sortBy: SortBranchesBy) {
branches(databaseName: $databaseName, sortBy: $sortBy) {
query BranchList(
$databaseName: String!
$sortBy: SortBranchesBy
$offset: Int
) {
branches(databaseName: $databaseName, sortBy: $sortBy, offset: $offset) {
list {
...Branch
}
nextOffset
}
}
`;
Expand Down
Loading

0 comments on commit 0067668

Please sign in to comment.