Skip to content

Commit

Permalink
add publish endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
harishv7 committed Jul 31, 2024
1 parent 26d6de3 commit ff7ac7d
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Image from "next/image"
import NextLink from "next/link"
import { useParams } from "next/navigation"
import { Flex, HStack } from "@chakra-ui/react"
import {
AvatarMenu,
Expand All @@ -13,10 +14,13 @@ import { BiLinkExternal } from "react-icons/bi"
import { ADMIN_NAVBAR_HEIGHT } from "~/constants/layouts"
import { useMe } from "~/features/me/api"
import { DASHBOARD, SETTINGS_PROFILE } from "~/lib/routes"
import PublishButton from "./PublishButton"

export function AppNavbar(): JSX.Element {
const { me, logout } = useMe()

const pathParams = useParams()

return (
<Flex flex="0 0 auto" gridColumn="1/-1" height={ADMIN_NAVBAR_HEIGHT}>
<Flex
Expand Down Expand Up @@ -62,6 +66,10 @@ export function AppNavbar(): JSX.Element {
>
Report an issue
</Button>
<PublishButton
pageId={pathParams.pageId as string}
siteId={pathParams.siteId as string}
/>
<AvatarMenu
name={me.name}
variant="subtle"
Expand Down
53 changes: 53 additions & 0 deletions apps/studio/src/components/AppNavbar/PublishButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useToast } from "@chakra-ui/react"
import { Button } from "@opengovsg/design-system-react"

import { trpc } from "~/utils/trpc"

interface PublishButtonProps {
pageId?: string
siteId?: string
}

const PublishButton = ({ pageId, siteId }: PublishButtonProps): JSX.Element => {
const toast = useToast()
const { mutate, isLoading } = trpc.page.publishPage.useMutation({
onSuccess: (data) => {
if (data.versionId) {
toast({
status: "success",
title: "Page published successfully",
})
} else {
toast({
status: "error",
title: data.error,
})
}
},
onError: () => {
toast({
status: "error",
title: "Failed to publish page",
})
},
})

const handlePublish = () => {
const coercedSiteId = Number(siteId)
const coercedPageId = Number(pageId)
if (coercedSiteId && coercedPageId)
mutate({ pageId: coercedPageId, siteId: coercedSiteId })
}
return (
<Button
variant="solid"
size="sm"
onClick={handlePublish}
isLoading={isLoading}
>
Publish
</Button>
)
}

export default PublishButton
1 change: 1 addition & 0 deletions apps/studio/src/components/AppNavbar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./AppNavbar"
6 changes: 6 additions & 0 deletions apps/studio/src/schemas/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,9 @@ export const createPageSchema = z.object({
// NOTE: implies that top level pages are allowed
folderId: z.number().min(1).optional(),
})

// TODO: siteId should be taken from user's context (not input)
export const publishPageSchema = z.object({
pageId: z.number().min(1),
siteId: z.number().min(1),
})
13 changes: 12 additions & 1 deletion apps/studio/src/server/modules/page/page.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import z from "zod"
import {
createPageSchema,
getEditPageSchema,
publishPageSchema,
reorderBlobSchema,
updatePageBlobSchema,
updatePageSchema,
Expand All @@ -23,7 +24,7 @@ import {
updatePageById,
} from "../resource/resource.service"
import { getSiteConfig } from "../site/site.service"
import { createDefaultPage } from "./page.service"
import { addNewVersion, createDefaultPage } from "./page.service"

const ajv = new Ajv({ allErrors: true, strict: false, logger: false })
const schemaValidator = ajv.compile<IsomerSchema>(schema)
Expand Down Expand Up @@ -233,4 +234,14 @@ export const pageRouter = router({
return { pageId: resource.id }
},
),
publishPage: protectedProcedure
.input(publishPageSchema)
.mutation(async ({ input: { siteId, pageId } }) => {
/* Step 1: Update DB table to latest state */
// Create a new version
const addedVersionResult = await addNewVersion(siteId, pageId)
return addedVersionResult

/* TODO: Step 2: Use AWS SDK to start a CodeBuild */
}),
})
50 changes: 50 additions & 0 deletions apps/studio/src/server/modules/page/page.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { format } from "date-fns"

import { db } from "../database"
import { getPageById, updatePageById } from "../resource/resource.service"
import { createVersion, getVersionById } from "../version/version.service"

export const createDefaultPage = ({
title,
layout,
Expand Down Expand Up @@ -42,3 +46,49 @@ export const createDefaultPage = ({
}
}
}

export const addNewVersion = async (siteId: number, pageId: number) => {
return await db.transaction().execute(async (tx) => {
const page = await getPageById({ siteId, resourceId: pageId })

if (!page.draftBlobId) {
return { error: "No drafts to publish for this page" }
}

let newVersionNum = 1
if (page.versionId) {
const currentVersion = await getVersionById({
versionId: page.versionId,
})
newVersionNum = Number(currentVersion.versionNum) + 1
}

// Create the new version
// TODO: To pass in the tx object
const newVersion = await createVersion(
{
versionNum: newVersionNum,
resourceId: pageId,
blobId: Number(page.draftBlobId),
},
tx,
)

// Update resource with new versionId and draft to be null
await updatePageById({
props: {
page: {
...page,
id: page.id,
versionId: newVersion.versionId,
draftBlobId: null,
state: "Published",
},
siteId,
},
tx,
})

return { versionId: newVersion }
})
}
5 changes: 2 additions & 3 deletions apps/studio/src/server/modules/resource/resource.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,11 @@ export const getFullPageById = async (
export const getPageById = (
db: SafeKysely,
args: { resourceId: number; siteId: number },
) => {
return getById(db, args)
) =>
getById(db, args)
.where("type", "is", "Page")
.select(defaultResourceSelect)
.executeTakeFirstOrThrow()
}

export const updatePageById = (
page: Partial<Omit<Page, "id" | "siteId" | "parentId">> & {
Expand Down
43 changes: 43 additions & 0 deletions apps/studio/src/server/modules/version/version.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { SelectExpression, Transaction } from "kysely"
import { type DB } from "~prisma/generated/generatedTypes"

import { db } from "../database"

const defaultVersionSelect: SelectExpression<DB, "Version">[] = [
"Version.id",
"Version.versionNum",
"Version.resourceId",
"Version.blobId",
"Version.publishedAt",
]

export const getVersionById = ({ versionId }: { versionId: string }) =>
db
.selectFrom("Version")
.where("Version.id", "=", versionId)
.select(defaultVersionSelect)
.executeTakeFirstOrThrow()

export const createVersion = async (
props: {
versionNum: number
resourceId: number
blobId: number
},
tx?: Transaction<DB>,
) => {
const { versionNum, resourceId, blobId } = props
const instance = tx ? tx : db
const addedVersion = await instance
.insertInto("Version")
.values({
versionNum: String(versionNum),
resourceId: String(resourceId),
blobId: String(blobId),
publishedAt: new Date(),
})
.returning("Version.id")
.executeTakeFirstOrThrow()

return { versionId: addedVersion.id }
}

0 comments on commit ff7ac7d

Please sign in to comment.