Skip to content

Commit

Permalink
feat: moved to SDK
Browse files Browse the repository at this point in the history
  • Loading branch information
gentlementlegen committed Oct 14, 2024
1 parent 597a7c3 commit 914bf62
Show file tree
Hide file tree
Showing 10 changed files with 2,111 additions and 2,120 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ junit.xml
cypress/screenshots
.dev.vars
/tests/http/http-client.private.env.json
.wrangler
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"scripts": {
"prebuild": "dotenv -- cross-env yarn supabase:generate:remote",
"dev": "wrangler dev --env dev --port 4000",
"dev": "tsx src/worker.ts",
"format": "run-s format:lint format:prettier format:cspell",
"format:lint": "eslint --fix .",
"format:prettier": "prettier --write .",
Expand All @@ -30,12 +30,15 @@
"open-source"
],
"dependencies": {
"@octokit/rest": "20.1.1",
"@octokit/webhooks": "13.2.7",
"@hono/node-server": "^1.13.2",
"@octokit/rest": "^20.1.0",
"@octokit/webhooks": "^12.3.1",
"@sinclair/typebox": "0.32.30",
"@supabase/supabase-js": "2.43.2",
"@ubiquity-os/ubiquity-os-kernel": "^2.2.0",
"@ubiquity-os/ubiquity-os-logger": "^1.3.2",
"commander": "12.1.0",
"hono": "^4.6.4",
"typebox-validators": "0.3.5"
},
"devDependencies": {
Expand Down Expand Up @@ -69,6 +72,7 @@
"prettier": "^3.2.5",
"supabase": "1.167.4",
"ts-jest": "29.1.2",
"tsx": "^4.19.1",
"typescript": "5.6.2",
"wrangler": "3.79.0"
},
Expand Down
4 changes: 2 additions & 2 deletions src/adapters/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SupabaseClient } from "@supabase/supabase-js";
import { Context } from "../types/context";
import { Context } from "@ubiquity-os/ubiquity-os-kernel";
import { Database } from "../types/database";

