All files / owid-grapher/db contentGraph.ts

49.06% Statements 52/106
100% Branches 3/3
100% Functions 1/1
49.06% Lines 52/106

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 1041x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 1x 1x 1x 1x                                                                                                             1x
import { WP_PostType } from "../clientUtils/owidTypes"
import { once } from "../clientUtils/Util"
import { ENTRIES_CATEGORY_ID, getDocumentsInfo } from "./wpdb"
 
const fortune = require("fortune") // Works in web browsers, too.
const MemoryAdapter = require("fortune/lib/adapter/adapters/memory")
const {
    errors: { ConflictError },
} = fortune
 
export enum GraphType {
    Document = "document",
    Chart = "chart",
}
 
const store = fortune(
    {
        [GraphType.Document]: {
            title: String,
            slug: String,
            data: [Array("chart"), "research"],
        },
        [GraphType.Chart]: {
            research: [Array("document"), "data"],
        },
    },
    {
        adapter: [
            MemoryAdapter,
            {
                // see https://github.com/fortunejs/fortune/commit/70593721efae304ff2db40d1b8f9b43295fed79b#diff-ebb028a2d1528eac83ee833036ef6e50bed6fb7b2b7137f59ac1fb567a5e6ec2R25
                recordsPerType: Infinity,
            },
        ],
    }
)
 
export const getGrapherSlugs = (content: string | null): Set<string> => {
    const slugs = new Set<string>()
    if (!content) return slugs
 
    const matches = content.matchAll(/\/grapher\/([a-zA-Z0-9-]+)/g)
    for (const match of matches) {
        slugs.add(match[1])
    }
    return slugs
}
 
export const getContentGraph = once(async () => {
    const orderBy = "orderby:{field:MODIFIED, order:DESC}"
    const entries = await getDocumentsInfo(
        WP_PostType.Page,
        "",
        `categoryId: ${ENTRIES_CATEGORY_ID}, ${orderBy}`
    )
    const posts = await getDocumentsInfo(WP_PostType.Post, "", orderBy)
    const documents = [...entries, ...posts]
    for (const document of documents) {
        // Add posts and entries to the content graph
        try {
            await store.create(GraphType.Document, {
                id: document.id,
                title: document.title,
                slug: document.slug,
            })
        } catch (err) {
            // There shouldn't be any ConflictErrors here as the posts are
            // unique in the list we're iterating on, and the call to generate
            // the graph is memoized.
            throw err
        }

        // Add charts within that post to the content graph
        const grapherSlugs = getGrapherSlugs(document.content)
        for (const slug of grapherSlugs) {
            try {
                await store.create(GraphType.Chart, {
                    id: slug,
                    research: [document.id],
                })
            } catch (err) {
                // ConflictErrors occur when attempting to create a chart that
                // already exists
                if (!(err instanceof ConflictError)) {
                    throw err
                }
                try {
                    await store.update(GraphType.Chart, {
                        id: slug,
                        push: { research: document.id },
                    })
                } catch (err) {
                    // ConflictErrors occur here when a chart <-> post
                    // relationship already exists
                    if (!(err instanceof ConflictError)) {
                        throw err
                    }
                }
            }
        }
    }
    return store
})