diff --git a/packages/hub/.gitignore b/packages/hub/.gitignore index e0f8307732..c04a9a2c17 100644 --- a/packages/hub/.gitignore +++ b/packages/hub/.gitignore @@ -4,4 +4,7 @@ /tsconfig.tsbuildinfo /.next /.vscode -__generated__ + +# Ignore all of the generated files, but keep the directory +src/__generated__/* +!src/__generated__/.gitkeep diff --git a/packages/hub/prisma/migrations/20240412183853_mode_export_to_variables/migration.sql b/packages/hub/prisma/migrations/20240412183853_mode_export_to_variables/migration.sql new file mode 100644 index 0000000000..ff99bcc569 --- /dev/null +++ b/packages/hub/prisma/migrations/20240412183853_mode_export_to_variables/migration.sql @@ -0,0 +1,48 @@ +/* + Warnings: + + - You are about to drop the `ModelExport` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "ModelExport" DROP CONSTRAINT "ModelExport_modelRevisionId_fkey"; + +-- DropTable +DROP TABLE "ModelExport"; + +-- CreateTable +CREATE TABLE "Variable" ( + "id" TEXT NOT NULL, + "variableName" TEXT NOT NULL, + "modelId" TEXT NOT NULL, + + CONSTRAINT "Variable_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "VariableRevision" ( + "id" TEXT NOT NULL, + "modelRevisionId" TEXT NOT NULL, + "variableId" TEXT NOT NULL, + "variableName" TEXT NOT NULL, + "title" TEXT, + "docstring" TEXT NOT NULL DEFAULT '', + "variableType" TEXT NOT NULL DEFAULT 'OTHER', + + CONSTRAINT "VariableRevision_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Variable_modelId_variableName_key" ON "Variable"("modelId", "variableName"); + +-- CreateIndex +CREATE UNIQUE INDEX "VariableRevision_modelRevisionId_variableName_key" ON "VariableRevision"("modelRevisionId", "variableName"); + +-- AddForeignKey +ALTER TABLE "Variable" ADD CONSTRAINT "Variable_modelId_fkey" FOREIGN KEY ("modelId") REFERENCES "Model"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "VariableRevision" ADD CONSTRAINT "VariableRevision_modelRevisionId_fkey" FOREIGN KEY ("modelRevisionId") REFERENCES "ModelRevision"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "VariableRevision" ADD CONSTRAINT "VariableRevision_variableId_fkey" FOREIGN KEY ("variableId") REFERENCES "Variable"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/hub/prisma/migrations/20240413012803_variable_current_revision_id/migration.sql b/packages/hub/prisma/migrations/20240413012803_variable_current_revision_id/migration.sql new file mode 100644 index 0000000000..31c84eebe4 --- /dev/null +++ b/packages/hub/prisma/migrations/20240413012803_variable_current_revision_id/migration.sql @@ -0,0 +1,14 @@ +/* + Warnings: + + - A unique constraint covering the columns `[currentRevisionId]` on the table `Variable` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "Variable" ADD COLUMN "currentRevisionId" TEXT; + +-- CreateIndex +CREATE UNIQUE INDEX "Variable_currentRevisionId_key" ON "Variable"("currentRevisionId"); + +-- AddForeignKey +ALTER TABLE "Variable" ADD CONSTRAINT "Variable_currentRevisionId_fkey" FOREIGN KEY ("currentRevisionId") REFERENCES "VariableRevision"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/packages/hub/prisma/schema.prisma b/packages/hub/prisma/schema.prisma index 13bbeb8812..df98921e92 100644 --- a/packages/hub/prisma/schema.prisma +++ b/packages/hub/prisma/schema.prisma @@ -194,6 +194,7 @@ model Model { currentRevisionId String? @unique searchable Searchable? + variables Variable[] @@unique([slug, ownerId]) @@index([createdAt]) @@ -219,7 +220,7 @@ model ModelRevision { contentId String? @unique relativeValuesExports RelativeValuesExport[] - exports ModelExport[] + variableRevisions VariableRevision[] // required by Prisma, but unused since `model` field should point at the same entity currentRevisionModel Model? @relation("CurrentRevision") @@ -252,17 +253,35 @@ model SquiggleSnippet { revision ModelRevision? } -model ModelExport { +model Variable { + id String @id @default(cuid()) + variableName String + model Model @relation(fields: [modelId], references: [id], onDelete: Cascade) + modelId String + revisions VariableRevision[] @relation("Revisions") + + // technically nullable, but won't ever be null in practice + currentRevision VariableRevision? @relation("CurrentRevision", fields: [currentRevisionId], references: [id]) + currentRevisionId String? @unique + + @@unique([modelId, variableName]) +} + +model VariableRevision { id String @id @default(cuid()) modelRevision ModelRevision @relation(fields: [modelRevisionId], references: [id], onDelete: Cascade) modelRevisionId String + variable Variable @relation("Revisions", fields: [variableId], references: [id], onDelete: Cascade) + variableId String variableName String title String? docstring String @default("") variableType String @default("OTHER") - isCurrent Boolean @default(false) - @@unique([modelRevisionId, variableName], name: "uniqueKey") + // required by Prisma, but unused since `model` field should point at the same entity + currentRevisionVariable Variable? @relation("CurrentRevision") + + @@unique([modelRevisionId, variableName]) } // Definitions are polymorphic, but organized differently from Models, without content types. diff --git a/packages/hub/schema.graphql b/packages/hub/schema.graphql index f75ff7249b..add529deeb 100644 --- a/packages/hub/schema.graphql +++ b/packages/hub/schema.graphql @@ -90,12 +90,12 @@ type Group implements Node & Owner { inviteForMe: GroupInvite invites(after: String, before: String, first: Int, last: Int): GroupInviteConnection memberships(after: String, before: String, first: Int, last: Int): UserGroupMembershipConnection! - modelExports(after: String, before: String, first: Int, last: Int): ModelExport! models(after: String, before: String, first: Int, last: Int): ModelConnection! myMembership: UserGroupMembership reusableInviteToken: String slug: String! updatedAtTimestamp: Float! + variableRevisions(after: String, before: String, first: Int, last: Int): VariableRevision! } type GroupConnection { @@ -153,7 +153,6 @@ enum MembershipRole { type Model implements Node { createdAtTimestamp: Float! currentRevision: ModelRevision! - exportRevisions(after: String, before: String, first: Int, last: Int, variableId: String!): ModelExportRevisionsConnection! id: ID! isEditable: Boolean! isPrivate: Boolean! @@ -163,6 +162,7 @@ type Model implements Node { revisions(after: String, before: String, first: Int, last: Int): ModelRevisionConnection! slug: String! updatedAtTimestamp: Float! + variables: [Variable!]! } type ModelConnection { @@ -177,43 +177,6 @@ type ModelEdge { node: Model! } -type ModelExport implements Node { - docstring: String! - id: ID! - isCurrent: Boolean! - modelRevision: ModelRevision! - owner: Owner! - title: String - variableName: String! - variableType: String! -} - -type ModelExportConnection { - edges: [ModelExportEdge!]! - pageInfo: PageInfo! -} - -type ModelExportEdge { - cursor: String! - node: ModelExport! -} - -input ModelExportQueryInput { - modelId: String - owner: String - variableName: String -} - -type ModelExportRevisionsConnection { - edges: [ModelExportRevisionsConnectionEdge!]! - pageInfo: PageInfo! -} - -type ModelExportRevisionsConnectionEdge { - cursor: String! - node: ModelExport! -} - type ModelRevision implements Node { author: User buildStatus: ModelRevisionBuildStatus! @@ -221,12 +184,12 @@ type ModelRevision implements Node { content: ModelContent! createdAtTimestamp: Float! exportNames: [String!]! - exports: [ModelExport!]! forRelativeValues(input: ModelRevisionForRelativeValuesInput!): ModelRevisionForRelativeValuesResult! id: ID! lastBuild: ModelRevisionBuild model: Model! relativeValuesExports: [RelativeValuesExport!]! + variableRevisions: [VariableRevision!]! } type ModelRevisionBuild implements Node { @@ -542,7 +505,6 @@ type Query { groups(after: String, before: String, first: Int, input: GroupsQueryInput, last: Int): GroupConnection! me: Me! model(input: QueryModelInput!): QueryModelResult! - modelExports(after: String, before: String, first: Int, input: ModelExportQueryInput, last: Int): ModelExportConnection! models(after: String, before: String, first: Int, last: Int): ModelConnection! """Admin-only query for listing models in /admin UI""" @@ -555,6 +517,8 @@ type Query { search(after: String, before: String, first: Int, last: Int, text: String!): QuerySearchResult! userByUsername(username: String!): QueryUserByUsernameResult! users(after: String, before: String, first: Int, input: UsersQueryInput, last: Int): QueryUsersConnection! + variable(input: QueryVariableInput!): QueryVariableResult! + variables(after: String, before: String, first: Int, input: VariableQueryInput, last: Int): VariableConnection! } union QueryGroupResult = BaseError | Group | NotFoundError @@ -592,6 +556,14 @@ type QueryUsersConnectionEdge { node: User! } +input QueryVariableInput { + owner: String! + slug: String! + variableName: String! +} + +union QueryVariableResult = BaseError | NotFoundError | Variable + type ReactToGroupInviteResult { invite: GroupInvite! membership: UserGroupMembership @@ -756,11 +728,11 @@ type User implements Node & Owner { groups(after: String, before: String, first: Int, last: Int): GroupConnection! id: ID! isRoot: Boolean! - modelExports(after: String, before: String, first: Int, last: Int): ModelExportConnection! models(after: String, before: String, first: Int, last: Int): ModelConnection! relativeValuesDefinitions(after: String, before: String, first: Int, last: Int): RelativeValuesDefinitionConnection! slug: String! username: String! + variables(after: String, before: String, first: Int, last: Int): VariableConnection! } type UserGroupInvite implements GroupInvite & Node { @@ -803,4 +775,50 @@ type ValidationError implements Error { type ValidationErrorIssue { message: String! path: [String!]! +} + +type Variable implements Node { + currentRevision: VariableRevision + id: ID! + model: Model! + owner: Owner! + revisions(after: String, before: String, first: Int, last: Int): VariableRevisionConnection! + variableName: String! +} + +type VariableConnection { + edges: [VariableEdge!]! + pageInfo: PageInfo! +} + +type VariableEdge { + cursor: String! + node: Variable! +} + +input VariableQueryInput { + modelId: String + owner: String + variableName: String + variableType: String +} + +type VariableRevision implements Node { + docstring: String! + id: ID! + modelRevision: ModelRevision! + title: String + variable: Variable! + variableName: String! + variableType: String! +} + +type VariableRevisionConnection { + edges: [VariableRevisionEdge!]! + pageInfo: PageInfo! +} + +type VariableRevisionEdge { + cursor: String! + node: VariableRevision! } \ No newline at end of file diff --git a/packages/hub/src/app/FrontPage.tsx b/packages/hub/src/app/FrontPage.tsx index cae4a2fdc8..1611d31b39 100644 --- a/packages/hub/src/app/FrontPage.tsx +++ b/packages/hub/src/app/FrontPage.tsx @@ -9,15 +9,15 @@ import { usePageQuery } from "@/relay/usePageQuery"; import { FrontPageDefinitionList } from "./FrontPageDefinitionList"; import { FrontPageGroupList } from "./FrontPageGroupList"; -import { FrontPageModelExportList } from "./FrontPageModelExportList"; import { FrontPageModelList } from "./FrontPageModelList"; +import { FrontPageVariableList } from "./FrontPageVariableList"; import { FrontPageQuery } from "@/__generated__/FrontPageQuery.graphql"; const Query = graphql` query FrontPageQuery { ...FrontPageModelList - ...FrontPageModelExportList + ...FrontPageVariableList ...FrontPageDefinitionList ...FrontPageGroupList } @@ -33,7 +33,7 @@ export const FrontPage: FC<{ - + @@ -43,7 +43,7 @@ export const FrontPage: FC<{ - + diff --git a/packages/hub/src/app/FrontPageModelExportList.tsx b/packages/hub/src/app/FrontPageModelExportList.tsx deleted file mode 100644 index 1a02285e70..0000000000 --- a/packages/hub/src/app/FrontPageModelExportList.tsx +++ /dev/null @@ -1,43 +0,0 @@ -"use client"; - -import { FC } from "react"; -import { graphql, usePaginationFragment } from "react-relay"; - -import { ModelExportList } from "@/modelExports/components/ModelExportList"; - -import { FrontPageModelExportList$key } from "@/__generated__/FrontPageModelExportList.graphql"; -import { FrontPageModelExportListPaginationQuery } from "@/__generated__/FrontPageModelExportListPaginationQuery.graphql"; - -const Fragment = graphql` - fragment FrontPageModelExportList on Query - @argumentDefinitions( - cursor: { type: "String" } - count: { type: "Int", defaultValue: 20 } - ) - @refetchable(queryName: "FrontPageModelExportListPaginationQuery") { - modelExports(first: $count, after: $cursor) - @connection(key: "FrontPageModelExportList_modelExports") { - # necessary for Relay - edges { - __typename - } - ...ModelExportList - } - } -`; - -type Props = { - dataRef: FrontPageModelExportList$key; -}; - -export const FrontPageModelExportList: FC = ({ dataRef }) => { - const { - data: { modelExports }, - loadNext, - } = usePaginationFragment< - FrontPageModelExportListPaginationQuery, - FrontPageModelExportList$key - >(Fragment, dataRef); - - return ; -}; diff --git a/packages/hub/src/app/FrontPageVariableList.tsx b/packages/hub/src/app/FrontPageVariableList.tsx new file mode 100644 index 0000000000..d31749e805 --- /dev/null +++ b/packages/hub/src/app/FrontPageVariableList.tsx @@ -0,0 +1,42 @@ +"use client"; + +import { FC } from "react"; +import { graphql, usePaginationFragment } from "react-relay"; + +import { VariableList } from "@/variables/components/VariableList"; + +import { FrontPageVariableList$key } from "@/__generated__/FrontPageVariableList.graphql"; +import { FrontPageVariableListPaginationQuery } from "@/__generated__/FrontPageVariableListPaginationQuery.graphql"; + +const Fragment = graphql` + fragment FrontPageVariableList on Query + @argumentDefinitions( + cursor: { type: "String" } + count: { type: "Int", defaultValue: 20 } + ) + @refetchable(queryName: "FrontPageVariableListPaginationQuery") { + variables(first: $count, after: $cursor) + @connection(key: "FrontPageVariableList_variables") { + edges { + __typename + } + ...VariableList + } + } +`; + +type Props = { + dataRef: FrontPageVariableList$key; +}; + +export const FrontPageVariableList: FC = ({ dataRef }) => { + const { + data: { variables }, + loadNext, + } = usePaginationFragment< + FrontPageVariableListPaginationQuery, + FrontPageVariableList$key + >(Fragment, dataRef); + + return ; +}; diff --git a/packages/hub/src/app/api/updateModelSeeds/route.ts b/packages/hub/src/app/api/updateModelSeeds/route.ts deleted file mode 100644 index b454ccc1dd..0000000000 --- a/packages/hub/src/app/api/updateModelSeeds/route.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { PrismaClient } from "@prisma/client"; -import { NextRequest, NextResponse } from "next/server"; - -import { generateSeed } from "@quri/squiggle-lang"; - -const prisma = new PrismaClient(); - -async function updateSquiggleSnippetsSeedForModels() { - // Retrieve all models - const models = await prisma.model.findMany({ - include: { - revisions: { - include: { - squiggleSnippet: true, - }, - orderBy: { - createdAt: "desc", - }, - take: 1, - }, - }, - }); - - for (const model of models) { - const lastRevision = model.revisions[0]; - - // Check if the last revision's SquiggleSnippet has the DEFAULT_SEED - if ( - lastRevision.squiggleSnippet && - lastRevision.squiggleSnippet.seed === "DEFAULT_SEED" - ) { - const newSeed = generateSeed(); - - // Update all SquiggleSnippets for all revisions of the current model - for (const revision of model.revisions) { - if (revision.squiggleSnippet) { - await prisma.squiggleSnippet.update({ - where: { - id: revision.squiggleSnippet.id, - }, - data: { - seed: newSeed, - }, - }); - } - } - } - } -} - -export async function POST(req: NextRequest) { - const adminToken = req.headers.get("x-admin-token"); - const secretToken = process.env["ADMIN_SECRET_TOKEN"]; - - if (!secretToken || adminToken !== secretToken) { - return new NextResponse(null, { - status: 401, - statusText: "Unauthorized access.", - }); - } - - try { - await updateSquiggleSnippetsSeedForModels(); - return new NextResponse( - JSON.stringify({ - message: - "Successfully updated seeds for models where the last revision had DEFAULT_SEED.", - }), - { status: 200 } - ); - } catch (error) { - console.error("An error occurred:", error); - return new NextResponse( - JSON.stringify({ - error: "An error occurred while updating seeds.", - }), - { status: 500 } - ); - } -} diff --git a/packages/hub/src/app/models/[owner]/[slug]/EditSquiggleSnippetModel.tsx b/packages/hub/src/app/models/[owner]/[slug]/EditSquiggleSnippetModel.tsx index b6340bf481..73f39fa2f5 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/EditSquiggleSnippetModel.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/EditSquiggleSnippetModel.tsx @@ -28,14 +28,14 @@ import { versionSupportsOnOpenExport, } from "@quri/versioned-squiggle-components"; -import { EditModelExports } from "@/components/exports/EditModelExports"; +import { EditRelativeValueExports } from "@/components/exports/EditRelativeValueExports"; import { ReactRoot } from "@/components/ReactRoot"; import { FormModal } from "@/components/ui/FormModal"; import { SAMPLE_COUNT_DEFAULT, XY_POINT_LENGTH_DEFAULT } from "@/constants"; import { useAvailableHeight } from "@/hooks/useAvailableHeight"; import { useMutationForm } from "@/hooks/useMutationForm"; import { extractFromGraphqlErrorUnion } from "@/lib/graphqlHelpers"; -import { modelExportRoute, modelRoute } from "@/routes"; +import { modelRoute, variableRoute } from "@/routes"; import { ImportTooltip } from "@/squiggle/components/ImportTooltip"; import { parseSourceId, @@ -137,7 +137,7 @@ export const EditSquiggleSnippetModel: FC = ({ id slug isEditable - ...EditModelExports_Model + ...EditRelativeValueExports_Model ...SquiggleSnippetDraftDialog_Model owner { slug @@ -162,13 +162,6 @@ export const EditSquiggleSnippetModel: FC = ({ } } exportNames - exports { - id - variableName - variableType - title - docstring - } relativeValuesExports { id variableName @@ -357,11 +350,11 @@ export const EditSquiggleSnippetModel: FC = ({ ), renderExtraModal: (name) => { - if (name === "exports") { + if (name === "Relative Values") { return { body: (
- { appendVariableWithDefinition(item); onSubmit(); @@ -375,7 +368,7 @@ export const EditSquiggleSnippetModel: FC = ({ />
), - title: "Exported Variables", + title: "Relative Value Exports", }; } }, @@ -396,9 +389,9 @@ export const EditSquiggleSnippetModel: FC = ({ <> Experimental openModal("exports")} + onClick={() => openModal("Relative Values")} /> ) : null; @@ -428,7 +421,7 @@ export const EditSquiggleSnippetModel: FC = ({ const { owner, slug } = parseSourceId(sourceId); if (varName) { router.push( - modelExportRoute({ owner, modelSlug: slug, variableName: varName }) + variableRoute({ owner, modelSlug: slug, variableName: varName }) ); } else { router.push(modelRoute({ owner, slug })); diff --git a/packages/hub/src/app/models/[owner]/[slug]/ModelEntityNodes.tsx b/packages/hub/src/app/models/[owner]/[slug]/ModelEntityNodes.tsx index 83d1415b44..378f23964f 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/ModelEntityNodes.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/ModelEntityNodes.tsx @@ -10,10 +10,10 @@ import { type EntityNode } from "@/components/EntityLayout"; import { ownerIcon } from "@/lib/ownerIcon"; import { isModelRelativeValuesRoute, - modelExportRoute, modelForRelativeValuesExportRoute, modelRoute, ownerRoute, + variableRoute, } from "@/routes"; function hasTypename(owner: { @@ -67,7 +67,7 @@ function entityNodes( if (variable && variable.type === "EXPORT") { nodes.push({ slug: variable.name, - href: modelExportRoute({ + href: variableRoute({ owner: owner.slug, modelSlug: slug, variableName: variable.name, diff --git a/packages/hub/src/app/models/[owner]/[slug]/ModelLayout.tsx b/packages/hub/src/app/models/[owner]/[slug]/ModelLayout.tsx index a3b87f73c7..728f534127 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/ModelLayout.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/ModelLayout.tsx @@ -7,12 +7,12 @@ import { CodeBracketIcon, RectangleStackIcon, ShareIcon } from "@quri/ui"; import { EntityLayout } from "@/components/EntityLayout"; import { EntityTab } from "@/components/ui/EntityTab"; +import { extractFromGraphqlErrorUnion } from "@/lib/graphqlHelpers"; import { - ExportsDropdown, - type ModelExport, totalImportLength, -} from "@/lib/ExportsDropdown"; -import { extractFromGraphqlErrorUnion } from "@/lib/graphqlHelpers"; + type VariableRevision, + VariablesDropdown, +} from "@/lib/VariablesDropdown"; import { SerializablePreloadedQuery } from "@/relay/loadPageQuery"; import { usePageQuery } from "@/relay/usePageQuery"; import { modelRevisionsRoute, modelRoute } from "@/routes"; @@ -46,16 +46,17 @@ const Query = graphql` ...FixModelUrlCasing ...ModelAccessControls ...ModelSettingsButton - currentRevision { + variables { id - # for length; TODO - "hasExports" field? - exportNames - exports { - id - variableName + variableName + currentRevision { variableType title } + } + currentRevision { + id + exportNames relativeValuesExports { id variableName @@ -86,19 +87,19 @@ export const ModelLayout: FC< slug: model.slug, }); - const modelExports: ModelExport[] = model.currentRevision.exportNames.map( - (name) => { - const matchingExport = model.currentRevision.exports.find( + const variableRevisions: VariableRevision[] = + model.currentRevision.exportNames.map((name) => { + const matchingVariable = model.variables.find( (e) => e.variableName === name ); return { variableName: name, - variableType: matchingExport?.variableType || undefined, - title: matchingExport?.title || undefined, + variableType: + matchingVariable?.currentRevision?.variableType || undefined, + title: matchingVariable?.currentRevision?.title || undefined, }; - } - ); + }); const relativeValuesExports = model.currentRevision.relativeValuesExports.map( ({ variableName, definition: { slug } }) => ({ @@ -108,7 +109,7 @@ export const ModelLayout: FC< ); const _totalImportLength = totalImportLength( - modelExports, + variableRevisions, relativeValuesExports ); @@ -121,24 +122,24 @@ export const ModelLayout: FC< {Boolean(_totalImportLength) && ( - { return ( pathname.startsWith(modelUrl + "/relative-values") || - pathname.startsWith(modelUrl + "/exports") + pathname.startsWith(modelUrl + "/variables") ); }} /> - + )} (QueryNode, { - input: { owner: params.owner, slug: params.slug }, - variableName: params.variableName, - }); - - return ( -
- -
- ); -} diff --git a/packages/hub/src/app/models/[owner]/[slug]/relative-values/[variableName]/RelativeValuesModelLayout.tsx b/packages/hub/src/app/models/[owner]/[slug]/relative-values/[variableName]/RelativeValuesModelLayout.tsx index 47dc57de9e..924913a8d7 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/relative-values/[variableName]/RelativeValuesModelLayout.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/relative-values/[variableName]/RelativeValuesModelLayout.tsx @@ -52,7 +52,7 @@ export const RelativeValuesModelLayout: FC< id slug isEditable - ...EditModelExports_Model + ...EditRelativeValueExports_Model owner { slug } diff --git a/packages/hub/src/app/models/[owner]/[slug]/revisions/ModelRevisionsList.tsx b/packages/hub/src/app/models/[owner]/[slug]/revisions/ModelRevisionsList.tsx index f84d26b03e..8a15c45798 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/revisions/ModelRevisionsList.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/revisions/ModelRevisionsList.tsx @@ -32,10 +32,8 @@ const ModelRevisionItem: FC<{ username } comment - exports { + variableRevisions { id - variableName - title } lastBuild { errors @@ -86,9 +84,9 @@ const ModelRevisionItem: FC<{ {revision.lastBuild && (
{`Build Time: ${revision.lastBuild.runSeconds.toFixed(2)}s`}
)} - {revision.exports.length > 0 ? ( + {revision.variableRevisions.length > 0 ? (
- {`${revision.exports.length} exports `} + {`${revision.variableRevisions.length} variables`}
) : null} @@ -131,19 +129,8 @@ export const ModelRevisionsList: FC<{ @connection(key: "ModelRevisionsList_revisions") { edges { node { - ...ModelRevisionsList_revision id - createdAtTimestamp - buildStatus - exports { - id - title - variableName - } - lastBuild { - errors - runSeconds - } + ...ModelRevisionsList_revision } } pageInfo { diff --git a/packages/hub/src/app/models/[owner]/[slug]/exports/[variableName]/SquiggleModelExportPage.tsx b/packages/hub/src/app/models/[owner]/[slug]/variables/[variableName]/SquiggleVariableRevisionPage.tsx similarity index 84% rename from packages/hub/src/app/models/[owner]/[slug]/exports/[variableName]/SquiggleModelExportPage.tsx rename to packages/hub/src/app/models/[owner]/[slug]/variables/[variableName]/SquiggleVariableRevisionPage.tsx index eacbc2542c..673ef22504 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/exports/[variableName]/SquiggleModelExportPage.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/variables/[variableName]/SquiggleVariableRevisionPage.tsx @@ -11,7 +11,7 @@ import { import { squiggleHubLinker } from "@/squiggle/components/linker"; -import { SquiggleModelExportPage$key } from "@/__generated__/SquiggleModelExportPage.graphql"; +import { SquiggleVariableRevisionPage$key } from "@/__generated__/SquiggleVariableRevisionPage.graphql"; type SquiggleProps = { variableName: string; @@ -23,7 +23,7 @@ type GuardedType = T extends (x: any) => x is infer T ? T : never; type SupportedVersion = GuardedType<(typeof versionSupportsExports)["plain"]>; -const VersionedSquiggleModelExportPage: FC< +const VersionedSquiggleVariableRevisionPage: FC< SquiggleProps & { version: SupportedVersion; } @@ -53,13 +53,13 @@ const VersionedSquiggleModelExportPage: FC< ); }; -export const SquiggleModelExportPage: FC<{ +export const SquiggleVariableRevisionPage: FC<{ variableName: string; - contentRef: SquiggleModelExportPage$key; + contentRef: SquiggleVariableRevisionPage$key; }> = ({ variableName, contentRef }) => { const content = useFragment( graphql` - fragment SquiggleModelExportPage on SquiggleSnippet { + fragment SquiggleVariableRevisionPage on SquiggleSnippet { id code version @@ -79,7 +79,7 @@ export const SquiggleModelExportPage: FC<{ } return ( - void; loadNext?: (count: number) => void; -}> = ({ exportRevisions, selected, changeId, loadNext }) => { +}> = ({ revisions, selected, changeId, loadNext }) => { return (

Revisions

    - {exportRevisions.edges.map(({ node: revision }) => ( + {revisions.edges.map(({ node: revision }) => (
  • changeId(revision.id)} @@ -52,52 +53,30 @@ const RevisionsPanel: FC<{ ); }; -export const ModelExportPage: FC<{ - params: { - owner: string; - slug: string; - variableName: string; +export const VariablePageBody: FC<{ + variableName: string; + result: { + readonly __typename: "Variable"; + readonly id: string; + readonly variableName: string; + readonly " $fragmentSpreads": FragmentRefs<"VariablePage">; }; - query: SerializablePreloadedQuery; -}> = ({ query, params }) => { - const [{ model: result }] = usePageQuery( - graphql` - query ModelExportPageQuery( - $input: QueryModelInput! - $variableName: String! - ) { - model(input: $input) { - __typename - ... on Model { - id - slug - ...ModelExportPage @arguments(variableName: $variableName) - } - } - } - `, - query - ); - - const model = extractFromGraphqlErrorUnion(result, "Model"); +}> = ({ result, variableName }) => { + const variable = extractFromGraphqlErrorUnion(result, "Variable"); const { - data: { exportRevisions }, + data: { revisions }, loadNext, - } = usePaginationFragment( + } = usePaginationFragment( graphql` - fragment ModelExportPage on Model + fragment VariablePage on Variable @argumentDefinitions( cursor: { type: "String" } count: { type: "Int", defaultValue: 20 } - variableName: { type: "String!" } ) - @refetchable(queryName: "ModelExportPagePaginationQuery") { - exportRevisions( - first: $count - after: $cursor - variableId: $variableName - ) @connection(key: "ModelExportPage_exportRevisions") { + @refetchable(queryName: "VariablePagePaginationQuery") { + revisions(first: $count, after: $cursor) + @connection(key: "VariablePage_revisions") { edges { node { id @@ -107,7 +86,7 @@ export const ModelExportPage: FC<{ createdAtTimestamp content { __typename - ...SquiggleModelExportPage + ...SquiggleVariableRevisionPage } } } @@ -118,16 +97,19 @@ export const ModelExportPage: FC<{ } } `, - model + variable ); - const [selected, changeId] = useState( - exportRevisions.edges.at(0)?.node.id || "" + const [selected, changeId] = useState( + revisions.edges.at(0)?.node.id ?? null ); - const content = exportRevisions.edges.find( - (edge) => edge.node.id === selected - )?.node.modelRevision.content; + if (selected === null) { + return
    No revisions found. They should be built shortly.
    ; + } + + const content = revisions.edges.find((edge) => edge.node.id === selected) + ?.node.modelRevision.content; if (content) { switch (content.__typename) { @@ -135,19 +117,17 @@ export const ModelExportPage: FC<{ return (
    -
    -
    ); @@ -157,3 +137,36 @@ export const ModelExportPage: FC<{ } } }; + +export const VariablePage: FC<{ + params: { + owner: string; + slug: string; + variableName: string; + }; + query: SerializablePreloadedQuery; +}> = ({ query, params }) => { + const [{ variable: result }] = usePageQuery( + graphql` + query VariablePageQuery($input: QueryVariableInput!) { + variable(input: $input) { + __typename + ... on Variable { + id + variableName + ...VariablePage + } + } + } + `, + query + ); + + if (result.__typename !== "Variable") { + return
    No revisions found. They should be built shortly.
    ; + } + + return ( + + ); +}; diff --git a/packages/hub/src/app/models/[owner]/[slug]/variables/[variableName]/page.tsx b/packages/hub/src/app/models/[owner]/[slug]/variables/[variableName]/page.tsx new file mode 100644 index 0000000000..d984bbcf29 --- /dev/null +++ b/packages/hub/src/app/models/[owner]/[slug]/variables/[variableName]/page.tsx @@ -0,0 +1,27 @@ +import { loadPageQuery } from "@/relay/loadPageQuery"; + +import { VariablePage } from "./VariablePage"; + +import QueryNode, { + VariablePageQuery, +} from "@/__generated__/VariablePageQuery.graphql"; + +type Props = { + params: { owner: string; slug: string; variableName: string }; +}; + +export default async function OuterVariablePage({ params }: Props) { + const query = await loadPageQuery(QueryNode, { + input: { + owner: params.owner, + slug: params.slug, + variableName: params.variableName, + }, + }); + + return ( +
    + +
    + ); +} diff --git a/packages/hub/src/app/users/[username]/UserLayout.tsx b/packages/hub/src/app/users/[username]/UserLayout.tsx index 9f3e0e0664..4558ffdbf1 100644 --- a/packages/hub/src/app/users/[username]/UserLayout.tsx +++ b/packages/hub/src/app/users/[username]/UserLayout.tsx @@ -17,8 +17,8 @@ import { newModelRoute, userDefinitionsRoute, userGroupsRoute, - userModelExportsRoute, userRoute, + userVariablesRoute, } from "@/routes"; import { UserLayoutQuery } from "@/__generated__/UserLayoutQuery.graphql"; @@ -42,7 +42,7 @@ const Query = graphql` __typename } } - modelExports(first: 1) { + variables(first: 1) { edges { __typename } @@ -116,10 +116,10 @@ export const UserLayout: FC< href={userRoute({ username: user.username })} /> ) : null} - {isMe || user.modelExports.edges.length ? ( + {isMe || user.variables.edges.length ? ( ) : null} {isMe || user.relativeValuesDefinitions.edges.length ? ( diff --git a/packages/hub/src/app/users/[username]/exports/UserModelExportList.tsx b/packages/hub/src/app/users/[username]/exports/UserModelExportList.tsx deleted file mode 100644 index 641c52142e..0000000000 --- a/packages/hub/src/app/users/[username]/exports/UserModelExportList.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { FC } from "react"; -import { usePaginationFragment } from "react-relay"; -import { graphql } from "relay-runtime"; - -import { ModelExportList } from "@/modelExports/components/ModelExportList"; - -import { UserModelExportList$key } from "@/__generated__/UserModelExportList.graphql"; - -const Fragment = graphql` - fragment UserModelExportList on User - @argumentDefinitions( - cursor: { type: "String" } - count: { type: "Int", defaultValue: 20 } - ) - @refetchable(queryName: "UserModelExportListPaginationQuery") { - modelExports(first: $count, after: $cursor) - @connection(key: "UserModelExportList_modelExports") { - edges { - __typename - } - ...ModelExportList - } - } -`; - -type Props = { - dataRef: UserModelExportList$key; -}; - -export const UserModelExportList: FC = ({ dataRef }) => { - const { - data: { modelExports }, - loadNext, - } = usePaginationFragment(Fragment, dataRef); - - return ( -
    - {modelExports.edges.length ? ( - - ) : ( -
    No modelExport to show.
    - )} -
    - ); -}; diff --git a/packages/hub/src/app/users/[username]/exports/page.tsx b/packages/hub/src/app/users/[username]/exports/page.tsx deleted file mode 100644 index b79bed155e..0000000000 --- a/packages/hub/src/app/users/[username]/exports/page.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Metadata } from "next"; - -import { loadPageQuery } from "@/relay/loadPageQuery"; - -import { UserModelExportsPage } from "./UserModelExportsPage"; - -import QueryNode, { - UserModelExportsPageQuery, -} from "@/__generated__/UserModelExportsPageQuery.graphql"; - -type Props = { - params: { username: string }; -}; - -export default async function OuterUserModelExportsPage({ params }: Props) { - const query = await loadPageQuery(QueryNode, { - username: params.username, - }); - - return ; -} - -export function generateMetadata({ params }: Props): Metadata { - return { title: params.username }; -} diff --git a/packages/hub/src/app/users/[username]/variables/UserVariableList.tsx b/packages/hub/src/app/users/[username]/variables/UserVariableList.tsx new file mode 100644 index 0000000000..07f622ae1f --- /dev/null +++ b/packages/hub/src/app/users/[username]/variables/UserVariableList.tsx @@ -0,0 +1,45 @@ +import { FC } from "react"; +import { usePaginationFragment } from "react-relay"; +import { graphql } from "relay-runtime"; + +import { VariableList } from "@/variables/components/VariableList"; + +import { UserVariableList$key } from "@/__generated__/UserVariableList.graphql"; + +const Fragment = graphql` + fragment UserVariableList on User + @argumentDefinitions( + cursor: { type: "String" } + count: { type: "Int", defaultValue: 20 } + ) + @refetchable(queryName: "UserVariableListPaginationQuery") { + variables(first: $count, after: $cursor) + @connection(key: "UserVariableList_variables") { + edges { + __typename + } + ...VariableList + } + } +`; + +type Props = { + dataRef: UserVariableList$key; +}; + +export const UserVariableList: FC = ({ dataRef }) => { + const { + data: { variables }, + loadNext, + } = usePaginationFragment(Fragment, dataRef); + + return ( +
    + {variables.edges.length ? ( + + ) : ( +
    No modelExport to show.
    + )} +
    + ); +}; diff --git a/packages/hub/src/app/users/[username]/exports/UserModelExportsPage.tsx b/packages/hub/src/app/users/[username]/variables/UserVariablesPage.tsx similarity index 58% rename from packages/hub/src/app/users/[username]/exports/UserModelExportsPage.tsx rename to packages/hub/src/app/users/[username]/variables/UserVariablesPage.tsx index 90ab30d24b..4a04fb386b 100644 --- a/packages/hub/src/app/users/[username]/exports/UserModelExportsPage.tsx +++ b/packages/hub/src/app/users/[username]/variables/UserVariablesPage.tsx @@ -6,27 +6,27 @@ import { extractFromGraphqlErrorUnion } from "@/lib/graphqlHelpers"; import { SerializablePreloadedQuery } from "@/relay/loadPageQuery"; import { usePageQuery } from "@/relay/usePageQuery"; -import { UserModelExportList } from "./UserModelExportList"; +import { UserVariableList } from "./UserVariableList"; -import { UserModelExportsPageQuery } from "@/__generated__/UserModelExportsPageQuery.graphql"; +import { UserVariablesPageQuery } from "@/__generated__/UserVariablesPageQuery.graphql"; const Query = graphql` - query UserModelExportsPageQuery($username: String!) { + query UserVariablesPageQuery($username: String!) { userByUsername(username: $username) { __typename ... on User { - ...UserModelExportList + ...UserVariableList } } } `; -export const UserModelExportsPage: FC<{ - query: SerializablePreloadedQuery; +export const UserVariablesPage: FC<{ + query: SerializablePreloadedQuery; }> = ({ query }) => { const [{ userByUsername: result }] = usePageQuery(Query, query); const user = extractFromGraphqlErrorUnion(result, "User"); - return ; + return ; }; diff --git a/packages/hub/src/app/users/[username]/variables/page.tsx b/packages/hub/src/app/users/[username]/variables/page.tsx new file mode 100644 index 0000000000..defcd69102 --- /dev/null +++ b/packages/hub/src/app/users/[username]/variables/page.tsx @@ -0,0 +1,25 @@ +import { Metadata } from "next"; + +import { loadPageQuery } from "@/relay/loadPageQuery"; + +import { UserVariablesPage } from "./UserVariablesPage"; + +import QueryNode, { + UserVariablesPageQuery, +} from "@/__generated__/UserVariablesPageQuery.graphql"; + +type Props = { + params: { username: string }; +}; + +export default async function OuterUserVariablesPage({ params }: Props) { + const query = await loadPageQuery(QueryNode, { + username: params.username, + }); + + return ; +} + +export function generateMetadata({ params }: Props): Metadata { + return { title: params.username }; +} diff --git a/packages/hub/src/components/EntityCard.tsx b/packages/hub/src/components/EntityCard.tsx index 939f848920..07e8b17f97 100644 --- a/packages/hub/src/components/EntityCard.tsx +++ b/packages/hub/src/components/EntityCard.tsx @@ -39,7 +39,7 @@ export const EntityCard: FC = ({ }) => { return ( -
    +
    = ({ {slug}
    - {children &&
    {children}
    } -
    + {
    {children}
    } +
    {footerItems} {isPrivate && }
    diff --git a/packages/hub/src/components/exports/EditModelExports.tsx b/packages/hub/src/components/exports/EditRelativeValueExports.tsx similarity index 92% rename from packages/hub/src/components/exports/EditModelExports.tsx rename to packages/hub/src/components/exports/EditRelativeValueExports.tsx index ba948c9e30..a914c18290 100644 --- a/packages/hub/src/components/exports/EditModelExports.tsx +++ b/packages/hub/src/components/exports/EditRelativeValueExports.tsx @@ -20,7 +20,7 @@ import { SelectRelativeValuesDefinitionOption, } from "./SelectRelativeValuesDefinition"; -import { EditModelExports_Model$key } from "@/__generated__/EditModelExports_Model.graphql"; +import { EditRelativeValueExports_Model$key } from "@/__generated__/EditRelativeValueExports_Model.graphql"; import { RelativeValuesExportInput } from "@/__generated__/EditSquiggleSnippetModelMutation.graphql"; const CreateVariableWithDefinitionModal: FC<{ @@ -90,12 +90,12 @@ const CreateVariableWithDefinitionModal: FC<{ const ExportItem: FC<{ item: RelativeValuesExportInput; - modelRef: EditModelExports_Model$key; + modelRef: EditRelativeValueExports_Model$key; remove: () => void; }> = ({ item, modelRef, remove }) => { const model = useFragment( graphql` - fragment EditModelExports_Model on Model { + fragment EditRelativeValueExports_Model on Model { id slug owner { @@ -139,10 +139,10 @@ type Props = { append: (item: RelativeValuesExportInput) => void; remove: (id: number) => void; items: RelativeValuesExportInput[]; - modelRef: EditModelExports_Model$key; + modelRef: EditRelativeValueExports_Model$key; }; -export const EditModelExports: FC = ({ +export const EditRelativeValueExports: FC = ({ append, remove, items, @@ -165,7 +165,7 @@ export const EditModelExports: FC = ({
    {isOpen && ( diff --git a/packages/hub/src/graphql/mutations/adminUpdateModelVersion.ts b/packages/hub/src/graphql/mutations/adminUpdateModelVersion.ts index b7fdbd75d7..5b23054488 100644 --- a/packages/hub/src/graphql/mutations/adminUpdateModelVersion.ts +++ b/packages/hub/src/graphql/mutations/adminUpdateModelVersion.ts @@ -33,7 +33,6 @@ builder.mutationField("adminUpdateModelVersion", (t) => include: { squiggleSnippet: true, relativeValuesExports: true, - exports: true, }, }, }, @@ -75,14 +74,6 @@ builder.mutationField("adminUpdateModelVersion", (t) => ), }, }, - exports: { - createMany: { - data: model.currentRevision.exports.map((exp) => ({ - variableName: exp.variableName, - title: exp.title, - })), - }, - }, }, include: { model: { diff --git a/packages/hub/src/graphql/mutations/updateSquiggleSnippetModel.ts b/packages/hub/src/graphql/mutations/updateSquiggleSnippetModel.ts index 424a9df770..a35d0ec4a8 100644 --- a/packages/hub/src/graphql/mutations/updateSquiggleSnippetModel.ts +++ b/packages/hub/src/graphql/mutations/updateSquiggleSnippetModel.ts @@ -130,17 +130,6 @@ builder.mutationField("updateSquiggleSnippetModel", (t) => const self = await getSelf(session); const model = await prisma.$transaction(async (tx) => { - if (existingModel.currentRevisionId) { - await tx.modelExport.updateMany({ - where: { - modelRevisionId: existingModel.currentRevisionId, - }, - data: { - isCurrent: false, - }, - }); - } - const revision = await tx.modelRevision.create({ data: { squiggleSnippet: { @@ -175,9 +164,9 @@ builder.mutationField("updateSquiggleSnippetModel", (t) => }, }, }); - const model = await tx.model.update({ + const updatedModel = await tx.model.update({ where: { - id: revision.model.id, + id: revision.modelId, }, data: { currentRevisionId: revision.id, @@ -185,7 +174,7 @@ builder.mutationField("updateSquiggleSnippetModel", (t) => // TODO - optimize with queryFromInfo, https://pothos-graphql.dev/docs/plugins/prisma#optimized-queries-without-tprismafield }); - return model; + return updatedModel; }); return { model }; diff --git a/packages/hub/src/graphql/queries/exports.ts b/packages/hub/src/graphql/queries/exports.ts deleted file mode 100644 index e74f0940ac..0000000000 --- a/packages/hub/src/graphql/queries/exports.ts +++ /dev/null @@ -1,61 +0,0 @@ -import merge from "lodash/merge"; - -import { builder } from "@/graphql/builder"; -import { prisma } from "@/prisma"; - -import { modelWhereHasAccess } from "../helpers/modelHelpers"; -import { ModelExport, ModelExportConnection } from "../types/ModelExport"; - -const ModelExportQueryInput = builder.inputType("ModelExportQueryInput", { - fields: (t) => ({ - modelId: t.string(), - variableName: t.string(), - owner: t.string(), - }), -}); - -builder.queryField("modelExports", (t) => - t.prismaConnection( - { - type: ModelExport, - cursor: "id", - args: { - input: t.arg({ type: ModelExportQueryInput }), - }, - resolve: (query, _, { input }, { session }) => { - const modelId = input?.modelId; - - const queries = merge( - {}, - { modelRevision: { model: modelWhereHasAccess(session) } }, - modelId && { - modelRevision: { - modelId: modelId, - }, - }, - input?.owner && { - modelRevision: { model: { owner: { slug: input.owner } } }, - }, - input && - input.variableName && { - variableName: input.variableName, - } - ); - - return prisma.modelExport.findMany({ - ...query, - where: { - ...queries, - isCurrent: true, - }, - orderBy: { - modelRevision: { - createdAt: "desc", - }, - }, - }); - }, - }, - ModelExportConnection - ) -); diff --git a/packages/hub/src/graphql/queries/variable.ts b/packages/hub/src/graphql/queries/variable.ts new file mode 100644 index 0000000000..f1d8e888b8 --- /dev/null +++ b/packages/hub/src/graphql/queries/variable.ts @@ -0,0 +1,44 @@ +import { builder } from "@/graphql/builder"; +import { prisma } from "@/prisma"; + +import { NotFoundError } from "../errors/NotFoundError"; + +builder.queryField("variable", (t) => + t.prismaFieldWithInput({ + type: "Variable", + input: { + variableName: t.input.string({ required: true }), + slug: t.input.string({ required: true }), + owner: t.input.string({ required: true }), + }, + errors: { + types: [NotFoundError], + }, + async resolve(query, _, { input }) { + const model = await prisma.model.findFirst({ + where: { + slug: input.slug, + owner: { slug: input.owner }, + }, + }); + + if (!model) { + throw new NotFoundError(); + } + + const variable = await prisma.variable.findFirst({ + ...query, + where: { + variableName: input.variableName, + modelId: model.id, + }, + }); + + if (!variable) { + throw new NotFoundError(); + } + + return variable; + }, + }) +); diff --git a/packages/hub/src/graphql/queries/variables.ts b/packages/hub/src/graphql/queries/variables.ts new file mode 100644 index 0000000000..7470053ef9 --- /dev/null +++ b/packages/hub/src/graphql/queries/variables.ts @@ -0,0 +1,51 @@ +import { builder } from "@/graphql/builder"; +import { prisma } from "@/prisma"; + +import { modelWhereHasAccess } from "../helpers/modelHelpers"; +import { Variable, VariableConnection } from "../types/Variable"; + +const VariableQueryInput = builder.inputType("VariableQueryInput", { + fields: (t) => ({ + modelId: t.string(), + variableName: t.string(), + owner: t.string(), + variableType: t.string(), + }), +}); + +builder.queryField("variables", (t) => + t.prismaConnection( + { + type: Variable, + cursor: "id", + args: { + input: t.arg({ type: VariableQueryInput }), + }, + resolve: (query, _, { input }, { session }) => { + const modelId = input?.modelId; + + const queries = { + model: { + ...modelWhereHasAccess(session), + ...(input?.owner && { owner: { slug: input.owner } }), + }, + ...(modelId && { modelId: modelId }), + ...(input?.variableName && { variableName: input.variableName }), + ...(input?.variableType && { + currentRevision: { + variableType: input.variableType, + }, + }), + }; + + return prisma.variable.findMany({ + ...query, + where: { + ...queries, + }, + }); + }, + }, + VariableConnection + ) +); diff --git a/packages/hub/src/graphql/schema.ts b/packages/hub/src/graphql/schema.ts index 2ddfe7f5cb..6a9c395ef4 100644 --- a/packages/hub/src/graphql/schema.ts +++ b/packages/hub/src/graphql/schema.ts @@ -6,7 +6,8 @@ import "./queries/group"; import "./queries/groups"; import "./queries/me"; import "./queries/model"; -import "./queries/exports"; +import "./queries/variables"; +import "./queries/variable"; import "./queries/models"; import "./queries/modelsByVersion"; import "./queries/relativeValuesDefinition"; diff --git a/packages/hub/src/graphql/types/Group.ts b/packages/hub/src/graphql/types/Group.ts index c287e6f4cb..774f51bf70 100644 --- a/packages/hub/src/graphql/types/Group.ts +++ b/packages/hub/src/graphql/types/Group.ts @@ -6,8 +6,8 @@ import { getMyMembershipById } from "../helpers/groupHelpers"; import { modelWhereHasAccess } from "../helpers/modelHelpers"; import { GroupInvite, GroupInviteConnection } from "./GroupInvite"; import { ModelConnection, modelConnectionHelpers } from "./Model"; -import { modelExportConnectionHelpers } from "./ModelExport"; import { Owner } from "./Owner"; +import { variableRevisionConnectionHelpers } from "./VariableRevision"; export const MembershipRoleType = builder.enumType(MembershipRole, { name: "MembershipRole", @@ -131,9 +131,9 @@ export const Group = builder.prismaNode("Group", { }, ModelConnection ), - modelExports: t.connection( + variableRevisions: t.connection( { - type: modelExportConnectionHelpers.ref, + type: variableRevisionConnectionHelpers.ref, select: (args, ctx, nestedSelection) => ({ asOwner: { select: { @@ -141,8 +141,8 @@ export const Group = builder.prismaNode("Group", { select: { currentRevision: { select: { - exports: { - ...modelExportConnectionHelpers.getQuery( + variableRevisions: { + ...variableRevisionConnectionHelpers.getQuery( args, ctx, nestedSelection @@ -156,14 +156,18 @@ export const Group = builder.prismaNode("Group", { }, }), resolve: (group, args, ctx) => { - const exports = + const variableRevisions = group.asOwner?.models - .map((model) => model.currentRevision?.exports ?? []) + .map((model) => model.currentRevision?.variableRevisions ?? []) .flat() ?? []; - return modelExportConnectionHelpers.resolve(exports, args, ctx); + return variableRevisionConnectionHelpers.resolve( + variableRevisions, + args, + ctx + ); }, }, - modelExportConnectionHelpers.ref + variableRevisionConnectionHelpers.ref ), }), }); diff --git a/packages/hub/src/graphql/types/Model.ts b/packages/hub/src/graphql/types/Model.ts index 4e506074ab..aab26ed401 100644 --- a/packages/hub/src/graphql/types/Model.ts +++ b/packages/hub/src/graphql/types/Model.ts @@ -3,7 +3,6 @@ import { prismaConnectionHelpers } from "@pothos/plugin-prisma"; import { builder } from "@/graphql/builder"; import { decodeGlobalIdWithTypename } from "../utils"; -import { ModelExport } from "./ModelExport"; import { ModelRevision, ModelRevisionConnection } from "./ModelRevision"; import { Owner } from "./Owner"; @@ -99,19 +98,7 @@ export const Model = builder.prismaNode("Model", { }, ModelRevisionConnection ), - exportRevisions: t.connection({ - type: ModelExport, - args: exportRevisionConnectionHelpers.getArgs(), - select: (args, ctx, nestedSelection) => ({ - revisions: exportRevisionConnectionHelpers.getQuery( - args, - ctx, - nestedSelection - ), - }), - resolve: (model, args, ctx) => - exportRevisionConnectionHelpers.resolve(model.revisions, args, ctx), - }), + variables: t.relation("variables"), lastRevisionWithBuild: t.field({ type: ModelRevision, nullable: true, @@ -149,34 +136,3 @@ export const modelConnectionHelpers = prismaConnectionHelpers( "Model", { cursor: "id" } ); - -const exportRevisionConnectionHelpers = prismaConnectionHelpers( - builder, - "ModelRevision", - { - cursor: "id", - args: (t) => ({ - variableId: t.string({ required: true }), - }), - select: (nodeSelection, args) => ({ - exports: nodeSelection({ - where: { - variableName: args.variableId, - }, - }), - }), - query: (args) => ({ - where: { - exports: { - some: { - variableName: args.variableId, - }, - }, - }, - orderBy: { - createdAt: "desc" as const, - }, - }), - resolveNode: (revision) => revision.exports[0], - } -); diff --git a/packages/hub/src/graphql/types/ModelExport.ts b/packages/hub/src/graphql/types/ModelExport.ts deleted file mode 100644 index 3354c19582..0000000000 --- a/packages/hub/src/graphql/types/ModelExport.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { prismaConnectionHelpers } from "@pothos/plugin-prisma"; - -import { builder } from "@/graphql/builder"; -import { prisma } from "@/prisma"; - -import { Owner } from "./Owner"; - -export const ModelExport = builder.prismaNode("ModelExport", { - id: { field: "id" }, - fields: (t) => ({ - modelRevision: t.relation("modelRevision"), - variableName: t.exposeString("variableName"), - variableType: t.exposeString("variableType"), - docstring: t.exposeString("docstring"), - isCurrent: t.exposeBoolean("isCurrent"), - title: t.exposeString("title", { nullable: true }), - owner: t.field({ - type: Owner, - select: { - modelRevision: { - select: { - model: { - select: { - owner: { - select: { - user: true, - group: true, - }, - }, - }, - }, - }, - }, - }, - resolve: async ({ modelRevision: { model } }) => { - const result = model.owner.user ?? model.owner.group; - if (!result) { - throw new Error("Invalid owner object, missing user or group"); - } - (result as any)["_owner"] = { - type: model.owner.user ? "User" : "Group", - }; - return result; - }, - }), - }), -}); - -export const ModelExportConnection = builder.connectionObject({ - type: ModelExport, - name: "ModelExportConnection", -}); - -export const modelExportConnectionHelpers = prismaConnectionHelpers( - builder, - "ModelExport", - { cursor: "id" } -); diff --git a/packages/hub/src/graphql/types/ModelRevision.ts b/packages/hub/src/graphql/types/ModelRevision.ts index aacbcb4123..793d7779e3 100644 --- a/packages/hub/src/graphql/types/ModelRevision.ts +++ b/packages/hub/src/graphql/types/ModelRevision.ts @@ -31,7 +31,7 @@ const ModelRevisionBuildStatus = builder.enumType("ModelRevisionBuildStatus", { values: ["Skipped", "Pending", "Success", "Failure"], }); -function getExportedVariableNames(ast: ASTNode): string[] { +function astToVariableNames(ast: ASTNode): string[] { const exportedVariableNames: string[] = []; if (ast.type === "Program") { @@ -49,6 +49,15 @@ function getExportedVariableNames(ast: ASTNode): string[] { return exportedVariableNames; } +export function getExportedVariableNames(code: string): string[] { + const ast = parse(code); + if (ast.ok) { + return astToVariableNames(ast.value); + } else { + return []; + } +} + export const ModelRevision = builder.prismaNode("ModelRevision", { id: { field: "id" }, fields: (t) => ({ @@ -58,7 +67,7 @@ export const ModelRevision = builder.prismaNode("ModelRevision", { // `relatedConnection` would be more principled, and in theory the number of variables with definitions could be high. // But connection is harder to deal with on the UI side, and since we send all variables back on updates, so it doesn't make much sense there. relativeValuesExports: t.relation("relativeValuesExports"), - exports: t.relation("exports"), + variableRevisions: t.relation("variableRevisions"), model: t.relation("model"), lastBuild: t.field({ @@ -123,12 +132,7 @@ export const ModelRevision = builder.prismaNode("ModelRevision", { select: { squiggleSnippet: true }, async resolve(revision) { if (revision.contentType === "SquiggleSnippet") { - const ast = parse(revision.squiggleSnippet!.code); - if (ast.ok) { - return getExportedVariableNames(ast.value); - } else { - return []; - } + return getExportedVariableNames(revision.squiggleSnippet!.code); } else { return []; } diff --git a/packages/hub/src/graphql/types/User.ts b/packages/hub/src/graphql/types/User.ts index 0c3e5c90b0..b26300a192 100644 --- a/packages/hub/src/graphql/types/User.ts +++ b/packages/hub/src/graphql/types/User.ts @@ -3,15 +3,12 @@ import { modelWhereHasAccess } from "../helpers/modelHelpers"; import { isRootUser } from "../helpers/userHelpers"; import { GroupConnection, groupFromMembershipConnectionHelpers } from "./Group"; import { ModelConnection, modelConnectionHelpers } from "./Model"; -import { - ModelExportConnection, - modelExportConnectionHelpers, -} from "./ModelExport"; import { Owner } from "./Owner"; import { RelativeValuesDefinitionConnection, relativeValuesDefinitionConnectionHelpers, } from "./RelativeValuesDefinition"; +import { VariableConnection, variableConnectionHelpers } from "./Variable"; export const User = builder.prismaNode("User", { id: { field: "id" }, @@ -57,37 +54,32 @@ export const User = builder.prismaNode("User", { }, ModelConnection ), - modelExports: t.connection( + variables: t.connection( { - type: modelExportConnectionHelpers.ref, + type: variableConnectionHelpers.ref, select: (args, ctx, nestedSelection) => ({ asOwner: { select: { models: { select: { - currentRevision: { - select: { - exports: modelExportConnectionHelpers.getQuery( - args, - ctx, - nestedSelection - ), - }, - }, + variables: variableConnectionHelpers.getQuery( + args, + ctx, + nestedSelection + ), }, }, }, }, }), resolve: (user, args, ctx) => { - const exports = - user.asOwner?.models - .map((model) => model.currentRevision?.exports ?? []) - .flat() ?? []; - return modelExportConnectionHelpers.resolve(exports, args, ctx); + const variables = + user.asOwner?.models.map((model) => model.variables ?? []).flat() ?? + []; + return variableConnectionHelpers.resolve(variables, args, ctx); }, }, - ModelExportConnection + VariableConnection ), relativeValuesDefinitions: t.connection( { diff --git a/packages/hub/src/graphql/types/Variable.ts b/packages/hub/src/graphql/types/Variable.ts new file mode 100644 index 0000000000..0660d46231 --- /dev/null +++ b/packages/hub/src/graphql/types/Variable.ts @@ -0,0 +1,68 @@ +import { prismaConnectionHelpers } from "@pothos/plugin-prisma"; + +import { builder } from "@/graphql/builder"; + +import { Owner } from "./Owner"; +import { VariableRevisionConnection } from "./VariableRevision"; + +export const Variable = builder.prismaNode("Variable", { + id: { field: "id" }, + fields: (t) => ({ + variableName: t.exposeString("variableName"), + model: t.relation("model"), + + owner: t.field({ + type: Owner, + select: { + model: { + select: { + owner: { + select: { + user: true, + group: true, + }, + }, + }, + }, + }, + resolve: async ({ model }) => { + const result = model.owner.user ?? model.owner.group; + if (!result) { + throw new Error("Invalid owner object, missing user or group"); + } + (result as any)["_owner"] = { + type: model.owner.user ? "User" : "Group", + }; + return result; + }, + }), + revisions: t.relatedConnection( + "revisions", + { + cursor: "id", + query: () => ({ + orderBy: { + modelRevision: { + createdAt: "desc", + }, + }, + }), + }, + VariableRevisionConnection + ), + currentRevision: t.relation("currentRevision", { + nullable: true, + }), + }), +}); + +export const VariableConnection = builder.connectionObject({ + type: Variable, + name: "VariableConnection", +}); + +export const variableConnectionHelpers = prismaConnectionHelpers( + builder, + "Variable", + { cursor: "id" } +); diff --git a/packages/hub/src/graphql/types/VariableRevision.ts b/packages/hub/src/graphql/types/VariableRevision.ts new file mode 100644 index 0000000000..397575fa16 --- /dev/null +++ b/packages/hub/src/graphql/types/VariableRevision.ts @@ -0,0 +1,78 @@ +import { prismaConnectionHelpers } from "@pothos/plugin-prisma"; + +import { builder } from "@/graphql/builder"; +import { prisma } from "@/prisma"; + +export const VariableRevision = builder.prismaNode("VariableRevision", { + id: { field: "id" }, + fields: (t) => ({ + modelRevision: t.relation("modelRevision"), + variableName: t.exposeString("variableName"), + variableType: t.exposeString("variableType"), + docstring: t.exposeString("docstring"), + title: t.exposeString("title", { nullable: true }), + variable: t.relation("variable"), + }), +}); + +export const variableRevisionConnectionHelpers = prismaConnectionHelpers( + builder, + "VariableRevision", + { cursor: "id" } +); + +export const VariableRevisionConnection = builder.connectionObject({ + type: VariableRevision, + name: "VariableRevisionConnection", +}); + +export type VariableRevisionInput = { + title?: string; + variableName: string; + variableType?: string; + docstring?: string; +}; + +export async function createVariableRevision( + modelId: string, + revisionId: string, + variableData: VariableRevisionInput +) { + const variable = await prisma.variable.findFirst({ + where: { + modelId: modelId, + variableName: variableData.variableName, + }, + }); + + let variableId: string; + if (!variable) { + const createdVariable = await prisma.variable.create({ + data: { + model: { connect: { id: modelId } }, + variableName: variableData.variableName, + }, + }); + variableId = createdVariable.id; + } else { + variableId = variable.id; + } + + const createdVariableRevision = await prisma.variableRevision.create({ + data: { + variableName: variableData.variableName, + variable: { connect: { id: variableId } }, + modelRevision: { connect: { id: revisionId } }, + variableType: variableData.variableType, + title: variableData.title, + docstring: variableData.docstring, + }, + }); + + await prisma.variable.update({ + where: { id: variableId }, + data: { + currentRevisionId: createdVariableRevision.id, + }, + }); +} diff --git a/packages/hub/src/lib/ExportsDropdown.tsx b/packages/hub/src/lib/VariablesDropdown.tsx similarity index 73% rename from packages/hub/src/lib/ExportsDropdown.tsx rename to packages/hub/src/lib/VariablesDropdown.tsx index d680b91132..146191c947 100644 --- a/packages/hub/src/lib/ExportsDropdown.tsx +++ b/packages/hub/src/lib/VariablesDropdown.tsx @@ -8,11 +8,11 @@ import { } from "@quri/ui"; import { DropdownMenuNextLinkItem } from "@/components/ui/DropdownMenuNextLinkItem"; -import { modelExportRoute, modelForRelativeValuesExportRoute } from "@/routes"; +import { modelForRelativeValuesExportRoute, variableRoute } from "@/routes"; import { exportTypeIcon } from "./typeIcon"; -export type ModelExport = { +export type VariableRevision = { title?: string; variableName: string; variableType?: string; @@ -21,11 +21,11 @@ export type ModelExport = { type RelativeValuesExport = { slug: string; variableName: string }; -const nonRelativeValuesExports = ( - modelExports: ModelExport[], +const nonRelativeValuesVariables = ( + variables: VariableRevision[], relativeValuesExports: RelativeValuesExport[] ) => - modelExports.filter( + variables.filter( (exportItem) => !relativeValuesExports.find( ({ variableName: v }) => v === exportItem.variableName @@ -34,33 +34,36 @@ const nonRelativeValuesExports = ( //It's a bit awkward that this here, but it's fairly closely coupled to ExportsDropdown. export const totalImportLength = ( - modelExports: ModelExport[], + variables: VariableRevision[], relativeValuesExports: RelativeValuesExport[] ) => - nonRelativeValuesExports(modelExports, relativeValuesExports).length + + nonRelativeValuesVariables(variables, relativeValuesExports).length + relativeValuesExports.length; -export const ExportsDropdown: FC< +export const VariablesDropdown: FC< PropsWithChildren<{ - modelExports: ModelExport[]; + variableRevisions: VariableRevision[]; relativeValuesExports: RelativeValuesExport[]; owner: string; slug: string; }> -> = ({ modelExports, relativeValuesExports, owner, slug, children }) => { - //We remove the relative values exports from the exports list, to not double count them. - const exports = nonRelativeValuesExports(modelExports, relativeValuesExports); +> = ({ variableRevisions, relativeValuesExports, owner, slug, children }) => { + //We remove the relative values variables from the exports list, to not double count them. + const allExports = nonRelativeValuesVariables( + variableRevisions, + relativeValuesExports + ); return ( ( - {exports.length > 0 && ( + {allExports.length > 0 && ( <> - Exports - {exports.map((exportItem) => ( + Exported Variables + {allExports.map((exportItem) => ( = ({ modelRef, showOwner = true }) => { slug: model.slug, }); - const modelExports = model.currentRevision.exports.map( - ({ variableName, variableType, title }) => ({ - variableName, - variableType, - title: title || undefined, - }) - ); + const variableRevisions: VariableRevision[] = model.variables.map((v) => ({ + variableName: v.variableName, + variableType: v.currentRevision?.variableType, + title: v.currentRevision?.title || undefined, + docString: undefined, + })); const relativeValuesExports = model.currentRevision.relativeValuesExports.map( ({ variableName, definition: { slug } }) => ({ @@ -62,22 +67,22 @@ export const ModelCard: FC = ({ modelRef, showOwner = true }) => { ); const _totalImportLength = totalImportLength( - modelExports, + variableRevisions, relativeValuesExports ); const footerItems = _totalImportLength > 0 ? ( -
    - {`${_totalImportLength} exports`} + {`${_totalImportLength} variables`}
    -
    + ) : undefined; return ( diff --git a/packages/hub/src/routes.ts b/packages/hub/src/routes.ts index 493690af21..28344fe264 100644 --- a/packages/hub/src/routes.ts +++ b/packages/hub/src/routes.ts @@ -68,7 +68,7 @@ export function isModelRelativeValuesRoute(url: string) { return url.match("^/models/[^/]+/[^/]+/relative-values/[^/]+$"); } -export function modelExportRoute({ +export function variableRoute({ owner, modelSlug, variableName, @@ -78,7 +78,7 @@ export function modelExportRoute({ variableName: string; }) { const modelUrl = modelRoute({ owner, slug: modelSlug }); - return `${modelUrl}/exports/${variableName}`; + return `${modelUrl}/variables/${variableName}`; } export function modelViewRoute({ @@ -136,8 +136,8 @@ export function userGroupsRoute({ username }: { username: string }) { return `/users/${username}/groups`; } -export function userModelExportsRoute({ username }: { username: string }) { - return `/users/${username}/exports`; +export function userVariablesRoute({ username }: { username: string }) { + return `/users/${username}/variables`; } export function groupRoute({ slug }: { slug: string }) { diff --git a/packages/hub/src/scripts/buildRecentModelRevision/main.ts b/packages/hub/src/scripts/buildRecentModelRevision/main.ts index 17a7e5da52..bfcc6e6771 100644 --- a/packages/hub/src/scripts/buildRecentModelRevision/main.ts +++ b/packages/hub/src/scripts/buildRecentModelRevision/main.ts @@ -1,6 +1,8 @@ import { PrismaClient } from "@prisma/client"; import { spawn } from "node:child_process"; +import { createVariableRevision } from "@/graphql/types/VariableRevision"; + import { NotFoundError } from "../../graphql/errors/NotFoundError"; import { WorkerOutput, WorkerRunMessage } from "./worker"; @@ -22,7 +24,10 @@ async function runWorker( const timeoutId = setTimeout(() => { worker.kill(); - resolve({ errors: `Timeout Error, at ${timeoutSeconds}s`, exports: [] }); + resolve({ + errors: `Timeout Error, at ${timeoutSeconds}s`, + variableRevisions: [], + }); }, timeoutSeconds * 1000); worker.stdout?.on("data", (data) => { @@ -48,7 +53,7 @@ async function runWorker( console.error(`Worker process exited with error code ${code}`); resolve({ errors: "Computation error, with code: " + code, - exports: [], + variableRevisions: [], }); } }); @@ -119,6 +124,8 @@ async function buildRecentModelVersion(): Promise { // For some reason, Typescript becomes unsure if `model.currentRevisionId` is null or not, even though it's checked above. const revisionId = model.currentRevisionId!; + const modelId = model.id; + await tx.modelRevisionBuild.create({ data: { modelRevision: { connect: { id: revisionId } }, @@ -127,31 +134,12 @@ async function buildRecentModelVersion(): Promise { }, }); - for (const e of response.exports) { - await tx.modelExport.upsert({ - where: { - uniqueKey: { - modelRevisionId: revisionId, - variableName: e.variableName, - }, - }, - update: { - variableType: e.variableType, - title: e.title, - docstring: e.docstring, - }, - create: { - modelRevision: { connect: { id: revisionId } }, - variableName: e.variableName, - variableType: e.variableType, - title: e.title, - docstring: e.docstring, - }, - }); + for (const e of response.variableRevisions) { + createVariableRevision(modelId, revisionId, e); } }); console.log( - `Build created for model revision ID: ${model.currentRevisionId}, in ${endTime - startTime}ms. Created ${response.exports.length} exports.` + `Build created for model revision ID: ${model.currentRevisionId}, in ${endTime - startTime}ms. Created ${response.variableRevisions.length} variableRevisions.` ); } catch (error) { console.error("Error building model revision:", error); diff --git a/packages/hub/src/scripts/buildRecentModelRevision/worker.ts b/packages/hub/src/scripts/buildRecentModelRevision/worker.ts index 30347ed95f..ce4b9825aa 100644 --- a/packages/hub/src/scripts/buildRecentModelRevision/worker.ts +++ b/packages/hub/src/scripts/buildRecentModelRevision/worker.ts @@ -1,5 +1,5 @@ import { runSquiggle } from "@/graphql/queries/runSquiggle"; -import { ModelExport } from "@/lib/ExportsDropdown"; +import { VariableRevisionInput } from "@/graphql/types/VariableRevision"; import { prisma } from "@/prisma"; export type WorkerRunMessage = { @@ -12,7 +12,7 @@ export type WorkerRunMessage = { export type WorkerOutput = { errors: string; - exports: ModelExport[]; + variableRevisions: VariableRevisionInput[]; }; export async function runSquiggleCode( @@ -21,11 +21,11 @@ export async function runSquiggleCode( ): Promise { const outputR = await runSquiggle(code, seed); - let exports: ModelExport[] = []; + let variableRevisions: VariableRevisionInput[] = []; if (outputR.ok) { // I Imagine it would be nice to move this out of this worker file, but this would require exporting a lot more information. It seems wise to instead wait for the Serialization PR to go in and then refactor this. - exports = outputR.value.exports.entries().map((e) => ({ + variableRevisions = outputR.value.exports.entries().map((e) => ({ variableName: e[0], variableType: e[1].tag, title: e[1].tags.name() ? e[1].tags.name() : e[1].title() || "", @@ -35,7 +35,7 @@ export async function runSquiggleCode( return { errors: outputR.ok ? "" : outputR.value.toString(), - exports, + variableRevisions, }; } diff --git a/packages/hub/src/modelExports/components/ModelExportCard.tsx b/packages/hub/src/variables/components/VariableCard.tsx similarity index 52% rename from packages/hub/src/modelExports/components/ModelExportCard.tsx rename to packages/hub/src/variables/components/VariableCard.tsx index 1fa11e177f..9b02ca53e5 100644 --- a/packages/hub/src/modelExports/components/ModelExportCard.tsx +++ b/packages/hub/src/variables/components/VariableCard.tsx @@ -7,42 +7,52 @@ import remarkGfm from "remark-gfm"; import { EntityCard } from "@/components/EntityCard"; import { exportTypeIcon } from "@/lib/typeIcon"; -import { modelExportRoute, modelRoute } from "@/routes"; +import { modelRoute, variableRoute } from "@/routes"; -import { ModelExportCard$key } from "@/__generated__/ModelExportCard.graphql"; +import { VariableCard$key } from "@/__generated__/VariableCard.graphql"; const Fragment = graphql` - fragment ModelExportCard on ModelExport { + fragment VariableCard on Variable { id variableName - title - docstring - variableType + currentRevision { + id + title + docstring + variableType + modelRevision { + createdAtTimestamp + } + } owner { slug } - modelRevision { - createdAtTimestamp - model { - slug - } + model { + slug + isPrivate } } `; type Props = { - modelExportRef: ModelExportCard$key; + variableRef: VariableCard$key; }; -export const ModelExportCard: FC = ({ modelExportRef }) => { - const modelExport = useFragment(Fragment, modelExportRef); +export const VariableCard: FC = ({ variableRef }) => { + const variable = useFragment(Fragment, variableRef); + + const currentRevision = variable.currentRevision; + + if (!currentRevision) { + return null; + } - const Icon = exportTypeIcon(modelExport.variableType); + const Icon = exportTypeIcon(currentRevision.variableType || ""); // This will have problems with markdown tags, but I looked into markdown-truncation packages, and they can get complicated. Will try this for now. const docstring = - (modelExport.docstring && - truncate(modelExport.docstring, { + (currentRevision.docstring && + truncate(currentRevision.docstring, { length: 500, separator: " ", omission: "...", @@ -51,28 +61,29 @@ export const ModelExportCard: FC = ({ modelExportRef }) => { return ( - {`${modelExport.owner.slug}/${modelExport.modelRevision.model.slug}`} + {`${variable.owner.slug}/${variable.model.slug}`}
    - {modelExport.variableType} + {currentRevision.variableType}
    } diff --git a/packages/hub/src/modelExports/components/ModelExportList.tsx b/packages/hub/src/variables/components/VariableList.tsx similarity index 60% rename from packages/hub/src/modelExports/components/ModelExportList.tsx rename to packages/hub/src/variables/components/VariableList.tsx index 67bec8c867..9354ae283c 100644 --- a/packages/hub/src/modelExports/components/ModelExportList.tsx +++ b/packages/hub/src/variables/components/VariableList.tsx @@ -4,16 +4,16 @@ import { graphql, useFragment } from "react-relay"; import { LoadMore } from "@/components/LoadMore"; -import { ModelExportCard } from "./ModelExportCard"; +import { VariableCard } from "./VariableCard"; -import { ModelExportList$key } from "@/__generated__/ModelExportList.graphql"; +import { VariableList$key } from "@/__generated__/VariableList.graphql"; const Fragment = graphql` - fragment ModelExportList on ModelExportConnection { + fragment VariableList on VariableConnection { edges { node { id - ...ModelExportCard + ...VariableCard } } pageInfo { @@ -23,18 +23,18 @@ const Fragment = graphql` `; type Props = { - connectionRef: ModelExportList$key; + connectionRef: VariableList$key; loadNext(count: number): unknown; }; -export const ModelExportList: FC = ({ connectionRef, loadNext }) => { +export const VariableList: FC = ({ connectionRef, loadNext }) => { const connection = useFragment(Fragment, connectionRef); return (
    {connection.edges.map((edge) => ( - + ))}
    {connection.pageInfo.hasNextPage && } diff --git a/packages/hub/test/gql-gen/graphql.ts b/packages/hub/test/gql-gen/graphql.ts index 95b9ad36d8..135050c3b3 100644 --- a/packages/hub/test/gql-gen/graphql.ts +++ b/packages/hub/test/gql-gen/graphql.ts @@ -248,8 +248,8 @@ export type ModelEdge = { node: Model; }; -export type ModelExport = Node & { - __typename?: 'ModelExport'; +export type Variable = Node & { + __typename?: 'Variable'; docstring: Scalars['String']['output']; id: Scalars['ID']['output']; modelRevision: ModelRevision; @@ -264,7 +264,7 @@ export type ModelRevision = Node & { comment: Scalars['String']['output']; content: ModelContent; createdAtTimestamp: Scalars['Float']['output']; - exports: Array; + variables: Array; forRelativeValues?: Maybe; id: Scalars['ID']['output']; model: Model; @@ -626,7 +626,7 @@ export type MutationUpdateRelativeValuesDefinitionResult = BaseError | UpdateRel export type MutationUpdateSquiggleSnippetModelInput = { comment?: InputMaybe; content: SquiggleSnippetContentInput; - exports?: InputMaybe>; + variables?: InputMaybe>; owner: Scalars['String']['input']; relativeValuesExports?: InputMaybe>; slug: Scalars['String']['input']; @@ -800,7 +800,7 @@ export type RelativeValuesDefinition = Node & { currentRevision: RelativeValuesDefinitionRevision; id: Scalars['ID']['output']; isEditable: Scalars['Boolean']['output']; - modelExports: Array; + Variables: Array; owner: Owner; slug: Scalars['String']['output']; updatedAtTimestamp: Scalars['Float']['output']; @@ -876,7 +876,7 @@ export type SquiggleErrorOutput = SquiggleOutput & { isCached: Scalars['Boolean']['output']; }; -export type SquiggleModelExportInput = { +export type SquiggleVariableInput = { docstring?: InputMaybe; title?: InputMaybe; variableName: Scalars['String']['input'];