Skip to content

Commit

Permalink
feat: supabase, typeguards
Browse files Browse the repository at this point in the history
  • Loading branch information
whilefoo committed Mar 10, 2024
1 parent 2728e2e commit 63643dd
Show file tree
Hide file tree
Showing 21 changed files with 1,132 additions and 38 deletions.
2 changes: 1 addition & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"ignorePaths": ["**/*.json", "**/*.css", "node_modules", "**/*.log"],
"useGitignore": true,
"language": "en",
"words": ["dataurl", "devpool", "outdir", "servedir"],
"words": ["dataurl", "devpool", "outdir", "servedir", "supabase", "typebox"],
"dictionaries": ["typescript", "node", "software-terms"],
"import": ["@cspell/dict-typescript/cspell-ext.json", "@cspell/dict-node/cspell-ext.json", "@cspell/dict-software-terms"],
"ignoreRegExpList": ["[0-9a-fA-F]{6}"]
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
"@actions/github": "^6.0.0",
"@octokit/rest": "^20.0.2",
"@octokit/webhooks": "^13.1.0",
"@sinclair/typebox": "^0.32.15",
"@supabase/supabase-js": "^2.39.7",
"@types/ms": "^0.7.34",
"dotenv": "^16.4.4",
"dotenv": "^16.4.5",
"ms": "^2.1.3"
},
"devDependencies": {
Expand Down
109 changes: 106 additions & 3 deletions pnpm-lock.yaml

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

19 changes: 19 additions & 0 deletions src/adapters/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { SupabaseClient } from "@supabase/supabase-js";
import { Context } from "../types/context";
import { Access } from "./supabase/helpers/tables/access";
import { User } from "./supabase/helpers/tables/user";
import { Label } from "./supabase/helpers/tables/label";
import { Locations } from "./supabase/helpers/tables/locations";
import { Super } from "./supabase/helpers/tables/super";

export function createAdapters(supabaseClient: SupabaseClient, context: Context) {
return {
supabase: {
access: new Access(supabaseClient, context),
user: new User(supabaseClient, context),
label: new Label(supabaseClient, context),
locations: new Locations(supabaseClient, context),
super: new Super(supabaseClient, context),
},
};
}
75 changes: 75 additions & 0 deletions src/adapters/supabase/helpers/tables/access.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { SupabaseClient } from "@supabase/supabase-js";
import { Database } from "../../types/database";
import { GitHubNode } from "../../types/github";
import { Super } from "./super";
import { UserRow } from "./user";
import { Context } from "../../../../types/context";
import { Comment } from "../../../../types/github";

type AccessRow = Database["public"]["Tables"]["access"]["Row"];
type AccessInsert = Database["public"]["Tables"]["access"]["Insert"];
type UserWithAccess = UserRow & { access: AccessRow[] };

type AccessData = {
user_id: number;
multiplier: number;
multiplier_reason: string;
node_id: string;
node_type: string;
node_url: string;
};

export class Access extends Super {
constructor(supabase: SupabaseClient, context: Context) {
super(supabase, context);
}

private async _getUserWithAccess(id: number): Promise<UserWithAccess> {
const { data, error } = await this.supabase.from("users").select("*, access(*)").filter("id", "eq", id).single();

if (error) {
this.context.logger.fatal(error.message, error);
throw new Error(error.message);
}
return data;
}

public async getAccess(id: number): Promise<AccessRow | null> {
const userWithAccess = await this._getUserWithAccess(id);
if (userWithAccess.access.length === 0) {
this.context.logger.debug("No access found for user", { id });
return null;
}
return userWithAccess.access[0];
}

public async setAccess(labels: string[], node: GitHubNode, userId?: number): Promise<null> {
const { data, error } = await this.supabase.from("access").upsert({
labels: labels,
...node,
user_id: userId,
} as AccessInsert);
if (error) throw new Error(error.message);
return data;
}

async upsertMultiplier(userId: number, multiplier: number, reason: string, comment: Comment) {
try {
const accessData: AccessData = {
user_id: userId,
multiplier: multiplier,
multiplier_reason: reason,
node_id: comment.node_id,
node_type: "IssueComment",
node_url: comment.html_url,
};

const { data, error } = await this.supabase.from("access").upsert(accessData, { onConflict: "location_id" });

if (error) throw new Error(error.message);
if (!data) throw new Error("Multiplier not upserted");
} catch (error) {
console.error("An error occurred while upserting multiplier:", error);
}
}
}
74 changes: 74 additions & 0 deletions src/adapters/supabase/helpers/tables/label.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { SupabaseClient } from "@supabase/supabase-js";

import { Database } from "../../types/database";
import { Super } from "./super";
import { Context } from "../../../../types/context";
import { WebhookEvent } from "../../../../types/github";

type LabelRow = Database["public"]["Tables"]["labels"]["Row"];

export class Label extends Super {
constructor(supabase: SupabaseClient, context: Context) {
super(supabase, context);
}

async saveLabelChange({
previousLabel,
currentLabel,
authorized,
repository,
}: {
previousLabel: string;
currentLabel: string;
authorized: boolean;
repository: WebhookEvent<"issues">["payload"]["repository"];
}): Promise<null> {
const { data, error } = await this.supabase.from("labels").insert({
label_from: previousLabel,
label_to: currentLabel,
authorized: authorized,
node_id: repository.node_id,
node_type: "Repository",
node_url: repository.html_url,
});

if (error) throw new Error(error.message);
return data;
}

async getLabelChanges(repositoryNodeId: string) {
const locationId = await this._getRepositoryLocationId(repositoryNodeId);
if (!locationId) {
return null;
}
return await this._getUnauthorizedLabelChanges(locationId);
}

async approveLabelChange(id: number): Promise<null> {
const { data, error } = await this.supabase.from("labels").update({ authorized: true }).eq("id", id);
if (error) throw new Error(error.message);
return data;
}

private async _getUnauthorizedLabelChanges(locationId: number): Promise<LabelRow[]> {
// Get label changes that are not authorized in the repository
const { data, error } = await this.supabase.from("labels").select("*").eq("location_id", locationId).eq("authorized", false);

if (error) throw new Error(error.message);

return data;
}

private async _getRepositoryLocationId(nodeId: string) {
// Get the location_id for the repository from the locations table
const { data: locationData, error: locationError } = await this.supabase.from("locations").select("id").eq("node_id", nodeId).maybeSingle();

if (locationError) throw new Error(locationError.message);
if (!locationData) {
this.context.logger.error("Repository location ID not found in database.");
return null;
}

return locationData.id;
}
}
Loading

0 comments on commit 63643dd

Please sign in to comment.