Deleting some unused API methods, storing interactions properly to the Task Backend, Adding explanatory comments, using a task queue in the client side

This commit is contained in:
Keith Stevens
2022-12-18 15:43:19 +09:00
parent 699d0a948c
commit a53d69b682
5 changed files with 97 additions and 58 deletions
-5
View File
@@ -1,5 +0,0 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
export default function handler(req, res) {
res.status(200).json({ name: "John Doe" });
}
+13 -1
View File
@@ -2,16 +2,25 @@ import { unstable_getServerSession } from "next-auth/next";
import { authOptions } from "./auth/[...nextauth]";
/**
* Returns a list of prompts from the Labeler Backend.
* Returns a new task created from the Task Backend. We do a few things here:
*
* 1) Get the task from the backend and register the requesting user.
* 2) Store the task in our local database.
* 3) Send and Ack to the Task Backend with our local id for the task.
* 4) Return everything to the client.
*/
export default async (req, res) => {
const session = await unstable_getServerSession(req, res, authOptions);
// Return nothing if the user isn't registered.
if (!session) {
res.status(401).end();
return;
}
// Fetch the new task.
//
// This needs to be refactored into an easier to use library.
const taskRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/tasks/`, {
method: "POST",
headers: {
@@ -29,6 +38,7 @@ export default async (req, res) => {
});
const task = await taskRes.json();
// Store the task and link it to the user..
const registeredTask = await prisma.registeredTask.create({
data: {
task,
@@ -40,6 +50,7 @@ export default async (req, res) => {
},
});
// Update the backend with our Task ID
const ackRes = await fetch(
`${process.env.FASTAPI_URL}/api/v1/tasks/${task.id}/ack`,
{
@@ -55,5 +66,6 @@ export default async (req, res) => {
);
const ack = await ackRes.json();
// Send the results to the client.
res.status(200).json(registeredTask);
};
-28
View File
@@ -1,28 +0,0 @@
import { unstable_getServerSession } from "next-auth/next";
import { authOptions } from "./auth/[...nextauth]";
/**
* Returns a list of prompts from the Labeler Backend.
*/
export default async (req, res) => {
const session = await unstable_getServerSession(req, res, authOptions);
if (!session) {
res.status(401).end();
return;
}
try {
const promptRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/prompts`, {
headers: {
"X-API-Key": process.env.FASTAPI_KEY,
},
});
const prompts = await promptRes.json();
res.status(200).json(prompts);
} catch (error) {
console.error(error);
res.status(500);
}
res.end();
};
+30 -4
View File
@@ -2,18 +2,28 @@ import { unstable_getServerSession } from "next-auth/next";
import { authOptions } from "./auth/[...nextauth]";
/**
* Returns a list of prompts from the Labeler Backend.
* Stores the task interaction with the Task Backend and then returns the next task generated.
*
* This implicity does a few things:
* 1) Stores the answer with the Task Backend.
* 2) Records the new task in our local database.
* 3) (TODO) Acks the new task with our local task ID to the Task Backend.
* 4) Returns the newly created task to the client.
*/
export default async (req, res) => {
const session = await unstable_getServerSession(req, res, authOptions);
// Return nothing if the user isn't registered.
if (!session) {
res.status(401).end();
return;
}
// Parse out the local task ID and the interaction contents.
const { id, content } = await JSON.parse(req.body);
// Log the interaction locally to create our user_post_id needed by the Task
// Backend.
const interaction = await prisma.taskInteraction.create({
data: {
content,
@@ -25,6 +35,8 @@ export default async (req, res) => {
},
});
// Send the interaction to the Task Backend. This automatically fetches the
// next task in the sequence (or the done task).
const interactionRes = await fetch(
`${process.env.FASTAPI_URL}/api/v1/tasks/interaction`,
{
@@ -46,9 +58,23 @@ export default async (req, res) => {
}),
}
);
console.log(interactionRes.status);
const newTask = await interactionRes.json();
console.log(newTask);
res.status(200).json(newTask);
// Stores the new task with our database.
const newRegisteredTask = await prisma.registeredTask.create({
data: {
task: newTask,
user: {
connect: {
id: session.user.id,
},
},
},
});
// TODO: Ack the task with the Task Backend using the newly created local
// task ID.
// Send the next task in the sequence to the client.
res.status(200).json(newRegisteredTask);
};
+54 -20
View File
@@ -8,6 +8,10 @@ import useSWRMutation from "swr/mutation";
const fetcher = (url) => axios.get(url).then((res) => res.data);
/**
* A helper function to post updates to tasks.
* This ensures the content sent is serialized to JSON.
*/
async function sendRequest(url, { arg }) {
return fetch(url, {
method: "POST",
@@ -16,49 +20,79 @@ async function sendRequest(url, { arg }) {
}
export default function NewPage() {
const [done, setDone] = useState(false);
// Use an array of tasks that record the sequence of steps until a task is
// deemed complete.
const [tasks, setTasks] = useState([]);
// A quick reference to the input element. This should be factored into the
// component doing the actual task rendering.
const responseEl = useRef(null);
const {
data: registeredTask,
errors,
isLoading,
} = useSWRImmutable("/api/new_task", fetcher);
// Fetch the very fist task. We can ignore everything except isLoading
// because the onSuccess handler will update `tasks` when ready.
const { isLoading } = useSWRImmutable("/api/new_task", fetcher, {
onSuccess: (data) => {
setTasks([data]);
},
});
// Every time we submit an answer to the latest task, let the backend handle
// all the interactions then add the resulting task to the queue. This ends
// when we hit the done task.
const { trigger, isMutating } = useSWRMutation(
"/api/update_task",
sendRequest,
{
onSuccess: async (data) => {
const newTask = await data.json();
console.log(newTask);
setDone(true);
// This is the more efficient way to update a react state array.
setTasks((oldTasks) => [...oldTasks, newTask]);
},
}
);
const submitResponse = () => {
// Trigger a mutation that updates the current task. We should probably
// signal somewhere that this interaction is being processed.
const submitResponse = (t) => {
trigger({
id: registeredTask.id,
id: t.id,
content: {
rating: responseEl.current.value,
},
});
};
// Show something informative while loading the first task.
if (isLoading) {
return <div>Loading</div>;
}
// Iterate through each of the tasks and show it's contents, get a response to it, or show the done state.
//
// Right now this just works for the rating task.
//
// Displaying and fetching results for each task type should be factored into
// different components that handle the presentation and response structures.
// The results should be packaged into a single object with all the fields
// sent to the backend.
return (
<div>
<div>{registeredTask.id}</div>
<div>{registeredTask.task.type}</div>
<div>{registeredTask.task.text}</div>
<div>{registeredTask.task.summary}</div>
<div>
{registeredTask.task.scale.min} to {registeredTask.task.scale.max}
</div>
<input type="text" ref={responseEl} />
<button onClick={submitResponse}>Submit Response</button>
{done && <div>Done!</div>}
{tasks.map((t) => (
<div key={t.id}>
<div>{t.task.type}</div>
<div>{t.task.text}</div>
{t.task.summary && (
<>
<div>{t.task.summary}</div>
<div>
{t.task.scale.min} to {t.task.scale.max}
</div>
<input type="text" ref={responseEl} />
<button onClick={() => submitResponse(t)}>Submit Response</button>
</>
)}
</div>
))}
</div>
);
}