Skip to content

Commit

Permalink
Merge pull request #4494 from owid/slug-collisions
Browse files Browse the repository at this point in the history
✨ check there's no slug collision before publishing
  • Loading branch information
ikesau authored Jan 28, 2025
2 parents 69987ee + 250bd65 commit 467ac6f
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 4 deletions.
3 changes: 2 additions & 1 deletion adminSiteClient/admin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,8 @@ $nav-height: 45px;
}

.errorMessage {
z-index: 2000;
// 1 above .ant-modal-mask
z-index: 2001;
.modal-dialog {
max-width: 80%;
}
Expand Down
20 changes: 18 additions & 2 deletions adminSiteServer/apiRoutes/gdocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
updateGdocContentOnly,
createOrLoadGdocById,
gdocFromJSON,
addImagesToContentGraph,
setImagesInContentGraph,
setLinksForGdoc,
GdocLinkUpdateMode,
upsertGdoc,
Expand Down Expand Up @@ -145,6 +145,20 @@ async function indexAndBakeGdocIfNeccesary(
.exhaustive()
}

async function validateSlugCollisionsIfPublishing(
trx: db.KnexReadonlyTransaction,
gdoc: GdocPost | GdocDataInsight | GdocHomepage | GdocAbout | GdocAuthor
) {
if (!gdoc.published) return

const hasSlugCollision = await db.checkIfSlugCollides(trx, gdoc)
if (hasSlugCollision) {
throw new JsonError(
`You are attempting to publish a Google Doc with a slug that already exists: "${gdoc.slug}"`
)
}
}

/**
* Only supports creating a new empty Gdoc or updating an existing one. Does not
* support creating a new Gdoc from an existing one. Relevant updates will
Expand All @@ -167,7 +181,9 @@ export async function createOrUpdateGdoc(
const nextGdoc = gdocFromJSON(req.body)
await nextGdoc.loadState(trx)

await addImagesToContentGraph(trx, nextGdoc)
await validateSlugCollisionsIfPublishing(trx, nextGdoc)

await setImagesInContentGraph(trx, nextGdoc)

await setLinksForGdoc(
trx,
Expand Down
26 changes: 26 additions & 0 deletions db/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ import {
DbEnrichedImageWithUserId,
MinimalTag,
BreadcrumbItem,
PostsGdocsTableName,
OwidGdocBaseInterface,
} from "@ourworldindata/types"
import { groupBy } from "lodash"
import { gdocFromJSON } from "./model/Gdoc/GdocFactory.js"
import { getCanonicalUrl } from "@ourworldindata/components"

// Return the first match from a mysql query
export const closeTypeOrmAndKnexConnections = async (): Promise<void> => {
Expand Down Expand Up @@ -288,6 +291,29 @@ export const getPublishedDataInsights = (
) as Promise<MinimalDataInsightInterface[]>
}

export async function checkIfSlugCollides(
knex: KnexReadonlyTransaction,
gdoc: OwidGdocBaseInterface
): Promise<boolean> {
const existingGdoc = await knex(PostsGdocsTableName)
.where({
slug: gdoc.slug,
published: true,
})
.whereNot({
id: gdoc.id,
})
.first()
.then((row) => (row ? parsePostsGdocsRow(row) : undefined))

if (!existingGdoc) return false

const existingCanonicalUrl = getCanonicalUrl("", existingGdoc)
const incomingCanonicalUrl = getCanonicalUrl("", gdoc)

return existingCanonicalUrl === incomingCanonicalUrl
}

export const getPublishedDataInsightCount = (
knex: KnexReadonlyTransaction
): Promise<number> => {
Expand Down
2 changes: 1 addition & 1 deletion db/model/Gdoc/GdocFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ export async function getAllGdocIndexItemsOrderedByUpdatedAt(
)
}

export async function addImagesToContentGraph(
export async function setImagesInContentGraph(
trx: KnexReadWriteTransaction,
gdoc: GdocPost | GdocDataInsight | GdocHomepage | GdocAbout | GdocAuthor
): Promise<void> {
Expand Down

0 comments on commit 467ac6f

Please sign in to comment.