budibase/packages/server/src/sdk/app/applications/import.ts

103 lines
2.9 KiB
TypeScript

import { db as dbCore } from "@budibase/backend-core"
import {
DocumentTypesToImport,
Document,
Database,
RowValue,
} from "@budibase/types"
import backups from "../backups"
export type FileAttributes = {
type: string
path: string
}
function mergeUpdateAndDeleteDocuments(
updateDocs: Document[],
deleteDocs: Document[]
) {
// compress the documents to create and to delete (if same ID, then just update the rev)
const finalToDelete = []
for (let deleteDoc of deleteDocs) {
const found = updateDocs.find(doc => doc._id === deleteDoc._id)
if (found) {
found._rev = deleteDoc._rev
} else {
finalToDelete.push(deleteDoc)
}
}
return [...updateDocs, ...finalToDelete]
}
async function removeImportableDocuments(db: Database) {
// get the references to the documents, not the whole document
const docPromises = []
for (let docType of DocumentTypesToImport) {
docPromises.push(db.allDocs(dbCore.getDocParams(docType)))
}
let documentRefs: { _id: string; _rev: string }[] = []
for (let response of await Promise.all(docPromises)) {
documentRefs = documentRefs.concat(
response.rows.map(row => ({
_id: row.id,
_rev: (row.value as RowValue).rev,
}))
)
}
// add deletion key
return documentRefs.map(ref => ({ _deleted: true, ...ref }))
}
async function getImportableDocuments(db: Database) {
// get the whole document
const docPromises = []
for (let docType of DocumentTypesToImport) {
docPromises.push(
db.allDocs(dbCore.getDocParams(docType, null, { include_docs: true }))
)
}
// map the responses to the document itself
let documents: Document[] = []
for (let response of await Promise.all(docPromises)) {
documents = documents.concat(response.rows.map(row => row.doc))
}
// remove the _rev, stops it being written
documents.forEach(doc => {
delete doc._rev
})
return documents
}
export async function updateWithExport(
appId: string,
file: FileAttributes,
password?: string
) {
const devId = dbCore.getDevAppID(appId)
const tempAppName = `temp_${devId}`
const tempDb = dbCore.getDB(tempAppName)
const appDb = dbCore.getDB(devId)
try {
const template = {
file: {
type: file.type!,
path: file.path!,
password,
},
}
// get a temporary version of the import
// don't need obj store, the existing app already has everything we need
await backups.importApp(devId, tempDb, template, {
importObjStoreContents: false,
})
// get the documents to copy
const toUpdate = await getImportableDocuments(tempDb)
// clear out the old documents
const toDelete = await removeImportableDocuments(appDb)
// now bulk update documents - add new ones, delete old ones and update common ones
await appDb.bulkDocs(mergeUpdateAndDeleteDocuments(toUpdate, toDelete))
} finally {
await tempDb.destroy()
}
}