#353 - Add OasstApiClient interaction with task
diff --git a/website/cypress/contract/oasst_api_contract_tests.cy.ts b/website/cypress/contract/oasst_api_contract_tests.cy.ts
index ff5bb1567a..1ba408d643 100644
--- a/website/cypress/contract/oasst_api_contract_tests.cy.ts
+++ b/website/cypress/contract/oasst_api_contract_tests.cy.ts
@@ -1,4 +1,4 @@
-import OasstApiClient from "src/lib/oasst_api_client";
+import { OasstApiClient } from "src/lib/oasst_api_client";
 
 describe("Contract test for Oasst API", function () {
   // Assumes this is running the mock server.
@@ -23,6 +23,27 @@ describe("Contract test for Oasst API", function () {
     expect(await oasstApiClient.ackTask(task.id, "321")).to.be.null;
   });
 
+  it("can record a taskInteraction", async () => {
+    const task = await oasstApiClient.fetchTask("random", {
+      sub: "test",
+      name: "test",
+      email: "test",
+    });
+    expect(
+      await oasstApiClient.interactTask(
+        "text_reply_to_message",
+        task.id,
+        "1",
+        { text: "Test" },
+        {
+          sub: "test",
+          name: "test",
+          email: "test",
+        }
+      )
+    ).to.be.not.null;
+  });
+
   // TODO(#354): Add test for 204
   // TODO(#354): Add test for parsing >=300, throwing an OasstError
   // TODO(#354): Add test for parsing >=300, throwing a generic error
diff --git a/website/src/lib/oasst_api_client.ts b/website/src/lib/oasst_api_client.ts
index 45a0859e15..7e22544ab5 100644
--- a/website/src/lib/oasst_api_client.ts
+++ b/website/src/lib/oasst_api_client.ts
@@ -1,5 +1,10 @@
 import { JWT } from "next-auth/jwt";
 
+declare global {
+  // eslint-disable-next-line no-var
+  var oasstApiClient: OasstApiClient | undefined;
+}
+
 class OasstError {
   message: string;
   errorCode: number;
@@ -12,7 +17,7 @@ class OasstError {
   }
 }
 
-export default class OasstApiClient {
+export class OasstApiClient {
   constructor(private readonly oasstApiUrl: string, private readonly oasstApiKey: string) {}
 
   private async post(path: string, body: any): Promise<any> {
@@ -61,4 +66,33 @@ export default class OasstApiClient {
       message_id: messageId,
     });
   }
+
+  // TODO return a strongly typed Task?
+  // This method is used to record interaction with task while fetching next task.
+  // This is a raw Json type, so we can't use it to strongly type the task.
+  async interactTask(
+    updateType: string,
+    messageId: string,
+    userMessageId: string,
+    content: object,
+    userToken: JWT
+  ): Promise<any> {
+    return this.post("/api/v1/tasks/interaction", {
+      type: updateType,
+      user: {
+        id: userToken.sub,
+        display_name: userToken.name || userToken.email,
+        auth_method: "local",
+      },
+      message_id: messageId,
+      user_message_id: userMessageId,
+      ...content,
+    });
+  }
+}
+
+export const oasstApiClient =
+  globalThis.oasstApiClient || new OasstApiClient(process.env.FASTAPI_URL, process.env.FASTAPI_KEY);
+if (process.env.NODE_ENV !== "production") {
+  globalThis.oasstApiClient = oasstApiClient;
 }
diff --git a/website/src/pages/api/new_task/[task_type].ts b/website/src/pages/api/new_task/[task_type].ts
index bbe31bef38..addcf3d8bc 100644
--- a/website/src/pages/api/new_task/[task_type].ts
+++ b/website/src/pages/api/new_task/[task_type].ts
@@ -1,5 +1,5 @@
 import { getToken } from "next-auth/jwt";
-import OasstApiClient from "src/lib/oasst_api_client";
+import { oasstApiClient } from "src/lib/oasst_api_client";
 import prisma from "src/lib/prismadb";
 
 /**
@@ -21,8 +21,6 @@ const handler = async (req, res) => {
     return;
   }
 
-  const oasstApiClient = new OasstApiClient(process.env.FASTAPI_URL, process.env.FASTAPI_KEY);
-
   // Fetch the new task.
   const task = await oasstApiClient.fetchTask(task_type, token);
 
diff --git a/website/src/pages/api/update_task.ts b/website/src/pages/api/update_task.ts
index 2d371354e5..4eea8c1e1f 100644
--- a/website/src/pages/api/update_task.ts
+++ b/website/src/pages/api/update_task.ts
@@ -1,4 +1,5 @@
 import { getToken } from "next-auth/jwt";
+import { oasstApiClient } from "src/lib/oasst_api_client";
 import prisma from "src/lib/prismadb";
 
 /**
@@ -34,28 +35,7 @@ const handler = async (req, res) => {
     },
   });
 
-  // Send the interaction to the Task Backend.  This automatically fetches the
-  // next task in the sequence (or the done task).
-  // TODO(#353): Move this into OasstApiClient.
-  const interactionRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/tasks/interaction`, {
-    method: "POST",
-    headers: {
-      "X-API-Key": process.env.FASTAPI_KEY,
-      "Content-Type": "application/json",
-    },
-    body: JSON.stringify({
-      type: update_type,
-      user: {
-        id: token.sub,
-        display_name: token.name || token.email,
-        auth_method: "local",
-      },
-      message_id: id,
-      user_message_id: interaction.id,
-      ...content,
-    }),
-  });
-  const newTask = await interactionRes.json();
+  const newTask = await oasstApiClient.interactTask(update_type, id, interaction.id, content, token);
 
   // Stores the new task with our database.
   const newRegisteredTask = await prisma.registeredTask.create({

#353
My attempt at adding a `taskInteraction` method with its use in `api/update_task.ts` and contract test.

This is my first time working with cypress, but I hope I understood the idea.