diff --git a/.env.example b/.env.example
index ff9cd4a..2117445 100644
--- a/.env.example
+++ b/.env.example
@@ -14,4 +14,10 @@ EMAIL_SERVER_PORT=
EMAIL_FROM=
# comma separated list of emails allowed to sign in to the app
-ALLOWED_EMAILS=
\ No newline at end of file
+ALLOWED_EMAILS=
+
+# used by the tests.js script. You must create the test credentials via the API Management page
+# when spinning this application, and using localhost as the origin
+TEST_VENDOR_ID=
+TEST_API_KEY=
+TEST_API_PORT=
\ No newline at end of file
diff --git a/README.md b/README.md
index f7eacc8..1a3f60f 100644
--- a/README.md
+++ b/README.md
@@ -11,12 +11,12 @@
```mermaid
sequenceDiagram
Human->>+Chiron: Accesses the service based on the ALLOWED_EMAILS list
- Human->>+Chiron: Provides vendor information (via API Management page)
+ Human->>+Chiron: Provides vendor information via API Management page (name, origin URL, callback URL)
Chiron->>+Human: Outputs vendor-specific API keys
Human->>+App: Configures App to hit Chiron with given API keys
- App->>+Chiron: Hits Chiron with any data generated by AI (or not)
+ App->>+Chiron: Hits Chiron with any data generated by AI (or not) with a POST request to /api/data/completions
Human->>+Chiron: Review generated data (Human-in-the-loop)
- Chiron->>+App: Hits App back with reviewed data
+ Chiron->>+App: Hits App back with reviewed data with a POST request to the callback URL
```
## Development
diff --git a/app/api/data/completions/review/route.js b/app/api/data/completions/review/route.js
index 11e8d0e..b4ba631 100644
--- a/app/api/data/completions/review/route.js
+++ b/app/api/data/completions/review/route.js
@@ -35,6 +35,10 @@ export async function POST(req) {
const result = await reviewCompletion(data, direction);
+ if (result?.acknowledged || result?.insertedId) {
+ // TODO: Call the vendor's webhook based on the result property
+ }
+
return new NextResponse(JSON.stringify(result), {
status: 200,
});
diff --git a/app/api/data/completions/route.js b/app/api/data/completions/route.js
index df8b911..2686509 100644
--- a/app/api/data/completions/route.js
+++ b/app/api/data/completions/route.js
@@ -15,13 +15,13 @@ export async function POST(req) {
];
if (!vendorId) {
- return new NextResponse("Bad Request", {
+ return new NextResponse(JSON.stringify("Bad Request"), {
status: 400,
});
}
if (!apiKey) {
- return new NextResponse("Bad Request", {
+ return new NextResponse(JSON.stringify("Bad Request"), {
status: 400,
});
}
@@ -31,7 +31,7 @@ export async function POST(req) {
const { host: vendorHost } = new URL(result.vendorUrl);
if (vendorHost !== originHost) {
- return new NextResponse("Forbidden", {
+ return new NextResponse(JSON.stringify("Forbidden"), {
status: 403,
});
}
@@ -39,7 +39,7 @@ export async function POST(req) {
const decryptedApiKey = decrypt(result.apiKey);
if (decryptedApiKey !== apiKey) {
- return new NextResponse("Unauthorized", {
+ return new NextResponse(JSON.stringify("Unauthorized"), {
status: 401,
});
}
@@ -47,7 +47,7 @@ export async function POST(req) {
const body = await req.json();
if (!body || !body?._id) {
- return new NextResponse("Bad Request", {
+ return new NextResponse(JSON.stringify("Bad Request"), {
status: 400,
});
}
@@ -63,13 +63,13 @@ export async function POST(req) {
try {
await saveCompletion(data);
- return new NextResponse("Created", {
+ return new NextResponse(JSON.stringify("Created"), {
status: 201,
});
} catch (error) {
console.error(error);
- return new NextResponse("Error", {
+ return new NextResponse(JSON.stringify("Error"), {
status: 500,
});
}
diff --git a/app/completions/approved/page.js b/app/completions/approved/page.js
index 82c73d5..cf9aa70 100644
--- a/app/completions/approved/page.js
+++ b/app/completions/approved/page.js
@@ -9,7 +9,7 @@ export default async function Home() {
if (!approvedCompletions || approvedCompletions.length === 0) {
return (
-
+
);
}
diff --git a/app/completions/pending/page.js b/app/completions/pending/page.js
index ff9a43b..547073a 100644
--- a/app/completions/pending/page.js
+++ b/app/completions/pending/page.js
@@ -8,7 +8,7 @@ export default async function PendingCompletionsReviewPage() {
const pendingReviews = await res.json();
if (!pendingReviews || pendingReviews.length === 0) {
- return ;
+ return ;
}
const completions = pendingReviews.map(
diff --git a/app/completions/rejected/page.js b/app/completions/rejected/page.js
index 0e969ad..d9fb0e7 100644
--- a/app/completions/rejected/page.js
+++ b/app/completions/rejected/page.js
@@ -9,7 +9,7 @@ export default async function Home() {
if (!rejectedCompletions || rejectedCompletions.length === 0) {
return (
-
+
);
}
diff --git a/bun.lockb b/bun.lockb
index 014a7f8..0281459 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/containers/api-management.js b/containers/api-management.js
index 1ba6ced..b93b84f 100644
--- a/containers/api-management.js
+++ b/containers/api-management.js
@@ -9,6 +9,7 @@ import {
Heading,
List,
Menu,
+ Text,
TextInput,
} from "grommet";
import { useState } from "react";
@@ -89,6 +90,17 @@ export default function ApiManagementContainer(props) {
(
+
+ {item.name}
+
+ )}
+ secondaryKey={(item) => (
+
+ Callback URL: {item.callbackUrl}
+
+ )}
+ itemKey={(item) => item.name}
pad={{ left: "small", right: "none" }}
action={(item, index) => (