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 { backendUiStore, store } from "builderStore"
|
||||
import { findAllMatchingComponents, findComponentPath } from "./storeUtils"
|
||||
import { TableNames } from "../constants"
|
||||
|
||||
// Regex to match all instances of template strings
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import API from "./api"
|
||||
import { enrichRows } from "./rows"
|
||||
import { TableNames } from "../constants"
|
||||
|
||||
/**
|
||||
* Performs a log in request.
|
||||
|
@ -15,3 +17,15 @@ export const logIn = async ({ 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 NotificationDisplay from "./NotificationDisplay.svelte"
|
||||
import SDK from "../sdk"
|
||||
import {
|
||||
createDataStore,
|
||||
initialise,
|
||||
screenStore,
|
||||
notificationStore,
|
||||
} from "../store"
|
||||
import { createDataStore, initialise, screenStore, authStore } from "../store"
|
||||
|
||||
// Provide contexts
|
||||
setContext("sdk", SDK)
|
||||
|
@ -22,6 +17,7 @@
|
|||
// Load app config
|
||||
onMount(async () => {
|
||||
await initialise()
|
||||
await authStore.actions.fetchUser()
|
||||
loaded = true
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import * as ComponentLibrary from "@budibase/standard-components"
|
||||
import Router from "./Router.svelte"
|
||||
import { enrichProps, propsAreSame } from "../utils/componentProps"
|
||||
import { bindingStore, builderStore } from "../store"
|
||||
import { authStore, bindingStore, builderStore } from "../store"
|
||||
|
||||
export let definition = {}
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
|||
$: constructor = getComponentConstructor(definition._component)
|
||||
$: children = definition._children || []
|
||||
$: id = definition._id
|
||||
$: enrichComponentProps(definition, $dataContext, $bindingStore)
|
||||
$: enrichComponentProps(definition, $dataContext, $bindingStore, $authStore)
|
||||
$: updateProps(enrichedProps)
|
||||
$: styles = definition._styles
|
||||
|
||||
|
@ -67,8 +67,8 @@
|
|||
}
|
||||
|
||||
// Enriches any string component props using handlebars
|
||||
const enrichComponentProps = async (definition, context, bindingStore) => {
|
||||
enrichedProps = await enrichProps(definition, context, bindingStore)
|
||||
const enrichComponentProps = async (definition, context, bindings, user) => {
|
||||
enrichedProps = await enrichProps(definition, context, bindings, user)
|
||||
}
|
||||
|
||||
// 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 { routeStore } from "./routes"
|
||||
import { builderStore } from "./builder"
|
||||
import { TableNames } from "../constants"
|
||||
|
||||
const createAuthStore = () => {
|
||||
const store = writable("")
|
||||
const store = writable(null)
|
||||
|
||||
const goToDefaultRoute = () => {
|
||||
// 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
|
||||
routeStore.actions.navigate("/")
|
||||
}
|
||||
|
||||
// Logs a user in
|
||||
const logIn = async ({ email, password }) => {
|
||||
const user = await API.logIn({ email, password })
|
||||
if (!user.error) {
|
||||
store.set(user.token)
|
||||
store.set(user)
|
||||
await initialise()
|
||||
goToDefaultRoute()
|
||||
}
|
||||
}
|
||||
|
||||
// Logs a user out
|
||||
const logOut = async () => {
|
||||
store.set("")
|
||||
store.set(null)
|
||||
const appId = get(builderStore).appId
|
||||
if (appId) {
|
||||
for (let environment of ["local", "cloud"]) {
|
||||
|
@ -35,9 +40,26 @@ const createAuthStore = () => {
|
|||
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 {
|
||||
subscribe: store.subscribe,
|
||||
actions: { logIn, logOut },
|
||||
actions: { logIn, logOut, fetchUser },
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ export const propsAreSame = (a, b) => {
|
|||
* Enriches component props.
|
||||
* 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
|
||||
let validProps = {}
|
||||
Object.entries(props)
|
||||
|
@ -35,6 +35,7 @@ export const enrichProps = async (props, dataContexts, dataBindings) => {
|
|||
const context = {
|
||||
...dataContexts,
|
||||
...dataBindings,
|
||||
user,
|
||||
data: dataContexts[dataContexts.closestComponentId],
|
||||
data_draft: dataContexts[`${dataContexts.closestComponentId}_draft`],
|
||||
}
|
||||
|
|
|
@ -57,3 +57,17 @@ exports.authenticate = async ctx => {
|
|||
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")
|
||||
console.log(appHbs)
|
||||
ctx.body = await processString(appHbs, {
|
||||
head,
|
||||
body: html,
|
||||
|
|
|
@ -4,5 +4,6 @@ const controller = require("../controllers/auth")
|
|||
const router = Router()
|
||||
|
||||
router.post("/api/authenticate", controller.authenticate)
|
||||
router.get("/api/self", controller.fetchSelf)
|
||||
|
||||
module.exports = router
|
||||
|
|
Loading…
Reference in New Issue