SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
4 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
4.2 KB · 84 lines typescript
Raw Blame History
1/** Slug-or-id resolution and entity reference lookups shared across repositories. */2import { pg } from "./sql.js";3import { reqStr, str, type Row } from "./rows.js";45const TABLES: Record<string, { table: string; idCol: string; slugCol: string; nameCol: string; extraSlugCol?: string }> = {6  facility: { table: "facilities", idCol: "id", slugCol: "slug", nameCol: "name" },7  campus: { table: "campuses", idCol: "id", slugCol: "slug", nameCol: "name" },8  operator: { table: "operators", idCol: "id", slugCol: "slug", nameCol: "name" },9  company: { table: "operators", idCol: "id", slugCol: "slug", nameCol: "name" },10  carrier: { table: "operators", idCol: "id", slugCol: "slug", nameCol: "name" },11  cloud_region: { table: "cloud_regions", idCol: "id", slugCol: "slug", nameCol: "name" },12  metro: { table: "metros", idCol: "id", slugCol: "slug", nameCol: "name" },13  country: { table: "countries", idCol: "iso2", slugCol: "slug", nameCol: "name" },14  ixp: { table: "ixps", idCol: "id", slugCol: "slug", nameCol: "name" },15  project: { table: "projects", idCol: "id", slugCol: "slug", nameCol: "name" },16  expansion: { table: "projects", idCol: "id", slugCol: "slug", nameCol: "name" },17  construction_phase: { table: "projects", idCol: "id", slugCol: "slug", nameCol: "name" },18  source: { table: "sources", idCol: "id", slugCol: "id", nameCol: "name" },19};2021/** Whole row for an entity by slug or id (`null` when unknown). Only whitelisted tables. */22export async function findBySlugOrId(table: "facilities" | "operators" | "metros" | "projects" | "cloud_regions" | "ixps" | "campuses", idOrSlug: string): Promise<Row | null> {23  const sql = pg();24  const rows = await sql<Row[]>`select * from ${sql(table)} where slug = ${idOrSlug} or id = ${idOrSlug} order by (id = ${idOrSlug}) desc limit 1`;25  return rows[0] ?? null;26}2728export async function findCountry(slugOrIso2: string): Promise<Row | null> {29  const sql = pg();30  const up = slugOrIso2.toUpperCase();31  const rows = await sql<Row[]>`select * from countries where slug = ${slugOrIso2.toLowerCase()} or iso2 = ${up} or iso3 = ${up} limit 1`;32  return rows[0] ?? null;33}3435/** Resolve {slug, name} for (entityType, entityId) pairs in one query per entity type. */36export async function resolveEntityRefs(pairs: Array<{ type: string; id: string | null }>): Promise<Map<string, { slug: string; name: string }>> {37  const sql = pg();38  const out = new Map<string, { slug: string; name: string }>();39  const byTable = new Map<string, { def: (typeof TABLES)[string]; ids: Set<string> }>();40  for (const p of pairs) {41    if (!p.id) continue;42    const def = TABLES[p.type];43    if (!def) continue;44    let g = byTable.get(def.table);45    if (!g) { g = { def, ids: new Set() }; byTable.set(def.table, g); }46    g.ids.add(p.id);47  }48  await Promise.all(49    [...byTable.values()].map(async ({ def, ids }) => {50      const list = [...ids];51      const rows = await sql<Row[]>`select ${sql(def.idCol)} as id, ${sql(def.slugCol)} as slug, ${sql(def.nameCol)} as name from ${sql(def.table)} where ${sql(def.idCol)} = any(${list})`;52      for (const r of rows) out.set(`${def.table}:${reqStr(r.id)}`, { slug: reqStr(r.slug), name: reqStr(r.name) });53    }),54  );55  // re-key by (type,id) so callers can look up with their own entity type56  const keyed = new Map<string, { slug: string; name: string }>();57  for (const p of pairs) {58    if (!p.id) continue;59    const def = TABLES[p.type];60    if (!def) continue;61    const v = out.get(`${def.table}:${p.id}`);62    if (v) keyed.set(`${p.type}:${p.id}`, v);63  }64  return keyed;65}6667export function entityKey(type: unknown, id: unknown): string {68  return `${str(type) ?? ""}:${str(id) ?? ""}`;69}7071/** href for a resolved entity on the web app. */72export function hrefFor(type: string, slug: string): string {73  switch (type) {74    case "facility": case "campus": return `/datacenters/${slug}`;75    case "operator": case "company": case "carrier": return `/operators/${slug}`;76    case "country": return `/countries/${slug}`;77    case "metro": return `/datacenters/markets/${slug}`;78    case "cloud_region": return `/cloud-regions/${slug}`;79    case "project": case "expansion": case "construction_phase": return `/projects/${slug}`;80    case "ixp": return `/ixps/${slug}`;81    default: return `/search?q=${encodeURIComponent(slug)}`;82  }83}84