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 all 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.

7 changes: 6 additions & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
"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": {
"walletAddress": {
"description": "Ethereum address or Ethereum Name Service",
"type": "string"
},
"unset": {
"description": "Unsets the wallet associated with a user",
"type": "boolean",
"default": false
}
}
}
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
139 changes: 55 additions & 84 deletions src/adapters/supabase/helpers/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,143 +6,140 @@ 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) {
super(supabase, context);
}

public async getAddress(id: number) {
const userWithWallet = await this._getUserWithWallet(id);
const userWithWallet = await this._getUserFromWalletId(id);
if (!userWithWallet) return null;
return this._validateAndGetWalletAddress(userWithWallet);
}

public async upsertWalletAddress(context: Context, address: string) {
const payload = context.payload;

const userData = await this._getUserData(payload);
const registeredWalletData = await this._getRegisteredWalletData(userData);

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

if (!registeredWalletData) {
await this._registerNewWallet(context, {
address,
locationMetaData,
payload,
});
} else {
await this._updateExistingWallet(context, {
address,
locationMetaData,
payload,
walletData: registeredWalletData,
});
}
}

private async _getUserWithWallet(id: number) {
const { data, error } = await this.supabase.from("users").select("*, wallets(*)").filter("id", "eq", id);
if (error) throw error;
public async unlinkWalletFromUserId(userId: number) {
const userData = await this._getUserFromId(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);
}
}

private async _getUserFromWalletId(id: number) {
const { data, error } = await this.supabase.from("users").select("*, wallets(*)").filter("wallet_id", "eq", id).maybeSingle();
if (error) throw this.context.logger.error(`Could not get the user from its wallet id.`, error);
return data;
}

private async _getUserFromId(id: number) {
const { data, error } = await this.supabase.from("users").select("*, wallets(*)").filter("id", "eq", id).maybeSingle();
if (error) throw this.context.logger.error(`Could not get the user from its id.`, 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);
return data as UserRow;
}

private async _getUserData(payload: Context["payload"]) {
let userData = await this._checkIfUserExists(payload.sender.id);
if (!userData) {
const user = payload.sender;
userData = await this._registerNewUser(user, this._getLocationMetaData(payload));
userData = await this._registerNewUser(user);
}
return userData;
}

private async _registerNewUser(user: Context["payload"]["sender"], locationMetaData: LocationMetaData) {
// Insert the location metadata into the locations table
const { data: locationData, error: locationError } = await this.supabase.from("locations").insert(locationMetaData).select().single();

if (locationError) {
throw new Error(locationError.message);
}

// Get the ID of the inserted location
const locationId = locationData.id;

// Register the new user with the location ID
private async _registerNewUser(user: Context["payload"]["sender"]) {
const { data: userData, error: userError } = await this.supabase
.from("users")
.insert([{ id: user.id, location_id: locationId }])
.insert([{ id: user.id }])
.select()
.single();

if (userError) {
throw new Error(userError.message);
throw this.context.logger.error(`A new user could not be registered.`, 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);
}
}

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);
return walletData;
}

private _getLocationMetaData(payload: Context["payload"]): LocationMetaData {
return {
user_id: payload.sender.id,
comment_id: payload.comment.id,
issue_id: payload.issue.id,
repository_id: payload.repository.id,
organization_id: payload.organization?.id ?? payload.repository.owner.id,
};
}

