Skip to content

Commit

Permalink
feat(lint): save and return linter violations
Browse files Browse the repository at this point in the history
  • Loading branch information
angristan committed Jul 27, 2021
1 parent ab3ab14 commit 6c4cd18
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 21 deletions.
9 changes: 9 additions & 0 deletions src/lint/dto/lint-error.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class LintErrorDTO {
message: string;

line: number | null;

column: number | null;

offset: number | null;
}
7 changes: 7 additions & 0 deletions src/lint/dto/lint-result.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LintErrorDTO } from './lint-error.dto';

export class LintResultDTO {
score: number;

errors: LintErrorDTO[];
}
80 changes: 66 additions & 14 deletions src/lint/lint.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import child_process from 'child_process';
import fs from 'fs';
import * as uuid from 'uuid';
import YAML from 'yaml';
import { LintErrorDTO } from './dto/lint-error.dto';
import { LintResultDTO } from './dto/lint-result.dto';
import { ConvertToGolangCILintOutput } from './linters/golangcilint';
import { ConvertToPylintOutput } from './linters/pylint';

@Injectable()
export class LintService {
lintPython3(code: string): number {
lintPython3(code: string): LintResultDTO {
const result = child_process.spawnSync(
'pylint',
['--from-stdin', '-f', 'json', 'module_or_package', '--'],
Expand Down Expand Up @@ -49,7 +51,22 @@ export class LintService {
Remove one point to the score per violation
*/
return 100 - pylintOutput.length;
const score = 100 - pylintOutput.length;
const errors: LintErrorDTO[] = [];

for (const violation of pylintOutput) {
errors.push({
message: violation.message,
line: violation.line,
column: violation.column,
offset: null,
});
}

return {
score,
errors,
};
}
} catch (e) {
throw new InternalServerErrorException();
Expand All @@ -58,7 +75,7 @@ export class LintService {
throw new InternalServerErrorException();
}

lintGolang(code: string): number {
lintGolang(code: string): LintResultDTO {
// Golangci-lint doesn't support stdin
const path = `/tmp/codebench_${uuid.v4()}.go`;
try {
Expand Down Expand Up @@ -112,19 +129,35 @@ export class LintService {
*/
if (lintOuput.issues) {
// Remove one point to the score per violation
return 100 - lintOuput.issues.length;
const score = 100 - lintOuput.issues.length;
const errors: LintErrorDTO[] = [];

for (const issue of lintOuput.issues) {
errors.push({
message: issue.text,
line: issue.pos.line,
column: issue.pos.column,
offset: null,
});
}
return {
score,
errors,
};
}
// Golangci-lint return `null` if there are no violation
return 100;
}
} catch (e) {
throw new InternalServerErrorException(e);
}

throw new InternalServerErrorException();
return {
score: 100,
errors: [],
};
}

lintCpp(code: string): number {
lintCpp(code: string): LintResultDTO {
// clang-tidy doesn't support stdin
const path = `/tmp/codebench_${uuid.v4()}.cpp`;
const outputPath = `${path}.yaml`;
Expand Down Expand Up @@ -152,27 +185,46 @@ export class LintService {
if (fs.existsSync(outputPath)) {
const file = fs.readFileSync(`${path}.yaml`, 'utf8');
const fixes: any = YAML.parse(file);

if (fixes) {
/*
Example file:
---
MainSourceFile: /root/fib.cpp
Diagnostics:
- DiagnosticName: clang-diagnostic-unused-variable
Message: 'unused variable ''d'''
FileOffset: 142
FilePath: /root/fib.cpp
Replacements:
- DiagnosticName: clang-diagnostic-unused-variable
Message: 'unused variable ''d'''
FileOffset: 142
FilePath: /root/fib.cpp
Replacements:
...
*/
if (fixes.Diagnostics) {
return 100 - fixes.Diagnostics.length;
const score = 100 - fixes.Diagnostics.length;
const errors: LintErrorDTO[] = [];

for (const diagnostic of fixes.Diagnostics) {
errors.push({
message: diagnostic.Message,
line: diagnostic.FileOffset,
column: null,
offset: null,
});
}
return {
score,
errors,
};
}
}
}
return 100;
} catch (e) {
throw new InternalServerErrorException(e);
}

return {
score: 100,
errors: [],
};
}
}
14 changes: 14 additions & 0 deletions src/migrations/1627337341637-AddSubmissionsLintReports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class AddSubmissionsLintReports1627337341637 implements MigrationInterface {
name = 'AddSubmissionsLintReports1627337341637'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "submissions" ADD "lintErrors" jsonb`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "submissions" DROP COLUMN "lintErrors"`);
}

}
3 changes: 3 additions & 0 deletions src/submissions/dto/insert-submission-dto.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LintErrorDTO } from 'src/lint/dto/lint-error.dto';
import { User } from 'src/users/user.entity';

export class InsertSubmissionDTO {
Expand All @@ -8,4 +9,6 @@ export class InsertSubmissionDTO {
code: string;

user: User;

lintErrors?: LintErrorDTO[];
}
3 changes: 3 additions & 0 deletions src/submissions/dto/submission-result.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { QualityDTO } from 'src/code-quality/dto/quality.dto';
import { LintErrorDTO } from 'src/lint/dto/lint-error.dto';
import { Submission } from '../submission.entity';

export class SubmissionResultDTO {
Expand All @@ -7,4 +8,6 @@ export class SubmissionResultDTO {
lint: { score: number };

quality: QualityDTO;

lintErrors?: LintErrorDTO[];
}
4 changes: 4 additions & 0 deletions src/submissions/submission.entity.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { ApiProperty } from '@nestjs/swagger';
import { LintErrorDTO } from 'src/lint/dto/lint-error.dto';
import { User } from 'src/users/user.entity';
import { jsonMember, jsonObject } from 'typedjson';
import {
Expand Down Expand Up @@ -92,6 +93,9 @@ export class Submission extends BaseEntity {
@Column({ nullable: true })
lintScore: number;

@Column('jsonb', { nullable: true })
lintErrors?: LintErrorDTO[];

@jsonMember
@Column({ nullable: true })
qualityScore: number;
Expand Down
14 changes: 7 additions & 7 deletions src/submissions/submissions.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { ValidatedJWTReq } from 'src/auth/dto/validated-jwt-req';
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
import { CodeQualityService } from 'src/code-quality/code-quality.service';
import { LintResultDTO } from 'src/lint/dto/lint-result.dto';
import { LintService } from 'src/lint/lint.service';
import { CreateSubmissionDTO } from './dto/create-submission-dto';
import { FindSubmissionDTO } from './dto/find-submission.dto';
Expand All @@ -34,32 +35,31 @@ export class SubmissionsController {
@Request() req: ValidatedJWTReq,
@Body() createSubmissionDTO: CreateSubmissionDTO,
): Promise<SubmissionResultDTO> {
let lintScore = { score: 100 };
let lintScore: LintResultDTO;
let qualityScore = { score: 100 };
switch (createSubmissionDTO.language) {
case 'python':
qualityScore = this.qualityService.run(createSubmissionDTO.code, 'py');
lintScore.score = this.lintService.lintPython3(
createSubmissionDTO.code,
);
lintScore = this.lintService.lintPython3(createSubmissionDTO.code);
break;
case 'cpp':
qualityScore = this.qualityService.run(createSubmissionDTO.code, 'cpp');
lintScore.score = this.lintService.lintCpp(createSubmissionDTO.code);
lintScore = this.lintService.lintCpp(createSubmissionDTO.code);
break;
case 'go':
qualityScore = this.qualityService.run(createSubmissionDTO.code, 'go');
lintScore.score = this.lintService.lintGolang(createSubmissionDTO.code);
lintScore = this.lintService.lintGolang(createSubmissionDTO.code);
break;
default:
lintScore = { score: 0 };
lintScore = { score: 0, errors: [] };
qualityScore = { score: 0 };
}

const submission = await this.submissionsService.create(
{
...createSubmissionDTO,
user: req.user,
lintErrors: lintScore.errors,
},
lintScore.score,
qualityScore.score,
Expand Down

0 comments on commit 6c4cd18

Please sign in to comment.