Final work to support app update process.
This commit is contained in:
parent
1808665bb3
commit
61c12d88cf
|
@ -1,5 +1,13 @@
|
|||
<script>
|
||||
import { ModalContent, Toggle, Input, Layout, Dropzone, notifications } from "@budibase/bbui"
|
||||
import {
|
||||
ModalContent,
|
||||
Toggle,
|
||||
Input,
|
||||
Layout,
|
||||
Dropzone,
|
||||
notifications,
|
||||
Body,
|
||||
} from "@budibase/bbui"
|
||||
import { API } from "api"
|
||||
import { automationStore, store } from "../../builderStore"
|
||||
|
||||
|
@ -35,6 +43,11 @@
|
|||
onConfirm={updateApp}
|
||||
bind:disabled
|
||||
>
|
||||
<Body size="S"
|
||||
>Updating an app using an app export will replace all tables, datasources,
|
||||
queries, screens and automations. It is recommended to perform a backup
|
||||
before running this operation.</Body
|
||||
>
|
||||
<Layout noPadding gap="XS">
|
||||
<Dropzone
|
||||
gallery={false}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { db as dbCore } from "@budibase/backend-core"
|
||||
import {
|
||||
DocumentsToImport,
|
||||
Document,
|
||||
Database,
|
||||
RowValue,
|
||||
} from "@budibase/types"
|
||||
import backups from "../backups"
|
||||
|
||||
export type FileAttributes = {
|
||||
|
@ -6,24 +12,74 @@ export type FileAttributes = {
|
|||
path: string
|
||||
}
|
||||
|
||||
async function removeImportableDocuments(db: Database) {
|
||||
// get the references to the documents, not the whole document
|
||||
const docPromises = []
|
||||
for (let docType of DocumentsToImport) {
|
||||
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
|
||||
documentRefs = documentRefs.map(ref => ({ _deleted: true, ...ref }))
|
||||
// perform deletion
|
||||
await db.bulkDocs(documentRefs)
|
||||
}
|
||||
|
||||
async function getImportableDocuments(db: Database) {
|
||||
// get the whole document
|
||||
const docPromises = []
|
||||
for (let docType of DocumentsToImport) {
|
||||
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)
|
||||
// TEMPORARY BEGIN
|
||||
const tempAppName = `temp_${devId}`
|
||||
const tempDb = dbCore.getDB(tempAppName)
|
||||
const appDb = dbCore.getDB(devId)
|
||||
await appDb.destroy()
|
||||
// TEMPORARY END
|
||||
// const tempAppName = `temp_${devId}`
|
||||
// const tempDb = dbCore.getDB(tempAppName)
|
||||
const template = {
|
||||
file: {
|
||||
type: file.type!,
|
||||
path: file.path!,
|
||||
password,
|
||||
},
|
||||
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, { objStore: false })
|
||||
// get the documents to copy
|
||||
const documents = await getImportableDocuments(tempDb)
|
||||
// clear out the old documents
|
||||
await removeImportableDocuments(appDb)
|
||||
// now write the import documents
|
||||
await appDb.bulkDocs(documents)
|
||||
} finally {
|
||||
await tempDb.destroy()
|
||||
}
|
||||
await backups.importApp(devId, appDb, template)
|
||||
}
|
||||
|
|
|
@ -151,7 +151,8 @@ export function getListOfAppsInMulti(tmpPath: string) {
|
|||
export async function importApp(
|
||||
appId: string,
|
||||
db: Database,
|
||||
template: TemplateType
|
||||
template: TemplateType,
|
||||
opts: { objStore: boolean } = { objStore: true }
|
||||
) {
|
||||
let prodAppId = dbCore.getProdAppID(appId)
|
||||
let dbStream: any
|
||||
|
@ -165,7 +166,7 @@ export async function importApp(
|
|||
}
|
||||
const contents = fs.readdirSync(tmpPath)
|
||||
// have to handle object import
|
||||
if (contents.length) {
|
||||
if (contents.length && opts.objStore) {
|
||||
let promises = []
|
||||
let excludedFiles = [GLOBAL_DB_EXPORT_FILE, DB_EXPORT_FILE]
|
||||
for (let filename of contents) {
|
||||
|
|
|
@ -39,6 +39,25 @@ export enum DocumentType {
|
|||
AUDIT_LOG = "al",
|
||||
}
|
||||
|
||||
// these are the core documents that make up the data, design
|
||||
// and automation sections of an app. This excludes any internal
|
||||
// rows as we shouldn't import data.
|
||||
export const DocumentsToImport: DocumentType[] = [
|
||||
DocumentType.ROLE,
|
||||
DocumentType.DATASOURCE,
|
||||
DocumentType.DATASOURCE_PLUS,
|
||||
DocumentType.TABLE,
|
||||
DocumentType.AUTOMATION,
|
||||
DocumentType.WEBHOOK,
|
||||
DocumentType.SCREEN,
|
||||
DocumentType.QUERY,
|
||||
DocumentType.METADATA,
|
||||
DocumentType.MEM_VIEW,
|
||||
// Deprecated but still copied
|
||||
DocumentType.INSTANCE,
|
||||
DocumentType.LAYOUT,
|
||||
]
|
||||
|
||||
// these documents don't really exist, they are part of other
|
||||
// documents or enriched into existence as part of get requests
|
||||
export enum VirtualDocumentType {
|
||||
|
|
Loading…
Reference in New Issue