Skip to content

Commit

Permalink
feat(reviews): display review history
Browse files Browse the repository at this point in the history
  • Loading branch information
gregberge committed Jan 4, 2025
1 parent 4f7c36c commit 5c9bc6d
Show file tree
Hide file tree
Showing 22 changed files with 445 additions and 46 deletions.
35 changes: 32 additions & 3 deletions apps/backend/src/graphql/__generated__/resolver-types.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions apps/backend/src/graphql/__generated__/schema.gql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions apps/backend/src/graphql/definitions/Build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ export const typeDefs = gql`
baseBranch: String
"Base branch resolved from"
baseBranchResolvedFrom: BaseBranchResolution
"Effective build reviews"
reviews: [BuildReview!]!
}
type BuildMetadata {
Expand Down Expand Up @@ -280,6 +282,9 @@ export const resolvers: IResolvers = {
assertNever(build.baseBranchResolvedFrom);
}
},
reviews: async (build, _args, ctx) => {
return ctx.loaders.BuildUniqueReviews.load(build.id);
},
},
Mutation: {
setValidationStatus: async (_root, args, ctx) => {
Expand Down
55 changes: 55 additions & 0 deletions apps/backend/src/graphql/definitions/BuildReview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { assertNever } from "@argos/util/assertNever";
import { invariant } from "@argos/util/invariant";
import gqlTag from "graphql-tag";

import {
IBuildReviewState,
type IResolvers,
} from "../__generated__/resolver-types.js";

const { gql } = gqlTag;

export const typeDefs = gql`
enum BuildReviewState {
APPROVED
REJECTED
PENDING
}
type BuildReview implements Node {
id: ID!
date: DateTime!
user: User
state: BuildReviewState!
}
`;

export const resolvers: IResolvers = {
BuildReview: {
date: (review) => {
return review.createdAt;
},
state: (review) => {
switch (review.state) {
case "approved":
return IBuildReviewState.Approved;
case "rejected":
return IBuildReviewState.Rejected;
case "pending":
return IBuildReviewState.Pending;
default:
assertNever(review.state);
}
},
user: async (review, _, ctx) => {
if (!review.userId) {
return null;
}
const account = await ctx.loaders.AccountFromRelation.load({
userId: review.userId,
});
invariant(account, "Account not found");
return account;
},
},
};
2 changes: 2 additions & 0 deletions apps/backend/src/graphql/definitions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { DocumentNode } from "graphql";
import * as Account from "./Account.js";
import * as AccountSubscription from "./AccountSubscription.js";
import * as Build from "./Build.js";
import * as BuildReview from "./BuildReview.js";
import * as Connection from "./Connection.js";
import * as DateDefs from "./Date.js";
import * as GhApiInstallation from "./GhApiInstallation.js";
Expand Down Expand Up @@ -36,6 +37,7 @@ export const definitions: { resolvers?: object; typeDefs?: DocumentNode }[] = [
Account,
AccountSubscription,
Build,
BuildReview,
Connection,
DateDefs,
GhApiInstallation,
Expand Down
31 changes: 31 additions & 0 deletions apps/backend/src/graphql/loaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Account,
Build,
BuildAggregatedStatus,
BuildReview,
File,
GithubAccount,
GithubInstallation,
Expand Down Expand Up @@ -193,12 +194,42 @@ function createGhApiInstallationLoader() {
});
}

function createBuildUniqueReviewsLoader() {
return new DataLoader<string, BuildReview[]>(async (inputs) => {
const reviews = await BuildReview.query()
.whereIn(
"id",
BuildReview.query()
.select("id")
.whereIn("buildId", inputs as string[])
.distinctOn(["buildId", "userId"])
.orderBy([
{ column: "buildId", order: "desc" },
{ column: "userId", order: "desc" },
{ column: "createdAt", order: "desc" },
]),
)
.orderBy("createdAt", "desc");
const reviewsMap = reviews.reduce<Record<string, BuildReview[]>>(
(map, review) => {
const array = map[review.buildId] ?? [];
array.push(review);
map[review.buildId] = array;
return map;
},
{},
);
return inputs.map((id) => reviewsMap[id] ?? []);
});
}

export const createLoaders = () => ({
Account: createModelLoader(Account),
AccountFromRelation: createAccountFromRelationLoader(),
BuildFromCompareScreenshotBucketId:
createBuildFromCompareScreenshotBucketIdLoader(),
BuildAggregatedStatus: createBuildAggregatedStatusLoader(),
BuildUniqueReviews: createBuildUniqueReviewsLoader(),
File: createModelLoader(File),
GhApiInstallation: createGhApiInstallationLoader(),
GithubAccount: createModelLoader(GithubAccount),
Expand Down
3 changes: 3 additions & 0 deletions apps/frontend/src/containers/AccountAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function AccountAvatar(props: {
className?: string;
size?: number;
avatar: FragmentType<typeof AvatarFragment>;
alt?: string;
}) {
const avatar = useFragment(AvatarFragment, props.avatar);
const size = props.size ?? 32;
Expand All @@ -26,6 +27,7 @@ export function AccountAvatar(props: {
initial={avatar.initial}
color={avatar.color}
size={size}
alt={props.alt}
className={props.className}
/>
);
Expand All @@ -36,6 +38,7 @@ export function AccountAvatar(props: {
url={avatar.url}
size={size}
className={props.className}
alt={props.alt}
/>
);
}
Loading

0 comments on commit 5c9bc6d

Please sign in to comment.