Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: error messages #29

Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
03017f3
chore: updated manifest.json and dist build
github-actions[bot] Dec 2, 2024
89109e0
chore: remove generated dist/index.js file
gentlementlegen Dec 6, 2024
8cb078c
chore: updated manifest.json and dist build
github-actions[bot] Dec 6, 2024
bcd24a5
chore: updated manifest.json and dist build
github-actions[bot] Dec 6, 2024
9c36cf6
fix: improved error messages
gentlementlegen Dec 6, 2024
965bcd4
chore: updated manifest.json and dist build
github-actions[bot] Dec 6, 2024
56faa8e
chore: fix unregister wallet
gentlementlegen Dec 7, 2024
940b581
chore: updated manifest.json and dist build
github-actions[bot] Dec 7, 2024
6fa4bb1
feat: user can unset their wallet or link multiple accounts to the sa…
gentlementlegen Dec 8, 2024
b947480
chore: updated manifest.json and dist build
github-actions[bot] Dec 8, 2024
78175e9
chore: changed unset flag to 'unset' argument
gentlementlegen Dec 8, 2024
026331b
chore: updated manifest.json and dist build
github-actions[bot] Dec 8, 2024
3199d2f
chore: added linguist file
gentlementlegen Dec 8, 2024
001e976
chore: remove generated dist/index.js file
gentlementlegen Dec 6, 2024
cf03791
chore: updated manifest.json and dist build
github-actions[bot] Dec 6, 2024
91780fd
Merge remote-tracking branch 'origin/development' into development
gentlementlegen Dec 8, 2024
3166ec2
Merge branch 'development' into fix/error-messages
gentlementlegen Dec 8, 2024
9d994e5
chore: updated manifest.json and dist build
github-actions[bot] Dec 8, 2024
e98524c
chore: updated manifest.json
gentlementlegen Dec 8, 2024
ff8c8c9
test: added test for wallet unlink
gentlementlegen Dec 8, 2024
8da54d8
test: fixed wallet address
gentlementlegen Dec 8, 2024
f4b38d9
chore: added support for LLM command with unset option
gentlementlegen Dec 10, 2024
b6ddcd9
chore: updated manifest.json and dist build
github-actions[bot] Dec 10, 2024
8be2137
chore: added support for LLM command with unset option in manifest.json
gentlementlegen Dec 10, 2024
23ca59d
chore: removed error details from displayed errors
gentlementlegen Dec 10, 2024
bf6109d
chore: updated manifest.json and dist build
github-actions[bot] Dec 10, 2024
c6a0eb2
chore: simplified error handling for the plugin sdk
gentlementlegen Dec 10, 2024
c00c024
chore: updated manifest.json and dist build
github-actions[bot] Dec 10, 2024
9e7235e
chore: removed unused file utils.ts
gentlementlegen Dec 10, 2024
69ad73d
chore: fixed tests
gentlementlegen Dec 10, 2024
dba4496
chore: fixed patch API call implementation
gentlementlegen Dec 11, 2024
4d47db8
fix: removed location from wallet DB references
gentlementlegen Dec 11, 2024
44df9ab
chore: updated manifest.json and dist build
github-actions[bot] Dec 11, 2024
dd47e9c
fix: users can only register to a unique wallet
gentlementlegen Dec 11, 2024
f9de807
chore: updated manifest.json and dist build
github-actions[bot] Dec 11, 2024
65f5ffd
chore: changed message contents
gentlementlegen Dec 12, 2024
8380526
chore: updated manifest.json and dist build
github-actions[bot] Dec 12, 2024
099ee70
chore: fixed unit tests
gentlementlegen Dec 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist/** linguist-generated
bun.lockb linguist-generated
Binary file modified bun.lockb
Binary file not shown.
6 changes: 3 additions & 3 deletions dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"commands": {
"wallet": {
"ubiquity:example": "/wallet ubq.eth",
"description": "Register your wallet address for payments.",
"description": "Register your wallet address for payments. Use '/wallet unset' to unlink your wallet.",
"parameters": {
gentlementlegen marked this conversation as resolved.
Show resolved Hide resolved
"type": "object",
"properties": {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"@sinclair/typebox": "0.34.3",
"@supabase/supabase-js": "2.43.5",
"@ubiquity-dao/rpc-handler": "1.3.0",
"@ubiquity-os/plugin-sdk": "^1.1.0",
"@ubiquity-os/plugin-sdk": "^1.1.1",
"@ubiquity-os/ubiquity-os-logger": "^1.3.2",
"commander": "12.1.0",
"dotenv": "16.4.5",
Expand Down Expand Up @@ -75,7 +75,7 @@
},
"lint-staged": {
"*.ts": [
"bun prettier --write",
"prettier --write",
"eslint --fix"
],
"src/**.{ts,json}": [
Expand Down
63 changes: 39 additions & 24 deletions src/adapters/supabase/helpers/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Super } from "./supabase";
type WalletRow = Database["public"]["Tables"]["wallets"]["Row"];
type WalletInsert = Database["public"]["Tables"]["wallets"]["Insert"];
type UserRow = Database["public"]["Tables"]["users"]["Row"];
type UserWithWallet = (UserRow & { wallets: WalletRow | null })[];
type UserWithWallet = UserRow & { wallets: WalletRow | null };

export class Wallet extends Super {
constructor(supabase: SupabaseClient<Database>, context: Context) {
Expand All @@ -21,8 +21,7 @@ export class Wallet extends Super {
public async upsertWalletAddress(context: Context, address: string) {
const payload = context.payload;

const userData = await this._getUserData(payload);
const registeredWalletData = await this._getRegisteredWalletData(userData);
const registeredWalletData = await this._getRegisteredWalletData(address);
gentlementlegen marked this conversation as resolved.
Show resolved Hide resolved

const locationMetaData = this._getLocationMetaData(payload);

Expand All @@ -42,21 +41,35 @@ export class Wallet extends Super {
}
}

public async unlinkWalletFromUserId(userId: number) {
const userData = await this._getUserWithWallet(userId);

if (!userData.wallet_id) {
throw this.context.logger.error("The user does not have an associated wallet to unlink");
}

const { error } = await this.supabase.from("users").update({ wallet_id: null }).eq("id", userData.id);
gentlementlegen marked this conversation as resolved.
Show resolved Hide resolved

if (error) {
throw this.context.logger.error(`Could not unlink the wallet: ${error.details}`, error);
}
}

private async _getUserWithWallet(id: number) {
const { data, error } = await this.supabase.from("users").select("*, wallets(*)").filter("id", "eq", id);
if (error) throw error;
const { data, error } = await this.supabase.from("users").select("*, wallets(*)").filter("id", "eq", id).single();
if (error) throw this.context.logger.error(`Could not check the user's wallet: ${error.details}`, error);
return data;
}

private _validateAndGetWalletAddress(userWithWallet: UserWithWallet): string {
if (userWithWallet[0]?.wallets?.address === undefined) throw new Error("Wallet address is undefined");
if (userWithWallet[0]?.wallets?.address === null) throw new Error("Wallet address is null");
return userWithWallet[0]?.wallets?.address;
if (userWithWallet?.wallets?.address === undefined) throw this.context.logger.error("The wallet address is undefined");
if (userWithWallet?.wallets?.address === null) throw this.context.logger.error("The wallet address is null");
return userWithWallet?.wallets?.address;
}

private async _checkIfUserExists(userId: number) {
const { data, error } = await this.supabase.from("users").select("*").eq("id", userId).maybeSingle();
if (error) throw error;
if (error) throw this.context.logger.error(`Could not check if the user exists: ${error.details}`, error);
return data as UserRow;
}

Expand All @@ -74,7 +87,7 @@ export class Wallet extends Super {
const { data: locationData, error: locationError } = await this.supabase.from("locations").insert(locationMetaData).select().single();

if (locationError) {
throw new Error(locationError.message);
throw this.context.logger.error(`Could not retrieve the location: ${locationError.details}`, locationError);
}

// Get the ID of the inserted location
Expand All @@ -88,35 +101,37 @@ export class Wallet extends Super {
.single();

if (userError) {
throw new Error(userError.message);
throw this.context.logger.error(`A new user could not be registered: ${userError.details}`, userError);
}

return userData as UserRow;
}

private async _checkIfWalletExists(userData: UserRow) {
if (userData.wallet_id === null) {
private async _checkIfWalletExists(wallet: string | number | null) {
if (wallet === null) {
return { data: null, error: null };
}
const { data, error } = await this.supabase.from("wallets").select("*").eq("id", userData.wallet_id).maybeSingle();

return { data, error };
if (typeof wallet === "number") {
return this.supabase.from("wallets").select("*").eq("id", wallet).maybeSingle();
} else {
return this.supabase.from("wallets").select("*").eq("address", wallet).maybeSingle();
}
}

private async _updateWalletId(walletId: number, userId: number) {
const { error } = await this.supabase.from("users").update({ wallet_id: walletId }).eq("id", userId);

if (error) {
throw error;
throw this.context.logger.error(`Could not update the wallet: ${error.details}`, error);
}
}

private async _getRegisteredWalletData(userData: UserRow) {
const walletResponse = await this._checkIfWalletExists(userData);
private async _getRegisteredWalletData(address: string) {
const walletResponse = await this._checkIfWalletExists(address);
const walletData = walletResponse.data;
const walletError = walletResponse.error;

if (walletError) throw walletError;
if (walletError) throw this.context.logger.error(`Could not get the registered wallet: ${walletError.details}`, walletError);
return walletData;
}

Expand All @@ -138,8 +153,8 @@ export class Wallet extends Super {
}
}

private async _updateExistingWallet(context: Context, { address, locationMetaData, walletData }: UpdateExistingWallet) {
await this._updateWalletAddress(walletData.id, address);
private async _updateExistingWallet(context: Context, { locationMetaData, walletData, payload }: UpdateExistingWallet) {
await this._updateWalletId(walletData.id, payload.sender.id);
if (walletData.location_id) {
await this._enrichLocationMetaData(context, walletData, locationMetaData);
}
Expand All @@ -152,7 +167,7 @@ export class Wallet extends Super {

const { data: walletInsertData, error: walletInsertError } = await this.supabase.from("wallets").insert(newWallet).select().single();

if (walletInsertError) throw walletInsertError;
if (walletInsertError) throw this.context.logger.error(`Could not insert the new wallet: ${walletInsertError.details}`, walletInsertError);
return walletInsertData as WalletRow;
}

Expand All @@ -167,7 +182,7 @@ export class Wallet extends Super {
private async _enrichLocationMetaData(context: Context, walletData: WalletRow, locationMetaData: LocationMetaData) {
const logger = context.logger;
if (walletData.location_id === null) {
throw new Error("Location ID is null");
throw this.context.logger.error("The location ID is null.");
}
logger.debug("Enriching wallet location metadata", { locationMetaData });
return this.supabase.from("locations").update(locationMetaData).eq("id", walletData.location_id);
gentlementlegen marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
14 changes: 11 additions & 3 deletions src/handlers/command-parser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Command, InvalidArgumentError } from "commander";
import packageJson from "../../package.json";
import { Context } from "../types";
import { registerWallet } from "./query-wallet";
import { registerWallet, unregisterWallet } from "./query-wallet";

export class CommandParser {
readonly _program;
Expand All @@ -11,8 +11,16 @@ export class CommandParser {
program
.command("/wallet")
.usage("<address>")
.argument("<address>", "Wallet address to query, e.g. 0x000000000000000000000000000000000000000", this._parseWalletAddress)
.action((address) => registerWallet(context, address))
.argument("[address]", "Wallet address to query, e.g. 0x000000000000000000000000000000000000000", this._parseWalletAddress)
.action((address: string) => {
if (address === "unset") {
return unregisterWallet(context);
} else if (address) {
return registerWallet(context, address);
} else {
throw new InvalidArgumentError("Wallet address is required, or 'unset' to remove your wallet.");
}
})
.helpCommand(false)
.exitOverride()
.version(packageJson.version);
Expand Down
8 changes: 8 additions & 0 deletions src/handlers/query-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ export async function handleCommand(context: Context) {
await registerWallet(context, walletAddress);
}

export async function unregisterWallet(context: Context) {
const { payload, adapters, logger } = context;
const sender = payload.sender.id;
logger.info(`Trying to unlink the wallet for user ${sender}`);
await adapters.supabase.wallet.unlinkWalletFromUserId(sender);
await addCommentToIssue(context, logger.info(`Successfully unlinked wallet from user @${payload.sender.login}`).logMessage.diff);
}

export async function registerWallet(context: Context, body: string) {
const { payload, config, logger, adapters } = context;
const sender = payload.sender.login;
Expand Down
11 changes: 7 additions & 4 deletions src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createClient } from "@supabase/supabase-js";
import { LogReturn } from "@ubiquity-os/ubiquity-os-logger";
import { CommanderError } from "commander";
import { createAdapters } from "./adapters";
import { CommandParser } from "./handlers/command-parser";
Expand Down Expand Up @@ -26,12 +27,14 @@ export async function plugin(context: Context) {
} catch (err) {
if (err instanceof CommanderError) {
if (err.code !== "commander.unknownCommand") {
await addCommentToIssue(context, `\`\`\`diff\n- ${err.message}`);
context.logger.error(err.message);
await addCommentToIssue(context, context.logger.error(err.message).logMessage.diff);
gentlementlegen marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
context.logger.error("An error occurred", { err });
throw err;
if (err instanceof LogReturn) {
throw err;
} else {
throw context.logger.error(`An error occurred: ${err}`, { err });
}
gentlementlegen marked this conversation as resolved.
Show resolved Hide resolved
}
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default {
envSchema: envSchema,
postCommentOnError: true,
settingsSchema: pluginSettingsSchema,
logLevel: env.LOG_LEVEL as LogLevel,
logLevel: (env.LOG_LEVEL as LogLevel) ?? "info",
kernelPublicKey: env.KERNEL_PUBLIC_KEY,
bypassSignatureVerification: process.env.NODE_ENV === "local",
}
Expand Down
2 changes: 1 addition & 1 deletion tests/__mocks__/db-seed.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"wallets": [
{
"id": 1,
"address": "0x0000000000000000000000000000000000000001"
"address": "0xefC0e701A824943b469a694aC564Aa1efF7Ab7dd"
}
]
}
14 changes: 12 additions & 2 deletions tests/__mocks__/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,25 @@ export const handlers = [
const idNumber = Number(id.match(/\d+/)?.[0]);
return HttpResponse.json(db.users.findFirst({ where: { id: { equals: idNumber } } }));
}),
http.get(`${process.env.SUPABASE_URL}/rest/v1/wallets*`, ({ request }) => {
http.patch(`${process.env.SUPABASE_URL}/rest/v1/users*`, ({ request }) => {
const url = new URL(request.url);
const id = url.searchParams.get("id");

if (!id) {
return HttpResponse.text("", { status: 400 });
}
const idNumber = Number(id.match(/\d+/)?.[0]);
return HttpResponse.json(db.wallets.findFirst({ where: { id: { equals: idNumber } } }));
return HttpResponse.json(db.users.findFirst({ where: { id: { equals: idNumber } } }));
gentlementlegen marked this conversation as resolved.
Show resolved Hide resolved
}),
http.get(`${process.env.SUPABASE_URL}/rest/v1/wallets*`, ({ request }) => {
const url = new URL(request.url);
const id = url.searchParams.get("address");

if (!id) {
return HttpResponse.text("", { status: 400 });
}
const address = id.replace("eq.", "");
return HttpResponse.json(db.wallets.findFirst({ where: { address: { equals: address } } }));
}),
http.patch(`${process.env.SUPABASE_URL}/rest/v1/wallets*`, async ({ request }) => {
const url = new URL(request.url);
Expand Down
4 changes: 2 additions & 2 deletions tests/http/request.http
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
### POST a successful request to the Worker
# Variables as follows:
# org_name: the organization you want to post to, e.g. "ubiquibot"
# org_name: the organization you want to post to, e.g. "ubiquity-os"
# repo_name: the target repository, e.g "command-wallet"
# issue_number: the number of the issue, same as the one you would see in the url to that issue, e.g. "1"
# command: the command you want to test, e.g. "/wallet @ubiquibot"
# command: the command you want to test, e.g. "/wallet @ubiquity-os"
# token: a valid GitHub token for auth, e.g. "ghs_xxxxx"
POST http://localhost:4002/
Content-Type: application/json
Expand Down
30 changes: 28 additions & 2 deletions tests/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ afterEach(() => {
});
afterAll(() => server.close());

const eventName = "issue_comment.created";

jest.mock("ethers", () => ({
ethers: {
JsonRpcProvider: jest.fn(() => ({
Expand All @@ -39,7 +41,7 @@ describe("Wallet command tests", () => {
it("Should handle /wallet comment", async () => {
const spy = jest.spyOn(Logs.prototype, "ok");
await plugin({
eventName: "issue_comment.created",
eventName: eventName,
config: { registerWalletWithVerification: false },
payload: {
...commentCreatedPayload,
Expand Down Expand Up @@ -69,7 +71,7 @@ describe("Wallet command tests", () => {
it("Should handle wallet command", async () => {
const spy = jest.spyOn(Logs.prototype, "ok");
await plugin({
eventName: "issue_comment.created",
eventName: eventName,
config: { registerWalletWithVerification: false },
payload: {
...commentCreatedPayload,
Expand Down Expand Up @@ -100,4 +102,28 @@ describe("Wallet command tests", () => {
})
);
}, 10000);

it("Should unregister a wallet", async () => {
const spy = jest.spyOn(Logs.prototype, "info");
await plugin({
eventName: eventName,
config: { registerWalletWithVerification: false },
payload: {
...commentCreatedPayload,
comment: {
...commentCreatedPayload.comment,
body: "/wallet unset",
},
},
command: null,
octokit: new Octokit(),
env: {
SUPABASE_URL: process.env.SUPABASE_URL,
SUPABASE_KEY: process.env.SUPABASE_KEY,
},
logger: new Logs("info"),
} as unknown as Context);
expect(spy).toHaveBeenCalledTimes(2);
expect(spy).toHaveBeenLastCalledWith("Successfully unlinked wallet from user @ubiquibot");
}, 10000);
});
Loading