fix(filters): correctly replace the same filter input part when it occurs multiple times

This fixes a bug where a query like "labels in lorem || labels in ipsum" would only replace the first occurrence, leading to errors when sending the query string to the api.

Resolves https://github.com/go-vikunja/vikunja/issues/346
This commit is contained in:
kolaente 2025-01-21 18:22:56 +01:00
parent 6a63ffca28
commit 3d33b7c8d1
No known key found for this signature in database
GPG Key ID: F40E70337AB24C9B
2 changed files with 37 additions and 3 deletions

View File

@ -78,6 +78,16 @@ describe('Filter Transformation', () => {
expect(transformed).toBe('labels not in 1, 2 && due_date = now')
})
it('should correctly resolve labels with multiple in clauses', () => {
const transformed = transformFilterStringForApi(
'labels in lorem || labels in ipsum',
multipleDummyResolver,
nullTitleToIdResolver,
)
expect(transformed).toBe('labels in 1 || labels in 2')
})
it('should correctly resolve projects', () => {
const transformed = transformFilterStringForApi(
'project = lorem',
@ -228,6 +238,16 @@ describe('Filter Transformation', () => {
expect(transformed).toBe('labels in lorem, ipsum')
})
it('should correctly resolve multiple labels in clauses', () => {
const transformed = transformFilterStringFromApi(
'labels in 1 || labels in 2',
multipleIdToTitleResolver,
nullIdToTitleResolver,
)
expect(transformed).toBe('labels in lorem || labels in ipsum')
})
it('should correctly resolve multiple labels not in', () => {
const transformed = transformFilterStringFromApi(

View File

@ -94,6 +94,8 @@ export function transformFilterStringForApi(
const pattern = getFilterFieldRegexPattern(field)
let match: RegExpExecArray | null
const replacements: { start: number, length: number, replacement: string }[] = []
while ((match = pattern.exec(filter)) !== null) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [matched, prefix, operator, space, keyword] = match
@ -116,10 +118,22 @@ export function transformFilterStringForApi(
})
const actualKeywordStart = (match?.index || 0) + prefix.length
filter = filter.substring(0, actualKeywordStart) +
replaced +
filter.substring(actualKeywordStart + keyword.length)
replacements.push({
start: actualKeywordStart,
length: keyword.length,
replacement: replaced,
})
}
// We're collecting the results first and then replacing the filter string in the end
// to avoid modifying the input string as we iterate over it.
let offset = 0
replacements.forEach(({start, length, replacement}) => {
filter = filter.substring(0, start + offset) +
replacement +
filter.substring(start + offset + length)
offset += replacement.length - length
})
})
return filter
}