@sanity/import
Version:
Import documents to a Sanity dataset
127 lines (103 loc) • 5.09 kB
text/typescript
import createDebug from 'debug'
import {absolutifyPaths, getAssetRefs, unsetAssetRefs} from './assetRefs.js'
import {assignArrayKeys} from './assignArrayKeys.js'
import {assignDocumentId} from './assignDocumentId.js'
import {batchDocuments} from './batchDocuments.js'
import {documentHasError} from './documentHasErrors.js'
import {importBatches} from './importBatches.js'
import {
cleanupReferences,
getStrongRefs,
strengthenReferences,
type StrongRefsTask,
weakenStrongRefs,
} from './references.js'
import {type ImportOptions, type ImportResult, type SanityDocument} from './types.js'
import {uploadAssets} from './uploadAssets.js'
import {ensureUniqueIds} from './util/ensureUniqueIds.js'
import {validateAssetMapForReplacementChars} from './util/validateReplacementCharacters.js'
import {validateAssetDocuments} from './validateAssetDocuments.js'
import {validateCdrDatasets} from './validateCdrDatasets.js'
const debug = createDebug('sanity:import:array')
async function importDocuments(
documents: SanityDocument[],
options: ImportOptions,
): Promise<ImportResult> {
options.onProgress({step: 'Reading/validating data file'})
for (const [i, doc] of documents.entries()) {
documentHasError(doc, i)
}
// Validate assetMap for replacement characters
if (options.assetMap && options.allowReplacementCharacters !== true) {
validateAssetMapForReplacementChars(options.assetMap)
}
// Validate that there are no duplicate IDs in the documents
ensureUniqueIds(documents)
// Ensure that any cross-dataset references has datasets to point to
if (!options.skipCrossDatasetReferences) {
await validateCdrDatasets(documents, options)
}
let filteredDocuments = documents
// Always filter out system documents unless explicitly allowed.
// Release system documents are an exception to this flag.
if (options.allowSystemDocuments !== true) {
filteredDocuments = documents.filter(
(doc) => doc._id?.startsWith('_.releases.') || !doc._id?.startsWith('_.'),
)
}
// Replace relative asset paths if one is defined
// (file://./images/foo-bar.png -> file:///abs/olute/images/foo-bar.png)
const absPathed = filteredDocuments.map((doc) => absolutifyPaths(doc, options.assetsBase))
// Assign document IDs for document that do not have one. This is necessary
// for us to strengthen references and import assets properly.
const ided = absPathed.map((doc) => assignDocumentId(doc))
// User might not have applied `_key` on array elements which are objects;
// if this is the case, generate random keys to help realtime engine
const keyed = ided.map((doc) => assignArrayKeys(doc))
// Sanity prefers to have a `_type` on every object. Make sure references
// has `_type` set to `reference`, and that there are no `_projectId` keys
const docs = keyed.map((doc) => cleanupReferences(doc, options))
// Find references that will need strengthening when import is done
const strongRefs = docs
.map((doc) => getStrongRefs(doc))
.filter((ref): ref is StrongRefsTask => ref !== null)
// Extract asset references from the documents
const assetRefs = docs.flatMap((doc) => getAssetRefs(doc))
// Remove asset references from the documents
const assetless = docs.map((doc) => unsetAssetRefs(doc))
// Make strong references weak so they can be imported in any order
const weakened = assetless.map((doc) => weakenStrongRefs(doc))
// Create batches of documents to import. Try to keep batches below a certain
// byte-size (since document may vary greatly in size depending on type etc)
const batches = batchDocuments(weakened)
// Ensure that we don't reference missing assets, or assets in different datasets
debug('Validating asset documents')
await validateAssetDocuments(docs, options)
// Trigger actual import process
debug('Starting import of documents')
const {count: docsImported, importedIds} = await importBatches(batches, options)
// When using createIfNotExists (--missing), only upload assets for documents
// that were actually created. Documents that already existed in the dataset
// are assumed to already have their assets in place.
let filteredAssetRefs = assetRefs
if (options.operation === 'createIfNotExists') {
const importedIdSet = new Set(importedIds)
filteredAssetRefs = assetRefs.filter((ref) => importedIdSet.has(ref.documentId))
const skipped = assetRefs.length - filteredAssetRefs.length
if (skipped > 0) {
debug('Skipping %d asset refs for %d already-existing documents', skipped, skipped)
}
}
// Documents are imported, now proceed with post-import operations
debug('Uploading assets')
const {failures: assetWarnings} = await uploadAssets(filteredAssetRefs, options)
// Strengthen references
debug('Strengthening references')
await strengthenReferences(strongRefs, options)
// Return number of documents imported
return {
numDocs: docsImported,
warnings: assetWarnings.map((warning) => ({message: `Failed to upload asset: ${warning.url}`})),
}
}
export {importDocuments}