Skip to content

Commit

Permalink
Feature: Possibility to override config (#45)
Browse files Browse the repository at this point in the history
* fix: Catch the ServiceError so it is possible catch the TypeError (etc).

* feat: Implement ConfigProvider to allow user to add config file to switch off some practices via name.

* feat: Bind the ConfigProvider.

* feat: implement ConfigProvider into Scanner.

* feat: Allow user to setup yaml config.

* WIP: ConfigProvider

* chore: add some architecture hints

* WIP: CongiProvider binding

* chore: fix config provider

* fix: Make the ConfigProvider work.

* WIP: try to save the default impact

* feat: Report the changed impact in CLI Reporter.

* fix: unnecessary async in JSONReport

* feat: add off type to PracticeImpact,
fix: rename offedPractices to practicesOff

* feat: Show deep JSON report.

* fix: refactor if else to ternary operator, change showHidden to false

* WIP: add config interface

* feat: add PracticeImpactType

* fix: Use the id of practice for switching off tha preacitces in config

* feat: Add info about config file in README.md

* Update README.md

Co-Authored-By: Prokop Simek <prokopsimek@users.noreply.github.com>

* Update src/contexts/projectComponent/projectComponentContextBinding.ts

Co-Authored-By: Prokop Simek <prokopsimek@users.noreply.github.com>

* Update src/scanner/Scanner.ts

Co-Authored-By: Prokop Simek <prokopsimek@users.noreply.github.com>

* fix: Show practices switched off in report,
fix: Use enum PracticeImpact instead of new type PracticeImpactType,
fix: add defaultImpact in decorator.

* refactor: Split the detectPractices() -> implement filterPractices() in ScannerUtils

* refactor: Add filtering just name from metadata to filterPractices()

* refactor: Passing practicesOff

* test: Add test for filterPractices()

* Update src/index.ts

* fix: remove unnecessary code

* fix: Change colour of output if no practice was switched off
  • Loading branch information
adelkahomolova authored and prokopsimek committed Sep 12, 2019
1 parent 6a01000 commit 7d5b33f
Show file tree
Hide file tree
Showing 20 changed files with 346 additions and 83 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,35 @@ dxscanner [path]
dxs [path]
```

## Configuration ⚙️
Add ```dxscannerrc.*``` config file to change default configuration. It can be a ```.json```, ```.yml``` even dotfile!

**Practices**
You can switch off practices you don't want to scan or change its impact. Use the id of the practice.

Possible impact:
```
high
medium
small
hint
off
```

Example :
```
{
"practices": {
"JavaScript.GitignoreCorrectlySet": "medium",
"JavaScript.LoggerUsed": "off"
}
}
```

## Contributing 👩‍💻 👨‍💻
Feel free to contribute to the DX Scanner. If you want to contribute, please follow our [Contribution Guide](CONTRIBUTING.md).

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@
"@oclif/config": "^1",
"@oclif/plugin-help": "^2",
"@octokit/rest": "^16.28.7",
"@types/js-yaml": "^3.12.1",
"axios": "^0.19.0",
"colors": "^1.3.3",
"debug": "^4.1.1",
"git-url-parse": "^11.1.2",
"glob": "^7.1.4",
"inversify": "^5.0.1",
"js-yaml": "^3.13.1",
"lodash": "^4.17.15",
"memfs": "^2.15.5",
"node-filter-async": "^1.1.3",
Expand Down
44 changes: 44 additions & 0 deletions src/contexts/ConfigProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { inject, injectable } from 'inversify';
import yaml from 'js-yaml';
import _ from 'lodash';
import { IFileInspector } from '../inspectors/IFileInspector';
import { Types } from '../types';
import { IConfigProvider, Config } from './IConfigProvider';

@injectable()
export class ConfigProvider implements IConfigProvider {
private readonly fileInspector: IFileInspector;
config: Config | undefined;

constructor(@inject(Types.IFileInspector) fileInspector: IFileInspector) {
this.fileInspector = fileInspector;
this.config = undefined;
}

async init() {
const regexConfigFile = new RegExp('dxscannerrc.', 'i');

const configFileMetadata = await this.fileInspector.scanFor(regexConfigFile, '/', { shallow: true });

if (configFileMetadata.length === 0) {
return undefined;
}
const configFile = configFileMetadata[0];

let parsedContent;
const content = await this.fileInspector.readFile(configFile.path);

if (configFile.extension === '.json' || configFile.extension === '') {
parsedContent = JSON.parse(content);
}
if (configFile.extension === '.yml' || configFile.extension === '.yaml') {
parsedContent = yaml.safeLoad(content);
}

this.config = parsedContent;
}

getOverridenPractice(practiceId: string) {
return _.get(this.config, ['practices', practiceId]);
}
}
43 changes: 43 additions & 0 deletions src/contexts/IConfigProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { PracticeImpact } from '../model';

export interface IConfigProvider {
init(): Promise<void>;
getOverridenPractice(practiceId: string): PracticeImpact;
}

export interface Config {
practices?: {
[key in Practices]?: PracticeImpact;
};
tokens?: {
[key in Service]: string;
};
}

enum Service {
Slack = 'Slack',
}

enum Practices {
'JavaScript.TypeScriptUsedPractice' = 'JavaScript.TypeScriptUsedPractice',
'JavaScript.PrettierUsedPractice' = 'JavaScript.PrettierUsedPractice',
'JavaScript.ESLintUsedPractice' = 'JavaScript.ESLintUsedPractice',
'LanguageIndependent.LockfileIsPresentPractice' = 'LanguageIndependent.LockfileIsPresentPractice',
'JavaScript.JsFrontendTestingFrameworkUsedPractice' = 'JavaScript.JsFrontendTestingFrameworkUsedPractice',
'JavaScript.JsBackendTestingFrameworkUsedPractice' = 'JavaScript.JsBackendTestingFrameworkUsedPractice',
'JavaScript.JsLoggerUsedPractice' = 'JavaScript.JsLoggerUsedPractice',
'LanguageIndependent.LicenseIsPresentPractice' = 'LanguageIndependent.LicenseIsPresentPractice',
'LanguageIndependent.ReadmeIsPresentPractice' = 'LanguageIndependent.ReadmeIsPresentPractice',
'LanguageIndependent.CIUsedPractice' = 'LanguageIndependent.CIUsedPractice',
'JavaScript.JsFEBuildtoolUsedPractice' = 'JavaScript.JsFEBuildtoolUsedPractice',
'JavaScript.JsPackageJsonConfigurationSetCorrectlyPractice' = 'JavaScript.JsPackageJsonConfigurationSetCorrectlyPractice',
'JavaScript.JsPackageManagementUsedPractice' = 'JavaScript.JsPackageManagementUsedPractice',
'JavaScript.DeprecatedTSLintPractice' = 'JavaScript.DeprecatedTSLintPractice',
'LanguageIndependent.DockerizationUsedPractice' = 'LanguageIndependent.DockerizationUsedPractice',
'LanguageIndependent.EditorConfigIsPresentPractice' = 'LanguageIndependent.EditorConfigIsPresentPractice',
'LanguageIndependent.DependenciesVersionPractice' = 'LanguageIndependent.DependenciesVersionPractice',
'JavaScript.JsGitignoreIsPresentPractice' = 'JavaScript.JsGitignoreIsPresentPractice',
'JavaScript.JsGitignoreCorrectlySetPractice' = 'JavaScript.JsGitignoreCorrectlySetPractice',
UnitTestPractice = 'UnitTestPractice',
PullRequestPractice = 'PullRequestPractice',
}
14 changes: 9 additions & 5 deletions src/contexts/language/languageContextBinding.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Container, tagged } from 'inversify';
import { LanguageContextFactory, Types } from '../../types';
import { JavaScriptComponentDetector } from '../../detectors/JavaScript/JavaScriptComponentDetector';
import { FileInspector } from '../../inspectors/FileInspector';
import { JavaScriptPackageInspector } from '../../inspectors/package/JavaScriptPackageInspector';
import { LanguageAtPath, ProgrammingLanguage } from '../../model';
import { LanguageContext } from './LanguageContext';
import { LanguageContextFactory, Types } from '../../types';
import { bindProjectComponentContext } from '../projectComponent/projectComponentContextBinding';
import { JavaScriptPackageInspector } from '../../inspectors/package/JavaScriptPackageInspector';
import { FileInspector } from '../../inspectors/FileInspector';
import { JavaScriptComponentDetector } from '../../detectors/JavaScript/JavaScriptComponentDetector';
import { LanguageContext } from './LanguageContext';

export const bindLanguageContext = (container: Container) => {
container.bind(Types.LanguageContextFactory).toFactory(
Expand All @@ -21,10 +21,12 @@ export const bindLanguageContext = (container: Container) => {
const createLanguageContainer = (languageAtPath: LanguageAtPath, rootContainer: Container): Container => {
const container = rootContainer.createChild();
container.bind(Types.LanguageAtPath).toConstantValue(languageAtPath);

bindFileAccess(languageAtPath, container);
bindComponentDetectors(container);
bindProjectComponentContext(container);
bindPackageInspectors(languageAtPath, container);

container.bind(LanguageContext).toSelf();
return container;
};
Expand All @@ -44,6 +46,8 @@ const bindPackageInspectors = (languageAtPath: LanguageAtPath, container: Contai
.bind(Types.IPackageInspector)
.to(JavaScriptPackageInspector)
.inSingletonScope();

// TODO: bind this as InitiableInspector instead of using next line binding
container.bind(JavaScriptPackageInspector).toDynamicValue((ctx) => {
return ctx.container.get(Types.IPackageInspector);
});
Expand Down
17 changes: 11 additions & 6 deletions src/contexts/projectComponent/ProjectComponentContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,29 @@ import { injectable, inject } from 'inversify';
import { Types, PracticeContextFactory } from '../../types';
import { ProgrammingLanguage, ProjectComponent } from '../../model';
import { PracticeContext } from '../practice/PracticeContext';
import { ConfigProvider } from '../ConfigProvider';

@injectable()
export class ProjectComponentContext {
readonly projectComponent: ProjectComponent;
get path(): string {
return this.projectComponent.path;
}
get language(): ProgrammingLanguage {
return this.projectComponent.language;
}
private readonly practiceContextFactory: PracticeContextFactory;
readonly configProvider: ConfigProvider;

constructor(
@inject(Types.ConfigProvider) configProvider: ConfigProvider,
@inject(Types.ProjectComponent) projectComponent: ProjectComponent,
@inject(Types.PracticeContextFactory) practiceContextFactory: PracticeContextFactory,
) {
this.projectComponent = projectComponent;
this.practiceContextFactory = practiceContextFactory;
this.configProvider = configProvider;
}

get path(): string {
return this.projectComponent.path;
}
get language(): ProgrammingLanguage {
return this.projectComponent.language;
}

getPracticeContext(): PracticeContext {
Expand Down
10 changes: 7 additions & 3 deletions src/contexts/projectComponent/projectComponentContextBinding.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Container } from 'inversify';
import { Types, ProjectComponentContextFactory, PracticeContextFactory } from '../../types';
import { ProjectComponentContext } from './ProjectComponentContext';
import { IGitInspector } from '../../inspectors/IGitInspector';
import { ProjectComponent } from '../../model';
import { PracticeContextFactory, ProjectComponentContextFactory, Types } from '../../types';
import { ConfigProvider } from '../ConfigProvider';
import { PracticeContext } from '../practice/PracticeContext';
import { IGitInspector } from '../../inspectors/IGitInspector';
import { ProjectComponentContext } from './ProjectComponentContext';

export const bindProjectComponentContext = (container: Container) => {
container.bind(Types.ProjectComponentContextFactory).toFactory(
Expand All @@ -18,11 +19,13 @@ export const bindProjectComponentContext = (container: Container) => {

const createProjectComponentContainer = (projectComponent: ProjectComponent, rootContainer: Container): Container => {
const container = rootContainer.createChild();
container.bind(Types.ConfigProvider).to(ConfigProvider);
container.bind(Types.ProjectComponent).toConstantValue(projectComponent);
container.bind(Types.PracticeContextFactory).toFactory(
(ctx): PracticeContextFactory => {
return (projectComponent: ProjectComponent): PracticeContext => {
let gitInspector: IGitInspector | undefined;

try {
gitInspector = ctx.container.get(Types.IGitInspector);
} catch {}
Expand All @@ -38,6 +41,7 @@ const createProjectComponentContainer = (projectComponent: ProjectComponent, roo
};
},
);

container.bind(ProjectComponentContext).toSelf();
return container;
};
11 changes: 5 additions & 6 deletions src/contexts/scanner/scannerContextBinding.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Types, ScannerContextFactory } from '../../types';
import { ScanningStrategy, ServiceType } from '../../detectors/ScanningStrategyDetector';
import { ScannerContext } from './ScannerContext';
import { Container } from 'inversify';
import { bindLanguageContext } from '../language/languageContextBinding';
import { JavaScriptLanguageDetector } from '../../detectors/JavaScript/JavaScriptLanguageDetector';
import { ScanningStrategy, ServiceType } from '../../detectors/ScanningStrategyDetector';
import { FileInspector } from '../../inspectors/FileInspector';
import { FileSystemService } from '../../services/FileSystemService';
import { GitInspector } from '../../inspectors/GitInspector';
import { FileSystemService } from '../../services/FileSystemService';
import { GitHubService } from '../../services/git/GitHubService';
import { ScannerContextFactory, Types } from '../../types';
import { bindLanguageContext } from '../language/languageContextBinding';
import { ScannerContext } from './ScannerContext';

export const bindScanningContext = (container: Container) => {
container.bind(Types.ScannerContextFactory).toFactory(
Expand Down Expand Up @@ -42,7 +42,6 @@ const bindFileAccess = (scanningStrategy: ScanningStrategy, container: Container
if (scanningStrategy.serviceType === ServiceType.github) {
container.bind(Types.IContentRepositoryBrowser).to(GitHubService);
}
// TODO: bind services for GitHub strategy
container
.bind(Types.IFileInspector)
.to(FileInspector)
Expand Down
13 changes: 9 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createRootContainer } from './inversify.config';
import { Scanner } from './scanner/Scanner';
import { Command, flags } from '@oclif/command';
import cli from 'cli-ux';
import { ServiceError } from './lib/errors';

class DXScannerCommand extends Command {
static description = 'Scan your project for possible DX recommendations.';
Expand Down Expand Up @@ -40,12 +41,16 @@ class DXScannerCommand extends Command {
try {
await scanner.scan();
} catch (error) {
authorization = await cli.prompt('Insert your GitHub personal access token.\nhttps://github.com/settings/tokens\n');
if (error instanceof ServiceError) {
authorization = await cli.prompt('Insert your GitHub personal access token.\nhttps://github.com/settings/tokens\n');

const container = createRootContainer({ uri: scanPath, auth: authorization });
const scanner = container.get(Scanner);
const container = createRootContainer({ uri: scanPath, auth: authorization, json: json });
const scanner = container.get(Scanner);

await scanner.scan();
await scanner.scan();
} else {
throw error;
}
}

cli.action.stop();
Expand Down
34 changes: 17 additions & 17 deletions src/inversify.config.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { Container } from 'inversify';
import { Scanner } from './scanner/Scanner';
import { Types } from './types';
import { IReporter } from './reporters/IReporter';
import { CLIReporter } from './reporters/CLIReporter';
import { practices } from './practices';
import { ScanningStrategyDetector } from './detectors/ScanningStrategyDetector';
import { bindScanningContext } from './contexts/scanner/scannerContextBinding';
import { FileSystemService } from './services/FileSystemService';
import { GitHubService } from './services/git/GitHubService';
import { DirectoryJSON } from 'memfs/lib/volume';
import { PracticeContext } from './contexts/practice/PracticeContext';
import { IPackageInspector } from './inspectors/IPackageInspector';
import { IFileInspector } from './inspectors/IFileInspector';
import { ProgrammingLanguage, ProjectComponentType, ProjectComponentPlatform, ProjectComponentFramework, ProjectComponent } from './model';
import { JavaScriptPackageInspector } from './inspectors/package/JavaScriptPackageInspector';
import { bindScanningContext } from './contexts/scanner/scannerContextBinding';
import { ScanningStrategyDetector } from './detectors/ScanningStrategyDetector';
import { packageJSONContents } from './detectors/__MOCKS__';
import { IPracticeWithMetadata } from './practices/DxPracticeDecorator';
import { ScannerUtils } from './scanner/ScannerUtils';
import { CollaborationInspector } from './inspectors/CollaborationInspector';
import { FileInspector } from './inspectors/FileInspector';
import { IFileInspector } from './inspectors/IFileInspector';
import { IPackageInspector } from './inspectors/IPackageInspector';
import { IssueTrackingInspector } from './inspectors/IssueTrackingInspector';
import { CollaborationInspector } from './inspectors/CollaborationInspector';
import { DirectoryJSON } from 'memfs/lib/volume';
import { JavaScriptPackageInspector } from './inspectors/package/JavaScriptPackageInspector';
import { ProgrammingLanguage, ProjectComponent, ProjectComponentFramework, ProjectComponentPlatform, ProjectComponentType } from './model';
import { practices } from './practices';
import { IPracticeWithMetadata } from './practices/DxPracticeDecorator';
import { CLIReporter } from './reporters/CLIReporter';
import { IReporter } from './reporters/IReporter';
import { JSONReporter } from './reporters/JSONReporter';
import { Scanner } from './scanner/Scanner';
import { ScannerUtils } from './scanner/ScannerUtils';
import { FileSystemService } from './services/FileSystemService';
import { GitHubService } from './services/git/GitHubService';
import { Types } from './types';

export const createRootContainer = (args: ArgumentsProvider): Container => {
const container = new Container();
Expand Down
2 changes: 2 additions & 0 deletions src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export interface DeprecatedProjectComponent {
}

export interface PracticeMetadata {
defaultImpact?: PracticeImpact;
id: string;
name: string;
suggestion: string;
Expand All @@ -104,6 +105,7 @@ export enum PracticeImpact {
medium = 'medium',
small = 'small',
hint = 'hint',
off = 'off',
}

export enum PracticeEvaluationResult {
Expand Down
2 changes: 1 addition & 1 deletion src/practices/DxPracticeDecorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function DxPracticeWrapperDecorator(practiceMetadata: PracticeMetadata) {
return function classDecorator<T extends new (...args: any[]) => {}>(constructor: T) {
return class extends constructor {
getMetadata = () => {
return { ...practiceMetadata, matcher: this };
return { ...practiceMetadata, defaultImpact: practiceMetadata.impact, matcher: this };
};
};
};
Expand Down
Loading

0 comments on commit 7d5b33f

Please sign in to comment.