Get existing tests passing.
This commit is contained in:
parent
cb41861d13
commit
17e946845e
|
@ -146,7 +146,7 @@ describe.each([
|
|||
})
|
||||
})
|
||||
|
||||
it.only("can persist views with all fields", async () => {
|
||||
it("can persist views with all fields", async () => {
|
||||
const newView: Required<Omit<CreateViewRequest, "query" | "type">> = {
|
||||
name: generator.name(),
|
||||
tableId: table._id!,
|
||||
|
@ -2332,10 +2332,13 @@ describe.each([
|
|||
})
|
||||
})
|
||||
|
||||
!isLucene &&
|
||||
describe("search", () => {
|
||||
it("returns empty rows from view when no schema is passed", async () => {
|
||||
const rows = await Promise.all(
|
||||
Array.from({ length: 10 }, () => config.api.row.save(table._id!, {}))
|
||||
Array.from({ length: 10 }, () =>
|
||||
config.api.row.save(table._id!, {})
|
||||
)
|
||||
)
|
||||
const response = await config.api.viewV2.search(view.id)
|
||||
expect(response.rows).toHaveLength(10)
|
||||
|
@ -2377,13 +2380,22 @@ describe.each([
|
|||
const view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
query: [
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "two",
|
||||
value: "bar2",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
schema: {
|
||||
id: { visible: true },
|
||||
one: { visible: false },
|
||||
|
@ -2432,13 +2444,22 @@ describe.each([
|
|||
const view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
query: [
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "two",
|
||||
value: "bar2",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
schema: {
|
||||
id: { visible: true },
|
||||
one: { visible: false },
|
||||
|
@ -2460,7 +2481,9 @@ describe.each([
|
|||
|
||||
it("respects the limit parameter", async () => {
|
||||
await Promise.all(
|
||||
Array.from({ length: 10 }, () => config.api.row.save(table._id!, {}))
|
||||
Array.from({ length: 10 }, () =>
|
||||
config.api.row.save(table._id!, {})
|
||||
)
|
||||
)
|
||||
const limit = generator.integer({ min: 1, max: 8 })
|
||||
const response = await config.api.viewV2.search(view.id, {
|
||||
|
@ -2472,7 +2495,9 @@ describe.each([
|
|||
|
||||
it("can handle pagination", async () => {
|
||||
await Promise.all(
|
||||
Array.from({ length: 10 }, () => config.api.row.save(table._id!, {}))
|
||||
Array.from({ length: 10 }, () =>
|
||||
config.api.row.save(table._id!, {})
|
||||
)
|
||||
)
|
||||
const rows = (await config.api.viewV2.search(view.id)).rows
|
||||
|
||||
|
@ -2708,13 +2733,22 @@ describe.each([
|
|||
const view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
query: [
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.NOT_EQUAL,
|
||||
field: "one",
|
||||
value: "foo2",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
schema: {
|
||||
id: { visible: true },
|
||||
one: { visible: true },
|
||||
|
@ -2734,7 +2768,9 @@ describe.each([
|
|||
})
|
||||
expect(response.rows).toHaveLength(1)
|
||||
expect(response.rows).toEqual(
|
||||
expect.arrayContaining([expect.objectContaining({ _id: three._id })])
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ _id: three._id }),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -2755,13 +2791,22 @@ describe.each([
|
|||
const view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
query: [
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.NOT_EQUAL,
|
||||
field: "two",
|
||||
value: "bar2",
|
||||
field: "one",
|
||||
value: "foo2",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
schema: {
|
||||
id: { visible: true },
|
||||
one: { visible: false },
|
||||
|
@ -2789,51 +2834,6 @@ describe.each([
|
|||
)
|
||||
})
|
||||
|
||||
isLucene &&
|
||||
it.each([true, false])(
|
||||
"in lucene, cannot override a view filter",
|
||||
async allOr => {
|
||||
await config.api.row.save(table._id!, {
|
||||
one: "foo",
|
||||
two: "bar",
|
||||
})
|
||||
const two = await config.api.row.save(table._id!, {
|
||||
one: "foo2",
|
||||
two: "bar2",
|
||||
})
|
||||
|
||||
const view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
query: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "two",
|
||||
value: "bar2",
|
||||
},
|
||||
],
|
||||
schema: {
|
||||
id: { visible: true },
|
||||
one: { visible: false },
|
||||
two: { visible: true },
|
||||
},
|
||||
})
|
||||
|
||||
const response = await config.api.viewV2.search(view.id, {
|
||||
query: {
|
||||
allOr,
|
||||
equal: {
|
||||
two: "bar",
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(response.rows).toHaveLength(1)
|
||||
expect(response.rows).toEqual([
|
||||
expect.objectContaining({ _id: two._id }),
|
||||
])
|
||||
}
|
||||
)
|
||||
|
||||
!isLucene &&
|
||||
it.each([true, false])(
|
||||
"can filter a view without a view filter",
|
||||
|
@ -2886,13 +2886,22 @@ describe.each([
|
|||
const view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
query: [
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "two",
|
||||
value: "bar2",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
schema: {
|
||||
id: { visible: true },
|
||||
one: { visible: false },
|
||||
|
@ -3125,7 +3134,10 @@ describe.each([
|
|||
expect(response.rows).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
"Quantity Sum": rows.reduce((acc, r) => acc + r.quantity, 0),
|
||||
"Quantity Sum": rows.reduce(
|
||||
(acc, r) => acc + r.quantity,
|
||||
0
|
||||
),
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
@ -3166,7 +3178,9 @@ describe.each([
|
|||
}
|
||||
|
||||
for (const row of response.rows) {
|
||||
expect(row["Total Price"]).toEqual(priceByQuantity[row.quantity])
|
||||
expect(row["Total Price"]).toEqual(
|
||||
priceByQuantity[row.quantity]
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -3316,10 +3330,21 @@ describe.each([
|
|||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
type: ViewV2Type.CALCULATION,
|
||||
query: {
|
||||
equal: {
|
||||
quantity: 1,
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "quantity",
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
schema: {
|
||||
sum: {
|
||||
|
@ -3598,10 +3623,21 @@ describe.each([
|
|||
const view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
query: {
|
||||
equal: {
|
||||
user: "{{ [user].[_id] }}",
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
field: "user",
|
||||
value: "{{ [user].[_id] }}",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
schema: {
|
||||
user: {
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import { ViewV2 } from "@budibase/types"
|
||||
import { utils, dataFilters } from "@budibase/shared-core"
|
||||
import { isPlainObject } from "lodash"
|
||||
|
||||
function isEmptyObject(obj: any) {
|
||||
return obj && isPlainObject(obj) && Object.keys(obj).length === 0
|
||||
}
|
||||
|
||||
export function ensureQueryUISet(view: ViewV2) {
|
||||
if (!view.queryUI && view.query) {
|
||||
if (!view.queryUI && view.query && !isEmptyObject(view.query)) {
|
||||
if (!Array.isArray(view.query)) {
|
||||
// In practice this should not happen. `view.query`, at the time this code
|
||||
// goes into the codebase, only contains LegacyFilter[] in production.
|
||||
|
@ -24,7 +29,7 @@ export function ensureQueryUISet(view: ViewV2) {
|
|||
}
|
||||
|
||||
export function ensureQuerySet(view: ViewV2) {
|
||||
if (!view.query && view.queryUI) {
|
||||
if (!view.query && view.queryUI && !isEmptyObject(view.queryUI)) {
|
||||
view.query = dataFilters.buildQuery(view.queryUI)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ import { Expectations, TestAPI } from "./base"
|
|||
|
||||
export class ViewV2API extends TestAPI {
|
||||
create = async (
|
||||
view: CreateViewRequest,
|
||||
// The frontend changed in v3 from sending query to sending only queryUI,
|
||||
// making sure tests reflect that.
|
||||
view: Omit<CreateViewRequest, "query">,
|
||||
expectations?: Expectations
|
||||
): Promise<ViewV2> => {
|
||||
const exp: Expectations = {
|
||||
|
|
|
@ -319,9 +319,6 @@ const buildCondition = (expression: LegacyFilter) => {
|
|||
return
|
||||
}
|
||||
|
||||
const isHbs =
|
||||
typeof value === "string" && (value.match(HBS_REGEX) || []).length > 0
|
||||
|
||||
if (operator === "allOr") {
|
||||
query.allOr = true
|
||||
return
|
||||
|
@ -338,13 +335,13 @@ const buildCondition = (expression: LegacyFilter) => {
|
|||
value = null
|
||||
}
|
||||
|
||||
if (
|
||||
type === "datetime" &&
|
||||
!isHbs &&
|
||||
operator !== "empty" &&
|
||||
operator !== "notEmpty"
|
||||
) {
|
||||
// Ensure date value is a valid date and parse into correct format
|
||||
const isHbs =
|
||||
typeof value === "string" && (value.match(HBS_REGEX) || []).length > 0
|
||||
|
||||
// Parsing value depending on what the type is.
|
||||
switch (type) {
|
||||
case FieldType.DATETIME:
|
||||
if (!isHbs && operator !== "empty" && operator !== "notEmpty") {
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
|
@ -354,25 +351,30 @@ const buildCondition = (expression: LegacyFilter) => {
|
|||
return
|
||||
}
|
||||
}
|
||||
if (type === "number" && typeof value === "string" && !isHbs) {
|
||||
break
|
||||
case FieldType.NUMBER:
|
||||
if (typeof value === "string" && !isHbs) {
|
||||
if (operator === "oneOf") {
|
||||
value = value.split(",").map(item => parseFloat(item))
|
||||
value = value.split(",").map(parseFloat)
|
||||
} else {
|
||||
value = parseFloat(value)
|
||||
}
|
||||
}
|
||||
if (type === "boolean") {
|
||||
value = `${value}`?.toLowerCase() === "true"
|
||||
}
|
||||
break
|
||||
case FieldType.BOOLEAN:
|
||||
value = `${value}`.toLowerCase() === "true"
|
||||
break
|
||||
case FieldType.ARRAY:
|
||||
if (
|
||||
["contains", "notContains", "containsAny"].includes(
|
||||
operator.toLocaleString()
|
||||
) &&
|
||||
type === "array" &&
|
||||
typeof value === "string"
|
||||
) {
|
||||
value = value.split(",")
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if (isRangeSearchOperator(operator)) {
|
||||
const key = externalType as keyof typeof SqlNumberTypeRangeMap
|
||||
|
@ -398,17 +400,17 @@ const buildCondition = (expression: LegacyFilter) => {
|
|||
...query.range[field],
|
||||
low: value,
|
||||
}
|
||||
} else if (isLogicalSearchOperator(operator)) {
|
||||
// TODO
|
||||
} else if (
|
||||
isBasicSearchOperator(operator) ||
|
||||
isArraySearchOperator(operator) ||
|
||||
isRangeSearchOperator(operator)
|
||||
) {
|
||||
if (type === "boolean") {
|
||||
// Transform boolean filters to cope with null.
|
||||
// "equals false" needs to be "not equals true"
|
||||
// "not equals false" needs to be "equals true"
|
||||
// TODO(samwho): I suspect this boolean transformation isn't needed anymore,
|
||||
// write some tests to confirm.
|
||||
|
||||
// Transform boolean filters to cope with null. "equals false" needs to
|
||||
// be "not equals true" "not equals false" needs to be "equals true"
|
||||
if (operator === "equal" && value === false) {
|
||||
query.notEqual = query.notEqual || {}
|
||||
query.notEqual[field] = true
|
||||
|
@ -423,6 +425,8 @@ const buildCondition = (expression: LegacyFilter) => {
|
|||
query[operator] ??= {}
|
||||
query[operator][field] = value
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Unsupported operator: ${operator}`)
|
||||
}
|
||||
|
||||
return query
|
||||
|
|
Loading…
Reference in New Issue