Updates to improve the view development experience.
This commit is contained in:
parent
b56397c2e1
commit
49fc65b584
|
@ -20,6 +20,7 @@ export enum ViewName {
|
||||||
AUTOMATION_LOGS = "automation_logs",
|
AUTOMATION_LOGS = "automation_logs",
|
||||||
ACCOUNT_BY_EMAIL = "account_by_email",
|
ACCOUNT_BY_EMAIL = "account_by_email",
|
||||||
PLATFORM_USERS_LOWERCASE = "platform_users_lowercase",
|
PLATFORM_USERS_LOWERCASE = "platform_users_lowercase",
|
||||||
|
USER_BY_GROUP = "by_group_user",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeprecatedViews = {
|
export const DeprecatedViews = {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { DEFAULT_TENANT_ID, Configs } from "../constants"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { SEPARATOR, DocumentType, UNICODE_MAX, ViewName } from "./constants"
|
import { SEPARATOR, DocumentType, UNICODE_MAX, ViewName } from "./constants"
|
||||||
import { getTenantId, getGlobalDB } from "../context"
|
import { getTenantId, getGlobalDB } from "../context"
|
||||||
import { getGlobalDBName } from "../tenancy/utils"
|
import { getGlobalDBName } from "../tenancy"
|
||||||
import fetch from "node-fetch"
|
import fetch from "node-fetch"
|
||||||
import { doWithDB, allDbs } from "./index"
|
import { doWithDB, allDbs } from "./index"
|
||||||
import { getCouchInfo } from "./pouch"
|
import { getCouchInfo } from "./pouch"
|
||||||
|
|
|
@ -36,154 +36,91 @@ async function removeDeprecated(db: PouchDB.Database, viewName: ViewName) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createNewUserEmailView = async () => {
|
export async function createView(db: any, viewJs: string, viewName: string) {
|
||||||
const db = getGlobalDB()
|
|
||||||
let designDoc
|
let designDoc
|
||||||
try {
|
try {
|
||||||
designDoc = await db.get(DESIGN_DB)
|
designDoc = (await db.get(DESIGN_DB)) as DesignDocument
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// no design doc, make one
|
// no design doc, make one
|
||||||
designDoc = DesignDoc()
|
designDoc = DesignDoc()
|
||||||
}
|
}
|
||||||
const view = {
|
const view = {
|
||||||
// if using variables in a map function need to inject them before use
|
map: viewJs,
|
||||||
map: `function(doc) {
|
|
||||||
if (doc._id.startsWith("${DocumentType.USER}${SEPARATOR}")) {
|
|
||||||
emit(doc.email.toLowerCase(), doc._id)
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
}
|
}
|
||||||
designDoc.views = {
|
designDoc.views = {
|
||||||
...designDoc.views,
|
...designDoc.views,
|
||||||
[ViewName.USER_BY_EMAIL]: view,
|
[viewName]: view,
|
||||||
}
|
}
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createNewUserEmailView = async () => {
|
||||||
|
const db = getGlobalDB()
|
||||||
|
const viewJs = `function(doc) {
|
||||||
|
if (doc._id.startsWith("${DocumentType.USER}${SEPARATOR}")) {
|
||||||
|
emit(doc.email.toLowerCase(), doc._id)
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
await createView(db, viewJs, ViewName.USER_BY_EMAIL)
|
||||||
|
}
|
||||||
|
|
||||||
export const createAccountEmailView = async () => {
|
export const createAccountEmailView = async () => {
|
||||||
|
const viewJs = `function(doc) {
|
||||||
|
if (doc._id.startsWith("${DocumentType.ACCOUNT_METADATA}${SEPARATOR}")) {
|
||||||
|
emit(doc.email.toLowerCase(), doc._id)
|
||||||
|
}
|
||||||
|
}`
|
||||||
await doWithDB(
|
await doWithDB(
|
||||||
StaticDatabases.PLATFORM_INFO.name,
|
StaticDatabases.PLATFORM_INFO.name,
|
||||||
async (db: PouchDB.Database) => {
|
async (db: PouchDB.Database) => {
|
||||||
let designDoc
|
await createView(db, viewJs, ViewName.ACCOUNT_BY_EMAIL)
|
||||||
try {
|
|
||||||
designDoc = await db.get<DesignDocument>(DESIGN_DB)
|
|
||||||
} catch (err) {
|
|
||||||
// no design doc, make one
|
|
||||||
designDoc = DesignDoc()
|
|
||||||
}
|
|
||||||
const view = {
|
|
||||||
// if using variables in a map function need to inject them before use
|
|
||||||
map: `function(doc) {
|
|
||||||
if (doc._id.startsWith("${DocumentType.ACCOUNT_METADATA}${SEPARATOR}")) {
|
|
||||||
emit(doc.email.toLowerCase(), doc._id)
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
}
|
|
||||||
designDoc.views = {
|
|
||||||
...designDoc.views,
|
|
||||||
[ViewName.ACCOUNT_BY_EMAIL]: view,
|
|
||||||
}
|
|
||||||
await db.put(designDoc)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createUserAppView = async () => {
|
export const createUserAppView = async () => {
|
||||||
const db = getGlobalDB() as PouchDB.Database
|
const db = getGlobalDB() as PouchDB.Database
|
||||||
let designDoc
|
const viewJs = `function(doc) {
|
||||||
try {
|
if (doc._id.startsWith("${DocumentType.USER}${SEPARATOR}") && doc.roles) {
|
||||||
designDoc = await db.get<DesignDocument>("_design/database")
|
for (let prodAppId of Object.keys(doc.roles)) {
|
||||||
} catch (err) {
|
let emitted = prodAppId + "${SEPARATOR}" + doc._id
|
||||||
// no design doc, make one
|
emit(emitted, null)
|
||||||
designDoc = DesignDoc()
|
|
||||||
}
|
|
||||||
const view = {
|
|
||||||
// if using variables in a map function need to inject them before use
|
|
||||||
map: `function(doc) {
|
|
||||||
if (doc._id.startsWith("${DocumentType.USER}${SEPARATOR}") && doc.roles) {
|
|
||||||
for (let prodAppId of Object.keys(doc.roles)) {
|
|
||||||
let emitted = prodAppId + "${SEPARATOR}" + doc._id
|
|
||||||
emit(emitted, null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}`,
|
}
|
||||||
}
|
}`
|
||||||
designDoc.views = {
|
await createView(db, viewJs, ViewName.USER_BY_APP)
|
||||||
...designDoc.views,
|
|
||||||
[ViewName.USER_BY_APP]: view,
|
|
||||||
}
|
|
||||||
await db.put(designDoc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createApiKeyView = async () => {
|
export const createApiKeyView = async () => {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
let designDoc
|
const viewJs = `function(doc) {
|
||||||
try {
|
if (doc._id.startsWith("${DocumentType.DEV_INFO}") && doc.apiKey) {
|
||||||
designDoc = await db.get("_design/database")
|
emit(doc.apiKey, doc.userId)
|
||||||
} catch (err) {
|
}
|
||||||
designDoc = DesignDoc()
|
}`
|
||||||
}
|
await createView(db, viewJs, ViewName.BY_API_KEY)
|
||||||
const view = {
|
|
||||||
map: `function(doc) {
|
|
||||||
if (doc._id.startsWith("${DocumentType.DEV_INFO}") && doc.apiKey) {
|
|
||||||
emit(doc.apiKey, doc.userId)
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
}
|
|
||||||
designDoc.views = {
|
|
||||||
...designDoc.views,
|
|
||||||
[ViewName.BY_API_KEY]: view,
|
|
||||||
}
|
|
||||||
await db.put(designDoc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createUserBuildersView = async () => {
|
export const createUserBuildersView = async () => {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
let designDoc
|
const viewJs = `function(doc) {
|
||||||
try {
|
if (doc.builder && doc.builder.global === true) {
|
||||||
designDoc = await db.get("_design/database")
|
emit(doc._id, doc._id)
|
||||||
} catch (err) {
|
}
|
||||||
// no design doc, make one
|
}`
|
||||||
designDoc = DesignDoc()
|
await createView(db, viewJs, ViewName.USER_BY_BUILDERS)
|
||||||
}
|
|
||||||
const view = {
|
|
||||||
map: `function(doc) {
|
|
||||||
if (doc.builder && doc.builder.global === true) {
|
|
||||||
emit(doc._id, doc._id)
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
}
|
|
||||||
designDoc.views = {
|
|
||||||
...designDoc.views,
|
|
||||||
[ViewName.USER_BY_BUILDERS]: view,
|
|
||||||
}
|
|
||||||
await db.put(designDoc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createPlatformUserView = async () => {
|
export const createPlatformUserView = async () => {
|
||||||
|
const viewJs = `function(doc) {
|
||||||
|
if (doc.tenantId) {
|
||||||
|
emit(doc._id.toLowerCase(), doc._id)
|
||||||
|
}
|
||||||
|
}`
|
||||||
await doWithDB(
|
await doWithDB(
|
||||||
StaticDatabases.PLATFORM_INFO.name,
|
StaticDatabases.PLATFORM_INFO.name,
|
||||||
async (db: PouchDB.Database) => {
|
async (db: PouchDB.Database) => {
|
||||||
let designDoc
|
await createView(db, viewJs, ViewName.PLATFORM_USERS_LOWERCASE)
|
||||||
try {
|
|
||||||
designDoc = await db.get<DesignDocument>(DESIGN_DB)
|
|
||||||
} catch (err) {
|
|
||||||
// no design doc, make one
|
|
||||||
designDoc = DesignDoc()
|
|
||||||
}
|
|
||||||
const view = {
|
|
||||||
// if using variables in a map function need to inject them before use
|
|
||||||
map: `function(doc) {
|
|
||||||
if (doc.tenantId) {
|
|
||||||
emit(doc._id.toLowerCase(), doc._id)
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
}
|
|
||||||
designDoc.views = {
|
|
||||||
...designDoc.views,
|
|
||||||
[ViewName.PLATFORM_USERS_LOWERCASE]: view,
|
|
||||||
}
|
|
||||||
await db.put(designDoc)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -196,7 +133,7 @@ export const queryView = async <T>(
|
||||||
viewName: ViewName,
|
viewName: ViewName,
|
||||||
params: PouchDB.Query.Options<T, T>,
|
params: PouchDB.Query.Options<T, T>,
|
||||||
db: PouchDB.Database,
|
db: PouchDB.Database,
|
||||||
CreateFuncByName: any,
|
createFunc: any,
|
||||||
opts?: QueryViewOptions
|
opts?: QueryViewOptions
|
||||||
): Promise<T[] | T | undefined> => {
|
): Promise<T[] | T | undefined> => {
|
||||||
try {
|
try {
|
||||||
|
@ -213,10 +150,9 @@ export const queryView = async <T>(
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err != null && err.name === "not_found") {
|
if (err != null && err.name === "not_found") {
|
||||||
const createFunc = CreateFuncByName[viewName]
|
|
||||||
await removeDeprecated(db, viewName)
|
await removeDeprecated(db, viewName)
|
||||||
await createFunc()
|
await createFunc()
|
||||||
return queryView(viewName, params, db, CreateFuncByName, opts)
|
return queryView(viewName, params, db, createFunc, opts)
|
||||||
} else {
|
} else {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
@ -228,7 +164,7 @@ export const queryPlatformView = async <T>(
|
||||||
params: PouchDB.Query.Options<T, T>,
|
params: PouchDB.Query.Options<T, T>,
|
||||||
opts?: QueryViewOptions
|
opts?: QueryViewOptions
|
||||||
): Promise<T[] | T | undefined> => {
|
): Promise<T[] | T | undefined> => {
|
||||||
const CreateFuncByName = {
|
const CreateFuncByName: any = {
|
||||||
[ViewName.ACCOUNT_BY_EMAIL]: createAccountEmailView,
|
[ViewName.ACCOUNT_BY_EMAIL]: createAccountEmailView,
|
||||||
[ViewName.PLATFORM_USERS_LOWERCASE]: createPlatformUserView,
|
[ViewName.PLATFORM_USERS_LOWERCASE]: createPlatformUserView,
|
||||||
}
|
}
|
||||||
|
@ -236,7 +172,8 @@ export const queryPlatformView = async <T>(
|
||||||
return doWithDB(
|
return doWithDB(
|
||||||
StaticDatabases.PLATFORM_INFO.name,
|
StaticDatabases.PLATFORM_INFO.name,
|
||||||
async (db: PouchDB.Database) => {
|
async (db: PouchDB.Database) => {
|
||||||
return queryView(viewName, params, db, CreateFuncByName, opts)
|
const createFn = CreateFuncByName[viewName]
|
||||||
|
return queryView(viewName, params, db, createFn, opts)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -247,7 +184,7 @@ export const queryGlobalView = async <T>(
|
||||||
db?: PouchDB.Database,
|
db?: PouchDB.Database,
|
||||||
opts?: QueryViewOptions
|
opts?: QueryViewOptions
|
||||||
): Promise<T[] | T | undefined> => {
|
): Promise<T[] | T | undefined> => {
|
||||||
const CreateFuncByName = {
|
const CreateFuncByName: any = {
|
||||||
[ViewName.USER_BY_EMAIL]: createNewUserEmailView,
|
[ViewName.USER_BY_EMAIL]: createNewUserEmailView,
|
||||||
[ViewName.BY_API_KEY]: createApiKeyView,
|
[ViewName.BY_API_KEY]: createApiKeyView,
|
||||||
[ViewName.USER_BY_BUILDERS]: createUserBuildersView,
|
[ViewName.USER_BY_BUILDERS]: createUserBuildersView,
|
||||||
|
@ -257,5 +194,6 @@ export const queryGlobalView = async <T>(
|
||||||
if (!db) {
|
if (!db) {
|
||||||
db = getGlobalDB() as PouchDB.Database
|
db = getGlobalDB() as PouchDB.Database
|
||||||
}
|
}
|
||||||
return queryView(viewName, params, db, CreateFuncByName, opts)
|
const createFn = CreateFuncByName[viewName]
|
||||||
|
return queryView(viewName, params, db, createFn, opts)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
import { auth } from "../stores/portal"
|
import { auth } from "../stores/portal"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
|
|
||||||
export const FEATURE_FLAGS = {
|
export const TENANT_FEATURE_FLAGS = {
|
||||||
LICENSING: "LICENSING",
|
LICENSING: "LICENSING",
|
||||||
USER_GROUPS: "USER_GROUPS",
|
USER_GROUPS: "USER_GROUPS",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isEnabled = featureFlag => {
|
export const isEnabled = featureFlag => {
|
||||||
const user = get(auth).user
|
const user = get(auth).user
|
||||||
if (user?.featureFlags?.includes(featureFlag)) {
|
return !!user?.featureFlags?.includes(featureFlag)
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import ChangePasswordModal from "components/settings/ChangePasswordModal.svelte"
|
import ChangePasswordModal from "components/settings/ChangePasswordModal.svelte"
|
||||||
import UpdateAPIKeyModal from "components/settings/UpdateAPIKeyModal.svelte"
|
import UpdateAPIKeyModal from "components/settings/UpdateAPIKeyModal.svelte"
|
||||||
import Logo from "assets/bb-emblem.svg"
|
import Logo from "assets/bb-emblem.svg"
|
||||||
import { isEnabled, FEATURE_FLAGS } from "../../../helpers/featureFlags"
|
import { isEnabled, TENANT_FEATURE_FLAGS } from "helpers/featureFlags"
|
||||||
|
|
||||||
let loaded = false
|
let loaded = false
|
||||||
let userInfoModal
|
let userInfoModal
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
href: "/builder/portal/manage/users",
|
href: "/builder/portal/manage/users",
|
||||||
heading: "Manage",
|
heading: "Manage",
|
||||||
},
|
},
|
||||||
isEnabled(FEATURE_FLAGS.USER_GROUPS)
|
isEnabled(TENANT_FEATURE_FLAGS.USER_GROUPS)
|
||||||
? {
|
? {
|
||||||
title: "User Groups",
|
title: "User Groups",
|
||||||
href: "/builder/portal/manage/groups",
|
href: "/builder/portal/manage/groups",
|
||||||
|
@ -103,7 +103,7 @@
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEnabled(FEATURE_FLAGS.LICENSING)) {
|
if (isEnabled(TENANT_FEATURE_FLAGS.LICENSING)) {
|
||||||
// always show usage in self-host or cloud if licensing enabled
|
// always show usage in self-host or cloud if licensing enabled
|
||||||
menu = menu.concat([
|
menu = menu.concat([
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { derived, writable, get } from "svelte/store"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { admin } from "stores/portal"
|
import { admin } from "stores/portal"
|
||||||
import analytics from "analytics"
|
import analytics from "analytics"
|
||||||
import { FEATURE_FLAGS } from "helpers/featureFlags"
|
import { TENANT_FEATURE_FLAGS } from "helpers/featureFlags"
|
||||||
import { Constants } from "@budibase/frontend-core"
|
import { Constants } from "@budibase/frontend-core"
|
||||||
|
|
||||||
export function createAuthStore() {
|
export function createAuthStore() {
|
||||||
|
@ -35,7 +35,7 @@ export function createAuthStore() {
|
||||||
isBuilder = !!user.builder?.global
|
isBuilder = !!user.builder?.global
|
||||||
groupsEnabled =
|
groupsEnabled =
|
||||||
user?.license.features.includes(Constants.Features.USER_GROUPS) &&
|
user?.license.features.includes(Constants.Features.USER_GROUPS) &&
|
||||||
user?.featureFlags.includes(FEATURE_FLAGS.USER_GROUPS)
|
user?.featureFlags.includes(TENANT_FEATURE_FLAGS.USER_GROUPS)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
user: $store.user,
|
user: $store.user,
|
||||||
|
|
Loading…
Reference in New Issue