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

feat: schema validation #19

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
58 changes: 58 additions & 0 deletions .github/workflows/update-configuration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: "Update Configuration"

on:
workflow_dispatch:
push:

jobs:
update:
name: "Update Configuration in manifest.json"
runs-on: ubuntu-latest
permissions: write-all

steps:
- uses: actions/checkout@v4

- name: Setup node
uses: actions/setup-node@v4
with:
node-version: "20.10.0"

- name: Install deps and run configuration update
run: |
yarn install --immutable --immutable-cache --check-cache
yarn tsc --noCheck --project tsconfig.json

- name: Update manifest configuration using GitHub Script
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = require('path');

const { pluginSettingsSchema } = require('./src/types');

const manifestPath = path.resolve("${{ github.workspace }}", './manifest.json');
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));

const configuration = JSON.stringify(pluginSettingsSchema);

manifest["configuration"] = JSON.parse(configuration);

const updatedManifest = JSON.stringify(manifest, null, 2)
console.log('Updated manifest:', updatedManifest);
fs.writeFileSync(manifestPath, updatedManifest);

- name: Commit and Push generated types
run: |
git config --global user.name 'ubiquity-os[bot]'
git config --global user.email 'ubiquity-os[bot]@users.noreply.github.com'
git add ./manifest.json
if [ -n "$(git diff-index --cached --name-only HEAD)" ]; then
git commit -m "chore: updated generated configuration" || echo "Lint-staged check failed"
git push origin HEAD:${{ github.ref_name }}
else
echo "No changes to commit"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35 changes: 33 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
{
"name": "Generate vector embeddings",
"description": "Enables the storage, updating, and deletion of issue comment embeddings.",
"ubiquity:listeners": ["issue_comment.created", "issue_comment.edited", "issue_comment.deleted", "issues.opened", "issues.edited", "issues.deleted", "issues.labeled"]
}
"ubiquity:listeners": [
"issue_comment.created",
"issue_comment.edited",
"issue_comment.deleted",
"issues.opened",
"issues.edited",
"issues.deleted",
"issues.labeled"
],
"configuration": {
"default": {},
"type": "object",
"properties": {
"matchThreshold": {
"default": 0.95,
"type": "number"
},
"warningThreshold": {
"default": 0.75,
"type": "number"
},
"jobMatchingThreshold": {
"default": 0.75,
"type": "number"
}
},
"required": [
"matchThreshold",
"warningThreshold",
"jobMatchingThreshold"
]
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"supabase": "1.200.3",
"ts-jest": "29.1.5",
"tsx": "4.15.6",
"typescript": "5.4.5",
"typescript": "5.6.2",
"typescript-eslint": "7.13.1",
"wrangler": "3.78.12"
},
Expand Down
36 changes: 36 additions & 0 deletions src/handlers/validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { TransformDecodeCheckError, TransformDecodeError, Value, ValueError } from "@sinclair/typebox/value";
import { Env, envValidator, PluginSettings, pluginSettingsSchema, pluginSettingsValidator } from "../types";

export function validateAndDecodeSchemas(env: Env, rawSettings: object) {
const errors: ValueError[] = [];
const settings = Value.Default(pluginSettingsSchema, rawSettings) as PluginSettings;

if (!pluginSettingsValidator.test(settings)) {
for (const error of pluginSettingsValidator.errors(settings)) {
console.error(error);
errors.push(error);
}
}

if (!envValidator.test(env)) {
for (const error of envValidator.errors(env)) {
console.error(error);
errors.push(error);
}
}

if (errors.length) {
throw { errors };
}

try {
const decodedEnv = Value.Decode(envValidator.schema, env);
const decodedSettings = Value.Decode(pluginSettingsSchema, settings);
return { decodedEnv, decodedSettings };
} catch (e) {
if (e instanceof TransformDecodeCheckError || e instanceof TransformDecodeError) {
throw { errors: [e.error] };
}
throw e;
}
}
51 changes: 16 additions & 35 deletions src/worker.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { Value } from "@sinclair/typebox/value";
import { plugin } from "./plugin";
import { Env, envValidator, pluginSettingsSchema, pluginSettingsValidator } from "./types";
import manifest from "../manifest.json";
import { validateAndDecodeSchemas } from "./handlers/validator";
import { plugin } from "./plugin";
import { Env } from "./types";

export default {
async fetch(request: Request, env: Env): Promise<Response> {
try {
if (request.method === "GET") {
const url = new URL(request.url);
if (url.pathname === "/manifest.json") {
const url = new URL(request.url);
if (url.pathname === "/manifest") {
if (request.method === "GET") {
return new Response(JSON.stringify(manifest), {
headers: { "content-type": "application/json" },
});
} else if (request.method === "POST") {
const webhookPayload = await request.json();
validateAndDecodeSchemas(env, webhookPayload.settings);
return new Response(JSON.stringify({ message: "Schema is valid" }), { status: 200, headers: { "content-type": "application/json" } });
}
}
if (request.method !== "POST") {
Expand All @@ -29,42 +33,19 @@ export default {
}

const webhookPayload = await request.json();
const settings = Value.Decode(pluginSettingsSchema, Value.Default(pluginSettingsSchema, webhookPayload.settings));

if (!pluginSettingsValidator.test(settings)) {
const errors: string[] = [];
for (const error of pluginSettingsValidator.errors(settings)) {
console.error(error);
errors.push(`${error.path}: ${error.message}`);
}
return new Response(JSON.stringify({ error: `Error: "Invalid settings provided. ${errors.join("; ")}"` }), {
status: 400,
headers: { "content-type": "application/json" },
});
}
if (!envValidator.test(env)) {
const errors: string[] = [];
for (const error of envValidator.errors(env)) {
console.error(error);
errors.push(`${error.path}: ${error.message}`);
}
return new Response(JSON.stringify({ error: `Error: "Invalid environment provided. ${errors.join("; ")}"` }), {
status: 400,
headers: { "content-type": "application/json" },
});
}
const { decodedSettings, decodedEnv } = validateAndDecodeSchemas(env, webhookPayload.settings);

webhookPayload.settings = settings;
await plugin(webhookPayload, env);
webhookPayload.settings = decodedSettings;
await plugin(webhookPayload, decodedEnv);
return new Response(JSON.stringify("OK"), { status: 200, headers: { "content-type": "application/json" } });
} catch (error) {
return handleUncaughtError(error);
}
},
};

function handleUncaughtError(error: unknown) {
console.error(error);
function handleUncaughtError(errors: unknown) {
console.error(errors);
const status = 500;
return new Response(JSON.stringify({ error }), { status: status, headers: { "content-type": "application/json" } });
return new Response(JSON.stringify(errors), { status: status, headers: { "content-type": "application/json" } });
}
2 changes: 1 addition & 1 deletion tests/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe("Plugin tests", () => {

it("Should serve the manifest file", async () => {
const worker = (await import("../src/worker")).default;
const response = await worker.fetch(new Request("http://localhost/manifest.json"), {
const response = await worker.fetch(new Request("http://localhost/manifest"), {
gentlementlegen marked this conversation as resolved.
Show resolved Hide resolved
SUPABASE_KEY: "test",
SUPABASE_URL: "test",
VOYAGEAI_API_KEY: "test",
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6656,10 +6656,10 @@ [email protected]:
"@typescript-eslint/parser" "7.13.1"
"@typescript-eslint/utils" "7.13.1"

typescript@5.4.5:
version "5.4.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611"
integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==
typescript@5.6.2:
version "5.6.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0"
integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==

uc.micro@^2.0.0, uc.micro@^2.1.0:
version "2.1.0"
Expand Down