From d62e6a81b49dbf87506b9b8bbaed17c87fccf8e2 Mon Sep 17 00:00:00 2001 From: ryzzaki Date: Mon, 2 Dec 2019 14:43:48 +0000 Subject: [PATCH 01/11] feat(Kotlin): added Kotlin recognition to Language Detector --- src/detectors/Java/JavaLanguageDetector.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/detectors/Java/JavaLanguageDetector.ts b/src/detectors/Java/JavaLanguageDetector.ts index 1311b9e4e..a24fbeb78 100644 --- a/src/detectors/Java/JavaLanguageDetector.ts +++ b/src/detectors/Java/JavaLanguageDetector.ts @@ -19,6 +19,7 @@ export class JavaLanguageDetector implements ILanguageDetector { const result: LanguageAtPath[] = []; let packageFiles: Metadata[] = await this.fileInspector.scanFor(fileNameRegExp('pom.xml'), '/'); const isMaven: boolean = packageFiles.length > 0; + const hasKtFiles = (await this.fileInspector.scanFor(fileExtensionRegExp(['kt', 'kts']), '/')).length > 0; if (!isMaven) { packageFiles = await this.fileInspector.scanFor(fileNameRegExp('build.gradle'), '/'); } @@ -27,13 +28,13 @@ export class JavaLanguageDetector implements ILanguageDetector { result.push({ language: ProgrammingLanguage.Java, path }); } } else { - const javaFiles: Metadata[] = await this.fileInspector.scanFor(fileExtensionRegExp(['java']), '/'); - if (javaFiles.length === 0) { + const javaOrKtFiles: Metadata[] = await this.fileInspector.scanFor(fileExtensionRegExp(['java', 'kt', 'kts']), '/'); + if (javaOrKtFiles.length === 0) { return result; } - const dirsWithProjects = uniq(javaFiles.map((f) => nodePath.dirname(f.path))); + const dirsWithProjects = uniq(javaOrKtFiles.map((f) => nodePath.dirname(f.path))); const commonPath = sharedSubpath(dirsWithProjects); - result.push({ language: ProgrammingLanguage.Java, path: commonPath }); + result.push({ language: hasKtFiles ? ProgrammingLanguage.Kotlin : ProgrammingLanguage.Java, path: commonPath }); } return result; } From 3cb77c68133ce797977341e825894fa37e00e4db Mon Sep 17 00:00:00 2001 From: ryzzaki Date: Sat, 7 Dec 2019 18:19:03 +0000 Subject: [PATCH 02/11] fix(java): improved PackageInspector for Maven to include groupId --- src/inspectors/package/JavaPackageInspector.ts | 3 ++- src/inspectors/package/JavaPackageInspectorMaven.spec.ts | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/inspectors/package/JavaPackageInspector.ts b/src/inspectors/package/JavaPackageInspector.ts index 103ae4d36..174b8ae6f 100644 --- a/src/inspectors/package/JavaPackageInspector.ts +++ b/src/inspectors/package/JavaPackageInspector.ts @@ -73,7 +73,8 @@ export class JavaPackageInspector extends PackageInspectorBase { for (const xmlDependency of xmlDependencies) { const dependencyAttributes = xmlDependency.dependency.values(); for (const attribute of dependencyAttributes) { - this.parsedDependencies.push({ packageName: String(attribute.artifactId.pop()), version: String(attribute.version.pop()) }); + const packageName = `${attribute.groupId.pop()}:${attribute.artifactId.pop()}`; + this.parsedDependencies.push({ packageName, version: String(attribute.version.pop()) }); } } }); diff --git a/src/inspectors/package/JavaPackageInspectorMaven.spec.ts b/src/inspectors/package/JavaPackageInspectorMaven.spec.ts index e945fa084..5953c2b2a 100644 --- a/src/inspectors/package/JavaPackageInspectorMaven.spec.ts +++ b/src/inspectors/package/JavaPackageInspectorMaven.spec.ts @@ -30,12 +30,12 @@ describe('JavaPackageInspector Maven', () => { it('Parses the packages correctly', async () => { await inspector.init(); - const pkg = inspector.findPackage('spring-boot-starter-actuator'); + const pkg = inspector.findPackage('org.springframework.boot:spring-boot-starter-actuator'); if (!pkg) { fail(); } else { - expect(pkg.name).toEqual('spring-boot-starter-actuator'); + expect(pkg.name).toEqual('org.springframework.boot:spring-boot-starter-actuator'); expect(pkg.requestedVersion.value).toEqual('2.1.8'); expect(pkg.requestedVersion.major).toEqual('2'); expect(pkg.requestedVersion.minor).toEqual('1'); @@ -50,11 +50,11 @@ describe('JavaPackageInspector Maven', () => { }); it('Returns true package if it exists', () => { - expect(inspector.hasPackage('mysql-connector-java')).toBe(true); + expect(inspector.hasPackage('mysql:mysql-connector-java')).toBe(true); }); it('Returns undefined if the package does not exist', async () => { - const pkg = inspector.findPackage('spring-boot-autoconfigure'); + const pkg = inspector.findPackage('org.springframework.boot:spring-boot-autoconfigure'); expect(pkg).toBeUndefined(); }); }); From 258c44e40262d3169a4cd379377d293ead1fccc4 Mon Sep 17 00:00:00 2001 From: ryzzaki Date: Sat, 7 Dec 2019 18:20:53 +0000 Subject: [PATCH 03/11] feat(java): Added Major Version Dependency check practice for Java --- .../Java/JavaDependenciesVersionMajorLevel.ts | 73 +++++++++++++++++++ src/practices/index.ts | 2 + 2 files changed, 75 insertions(+) create mode 100644 src/practices/Java/JavaDependenciesVersionMajorLevel.ts diff --git a/src/practices/Java/JavaDependenciesVersionMajorLevel.ts b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts new file mode 100644 index 000000000..4b142baf4 --- /dev/null +++ b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts @@ -0,0 +1,73 @@ +import { PracticeContext } from '../../contexts/practice/PracticeContext'; +import { Package } from '../../inspectors/IPackageInspector'; +import { PackageInspectorBase, SemverLevel } from '../../inspectors/package/PackageInspectorBase'; +import { PracticeEvaluationResult, PracticeImpact, ProgrammingLanguage } from '../../model'; +import { DxPractice } from '../DxPracticeDecorator'; +import { IPractice } from '../IPractice'; +import * as axios from 'axios'; + +@DxPractice({ + id: 'Java.DependenciesVersionMajorLevel', + name: 'Update Dependencies of Major Level', + impact: PracticeImpact.medium, + suggestion: 'Keep the dependencies updated to have all possible features. Use, for example, Renovate Bot.', + reportOnlyOnce: true, + url: 'https://renovatebot.com/', +}) +export class JavaDependenciesVersionMajorLevel implements IPractice { + async isApplicable(ctx: PracticeContext): Promise { + return ctx.projectComponent.language === ProgrammingLanguage.Java || ctx.projectComponent.language === ProgrammingLanguage.Kotlin; + } + + async evaluate(ctx: PracticeContext): Promise { + if (ctx.fileInspector === undefined || ctx.packageInspector === undefined) { + return PracticeEvaluationResult.unknown; + } + + const pkgs = ctx.packageInspector.packages; + + if (pkgs === undefined) { + return PracticeEvaluationResult.unknown; + } + + const result = await JavaDependenciesVersionMajorLevel.searchMavenCentral(pkgs, 5); + const practiceEvaluationResult = JavaDependenciesVersionMajorLevel.isPracticing(result, SemverLevel.major, pkgs); + + return practiceEvaluationResult || PracticeEvaluationResult.practicing; + } + + static async searchMavenCentral(pkgs: Package[] | undefined, rows: number) { + const latestVersionsJson: { [key: string]: string } = {}; + const URL = 'http://search.maven.org/solrsearch/select?q='; + if (pkgs) { + for (const p of pkgs) { + const listOfIds = p.name.split(':', 2); + const listVersionsEndpoint = `${URL}${listOfIds[0]}+AND+a:${listOfIds[1]}&rows=${rows}&wt=json`; + await axios.default.get(listVersionsEndpoint).then((response) => { + latestVersionsJson[p.name] = String(response.data.response.docs.pop().latestVersion); + }); + } + } + return latestVersionsJson; + } + + static isPracticing( + result: { [key: string]: string }, + semverVersion: SemverLevel, + pkgs: Package[], + ): PracticeEvaluationResult | undefined { + for (const packageName in result) { + const parsedVersion = PackageInspectorBase.semverToPackageVersion(result[packageName]); + if (parsedVersion) { + for (const pkg of pkgs) { + if (pkg.name === packageName) { + if (parsedVersion[semverVersion] > pkg.lockfileVersion[semverVersion]) { + return PracticeEvaluationResult.notPracticing; + } + } + } + } + } + return undefined; + } +} diff --git a/src/practices/index.ts b/src/practices/index.ts index 08aa75ec8..e17a1df48 100644 --- a/src/practices/index.ts +++ b/src/practices/index.ts @@ -21,6 +21,7 @@ import { DependenciesVersionMajorLevel } from './JavaScript/DependenciesVersionM import { ESLintWithoutErrorsPractice } from './JavaScript/ESLintWithoutErrorsPractice'; import { TsGitignoreCorrectlySetPractice } from './TypeScript/TsGitignoreCorrectlySetPractice'; import { DependenciesVersionMinorPatchLevel } from './JavaScript/DependenciesVersionMinorPatchLevel'; +import { JavaDependenciesVersionMajorLevel } from './Java/JavaDependenciesVersionMajorLevel'; // register practices here export const practices = [ @@ -42,6 +43,7 @@ export const practices = [ DockerizationUsedPractice, EditorConfigIsPresentPractice, DependenciesVersionMajorLevel, + JavaDependenciesVersionMajorLevel, DependenciesVersionMinorPatchLevel, GitignoreIsPresentPractice, JsGitignoreCorrectlySetPractice, From 503614aee9afee241472f83e5adf8b3a52dd6d41 Mon Sep 17 00:00:00 2001 From: ryzzaki Date: Sun, 8 Dec 2019 11:39:54 +0000 Subject: [PATCH 04/11] refactor: reduced code duplication --- .../Java/JavaDependenciesVersionMajorLevel.ts | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/src/practices/Java/JavaDependenciesVersionMajorLevel.ts b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts index 4b142baf4..324fb4ed8 100644 --- a/src/practices/Java/JavaDependenciesVersionMajorLevel.ts +++ b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts @@ -1,10 +1,11 @@ import { PracticeContext } from '../../contexts/practice/PracticeContext'; import { Package } from '../../inspectors/IPackageInspector'; -import { PackageInspectorBase, SemverLevel } from '../../inspectors/package/PackageInspectorBase'; +import { SemverLevel } from '../../inspectors/package/PackageInspectorBase'; import { PracticeEvaluationResult, PracticeImpact, ProgrammingLanguage } from '../../model'; import { DxPractice } from '../DxPracticeDecorator'; import { IPractice } from '../IPractice'; import * as axios from 'axios'; +import { DependenciesVersionMajorLevel } from '../JavaScript/DependenciesVersionMajorLevel'; @DxPractice({ id: 'Java.DependenciesVersionMajorLevel', @@ -31,7 +32,7 @@ export class JavaDependenciesVersionMajorLevel implements IPractice { } const result = await JavaDependenciesVersionMajorLevel.searchMavenCentral(pkgs, 5); - const practiceEvaluationResult = JavaDependenciesVersionMajorLevel.isPracticing(result, SemverLevel.major, pkgs); + const practiceEvaluationResult = DependenciesVersionMajorLevel.isPracticing(result, SemverLevel.major, pkgs); return practiceEvaluationResult || PracticeEvaluationResult.practicing; } @@ -50,24 +51,4 @@ export class JavaDependenciesVersionMajorLevel implements IPractice { } return latestVersionsJson; } - - static isPracticing( - result: { [key: string]: string }, - semverVersion: SemverLevel, - pkgs: Package[], - ): PracticeEvaluationResult | undefined { - for (const packageName in result) { - const parsedVersion = PackageInspectorBase.semverToPackageVersion(result[packageName]); - if (parsedVersion) { - for (const pkg of pkgs) { - if (pkg.name === packageName) { - if (parsedVersion[semverVersion] > pkg.lockfileVersion[semverVersion]) { - return PracticeEvaluationResult.notPracticing; - } - } - } - } - } - return undefined; - } } From 5b6f05352fd2362ea2348a5f2e0332b2e830ca2b Mon Sep 17 00:00:00 2001 From: ryzzaki Date: Mon, 9 Dec 2019 09:56:41 +0000 Subject: [PATCH 05/11] init test for JavaDependenciesVersionMajorLevel --- .../JavaDependenciesVersionMajorLevel.spec.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/practices/Java/JavaDependenciesVersionMajorLevel.spec.ts diff --git a/src/practices/Java/JavaDependenciesVersionMajorLevel.spec.ts b/src/practices/Java/JavaDependenciesVersionMajorLevel.spec.ts new file mode 100644 index 000000000..522b6edea --- /dev/null +++ b/src/practices/Java/JavaDependenciesVersionMajorLevel.spec.ts @@ -0,0 +1,46 @@ +import { PracticeEvaluationResult } from '../../model'; +import { JavaDependenciesVersionMajorLevel } from './JavaDependenciesVersionMajorLevel'; +import { createTestContainer, TestContainerContext } from '../../inversify.config'; +import { mockPackage } from '../../test/helpers/mockPackage'; +import { JavaPackageInspector } from '../../inspectors'; +import * as axios from 'axios'; +jest.mock('axios'); + +describe('JavaDependenciesVersionPractice of Major Level', () => { + let practice: JavaDependenciesVersionMajorLevel; + let containerCtx: TestContainerContext; + const MockedJavaPackageInspector = >(JavaPackageInspector); + let mockJavaPackageInspector: JavaPackageInspector; + const mockedAxios = axios.default.get; + + beforeAll(async () => { + containerCtx = createTestContainer(); + containerCtx.container.bind('JavaDependenciesVersionMajorLevel').to(JavaDependenciesVersionMajorLevel); + practice = containerCtx.container.get('JavaDependenciesVersionMajorLevel'); + mockJavaPackageInspector = new MockedJavaPackageInspector(); + }); + + afterEach(async () => { + containerCtx.virtualFileSystemService.clearFileSystem(); + containerCtx.practiceContext.fileInspector!.purgeCache(); + }); + + it('not practicing if newer dependency versions of major level exists', async () => { + mockedAxios.mockResolvedValueOnce({ data: { response: { docs: [{ latestVersion: '2.2.1.RELEASE' }] } } }); + mockJavaPackageInspector.packages = [mockPackage('org.springframework.boot:spring-boot-starter-actuator')]; + containerCtx.practiceContext.packageInspector!.packages = mockJavaPackageInspector.packages; + + const evaluated = await practice.evaluate(containerCtx.practiceContext); + expect(evaluated).toEqual(PracticeEvaluationResult.notPracticing); + }); + + it('practicing if newest dependency version of major level does not exist', async () => { + mockedAxios.mockResolvedValueOnce({ data: { response: { docs: [{ latestVersion: '' }] } } }); + + mockJavaPackageInspector.packages = [mockPackage('org.springframework.boot:spring-boot-starter-actuator')]; + containerCtx.practiceContext.packageInspector!.packages = mockJavaPackageInspector.packages; + + const evaluated = await practice.evaluate(containerCtx.practiceContext); + expect(evaluated).toEqual(PracticeEvaluationResult.practicing); + }); +}); From ed278337c527a4d494e7d5cb2d64c947b1551054 Mon Sep 17 00:00:00 2001 From: ryzzaki Date: Mon, 9 Dec 2019 09:57:18 +0000 Subject: [PATCH 06/11] init test for JavaDependenciesVersionMinorPatchLevel --- ...DependenciesVersionMinorPatchLevel.spec.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/practices/Java/JavaDependenciesVersionMinorPatchLevel.spec.ts diff --git a/src/practices/Java/JavaDependenciesVersionMinorPatchLevel.spec.ts b/src/practices/Java/JavaDependenciesVersionMinorPatchLevel.spec.ts new file mode 100644 index 000000000..fb518bb66 --- /dev/null +++ b/src/practices/Java/JavaDependenciesVersionMinorPatchLevel.spec.ts @@ -0,0 +1,47 @@ +import { mockPackage } from '../../test/helpers/mockPackage'; +import { createTestContainer, TestContainerContext } from '../../inversify.config'; +import { PracticeEvaluationResult } from '../../model'; +import { JavaDependenciesVersionMinorPatchLevel } from './JavaDependenciesVersionMinorPatchLevel'; +import { JavaPackageInspector } from '../../inspectors'; +import * as axios from 'axios'; +jest.mock('axios'); + +describe('JavaDependenciesVersionPractice of Minor and Patch Level', () => { + let practice: JavaDependenciesVersionMinorPatchLevel; + let containerCtx: TestContainerContext; + const MockedJavaPackageInspector = >(JavaPackageInspector); + let mockJavaPackageInspector: JavaPackageInspector; + const mockedAxios = axios.default.get; + + beforeAll(async () => { + containerCtx = createTestContainer(); + containerCtx.container.bind('JavaDependenciesVersionMinorPatchLevel').to(JavaDependenciesVersionMinorPatchLevel); + practice = containerCtx.container.get('JavaDependenciesVersionMinorPatchLevel'); + mockJavaPackageInspector = new MockedJavaPackageInspector(); + }); + + afterEach(async () => { + containerCtx.virtualFileSystemService.clearFileSystem(); + containerCtx.practiceContext.fileInspector!.purgeCache(); + }); + + it('not practicing if newer dependency versions of minor or patch level exists', async () => { + mockedAxios.mockResolvedValueOnce({ data: { response: { docs: [{ latestVersion: '2.2.1.RELEASE' }] } } }); + + mockJavaPackageInspector.packages = [mockPackage('org.springframework.boot:spring-boot-starter-actuator')]; + containerCtx.practiceContext.packageInspector!.packages = mockJavaPackageInspector.packages; + + const evaluated = await practice.evaluate(containerCtx.practiceContext); + expect(evaluated).toEqual(PracticeEvaluationResult.notPracticing); + }); + + it('practicing if newest dependency version dependency of minor or patch level does not exist', async () => { + mockedAxios.mockResolvedValueOnce({ data: { response: { docs: [{ latestVersion: '' }] } } }); + + mockJavaPackageInspector.packages = [mockPackage('org.springframework.boot:spring-boot-starter-actuator')]; + containerCtx.practiceContext.packageInspector!.packages = mockJavaPackageInspector.packages; + + const evaluated = await practice.evaluate(containerCtx.practiceContext); + expect(evaluated).toEqual(PracticeEvaluationResult.practicing); + }); +}); From c5aaa4c66349752f0ea21e568925a0dd2dfb29f6 Mon Sep 17 00:00:00 2001 From: ryzzaki Date: Sat, 14 Dec 2019 13:30:47 +0000 Subject: [PATCH 07/11] fix: Kotlin recognition fixed & improved detector --- src/detectors/Java/JavaLanguageDetector.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/detectors/Java/JavaLanguageDetector.ts b/src/detectors/Java/JavaLanguageDetector.ts index a24fbeb78..491743979 100644 --- a/src/detectors/Java/JavaLanguageDetector.ts +++ b/src/detectors/Java/JavaLanguageDetector.ts @@ -19,19 +19,21 @@ export class JavaLanguageDetector implements ILanguageDetector { const result: LanguageAtPath[] = []; let packageFiles: Metadata[] = await this.fileInspector.scanFor(fileNameRegExp('pom.xml'), '/'); const isMaven: boolean = packageFiles.length > 0; - const hasKtFiles = (await this.fileInspector.scanFor(fileExtensionRegExp(['kt', 'kts']), '/')).length > 0; + const ktFiles: Metadata[] = await this.fileInspector.scanFor(fileExtensionRegExp(['kt', 'kts']), '/'); + const hasKtFiles = ktFiles.length > 0; if (!isMaven) { packageFiles = await this.fileInspector.scanFor(fileNameRegExp('build.gradle'), '/'); } if (packageFiles.length > 0) { for (const path of packageFiles.map((file) => nodePath.dirname(file.path))) { - result.push({ language: ProgrammingLanguage.Java, path }); + result.push({ language: hasKtFiles ? ProgrammingLanguage.Kotlin : ProgrammingLanguage.Java, path }); } } else { - const javaOrKtFiles: Metadata[] = await this.fileInspector.scanFor(fileExtensionRegExp(['java', 'kt', 'kts']), '/'); - if (javaOrKtFiles.length === 0) { + const javaFiles: Metadata[] = await this.fileInspector.scanFor(fileExtensionRegExp(['java']), '/'); + if (javaFiles.length === 0 && ktFiles.length === 0) { return result; } + const javaOrKtFiles = javaFiles.concat(ktFiles); const dirsWithProjects = uniq(javaOrKtFiles.map((f) => nodePath.dirname(f.path))); const commonPath = sharedSubpath(dirsWithProjects); result.push({ language: hasKtFiles ? ProgrammingLanguage.Kotlin : ProgrammingLanguage.Java, path: commonPath }); From b278dae9ba92daec39bb72b83e1d5612bd6a9c71 Mon Sep 17 00:00:00 2001 From: ryzzaki Date: Sat, 14 Dec 2019 13:32:14 +0000 Subject: [PATCH 08/11] fix: streamlined practice business logic & introduced new utils --- .../Java/JavaDependenciesVersionMajorLevel.ts | 41 +++++++------------ .../JavaDependenciesVersionMinorPatchLevel.ts | 10 +++-- .../DependenciesVersionEvaluationUtils.ts | 23 +++++++++++ 3 files changed, 44 insertions(+), 30 deletions(-) create mode 100644 src/practices/utils/DependenciesVersionEvaluationUtils.ts diff --git a/src/practices/Java/JavaDependenciesVersionMajorLevel.ts b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts index 800f7788b..acf7d4e5d 100644 --- a/src/practices/Java/JavaDependenciesVersionMajorLevel.ts +++ b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts @@ -1,10 +1,13 @@ import { PracticeContext } from '../../contexts/practice/PracticeContext'; import { Package } from '../../inspectors/IPackageInspector'; -import { SemverLevel, PackageInspectorBase } from '../../inspectors/package/PackageInspectorBase'; +import { SemverLevel } from '../../inspectors/package/PackageInspectorBase'; import { PracticeEvaluationResult, PracticeImpact, ProgrammingLanguage } from '../../model'; import { DxPractice } from '../DxPracticeDecorator'; -import { IPractice } from '../IPractice'; import * as axios from 'axios'; +import { DependenciesVersionEvaluationUtils } from '../utils/DependenciesVersionEvaluationUtils'; +import { PracticeBase } from '../PracticeBase'; +import { PkgToUpdate } from '../JavaScript/DependenciesVersionMajorLevel'; // @Todo: refactor this type to utils? +import { ReportDetailType } from '../../reporters/ReporterData'; @DxPractice({ id: 'Java.DependenciesVersionMajorLevel', @@ -14,26 +17,28 @@ import * as axios from 'axios'; reportOnlyOnce: true, url: 'https://renovatebot.com/', }) -export class JavaDependenciesVersionMajorLevel implements IPractice { +export class JavaDependenciesVersionMajorLevel extends PracticeBase { async isApplicable(ctx: PracticeContext): Promise { return ctx.projectComponent.language === ProgrammingLanguage.Java || ctx.projectComponent.language === ProgrammingLanguage.Kotlin; } async evaluate(ctx: PracticeContext): Promise { - if (ctx.fileInspector === undefined || ctx.packageInspector === undefined) { + if (!ctx.fileInspector || !ctx.packageInspector) { return PracticeEvaluationResult.unknown; } const pkgs = ctx.packageInspector.packages; - if (pkgs === undefined) { + if (!pkgs) { return PracticeEvaluationResult.unknown; } const result = await JavaDependenciesVersionMajorLevel.searchMavenCentral(pkgs, 5); - const practiceEvaluationResult = JavaDependenciesVersionMajorLevel.isPracticing(result, SemverLevel.major, pkgs); + const pkgsToUpdate = DependenciesVersionEvaluationUtils.packagesToBeUpdated(result, SemverLevel.major, pkgs); + this.setData(pkgsToUpdate); - return practiceEvaluationResult || PracticeEvaluationResult.practicing; + if (pkgsToUpdate.length > 0) return PracticeEvaluationResult.notPracticing; + return PracticeEvaluationResult.practicing; } static async searchMavenCentral(pkgs: Package[] | undefined, rows: number) { @@ -44,30 +49,14 @@ export class JavaDependenciesVersionMajorLevel implements IPractice { const listOfIds = p.name.split(':', 2); const listVersionsEndpoint = `${URL}${listOfIds[0]}+AND+a:${listOfIds[1]}&rows=${rows}&wt=json`; await axios.default.get(listVersionsEndpoint).then((response) => { - latestVersionsJson[p.name] = String(response.data.response.docs.pop().latestVersion); + latestVersionsJson[p.name] = `${response.data.response.docs.pop().latestVersion}`; }); } } return latestVersionsJson; } - static isPracticing( - result: { [key: string]: string }, - semverVersion: SemverLevel, - pkgs: Package[], - ): PracticeEvaluationResult | undefined { - for (const packageName in result) { - const parsedVersion = PackageInspectorBase.semverToPackageVersion(result[packageName]); - if (parsedVersion) { - for (const pkg of pkgs) { - if (pkg.name === packageName) { - if (parsedVersion[semverVersion] > pkg.lockfileVersion[semverVersion]) { - return PracticeEvaluationResult.notPracticing; - } - } - } - } - } - return undefined; + setData(pkgsToUpdate: PkgToUpdate[]): void { + this.data.details = [{ type: ReportDetailType.table, headers: ['Name', 'New', 'Current'], data: pkgsToUpdate }]; } } diff --git a/src/practices/Java/JavaDependenciesVersionMinorPatchLevel.ts b/src/practices/Java/JavaDependenciesVersionMinorPatchLevel.ts index f41e2cec5..7eac7999e 100644 --- a/src/practices/Java/JavaDependenciesVersionMinorPatchLevel.ts +++ b/src/practices/Java/JavaDependenciesVersionMinorPatchLevel.ts @@ -3,7 +3,9 @@ import { PracticeEvaluationResult, PracticeImpact, ProgrammingLanguage } from '. import { DxPractice } from '../DxPracticeDecorator'; import { IPractice } from '../IPractice'; import { JavaDependenciesVersionMajorLevel } from './JavaDependenciesVersionMajorLevel'; +import { DependenciesVersionEvaluationUtils } from '../utils/DependenciesVersionEvaluationUtils'; import { SemverLevel } from '../../inspectors/package/PackageInspectorBase'; +import { flatten } from 'lodash'; @DxPractice({ id: 'Java.DependenciesVersionMinorPatchLevel', @@ -30,13 +32,13 @@ export class JavaDependenciesVersionMinorPatchLevel extends JavaDependenciesVers const result = await JavaDependenciesVersionMajorLevel.searchMavenCentral(pkgs, 5); - const patchLevel = JavaDependenciesVersionMajorLevel.isPracticing(result, SemverLevel.patch, pkgs); - const minorLevel = JavaDependenciesVersionMajorLevel.isPracticing(result, SemverLevel.minor, pkgs); + const patchLevelPkgs = DependenciesVersionEvaluationUtils.packagesToBeUpdated(result, SemverLevel.patch, pkgs); + const minorLevelPkgs = DependenciesVersionEvaluationUtils.packagesToBeUpdated(result, SemverLevel.minor, pkgs); + this.setData(flatten([patchLevelPkgs, minorLevelPkgs])); - if (patchLevel === PracticeEvaluationResult.notPracticing || minorLevel === PracticeEvaluationResult.notPracticing) { + if (patchLevelPkgs.length > 0 || minorLevelPkgs.length > 0) { return PracticeEvaluationResult.notPracticing; } - return PracticeEvaluationResult.practicing; } } diff --git a/src/practices/utils/DependenciesVersionEvaluationUtils.ts b/src/practices/utils/DependenciesVersionEvaluationUtils.ts new file mode 100644 index 000000000..8d9ac10d1 --- /dev/null +++ b/src/practices/utils/DependenciesVersionEvaluationUtils.ts @@ -0,0 +1,23 @@ +import { SemverLevel, Package, PackageInspectorBase } from '../../inspectors'; +import { PkgToUpdate } from '../JavaScript/DependenciesVersionMajorLevel'; + +export class DependenciesVersionEvaluationUtils { + static packagesToBeUpdated(pkgsWithNewVersion: { [key: string]: string }, semverLevel: SemverLevel, pkgs: Package[]) { + const pkgsToUpdate: PkgToUpdate[] = []; + + for (const packageName in pkgsWithNewVersion) { + const parsedVersion = PackageInspectorBase.semverToPackageVersion(pkgsWithNewVersion[packageName]); + if (parsedVersion) { + for (const pkg of pkgs) { + if (pkg.name === packageName) { + if (parsedVersion[semverLevel] > pkg.lockfileVersion[semverLevel]) { + pkgsToUpdate.push({ name: pkg.name, newVersion: parsedVersion.value, currentVersion: pkg.lockfileVersion.value }); + } + } + } + } + } + + return pkgsToUpdate; + } +} From b3145c1c85464b9002d16394d0ee10d7c820907a Mon Sep 17 00:00:00 2001 From: ryzzaki Date: Sat, 14 Dec 2019 14:19:21 +0000 Subject: [PATCH 09/11] fix: replacing a string templator with query stringify --- src/practices/Java/JavaDependenciesVersionMajorLevel.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/practices/Java/JavaDependenciesVersionMajorLevel.ts b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts index acf7d4e5d..9b42f38e2 100644 --- a/src/practices/Java/JavaDependenciesVersionMajorLevel.ts +++ b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts @@ -8,6 +8,7 @@ import { DependenciesVersionEvaluationUtils } from '../utils/DependenciesVersion import { PracticeBase } from '../PracticeBase'; import { PkgToUpdate } from '../JavaScript/DependenciesVersionMajorLevel'; // @Todo: refactor this type to utils? import { ReportDetailType } from '../../reporters/ReporterData'; +import qs from 'qs'; @DxPractice({ id: 'Java.DependenciesVersionMajorLevel', @@ -43,11 +44,12 @@ export class JavaDependenciesVersionMajorLevel extends PracticeBase { static async searchMavenCentral(pkgs: Package[] | undefined, rows: number) { const latestVersionsJson: { [key: string]: string } = {}; - const URL = 'http://search.maven.org/solrsearch/select?q='; + const URL = 'http://search.maven.org/solrsearch/select?'; if (pkgs) { for (const p of pkgs) { const listOfIds = p.name.split(':', 2); - const listVersionsEndpoint = `${URL}${listOfIds[0]}+AND+a:${listOfIds[1]}&rows=${rows}&wt=json`; + const queryRequest = qs.stringify({ q: `${listOfIds[0]}+AND+a:${listOfIds[1]}`, rows, wt: 'json' }, { encode: false }); + const listVersionsEndpoint = `${URL}${queryRequest}`; await axios.default.get(listVersionsEndpoint).then((response) => { latestVersionsJson[p.name] = `${response.data.response.docs.pop().latestVersion}`; }); From d1215608b87e3e968909ad18da3b5ff97a28d729 Mon Sep 17 00:00:00 2001 From: ryzzaki Date: Mon, 16 Dec 2019 11:29:23 +0000 Subject: [PATCH 10/11] streamlined code for java dependencies --- .../Java/JavaDependenciesVersionMajorLevel.ts | 12 ++++-------- .../Java/JavaDependenciesVersionMinorPatchLevel.ts | 9 +++------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/practices/Java/JavaDependenciesVersionMajorLevel.ts b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts index 9b42f38e2..963b3da57 100644 --- a/src/practices/Java/JavaDependenciesVersionMajorLevel.ts +++ b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts @@ -13,10 +13,10 @@ import qs from 'qs'; @DxPractice({ id: 'Java.DependenciesVersionMajorLevel', name: 'Update Dependencies of Major Level', - impact: PracticeImpact.medium, - suggestion: 'Keep the dependencies updated to have all possible features. Use, for example, Renovate Bot.', + impact: PracticeImpact.small, + suggestion: 'Keep the dependencies updated to have all possible features. Use, for example, versions-maven-plugin', reportOnlyOnce: true, - url: 'https://renovatebot.com/', + url: 'https://www.mojohaus.org/versions-maven-plugin/', }) export class JavaDependenciesVersionMajorLevel extends PracticeBase { async isApplicable(ctx: PracticeContext): Promise { @@ -24,16 +24,12 @@ export class JavaDependenciesVersionMajorLevel extends PracticeBase { } async evaluate(ctx: PracticeContext): Promise { - if (!ctx.fileInspector || !ctx.packageInspector) { + if (!ctx.fileInspector || !ctx.packageInspector || !ctx.packageInspector.packages) { return PracticeEvaluationResult.unknown; } const pkgs = ctx.packageInspector.packages; - if (!pkgs) { - return PracticeEvaluationResult.unknown; - } - const result = await JavaDependenciesVersionMajorLevel.searchMavenCentral(pkgs, 5); const pkgsToUpdate = DependenciesVersionEvaluationUtils.packagesToBeUpdated(result, SemverLevel.major, pkgs); this.setData(pkgsToUpdate); diff --git a/src/practices/Java/JavaDependenciesVersionMinorPatchLevel.ts b/src/practices/Java/JavaDependenciesVersionMinorPatchLevel.ts index 7eac7999e..31478d841 100644 --- a/src/practices/Java/JavaDependenciesVersionMinorPatchLevel.ts +++ b/src/practices/Java/JavaDependenciesVersionMinorPatchLevel.ts @@ -11,9 +11,9 @@ import { flatten } from 'lodash'; id: 'Java.DependenciesVersionMinorPatchLevel', name: 'Update Dependencies of Minor and Patch Level', impact: PracticeImpact.high, - suggestion: 'Keep the dependencies updated to eliminate security concerns and compatibility issues. Use, for example, Renovate Bot.', + suggestion: 'Keep the dependencies updated to have all possible features. Use, for example, versions-maven-plugin', reportOnlyOnce: true, - url: 'https://renovatebot.com/', + url: 'https://www.mojohaus.org/versions-maven-plugin/', }) export class JavaDependenciesVersionMinorPatchLevel extends JavaDependenciesVersionMajorLevel implements IPractice { async isApplicable(ctx: PracticeContext): Promise { @@ -21,14 +21,11 @@ export class JavaDependenciesVersionMinorPatchLevel extends JavaDependenciesVers } async evaluate(ctx: PracticeContext): Promise { - if (ctx.fileInspector === undefined || ctx.packageInspector === undefined) { + if (!ctx.fileInspector || !ctx.packageInspector || !ctx.packageInspector.packages) { return PracticeEvaluationResult.unknown; } const pkgs = ctx.packageInspector.packages; - if (pkgs === undefined) { - return PracticeEvaluationResult.unknown; - } const result = await JavaDependenciesVersionMajorLevel.searchMavenCentral(pkgs, 5); From 5d839cd1c985c404b34631b3d79e70612a91aeba Mon Sep 17 00:00:00 2001 From: ryzzaki Date: Wed, 18 Dec 2019 09:36:06 +0100 Subject: [PATCH 11/11] refactor: moved evaluation of dependencies to utils --- .../Java/JavaDependenciesVersionMajorLevel.ts | 3 +-- .../DependenciesVersionMajorLevel.ts | 27 +++---------------- .../DependenciesVersionMinorPatchLevel.ts | 5 ++-- .../DependenciesVersionEvaluationUtils.ts | 3 ++- 4 files changed, 9 insertions(+), 29 deletions(-) diff --git a/src/practices/Java/JavaDependenciesVersionMajorLevel.ts b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts index 963b3da57..3c3f1c72c 100644 --- a/src/practices/Java/JavaDependenciesVersionMajorLevel.ts +++ b/src/practices/Java/JavaDependenciesVersionMajorLevel.ts @@ -4,9 +4,8 @@ import { SemverLevel } from '../../inspectors/package/PackageInspectorBase'; import { PracticeEvaluationResult, PracticeImpact, ProgrammingLanguage } from '../../model'; import { DxPractice } from '../DxPracticeDecorator'; import * as axios from 'axios'; -import { DependenciesVersionEvaluationUtils } from '../utils/DependenciesVersionEvaluationUtils'; +import { DependenciesVersionEvaluationUtils, PkgToUpdate } from '../utils/DependenciesVersionEvaluationUtils'; import { PracticeBase } from '../PracticeBase'; -import { PkgToUpdate } from '../JavaScript/DependenciesVersionMajorLevel'; // @Todo: refactor this type to utils? import { ReportDetailType } from '../../reporters/ReporterData'; import qs from 'qs'; diff --git a/src/practices/JavaScript/DependenciesVersionMajorLevel.ts b/src/practices/JavaScript/DependenciesVersionMajorLevel.ts index 33ce18ae2..3d679d341 100644 --- a/src/practices/JavaScript/DependenciesVersionMajorLevel.ts +++ b/src/practices/JavaScript/DependenciesVersionMajorLevel.ts @@ -1,11 +1,12 @@ import ncu from 'npm-check-updates'; import { PracticeContext } from '../../contexts/practice/PracticeContext'; import { Package } from '../../inspectors/IPackageInspector'; -import { PackageInspectorBase, SemverLevel } from '../../inspectors/package/PackageInspectorBase'; +import { SemverLevel } from '../../inspectors/package/PackageInspectorBase'; import { PracticeEvaluationResult, PracticeImpact, ProgrammingLanguage } from '../../model'; import { DxPractice } from '../DxPracticeDecorator'; import { PracticeBase } from '../PracticeBase'; import { ReportDetailType } from '../../reporters/ReporterData'; +import { DependenciesVersionEvaluationUtils, PkgToUpdate } from '../utils/DependenciesVersionEvaluationUtils'; @DxPractice({ id: 'JavaScript.DependenciesVersionMajorLevel', @@ -29,7 +30,7 @@ export class DependenciesVersionMajorLevelPractice extends PracticeBase { const pkgs = ctx.packageInspector.packages; const result = await this.runNcu(pkgs); - const pkgsToUpdate = this.packagesToBeUpdated(result, SemverLevel.major, pkgs); + const pkgsToUpdate = DependenciesVersionEvaluationUtils.packagesToBeUpdated(result, SemverLevel.major, pkgs); this.setData(pkgsToUpdate); if (pkgsToUpdate.length > 0) return PracticeEvaluationResult.notPracticing; @@ -51,29 +52,7 @@ export class DependenciesVersionMajorLevelPractice extends PracticeBase { return pkgsToBeUpdated; } - packagesToBeUpdated(pkgsWithNewVersion: { [key: string]: string }, semverLevel: SemverLevel, pkgs: Package[]) { - // packages with Major level to be updated - const pkgsToUpdate: PkgToUpdate[] = []; - - for (const packageName in pkgsWithNewVersion) { - const parsedVersion = PackageInspectorBase.semverToPackageVersion(pkgsWithNewVersion[packageName]); - if (parsedVersion) { - for (const pkg of pkgs) { - if (pkg.name === packageName) { - if (parsedVersion[semverLevel] > pkg.lockfileVersion[semverLevel]) { - pkgsToUpdate.push({ name: pkg.name, newVersion: parsedVersion.value, currentVersion: pkg.lockfileVersion.value }); - } - } - } - } - } - - return pkgsToUpdate; - } - setData(pkgsToUpdate: PkgToUpdate[]): void { this.data.details = [{ type: ReportDetailType.table, headers: ['Name', 'New', 'Current'], data: pkgsToUpdate }]; } } - -export type PkgToUpdate = { name: string; newVersion: string; currentVersion: string }; diff --git a/src/practices/JavaScript/DependenciesVersionMinorPatchLevel.ts b/src/practices/JavaScript/DependenciesVersionMinorPatchLevel.ts index 6009f0948..8de61dd81 100644 --- a/src/practices/JavaScript/DependenciesVersionMinorPatchLevel.ts +++ b/src/practices/JavaScript/DependenciesVersionMinorPatchLevel.ts @@ -3,6 +3,7 @@ import { PracticeEvaluationResult, PracticeImpact, ProgrammingLanguage } from '. import { DxPractice } from '../DxPracticeDecorator'; import { IPractice } from '../IPractice'; import { DependenciesVersionMajorLevelPractice } from './DependenciesVersionMajorLevel'; +import { DependenciesVersionEvaluationUtils } from '../utils/DependenciesVersionEvaluationUtils'; import { SemverLevel } from '../../inspectors/package/PackageInspectorBase'; import { flatten } from 'lodash'; @@ -30,8 +31,8 @@ export class DependenciesVersionMinorPatchLevelPractice extends DependenciesVers const result = await this.runNcu(pkgs); - const patchLevelPkgs = this.packagesToBeUpdated(result, SemverLevel.patch, pkgs); - const minorLevelPkgs = this.packagesToBeUpdated(result, SemverLevel.minor, pkgs); + const patchLevelPkgs = DependenciesVersionEvaluationUtils.packagesToBeUpdated(result, SemverLevel.patch, pkgs); + const minorLevelPkgs = DependenciesVersionEvaluationUtils.packagesToBeUpdated(result, SemverLevel.minor, pkgs); this.setData(flatten([patchLevelPkgs, minorLevelPkgs])); if (patchLevelPkgs.length > 0 || minorLevelPkgs.length > 0) { diff --git a/src/practices/utils/DependenciesVersionEvaluationUtils.ts b/src/practices/utils/DependenciesVersionEvaluationUtils.ts index 8d9ac10d1..82cebf033 100644 --- a/src/practices/utils/DependenciesVersionEvaluationUtils.ts +++ b/src/practices/utils/DependenciesVersionEvaluationUtils.ts @@ -1,5 +1,4 @@ import { SemverLevel, Package, PackageInspectorBase } from '../../inspectors'; -import { PkgToUpdate } from '../JavaScript/DependenciesVersionMajorLevel'; export class DependenciesVersionEvaluationUtils { static packagesToBeUpdated(pkgsWithNewVersion: { [key: string]: string }, semverLevel: SemverLevel, pkgs: Package[]) { @@ -21,3 +20,5 @@ export class DependenciesVersionEvaluationUtils { return pkgsToUpdate; } } + +export type PkgToUpdate = { name: string; newVersion: string; currentVersion: string };