dev mode E2E

This commit is contained in:
Martin McKeaveney 2021-05-13 11:06:08 +01:00
parent 2fa7ee5bcb
commit 61a5b109f5
13 changed files with 157 additions and 1318 deletions

View File

@ -0,0 +1,78 @@
const { getDB } = require(".")
class Replication {
/**
*
* @param {String} source - the DB you want to replicate or rollback to
* @param {String} target - the DB you want to replicate to, or rollback from
*/
constructor({ source, target }) {
this.source = getDB(source)
this.target = getDB(target)
}
sync(opts) {
return new Promise((resolve, reject) => {
this.source
.sync(this.target, opts)
.on("change", function (info) {
// handle change
})
.on("paused", function (err) {
// replication paused (e.g. replication up to date, user went offline)
})
.on("active", function () {
// replicate resumed (e.g. new changes replicating, user went back online)
})
.on("denied", function (err) {
// a document failed to replicate (e.g. due to permissions)
return reject(
new Error(`Denied: Document failed to replicate ${err}`)
)
})
.on("complete", function (info) {
return resolve(info)
})
.on("error", function (err) {
return reject(new Error(`Replication Error: ${err}`))
})
})
}
replicate() {
return new Promise((resolve, reject) => {
this.replication = this.source.replicate
.to(this.target)
// .on("change", function (info) {
// // handle change
// })
// .on("paused", function (err) {
// // replication paused (e.g. replication up to date, user went offline)
// })
// .on("active", function () {
// // replicate resumed (e.g. new changes replicating, user went back online)
// })
.on("denied", function (err) {
// a document failed to replicate (e.g. due to permissions)
throw new Error(`Denied: Document failed to replicate ${err}`)
})
.on("complete", function (info) {
return resolve(info)
})
.on("error", function (err) {
throw new Error(`Replication Error: ${err}`)
})
})
}
async rollback() {
await this.target.destroy()
await this.replicate()
}
cancel() {
this.replication.cancel()
}
}
module.exports = Replication

View File

