Skip to content

Commit

Permalink
🔨 extract methods to prevent circular imports, fixes runDbMigrations
Browse files Browse the repository at this point in the history
  • Loading branch information
ikesau committed Nov 29, 2023
1 parent c23e77b commit e7f5b8a
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 81 deletions.
2 changes: 1 addition & 1 deletion adminSiteServer/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export class OwidAdminApp {
const publishedExplorersBySlug =
await adminExplorerServer.getAllPublishedExplorersBySlugCached()
try {
const gdoc = await GdocPost.getGdocFromContentSource(
const gdoc = await GdocPost.getGdocFromContentSource<GdocPost>(
req.params.id,
publishedExplorersBySlug,
GdocsContentSource.Gdocs
Expand Down
11 changes: 6 additions & 5 deletions baker/siteRenderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,11 @@ export const renderGdocsPageBySlug = async (
const publishedExplorersBySlug =
await explorerAdminServer.getAllPublishedExplorersBySlug()

const gdocWithAttachments = await GdocPost.getGdocFromContentSource(
gdoc.id,
publishedExplorersBySlug
)
const gdocWithAttachments =
await GdocPost.getGdocFromContentSource<GdocPost>(
gdoc.id,
publishedExplorersBySlug
)

return renderGdoc(gdocWithAttachments)
}
Expand Down Expand Up @@ -306,7 +307,7 @@ export const renderFrontPage = async () => {
}

export const renderDonatePage = async () => {
const faqsGdoc = await GdocPost.getGdocFromContentSource(
const faqsGdoc = await GdocPost.getGdocFromContentSource<GdocPost>(
GDOCS_DONATE_FAQS_DOCUMENT_ID,
{}
)
Expand Down
2 changes: 1 addition & 1 deletion datapage/Datapage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export const getDatapageGdoc = async (
isPreviewing &&
publishedExplorersBySlug &&
OwidGoogleAuth.areGdocAuthKeysSet()
? await GdocPost.getGdocFromContentSource(
? await GdocPost.getGdocFromContentSource<GdocPost>(
googleDocId,
publishedExplorersBySlug,
GdocsContentSource.Gdocs
Expand Down
88 changes: 22 additions & 66 deletions db/model/Gdoc/GdocBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
import { getUrlTarget } from "@ourworldindata/components"
import {
LinkedChart,
OwidGdocInterface,
GdocsContentSource,
JsonError,
keyBy,
Expand All @@ -27,11 +26,9 @@ import {
uniq,
omit,
identity,
OwidGdocBaseInterface,
} from "@ourworldindata/utils"
import {
BAKED_GRAPHER_URL,
GDOCS_DETAILS_ON_DEMAND_ID,
} from "../../../settings/serverSettings.js"
import { BAKED_GRAPHER_URL } from "../../../settings/serverSettings.js"
import { google } from "googleapis"
import { gdocToArchie } from "./gdocToArchie.js"
import { archieToEnriched } from "./archieToEnriched.js"
Expand All @@ -43,17 +40,15 @@ import {
BAKED_GRAPHER_EXPORTS_BASE_URL,
} from "../../../settings/clientSettings.js"
import { EXPLORERS_ROUTE_FOLDER } from "../../../explorer/ExplorerConstants.js"
import { parseDetails } from "./rawToEnriched.js"
import { match, P } from "ts-pattern"
import {
getAllLinksFromResearchAndWritingBlock,
spansToSimpleString,
} from "./gdocUtils.js"
import { GdocPost } from "./GdocPost.js"
import { OwidGoogleAuth } from "../../OwidGoogleAuth.js"

@Entity()
export class GdocBase extends BaseEntity {
@Entity("posts_gdocs")
export class GdocBase extends BaseEntity implements OwidGdocBaseInterface {
@PrimaryColumn() id!: string
@Column() slug: string = ""
@Column({ default: "{}", type: "json" }) content!: Record<string, any>
Expand All @@ -66,15 +61,18 @@ export class GdocBase extends BaseEntity {
errors: OwidGdocErrorMessage[] = []
imageMetadata: Record<string, ImageMetadata> = {}
linkedCharts: Record<string, LinkedChart> = {}
linkedDocuments: Record<string, GdocPost> = {}
linkedDocuments: Record<string, OwidGdocBaseInterface> = {}
relatedCharts: RelatedChart[] = []

_getSubclassEnrichedBlocks: (gdoc: typeof this) => OwidEnrichedGdocBlock[] =
() => []
_enrichSubclassContent: (content: Record<string, any>) => void = identity
_validateSubclass: (gdoc: typeof this) => OwidGdocErrorMessage[] = () => []
_validateSubclass: (gdoc: typeof this) => Promise<OwidGdocErrorMessage[]> =
() => new Promise(() => [])
_filenameProperties: string[] = []
_omittableFields: string[] = []
_loadSubclassAttachments: (gdoc: typeof this) => Promise<void> = () =>
new Promise(() => undefined)

get enrichedBlockSources(): OwidEnrichedGdocBlock[][] {
const enrichedBlockSources: OwidEnrichedGdocBlock[][] = [
Expand Down Expand Up @@ -183,9 +181,12 @@ export class GdocBase extends BaseEntity {
}

get linkedImageFilenames(): string[] {
// Used for prominent links
// The typing logic is a little strange here
// `featured-image` isn't guaranteed to be on all types of Gdoc (it's a GdocPost thing)
// but we try (and then filter nulls) because we need featured images if we're using prominent links
// even if this method is being called on a GdocFaq (for example)
const featuredImages = Object.values(this.linkedDocuments)
.map((gdoc: GdocPost) => gdoc.content["featured-image"])
.map((d: OwidGdocBaseInterface) => d.content["featured-image"])
.filter((filename?: string): filename is string => !!filename)

return [...this.filenames, ...featuredImages]
Expand Down Expand Up @@ -491,7 +492,7 @@ export class GdocBase extends BaseEntity {
async loadLinkedDocuments(): Promise<void> {
const linkedDocuments = await Promise.all(
this.linkedDocumentIds.map(async (target) => {
const linkedDocument = await GdocPost.findOneBy({
const linkedDocument = await GdocBase.findOneBy({
id: target,
})
return linkedDocument
Expand Down Expand Up @@ -601,62 +602,17 @@ export class GdocBase extends BaseEntity {
[]
)

let dodErrors: OwidGdocErrorMessage[] = []
// Validating the DoD document is infinitely recursive :)
if (this.id !== GDOCS_DETAILS_ON_DEMAND_ID) {
const { details } = await GdocPost.getDetailsOnDemandGdoc()
dodErrors = this.details.reduce(
(
acc: OwidGdocErrorMessage[],
detailId
): OwidGdocErrorMessage[] => {
if (details && !details[detailId]) {
acc.push({
type: OwidGdocErrorMessageType.Error,
message: `Invalid DoD referenced: "${detailId}"`,
property: "content",
})
}
return acc
},
[]
)
}

// A one-off custom validation for this particular case
// Until we implement a more robust validation abstraction for fragments
// This is to validate the details document itself
// Whereas dodErrors is to validate *other documents* that are referencing dods
const dodDocumentErrors: OwidGdocErrorMessage[] = []
if (this.id === GDOCS_DETAILS_ON_DEMAND_ID) {
const results = parseDetails(this.content.details)
const errors: OwidGdocErrorMessage[] = results.parseErrors.map(
(parseError) => ({
...parseError,
property: "details",
type: OwidGdocErrorMessageType.Error,
})
)
dodDocumentErrors.push(...errors)
}

const subclassErrors = this._validateSubclass(this)
const subclassErrors = await this._validateSubclass(this)

this.errors = [
...filenameErrors,
...linkErrors,
...dodErrors,
...dodDocumentErrors,
...subclassErrors,
]
this.errors = [...filenameErrors, ...linkErrors, ...subclassErrors]
}

static async getGdocFromContentSource(
static async getGdocFromContentSource<T extends GdocBase>(
id: string,
publishedExplorersBySlug: Record<string, any>,
contentSource?: GdocsContentSource
): Promise<OwidGdocInterface> {
const gdoc = await GdocPost.findOne({
): Promise<T> {
const gdoc = await GdocBase.findOne({
where: {
id,
},
Expand All @@ -672,11 +628,11 @@ export class GdocBase extends BaseEntity {
await gdoc.loadLinkedDocuments()
await gdoc.loadImageMetadata()
await gdoc.loadLinkedCharts(publishedExplorersBySlug)
await gdoc.loadRelatedCharts()
await gdoc._loadSubclassAttachments(gdoc)

await gdoc.validate(publishedExplorersBySlug)

return gdoc
return gdoc as T
}

toJSON(): Record<string, any> {
Expand Down
2 changes: 1 addition & 1 deletion db/model/Gdoc/GdocFaq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class GdocFaq extends GdocBase {
}
}

_validateSubclass = (): OwidGdocErrorMessage[] => {
_validateSubclass = async (): Promise<OwidGdocErrorMessage[]> => {
const errors: OwidGdocErrorMessage[] = []

for (const parseError of this.content.parseErrors) {
Expand Down
34 changes: 33 additions & 1 deletion db/model/Gdoc/GdocPost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export class GdocPost extends GdocBase implements OwidGdocInterface {
}
}

linkedDocuments: Record<string, OwidGdocInterface> = {}
_filenameProperties = ["cover-image", "featured-image"]

_getSubclassEnrichedBlocks = (gdoc: this): OwidEnrichedGdocBlock[] => {
Expand Down Expand Up @@ -121,7 +122,7 @@ export class GdocPost extends GdocBase implements OwidGdocInterface {
}
}

_validateSubclass = (): OwidGdocErrorMessage[] => {
_validateSubclass = async (): Promise<OwidGdocErrorMessage[]> => {
const errors: OwidGdocErrorMessage[] = []

if (this.hasAllChartsBlock && !this.tags.length) {
Expand All @@ -147,9 +148,40 @@ export class GdocPost extends GdocBase implements OwidGdocInterface {
}
}

// Unless this is the DoD document, validate that all referenced dods exist
if (this.id !== GDOCS_DETAILS_ON_DEMAND_ID) {
const { details } = await GdocPost.getDetailsOnDemandGdoc()
for (const detailId of this.details) {
if (details && !details[detailId]) {
errors.push({
type: OwidGdocErrorMessageType.Error,
message: `Invalid DoD referenced: "${detailId}"`,
property: "content",
})
}
}
}

// This is to validate the DoD document itself
// TODO: this should be done on a GdocDods subclass
if (this.id === GDOCS_DETAILS_ON_DEMAND_ID) {
const results = parseDetails(this.content.details)
for (const parseError of results.parseErrors) {
errors.push({
...parseError,
property: "details",
type: OwidGdocErrorMessageType.Error,
})
}
}

return errors
}

_loadSubclassAttachments: (gdoc: this) => Promise<void> = async () => {
await this.loadRelatedCharts()
}

static async getDetailsOnDemandGdoc(): Promise<{
details: DetailDictionary
parseErrors: ParseError[]
Expand Down
1 change: 1 addition & 0 deletions packages/@ourworldindata/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export {
type OwidGdocErrorMessage,
OwidGdocErrorMessageType,
type OwidGdocLinkJSON,
type OwidGdocBaseInterface,
type OwidGdocInterface,
type Tag,
OwidGdocType,
Expand Down
17 changes: 11 additions & 6 deletions packages/@ourworldindata/utils/src/owidTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1346,22 +1346,27 @@ export enum OwidGdocType {
LinearTopicPage = "linear-topic-page",
}

export interface OwidGdocInterface {
export interface OwidGdocBaseInterface {
id: string
slug: string
content: OwidGdocContent
content: Record<string, any>
published: boolean
createdAt: Date
publishedAt: Date | null
updatedAt: Date | null
publicationContext: OwidGdocPublicationContext
revisionId: string | null
breadcrumbs?: BreadcrumbItem[] | null
linkedDocuments?: Record<string, OwidGdocBaseInterface>
linkedCharts?: Record<string, LinkedChart>
linkedDocuments?: Record<string, OwidGdocInterface>
relatedCharts?: RelatedChart[]
imageMetadata?: Record<string, ImageMetadata>
errors?: OwidGdocErrorMessage[]
}

export interface OwidGdocInterface extends OwidGdocBaseInterface {
content: OwidGdocContent
linkedDocuments?: Record<string, OwidGdocInterface>
publicationContext: OwidGdocPublicationContext
breadcrumbs?: BreadcrumbItem[] | null
relatedCharts?: RelatedChart[]
tags?: Tag[]
}

Expand Down

0 comments on commit e7f5b8a

Please sign in to comment.