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

168 lines (148 loc) 5.6 kB
import {type CliCommandContext, type CliOutputter} from '@sanity/cli' import {type SanityClient} from '@sanity/client' import chalk from 'chalk' import partition from 'lodash/partition' import { type ManifestWorkspaceFile, SANITY_WORKSPACE_SCHEMA_TYPE, type StoredWorkspaceSchema, } from '../../../manifest/manifestTypes' import {type SchemaStoreActionResult, type SchemaStoreContext} from './schemaStoreTypes' import {createManifestExtractor, ensureManifestExtractSatisfied} from './utils/mainfestExtractor' import {type CreateManifestReader, createManifestReader} from './utils/manifestReader' import {createSchemaApiClient} from './utils/schemaApiClient' import { FlagValidationError, parseDeploySchemasConfig, type SchemaStoreCommonFlags, throwWriteProjectIdMismatch, } from './utils/schemaStoreValidation' import {getWorkspaceSchemaId} from './utils/workspaceSchemaId' export interface DeploySchemasFlags extends SchemaStoreCommonFlags { 'workspace'?: string 'id-prefix'?: string 'schema-required'?: boolean } export default function deploySchemasActionForCommand( flags: DeploySchemasFlags, context: CliCommandContext, ): Promise<SchemaStoreActionResult> { return deploySchemasAction( { ...flags, //invoking the command through CLI implies that schema is required 'schema-required': true, }, { ...context, manifestExtractor: createManifestExtractor(context), }, ) } /** * * Stores schemas for configured workspaces into 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 deploySchemasAction( flags: DeploySchemasFlags, context: SchemaStoreContext, ): Promise<SchemaStoreActionResult> { const {workspaceName, verbose, idPrefix, manifestDir, extractManifest, schemaRequired} = parseDeploySchemasConfig(flags, context) const {output, apiClient, jsonReader, manifestExtractor} = context // prettier-ignore if (!(await ensureManifestExtractSatisfied({schemaRequired, extractManifest, manifestDir, manifestExtractor, output,}))) { return 'failure' } try { const {client, projectId} = createSchemaApiClient(apiClient) const manifestReader = createManifestReader({manifestDir, output, jsonReader}) const manifest = await manifestReader.getManifest() const storeWorkspaceSchema = createStoreWorkspaceSchema({ idPrefix, projectId, verbose, client, output, manifestReader, }) const targetWorkspaces = manifest.workspaces.filter( (workspace) => !workspaceName || workspace.name === workspaceName, ) if (!targetWorkspaces.length) { if (workspaceName) { throw new FlagValidationError(`Found no workspaces named "${workspaceName}"`) } else { throw new Error(`Workspace array in manifest is empty.`) } } //known caveat: we _dont_ rollback failed operations or partial success const results = await Promise.allSettled( targetWorkspaces.map(async (workspace: ManifestWorkspaceFile): Promise<void> => { await storeWorkspaceSchema(workspace) }), ) const [successes, failures] = partition(results, (result) => result.status === 'fulfilled') if (failures.length) { throw new Error( `Failed to deploy ${failures.length}/${targetWorkspaces.length} schemas. Successfully deployed ${successes.length}/${targetWorkspaces.length} schemas.`, ) } output.success(`Deployed ${successes.length}/${targetWorkspaces.length} schemas`) return 'success' } catch (err) { if (schemaRequired || err instanceof FlagValidationError) { throw err } else { output.print(`↳ Error when storing schemas:\n ${err.message}`) return 'failure' } } finally { output.print( `${chalk.gray('↳ List deployed schemas with:')} ${chalk.cyan('sanity schema list')}`, ) } } function createStoreWorkspaceSchema(args: { idPrefix?: string projectId: string verbose: boolean client: SanityClient output: CliOutputter manifestReader: CreateManifestReader }): (workspace: ManifestWorkspaceFile) => Promise<void> { const {idPrefix, projectId, verbose, client, output, manifestReader} = args return async (workspace) => { const {safeId: id, idWarning} = getWorkspaceSchemaId({workspaceName: workspace.name, idPrefix}) if (idWarning) output.warn(idWarning) try { throwWriteProjectIdMismatch(workspace, projectId) const schema = await manifestReader.getWorkspaceSchema(workspace.name) const storedWorkspaceSchema: StoredWorkspaceSchema = { _type: SANITY_WORKSPACE_SCHEMA_TYPE, _id: id, workspace, // we have to stringify the schema to save on attribute paths schema: JSON.stringify(schema), } await client .withConfig({dataset: workspace.dataset, projectId: workspace.projectId}) .createOrReplace(storedWorkspaceSchema) if (verbose) { output.print( chalk.gray(`↳ schemaId: ${id}, projectId: ${projectId}, dataset: ${workspace.dataset}`), ) } } catch (err) { output.error( `↳ Error deploying schema for workspace "${workspace.name}":\n ${chalk.red(`${err.message}`)}`, ) throw err } } }