Skip to content

Commit

Permalink
Implement advanced search for distribution version on cluster list ta…
Browse files Browse the repository at this point in the history
…ble (#3902)

Signed-off-by: Randy Bruno Piverger <rbrunopi@redhat.com>
  • Loading branch information
Randy424 authored Sep 26, 2024
1 parent 653cf30 commit 3a88e8e
Show file tree
Hide file tree
Showing 12 changed files with 371 additions and 112 deletions.
29 changes: 14 additions & 15 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"react-error-boundary": "3.1.4",
"react-monaco-editor": "0.41.x",
"screenfull": "^6.0.2",
"semver": "^7.6.3",
"set-value": "^4.1.0",
"string-similarity": "4.0.4",
"svg.js": "2.7.1",
Expand Down Expand Up @@ -140,7 +141,7 @@
"@types/react": "^18.2.14",
"@types/react-beforeunload": "^2.1.1",
"@types/react-dom": "^18.2.6",
"@types/semver": "^7.3.13",
"@types/semver": "^7.5.8",
"@types/set-value": "^4.0.1",
"@types/string-similarity": "4.0.0",
"@types/testing-library__jest-dom": "5.14.5",
Expand Down
2 changes: 2 additions & 0 deletions frontend/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -2358,6 +2358,7 @@
"Select <italic>{{appType}}</italic> to delete {{name}} and all related resources.": "Select <italic>{{appType}}</italic> to delete {{name}} and all related resources.",
"Select a cluster to view details": "Select a cluster to view details",
"Select a column": "Select a column",
"Select a column name to choose an operator": "Select a column name to choose an operator",
"Select a namespace for the credential": "Select a namespace for the credential",
"Select a namespace to be able to select policies in that namespace.": "Select a namespace to be able to select policies in that namespace.",
"Select a policy set": "Select a policy set",
Expand All @@ -2372,6 +2373,7 @@
"Select an active cloud name for the credential.": "Select an active cloud name for the credential.",
"Select an authentication method": "Select an authentication method",
"Select an inventory": "Select an inventory",
"Select an operator": "Select an operator",
"Select at least one cluster set to deploy application resources.": "Select at least one cluster set to deploy application resources.",
"Select cluster label": "Select cluster label",
"Select cluster sets from which to select clusters. If you do not select a cluster set, all clusters are selected from all cluster sets bound to the namespace.": "Select cluster sets from which to select clusters. If you do not select a cluster set, all clusters are selected from all cluster sets bound to the namespace.",
Expand Down
69 changes: 69 additions & 0 deletions frontend/src/lib/search-utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/* Copyright Contributors to the Open Cluster Management project */
import { SearchOperator } from '../ui-components/AcmSearchInput'
import { handleStandardComparison, handleSemverOperatorComparison } from './search-utils'

const stringData = {
stringOne: 'Adam',
stringTwo: 'Becky',
stringThree: 'Charlie',
stringFour: 'David',
stringFive: 'Eve',
}
const versionData = {
versionOne: '1.0.0',
versionTwo: '1.5.0',
versionThree: '2.0.0',
versionFour: '2.5.0',
versionIncomplete: '1.',
}

describe('search-utils basic string operator', () => {
const { stringOne, stringTwo, stringThree, stringFour, stringFive } = stringData
it('can determine equals', () => {
expect(handleStandardComparison(stringOne, stringOne, SearchOperator.Equals)).toBeTruthy()
})
it('can determine greater', () => {
expect(handleStandardComparison(stringOne, stringTwo, SearchOperator.GreaterThan)).toBeTruthy()
})
it('can determine less', () => {
expect(handleStandardComparison(stringFour, stringThree, SearchOperator.LessThan)).toBeTruthy()
})
it('can determine greater than or equal to', () => {
expect(handleStandardComparison(stringOne, stringOne, SearchOperator.GreaterThanOrEqualTo)).toBeTruthy()
expect(handleStandardComparison(stringOne, stringTwo, SearchOperator.GreaterThanOrEqualTo)).toBeTruthy()
})
it('can determine less than or equal to', () => {
expect(handleStandardComparison(stringOne, stringOne, SearchOperator.LessThanOrEqualTo)).toBeTruthy()
expect(handleStandardComparison(stringFive, stringOne, SearchOperator.LessThanOrEqualTo)).toBeTruthy()
})
it('can determine non-equals', () => {
expect(handleStandardComparison(stringOne, stringFive, SearchOperator.Equals)).toBeFalsy()
})
})

describe('search-utils sermver operator', () => {
const { versionOne, versionTwo, versionThree, versionFour, versionIncomplete } = versionData
it('can determine greater semver', () => {
expect(handleSemverOperatorComparison(versionFour, versionThree, SearchOperator.GreaterThan)).toBeTruthy()
})
it('can determine less semver', () => {
expect(handleSemverOperatorComparison(versionOne, versionTwo, SearchOperator.LessThan)).toBeTruthy()
})
it('can determine equals semver', () => {
expect(handleSemverOperatorComparison(versionOne, versionOne, SearchOperator.Equals)).toBeTruthy()
})
it('can determine greater than or equal to semver', () => {
expect(handleSemverOperatorComparison(versionThree, versionThree, SearchOperator.GreaterThanOrEqualTo)).toBeTruthy()
expect(handleSemverOperatorComparison(versionFour, versionThree, SearchOperator.GreaterThanOrEqualTo)).toBeTruthy()
})
it('can determine less than or equal to semver', () => {
expect(handleSemverOperatorComparison(versionTwo, versionTwo, SearchOperator.LessThanOrEqualTo)).toBeTruthy()
expect(handleSemverOperatorComparison(versionOne, versionTwo, SearchOperator.LessThanOrEqualTo)).toBeTruthy()
})
it('can determine non-equal semver', () => {
expect(handleSemverOperatorComparison(versionOne, versionTwo, SearchOperator.NotEquals)).toBeTruthy()
})
it('can coerce incomplete semver strings', () => {
expect(handleSemverOperatorComparison(versionIncomplete, versionOne, SearchOperator.Equals)).toBeTruthy()
})
})
53 changes: 51 additions & 2 deletions frontend/src/lib/search-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,54 @@
/* Copyright Contributors to the Open Cluster Management project */
import * as semver from 'semver'
import { SearchOperator } from '../ui-components/AcmSearchInput'

export const handleOperatorComparison = (value: string, selectedValue: string) => {
return value.localeCompare(selectedValue) === 0
// handleStandardComparison uses localeCompare's API to evaluate string sort order. We assume values sorted earlier are "greater than"
export const handleStandardComparison = (valueOne: string, valueTwo: string, operator: SearchOperator) => {
switch (operator) {
case SearchOperator.Equals:
return valueOne.localeCompare(valueTwo) === 0
case SearchOperator.GreaterThan:
return valueOne.localeCompare(valueTwo) < 0
case SearchOperator.LessThan:
return valueOne.localeCompare(valueTwo) > 0
case SearchOperator.GreaterThanOrEqualTo:
return valueOne.localeCompare(valueTwo) < 0 || valueOne.localeCompare(valueTwo) === 0
case SearchOperator.LessThanOrEqualTo:
return valueOne.localeCompare(valueTwo) > 0 || valueOne.localeCompare(valueTwo) === 0
case SearchOperator.NotEquals:
return valueOne.localeCompare(valueTwo) !== 0
default:
return false
}
}

// for a given displayVersion string there is a distribution value a ' ' and a semver value, here we divide them and take the semver
export const handleSemverOperatorComparison = (versionOne: string, versionTwo: string, operator: SearchOperator) => {
// Semver coerces the version to a valid semver version if possible, otherwise it returns the original value
const coercedVersionOne = semver.valid(semver.coerce(versionOne)) ?? versionOne
const coercedVersionTwo = semver.valid(semver.coerce(versionTwo)) ?? versionTwo

const validInputSemvers = !!semver.valid(coercedVersionOne) && !!semver.valid(coercedVersionTwo)
if (!validInputSemvers) {
if (operator === SearchOperator.NotEquals) {
return true
}
return false
}
switch (operator) {
case SearchOperator.Equals:
return semver.eq(coercedVersionOne, coercedVersionTwo)
case SearchOperator.GreaterThan:
return semver.gt(coercedVersionOne, coercedVersionTwo)
case SearchOperator.LessThan:
return semver.lt(coercedVersionOne, coercedVersionTwo)
case SearchOperator.GreaterThanOrEqualTo:
return semver.gte(coercedVersionOne, coercedVersionTwo)
case SearchOperator.LessThanOrEqualTo:
return semver.lte(coercedVersionOne, coercedVersionTwo)
case SearchOperator.NotEquals:
return !semver.eq(coercedVersionOne, coercedVersionTwo)
default:
return false
}
}
Loading

0 comments on commit 3a88e8e

Please sign in to comment.