SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
5 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
2.8 KB · 61 lines typescript
Raw Blame History
1import type { Parser, ExtractedRecord, RawDocument } from "@dci/connectors";2import { countryFromText, cleanText } from "@dci/core";3import { loadHtml, tableRows, txt, resolvePlace, regionRecord, type Place } from "./shared.js";45/**6 * IBM Cloud — https://cloud.ibm.com/docs/overview?topic=overview-locations (server-rendered docs):7 *   "Multizone regions" / "Single-campus MZRs" tables: Region ("Dallas (us-south)") | Zone ("us-south-1 us-south-2 us-south-3").8 *   Later tables (fault domains, zone mapping, classic data centers) are ignored for regions.9 */10export const ibmRegionsParser: Parser = {11  name: "cloud_ibm_regions_v1",12  version: "1.0.0",13  pageTypes: ["cloud_region"],14  parse(doc: RawDocument, ctx): ExtractedRecord[] {15    const $ = loadHtml(doc);16    const out: ExtractedRecord[] = [];17    const seen = new Set<string>();18    $("table").each((_, table) => {19      const rows = tableRows($, $(table));20      const header = rows[0] ?? [];21      if (header.length !== 2 || !/^Region$/i.test(header[0] ?? "") || !/^Zone/i.test(header[1] ?? "")) return;22      // find the nearest preceding h2/h3 for the MZR type23      const heading = txt($(table).prevAll("h2,h3").first()) || txt($(table).parent().prevAll("h2,h3").first());24      const kind = /single-campus/i.test(heading) ? "single_campus_mzr" : "mzr";25      for (const row of rows.slice(1)) {26        const m = row[0]!.match(/^(.*?)\s*\(\s*([a-z]{2}-[a-z]{2,4})\s*\)/);27        if (!m) continue;28        const code = m[2]!;29        if (seen.has(code)) continue;30        seen.add(code);31        const rawCity = cleanText(m[1]!) ?? m[1]!;32        const partner = rawCity.match(/-\s*(.+)$/)?.[1]?.trim() ?? null; // "Chennai - Airtel"33        const cityLabel = rawCity.replace(/\s*-\s*.+$/, "").trim();34        const zones = (row[1] ?? "").split(/\s+/).filter((z) => /^[a-z]{2}-[a-z]{2,4}-\d$/.test(z));35        let place: Place = resolvePlace(cityLabel);36        if (!place.countryIso2) place.countryIso2 = countryFromText(cityLabel);37        if (!place.city) place.city = cityLabel;38        const externalIds: Record<string, string | number> = { ibm_region: code, kind, ibm_zones: zones.join(","), published_location: rawCity };39        if (partner) externalIds.partner = partner;40        out.push(41          regionRecord({42            connectorId: ctx.connectorId,43            keyPrefix: "ibm:region",44            providerName: "IBM Cloud",45            code,46            name: `${cityLabel} (${code})`,47            place,48            availabilityZones: zones.length || null,49            status: "operational",50            externalIds,51            url: doc.finalUrl,52            methods: { code: "html:table.Region", availabilityZones: "count:zones", city: "curated-city-table" },53          }),54        );55      }56    });57    ctx.log("info", `ibm: ${out.length} regions`);58    return out;59  },60};61