Merge pull request #1039 from Budibase/feature/current-user-datasource
Add current user relationships as a data source, and current user bindings
This commit is contained in:
commit
ea1ca745d1
|
@ -2,6 +2,7 @@ import { cloneDeep } from "lodash/fp"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { backendUiStore, store } from "builderStore"
|
import { backendUiStore, store } from "builderStore"
|
||||||
import { findAllMatchingComponents, findComponentPath } from "./storeUtils"
|
import { findAllMatchingComponents, findComponentPath } from "./storeUtils"
|
||||||
|
import { TableNames } from "../constants"
|
||||||
|
|
||||||
// Regex to match all instances of template strings
|
// Regex to match all instances of template strings
|
||||||
const CAPTURE_VAR_INSIDE_TEMPLATE = /{{([^}]+)}}/g
|
const CAPTURE_VAR_INSIDE_TEMPLATE = /{{([^}]+)}}/g
|
||||||
|
@ -114,6 +115,37 @@ export const getContextBindings = (rootComponent, componentId) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Add logged in user bindings
|
||||||
|
const tables = get(backendUiStore).tables
|
||||||
|
const userTable = tables.find(table => table._id === TableNames.USERS)
|
||||||
|
const schema = {
|
||||||
|
...userTable.schema,
|
||||||
|
_id: { type: "string" },
|
||||||
|
_rev: { type: "string" },
|
||||||
|
}
|
||||||
|
const keys = Object.keys(schema).sort()
|
||||||
|
keys.forEach(key => {
|
||||||
|
const fieldSchema = schema[key]
|
||||||
|
// Replace certain bindings with a new property to help display components
|
||||||
|
let runtimeBoundKey = key
|
||||||
|
if (fieldSchema.type === "link") {
|
||||||
|
runtimeBoundKey = `${key}_count`
|
||||||
|
} else if (fieldSchema.type === "attachment") {
|
||||||
|
runtimeBoundKey = `${key}_first`
|
||||||
|
}
|
||||||
|
|
||||||
|
contextBindings.push({
|
||||||
|
type: "context",
|
||||||
|
runtimeBinding: `user.${runtimeBoundKey}`,
|
||||||
|
readableBinding: `Current User.${key}`,
|
||||||
|
fieldSchema,
|
||||||
|
providerId: "user",
|
||||||
|
tableId: TableNames.USERS,
|
||||||
|
field: key,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return contextBindings
|
return contextBindings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import API from "./api"
|
import API from "./api"
|
||||||
|
import { enrichRows } from "./rows"
|
||||||
|
import { TableNames } from "../constants"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a log in request.
|
* Performs a log in request.
|
||||||
|
@ -15,3 +17,15 @@ export const logIn = async ({ email, password }) => {
|
||||||
body: { email, password },
|
body: { email, password },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the currently logged in user object
|
||||||
|
*/
|
||||||
|
export const fetchSelf = async () => {
|
||||||
|
const user = await API.get({ url: "/api/self" })
|
||||||
|
if (user?._id) {
|
||||||
|
return (await enrichRows([user], TableNames.USERS))[0]
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,12 +4,7 @@
|
||||||
import Component from "./Component.svelte"
|
import Component from "./Component.svelte"
|
||||||
import NotificationDisplay from "./NotificationDisplay.svelte"
|
import NotificationDisplay from "./NotificationDisplay.svelte"
|
||||||
import SDK from "../sdk"
|
import SDK from "../sdk"
|
||||||
import {
|
import { createDataStore, initialise, screenStore, authStore } from "../store"
|
||||||
createDataStore,
|
|
||||||
initialise,
|
|
||||||
screenStore,
|
|
||||||
notificationStore,
|
|
||||||
} from "../store"
|
|
||||||
|
|
||||||
// Provide contexts
|
// Provide contexts
|
||||||
setContext("sdk", SDK)
|
setContext("sdk", SDK)
|
||||||
|
@ -22,6 +17,7 @@
|
||||||
// Load app config
|
// Load app config
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await initialise()
|
await initialise()
|
||||||
|
await authStore.actions.fetchUser()
|
||||||
loaded = true
|
loaded = true
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import * as ComponentLibrary from "@budibase/standard-components"
|
import * as ComponentLibrary from "@budibase/standard-components"
|
||||||
import Router from "./Router.svelte"
|
import Router from "./Router.svelte"
|
||||||
import { enrichProps, propsAreSame } from "../utils/componentProps"
|
import { enrichProps, propsAreSame } from "../utils/componentProps"
|
||||||
import { bindingStore, builderStore } from "../store"
|
import { authStore, bindingStore, builderStore } from "../store"
|
||||||
|
|
||||||
export let definition = {}
|
export let definition = {}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
$: constructor = getComponentConstructor(definition._component)
|
$: constructor = getComponentConstructor(definition._component)
|
||||||
$: children = definition._children || []
|
$: children = definition._children || []
|
||||||
$: id = definition._id
|
$: id = definition._id
|
||||||
$: enrichComponentProps(definition, $dataContext, $bindingStore)
|
$: enrichComponentProps(definition, $dataContext, $bindingStore, $authStore)
|
||||||
$: updateProps(enrichedProps)
|
$: updateProps(enrichedProps)
|
||||||
$: styles = definition._styles
|
$: styles = definition._styles
|
||||||
|
|
||||||
|
@ -67,8 +67,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enriches any string component props using handlebars
|
// Enriches any string component props using handlebars
|
||||||
const enrichComponentProps = async (definition, context, bindingStore) => {
|
const enrichComponentProps = async (definition, context, bindings, user) => {
|
||||||
enrichedProps = await enrichProps(definition, context, bindingStore)
|
enrichedProps = await enrichProps(definition, context, bindings, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a unique key to let svelte know when to remount components.
|
// Returns a unique key to let svelte know when to remount components.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const TableNames = {
|
||||||
|
USERS: "ta_users",
|
||||||
|
}
|
|
@ -3,9 +3,10 @@ import { writable, get } from "svelte/store"
|
||||||
import { initialise } from "./initialise"
|
import { initialise } from "./initialise"
|
||||||
import { routeStore } from "./routes"
|
import { routeStore } from "./routes"
|
||||||
import { builderStore } from "./builder"
|
import { builderStore } from "./builder"
|
||||||
|
import { TableNames } from "../constants"
|
||||||
|
|
||||||
const createAuthStore = () => {
|
const createAuthStore = () => {
|
||||||
const store = writable("")
|
const store = writable(null)
|
||||||
|
|
||||||
const goToDefaultRoute = () => {
|
const goToDefaultRoute = () => {
|
||||||
// Setting the active route forces an update of the active screen ID,
|
// Setting the active route forces an update of the active screen ID,
|
||||||
|
@ -15,16 +16,20 @@ const createAuthStore = () => {
|
||||||
// Navigating updates the URL to reflect this route
|
// Navigating updates the URL to reflect this route
|
||||||
routeStore.actions.navigate("/")
|
routeStore.actions.navigate("/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs a user in
|
||||||
const logIn = async ({ email, password }) => {
|
const logIn = async ({ email, password }) => {
|
||||||
const user = await API.logIn({ email, password })
|
const user = await API.logIn({ email, password })
|
||||||
if (!user.error) {
|
if (!user.error) {
|
||||||
store.set(user.token)
|
store.set(user)
|
||||||
await initialise()
|
await initialise()
|
||||||
goToDefaultRoute()
|
goToDefaultRoute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs a user out
|
||||||
const logOut = async () => {
|
const logOut = async () => {
|
||||||
store.set("")
|
store.set(null)
|
||||||
const appId = get(builderStore).appId
|
const appId = get(builderStore).appId
|
||||||
if (appId) {
|
if (appId) {
|
||||||
for (let environment of ["local", "cloud"]) {
|
for (let environment of ["local", "cloud"]) {
|
||||||
|
@ -35,9 +40,26 @@ const createAuthStore = () => {
|
||||||
goToDefaultRoute()
|
goToDefaultRoute()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetches the user object if someone is logged in and has reloaded the page
|
||||||
|
const fetchUser = async () => {
|
||||||
|
// Fetch the first user if inside the builder
|
||||||
|
if (get(builderStore).inBuilder) {
|
||||||
|
const users = await API.fetchTableData(TableNames.USERS)
|
||||||
|
if (!users.error && users[0] != null) {
|
||||||
|
store.set(users[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or fetch the current user from localstorage in a real app
|
||||||
|
else {
|
||||||
|
const user = await API.fetchSelf()
|
||||||
|
store.set(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe: store.subscribe,
|
subscribe: store.subscribe,
|
||||||
actions: { logIn, logOut },
|
actions: { logIn, logOut, fetchUser },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const propsAreSame = (a, b) => {
|
||||||
* Enriches component props.
|
* Enriches component props.
|
||||||
* Data bindings are enriched, and button actions are enriched.
|
* Data bindings are enriched, and button actions are enriched.
|
||||||
*/
|
*/
|
||||||
export const enrichProps = async (props, dataContexts, dataBindings) => {
|
export const enrichProps = async (props, dataContexts, dataBindings, user) => {
|
||||||
// Exclude all private props that start with an underscore
|
// Exclude all private props that start with an underscore
|
||||||
let validProps = {}
|
let validProps = {}
|
||||||
Object.entries(props)
|
Object.entries(props)
|
||||||
|
@ -35,6 +35,7 @@ export const enrichProps = async (props, dataContexts, dataBindings) => {
|
||||||
const context = {
|
const context = {
|
||||||
...dataContexts,
|
...dataContexts,
|
||||||
...dataBindings,
|
...dataBindings,
|
||||||
|
user,
|
||||||
data: dataContexts[dataContexts.closestComponentId],
|
data: dataContexts[dataContexts.closestComponentId],
|
||||||
data_draft: dataContexts[`${dataContexts.closestComponentId}_draft`],
|
data_draft: dataContexts[`${dataContexts.closestComponentId}_draft`],
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,3 +57,17 @@ exports.authenticate = async ctx => {
|
||||||
ctx.throw(401, "Invalid credentials.")
|
ctx.throw(401, "Invalid credentials.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.fetchSelf = async ctx => {
|
||||||
|
const { userId, appId } = ctx.user
|
||||||
|
if (!userId || !appId) {
|
||||||
|
ctx.body = {}
|
||||||
|
} else {
|
||||||
|
const database = new CouchDB(appId)
|
||||||
|
const user = await database.get(userId)
|
||||||
|
if (user) {
|
||||||
|
delete user.password
|
||||||
|
}
|
||||||
|
ctx.body = user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -177,7 +177,6 @@ exports.serveApp = async function(ctx) {
|
||||||
})
|
})
|
||||||
|
|
||||||
const appHbs = fs.readFileSync(`${__dirname}/templates/app.hbs`, "utf8")
|
const appHbs = fs.readFileSync(`${__dirname}/templates/app.hbs`, "utf8")
|
||||||
console.log(appHbs)
|
|
||||||
ctx.body = await processString(appHbs, {
|
ctx.body = await processString(appHbs, {
|
||||||
head,
|
head,
|
||||||
body: html,
|
body: html,
|
||||||
|
|
|
@ -4,5 +4,6 @@ const controller = require("../controllers/auth")
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router.post("/api/authenticate", controller.authenticate)
|
router.post("/api/authenticate", controller.authenticate)
|
||||||
|
router.get("/api/self", controller.fetchSelf)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
Loading…
Reference in New Issue