Merge remote-tracking branch 'origin/master' into feature/screen-deselect

This commit is contained in:
Dean 2024-04-03 09:05:12 +01:00
commit f4fc8e5e97
9 changed files with 46 additions and 13 deletions

View File

@ -1,5 +1,5 @@
{ {
"version": "2.22.13", "version": "2.22.14",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*", "packages/*",

@ -1 +1 @@
Subproject commit 63ce32bca871f0a752323f5f7ebb5ec16bbbacc3 Subproject commit 360ad2dc29c3f1fd5a1182ae258c45666b7f5eb1

View File

@ -14,16 +14,16 @@ import {
} from "../db" } from "../db"
import { import {
BulkDocsResponse, BulkDocsResponse,
ContextUser,
CouchFindOptions,
DatabaseQueryOpts,
SearchQuery, SearchQuery,
SearchQueryOperators, SearchQueryOperators,
SearchUsersRequest, SearchUsersRequest,
User, User,
ContextUser,
DatabaseQueryOpts,
CouchFindOptions,
} from "@budibase/types" } from "@budibase/types"
import { getGlobalDB } from "../context"
import * as context from "../context" import * as context from "../context"
import { getGlobalDB } from "../context"
import { isCreator } from "./utils" import { isCreator } from "./utils"
import { UserDB } from "./db" import { UserDB } from "./db"
@ -48,6 +48,7 @@ export function isSupportedUserSearch(query: SearchQuery) {
const allowed = [ const allowed = [
{ op: SearchQueryOperators.STRING, key: "email" }, { op: SearchQueryOperators.STRING, key: "email" },
{ op: SearchQueryOperators.EQUAL, key: "_id" }, { op: SearchQueryOperators.EQUAL, key: "_id" },
{ op: SearchQueryOperators.ONE_OF, key: "_id" },
] ]
for (let [key, operation] of Object.entries(query)) { for (let [key, operation] of Object.entries(query)) {
if (typeof operation !== "object") { if (typeof operation !== "object") {
@ -285,6 +286,10 @@ export async function paginatedUsers({
} else if (query?.string?.email) { } else if (query?.string?.email) {
userList = await searchGlobalUsersByEmail(query?.string?.email, opts) userList = await searchGlobalUsersByEmail(query?.string?.email, opts)
property = "email" property = "email"
} else if (query?.oneOf?._id) {
userList = await bulkGetGlobalUsersById(query?.oneOf?._id, {
cleanup: true,
})
} else { } else {
// no search, query allDocs // no search, query allDocs
const response = await db.allDocs(getGlobalUserParams(null, opts)) const response = await db.allDocs(getGlobalUserParams(null, opts))

View File

@ -31,7 +31,7 @@
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte" import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte" import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte"
import BindingSidePanel from "components/common/bindings/BindingSidePanel.svelte" import BindingSidePanel from "components/common/bindings/BindingSidePanel.svelte"
import { BindingHelpers } from "components/common/bindings/utils" import { BindingHelpers, BindingType } from "components/common/bindings/utils"
import { import {
bindingsToCompletions, bindingsToCompletions,
hbAutocomplete, hbAutocomplete,
@ -576,6 +576,7 @@
{ {
js: true, js: true,
dontDecode: true, dontDecode: true,
type: BindingType.RUNTIME,
} }
)} )}
mode="javascript" mode="javascript"

View File

@ -5,7 +5,7 @@
import { licensing } from "stores/portal" import { licensing } from "stores/portal"
import { isPremiumOrAbove } from "helpers/planTitle" import { isPremiumOrAbove } from "helpers/planTitle"
$: premiumOrAboveLicense = isPremiumOrAbove($licensing?.license.plan.type) $: premiumOrAboveLicense = isPremiumOrAbove($licensing?.license?.plan?.type)
let show let show
let hide let hide

View File

@ -1,6 +1,11 @@
import { decodeJSBinding } from "@budibase/string-templates" import { decodeJSBinding } from "@budibase/string-templates"
import { hbInsert, jsInsert } from "components/common/CodeEditor" import { hbInsert, jsInsert } from "components/common/CodeEditor"
export const BindingType = {
READABLE: "readableBinding",
RUNTIME: "runtimeBinding",
}
export class BindingHelpers { export class BindingHelpers {
constructor(getCaretPosition, insertAtPos, { disableWrapping } = {}) { constructor(getCaretPosition, insertAtPos, { disableWrapping } = {}) {
this.getCaretPosition = getCaretPosition this.getCaretPosition = getCaretPosition
@ -25,16 +30,20 @@ export class BindingHelpers {
} }
// Adds a data binding to the expression // Adds a data binding to the expression
onSelectBinding(value, binding, { js, dontDecode }) { onSelectBinding(
value,
binding,
{ js, dontDecode, type = BindingType.READABLE }
) {
const { start, end } = this.getCaretPosition() const { start, end } = this.getCaretPosition()
if (js) { if (js) {
const jsVal = dontDecode ? value : decodeJSBinding(value) const jsVal = dontDecode ? value : decodeJSBinding(value)
const insertVal = jsInsert(jsVal, start, end, binding.readableBinding, { const insertVal = jsInsert(jsVal, start, end, binding[type], {
disableWrapping: this.disableWrapping, disableWrapping: this.disableWrapping,
}) })
this.insertAtPos({ start, end, value: insertVal }) this.insertAtPos({ start, end, value: insertVal })
} else { } else {
const insertVal = hbInsert(value, start, end, binding.readableBinding) const insertVal = hbInsert(value, start, end, binding[type])
this.insertAtPos({ start, end, value: insertVal }) this.insertAtPos({ start, end, value: insertVal })
} }
} }

View File

@ -148,7 +148,7 @@ export const enrichedApps = derived([appsStore, auth], ([$store, $auth]) => {
deployed: app.status === AppStatus.DEPLOYED, deployed: app.status === AppStatus.DEPLOYED,
lockedYou: app.lockedBy && app.lockedBy.email === $auth.user?.email, lockedYou: app.lockedBy && app.lockedBy.email === $auth.user?.email,
lockedOther: app.lockedBy && app.lockedBy.email !== $auth.user?.email, lockedOther: app.lockedBy && app.lockedBy.email !== $auth.user?.email,
favourite: $auth?.user.appFavourites?.includes(app.appId), favourite: $auth.user?.appFavourites?.includes(app.appId),
})) }))
: [] : []

View File

@ -229,7 +229,7 @@ export const search = async (ctx: Ctx<SearchUsersRequest>) => {
} }
// Validate we aren't trying to search on any illegal fields // Validate we aren't trying to search on any illegal fields
if (!userSdk.core.isSupportedUserSearch(body.query)) { if (!userSdk.core.isSupportedUserSearch(body.query)) {
ctx.throw(400, "Can only search by string.email or equal._id") ctx.throw(400, "Can only search by string.email, equal._id or oneOf._id")
} }
} }

View File

@ -649,6 +649,24 @@ describe("/api/global/users", () => {
expect(response.body.data[0]._id).toBe(user._id) expect(response.body.data[0]._id).toBe(user._id)
}) })
it("should be able to search by oneOf _id", async () => {
const [user, user2, user3] = await Promise.all([
config.createUser(),
config.createUser(),
config.createUser(),
])
const response = await config.api.users.searchUsers({
query: { oneOf: { _id: [user._id, user2._id] } },
})
expect(response.body.data.length).toBe(2)
const foundUserIds = response.body.data.map((user: User) => user._id)
expect(foundUserIds).toContain(user._id)
expect(foundUserIds).toContain(user2._id)
expect(
response.body.data.find((user: User) => user._id === user3._id)
).toBeUndefined()
})
it("should be able to search by _id with numeric prefixing", async () => { it("should be able to search by _id with numeric prefixing", async () => {
const user = await config.createUser() const user = await config.createUser()
const response = await config.api.users.searchUsers({ const response = await config.api.users.searchUsers({