Yet more tests.
This commit is contained in:
parent
205d6d0671
commit
67b814a5ca
|
@ -2192,7 +2192,7 @@ describe.each([
|
|||
})
|
||||
|
||||
describe("contains", () => {
|
||||
it.only("successfully finds a row", async () => {
|
||||
it("successfully finds a row", async () => {
|
||||
await expectQuery({
|
||||
contains: { users: [user1._id] },
|
||||
}).toContainExactly([
|
||||
|
|
|
@ -39,7 +39,7 @@ import { generator, mocks } from "@budibase/backend-core/tests"
|
|||
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
|
||||
import merge from "lodash/merge"
|
||||
import { quotas } from "@budibase/pro"
|
||||
import { db, roles, features } from "@budibase/backend-core"
|
||||
import { db, roles, features, context } from "@budibase/backend-core"
|
||||
|
||||
describe.each([
|
||||
["lucene", undefined],
|
||||
|
@ -56,7 +56,8 @@ describe.each([
|
|||
const isInternal = isSqs || isLucene
|
||||
|
||||
let table: Table
|
||||
let datasource: Datasource
|
||||
let rawDatasource: Datasource | undefined
|
||||
let datasource: Datasource | undefined
|
||||
let envCleanup: (() => void) | undefined
|
||||
|
||||
function saveTableRequest(
|
||||
|
@ -113,8 +114,9 @@ describe.each([
|
|||
})
|
||||
|
||||
if (dsProvider) {
|
||||
rawDatasource = await dsProvider
|
||||
datasource = await config.createDatasource({
|
||||
datasource: await dsProvider,
|
||||
datasource: rawDatasource,
|
||||
})
|
||||
}
|
||||
table = await config.api.table.save(priceTable())
|
||||
|
@ -923,6 +925,7 @@ describe.each([
|
|||
|
||||
describe("update", () => {
|
||||
let view: ViewV2
|
||||
let table: Table
|
||||
|
||||
beforeEach(async () => {
|
||||
table = await config.api.table.save(priceTable())
|
||||
|
@ -955,6 +958,21 @@ describe.each([
|
|||
query: [
|
||||
{ operator: "equal", field: "newField", value: "thatValue" },
|
||||
],
|
||||
// Should also update queryUI because query was not previously set.
|
||||
queryUI: {
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: "all",
|
||||
filters: [
|
||||
{
|
||||
operator: "equal",
|
||||
field: "newField",
|
||||
value: "thatValue",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
schema: expect.anything(),
|
||||
},
|
||||
})
|
||||
|
@ -974,8 +992,8 @@ describe.each([
|
|||
query: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: generator.word(),
|
||||
value: generator.word(),
|
||||
field: "newField",
|
||||
value: "newValue",
|
||||
},
|
||||
],
|
||||
sort: {
|
||||
|
@ -999,6 +1017,21 @@ describe.each([
|
|||
expect((await config.api.table.get(tableId)).views).toEqual({
|
||||
[view.name]: {
|
||||
...updatedData,
|
||||
// queryUI gets generated from query
|
||||
queryUI: {
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: "all",
|
||||
filters: [
|
||||
{
|
||||
operator: "equal",
|
||||
field: "newField",
|
||||
value: "newValue",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
schema: {
|
||||
...table.schema,
|
||||
id: expect.objectContaining({
|
||||
|
@ -1244,6 +1277,145 @@ describe.each([
|
|||
)
|
||||
})
|
||||
|
||||
it("can update queryUI field and query gets regenerated", async () => {
|
||||
await config.api.viewV2.update({
|
||||
...view,
|
||||
queryUI: {
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "field",
|
||||
value: "value",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
let updatedView = await config.api.viewV2.get(view.id)
|
||||
expect(updatedView.query).toEqual({
|
||||
$and: {
|
||||
conditions: [
|
||||
{
|
||||
$and: {
|
||||
conditions: [
|
||||
{
|
||||
equal: { field: "value" },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
await config.api.viewV2.update({
|
||||
...updatedView,
|
||||
queryUI: {
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "newField",
|
||||
value: "newValue",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
updatedView = await config.api.viewV2.get(view.id)
|
||||
expect(updatedView.query).toEqual({
|
||||
$and: {
|
||||
conditions: [
|
||||
{
|
||||
$and: {
|
||||
conditions: [
|
||||
{
|
||||
equal: { newField: "newValue" },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("can delete either query and it will get regenerated from queryUI", async () => {
|
||||
await config.api.viewV2.update({
|
||||
...view,
|
||||
query: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "field",
|
||||
value: "value",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
let updatedView = await config.api.viewV2.get(view.id)
|
||||
expect(updatedView.queryUI).toBeDefined()
|
||||
|
||||
await config.api.viewV2.update({
|
||||
...updatedView,
|
||||
query: undefined,
|
||||
})
|
||||
|
||||
updatedView = await config.api.viewV2.get(view.id)
|
||||
expect(updatedView.query).toBeDefined()
|
||||
})
|
||||
|
||||
// This is because the conversion from queryUI -> query loses data, so you
|
||||
// can't accurately reproduce the original queryUI from the query. If
|
||||
// query is a LegacyFilter[] we allow it, because for Budibase v3
|
||||
// everything in the db had query set to a LegacyFilter[], and there's no
|
||||
// loss of information converting from a LegacyFilter[] to a
|
||||
// UISearchFilter. But we convert to a SearchFilters and that can't be
|
||||
// accurately converted to a UISearchFilter.
|
||||
it("can't regenerate queryUI from a query once it has been generated from a queryUI", async () => {
|
||||
await config.api.viewV2.update({
|
||||
...view,
|
||||
queryUI: {
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "field",
|
||||
value: "value",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
let updatedView = await config.api.viewV2.get(view.id)
|
||||
expect(updatedView.query).toBeDefined()
|
||||
|
||||
await config.api.viewV2.update(
|
||||
{
|
||||
...updatedView,
|
||||
queryUI: undefined,
|
||||
},
|
||||
{
|
||||
status: 400,
|
||||
body: {
|
||||
message: "view is missing queryUI field",
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
!isLucene &&
|
||||
describe("calculation views", () => {
|
||||
let table: Table
|
||||
|
@ -1597,6 +1769,62 @@ describe.each([
|
|||
expect.objectContaining({ visible: true, readonly: true })
|
||||
)
|
||||
})
|
||||
|
||||
it("should fill in the queryUI field if it's missing", async () => {
|
||||
const res = await config.api.viewV2.create({
|
||||
name: generator.name(),
|
||||
tableId: tableId,
|
||||
query: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "one",
|
||||
value: "1",
|
||||
},
|
||||
],
|
||||
schema: {
|
||||
id: { visible: true },
|
||||
one: { visible: true },
|
||||
},
|
||||
})
|
||||
|
||||
const table = await config.api.table.get(tableId)
|
||||
const rawView = table.views![res.name] as ViewV2
|
||||
delete rawView.queryUI
|
||||
|
||||
await context.doInAppContext(config.getAppId(), async () => {
|
||||
const db = context.getAppDB()
|
||||
|
||||
if (!rawDatasource) {
|
||||
await db.put(table)
|
||||
} else {
|
||||
const ds = await config.api.datasource.get(datasource!._id!)
|
||||
ds.entities![table.name] = table
|
||||
const updatedDs = {
|
||||
...rawDatasource,
|
||||
_id: ds._id,
|
||||
_rev: ds._rev,
|
||||
entities: ds.entities,
|
||||
}
|
||||
await db.put(updatedDs)
|
||||
}
|
||||
})
|
||||
|
||||
const view = await getDelegate(res)
|
||||
expect(view.queryUI).toEqual({
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "one",
|
||||
value: "1",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("updating table schema", () => {
|
||||
|
|
|
@ -12,9 +12,26 @@ import {
|
|||
TableResponse,
|
||||
TableSourceType,
|
||||
TableViewsResponse,
|
||||
View,
|
||||
ViewV2,
|
||||
} from "@budibase/types"
|
||||
import datasources from "../datasources"
|
||||
import sdk from "../../../sdk"
|
||||
import { ensureQueryUISet } from "../views/utils"
|
||||
import { isV2 } from "../views"
|
||||
|
||||
function processView(view: ViewV2 | View) {
|
||||
if (!isV2(view)) {
|
||||
return
|
||||
}
|
||||
ensureQueryUISet(view)
|
||||
}
|
||||
|
||||
function processViews(views: (ViewV2 | View)[]) {
|
||||
for (const view of views) {
|
||||
processView(view)
|
||||
}
|
||||
}
|
||||
|
||||
export async function processTable(table: Table): Promise<Table> {
|
||||
if (!table) {
|
||||
|
@ -22,6 +39,9 @@ export async function processTable(table: Table): Promise<Table> {
|
|||
}
|
||||
|
||||
table = { ...table }
|
||||
if (table.views) {
|
||||
processViews(Object.values(table.views))
|
||||
}
|
||||
if (table._id && isExternalTableID(table._id)) {
|
||||
// Old created external tables via Budibase might have a missing field name breaking some UI such as filters
|
||||
if (table.schema["id"] && !table.schema["id"].name) {
|
||||
|
|
|
@ -81,6 +81,7 @@ export async function update(tableId: string, view: ViewV2): Promise<ViewV2> {
|
|||
}
|
||||
|
||||
ensureQuerySet(view)
|
||||
ensureQueryUISet(view)
|
||||
|
||||
delete views[existingView.name]
|
||||
views[view.name] = view
|
||||
|
|
|
@ -5,6 +5,7 @@ import sdk from "../../../sdk"
|
|||
import * as utils from "../../../db/utils"
|
||||
import { enrichSchema, isV2 } from "."
|
||||
import { ensureQuerySet, ensureQueryUISet } from "./utils"
|
||||
import { processTable } from "../tables/getters"
|
||||
|
||||
export async function get(viewId: string): Promise<ViewV2> {
|
||||
const { tableId } = utils.extractViewInfoFromID(viewId)
|
||||
|
@ -70,6 +71,7 @@ export async function update(tableId: string, view: ViewV2): Promise<ViewV2> {
|
|||
}
|
||||
|
||||
ensureQuerySet(view)
|
||||
ensureQueryUISet(view)
|
||||
|
||||
delete table.views[existingView.name]
|
||||
table.views[view.name] = view
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { ViewV2 } from "@budibase/types"
|
||||
import { utils, dataFilters } from "@budibase/shared-core"
|
||||
import { isPlainObject } from "lodash"
|
||||
import { HTTPError } from "@budibase/backend-core"
|
||||
|
||||
function isEmptyObject(obj: any) {
|
||||
return obj && isPlainObject(obj) && Object.keys(obj).length === 0
|
||||
|
@ -21,7 +22,7 @@ export function ensureQueryUISet(view: ViewV2) {
|
|||
// So despite the type saying that `view.query` is a LegacyFilter[] |
|
||||
// SearchFilters, it will never be a SearchFilters when a `view.queryUI`
|
||||
// is specified, making it "safe" to throw an error here.
|
||||
throw new Error("view is missing queryUI field")
|
||||
throw new HTTPError("view is missing queryUI field", 400)
|
||||
}
|
||||
|
||||
view.queryUI = utils.processSearchFilters(view.query)
|
||||
|
@ -29,7 +30,10 @@ export function ensureQueryUISet(view: ViewV2) {
|
|||
}
|
||||
|
||||
export function ensureQuerySet(view: ViewV2) {
|
||||
if (!view.query && view.queryUI && !isEmptyObject(view.queryUI)) {
|
||||
// We consider queryUI to be the source of truth, so we don't check for the
|
||||
// presence of query here. We will overwrite it regardless of whether it is
|
||||
// present or not.
|
||||
if (view.queryUI && !isEmptyObject(view.queryUI)) {
|
||||
view.query = dataFilters.buildQuery(view.queryUI)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,7 @@ import { Expectations, TestAPI } from "./base"
|
|||
|
||||
export class ViewV2API extends TestAPI {
|
||||
create = async (
|
||||
// The frontend changed in v3 from sending query to sending only queryUI,
|
||||
// making sure tests reflect that.
|
||||
view: Omit<CreateViewRequest, "query">,
|
||||
view: CreateViewRequest,
|
||||
expectations?: Expectations
|
||||
): Promise<ViewV2> => {
|
||||
const exp: Expectations = {
|
||||
|
|
Loading…
Reference in New Issue