UNPKG

convex

Version:

Client for the Convex Cloud

122 lines (112 loc) 3.25 kB
import axios, { AxiosResponse } from "axios"; import chalk from "chalk"; import ora from "ora"; import path from "path"; import { bundleSchema } from "../../bundler/index.js"; import { version } from "../../index.js"; import { Context } from "./context.js"; import { poll, fatalServerErr, deprecationCheckWarning } from "./utils.js"; type IndexMetadata = { table: string; name: string; fields: | string[] | { searchField: string; filterFields: string[]; }; backfill: { state: "in_progress" | "done"; }; }; function stringifyIndex(index: IndexMetadata) { return `${index.table}.${index.name} ${JSON.stringify(index.fields)}`; } function diffIndexes(indexes: { added: IndexMetadata[]; dropped: IndexMetadata[]; }) { let indexDiff = ""; if (indexes.dropped.length > 0) { indexDiff += "Delete the following indexes:\n"; for (const index of indexes.dropped) { indexDiff += `[-] ${stringifyIndex(index)}\n`; } } if (indexes.added.length > 0) { indexDiff += "Add the following indexes:\n"; for (const index of indexes.added) { indexDiff += `[+] ${stringifyIndex(index)}\n`; } } return indexDiff; } export async function buildIndexes( ctx: Context, origin: string, adminKey: string, schemaDir: string, dryRun: boolean ): Promise<void> { if (!ctx.fs.exists(path.resolve(schemaDir, "schema.ts"))) { // Don't do anything. return; } const bundles = await bundleSchema(ctx.fs, schemaDir); const spinner = (ctx.spinner = ora({ text: "Checking for changed table indexes...", stream: process.stdout, })); if (!dryRun) { spinner.start(); } try { const res = await axios.post<{ added: IndexMetadata[]; dropped: IndexMetadata[]; }>(`${origin}/api/${version}/build_indexes`, { bundle: bundles[0], adminKey, dryRun, }); deprecationCheckWarning(ctx, res); const indexDiff = diffIndexes(res.data); if (indexDiff !== "") { console.log( chalk.bold( `\nIndexes ${ dryRun ? "would" : "will" } be overwritten with the following changes:` ) ); console.log(indexDiff); } if (dryRun) { return; } spinner.text = "Waiting for all table indexes to be backfilled..."; await waitForIndexesToBuild(origin, adminKey); res.data.added.length > 0 ? spinner.succeed(chalk.green("Successfully backfilled table indexes.")) : res.data.dropped.length > 0 ? spinner.succeed( chalk.green("Successfully dropped deleted table indexes.") ) : spinner.stop(); } catch (err) { spinner.fail(chalk.red("Error: Unable to build indexes on", origin)); return await fatalServerErr(ctx, err); } } async function waitForIndexesToBuild(origin: string, adminKey: string) { const fetch = () => axios.get<{ indexes: IndexMetadata[] }>( `${origin}/api/${version}/get_indexes`, { headers: { Authorization: `Convex ${adminKey}` }, } ); const validate = (result: AxiosResponse<{ indexes: IndexMetadata[] }, any>) => result.data.indexes.every(index => index.backfill.state === "done"); await poll(fetch, validate); }