diff --git a/.eslintrc.json b/.eslintrc.json index 18d6053bb..ac28096a7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -55,6 +55,9 @@ "@typescript-eslint/no-use-before-define": [ "off" ], + "@typescript-eslint/explicit-module-boundary-types": [ + "off" + ], "@typescript-eslint/no-angle-bracket-type-assertion": [ "off" ], diff --git a/jest.config.js b/jest.config.js index d1debdf9d..5d1424f69 100644 --- a/jest.config.js +++ b/jest.config.js @@ -27,10 +27,7 @@ module.exports = { coverageDirectory: 'coverage', // An array of regexp pattern strings used to skip coverage collection - coveragePathIgnorePatterns: [ - "/node_modules/", - "__MOCKS__", - ], + coveragePathIgnorePatterns: ['/node_modules/', '__MOCKS__'], // A list of reporter names that Jest uses when writing coverage reports // coverageReporters: [ @@ -171,7 +168,7 @@ module.exports = { // verbose: null, // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - watchPathIgnorePatterns: ['__MOCKS__'], + watchPathIgnorePatterns: ['node_modules', '__MOCKS__'], // Whether to use watchman for file crawling // watchman: true, diff --git a/src/contexts/scanner/scannerContextBinding.ts b/src/contexts/scanner/scannerContextBinding.ts index 48f5d9ca8..fab92a4d5 100644 --- a/src/contexts/scanner/scannerContextBinding.ts +++ b/src/contexts/scanner/scannerContextBinding.ts @@ -13,7 +13,7 @@ import { BitbucketService } from '../../services/bitbucket/BitbucketService'; import { GitLabService } from '../../services/gitlab/GitLabService'; import { PythonLanguageDetector } from '../../detectors/Python/PythonLanguageDetector'; import { ArgumentsProvider } from '../../scanner'; -import { IReporter, FixReporter, JSONReporter, CLIReporter, CIReporter, HTMLReporter, EnterpriseReporter } from '../../reporters'; +import { IReporter, FixReporter, JSONReporter, CLIReporter, CIReporter, HTMLReporter, DashboardReporter } from '../../reporters'; import { ServiceType, AccessType } from '../../detectors/IScanningStrategy'; export const bindScanningContext = (container: Container) => { @@ -77,7 +77,7 @@ const bindReporters = (container: Container, args: ArgumentsProvider, accessType } if (accessType !== AccessType.private || (accessType === AccessType.private && args.apiToken)) { - container.bind(Types.IReporter).to(EnterpriseReporter); + container.bind(Types.IReporter).to(DashboardReporter); } }; diff --git a/src/detectors/ScanningStrategyDetector.spec.ts b/src/detectors/ScanningStrategyDetector.spec.ts index d055bd5bb..e2acea8ef 100644 --- a/src/detectors/ScanningStrategyDetector.spec.ts +++ b/src/detectors/ScanningStrategyDetector.spec.ts @@ -51,6 +51,7 @@ describe('ScanningStrategyDetector', () => { getRemotes: () => [{ name: 'origin', refs: { fetch: repoPath, push: repoPath } }], }; }); + new GitHubNock('1', 'DXHeroes', 1, 'dx-scanner').getRepo('').reply(200); const scanningStrategyDetector = await createScanningStrategyDetector({ uri: '/local/rootDir/subDir' }); const result = await scanningStrategyDetector.detect(); diff --git a/src/detectors/ScanningStrategyDetector.ts b/src/detectors/ScanningStrategyDetector.ts index 69dda07a4..0b288e4a5 100644 --- a/src/detectors/ScanningStrategyDetector.ts +++ b/src/detectors/ScanningStrategyDetector.ts @@ -11,6 +11,7 @@ import { ScanningStrategyDetectorUtils } from './utils/ScanningStrategyDetectorU import { ErrorFactory } from '../lib/errors'; import { AccessType, ServiceType } from './IScanningStrategy'; import git from 'simple-git/promise'; +import nodePath from 'path'; @injectable() export class ScanningStrategyDetector implements IDetector { @@ -38,9 +39,10 @@ export class ScanningStrategyDetector implements IDetector { - let accessType: AccessType | undefined = undefined; - let remoteUrl: RemoteUrl = undefined; + let accessType: AccessType | undefined; + let remoteUrl: RemoteUrl; let rootPath: string | undefined; + let localPath: string | undefined; const path = ScanningStrategyDetectorUtils.normalizePath(this.argumentsProvider.uri); @@ -60,6 +62,7 @@ export class ScanningStrategyDetector implements IDetector { let practice: GitignoreIsPresentPractice; @@ -56,7 +57,10 @@ describe('GitignoreIsPresentPractice', () => { 'package.json': '{}', }); containerCtx.fixerContext.projectComponent.language = ProgrammingLanguage.Java; - + nock('https://api.github.com') + .get('/repos/github/gitignore/contents') + .reply(200, [{ name: 'Java.gitignore' }]); + nock('https://raw.githubusercontent.com').get('/github/gitignore/master/Java.gitignore').reply(200, '*.log'); await practice.fix(containerCtx.fixerContext); const exists = await containerCtx.virtualFileSystemService.exists('.gitignore'); diff --git a/src/reporters/CLIReporter.ts b/src/reporters/CLIReporter.ts index 8926f6e90..470930880 100644 --- a/src/reporters/CLIReporter.ts +++ b/src/reporters/CLIReporter.ts @@ -32,8 +32,8 @@ export class CLIReporter implements IReporter { buildReport(practicesAndComponents: PracticeWithContextForReporter[]): string { const lines: string[] = []; - const componentsWithPractices = ReporterUtils.getComponentsWithPractices(practicesAndComponents); - const dxScore = ReporterUtils.computeDXScore(practicesAndComponents); + const componentsWithPractices = ReporterUtils.getComponentsWithPractices(practicesAndComponents, this.scanningStrategy); + const dxScore = ReporterUtils.computeDXScore(practicesAndComponents, this.scanningStrategy); lines.push(bold(blue('----------------------------'))); lines.push(bold(blue('| |'))); diff --git a/src/reporters/EnterpriseReporter.spec.ts b/src/reporters/DashboardReporter.spec.ts similarity index 78% rename from src/reporters/EnterpriseReporter.spec.ts rename to src/reporters/DashboardReporter.spec.ts index 44646f783..10c324c5e 100644 --- a/src/reporters/EnterpriseReporter.spec.ts +++ b/src/reporters/DashboardReporter.spec.ts @@ -1,15 +1,24 @@ import { PracticeEvaluationResult } from '../model'; import { argumentsProviderFactory } from '../test/factories/ArgumentsProviderFactory'; import { practiceWithContextFactory } from '../test/factories/PracticeWithContextFactory'; -import { EnterpriseReporter } from './EnterpriseReporter'; +import { DashboardReporter } from './DashboardReporter'; +import { AccessType, ServiceType } from '../detectors/IScanningStrategy'; -describe('EnterpriseReporter', () => { +describe('DashboardReporter', () => { const practicingHighImpactPracticeWithCtx = practiceWithContextFactory(); const notPracticingHighImpactPracticeWithCtx = practiceWithContextFactory({ evaluation: PracticeEvaluationResult.notPracticing }); + const scanningStrategy = { + accessType: AccessType.public, + localPath: '.', + rootPath: undefined, + remoteUrl: 'https://github.com/DXHeroes/dx-scanner', + isOnline: true, + serviceType: ServiceType.github, + }; describe('#report', () => { it('one practicing practice', async () => { - const result = new EnterpriseReporter(argumentsProviderFactory()).buildReport([practicingHighImpactPracticeWithCtx]); + const result = new DashboardReporter(argumentsProviderFactory(), scanningStrategy).buildReport([practicingHighImpactPracticeWithCtx]); await expect(result.componentsWithDxScore).toContainObject({ dxScore: { points: { total: 100, max: 100, percentage: 100 }, value: '100% | 1/1' }, @@ -19,7 +28,7 @@ describe('EnterpriseReporter', () => { }); it('one practicing practice and one not practicing in two components', async () => { - const result = new EnterpriseReporter(argumentsProviderFactory()).buildReport([ + const result = new DashboardReporter(argumentsProviderFactory(), scanningStrategy).buildReport([ practicingHighImpactPracticeWithCtx, notPracticingHighImpactPracticeWithCtx, ]); @@ -47,7 +56,9 @@ describe('EnterpriseReporter', () => { }); it('one not practicing practice', async () => { - const result = new EnterpriseReporter(argumentsProviderFactory()).buildReport([notPracticingHighImpactPracticeWithCtx]); + const result = new DashboardReporter(argumentsProviderFactory(), scanningStrategy).buildReport([ + notPracticingHighImpactPracticeWithCtx, + ]); await expect(result.componentsWithDxScore).toContainObject({ dxScore: { diff --git a/src/reporters/EnterpriseReporter.ts b/src/reporters/DashboardReporter.ts similarity index 86% rename from src/reporters/EnterpriseReporter.ts rename to src/reporters/DashboardReporter.ts index e4e53923f..5968528be 100644 --- a/src/reporters/EnterpriseReporter.ts +++ b/src/reporters/DashboardReporter.ts @@ -6,20 +6,26 @@ import { PracticeWithContextForReporter, IReporter } from './IReporter'; import { ProjectComponent } from '../model'; import axios from 'axios'; import * as uuid from 'uuid'; +import { ScanningStrategy } from '../detectors'; +import { inspect } from 'util'; // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires const pjson = require('../../package.json'); @injectable() -export class EnterpriseReporter implements IReporter { +export class DashboardReporter implements IReporter { private readonly argumentsProvider: ArgumentsProvider; + private readonly scanningStrategy: ScanningStrategy; - constructor(@inject(Types.ArgumentsProvider) argumentsProvider: ArgumentsProvider) { + constructor( + @inject(Types.ArgumentsProvider) argumentsProvider: ArgumentsProvider, + @inject(Types.ScanningStrategy) scanningStrategy: ScanningStrategy, + ) { this.argumentsProvider = argumentsProvider; + this.scanningStrategy = scanningStrategy; } async report(practicesAndComponents: PracticeWithContextForReporter[]): Promise { const reportData = this.buildReport(practicesAndComponents); - try { // send data await axios.post('https://provider.dxscanner.io/api/v1/data-report', reportData, { @@ -33,9 +39,9 @@ export class EnterpriseReporter implements IReporter { } buildReport(practicesAndComponents: PracticeWithContextForReporter[]): DataReportDto { - const componentsWithPractices = ReporterUtils.getComponentsWithPractices(practicesAndComponents); + const componentsWithPractices = ReporterUtils.getComponentsWithPractices(practicesAndComponents, this.scanningStrategy); - const dxScore = ReporterUtils.computeDXScore(practicesAndComponents); + const dxScore = ReporterUtils.computeDXScore(practicesAndComponents, this.scanningStrategy); const report: DataReportDto = { componentsWithDxScore: [], diff --git a/src/reporters/FixReporter.ts b/src/reporters/FixReporter.ts index 2d8d5decf..f7b738d77 100644 --- a/src/reporters/FixReporter.ts +++ b/src/reporters/FixReporter.ts @@ -36,10 +36,10 @@ export class FixReporter implements IReporter { ): string { const lines: string[] = []; - const componentsWithPractices = ReporterUtils.getComponentsWithPractices(practicesAndComponents); - const componentsWithPracticesAfterFix = ReporterUtils.getComponentsWithPractices(practicesAndComponentsAfterFix); - const dxScore = ReporterUtils.computeDXScore(practicesAndComponents); - const dxScoreAfterFix = ReporterUtils.computeDXScore(practicesAndComponentsAfterFix); + const componentsWithPractices = ReporterUtils.getComponentsWithPractices(practicesAndComponents, this.scanningStrategy); + const componentsWithPracticesAfterFix = ReporterUtils.getComponentsWithPractices(practicesAndComponentsAfterFix, this.scanningStrategy); + const dxScore = ReporterUtils.computeDXScore(practicesAndComponents, this.scanningStrategy); + const dxScoreAfterFix = ReporterUtils.computeDXScore(practicesAndComponentsAfterFix, this.scanningStrategy); lines.push(bold(blue('----------------------------'))); lines.push(bold(blue('| |'))); diff --git a/src/reporters/HTMLReporter.spec.ts b/src/reporters/HTMLReporter.spec.ts index 22878167e..633dfb97c 100644 --- a/src/reporters/HTMLReporter.spec.ts +++ b/src/reporters/HTMLReporter.spec.ts @@ -5,6 +5,7 @@ import { argumentsProviderFactory } from '../test/factories/ArgumentsProviderFac import { FileSystemService } from '../services'; import path from 'path'; import { DirectoryJSON } from 'memfs/lib/volume'; +import { scanningStrategy } from '../scanner/__MOCKS__/ScanningStrategy.mock'; describe('HTMLReporter', () => { const practicingHighImpactPracticeWithCtx = practiceWithContextFactory(); @@ -24,7 +25,7 @@ describe('HTMLReporter', () => { describe('#report', () => { it('one practicing practice', async () => { - await new HTMLReporter(argumentsProviderFactory({ html: true }), virtualFileSystemService).report([ + await new HTMLReporter(argumentsProviderFactory({ html: true }), virtualFileSystemService, scanningStrategy).report([ practicingHighImpactPracticeWithCtx, ]); @@ -34,7 +35,7 @@ describe('HTMLReporter', () => { }); it('one practicing practice and one not practicing', async () => { - await new HTMLReporter(argumentsProviderFactory({ html: true }), virtualFileSystemService).report([ + await new HTMLReporter(argumentsProviderFactory({ html: true }), virtualFileSystemService, scanningStrategy).report([ practicingHighImpactPracticeWithCtx, notPracticingHighImpactPracticeWithCtx, ]); @@ -45,7 +46,7 @@ describe('HTMLReporter', () => { }); it('all impacted practices', async () => { - await new HTMLReporter(argumentsProviderFactory({ html: true }), virtualFileSystemService).report([ + await new HTMLReporter(argumentsProviderFactory({ html: true }), virtualFileSystemService, scanningStrategy).report([ practicingHighImpactPracticeWithCtx, notPracticingHighImpactPracticeWithCtx, practiceWithContextFactory({ diff --git a/src/reporters/HTMLReporter.ts b/src/reporters/HTMLReporter.ts index 86b72a0f3..08cc805e6 100644 --- a/src/reporters/HTMLReporter.ts +++ b/src/reporters/HTMLReporter.ts @@ -9,17 +9,21 @@ import { ArgumentsProvider } from '../scanner'; import { FileSystemService, GitServiceUtils } from '../services'; import { Types } from '../types'; import path from 'path'; +import { ScanningStrategy } from '../detectors'; @injectable() export class HTMLReporter implements IReporter { private readonly argumentsProvider: ArgumentsProvider; private readonly fileSystemService: FileSystemService; + private readonly scanningStrategy: ScanningStrategy; constructor( @inject(Types.ArgumentsProvider) argumentsProvider: ArgumentsProvider, @inject(FileSystemService) fileSystemService: FileSystemService, + @inject(Types.ScanningStrategy) scanningStrategy: ScanningStrategy, ) { this.argumentsProvider = argumentsProvider; + this.scanningStrategy = scanningStrategy; this.fileSystemService = fileSystemService; } @@ -37,8 +41,8 @@ export class HTMLReporter implements IReporter { buildReport(practicesAndComponents: PracticeWithContextForReporter[]): string { const lines: string[] = []; - const componentsWithPractices = ReporterUtils.getComponentsWithPractices(practicesAndComponents); - const dxScore = ReporterUtils.computeDXScore(practicesAndComponents); + const componentsWithPractices = ReporterUtils.getComponentsWithPractices(practicesAndComponents, this.scanningStrategy); + const dxScore = ReporterUtils.computeDXScore(practicesAndComponents, this.scanningStrategy); lines.push('

DX Scanner Result

'); diff --git a/src/reporters/IReporter.ts b/src/reporters/IReporter.ts index 74251dec2..8bdccb7cb 100644 --- a/src/reporters/IReporter.ts +++ b/src/reporters/IReporter.ts @@ -7,7 +7,7 @@ import { PracticeEvaluationResult, } from '../model'; import { PracticeData } from '../practices/IPractice'; -import { DataReportDto } from './EnterpriseReporter'; +import { DataReportDto } from './DashboardReporter'; export interface IReporter { report( diff --git a/src/reporters/ReporterUtils.spec.ts b/src/reporters/ReporterUtils.spec.ts index 203ff10be..5ee1c94ee 100644 --- a/src/reporters/ReporterUtils.spec.ts +++ b/src/reporters/ReporterUtils.spec.ts @@ -1,14 +1,32 @@ -import { PracticeEvaluationResult, PracticeImpact } from '../model'; +import { + PracticeEvaluationResult, + PracticeImpact, + ProgrammingLanguage, + ProjectComponentFramework, + ProjectComponentType, + ProjectComponentPlatform, +} from '../model'; +import nodePath from 'path'; import { ReporterUtils } from './ReporterUtils'; import { practiceWithContextFactory } from '../test/factories/PracticeWithContextFactory'; +import { AccessType, ServiceType } from '../detectors/IScanningStrategy'; +import { ScanningStrategy } from '../detectors'; describe('ReporterUtils', () => { const practicingHighImpactPracticeWithCtx = practiceWithContextFactory(); const notPracticingHighImpactPracticeWithCtx = practiceWithContextFactory({ evaluation: PracticeEvaluationResult.notPracticing }); + const scanningStrategy: ScanningStrategy = { + accessType: AccessType.public, + localPath: nodePath.resolve('./'), + rootPath: undefined, + remoteUrl: 'https://github.com/DXHeroes/dx-scanner', + isOnline: true, + serviceType: ServiceType.github, + }; describe('#computeDXScore', () => { it('one practicing practice', () => { - const result = ReporterUtils.computeDXScore([practicingHighImpactPracticeWithCtx]); + const result = ReporterUtils.computeDXScore([practicingHighImpactPracticeWithCtx], scanningStrategy); expect(result.points.max).toEqual(100); expect(result.points.total).toEqual(100); @@ -17,7 +35,10 @@ describe('ReporterUtils', () => { }); it('one practicing practice and one not practicing', () => { - const result = ReporterUtils.computeDXScore([practicingHighImpactPracticeWithCtx, notPracticingHighImpactPracticeWithCtx]); + const result = ReporterUtils.computeDXScore( + [practicingHighImpactPracticeWithCtx, notPracticingHighImpactPracticeWithCtx], + scanningStrategy, + ); expect(result.points.max).toEqual(200); expect(result.points.total).toEqual(100); @@ -29,7 +50,10 @@ describe('ReporterUtils', () => { notPracticingHighImpactPracticeWithCtx.overridenImpact = PracticeImpact.off; notPracticingHighImpactPracticeWithCtx.isOn = false; - const result = ReporterUtils.computeDXScore([practicingHighImpactPracticeWithCtx, notPracticingHighImpactPracticeWithCtx]); + const result = ReporterUtils.computeDXScore( + [practicingHighImpactPracticeWithCtx, notPracticingHighImpactPracticeWithCtx], + scanningStrategy, + ); expect(result.points.max).toEqual(100); expect(result.points.total).toEqual(100); @@ -41,7 +65,7 @@ describe('ReporterUtils', () => { notPracticingHighImpactPracticeWithCtx.overridenImpact = PracticeImpact.off; notPracticingHighImpactPracticeWithCtx.isOn = false; - const result = ReporterUtils.computeDXScore([notPracticingHighImpactPracticeWithCtx]); + const result = ReporterUtils.computeDXScore([notPracticingHighImpactPracticeWithCtx], scanningStrategy); expect(result.points.max).toEqual(0); expect(result.points.total).toEqual(0); @@ -52,7 +76,7 @@ describe('ReporterUtils', () => { describe('#getComponentsWithPractices', () => { it('returns one component with one practiceWithContext', () => { - const result = ReporterUtils.getComponentsWithPractices([practicingHighImpactPracticeWithCtx]); + const result = ReporterUtils.getComponentsWithPractices([practicingHighImpactPracticeWithCtx], scanningStrategy); expect(result.length).toEqual(1); @@ -62,7 +86,10 @@ describe('ReporterUtils', () => { }); it('returns one component with two practiceWithContext', () => { - const result = ReporterUtils.getComponentsWithPractices([practicingHighImpactPracticeWithCtx, practicingHighImpactPracticeWithCtx]); + const result = ReporterUtils.getComponentsWithPractices( + [practicingHighImpactPracticeWithCtx, practicingHighImpactPracticeWithCtx], + scanningStrategy, + ); expect(result.length).toEqual(1); @@ -73,19 +100,123 @@ describe('ReporterUtils', () => { }); it('returns two components on different path with one practiceWithContext each', () => { - const mockPracticeWithContext2nd = practiceWithContextFactory({ component: { path: './2nd' } }); + const mockPracticeWithContext1st = practiceWithContextFactory({ + component: { repositoryPath: 'https://github.com/dxheroes/dx-scanner', path: './1stService' }, + }); + const mockPracticeWithContext2nd = practiceWithContextFactory({ + component: { repositoryPath: 'https://github.com/dxheroes/dx-scanner', path: './2ndService' }, + }); - const result = ReporterUtils.getComponentsWithPractices([practicingHighImpactPracticeWithCtx, mockPracticeWithContext2nd]); + const result = ReporterUtils.getComponentsWithPractices([mockPracticeWithContext1st, mockPracticeWithContext2nd], scanningStrategy); expect(result.length).toEqual(2); - expect(result[0].component).toEqual(practicingHighImpactPracticeWithCtx.component); + expect(result[0].component).toEqual({ + repositoryPath: 'https://github.com/dxheroes/dx-scanner/tree/master/1stService', + path: './1stService', + language: ProgrammingLanguage.JavaScript, + framework: ProjectComponentFramework.UNKNOWN, + type: ProjectComponentType.UNKNOWN, + platform: ProjectComponentPlatform.UNKNOWN, + }); expect(result[0].practicesAndComponents.length).toEqual(1); - expect(result[0].practicesAndComponents[0]).toEqual(practicingHighImpactPracticeWithCtx); - - expect(result[1].component).toEqual(mockPracticeWithContext2nd.component); + expect(result[0].practicesAndComponents[0]).toEqual(mockPracticeWithContext1st); + + expect(result[1].component).toEqual({ + repositoryPath: 'https://github.com/dxheroes/dx-scanner/tree/master/2ndService', + path: './2ndService', + language: ProgrammingLanguage.JavaScript, + framework: ProjectComponentFramework.UNKNOWN, + type: ProjectComponentType.UNKNOWN, + platform: ProjectComponentPlatform.UNKNOWN, + }); expect(result[1].practicesAndComponents.length).toEqual(1); expect(result[1].practicesAndComponents[0]).toEqual(mockPracticeWithContext2nd); }); + + it('returns component with Github strategy and correct repositoryPath and path', () => { + const scStr: ScanningStrategy = { + accessType: AccessType.private, + localPath: 'myApp', + rootPath: undefined, + remoteUrl: 'https://user:password@github.com/DXHeroes/dx-scanner.git', + isOnline: true, + serviceType: ServiceType.github, + }; + const practiceWithContext = practiceWithContextFactory({ component: { repositoryPath: scStr.remoteUrl, path: scStr.localPath } }); + const result = ReporterUtils.getComponentsWithPractices([practiceWithContext], scStr); + + expect(result.length).toEqual(1); + + expect(result[0].component).toEqual({ + ...practiceWithContext.component, + repositoryPath: 'https://github.com/DXHeroes/dx-scanner/tree/master/myApp', + }); + expect(result[0].practicesAndComponents.length).toEqual(1); + expect(result[0].practicesAndComponents[0]).toEqual(practiceWithContext); + }); + + it('returns component with GitLab strategy and correct repositoryPath and path', () => { + const scStr: ScanningStrategy = { + accessType: AccessType.private, + localPath: 'myApp', + rootPath: undefined, + remoteUrl: 'https://user:password@gitlab.com/DXHeroes/dx-scanner.git', + isOnline: true, + serviceType: ServiceType.gitlab, + }; + const practiceWithContext = practiceWithContextFactory({ component: { repositoryPath: scStr.remoteUrl, path: scStr.localPath } }); + const result = ReporterUtils.getComponentsWithPractices([practiceWithContext], scStr); + + expect(result.length).toEqual(1); + + expect(result[0].component).toEqual({ + ...practiceWithContext.component, + repositoryPath: 'https://gitlab.com/DXHeroes/dx-scanner/tree/master/myApp', + }); + expect(result[0].practicesAndComponents.length).toEqual(1); + expect(result[0].practicesAndComponents[0]).toEqual(practiceWithContext); + }); + + it('returns component with Bitbucket strategy and correct repositoryPath and path', () => { + const scStr: ScanningStrategy = { + accessType: AccessType.private, + localPath: 'myApp', + rootPath: undefined, + remoteUrl: 'https://user:password@bitbucket.org/DXHeroes/dx-scanner.git', + isOnline: true, + serviceType: ServiceType.bitbucket, + }; + const practiceWithContext = practiceWithContextFactory({ component: { repositoryPath: scStr.remoteUrl, path: scStr.localPath } }); + const result = ReporterUtils.getComponentsWithPractices([practiceWithContext], scStr); + + expect(result.length).toEqual(1); + + expect(result[0].component).toEqual({ + ...practiceWithContext.component, + repositoryPath: 'https://bitbucket.org/DXHeroes/dx-scanner/src/master/myApp', + }); + expect(result[0].practicesAndComponents.length).toEqual(1); + expect(result[0].practicesAndComponents[0]).toEqual(practiceWithContext); + }); + + it('returns component with local strategy and correct repositoryPath and path', () => { + const scStr: ScanningStrategy = { + accessType: AccessType.private, + localPath: 'myApp', + rootPath: undefined, + remoteUrl: undefined, + isOnline: true, + serviceType: ServiceType.local, + }; + const practiceWithContext = practiceWithContextFactory({ component: { repositoryPath: scStr.remoteUrl, path: scStr.localPath } }); + const result = ReporterUtils.getComponentsWithPractices([practiceWithContext], scStr); + + expect(result.length).toEqual(1); + + expect(result[0].component).toEqual(practiceWithContext.component); + expect(result[0].practicesAndComponents.length).toEqual(1); + expect(result[0].practicesAndComponents[0]).toEqual(practiceWithContext); + }); }); }); diff --git a/src/reporters/ReporterUtils.ts b/src/reporters/ReporterUtils.ts index 7b5a578c8..a744c1e5e 100644 --- a/src/reporters/ReporterUtils.ts +++ b/src/reporters/ReporterUtils.ts @@ -3,19 +3,26 @@ import { assertNever } from '../lib/assertNever'; import { PracticeEvaluationResult, PracticeImpact, PracticeMetadata, ProjectComponent } from '../model'; import { PracticeWithContextForReporter } from './IReporter'; import { DXScoreOverallResult, DXScoreResult } from './model'; +import { GitServiceUtils } from '../services'; +import { ScanningStrategy } from '../detectors'; export class ReporterUtils { - static getComponentsWithPractices(practicesAndComponents: PracticeWithContextForReporter[]): ComponentWithPractices[] { - const result: { - component: ProjectComponent; - practicesAndComponents: PracticeWithContextForReporter[]; - }[] = []; + static getComponentsWithPractices( + practicesAndComponents: PracticeWithContextForReporter[], + scanningStrategy: ScanningStrategy, + ): ComponentWithPractices[] { + const result: ComponentResult[] = []; for (const pac of practicesAndComponents) { - let component = _.find(result, { component: { path: pac.component.path } }); + let component: ComponentResult | undefined = _.find(result, { component: { path: pac.component.path } }); if (!component) { const currentComponentReport = { - component: pac.component, + component: { + ...pac.component, + repositoryPath: + pac.component.repositoryPath && + GitServiceUtils.getPathOrRepoUrl(pac.component.repositoryPath, scanningStrategy, pac.component.path), + }, practicesAndComponents: [pac], }; @@ -30,10 +37,13 @@ export class ReporterUtils { return result; } - static computeDXScore(practicesAndComponents: PracticeWithContextForReporter[]): DXScoreOverallResult { + static computeDXScore( + practicesAndComponents: PracticeWithContextForReporter[], + scanningStrategy: ScanningStrategy, + ): DXScoreOverallResult { const scoreResult: DXScoreOverallResult = { ...this.computeDXScoreResult(practicesAndComponents), components: [] }; - const componentsWithPractices = this.getComponentsWithPractices(practicesAndComponents); + const componentsWithPractices = this.getComponentsWithPractices(practicesAndComponents, scanningStrategy); for (const cwp of componentsWithPractices) { scoreResult.components.push({ path: cwp.component.path, @@ -119,6 +129,11 @@ export class ReporterUtils { } } +type ComponentResult = { + component: ProjectComponent; + practicesAndComponents: PracticeWithContextForReporter[]; +}; + export type ComponentWithPractices = { component: ProjectComponent; practicesAndComponents: PracticeWithContextForReporter[]; diff --git a/src/reporters/builders/CIReportBuilder.ts b/src/reporters/builders/CIReportBuilder.ts index f294f9d92..66cf061f4 100644 --- a/src/reporters/builders/CIReportBuilder.ts +++ b/src/reporters/builders/CIReportBuilder.ts @@ -33,8 +33,8 @@ export class CIReportBuilder implements IReportBuilder { renderFromTemplate(): string { const lines: string[] = []; - const componentsWithPractices = ReporterUtils.getComponentsWithPractices(this.practicesAndComponents); - const dxScore = ReporterUtils.computeDXScore(this.practicesAndComponents); + const componentsWithPractices = ReporterUtils.getComponentsWithPractices(this.practicesAndComponents, this.scanningStrategy); + const dxScore = ReporterUtils.computeDXScore(this.practicesAndComponents, this.scanningStrategy); // render html comment as an ID for updating PR comment lines.push(CIReportBuilder.ciReportIndicator); diff --git a/src/reporters/index.ts b/src/reporters/index.ts index aca3a71c0..4626ea922 100644 --- a/src/reporters/index.ts +++ b/src/reporters/index.ts @@ -6,4 +6,4 @@ export { FixReporter } from './FixReporter'; export * from './model'; export * from './ReporterUtils'; export * from './HTMLReporter'; -export * from './EnterpriseReporter'; +export * from './DashboardReporter'; diff --git a/src/services/git/GitServiceUtils.spec.ts b/src/services/git/GitServiceUtils.spec.ts index f1363456d..a1d81e069 100644 --- a/src/services/git/GitServiceUtils.spec.ts +++ b/src/services/git/GitServiceUtils.spec.ts @@ -19,7 +19,7 @@ describe('GitServiceUtils', () => { const response = GitServiceUtils.getUrlToRepo( 'https://www.bitbucket.com/pypy/pypy.git', { ...scanningStrategy, serviceType: ServiceType.bitbucket }, - '/component', + 'component', ); expect(response).toEqual('https://www.bitbucket.com/pypy/pypy/src/master/component'); }); @@ -48,7 +48,7 @@ describe('GitServiceUtils', () => { type: ProjectComponentType.Library, repositoryPath: 'https://www.github.com/DXHeroes/dx-scanner', }; - scanningStrategy.localPath = '../dx-scannerSAJK'; + scanningStrategy.localPath = '../dx-scannerSAJK/'; const response = GitServiceUtils.getComponentPath(componentMock, scanningStrategy); expect(response).toEqual('https://www.github.com/DXHeroes/dx-scanner/tree/master/component'); diff --git a/src/services/git/GitServiceUtils.ts b/src/services/git/GitServiceUtils.ts index 3dbb3eb52..572ac0ce8 100644 --- a/src/services/git/GitServiceUtils.ts +++ b/src/services/git/GitServiceUtils.ts @@ -1,5 +1,6 @@ import gitUrlParse from 'git-url-parse'; -import _ from 'lodash'; +import _, { replace } from 'lodash'; +import nodePath from 'path'; import { ScanningStrategy } from '../../detectors'; import { ServiceType } from '../../detectors/IScanningStrategy'; import { assertNever } from '../../lib/assertNever'; @@ -9,10 +10,11 @@ export class GitServiceUtils { static getUrlToRepo = (url: string, scanningStrategy: ScanningStrategy, path?: string | undefined, branch = 'master') => { const parsedUrl = gitUrlParse(url); - let completeUrl = `${parsedUrl.protocol}://${parsedUrl.resource}/${parsedUrl.owner}/${parsedUrl.name}`; + let completeUrl = `https://${parsedUrl.resource}/${parsedUrl.owner}/${parsedUrl.name}`; if (path) { - completeUrl += GitServiceUtils.getPath(path, branch || parsedUrl.ref, scanningStrategy.serviceType!); + const relPath = replace(path, scanningStrategy.rootPath || '', ''); + completeUrl += GitServiceUtils.getPath(relPath, branch || parsedUrl.ref, scanningStrategy.serviceType!); } return completeUrl; @@ -30,21 +32,24 @@ export class GitServiceUtils { }; static getPath = (componentPath: string, branch = 'master', serviceType: ServiceType) => { - switch (serviceType) { - case ServiceType.github: - return `/tree/${branch}${componentPath}`; - case ServiceType.bitbucket: - return `/src/${branch}${componentPath}`; - case ServiceType.gitlab: - return `/tree/${branch}${componentPath}`; - case ServiceType.local: - return componentPath; - case ServiceType.git: - return `${branch}${componentPath}`; - - default: - return assertNever(serviceType); - } + const resPath = (): string => { + switch (serviceType) { + case ServiceType.github: + return `/tree/${branch}/${componentPath}`; + case ServiceType.bitbucket: + return `/src/${branch}/${componentPath}`; + case ServiceType.gitlab: + return `/tree/${branch}/${componentPath}`; + case ServiceType.local: + return componentPath; + case ServiceType.git: + return `${branch}/${componentPath}`; + + default: + return assertNever(serviceType); + } + }; + return nodePath.normalize(resPath()); }; static getRepoName = (repositoryPath: string | undefined, path: string): string => { @@ -90,6 +95,11 @@ export class GitServiceUtils { // if scanner is running remotely, concat repo path with component path, if not return local path directly return urlComponentPath ? (repoPath += urlComponentPath) : component.path; }; + + static getComponentLocalPath = (component: ProjectComponent, scanningStrategy: ScanningStrategy): string => { + const cwp = _.replace(component.path, scanningStrategy.localPath, ''); + return nodePath.basename(cwp); + }; } export interface ParsedUrl {