UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

155 lines (138 loc) 5.32 kB
import {type CliCommandContext} from '@sanity/cli' import chalk from 'chalk' import uniq from 'lodash/uniq' import {isDefined} from '../../../manifest/manifestTypeHelpers' import {type SchemaStoreActionResult, type SchemaStoreContext} from './schemaStoreTypes' import {createManifestExtractor, ensureManifestExtractSatisfied} from './utils/mainfestExtractor' import {createManifestReader} from './utils/manifestReader' import {createSchemaApiClient} from './utils/schemaApiClient' import {getDatasetsOutString, getStringList} from './utils/schemaStoreOutStrings' import { filterLogReadProjectIdMismatch, parseDeleteSchemasConfig, type SchemaStoreCommonFlags, } from './utils/schemaStoreValidation' export interface DeleteSchemaFlags extends SchemaStoreCommonFlags { ids?: string dataset?: string } interface DeleteResult { dataset: string schemaId: string deleted: boolean } class DeleteIdError extends Error { public id: string public dataset: string constructor(id: string, dataset: string, options?: ErrorOptions) { super((options?.cause as {message?: string})?.message, options) this.name = 'DeleteIdError' this.id = id this.dataset = dataset } } export default function deleteSchemasActionForCommand( flags: DeleteSchemaFlags, context: CliCommandContext, ): Promise<SchemaStoreActionResult> { return deleteSchemaAction(flags, { ...context, manifestExtractor: createManifestExtractor(context), }) } /** * Deletes all stored schemas matching --ids in workspace datasets. * * Workspaces are determined by on-disk manifest file – not directly from sanity.config. * All schema store actions require a manifest to exist, so we regenerate it by default. * Manifest generation can be optionally disabled with --no-manifest-extract. * In this case the command uses and existing file or throws when missing. */ export async function deleteSchemaAction( flags: DeleteSchemaFlags, context: SchemaStoreContext, ): Promise<SchemaStoreActionResult> { const {ids, dataset, extractManifest, manifestDir, verbose} = parseDeleteSchemasConfig( flags, context, ) const {output, apiClient, jsonReader, manifestExtractor} = context // prettier-ignore if (!(await ensureManifestExtractSatisfied({schemaRequired: true, extractManifest, manifestDir, manifestExtractor, output,}))) { return 'failure' } const {client, projectId} = createSchemaApiClient(apiClient) const manifest = await createManifestReader({manifestDir, output, jsonReader}).getManifest() const workspaces = manifest.workspaces .filter((workspace) => !dataset || workspace.dataset === dataset) .filter((workspace) => filterLogReadProjectIdMismatch(workspace, projectId, output)) const datasets = uniq(workspaces.map((w) => w.dataset)) const results = await Promise.allSettled( datasets.flatMap((targetDataset: string) => { return ids.map(async ({schemaId}): Promise<DeleteResult> => { try { const deletedSchema = await client.withConfig({dataset: targetDataset}).delete(schemaId) return {dataset: targetDataset, schemaId, deleted: deletedSchema.results.length} } catch (err) { throw new DeleteIdError(schemaId, targetDataset, {cause: err}) } }) }), ) const deletedIds = results .filter((r): r is PromiseFulfilledResult<DeleteResult> => r.status === 'fulfilled') .filter((r) => r.value.deleted) .map((r) => r.value) const notFound = uniq( results .filter((r): r is PromiseFulfilledResult<DeleteResult> => r.status === 'fulfilled') .filter((r) => !r.value.deleted) .filter((r) => !deletedIds.map(({schemaId}) => schemaId).includes(r.value.schemaId)) .map((r) => r.value.schemaId), ) const deleteFailureIds = uniq( results .filter((r) => r.status === 'rejected') .map((result) => { const error = result.reason if (error instanceof DeleteIdError) { output.error( chalk.red( `Failed to delete schema "${error.id}" in dataset "${error.dataset}":\n${error.message}`, ), ) if (verbose) output.error(error) return error.id } //hubris inc: given the try-catch wrapping the full promise "this should never happen" throw error }), ) const success = deletedIds.length === ids.length if (success) { output.success(`Successfully deleted ${deletedIds.length}/${ids.length} schemas`) } else { output.error( [ `Deleted ${deletedIds.length}/${ids.length} schemas.`, deletedIds.length ? `Successfully deleted ids:\n${deletedIds .map( ({schemaId, dataset: targetDataset}) => `- "${schemaId}" (in ${getDatasetsOutString([targetDataset])})`, ) .join('\n')}` : undefined, notFound.length ? `Ids not found in ${getDatasetsOutString(datasets)}:\n${getStringList(notFound)}` : undefined, ...(deleteFailureIds.length ? [`Failed to delete ids:\n${getStringList(deleteFailureIds)}`, 'Check logs for errors.'] : []), ] .filter(isDefined) .join('\n'), ) } return success ? 'success' : 'failure' }