mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(Data Table Node): Show 'Is False' / 'Is True' condition on boolean columns (no-changelog) (#19478)
This commit is contained in:
@@ -10,7 +10,7 @@ export type FilterType = typeof ANY_CONDITION | typeof ALL_CONDITIONS;
|
|||||||
export type FieldEntry =
|
export type FieldEntry =
|
||||||
| {
|
| {
|
||||||
keyName: string;
|
keyName: string;
|
||||||
condition: 'isEmpty' | 'isNotEmpty';
|
condition: 'isEmpty' | 'isNotEmpty' | 'isTrue' | 'isFalse';
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
keyName: string;
|
keyName: string;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type {
|
import {
|
||||||
ILoadOptionsFunctions,
|
type ILoadOptionsFunctions,
|
||||||
INodeListSearchResult,
|
type INodeListSearchResult,
|
||||||
INodePropertyOptions,
|
type INodePropertyOptions,
|
||||||
ResourceMapperField,
|
type ResourceMapperField,
|
||||||
ResourceMapperFields,
|
type ResourceMapperFields,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { getDataTableAggregateProxy, getDataTableProxyLoadOptions } from './utils';
|
import { getDataTableAggregateProxy, getDataTableProxyLoadOptions } from './utils';
|
||||||
@@ -80,14 +80,21 @@ export async function getConditionsForColumn(this: ILoadOptionsFunctions) {
|
|||||||
}
|
}
|
||||||
const keyName = this.getCurrentNodeParameter('&keyName') as string;
|
const keyName = this.getCurrentNodeParameter('&keyName') as string;
|
||||||
|
|
||||||
// Base conditions available for all column types
|
const nullConditions: INodePropertyOptions[] = [
|
||||||
const baseConditions: INodePropertyOptions[] = [
|
|
||||||
{ name: 'Equals', value: 'eq' },
|
|
||||||
{ name: 'Not Equals', value: 'neq' },
|
|
||||||
{ name: 'Is Empty', value: 'isEmpty' },
|
{ name: 'Is Empty', value: 'isEmpty' },
|
||||||
{ name: 'Is Not Empty', value: 'isNotEmpty' },
|
{ name: 'Is Not Empty', value: 'isNotEmpty' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const equalsConditions: INodePropertyOptions[] = [
|
||||||
|
{ name: 'Equals', value: 'eq' },
|
||||||
|
{ name: 'Not Equals', value: 'neq' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const booleanConditions: INodePropertyOptions[] = [
|
||||||
|
{ name: 'Is True', value: 'isTrue' },
|
||||||
|
{ name: 'Is False', value: 'isFalse' },
|
||||||
|
];
|
||||||
|
|
||||||
const comparableConditions: INodePropertyOptions[] = [
|
const comparableConditions: INodePropertyOptions[] = [
|
||||||
{ name: 'Greater Than', value: 'gt' },
|
{ name: 'Greater Than', value: 'gt' },
|
||||||
{ name: 'Greater Than or Equal', value: 'gte' },
|
{ name: 'Greater Than or Equal', value: 'gte' },
|
||||||
@@ -100,7 +107,13 @@ export async function getConditionsForColumn(this: ILoadOptionsFunctions) {
|
|||||||
{ name: 'Contains (Case-Insensitive)', value: 'ilike' },
|
{ name: 'Contains (Case-Insensitive)', value: 'ilike' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const allConditions = [...baseConditions, ...comparableConditions, ...stringConditions];
|
const allConditions = [
|
||||||
|
...nullConditions,
|
||||||
|
...equalsConditions,
|
||||||
|
...booleanConditions,
|
||||||
|
...comparableConditions,
|
||||||
|
...stringConditions,
|
||||||
|
];
|
||||||
|
|
||||||
// If no column is selected yet, return all conditions
|
// If no column is selected yet, return all conditions
|
||||||
if (!keyName) {
|
if (!keyName) {
|
||||||
@@ -113,20 +126,28 @@ export async function getConditionsForColumn(this: ILoadOptionsFunctions) {
|
|||||||
(await proxy.getColumns()).find((col) => col.name === keyName);
|
(await proxy.getColumns()).find((col) => col.name === keyName);
|
||||||
|
|
||||||
if (!column) {
|
if (!column) {
|
||||||
return baseConditions;
|
return [...equalsConditions, ...nullConditions];
|
||||||
}
|
}
|
||||||
|
|
||||||
const conditions = baseConditions;
|
const conditions: INodePropertyOptions[] = [];
|
||||||
|
|
||||||
|
if (column.type === 'boolean') {
|
||||||
|
conditions.push.apply(conditions, booleanConditions);
|
||||||
|
}
|
||||||
|
|
||||||
// String columns get LIKE operators
|
// String columns get LIKE operators
|
||||||
if (column.type === 'string') {
|
if (column.type === 'string') {
|
||||||
|
conditions.push.apply(conditions, equalsConditions);
|
||||||
conditions.push.apply(conditions, stringConditions);
|
conditions.push.apply(conditions, stringConditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['number', 'date', 'string'].includes(column.type)) {
|
if (['number', 'date', 'string'].includes(column.type)) {
|
||||||
|
conditions.push.apply(conditions, equalsConditions);
|
||||||
conditions.push.apply(conditions, comparableConditions);
|
conditions.push.apply(conditions, comparableConditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conditions.push.apply(conditions, nullConditions);
|
||||||
|
|
||||||
return conditions;
|
return conditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export function getSelectFields(
|
|||||||
default: '',
|
default: '',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
hide: {
|
hide: {
|
||||||
condition: ['isEmpty', 'isNotEmpty'],
|
condition: ['isEmpty', 'isNotEmpty', 'isTrue', 'isFalse'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -101,6 +101,18 @@ export function buildGetManyFilter(
|
|||||||
condition: 'neq' as const,
|
condition: 'neq' as const,
|
||||||
value: null,
|
value: null,
|
||||||
};
|
};
|
||||||
|
case 'isTrue':
|
||||||
|
return {
|
||||||
|
columnName: x.keyName,
|
||||||
|
condition: 'eq' as const,
|
||||||
|
value: true,
|
||||||
|
};
|
||||||
|
case 'isFalse':
|
||||||
|
return {
|
||||||
|
columnName: x.keyName,
|
||||||
|
condition: 'eq' as const,
|
||||||
|
value: false,
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
columnName: x.keyName,
|
columnName: x.keyName,
|
||||||
|
|||||||
@@ -203,75 +203,150 @@ describe('dataObjectToApiInput', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buildGetManyFilter - isEmpty/isNotEmpty translation', () => {
|
describe('buildGetManyFilter', () => {
|
||||||
it('should translate isEmpty to eq with null value', () => {
|
describe('isEmpty/isNotEmpty translation', () => {
|
||||||
const fieldEntries = [{ keyName: 'name', condition: 'isEmpty' as const, keyValue: 'ignored' }];
|
it('should translate isEmpty to eq with null value', () => {
|
||||||
|
const fieldEntries = [
|
||||||
|
{ keyName: 'name', condition: 'isEmpty' as const, keyValue: 'ignored' },
|
||||||
|
];
|
||||||
|
|
||||||
const result = buildGetManyFilter(fieldEntries, ALL_CONDITIONS);
|
const result = buildGetManyFilter(fieldEntries, ALL_CONDITIONS);
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
type: 'and',
|
type: 'and',
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
columnName: 'name',
|
columnName: 'name',
|
||||||
condition: 'eq',
|
condition: 'eq',
|
||||||
value: null,
|
value: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate isNotEmpty to neq with null value', () => {
|
||||||
|
const fieldEntries = [
|
||||||
|
{ keyName: 'email', condition: 'isNotEmpty' as const, keyValue: 'ignored' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = buildGetManyFilter(fieldEntries, ANY_CONDITION);
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
type: 'or',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
columnName: 'email',
|
||||||
|
condition: 'neq',
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle mixed conditions including isEmpty/isNotEmpty', () => {
|
||||||
|
const fieldEntries = [
|
||||||
|
{ keyName: 'name', condition: 'eq' as const, keyValue: 'John' },
|
||||||
|
{ keyName: 'email', condition: 'isEmpty' as const, keyValue: 'ignored' },
|
||||||
|
{ keyName: 'phone', condition: 'isNotEmpty' as const, keyValue: 'ignored' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = buildGetManyFilter(fieldEntries, ALL_CONDITIONS);
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
type: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
columnName: 'name',
|
||||||
|
condition: 'eq',
|
||||||
|
value: 'John',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
columnName: 'email',
|
||||||
|
condition: 'eq',
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
columnName: 'phone',
|
||||||
|
condition: 'neq',
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should translate isNotEmpty to neq with null value', () => {
|
describe('isTrue/isFalse translation', () => {
|
||||||
const fieldEntries = [
|
it('should translate isTrue to eq with true value', () => {
|
||||||
{ keyName: 'email', condition: 'isNotEmpty' as const, keyValue: 'ignored' },
|
const fieldEntries = [
|
||||||
];
|
{ keyName: 'isActive', condition: 'isTrue' as const, keyValue: 'ignored' },
|
||||||
|
];
|
||||||
|
|
||||||
const result = buildGetManyFilter(fieldEntries, ANY_CONDITION);
|
const result = buildGetManyFilter(fieldEntries, ALL_CONDITIONS);
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
type: 'or',
|
type: 'and',
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
columnName: 'email',
|
columnName: 'isActive',
|
||||||
condition: 'neq',
|
condition: 'eq',
|
||||||
value: null,
|
value: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate isFalse to eq with false value', () => {
|
||||||
|
const fieldEntries = [
|
||||||
|
{ keyName: 'email', condition: 'isFalse' as const, keyValue: 'ignored' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = buildGetManyFilter(fieldEntries, ANY_CONDITION);
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
type: 'or',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
columnName: 'email',
|
||||||
|
condition: 'eq',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle mixed conditions including isTrue/isFalse', () => {
|
||||||
|
const fieldEntries = [
|
||||||
|
{ keyName: 'name', condition: 'eq' as const, keyValue: 'John' },
|
||||||
|
{ keyName: 'isActive', condition: 'isTrue' as const, keyValue: 'ignored' },
|
||||||
|
{ keyName: 'isDeleted', condition: 'isFalse' as const, keyValue: 'ignored' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = buildGetManyFilter(fieldEntries, ALL_CONDITIONS);
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
type: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
columnName: 'name',
|
||||||
|
condition: 'eq',
|
||||||
|
value: 'John',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
columnName: 'isActive',
|
||||||
|
condition: 'eq',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
columnName: 'isDeleted',
|
||||||
|
condition: 'eq',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle mixed conditions including isEmpty/isNotEmpty', () => {
|
it('should handle other conditions', () => {
|
||||||
const fieldEntries = [
|
|
||||||
{ keyName: 'name', condition: 'eq' as const, keyValue: 'John' },
|
|
||||||
{ keyName: 'email', condition: 'isEmpty' as const, keyValue: 'ignored' },
|
|
||||||
{ keyName: 'phone', condition: 'isNotEmpty' as const, keyValue: 'ignored' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = buildGetManyFilter(fieldEntries, ALL_CONDITIONS);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
type: 'and',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
columnName: 'name',
|
|
||||||
condition: 'eq',
|
|
||||||
value: 'John',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnName: 'email',
|
|
||||||
condition: 'eq',
|
|
||||||
value: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnName: 'phone',
|
|
||||||
condition: 'neq',
|
|
||||||
value: null,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should preserve existing conditions unchanged', () => {
|
|
||||||
const fieldEntries = [
|
const fieldEntries = [
|
||||||
{ keyName: 'age', condition: 'gt' as const, keyValue: 18 },
|
{ keyName: 'age', condition: 'gt' as const, keyValue: 18 },
|
||||||
{ keyName: 'name', condition: 'like' as const, keyValue: '%john%' },
|
{ keyName: 'name', condition: 'like' as const, keyValue: '%john%' },
|
||||||
|
|||||||
Reference in New Issue
Block a user