Merge branch 'master' of github.com:Budibase/budibase into develop
This commit is contained in:
commit
18b515f71b
|
@ -25,6 +25,13 @@ jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Maximize build space
|
||||||
|
uses: easimon/maximize-build-space@master
|
||||||
|
with:
|
||||||
|
root-reserve-mb: 35000
|
||||||
|
swap-size-mb: 1024
|
||||||
|
remove-android: 'true'
|
||||||
|
remove-dotnet: 'true'
|
||||||
- name: Checkout repo and submodules
|
- name: Checkout repo and submodules
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase'
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.10.6",
|
"version": "2.10.11",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -235,7 +235,7 @@
|
||||||
const baseExtensions = buildBaseExtensions()
|
const baseExtensions = buildBaseExtensions()
|
||||||
|
|
||||||
editor = new EditorView({
|
editor = new EditorView({
|
||||||
doc: value,
|
doc: value?.toString(),
|
||||||
extensions: buildExtensions(baseExtensions),
|
extensions: buildExtensions(baseExtensions),
|
||||||
parent: textarea,
|
parent: textarea,
|
||||||
})
|
})
|
||||||
|
|
|
@ -126,7 +126,7 @@
|
||||||
user,
|
user,
|
||||||
prodAppId
|
prodAppId
|
||||||
)
|
)
|
||||||
const isAppBuilder = sdk.users.hasAppBuilderPermissions(user, prodAppId)
|
const isAppBuilder = user.builder?.apps?.includes(prodAppId)
|
||||||
let role
|
let role
|
||||||
if (isAdminOrGlobalBuilder) {
|
if (isAdminOrGlobalBuilder) {
|
||||||
role = Constants.Roles.ADMIN
|
role = Constants.Roles.ADMIN
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return availableApps.map(app => {
|
return availableApps.map(app => {
|
||||||
const prodAppId = apps.getProdAppID(app.appId)
|
const prodAppId = apps.getProdAppID(app.devId)
|
||||||
return {
|
return {
|
||||||
name: app.name,
|
name: app.name,
|
||||||
devId: app.devId,
|
devId: app.devId,
|
||||||
|
|
|
@ -17,6 +17,13 @@
|
||||||
let fieldApi
|
let fieldApi
|
||||||
let localFiles = []
|
let localFiles = []
|
||||||
|
|
||||||
|
$: {
|
||||||
|
// If the field state is reset, clear the local files
|
||||||
|
if (!fieldState?.value?.length) {
|
||||||
|
localFiles = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { API, notificationStore, uploadStore } = getContext("sdk")
|
const { API, notificationStore, uploadStore } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
SELECT 'CREATE DATABASE main'
|
SELECT 'CREATE DATABASE main'
|
||||||
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'main')\gexec
|
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'main')\gexec
|
||||||
CREATE SCHEMA "test-1";
|
CREATE SCHEMA "test-1";
|
||||||
CREATE TYPE person_job AS ENUM ('qa', 'programmer', 'designer');
|
CREATE TYPE person_job AS ENUM ('qa', 'programmer', 'designer', 'support');
|
||||||
CREATE TABLE Persons (
|
CREATE TABLE Persons (
|
||||||
PersonID SERIAL PRIMARY KEY,
|
PersonID SERIAL PRIMARY KEY,
|
||||||
LastName varchar(255),
|
LastName varchar(255),
|
||||||
|
@ -51,6 +51,7 @@ CREATE TABLE CompositeTable (
|
||||||
);
|
);
|
||||||
INSERT INTO Persons (FirstName, LastName, Address, City, Type) VALUES ('Mike', 'Hughes', '123 Fake Street', 'Belfast', 'qa');
|
INSERT INTO Persons (FirstName, LastName, Address, City, Type) VALUES ('Mike', 'Hughes', '123 Fake Street', 'Belfast', 'qa');
|
||||||
INSERT INTO Persons (FirstName, LastName, Address, City, Type) VALUES ('John', 'Smith', '64 Updown Road', 'Dublin', 'programmer');
|
INSERT INTO Persons (FirstName, LastName, Address, City, Type) VALUES ('John', 'Smith', '64 Updown Road', 'Dublin', 'programmer');
|
||||||
|
INSERT INTO Persons (FirstName, LastName, Address, City, Type, Age) VALUES ('Foo', 'Bar', 'Foo Street', 'Bartown', 'support', 0);
|
||||||
INSERT INTO Tasks (ExecutorID, QaID, TaskName, Completed) VALUES (1, 2, 'assembling', TRUE);
|
INSERT INTO Tasks (ExecutorID, QaID, TaskName, Completed) VALUES (1, 2, 'assembling', TRUE);
|
||||||
INSERT INTO Tasks (ExecutorID, QaID, TaskName, Completed) VALUES (2, 1, 'processing', FALSE);
|
INSERT INTO Tasks (ExecutorID, QaID, TaskName, Completed) VALUES (2, 1, 'processing', FALSE);
|
||||||
INSERT INTO Products (ProductName) VALUES ('Computers');
|
INSERT INTO Products (ProductName) VALUES ('Computers');
|
||||||
|
|
|
@ -26,20 +26,8 @@ export async function handleRequest(
|
||||||
) {
|
) {
|
||||||
// make sure the filters are cleaned up, no empty strings for equals, fuzzy or string
|
// make sure the filters are cleaned up, no empty strings for equals, fuzzy or string
|
||||||
if (opts && opts.filters) {
|
if (opts && opts.filters) {
|
||||||
for (let filterField of NoEmptyFilterStrings) {
|
opts.filters = sdk.rows.removeEmptyFilters(opts.filters)
|
||||||
if (!opts.filters[filterField]) {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
|
||||||
for (let [key, value] of Object.entries(opts.filters[filterField])) {
|
|
||||||
if (!value || value === "") {
|
|
||||||
// @ts-ignore
|
|
||||||
delete opts.filters[filterField][key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!dataFilters.hasFilters(opts?.filters) &&
|
!dataFilters.hasFilters(opts?.filters) &&
|
||||||
opts?.filters?.onEmptyFilter === EmptyFilterOption.RETURN_NONE
|
opts?.filters?.onEmptyFilter === EmptyFilterOption.RETURN_NONE
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
import { InternalTables } from "../../../db/utils"
|
import { InternalTables } from "../../../db/utils"
|
||||||
import * as userController from "../user"
|
import * as userController from "../user"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import { Ctx, FieldType, Row, Table, UserCtx } from "@budibase/types"
|
import {
|
||||||
import { FieldTypes } from "../../../constants"
|
Ctx,
|
||||||
|
FieldType,
|
||||||
|
Row,
|
||||||
|
SearchFilters,
|
||||||
|
Table,
|
||||||
|
UserCtx,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import { FieldTypes, NoEmptyFilterStrings } from "../../../constants"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
|
|
||||||
import validateJs from "validate.js"
|
import validateJs from "validate.js"
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { Knex, knex } from "knex"
|
import { Knex, knex } from "knex"
|
||||||
|
import { db as dbCore } from "@budibase/backend-core"
|
||||||
|
import { QueryOptions } from "../../definitions/datasource"
|
||||||
|
import { isIsoDateString, SqlClient } from "../utils"
|
||||||
|
import SqlTableQueryBuilder from "./sqlTable"
|
||||||
import {
|
import {
|
||||||
Operation,
|
Operation,
|
||||||
QueryJson,
|
QueryJson,
|
||||||
|
@ -6,11 +10,8 @@ import {
|
||||||
SearchFilters,
|
SearchFilters,
|
||||||
SortDirection,
|
SortDirection,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { db as dbCore } from "@budibase/backend-core"
|
|
||||||
import { QueryOptions } from "../../definitions/datasource"
|
|
||||||
import { isIsoDateString, SqlClient } from "../utils"
|
|
||||||
import SqlTableQueryBuilder from "./sqlTable"
|
|
||||||
import environment from "../../environment"
|
import environment from "../../environment"
|
||||||
|
import { isValidFilter } from "../utils"
|
||||||
|
|
||||||
const envLimit = environment.SQL_MAX_ROWS
|
const envLimit = environment.SQL_MAX_ROWS
|
||||||
? parseInt(environment.SQL_MAX_ROWS)
|
? parseInt(environment.SQL_MAX_ROWS)
|
||||||
|
@ -261,15 +262,17 @@ class InternalBuilder {
|
||||||
if (isEmptyObject(value.high)) {
|
if (isEmptyObject(value.high)) {
|
||||||
value.high = ""
|
value.high = ""
|
||||||
}
|
}
|
||||||
if (value.low && value.high) {
|
const lowValid = isValidFilter(value.low),
|
||||||
|
highValid = isValidFilter(value.high)
|
||||||
|
if (lowValid && highValid) {
|
||||||
// Use a between operator if we have 2 valid range values
|
// Use a between operator if we have 2 valid range values
|
||||||
const fnc = allOr ? "orWhereBetween" : "whereBetween"
|
const fnc = allOr ? "orWhereBetween" : "whereBetween"
|
||||||
query = query[fnc](key, [value.low, value.high])
|
query = query[fnc](key, [value.low, value.high])
|
||||||
} else if (value.low) {
|
} else if (lowValid) {
|
||||||
// Use just a single greater than operator if we only have a low
|
// Use just a single greater than operator if we only have a low
|
||||||
const fnc = allOr ? "orWhere" : "where"
|
const fnc = allOr ? "orWhere" : "where"
|
||||||
query = query[fnc](key, ">", value.low)
|
query = query[fnc](key, ">", value.low)
|
||||||
} else if (value.high) {
|
} else if (highValid) {
|
||||||
// Use just a single less than operator if we only have a high
|
// Use just a single less than operator if we only have a high
|
||||||
const fnc = allOr ? "orWhere" : "where"
|
const fnc = allOr ? "orWhere" : "where"
|
||||||
query = query[fnc](key, "<", value.high)
|
query = query[fnc](key, "<", value.high)
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { SourceName, SqlQuery, Datasource, Table } from "@budibase/types"
|
import { SqlQuery, Table, SearchFilters } from "@budibase/types"
|
||||||
import { DocumentType, SEPARATOR } from "../db/utils"
|
import { DocumentType, SEPARATOR } from "../db/utils"
|
||||||
import { FieldTypes, BuildSchemaErrors, InvalidColumns } from "../constants"
|
import {
|
||||||
|
FieldTypes,
|
||||||
|
BuildSchemaErrors,
|
||||||
|
InvalidColumns,
|
||||||
|
NoEmptyFilterStrings,
|
||||||
|
} from "../constants"
|
||||||
import { helpers } from "@budibase/shared-core"
|
import { helpers } from "@budibase/shared-core"
|
||||||
|
|
||||||
const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
|
const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
|
||||||
|
@ -343,3 +348,36 @@ export function getPrimaryDisplay(testValue: unknown): string | undefined {
|
||||||
}
|
}
|
||||||
return testValue as string
|
return testValue as string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isValidFilter(value: any) {
|
||||||
|
return value != null && value !== ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't do a pure falsy check, as 0 is included
|
||||||
|
// https://github.com/Budibase/budibase/issues/10118
|
||||||
|
export function removeEmptyFilters(filters: SearchFilters) {
|
||||||
|
for (let filterField of NoEmptyFilterStrings) {
|
||||||
|
if (!filters[filterField]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let filterType of Object.keys(filters)) {
|
||||||
|
if (filterType !== filterField) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// don't know which one we're checking, type could be anything
|
||||||
|
const value = filters[filterType] as unknown
|
||||||
|
if (typeof value === "object") {
|
||||||
|
for (let [key, value] of Object.entries(
|
||||||
|
filters[filterType] as object
|
||||||
|
)) {
|
||||||
|
if (value == null || value === "") {
|
||||||
|
// @ts-ignore
|
||||||
|
delete filters[filterField][key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filters
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { AppStatus } from "../../../db/utils"
|
import { AppStatus } from "../../../db/utils"
|
||||||
import { App, ContextUser } from "@budibase/types"
|
import { App, ContextUser, User } from "@budibase/types"
|
||||||
import { getLocksById } from "../../../utilities/redis"
|
import { getLocksById } from "../../../utilities/redis"
|
||||||
import { enrichApps } from "../../users/sessions"
|
import { enrichApps } from "../../users/sessions"
|
||||||
import { checkAppMetadata } from "../../../automations/logging"
|
import { checkAppMetadata } from "../../../automations/logging"
|
||||||
import { db as dbCore, users } from "@budibase/backend-core"
|
import { db as dbCore, users } from "@budibase/backend-core"
|
||||||
|
import { groups } from "@budibase/pro"
|
||||||
|
|
||||||
export function filterAppList(user: ContextUser, apps: App[]) {
|
export function filterAppList(user: User, apps: App[]) {
|
||||||
let appList: string[] = []
|
let appList: string[] = []
|
||||||
const roleApps = Object.keys(user.roles || {})
|
const roleApps = Object.keys(user.roles)
|
||||||
if (users.hasAppBuilderPermissions(user)) {
|
if (users.hasAppBuilderPermissions(user)) {
|
||||||
appList = user.builder?.apps || []
|
appList = user.builder?.apps || []
|
||||||
appList = appList.concat(roleApps)
|
appList = appList.concat(roleApps)
|
||||||
|
@ -23,7 +24,12 @@ export async function fetch(status: AppStatus, user: ContextUser) {
|
||||||
const dev = status === AppStatus.DEV
|
const dev = status === AppStatus.DEV
|
||||||
const all = status === AppStatus.ALL
|
const all = status === AppStatus.ALL
|
||||||
let apps = (await dbCore.getAllApps({ dev, all })) as App[]
|
let apps = (await dbCore.getAllApps({ dev, all })) as App[]
|
||||||
apps = filterAppList(user, apps)
|
|
||||||
|
const enrichedUser = await groups.enrichUserRolesFromGroups({
|
||||||
|
...user,
|
||||||
|
roles: user.roles || {},
|
||||||
|
})
|
||||||
|
apps = filterAppList(enrichedUser, apps)
|
||||||
|
|
||||||
const appIds = apps
|
const appIds = apps
|
||||||
.filter((app: any) => app.status === "development")
|
.filter((app: any) => app.status === "development")
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { isExternalTable } from "../../../integrations/utils"
|
||||||
import * as internal from "./search/internal"
|
import * as internal from "./search/internal"
|
||||||
import * as external from "./search/external"
|
import * as external from "./search/external"
|
||||||
import { Format } from "../../../api/controllers/view/exporters"
|
import { Format } from "../../../api/controllers/view/exporters"
|
||||||
|
export { isValidFilter, removeEmptyFilters } from "../../../integrations/utils"
|
||||||
|
|
||||||
export interface ViewParams {
|
export interface ViewParams {
|
||||||
calculation: string
|
calculation: string
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import * as search from "../../app/rows/search"
|
||||||
|
|
||||||
|
describe("removeEmptyFilters", () => {
|
||||||
|
it("0 should not be removed", () => {
|
||||||
|
const filters = search.removeEmptyFilters({
|
||||||
|
equal: {
|
||||||
|
column: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect((filters.equal as any).column).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("empty string should be removed", () => {
|
||||||
|
const filters = search.removeEmptyFilters({
|
||||||
|
equal: {
|
||||||
|
column: "",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(Object.values(filters.equal as any).length).toBe(0)
|
||||||
|
})
|
||||||
|
})
|
13
yarn.lock
13
yarn.lock
|
@ -6269,6 +6269,14 @@
|
||||||
"@types/tedious" "*"
|
"@types/tedious" "*"
|
||||||
tarn "^3.0.1"
|
tarn "^3.0.1"
|
||||||
|
|
||||||
|
"@types/node-fetch@2.6.1":
|
||||||
|
version "2.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975"
|
||||||
|
integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
form-data "^3.0.0"
|
||||||
|
|
||||||
"@types/node-fetch@2.6.4":
|
"@types/node-fetch@2.6.4":
|
||||||
version "2.6.4"
|
version "2.6.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.4.tgz#1bc3a26de814f6bf466b25aeb1473fa1afe6a660"
|
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.4.tgz#1bc3a26de814f6bf466b25aeb1473fa1afe6a660"
|
||||||
|
@ -6290,6 +6298,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f"
|
||||||
integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==
|
integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==
|
||||||
|
|
||||||
|
"@types/node@14.18.20":
|
||||||
|
version "14.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.20.tgz#268f028b36eaf51181c3300252f605488c4f0650"
|
||||||
|
integrity sha512-Q8KKwm9YqEmUBRsqJ2GWJDtXltBDxTdC4m5vTdXBolu2PeQh8LX+f6BTwU+OuXPu37fLxoN6gidqBmnky36FXA==
|
||||||
|
|
||||||
"@types/node@16.9.1":
|
"@types/node@16.9.1":
|
||||||
version "16.9.1"
|
version "16.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708"
|
||||||
|
|
Loading…
Reference in New Issue