From 4397a310fef9633d2726ff4f3d371a25e283e2e2 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Wed, 20 Apr 2022 01:29:12 +0400 Subject: [PATCH] feat: create dashboards with graphql --- schema.graphql | 15 +++++++ src/backend/migrate/noSchemaMigrate.ts | 2 +- src/graphql/builder.ts | 1 + src/graphql/schema/dashboards.ts | 47 ++++++++++++++++++++++ src/graphql/schema/search.ts | 2 +- src/graphql/types.generated.ts | 22 ++++++++++ src/pages/api/create-dashboard-from-ids.ts | 39 ------------------ src/pages/dashboards/index.tsx | 31 +++++++++----- src/web/dashboards/queries.generated.tsx | 10 ++++- src/web/dashboards/queries.graphql | 8 ++++ src/web/worker/getDashboardQuestions.ts | 26 ------------ 11 files changed, 124 insertions(+), 79 deletions(-) delete mode 100644 src/pages/api/create-dashboard-from-ids.ts delete mode 100644 src/web/worker/getDashboardQuestions.ts diff --git a/schema.graphql b/schema.graphql index d6b6445..d998103 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1,3 +1,14 @@ +input CreateDashboardInput { + creator: String + description: String + ids: [ID!]! + title: String! +} + +type CreateDashboardResult { + dashboard: Dashboard! +} + type Dashboard { creator: String! description: String! @@ -9,6 +20,10 @@ type Dashboard { """Date serialized as the Unix timestamp.""" scalar Date +type Mutation { + createDashboard(input: CreateDashboardInput!): CreateDashboardResult! +} + type PageInfo { endCursor: String hasNextPage: Boolean! diff --git a/src/backend/migrate/noSchemaMigrate.ts b/src/backend/migrate/noSchemaMigrate.ts index 19a1f5a..43bbf20 100644 --- a/src/backend/migrate/noSchemaMigrate.ts +++ b/src/backend/migrate/noSchemaMigrate.ts @@ -15,7 +15,7 @@ const migrate = async () => { FantasySCOTUS: "fantasyscotus", Foretold: "foretold", "GiveWell/OpenPhilanthropy": "givewellopenphil", - "Good Judgment": "goodjudgement", + "Good Judgment": "goodjudgment", "Good Judgment Open": "goodjudgmentopen", Infer: "infer", Kalshi: "kalshi", diff --git a/src/graphql/builder.ts b/src/graphql/builder.ts index 1c42aa1..a5c1bba 100644 --- a/src/graphql/builder.ts +++ b/src/graphql/builder.ts @@ -37,3 +37,4 @@ builder.scalarType("Date", { }); builder.queryType({}); +builder.mutationType({}); diff --git a/src/graphql/schema/dashboards.ts b/src/graphql/schema/dashboards.ts index 4afc779..e1e8b4d 100644 --- a/src/graphql/schema/dashboards.ts +++ b/src/graphql/schema/dashboards.ts @@ -1,6 +1,7 @@ import { Dashboard } from "@prisma/client"; import { prisma } from "../../backend/database/prisma"; +import { hash } from "../../backend/utils/hash"; import { builder } from "../builder"; import { QuestionObj } from "./questions"; @@ -40,3 +41,49 @@ builder.queryField("dashboard", (t) => }, }) ); + +const CreateDashboardResult = builder + .objectRef<{ dashboard: Dashboard }>("CreateDashboardResult") + .implement({ + fields: (t) => ({ + dashboard: t.field({ + type: DashboardObj, + resolve: (parent) => parent.dashboard, + }), + }), + }); + +const CreateDashboardInput = builder.inputType("CreateDashboardInput", { + fields: (t) => ({ + title: t.string({ required: true }), + description: t.string(), + creator: t.string(), + ids: t.idList({ required: true }), + }), +}); + +builder.mutationField("createDashboard", (t) => + t.field({ + type: CreateDashboardResult, + args: { + input: t.arg({ type: CreateDashboardInput, required: true }), + }, + resolve: async (parent, args) => { + const id = hash(JSON.stringify(args.input.ids)); + const dashboard = await prisma.dashboard.create({ + data: { + id, + title: args.input.title, + description: args.input.description || "", + creator: args.input.creator || "", + contents: args.input.ids, + extra: [], + timestamp: new Date(), + }, + }); + return { + dashboard, + }; + }, + }) +); diff --git a/src/graphql/schema/search.ts b/src/graphql/schema/search.ts index 393bc1b..2eff5dc 100644 --- a/src/graphql/schema/search.ts +++ b/src/graphql/schema/search.ts @@ -27,7 +27,7 @@ builder.queryField("searchQuestions", (t) => const forecastsThreshold = input.forecastsThreshold; const starsThreshold = input.starsThreshold; const platformsIncludeGuesstimate = - input.forecastingPlatforms.includes("guesstimate") && + input.forecastingPlatforms?.includes("guesstimate") && starsThreshold <= 1; // preparation diff --git a/src/graphql/types.generated.ts b/src/graphql/types.generated.ts index ba99a6d..32b4855 100644 --- a/src/graphql/types.generated.ts +++ b/src/graphql/types.generated.ts @@ -14,6 +14,18 @@ export type Scalars = { Date: any; }; +export type CreateDashboardInput = { + creator?: InputMaybe; + description?: InputMaybe; + ids: Array; + title: Scalars['String']; +}; + +export type CreateDashboardResult = { + __typename?: 'CreateDashboardResult'; + dashboard: Dashboard; +}; + export type Dashboard = { __typename?: 'Dashboard'; creator: Scalars['String']; @@ -23,6 +35,16 @@ export type Dashboard = { title: Scalars['String']; }; +export type Mutation = { + __typename?: 'Mutation'; + createDashboard: CreateDashboardResult; +}; + + +export type MutationCreateDashboardArgs = { + input: CreateDashboardInput; +}; + export type PageInfo = { __typename?: 'PageInfo'; endCursor?: Maybe; diff --git a/src/pages/api/create-dashboard-from-ids.ts b/src/pages/api/create-dashboard-from-ids.ts deleted file mode 100644 index be4ec52..0000000 --- a/src/pages/api/create-dashboard-from-ids.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { NextApiRequest, NextApiResponse } from "next/types"; - -import { pgInsertIntoDashboard } from "../../backend/database/pg-wrapper"; -import { hash } from "../../backend/utils/hash"; - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { - if (req.method !== "POST") { - res.status(400).send("Expected POST request"); - return; - } - - let body = req.body; - console.log(body); - try { - let id = hash(JSON.stringify(body.ids)); - let pgResponse = await pgInsertIntoDashboard({ - datum: { - id: id, - title: body.title || "", - description: body.description || "", - contents: body.ids, - creator: body.creator || "", - extra: [], - }, - }); - res.status(200).send({ - dashboardId: id, - pgResponse: pgResponse, - }); - } catch (error) { - res.status(400).send({ - id: null, - pgResponse: JSON.stringify(error), - }); - } -} diff --git a/src/pages/dashboards/index.tsx b/src/pages/dashboards/index.tsx index f1ba14a..c90e332 100644 --- a/src/pages/dashboards/index.tsx +++ b/src/pages/dashboards/index.tsx @@ -1,24 +1,33 @@ -import axios from "axios"; import { NextPage } from "next"; import { useRouter } from "next/router"; +import { useMutation } from "urql"; +import { CreateDashboardDocument } from "../../web/dashboards/queries.generated"; import { DashboardCreator } from "../../web/display/DashboardCreator"; import { Layout } from "../../web/display/Layout"; import { LineHeader } from "../../web/display/LineHeader"; const DashboardsPage: NextPage = () => { const router = useRouter(); + const [createDashboardResult, createDashboard] = useMutation( + CreateDashboardDocument + ); - const handleSubmit = async (data) => { - // Send to server to create - // Get back the id - let response = await axios({ - url: "/api/create-dashboard-from-ids", - method: "POST", - headers: { "Content-Type": "application/json" }, - data: JSON.stringify(data), - }).then((res) => res.data); - await router.push(`/dashboards/view/${response.dashboardId}`); + const handleSubmit = async (data: any) => { + await createDashboard({ + input: { + title: data.title, + description: data.description, + creator: data.creator, + ids: data.ids, + }, + }); + console.log(createDashboardResult); + const dashboardId = createDashboardResult?.data?.result?.dashboard?.id; + if (!dashboardId) { + throw new Error("Couldn't create a dashboard"); // TODO - toaster + } + await router.push(`/dashboards/view/${dashboardId}`); }; return ( diff --git a/src/web/dashboards/queries.generated.tsx b/src/web/dashboards/queries.generated.tsx index 6ab8352..15666f9 100644 --- a/src/web/dashboards/queries.generated.tsx +++ b/src/web/dashboards/queries.generated.tsx @@ -11,5 +11,13 @@ export type DashboardByIdQueryVariables = Types.Exact<{ export type DashboardByIdQuery = { __typename?: 'Query', result: { __typename?: 'Dashboard', id: string, title: string, description: string, creator: string, questions: Array<{ __typename?: 'Question', id: string, url: string, title: string, description: string, timestamp: any, visualization?: string | null, options: Array<{ __typename?: 'ProbabilityOption', name?: string | null, probability?: number | null }>, platform: { __typename?: 'Platform', id: string, label: string }, qualityIndicators: { __typename?: 'QualityIndicators', stars: number, numForecasts?: number | null } }> } }; +export type CreateDashboardMutationVariables = Types.Exact<{ + input: Types.CreateDashboardInput; +}>; + + +export type CreateDashboardMutation = { __typename?: 'Mutation', result: { __typename?: 'CreateDashboardResult', dashboard: { __typename?: 'Dashboard', id: string, title: string, description: string, creator: string, questions: Array<{ __typename?: 'Question', id: string, url: string, title: string, description: string, timestamp: any, visualization?: string | null, options: Array<{ __typename?: 'ProbabilityOption', name?: string | null, probability?: number | null }>, platform: { __typename?: 'Platform', id: string, label: string }, qualityIndicators: { __typename?: 'QualityIndicators', stars: number, numForecasts?: number | null } }> } } }; + export const DashboardFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Dashboard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Dashboard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"creator"}},{"kind":"Field","name":{"kind":"Name","value":"questions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Question"}}]}}]}},...QuestionFragmentDoc.definitions]} as unknown as DocumentNode; -export const DashboardByIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"DashboardById"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"result"},"name":{"kind":"Name","value":"dashboard"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Dashboard"}}]}}]}},...DashboardFragmentDoc.definitions]} as unknown as DocumentNode; \ No newline at end of file +export const DashboardByIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"DashboardById"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"result"},"name":{"kind":"Name","value":"dashboard"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Dashboard"}}]}}]}},...DashboardFragmentDoc.definitions]} as unknown as DocumentNode; +export const CreateDashboardDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateDashboard"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateDashboardInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"result"},"name":{"kind":"Name","value":"createDashboard"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dashboard"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Dashboard"}}]}}]}}]}},...DashboardFragmentDoc.definitions]} as unknown as DocumentNode; \ No newline at end of file diff --git a/src/web/dashboards/queries.graphql b/src/web/dashboards/queries.graphql index fe9cbfa..def3854 100644 --- a/src/web/dashboards/queries.graphql +++ b/src/web/dashboards/queries.graphql @@ -13,3 +13,11 @@ query DashboardById($id: ID!) { ...Dashboard } } + +mutation CreateDashboard($input: CreateDashboardInput!) { + result: createDashboard(input: $input) { + dashboard { + ...Dashboard + } + } +} diff --git a/src/web/worker/getDashboardQuestions.ts b/src/web/worker/getDashboardQuestions.ts deleted file mode 100644 index 49ed1b6..0000000 --- a/src/web/worker/getDashboardQuestions.ts +++ /dev/null @@ -1,26 +0,0 @@ -import axios from "axios"; - -export async function createDashboard(payload) { - let data = { dashboardId: null }; - try { - let { title, description, ids, creator, extra } = payload; - console.log(payload); - let response = await axios({ - url: "/api/create-dashboard-from-ids", - method: "post", - data: { - title: title || "", - description: description || "", - ids: ids, - creator: creator || "", - extra: [], - }, - }); - data = response.data; - console.log(data); - } catch (error) { - console.log(error); - } finally { - return data; - } -}