@ -1,4 +1,5 @@
const { newid } = require("../hashing") const { newid } = require("../hashing")
const Replication = require("./Replication")
exports.ViewNames = { exports.ViewNames = {
USER_BY_EMAIL: "by_email", USER_BY_EMAIL: "by_email",
@ -165,6 +166,7 @@ async function getScopedConfig(db, params) {
return configDoc && configDoc.config ? configDoc.config : configDoc return configDoc && configDoc.config ? configDoc.config : configDoc
} }
exports.Replication = Replication
exports.getScopedConfig = getScopedConfig exports.getScopedConfig = getScopedConfig
exports.generateConfigID = generateConfigID exports.generateConfigID = generateConfigID
exports.getConfigParams = getConfigParams exports.getConfigParams = getConfigParams

View File

@ -22,7 +22,7 @@
<div class="title"> <div class="title">
<Link href={$url(`../../app/${app._id}`)}> <Link href={$url(`../../app/${app._id}`)}>
<Heading size="XS"> <Heading size="XS">
{app.name} {app._id} {app.name}
</Heading> </Heading>
</Link> </Link>
<ActionMenu align="right"> <ActionMenu align="right">

View File

@ -15,11 +15,9 @@
$: appId = $store.appId $: appId = $store.appId
async function deployApp() { async function deployApp() {
const DEPLOY_URL = `/api/deploy`
try { try {
notifications.info(`Deployment started. Please wait.`) notifications.info(`Deployment started. Please wait.`)
const response = await api.post(DEPLOY_URL) const response = await api.post("/api/deploy")
const json = await response.json() const json = await response.json()
if (response.status !== 200) { if (response.status !== 200) {
throw new Error() throw new Error()

View File

@ -25,6 +25,7 @@
import AppRow from "components/start/AppRow.svelte" import AppRow from "components/start/AppRow.svelte"
let layout = "grid" let layout = "grid"
let appStatus = "deployed"
let template let template
let appToDelete let appToDelete
let creationModal let creationModal
@ -32,6 +33,8 @@
let creatingApp = false let creatingApp = false
let loaded = false let loaded = false
$: appStatus && apps.load(appStatus)
const checkKeys = async () => { const checkKeys = async () => {
const response = await api.get(`/api/keys/`) const response = await api.get(`/api/keys/`)
const keys = await response.json() const keys = await response.json()
@ -107,7 +110,13 @@
</div> </div>
<div class="filter"> <div class="filter">
<div class="select"> <div class="select">
<Select quiet placeholder="Filter by groups" /> <Select
bind:value={appStatus}
options={[
{ label: "Deployed", value: "deployed" },
{ label: "In Development", value: "dev" },
]}
/>
</div> </div>
<ActionGroup> <ActionGroup>
<ActionButton <ActionButton

View File

@ -4,9 +4,9 @@ import { get } from "builderStore/api"
export function createAppStore() { export function createAppStore() {
const store = writable([]) const store = writable([])
async function load() { async function load(status = "") {
try { try {
const res = await get("/api/applications") const res = await get(`/api/applications?status=${status}`)
const json = await res.json() const json = await res.json()
if (res.ok && Array.isArray(json)) { if (res.ok && Array.isArray(json)) {
store.set(json) store.set(json)

View File

@ -2873,7 +2873,7 @@ svelte-spa-router@^3.0.5:
dependencies: dependencies:
regexparam "1.3.0" regexparam "1.3.0"
svelte@^3.37.0: svelte@^3.38.2:
version "3.38.2" version "3.38.2"
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5"
integrity sha512-q5Dq0/QHh4BLJyEVWGe7Cej5NWs040LWjMbicBGZ+3qpFWJ1YObRmUDZKbbovddLC9WW7THTj3kYbTOFmU9fbg== integrity sha512-q5Dq0/QHh4BLJyEVWGe7Cej5NWs040LWjMbicBGZ+3qpFWJ1YObRmUDZKbbovddLC9WW7THTj3kYbTOFmU9fbg==

View File

@ -16,6 +16,9 @@ const {
getLayoutParams, getLayoutParams,
getScreenParams, getScreenParams,
generateScreenID, generateScreenID,
generateDevAppID,
DocumentTypes,
AppStatus,
} = require("../../db/utils") } = require("../../db/utils")
const { const {
BUILTIN_ROLE_IDS, BUILTIN_ROLE_IDS,
@ -84,7 +87,10 @@ async function getAppUrlIfNotInUse(ctx) {
} }
async function createInstance(template) { async function createInstance(template) {
const appId = generateAppID() // TODO: Do we need the normal app ID?
const baseAppId = generateAppID()
const appId = generateDevAppID(baseAppId)
const db = new CouchDB(appId) const db = new CouchDB(appId)
await db.put({ await db.put({
_id: "_design/database", _id: "_design/database",
@ -114,7 +120,20 @@ async function createInstance(template) {
} }
exports.fetch = async function (ctx) { exports.fetch = async function (ctx) {
ctx.body = await getAllApps() let apps = await getAllApps()
const appStatus = ctx.query.status
if (appStatus) {
apps = apps.filter(app => {
if (appStatus === AppStatus.DEV) {
return app._id.startsWith(DocumentTypes.APP_DEV)
}
return !app._id.startsWith(DocumentTypes.APP_DEV)
})
}
ctx.body = apps
} }
exports.fetchAppDefinition = async function (ctx) { exports.fetchAppDefinition = async function (ctx) {

View File

@ -1,5 +1,6 @@
const PouchDB = require("../../../db") const PouchDB = require("../../../db")
const Deployment = require("./Deployment") const Deployment = require("./Deployment")
const { Replication } = require("@budibase/auth").db
// the max time we can wait for an invalidation to complete before considering it failed // the max time we can wait for an invalidation to complete before considering it failed
const MAX_PENDING_TIME_MS = 30 * 60000 const MAX_PENDING_TIME_MS = 30 * 60000
const DeploymentStatus = { const DeploymentStatus = {
@ -56,7 +57,24 @@ async function storeLocalDeploymentHistory(deployment) {
async function deployApp(deployment) { async function deployApp(deployment) {
try { try {
// TODO: DB replication was here but wasn't accurate to new system const deployTarget = deployment.appId.replace("_dev", "")
const replication = new Replication({
source: deployment.appId,
target: deployTarget,
})
await replication.replicate()
// Strip the _dev prefix and update the appID document in the new DB
const db = new PouchDB(deployTarget)
const appDoc = await db.get(deployment.appId)
await db.remove(appDoc)
appDoc._id = deployTarget
delete appDoc._rev
appDoc.instance._id = deployTarget
await db.put(appDoc)
deployment.setStatus(DeploymentStatus.SUCCESS) deployment.setStatus(DeploymentStatus.SUCCESS)
await storeLocalDeploymentHistory(deployment) await storeLocalDeploymentHistory(deployment)
} catch (err) { } catch (err) {

View File

@ -37,7 +37,7 @@ router
}) })
) )
.use(currentApp) .use(currentApp)
.use(development) // .use(development)
// error handling middleware // error handling middleware
router.use(async (ctx, next) => { router.use(async (ctx, next) => {

View File

@ -10,6 +10,11 @@ const StaticDatabases = {
}, },
} }
const AppStatus = {
DEV: "dev",
DEPLOYED: "deployed",
}
const DocumentTypes = { const DocumentTypes = {
TABLE: "ta", TABLE: "ta",
ROW: "ro", ROW: "ro",
@ -49,6 +54,7 @@ exports.DocumentTypes = DocumentTypes
exports.SEPARATOR = SEPARATOR exports.SEPARATOR = SEPARATOR
exports.UNICODE_MAX = UNICODE_MAX exports.UNICODE_MAX = UNICODE_MAX
exports.SearchIndexes = SearchIndexes exports.SearchIndexes = SearchIndexes
exports.AppStatus = AppStatus
exports.getQueryIndex = viewName => { exports.getQueryIndex = viewName => {
return `database/${viewName}` return `database/${viewName}`

File diff suppressed because it is too large Load Diff

View File

@ -226,7 +226,7 @@ svelte-hmr@^0.13.3:
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.13.3.tgz#fba5739b477ea44caf70e542a24a4352bee2b897" resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.13.3.tgz#fba5739b477ea44caf70e542a24a4352bee2b897"
integrity sha512-gagW62pLQ2lULmvNA3pIZu9pBCYOaGu3rQikUOv6Nokz5VxUgT9/mQLfMxj9phDEKHCg/lgr3i6PkqZDbO9P2Q== integrity sha512-gagW62pLQ2lULmvNA3pIZu9pBCYOaGu3rQikUOv6Nokz5VxUgT9/mQLfMxj9phDEKHCg/lgr3i6PkqZDbO9P2Q==
svelte@^3.37.0: svelte@^3.38.2:
version "3.38.2" version "3.38.2"
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5"
integrity sha512-q5Dq0/QHh4BLJyEVWGe7Cej5NWs040LWjMbicBGZ+3qpFWJ1YObRmUDZKbbovddLC9WW7THTj3kYbTOFmU9fbg== integrity sha512-q5Dq0/QHh4BLJyEVWGe7Cej5NWs040LWjMbicBGZ+3qpFWJ1YObRmUDZKbbovddLC9WW7THTj3kYbTOFmU9fbg==