export function createAdapters(supabaseClient: SupabaseClient<Database>, context: Context) {
Expand All @@ -19,7 +19,7 @@ export function createAdapters(supabaseClient: SupabaseClient<Database>, context
async getWallet(userId: number) {
const { data, error } = await supabaseClient.from("users").select("*, wallets(address)").eq("id", userId).single();
if (error) {
context.logger.error("Failed to fetch wallet for user", userId, error);
context.logger.error("Failed to fetch wallet for user", { userId, postgres: error });
return null;
}
return data.wallets;
Expand Down
4 changes: 2 additions & 2 deletions src/handlers/command-parser.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Command, InvalidArgumentError } from "commander";
import packageJson from "../../package.json";
import { Context } from "../types/context";
import { CommandContext } from "../types/plugin-input";
import { queryUser } from "./query-user";

export class CommandParser {
readonly _program;

constructor(context: Context) {
constructor(context: CommandContext) {
const program = new Command();
program
.command("/query")
Expand Down
17 changes: 8 additions & 9 deletions src/handlers/query-user.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
import { Context } from "../types/context";
import { CommandContext } from "../types/plugin-input";

async function checkUserAccess(context: Context, username: string) {
async function checkUserAccess(context: CommandContext, username: string) {
const { octokit, payload } = context;
if (!payload.comment.user?.login) {
throw new Error("Missing User Login from payload, cannot check for collaborator status.");
}
try {
await octokit.repos.checkCollaborator({
await octokit.rest.repos.checkCollaborator({
username: payload.comment.user.login,
repo: payload.repository.name,
owner: payload.repository.owner.login,
});
} catch (e) {
if (!!e && typeof e === "object" && "status" in e && e.status === 404) {
await context.logger.fatal(`User @${payload.comment.user.login} cannot request user ${username} as it is not a collaborator.`);
return false;
throw context.logger.fatal(`User @${payload.comment.user.login} cannot request user ${username} as it is not a collaborator.`);
}
throw e;
}
return true;
}

export async function queryUser(context: Context, username: string) {
export async function queryUser(context: CommandContext, username: string) {
const {
octokit,
payload,
Expand All @@ -32,7 +31,7 @@ export async function queryUser(context: Context, username: string) {
try {
const {
data: { id },
} = await octokit.users.getByUsername({
} = await octokit.rest.users.getByUsername({
username,
});
if (!config.allowPublicQuery && !(await checkUserAccess(context, username))) {
Expand All @@ -55,13 +54,13 @@ User information for ${username} was not found.
body.push(`| Access | \`\`\`${Array.isArray(access.labels) ? access.labels.join(", ") : JSON.stringify(access.labels, null, 2)}\`\`\` |`);
}
}
await octokit.issues.createComment({
await octokit.rest.issues.createComment({
body: body.join("\n"),
owner: payload.repository.owner.login,
repo: payload.repository.name,
issue_number: payload.issue.number,
});
} catch (e) {
await context.logger.fatal(`Could not query user ${username}.`, e);
throw context.logger.fatal(`Could not query user ${username}.`, { e });
}
}
33 changes: 8 additions & 25 deletions src/run.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,22 @@
import { createClient } from "@supabase/supabase-js";
import { Logs } from "@ubiquity-os/ubiquity-os-logger";
import { CommanderError } from "commander";
import { Octokit } from "@octokit/rest";
import { createAdapters } from "./adapters";
import { CommandParser } from "./handlers/command-parser";
import { Context } from "./types/context";
import { Database } from "./types/database";
import { Env } from "./types/env";
import { PluginInputs } from "./types/plugin-input";
import { CommandContext } from "./types/plugin-input";

export async function run(inputs: PluginInputs, env: Env) {
const octokit = new Octokit({ auth: inputs.authToken });
const logger = new Logs("verbose");
if (inputs.eventName !== "issue_comment.created") {
logger.info(`Unsupported event ${inputs.eventName}, skipping.`);
export async function run(context: CommandContext) {
const { octokit, logger, eventName, payload } = context;
if (eventName !== "issue_comment.created") {
logger.info(`Unsupported event ${eventName}, skipping.`);
return;
}
const args = inputs.eventPayload.comment.body.trim().split(/\s+/);
const supabase = createClient<Database>(env.SUPABASE_URL, env.SUPABASE_KEY);
const context = {
eventName: inputs.eventName,
payload: inputs.eventPayload,
config: inputs.settings,
octokit,
logger,
adapters: {} as unknown as ReturnType<typeof createAdapters>,
} as Context;
context.adapters = createAdapters(supabase, context);
const args = payload.comment.body.trim().split(/\s+/);
const commandParser = new CommandParser(context);
try {
await commandParser.parse(args);
} catch (e) {
if (e instanceof CommanderError) {
if (e.code !== "commander.unknownCommand") {
context.logger.fatal("Commander error", { e });
await octokit.issues.createComment({
await octokit.rest.issues.createComment({
body: `\`\`\`
Failed to run command-query-user.
${e.message}
Expand All @@ -51,4 +33,5 @@ export async function run(inputs: PluginInputs, env: Env) {
throw e;
}
}
return { message: "ok" };
}
5 changes: 5 additions & 0 deletions src/types/plugin-input.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { EmitterWebhookEvent as WebhookEvent, EmitterWebhookEventName as WebhookEventName } from "@octokit/webhooks";
import { Context } from "@ubiquity-os/ubiquity-os-kernel";
import { LOG_LEVEL } from "@ubiquity-os/ubiquity-os-logger";
import { StandardValidator } from "typebox-validators";
import { createAdapters } from "../adapters";
import { SupportedEvents } from "./context";
import { StaticDecode, Type as T } from "@sinclair/typebox";
import { Env } from "./env";

export interface PluginInputs<T extends WebhookEventName = SupportedEvents> {
stateId: string;
Expand All @@ -24,3 +27,5 @@ export const pluginSettingsSchema = T.Object({
export const commandQueryUserSchemaValidator = new StandardValidator(pluginSettingsSchema);

export type PluginSettings = StaticDecode<typeof pluginSettingsSchema>;

export type CommandContext = Context<PluginSettings, Env, SupportedEvents> & { adapters: ReturnType<typeof createAdapters> };
55 changes: 16 additions & 39 deletions src/worker.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,20 @@
import { serve } from "@hono/node-server";
import { createClient } from "@supabase/supabase-js";
import { createPlugin } from "@ubiquity-os/ubiquity-os-kernel";
import manifest from "../manifest.json";
import { validateAndDecodeSchemas } from "./handlers/validator";
import { createAdapters } from "./adapters";
import { run } from "./run";
import { SupportedEvents } from "./types/context";
import { Database } from "./types/database";
import { Env } from "./types/env";
import { PluginSettings } from "./types/plugin-input";

export default {
async fetch(request: Request, env: Env): Promise<Response> {
try {
const url = new URL(request.url);
if (url.pathname === "/manifest.json" && request.method === "GET") {
return new Response(JSON.stringify(manifest), {
headers: { "content-type": "application/json" },
});
}
if (request.method !== "POST") {
return new Response(JSON.stringify({ error: `Only POST requests are supported.` }), {
status: 405,
headers: { "content-type": "application/json", Allow: "POST" },
});
}
const contentType = request.headers.get("content-type");
if (contentType !== "application/json") {
return new Response(JSON.stringify({ error: `Error: ${contentType} is not a valid content type` }), {
status: 400,
headers: { "content-type": "application/json" },
});
}
const webhookPayload = await request.json();
const result = validateAndDecodeSchemas(env, webhookPayload.settings);
webhookPayload.settings = result.decodedSettings;
await run(webhookPayload, result.decodedEnv);
return new Response(JSON.stringify("OK"), { status: 200, headers: { "content-type": "application/json" } });
} catch (error) {
return handleUncaughtError(error);
}
},
};

function handleUncaughtError(errors: unknown) {
console.error(errors);
const status = 500;
return new Response(JSON.stringify(errors), { status: status, headers: { "content-type": "application/json" } });
}
createPlugin<PluginSettings, Env, SupportedEvents>((context) => {
const supabase = createClient<Database>(context.env.SUPABASE_URL, context.env.SUPABASE_KEY);
return run({ ...context, adapters: createAdapters(supabase, context) });
}, manifest)
.then((server) => {
console.log("Starting server...");
return serve(server);
})
.catch(console.error);
6 changes: 3 additions & 3 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */

/* Language and Environment */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
"target": "es2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"lib": ["es2021"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
Expand All @@ -27,7 +27,7 @@
/* Modules */
"module": "commonjs" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
Expand Down
Loading

0 comments on commit 914bf62

Please sign in to comment.