diff --git a/src/browser/components/ManualLink.test.tsx b/src/browser/components/ManualLink.test.tsx index 8f92848fc46..3926b434294 100644 --- a/src/browser/components/ManualLink.test.tsx +++ b/src/browser/components/ManualLink.test.tsx @@ -109,6 +109,16 @@ const movedPages: [ text: 'Indexes', url: 'https://neo4j.com/docs/cypher-manual/4.3/indexes-for-search-performance/' } + ], + [ + { + neo4jVersion: '2025.01.0', + page: '/administration/indexes-for-search-performance/' + }, + { + text: 'Indexes', + url: 'https://neo4j.com/docs/cypher-manual/current/indexes-for-search-performance/' + } ] ] diff --git a/src/browser/components/ManualLink.tsx b/src/browser/components/ManualLink.tsx index 8037268b62c..0baae463a62 100644 --- a/src/browser/components/ManualLink.tsx +++ b/src/browser/components/ManualLink.tsx @@ -24,7 +24,10 @@ import semver from 'semver' import { DrawerExternalLink } from './drawer/drawer-styled' import { formatDocVersion } from 'browser/modules/Sidebar/docsUtils' import { GlobalState } from 'project-root/src/shared/globalState' -import { getRawVersion } from 'shared/modules/dbMeta/dbMetaDuck' +import { + getCleanedVersion, + getRawVersion +} from 'shared/modules/dbMeta/dbMetaDuck' const oldPages: { [key: string]: { oldPage: string; oldContent: string } } = { '/administration/indexes-for-search-performance/': { @@ -60,21 +63,25 @@ const isPageOld = ( chapter: string, page: string, neo4jVersion: string | null -) => - chapter === 'cypher-manual' && - oldPages[page] && - neo4jVersion && - semver.satisfies(neo4jVersion, '<4.0.0-alpha.1') +) => { + if (chapter !== 'cypher-manual' || !oldPages[page] || !neo4jVersion) + return false + const cleanedVersion = getCleanedVersion(neo4jVersion) + return cleanedVersion && semver.satisfies(cleanedVersion, '<4.0.0-alpha.1') +} const isPageNew = ( chapter: string, page: string, neo4jVersion: string | null -) => - chapter === 'cypher-manual' && - newPages[page] && - ((neo4jVersion && semver.satisfies(neo4jVersion, '>=4.3')) || - neo4jVersion === null) // if no version is available, we treat it like the newest version. +) => { + if (chapter !== 'cypher-manual' || !newPages[page]) return false + const cleanedVersion = getCleanedVersion(neo4jVersion) + return ( + (cleanedVersion && semver.satisfies(cleanedVersion, '>=4.3')) || + neo4jVersion === null + ) +} export type ManualLinkProps = { chapter: string @@ -103,7 +110,7 @@ export function ManualLink({ let version = formatDocVersion(neo4jVersion) if ( minVersion && - (!neo4jVersion || semver.cmp(neo4jVersion, '<', minVersion)) + (!neo4jVersion || semver.compareLoose(neo4jVersion, minVersion) === -1) ) { version = formatDocVersion(minVersion) } diff --git a/src/browser/components/VersionConditionalDoc.test.tsx b/src/browser/components/VersionConditionalDoc.test.tsx index a7f89308f3b..0c8c135f399 100644 --- a/src/browser/components/VersionConditionalDoc.test.tsx +++ b/src/browser/components/VersionConditionalDoc.test.tsx @@ -46,6 +46,22 @@ const tests: [Omit, boolean][] = [ includeCurrent: false }, true + ], + [ + { + neo4jVersion: '2025.03.0', + versionCondition: '>=4.3', + includeCurrent: false + }, + true + ], + [ + { + neo4jVersion: '2025.03.0', + versionCondition: '<4.3', + includeCurrent: true + }, + false ] ] diff --git a/src/browser/components/VersionConditionalDoc.tsx b/src/browser/components/VersionConditionalDoc.tsx index 93d85aa1063..d99f99daff6 100644 --- a/src/browser/components/VersionConditionalDoc.tsx +++ b/src/browser/components/VersionConditionalDoc.tsx @@ -22,7 +22,10 @@ import { connect } from 'react-redux' import semver from 'semver' import { GlobalState } from 'project-root/src/shared/globalState' -import { getRawVersion } from 'shared/modules/dbMeta/dbMetaDuck' +import { + getCleanedVersion, + getRawVersion +} from 'shared/modules/dbMeta/dbMetaDuck' export type VersionConditionalDocProps = { versionCondition: string @@ -36,11 +39,10 @@ export function VersionConditionalDoc({ neo4jVersion, includeCurrent = false }: VersionConditionalDocProps): JSX.Element { + const cleanedVersion = getCleanedVersion(neo4jVersion) if ( (includeCurrent && neo4jVersion === null) || - (neo4jVersion !== null && - semver.valid(neo4jVersion) && - semver.satisfies(neo4jVersion, versionCondition)) + (cleanedVersion && semver.satisfies(cleanedVersion, versionCondition)) ) { return <>{children} } else { diff --git a/src/browser/modules/Sidebar/docsUtils.test.ts b/src/browser/modules/Sidebar/docsUtils.test.ts index 91abc10f671..77caf939216 100644 --- a/src/browser/modules/Sidebar/docsUtils.test.ts +++ b/src/browser/modules/Sidebar/docsUtils.test.ts @@ -26,7 +26,9 @@ test('formatDocVersion', () => { { test: '1.1.0', expect: '1.1' }, { test: '1.1.0-beta01', expect: '1.1-preview' }, { test: '1.1.2', expect: '1.1' }, - { test: '2.1.10', expect: '2.1' } + { test: '2.1.10', expect: '2.1' }, + { test: '2025.01.0', expect: 'current' }, + { test: '2024.11.10', expect: 'current' } ] tests.forEach(t => expect(formatDocVersion(t.test)).toEqual(t.expect)) diff --git a/src/browser/modules/Sidebar/docsUtils.ts b/src/browser/modules/Sidebar/docsUtils.ts index 3e963aea13e..c7de61a9138 100644 --- a/src/browser/modules/Sidebar/docsUtils.ts +++ b/src/browser/modules/Sidebar/docsUtils.ts @@ -24,7 +24,9 @@ export const formatDocVersion = (v: string | null = ''): string => { // All non-strings return return 'current' } - if (semver.prerelease(v)) { + if (semver.compareLoose(v, '6.0.0') === 1) { + return 'current' + } else if (semver.prerelease(v)) { return `${semver.major(v)}.${semver.minor(v)}-preview` } return `${semver.major(v)}.${semver.minor(v)}` || 'current' diff --git a/src/browser/modules/Sidebar/static-scripts.test.tsx b/src/browser/modules/Sidebar/static-scripts.test.tsx index f19185539bb..401218a37d6 100644 --- a/src/browser/modules/Sidebar/static-scripts.test.tsx +++ b/src/browser/modules/Sidebar/static-scripts.test.tsx @@ -35,8 +35,8 @@ describe('', () => { content: !script.content.includes('Show meta-graph') ? script.content : script.versionRange === '>=3 <4' - ? script.content.replace('Show meta-graph', 'Show meta-graph v3') - : script.content.replace('Show meta-graph', 'Show meta-graph v4') + ? script.content.replace('Show meta-graph', 'Show meta-graph v3') + : script.content.replace('Show meta-graph', 'Show meta-graph v4') })) const renderWithDBMSVersion = (version: any) => { @@ -83,4 +83,14 @@ describe('', () => { expect(queryByText('Show meta-graph v3')).toBeFalsy() expect(queryByText('Show meta-graph v4')).toBeTruthy() }) + + it('lists examples correctly when using calendar version', () => { + const version = '2025.01.0' + const { queryByText } = renderWithDBMSVersion(version) + + fireEvent.click(queryByText('Common Procedures') as HTMLElement) + + expect(queryByText('Show meta-graph v3')).toBeFalsy() + expect(queryByText('Show meta-graph v4')).toBeTruthy() + }) }) diff --git a/src/browser/modules/Sidebar/static-scripts.ts b/src/browser/modules/Sidebar/static-scripts.ts index afbaebf99ea..65c0ec08040 100644 --- a/src/browser/modules/Sidebar/static-scripts.ts +++ b/src/browser/modules/Sidebar/static-scripts.ts @@ -29,7 +29,8 @@ import { getRawVersion } from 'shared/modules/dbMeta/dbMetaDuck' import * as editor from 'shared/modules/editor/editorDuck' const mapFavoritesStateToProps = (state: any) => { - const version = semver.coerce(getRawVersion(state) || '0') ?? '0' + const version = + semver.coerce(semver.clean(getRawVersion(state) || '', true) ?? '0') ?? '0' const folders = getFolders(state).filter(folder => folder.isStatic) const scripts = favorites .getFavorites(state) @@ -37,6 +38,7 @@ const mapFavoritesStateToProps = (state: any) => { fav => fav.isStatic && fav.versionRange && + version && semver.satisfies(version, fav.versionRange) ) diff --git a/src/browser/modules/Stream/SchemaFrame.tsx b/src/browser/modules/Stream/SchemaFrame.tsx index 444c98a6ce8..81740878ee4 100644 --- a/src/browser/modules/Stream/SchemaFrame.tsx +++ b/src/browser/modules/Stream/SchemaFrame.tsx @@ -36,7 +36,10 @@ import Directives from 'browser-components/Directives' import { GlobalState } from 'project-root/src/shared/globalState' import { NEO4J_BROWSER_USER_ACTION_QUERY } from 'services/bolt/txMetadata' import { CYPHER_REQUEST } from 'shared/modules/cypher/cypherDuck' -import { getSemanticVersion } from 'shared/modules/dbMeta/dbMetaDuck' +import { + getCleanedVersion, + getSemanticVersion +} from 'shared/modules/dbMeta/dbMetaDuck' type IndexesProps = { indexes: any @@ -192,13 +195,14 @@ export class SchemaFrame extends Component { } } - fetchData(neo4jVersion: SemVer) { + fetchData(neo4jVersion: SemVer | null) { if (this.props.bus) { // Indexes this.props.bus.self( CYPHER_REQUEST, { query: + neo4jVersion && semver.valid(neo4jVersion) && semver.satisfies(neo4jVersion, '<4.2.*') ? 'CALL db.indexes()' @@ -212,6 +216,7 @@ export class SchemaFrame extends Component { CYPHER_REQUEST, { query: + neo4jVersion && semver.valid(neo4jVersion) && semver.satisfies(neo4jVersion, '<4.2.*') ? 'CALL db.constraints()' @@ -244,8 +249,10 @@ export class SchemaFrame extends Component { render(): JSX.Element { const { neo4jVersion } = this.props const { indexes, constraints } = this.state + const cleanedVersion = + typeof neo4jVersion === 'string' ? getCleanedVersion(neo4jVersion) : null const schemaCommand = - semver.valid(neo4jVersion) && semver.satisfies(neo4jVersion, '<=3.4.*') + cleanedVersion && semver.satisfies(cleanedVersion, '<=3.4.*') ? 'CALL db.schema()' : 'CALL db.schema.visualization' diff --git a/src/shared/modules/dbMeta/dbMetaDuck.ts b/src/shared/modules/dbMeta/dbMetaDuck.ts index 04a2afac638..a5a1184d250 100644 --- a/src/shared/modules/dbMeta/dbMetaDuck.ts +++ b/src/shared/modules/dbMeta/dbMetaDuck.ts @@ -19,7 +19,7 @@ */ import { uniq } from 'lodash-es' import { QueryResult } from 'neo4j-driver' -import { SemVer, coerce, gte } from 'semver' +import { SemVer, clean, coerce, gte, valid } from 'semver' import { isConfigValFalsy } from 'services/bolt/boltHelpers' import { GlobalState } from 'shared/globalState' import { APP_START } from 'shared/modules/app/appDuck' @@ -270,8 +270,17 @@ export function getUniqueDatbases(state: GlobalState): Database[] { export const getRawVersion = (state: GlobalState): string | null => (state[NAME] || {}).server ? (state[NAME] || {}).server.version : null + export const getSemanticVersion = (state: GlobalState): SemVer | null => - coerce(getRawVersion(state)) + coerce(clean(getRawVersion(state) || '', true)) + +export const getCleanedVersion = (version: string | null): string | null => { + const cleanedVersion = clean(version || '', true) + if (valid(cleanedVersion)) { + return cleanedVersion + } + return null +} export const supportsMultiDb = (state: GlobalState): boolean => { const version = getSemanticVersion(state) diff --git a/src/shared/modules/dbMeta/utils.ts b/src/shared/modules/dbMeta/utils.ts index a3e0d8c5aee..f2be4448e9c 100644 --- a/src/shared/modules/dbMeta/utils.ts +++ b/src/shared/modules/dbMeta/utils.ts @@ -21,7 +21,11 @@ import { Integer, QueryResult } from 'neo4j-driver' import semver from 'semver' import { guessSemverVersion } from '../features/featureDuck.utils' -import { TrialStatus, VERSION_FOR_EDITOR_HISTORY_SETTING } from './dbMetaDuck' +import { + getCleanedVersion, + TrialStatus, + VERSION_FOR_EDITOR_HISTORY_SETTING +} from './dbMetaDuck' type ServerInfo = { version: string | null @@ -52,7 +56,7 @@ export function extractServerInfo(res: QueryResult): ServerInfo { } // Some aura servers self report versions that need coercing (eg. 3.5 or 4.3-aura) - if (!semver.valid(serverInfo.version)) { + if (!getCleanedVersion(serverInfo.version)) { serverInfo.version = guessSemverVersion(serverInfo.version) } @@ -113,5 +117,9 @@ export const extractTrialStatusOld = (res: QueryResult): TrialStatus => { export const versionHasEditorHistorySetting = (version: string | null) => { if (!version) return false - return semver.gte(version, VERSION_FOR_EDITOR_HISTORY_SETTING) + const cleanedVersion = getCleanedVersion(version) + return ( + cleanedVersion && + semver.gte(cleanedVersion, VERSION_FOR_EDITOR_HISTORY_SETTING) + ) } diff --git a/src/shared/modules/features/featureDuck.utils.test.ts b/src/shared/modules/features/featureDuck.utils.test.ts index 375fdcc5253..d90fbc26122 100644 --- a/src/shared/modules/features/featureDuck.utils.test.ts +++ b/src/shared/modules/features/featureDuck.utils.test.ts @@ -29,7 +29,11 @@ describe('guessSemverVersion', () => { ['3.5.3', '3.5.3'], ['3.5', '3.5.0'], ['3.5-test', '3.5.0'], - ['4.0-aura', '4.0.0'] + ['4.0-aura', '4.0.0'], + ['2025.01.0', '2025.1.0'], + ['2025.07.15-1242', '2025.7.15-1242'], + ['2025.12.1', '2025.12.1'], + ['2025.1.1', '2025.1.1'] ] test.each(tests)( diff --git a/src/shared/modules/features/featureDuck.utils.ts b/src/shared/modules/features/featureDuck.utils.ts index db8e0d87d9c..82466242423 100644 --- a/src/shared/modules/features/featureDuck.utils.ts +++ b/src/shared/modules/features/featureDuck.utils.ts @@ -18,13 +18,16 @@ * along with this program. If not, see . */ import semver from 'semver' +import { getCleanedVersion } from '../dbMeta/dbMetaDuck' export const guessSemverVersion = (versionString: string | null) => { if (!versionString) { return null } - if (semver.valid(versionString)) { - return versionString + const cleanedVersion = + typeof versionString === 'string' ? getCleanedVersion(versionString) : null + if (cleanedVersion) { + return cleanedVersion } const coerceVersion = semver.coerce(versionString)