private async _registerNewWallet(context: Context, { address, locationMetaData, payload }: RegisterNewWallet) {
private async _registerNewWallet(context: Context, { address, payload }: RegisterNewWallet) {
context.logger.debug(`Registering a new wallet for the user ${payload.sender.id}: ${address}`);
const walletData = await this._insertNewWallet(address);
await this._updateWalletId(walletData.id, payload.sender.id);
if (walletData.location_id) {
await this._enrichLocationMetaData(context, walletData, locationMetaData);
}
}

private async _updateExistingWallet(context: Context, { address, locationMetaData, walletData }: UpdateExistingWallet) {
await this._updateWalletAddress(walletData.id, address);
if (walletData.location_id) {
await this._enrichLocationMetaData(context, walletData, locationMetaData);
private async _updateExistingWallet(context: Context, { walletData, payload }: UpdateExistingWallet) {
context.logger.debug(`Updating a new wallet for the user ${payload.sender.id}: ${walletData.address}`);
const existingLinkToUserWallet = await this._getUserFromWalletId(walletData.id);
if (existingLinkToUserWallet && existingLinkToUserWallet.id !== context.payload.sender.id) {
throw this.context.logger.error(`Failed to register wallet because it is already associated with another user.`, existingLinkToUserWallet);
}
await this._updateWalletId(walletData.id, payload.sender.id);
}

private async _insertNewWallet(address: string): Promise<WalletRow> {
Expand All @@ -152,42 +149,16 @@ 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);
return walletInsertData as WalletRow;
}

private async _updateWalletAddress(walletId: number, address: string) {
const basicLocationInfo = {
address: address,
} as WalletRow;

await this.supabase.from("wallets").update(basicLocationInfo).eq("id", walletId).maybeSingle();
}

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");
}
logger.debug("Enriching wallet location metadata", { locationMetaData });
return this.supabase.from("locations").update(locationMetaData).eq("id", walletData.location_id);
}
}

interface RegisterNewWallet {
address: string;
payload: Context["payload"];
locationMetaData: LocationMetaData;
}

interface UpdateExistingWallet extends RegisterNewWallet {
walletData: WalletRow;
}

interface LocationMetaData {
user_id: number;
comment_id: number;
issue_id: number;
repository_id: number;
organization_id: number;
}
16 changes: 13 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,18 @@ 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(
`Please provide your wallet address after to register it i.e.: \`/wallet 0xYourAddress\`\nWrite \`/wallet unset\` to remove your wallet.`
);
}
})
.helpCommand(false)
.exitOverride()
.version(packageJson.version);
Expand Down
28 changes: 18 additions & 10 deletions src/handlers/query-wallet.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { postComment } from "@ubiquity-os/plugin-sdk";
import { ethers } from "ethers";
import { Context } from "../types";
import { RPCHandler } from "@ubiquity-dao/rpc-handler";
import { addCommentToIssue } from "../utils";

function extractEnsName(text: string) {
const ensRegex = /^(?=.{3,40}$)([a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}$/gm;
Expand All @@ -18,8 +18,20 @@ export async function handleCommand(context: Context) {
if (!command) {
throw new Error("Command is undefined");
}
const { walletAddress } = command.parameters;
await registerWallet(context, walletAddress);
const { walletAddress, unset: shouldUnset } = command.parameters;
if (shouldUnset) {
await unregisterWallet(context);
} else {
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 postComment(context, logger.ok(`Successfully unset wallet`));
}

export async function registerWallet(context: Context, body: string) {
Expand All @@ -40,7 +52,7 @@ export async function registerWallet(context: Context, body: string) {
}

if (!address) {
await addCommentToIssue(context, logger.info("Skipping to register a wallet address because both address/ens doesn't exist").logMessage.diff);
await postComment(context, logger.info("Skipping to register a wallet address because both address/ens doesn't exist"));
return;
}

Expand All @@ -49,11 +61,7 @@ export async function registerWallet(context: Context, body: string) {
}

if (address == ethers.ZeroAddress) {
await addCommentToIssue(
context,
logger.error("Skipping to register a wallet address because user is trying to set their address to null address").logMessage.diff
);

await postComment(context, logger.error("Skipping to register a wallet address because user is trying to set their address to null address"));
return;
}

Expand All @@ -63,7 +71,7 @@ export async function registerWallet(context: Context, body: string) {
const { wallet } = adapters.supabase;
await wallet.upsertWalletAddress(context, address);

await addCommentToIssue(context, logger.ok("Successfully registered wallet address", { sender, address }).logMessage.diff);
await postComment(context, logger.ok("Successfully set wallet", { sender, address }));
} else {
throw new Error("Payload comment is undefined");
}
Expand Down
5 changes: 1 addition & 4 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { CommanderError } from "commander";
import { createAdapters } from "./adapters";
import { CommandParser } from "./handlers/command-parser";
import { Context } from "./types";
import { addCommentToIssue } from "./utils";
import { handleCommand } from "./handlers/query-wallet";

/**
Expand All @@ -26,11 +25,9 @@ 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);
throw context.logger.error(err.message);
}
} else {
context.logger.error("An error occurred", { err });
throw err;
}
}
Expand Down
Loading
Loading