diff --git a/packages/canyon-backend/.eslintrc.js b/packages/canyon-backend/.eslintrc.js index 791ca93b..9ade1ea0 100755 --- a/packages/canyon-backend/.eslintrc.js +++ b/packages/canyon-backend/.eslintrc.js @@ -1,25 +1,25 @@ module.exports = { - parser: "@typescript-eslint/parser", - parserOptions: { - project: "tsconfig.json", - tsconfigRootDir: __dirname, - sourceType: "module", - }, - plugins: ["@typescript-eslint/eslint-plugin"], - extends: [ - "plugin:@typescript-eslint/recommended", - "plugin:prettier/recommended", - ], - root: true, - env: { - node: true, - jest: true, - }, - ignorePatterns: [".eslintrc.js"], - rules: { - "@typescript-eslint/interface-name-prefix": "off", - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/no-explicit-any": "off", - }, + parser: "@typescript-eslint/parser", + parserOptions: { + project: "tsconfig.json", + tsconfigRootDir: __dirname, + sourceType: "module", + }, + plugins: ["@typescript-eslint/eslint-plugin"], + extends: [ + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + ], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: [".eslintrc.js"], + rules: { + "@typescript-eslint/interface-name-prefix": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "off", + }, }; diff --git a/packages/canyon-backend/src/adapter/gitlab.adapter.ts b/packages/canyon-backend/src/adapter/gitlab.adapter.ts index 7b18da2c..58adbaf4 100755 --- a/packages/canyon-backend/src/adapter/gitlab.adapter.ts +++ b/packages/canyon-backend/src/adapter/gitlab.adapter.ts @@ -1,193 +1,193 @@ import axios from "axios"; interface FileInfo { - file_name: string; - file_path: string; - size: number; - encoding: string; - content_sha256: string; - ref: string; - blob_id: string; - commit_id: string; - last_commit_id: string; - content: string; + file_name: string; + file_path: string; + size: number; + encoding: string; + content_sha256: string; + ref: string; + blob_id: string; + commit_id: string; + last_commit_id: string; + content: string; } interface Commit { - id: string; - short_id: string; + id: string; + short_id: string; + created_at: string; + parent_ids: string[]; + title: string; + message: string; + author_name: string; + author_email: string; + authored_date: string; + committer_name: string; + committer_email: string; + committed_date: string; + web_url: string; + ci_reports: any[]; + stats: { + additions: number; + deletions: number; + total: number; + }; + status: string; + project_id: number; + last_pipeline: { + id: number; + project_id: number; + sha: string; + ref: string; + status: string; created_at: string; - parent_ids: string[]; - title: string; - message: string; - author_name: string; - author_email: string; - authored_date: string; - committer_name: string; - committer_email: string; - committed_date: string; + updated_at: string; web_url: string; - ci_reports: any[]; - stats: { - additions: number; - deletions: number; - total: number; - }; - status: string; - project_id: number; - last_pipeline: { - id: number; - project_id: number; - sha: string; - ref: string; - status: string; - created_at: string; - updated_at: string; - web_url: string; - }; + }; } export interface ProjectInfo { - id: number; - description: string; - main_language: string; - name: string; - name_with_namespace: string; - path: string; - path_with_namespace: string; - created_at: string; - default_branch: string; - tag_list: string[]; - ssh_url_to_repo: string; - http_url_to_repo: string; - web_url: string; - avatar_url: string | null; - forks_count: number; - star_count: number; - last_activity_at: string; - bu: string; - properties: { - node_version: string; - }; - packages_enabled: boolean; - empty_repo: boolean; - archived: boolean; - visibility: string; - resolve_outdated_diff_discussions: boolean; - container_registry_enabled: boolean; - issues_enabled: boolean; - merge_requests_enabled: boolean; - wiki_enabled: boolean; - jobs_enabled: boolean; - snippets_enabled: boolean; - service_desk_enabled: boolean; - service_desk_address: string | null; - can_create_merge_request_in: boolean; - issues_access_level: string; - repository_access_level: string; - merge_requests_access_level: string; - forking_access_level: string; - wiki_access_level: string; - builds_access_level: string; - snippets_access_level: string; - pages_access_level: string; - operations_access_level: string; - analytics_access_level: string; - emails_disabled: any; // You may want to define a more specific type here - shared_runners_enabled: boolean; - lfs_enabled: boolean; - creator_id: number; - import_status: string; - import_error: string | null; - open_issues_count: number; - runners_token: string; - ci_default_git_depth: number; - ci_forward_deployment_enabled: boolean; - public_jobs: boolean; - build_git_strategy: string; - build_timeout: number; - auto_cancel_pending_pipelines: string; - build_coverage_regex: string | null; - ci_config_path: string | null; - shared_with_groups: any[]; // You may want to define a more specific type here - only_allow_merge_if_pipeline_succeeds: boolean; - allow_merge_on_skipped_pipeline: any; // You may want to define a more specific type here - restrict_user_defined_variables: boolean; - request_access_enabled: boolean; - only_allow_merge_if_all_discussions_are_resolved: boolean; - remove_source_branch_after_merge: boolean; - printing_merge_request_link_enabled: boolean; - merge_method: string; - suggestion_commit_message: string | null; - auto_devops_enabled: boolean; - auto_devops_deploy_strategy: string; - autoclose_referenced_issues: boolean; + id: number; + description: string; + main_language: string; + name: string; + name_with_namespace: string; + path: string; + path_with_namespace: string; + created_at: string; + default_branch: string; + tag_list: string[]; + ssh_url_to_repo: string; + http_url_to_repo: string; + web_url: string; + avatar_url: string | null; + forks_count: number; + star_count: number; + last_activity_at: string; + bu: string; + properties: { + node_version: string; + }; + packages_enabled: boolean; + empty_repo: boolean; + archived: boolean; + visibility: string; + resolve_outdated_diff_discussions: boolean; + container_registry_enabled: boolean; + issues_enabled: boolean; + merge_requests_enabled: boolean; + wiki_enabled: boolean; + jobs_enabled: boolean; + snippets_enabled: boolean; + service_desk_enabled: boolean; + service_desk_address: string | null; + can_create_merge_request_in: boolean; + issues_access_level: string; + repository_access_level: string; + merge_requests_access_level: string; + forking_access_level: string; + wiki_access_level: string; + builds_access_level: string; + snippets_access_level: string; + pages_access_level: string; + operations_access_level: string; + analytics_access_level: string; + emails_disabled: any; // You may want to define a more specific type here + shared_runners_enabled: boolean; + lfs_enabled: boolean; + creator_id: number; + import_status: string; + import_error: string | null; + open_issues_count: number; + runners_token: string; + ci_default_git_depth: number; + ci_forward_deployment_enabled: boolean; + public_jobs: boolean; + build_git_strategy: string; + build_timeout: number; + auto_cancel_pending_pipelines: string; + build_coverage_regex: string | null; + ci_config_path: string | null; + shared_with_groups: any[]; // You may want to define a more specific type here + only_allow_merge_if_pipeline_succeeds: boolean; + allow_merge_on_skipped_pipeline: any; // You may want to define a more specific type here + restrict_user_defined_variables: boolean; + request_access_enabled: boolean; + only_allow_merge_if_all_discussions_are_resolved: boolean; + remove_source_branch_after_merge: boolean; + printing_merge_request_link_enabled: boolean; + merge_method: string; + suggestion_commit_message: string | null; + auto_devops_enabled: boolean; + auto_devops_deploy_strategy: string; + autoclose_referenced_issues: boolean; } export const getFileInfo = async ( - { - projectID, - filepath, - commitSha, - }: { projectID: string; filepath: string; commitSha: string }, - token: string, - gitProviderUrl: string, + { + projectID, + filepath, + commitSha, + }: { projectID: string; filepath: string; commitSha: string }, + token: string, + gitProviderUrl: string, ) => { - return await axios - .get( - `${gitProviderUrl}/api/v4/projects/${projectID}/repository/files/${filepath}`, - { - params: { - ref: commitSha, - }, - headers: { - // Authorization: `Bearer ${token}`, - "private-token": token, - }, - }, - ) - .then(({ data }) => data); + return await axios + .get( + `${gitProviderUrl}/api/v4/projects/${projectID}/repository/files/${filepath}`, + { + params: { + ref: commitSha, + }, + headers: { + // Authorization: `Bearer ${token}`, + "private-token": token, + }, + }, + ) + .then(({ data }) => data); }; export const getCommits = async ( - { projectID, commitShas }: { projectID: string; commitShas: string[] }, - token: string, - gitProviderUrl: string, + { projectID, commitShas }: { projectID: string; commitShas: string[] }, + token: string, + gitProviderUrl: string, ) => { - return await Promise.all( - commitShas.map((commitSha) => { - return axios - .get( - `${gitProviderUrl}/api/v4/projects/${projectID}/repository/commits/${commitSha}`, - { - headers: { - // Authorization: `Bearer ${token}`, - "private-token": token, - }, - }, - ) - .then(({ data }) => data) - .catch(() => { - return { - id: commitSha, - message: "???", - web_url: "???", - }; - }); - }), - ); + return await Promise.all( + commitShas.map((commitSha) => { + return axios + .get( + `${gitProviderUrl}/api/v4/projects/${projectID}/repository/commits/${commitSha}`, + { + headers: { + // Authorization: `Bearer ${token}`, + "private-token": token, + }, + }, + ) + .then(({ data }) => data) + .catch(() => { + return { + id: commitSha, + message: "???", + web_url: "???", + }; + }); + }), + ); }; export async function getProjectByID( - projectID: string, - token: string, - gitProviderUrl: string, + projectID: string, + token: string, + gitProviderUrl: string, ) { - return await axios - .get(`${gitProviderUrl}/api/v4/projects/${projectID}`, { - headers: { - // Authorization: `Bearer ${token}`, - "private-token": token, - }, - }) - .then(({ data }) => data); + return await axios + .get(`${gitProviderUrl}/api/v4/projects/${projectID}`, { + headers: { + // Authorization: `Bearer ${token}`, + "private-token": token, + }, + }) + .then(({ data }) => data); } diff --git a/packages/canyon-backend/src/app.controller.ts b/packages/canyon-backend/src/app.controller.ts index 6bd9215e..ff7ff971 100755 --- a/packages/canyon-backend/src/app.controller.ts +++ b/packages/canyon-backend/src/app.controller.ts @@ -4,155 +4,155 @@ import { convertSystemSettingsFromTheDatabase } from "./utils/sys"; @Controller() export class AppController { - constructor(private readonly prisma: PrismaService) {} - @Get("/api/vi/health") - async viHealth() { - return "230614"; - } + constructor(private readonly prisma: PrismaService) {} + @Get("/api/vi/health") + async viHealth() { + return "230614"; + } - @Get("/api/gitprovider") - gitprovider() { - return this.prisma.gitProvider.findMany({ - where: {}, - }); - } + @Get("/api/gitprovider") + gitprovider() { + return this.prisma.gitProvider.findMany({ + where: {}, + }); + } - @Get("/api/base") - async base() { - const { gitlabServer, gitlabClientID, docsLink } = - await this.prisma.sysSetting - .findMany({}) - .then((res) => convertSystemSettingsFromTheDatabase(res)); - return { - SYSTEM_QUESTION_LINK: docsLink, - GITLAB_URL: gitlabServer, - GITLAB_CLIENT_ID: gitlabClientID, - }; - } + @Get("/api/base") + async base() { + const { gitlabServer, gitlabClientID, docsLink } = + await this.prisma.sysSetting + .findMany({}) + .then((res) => convertSystemSettingsFromTheDatabase(res)); + return { + SYSTEM_QUESTION_LINK: docsLink, + GITLAB_URL: gitlabServer, + GITLAB_CLIENT_ID: gitlabClientID, + }; + } - @Get("/api/monthly-coverage-trend") - async monthlyCoverageTrend() { - return [ - { - month: 0, - year: 2024, - uiTestBranchCoverage: 50.59, - uiTestLineCoverage: 56.08, - uiTestChangedLineCoverage: 100.0, - utBranchCoverage: 64.96, - utLineCoverage: 62.56, - codeChangeNum: 0, - }, - { - month: 1, - year: 2024, - uiTestBranchCoverage: 51.45, - uiTestLineCoverage: 59.19, - uiTestChangedLineCoverage: 93.76, - utBranchCoverage: 64.74, - utLineCoverage: 64.0, - codeChangeNum: 0, - }, - { - month: 2, - year: 2024, - uiTestBranchCoverage: 50.05, - uiTestLineCoverage: 63.23, - uiTestChangedLineCoverage: 93.95, - utBranchCoverage: 64.68, - utLineCoverage: 64.38, - codeChangeNum: 0, - }, - { - month: 3, - year: 2024, - uiTestBranchCoverage: 55.15, - uiTestLineCoverage: 66.47, - uiTestChangedLineCoverage: 95.49, - utBranchCoverage: 64.62, - utLineCoverage: 65.61, - codeChangeNum: 0, - }, - { - month: 4, - year: 2024, - uiTestBranchCoverage: 57.84, - uiTestLineCoverage: 71.61, - uiTestChangedLineCoverage: 95.1, - utBranchCoverage: 64.69, - utLineCoverage: 66.15, - codeChangeNum: 0, - }, - { - month: 5, - year: 2024, - uiTestBranchCoverage: 62.84, - uiTestLineCoverage: 77.23, - uiTestChangedLineCoverage: 96.13, - utBranchCoverage: 65.21, - utLineCoverage: 68.91, - codeChangeNum: 730591, - }, - { - month: 6, - year: 2024, - uiTestBranchCoverage: 63.22, - uiTestLineCoverage: 77.66, - uiTestChangedLineCoverage: 95.35, - utBranchCoverage: 66.81, - utLineCoverage: 69.34, - codeChangeNum: 988195, - }, - { - month: 7, - year: 2024, - uiTestBranchCoverage: 63.43, - uiTestLineCoverage: 77.92, - uiTestChangedLineCoverage: 95.61, - utBranchCoverage: 67.1, - utLineCoverage: 69.83, - codeChangeNum: 940460, - }, - { - month: 8, - year: 2024, - uiTestBranchCoverage: 63.5, - uiTestLineCoverage: 78.11, - uiTestChangedLineCoverage: 95.33, - utBranchCoverage: 67.9, - utLineCoverage: 70.21, - codeChangeNum: 951122, - }, - { - month: 9, - year: 2024, - uiTestBranchCoverage: 63.35, - uiTestLineCoverage: 77.91, - uiTestChangedLineCoverage: 95.31, - utBranchCoverage: 67.68, - utLineCoverage: 69.97, - codeChangeNum: 935567, - }, - { - month: 10, - year: 2024, - uiTestBranchCoverage: 63.6, - uiTestLineCoverage: 78.2, - uiTestChangedLineCoverage: 95.4, - utBranchCoverage: 68.0, - utLineCoverage: 70.4, - codeChangeNum: 945000, - }, - { - month: 11, - year: 2024, - uiTestBranchCoverage: 63.85, - uiTestLineCoverage: 78.49, - uiTestChangedLineCoverage: 95.49, - utBranchCoverage: 68.32, - utLineCoverage: 70.83, - codeChangeNum: 954410, - }, - ]; - } + @Get("/api/monthly-coverage-trend") + async monthlyCoverageTrend() { + return [ + { + month: 0, + year: 2024, + uiTestBranchCoverage: 50.59, + uiTestLineCoverage: 56.08, + uiTestChangedLineCoverage: 100.0, + utBranchCoverage: 64.96, + utLineCoverage: 62.56, + codeChangeNum: 0, + }, + { + month: 1, + year: 2024, + uiTestBranchCoverage: 51.45, + uiTestLineCoverage: 59.19, + uiTestChangedLineCoverage: 93.76, + utBranchCoverage: 64.74, + utLineCoverage: 64.0, + codeChangeNum: 0, + }, + { + month: 2, + year: 2024, + uiTestBranchCoverage: 50.05, + uiTestLineCoverage: 63.23, + uiTestChangedLineCoverage: 93.95, + utBranchCoverage: 64.68, + utLineCoverage: 64.38, + codeChangeNum: 0, + }, + { + month: 3, + year: 2024, + uiTestBranchCoverage: 55.15, + uiTestLineCoverage: 66.47, + uiTestChangedLineCoverage: 95.49, + utBranchCoverage: 64.62, + utLineCoverage: 65.61, + codeChangeNum: 0, + }, + { + month: 4, + year: 2024, + uiTestBranchCoverage: 57.84, + uiTestLineCoverage: 71.61, + uiTestChangedLineCoverage: 95.1, + utBranchCoverage: 64.69, + utLineCoverage: 66.15, + codeChangeNum: 0, + }, + { + month: 5, + year: 2024, + uiTestBranchCoverage: 62.84, + uiTestLineCoverage: 77.23, + uiTestChangedLineCoverage: 96.13, + utBranchCoverage: 65.21, + utLineCoverage: 68.91, + codeChangeNum: 730591, + }, + { + month: 6, + year: 2024, + uiTestBranchCoverage: 63.22, + uiTestLineCoverage: 77.66, + uiTestChangedLineCoverage: 95.35, + utBranchCoverage: 66.81, + utLineCoverage: 69.34, + codeChangeNum: 988195, + }, + { + month: 7, + year: 2024, + uiTestBranchCoverage: 63.43, + uiTestLineCoverage: 77.92, + uiTestChangedLineCoverage: 95.61, + utBranchCoverage: 67.1, + utLineCoverage: 69.83, + codeChangeNum: 940460, + }, + { + month: 8, + year: 2024, + uiTestBranchCoverage: 63.5, + uiTestLineCoverage: 78.11, + uiTestChangedLineCoverage: 95.33, + utBranchCoverage: 67.9, + utLineCoverage: 70.21, + codeChangeNum: 951122, + }, + { + month: 9, + year: 2024, + uiTestBranchCoverage: 63.35, + uiTestLineCoverage: 77.91, + uiTestChangedLineCoverage: 95.31, + utBranchCoverage: 67.68, + utLineCoverage: 69.97, + codeChangeNum: 935567, + }, + { + month: 10, + year: 2024, + uiTestBranchCoverage: 63.6, + uiTestLineCoverage: 78.2, + uiTestChangedLineCoverage: 95.4, + utBranchCoverage: 68.0, + utLineCoverage: 70.4, + codeChangeNum: 945000, + }, + { + month: 11, + year: 2024, + uiTestBranchCoverage: 63.85, + uiTestLineCoverage: 78.49, + uiTestChangedLineCoverage: 95.49, + utBranchCoverage: 68.32, + utLineCoverage: 70.83, + codeChangeNum: 954410, + }, + ]; + } } diff --git a/packages/canyon-backend/src/app.module.ts b/packages/canyon-backend/src/app.module.ts index e38f1de0..07176941 100755 --- a/packages/canyon-backend/src/app.module.ts +++ b/packages/canyon-backend/src/app.module.ts @@ -19,33 +19,33 @@ import { CollectModule } from "./apps/collect/collect.module"; import { CoveragediskEntity } from "./apps/collect/entity/coveragedisk.entity"; @Module({ - imports: [ - TypeOrmModule.forRoot({ - type: "sqlite", - database: "db/sql", - synchronize: true, - entities: [CoveragediskEntity], - }), - ScheduleModule.forRoot(), - AuthModule, - CollectModule, - UserModule, - ProjectModule, - PrismaModule, - CoverageModule, - CodechangeModule, - SourcecodeModule, - PrismaModule, - ServeStaticModule.forRoot({ - rootPath: join(__dirname, "../../canyon-platform", "dist"), - exclude: ["/graphql/(.*)"], - }), - GraphQLModule.forRoot({ - autoSchemaFile: "schema.gql", - driver: ApolloDriver, - }), - ], - controllers: [AppController, SourcecodeController], - providers: [SourcecodeService], + imports: [ + TypeOrmModule.forRoot({ + type: "sqlite", + database: "db/sql", + synchronize: true, + entities: [CoveragediskEntity], + }), + ScheduleModule.forRoot(), + AuthModule, + CollectModule, + UserModule, + ProjectModule, + PrismaModule, + CoverageModule, + CodechangeModule, + SourcecodeModule, + PrismaModule, + ServeStaticModule.forRoot({ + rootPath: join(__dirname, "../../canyon-platform", "dist"), + exclude: ["/graphql/(.*)"], + }), + GraphQLModule.forRoot({ + autoSchemaFile: "schema.gql", + driver: ApolloDriver, + }), + ], + controllers: [AppController, SourcecodeController], + providers: [SourcecodeService], }) export class AppModule {} diff --git a/packages/canyon-backend/src/apps/collect/collect.controller.ts b/packages/canyon-backend/src/apps/collect/collect.controller.ts index 6c4c5a6c..ec3a9029 100644 --- a/packages/canyon-backend/src/apps/collect/collect.controller.ts +++ b/packages/canyon-backend/src/apps/collect/collect.controller.ts @@ -1,11 +1,11 @@ import { - Body, - Controller, - Post, - UploadedFile, - UseGuards, - UseInterceptors, - Request, + Body, + Controller, + Post, + UploadedFile, + UseGuards, + UseInterceptors, + Request, } from "@nestjs/common"; import { PrismaService } from "../../prisma/prisma.service"; import { CoverageClientService } from "./services/coverage-client.service"; @@ -18,24 +18,24 @@ import { JwtAuthGuard } from "../../auth/guards/jwt-auth.guard"; // import { JwtAuthGuard } from "../auth/guards/jwt-auth.guard"; // 解压 GZIP 的 Buffer 数据 async function decompressData(buffer) { - return new Promise((resolve, reject) => { - zlib.gunzip(buffer, (err, decompressed) => { - if (err) { - return reject(err); - } - resolve(decompressed.toString("utf-8")); // 转换为字符串返回 - }); + return new Promise((resolve, reject) => { + zlib.gunzip(buffer, (err, decompressed) => { + if (err) { + return reject(err); + } + resolve(decompressed.toString("utf-8")); // 转换为字符串返回 }); + }); } @Controller() export class CollectController { - constructor( - private readonly prisma: PrismaService, - private coverageClientService: CoverageClientService, - private coverageMapClientService: CoverageMapClientService, - ) {} + constructor( + private readonly prisma: PrismaService, + private coverageClientService: CoverageClientService, + private coverageMapClientService: CoverageMapClientService, + ) {} - /* + /* 核心代码 1. 接受application/json类型的数据,coverage可以是对象或者字符串 2. 接受formData类型的数据,coverage是二进制数据,可以是compressDataWithStream压缩过的,也可以是json字符串 @@ -43,41 +43,41 @@ export class CollectController { 4. 经过测试在macbookpro上compressDataWithStream压缩1600kb的数据,压缩后64kb左右,耗时8ms左右 TODO 得调研8ms对navigator.sendBeacon有没有影响 */ - @UseGuards(JwtAuthGuard) - @UseInterceptors(FileInterceptor("coverage")) - @Post("coverage/client") - async coverageClient( - @UploadedFile() cov: any, - @Body() coverageClientDto: CoverageClientDto, - @Request() req: any, - ) { - if (coverageClientDto.coverage) { - return this.coverageClientService.invoke({ - ...coverageClientDto, - reporter: String(req?.user?.id || "canyon"), - }); - } - let coverage = {}; - if (cov.mimetype === "application/octet-stream") { - coverage = await decompressData(cov.buffer).then( - (jsonString: any) => JSON.parse(jsonString), - ); - } else { - // 先将buffer中的二进制数据转换为字符串 - const jsonString = cov.buffer.toString(); - - coverage = JSON.parse(jsonString); - } - - return this.coverageClientService.invoke({ - ...coverageClientDto, - coverage, - reporter: String(req?.user?.id || "canyon"), - }); + @UseGuards(JwtAuthGuard) + @UseInterceptors(FileInterceptor("coverage")) + @Post("coverage/client") + async coverageClient( + @UploadedFile() cov: any, + @Body() coverageClientDto: CoverageClientDto, + @Request() req: any, + ) { + if (coverageClientDto.coverage) { + return this.coverageClientService.invoke({ + ...coverageClientDto, + reporter: String(req?.user?.id || "canyon"), + }); } + let coverage = {}; + if (cov.mimetype === "application/octet-stream") { + coverage = await decompressData(cov.buffer).then((jsonString: any) => + JSON.parse(jsonString), + ); + } else { + // 先将buffer中的二进制数据转换为字符串 + const jsonString = cov.buffer.toString(); - @Post("coverage/map/client") - coverageMapClient(@Body() coverageMapClientDto: CoverageMapClientDto) { - return this.coverageMapClientService.invoke(coverageMapClientDto); + coverage = JSON.parse(jsonString); } + + return this.coverageClientService.invoke({ + ...coverageClientDto, + coverage, + reporter: String(req?.user?.id || "canyon"), + }); + } + + @Post("coverage/map/client") + coverageMapClient(@Body() coverageMapClientDto: CoverageMapClientDto) { + return this.coverageMapClientService.invoke(coverageMapClientDto); + } } diff --git a/packages/canyon-backend/src/apps/collect/collect.module.ts b/packages/canyon-backend/src/apps/collect/collect.module.ts index 716d036c..62112e8a 100644 --- a/packages/canyon-backend/src/apps/collect/collect.module.ts +++ b/packages/canyon-backend/src/apps/collect/collect.module.ts @@ -12,23 +12,23 @@ import { TestExcludeService } from "./services/common/test-exclude.service"; import { CoverageFinalService } from "./services/common/coverage-final.service"; @Module({ - imports: [TypeOrmModule.forFeature([CoveragediskEntity])], - controllers: [CollectController], - providers: [ - PrismaService, - CoverageClientService, - CoverageMapClientService, - ConsumerCoverageService, - CoveragediskService, - PullChangeCodeAndInsertDbService, - TestExcludeService, - CoverageFinalService, - ], + imports: [TypeOrmModule.forFeature([CoveragediskEntity])], + controllers: [CollectController], + providers: [ + PrismaService, + CoverageClientService, + CoverageMapClientService, + ConsumerCoverageService, + CoveragediskService, + PullChangeCodeAndInsertDbService, + TestExcludeService, + CoverageFinalService, + ], }) export class CollectModule { - constructor( - private readonly consumerCoverageService: ConsumerCoverageService, - ) { - this.consumerCoverageService.invoke(); - } + constructor( + private readonly consumerCoverageService: ConsumerCoverageService, + ) { + this.consumerCoverageService.invoke(); + } } diff --git a/packages/canyon-backend/src/apps/collect/dto/coverage-client.dto.ts b/packages/canyon-backend/src/apps/collect/dto/coverage-client.dto.ts index 8ee629ec..8d8fdef3 100755 --- a/packages/canyon-backend/src/apps/collect/dto/coverage-client.dto.ts +++ b/packages/canyon-backend/src/apps/collect/dto/coverage-client.dto.ts @@ -1,48 +1,48 @@ import { - IsNotEmpty, - IsOptional, - IsString, - Matches, - MinLength, - Validate, + IsNotEmpty, + IsOptional, + IsString, + Matches, + MinLength, + Validate, } from "class-validator"; import { IsValidCoverage } from "../valids/is-valid-coverage"; export class CoverageClientDto { - // git仓库相关 - @IsString() - @Matches(/^[a-f0-9]{40}$/i, { message: "sha格式不正确" }) - @IsNotEmpty({ message: "sha 不能为空" }) - sha: string; + // git仓库相关 + @IsString() + @Matches(/^[a-f0-9]{40}$/i, { message: "sha格式不正确" }) + @IsNotEmpty({ message: "sha 不能为空" }) + sha: string; - @IsString() - @IsNotEmpty({ message: "projectID 不能为空" }) - projectID: string; + @IsString() + @IsNotEmpty({ message: "projectID 不能为空" }) + projectID: string; - // 单次 case 触发相关 - @IsString() - @MinLength(1, { message: "reportID长度最小为1" }) - @IsOptional({ message: "reportID 可以为空" }) - reportID: string; + // 单次 case 触发相关 + @IsString() + @MinLength(1, { message: "reportID长度最小为1" }) + @IsOptional({ message: "reportID 可以为空" }) + reportID: string; - // istanbul覆盖率相关 - @IsString() - @MinLength(1, { message: "instrumentCwd长度最小为1" }) - @IsNotEmpty({ message: "instrumentCwd不能为空" }) - instrumentCwd: string; + // istanbul覆盖率相关 + @IsString() + @MinLength(1, { message: "instrumentCwd长度最小为1" }) + @IsNotEmpty({ message: "instrumentCwd不能为空" }) + instrumentCwd: string; - // @IsNotEmpty({ message: 'coverage不能为空' }) - @Validate(IsValidCoverage) - coverage: any; + // @IsNotEmpty({ message: 'coverage不能为空' }) + @Validate(IsValidCoverage) + coverage: any; - @IsString() - @MinLength(1, { message: "branch长度最小为1" }) - @IsOptional({ message: "branch 可以为空" }) - branch: string; + @IsString() + @MinLength(1, { message: "branch长度最小为1" }) + @IsOptional({ message: "branch 可以为空" }) + branch: string; - // 允许为空,但是最小长度为1 - @IsString() - @MinLength(1, { message: "compareTarget长度最小为1" }) - @IsOptional({ message: "compareTarget可以为空" }) - compareTarget: string; + // 允许为空,但是最小长度为1 + @IsString() + @MinLength(1, { message: "compareTarget长度最小为1" }) + @IsOptional({ message: "compareTarget可以为空" }) + compareTarget: string; } diff --git a/packages/canyon-backend/src/apps/collect/dto/coverage-map-client.dto.ts b/packages/canyon-backend/src/apps/collect/dto/coverage-map-client.dto.ts index 47f7fc12..6dbb7647 100755 --- a/packages/canyon-backend/src/apps/collect/dto/coverage-map-client.dto.ts +++ b/packages/canyon-backend/src/apps/collect/dto/coverage-map-client.dto.ts @@ -1,42 +1,42 @@ import { - IsNotEmpty, - IsOptional, - IsString, - Matches, - MinLength, - Validate, + IsNotEmpty, + IsOptional, + IsString, + Matches, + MinLength, + Validate, } from "class-validator"; import { IsValidCoverage } from "../valids/is-valid-coverage-map"; export class CoverageMapClientDto { - // git仓库相关 - @IsString() - @Matches(/^[a-f0-9]{40}$/i, { message: "sha格式不正确" }) - @IsNotEmpty({ message: "sha 不能为空" }) - sha: string; + // git仓库相关 + @IsString() + @Matches(/^[a-f0-9]{40}$/i, { message: "sha格式不正确" }) + @IsNotEmpty({ message: "sha 不能为空" }) + sha: string; - @IsString() - @MinLength(1, { message: "branch长度最小为1" }) - @IsOptional({ message: "branch 可以为空" }) - branch: string; + @IsString() + @MinLength(1, { message: "branch长度最小为1" }) + @IsOptional({ message: "branch 可以为空" }) + branch: string; - @IsString() - @IsNotEmpty({ message: "projectID 不能为空" }) - projectID: string; + @IsString() + @IsNotEmpty({ message: "projectID 不能为空" }) + projectID: string; - // istanbul覆盖率相关 - @IsString() - @MinLength(1, { message: "instrumentCwd长度最小为1" }) - @IsNotEmpty({ message: "instrumentCwd不能为空" }) - instrumentCwd: string; + // istanbul覆盖率相关 + @IsString() + @MinLength(1, { message: "instrumentCwd长度最小为1" }) + @IsNotEmpty({ message: "instrumentCwd不能为空" }) + instrumentCwd: string; - @IsNotEmpty({ message: "coverage不能为空" }) - @Validate(IsValidCoverage) - coverage: any; + @IsNotEmpty({ message: "coverage不能为空" }) + @Validate(IsValidCoverage) + coverage: any; - // 允许为空,但是最小长度为1 - @IsString() - @MinLength(1, { message: "compareTarget长度最小为1" }) - @IsOptional({ message: "compareTarget可以为空" }) - compareTarget: string; + // 允许为空,但是最小长度为1 + @IsString() + @MinLength(1, { message: "compareTarget长度最小为1" }) + @IsOptional({ message: "compareTarget可以为空" }) + compareTarget: string; } diff --git a/packages/canyon-backend/src/apps/collect/entity/coveragedisk.entity.ts b/packages/canyon-backend/src/apps/collect/entity/coveragedisk.entity.ts index e6a91b8e..23f2fb19 100644 --- a/packages/canyon-backend/src/apps/collect/entity/coveragedisk.entity.ts +++ b/packages/canyon-backend/src/apps/collect/entity/coveragedisk.entity.ts @@ -2,24 +2,24 @@ import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; @Entity("coveragedisk") export class CoveragediskEntity { - @PrimaryGeneratedColumn() - id: number; + @PrimaryGeneratedColumn() + id: number; - @Column() - pid: string; + @Column() + pid: string; - @Column() - projectID: string; + @Column() + projectID: string; - @Column() - reportID: string; + @Column() + reportID: string; - @Column() - sha: string; + @Column() + sha: string; - @Column() - data: string; + @Column() + data: string; - @Column() - createdAt: Date; + @Column() + createdAt: Date; } diff --git a/packages/canyon-backend/src/apps/collect/models/coverage.model.ts b/packages/canyon-backend/src/apps/collect/models/coverage.model.ts index 88e55c2e..132b8087 100644 --- a/packages/canyon-backend/src/apps/collect/models/coverage.model.ts +++ b/packages/canyon-backend/src/apps/collect/models/coverage.model.ts @@ -1,28 +1,28 @@ export const coverageObj = { - // id: require('cuid')(), // 假设这里有类似cuid的函数来生成唯一ID,需根据实际情况引入相应库 - branch: "-", - compareTarget: "", - provider: "", - buildProvider: "", - buildID: "", - projectID: "", - sha: "", - reporter: "", - reportID: "", - covType: "all", - statementsTotal: 0, - statementsCovered: 0, - branchesTotal: 0, - branchesCovered: 0, - functionsTotal: 0, - functionsCovered: 0, - linesTotal: 0, - linesCovered: 0, - newlinesTotal: 0, - newlinesCovered: 0, - summary: Buffer.from([]), - hit: Buffer.from([]), - // instrumentCwd: "", - // createdAt: new Date(), - // updatedAt: new Date(), + // id: require('cuid')(), // 假设这里有类似cuid的函数来生成唯一ID,需根据实际情况引入相应库 + branch: "-", + compareTarget: "", + provider: "", + buildProvider: "", + buildID: "", + projectID: "", + sha: "", + reporter: "", + reportID: "", + covType: "all", + statementsTotal: 0, + statementsCovered: 0, + branchesTotal: 0, + branchesCovered: 0, + functionsTotal: 0, + functionsCovered: 0, + linesTotal: 0, + linesCovered: 0, + newlinesTotal: 0, + newlinesCovered: 0, + summary: Buffer.from([]), + hit: Buffer.from([]), + // instrumentCwd: "", + // createdAt: new Date(), + // updatedAt: new Date(), }; diff --git a/packages/canyon-backend/src/apps/collect/services/common/coverage-final.service.ts b/packages/canyon-backend/src/apps/collect/services/common/coverage-final.service.ts index d1fa500a..886cba13 100644 --- a/packages/canyon-backend/src/apps/collect/services/common/coverage-final.service.ts +++ b/packages/canyon-backend/src/apps/collect/services/common/coverage-final.service.ts @@ -1,15 +1,15 @@ import { Injectable } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { - remapCoverageWithInstrumentCwd, - convertDataFromCoverageMapDatabase, - decompressedData, + remapCoverageWithInstrumentCwd, + convertDataFromCoverageMapDatabase, + decompressedData, } from "canyon-map"; import { - mergeCoverageMap, - parseProjectID, - reorganizeCompleteCoverageObjects, - resetCoverageDataMap, + mergeCoverageMap, + parseProjectID, + reorganizeCompleteCoverageObjects, + resetCoverageDataMap, } from "canyon-data"; /* @@ -19,94 +19,94 @@ import { @Injectable() export class CoverageFinalService { - constructor(private readonly prisma: PrismaService) {} - async invoke( - { - projectID, - sha, - reportID, - filepath, - }: { - projectID: string; - sha: string; - reportID?: string; - filepath?: string; - }, - hit?: { [key: string]: object }, - hitType?: boolean, - ) { - const { provider, repoID } = parseProjectID(projectID); - // 如果外部传入了hit,就不再从数据库中获取hit - hit = - hit || - (await this.getHitByProjectIDShaReportID({ - projectID, - sha, - reportID, - })); + constructor(private readonly prisma: PrismaService) {} + async invoke( + { + projectID, + sha, + reportID, + filepath, + }: { + projectID: string; + sha: string; + reportID?: string; + filepath?: string; + }, + hit?: { [key: string]: object }, + hitType?: boolean, + ) { + const { provider, repoID } = parseProjectID(projectID); + // 如果外部传入了hit,就不再从数据库中获取hit + hit = + hit || + (await this.getHitByProjectIDShaReportID({ + projectID, + sha, + reportID, + })); - // 无论是外部hit,还是查询到的hit,都为空对象,直接返回空对象 - if (Object.keys(hit).length === 0) { - return {}; - } + // 无论是外部hit,还是查询到的hit,都为空对象,直接返回空对象 + if (Object.keys(hit).length === 0) { + return {}; + } - const coverageMaps = await this.prisma.coverageMap.findMany({ - where: { - provider, - repoID, - sha, - path: filepath, - }, - }); + const coverageMaps = await this.prisma.coverageMap.findMany({ + where: { + provider, + repoID, + sha, + path: filepath, + }, + }); - // map没有也一样返回空对象 - if (coverageMaps.length === 0) { - return {}; - } + // map没有也一样返回空对象 + if (coverageMaps.length === 0) { + return {}; + } - const { map, instrumentCwd } = - await convertDataFromCoverageMapDatabase(coverageMaps); + const { map, instrumentCwd } = + await convertDataFromCoverageMapDatabase(coverageMaps); - // hitType是true的时候,说明是reMap过后的 - if (hitType) { - const reMapMap2 = await remapCoverageWithInstrumentCwd( - resetCoverageDataMap(map), - instrumentCwd, - ); - const r = reorganizeCompleteCoverageObjects(reMapMap2, hit); - return r; - } + // hitType是true的时候,说明是reMap过后的 + if (hitType) { + const reMapMap2 = await remapCoverageWithInstrumentCwd( + resetCoverageDataMap(map), + instrumentCwd, + ); + const r = reorganizeCompleteCoverageObjects(reMapMap2, hit); + return r; + } - const chongzu = reorganizeCompleteCoverageObjects(map, hit); + const chongzu = reorganizeCompleteCoverageObjects(map, hit); - const reMapMap = await remapCoverageWithInstrumentCwd( - chongzu, - instrumentCwd, - ); - return reMapMap; - } + const reMapMap = await remapCoverageWithInstrumentCwd( + chongzu, + instrumentCwd, + ); + return reMapMap; + } - private async getHitByProjectIDShaReportID({ projectID, sha, reportID }) { - const coverages = await this.prisma.coverage.findMany({ - where: { - projectID, - sha, - reportID: reportID - ? { - in: reportID.split(","), - } - : undefined, - covType: reportID ? "agg" : "all", - }, - }); + private async getHitByProjectIDShaReportID({ projectID, sha, reportID }) { + const coverages = await this.prisma.coverage.findMany({ + where: { + projectID, + sha, + reportID: reportID + ? { + in: reportID.split(","), + } + : undefined, + covType: reportID ? "agg" : "all", + }, + }); - let hitBox = {}; - for (let i = 0; i < coverages.length; i++) { - hitBox = mergeCoverageMap( - hitBox, - await decompressedData(coverages[i].hit), - ); - } - return hitBox; + let hitBox = {}; + for (let i = 0; i < coverages.length; i++) { + hitBox = mergeCoverageMap( + hitBox, + await decompressedData(coverages[i].hit), + ); } + return hitBox; + } } diff --git a/packages/canyon-backend/src/apps/collect/services/common/pull-change-code-and-insert-db.service.ts b/packages/canyon-backend/src/apps/collect/services/common/pull-change-code-and-insert-db.service.ts index 05c75f96..9d097a2e 100644 --- a/packages/canyon-backend/src/apps/collect/services/common/pull-change-code-and-insert-db.service.ts +++ b/packages/canyon-backend/src/apps/collect/services/common/pull-change-code-and-insert-db.service.ts @@ -1,47 +1,47 @@ import { PrismaService } from "../../../../prisma/prisma.service"; import { parseProjectID } from "canyon-data"; import { Injectable } from "@nestjs/common"; -import {diffLine} from "../../../../utils/diffline"; +import { diffLine } from "../../../../utils/diffline"; @Injectable() export class PullChangeCodeAndInsertDbService { - constructor(private readonly prisma: PrismaService) {} - async invoke({ projectID, sha, compareTarget }) { - const { provider, repoID } = parseProjectID(projectID); - const codechanges = await this.prisma.codechange.findMany({ - where: { - sha: sha, - compareTarget, - }, - }); - const gitProvider = await this.prisma.gitProvider.findFirst({ - where: { - disabled: false, - }, - }); - if (codechanges.length === 0) { - const diffLineData = await diffLine({ - repoID: repoID, - baseCommitSha: compareTarget, - compareCommitSha: sha, - token: gitProvider?.privateToken, - gitlabUrl: gitProvider?.url, - }); - const data = diffLineData.map(({ path, additions, deletions }) => { - return { - provider, - repoID, - sha: sha, - compareTarget, - path, - additions, - deletions, - }; - }); + constructor(private readonly prisma: PrismaService) {} + async invoke({ projectID, sha, compareTarget }) { + const { provider, repoID } = parseProjectID(projectID); + const codechanges = await this.prisma.codechange.findMany({ + where: { + sha: sha, + compareTarget, + }, + }); + const gitProvider = await this.prisma.gitProvider.findFirst({ + where: { + disabled: false, + }, + }); + if (codechanges.length === 0) { + const diffLineData = await diffLine({ + repoID: repoID, + baseCommitSha: compareTarget, + compareCommitSha: sha, + token: gitProvider?.privateToken, + gitlabUrl: gitProvider?.url, + }); + const data = diffLineData.map(({ path, additions, deletions }) => { + return { + provider, + repoID, + sha: sha, + compareTarget, + path, + additions, + deletions, + }; + }); - await this.prisma.codechange.createMany({ - data: data, - }); - } + await this.prisma.codechange.createMany({ + data: data, + }); } + } } diff --git a/packages/canyon-backend/src/apps/collect/services/common/test-exclude.service.ts b/packages/canyon-backend/src/apps/collect/services/common/test-exclude.service.ts index 2cb4ad50..d45da76f 100644 --- a/packages/canyon-backend/src/apps/collect/services/common/test-exclude.service.ts +++ b/packages/canyon-backend/src/apps/collect/services/common/test-exclude.service.ts @@ -3,50 +3,48 @@ import * as TestExclude from "test-exclude"; import { PrismaService } from "../../../../prisma/prisma.service"; @Injectable() export class TestExcludeService { - constructor(private readonly prisma: PrismaService) {} + constructor(private readonly prisma: PrismaService) {} - async invoke(projectID, coverage) { - const project = await this.prisma.project.findFirst({ - where: { - id: projectID, - }, - }); + async invoke(projectID, coverage) { + const project = await this.prisma.project.findFirst({ + where: { + id: projectID, + }, + }); - let matchRule: any = {}; // Default value + let matchRule: any = {}; // Default value - try { - // Attempt to parse project?.coverage - matchRule = JSON.parse(project?.coverage || "{}"); - } catch (error) { - // console.error('Error parsing coverage:', error); - // Log the error or handle it as needed - // You can also return an empty object or any default value - } - const exclude = new TestExclude({ - cwd: "", - include: matchRule.include, - exclude: matchRule.exclude || [], - extension: matchRule.extensions || [ - ".js", - ".cjs", - ".mjs", - ".ts", - ".tsx", - ".jsx", - ".vue", - ], - }); + try { + // Attempt to parse project?.coverage + matchRule = JSON.parse(project?.coverage || "{}"); + } catch (error) { + // console.error('Error parsing coverage:', error); + // Log the error or handle it as needed + // You can also return an empty object or any default value + } + const exclude = new TestExclude({ + cwd: "", + include: matchRule.include, + exclude: matchRule.exclude || [], + extension: matchRule.extensions || [ + ".js", + ".cjs", + ".mjs", + ".ts", + ".tsx", + ".jsx", + ".vue", + ], + }); - const filterCoverage = {}; + const filterCoverage = {}; - for (const filterCoverageKey of Object.keys(coverage)) { - // TODO 当过滤条件特别多的时候,性能会很差,大概能达到3s的计算时间,所以得在消费的时候就落库概览数据,summarys - if (exclude.shouldInstrument(filterCoverageKey)) { - filterCoverage[filterCoverageKey] = coverage[filterCoverageKey]; - } - } - return Object.keys(filterCoverage).length > 0 - ? filterCoverage - : coverage; + for (const filterCoverageKey of Object.keys(coverage)) { + // TODO 当过滤条件特别多的时候,性能会很差,大概能达到3s的计算时间,所以得在消费的时候就落库概览数据,summarys + if (exclude.shouldInstrument(filterCoverageKey)) { + filterCoverage[filterCoverageKey] = coverage[filterCoverageKey]; + } } + return Object.keys(filterCoverage).length > 0 ? filterCoverage : coverage; + } } diff --git a/packages/canyon-backend/src/apps/collect/services/core/consumer-coverage.service.ts b/packages/canyon-backend/src/apps/collect/services/core/consumer-coverage.service.ts index 09d54d39..084ba698 100644 --- a/packages/canyon-backend/src/apps/collect/services/core/consumer-coverage.service.ts +++ b/packages/canyon-backend/src/apps/collect/services/core/consumer-coverage.service.ts @@ -1,8 +1,8 @@ import { Injectable } from "@nestjs/common"; import { - genSummaryMapByCoverageMap, - getSummaryByPath, - mergeCoverageMap, + genSummaryMapByCoverageMap, + getSummaryByPath, + mergeCoverageMap, } from "canyon-data"; import { CoveragediskService } from "./coveragedisk.service"; @@ -26,244 +26,230 @@ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); */ @Injectable() export class ConsumerCoverageService { - constructor( - private readonly prisma: PrismaService, - private readonly coveragediskService: CoveragediskService, - private readonly pullChangeCodeAndInsertDbService: PullChangeCodeAndInsertDbService, - private readonly testExcludeService: TestExcludeService, - private readonly coverageFinalService: CoverageFinalService, - ) {} - - async invoke() { - console.log("ConsumerCoverageService start!!!"); - while (true) { + constructor( + private readonly prisma: PrismaService, + private readonly coveragediskService: CoveragediskService, + private readonly pullChangeCodeAndInsertDbService: PullChangeCodeAndInsertDbService, + private readonly testExcludeService: TestExcludeService, + private readonly coverageFinalService: CoverageFinalService, + ) {} + + async invoke() { + console.log("ConsumerCoverageService start!!!"); + while (true) { + try { + // 获取将要消费的队列数据,sha和projectID相同,并且已经聚合 + const queueDataToBeConsumed = + await this.coveragediskService.getQueueWithSameShaAndProjectID(); + // 如果存在 + if (queueDataToBeConsumed) { + const _lockName = "consumer_coverage"; + const lockName = `${_lockName}_${queueDataToBeConsumed.projectID}_${queueDataToBeConsumed.sha}`; + + const lockAcquired = await this.acquireLock(lockName, 1000 * 60 * 2); + + if (lockAcquired) { + const dataFormatAndCheckQueueDataToBeConsumed = + queueDataToBeConsumed; try { - // 获取将要消费的队列数据,sha和projectID相同,并且已经聚合 - const queueDataToBeConsumed = - await this.coveragediskService.getQueueWithSameShaAndProjectID(); - // 如果存在 - if (queueDataToBeConsumed) { - const _lockName = "consumer_coverage"; - const lockName = `${_lockName}_${queueDataToBeConsumed.projectID}_${queueDataToBeConsumed.sha}`; - - const lockAcquired = await this.acquireLock( - lockName, - 1000 * 60 * 2, - ); - - if (lockAcquired) { - const dataFormatAndCheckQueueDataToBeConsumed = - queueDataToBeConsumed; - try { - const reportIDs = [ - ...new Set( - dataFormatAndCheckQueueDataToBeConsumed.reportID.split( - ",", - ), - ), - ]; - for (let i = 0; i < reportIDs.length; i++) { - await this.consume( - Object.assign( - dataFormatAndCheckQueueDataToBeConsumed, - { - reportID: reportIDs[i], - }, - ), - "agg", - ); - } - await this.consume( - dataFormatAndCheckQueueDataToBeConsumed, - "all", - ); - // 执行任务 - } finally { - await this.releaseLock(lockName); - } - } else { - await this.coveragediskService.pushQueue( - queueDataToBeConsumed, - ); - await sleep(1000); - // 锁已被其他实例持有,无法获取锁 - } - } else { - // 空闲等待1s,防止cpu占用过高 - await sleep(3000); - } - } catch (e) { - console.log(e); - await sleep(3000); + const reportIDs = [ + ...new Set( + dataFormatAndCheckQueueDataToBeConsumed.reportID.split(","), + ), + ]; + for (let i = 0; i < reportIDs.length; i++) { + await this.consume( + Object.assign(dataFormatAndCheckQueueDataToBeConsumed, { + reportID: reportIDs[i], + }), + "agg", + ); + } + await this.consume( + dataFormatAndCheckQueueDataToBeConsumed, + "all", + ); + // 执行任务 + } finally { + await this.releaseLock(lockName); } + } else { + await this.coveragediskService.pushQueue(queueDataToBeConsumed); + await sleep(1000); + // 锁已被其他实例持有,无法获取锁 + } + } else { + // 空闲等待1s,防止cpu占用过高 + await sleep(3000); } + } catch (e) { + console.log(e); + await sleep(3000); + } } - async consume(queueDataToBeConsumed, covType) { - // 读取agg类型的数据 - - const coverage = await this.prisma.coverage.findFirst({ + } + async consume(queueDataToBeConsumed, covType) { + // 读取agg类型的数据 + + const coverage = await this.prisma.coverage.findFirst({ + where: { + sha: queueDataToBeConsumed.sha, + projectID: queueDataToBeConsumed.projectID, + covType: covType, + reportID: + covType === "agg" ? queueDataToBeConsumed.reportID : undefined, + }, + }); + + // 拉取变更代码 + await this.pullChangeCode(queueDataToBeConsumed); + // 判断是否需要拉取变更代码,对比sha和compareTarget + const codechanges = + queueDataToBeConsumed.sha === queueDataToBeConsumed.compareTarget + ? [] + : await this.prisma.codechange.findMany({ where: { - sha: queueDataToBeConsumed.sha, - projectID: queueDataToBeConsumed.projectID, - covType: covType, - reportID: - covType === "agg" - ? queueDataToBeConsumed.reportID - : undefined, + sha: queueDataToBeConsumed.sha, + compareTarget: queueDataToBeConsumed.compareTarget, }, - }); - - // 拉取变更代码 - await this.pullChangeCode(queueDataToBeConsumed); - // 判断是否需要拉取变更代码,对比sha和compareTarget - const codechanges = - queueDataToBeConsumed.sha === queueDataToBeConsumed.compareTarget - ? [] - : await this.prisma.codechange.findMany({ - where: { - sha: queueDataToBeConsumed.sha, - compareTarget: queueDataToBeConsumed.compareTarget, - }, - }); - - // TODO cov应该是全量的,应该是find出来的hit,因为已经合并过了,避免重复 - - // 关键节点,合并过后的数据,这里的合并并不需要map数据参与,也不一定,因为sourceMap的存在 - const mergedHit = mergeCoverageMap( - queueDataToBeConsumed.coverage, - // 不一定有 - await decompressedData(coverage?.hit), - ); - - const newCoverage = await this.coverageFinalService.invoke( - { - projectID: queueDataToBeConsumed.projectID, - sha: queueDataToBeConsumed.sha, - // 这里要手动指定reportID,因为如果是all类型,reportID也存在 - reportID: - covType === "agg" - ? queueDataToBeConsumed.reportID - : undefined, - }, - mergedHit, - true, - ); - - const summary = genSummaryMapByCoverageMap( - await this.testExcludeService.invoke( - queueDataToBeConsumed.projectID, - newCoverage, - ), - codechanges, - ); - const sum: any = getSummaryByPath("", summary); - const summaryZstd = await compressedData(summary); - - // 实际存储不能用全量数据,大10倍,非常重要,只留下bfs的数据,体积小10倍,是reMap的 - const compressedHit = await compressedData( - IstanbulHitMapSchema.parse(mergedHit), - ); - if (coverage) { - await this.prisma.coverage.update({ - where: { - id: coverage.id, - }, - data: { - hit: compressedHit, - ...summaryToDbSummary(sum), - summary: summaryZstd, - // TODO 暂时 - updatedAt: new Date(), - compareTarget: queueDataToBeConsumed.compareTarget, - }, - }); // 更新时间 - } else { - // 创建新的agg - await this.prisma.coverage.create({ - data: { - ...coverageObj, - hit: compressedHit, - covType: covType, - ...summaryToDbSummary(sum), - summary: summaryZstd, - sha: queueDataToBeConsumed.sha, - projectID: queueDataToBeConsumed.projectID, - reporter: String(queueDataToBeConsumed.reporter), - reportID: queueDataToBeConsumed.reportID, - }, - }); - } + }); + + // TODO cov应该是全量的,应该是find出来的hit,因为已经合并过了,避免重复 + + // 关键节点,合并过后的数据,这里的合并并不需要map数据参与,也不一定,因为sourceMap的存在 + const mergedHit = mergeCoverageMap( + queueDataToBeConsumed.coverage, + // 不一定有 + await decompressedData(coverage?.hit), + ); + + const newCoverage = await this.coverageFinalService.invoke( + { + projectID: queueDataToBeConsumed.projectID, + sha: queueDataToBeConsumed.sha, + // 这里要手动指定reportID,因为如果是all类型,reportID也存在 + reportID: + covType === "agg" ? queueDataToBeConsumed.reportID : undefined, + }, + mergedHit, + true, + ); + + const summary = genSummaryMapByCoverageMap( + await this.testExcludeService.invoke( + queueDataToBeConsumed.projectID, + newCoverage, + ), + codechanges, + ); + const sum: any = getSummaryByPath("", summary); + const summaryZstd = await compressedData(summary); + + // 实际存储不能用全量数据,大10倍,非常重要,只留下bfs的数据,体积小10倍,是reMap的 + const compressedHit = await compressedData( + IstanbulHitMapSchema.parse(mergedHit), + ); + if (coverage) { + await this.prisma.coverage.update({ + where: { + id: coverage.id, + }, + data: { + hit: compressedHit, + ...summaryToDbSummary(sum), + summary: summaryZstd, + // TODO 暂时 + updatedAt: new Date(), + compareTarget: queueDataToBeConsumed.compareTarget, + }, + }); // 更新时间 + } else { + // 创建新的agg + await this.prisma.coverage.create({ + data: { + ...coverageObj, + hit: compressedHit, + covType: covType, + ...summaryToDbSummary(sum), + summary: summaryZstd, + sha: queueDataToBeConsumed.sha, + projectID: queueDataToBeConsumed.projectID, + reporter: String(queueDataToBeConsumed.reporter), + reportID: queueDataToBeConsumed.reportID, + }, + }); } - async pullChangeCode(coverage) { - if (coverage.sha !== coverage.compareTarget) { - try { - await this.pullChangeCodeAndInsertDbService.invoke({ - projectID: coverage.projectID, - sha: coverage.sha, - compareTarget: coverage.compareTarget, - }); - } catch (e) { - logger({ - type: "error", - title: "pullChangeCode", - message: String(e), - }); - } - } - } - - async acquireLock(lockName: string, lockTimeout: number): Promise { - const now = new Date(); - const expirationTime = new Date(now.getTime() + lockTimeout); - try { - // 查询锁 - const existingLock = await this.prisma.distributedlock.findUnique({ - where: { - lockName, - }, - }); - - if (existingLock) { - // 锁已存在,检查是否已过期或者已被释放 - if (existingLock.lockExpiration < now) { - // 锁未被持有或者已过期,尝试更新锁 - await this.prisma.distributedlock.update({ - where: { - lockName, - }, - data: { - lockTimestamp: now, - lockExpiration: expirationTime, - }, - }); - return true; // 锁获取成功 - } else { - // 锁被其他实例持有且未过期 - return false; // 锁获取失败 - } - } else { - // 锁不存在,创建新锁 - await this.prisma.distributedlock.create({ - data: { - lockName, - isLocked: true, - lockTimestamp: now, - lockExpiration: expirationTime, - }, - }); - return true; // 锁获取成功 - } - } catch (error) { - console.error("Error acquiring lock:", error); - return false; // 锁获取失败 - } + } + async pullChangeCode(coverage) { + if (coverage.sha !== coverage.compareTarget) { + try { + await this.pullChangeCodeAndInsertDbService.invoke({ + projectID: coverage.projectID, + sha: coverage.sha, + compareTarget: coverage.compareTarget, + }); + } catch (e) { + logger({ + type: "error", + title: "pullChangeCode", + message: String(e), + }); + } } - - async releaseLock(lockName: string): Promise { - await this.prisma.distributedlock.delete({ + } + + async acquireLock(lockName: string, lockTimeout: number): Promise { + const now = new Date(); + const expirationTime = new Date(now.getTime() + lockTimeout); + try { + // 查询锁 + const existingLock = await this.prisma.distributedlock.findUnique({ + where: { + lockName, + }, + }); + + if (existingLock) { + // 锁已存在,检查是否已过期或者已被释放 + if (existingLock.lockExpiration < now) { + // 锁未被持有或者已过期,尝试更新锁 + await this.prisma.distributedlock.update({ where: { - lockName, + lockName, }, + data: { + lockTimestamp: now, + lockExpiration: expirationTime, + }, + }); + return true; // 锁获取成功 + } else { + // 锁被其他实例持有且未过期 + return false; // 锁获取失败 + } + } else { + // 锁不存在,创建新锁 + await this.prisma.distributedlock.create({ + data: { + lockName, + isLocked: true, + lockTimestamp: now, + lockExpiration: expirationTime, + }, }); + return true; // 锁获取成功 + } + } catch (error) { + console.error("Error acquiring lock:", error); + return false; // 锁获取失败 } + } + + async releaseLock(lockName: string): Promise { + await this.prisma.distributedlock.delete({ + where: { + lockName, + }, + }); + } } diff --git a/packages/canyon-backend/src/apps/collect/services/core/coveragedisk.service.ts b/packages/canyon-backend/src/apps/collect/services/core/coveragedisk.service.ts index 19a1b91b..6db72a5e 100644 --- a/packages/canyon-backend/src/apps/collect/services/core/coveragedisk.service.ts +++ b/packages/canyon-backend/src/apps/collect/services/core/coveragedisk.service.ts @@ -10,79 +10,75 @@ import { mergeCoverageMap } from "canyon-data"; @Injectable() export class CoveragediskService { - constructor( - @InjectRepository(CoveragediskEntity) - private readonly coveragediskRepository: Repository, - ) {} - async pushQueue(data: { - projectID: string; - sha: string; - reportID: string; - coverage: any; - compareTarget: string; - reporter: string; - }) { - return this.coveragediskRepository.insert({ - pid: String(process.pid), - projectID: data.projectID, - sha: data.sha, - reportID: data.reportID, - data: JSON.stringify(data), - createdAt: new Date(), - }); + constructor( + @InjectRepository(CoveragediskEntity) + private readonly coveragediskRepository: Repository, + ) {} + async pushQueue(data: { + projectID: string; + sha: string; + reportID: string; + coverage: any; + compareTarget: string; + reporter: string; + }) { + return this.coveragediskRepository.insert({ + pid: String(process.pid), + projectID: data.projectID, + sha: data.sha, + reportID: data.reportID, + data: JSON.stringify(data), + createdAt: new Date(), + }); + } + async getQueueWithSameShaAndProjectID() { + const old = await this.coveragediskRepository.findOne({ + where: { + pid: String(process.pid), + }, + order: { + createdAt: "ASC", + }, + select: { + pid: true, + projectID: true, + sha: true, + data: true, + reportID: true, + }, + }); + if (!old) { + return false; } - async getQueueWithSameShaAndProjectID() { - const old = await this.coveragediskRepository.findOne({ - where: { - pid: String(process.pid), - }, - order: { - createdAt: "ASC", - }, - select: { - pid: true, - projectID: true, - sha: true, - data: true, - reportID: true, - }, - }); - if (!old) { - return false; - } - const coveragedisks = await this.coveragediskRepository.find({ - where: { - projectID: old.projectID, - sha: old.sha, - pid: String(process.pid), - reportID: old.reportID, - }, - select: { - id: true, - }, - }); - let cov = {}; - for (let i = 0; i < coveragedisks.length; i++) { - const toBeConsumedQueues = - await this.coveragediskRepository.findOne({ - where: { - id: coveragedisks[i].id, - pid: String(process.pid), - }, - }); - // 聚合 - // cov = JSON.parse(merge_coverage_json_str(JSON.stringify(cov), JSON.stringify(JSON.parse(toBeConsumedQueues.data).coverage))); - cov = mergeCoverageMap( - cov, - JSON.parse(toBeConsumedQueues.data).coverage, - ); - await this.coveragediskRepository.delete({ - id: coveragedisks[i].id, - }); - } - return { - ...JSON.parse(old.data), - coverage: cov, - }; + const coveragedisks = await this.coveragediskRepository.find({ + where: { + projectID: old.projectID, + sha: old.sha, + pid: String(process.pid), + reportID: old.reportID, + }, + select: { + id: true, + }, + }); + let cov = {}; + for (let i = 0; i < coveragedisks.length; i++) { + const toBeConsumedQueues = await this.coveragediskRepository.findOne({ + where: { + id: coveragedisks[i].id, + pid: String(process.pid), + }, + }); + // 聚合 + // cov = JSON.parse(merge_coverage_json_str(JSON.stringify(cov), JSON.stringify(JSON.parse(toBeConsumedQueues.data).coverage))); + cov = mergeCoverageMap(cov, JSON.parse(toBeConsumedQueues.data).coverage); + await this.coveragediskRepository.delete({ + id: coveragedisks[i].id, + }); } + return { + ...JSON.parse(old.data), + coverage: cov, + }; + } } diff --git a/packages/canyon-backend/src/apps/collect/services/coverage-client.service.ts b/packages/canyon-backend/src/apps/collect/services/coverage-client.service.ts index 08be933e..c0ae1cde 100755 --- a/packages/canyon-backend/src/apps/collect/services/coverage-client.service.ts +++ b/packages/canyon-backend/src/apps/collect/services/coverage-client.service.ts @@ -5,91 +5,91 @@ import { IstanbulHitMapSchema } from "../../../zod/istanbul.zod"; import { formatCoverageData, parseProjectID } from "canyon-data"; import { CoveragediskService } from "./core/coveragedisk.service"; import { CoverageFinalService } from "./common/coverage-final.service"; -import {formatReportObject} from "../../../utils/coverage"; +import { formatReportObject } from "../../../utils/coverage"; // 此代码重中之重、核心中的核心!!! @Injectable() export class CoverageClientService { - constructor( - private readonly prisma: PrismaService, - private readonly coverageMapClientService: CoverageMapClientService, - private coveragediskService: CoveragediskService, - private coverageFinalService: CoverageFinalService, - ) {} - async invoke({ + constructor( + private readonly prisma: PrismaService, + private readonly coverageMapClientService: CoverageMapClientService, + private coveragediskService: CoveragediskService, + private coverageFinalService: CoverageFinalService, + ) {} + async invoke({ + sha, + projectID, + coverage, + instrumentCwd, + reportID: _reportID, + branch, + compareTarget, + reporter, + }) { + const { repoID } = parseProjectID(projectID); + const reportID = _reportID || sha; + // #region == Step x: 解析出上报上来的覆盖率数据 + const coverageFromExternalReport = + typeof coverage === "string" ? JSON.parse(coverage) : coverage; + // #endregion + + // 首先就要判断,这个是可选步骤,所以用单if,以statementMap来判断 + if ( + Object.keys(coverageFromExternalReport).length > 0 && + Object.values(coverageFromExternalReport)[0]["statementMap"] + ) { + // 构建一个coverageMapClientService + await this.coverageMapClientService.invoke({ sha, projectID, coverage, instrumentCwd, - reportID: _reportID, - branch, - compareTarget, - reporter, - }) { - const { repoID } = parseProjectID(projectID); - const reportID = _reportID || sha; - // #region == Step x: 解析出上报上来的覆盖率数据 - const coverageFromExternalReport = - typeof coverage === "string" ? JSON.parse(coverage) : coverage; - // #endregion - - // 首先就要判断,这个是可选步骤,所以用单if,以statementMap来判断 - if ( - Object.keys(coverageFromExternalReport).length > 0 && - Object.values(coverageFromExternalReport)[0]["statementMap"] - ) { - // 构建一个coverageMapClientService - await this.coverageMapClientService.invoke({ - sha, - projectID, - coverage, - instrumentCwd, - branch: branch || "-", - compareTarget: compareTarget || sha, - }); - } - const count = await this.prisma.coverageMap.count({ - where: { - repoID: { - contains: repoID, - }, - sha: sha, - }, - }); - if (count === 0) { - throw new HttpException("coverage map not found", 400); - } + branch: branch || "-", + compareTarget: compareTarget || sha, + }); + } + const count = await this.prisma.coverageMap.count({ + where: { + repoID: { + contains: repoID, + }, + sha: sha, + }, + }); + if (count === 0) { + throw new HttpException("coverage map not found", 400); + } - const { coverage: formartCOv } = formatReportObject({ - coverage: formatCoverageData(coverageFromExternalReport), - instrumentCwd: instrumentCwd, - }); + const { coverage: formartCOv } = formatReportObject({ + coverage: formatCoverageData(coverageFromExternalReport), + instrumentCwd: instrumentCwd, + }); - const originalHit = IstanbulHitMapSchema.parse(formartCOv); + const originalHit = IstanbulHitMapSchema.parse(formartCOv); - // 重要!!!,这里是reMap过的数据 - const coveragewenhao = await this.coverageFinalService.invoke( - { - projectID, - sha, - // reportID, 这里不需要reportID了 - }, - originalHit, - ); + // 重要!!!,这里是reMap过的数据 + const coveragewenhao = await this.coverageFinalService.invoke( + { + projectID, + sha, + // reportID, 这里不需要reportID了 + }, + originalHit, + ); - await this.coveragediskService.pushQueue({ - projectID, - sha, - reportID, - compareTarget: compareTarget || sha, - coverage: IstanbulHitMapSchema.parse(coveragewenhao), - reporter, - }); - return { - msg: "ok", - coverageId: "", - dataFormatAndCheckTime: "", - coverageInsertDbTime: "", - }; - } + await this.coveragediskService.pushQueue({ + projectID, + sha, + reportID, + compareTarget: compareTarget || sha, + coverage: IstanbulHitMapSchema.parse(coveragewenhao), + reporter, + }); + return { + msg: "ok", + coverageId: "", + dataFormatAndCheckTime: "", + coverageInsertDbTime: "", + }; + } } diff --git a/packages/canyon-backend/src/apps/collect/services/coverage-map-client.service.ts b/packages/canyon-backend/src/apps/collect/services/coverage-map-client.service.ts index edf94b5f..6e435db6 100755 --- a/packages/canyon-backend/src/apps/collect/services/coverage-map-client.service.ts +++ b/packages/canyon-backend/src/apps/collect/services/coverage-map-client.service.ts @@ -2,133 +2,133 @@ import { PrismaService } from "../../../prisma/prisma.service"; import { Injectable } from "@nestjs/common"; import { coverageObj } from "../models/coverage.model"; import { - formatCoverageData, - genSummaryMapByCoverageMap, - getSummaryByPath, - parseProjectID, - resetCoverageDataMap, + formatCoverageData, + genSummaryMapByCoverageMap, + getSummaryByPath, + parseProjectID, + resetCoverageDataMap, } from "canyon-data"; import { - IstanbulHitMapSchema, - IstanbulMapMapSchema, + IstanbulHitMapSchema, + IstanbulMapMapSchema, } from "../../../zod/istanbul.zod"; import { compressedData, remapCoverageWithInstrumentCwd } from "canyon-map"; import { formatReportObject } from "../../../utils/coverage"; -import {summaryToDbSummary} from "../../../utils/utils"; +import { summaryToDbSummary } from "../../../utils/utils"; function getNewPathByOldPath(covMap, path) { + // @ts-ignore + const arr = Object.values(covMap).filter((item) => item.oldPath === path); + if (arr.length > 0) { // @ts-ignore - const arr = Object.values(covMap).filter((item) => item.oldPath === path); - if (arr.length > 0) { - // @ts-ignore - return arr[0].path; - } else { - return path; - } + return arr[0].path; + } else { + return path; + } } @Injectable() export class CoverageMapClientService { - constructor(private readonly prisma: PrismaService) {} - async invoke({ - sha, - projectID, - coverage, - instrumentCwd, - branch, - compareTarget, - }) { - const { provider, repoID } = parseProjectID(projectID); - const coverageFromExternalReport = - typeof coverage === "string" ? JSON.parse(coverage) : coverage; - // #endregion + constructor(private readonly prisma: PrismaService) {} + async invoke({ + sha, + projectID, + coverage, + instrumentCwd, + branch, + compareTarget, + }) { + const { provider, repoID } = parseProjectID(projectID); + const coverageFromExternalReport = + typeof coverage === "string" ? JSON.parse(coverage) : coverage; + // #endregion - // 原来的代码 - // ************************************************************ - const { coverage: formatedCoverage } = formatReportObject({ - coverage: formatCoverageData(coverageFromExternalReport), - instrumentCwd: instrumentCwd, - }); + // 原来的代码 + // ************************************************************ + const { coverage: formatedCoverage } = formatReportObject({ + coverage: formatCoverageData(coverageFromExternalReport), + instrumentCwd: instrumentCwd, + }); - const formatCoverageMap = IstanbulMapMapSchema.parse(formatedCoverage); + const formatCoverageMap = IstanbulMapMapSchema.parse(formatedCoverage); - const resetCovMap = resetCoverageDataMap(formatCoverageMap); + const resetCovMap = resetCoverageDataMap(formatCoverageMap); - // #region == Step x: 覆盖率回溯,在覆盖率存储之前转换(这里一定要用数据库里的instrumentCwd,因为和map是对应的!!!) - const hitObject: any = await remapCoverageWithInstrumentCwd( - resetCovMap, - instrumentCwd, - ); + // #region == Step x: 覆盖率回溯,在覆盖率存储之前转换(这里一定要用数据库里的instrumentCwd,因为和map是对应的!!!) + const hitObject: any = await remapCoverageWithInstrumentCwd( + resetCovMap, + instrumentCwd, + ); - // const compressedFormatCoverageStr = - // await compressedData(formatCoverageMap); - const hit = await compressedData(IstanbulHitMapSchema.parse(hitObject)); + // const compressedFormatCoverageStr = + // await compressedData(formatCoverageMap); + const hit = await compressedData(IstanbulHitMapSchema.parse(hitObject)); - const summaryObject = genSummaryMapByCoverageMap(hitObject, []); - const overallSummary: any = getSummaryByPath("", summaryObject); - const summary = await compressedData(summaryObject); - // ************************************************************ - // 原来的代码 + const summaryObject = genSummaryMapByCoverageMap(hitObject, []); + const overallSummary: any = getSummaryByPath("", summaryObject); + const summary = await compressedData(summaryObject); + // ************************************************************ + // 原来的代码 - // 提前插入 - await this.prisma.coverage - .create({ - data: { - ...coverageObj, - id: `__${projectID}__${sha}__`, - sha: sha, - projectID: projectID, - branch: branch, - summary: summary, - hit: hit, - ...summaryToDbSummary(overallSummary), - reportID: sha, - compareTarget: compareTarget || sha, // 默认是自己 - reporter: "canyon", - }, - }) - .catch(() => { - // console.log("coverage create error"); - }); + // 提前插入 + await this.prisma.coverage + .create({ + data: { + ...coverageObj, + id: `__${projectID}__${sha}__`, + sha: sha, + projectID: projectID, + branch: branch, + summary: summary, + hit: hit, + ...summaryToDbSummary(overallSummary), + reportID: sha, + compareTarget: compareTarget || sha, // 默认是自己 + reporter: "canyon", + }, + }) + .catch(() => { + // console.log("coverage create error"); + }); - /* + /* 这里的逻辑是每次批量插入上报的map数据,按文件存,不更新。 借助于数据库的id不重复,保证了数据不会重复插入。 // TODO 不确定对数据库的压力,是否需要优化 */ - const arr = Object.entries(formatCoverageMap).map(([path, map]) => { - return { - ...map, - path, - }; - }); - - const compressedArr = await Promise.all( - arr.map((item) => { - return compressedData(item).then((map) => { - return { - path: item.path, - map, - }; - }); - }), - ); + const arr = Object.entries(formatCoverageMap).map(([path, map]) => { + return { + ...map, + path, + }; + }); - // 避免重复录入 - return await this.prisma.coverageMap.createMany({ - data: compressedArr.map(({ path, map }: any) => { - return { - id: `__${provider}__${repoID}__${sha}__${path}__`, - map: map, //???没删除bfs - provider: provider, - repoID: repoID, - sha: sha, - path: getNewPathByOldPath(hitObject, path), - instrumentCwd: instrumentCwd, - }; - }), - skipDuplicates: true, + const compressedArr = await Promise.all( + arr.map((item) => { + return compressedData(item).then((map) => { + return { + path: item.path, + map, + }; }); - } + }), + ); + + // 避免重复录入 + return await this.prisma.coverageMap.createMany({ + data: compressedArr.map(({ path, map }: any) => { + return { + id: `__${provider}__${repoID}__${sha}__${path}__`, + map: map, //???没删除bfs + provider: provider, + repoID: repoID, + sha: sha, + path: getNewPathByOldPath(hitObject, path), + instrumentCwd: instrumentCwd, + }; + }), + skipDuplicates: true, + }); + } } diff --git a/packages/canyon-backend/src/apps/collect/valids/is-valid-coverage-map.ts b/packages/canyon-backend/src/apps/collect/valids/is-valid-coverage-map.ts index 95b9c8a5..d5b653d9 100755 --- a/packages/canyon-backend/src/apps/collect/valids/is-valid-coverage-map.ts +++ b/packages/canyon-backend/src/apps/collect/valids/is-valid-coverage-map.ts @@ -1,6 +1,6 @@ import { - ValidatorConstraint, - ValidatorConstraintInterface, + ValidatorConstraint, + ValidatorConstraintInterface, } from "class-validator"; /*// @@ -9,68 +9,66 @@ import { // */ function isValidCoverageMap(coverage) { - // 检查是否是对象 - if (typeof coverage !== "object" || coverage === null) { - return false; - } - // 检查是否有必须的属性 - const requiredProperties = [ - // "path", - "statementMap", - "fnMap", - "branchMap", - // "s", - // "f", - // "b", - ]; - for (const prop of requiredProperties) { - if (!(prop in coverage)) { - return false; - } - } - // 检查属性的类型和结构 - if ( - // typeof coverage.path !== "string" || - typeof coverage.statementMap !== "object" || - typeof coverage.fnMap !== "object" || - typeof coverage.branchMap !== "object" - // typeof coverage.s !== "object" || - // typeof coverage.f !== "object" || - // typeof coverage.b !== "object" - ) { - return false; + // 检查是否是对象 + if (typeof coverage !== "object" || coverage === null) { + return false; + } + // 检查是否有必须的属性 + const requiredProperties = [ + // "path", + "statementMap", + "fnMap", + "branchMap", + // "s", + // "f", + // "b", + ]; + for (const prop of requiredProperties) { + if (!(prop in coverage)) { + return false; } + } + // 检查属性的类型和结构 + if ( + // typeof coverage.path !== "string" || + typeof coverage.statementMap !== "object" || + typeof coverage.fnMap !== "object" || + typeof coverage.branchMap !== "object" + // typeof coverage.s !== "object" || + // typeof coverage.f !== "object" || + // typeof coverage.b !== "object" + ) { + return false; + } - // 如果所有检查通过,返回 true - return true; + // 如果所有检查通过,返回 true + return true; } // 安全转JSON function safeParseJSON(json) { - try { - return JSON.parse(json); - } catch (e) { - return {}; - } + try { + return JSON.parse(json); + } catch (e) { + return {}; + } } @ValidatorConstraint({ name: "isValidCoverage", async: false }) export class IsValidCoverage implements ValidatorConstraintInterface { - validate(_coverage: unknown) { - if (_coverage === null || _coverage === undefined) { - return false; - } - const coverage = - typeof _coverage === "string" - ? safeParseJSON(_coverage) - : _coverage; - if (Object.keys(coverage).length === 0) { - return false; - } - return Object.values(coverage || {}).every((item) => { - return isValidCoverageMap(item); - }); + validate(_coverage: unknown) { + if (_coverage === null || _coverage === undefined) { + return false; } - - defaultMessage() { - return "coverageMap格式不正确"; + const coverage = + typeof _coverage === "string" ? safeParseJSON(_coverage) : _coverage; + if (Object.keys(coverage).length === 0) { + return false; } + return Object.values(coverage || {}).every((item) => { + return isValidCoverageMap(item); + }); + } + + defaultMessage() { + return "coverageMap格式不正确"; + } } diff --git a/packages/canyon-backend/src/apps/collect/valids/is-valid-coverage.ts b/packages/canyon-backend/src/apps/collect/valids/is-valid-coverage.ts index 58663728..442345ec 100755 --- a/packages/canyon-backend/src/apps/collect/valids/is-valid-coverage.ts +++ b/packages/canyon-backend/src/apps/collect/valids/is-valid-coverage.ts @@ -1,75 +1,73 @@ import { - ValidatorConstraint, - ValidatorConstraintInterface, + ValidatorConstraint, + ValidatorConstraintInterface, } from "class-validator"; function isValidCoverage(coverage) { - // 检查是否是对象 - if (typeof coverage !== "object" || coverage === null) { - return false; - } - // 检查是否有必须的属性 - const requiredProperties = [ - // "path", - // "statementMap", - // "fnMap", - // "branchMap", - "s", - "f", - "b", - ]; - for (const prop of requiredProperties) { - if (!(prop in coverage)) { - return false; - } - } - // 检查属性的类型和结构 - if ( - // typeof coverage.path !== "string" || - // typeof coverage.statementMap !== "object" || - // typeof coverage.fnMap !== "object" || - // typeof coverage.branchMap !== "object" || - typeof coverage.s !== "object" || - typeof coverage.f !== "object" || - typeof coverage.b !== "object" - ) { - return false; + // 检查是否是对象 + if (typeof coverage !== "object" || coverage === null) { + return false; + } + // 检查是否有必须的属性 + const requiredProperties = [ + // "path", + // "statementMap", + // "fnMap", + // "branchMap", + "s", + "f", + "b", + ]; + for (const prop of requiredProperties) { + if (!(prop in coverage)) { + return false; } + } + // 检查属性的类型和结构 + if ( + // typeof coverage.path !== "string" || + // typeof coverage.statementMap !== "object" || + // typeof coverage.fnMap !== "object" || + // typeof coverage.branchMap !== "object" || + typeof coverage.s !== "object" || + typeof coverage.f !== "object" || + typeof coverage.b !== "object" + ) { + return false; + } - // 如果所有检查通过,返回 true - return true; + // 如果所有检查通过,返回 true + return true; } // 安全转JSON function safeParseJSON(json) { - try { - return JSON.parse(json); - } catch (e) { - return {}; - } + try { + return JSON.parse(json); + } catch (e) { + return {}; + } } @ValidatorConstraint({ name: "isValidCoverage", async: false }) export class IsValidCoverage implements ValidatorConstraintInterface { - validate(_coverage: unknown) { - if (_coverage === undefined) { - return true; - } - if (_coverage === null) { - // 把undefined的情况排出,有可能是blob数据 - // if (_coverage === null || _coverage === undefined) { - return false; - } - const coverage = - typeof _coverage === "string" - ? safeParseJSON(_coverage) - : _coverage; - if (Object.keys(coverage).length === 0) { - return false; - } - return Object.values(coverage || {}).every((item) => { - return isValidCoverage(item); - }); + validate(_coverage: unknown) { + if (_coverage === undefined) { + return true; } - - defaultMessage() { - return "coverage格式不正确"; + if (_coverage === null) { + // 把undefined的情况排出,有可能是blob数据 + // if (_coverage === null || _coverage === undefined) { + return false; + } + const coverage = + typeof _coverage === "string" ? safeParseJSON(_coverage) : _coverage; + if (Object.keys(coverage).length === 0) { + return false; } + return Object.values(coverage || {}).every((item) => { + return isValidCoverage(item); + }); + } + + defaultMessage() { + return "coverage格式不正确"; + } } diff --git a/packages/canyon-backend/src/auth/auth.controller.ts b/packages/canyon-backend/src/auth/auth.controller.ts index ccbf261a..1e988960 100755 --- a/packages/canyon-backend/src/auth/auth.controller.ts +++ b/packages/canyon-backend/src/auth/auth.controller.ts @@ -2,31 +2,29 @@ import { Body, Controller, Post } from "@nestjs/common"; import { AuthService } from "./auth.service"; import { OauthgitproviderService } from "./services/oauthgitprovider.service"; interface Oauthgitproviderparams { - code: string; - type: string; //github + code: string; + type: string; //github } @Controller() export class AuthController { - constructor( - private readonly authService: AuthService, - private readonly oauthgitproviderService: OauthgitproviderService, - ) {} + constructor( + private readonly authService: AuthService, + private readonly oauthgitproviderService: OauthgitproviderService, + ) {} - @Post("/api/oauth/token") - async oauthToken(@Body() params: any) { - return this.authService.oauthToken(params); - } + @Post("/api/oauth/token") + async oauthToken(@Body() params: any) { + return this.authService.oauthToken(params); + } - @Post("/api/oauth/git/provider") - async oauthgitprovider(@Body() params: Oauthgitproviderparams) { - // 通过code兑换到用户信息后,写入git表,主要保存token - return this.oauthgitproviderService.invoke(params); - } + @Post("/api/oauth/git/provider") + async oauthgitprovider(@Body() params: Oauthgitproviderparams) { + // 通过code兑换到用户信息后,写入git表,主要保存token + return this.oauthgitproviderService.invoke(params); + } - @Post("/api/login") - async passwordLogin( - @Body() reqBody: { username: string; password: string }, - ) { - return this.authService.passwordLogin(reqBody); - } + @Post("/api/login") + async passwordLogin(@Body() reqBody: { username: string; password: string }) { + return this.authService.passwordLogin(reqBody); + } } diff --git a/packages/canyon-backend/src/auth/auth.module.ts b/packages/canyon-backend/src/auth/auth.module.ts index 543634ce..02056788 100755 --- a/packages/canyon-backend/src/auth/auth.module.ts +++ b/packages/canyon-backend/src/auth/auth.module.ts @@ -10,21 +10,16 @@ import { PrismaModule } from "../prisma/prisma.module"; import { OauthgitproviderService } from "./services/oauthgitprovider.service"; @Module({ - imports: [ - PassportModule, - JwtModule.register({ - secret: jwtConstants.secret, - signOptions: { expiresIn: "10y" }, - }), - PrismaModule, - ], - controllers: [AuthController], - providers: [ - AuthService, - LocalStrategy, - JwtStrategy, - OauthgitproviderService, - ], - exports: [AuthService], + imports: [ + PassportModule, + JwtModule.register({ + secret: jwtConstants.secret, + signOptions: { expiresIn: "10y" }, + }), + PrismaModule, + ], + controllers: [AuthController], + providers: [AuthService, LocalStrategy, JwtStrategy, OauthgitproviderService], + exports: [AuthService], }) export class AuthModule {} diff --git a/packages/canyon-backend/src/auth/auth.service.ts b/packages/canyon-backend/src/auth/auth.service.ts index 24399d6f..18514d55 100755 --- a/packages/canyon-backend/src/auth/auth.service.ts +++ b/packages/canyon-backend/src/auth/auth.service.ts @@ -6,137 +6,137 @@ import { convertSystemSettingsFromTheDatabase } from "../utils/sys"; @Injectable() export class AuthService { - constructor( - private readonly jwtService: JwtService, - private readonly prisma: PrismaService, - ) { - this.prisma.user.upsert({ - where: { - id: "1", - }, - update: {}, - create: { - id: "1", - // username: "canyon", - password: "123456", - nickname: "canyon", - email: "canyon@canyon.com", - avatar: "/default-avatar.png", - favor: "", - createdAt: new Date(), - // accessToken: "", - // refreshToken: "", - }, - }); - } - async validateUser(username: string, password: string): Promise { - return ( - (await this.prisma.user.findFirst({ - where: { - email: username, - password, - }, - })) || null - ); - } - - async login(user: any) { - const payload = { - username: user.username, - id: user.id, - }; - // AuthService.login 第三步:存储信息 - return { - token: this.jwtService.sign(payload), - }; - } + constructor( + private readonly jwtService: JwtService, + private readonly prisma: PrismaService, + ) { + this.prisma.user.upsert({ + where: { + id: "1", + }, + update: {}, + create: { + id: "1", + // username: "canyon", + password: "123456", + nickname: "canyon", + email: "canyon@canyon.com", + avatar: "/default-avatar.png", + favor: "", + createdAt: new Date(), + // accessToken: "", + // refreshToken: "", + }, + }); + } + async validateUser(username: string, password: string): Promise { + return ( + (await this.prisma.user.findFirst({ + where: { + email: username, + password, + }, + })) || null + ); + } - async oauthToken(params) { - const { gitlabServer, gitlabClientID, gitlabClientSecret } = - await this.prisma.sysSetting - .findMany({}) - .then((res) => convertSystemSettingsFromTheDatabase(res)); - const { access_token: accessToken, refresh_token: refreshToken } = - await axios - .post(`${gitlabServer}/oauth/token`, undefined, { - params: { - client_id: gitlabClientID, - client_secret: gitlabClientSecret, - code: params.code, - grant_type: "authorization_code", - redirect_uri: params.redirectUri, - }, - }) - .then((res) => { - return res.data; - }) - .catch((err) => { - console.log(err); - // 如果没兑换到就抛异常 - throw new UnauthorizedException(); - }); - // 2.如果成功拿到token,先去gitlab那边校验一下,拿到用户信息 - const { - username, - name: nickname, - avatar_url: avatar, - email, - id: id, - } = await axios - .get(`${gitlabServer}/api/v4/user`, { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }) - .then((res) => { - return res.data; - }); - // 3.通过gitlab userId到db中查找 - const user = { - // accessToken, - // refreshToken, - // username, - nickname, - avatar: avatar || "/default-avatar.png", - email, - password: "123456", - favor: "", - createdAt: new Date(), - }; + async login(user: any) { + const payload = { + username: user.username, + id: user.id, + }; + // AuthService.login 第三步:存储信息 + return { + token: this.jwtService.sign(payload), + }; + } - const userFindDB = await this.prisma.user.upsert({ - where: { - id: String(id), - }, - update: { - // accessToken, - // refreshToken, - }, - create: { - id: String(id), - ...user, - }, + async oauthToken(params) { + const { gitlabServer, gitlabClientID, gitlabClientSecret } = + await this.prisma.sysSetting + .findMany({}) + .then((res) => convertSystemSettingsFromTheDatabase(res)); + const { access_token: accessToken, refresh_token: refreshToken } = + await axios + .post(`${gitlabServer}/oauth/token`, undefined, { + params: { + client_id: gitlabClientID, + client_secret: gitlabClientSecret, + code: params.code, + grant_type: "authorization_code", + redirect_uri: params.redirectUri, + }, + }) + .then((res) => { + return res.data; + }) + .catch((err) => { + console.log(err); + // 如果没兑换到就抛异常 + throw new UnauthorizedException(); }); + // 2.如果成功拿到token,先去gitlab那边校验一下,拿到用户信息 + const { + username, + name: nickname, + avatar_url: avatar, + email, + id: id, + } = await axios + .get(`${gitlabServer}/api/v4/user`, { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }) + .then((res) => { + return res.data; + }); + // 3.通过gitlab userId到db中查找 + const user = { + // accessToken, + // refreshToken, + // username, + nickname, + avatar: avatar || "/default-avatar.png", + email, + password: "123456", + favor: "", + createdAt: new Date(), + }; - return this.login({ - username: user.email, - id: userFindDB.id, - }); - } + const userFindDB = await this.prisma.user.upsert({ + where: { + id: String(id), + }, + update: { + // accessToken, + // refreshToken, + }, + create: { + id: String(id), + ...user, + }, + }); - async passwordLogin(params) { - const isHasUser = await this.prisma.user.findFirst({ - where: { - email: params.username, - password: params.password, - }, - }); - if (!isHasUser) { - throw new UnauthorizedException(); - } - return this.login({ - username: isHasUser.email, - id: isHasUser.id, - }); + return this.login({ + username: user.email, + id: userFindDB.id, + }); + } + + async passwordLogin(params) { + const isHasUser = await this.prisma.user.findFirst({ + where: { + email: params.username, + password: params.password, + }, + }); + if (!isHasUser) { + throw new UnauthorizedException(); } + return this.login({ + username: isHasUser.email, + id: isHasUser.id, + }); + } } diff --git a/packages/canyon-backend/src/auth/constants.ts b/packages/canyon-backend/src/auth/constants.ts index e29b040c..ff0570a1 100755 --- a/packages/canyon-backend/src/auth/constants.ts +++ b/packages/canyon-backend/src/auth/constants.ts @@ -1,3 +1,3 @@ export const jwtConstants = { - secret: "secretKey", + secret: "secretKey", }; diff --git a/packages/canyon-backend/src/auth/guards/jwt-auth.guard.ts b/packages/canyon-backend/src/auth/guards/jwt-auth.guard.ts index c87d4c98..7d5050b8 100755 --- a/packages/canyon-backend/src/auth/guards/jwt-auth.guard.ts +++ b/packages/canyon-backend/src/auth/guards/jwt-auth.guard.ts @@ -4,17 +4,17 @@ import { Reflector } from "@nestjs/core"; import { IS_PUBLIC_KEY } from "../../decorators/public.decorator"; @Injectable() export class JwtAuthGuard extends AuthGuard("jwt") { - constructor(private reflector: Reflector) { - super(); - } - canActivate(context: ExecutionContext): any { - const isPublic = this.reflector.getAllAndOverride( - IS_PUBLIC_KEY, - [context.getHandler(), context.getClass()], - ); - if (isPublic) { - return true; - } - return super.canActivate(context); + constructor(private reflector: Reflector) { + super(); + } + canActivate(context: ExecutionContext): any { + const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + if (isPublic) { + return true; } + return super.canActivate(context); + } } diff --git a/packages/canyon-backend/src/auth/services/oauthgitprovider.service.ts b/packages/canyon-backend/src/auth/services/oauthgitprovider.service.ts index 3147b9c6..2e5b6dd3 100644 --- a/packages/canyon-backend/src/auth/services/oauthgitprovider.service.ts +++ b/packages/canyon-backend/src/auth/services/oauthgitprovider.service.ts @@ -4,11 +4,11 @@ import { PrismaService } from "../../prisma/prisma.service"; @Injectable() export class OauthgitproviderService { - constructor( - // private readonly jwtService: JwtService, - private readonly prisma: PrismaService, - ) {} - invoke(params) { - return {}; - } + constructor( + // private readonly jwtService: JwtService, + private readonly prisma: PrismaService, + ) {} + invoke(params) { + return {}; + } } diff --git a/packages/canyon-backend/src/auth/strategies/jwt.strategy.ts b/packages/canyon-backend/src/auth/strategies/jwt.strategy.ts index 3b9f2420..bfd035f3 100755 --- a/packages/canyon-backend/src/auth/strategies/jwt.strategy.ts +++ b/packages/canyon-backend/src/auth/strategies/jwt.strategy.ts @@ -5,15 +5,15 @@ import { jwtConstants } from "../constants"; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { - constructor() { - super({ - jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), - ignoreExpiration: false, - secretOrKey: jwtConstants.secret, - }); - } + constructor() { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: jwtConstants.secret, + }); + } - async validate(payload: any) { - return payload; - } + async validate(payload: any) { + return payload; + } } diff --git a/packages/canyon-backend/src/auth/strategies/local.strategy.ts b/packages/canyon-backend/src/auth/strategies/local.strategy.ts index 74a23784..290be071 100755 --- a/packages/canyon-backend/src/auth/strategies/local.strategy.ts +++ b/packages/canyon-backend/src/auth/strategies/local.strategy.ts @@ -5,19 +5,19 @@ import { AuthService } from "../auth.service"; @Injectable() export class LocalStrategy extends PassportStrategy(Strategy) { - constructor(private readonly authService: AuthService) { - super({ - usernameField: "username", - passwordField: "password", - }); - } + constructor(private readonly authService: AuthService) { + super({ + usernameField: "username", + passwordField: "password", + }); + } - async validate(username: string, password: string): Promise { - // 第一步:从鉴权服务拿到username password,实际已经被转换过了 - const user = await this.authService.validateUser(username, password); - if (!user) { - throw new UnauthorizedException(); - } - return user; + async validate(username: string, password: string): Promise { + // 第一步:从鉴权服务拿到username password,实际已经被转换过了 + const user = await this.authService.validateUser(username, password); + if (!user) { + throw new UnauthorizedException(); } + return user; + } } diff --git a/packages/canyon-backend/src/codechange/codechange.controller.ts b/packages/canyon-backend/src/codechange/codechange.controller.ts index d63836ee..00df9d51 100644 --- a/packages/canyon-backend/src/codechange/codechange.controller.ts +++ b/packages/canyon-backend/src/codechange/codechange.controller.ts @@ -3,10 +3,10 @@ import { CodechangeService } from "./codechange.service"; @Controller("") export class CodechangeController { - constructor(private readonly codechangeService: CodechangeService) {} - @Get("api/codechange") - getCodechange(@Query() query): Promise { - const { sha = "", filepath = "" } = query; - return this.codechangeService.getCodechange(sha, filepath); - } + constructor(private readonly codechangeService: CodechangeService) {} + @Get("api/codechange") + getCodechange(@Query() query): Promise { + const { sha = "", filepath = "" } = query; + return this.codechangeService.getCodechange(sha, filepath); + } } diff --git a/packages/canyon-backend/src/codechange/codechange.module.ts b/packages/canyon-backend/src/codechange/codechange.module.ts index c46c439c..d1298cfa 100644 --- a/packages/canyon-backend/src/codechange/codechange.module.ts +++ b/packages/canyon-backend/src/codechange/codechange.module.ts @@ -4,7 +4,7 @@ import { CodechangeController } from "./codechange.controller"; import { PrismaService } from "../prisma/prisma.service"; @Module({ - controllers: [CodechangeController], - providers: [CodechangeService, PrismaService], + controllers: [CodechangeController], + providers: [CodechangeService, PrismaService], }) export class CodechangeModule {} diff --git a/packages/canyon-backend/src/codechange/codechange.service.ts b/packages/canyon-backend/src/codechange/codechange.service.ts index 9964b062..81789988 100644 --- a/packages/canyon-backend/src/codechange/codechange.service.ts +++ b/packages/canyon-backend/src/codechange/codechange.service.ts @@ -3,44 +3,44 @@ import { PrismaService } from "../prisma/prisma.service"; @Injectable() export class CodechangeService { - constructor(private readonly prisma: PrismaService) {} + constructor(private readonly prisma: PrismaService) {} - async getCodechange(sha, filepath) { - const { compareTarget } = await this.prisma.coverage - .findFirst({ - where: { - sha: sha, - covType: "all", - projectID: { - not: { - contains: "-ut", - }, - }, - }, - }) - .then((res) => res || { compareTarget: sha }); - return this.prisma.codechange - .findFirst({ - where: { - compareTarget, - sha: sha, - path: filepath, - }, - }) - .then((r) => { - if (r) { - return r; - } else { - return { - id: "", - projectID: "", - compareTarget: compareTarget, - sha: sha, - path: filepath, - additions: [], - deletions: [], - }; - } - }); - } + async getCodechange(sha, filepath) { + const { compareTarget } = await this.prisma.coverage + .findFirst({ + where: { + sha: sha, + covType: "all", + projectID: { + not: { + contains: "-ut", + }, + }, + }, + }) + .then((res) => res || { compareTarget: sha }); + return this.prisma.codechange + .findFirst({ + where: { + compareTarget, + sha: sha, + path: filepath, + }, + }) + .then((r) => { + if (r) { + return r; + } else { + return { + id: "", + projectID: "", + compareTarget: compareTarget, + sha: sha, + path: filepath, + additions: [], + deletions: [], + }; + } + }); + } } diff --git a/packages/canyon-backend/src/cov/coverage.controller.ts b/packages/canyon-backend/src/cov/coverage.controller.ts index f08ae5c0..c9fd58c6 100755 --- a/packages/canyon-backend/src/cov/coverage.controller.ts +++ b/packages/canyon-backend/src/cov/coverage.controller.ts @@ -10,70 +10,66 @@ import { CoverageReportsService } from "./services/coverage-reports.service"; @Controller() export class CoverageController { - constructor( - private readonly coveragePreStoreService: CoveragePreStoreService, - private readonly coverageDataComputeService: CoverageDataComputeService, - private readonly coverageService: CoverageService, - private readonly coverageReportsService: CoverageReportsService, - ) {} + constructor( + private readonly coveragePreStoreService: CoveragePreStoreService, + private readonly coverageDataComputeService: CoverageDataComputeService, + private readonly coverageService: CoverageService, + private readonly coverageReportsService: CoverageReportsService, + ) {} - // TODO 马上要废弃的接口 - @Get("api/coverage/summary/map") - async oldCoverageSummary(@Query() coverageSummaryDto: any): Promise { - const { projectID, sha, reportID } = coverageSummaryDto; + // TODO 马上要废弃的接口 + @Get("api/coverage/summary/map") + async oldCoverageSummary(@Query() coverageSummaryDto: any): Promise { + const { projectID, sha, reportID } = coverageSummaryDto; - return this.coverageService.coverageSummaryMap( - projectID, - sha, - reportID, - ); - } - - @Get("api/coverage/summary/v2/map") - async coverageSummary( - @Query() coverageSummaryDto: CoverageSummaryDto, - ): Promise { - const { projectID, sha, reportID } = coverageSummaryDto; + return this.coverageService.coverageSummaryMap(projectID, sha, reportID); + } - if (reportID && reportID.includes(",")) { - return this.coverageDataComputeService.coverageSummaryMap({ - projectID, - sha, - reportID, - }); - } + @Get("api/coverage/summary/v2/map") + async coverageSummary( + @Query() coverageSummaryDto: CoverageSummaryDto, + ): Promise { + const { projectID, sha, reportID } = coverageSummaryDto; - return this.coveragePreStoreService.coverageSummaryMap({ - projectID, - sha, - reportID, - }); + if (reportID && reportID.includes(",")) { + return this.coverageDataComputeService.coverageSummaryMap({ + projectID, + sha, + reportID, + }); } - @Get("api/coverage/map") - async coverageMap( - @Query() coverageMapDto: CoverageMapDto, - ): Promise { - const { projectID, sha, reportID, filepath } = coverageMapDto; - if (reportID && reportID.includes(",")) { - return this.coverageDataComputeService.coverageMap({ - projectID, - sha, - reportID, - filepath, - }); - } - return this.coveragePreStoreService.coverageMap({ - projectID, - sha, - reportID, - filepath, - }); - } + return this.coveragePreStoreService.coverageSummaryMap({ + projectID, + sha, + reportID, + }); + } - @Get("api/coverage/reports") - async coverageReports(@Query() query): Promise { - const { bu, start, end } = query; - return this.coverageReportsService.invoke({ bu, start, end }); + @Get("api/coverage/map") + async coverageMap( + @Query() coverageMapDto: CoverageMapDto, + ): Promise { + const { projectID, sha, reportID, filepath } = coverageMapDto; + if (reportID && reportID.includes(",")) { + return this.coverageDataComputeService.coverageMap({ + projectID, + sha, + reportID, + filepath, + }); } + return this.coveragePreStoreService.coverageMap({ + projectID, + sha, + reportID, + filepath, + }); + } + + @Get("api/coverage/reports") + async coverageReports(@Query() query): Promise { + const { bu, start, end } = query; + return this.coverageReportsService.invoke({ bu, start, end }); + } } diff --git a/packages/canyon-backend/src/cov/coverage.module.ts b/packages/canyon-backend/src/cov/coverage.module.ts index 9c62e477..88b8fffd 100755 --- a/packages/canyon-backend/src/cov/coverage.module.ts +++ b/packages/canyon-backend/src/cov/coverage.module.ts @@ -9,15 +9,15 @@ import { CoverageFinalService } from "./services/common/coverage-final.service"; import { CoverageReportsService } from "./services/coverage-reports.service"; @Module({ - controllers: [CoverageController], - providers: [ - PrismaService, - CoveragePreStoreService, - CoverageService, - CoverageDataComputeService, - CoverageFinalService, - TestExcludeService, - CoverageReportsService, - ], + controllers: [CoverageController], + providers: [ + PrismaService, + CoveragePreStoreService, + CoverageService, + CoverageDataComputeService, + CoverageFinalService, + TestExcludeService, + CoverageReportsService, + ], }) export class CoverageModule {} diff --git a/packages/canyon-backend/src/cov/dto/coverage-map.dto.ts b/packages/canyon-backend/src/cov/dto/coverage-map.dto.ts index 134f6c33..a2de4331 100755 --- a/packages/canyon-backend/src/cov/dto/coverage-map.dto.ts +++ b/packages/canyon-backend/src/cov/dto/coverage-map.dto.ts @@ -1,30 +1,30 @@ import { - IsNotEmpty, - IsOptional, - IsString, - Matches, - MinLength, + IsNotEmpty, + IsOptional, + IsString, + Matches, + MinLength, } from "class-validator"; export class CoverageMapDto { - // git仓库相关 - @IsString() - @Matches(/^[a-f0-9]{40}$/i, { message: "sha格式不正确" }) - @IsNotEmpty({ message: "sha 不能为空" }) - sha: string; + // git仓库相关 + @IsString() + @Matches(/^[a-f0-9]{40}$/i, { message: "sha格式不正确" }) + @IsNotEmpty({ message: "sha 不能为空" }) + sha: string; - @IsString() - @IsNotEmpty({ message: "projectID 不能为空" }) - projectID: string; + @IsString() + @IsNotEmpty({ message: "projectID 不能为空" }) + projectID: string; - // 单次 case 触发相关 - @IsString() - @MinLength(1, { message: "reportID长度最小为1" }) - @IsOptional({ message: "reportID 可以为空" }) - reportID: string; + // 单次 case 触发相关 + @IsString() + @MinLength(1, { message: "reportID长度最小为1" }) + @IsOptional({ message: "reportID 可以为空" }) + reportID: string; - @IsString() - @MinLength(1, { message: "filepath长度最小为1" }) - @IsOptional({ message: "filepath 可以为空" }) - filepath: string; + @IsString() + @MinLength(1, { message: "filepath长度最小为1" }) + @IsOptional({ message: "filepath 可以为空" }) + filepath: string; } diff --git a/packages/canyon-backend/src/cov/dto/coverage-summary.dto.ts b/packages/canyon-backend/src/cov/dto/coverage-summary.dto.ts index f1ff265f..2bdb20f6 100755 --- a/packages/canyon-backend/src/cov/dto/coverage-summary.dto.ts +++ b/packages/canyon-backend/src/cov/dto/coverage-summary.dto.ts @@ -1,25 +1,25 @@ import { - IsNotEmpty, - IsOptional, - IsString, - Matches, - MinLength, + IsNotEmpty, + IsOptional, + IsString, + Matches, + MinLength, } from "class-validator"; export class CoverageSummaryDto { - // git仓库相关 - @IsString() - @Matches(/^[a-f0-9]{40}$/i, { message: "sha格式不正确" }) - @IsNotEmpty({ message: "sha 不能为空" }) - sha: string; + // git仓库相关 + @IsString() + @Matches(/^[a-f0-9]{40}$/i, { message: "sha格式不正确" }) + @IsNotEmpty({ message: "sha 不能为空" }) + sha: string; - @IsString() - @IsNotEmpty({ message: "projectID 不能为空" }) - projectID: string; + @IsString() + @IsNotEmpty({ message: "projectID 不能为空" }) + projectID: string; - // 单次 case 触发相关 - @IsString() - @MinLength(1, { message: "reportID长度最小为1" }) - @IsOptional({ message: "reportID 可以为空" }) - reportID: string; + // 单次 case 触发相关 + @IsString() + @MinLength(1, { message: "reportID长度最小为1" }) + @IsOptional({ message: "reportID 可以为空" }) + reportID: string; } diff --git a/packages/canyon-backend/src/cov/services/common/coverage-final.service.ts b/packages/canyon-backend/src/cov/services/common/coverage-final.service.ts index cbd9a70b..f9169758 100644 --- a/packages/canyon-backend/src/cov/services/common/coverage-final.service.ts +++ b/packages/canyon-backend/src/cov/services/common/coverage-final.service.ts @@ -1,15 +1,15 @@ import { Injectable } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { - remapCoverageWithInstrumentCwd, - convertDataFromCoverageMapDatabase, - decompressedData, + remapCoverageWithInstrumentCwd, + convertDataFromCoverageMapDatabase, + decompressedData, } from "canyon-map"; import { - mergeCoverageMap, - parseProjectID, - reorganizeCompleteCoverageObjects, - resetCoverageDataMap, + mergeCoverageMap, + parseProjectID, + reorganizeCompleteCoverageObjects, + resetCoverageDataMap, } from "canyon-data"; /* @@ -19,81 +19,81 @@ import { @Injectable() export class CoverageFinalService { - constructor(private readonly prisma: PrismaService) {} - async invoke( - { - projectID, - sha, - reportID, - filepath, - }: { - projectID: string; - sha: string; - reportID?: string; - filepath?: string; - }, - hit?: { [key: string]: object }, - ) { - const { provider, repoID } = parseProjectID(projectID); - // 如果外部传入了hit,就不再从数据库中获取hit - hit = - hit || - (await this.getHitByProjectIDShaReportID({ - projectID, - sha, - reportID, - })); + constructor(private readonly prisma: PrismaService) {} + async invoke( + { + projectID, + sha, + reportID, + filepath, + }: { + projectID: string; + sha: string; + reportID?: string; + filepath?: string; + }, + hit?: { [key: string]: object }, + ) { + const { provider, repoID } = parseProjectID(projectID); + // 如果外部传入了hit,就不再从数据库中获取hit + hit = + hit || + (await this.getHitByProjectIDShaReportID({ + projectID, + sha, + reportID, + })); - // 无论是外部hit,还是查询到的hit,都为空对象,直接返回空对象 - if (Object.keys(hit).length === 0) { - return {}; - } + // 无论是外部hit,还是查询到的hit,都为空对象,直接返回空对象 + if (Object.keys(hit).length === 0) { + return {}; + } - const coverageMaps = await this.prisma.coverageMap.findMany({ - where: { - provider, - repoID, - sha, - path: filepath, - }, - }); + const coverageMaps = await this.prisma.coverageMap.findMany({ + where: { + provider, + repoID, + sha, + path: filepath, + }, + }); - // map没有也一样返回空对象 - if (coverageMaps.length === 0) { - return {}; - } + // map没有也一样返回空对象 + if (coverageMaps.length === 0) { + return {}; + } - const { map, instrumentCwd } = - await convertDataFromCoverageMapDatabase(coverageMaps); + const { map, instrumentCwd } = + await convertDataFromCoverageMapDatabase(coverageMaps); - const reMapMap = await remapCoverageWithInstrumentCwd( - resetCoverageDataMap(map), - instrumentCwd, - ); - return reorganizeCompleteCoverageObjects(reMapMap, hit); - } + const reMapMap = await remapCoverageWithInstrumentCwd( + resetCoverageDataMap(map), + instrumentCwd, + ); + return reorganizeCompleteCoverageObjects(reMapMap, hit); + } - private async getHitByProjectIDShaReportID({ projectID, sha, reportID }) { - const coverages = await this.prisma.coverage.findMany({ - where: { - projectID, - sha, - reportID: reportID - ? { - in: reportID.split(","), - } - : undefined, - covType: reportID ? "agg" : "all", - }, - }); + private async getHitByProjectIDShaReportID({ projectID, sha, reportID }) { + const coverages = await this.prisma.coverage.findMany({ + where: { + projectID, + sha, + reportID: reportID + ? { + in: reportID.split(","), + } + : undefined, + covType: reportID ? "agg" : "all", + }, + }); - let hitBox = {}; - for (let i = 0; i < coverages.length; i++) { - hitBox = mergeCoverageMap( - hitBox, - await decompressedData(coverages[i].hit), - ); - } - return hitBox; + let hitBox = {}; + for (let i = 0; i < coverages.length; i++) { + hitBox = mergeCoverageMap( + hitBox, + await decompressedData(coverages[i].hit), + ); } + return hitBox; + } } diff --git a/packages/canyon-backend/src/cov/services/common/test-exclude.service.ts b/packages/canyon-backend/src/cov/services/common/test-exclude.service.ts index 27b85af5..07e188c3 100644 --- a/packages/canyon-backend/src/cov/services/common/test-exclude.service.ts +++ b/packages/canyon-backend/src/cov/services/common/test-exclude.service.ts @@ -3,50 +3,48 @@ import { PrismaService } from "../../../prisma/prisma.service"; import * as TestExclude from "test-exclude"; @Injectable() export class TestExcludeService { - constructor(private readonly prisma: PrismaService) {} + constructor(private readonly prisma: PrismaService) {} - async invoke(projectID, coverage) { - const project = await this.prisma.project.findFirst({ - where: { - id: projectID, - }, - }); + async invoke(projectID, coverage) { + const project = await this.prisma.project.findFirst({ + where: { + id: projectID, + }, + }); - let matchRule: any = {}; // Default value + let matchRule: any = {}; // Default value - try { - // Attempt to parse project?.coverage - matchRule = JSON.parse(project?.coverage || "{}"); - } catch (error) { - // console.error('Error parsing coverage:', error); - // Log the error or handle it as needed - // You can also return an empty object or any default value - } - const exclude = new TestExclude({ - cwd: "", - include: matchRule.include, - exclude: matchRule.exclude || [], - extension: matchRule.extensions || [ - ".js", - ".cjs", - ".mjs", - ".ts", - ".tsx", - ".jsx", - ".vue", - ], - }); + try { + // Attempt to parse project?.coverage + matchRule = JSON.parse(project?.coverage || "{}"); + } catch (error) { + // console.error('Error parsing coverage:', error); + // Log the error or handle it as needed + // You can also return an empty object or any default value + } + const exclude = new TestExclude({ + cwd: "", + include: matchRule.include, + exclude: matchRule.exclude || [], + extension: matchRule.extensions || [ + ".js", + ".cjs", + ".mjs", + ".ts", + ".tsx", + ".jsx", + ".vue", + ], + }); - const filterCoverage = {}; + const filterCoverage = {}; - for (const filterCoverageKey of Object.keys(coverage)) { - // TODO 当过滤条件特别多的时候,性能会很差,大概能达到3s的计算时间,所以得在消费的时候就落库概览数据,summarys - if (exclude.shouldInstrument(filterCoverageKey)) { - filterCoverage[filterCoverageKey] = coverage[filterCoverageKey]; - } - } - return Object.keys(filterCoverage).length > 0 - ? filterCoverage - : coverage; + for (const filterCoverageKey of Object.keys(coverage)) { + // TODO 当过滤条件特别多的时候,性能会很差,大概能达到3s的计算时间,所以得在消费的时候就落库概览数据,summarys + if (exclude.shouldInstrument(filterCoverageKey)) { + filterCoverage[filterCoverageKey] = coverage[filterCoverageKey]; + } } + return Object.keys(filterCoverage).length > 0 ? filterCoverage : coverage; + } } diff --git a/packages/canyon-backend/src/cov/services/coverage-data-compute.service.ts b/packages/canyon-backend/src/cov/services/coverage-data-compute.service.ts index 8a3335db..9d7ea221 100644 --- a/packages/canyon-backend/src/cov/services/coverage-data-compute.service.ts +++ b/packages/canyon-backend/src/cov/services/coverage-data-compute.service.ts @@ -3,8 +3,8 @@ import { Injectable } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { - CoverageSummaryDataMap, - genSummaryMapByCoverageMap, + CoverageSummaryDataMap, + genSummaryMapByCoverageMap, } from "canyon-data"; import { CoverageSummaryDto } from "../dto/coverage-summary.dto"; import { CoverageMapDto } from "../dto/coverage-map.dto"; @@ -30,59 +30,59 @@ import { CoverageFinalService } from "./common/coverage-final.service"; */ @Injectable() export class CoverageDataComputeService { - constructor( - private readonly prisma: PrismaService, - private readonly testExcludeService: TestExcludeService, - private readonly coverageFinalService: CoverageFinalService, - ) {} + constructor( + private readonly prisma: PrismaService, + private readonly testExcludeService: TestExcludeService, + private readonly coverageFinalService: CoverageFinalService, + ) {} - /** - * Retrieves the coverage summary map for a specific project, commit (SHA), and report ID. - * - * @param {CoverageSummaryDto} dto - The data transfer object containing project ID, SHA, and report ID. - * @returns {Promise} - A promise that resolves to the decompressed coverage summary map. - * @throws {HttpException} - If the coverage summary data is not found, an exception with status 404 is thrown. - */ - async coverageSummaryMap({ - projectID, + /** + * Retrieves the coverage summary map for a specific project, commit (SHA), and report ID. + * + * @param {CoverageSummaryDto} dto - The data transfer object containing project ID, SHA, and report ID. + * @returns {Promise} - A promise that resolves to the decompressed coverage summary map. + * @throws {HttpException} - If the coverage summary data is not found, an exception with status 404 is thrown. + */ + async coverageSummaryMap({ + projectID, + sha, + reportID, + }: CoverageSummaryDto): Promise { + // 直接调用同class下的coverageMap方法 + const coverage = await this.coverageMap({ + projectID, + sha, + reportID, + filepath: undefined, + }); + const codechanges = await this.prisma.codechange.findMany({ + where: { sha, - reportID, - }: CoverageSummaryDto): Promise { - // 直接调用同class下的coverageMap方法 - const coverage = await this.coverageMap({ - projectID, - sha, - reportID, - filepath: undefined, - }); - const codechanges = await this.prisma.codechange.findMany({ - where: { - sha, - }, - }); - return genSummaryMapByCoverageMap( - await this.testExcludeService.invoke(projectID, coverage), - codechanges, - ); - } + }, + }); + return genSummaryMapByCoverageMap( + await this.testExcludeService.invoke(projectID, coverage), + codechanges, + ); + } - /** - * Retrieves and processes the coverage map data for a specific project, commit (SHA), report ID, and file path. - * - * @param {CoverageMapDto} dto - The data transfer object containing project ID, SHA, report ID, and file path. - * @returns {Promise} - A promise that resolves to the processed and reorganized coverage map data. - */ - async coverageMap({ - projectID, - sha, - reportID, - filepath, - }: CoverageMapDto): Promise { - return this.coverageFinalService.invoke({ - projectID, - sha, - reportID, - filepath, - }); - } + /** + * Retrieves and processes the coverage map data for a specific project, commit (SHA), report ID, and file path. + * + * @param {CoverageMapDto} dto - The data transfer object containing project ID, SHA, report ID, and file path. + * @returns {Promise} - A promise that resolves to the processed and reorganized coverage map data. + */ + async coverageMap({ + projectID, + sha, + reportID, + filepath, + }: CoverageMapDto): Promise { + return this.coverageFinalService.invoke({ + projectID, + sha, + reportID, + filepath, + }); + } } diff --git a/packages/canyon-backend/src/cov/services/coverage-pre-store.service.ts b/packages/canyon-backend/src/cov/services/coverage-pre-store.service.ts index 48f7ba09..0aac8d87 100755 --- a/packages/canyon-backend/src/cov/services/coverage-pre-store.service.ts +++ b/packages/canyon-backend/src/cov/services/coverage-pre-store.service.ts @@ -26,56 +26,56 @@ import { CoverageFinalService } from "./common/coverage-final.service"; */ @Injectable() export class CoveragePreStoreService { - constructor( - private readonly prisma: PrismaService, - private readonly coverageFinalService: CoverageFinalService, - ) {} + constructor( + private readonly prisma: PrismaService, + private readonly coverageFinalService: CoverageFinalService, + ) {} - /** - * Retrieves the coverage summary map for a specific project, commit (SHA), and report ID. - * - * @param {CoverageSummaryDto} dto - The data transfer object containing project ID, SHA, and report ID. - * @returns {Promise} - A promise that resolves to the decompressed coverage summary map. - * @throws {HttpException} - If the coverage summary data is not found, an exception with status 404 is thrown. - */ - async coverageSummaryMap({ - projectID, + /** + * Retrieves the coverage summary map for a specific project, commit (SHA), and report ID. + * + * @param {CoverageSummaryDto} dto - The data transfer object containing project ID, SHA, and report ID. + * @returns {Promise} - A promise that resolves to the decompressed coverage summary map. + * @throws {HttpException} - If the coverage summary data is not found, an exception with status 404 is thrown. + */ + async coverageSummaryMap({ + projectID, + sha, + reportID, + }: CoverageSummaryDto): Promise { + const coverage = await this.prisma.coverage.findFirst({ + where: { sha, - reportID, - }: CoverageSummaryDto): Promise { - const coverage = await this.prisma.coverage.findFirst({ - where: { - sha, - projectID, - reportID: reportID, - covType: reportID ? "agg" : "all", - }, - }); + projectID, + reportID: reportID, + covType: reportID ? "agg" : "all", + }, + }); - if (coverage?.summary) { - return decompressedData(coverage.summary); - } - // 不报错,直接返回空对象 - return {}; + if (coverage?.summary) { + return decompressedData(coverage.summary); } + // 不报错,直接返回空对象 + return {}; + } - /** - * Retrieves and processes the coverage map data for a specific project, commit (SHA), report ID, and file path. - * - * @param {CoverageMapDto} dto - The data transfer object containing project ID, SHA, report ID, and file path. - * @returns {Promise} - A promise that resolves to the processed and reorganized coverage map data. - */ - async coverageMap({ - projectID, - sha, - reportID, - filepath, - }: CoverageMapDto): Promise { - return this.coverageFinalService.invoke({ - projectID, - sha, - reportID, - filepath, - }); - } + /** + * Retrieves and processes the coverage map data for a specific project, commit (SHA), report ID, and file path. + * + * @param {CoverageMapDto} dto - The data transfer object containing project ID, SHA, report ID, and file path. + * @returns {Promise} - A promise that resolves to the processed and reorganized coverage map data. + */ + async coverageMap({ + projectID, + sha, + reportID, + filepath, + }: CoverageMapDto): Promise { + return this.coverageFinalService.invoke({ + projectID, + sha, + reportID, + filepath, + }); + } } diff --git a/packages/canyon-backend/src/cov/services/coverage-reports.service.ts b/packages/canyon-backend/src/cov/services/coverage-reports.service.ts index 77a1a1e5..c878f227 100644 --- a/packages/canyon-backend/src/cov/services/coverage-reports.service.ts +++ b/packages/canyon-backend/src/cov/services/coverage-reports.service.ts @@ -5,91 +5,90 @@ import { percent } from "canyon-data"; @Injectable() export class CoverageReportsService { - constructor(private readonly prisma: PrismaService) {} + constructor(private readonly prisma: PrismaService) {} - async invoke({ bu, start, end }) { - const projects = await this.prisma.project.findMany({ - where: { - bu: bu, - pathWithNamespace: { - not: { - contains: "canyon", - }, - }, - }, - select: { - id: true, - name: true, - pathWithNamespace: true, - description: true, - }, - }); - // await sleep(1000) - const covs = await this.prisma.coverage.findMany({ - where: { - covType: "all", - projectID: { - in: projects.map((item) => item.id), - }, - statementsTotal: { - not: 0, - }, - // updatedAt:{} - // 根据updatedAt的一个范围 - updatedAt: { - gte: dayjs(start).toDate(), - lte: dayjs(end).toDate(), - }, - }, - select: { - projectID: true, - statementsCovered: true, - statementsTotal: true, - }, - }); - const obj = {}; - for (let i = 0; i < covs.length; i++) { - const projectID = `${covs[i].projectID.split("-")[1]}-${covs[i].projectID.includes("-ut") ? "ut" : "auto"}`; - if (obj[projectID] === undefined) { - obj[projectID] = { - maxCoverage: percent( - covs[i].statementsCovered, - covs[i].statementsTotal, - ), - projectID: covs[i].projectID.split("-")[1], - }; - } else { - obj[projectID].maxCoverage = Math.max( - obj[projectID].maxCoverage, - percent(covs[i].statementsCovered, covs[i].statementsTotal), - ); - } - } - const rows = []; - Object.keys(obj).forEach((key) => { - const index = rows.findIndex( - (item) => item.projectID === obj[key].projectID, - ); - if (index > -1) { - rows[index][key.includes("-ut") ? "ut" : "auto"] = - obj[key].maxCoverage; - } else { - rows.push({ - projectID: obj[key].projectID, - ut: key.includes("-ut") ? obj[key].maxCoverage : 0, - auto: key.includes("-auto") ? obj[key].maxCoverage : 0, - }); - } + async invoke({ bu, start, end }) { + const projects = await this.prisma.project.findMany({ + where: { + bu: bu, + pathWithNamespace: { + not: { + contains: "canyon", + }, + }, + }, + select: { + id: true, + name: true, + pathWithNamespace: true, + description: true, + }, + }); + // await sleep(1000) + const covs = await this.prisma.coverage.findMany({ + where: { + covType: "all", + projectID: { + in: projects.map((item) => item.id), + }, + statementsTotal: { + not: 0, + }, + // updatedAt:{} + // 根据updatedAt的一个范围 + updatedAt: { + gte: dayjs(start).toDate(), + lte: dayjs(end).toDate(), + }, + }, + select: { + projectID: true, + statementsCovered: true, + statementsTotal: true, + }, + }); + const obj = {}; + for (let i = 0; i < covs.length; i++) { + const projectID = `${covs[i].projectID.split("-")[1]}-${covs[i].projectID.includes("-ut") ? "ut" : "auto"}`; + if (obj[projectID] === undefined) { + obj[projectID] = { + maxCoverage: percent( + covs[i].statementsCovered, + covs[i].statementsTotal, + ), + projectID: covs[i].projectID.split("-")[1], + }; + } else { + obj[projectID].maxCoverage = Math.max( + obj[projectID].maxCoverage, + percent(covs[i].statementsCovered, covs[i].statementsTotal), + ); + } + } + const rows = []; + Object.keys(obj).forEach((key) => { + const index = rows.findIndex( + (item) => item.projectID === obj[key].projectID, + ); + if (index > -1) { + rows[index][key.includes("-ut") ? "ut" : "auto"] = obj[key].maxCoverage; + } else { + rows.push({ + projectID: obj[key].projectID, + ut: key.includes("-ut") ? obj[key].maxCoverage : 0, + auto: key.includes("-auto") ? obj[key].maxCoverage : 0, }); + } + }); - for (let i = 0; i < rows.length; i++) { - const project = projects.find((item) => - item.id.includes(rows[i].projectID), - ); - // rows[i].name = project.name - rows[i].pathWithNamespace = project.pathWithNamespace; - rows[i].description = project.description; - } - return rows.sort((a, b) => a.auto - b.auto); + for (let i = 0; i < rows.length; i++) { + const project = projects.find((item) => + item.id.includes(rows[i].projectID), + ); + // rows[i].name = project.name + rows[i].pathWithNamespace = project.pathWithNamespace; + rows[i].description = project.description; } + return rows.sort((a, b) => a.auto - b.auto); + } } diff --git a/packages/canyon-backend/src/cov/services/coverage.service.ts b/packages/canyon-backend/src/cov/services/coverage.service.ts index ce410015..33e83255 100755 --- a/packages/canyon-backend/src/cov/services/coverage.service.ts +++ b/packages/canyon-backend/src/cov/services/coverage.service.ts @@ -6,37 +6,37 @@ import { decompressedData } from "canyon-map"; // 马上废弃,勿动 @Injectable() export class CoverageService { - constructor(private readonly prisma: PrismaService) {} + constructor(private readonly prisma: PrismaService) {} - async coverageSummaryMap(projectID, sha: string, reportID: string) { - const coverage = await this.prisma.coverage.findFirst({ - where: { - sha: sha || undefined, - projectID: projectID || undefined, - reportID: reportID || undefined, - covType: reportID ? "agg" : "all", - }, - }); + async coverageSummaryMap(projectID, sha: string, reportID: string) { + const coverage = await this.prisma.coverage.findFirst({ + where: { + sha: sha || undefined, + projectID: projectID || undefined, + reportID: reportID || undefined, + covType: reportID ? "agg" : "all", + }, + }); - if (coverage?.summary) { - return decompressedData( - coverage.summary, - ).then((data) => { - return Object.entries(data).map(([key, value]) => { - return { - ...value, - path: key, - }; - }); - }); - } - throw new HttpException( - { - statusCode: 404, - message: "summary data not found", - errorCode: "SUMMARY_DATA_NOT_FOUND", - }, - 404, - ); + if (coverage?.summary) { + return decompressedData(coverage.summary).then( + (data) => { + return Object.entries(data).map(([key, value]) => { + return { + ...value, + path: key, + }; + }); + }, + ); } + throw new HttpException( + { + statusCode: 404, + message: "summary data not found", + errorCode: "SUMMARY_DATA_NOT_FOUND", + }, + 404, + ); + } } diff --git a/packages/canyon-backend/src/decorators/gql-user.decorator.ts b/packages/canyon-backend/src/decorators/gql-user.decorator.ts index bf904271..804b5900 100755 --- a/packages/canyon-backend/src/decorators/gql-user.decorator.ts +++ b/packages/canyon-backend/src/decorators/gql-user.decorator.ts @@ -2,9 +2,9 @@ import { createParamDecorator, ExecutionContext } from "@nestjs/common"; import { GqlExecutionContext } from "@nestjs/graphql"; export const GqlUser = createParamDecorator( - (data: unknown, context: ExecutionContext) => { - const ctx = GqlExecutionContext.create(context); - const { req, headers } = ctx.getContext(); - return headers ? headers.user : req.user; - }, + (data: unknown, context: ExecutionContext) => { + const ctx = GqlExecutionContext.create(context); + const { req, headers } = ctx.getContext(); + return headers ? headers.user : req.user; + }, ); diff --git a/packages/canyon-backend/src/guards/gql-auth.guard.ts b/packages/canyon-backend/src/guards/gql-auth.guard.ts index d8403a8f..91ebca15 100755 --- a/packages/canyon-backend/src/guards/gql-auth.guard.ts +++ b/packages/canyon-backend/src/guards/gql-auth.guard.ts @@ -4,9 +4,9 @@ import { AuthGuard } from "@nestjs/passport"; @Injectable() export class GqlAuthGuard extends AuthGuard("jwt") { - getRequest(context: ExecutionContext) { - const ctx = GqlExecutionContext.create(context); - const { req, headers } = ctx.getContext(); - return headers ? headers : req; - } + getRequest(context: ExecutionContext) { + const ctx = GqlExecutionContext.create(context); + const { req, headers } = ctx.getContext(); + return headers ? headers : req; + } } diff --git a/packages/canyon-backend/src/logger/index.ts b/packages/canyon-backend/src/logger/index.ts index 8c428de6..ed0d49b3 100644 --- a/packages/canyon-backend/src/logger/index.ts +++ b/packages/canyon-backend/src/logger/index.ts @@ -1,3 +1,3 @@ export function logger(data: any) { - console.log(data); + console.log(data); } diff --git a/packages/canyon-backend/src/main.ts b/packages/canyon-backend/src/main.ts index e4f94a5f..8847c2d0 100755 --- a/packages/canyon-backend/src/main.ts +++ b/packages/canyon-backend/src/main.ts @@ -4,15 +4,15 @@ import * as dotenv from "dotenv"; import * as path from "node:path"; dotenv.config({ - path: path.resolve(__dirname, "../../../.env"), + path: path.resolve(__dirname, "../../../.env"), }); async function bootstrap() { - const { AppModule } = await import("./app.module"); - const app = await NestFactory.create(AppModule); - app.useGlobalPipes(new ValidationPipe()); - app.enableCors(); - await app.listen(process.env["PORT"] || 8080); + const { AppModule } = await import("./app.module"); + const app = await NestFactory.create(AppModule); + app.useGlobalPipes(new ValidationPipe()); + app.enableCors(); + await app.listen(process.env["PORT"] || 8080); } bootstrap(); diff --git a/packages/canyon-backend/src/prisma/prisma.module.ts b/packages/canyon-backend/src/prisma/prisma.module.ts index 6dcc7eb0..adc06a74 100755 --- a/packages/canyon-backend/src/prisma/prisma.module.ts +++ b/packages/canyon-backend/src/prisma/prisma.module.ts @@ -2,7 +2,7 @@ import { Module } from "@nestjs/common/decorators"; import { PrismaService } from "./prisma.service"; @Module({ - providers: [PrismaService], - exports: [PrismaService], + providers: [PrismaService], + exports: [PrismaService], }) export class PrismaModule {} diff --git a/packages/canyon-backend/src/prisma/prisma.service.ts b/packages/canyon-backend/src/prisma/prisma.service.ts index 22e86392..bcd4ea3c 100755 --- a/packages/canyon-backend/src/prisma/prisma.service.ts +++ b/packages/canyon-backend/src/prisma/prisma.service.ts @@ -3,19 +3,19 @@ import { PrismaClient } from "@prisma/client"; @Injectable() export class PrismaService - extends PrismaClient - implements OnModuleInit, OnModuleDestroy + extends PrismaClient + implements OnModuleInit, OnModuleDestroy { - constructor() { - super({ - // log: ['query', 'info', 'warn', 'error'], - }); - } - async onModuleInit() { - await this.$connect(); - } + constructor() { + super({ + // log: ['query', 'info', 'warn', 'error'], + }); + } + async onModuleInit() { + await this.$connect(); + } - async onModuleDestroy() { - await this.$disconnect(); - } + async onModuleDestroy() { + await this.$disconnect(); + } } diff --git a/packages/canyon-backend/src/project/input-type.args.ts b/packages/canyon-backend/src/project/input-type.args.ts index 1c8d4fa3..648d0403 100644 --- a/packages/canyon-backend/src/project/input-type.args.ts +++ b/packages/canyon-backend/src/project/input-type.args.ts @@ -2,71 +2,71 @@ import { ArgsType, Field, ID, InputType } from "@nestjs/graphql"; @InputType() // 定义规则输入类型 class TagInput { - @Field() - id: string; - @Field() - name: string; - @Field() - link: string; - @Field() - color: string; + @Field() + id: string; + @Field() + name: string; + @Field() + link: string; + @Field() + color: string; } @InputType() // 定义规则输入类型 class MemberInput { - @Field() - userID: string; - @Field() - role: string; + @Field() + userID: string; + @Field() + role: string; } @InputType() // 定义规则输入类型 class AutoInstrumentInput { - @Field() - filepath: string; - @Field() - content: string; + @Field() + filepath: string; + @Field() + content: string; } @ArgsType() export class UpdateProjectArgs { - @Field(() => ID, { - description: "Project ID", - }) - projectID: string; + @Field(() => ID, { + description: "Project ID", + }) + projectID: string; - @Field({ - nullable: true, - }) - description?: string; + @Field({ + nullable: true, + }) + description?: string; - @Field({ - nullable: true, - }) - instrumentCwd?: string; + @Field({ + nullable: true, + }) + instrumentCwd?: string; - @Field({ - nullable: true, - }) - coverage?: string; + @Field({ + nullable: true, + }) + coverage?: string; - @Field({ - nullable: true, - }) - defaultBranch?: string; + @Field({ + nullable: true, + }) + defaultBranch?: string; - @Field(() => [TagInput], { - nullable: true, - }) - tags?: TagInput[]; + @Field(() => [TagInput], { + nullable: true, + }) + tags?: TagInput[]; - @Field(() => [MemberInput], { - nullable: true, - }) - members?: MemberInput[]; + @Field(() => [MemberInput], { + nullable: true, + }) + members?: MemberInput[]; - @Field(() => [AutoInstrumentInput], { - nullable: true, - }) - autoInstrument?: AutoInstrumentInput[]; + @Field(() => [AutoInstrumentInput], { + nullable: true, + }) + autoInstrument?: AutoInstrumentInput[]; } diff --git a/packages/canyon-backend/src/project/models/project-chart-data.model.ts b/packages/canyon-backend/src/project/models/project-chart-data.model.ts index 6a8fe870..328b8871 100644 --- a/packages/canyon-backend/src/project/models/project-chart-data.model.ts +++ b/packages/canyon-backend/src/project/models/project-chart-data.model.ts @@ -2,16 +2,16 @@ import { Field, ObjectType } from "@nestjs/graphql"; @ObjectType() export class ProjectChartDataModel { - @Field(() => Number, { - description: "整体覆盖率", - }) - statements: number; - @Field(() => Number, { - description: "New Lines", - }) - newlines: number; - @Field(() => String, { - description: "sha", - }) - sha: string; + @Field(() => Number, { + description: "整体覆盖率", + }) + statements: number; + @Field(() => Number, { + description: "New Lines", + }) + newlines: number; + @Field(() => String, { + description: "sha", + }) + sha: string; } diff --git a/packages/canyon-backend/src/project/models/project-compartment-data.model.ts b/packages/canyon-backend/src/project/models/project-compartment-data.model.ts index 42ea8b2e..44d9af8b 100644 --- a/packages/canyon-backend/src/project/models/project-compartment-data.model.ts +++ b/packages/canyon-backend/src/project/models/project-compartment-data.model.ts @@ -2,13 +2,13 @@ import { Field, ObjectType } from "@nestjs/graphql"; @ObjectType() export class ProjectCompartmentDataModel { - @Field(() => String, { - description: "label", - }) - label: string; + @Field(() => String, { + description: "label", + }) + label: string; - @Field(() => String, { - description: "value", - }) - value: string; + @Field(() => String, { + description: "value", + }) + value: string; } diff --git a/packages/canyon-backend/src/project/models/project-pages.model.ts b/packages/canyon-backend/src/project/models/project-pages.model.ts index 4be7d573..242c6369 100644 --- a/packages/canyon-backend/src/project/models/project-pages.model.ts +++ b/packages/canyon-backend/src/project/models/project-pages.model.ts @@ -3,9 +3,9 @@ import { Project } from "../project.model"; @ObjectType() export class ProjectPagesModel { - @Field(() => [Project]) - data: Project[]; + @Field(() => [Project]) + data: Project[]; - @Field(() => Number) - total: number; + @Field(() => Number) + total: number; } diff --git a/packages/canyon-backend/src/project/models/project-record-detail.model.ts b/packages/canyon-backend/src/project/models/project-record-detail.model.ts index 74490536..a41366b2 100644 --- a/packages/canyon-backend/src/project/models/project-record-detail.model.ts +++ b/packages/canyon-backend/src/project/models/project-record-detail.model.ts @@ -2,54 +2,54 @@ import { Field, ObjectType } from "@nestjs/graphql"; @ObjectType() export class ProjectRecordDetailModel { - @Field(() => String, { - description: "ID", - }) - id: string; - @Field(() => String, { - description: "Commit Sha", - }) - sha: string; - @Field(() => String, { - description: "上报ID", - }) - reportID: string; - @Field(() => String, { - description: "关系ID", - }) - relationID: string; - @Field(() => Date, { - description: "创建时间", - }) - createdAt: string; - @Field(() => Date, { - description: "最近一次上报时间", - }) - lastReportTime: string; - @Field(() => String, { - description: "上报人", - }) - reporterUsername: string; - @Field(() => String, { - description: "上报人头像", - }) - reporterAvatar: string; + @Field(() => String, { + description: "ID", + }) + id: string; + @Field(() => String, { + description: "Commit Sha", + }) + sha: string; + @Field(() => String, { + description: "上报ID", + }) + reportID: string; + @Field(() => String, { + description: "关系ID", + }) + relationID: string; + @Field(() => Date, { + description: "创建时间", + }) + createdAt: string; + @Field(() => Date, { + description: "最近一次上报时间", + }) + lastReportTime: string; + @Field(() => String, { + description: "上报人", + }) + reporterUsername: string; + @Field(() => String, { + description: "上报人头像", + }) + reporterAvatar: string; - @Field(() => Number, { - description: "新增", - }) - newlines: number; + @Field(() => Number, { + description: "新增", + }) + newlines: number; - @Field(() => Number, { - description: "全量", - }) - statements: number; + @Field(() => Number, { + description: "全量", + }) + statements: number; } @ObjectType() export class DeleModel { - @Field(() => Number, { - description: "删除数量", - }) - count: number; + @Field(() => Number, { + description: "删除数量", + }) + count: number; } diff --git a/packages/canyon-backend/src/project/models/project-records-pages.model.ts b/packages/canyon-backend/src/project/models/project-records-pages.model.ts index fb1dc6cb..d86799f6 100644 --- a/packages/canyon-backend/src/project/models/project-records-pages.model.ts +++ b/packages/canyon-backend/src/project/models/project-records-pages.model.ts @@ -3,9 +3,9 @@ import { ProjectRecordsModel } from "./project-records.model"; @ObjectType() export class ProjectRecordsPagesModel { - @Field(() => [ProjectRecordsModel]) - data: ProjectRecordsModel[]; + @Field(() => [ProjectRecordsModel]) + data: ProjectRecordsModel[]; - @Field(() => Number) - total: number; + @Field(() => Number) + total: number; } diff --git a/packages/canyon-backend/src/project/models/project-records.model.ts b/packages/canyon-backend/src/project/models/project-records.model.ts index 6a447b9c..b8c9eb7d 100644 --- a/packages/canyon-backend/src/project/models/project-records.model.ts +++ b/packages/canyon-backend/src/project/models/project-records.model.ts @@ -2,127 +2,127 @@ import { Field, ObjectType } from "@nestjs/graphql"; @ObjectType() class Log { - @Field(() => String, { - description: "ID", - }) - id: string; - @Field(() => String, { - description: "Commit Sha", - }) - commitSha: string; - @Field(() => String, { - description: "上报ID", - }) - reportID: string; - @Field(() => String, { - description: "关系ID", - }) - relationID: string; - @Field(() => Date, { - description: "创建时间", - }) - createdAt: string; - @Field(() => String, { - description: "上报人", - }) - reporterUsername: string; - @Field(() => String, { - description: "上报人头像", - }) - reporterAvatar: string; - - @Field(() => Number, { - description: "新增", - }) - newlines: number; - - @Field(() => Number, { - description: "全量", - }) - statements: number; + @Field(() => String, { + description: "ID", + }) + id: string; + @Field(() => String, { + description: "Commit Sha", + }) + commitSha: string; + @Field(() => String, { + description: "上报ID", + }) + reportID: string; + @Field(() => String, { + description: "关系ID", + }) + relationID: string; + @Field(() => Date, { + description: "创建时间", + }) + createdAt: string; + @Field(() => String, { + description: "上报人", + }) + reporterUsername: string; + @Field(() => String, { + description: "上报人头像", + }) + reporterAvatar: string; + + @Field(() => Number, { + description: "新增", + }) + newlines: number; + + @Field(() => Number, { + description: "全量", + }) + statements: number; } @ObjectType() export class ProjectRecordsModel { - @Field(() => String, { - description: "commit信息", - }) - message: string; - @Field(() => String, { - description: "commit sha", - }) - sha: string; - - @Field(() => String, { - description: "Compare Target", - }) - compareTarget: string; - - @Field(() => String, { - description: "branch", - }) - branch: string; - - @Field(() => String, { - description: "buildURL", - }) - buildURL: string; - - @Field(() => String, { - description: "buildID", - }) - buildID: string; - - @Field(() => String, { - description: "buildProvider", - }) - buildProvider: string; - - @Field(() => String, { - description: "Compare Url", - }) - compareUrl: string; - - @Field(() => String, { - description: "web url", - }) - webUrl: string; - - @Field(() => Number, { - description: "新增", - }) - newlines: number; - - @Field(() => Number, { - description: "全量", - }) - statements: number; - - @Field(() => Number, { - description: "分支覆盖率", - }) - branches: number; - - @Field(() => Number, { - description: "函数覆盖率", - }) - functions: number; - - @Field(() => Number, { - description: "行覆盖率", - }) - lines: number; - - @Field(() => Date, { - description: "最近一次上报", - }) - lastReportTime: string; - @Field(() => Number, { - description: "上报次数", - }) - times: number; - @Field(() => [Log], { - description: "上报日志", - }) - logs: Log[]; + @Field(() => String, { + description: "commit信息", + }) + message: string; + @Field(() => String, { + description: "commit sha", + }) + sha: string; + + @Field(() => String, { + description: "Compare Target", + }) + compareTarget: string; + + @Field(() => String, { + description: "branch", + }) + branch: string; + + @Field(() => String, { + description: "buildURL", + }) + buildURL: string; + + @Field(() => String, { + description: "buildID", + }) + buildID: string; + + @Field(() => String, { + description: "buildProvider", + }) + buildProvider: string; + + @Field(() => String, { + description: "Compare Url", + }) + compareUrl: string; + + @Field(() => String, { + description: "web url", + }) + webUrl: string; + + @Field(() => Number, { + description: "新增", + }) + newlines: number; + + @Field(() => Number, { + description: "全量", + }) + statements: number; + + @Field(() => Number, { + description: "分支覆盖率", + }) + branches: number; + + @Field(() => Number, { + description: "函数覆盖率", + }) + functions: number; + + @Field(() => Number, { + description: "行覆盖率", + }) + lines: number; + + @Field(() => Date, { + description: "最近一次上报", + }) + lastReportTime: string; + @Field(() => Number, { + description: "上报次数", + }) + times: number; + @Field(() => [Log], { + description: "上报日志", + }) + logs: Log[]; } diff --git a/packages/canyon-backend/src/project/models/project2.model.ts b/packages/canyon-backend/src/project/models/project2.model.ts index 93cb5aad..3b6690a5 100755 --- a/packages/canyon-backend/src/project/models/project2.model.ts +++ b/packages/canyon-backend/src/project/models/project2.model.ts @@ -2,15 +2,15 @@ import { Field, ID, ObjectType } from "@nestjs/graphql"; @ObjectType() export class Project2 { - @Field(() => ID) - id: string; + @Field(() => ID) + id: string; - @Field(() => String) - name: string; + @Field(() => String) + name: string; - @Field(() => String) - pathWithNamespace: string; + @Field(() => String) + pathWithNamespace: string; - @Field(() => String) - description: string; + @Field(() => String) + description: string; } diff --git a/packages/canyon-backend/src/project/project.model.ts b/packages/canyon-backend/src/project/project.model.ts index 57c2202e..03c4fe44 100755 --- a/packages/canyon-backend/src/project/project.model.ts +++ b/packages/canyon-backend/src/project/project.model.ts @@ -2,108 +2,108 @@ import { Field, ID, ObjectType } from "@nestjs/graphql"; @ObjectType() export class Tag { - @Field(() => String) - id: string; + @Field(() => String) + id: string; - @Field(() => String) - name: string; + @Field(() => String) + name: string; - @Field(() => String) - link: string; + @Field(() => String) + link: string; - @Field(() => String) - color: string; + @Field(() => String) + color: string; } @ObjectType() export class Member { - @Field(() => String) - userID: string; + @Field(() => String) + userID: string; - @Field(() => String) - role: string; + @Field(() => String) + role: string; } @ObjectType() export class AutoInstrument { - @Field(() => String) - filepath: string; + @Field(() => String) + filepath: string; - @Field(() => String) - content: string; + @Field(() => String) + content: string; } @ObjectType() export class Project { - @Field(() => ID) - id: string; + @Field(() => ID) + id: string; - @Field(() => String) - name: string; + @Field(() => String) + name: string; - @Field(() => String) - pathWithNamespace: string; + @Field(() => String) + pathWithNamespace: string; - @Field(() => String) - description: string; + @Field(() => String) + description: string; - @Field(() => String) - coverage: string; + @Field(() => String) + coverage: string; - // @Field(() => String) - // language: string; + // @Field(() => String) + // language: string; - @Field(() => [Tag]) - tags: Tag[]; + @Field(() => [Tag]) + tags: Tag[]; - @Field(() => [Member]) - members: Member[]; + @Field(() => [Member]) + members: Member[]; - @Field(() => [AutoInstrument]) - autoInstrument: AutoInstrument[]; + @Field(() => [AutoInstrument]) + autoInstrument: AutoInstrument[]; - @Field(() => String) - bu: string; + @Field(() => String) + bu: string; - @Field(() => [String]) - branchOptions: string[]; + @Field(() => [String]) + branchOptions: string[]; - @Field(() => Number) - maxCoverage: number; + @Field(() => Number) + maxCoverage: number; - @Field(() => String) - defaultBranch: string; + @Field(() => String) + defaultBranch: string; - // @Field(() => String) - // instrumentCwd: string; + // @Field(() => String) + // instrumentCwd: string; - @Field(() => Number) - reportTimes: number; + @Field(() => Number) + reportTimes: number; - @Field(() => Date) - lastReportTime: Date; + @Field(() => Date) + lastReportTime: Date; - @Field(() => Boolean) - favored: boolean; + @Field(() => Boolean) + favored: boolean; - @Field(() => Date) - createdAt: Date; + @Field(() => Date) + createdAt: Date; } // buOption @ObjectType() export class BuOption { - @Field(() => String) - bu: string; - @Field(() => Number) - count: number; + @Field(() => String) + bu: string; + @Field(() => Number) + count: number; } // TagOption @ObjectType() export class TagOption { - @Field(() => String) - name: string; + @Field(() => String) + name: string; } diff --git a/packages/canyon-backend/src/project/project.module.ts b/packages/canyon-backend/src/project/project.module.ts index 77d06b88..bc305385 100755 --- a/packages/canyon-backend/src/project/project.module.ts +++ b/packages/canyon-backend/src/project/project.module.ts @@ -12,19 +12,19 @@ import { GetProjectsService } from "./services/get-projects.service"; import { DeleteProjectRecordService } from "./services/delete-project-record.service"; import { UpdateProjectService } from "./services/crud/update-project.service"; @Module({ - imports: [PrismaModule], - controllers: [], - providers: [ - ProjectResolver, - ProjectService, - GetProjectChartDataService, - GetProjectRecordsService, - GetProjectCompartmentDataService, - GetProjectRecordDetailByShaService, - GetProjectsService, - DeleteProjectRecordService, - UpdateProjectService, - ], - exports: [], + imports: [PrismaModule], + controllers: [], + providers: [ + ProjectResolver, + ProjectService, + GetProjectChartDataService, + GetProjectRecordsService, + GetProjectCompartmentDataService, + GetProjectRecordDetailByShaService, + GetProjectsService, + DeleteProjectRecordService, + UpdateProjectService, + ], + exports: [], }) export class ProjectModule {} diff --git a/packages/canyon-backend/src/project/project.resolver.ts b/packages/canyon-backend/src/project/project.resolver.ts index 8e502875..2d71c138 100755 --- a/packages/canyon-backend/src/project/project.resolver.ts +++ b/packages/canyon-backend/src/project/project.resolver.ts @@ -14,8 +14,8 @@ import { GetProjectCompartmentDataService } from "./services/get-project-compart import { ProjectCompartmentDataModel } from "./models/project-compartment-data.model"; import { GetProjectRecordDetailByShaService } from "./services/get-project-record-detail-by-sha.service"; import { - DeleModel, - ProjectRecordDetailModel, + DeleModel, + ProjectRecordDetailModel, } from "./models/project-record-detail.model"; import { Project2 } from "./models/project2.model"; import { PaginationArgs, SorterArgs } from "../types/input-types.args"; @@ -27,182 +27,178 @@ import { UpdateProjectService } from "./services/crud/update-project.service"; @Resolver(() => "Project") export class ProjectResolver { - constructor( - private readonly projectService: ProjectService, - private readonly getProjectChartDataService: GetProjectChartDataService, - private readonly getProjectRecordsService: GetProjectRecordsService, - private readonly getProjectCompartmentDataService: GetProjectCompartmentDataService, - private readonly getProjectRecordDetailByShaService: GetProjectRecordDetailByShaService, - private readonly getProjectsService: GetProjectsService, - private readonly deleteProjectRecordService: DeleteProjectRecordService, - private readonly updateProjectService: UpdateProjectService, - ) {} - @UseGuards(GqlAuthGuard) - @Query(() => ProjectPagesModel, { - description: "获取Project", - }) - getProjects( - @GqlUser() user: User, - @Args("keyword", { type: () => String }) keyword: string, - @Args("lang", { type: () => [String] }) lang: string[], - @Args("bu", { type: () => [String] }) bu: string[], - @Args("tag", { type: () => String }) tag: string, - @Args() paginationArgs: PaginationArgs, - @Args() sorterArgs: SorterArgs, - @Args("favorOnly", { type: () => Boolean }) favorOnly: boolean, - ): Promise { - return this.getProjectsService.invoke( - user?.id, - paginationArgs.current, - paginationArgs.pageSize, - keyword, - lang, - bu, - tag, - sorterArgs.field, - sorterArgs.order, - favorOnly, - ); - } + constructor( + private readonly projectService: ProjectService, + private readonly getProjectChartDataService: GetProjectChartDataService, + private readonly getProjectRecordsService: GetProjectRecordsService, + private readonly getProjectCompartmentDataService: GetProjectCompartmentDataService, + private readonly getProjectRecordDetailByShaService: GetProjectRecordDetailByShaService, + private readonly getProjectsService: GetProjectsService, + private readonly deleteProjectRecordService: DeleteProjectRecordService, + private readonly updateProjectService: UpdateProjectService, + ) {} + @UseGuards(GqlAuthGuard) + @Query(() => ProjectPagesModel, { + description: "获取Project", + }) + getProjects( + @GqlUser() user: User, + @Args("keyword", { type: () => String }) keyword: string, + @Args("lang", { type: () => [String] }) lang: string[], + @Args("bu", { type: () => [String] }) bu: string[], + @Args("tag", { type: () => String }) tag: string, + @Args() paginationArgs: PaginationArgs, + @Args() sorterArgs: SorterArgs, + @Args("favorOnly", { type: () => Boolean }) favorOnly: boolean, + ): Promise { + return this.getProjectsService.invoke( + user?.id, + paginationArgs.current, + paginationArgs.pageSize, + keyword, + lang, + bu, + tag, + sorterArgs.field, + sorterArgs.order, + favorOnly, + ); + } - @Query(() => [BuOption], { - description: "获取Projects部门选项", - }) - getProjectsBuOptions(): Promise { - return this.projectService.getProjectsBuOptions(); - } + @Query(() => [BuOption], { + description: "获取Projects部门选项", + }) + getProjectsBuOptions(): Promise { + return this.projectService.getProjectsBuOptions(); + } - @Query(() => [TagOption], { - description: "获取Projects标签选项", - }) - getProjectsTagOptions(): Promise { - return this.projectService.getProjectsTagOptions(); - } + @Query(() => [TagOption], { + description: "获取Projects标签选项", + }) + getProjectsTagOptions(): Promise { + return this.projectService.getProjectsTagOptions(); + } - @Query(() => [ProjectChartDataModel], { - description: "获取Project图表", - }) - getProjectChartData( - @Args("projectID", { type: () => String }) projectID: string, - @Args("branch", { type: () => String }) branch: string, - ): Promise { - return this.getProjectChartDataService.invoke(projectID, branch); - } + @Query(() => [ProjectChartDataModel], { + description: "获取Project图表", + }) + getProjectChartData( + @Args("projectID", { type: () => String }) projectID: string, + @Args("branch", { type: () => String }) branch: string, + ): Promise { + return this.getProjectChartDataService.invoke(projectID, branch); + } - @Query(() => [ProjectCompartmentDataModel], { - description: "获取Project宫格", - }) - getProjectCompartmentData( - @Args("projectID", { type: () => String }) projectID: string, - ): Promise { - return this.getProjectCompartmentDataService.invoke(projectID); - } + @Query(() => [ProjectCompartmentDataModel], { + description: "获取Project宫格", + }) + getProjectCompartmentData( + @Args("projectID", { type: () => String }) projectID: string, + ): Promise { + return this.getProjectCompartmentDataService.invoke(projectID); + } - @Query(() => ProjectRecordsPagesModel, { - description: "获取Project记录", - }) - getProjectRecords( - @Args("projectID", { type: () => String }) projectID: string, - @Args("current", { type: () => Int }) current: number, - @Args("pageSize", { type: () => Int }) pageSize: number, - @Args("keyword", { type: () => String }) keyword: string, - @Args("onlyDefault", { type: () => Boolean, nullable: true }) - onlyDefault?: boolean, - ): Promise { - return this.getProjectRecordsService.invoke( - projectID, - current, - pageSize, - keyword, - onlyDefault, - ); - } + @Query(() => ProjectRecordsPagesModel, { + description: "获取Project记录", + }) + getProjectRecords( + @Args("projectID", { type: () => String }) projectID: string, + @Args("current", { type: () => Int }) current: number, + @Args("pageSize", { type: () => Int }) pageSize: number, + @Args("keyword", { type: () => String }) keyword: string, + @Args("onlyDefault", { type: () => Boolean, nullable: true }) + onlyDefault?: boolean, + ): Promise { + return this.getProjectRecordsService.invoke( + projectID, + current, + pageSize, + keyword, + onlyDefault, + ); + } - @Query(() => Project) - getProjectByID( - @Args("projectID", { type: () => ID }) projectID: string, - ): Promise { - return this.projectService.getProjectByID(projectID); - } + @Query(() => Project) + getProjectByID( + @Args("projectID", { type: () => ID }) projectID: string, + ): Promise { + return this.projectService.getProjectByID(projectID); + } - @Query(() => [ProjectRecordDetailModel], { - description: "获取Project记录的详细通过sha", - }) - getProjectRecordDetailBySha( - @Args("projectID", { type: () => ID }) projectID: string, - @Args("sha", { type: () => String }) sha: string, - ): Promise { - return this.getProjectRecordDetailByShaService.invoke(projectID, sha); - } + @Query(() => [ProjectRecordDetailModel], { + description: "获取Project记录的详细通过sha", + }) + getProjectRecordDetailBySha( + @Args("projectID", { type: () => ID }) projectID: string, + @Args("sha", { type: () => String }) sha: string, + ): Promise { + return this.getProjectRecordDetailByShaService.invoke(projectID, sha); + } - // getProjectRecords - // getProjectChartData - // getProjectCompartmentData + // getProjectRecords + // getProjectChartData + // getProjectCompartmentData - @Mutation(() => Project2, { - description: "检查输入的gitlab链接", - }) - @UseGuards(GqlAuthGuard) - checkProjectUrl( - @GqlUser() user: AuthUser, - @Args("projectUrl", { type: () => String }) projectUrl: string, - ): Promise { - return this.projectService.checkProjectUrl(user, projectUrl); - } + @Mutation(() => Project2, { + description: "检查输入的gitlab链接", + }) + @UseGuards(GqlAuthGuard) + checkProjectUrl( + @GqlUser() user: AuthUser, + @Args("projectUrl", { type: () => String }) projectUrl: string, + ): Promise { + return this.projectService.checkProjectUrl(user, projectUrl); + } - @Mutation(() => Project2, { - description: "创建项目", - }) - @UseGuards(GqlAuthGuard) - createProject( - @GqlUser() user: AuthUser, - @Args("projectID", { type: () => String }) projectID: string, - // @Args("language", { type: () => String }) language: string, - ): Promise { - return this.projectService.createProject(user, projectID); - } + @Mutation(() => Project2, { + description: "创建项目", + }) + @UseGuards(GqlAuthGuard) + createProject( + @GqlUser() user: AuthUser, + @Args("projectID", { type: () => String }) projectID: string, + // @Args("language", { type: () => String }) language: string, + ): Promise { + return this.projectService.createProject(user, projectID); + } - @Mutation(() => Project2, { - description: "删除项目", - }) - @UseGuards(GqlAuthGuard) - deleteProject( - @GqlUser() user: AuthUser, - @Args("projectID", { type: () => String }) projectID: string, - ): Promise { - return this.projectService.deleteProject(user, projectID); - } + @Mutation(() => Project2, { + description: "删除项目", + }) + @UseGuards(GqlAuthGuard) + deleteProject( + @GqlUser() user: AuthUser, + @Args("projectID", { type: () => String }) projectID: string, + ): Promise { + return this.projectService.deleteProject(user, projectID); + } - @Mutation(() => Project2, { - description: "更新项目", - }) - @UseGuards(GqlAuthGuard) - updateProject( - @GqlUser() user: AuthUser, - @Args() args: UpdateProjectArgs, - ): Promise { - const { projectID, ...otherData } = args; - return this.updateProjectService.invoke( - user, - projectID, - otherData, - // args.rules, - ); - } + @Mutation(() => Project2, { + description: "更新项目", + }) + @UseGuards(GqlAuthGuard) + updateProject( + @GqlUser() user: AuthUser, + @Args() args: UpdateProjectArgs, + ): Promise { + const { projectID, ...otherData } = args; + return this.updateProjectService.invoke( + user, + projectID, + otherData, + // args.rules, + ); + } - @Mutation(() => DeleModel, { - description: "删除sha记录", - }) - @UseGuards(GqlAuthGuard) - deleteProjectRecord( - @GqlUser() currentUser: AuthUser, - @Args("projectID", { type: () => ID }) projectID: string, - @Args("sha", { type: () => String }) sha: string, - ): Promise<{ count: number }> { - return this.deleteProjectRecordService.invoke( - currentUser, - projectID, - sha, - ); - } + @Mutation(() => DeleModel, { + description: "删除sha记录", + }) + @UseGuards(GqlAuthGuard) + deleteProjectRecord( + @GqlUser() currentUser: AuthUser, + @Args("projectID", { type: () => ID }) projectID: string, + @Args("sha", { type: () => String }) sha: string, + ): Promise<{ count: number }> { + return this.deleteProjectRecordService.invoke(currentUser, projectID, sha); + } } diff --git a/packages/canyon-backend/src/project/project.zod.ts b/packages/canyon-backend/src/project/project.zod.ts index 28c839a8..f03c3228 100644 --- a/packages/canyon-backend/src/project/project.zod.ts +++ b/packages/canyon-backend/src/project/project.zod.ts @@ -1,24 +1,24 @@ import { z } from "zod"; export const projectTags = z.array( - z.object({ - id: z.string().default(""), - name: z.string().default(""), - link: z.string().default(""), - color: z.string().default(""), - }), + z.object({ + id: z.string().default(""), + name: z.string().default(""), + link: z.string().default(""), + color: z.string().default(""), + }), ); export const projectMembers = z.array( - z.object({ - userID: z.string().default(""), - role: z.string().default(""), - }), + z.object({ + userID: z.string().default(""), + role: z.string().default(""), + }), ); export const projectsAutoInstrument = z.array( - z.object({ - filepath: z.string().default(""), - content: z.string().default(""), - }), + z.object({ + filepath: z.string().default(""), + content: z.string().default(""), + }), ); diff --git a/packages/canyon-backend/src/project/services/crud/update-project.service.ts b/packages/canyon-backend/src/project/services/crud/update-project.service.ts index da688992..100ff880 100644 --- a/packages/canyon-backend/src/project/services/crud/update-project.service.ts +++ b/packages/canyon-backend/src/project/services/crud/update-project.service.ts @@ -1,26 +1,26 @@ import { Injectable } from "@nestjs/common"; import { PrismaService } from "../../../prisma/prisma.service"; function removeEmptyValues(obj) { - for (const key in obj) { - if ( - obj[key] === undefined || - obj[key] === null || - obj[key] === "__null__" - ) { - delete obj[key]; - } + for (const key in obj) { + if ( + obj[key] === undefined || + obj[key] === null || + obj[key] === "__null__" + ) { + delete obj[key]; } - return obj; + } + return obj; } @Injectable() export class UpdateProjectService { - constructor(private readonly prisma: PrismaService) {} - async invoke(currentUser, projectID, otherData) { - return this.prisma.project.update({ - where: { - id: projectID, - }, - data: removeEmptyValues(otherData), - }); - } + constructor(private readonly prisma: PrismaService) {} + async invoke(currentUser, projectID, otherData) { + return this.prisma.project.update({ + where: { + id: projectID, + }, + data: removeEmptyValues(otherData), + }); + } } diff --git a/packages/canyon-backend/src/project/services/delete-project-record.service.ts b/packages/canyon-backend/src/project/services/delete-project-record.service.ts index 981439b5..a41018f2 100644 --- a/packages/canyon-backend/src/project/services/delete-project-record.service.ts +++ b/packages/canyon-backend/src/project/services/delete-project-record.service.ts @@ -2,44 +2,44 @@ import { HttpException, HttpStatus, Injectable } from "@nestjs/common"; import { PrismaService } from "../../prisma/prisma.service"; @Injectable() export class DeleteProjectRecordService { - constructor(private readonly prisma: PrismaService) {} - async invoke(currentUser, projectID, sha) { - const members = await this.prisma.project - .findFirst({ - where: { - id: projectID, - }, - }) - .then((res) => res.members); - console.log(members); + constructor(private readonly prisma: PrismaService) {} + async invoke(currentUser, projectID, sha) { + const members = await this.prisma.project + .findFirst({ + where: { + id: projectID, + }, + }) + .then((res) => res.members); + console.log(members); - if ( - // @ts-ignore - !members - // @ts-ignore - .map((member) => member.userID) - .includes(String(currentUser.id)) - ) { - throw new HttpException( - { - statusCode: HttpStatus.UNAUTHORIZED, - message: "没有权限删除", - }, - HttpStatus.UNAUTHORIZED, - ); - } else { - return this.prisma.coverage - .deleteMany({ - where: { - projectID, - sha, - }, - }) - .then((res) => { - return { - count: res.count || 0, - }; - }); - } + if ( + // @ts-ignore + !members + // @ts-ignore + .map((member) => member.userID) + .includes(String(currentUser.id)) + ) { + throw new HttpException( + { + statusCode: HttpStatus.UNAUTHORIZED, + message: "没有权限删除", + }, + HttpStatus.UNAUTHORIZED, + ); + } else { + return this.prisma.coverage + .deleteMany({ + where: { + projectID, + sha, + }, + }) + .then((res) => { + return { + count: res.count || 0, + }; + }); } + } } diff --git a/packages/canyon-backend/src/project/services/get-project-chart-data.service.ts b/packages/canyon-backend/src/project/services/get-project-chart-data.service.ts index 11d7484e..9201184b 100644 --- a/packages/canyon-backend/src/project/services/get-project-chart-data.service.ts +++ b/packages/canyon-backend/src/project/services/get-project-chart-data.service.ts @@ -4,59 +4,53 @@ import { percent } from "canyon-data"; @Injectable() export class GetProjectChartDataService { - constructor(private readonly prisma: PrismaService) {} - async invoke(projectID, branch) { - const { defaultBranch } = await this.prisma.project.findFirst({ - where: { - id: projectID, - }, - }); - const allCovTypeCoverages = await this.prisma.coverage.findMany({ - where: { - projectID: projectID, - covType: "all", - branch: defaultBranch === "-" ? undefined : defaultBranch, - NOT: { - statementsCovered: 0, - // 老的逻辑,不再使用 - // summary: { - // path: ["statements", "covered"], - // equals: 0, - // }, - }, - // NOT: { - // summary: { - // path: ["statements", "covered"], - // equals: 0, - // }, - // }, - }, - orderBy: { - updatedAt: "desc", - }, - select: { - sha: true, - statementsCovered: true, - statementsTotal: true, - newlinesCovered: true, - newlinesTotal: true, - }, - }); + constructor(private readonly prisma: PrismaService) {} + async invoke(projectID, branch) { + const { defaultBranch } = await this.prisma.project.findFirst({ + where: { + id: projectID, + }, + }); + const allCovTypeCoverages = await this.prisma.coverage.findMany({ + where: { + projectID: projectID, + covType: "all", + branch: defaultBranch === "-" ? undefined : defaultBranch, + NOT: { + statementsCovered: 0, + // 老的逻辑,不再使用 + // summary: { + // path: ["statements", "covered"], + // equals: 0, + // }, + }, + // NOT: { + // summary: { + // path: ["statements", "covered"], + // equals: 0, + // }, + // }, + }, + orderBy: { + updatedAt: "desc", + }, + select: { + sha: true, + statementsCovered: true, + statementsTotal: true, + newlinesCovered: true, + newlinesTotal: true, + }, + }); - return allCovTypeCoverages - .map((item) => { - return { - sha: item.sha, - statements: percent( - item.statementsCovered, - item.statementsTotal, - ), - newlines: percent( - item.newlinesCovered, - item.newlinesCovered, - ), - }; - }) - .reverse(); - } + return allCovTypeCoverages + .map((item) => { + return { + sha: item.sha, + statements: percent(item.statementsCovered, item.statementsTotal), + newlines: percent(item.newlinesCovered, item.newlinesCovered), + }; + }) + .reverse(); + } } diff --git a/packages/canyon-backend/src/project/services/get-project-compartment-data.service.ts b/packages/canyon-backend/src/project/services/get-project-compartment-data.service.ts index 264cb841..370d28a6 100644 --- a/packages/canyon-backend/src/project/services/get-project-compartment-data.service.ts +++ b/packages/canyon-backend/src/project/services/get-project-compartment-data.service.ts @@ -5,77 +5,77 @@ import { percent } from "canyon-data"; @Injectable() export class GetProjectCompartmentDataService { - constructor(private readonly prisma: PrismaService) {} - async invoke(projectID) { - const project = await this.prisma.project.findFirst({ - where: { - id: projectID, - }, - }); - const coverages = await this.prisma.coverage.findMany({ - where: { - projectID: projectID, - covType: "all", - branch: ["", "-"].includes(project.defaultBranch) - ? undefined - : project.defaultBranch, - }, - orderBy: { - updatedAt: "desc", - }, - select: { - statementsCovered: true, - statementsTotal: true, - updatedAt: true, - }, - }); - if (coverages.length > 0) { - return [ - { - label: "projects.total_times", - value: String(coverages.length), - }, - { - label: "projects.max_coverage", - value: - Math.max( - ...coverages.map((c) => - percent(c.statementsCovered, c.statementsTotal), - ), - ) + "%", - }, - { - label: "projects.latest_report_time", - value: dayjs(coverages[0].updatedAt).format("MM-DD HH:mm"), - }, - { - label: "projects.latest_report_coverage", - value: - percent( - coverages[0].statementsCovered, - coverages[0].statementsTotal, - ) + "%", - }, - ]; - } else { - return [ - { - label: "projects.total_times", - value: "0", - }, - { - label: "projects.max_coverage", - value: "0%", - }, - { - label: "projects.latest_report_time", - value: "0", - }, - { - label: "projects.latest_report_coverage", - value: "0%", - }, - ]; - } + constructor(private readonly prisma: PrismaService) {} + async invoke(projectID) { + const project = await this.prisma.project.findFirst({ + where: { + id: projectID, + }, + }); + const coverages = await this.prisma.coverage.findMany({ + where: { + projectID: projectID, + covType: "all", + branch: ["", "-"].includes(project.defaultBranch) + ? undefined + : project.defaultBranch, + }, + orderBy: { + updatedAt: "desc", + }, + select: { + statementsCovered: true, + statementsTotal: true, + updatedAt: true, + }, + }); + if (coverages.length > 0) { + return [ + { + label: "projects.total_times", + value: String(coverages.length), + }, + { + label: "projects.max_coverage", + value: + Math.max( + ...coverages.map((c) => + percent(c.statementsCovered, c.statementsTotal), + ), + ) + "%", + }, + { + label: "projects.latest_report_time", + value: dayjs(coverages[0].updatedAt).format("MM-DD HH:mm"), + }, + { + label: "projects.latest_report_coverage", + value: + percent( + coverages[0].statementsCovered, + coverages[0].statementsTotal, + ) + "%", + }, + ]; + } else { + return [ + { + label: "projects.total_times", + value: "0", + }, + { + label: "projects.max_coverage", + value: "0%", + }, + { + label: "projects.latest_report_time", + value: "0", + }, + { + label: "projects.latest_report_coverage", + value: "0%", + }, + ]; } + } } diff --git a/packages/canyon-backend/src/project/services/get-project-record-detail-by-sha.service.ts b/packages/canyon-backend/src/project/services/get-project-record-detail-by-sha.service.ts index ec928ed2..a7ffab54 100644 --- a/packages/canyon-backend/src/project/services/get-project-record-detail-by-sha.service.ts +++ b/packages/canyon-backend/src/project/services/get-project-record-detail-by-sha.service.ts @@ -3,69 +3,65 @@ import { PrismaService } from "../../prisma/prisma.service"; import { percent } from "canyon-data"; @Injectable() export class GetProjectRecordDetailByShaService { - constructor(private readonly prisma: PrismaService) {} - async invoke(projectID, sha): Promise { - const current = 1; - const pageSize = 200; - const coverages = await this.prisma.coverage.findMany({ - where: { - projectID, - sha, - covType: "agg", - }, - skip: (current - 1) * pageSize, - take: pageSize, - orderBy: { - updatedAt: "desc", - }, - select: { - id: true, - sha: true, - reportID: true, - statementsCovered: true, - statementsTotal: true, - newlinesCovered: true, - newlinesTotal: true, - createdAt: true, - updatedAt: true, - reporter: true, - }, - }); - const users = await this.prisma.user.findMany({ - where: {}, - }); - const rows = []; + constructor(private readonly prisma: PrismaService) {} + async invoke(projectID, sha): Promise { + const current = 1; + const pageSize = 200; + const coverages = await this.prisma.coverage.findMany({ + where: { + projectID, + sha, + covType: "agg", + }, + skip: (current - 1) * pageSize, + take: pageSize, + orderBy: { + updatedAt: "desc", + }, + select: { + id: true, + sha: true, + reportID: true, + statementsCovered: true, + statementsTotal: true, + newlinesCovered: true, + newlinesTotal: true, + createdAt: true, + updatedAt: true, + reporter: true, + }, + }); + const users = await this.prisma.user.findMany({ + where: {}, + }); + const rows = []; - for (let i = 0; i < coverages.length; i++) { - const coverage = coverages[i]; - const data = { - ...coverage, - relationID: "", - compareUrl: "", - webUrl: "", - statements: percent( - coverage.statementsCovered, - coverage.statementsTotal, - ), - newlines: percent( - coverage.newlinesCovered, - coverage.newlinesTotal, - ), - lastReportTime: coverage.updatedAt, - times: 0, - logs: [], - message: "", - reporterUsername: - users.find(({ id: uId }) => { - return String(uId) === coverage.reporter; - })?.nickname || "not found", - reporterAvatar: - users.find( - ({ id: uId }) => String(uId) === coverage.reporter, - )?.avatar || "not found", - }; - rows.push(data); - } - return rows; + for (let i = 0; i < coverages.length; i++) { + const coverage = coverages[i]; + const data = { + ...coverage, + relationID: "", + compareUrl: "", + webUrl: "", + statements: percent( + coverage.statementsCovered, + coverage.statementsTotal, + ), + newlines: percent(coverage.newlinesCovered, coverage.newlinesTotal), + lastReportTime: coverage.updatedAt, + times: 0, + logs: [], + message: "", + reporterUsername: + users.find(({ id: uId }) => { + return String(uId) === coverage.reporter; + })?.nickname || "not found", + reporterAvatar: + users.find(({ id: uId }) => String(uId) === coverage.reporter) + ?.avatar || "not found", + }; + rows.push(data); } + return rows; + } } diff --git a/packages/canyon-backend/src/project/services/get-project-records.service.ts b/packages/canyon-backend/src/project/services/get-project-records.service.ts index 24cae32f..74945539 100644 --- a/packages/canyon-backend/src/project/services/get-project-records.service.ts +++ b/packages/canyon-backend/src/project/services/get-project-records.service.ts @@ -4,163 +4,151 @@ import { getCommits } from "../../adapter/gitlab.adapter"; import { percent } from "canyon-data"; @Injectable() export class GetProjectRecordsService { - constructor(private readonly prisma: PrismaService) {} - async invoke( - projectID, - current, - pageSize, - keyword, - onlyDefault, - ): Promise { - const project = await this.prisma.project.findFirst({ - where: { - id: projectID, - }, - }); - const whereCondition = { - projectID, - covType: "all", - branch: project.defaultBranch, - OR: [ - // { description: { contains: keyword } }, - // { name: { contains: keyword } }, - { sha: { contains: keyword } }, - { branch: { contains: keyword } }, - { compareTarget: { contains: keyword } }, - // { message: { contains: keyword } }, - ], - NOT: { - statementsCovered: 0, - // 老的逻辑,不再使用 - // summary: { - // path: ["statements", "covered"], - // equals: 0, - // }, - }, - }; + constructor(private readonly prisma: PrismaService) {} + async invoke( + projectID, + current, + pageSize, + keyword, + onlyDefault, + ): Promise { + const project = await this.prisma.project.findFirst({ + where: { + id: projectID, + }, + }); + const whereCondition = { + projectID, + covType: "all", + branch: project.defaultBranch, + OR: [ + // { description: { contains: keyword } }, + // { name: { contains: keyword } }, + { sha: { contains: keyword } }, + { branch: { contains: keyword } }, + { compareTarget: { contains: keyword } }, + // { message: { contains: keyword } }, + ], + NOT: { + statementsCovered: 0, + // 老的逻辑,不再使用 + // summary: { + // path: ["statements", "covered"], + // equals: 0, + // }, + }, + }; - if (Boolean(onlyDefault) && project.defaultBranch !== "-") { - } else { - delete whereCondition.branch; - } + if (Boolean(onlyDefault) && project.defaultBranch !== "-") { + } else { + delete whereCondition.branch; + } - const total = await this.prisma.coverage.count({ - where: whereCondition, - }); - const coverages = await this.prisma.coverage.findMany({ - where: whereCondition, - skip: (current - 1) * pageSize, - take: pageSize, - orderBy: { - updatedAt: "desc", - }, - select: { - sha: true, - compareTarget: true, - branch: true, - buildID: true, - buildProvider: true, - createdAt: true, - updatedAt: true, - statementsCovered: true, - statementsTotal: true, - functionsCovered: true, - functionsTotal: true, - branchesCovered: true, - branchesTotal: true, - linesCovered: true, - linesTotal: true, - newlinesCovered: true, - newlinesTotal: true, - }, - }); - const gitProvider = await this.prisma.gitProvider.findFirst({ - where: { - disabled: false, - }, - }); - const commits = await getCommits( - { - projectID: projectID.split("-")[1], - commitShas: coverages.map((item) => item.sha), - }, - gitProvider?.privateToken, - gitProvider?.url, - ); - // const commits = []; - const rows = []; + const total = await this.prisma.coverage.count({ + where: whereCondition, + }); + const coverages = await this.prisma.coverage.findMany({ + where: whereCondition, + skip: (current - 1) * pageSize, + take: pageSize, + orderBy: { + updatedAt: "desc", + }, + select: { + sha: true, + compareTarget: true, + branch: true, + buildID: true, + buildProvider: true, + createdAt: true, + updatedAt: true, + statementsCovered: true, + statementsTotal: true, + functionsCovered: true, + functionsTotal: true, + branchesCovered: true, + branchesTotal: true, + linesCovered: true, + linesTotal: true, + newlinesCovered: true, + newlinesTotal: true, + }, + }); + const gitProvider = await this.prisma.gitProvider.findFirst({ + where: { + disabled: false, + }, + }); + const commits = await getCommits( + { + projectID: projectID.split("-")[1], + commitShas: coverages.map((item) => item.sha), + }, + gitProvider?.privateToken, + gitProvider?.url, + ); + // const commits = []; + const rows = []; - const csList = await Promise.all( - coverages.map((coverage) => - this.prisma.coverage.findMany({ - where: { - projectID, - sha: coverage.sha, - covType: "agg", - }, - select: { - createdAt: true, - updatedAt: true, - }, - orderBy: { - updatedAt: "desc", - }, - }), - ), - ); + const csList = await Promise.all( + coverages.map((coverage) => + this.prisma.coverage.findMany({ + where: { + projectID, + sha: coverage.sha, + covType: "agg", + }, + select: { + createdAt: true, + updatedAt: true, + }, + orderBy: { + updatedAt: "desc", + }, + }), + ), + ); - for (let i = 0; i < coverages.length; i++) { - const coverage = coverages[i]; + for (let i = 0; i < coverages.length; i++) { + const coverage = coverages[i]; - const cs = csList[i]; + const cs = csList[i]; - const data = { - ...coverage, - compareUrl: `${gitProvider?.url}/${project.pathWithNamespace}/-/compare/${coverage.compareTarget}...${coverage.sha}`, - webUrl: - commits.find(({ id }) => id === coverage.sha)?.web_url || - "???", - message: - commits.find(({ id }) => id === coverage.sha)?.message || - "???", - statements: percent( - coverage.statementsCovered, - coverage.statementsTotal, - ), - functions: percent( - coverage.functionsCovered, - coverage.functionsTotal, - ), - branches: percent( - coverage.branchesCovered, - coverage.branchesTotal, - ), - lines: percent(coverage.linesCovered, coverage.linesTotal), - newlines: percent( - coverage.newlinesCovered, - coverage.newlinesTotal, - ), - // statements: coverage.summary["statements"]["pct"], - // functions: coverage.summary["functions"]["pct"], - // branches: coverage.summary["branches"]["pct"], - // lines: coverage.summary["lines"]["pct"], - lastReportTime: cs[0]?.updatedAt || coverage.createdAt, //没有agg类型的时候就用all的创建时间 - times: cs.length, - logs: [], - buildID: coverage.buildID, - buildProvider: coverage.buildProvider, - buildURL: - coverage.buildProvider === "mpaas" - ? `${atob("aHR0cHM6Ly9tcGFhcy5jdHJpcGNvcnAuY29tL3NwcmluZy9tY2R2Mi9wYWNrYWdlUHVibGlzaFYyL1JlYWN0TmF0aXZl") || "??"}?appId=${coverage.buildID.split("|")[0]}&module=${coverage.buildID.split("|")[1]}&filters={"buildId":"${coverage.buildID.split("|")[2]}"}` - : `${gitProvider?.url}/${project.pathWithNamespace}/-/jobs/${coverage.buildID}`, - }; - rows.push(data); - } - return { - data: rows, - total, - }; + const data = { + ...coverage, + compareUrl: `${gitProvider?.url}/${project.pathWithNamespace}/-/compare/${coverage.compareTarget}...${coverage.sha}`, + webUrl: commits.find(({ id }) => id === coverage.sha)?.web_url || "???", + message: + commits.find(({ id }) => id === coverage.sha)?.message || "???", + statements: percent( + coverage.statementsCovered, + coverage.statementsTotal, + ), + functions: percent(coverage.functionsCovered, coverage.functionsTotal), + branches: percent(coverage.branchesCovered, coverage.branchesTotal), + lines: percent(coverage.linesCovered, coverage.linesTotal), + newlines: percent(coverage.newlinesCovered, coverage.newlinesTotal), + // statements: coverage.summary["statements"]["pct"], + // functions: coverage.summary["functions"]["pct"], + // branches: coverage.summary["branches"]["pct"], + // lines: coverage.summary["lines"]["pct"], + lastReportTime: cs[0]?.updatedAt || coverage.createdAt, //没有agg类型的时候就用all的创建时间 + times: cs.length, + logs: [], + buildID: coverage.buildID, + buildProvider: coverage.buildProvider, + buildURL: + coverage.buildProvider === "mpaas" + ? `${atob("aHR0cHM6Ly9tcGFhcy5jdHJpcGNvcnAuY29tL3NwcmluZy9tY2R2Mi9wYWNrYWdlUHVibGlzaFYyL1JlYWN0TmF0aXZl") || "??"}?appId=${coverage.buildID.split("|")[0]}&module=${coverage.buildID.split("|")[1]}&filters={"buildId":"${coverage.buildID.split("|")[2]}"}` + : `${gitProvider?.url}/${project.pathWithNamespace}/-/jobs/${coverage.buildID}`, + }; + rows.push(data); } + return { + data: rows, + total, + }; + } } //get-project-record-detail-by-sha.service.ts diff --git a/packages/canyon-backend/src/project/services/get-projects.service.ts b/packages/canyon-backend/src/project/services/get-projects.service.ts index 158d11b9..2a1b791a 100644 --- a/packages/canyon-backend/src/project/services/get-projects.service.ts +++ b/packages/canyon-backend/src/project/services/get-projects.service.ts @@ -6,166 +6,158 @@ import * as dayjs from "dayjs"; @Injectable() export class GetProjectsService { - constructor(private readonly prisma: PrismaService) {} - async invoke( - userID, - current, - pageSize, - keyword, - lang, - bu, - tag, - field, - order, - favorOnly, - ): Promise { - const favorProjects = await this.prisma.user - .findUnique({ - where: { - id: String(userID), - }, - }) - .then((r) => r.favor.split(",").filter((item) => item !== "")); - // 2.根据项目ID再查询到对应的项目信息,使用promise.all - const whereCondition: any = { - OR: [ - { - pathWithNamespace: { - contains: keyword, - mode: "insensitive", // Ignore case sensitivity - }, - }, - { - id: { - contains: keyword, - mode: "insensitive", // Ignore case sensitivity - }, - }, - ], - }; + constructor(private readonly prisma: PrismaService) {} + async invoke( + userID, + current, + pageSize, + keyword, + lang, + bu, + tag, + field, + order, + favorOnly, + ): Promise { + const favorProjects = await this.prisma.user + .findUnique({ + where: { + id: String(userID), + }, + }) + .then((r) => r.favor.split(",").filter((item) => item !== "")); + // 2.根据项目ID再查询到对应的项目信息,使用promise.all + const whereCondition: any = { + OR: [ + { + pathWithNamespace: { + contains: keyword, + mode: "insensitive", // Ignore case sensitivity + }, + }, + { + id: { + contains: keyword, + mode: "insensitive", // Ignore case sensitivity + }, + }, + ], + }; - if (bu.length > 0) { - whereCondition.bu = { - in: bu, - }; - } - if (tag) { - whereCondition.tags = { - array_contains: [ - { - name: tag, - }, - ], - }; - } - const pro = await this.prisma.project.findMany({ - where: whereCondition, - select: { - id: true, - pathWithNamespace: true, - description: true, - bu: true, - // language: true, - }, - }); - const cov = await this.prisma.coverage.findMany({ - where: { - covType: "all", - projectID: { - in: pro.map(({ id }) => id), - }, - statementsTotal: { - not: 0, - }, - }, - select: { - projectID: true, - // project: true, - sha: true, - // branch: true, - // report: true, - createdAt: true, - updatedAt: true, - statementsCovered: true, - statementsTotal: true, - // summary: true, - }, - }); + if (bu.length > 0) { + whereCondition.bu = { + in: bu, + }; + } + if (tag) { + whereCondition.tags = { + array_contains: [ + { + name: tag, + }, + ], + }; + } + const pro = await this.prisma.project.findMany({ + where: whereCondition, + select: { + id: true, + pathWithNamespace: true, + description: true, + bu: true, + // language: true, + }, + }); + const cov = await this.prisma.coverage.findMany({ + where: { + covType: "all", + projectID: { + in: pro.map(({ id }) => id), + }, + statementsTotal: { + not: 0, + }, + }, + select: { + projectID: true, + // project: true, + sha: true, + // branch: true, + // report: true, + createdAt: true, + updatedAt: true, + statementsCovered: true, + statementsTotal: true, + // summary: true, + }, + }); - const rows = await Promise.all([cov, pro]).then((res) => { - const reslut = []; - const pros = res[1]; - for (let i = 0; i < pros.length; i++) { - const { - id, - pathWithNamespace, - description, - bu: _bu, - // language, - } = pros[i]; - const covs = res[0].filter((item) => { - return item.projectID === id; - }); - if ((favorOnly && favorProjects.includes(id)) || !favorOnly) { - reslut.push({ - favored: favorProjects.includes(id), - id: id, - bu: _bu, - description: description, - lastReportTime: - covs.length > 0 - ? covs.sort((a, b) => - dayjs(b.updatedAt).isBefore(a.updatedAt) - ? -1 - : 1, - )[0].updatedAt - : new Date("1970-01-01T00:00:00Z"), - maxCoverage: - covs.filter((item) => within30days(item.updatedAt)) - .length > 0 - ? Math.max( - ...covs - .filter((item) => - within30days(item.updatedAt), - ) - .map((item) => - percent( - item.statementsCovered, - item.statementsTotal, - ), - ), - ) - : 0, - reportTimes: covs.length, - pathWithNamespace: pathWithNamespace, - // language: language, - }); - } - } - field = field || "lastReportTime"; - return reslut.sort((a, b) => { - if (field === "lastReportTime") { - return order === "ascend" - ? dayjs(a.lastReportTime).isBefore(b.lastReportTime) - ? -1 - : 1 - : dayjs(a.lastReportTime).isBefore(b.lastReportTime) - ? 1 - : -1; - } else if (field === "maxCoverage") { - return order === "ascend" - ? a.maxCoverage - b.maxCoverage - : b.maxCoverage - a.maxCoverage; - } else if (field === "reportTimes") { - return order === "ascend" - ? a.reportTimes - b.reportTimes - : b.reportTimes - a.reportTimes; - } - }); + const rows = await Promise.all([cov, pro]).then((res) => { + const reslut = []; + const pros = res[1]; + for (let i = 0; i < pros.length; i++) { + const { + id, + pathWithNamespace, + description, + bu: _bu, + // language, + } = pros[i]; + const covs = res[0].filter((item) => { + return item.projectID === id; }); - return { - data: rows, - total: rows.length, - }; - } + if ((favorOnly && favorProjects.includes(id)) || !favorOnly) { + reslut.push({ + favored: favorProjects.includes(id), + id: id, + bu: _bu, + description: description, + lastReportTime: + covs.length > 0 + ? covs.sort((a, b) => + dayjs(b.updatedAt).isBefore(a.updatedAt) ? -1 : 1, + )[0].updatedAt + : new Date("1970-01-01T00:00:00Z"), + maxCoverage: + covs.filter((item) => within30days(item.updatedAt)).length > 0 + ? Math.max( + ...covs + .filter((item) => within30days(item.updatedAt)) + .map((item) => + percent(item.statementsCovered, item.statementsTotal), + ), + ) + : 0, + reportTimes: covs.length, + pathWithNamespace: pathWithNamespace, + // language: language, + }); + } + } + field = field || "lastReportTime"; + return reslut.sort((a, b) => { + if (field === "lastReportTime") { + return order === "ascend" + ? dayjs(a.lastReportTime).isBefore(b.lastReportTime) + ? -1 + : 1 + : dayjs(a.lastReportTime).isBefore(b.lastReportTime) + ? 1 + : -1; + } else if (field === "maxCoverage") { + return order === "ascend" + ? a.maxCoverage - b.maxCoverage + : b.maxCoverage - a.maxCoverage; + } else if (field === "reportTimes") { + return order === "ascend" + ? a.reportTimes - b.reportTimes + : b.reportTimes - a.reportTimes; + } + }); + }); + return { + data: rows, + total: rows.length, + }; + } } diff --git a/packages/canyon-backend/src/project/services/project.service.ts b/packages/canyon-backend/src/project/services/project.service.ts index f9beb0d2..dd006bfb 100755 --- a/packages/canyon-backend/src/project/services/project.service.ts +++ b/packages/canyon-backend/src/project/services/project.service.ts @@ -3,202 +3,196 @@ import { PrismaService } from "../../prisma/prisma.service"; import { Project } from "../project.model"; import { getProjectByID } from "src/adapter/gitlab.adapter"; import { - projectMembers, - projectsAutoInstrument, - projectTags, + projectMembers, + projectsAutoInstrument, + projectTags, } from "../project.zod"; // import { getProjectByID } from '../adapter/gitlab.adapter'; function parseGitLabUrl(gitLabUrl) { - // 匹配 GitLab URL 的正则表达式 - const gitLabRegex = /^(?:https?:\/\/)?([^\/]+)\/(.+)$/; + // 匹配 GitLab URL 的正则表达式 + const gitLabRegex = /^(?:https?:\/\/)?([^\/]+)\/(.+)$/; - // 尝试匹配正则表达式 - const match = gitLabUrl.match(gitLabRegex); + // 尝试匹配正则表达式 + const match = gitLabUrl.match(gitLabRegex); - if (match) { - // 提取匹配的组和仓库名 - const groupAndRepo = match[2].split("/"); - const groupName = groupAndRepo.slice(0, -1).join("/"); - const repositoryName = groupAndRepo.slice(-1)[0]; - return { groupName, repositoryName }; - } else { - // 如果没有匹配到,返回 null - return { groupName: null, repositoryName: null }; - } + if (match) { + // 提取匹配的组和仓库名 + const groupAndRepo = match[2].split("/"); + const groupName = groupAndRepo.slice(0, -1).join("/"); + const repositoryName = groupAndRepo.slice(-1)[0]; + return { groupName, repositoryName }; + } else { + // 如果没有匹配到,返回 null + return { groupName: null, repositoryName: null }; + } } @Injectable() export class ProjectService { - constructor(private readonly prisma: PrismaService) {} - async checkProjectUrl(user, projectUrl) { - let project = ""; - if (isNaN(Number(projectUrl))) { - project = `${parseGitLabUrl(projectUrl).groupName}%2F${ - parseGitLabUrl(projectUrl).repositoryName - }`; - } else { - project = projectUrl; - } - const gitProvider = await this.prisma.gitProvider.findFirst({ - where: { - disabled: false, - }, - }); - const { path_with_namespace, name, id, description } = - await getProjectByID( - project, - gitProvider?.privateToken, - gitProvider?.url, - ); - - return { - id: String(id), - pathWithNamespace: path_with_namespace, - name: name, - description: description || "", - }; + constructor(private readonly prisma: PrismaService) {} + async checkProjectUrl(user, projectUrl) { + let project = ""; + if (isNaN(Number(projectUrl))) { + project = `${parseGitLabUrl(projectUrl).groupName}%2F${ + parseGitLabUrl(projectUrl).repositoryName + }`; + } else { + project = projectUrl; } + const gitProvider = await this.prisma.gitProvider.findFirst({ + where: { + disabled: false, + }, + }); + const { path_with_namespace, name, id, description } = await getProjectByID( + project, + gitProvider?.privateToken, + gitProvider?.url, + ); - async createProject(user, projectID) { - const gitProvider = await this.prisma.gitProvider.findFirst({ - where: { - disabled: false, - }, - }); - const { path_with_namespace, description, name, bu } = - await getProjectByID( - projectID.split("-")[1], - gitProvider?.privateToken, - gitProvider?.url, - ); - return this.prisma.project.create({ - data: { - id: String(projectID), - pathWithNamespace: path_with_namespace, - name: name, - description: description || "", - bu: bu || "默认", - coverage: "", - defaultBranch: "-", - tags: [], - members: [], - // language: language, - // instrumentCwd: "", - }, - }); - } + return { + id: String(id), + pathWithNamespace: path_with_namespace, + name: name, + description: description || "", + }; + } - async deleteProject(user, projectID) { - return this.prisma.project.delete({ - where: { - id: projectID, - }, - }); - } + async createProject(user, projectID) { + const gitProvider = await this.prisma.gitProvider.findFirst({ + where: { + disabled: false, + }, + }); + const { path_with_namespace, description, name, bu } = await getProjectByID( + projectID.split("-")[1], + gitProvider?.privateToken, + gitProvider?.url, + ); + return this.prisma.project.create({ + data: { + id: String(projectID), + pathWithNamespace: path_with_namespace, + name: name, + description: description || "", + bu: bu || "默认", + coverage: "", + defaultBranch: "-", + tags: [], + members: [], + // language: language, + // instrumentCwd: "", + }, + }); + } - async getProjectByID(projectID): Promise { - const branchOptions = await this.prisma.coverage - .groupBy({ - by: ["branch"], - where: { - projectID: projectID, - }, - }) - .then((res) => res.map((item) => item.branch)); - return this.prisma.project - .findFirst({ - where: { - id: projectID, - }, - }) - .then( - ({ - id, - name, - pathWithNamespace, - description, - bu, - createdAt, - coverage, - defaultBranch, - tags, - // language, - members, - // instrumentCwd, - autoInstrument, - }) => { - return { - id, - name, - pathWithNamespace, - description, - createdAt, - bu: bu, - reportTimes: 0, - lastReportTime: new Date(), - maxCoverage: 0, - coverage, - defaultBranch, - branchOptions, - favored: false, - // language, - // instrumentCwd, - tags: projectTags - .parse(tags) - .map(({ id, name, link, color }) => ({ - id, - name, - link, - color, - })), - members: projectMembers - .parse(members) - .map(({ userID, role }) => ({ - userID, - role, - })), - autoInstrument: projectsAutoInstrument - .parse(autoInstrument || []) - .map(({ filepath, content }) => ({ - filepath, - content, - })), - }; - }, - ); - } + async deleteProject(user, projectID) { + return this.prisma.project.delete({ + where: { + id: projectID, + }, + }); + } - async getProjectsBuOptions() { - return this.prisma.project - .groupBy({ - by: ["bu"], - _count: true, - }) - .then((res) => { - return res.map((item) => { - return { - bu: item.bu, - count: item._count, - }; - }); - }); - } + async getProjectByID(projectID): Promise { + const branchOptions = await this.prisma.coverage + .groupBy({ + by: ["branch"], + where: { + projectID: projectID, + }, + }) + .then((res) => res.map((item) => item.branch)); + return this.prisma.project + .findFirst({ + where: { + id: projectID, + }, + }) + .then( + ({ + id, + name, + pathWithNamespace, + description, + bu, + createdAt, + coverage, + defaultBranch, + tags, + // language, + members, + // instrumentCwd, + autoInstrument, + }) => { + return { + id, + name, + pathWithNamespace, + description, + createdAt, + bu: bu, + reportTimes: 0, + lastReportTime: new Date(), + maxCoverage: 0, + coverage, + defaultBranch, + branchOptions, + favored: false, + // language, + // instrumentCwd, + tags: projectTags.parse(tags).map(({ id, name, link, color }) => ({ + id, + name, + link, + color, + })), + members: projectMembers.parse(members).map(({ userID, role }) => ({ + userID, + role, + })), + autoInstrument: projectsAutoInstrument + .parse(autoInstrument || []) + .map(({ filepath, content }) => ({ + filepath, + content, + })), + }; + }, + ); + } - async getProjectsTagOptions() { - const projects = await this.prisma.project.findMany({ - where: {}, - select: { - tags: true, - }, + async getProjectsBuOptions() { + return this.prisma.project + .groupBy({ + by: ["bu"], + _count: true, + }) + .then((res) => { + return res.map((item) => { + return { + bu: item.bu, + count: item._count, + }; }); - const allTags = projects.reduce((acc, { tags }) => { - return acc.concat(tags); - }, []); - return [...new Set(allTags.map(({ name }) => name))] - .sort((a, b) => a.length - b.length) - .map((name) => ({ - name, - })); - } + }); + } + + async getProjectsTagOptions() { + const projects = await this.prisma.project.findMany({ + where: {}, + select: { + tags: true, + }, + }); + const allTags = projects.reduce((acc, { tags }) => { + return acc.concat(tags); + }, []); + return [...new Set(allTags.map(({ name }) => name))] + .sort((a, b) => a.length - b.length) + .map((name) => ({ + name, + })); + } } diff --git a/packages/canyon-backend/src/sourcecode/sourcecode.controller.ts b/packages/canyon-backend/src/sourcecode/sourcecode.controller.ts index 7653b9fa..546f62c7 100644 --- a/packages/canyon-backend/src/sourcecode/sourcecode.controller.ts +++ b/packages/canyon-backend/src/sourcecode/sourcecode.controller.ts @@ -3,10 +3,10 @@ import { SourcecodeService } from "./sourcecode.service"; @Controller("") export class SourcecodeController { - constructor(private readonly sourcecodeService: SourcecodeService) {} - @Get("api/sourcecode") - getHello(@Query() query): Promise { - const { projectID, sha, filepath } = query; - return this.sourcecodeService.getsourcecode(projectID, sha, filepath); - } + constructor(private readonly sourcecodeService: SourcecodeService) {} + @Get("api/sourcecode") + getHello(@Query() query): Promise { + const { projectID, sha, filepath } = query; + return this.sourcecodeService.getsourcecode(projectID, sha, filepath); + } } diff --git a/packages/canyon-backend/src/sourcecode/sourcecode.module.ts b/packages/canyon-backend/src/sourcecode/sourcecode.module.ts index 49f9af30..33f569df 100644 --- a/packages/canyon-backend/src/sourcecode/sourcecode.module.ts +++ b/packages/canyon-backend/src/sourcecode/sourcecode.module.ts @@ -4,7 +4,7 @@ import { PrismaService } from "../prisma/prisma.service"; import { SourcecodeService } from "./sourcecode.service"; @Module({ - controllers: [SourcecodeController], - providers: [SourcecodeService, PrismaService], + controllers: [SourcecodeController], + providers: [SourcecodeService, PrismaService], }) export class SourcecodeModule {} diff --git a/packages/canyon-backend/src/sourcecode/sourcecode.service.ts b/packages/canyon-backend/src/sourcecode/sourcecode.service.ts index 8bfd1b9d..8e06a0ff 100644 --- a/packages/canyon-backend/src/sourcecode/sourcecode.service.ts +++ b/packages/canyon-backend/src/sourcecode/sourcecode.service.ts @@ -4,22 +4,22 @@ import { getFileInfo } from "../adapter/gitlab.adapter"; @Injectable() export class SourcecodeService { - constructor(private readonly prisma: PrismaService) {} - async getsourcecode(projectID, sha, filepath): Promise { - const gitProvider = await this.prisma.gitProvider.findFirst({ - where: { - id: projectID.split("-")[0], - }, - }); - // console.log(gitProvider,'gitProvider') - return getFileInfo( - { - projectID: projectID.split("-")[1], - filepath: encodeURIComponent(filepath), - commitSha: sha, - }, - gitProvider?.privateToken, - gitProvider?.url, - ); - } + constructor(private readonly prisma: PrismaService) {} + async getsourcecode(projectID, sha, filepath): Promise { + const gitProvider = await this.prisma.gitProvider.findFirst({ + where: { + id: projectID.split("-")[0], + }, + }); + // console.log(gitProvider,'gitProvider') + return getFileInfo( + { + projectID: projectID.split("-")[1], + filepath: encodeURIComponent(filepath), + commitSha: sha, + }, + gitProvider?.privateToken, + gitProvider?.url, + ); + } } diff --git a/packages/canyon-backend/src/types/input-types.args.ts b/packages/canyon-backend/src/types/input-types.args.ts index 84a3756e..9ab3bc75 100644 --- a/packages/canyon-backend/src/types/input-types.args.ts +++ b/packages/canyon-backend/src/types/input-types.args.ts @@ -3,27 +3,27 @@ import { ArgsType, Field, InputType, Int } from "@nestjs/graphql"; @ArgsType() @InputType() export class PaginationArgs { - @Field(() => Int, { - description: "当前页码", - }) - current: number; + @Field(() => Int, { + description: "当前页码", + }) + current: number; - @Field(() => Int, { - description: "每页数量", - }) - pageSize: number; + @Field(() => Int, { + description: "每页数量", + }) + pageSize: number; } @ArgsType() @InputType() export class SorterArgs { - @Field({ - description: "排序字段名称", - }) - field: string; + @Field({ + description: "排序字段名称", + }) + field: string; - @Field({ - description: "升序或降序", - }) - order: string; + @Field({ + description: "升序或降序", + }) + order: string; } diff --git a/packages/canyon-backend/src/user/crud/list-user.service.ts b/packages/canyon-backend/src/user/crud/list-user.service.ts index f7d6d8f1..3be9bfd8 100644 --- a/packages/canyon-backend/src/user/crud/list-user.service.ts +++ b/packages/canyon-backend/src/user/crud/list-user.service.ts @@ -3,10 +3,10 @@ import { PrismaService } from "../../prisma/prisma.service"; @Injectable() export class ListUserService { - constructor(private readonly prisma: PrismaService) {} - async invoke() { - return this.prisma.user.findMany({ - where: {}, - }); - } + constructor(private readonly prisma: PrismaService) {} + async invoke() { + return this.prisma.user.findMany({ + where: {}, + }); + } } diff --git a/packages/canyon-backend/src/user/user.controller.ts b/packages/canyon-backend/src/user/user.controller.ts index 026c1477..fdac4bb7 100755 --- a/packages/canyon-backend/src/user/user.controller.ts +++ b/packages/canyon-backend/src/user/user.controller.ts @@ -4,14 +4,14 @@ import { JwtAuthGuard } from "../auth/guards/jwt-auth.guard"; @Controller() export class UserController { - constructor(private readonly prismaService: PrismaService) {} - @UseGuards(JwtAuthGuard) - @Get("/api/user") - getHello(@Request() req): Promise { - return this.prismaService.user.findFirst({ - where: { - id: String(req.user.id), - }, - }); - } + constructor(private readonly prismaService: PrismaService) {} + @UseGuards(JwtAuthGuard) + @Get("/api/user") + getHello(@Request() req): Promise { + return this.prismaService.user.findFirst({ + where: { + id: String(req.user.id), + }, + }); + } } diff --git a/packages/canyon-backend/src/user/user.model.ts b/packages/canyon-backend/src/user/user.model.ts index 61b93de4..5d6eb309 100755 --- a/packages/canyon-backend/src/user/user.model.ts +++ b/packages/canyon-backend/src/user/user.model.ts @@ -2,33 +2,33 @@ import { Field, ID, ObjectType } from "@nestjs/graphql"; @ObjectType() export class User { - @Field(() => ID) - id: string; + @Field(() => ID) + id: string; - // @Field() - // username: string; + // @Field() + // username: string; - @Field() - password: string; + @Field() + password: string; - @Field() - nickname: string; + @Field() + nickname: string; - @Field() - avatar: string; + @Field() + avatar: string; - // @Field() - // refreshToken: string; - // - // @Field() - // accessToken: string; + // @Field() + // refreshToken: string; + // + // @Field() + // accessToken: string; - @Field() - email: string; + @Field() + email: string; - @Field() - favor: string; + @Field() + favor: string; - @Field() - createdAt: Date; + @Field() + createdAt: Date; } diff --git a/packages/canyon-backend/src/user/user.module.ts b/packages/canyon-backend/src/user/user.module.ts index 4b3be401..d806e441 100755 --- a/packages/canyon-backend/src/user/user.module.ts +++ b/packages/canyon-backend/src/user/user.module.ts @@ -6,9 +6,9 @@ import { UserController } from "./user.controller"; import { ListUserService } from "./crud/list-user.service"; @Module({ - imports: [PrismaModule], - controllers: [UserController], - providers: [UserResolver, UserService, ListUserService], - exports: [UserService], + imports: [PrismaModule], + controllers: [UserController], + providers: [UserResolver, UserService, ListUserService], + exports: [UserService], }) export class UserModule {} diff --git a/packages/canyon-backend/src/user/user.resolver.ts b/packages/canyon-backend/src/user/user.resolver.ts index a6a3c527..89754509 100755 --- a/packages/canyon-backend/src/user/user.resolver.ts +++ b/packages/canyon-backend/src/user/user.resolver.ts @@ -9,36 +9,36 @@ import { ListUserService } from "./crud/list-user.service"; @Resolver(() => User) export class UserResolver { - constructor( - private readonly userService: UserService, - private readonly listUserService: ListUserService, - ) {} + constructor( + private readonly userService: UserService, + private readonly listUserService: ListUserService, + ) {} - @Query(() => User, { - description: "提供执行此查询的用户的详细信息(通过授权 Bearer 标头)", - }) - @UseGuards(GqlAuthGuard) - me(@GqlUser() user: User) { - return this.userService.convertDbUserToUser(user); - } + @Query(() => User, { + description: "提供执行此查询的用户的详细信息(通过授权 Bearer 标头)", + }) + @UseGuards(GqlAuthGuard) + me(@GqlUser() user: User) { + return this.userService.convertDbUserToUser(user); + } - @Query(() => [User], { - description: "列出所有用户", - }) - // @UseGuards(GqlAuthGuard) - listUser() { - return this.listUserService.invoke(); - } + @Query(() => [User], { + description: "列出所有用户", + }) + // @UseGuards(GqlAuthGuard) + listUser() { + return this.listUserService.invoke(); + } - @Mutation(() => User, { - description: "关注项目", - }) - @UseGuards(GqlAuthGuard) - favorProject( - @GqlUser() user: User, - @Args("projectID", { type: () => ID }) projectID: string, - @Args("favored", { type: () => Boolean }) favored: boolean, - ) { - return this.userService.favorProject(user, projectID, favored); - } + @Mutation(() => User, { + description: "关注项目", + }) + @UseGuards(GqlAuthGuard) + favorProject( + @GqlUser() user: User, + @Args("projectID", { type: () => ID }) projectID: string, + @Args("favored", { type: () => Boolean }) favored: boolean, + ) { + return this.userService.favorProject(user, projectID, favored); + } } diff --git a/packages/canyon-backend/src/user/user.service.ts b/packages/canyon-backend/src/user/user.service.ts index d3e45143..a7475701 100755 --- a/packages/canyon-backend/src/user/user.service.ts +++ b/packages/canyon-backend/src/user/user.service.ts @@ -4,46 +4,46 @@ import { User } from "./user.model"; import { User as DbUser } from "@prisma/client"; @Injectable() export class UserService { - constructor(private prisma: PrismaService) {} - /** - * 将 prisma 用户对象转换为用户对象 - * - * @param dbUser Prisma User object - * @returns User object - */ - convertDbUserToUser(dbUser: DbUser): Promise { - return this.prisma.user.findFirst({ - where: { - id: String(dbUser.id), - }, - }); - } - async favorProject( - user: User, - projectID: string, - favored: boolean, - ): Promise { - const favorProjects = await this.prisma.user - .findUnique({ - where: { - id: String(user.id), - }, - }) - .then((r) => r.favor.split(",").filter((item) => item !== "")); + constructor(private prisma: PrismaService) {} + /** + * 将 prisma 用户对象转换为用户对象 + * + * @param dbUser Prisma User object + * @returns User object + */ + convertDbUserToUser(dbUser: DbUser): Promise { + return this.prisma.user.findFirst({ + where: { + id: String(dbUser.id), + }, + }); + } + async favorProject( + user: User, + projectID: string, + favored: boolean, + ): Promise { + const favorProjects = await this.prisma.user + .findUnique({ + where: { + id: String(user.id), + }, + }) + .then((r) => r.favor.split(",").filter((item) => item !== "")); - let favors = []; - if (favored) { - favors = favorProjects.concat(projectID); - } else { - favors = favorProjects.filter((item) => item !== projectID); - } - return this.prisma.user.update({ - where: { - id: String(user.id), - }, - data: { - favor: favors.join(","), - }, - }); + let favors = []; + if (favored) { + favors = favorProjects.concat(projectID); + } else { + favors = favorProjects.filter((item) => item !== projectID); } + return this.prisma.user.update({ + where: { + id: String(user.id), + }, + data: { + favor: favors.join(","), + }, + }); + } } diff --git a/packages/canyon-backend/src/utils/coverage.ts b/packages/canyon-backend/src/utils/coverage.ts index e90951e3..c747c3b3 100644 --- a/packages/canyon-backend/src/utils/coverage.ts +++ b/packages/canyon-backend/src/utils/coverage.ts @@ -1,60 +1,60 @@ function parseInstrumentCwd(instrumentCwd) { - if (instrumentCwd.includes("=>")) { - const instrumentCwdSplit = instrumentCwd.split("=>"); - return [instrumentCwdSplit[0], instrumentCwdSplit[1]]; - } else { - return [instrumentCwd, ""]; - } + if (instrumentCwd.includes("=>")) { + const instrumentCwdSplit = instrumentCwd.split("=>"); + return [instrumentCwdSplit[0], instrumentCwdSplit[1]]; + } else { + return [instrumentCwd, ""]; + } } function convertInstrumentCwd({ path, instrumentCwd, projectInstrumentCwd }) { - if (!projectInstrumentCwd) { - return path.replace(instrumentCwd, ""); - } else { - // 这里需要解析一下instrumentCwd,如果包含"=>",则需要替换。 - const [leftInstrumentCwd, rightInstrumentCwd] = - parseInstrumentCwd(projectInstrumentCwd); - return path - .replace(instrumentCwd, "") - .replace(leftInstrumentCwd, rightInstrumentCwd); - } + if (!projectInstrumentCwd) { + return path.replace(instrumentCwd, ""); + } else { + // 这里需要解析一下instrumentCwd,如果包含"=>",则需要替换。 + const [leftInstrumentCwd, rightInstrumentCwd] = + parseInstrumentCwd(projectInstrumentCwd); + return path + .replace(instrumentCwd, "") + .replace(leftInstrumentCwd, rightInstrumentCwd); + } } // 格式化上报的覆盖率对象 export function formatReportObject(c: any) { - // 去除斜杠\\ - const removeSlash = (x: any) => - JSON.parse(JSON.stringify(x).replace(/\\\\/g, "/")); - // 暂时解决方案,需要解决sourceMap问题 - const coverage = removeSlash(c.coverage); - const instrumentCwd = removeSlash(c.instrumentCwd); - const projectInstrumentCwd = removeSlash(c.projectInstrumentCwd || ""); - const reversePath = (p: string) => { - const a = convertInstrumentCwd({ - path: p, - instrumentCwd, - projectInstrumentCwd, - }); - let b = ""; - // 从第二个字符开始 - for (let i = 1; i < a.length; i++) { - b += a[i]; - } - return "" + b; - }; - const obj: any = {}; - for (const coverageKey in coverage) { - // 这里是特殊逻辑,只有他们不想等的时候才是成功替换过的 - if (reversePath(coverageKey) !== coverageKey) { - obj[reversePath(coverageKey)] = { - ...coverage[coverageKey], - path: reversePath(coverageKey), - }; - } + // 去除斜杠\\ + const removeSlash = (x: any) => + JSON.parse(JSON.stringify(x).replace(/\\\\/g, "/")); + // 暂时解决方案,需要解决sourceMap问题 + const coverage = removeSlash(c.coverage); + const instrumentCwd = removeSlash(c.instrumentCwd); + const projectInstrumentCwd = removeSlash(c.projectInstrumentCwd || ""); + const reversePath = (p: string) => { + const a = convertInstrumentCwd({ + path: p, + instrumentCwd, + projectInstrumentCwd, + }); + let b = ""; + // 从第二个字符开始 + for (let i = 1; i < a.length; i++) { + b += a[i]; + } + return "" + b; + }; + const obj: any = {}; + for (const coverageKey in coverage) { + // 这里是特殊逻辑,只有他们不想等的时候才是成功替换过的 + if (reversePath(coverageKey) !== coverageKey) { + obj[reversePath(coverageKey)] = { + ...coverage[coverageKey], + path: reversePath(coverageKey), + }; } + } - // 确保修改成istanbul格式,去掉start、end为空的情况 + // 确保修改成istanbul格式,去掉start、end为空的情况 - return { - coverage: obj, - instrumentCwd, - }; + return { + coverage: obj, + instrumentCwd, + }; } diff --git a/packages/canyon-backend/src/utils/diffline.ts b/packages/canyon-backend/src/utils/diffline.ts index 818209db..b15de9a9 100755 --- a/packages/canyon-backend/src/utils/diffline.ts +++ b/packages/canyon-backend/src/utils/diffline.ts @@ -1,185 +1,182 @@ import * as Diff from "diff"; import { Change } from "diff"; interface DiffLine { - repoID: string; - baseCommitSha?: string; - compareCommitSha: string; - includesFileExtensions?: string[]; - gitlabUrl?: string; - token?: string; + repoID: string; + baseCommitSha?: string; + compareCommitSha: string; + includesFileExtensions?: string[]; + gitlabUrl?: string; + token?: string; } function calculateNewRows( - a: string, - b: string, + a: string, + b: string, ): { additions: number[]; deletions: number[] } { - const diffResult: Change[] = Diff.diffLines(a, b); - function generateArray(startValue: number, length: number) { - return Array.from( - { length }, - (_, index) => startValue - index, - ).reverse(); + const diffResult: Change[] = Diff.diffLines(a, b); + function generateArray(startValue: number, length: number) { + return Array.from({ length }, (_, index) => startValue - index).reverse(); + } + function sumToIndex(arr: number[], index: number) { + return arr.slice(0, index + 1).reduce((sum, value) => sum + value, 0); + } + const additionsDiffResult = diffResult.filter((i) => !i.removed); + const additions: any = []; + additionsDiffResult.forEach((i, index) => { + if (i.added) { + additions.push( + generateArray( + sumToIndex( + additionsDiffResult.map((i) => i.count || 0), + index, + ), + i.count || 0, + ), + ); } - function sumToIndex(arr: number[], index: number) { - return arr.slice(0, index + 1).reduce((sum, value) => sum + value, 0); - } - const additionsDiffResult = diffResult.filter((i) => !i.removed); - const additions: any = []; - additionsDiffResult.forEach((i, index) => { - if (i.added) { - additions.push( - generateArray( - sumToIndex( - additionsDiffResult.map((i) => i.count || 0), - index, - ), - i.count || 0, - ), - ); - } - }); + }); - const deletionsDiffResult = diffResult.filter((i) => i.removed); - const deletions: any = []; - deletionsDiffResult.forEach((i, index) => { - if (i.removed) { - deletions.push( - generateArray( - sumToIndex( - deletionsDiffResult.map((i) => i.count || 0), - index, - ), - i.count || 0, - ), - ); - } - }); - return { - additions: additions.flat(Infinity), - deletions: deletions.flat(Infinity), - }; + const deletionsDiffResult = diffResult.filter((i) => i.removed); + const deletions: any = []; + deletionsDiffResult.forEach((i, index) => { + if (i.removed) { + deletions.push( + generateArray( + sumToIndex( + deletionsDiffResult.map((i) => i.count || 0), + index, + ), + i.count || 0, + ), + ); + } + }); + return { + additions: additions.flat(Infinity), + deletions: deletions.flat(Infinity), + }; } function getDecode(str: string) { - return decodeURIComponent( - atob(str) - .split("") - .map(function (c) { - return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); - }) - .join(""), - ); + return decodeURIComponent( + atob(str) + .split("") + .map(function (c) { + return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); + }) + .join(""), + ); } export async function diffLine({ - repoID, - baseCommitSha = undefined, - compareCommitSha, - includesFileExtensions = ["ts", "tsx", "jsx", "vue", "js"], - gitlabUrl = "https://gitlab.com", - token = "default_token", + repoID, + baseCommitSha = undefined, + compareCommitSha, + includesFileExtensions = ["ts", "tsx", "jsx", "vue", "js"], + gitlabUrl = "https://gitlab.com", + token = "default_token", }: DiffLine): Promise< - { path: string; additions: number[]; deletions: number[] }[] + { path: string; additions: number[]; deletions: number[] }[] > { - const gitlabApiUrlFile = `${gitlabUrl}/api/v4/projects/${repoID}/repository/files`; - const gitlabApiUrlCommit = `${gitlabUrl}/api/v4/projects/${repoID}/repository/commits/${compareCommitSha}`; + const gitlabApiUrlFile = `${gitlabUrl}/api/v4/projects/${repoID}/repository/files`; + const gitlabApiUrlCommit = `${gitlabUrl}/api/v4/projects/${repoID}/repository/commits/${compareCommitSha}`; + + const gitlabApiUrlCommitResponse = await fetch(gitlabApiUrlCommit, { + headers: { + // Authorization: 'Bearer ' + token, // 在请求头中使用 GitLab API token + "private-token": token, + }, + }) + .then((res) => res.json()) + .then((data) => { + return { + parent_ids: data.parent_ids || [], + stats: data.stats, + }; + }); - const gitlabApiUrlCommitResponse = await fetch(gitlabApiUrlCommit, { + const result = []; + // 只关心 50000 行以内的更改 + if ( + gitlabApiUrlCommitResponse.parent_ids.length > 0 && + gitlabApiUrlCommitResponse.stats.additions < 5000000 + ) { + // 声明realBaseCommitSha,如果baseCommitSha存在,则使用baseCommitSha,否则使用gitlabApiUrlCommitResponse.parent_ids[0] + const realBaseCommitSha = + baseCommitSha || gitlabApiUrlCommitResponse.parent_ids[0]; + const gitDiffs = await fetch( + `${gitlabUrl}/api/v4/projects/${repoID}/repository/compare?from=${realBaseCommitSha}&to=${compareCommitSha}`, + { headers: { - // Authorization: 'Bearer ' + token, // 在请求头中使用 GitLab API token - "private-token": token, + // Authorization: 'Bearer ' + token, // 在请求头中使用 GitLab API token + "private-token": token, }, - }) - .then((res) => res.json()) - .then((data) => { + }, + ) + .then((res) => res.json()) + .then((response) => { + return (response.diffs || []).map( + ({ + old_path, + new_path, + a_mode, + b_mode, + new_file, + renamed_file, + deleted_file, + }) => { return { - parent_ids: data.parent_ids || [], - stats: data.stats, + old_path, + new_path, + a_mode, + b_mode, + new_file, + renamed_file, + deleted_file, }; - }); - - const result = []; - // 只关心 50000 行以内的更改 - if ( - gitlabApiUrlCommitResponse.parent_ids.length > 0 && - gitlabApiUrlCommitResponse.stats.additions < 5000000 - ) { - // 声明realBaseCommitSha,如果baseCommitSha存在,则使用baseCommitSha,否则使用gitlabApiUrlCommitResponse.parent_ids[0] - const realBaseCommitSha = - baseCommitSha || gitlabApiUrlCommitResponse.parent_ids[0]; - const gitDiffs = await fetch( - `${gitlabUrl}/api/v4/projects/${repoID}/repository/compare?from=${realBaseCommitSha}&to=${compareCommitSha}`, - { - headers: { - // Authorization: 'Bearer ' + token, // 在请求头中使用 GitLab API token - "private-token": token, - }, - }, - ) - .then((res) => res.json()) - .then((response) => { - return (response.diffs || []).map( - ({ - old_path, - new_path, - a_mode, - b_mode, - new_file, - renamed_file, - deleted_file, - }) => { - return { - old_path, - new_path, - a_mode, - b_mode, - new_file, - renamed_file, - deleted_file, - }; - }, - ); - }); + }, + ); + }); - // const includesFileExtensions = /\.tsx?$|\.jsx?$|\.vue$|\.js$/i; + // const includesFileExtensions = /\.tsx?$|\.jsx?$|\.vue$|\.js$/i; - const isMatchingExtension = ( - includesFileExtensions: string[], - pathname: string, - ) => includesFileExtensions.some((ext) => pathname.endsWith("." + ext)); + const isMatchingExtension = ( + includesFileExtensions: string[], + pathname: string, + ) => includesFileExtensions.some((ext) => pathname.endsWith("." + ext)); - const gitDiffsFiltered = gitDiffs.filter((gitDiff) => - isMatchingExtension(includesFileExtensions, gitDiff.new_path), - ); + const gitDiffsFiltered = gitDiffs.filter((gitDiff) => + isMatchingExtension(includesFileExtensions, gitDiff.new_path), + ); - for (let i = 0; i < gitDiffsFiltered.length; i++) { - const contents = await Promise.all( - [realBaseCommitSha, compareCommitSha].map((c) => { - return fetch( - `${gitlabApiUrlFile}/${encodeURIComponent( - gitDiffsFiltered[i].new_path, - )}?ref=${c}`, - { - headers: { - // Authorization: 'Bearer ' + token, // 在请求头中使用 GitLab API token - "private-token": token, - }, - method: "GET", - }, - ) - .then((res) => res.json()) - .then((r) => { - return getDecode(r.content); - }) - .catch(() => { - return ""; - }); - }), - ); - result.push({ - path: gitDiffsFiltered[i].new_path, - ...calculateNewRows(contents[0], contents[1]), + for (let i = 0; i < gitDiffsFiltered.length; i++) { + const contents = await Promise.all( + [realBaseCommitSha, compareCommitSha].map((c) => { + return fetch( + `${gitlabApiUrlFile}/${encodeURIComponent( + gitDiffsFiltered[i].new_path, + )}?ref=${c}`, + { + headers: { + // Authorization: 'Bearer ' + token, // 在请求头中使用 GitLab API token + "private-token": token, + }, + method: "GET", + }, + ) + .then((res) => res.json()) + .then((r) => { + return getDecode(r.content); + }) + .catch(() => { + return ""; }); - } + }), + ); + result.push({ + path: gitDiffsFiltered[i].new_path, + ...calculateNewRows(contents[0], contents[1]), + }); } - return result; + } + return result; } diff --git a/packages/canyon-backend/src/utils/sys.ts b/packages/canyon-backend/src/utils/sys.ts index 12b71119..dc0294d3 100644 --- a/packages/canyon-backend/src/utils/sys.ts +++ b/packages/canyon-backend/src/utils/sys.ts @@ -1,27 +1,27 @@ const obj = { - GITLAB_CLIENT_ID: "gitlabClientID", - GITLAB_CLIENT_SECRET: "gitlabClientSecret", - GITLAB_SERVER: "gitlabServer", - DOCS_LINK: "docsLink", + GITLAB_CLIENT_ID: "gitlabClientID", + GITLAB_CLIENT_SECRET: "gitlabClientSecret", + GITLAB_SERVER: "gitlabServer", + DOCS_LINK: "docsLink", }; export const convertSystemSettingsFromTheDatabase = ( - settings, + settings, ): { - gitlabClientSecret: string; - gitlabClientID: string; - gitlabServer: string; - docsLink: string; - canyonServer: string; + gitlabClientSecret: string; + gitlabClientID: string; + gitlabServer: string; + docsLink: string; + canyonServer: string; } => { - const map = { - gitlabClientSecret: "", - gitlabClientID: "", - gitlabServer: "", - docsLink: "", - canyonServer: "", - }; - settings.forEach((setting) => { - map[obj[setting.key]] = setting.value; - }); - return map; + const map = { + gitlabClientSecret: "", + gitlabClientID: "", + gitlabServer: "", + docsLink: "", + canyonServer: "", + }; + settings.forEach((setting) => { + map[obj[setting.key]] = setting.value; + }); + return map; }; diff --git a/packages/canyon-backend/src/utils/utils.ts b/packages/canyon-backend/src/utils/utils.ts index 3aa4e95b..a2ca0024 100755 --- a/packages/canyon-backend/src/utils/utils.ts +++ b/packages/canyon-backend/src/utils/utils.ts @@ -1,27 +1,27 @@ import * as dayjs from "dayjs"; export const within30days = (time) => { - // 获取当前日期 - const currentDate = dayjs(); - // 获取item的更新日期 - const updatedAt = dayjs(time); - // 计算当前日期和更新日期之间的天数差 - const differenceInDays = currentDate.diff(updatedAt, "day"); - // 判断是否在30天以内 - return differenceInDays <= 30; + // 获取当前日期 + const currentDate = dayjs(); + // 获取item的更新日期 + const updatedAt = dayjs(time); + // 计算当前日期和更新日期之间的天数差 + const differenceInDays = currentDate.diff(updatedAt, "day"); + // 判断是否在30天以内 + return differenceInDays <= 30; }; export const summaryToDbSummary = (summary) => { - return { - statementsCovered: summary.statements.covered, - statementsTotal: summary.statements.total, - branchesCovered: summary.branches.covered, - branchesTotal: summary.branches.total, - functionsCovered: summary.functions.covered, - functionsTotal: summary.functions.total, - linesCovered: summary.lines.covered, - linesTotal: summary.lines.total, - newlinesCovered: summary.newlines.covered, - newlinesTotal: summary.newlines.total, - }; + return { + statementsCovered: summary.statements.covered, + statementsTotal: summary.statements.total, + branchesCovered: summary.branches.covered, + branchesTotal: summary.branches.total, + functionsCovered: summary.functions.covered, + functionsTotal: summary.functions.total, + linesCovered: summary.lines.covered, + linesTotal: summary.lines.total, + newlinesCovered: summary.newlines.covered, + newlinesTotal: summary.newlines.total, + }; }; diff --git a/packages/canyon-backend/src/zod/istanbul.zod.ts b/packages/canyon-backend/src/zod/istanbul.zod.ts index f0d4b0fe..b8531bc9 100644 --- a/packages/canyon-backend/src/zod/istanbul.zod.ts +++ b/packages/canyon-backend/src/zod/istanbul.zod.ts @@ -1,14 +1,14 @@ import { z } from "zod"; const Range = z.object({ - start: z.object({ line: z.number(), column: z.number() }), - end: z.object({ line: z.number(), column: z.number() }), + start: z.object({ line: z.number(), column: z.number() }), + end: z.object({ line: z.number(), column: z.number() }), }); const IstanbulHitSchema = z.object({ - s: z.unknown(), - f: z.unknown(), - b: z.unknown(), + s: z.unknown(), + f: z.unknown(), + b: z.unknown(), }); // map类型的key是文件路径,value是IstanbulDataSchema @@ -17,10 +17,10 @@ export const IstanbulHitMapSchema = z.record(IstanbulHitSchema); export type IstanbulHitMapType = z.infer; const IstanbulMapSchema = z.object({ - statementMap: z.unknown(), - fnMap: z.unknown(), - branchMap: z.unknown(), - inputSourceMap: z.unknown(), + statementMap: z.unknown(), + fnMap: z.unknown(), + branchMap: z.unknown(), + inputSourceMap: z.unknown(), }); // map类型的key是文件路径,value是IstanbulDataSchema diff --git a/packages/canyon-platform/codegen.ts b/packages/canyon-platform/codegen.ts index 74aa413d..5f9e59a2 100755 --- a/packages/canyon-platform/codegen.ts +++ b/packages/canyon-platform/codegen.ts @@ -1,16 +1,16 @@ import { type CodegenConfig } from "@graphql-codegen/cli"; const config: CodegenConfig = { - schema: "../../packages/canyon-backend/*.gql", - documents: ["src/**/*.graphql"], - generates: { - "./src/helpers/backend/gen/": { - preset: "client", - presetConfig: { - persistedDocuments: "string", - }, - }, + schema: "../../packages/canyon-backend/*.gql", + documents: ["src/**/*.graphql"], + generates: { + "./src/helpers/backend/gen/": { + preset: "client", + presetConfig: { + persistedDocuments: "string", + }, }, + }, }; export default config; diff --git a/packages/canyon-platform/eslint.config.js b/packages/canyon-platform/eslint.config.js index 0a8821a4..28426aa9 100644 --- a/packages/canyon-platform/eslint.config.js +++ b/packages/canyon-platform/eslint.config.js @@ -6,28 +6,28 @@ import tseslint from "typescript-eslint"; import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; export default tseslint.config( - { ignores: ["dist"] }, - { - extends: [ - js.configs.recommended, - ...tseslint.configs.recommended, - eslintPluginPrettierRecommended, - ], - files: ["**/*.{ts,tsx}"], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - plugins: { - "react-hooks": reactHooks, - "react-refresh": reactRefresh, - }, - rules: { - ...reactHooks.configs.recommended.rules, - "react-refresh/only-export-components": [ - "warn", - { allowConstantExport: true }, - ], - }, + { ignores: ["dist"] }, + { + extends: [ + js.configs.recommended, + ...tseslint.configs.recommended, + eslintPluginPrettierRecommended, + ], + files: ["**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, + }, ); diff --git a/packages/canyon-platform/src/App.tsx b/packages/canyon-platform/src/App.tsx index 1d6b08fb..7247f890 100644 --- a/packages/canyon-platform/src/App.tsx +++ b/packages/canyon-platform/src/App.tsx @@ -7,39 +7,39 @@ import routes from "~react-pages"; import Sha from "@/pages/index/projects/[id]/commits/[sha].tsx"; routes.push({ - element: , - path: "/open-projects/:id/commits/:sha", + element: , + path: "/open-projects/:id/commits/:sha", }); const languages = { - cn: zhCN, - en: enUS, - ja: jaJP, + cn: zhCN, + en: enUS, + ja: jaJP, }; const lng = (localStorage.getItem("language") || - "cn") as keyof typeof languages; + "cn") as keyof typeof languages; const { darkAlgorithm } = theme; const App = () => { - const isDark = localStorage.getItem("theme") - ? localStorage.getItem("theme") === "dark" - : false; - return ( -
- - {useRoutes(routes)} - -
- ); + const isDark = localStorage.getItem("theme") + ? localStorage.getItem("theme") === "dark" + : false; + return ( +
+ + {useRoutes(routes)} + +
+ ); }; export default App; diff --git a/packages/canyon-platform/src/assets/icons/EpTopRight.tsx b/packages/canyon-platform/src/assets/icons/EpTopRight.tsx index 578c845d..413d1e2b 100644 --- a/packages/canyon-platform/src/assets/icons/EpTopRight.tsx +++ b/packages/canyon-platform/src/assets/icons/EpTopRight.tsx @@ -2,22 +2,22 @@ import type { SVGProps } from "react"; export function EpTopRight(props: SVGProps) { - return ( - - - - - ); + return ( + + + + + ); } diff --git a/packages/canyon-platform/src/assets/users-icon.tsx b/packages/canyon-platform/src/assets/users-icon.tsx index 4c6955ac..bf5c8566 100644 --- a/packages/canyon-platform/src/assets/users-icon.tsx +++ b/packages/canyon-platform/src/assets/users-icon.tsx @@ -2,18 +2,18 @@ import type { SVGProps } from "react"; export default function UilUsersAlt(props: SVGProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/packages/canyon-platform/src/auto-imports.d.ts b/packages/canyon-platform/src/auto-imports.d.ts index 13f28c11..407ab47b 100644 --- a/packages/canyon-platform/src/auto-imports.d.ts +++ b/packages/canyon-platform/src/auto-imports.d.ts @@ -6,79 +6,79 @@ // biome-ignore lint: disable export {} declare global { - const Alert: typeof import('antd')['Alert'] - const Avatar: typeof import('antd')['Avatar'] - const Breadcrumb: typeof import('antd')['Breadcrumb'] - const Button: typeof import('antd')['Button'] - const Card: typeof import('antd')['Card'] - const ColorPicker: typeof import('antd')['ColorPicker'] - const ConfigProvider: typeof import('antd')['ConfigProvider'] - const DatePicker: typeof import('antd')['DatePicker'] - const Divider: typeof import('antd')['Divider'] - const Drawer: typeof import('antd')['Drawer'] - const Dropdown: typeof import('antd')['Dropdown'] - const Flex: (typeof import("antd"))["Flex"] - const FloatButton: typeof import('antd')['FloatButton'] - const Form: typeof import('antd')['Form'] - const Input: typeof import('antd')['Input'] - const Link: typeof import('react-router-dom')['Link'] - const Menu: typeof import('antd')['Menu'] - const Modal: typeof import('antd')['Modal'] - const NavLink: typeof import('react-router-dom')['NavLink'] - const Navigate: typeof import('react-router-dom')['Navigate'] - const Outlet: typeof import('react-router-dom')['Outlet'] - const Popconfirm: typeof import('antd')['Popconfirm'] - const Popover: (typeof import("antd"))["Popover"] - const Progress: typeof import('antd')['Progress'] - const Radio: typeof import('antd')['Radio'] - const Result: (typeof import("antd"))["Result"] - const Route: typeof import('react-router-dom')['Route'] - const Routes: typeof import('react-router-dom')['Routes'] - const Segmented: typeof import('antd')['Segmented'] - const Select: typeof import('antd')['Select'] - const Slider: typeof import('antd')['Slider'] - const Space: typeof import('antd')['Space'] - const Spin: typeof import('antd')['Spin'] - const Switch: typeof import('antd')['Switch'] - const Table: typeof import('antd')['Table'] - const Tabs: typeof import('antd')['Tabs'] - const Tag: typeof import('antd')['Tag'] - const Tooltip: typeof import('antd')['Tooltip'] - const Tour: typeof import('antd')['Tour'] - const Typography: typeof import('antd')['Typography'] - const createRef: typeof import('react')['createRef'] - const forwardRef: typeof import('react')['forwardRef'] - const lazy: typeof import('react')['lazy'] - const memo: typeof import('react')['memo'] - const message: typeof import('antd')['message'] - const startTransition: typeof import('react')['startTransition'] - const theme: typeof import('antd')['theme'] - const useCallback: typeof import('react')['useCallback'] - const useContext: typeof import('react')['useContext'] - const useDebugValue: typeof import('react')['useDebugValue'] - const useDeferredValue: typeof import('react')['useDeferredValue'] - const useEffect: typeof import('react')['useEffect'] - const useHref: typeof import('react-router-dom')['useHref'] - const useId: typeof import('react')['useId'] - const useImperativeHandle: typeof import('react')['useImperativeHandle'] - const useInRouterContext: typeof import('react-router-dom')['useInRouterContext'] - const useInsertionEffect: typeof import('react')['useInsertionEffect'] - const useLayoutEffect: typeof import('react')['useLayoutEffect'] - const useLinkClickHandler: typeof import('react-router-dom')['useLinkClickHandler'] - const useLocation: typeof import('react-router-dom')['useLocation'] - const useMemo: typeof import('react')['useMemo'] - const useNavigate: typeof import('react-router-dom')['useNavigate'] - const useNavigationType: typeof import('react-router-dom')['useNavigationType'] - const useOutlet: typeof import('react-router-dom')['useOutlet'] - const useOutletContext: typeof import('react-router-dom')['useOutletContext'] - const useParams: typeof import('react-router-dom')['useParams'] - const useReducer: typeof import('react')['useReducer'] - const useRef: typeof import('react')['useRef'] - const useResolvedPath: typeof import('react-router-dom')['useResolvedPath'] - const useRoutes: typeof import('react-router-dom')['useRoutes'] - const useSearchParams: typeof import('react-router-dom')['useSearchParams'] - const useState: typeof import('react')['useState'] - const useSyncExternalStore: typeof import('react')['useSyncExternalStore'] - const useTransition: typeof import('react')['useTransition'] - const useTranslation: typeof import('react-i18next')['useTranslation'] + const Alert: (typeof import("antd"))["Alert"]; + const Avatar: (typeof import("antd"))["Avatar"]; + const Breadcrumb: (typeof import("antd"))["Breadcrumb"]; + const Button: (typeof import("antd"))["Button"]; + const Card: (typeof import("antd"))["Card"]; + const ColorPicker: (typeof import("antd"))["ColorPicker"]; + const ConfigProvider: (typeof import("antd"))["ConfigProvider"]; + const DatePicker: (typeof import("antd"))["DatePicker"]; + const Divider: (typeof import("antd"))["Divider"]; + const Drawer: (typeof import("antd"))["Drawer"]; + const Dropdown: (typeof import("antd"))["Dropdown"]; + const Flex: (typeof import("antd"))["Flex"]; + const FloatButton: (typeof import("antd"))["FloatButton"]; + const Form: (typeof import("antd"))["Form"]; + const Input: (typeof import("antd"))["Input"]; + const Link: (typeof import("react-router-dom"))["Link"]; + const Menu: (typeof import("antd"))["Menu"]; + const Modal: (typeof import("antd"))["Modal"]; + const NavLink: (typeof import("react-router-dom"))["NavLink"]; + const Navigate: (typeof import("react-router-dom"))["Navigate"]; + const Outlet: (typeof import("react-router-dom"))["Outlet"]; + const Popconfirm: (typeof import("antd"))["Popconfirm"]; + const Popover: (typeof import("antd"))["Popover"]; + const Progress: (typeof import("antd"))["Progress"]; + const Radio: (typeof import("antd"))["Radio"]; + const Result: (typeof import("antd"))["Result"]; + const Route: (typeof import("react-router-dom"))["Route"]; + const Routes: (typeof import("react-router-dom"))["Routes"]; + const Segmented: (typeof import("antd"))["Segmented"]; + const Select: (typeof import("antd"))["Select"]; + const Slider: (typeof import("antd"))["Slider"]; + const Space: (typeof import("antd"))["Space"]; + const Spin: (typeof import("antd"))["Spin"]; + const Switch: (typeof import("antd"))["Switch"]; + const Table: (typeof import("antd"))["Table"]; + const Tabs: (typeof import("antd"))["Tabs"]; + const Tag: (typeof import("antd"))["Tag"]; + const Tooltip: (typeof import("antd"))["Tooltip"]; + const Tour: (typeof import("antd"))["Tour"]; + const Typography: (typeof import("antd"))["Typography"]; + const createRef: (typeof import("react"))["createRef"]; + const forwardRef: (typeof import("react"))["forwardRef"]; + const lazy: (typeof import("react"))["lazy"]; + const memo: (typeof import("react"))["memo"]; + const message: (typeof import("antd"))["message"]; + const startTransition: (typeof import("react"))["startTransition"]; + const theme: (typeof import("antd"))["theme"]; + const useCallback: (typeof import("react"))["useCallback"]; + const useContext: (typeof import("react"))["useContext"]; + const useDebugValue: (typeof import("react"))["useDebugValue"]; + const useDeferredValue: (typeof import("react"))["useDeferredValue"]; + const useEffect: (typeof import("react"))["useEffect"]; + const useHref: (typeof import("react-router-dom"))["useHref"]; + const useId: (typeof import("react"))["useId"]; + const useImperativeHandle: (typeof import("react"))["useImperativeHandle"]; + const useInRouterContext: (typeof import("react-router-dom"))["useInRouterContext"]; + const useInsertionEffect: (typeof import("react"))["useInsertionEffect"]; + const useLayoutEffect: (typeof import("react"))["useLayoutEffect"]; + const useLinkClickHandler: (typeof import("react-router-dom"))["useLinkClickHandler"]; + const useLocation: (typeof import("react-router-dom"))["useLocation"]; + const useMemo: (typeof import("react"))["useMemo"]; + const useNavigate: (typeof import("react-router-dom"))["useNavigate"]; + const useNavigationType: (typeof import("react-router-dom"))["useNavigationType"]; + const useOutlet: (typeof import("react-router-dom"))["useOutlet"]; + const useOutletContext: (typeof import("react-router-dom"))["useOutletContext"]; + const useParams: (typeof import("react-router-dom"))["useParams"]; + const useReducer: (typeof import("react"))["useReducer"]; + const useRef: (typeof import("react"))["useRef"]; + const useResolvedPath: (typeof import("react-router-dom"))["useResolvedPath"]; + const useRoutes: (typeof import("react-router-dom"))["useRoutes"]; + const useSearchParams: (typeof import("react-router-dom"))["useSearchParams"]; + const useState: (typeof import("react"))["useState"]; + const useSyncExternalStore: (typeof import("react"))["useSyncExternalStore"]; + const useTransition: (typeof import("react"))["useTransition"]; + const useTranslation: (typeof import("react-i18next"))["useTranslation"]; } diff --git a/packages/canyon-platform/src/components/CanyonReport/Control.tsx b/packages/canyon-platform/src/components/CanyonReport/Control.tsx index 67722180..62fa14a5 100644 --- a/packages/canyon-platform/src/components/CanyonReport/Control.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/Control.tsx @@ -5,157 +5,149 @@ import { Divider, SliderSingleProps } from "antd"; import { getCOlor } from "@/helpers/utils/common.ts"; const marks: SliderSingleProps["marks"] = { - 50: { - style: { - fontSize: "10px", - }, - label: <>50, + 50: { + style: { + fontSize: "10px", }, - 80: { - style: { - fontSize: "10px", - }, - label: <>80, + label: <>50, + }, + 80: { + style: { + fontSize: "10px", }, + label: <>80, + }, }; function returnPositiveNumbers(num) { - if (num < 0) { - return 0; - } else { - return num; - } + if (num < 0) { + return 0; + } else { + return num; + } } function genBackground(range) { - const a = range[0]; - const b = range[1]; - return `linear-gradient(to right, ${getCOlor(0)} 0%, ${getCOlor(0)} ${(returnPositiveNumbers(50 - a) * 100) / (b - a)}%, ${getCOlor(60)} ${(returnPositiveNumbers(50 - a) * 100) / (b - a)}%, ${getCOlor(60)} ${(returnPositiveNumbers(80 - a) * 100) / (b - a)}%, ${getCOlor(90)} ${((80 - a) * 100) / (b - a)}%, ${getCOlor(90)} 100%)`; + const a = range[0]; + const b = range[1]; + return `linear-gradient(to right, ${getCOlor(0)} 0%, ${getCOlor(0)} ${(returnPositiveNumbers(50 - a) * 100) / (b - a)}%, ${getCOlor(60)} ${(returnPositiveNumbers(50 - a) * 100) / (b - a)}%, ${getCOlor(60)} ${(returnPositiveNumbers(80 - a) * 100) / (b - a)}%, ${getCOlor(90)} ${((80 - a) * 100) / (b - a)}%, ${getCOlor(90)} 100%)`; } const CanyonReportControl = ({ - numberFiles, - onChangeOnlyChange, - onChangeOnlyChangeKeywords, - keywords, - onlyChange, - onChangeShowMode, - onChangeRange, - showMode, - range, + numberFiles, + onChangeOnlyChange, + onChangeOnlyChangeKeywords, + keywords, + onlyChange, + onChangeShowMode, + onChangeRange, + showMode, + range, }) => { - const { t } = useTranslation(); - // TODO 暂时不能删除prm - const prm = useParams(); - // const [range, setRange] = useState([0, 100]); - return ( - <> -
-
-
- { - onChangeShowMode(v); - }} - options={[ - { - label: t("projects.detail.code.tree"), - value: "tree", - icon: , - }, - { - label: t("projects.detail.file.list"), - value: "list", - icon: , - }, - ]} - /> + const { t } = useTranslation(); + // TODO 暂时不能删除prm + const prm = useParams(); + // const [range, setRange] = useState([0, 100]); + return ( + <> +
+
+
+ { + onChangeShowMode(v); + }} + options={[ + { + label: t("projects.detail.code.tree"), + value: "tree", + icon: , + }, + { + label: t("projects.detail.file.list"), + value: "list", + icon: , + }, + ]} + /> - - {t("projects.detail.total.files", { - msg: numberFiles, - })} - {/*覆盖率提升优先级列表*/} - {/*转换生产流量为测试用例*/} - - -
-
+ + {t("projects.detail.total.files", { + msg: numberFiles, + })} + {/*覆盖率提升优先级列表*/} + {/*转换生产流量为测试用例*/} + + +
+
-
+
+
+ + 未覆盖维度: + + +
- -
- - 未覆盖维度: - - -
- - -
- - {t("projects.detail.only.changed")}:{" "} - - -
- -
- - 范围: - -
- { - onChangeRange(va); - }} - styles={{ - track: { - background: "transparent", - }, - tracks: { - background: genBackground(range), - }, - }} - /> -
-
- - } - placeholder={t("projects.detail.search.placeholder")} - className={"w-[240px]"} - size={"small"} - onChange={onChangeOnlyChangeKeywords} - /> -
+ +
+ + {t("projects.detail.only.changed")}:{" "} + + +
+ +
+ + 范围: + +
+ { + onChangeRange(va); + }} + styles={{ + track: { + background: "transparent", + }, + tracks: { + background: genBackground(range), + }, + }} + />
- - ); +
+ + } + placeholder={t("projects.detail.search.placeholder")} + className={"w-[240px]"} + size={"small"} + onChange={onChangeOnlyChangeKeywords} + /> +
+
+ + ); }; export default CanyonReportControl; diff --git a/packages/canyon-platform/src/components/CanyonReport/CoverageDetail.tsx b/packages/canyon-platform/src/components/CanyonReport/CoverageDetail.tsx index cc29b55c..3451f6f0 100644 --- a/packages/canyon-platform/src/components/CanyonReport/CoverageDetail.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/CoverageDetail.tsx @@ -5,53 +5,53 @@ import LineNumber from "./line/number.tsx"; import ShikiDetail from "./ShikiDetail.tsx"; const CanyonReportCoverageDetail = ({ data, theme }: any) => { - const code = data.sourcecode; - const { lines } = coreFn(data.coverage, code); - return ( - <> -
- - - { - if (i.executionNumber > 0) { - return { - covered: "yes", - hits: i.executionNumber, - }; - } else if (i.executionNumber === 0) { - return { - covered: "no", - hits: i.executionNumber, - }; - } else { - return { - covered: "neutral", - hits: 0, - }; - } - })} - /> - -
- - ); + const code = data.sourcecode; + const { lines } = coreFn(data.coverage, code); + return ( + <> +
+ + + { + if (i.executionNumber > 0) { + return { + covered: "yes", + hits: i.executionNumber, + }; + } else if (i.executionNumber === 0) { + return { + covered: "no", + hits: i.executionNumber, + }; + } else { + return { + covered: "neutral", + hits: 0, + }; + } + })} + /> + +
+ + ); }; export default CanyonReportCoverageDetail; diff --git a/packages/canyon-platform/src/components/CanyonReport/ListTable.tsx b/packages/canyon-platform/src/components/CanyonReport/ListTable.tsx index 691fe5ca..3c60aca5 100644 --- a/packages/canyon-platform/src/components/CanyonReport/ListTable.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/ListTable.tsx @@ -1,156 +1,148 @@ import Highlighter from "react-highlight-words"; -import {percent} from 'canyon-data' +import { percent } from "canyon-data"; import { getCOlor } from "@/helpers/utils/common.ts"; const CanyonReportListTable = ({ - dataSource, - loading, - keywords, - onSelect, - onlyChange, + dataSource, + loading, + keywords, + onSelect, + onlyChange, }) => { - const { t } = useTranslation(); - const newlinesColumns = onlyChange - ? [ - { - title: t("projects.newlines"), - width: "240px", - sorter: (a, b) => { - return ( - percent(a.newlines.covered, a.newlines.total) - - percent(b.newlines.covered, b.newlines.total) - ); - }, - // key: 'total', - dataIndex: ["newlines", "total"], - render(text, record) { - return ( - - - - ({record.newlines.covered}/ - {record.newlines.total}) - - - ); - }, + const { t } = useTranslation(); + const newlinesColumns = onlyChange + ? [ + { + title: t("projects.newlines"), + width: "240px", + sorter: (a, b) => { + return ( + percent(a.newlines.covered, a.newlines.total) - + percent(b.newlines.covered, b.newlines.total) + ); + }, + // key: 'total', + dataIndex: ["newlines", "total"], + render(text, record) { + return ( + + + + ({record.newlines.covered}/{record.newlines.total}) + + + ); + }, + }, + // { + // title: 'covered', + // key: 'covered', + // dataIndex: ['summary', 'newlines', 'covered'], + // }, + ] + : []; + return ( +
+ + {" "} + { + onSelect({ + path: text, + }); + }} + > + + + ); + }, + }, + { + title: t("common.total"), + key: "total", + dataIndex: ["statements", "total"], + sorter(a, b) { + return a.statements.total - b.statements.total; + }, + }, + { + title: t("common.covered"), + key: "covered", + dataIndex: ["statements", "covered"], + sorter(a, b) { + return a.statements.covered - b.statements.covered; }, - // { - // title: 'covered', - // key: 'covered', - // dataIndex: ['summary', 'newlines', 'covered'], - // }, + }, ] - : []; - return ( -
- - {" "} -
{ - onSelect({ - path: text, - }); - }} - > - - - ); - }, - }, - { - title: t("common.total"), - key: "total", - dataIndex: ["statements", "total"], - sorter(a, b) { - return a.statements.total - b.statements.total; - }, - }, - { - title: t("common.covered"), - key: "covered", - dataIndex: ["statements", "covered"], - sorter(a, b) { - return ( - a.statements.covered - b.statements.covered - ); - }, - }, - ] - .concat(newlinesColumns) - .concat([ - { - title: t("projects.config.coverage") + " %", - width: "300px", - key: "c", - sorter: (a, b) => { - return a.statements.pct - b.statements.pct; - }, - dataIndex: ["statements", "pct"], - render(text) { - return ( - - ); - }, - }, - ])} - /> - - - ); + .concat(newlinesColumns) + .concat([ + { + title: t("projects.config.coverage") + " %", + width: "300px", + key: "c", + sorter: (a, b) => { + return a.statements.pct - b.statements.pct; + }, + dataIndex: ["statements", "pct"], + render(text) { + return ( + + ); + }, + }, + ])} + /> + + + ); }; export default CanyonReportListTable; diff --git a/packages/canyon-platform/src/components/CanyonReport/Overview.tsx b/packages/canyon-platform/src/components/CanyonReport/Overview.tsx index f7550c88..afe4301f 100644 --- a/packages/canyon-platform/src/components/CanyonReport/Overview.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/Overview.tsx @@ -6,128 +6,114 @@ import { useSearchParams } from "react-router-dom"; import { getCOlor } from "../../helpers/utils/common.ts"; import { capitalizeFirstLetter } from "./helper.tsx"; const obj = { - statements: 0, - branches: 1, - functions: 2, - lines: 3, - newlines: 4, + statements: 0, + branches: 1, + functions: 2, + lines: 3, + newlines: 4, }; const { Text } = Typography; const CanyonReportOverview = ({ - activatedPath, - pathWithNamespace, - onSelect, - summaryTreeItem, + activatedPath, + pathWithNamespace, + onSelect, + summaryTreeItem, }) => { - const [sprm] = useSearchParams(); - const { t } = useTranslation(); - return ( -
- {/*{JSON.stringify(summaryTreeItem.summary)}*/} -
- { - onSelect({ path: "" }); - }} - > - {pathWithNamespace} - - {/* / */} - {activatedPath?.split("/").map((i, index, arr) => { - return ( - <> - {activatedPath !== "" ? / : null} - { - const newpath = arr - .filter( - (i, index3) => index3 < index + 1, - ) - .reduce((c, p, index) => { - return ( - c + (index === 0 ? "" : "/") + p - ); - }, ""); - onSelect({ path: newpath }); - }} - > - {i.replace("~", pathWithNamespace)} - - - ); - })} - - { - message.success("复制路径成功"); - }} - > - - - - + const [sprm] = useSearchParams(); + const { t } = useTranslation(); + return ( +
+ {/*{JSON.stringify(summaryTreeItem.summary)}*/} +
+ { + onSelect({ path: "" }); + }} + > + {pathWithNamespace} + + {/* / */} + {activatedPath?.split("/").map((i, index, arr) => { + return ( + <> + {activatedPath !== "" ? / : null} + { + const newpath = arr + .filter((i, index3) => index3 < index + 1) + .reduce((c, p, index) => { + return c + (index === 0 ? "" : "/") + p; + }, ""); + onSelect({ path: newpath }); + }} + > + {i.replace("~", pathWithNamespace)} + + + ); + })} + + { + message.success("复制路径成功"); + }} + > + + + + - { - message.success("复制分享链接成功"); - }} - > - - - - -
+ { + message.success("复制分享链接成功"); + }} + > + + + + +
-
- {Object.entries(summaryTreeItem.summary) - .sort((a, b) => { - return obj[a[0]] - obj[b[0]]; - }) - .map(([key, value]) => { - return ( -
- - {value.pct}% - - - {t("projects." + key)}: - - - {value.covered}/{value.total} - -
- ); - })} -
-
-
- ); +
+ {Object.entries(summaryTreeItem.summary) + .sort((a, b) => { + return obj[a[0]] - obj[b[0]]; + }) + .map(([key, value]) => { + return ( +
+ + {value.pct}% + + + {t("projects." + key)}: + + + {value.covered}/{value.total} + +
+ ); + })} +
+
+
+ ); }; export default CanyonReportOverview; diff --git a/packages/canyon-platform/src/components/CanyonReport/PhTreeView.tsx b/packages/canyon-platform/src/components/CanyonReport/PhTreeView.tsx index 46e9fa17..c3558dbb 100644 --- a/packages/canyon-platform/src/components/CanyonReport/PhTreeView.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/PhTreeView.tsx @@ -1,18 +1,18 @@ import type { SVGProps } from "react"; export default function PhTreeView(props: SVGProps) { - return ( - - - - ); + return ( + + + + ); } diff --git a/packages/canyon-platform/src/components/CanyonReport/PrepareProdFn.tsx b/packages/canyon-platform/src/components/CanyonReport/PrepareProdFn.tsx index d2e8f1c1..545e6168 100644 --- a/packages/canyon-platform/src/components/CanyonReport/PrepareProdFn.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/PrepareProdFn.tsx @@ -5,105 +5,102 @@ import { useRequest } from "ahooks"; import axios from "axios"; const PrepareProdFn: React.FC = () => { - const [open, setOpen] = useState(false); - const prm = useParams(); - const [spams] = useSearchParams(); - const { data, loading, run } = useRequest( - () => - axios - .post( - atob( - `aHR0cHM6Ly90cmlwY2FueW9uLmZ3cy5xYS5udC5jdHJpcGNvcnAuY29t`, - ) + "/api/coverage/prepareProdFn", - { - projectID: prm.id, - sha: prm.sha, - username: localStorage.getItem("username"), - path: spams.get("path"), - }, - ) - .then(({ data }) => data), - { - manual: true, - }, - ); + const [open, setOpen] = useState(false); + const prm = useParams(); + const [spams] = useSearchParams(); + const { data, loading, run } = useRequest( + () => + axios + .post( + atob(`aHR0cHM6Ly90cmlwY2FueW9uLmZ3cy5xYS5udC5jdHJpcGNvcnAuY29t`) + + "/api/coverage/prepareProdFn", + { + projectID: prm.id, + sha: prm.sha, + username: localStorage.getItem("username"), + path: spams.get("path"), + }, + ) + .then(({ data }) => data), + { + manual: true, + }, + ); - const { - data: da1, - run: run1, - loading: loading1, - } = useRequest( - () => - axios - .post( - atob( - `aHR0cHM6Ly90cmlwY2FueW9uLmZ3cy5xYS5udC5jdHJpcGNvcnAuY29t`, - ) + - "/flytest-api-ctrip-coffeebean-transfer/api/task/triggerPullTrafficByFn", - data, - ) - .then(({ data }) => data), - { - manual: true, - onSuccess() { - message.success("开始转换,请留意后续Flybirds消息推送"); - }, - }, - ); + const { + data: da1, + run: run1, + loading: loading1, + } = useRequest( + () => + axios + .post( + atob(`aHR0cHM6Ly90cmlwY2FueW9uLmZ3cy5xYS5udC5jdHJpcGNvcnAuY29t`) + + "/flytest-api-ctrip-coffeebean-transfer/api/task/triggerPullTrafficByFn", + data, + ) + .then(({ data }) => data), + { + manual: true, + onSuccess() { + message.success("开始转换,请留意后续Flybirds消息推送"); + }, + }, + ); - const showDrawer = () => { - setOpen(true); - }; + const showDrawer = () => { + setOpen(true); + }; - const onClose = () => { - setOpen(false); - }; + const onClose = () => { + setOpen(false); + }; - useEffect(() => { - if (open) { - run(); - } - }, [open]); + useEffect(() => { + if (open) { + run(); + } + }, [open]); - return ( - <> - {[ - "tripgl-37885-auto", - "tripgl-62594-auto", - "tripgl-108960-auto", - ].includes(prm.id || "") && ( - - )} + return ( + <> + {[ + "tripgl-37885-auto", + "tripgl-62594-auto", + "tripgl-108960-auto", + ].includes(prm.id || "") && ( + + )} - - - -
- -
- - - - - ); + + + +
+ +
+ + + + + ); }; export default PrepareProdFn; diff --git a/packages/canyon-platform/src/components/CanyonReport/ShikiDetail.tsx b/packages/canyon-platform/src/components/CanyonReport/ShikiDetail.tsx index abcf8039..4255296e 100644 --- a/packages/canyon-platform/src/components/CanyonReport/ShikiDetail.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/ShikiDetail.tsx @@ -2,187 +2,187 @@ import { mergeIntervals } from "./helper.tsx"; import { createHighlighterCoreInstance } from "@/components/CanyonReport/loadShiki.ts"; const ShikiDetail = ({ defaultValue, filecoverage, theme }) => { - const [content, setContent] = useState(""); + const [content, setContent] = useState(""); + + const statementStats = filecoverage.s; + const statementMeta = filecoverage.statementMap; + const structuredText = defaultValue + .split("\n") + .reduce((previousValue, currentValue, currentIndex) => { + return { + ...previousValue, + [currentIndex]: currentValue, + }; + }, {}); + const statementDecorations = []; + + Object.entries(statementStats).forEach(([stName, count]) => { + const meta = statementMeta[stName]; + if (meta) { + const type = count > 0 ? "yes" : "no"; + const startCol = meta.start.column; + const endCol = meta.end.column + 1; + const startLine = meta.start.line; + const endLine = meta.end.line; + + if (type === "no" && structuredText[startLine]) { + if (endLine !== startLine) { + // endCol = structuredText[startLine].length; + } + // 转化为字符的起始 - const statementStats = filecoverage.s; - const statementMeta = filecoverage.statementMap; - const structuredText = defaultValue - .split("\n") - .reduce((previousValue, currentValue, currentIndex) => { - return { - ...previousValue, - [currentIndex]: currentValue, - }; - }, {}); - const statementDecorations = []; - - Object.entries(statementStats).forEach(([stName, count]) => { - const meta = statementMeta[stName]; - if (meta) { - const type = count > 0 ? "yes" : "no"; - const startCol = meta.start.column; - const endCol = meta.end.column + 1; - const startLine = meta.start.line; - const endLine = meta.end.line; - - if (type === "no" && structuredText[startLine]) { - if (endLine !== startLine) { - // endCol = structuredText[startLine].length; - } - // 转化为字符的起始 - - let start = 0; - let end = 0; - - for (let i = 0; i < startLine - 1; i++) { - start += structuredText[i].length + 1; - } - for (let i = 0; i < endLine - 1; i++) { - // TODO: 这里有问题,可能是源码编译前有代码格式化 - end += (structuredText[i]?.length || 0) + 1; - } - - start += startCol; - end += endCol; - statementDecorations.push([start, end]); - } + let start = 0; + let end = 0; + + for (let i = 0; i < startLine - 1; i++) { + start += structuredText[i].length + 1; + } + for (let i = 0; i < endLine - 1; i++) { + // TODO: 这里有问题,可能是源码编译前有代码格式化 + end += (structuredText[i]?.length || 0) + 1; } - }); - const fnDecorations = []; - const fnStats = filecoverage.f; - const fnMeta = filecoverage.fnMap; - Object.entries(fnStats).forEach(([fName, count]) => { - const meta = fnMeta[fName]; - if (meta) { - const type = count > 0 ? "yes" : "no"; - // Some versions of the instrumenter in the wild populate 'func' - // but not 'decl': - const decl = meta.decl || meta.loc; - const startCol = decl.start.column; - const endCol = decl.end.column + 1; - const startLine = decl.start.line; - const endLine = decl.end.line; - - if (type === "no" && structuredText[startLine]) { - if (endLine !== startLine) { - // endCol = structuredText[startLine].length; - } - - // 转化为字符的起始 - - let start = 0; - let end = 0; - - for (let i = 0; i < startLine - 1; i++) { - start += structuredText[i].length + 1; - } - for (let i = 0; i < endLine - 1; i++) { - end += structuredText[i].length + 1; - } - - start += startCol; - end += endCol; - fnDecorations.push([start, end]); - } + start += startCol; + end += endCol; + statementDecorations.push([start, end]); + } + } + }); + + const fnDecorations = []; + const fnStats = filecoverage.f; + const fnMeta = filecoverage.fnMap; + Object.entries(fnStats).forEach(([fName, count]) => { + const meta = fnMeta[fName]; + if (meta) { + const type = count > 0 ? "yes" : "no"; + // Some versions of the instrumenter in the wild populate 'func' + // but not 'decl': + const decl = meta.decl || meta.loc; + const startCol = decl.start.column; + const endCol = decl.end.column + 1; + const startLine = decl.start.line; + const endLine = decl.end.line; + + if (type === "no" && structuredText[startLine]) { + if (endLine !== startLine) { + // endCol = structuredText[startLine].length; } - }); - const branchStats = filecoverage.b; - const branchMeta = filecoverage.branchMap; + // 转化为字符的起始 - const branchDecorations = []; + let start = 0; + let end = 0; - function specialLogicByIf(branchRange, index) { - if ( - branchRange.type === "if" && - branchRange.locations.length > 1 && - Number(index) === 0 - ) { - return false; - } else { - return true; + for (let i = 0; i < startLine - 1; i++) { + start += structuredText[i].length + 1; } - } - - Object.entries(branchStats).forEach(([bName, counts]) => { - const meta = branchMeta[bName]; - if (meta) { - Object.entries(meta.locations).forEach(([index, location]) => { - const count = counts[index]; - if (count === 0 && specialLogicByIf(meta)) { - const startCol = location.start.column; - const endCol = location.end.column + 1; - const startLine = location.start.line; - const endLine = location.end.line; - - // 转化为字符的起始 - - let start = 0; - let end = 0; - - for (let i = 0; i < startLine - 1; i++) { - start += structuredText[i].length + 1; - } - for (let i = 0; i < endLine - 1; i++) { - end += structuredText[i].length + 1; - } - - start += startCol; - end += endCol; - branchDecorations.push([start, end]); - } - }); + for (let i = 0; i < endLine - 1; i++) { + end += structuredText[i].length + 1; } - }); - useEffect(() => { - createHighlighterCoreInstance().then(({ codeToHtml }) => { - try { - const res = codeToHtml(defaultValue, { - lang: "javascript", - theme: theme === "light" ? "light-plus" : "tokyo-night", - decorations: mergeIntervals( - [ - ...statementDecorations, - ...fnDecorations, - ...branchDecorations, - ].filter((item) => { - // defaultValue - if (item[0] >= item[1]) { - return false; - } else if (item[1] > defaultValue.length) { - return false; - } else { - return item[0] < item[1]; - } - }), - ).map(([start, end]) => { - return { - start, - end, - properties: { class: "content-class-no-found" }, - }; - }), - }); - setContent(res); - } catch (err) { - console.log("覆盖率着色失败", err); - const r = codeToHtml(defaultValue, { - lang: "javascript", - theme: theme === "light" ? "light-plus" : "tokyo-night", - }); - - setContent(r); - } + start += startCol; + end += endCol; + fnDecorations.push([start, end]); + } + } + }); + + const branchStats = filecoverage.b; + const branchMeta = filecoverage.branchMap; + + const branchDecorations = []; + + function specialLogicByIf(branchRange, index) { + if ( + branchRange.type === "if" && + branchRange.locations.length > 1 && + Number(index) === 0 + ) { + return false; + } else { + return true; + } + } + + Object.entries(branchStats).forEach(([bName, counts]) => { + const meta = branchMeta[bName]; + if (meta) { + Object.entries(meta.locations).forEach(([index, location]) => { + const count = counts[index]; + if (count === 0 && specialLogicByIf(meta)) { + const startCol = location.start.column; + const endCol = location.end.column + 1; + const startLine = location.start.line; + const endLine = location.end.line; + + // 转化为字符的起始 + + let start = 0; + let end = 0; + + for (let i = 0; i < startLine - 1; i++) { + start += structuredText[i].length + 1; + } + for (let i = 0; i < endLine - 1; i++) { + end += structuredText[i].length + 1; + } + + start += startCol; + end += endCol; + branchDecorations.push([start, end]); + } + }); + } + }); + + useEffect(() => { + createHighlighterCoreInstance().then(({ codeToHtml }) => { + try { + const res = codeToHtml(defaultValue, { + lang: "javascript", + theme: theme === "light" ? "light-plus" : "tokyo-night", + decorations: mergeIntervals( + [ + ...statementDecorations, + ...fnDecorations, + ...branchDecorations, + ].filter((item) => { + // defaultValue + if (item[0] >= item[1]) { + return false; + } else if (item[1] > defaultValue.length) { + return false; + } else { + return item[0] < item[1]; + } + }), + ).map(([start, end]) => { + return { + start, + end, + properties: { class: "content-class-no-found" }, + }; + }), + }); + setContent(res); + } catch (err) { + console.log("覆盖率着色失败", err); + const r = codeToHtml(defaultValue, { + lang: "javascript", + theme: theme === "light" ? "light-plus" : "tokyo-night", }); - }, []); - return ( -
-
-
- ); + setContent(r); + } + }); + }, []); + + return ( +
+
+
+ ); }; export default ShikiDetail; diff --git a/packages/canyon-platform/src/components/CanyonReport/TreeTable.tsx b/packages/canyon-platform/src/components/CanyonReport/TreeTable.tsx index e428d6a2..ae8f8cba 100644 --- a/packages/canyon-platform/src/components/CanyonReport/TreeTable.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/TreeTable.tsx @@ -4,155 +4,141 @@ import { getCOlor, percent } from "../../helpers/utils/common.ts"; import { checkSuffix } from "./helper.tsx"; const CanyonReportTreeTable = ({ - dataSource, - loading, - activatedPath, - onSelect, - onlyChange, + dataSource, + loading, + activatedPath, + onSelect, + onlyChange, }) => { - const { t } = useTranslation(); - const newlinesColumns = onlyChange - ? [ - { - title: t("projects.newlines"), - width: "200px", - sorter: (a, b) => { - return a.summary.newlines.pct - b.summary.newlines.pct; - }, - dataIndex: ["summary", "newlines", "total"], - render(text, record) { - return ( - - - - ({record.summary.newlines.covered}/ - {record.summary.newlines.total}) - - {/*{record.summary.newlines.covered}%*/} - - ); - }, + const { t } = useTranslation(); + const newlinesColumns = onlyChange + ? [ + { + title: t("projects.newlines"), + width: "200px", + sorter: (a, b) => { + return a.summary.newlines.pct - b.summary.newlines.pct; + }, + dataIndex: ["summary", "newlines", "total"], + render(text, record) { + return ( + + + + ({record.summary.newlines.covered}/ + {record.summary.newlines.total}) + + {/*{record.summary.newlines.covered}%*/} + + ); + }, + }, + ] + : []; + // const newlinesColumns = []; + return ( +
+ +
{ + // return { + // onClick: (event) => { + // console.log(record); + // onSelect(record); + // }, // click row + // }; + // }} + columns={[ + { + title: t("projects.detail.files"), + key: "path", + dataIndex: "path", + render(text, record) { + return ( + { + onSelect(record); + }} + > + {text.includes(".") && checkSuffix(text) ? ( + + ) : ( + + )} + {text.split("/").at(-1)} + + ); }, - ] - : []; - // const newlinesColumns = []; - return ( -
- -
{ - // return { - // onClick: (event) => { - // console.log(record); - // onSelect(record); - // }, // click row - // }; - // }} - columns={[ - { - title: t("projects.detail.files"), - key: "path", - dataIndex: "path", - render(text, record) { - return ( - { - onSelect(record); - }} - > - {text.includes(".") && - checkSuffix(text) ? ( - - ) : ( - - )} - {text.split("/").at(-1)} - - ); - }, - }, + }, - { - title: t("common.total"), - key: "total", - dataIndex: ["summary", "statements", "total"], - sorter(a, b) { - return ( - a.summary.statements.total - - b.summary.statements.total - ); - }, - }, - { - title: t("common.covered"), - key: "covered", - dataIndex: ["summary", "statements", "covered"], - sorter(a, b) { - return ( - a.summary.statements.covered - - b.summary.statements.covered - ); - }, - }, - ] - .concat(newlinesColumns) - .concat([ - { - title: t("projects.config.coverage") + " %", - width: "300px", - key: "c", - dataIndex: ["summary", "statements", "pct"], - sorter(a, b) { - return ( - a.summary.statements.pct - - b.summary.statements.pct - ); - }, - render(text) { - return ( - - ); - }, - }, - ])} - /> - - - ); + { + title: t("common.total"), + key: "total", + dataIndex: ["summary", "statements", "total"], + sorter(a, b) { + return a.summary.statements.total - b.summary.statements.total; + }, + }, + { + title: t("common.covered"), + key: "covered", + dataIndex: ["summary", "statements", "covered"], + sorter(a, b) { + return ( + a.summary.statements.covered - b.summary.statements.covered + ); + }, + }, + ] + .concat(newlinesColumns) + .concat([ + { + title: t("projects.config.coverage") + " %", + width: "300px", + key: "c", + dataIndex: ["summary", "statements", "pct"], + sorter(a, b) { + return a.summary.statements.pct - b.summary.statements.pct; + }, + render(text) { + return ( + + ); + }, + }, + ])} + /> + + + ); }; export default CanyonReportTreeTable; diff --git a/packages/canyon-platform/src/components/CanyonReport/helper.tsx b/packages/canyon-platform/src/components/CanyonReport/helper.tsx index 5e39b7a9..a4b65a6a 100644 --- a/packages/canyon-platform/src/components/CanyonReport/helper.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/helper.tsx @@ -1,208 +1,208 @@ export function coreFn( - fileCoverage: any, - fileDetail: string, + fileCoverage: any, + fileDetail: string, ): { - times: { - lineNumber: number; - count: number; - }[]; - rows: string[]; - maxWidth: number; - lines: { - executionNumber: number; - }[]; + times: { + lineNumber: number; + count: number; + }[]; + rows: string[]; + maxWidth: number; + lines: { + executionNumber: number; + }[]; } { - const nullData = { - times: [], - rows: [], - maxWidth: 0, - lines: [], - }; - if (!fileCoverage.s) { - return nullData; + const nullData = { + times: [], + rows: [], + maxWidth: 0, + lines: [], + }; + if (!fileCoverage.s) { + return nullData; + } + + const content = fileDetail; + // 1.转换成数组 + const rows = [""]; + let index = 0; + for (let i = 0; i < content.length; i++) { + if (content[i] === "\n") { + index += 1; + rows.push(""); + } else { + rows[index] += content[i]; } - - const content = fileDetail; - // 1.转换成数组 - const rows = [""]; - let index = 0; - for (let i = 0; i < content.length; i++) { - if (content[i] === "\n") { - index += 1; - rows.push(""); - } else { - rows[index] += content[i]; - } - } - const maxWidth = JSON.parse(JSON.stringify(rows)).sort( - (a: string, b: string) => -(a.length - b.length), - )[0].length; - - // 获取numberOfRows - // 获取行覆盖率 - function getLineCoverage(data: any) { - const statementMap = data.statementMap; - const statements = data.s; - const lineMap = Object.create(null); - Object.entries(statements).forEach(([st, count]: any) => { - if (!statementMap[st]) { - return; - } - const { line } = statementMap[st].start; - const prevVal = lineMap[line]; - if (prevVal === undefined || prevVal < count) { - lineMap[line] = count; - } - }); - return lineMap; - } - - // 计算行 - const lineStats = getLineCoverage(fileCoverage); - if (!lineStats) { - return nullData; - } - // numberOfRows - const numberOfRows: any[] = []; - Object.entries(lineStats).forEach(([lineNumber, count]) => { - numberOfRows.push({ lineNumber, count }); - // 这边计算出了行的次数!!!!!! + } + const maxWidth = JSON.parse(JSON.stringify(rows)).sort( + (a: string, b: string) => -(a.length - b.length), + )[0].length; + + // 获取numberOfRows + // 获取行覆盖率 + function getLineCoverage(data: any) { + const statementMap = data.statementMap; + const statements = data.s; + const lineMap = Object.create(null); + Object.entries(statements).forEach(([st, count]: any) => { + if (!statementMap[st]) { + return; + } + const { line } = statementMap[st].start; + const prevVal = lineMap[line]; + if (prevVal === undefined || prevVal < count) { + lineMap[line] = count; + } }); - - const lines = []; - for (let i = 0; i < rows.length; i++) { - if (numberOfRows.find((n) => Number(n.lineNumber) === i + 1)) { - lines.push({ - executionNumber: numberOfRows.find( - (n) => Number(n.lineNumber) === i + 1, - ).count, - }); - } else { - lines.push({ - executionNumber: -1, - }); - } + return lineMap; + } + + // 计算行 + const lineStats = getLineCoverage(fileCoverage); + if (!lineStats) { + return nullData; + } + // numberOfRows + const numberOfRows: any[] = []; + Object.entries(lineStats).forEach(([lineNumber, count]) => { + numberOfRows.push({ lineNumber, count }); + // 这边计算出了行的次数!!!!!! + }); + + const lines = []; + for (let i = 0; i < rows.length; i++) { + if (numberOfRows.find((n) => Number(n.lineNumber) === i + 1)) { + lines.push({ + executionNumber: numberOfRows.find( + (n) => Number(n.lineNumber) === i + 1, + ).count, + }); + } else { + lines.push({ + executionNumber: -1, + }); } - return { - times: numberOfRows, - rows, - lines, - maxWidth, - }; + } + return { + times: numberOfRows, + rows, + lines, + maxWidth, + }; } export function genDecorationsLv2Array(code, startends) { - const lines = code.split("\n"); - function convertRanges(arr) { - const result = []; - arr.forEach((data) => { - const start = data.start; - const end = data.end; - - for (let i = start[0]; i <= end[0]; i++) { - const intervalStart = i === start[0] ? start[1] : 0; - const intervalEnd = lines[i].length; - result.push([i, intervalStart, intervalEnd]); - } - }); - // 输出每一行的区间值 - return result; - } - - const convertedData = convertRanges(startends); - function mergeRanges(ranges) { - // 对区间按照起始位置进行排序 - ranges.sort((a, b) => a[0] - b[0]); + const lines = code.split("\n"); + function convertRanges(arr) { + const result = []; + arr.forEach((data) => { + const start = data.start; + const end = data.end; + + for (let i = start[0]; i <= end[0]; i++) { + const intervalStart = i === start[0] ? start[1] : 0; + const intervalEnd = lines[i].length; + result.push([i, intervalStart, intervalEnd]); + } + }); + // 输出每一行的区间值 + return result; + } - const merged = []; + const convertedData = convertRanges(startends); + function mergeRanges(ranges) { + // 对区间按照起始位置进行排序 + ranges.sort((a, b) => a[0] - b[0]); - let currentRange = ranges[0]; - for (let i = 1; i < ranges.length; i++) { - const nextRange = ranges[i]; + const merged = []; - // 如果当前区间和下一个区间有重叠,则合并它们 - if (currentRange[1] >= nextRange[0]) { - currentRange[1] = Math.max(currentRange[1], nextRange[1]); - } else { - merged.push(currentRange); - currentRange = nextRange; - } - } + let currentRange = ranges[0]; + for (let i = 1; i < ranges.length; i++) { + const nextRange = ranges[i]; + // 如果当前区间和下一个区间有重叠,则合并它们 + if (currentRange[1] >= nextRange[0]) { + currentRange[1] = Math.max(currentRange[1], nextRange[1]); + } else { merged.push(currentRange); - - return merged; + currentRange = nextRange; + } } - function mergeRows(array) { - const groupedRows = {}; + merged.push(currentRange); - // 将相同行的元素分组 - array.forEach(([row, col, value]) => { - if (!groupedRows[row]) { - groupedRows[row] = []; - } - groupedRows[row].push([col, value]); - }); + return merged; + } - const mergedArray = []; + function mergeRows(array) { + const groupedRows = {}; - // 对每个分组合并区间 - for (const row in groupedRows) { - const mergedRanges = mergeRanges(groupedRows[row]); - mergedRanges.forEach((range) => { - mergedArray.push([parseInt(row), range[0], range[1]]); - }); - } + // 将相同行的元素分组 + array.forEach(([row, col, value]) => { + if (!groupedRows[row]) { + groupedRows[row] = []; + } + groupedRows[row].push([col, value]); + }); - return mergedArray; + const mergedArray = []; + + // 对每个分组合并区间 + for (const row in groupedRows) { + const mergedRanges = mergeRanges(groupedRows[row]); + mergedRanges.forEach((range) => { + mergedArray.push([parseInt(row), range[0], range[1]]); + }); } - const mergedArray = mergeRows(convertedData); return mergedArray; + } + + const mergedArray = mergeRows(convertedData); + return mergedArray; } export function capitalizeFirstLetter(string) { - return string.charAt(0).toUpperCase() + string.slice(1); + return string.charAt(0).toUpperCase() + string.slice(1); } export function checkSuffix(path) { - // 只要path里含有vue、js、jsx等就返回true - return ( - path.includes(".vue") || - path.includes(".js") || - path.includes(".jsx") || - path.includes(".ts") || - path.includes(".tsx") - ); + // 只要path里含有vue、js、jsx等就返回true + return ( + path.includes(".vue") || + path.includes(".js") || + path.includes(".jsx") || + path.includes(".ts") || + path.includes(".tsx") + ); } export function mergeIntervals(intervals) { - // 如果输入为空,直接返回空列表 - if (intervals.length === 0) { - return []; + // 如果输入为空,直接返回空列表 + if (intervals.length === 0) { + return []; + } + + // 将所有线段按起始位置进行排序 + intervals.sort((a, b) => a[0] - b[0]); + + // 初始化结果列表 + const merged = []; + let [currentStart, currentEnd] = intervals[0]; + + for (const [start, end] of intervals.slice(1)) { + if (start <= currentEnd) { + // 当前线段与前一个线段有重叠 + currentEnd = Math.max(currentEnd, end); // 更新结束位置 + } else { + // 当前线段与前一个线段没有重叠 + merged.push([currentStart, currentEnd]); // 将前一个线段加入结果列表 + [currentStart, currentEnd] = [start, end]; // 更新当前线段的起始和结束位置 } + } - // 将所有线段按起始位置进行排序 - intervals.sort((a, b) => a[0] - b[0]); + // 添加最后一个线段 + merged.push([currentStart, currentEnd]); - // 初始化结果列表 - const merged = []; - let [currentStart, currentEnd] = intervals[0]; - - for (const [start, end] of intervals.slice(1)) { - if (start <= currentEnd) { - // 当前线段与前一个线段有重叠 - currentEnd = Math.max(currentEnd, end); // 更新结束位置 - } else { - // 当前线段与前一个线段没有重叠 - merged.push([currentStart, currentEnd]); // 将前一个线段加入结果列表 - [currentStart, currentEnd] = [start, end]; // 更新当前线段的起始和结束位置 - } - } - - // 添加最后一个线段 - merged.push([currentStart, currentEnd]); - - return merged; + return merged; } diff --git a/packages/canyon-platform/src/components/CanyonReport/index.tsx b/packages/canyon-platform/src/components/CanyonReport/index.tsx index 33309433..6859f452 100644 --- a/packages/canyon-platform/src/components/CanyonReport/index.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/index.tsx @@ -8,153 +8,151 @@ import CanyonReportOverview from "./Overview.tsx"; import CanyonReportTreeTable from "./TreeTable.tsx"; function checkSummaryOnlyChange(item, onlyChange) { - // 如果只看改变的为false,就返回全部 - if (onlyChange === false) { - return true; - } - // 不然就检查item.change - if (onlyChange && item.change) { - return true; - } else { - return false; - } + // 如果只看改变的为false,就返回全部 + if (onlyChange === false) { + return true; + } + // 不然就检查item.change + if (onlyChange && item.change) { + return true; + } else { + return false; + } } function checkSummaryKeywords(item, keywords) { - return item.path.toLowerCase().includes(keywords.toLowerCase()); + return item.path.toLowerCase().includes(keywords.toLowerCase()); } function checkSummaryRange(item, range) { - const pct = item.statements.pct; - return pct >= range[0] && pct <= range[1]; + const pct = item.statements.pct; + return pct >= range[0] && pct <= range[1]; } // 1.summary最主要的数据,有外面传入 // 2.当前默认defaultPath = sprm.get('path'),锚点 const CanyonReport = ({ - // summary, - activatedPath, - pathWithNamespace, - coverageSummaryMapData, - loading, - onSelect, - mainData, - theme, - defaultOnlyShowChanged = false, + // summary, + activatedPath, + pathWithNamespace, + coverageSummaryMapData, + loading, + onSelect, + mainData, + theme, + defaultOnlyShowChanged = false, }) => { - // 几个状态 - // 1.展示模式//tree||list - const [showMode, setShowMode] = useState("tree"); - // 2.当前是文件还是文件夹 - const fMode = useMemo(() => { - // return 获取当前path,判断是否含有 . - return activatedPath.includes(".") && checkSuffix(activatedPath) - ? "file" - : "folder"; - }, [activatedPath]); - // 3.是否只展示变更文件 - // 4.其他的放在各自的状态里 + // 几个状态 + // 1.展示模式//tree||list + const [showMode, setShowMode] = useState("tree"); + // 2.当前是文件还是文件夹 + const fMode = useMemo(() => { + // return 获取当前path,判断是否含有 . + return activatedPath.includes(".") && checkSuffix(activatedPath) + ? "file" + : "folder"; + }, [activatedPath]); + // 3.是否只展示变更文件 + // 4.其他的放在各自的状态里 - // 5.文件路径关键字搜索 - const [keywords, setKeywords] = useState(""); - const [onlyChange, setOnlyChange] = useState( - Boolean(defaultOnlyShowChanged), - ); - const [range, setRange] = useState([0, 100]); - - // useEffect(()=>{ - // document.querySelector("#nihao").scrollIntoView(true); - // },[]) + // 5.文件路径关键字搜索 + const [keywords, setKeywords] = useState(""); + const [onlyChange, setOnlyChange] = useState(Boolean(defaultOnlyShowChanged)); + const [range, setRange] = useState([0, 100]); - const coverageSummaryMapDataFiltered = useMemo(() => { - return coverageSummaryMapData.filter( - (item) => - checkSummaryOnlyChange(item, onlyChange) && - checkSummaryKeywords(item, keywords) && - checkSummaryRange(item, range), - ); - }, [coverageSummaryMapData, onlyChange, keywords, range]); + // useEffect(()=>{ + // document.querySelector("#nihao").scrollIntoView(true); + // },[]) - const summary = coverageSummaryMapDataFiltered.reduce( - (acc: any, cur: any) => { - acc[cur.path] = cur; - return acc; - }, - {}, + const coverageSummaryMapDataFiltered = useMemo(() => { + return coverageSummaryMapData.filter( + (item) => + checkSummaryOnlyChange(item, onlyChange) && + checkSummaryKeywords(item, keywords) && + checkSummaryRange(item, range), ); - const summaryTreeItem = genSummaryTreeItem(activatedPath, summary); - function onChangeOnlyChangeKeywords(v) { - setKeywords(v.target.value); - } + }, [coverageSummaryMapData, onlyChange, keywords, range]); - function onChangeOnlyChange(v) { - // console.log(v,'v') - setOnlyChange(v); - } - function onChangeShowMode(mode) { - setShowMode(mode); - } - function onChangeRange(va) { - setRange(va); - } - return ( -
- - item.path.includes(activatedPath), - ).length - } - keywords={keywords} - range={range} - onlyChange={onlyChange} - onChangeOnlyChange={onChangeOnlyChange} - onChangeOnlyChangeKeywords={onChangeOnlyChangeKeywords} - onChangeShowMode={onChangeShowMode} - onChangeRange={onChangeRange} - /> - - - {showMode === "tree" && fMode === "folder" && ( - - )} - {showMode === "list" && fMode === "folder" && ( - - item.path.includes(activatedPath), - )} - /> - )} - - {fMode === "file" && mainData && ( - - )} - + const summary = coverageSummaryMapDataFiltered.reduce( + (acc: any, cur: any) => { + acc[cur.path] = cur; + return acc; + }, + {}, + ); + const summaryTreeItem = genSummaryTreeItem(activatedPath, summary); + function onChangeOnlyChangeKeywords(v) { + setKeywords(v.target.value); + } - -
- ); + function onChangeOnlyChange(v) { + // console.log(v,'v') + setOnlyChange(v); + } + function onChangeShowMode(mode) { + setShowMode(mode); + } + function onChangeRange(va) { + setRange(va); + } + return ( +
+ + item.path.includes(activatedPath), + ).length + } + keywords={keywords} + range={range} + onlyChange={onlyChange} + onChangeOnlyChange={onChangeOnlyChange} + onChangeOnlyChangeKeywords={onChangeOnlyChangeKeywords} + onChangeShowMode={onChangeShowMode} + onChangeRange={onChangeRange} + /> + + + {showMode === "tree" && fMode === "folder" && ( + + )} + {showMode === "list" && fMode === "folder" && ( + + item.path.includes(activatedPath), + )} + /> + )} + + {fMode === "file" && mainData && ( + + )} + + + +
+ ); }; export default CanyonReport; diff --git a/packages/canyon-platform/src/components/CanyonReport/line/coverage.tsx b/packages/canyon-platform/src/components/CanyonReport/line/coverage.tsx index 0591791c..e9caa47b 100644 --- a/packages/canyon-platform/src/components/CanyonReport/line/coverage.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/line/coverage.tsx @@ -3,69 +3,55 @@ import { getViewLineHeight } from "../../../helpers/utils/getViewLineHeight.tsx"; const LineCoverage = ({ covers, theme }) => { - const viewLineHeight = getViewLineHeight(); - return ( -
- {covers.map(({ covered, hits }, index) => { - if (covered === "yes") { - return ( -
- {hits}x -
- ); - } else if (covered === "no") { - return ( -
- ); - } else { - return ( -
- ); - } - })} -
- ); + const viewLineHeight = getViewLineHeight(); + return ( +
+ {covers.map(({ covered, hits }, index) => { + if (covered === "yes") { + return ( +
+ {hits}x +
+ ); + } else if (covered === "no") { + return ( +
+ ); + } else { + return ( +
+ ); + } + })} +
+ ); }; export default LineCoverage; diff --git a/packages/canyon-platform/src/components/CanyonReport/line/new.tsx b/packages/canyon-platform/src/components/CanyonReport/line/new.tsx index d1bd1231..e6473f36 100644 --- a/packages/canyon-platform/src/components/CanyonReport/line/new.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/line/new.tsx @@ -1,26 +1,26 @@ import { getViewLineHeight } from "../../../helpers/utils/getViewLineHeight.tsx"; const LineNew = ({ news, count }) => { - const viewLineHeight = getViewLineHeight(); - return ( -
- {[...Array(count)].map((line, index) => { - return ( -
- {/*{news.includes(index + 1) ? '+' : ''}*/} -
- ); - })} -
- ); + const viewLineHeight = getViewLineHeight(); + return ( +
+ {[...Array(count)].map((line, index) => { + return ( +
+ {/*{news.includes(index + 1) ? '+' : ''}*/} +
+ ); + })} +
+ ); }; export default LineNew; diff --git a/packages/canyon-platform/src/components/CanyonReport/line/number.tsx b/packages/canyon-platform/src/components/CanyonReport/line/number.tsx index e7544598..0cf29821 100644 --- a/packages/canyon-platform/src/components/CanyonReport/line/number.tsx +++ b/packages/canyon-platform/src/components/CanyonReport/line/number.tsx @@ -1,47 +1,46 @@ import { getViewLineHeight } from "../../../helpers/utils/getViewLineHeight.tsx"; const LineNumber = ({ count, theme }) => { - const viewLineHeight = getViewLineHeight(); - const style: any = { - color: theme === "light" ? "#0074D9" : "#0074D9", - textAlign: "right", - padding: "0 5px 0 20px", - }; - setTimeout(() => { - try { - document - .getElementById(`${window.location.hash.replace("#", "")}`) - .scrollIntoView(); - window.scrollBy(0, -160); // 向上滚动160px - } catch (e) { - // console.error(e); - } - }, 0); + const viewLineHeight = getViewLineHeight(); + const style: any = { + color: theme === "light" ? "#0074D9" : "#0074D9", + textAlign: "right", + padding: "0 5px 0 20px", + }; + setTimeout(() => { + try { + document + .getElementById(`${window.location.hash.replace("#", "")}`) + .scrollIntoView(); + window.scrollBy(0, -160); // 向上滚动160px + } catch (e) { + // console.error(e); + } + }, 0); - const activeLine = Number(window.location.hash.replace("#L", "")) - 1; - return ( -
- {[...Array(count)].map((i, index) => { - return ( - - {index + 1} - - ); - })} -
- ); + const activeLine = Number(window.location.hash.replace("#L", "")) - 1; + return ( +
+ {[...Array(count)].map((i, index) => { + return ( + + {index + 1} + + ); + })} +
+ ); }; export default LineNumber; diff --git a/packages/canyon-platform/src/components/CanyonReport/loadShiki.ts b/packages/canyon-platform/src/components/CanyonReport/loadShiki.ts index fb4d62d4..35065df7 100644 --- a/packages/canyon-platform/src/components/CanyonReport/loadShiki.ts +++ b/packages/canyon-platform/src/components/CanyonReport/loadShiki.ts @@ -8,14 +8,14 @@ import json from "shiki/langs/json.mjs"; import { createOnigurumaEngine } from "shiki/engine/oniguruma"; export const createHighlighterCoreInstance = async () => { - return await createHighlighterCore({ - themes: [ - // 传入导入的包,而不是字符串 - lightplus, - tokyoNight, - ], - langs: [css, jscss,json], - // `shiki/wasm` contains the wasm binary inlined as base64 string. - engine: createOnigurumaEngine(getWasm), - }); + return await createHighlighterCore({ + themes: [ + // 传入导入的包,而不是字符串 + lightplus, + tokyoNight, + ], + langs: [css, jscss, json], + // `shiki/wasm` contains the wasm binary inlined as base64 string. + engine: createOnigurumaEngine(getWasm), + }); }; diff --git a/packages/canyon-platform/src/components/LineChart.tsx b/packages/canyon-platform/src/components/LineChart.tsx index b59b1ab8..0b66cacc 100644 --- a/packages/canyon-platform/src/components/LineChart.tsx +++ b/packages/canyon-platform/src/components/LineChart.tsx @@ -2,280 +2,280 @@ import EChartsReact from "echarts-for-react"; import { CanyonCardPrimary } from "@/components/old-ui"; const coreData = [ - { - month: 0, - year: 2024, - uiTestBranchCoverage: 50.59, - uiTestLineCoverage: 56.08, - uiTestChangedLineCoverage: 100, - utBranchCoverage: 64.96, - utLineCoverage: 62.56, - }, - { - month: 1, - year: 2024, - uiTestBranchCoverage: 51.45, - uiTestLineCoverage: 59.19, - uiTestChangedLineCoverage: 93.76, - utBranchCoverage: 64.74, - utLineCoverage: 64, - }, - { - month: 2, - year: 2024, - uiTestBranchCoverage: 50.05, - uiTestLineCoverage: 63.23, - uiTestChangedLineCoverage: 93.95, - utBranchCoverage: 64.68, - utLineCoverage: 64.38, - }, - { - month: 3, - year: 2024, - uiTestBranchCoverage: 55.15, - uiTestLineCoverage: 66.47, - uiTestChangedLineCoverage: 95.49, - utBranchCoverage: 64.62, - utLineCoverage: 65.61, - }, - { - month: 4, - year: 2024, - uiTestBranchCoverage: 57.84, - uiTestLineCoverage: 71.61, - uiTestChangedLineCoverage: 95.1, - utBranchCoverage: 64.69, - utLineCoverage: 66.15, - }, - { - month: 5, - year: 2024, - uiTestBranchCoverage: 62.84, - uiTestLineCoverage: 77.23, - uiTestChangedLineCoverage: 96.13, - utBranchCoverage: 65.21, - utLineCoverage: 68.91, - }, - { - month: 6, - year: 2024, - uiTestBranchCoverage: 63.22, - uiTestLineCoverage: 77.66, - uiTestChangedLineCoverage: 95.35, - utBranchCoverage: 66.81, - utLineCoverage: 69.34, - }, - { - month: 7, - year: 2024, - uiTestBranchCoverage: 63.43, - uiTestLineCoverage: 77.92, - uiTestChangedLineCoverage: 95.61, - utBranchCoverage: 67.1, - utLineCoverage: 69.83, - }, - { - month: 8, - year: 2024, - uiTestBranchCoverage: 63.5, - uiTestLineCoverage: 78.11, - uiTestChangedLineCoverage: 95.33, - utBranchCoverage: 67.9, - utLineCoverage: 70.21, - codeChangeNum: 951122, - }, - { - month: 9, - year: 2024, - uiTestBranchCoverage: 63.35, - uiTestLineCoverage: 77.91, - uiTestChangedLineCoverage: 95.31, - utBranchCoverage: 67.68, - utLineCoverage: 69.97, - codeChangeNum: 935567, - }, - { - month: 10, - year: 2024, - uiTestBranchCoverage: 63.6, - uiTestLineCoverage: 78.2, - uiTestChangedLineCoverage: 95.4, - utBranchCoverage: 68.0, - utLineCoverage: 70.4, - codeChangeNum: 945000, - }, - { - month: 11, - year: 2024, - uiTestBranchCoverage: 63.85, - uiTestLineCoverage: 78.49, - uiTestChangedLineCoverage: 95.49, - utBranchCoverage: 68.32, - utLineCoverage: 70.83, - codeChangeNum: 954410, - }, + { + month: 0, + year: 2024, + uiTestBranchCoverage: 50.59, + uiTestLineCoverage: 56.08, + uiTestChangedLineCoverage: 100, + utBranchCoverage: 64.96, + utLineCoverage: 62.56, + }, + { + month: 1, + year: 2024, + uiTestBranchCoverage: 51.45, + uiTestLineCoverage: 59.19, + uiTestChangedLineCoverage: 93.76, + utBranchCoverage: 64.74, + utLineCoverage: 64, + }, + { + month: 2, + year: 2024, + uiTestBranchCoverage: 50.05, + uiTestLineCoverage: 63.23, + uiTestChangedLineCoverage: 93.95, + utBranchCoverage: 64.68, + utLineCoverage: 64.38, + }, + { + month: 3, + year: 2024, + uiTestBranchCoverage: 55.15, + uiTestLineCoverage: 66.47, + uiTestChangedLineCoverage: 95.49, + utBranchCoverage: 64.62, + utLineCoverage: 65.61, + }, + { + month: 4, + year: 2024, + uiTestBranchCoverage: 57.84, + uiTestLineCoverage: 71.61, + uiTestChangedLineCoverage: 95.1, + utBranchCoverage: 64.69, + utLineCoverage: 66.15, + }, + { + month: 5, + year: 2024, + uiTestBranchCoverage: 62.84, + uiTestLineCoverage: 77.23, + uiTestChangedLineCoverage: 96.13, + utBranchCoverage: 65.21, + utLineCoverage: 68.91, + }, + { + month: 6, + year: 2024, + uiTestBranchCoverage: 63.22, + uiTestLineCoverage: 77.66, + uiTestChangedLineCoverage: 95.35, + utBranchCoverage: 66.81, + utLineCoverage: 69.34, + }, + { + month: 7, + year: 2024, + uiTestBranchCoverage: 63.43, + uiTestLineCoverage: 77.92, + uiTestChangedLineCoverage: 95.61, + utBranchCoverage: 67.1, + utLineCoverage: 69.83, + }, + { + month: 8, + year: 2024, + uiTestBranchCoverage: 63.5, + uiTestLineCoverage: 78.11, + uiTestChangedLineCoverage: 95.33, + utBranchCoverage: 67.9, + utLineCoverage: 70.21, + codeChangeNum: 951122, + }, + { + month: 9, + year: 2024, + uiTestBranchCoverage: 63.35, + uiTestLineCoverage: 77.91, + uiTestChangedLineCoverage: 95.31, + utBranchCoverage: 67.68, + utLineCoverage: 69.97, + codeChangeNum: 935567, + }, + { + month: 10, + year: 2024, + uiTestBranchCoverage: 63.6, + uiTestLineCoverage: 78.2, + uiTestChangedLineCoverage: 95.4, + utBranchCoverage: 68.0, + utLineCoverage: 70.4, + codeChangeNum: 945000, + }, + { + month: 11, + year: 2024, + uiTestBranchCoverage: 63.85, + uiTestLineCoverage: 78.49, + uiTestChangedLineCoverage: 95.49, + utBranchCoverage: 68.32, + utLineCoverage: 70.83, + codeChangeNum: 954410, + }, ]; const data = { - Month: coreData.map((item) => `${item.month + 1}月`), - "Branch Coverage (%)": coreData.map((item) => item.uiTestBranchCoverage), - "Line Coverage (%)": coreData.map((item) => item.uiTestLineCoverage), - "Changed Line Coverage (%)": coreData.map( - (item) => item.uiTestChangedLineCoverage, - ), + Month: coreData.map((item) => `${item.month + 1}月`), + "Branch Coverage (%)": coreData.map((item) => item.uiTestBranchCoverage), + "Line Coverage (%)": coreData.map((item) => item.uiTestLineCoverage), + "Changed Line Coverage (%)": coreData.map( + (item) => item.uiTestChangedLineCoverage, + ), }; const d1 = coreData.map((item) => item.utBranchCoverage); const d2 = coreData.map((item) => item.utLineCoverage); const data1 = { - Month: coreData.map((item) => `${item.month + 1}月`), - "Branch Coverage (%)": d1, - "Line Coverage (%)": d2, + Month: coreData.map((item) => `${item.month + 1}月`), + "Branch Coverage (%)": d1, + "Line Coverage (%)": d2, }; const option = { - title: { - text: "UI自动化覆盖率", - }, - tooltip: { - trigger: "axis", - }, - legend: { - data: [ - "Branch Coverage (%)", - "Line Coverage (%)", - "Changed Line Coverage (%)", - ], - right: 10, - }, - grid: { - left: "3%", - right: "4%", - bottom: "3%", - containLabel: true, + title: { + text: "UI自动化覆盖率", + }, + tooltip: { + trigger: "axis", + }, + legend: { + data: [ + "Branch Coverage (%)", + "Line Coverage (%)", + "Changed Line Coverage (%)", + ], + right: 10, + }, + grid: { + left: "3%", + right: "4%", + bottom: "3%", + containLabel: true, + }, + // toolbox: { + // feature: { + // saveAsImage: {}, + // }, + // }, + xAxis: { + type: "category", + boundaryGap: true, + data: data.Month, + }, + yAxis: { + type: "value", + max: 100, + }, + series: [ + { + name: "Branch Coverage (%)", + type: "line", + data: data["Branch Coverage (%)"], + // 线上加文字 + label: { + show: true, + position: "bottom", + }, }, - // toolbox: { - // feature: { - // saveAsImage: {}, - // }, - // }, - xAxis: { - type: "category", - boundaryGap: true, - data: data.Month, + { + name: "Line Coverage (%)", + type: "line", + data: data["Line Coverage (%)"], + // 线上加文字 + label: { + show: true, + position: "top", + }, }, - yAxis: { - type: "value", - max: 100, + { + name: "Changed Line Coverage (%)", + type: "line", + data: data["Changed Line Coverage (%)"], + // 线上加文字 + label: { + show: true, + position: "top", + }, }, - series: [ - { - name: "Branch Coverage (%)", - type: "line", - data: data["Branch Coverage (%)"], - // 线上加文字 - label: { - show: true, - position: "bottom", - }, - }, - { - name: "Line Coverage (%)", - type: "line", - data: data["Line Coverage (%)"], - // 线上加文字 - label: { - show: true, - position: "top", - }, - }, - { - name: "Changed Line Coverage (%)", - type: "line", - data: data["Changed Line Coverage (%)"], - // 线上加文字 - label: { - show: true, - position: "top", - }, - }, - ], + ], }; const option1 = { - title: { - text: "UT覆盖率", - }, - tooltip: { - trigger: "axis", - }, - legend: { - data: [ - "Branch Coverage (%)", - "Line Coverage (%)", - "Changed Line Coverage (%)", - ], - right: 10, - }, - grid: { - left: "3%", - right: "4%", - bottom: "3%", - containLabel: true, - }, - // toolbox: { - // feature: { - // saveAsImage: {}, - // }, - // }, - xAxis: { - type: "category", - boundaryGap: true, - data: data.Month, + title: { + text: "UT覆盖率", + }, + tooltip: { + trigger: "axis", + }, + legend: { + data: [ + "Branch Coverage (%)", + "Line Coverage (%)", + "Changed Line Coverage (%)", + ], + right: 10, + }, + grid: { + left: "3%", + right: "4%", + bottom: "3%", + containLabel: true, + }, + // toolbox: { + // feature: { + // saveAsImage: {}, + // }, + // }, + xAxis: { + type: "category", + boundaryGap: true, + data: data.Month, + }, + yAxis: { + type: "value", + max: 100, + }, + series: [ + { + name: "Branch Coverage (%)", + type: "line", + data: data1["Branch Coverage (%)"], + label: { + show: true, + position: "top", + color: "blue", + }, }, - yAxis: { - type: "value", - max: 100, + { + name: "Line Coverage (%)", + type: "line", + data: data1["Line Coverage (%)"], + label: { + show: true, + position: "bottom", + color: "green", + }, }, - series: [ - { - name: "Branch Coverage (%)", - type: "line", - data: data1["Branch Coverage (%)"], - label: { - show: true, - position: "top", - color: "blue", - }, - }, - { - name: "Line Coverage (%)", - type: "line", - data: data1["Line Coverage (%)"], - label: { - show: true, - position: "bottom", - color: "green", - }, - }, - ], + ], }; const LineChart = () => { - return ( - -
- - -
-
- ); + return ( + +
+ + +
+
+ ); }; export default LineChart; diff --git a/packages/canyon-platform/src/components/app/CopyCode.tsx b/packages/canyon-platform/src/components/app/CopyCode.tsx index c537f7e0..f8aa8d70 100755 --- a/packages/canyon-platform/src/components/app/CopyCode.tsx +++ b/packages/canyon-platform/src/components/app/CopyCode.tsx @@ -7,37 +7,37 @@ import { CopyToClipboard } from "react-copy-to-clipboard"; import { createHighlighterCoreInstance } from "@/components/CanyonReport/loadShiki.ts"; const CopyCode: FC<{ code: string }> = ({ code }) => { - const fileContent = code; - const [content, setContent] = useState(""); + const fileContent = code; + const [content, setContent] = useState(""); - useEffect(() => { - if (fileContent) { - createHighlighterCoreInstance().then(({ codeToHtml }) => { - const html = codeToHtml(fileContent, { - lang: "json", - theme: "tokyo-night", - }); - setContent(html); - }); - } - }, [fileContent]); - return ( -
-
- -
+ useEffect(() => { + if (fileContent) { + createHighlighterCoreInstance().then(({ codeToHtml }) => { + const html = codeToHtml(fileContent, { + lang: "json", + theme: "tokyo-night", + }); + setContent(html); + }); + } + }, [fileContent]); + return ( +
+
+ +
-
-
-
-
- ); +
+
+
+
+ ); }; export default CopyCode; diff --git a/packages/canyon-platform/src/components/app/ProjectRecordDetailDrawer.tsx b/packages/canyon-platform/src/components/app/ProjectRecordDetailDrawer.tsx index c2ec3457..cb8e0e29 100644 --- a/packages/canyon-platform/src/components/app/ProjectRecordDetailDrawer.tsx +++ b/packages/canyon-platform/src/components/app/ProjectRecordDetailDrawer.tsx @@ -9,98 +9,96 @@ import { useRequest } from "ahooks"; import axios from "axios"; console.log(useRequest, axios); const ProjectRecordDetailDrawer = ({ open, onClose, sha }: any) => { - const pam = useParams(); - const { data, loading } = useQuery(GetProjectRecordDetailByShaDocument, { - variables: { - projectID: pam.id as string, - sha: sha, - }, - }); + const pam = useParams(); + const { data, loading } = useQuery(GetProjectRecordDetailByShaDocument, { + variables: { + projectID: pam.id as string, + sha: sha, + }, + }); - const { t } = useTranslation(); - // const pam = useParams(); - const columns = [ - { - title: t("projects.report_id"), - dataIndex: "reportID", - render(_: any): JSX.Element { - // 标识位(勿动) - return
{_}
; - }, - }, - { - title: t("projects.statements"), - dataIndex: "statements", - render(_: any): JSX.Element { - return {_}%; - }, - }, - { - title: t("projects.newlines"), - dataIndex: "newlines", - render(_: any): JSX.Element { - return {_}%; - }, - }, - { - title: t("projects.reporter"), - dataIndex: "reporterUsername", - render(_: any, t: any): any { - return ( -
- - - {t.reporterUsername} - -
- ); - }, - }, - { - title: t("projects.report_time"), - dataIndex: "lastReportTime", - render(_: any) { - return dayjs(_).format("MM-DD HH:mm"); - }, - }, - { - title: t("common.option"), - render(_: any) { - return ( -
- - {t("common.detail")} - -
- ); - }, - }, - ]; - - return ( - <> - {_}
; + }, + }, + { + title: t("projects.statements"), + dataIndex: "statements", + render(_: any): JSX.Element { + return {_}%; + }, + }, + { + title: t("projects.newlines"), + dataIndex: "newlines", + render(_: any): JSX.Element { + return {_}%; + }, + }, + { + title: t("projects.reporter"), + dataIndex: "reporterUsername", + render(_: any, t: any): any { + return ( +
+ + {t.reporterUsername} +
+ ); + }, + }, + { + title: t("projects.report_time"), + dataIndex: "lastReportTime", + render(_: any) { + return dayjs(_).format("MM-DD HH:mm"); + }, + }, + { + title: t("common.option"), + render(_: any) { + return ( +
+ -
- - - ); + {t("common.detail")} + + + ); + }, + }, + ]; + + return ( + <> + +
+ + + ); }; export default ProjectRecordDetailDrawer; diff --git a/packages/canyon-platform/src/components/app/footer.tsx b/packages/canyon-platform/src/components/app/footer.tsx index 6c844892..7c52dbdd 100644 --- a/packages/canyon-platform/src/components/app/footer.tsx +++ b/packages/canyon-platform/src/components/app/footer.tsx @@ -1,40 +1,40 @@ const { Text } = Typography; import app from "./app.json"; const AppFooter = () => { - const lists = app.footer; - return ( -
- +
+ ); }; export default AppFooter; diff --git a/packages/canyon-platform/src/components/app/getting-started-content.ts b/packages/canyon-platform/src/components/app/getting-started-content.ts index 2c49bcea..ec19c735 100755 --- a/packages/canyon-platform/src/components/app/getting-started-content.ts +++ b/packages/canyon-platform/src/components/app/getting-started-content.ts @@ -1,6 +1,6 @@ export const gettingStartedContent = { - babel: `npm i babel-plugin-istanbul babel-plugin-canyon -D `, - webpack: `{ + babel: `npm i babel-plugin-istanbul babel-plugin-canyon -D `, + webpack: `{ "plugins": [ "istanbul", "canyon" diff --git a/packages/canyon-platform/src/components/icons/MaterialSymbolsCommitSharp.tsx b/packages/canyon-platform/src/components/icons/MaterialSymbolsCommitSharp.tsx index 5c6d8f73..35f7638d 100644 --- a/packages/canyon-platform/src/components/icons/MaterialSymbolsCommitSharp.tsx +++ b/packages/canyon-platform/src/components/icons/MaterialSymbolsCommitSharp.tsx @@ -1,20 +1,20 @@ import type { SVGProps } from "react"; export default function MaterialSymbolsCommitSharp( - props: SVGProps, + props: SVGProps, ) { - return ( - - - - ); + return ( + + + + ); } diff --git a/packages/canyon-platform/src/components/old-ui/components/card/Primary.tsx b/packages/canyon-platform/src/components/old-ui/components/card/Primary.tsx index 28e93b6a..7e4480c4 100644 --- a/packages/canyon-platform/src/components/old-ui/components/card/Primary.tsx +++ b/packages/canyon-platform/src/components/old-ui/components/card/Primary.tsx @@ -2,22 +2,22 @@ import { FC } from "react"; const { useToken } = theme; const CanyonCardPrimary: FC<{ - theme?: any; - language?: any; - children: any; + theme?: any; + language?: any; + children: any; }> = ({ theme, language, children }) => { - const { token } = useToken(); - return ( -
- {children} -
- ); + const { token } = useToken(); + return ( +
+ {children} +
+ ); }; export default CanyonCardPrimary; diff --git a/packages/canyon-platform/src/components/old-ui/components/layout/Base.tsx b/packages/canyon-platform/src/components/old-ui/components/layout/Base.tsx index 4547f1ac..41bba244 100644 --- a/packages/canyon-platform/src/components/old-ui/components/layout/Base.tsx +++ b/packages/canyon-platform/src/components/old-ui/components/layout/Base.tsx @@ -1,8 +1,8 @@ import { - BarChartOutlined, - LinkOutlined, - MoreOutlined, - SearchOutlined, + BarChartOutlined, + LinkOutlined, + MoreOutlined, + SearchOutlined, } from "@ant-design/icons"; import { FC, ReactNode } from "react"; import { ErrorBoundary } from "react-error-boundary"; @@ -13,248 +13,212 @@ import ScrollBasedLayout from "./ScrollBasedLayout.tsx"; const { useToken } = theme; const { Text, Title } = Typography; interface CanyonLayoutBaseProps { - title?: string; - logo?: ReactNode; - mainTitleRightNode?: ReactNode; - menuSelectedKey?: string; - onSelectMenu?: (selectInfo: { key: string }) => void; - menuItems: MenuProps["items"]; - renderMainContent?: ReactNode; - onClickGlobalSearch?: () => void; - MeData: any; - itemsDropdown: any; - search: any; - account: any; - breadcrumb: any; - footerName?: string; + title?: string; + logo?: ReactNode; + mainTitleRightNode?: ReactNode; + menuSelectedKey?: string; + onSelectMenu?: (selectInfo: { key: string }) => void; + menuItems: MenuProps["items"]; + renderMainContent?: ReactNode; + onClickGlobalSearch?: () => void; + MeData: any; + itemsDropdown: any; + search: any; + account: any; + breadcrumb: any; + footerName?: string; } const CanyonLayoutBase: FC = ({ - title = "Canyon", - logo, - mainTitleRightNode, - menuSelectedKey = "", - onSelectMenu, - menuItems, - renderMainContent, - onClickGlobalSearch, - MeData, - itemsDropdown, - search, - account, - breadcrumb, - footerName = "CANYON", + title = "Canyon", + logo, + mainTitleRightNode, + menuSelectedKey = "", + onSelectMenu, + menuItems, + renderMainContent, + onClickGlobalSearch, + MeData, + itemsDropdown, + search, + account, + breadcrumb, + footerName = "CANYON", }) => { - const { token } = useToken(); + const { token } = useToken(); - return ( -
- <> - -
-
-
{ - window.location.href = "/"; - }} - > - {logo} - - {title} - -
+ return ( +
+ <> + +
+
+
{ + window.location.href = "/"; + }} + > + {logo} + + {title} + +
-
{mainTitleRightNode}
-
-
- {search && ( -
- -
+ {search && ( +
+ + - -
- )} +
+ + ⌘ + + + K + +
+
+ + +
+ )} - {account && ( -
- - Account - - - + +
+ )} -
+
- { - onSelectMenu?.(selectInfo); - }} - selectedKeys={[menuSelectedKey]} - items={menuItems.concat({ - key: "open-reports", - icon: , - label: ( - - 报表 - - ), - })} - className={"dark:bg-[#151718] px-1"} - style={{ flex: "1" }} - /> - {}, - }} - > -
- -
- - {MeData?.me.nickname} - - - {MeData?.me.email || ""} - -
- -
-
-
- } - mainContent={ -
-
- {breadcrumb} -
-
- ⚠️Something went wrong

} - > - {renderMainContent} -
-
-
- } - footer={
} - /> - -
- ); + { + onSelectMenu?.(selectInfo); + }} + selectedKeys={[menuSelectedKey]} + items={menuItems.concat({ + key: "open-reports", + icon: , + label: ( + + 报表 + + ), + })} + className={"dark:bg-[#151718] px-1"} + style={{ flex: "1" }} + /> + {}, + }} + > +
+ +
+ + {MeData?.me.nickname} + + + {MeData?.me.email || ""} + +
+ +
+
+
+ } + mainContent={ +
+
+ {breadcrumb} +
+
+ ⚠️Something went wrong

}> + {renderMainContent} +
+
+
+ } + footer={
} + /> + +
+ ); }; export default CanyonLayoutBase; diff --git a/packages/canyon-platform/src/components/old-ui/components/layout/ScrollBasedLayout.tsx b/packages/canyon-platform/src/components/old-ui/components/layout/ScrollBasedLayout.tsx index 733e9a51..88ec2999 100644 --- a/packages/canyon-platform/src/components/old-ui/components/layout/ScrollBasedLayout.tsx +++ b/packages/canyon-platform/src/components/old-ui/components/layout/ScrollBasedLayout.tsx @@ -2,75 +2,72 @@ import { FC, ReactNode, useEffect, useState } from "react"; const { useToken } = theme; const ScrollBasedLayout: FC<{ - sideBar: ReactNode; - mainContent: ReactNode; - footer: ReactNode; + sideBar: ReactNode; + mainContent: ReactNode; + footer: ReactNode; }> = ({ sideBar, mainContent, footer }) => { - const { token } = useToken(); - const [isScrolled, setIsScrolled] = useState(false); + const { token } = useToken(); + const [isScrolled, setIsScrolled] = useState(false); - useEffect(() => { - const handleScroll = () => { - const scrollY = - window.scrollY || document.documentElement.scrollTop; - const footer = document.getElementById("footer"); + useEffect(() => { + const handleScroll = () => { + const scrollY = window.scrollY || document.documentElement.scrollTop; + const footer = document.getElementById("footer"); - // 检查滚动是否超过100vh - setIsScrolled(scrollY + window.innerHeight > footer.offsetTop); - }; + // 检查滚动是否超过100vh + setIsScrolled(scrollY + window.innerHeight > footer.offsetTop); + }; - // 添加滚动事件监听器 - window.addEventListener("scroll", handleScroll); + // 添加滚动事件监听器 + window.addEventListener("scroll", handleScroll); - setTimeout(() => { - document.documentElement.scrollTop += 0.5; - }, 1000); + setTimeout(() => { + document.documentElement.scrollTop += 0.5; + }, 1000); - // 在组件卸载时移除监听器,以防止内存泄漏 - return () => { - window.removeEventListener("scroll", handleScroll); - }; - }, []); // 仅在组件挂载和卸载时运行 + // 在组件卸载时移除监听器,以防止内存泄漏 + return () => { + window.removeEventListener("scroll", handleScroll); + }; + }, []); // 仅在组件挂载和卸载时运行 - return ( -
-
-
-
+ return ( +
+
+
+
-
- {sideBar} -
-
+
{sideBar}
+
-
- {mainContent} -
-
-
- {footer} -
+
+ {mainContent}
- ); +
+
+ {footer} +
+
+ ); }; export default ScrollBasedLayout; diff --git a/packages/canyon-platform/src/components/old-ui/components/layout/footer.tsx b/packages/canyon-platform/src/components/old-ui/components/layout/footer.tsx index 3ff8862f..303927f4 100644 --- a/packages/canyon-platform/src/components/old-ui/components/layout/footer.tsx +++ b/packages/canyon-platform/src/components/old-ui/components/layout/footer.tsx @@ -1,18 +1,18 @@ const AppFooter = ({ name, corp }) => { - return ( -
-
- {name} -
- Copyright © 2024 {corp}, Inc. All rights reserved. -
- ); + return ( +
+
+ {name} +
+ Copyright © 2024 {corp}, Inc. All rights reserved. +
+ ); }; export default AppFooter; diff --git a/packages/canyon-platform/src/components/old-ui/components/modal/GlobalSearch.tsx b/packages/canyon-platform/src/components/old-ui/components/modal/GlobalSearch.tsx index 06d5d5e1..7e83398b 100644 --- a/packages/canyon-platform/src/components/old-ui/components/modal/GlobalSearch.tsx +++ b/packages/canyon-platform/src/components/old-ui/components/modal/GlobalSearch.tsx @@ -2,30 +2,30 @@ import { forwardRef, useImperativeHandle, useState } from "react"; const { Search } = Input; const CanyonModalGlobalSearch = (props, ref) => { - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(false); - useImperativeHandle(ref, () => ({ - report: () => { - setOpen(true); - }, - })); + useImperativeHandle(ref, () => ({ + report: () => { + setOpen(true); + }, + })); - return ( - { - setOpen(false); - }} - onOk={() => { - setOpen(false); - }} - footer={false} - > - - - ); + return ( + { + setOpen(false); + }} + onOk={() => { + setOpen(false); + }} + footer={false} + > + + + ); }; export default forwardRef(CanyonModalGlobalSearch); diff --git a/packages/canyon-platform/src/components/old-ui/components/page/Oauth.tsx b/packages/canyon-platform/src/components/old-ui/components/page/Oauth.tsx index a35d8885..4ab8a273 100644 --- a/packages/canyon-platform/src/components/old-ui/components/page/Oauth.tsx +++ b/packages/canyon-platform/src/components/old-ui/components/page/Oauth.tsx @@ -1,34 +1,33 @@ import { useEffect } from "react"; const CanyonPageOauth = ({ URLSearchParams, onOauthFail }) => { - const fetcher = (url: string) => - fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - code: URLSearchParams.get("code"), - redirectUri: location.origin + "/oauth", - }), - }) - .then((res) => res.json()) - .then((res) => { - if (res.statusCode >= 400) { - message.error(res.message); - localStorage.clear(); - onOauthFail(); - } else { - localStorage.setItem("token", res.token); - window.location.href = - localStorage.getItem("callback") || "/"; - } - }); - useEffect(() => { - fetcher("/api/oauth/token"); - }, []); + const fetcher = (url: string) => + fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + code: URLSearchParams.get("code"), + redirectUri: location.origin + "/oauth", + }), + }) + .then((res) => res.json()) + .then((res) => { + if (res.statusCode >= 400) { + message.error(res.message); + localStorage.clear(); + onOauthFail(); + } else { + localStorage.setItem("token", res.token); + window.location.href = localStorage.getItem("callback") || "/"; + } + }); + useEffect(() => { + fetcher("/api/oauth/token"); + }, []); - return logging in...; + return logging in...; }; export default CanyonPageOauth; diff --git a/packages/canyon-platform/src/components/old-ui/components/page/login/LoginBtn.tsx b/packages/canyon-platform/src/components/old-ui/components/page/login/LoginBtn.tsx index 3966680b..bd77eb75 100644 --- a/packages/canyon-platform/src/components/old-ui/components/page/login/LoginBtn.tsx +++ b/packages/canyon-platform/src/components/old-ui/components/page/login/LoginBtn.tsx @@ -6,53 +6,41 @@ import google from "../../../../../assets/img/google.svg"; // import img from '../../../../../assets/img/img.png'; const LoginBtn: FC<{ - oauthUrl: { google?: string; github?: string; gitlab?: string }; + oauthUrl: { google?: string; github?: string; gitlab?: string }; }> = ({ oauthUrl }) => { - return ( -
- - + return ( +
+ + - -
- ); + +
+ ); }; export default LoginBtn; diff --git a/packages/canyon-platform/src/components/old-ui/components/page/login/LoginForm.tsx b/packages/canyon-platform/src/components/old-ui/components/page/login/LoginForm.tsx index c5b65713..dd36232d 100644 --- a/packages/canyon-platform/src/components/old-ui/components/page/login/LoginForm.tsx +++ b/packages/canyon-platform/src/components/old-ui/components/page/login/LoginForm.tsx @@ -1,97 +1,93 @@ import { useRequest } from "ahooks"; import { FC } from "react"; const onFinishFailed = (errorInfo: any) => { - console.log("Failed:", errorInfo); + console.log("Failed:", errorInfo); }; type FieldType = { - companyname?: string; - username?: string; - password?: string; + companyname?: string; + username?: string; + password?: string; }; const LoginForm: FC<{ - onLoginSuccess: () => void; + onLoginSuccess: () => void; }> = ({ onLoginSuccess }) => { - const { run } = useRequest( - ({ username, password, companyname }) => - fetch(`/api/login`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - username: username, - password: password, - companyname: companyname, - }), - }) - .then((res) => res.json()) - .then((res) => { - if (res.statusCode >= 400) { - return Promise.reject(res); - } else { - return res; - } - }), - { - onSuccess: (data) => { - message.success("登录成功"); - onLoginSuccess(); - localStorage.setItem("token", data.token); - }, - onError: (error) => { - console.log(error); - message.error(error.message); - }, - manual: true, + const { run } = useRequest( + ({ username, password, companyname }) => + fetch(`/api/login`, { + method: "POST", + headers: { + "Content-Type": "application/json", }, - ); - const [form] = Form.useForm(); - const onFinish = (values: any) => { - console.log("Success:", values); - run({ - companyname: String(values.companyname), - username: String(values.username), - password: String(values.password), - }); - }; - return ( -
- - label="Username" - name="username" - rules={[ - { required: true, message: "Please input your username!" }, - ]} - > - - + body: JSON.stringify({ + username: username, + password: password, + companyname: companyname, + }), + }) + .then((res) => res.json()) + .then((res) => { + if (res.statusCode >= 400) { + return Promise.reject(res); + } else { + return res; + } + }), + { + onSuccess: (data) => { + message.success("登录成功"); + onLoginSuccess(); + localStorage.setItem("token", data.token); + }, + onError: (error) => { + console.log(error); + message.error(error.message); + }, + manual: true, + }, + ); + const [form] = Form.useForm(); + const onFinish = (values: any) => { + console.log("Success:", values); + run({ + companyname: String(values.companyname), + username: String(values.username), + password: String(values.password), + }); + }; + return ( + + + label="Username" + name="username" + rules={[{ required: true, message: "Please input your username!" }]} + > + + - - label="Password" - name="password" - rules={[ - { required: true, message: "Please input your password!" }, - ]} - > - - + + label="Password" + name="password" + rules={[{ required: true, message: "Please input your password!" }]} + > + + - - - - - ); + + + + + ); }; export default LoginForm; diff --git a/packages/canyon-platform/src/components/old-ui/components/page/login/index.tsx b/packages/canyon-platform/src/components/old-ui/components/page/login/index.tsx index 8bd9c413..dfd4a59e 100644 --- a/packages/canyon-platform/src/components/old-ui/components/page/login/index.tsx +++ b/packages/canyon-platform/src/components/old-ui/components/page/login/index.tsx @@ -10,86 +10,70 @@ import LoginForm from "./LoginForm.tsx"; const { Title } = Typography; const CanyonPageLogin: FC<{ - onLoginSuccess: () => void; - oauthUrl: { gitlab: string }; - logo: React.ReactNode; - register?: string; + onLoginSuccess: () => void; + oauthUrl: { gitlab: string }; + logo: React.ReactNode; + register?: string; }> = ({ onLoginSuccess, oauthUrl, logo, register }) => { - return ( -
-
- -
-
- - {logo} - -
+ return ( +
+
+ +
+
+ + {logo} + +
-
-
- Log in and continue -
- - - Register - - -
- ) - } - items={[ - { - label: "Sign In", - key: "login", - children: ( -
- - - -
- ), - }, - ]} - /> -
+
+
+ Log in and continue +
+ + + Register + +
-
+ ) + } + items={[ + { + label: "Sign In", + key: "login", + children: ( +
+ + + +
+ ), + }, + ]} + />
+
+ +
-
-
- ); +
+
+ ); }; export default CanyonPageLogin; diff --git a/packages/canyon-platform/src/components/ui/components/form/region.tsx b/packages/canyon-platform/src/components/ui/components/form/region.tsx index 789c024a..d2d3eef0 100644 --- a/packages/canyon-platform/src/components/ui/components/form/region.tsx +++ b/packages/canyon-platform/src/components/ui/components/form/region.tsx @@ -3,39 +3,37 @@ import { Button, Card, Space } from "antd"; import type { FC, ReactNode } from "react"; const RegionForm: FC<{ - title: string; - icon: ReactNode; - onSave?: () => void; - onAdd?: () => void; - children: ReactNode; + title: string; + icon: ReactNode; + onSave?: () => void; + onAdd?: () => void; + children: ReactNode; }> = ({ title, icon, onSave, onAdd, children }) => { - return ( - -
- {icon} -
- {title} -
- } - > - -
{children}
- - - {onAdd && ( - - )} - -
- - ); + return ( + +
{icon}
+ {title} + + } + > + +
{children}
+ + + {onAdd && ( + + )} + +
+
+ ); }; export default RegionForm; diff --git a/packages/canyon-platform/src/components/ui/components/margin.tsx b/packages/canyon-platform/src/components/ui/components/margin.tsx index 603e6c8b..0fec2cda 100644 --- a/packages/canyon-platform/src/components/ui/components/margin.tsx +++ b/packages/canyon-platform/src/components/ui/components/margin.tsx @@ -2,6 +2,6 @@ import type { FC, ReactNode } from "react"; import { cn } from "@/lib/utils.ts"; const Margin: FC<{ children: ReactNode }> = ({ children }) => { - return
{children}
; + return
{children}
; }; export default Margin; diff --git a/packages/canyon-platform/src/components/ui/components/typography/text.tsx b/packages/canyon-platform/src/components/ui/components/typography/text.tsx index 5848c51e..1bb7780c 100644 --- a/packages/canyon-platform/src/components/ui/components/typography/text.tsx +++ b/packages/canyon-platform/src/components/ui/components/typography/text.tsx @@ -2,29 +2,29 @@ import { Space } from "antd"; import type { CSSProperties, FC, ReactNode } from "react"; import { cn } from "@/lib/utils.ts"; interface TextTypographyProps { - title: string; - icon: ReactNode; - right?: ReactNode; - style?: CSSProperties; + title: string; + icon: ReactNode; + right?: ReactNode; + style?: CSSProperties; } const TextTypography: FC = ({ - title, - icon, - right, - style, + title, + icon, + right, + style, }) => { - return ( -
- - {icon} - {title} - -
{right}
-
- ); + return ( +
+ + {icon} + {title} + +
{right}
+
+ ); }; export default TextTypography; diff --git a/packages/canyon-platform/src/helpers/backend/GQLClient.ts b/packages/canyon-platform/src/helpers/backend/GQLClient.ts index 359c22ed..709f3ce4 100755 --- a/packages/canyon-platform/src/helpers/backend/GQLClient.ts +++ b/packages/canyon-platform/src/helpers/backend/GQLClient.ts @@ -2,11 +2,11 @@ * A wrapper type for defining errors possible in a GQL operation */ export type GQLError = - | { - type: "network_error"; - error: Error; - } - | { - type: "gql_error"; - error: T; - }; + | { + type: "network_error"; + error: Error; + } + | { + type: "gql_error"; + error: T; + }; diff --git a/packages/canyon-platform/src/helpers/backend/types/Email.ts b/packages/canyon-platform/src/helpers/backend/types/Email.ts index 73c9c283..36e92070 100755 --- a/packages/canyon-platform/src/helpers/backend/types/Email.ts +++ b/packages/canyon-platform/src/helpers/backend/types/Email.ts @@ -1,16 +1,16 @@ import * as t from "io-ts"; const emailRegex = - /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; interface EmailBrand { - readonly Email: unique symbol; + readonly Email: unique symbol; } export const EmailCodec = t.brand( - t.string, - (x): x is t.Branded => emailRegex.test(x), - "Email", + t.string, + (x): x is t.Branded => emailRegex.test(x), + "Email", ); export type Email = t.TypeOf; diff --git a/packages/canyon-platform/src/helpers/backend/types/TeamName.ts b/packages/canyon-platform/src/helpers/backend/types/TeamName.ts index 3076cdcc..07121aa3 100755 --- a/packages/canyon-platform/src/helpers/backend/types/TeamName.ts +++ b/packages/canyon-platform/src/helpers/backend/types/TeamName.ts @@ -1,13 +1,13 @@ import * as t from "io-ts"; interface TeamNameBrand { - readonly TeamName: unique symbol; + readonly TeamName: unique symbol; } export const TeamNameCodec = t.brand( - t.string, - (x): x is t.Branded => x.trim().length >= 6, - "TeamName", + t.string, + (x): x is t.Branded => x.trim().length >= 6, + "TeamName", ); export type TeamName = t.TypeOf; diff --git a/packages/canyon-platform/src/helpers/gitprovider/genOAuthUrl.ts b/packages/canyon-platform/src/helpers/gitprovider/genOAuthUrl.ts index 820a7556..04891ded 100644 --- a/packages/canyon-platform/src/helpers/gitprovider/genOAuthUrl.ts +++ b/packages/canyon-platform/src/helpers/gitprovider/genOAuthUrl.ts @@ -1,15 +1,15 @@ function redirect_uri(type) { - return `${window.location.origin}/${type}/oauth`; + return `${window.location.origin}/${type}/oauth`; } export const genOAuthUrl = ({ url, type, clientID }) => { - // TODO 暂时还不知道state干嘛的 - if (type === "github") { - return `${url}/login/oauth/authorize?client_id=${clientID}&redirect_uri=${redirect_uri(type)}&response_type=code&scope=user&state=STATE`; - } else if (type === "gitlab") { - return `https://gitlab.com/oauth/authorize?client_id=${clientID}&redirect_uri=${redirect_uri(type)}&response_type=code&state=STATE`; - } else if (type === "gitee") { - return `https://gitee.com/oauth/authorize?client_id=${clientID}&redirect_uri=${redirect_uri(type)}&response_type=code`; - } else { - return ""; - } + // TODO 暂时还不知道state干嘛的 + if (type === "github") { + return `${url}/login/oauth/authorize?client_id=${clientID}&redirect_uri=${redirect_uri(type)}&response_type=code&scope=user&state=STATE`; + } else if (type === "gitlab") { + return `https://gitlab.com/oauth/authorize?client_id=${clientID}&redirect_uri=${redirect_uri(type)}&response_type=code&state=STATE`; + } else if (type === "gitee") { + return `https://gitee.com/oauth/authorize?client_id=${clientID}&redirect_uri=${redirect_uri(type)}&response_type=code`; + } else { + return ""; + } }; diff --git a/packages/canyon-platform/src/helpers/utils/common.ts b/packages/canyon-platform/src/helpers/utils/common.ts index 75f6a534..1cbe93b7 100644 --- a/packages/canyon-platform/src/helpers/utils/common.ts +++ b/packages/canyon-platform/src/helpers/utils/common.ts @@ -1,31 +1,31 @@ // 用于文件base64解码后的格式化 export function getDecode(str: string) { - return decodeURIComponent( - atob(str) - .split("") - .map(function (c) { - return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); - }) - .join(""), - ); + return decodeURIComponent( + atob(str) + .split("") + .map(function (c) { + return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); + }) + .join(""), + ); } export function getCOlor(num) { - if (num >= 80) { - return "rgb(33,181,119)"; - } else if (num >= 50) { - return "rgb(244,176,27)"; - } else { - return "rgb(245,32,32)"; - } + if (num >= 80) { + return "rgb(33,181,119)"; + } else if (num >= 50) { + return "rgb(244,176,27)"; + } else { + return "rgb(245,32,32)"; + } } export function percent(covered, total) { - let tmp; - if (total > 0) { - tmp = (1000 * 100 * covered) / total; - return Math.floor(tmp / 10) / 100; - } else { - return 100.0; - } + let tmp; + if (total > 0) { + tmp = (1000 * 100 * covered) / total; + return Math.floor(tmp / 10) / 100; + } else { + return 100.0; + } } diff --git a/packages/canyon-platform/src/helpers/utils/getViewLineHeight.tsx b/packages/canyon-platform/src/helpers/utils/getViewLineHeight.tsx index 47b1cea2..07ad11f7 100644 --- a/packages/canyon-platform/src/helpers/utils/getViewLineHeight.tsx +++ b/packages/canyon-platform/src/helpers/utils/getViewLineHeight.tsx @@ -1,3 +1,3 @@ export const getViewLineHeight = () => { - return 20; + return 20; }; diff --git a/packages/canyon-platform/src/helpers/utils/reportCoverage.ts b/packages/canyon-platform/src/helpers/utils/reportCoverage.ts index d7843637..a070b950 100644 --- a/packages/canyon-platform/src/helpers/utils/reportCoverage.ts +++ b/packages/canyon-platform/src/helpers/utils/reportCoverage.ts @@ -1,22 +1,22 @@ export function reportCoverage() { - try { + try { + // @ts-ignore + const canyon = Object.values(window.__coverage__)[0] as any; + return fetch(canyon.dsn, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${localStorage.getItem("token")}`, + }, + method: "POST", + body: JSON.stringify({ + ...canyon, // @ts-ignore - const canyon = Object.values(window.__coverage__)[0] as any; - return fetch(canyon.dsn,{ - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, - method: "POST", - body: JSON.stringify({ - ...canyon, - // @ts-ignore - coverage: window.__coverage__, - reportID: localStorage.getItem("username")||undefined, - }), - }) - } catch (e) { - console.log(e); - return Promise.resolve(); - } + coverage: window.__coverage__, + reportID: localStorage.getItem("username") || undefined, + }), + }); + } catch (e) { + console.log(e); + return Promise.resolve(); + } } diff --git a/packages/canyon-platform/src/i18n.ts b/packages/canyon-platform/src/i18n.ts index 05d8037e..09e12baf 100644 --- a/packages/canyon-platform/src/i18n.ts +++ b/packages/canyon-platform/src/i18n.ts @@ -6,26 +6,27 @@ import en from "../locales/en.json"; import ja from "../locales/ja.json"; const resources = { - cn: { - translation: cn, - }, - en: { - translation: en, - }, - ja: { - translation: ja, - }, + cn: { + translation: cn, + }, + en: { + translation: en, + }, + ja: { + translation: ja, + }, }; -i18n.use(LanguageDetector) - .use(initReactI18next) - .init({ - fallbackLng: "en", - interpolation: { - escapeValue: false, - }, - resources: resources, - lng: localStorage.getItem("language") || "cn", - }); +i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + fallbackLng: "en", + interpolation: { + escapeValue: false, + }, + resources: resources, + lng: localStorage.getItem("language") || "cn", + }); export default i18n; diff --git a/packages/canyon-platform/src/layouts/genBreadcrumbItems.tsx b/packages/canyon-platform/src/layouts/genBreadcrumbItems.tsx index fb831ae0..2d4bd853 100755 --- a/packages/canyon-platform/src/layouts/genBreadcrumbItems.tsx +++ b/packages/canyon-platform/src/layouts/genBreadcrumbItems.tsx @@ -1,107 +1,94 @@ import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; function matchPattern(str: string) { - if ( - str.includes("projects") && - str.split("/").length === 3 && - !["new"].includes(str.split("/")[2]) - ) { - return true; - } + if ( + str.includes("projects") && + str.split("/").length === 3 && + !["new"].includes(str.split("/")[2]) + ) { + return true; + } } export function genBreadcrumbItems(pathname: string) { - // eslint-disable-next-line react-hooks/rules-of-hooks - const { t } = useTranslation(); - // eslint-disable-next-line react-hooks/rules-of-hooks - const nav = useNavigate(); - if (matchPattern(pathname)) { - return [ - { - title: ( - - {t("menus.projects")} - - ), - onClick() { - nav("/projects"); - }, - }, - { - title: t("projects.overview"), - }, - ]; - } else if (pathname.includes("commits")) { - return [ - { - title: ( - - {t("menus.projects")} - - ), - onClick() { - nav("/projects"); - }, - }, - { - title: ( - - {t("projects.overview")} - - ), - onClick() { - const regex = /\/projects\/(.+?)\//; - // const regex = /\/projects\/(\d+)\//; - const match = pathname.match(regex); - if (match) { - const projectId = match[1]; - nav(`/projects/${projectId}`); - } else { - console.log("未找到匹配的项目ID"); - } - }, - }, - { - title: t("projects.coverage_details"), - // title: 'Coverage Details', - }, - ]; - } else if (pathname.includes("settings")&&pathname.split("/").length===4) { - return [ - { - title: ( - - {t("menus.projects")} - - ), - onClick() { - nav("/projects"); - }, - }, - { - title: ( - - {t("projects.overview")} - - ), - onClick() { - const regex = /\/projects\/(.+?)\//; - // const regex = /\/projects\/(\d+)\//; - const match = pathname.match(regex); - if (match) { - const projectId = match[1]; - nav(`/projects/${projectId}`); - } else { - console.log("未找到匹配的项目ID"); - } - }, - }, - { - title: "项目配置", - // title: 'Coverage Details', - }, - ]; - } else { - return []; - } + // eslint-disable-next-line react-hooks/rules-of-hooks + const { t } = useTranslation(); + // eslint-disable-next-line react-hooks/rules-of-hooks + const nav = useNavigate(); + if (matchPattern(pathname)) { + return [ + { + title: {t("menus.projects")}, + onClick() { + nav("/projects"); + }, + }, + { + title: t("projects.overview"), + }, + ]; + } else if (pathname.includes("commits")) { + return [ + { + title: {t("menus.projects")}, + onClick() { + nav("/projects"); + }, + }, + { + title: ( + {t("projects.overview")} + ), + onClick() { + const regex = /\/projects\/(.+?)\//; + // const regex = /\/projects\/(\d+)\//; + const match = pathname.match(regex); + if (match) { + const projectId = match[1]; + nav(`/projects/${projectId}`); + } else { + console.log("未找到匹配的项目ID"); + } + }, + }, + { + title: t("projects.coverage_details"), + // title: 'Coverage Details', + }, + ]; + } else if ( + pathname.includes("settings") && + pathname.split("/").length === 4 + ) { + return [ + { + title: {t("menus.projects")}, + onClick() { + nav("/projects"); + }, + }, + { + title: ( + {t("projects.overview")} + ), + onClick() { + const regex = /\/projects\/(.+?)\//; + // const regex = /\/projects\/(\d+)\//; + const match = pathname.match(regex); + if (match) { + const projectId = match[1]; + nav(`/projects/${projectId}`); + } else { + console.log("未找到匹配的项目ID"); + } + }, + }, + { + title: "项目配置", + // title: 'Coverage Details', + }, + ]; + } else { + return []; + } } diff --git a/packages/canyon-platform/src/layouts/genTitle.ts b/packages/canyon-platform/src/layouts/genTitle.ts index d9ce4d13..8a99a884 100644 --- a/packages/canyon-platform/src/layouts/genTitle.ts +++ b/packages/canyon-platform/src/layouts/genTitle.ts @@ -1,21 +1,24 @@ function matchPattern(str: string) { - if ( - str.includes("projects") && - str.split("/").length === 3 && - !["new"].includes(str.split("/")[2]) - ) { - return true; - } + if ( + str.includes("projects") && + str.split("/").length === 3 && + !["new"].includes(str.split("/")[2]) + ) { + return true; + } } export const genTitle = (pathname: string) => { - if (matchPattern(pathname)) { - return `${pathname.split("/")[2].split("-")[1]} | Overview | Canyon`; - } else if (pathname.includes("commits")) { - return `${pathname.split("/")[2].split("-")[1]} | Coverage Details | Canyon`; - } else if (pathname.includes("settings")&&pathname.split("/").length===4) { - return `${pathname.split("/")[2].split("-")[1]} | Settings | Canyon`; - } else if (pathname.includes("settings")) { - return `Settings | Canyon`; - } - return `Canyon`; + if (matchPattern(pathname)) { + return `${pathname.split("/")[2].split("-")[1]} | Overview | Canyon`; + } else if (pathname.includes("commits")) { + return `${pathname.split("/")[2].split("-")[1]} | Coverage Details | Canyon`; + } else if ( + pathname.includes("settings") && + pathname.split("/").length === 4 + ) { + return `${pathname.split("/")[2].split("-")[1]} | Settings | Canyon`; + } else if (pathname.includes("settings")) { + return `Settings | Canyon`; + } + return `Canyon`; }; diff --git a/packages/canyon-platform/src/lib/utils.ts b/packages/canyon-platform/src/lib/utils.ts index e6447944..365058ce 100644 --- a/packages/canyon-platform/src/lib/utils.ts +++ b/packages/canyon-platform/src/lib/utils.ts @@ -2,5 +2,5 @@ import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); + return twMerge(clsx(inputs)); } diff --git a/packages/canyon-platform/src/main.tsx b/packages/canyon-platform/src/main.tsx index 1976d024..e5244b2f 100644 --- a/packages/canyon-platform/src/main.tsx +++ b/packages/canyon-platform/src/main.tsx @@ -5,10 +5,10 @@ import "antd/dist/reset.css"; import "./index.css"; import { - ApolloClient, - ApolloProvider, - createHttpLink, - InMemoryCache, + ApolloClient, + ApolloProvider, + createHttpLink, + InMemoryCache, } from "@apollo/client"; import { onError } from "@apollo/client/link/error"; import ReactDOM from "react-dom/client"; @@ -18,54 +18,54 @@ import App from "./App.tsx"; // 创建一个error link来处理错误 const errorLink = onError(({ graphQLErrors, networkError }) => { - if (graphQLErrors) { - graphQLErrors.forEach(({ message: msg, locations, path }) => { - console.error( - `[GraphQL error]: msg: ${msg}, Location: ${locations}, Path: ${path}`, - ); - message.error(`[GraphQL error]: msg: ${msg}, Path: ${path}`); - if ( - msg === "Unauthorized" && - window.location.pathname !== "/oauth" && - window.location.pathname !== "/login" - ) { - localStorage.clear(); - window.location.href = "/login"; - } - // 在这里你可以执行自定义的操作,比如显示错误提示 - }); - } - if (networkError) { - console.error(`[Network error]: ${networkError}`); - // 在这里你可以执行自定义的操作,比如显示网络错误提示 - } + if (graphQLErrors) { + graphQLErrors.forEach(({ message: msg, locations, path }) => { + console.error( + `[GraphQL error]: msg: ${msg}, Location: ${locations}, Path: ${path}`, + ); + message.error(`[GraphQL error]: msg: ${msg}, Path: ${path}`); + if ( + msg === "Unauthorized" && + window.location.pathname !== "/oauth" && + window.location.pathname !== "/login" + ) { + localStorage.clear(); + window.location.href = "/login"; + } + // 在这里你可以执行自定义的操作,比如显示错误提示 + }); + } + if (networkError) { + console.error(`[Network error]: ${networkError}`); + // 在这里你可以执行自定义的操作,比如显示网络错误提示 + } }); // 创建一个http link来发送GraphQL请求 const httpLink = createHttpLink({ - uri: "/graphql", // 你的GraphQL API的URL + uri: "/graphql", // 你的GraphQL API的URL - headers: { - Authorization: `Bearer ` + (localStorage.getItem("token") || ""), - }, + headers: { + Authorization: `Bearer ` + (localStorage.getItem("token") || ""), + }, }); // 创建Apollo Client实例 const client = new ApolloClient({ - link: errorLink.concat(httpLink), // 将error link和http link组合起来 - cache: new InMemoryCache(), + link: errorLink.concat(httpLink), // 将error link和http link组合起来 + cache: new InMemoryCache(), }); if (localStorage.getItem("theme") === "dark") { - document.documentElement.classList.add("dark"); + document.documentElement.classList.add("dark"); } else { - document.documentElement.classList.remove("dark"); + document.documentElement.classList.remove("dark"); } ReactDOM.createRoot(document.getElementById("root")!).render( - - - - - , + + + + + , ); diff --git a/packages/canyon-platform/src/pages/index.tsx b/packages/canyon-platform/src/pages/index.tsx index c8eba092..62d538e7 100644 --- a/packages/canyon-platform/src/pages/index.tsx +++ b/packages/canyon-platform/src/pages/index.tsx @@ -1,8 +1,8 @@ import { - ArrowRightOutlined, - FolderOutlined, - LogoutOutlined, - SettingOutlined, + ArrowRightOutlined, + FolderOutlined, + LogoutOutlined, + SettingOutlined, } from "@ant-design/icons"; import { useQuery } from "@apollo/client"; import { useRequest } from "ahooks"; @@ -11,143 +11,139 @@ import { useTranslation } from "react-i18next"; import book from "../assets/book.svg"; import { - CanyonLayoutBase, - CanyonModalGlobalSearch, + CanyonLayoutBase, + CanyonModalGlobalSearch, } from "../components/old-ui"; import { MeDocument } from "../helpers/backend/gen/graphql.ts"; import { genBreadcrumbItems } from "../layouts/genBreadcrumbItems.tsx"; import { genTitle } from "../layouts/genTitle.ts"; -import {reportCoverage} from "@/helpers/utils/reportCoverage.ts"; +import { reportCoverage } from "@/helpers/utils/reportCoverage.ts"; function Index() { - const { t } = useTranslation(); - const loc = useLocation(); - const nav = useNavigate(); + const { t } = useTranslation(); + const loc = useLocation(); + const nav = useNavigate(); - useEffect(() => { - setMenuSelectedKey(loc.pathname.replace("/", "")); - document.title = genTitle(loc.pathname); - if (localStorage.getItem("token") === null) { - localStorage.clear(); - localStorage.setItem("callback", window.location.href); - nav("/login"); - } else if (loc.pathname === "/"){ - nav("/projects"); - } - reportCoverage(); - }, [loc.pathname]); + useEffect(() => { + setMenuSelectedKey(loc.pathname.replace("/", "")); + document.title = genTitle(loc.pathname); + if (localStorage.getItem("token") === null) { + localStorage.clear(); + localStorage.setItem("callback", window.location.href); + nav("/login"); + } else if (loc.pathname === "/") { + nav("/projects"); + } + reportCoverage(); + }, [loc.pathname]); - const { data: meData } = useQuery(MeDocument); - useEffect(() => { - localStorage.setItem("username", meData?.me.email || ""); - }, [meData]); - const { data: baseData } = useRequest( - () => axios.get("/api/base").then(({ data }) => data), - { - onSuccess(data) { - // @ts-ignore - window.GITLAB_URL = data.GITLAB_URL; - }, - }, - ); - const [menuSelectedKey, setMenuSelectedKey] = useState("projects"); - // @ts-ignore - window.canyonModalGlobalSearchRef = useRef(null); - return ( - <> - {/**/} - - {/*榜单mark*/} - - - } - itemsDropdown={[ - { - label: ( -
- - Logout -
- ), - onClick: () => { - localStorage.clear(); - window.location.href = "/login"; - }, - }, - ]} - MeData={meData} - onClickGlobalSearch={() => { - // @ts-ignore - window.canyonModalGlobalSearchRef.current.report(); - }} - title={"Canyon"} - logo={ -
- -
- } - mainTitleRightNode={ -
- - {t("menus.docs")} - -
- } - > - - {/* eslint-disable-next-line jsx-a11y/alt-text */} - - - - {/*marker position*/} - - } - menuSelectedKey={menuSelectedKey} - onSelectMenu={(selectInfo) => { - setMenuSelectedKey(selectInfo.key); - nav(`/${selectInfo.key}`); - }} - menuItems={[ - { - label: t("menus.projects"), - key: "projects", - icon: , - }, - // { - // label: t("报表"), - // key: "reports", - // icon: , - // }, - { - label: t("menus.settings"), - key: "settings", - icon: , - }, - ]} - renderMainContent={} - search={false} - account={false} + const { data: meData } = useQuery(MeDocument); + useEffect(() => { + localStorage.setItem("username", meData?.me.email || ""); + }, [meData]); + const { data: baseData } = useRequest( + () => axios.get("/api/base").then(({ data }) => data), + { + onSuccess(data) { + // @ts-ignore + window.GITLAB_URL = data.GITLAB_URL; + }, + }, + ); + const [menuSelectedKey, setMenuSelectedKey] = useState("projects"); + // @ts-ignore + window.canyonModalGlobalSearchRef = useRef(null); + return ( + <> + {/**/} + + {/*榜单mark*/} + - {/*// @ts-ignore*/} - - - ); + + } + itemsDropdown={[ + { + label: ( +
+ + Logout +
+ ), + onClick: () => { + localStorage.clear(); + window.location.href = "/login"; + }, + }, + ]} + MeData={meData} + onClickGlobalSearch={() => { + // @ts-ignore + window.canyonModalGlobalSearchRef.current.report(); + }} + title={"Canyon"} + logo={ +
+ +
+ } + mainTitleRightNode={ +
+ + {t("menus.docs")} + +
+ } + > + + {/* eslint-disable-next-line jsx-a11y/alt-text */} + + + + {/*marker position*/} + + } + menuSelectedKey={menuSelectedKey} + onSelectMenu={(selectInfo) => { + setMenuSelectedKey(selectInfo.key); + nav(`/${selectInfo.key}`); + }} + menuItems={[ + { + label: t("menus.projects"), + key: "projects", + icon: , + }, + // { + // label: t("报表"), + // key: "reports", + // icon: , + // }, + { + label: t("menus.settings"), + key: "settings", + icon: , + }, + ]} + renderMainContent={} + search={false} + account={false} + /> + {/*// @ts-ignore*/} + + + ); } export default Index; diff --git a/packages/canyon-platform/src/pages/index/projects/[id]/commits/[sha].tsx b/packages/canyon-platform/src/pages/index/projects/[id]/commits/[sha].tsx index 274000e0..a86205b6 100755 --- a/packages/canyon-platform/src/pages/index/projects/[id]/commits/[sha].tsx +++ b/packages/canyon-platform/src/pages/index/projects/[id]/commits/[sha].tsx @@ -1,10 +1,10 @@ import { useQuery } from "@apollo/client"; import { useRequest } from "ahooks"; import { - useLocation, - useNavigate, - useParams, - useSearchParams, + useLocation, + useNavigate, + useParams, + useSearchParams, } from "react-router-dom"; import CanyonReport from "../../../../../components/CanyonReport"; @@ -13,103 +13,101 @@ import { getCoverageSummaryMapService, handleSelectFile } from "./helper"; const { useToken } = theme; const Sha = () => { - const prm = useParams(); - const nav = useNavigate(); - const [sprm] = useSearchParams(); - // 在组件中 - const location = useLocation(); - const currentPathname = location.pathname; - const { data: getProjectByIdDocumentData } = useQuery( - GetProjectByIdDocument, - { - variables: { - projectID: prm["id"] as string, - }, - }, - ); - const pathWithNamespace = - getProjectByIdDocumentData?.getProjectByID.pathWithNamespace.split( - "/", - )[1]; - const { token } = useToken(); + const prm = useParams(); + const nav = useNavigate(); + const [sprm] = useSearchParams(); + // 在组件中 + const location = useLocation(); + const currentPathname = location.pathname; + const { data: getProjectByIdDocumentData } = useQuery( + GetProjectByIdDocument, + { + variables: { + projectID: prm["id"] as string, + }, + }, + ); + const pathWithNamespace = + getProjectByIdDocumentData?.getProjectByID.pathWithNamespace.split("/")[1]; + const { token } = useToken(); - const { data: coverageSummaryMapData, loading } = useRequest( - () => - getCoverageSummaryMapService({ - projectID: prm.id as string, - reportID: sprm.get("report_id"), - sha: prm.sha, - }), - { - onSuccess() {}, - }, - ); + const { data: coverageSummaryMapData, loading } = useRequest( + () => + getCoverageSummaryMapService({ + projectID: prm.id as string, + reportID: sprm.get("report_id"), + sha: prm.sha, + }), + { + onSuccess() {}, + }, + ); - const [activatedPath, setActivatedPath] = useState(sprm.get("path") || ""); - const [mainData, setMainData] = useState(false); + const [activatedPath, setActivatedPath] = useState(sprm.get("path") || ""); + const [mainData, setMainData] = useState(false); - useEffect(() => { - const params = new URLSearchParams(); - if (sprm.get("report_id")) { - params.append("report_id", sprm.get("report_id") || ""); - } - if (sprm.get("mode")) { - params.append("mode", sprm.get("mode") || ""); - } - params.append("path", activatedPath); + useEffect(() => { + const params = new URLSearchParams(); + if (sprm.get("report_id")) { + params.append("report_id", sprm.get("report_id") || ""); + } + if (sprm.get("mode")) { + params.append("mode", sprm.get("mode") || ""); + } + params.append("path", activatedPath); - // 将参数拼接到路径中 - const pathWithParams = `${currentPathname}?${params.toString()}${location.hash}`; + // 将参数拼接到路径中 + const pathWithParams = `${currentPathname}?${params.toString()}${location.hash}`; - nav(pathWithParams); + nav(pathWithParams); - if (activatedPath.includes(".")) { - handleSelectFile({ - filepath: activatedPath, - reportID: sprm.get("report_id"), - sha: prm.sha || "", - projectID: prm.id || "", - // mode: sprm.get("mode") || "", - }).then((r) => { - if (r.fileCoverage) { - // console.log(r) - setMainData(r); - } else { - setMainData(false); - } - }); + if (activatedPath.includes(".")) { + handleSelectFile({ + filepath: activatedPath, + reportID: sprm.get("report_id"), + sha: prm.sha || "", + projectID: prm.id || "", + // mode: sprm.get("mode") || "", + }).then((r) => { + if (r.fileCoverage) { + // console.log(r) + setMainData(r); } else { - // console.log('设么也不做'); - setMainData(false); + setMainData(false); } - }, [activatedPath]); + }); + } else { + // console.log('设么也不做'); + setMainData(false); + } + }, [activatedPath]); - // @ts-ignore - return ( + // @ts-ignore + return ( + <> +
<> -
- <> - { - setActivatedPath(v.path); - }} - /> - -
+ { + setActivatedPath(v.path); + }} + /> - ); +
+ + ); }; export default Sha; diff --git a/packages/canyon-platform/src/pages/index/projects/[id]/commits/helper/index.ts b/packages/canyon-platform/src/pages/index/projects/[id]/commits/helper/index.ts index cfd397e7..9d01a85f 100755 --- a/packages/canyon-platform/src/pages/index/projects/[id]/commits/helper/index.ts +++ b/packages/canyon-platform/src/pages/index/projects/[id]/commits/helper/index.ts @@ -3,74 +3,74 @@ import axios from "axios"; import { getDecode } from "@/helpers/utils/common.ts"; interface HandleSelectFile { - projectID: string; - sha: string; - filepath?: string|null; - reportID?: string|null; + projectID: string; + sha: string; + filepath?: string | null; + reportID?: string | null; } export function handleSelectFile({ - projectID, - sha, - filepath, - reportID, + projectID, + sha, + filepath, + reportID, }: HandleSelectFile) { - const fileContentRequest = axios - .get(`/api/sourcecode`, { - params: { - projectID: projectID, - sha: sha, - filepath: filepath, - }, - }) - .then(({ data }) => data); - const fileCoverageRequest = axios - .get(`/api/coverage/map`, { - params: { - projectID, - reportID: reportID, - sha: sha, - filepath: filepath, - }, - }) - .then(({ data }) => data[filepath||'']); + const fileContentRequest = axios + .get(`/api/sourcecode`, { + params: { + projectID: projectID, + sha: sha, + filepath: filepath, + }, + }) + .then(({ data }) => data); + const fileCoverageRequest = axios + .get(`/api/coverage/map`, { + params: { + projectID, + reportID: reportID, + sha: sha, + filepath: filepath, + }, + }) + .then(({ data }) => data[filepath || ""]); - const fileCodeChangeRequest = axios - .get(`/api/codechange`, { - // operationName: 'GetCodeChange', - params: { - sha: sha, - filepath: filepath, - }, - }) - .then(({ data }) => data); - return Promise.all([ - fileContentRequest, - fileCoverageRequest, - fileCodeChangeRequest, - ]).then(([fileContent, fileCoverage, fileCodeChange]) => { - return { - fileContent: getDecode(fileContent.content), - fileCoverage: fileCoverage, - fileCodeChange: fileCodeChange.additions || [], - }; - }); + const fileCodeChangeRequest = axios + .get(`/api/codechange`, { + // operationName: 'GetCodeChange', + params: { + sha: sha, + filepath: filepath, + }, + }) + .then(({ data }) => data); + return Promise.all([ + fileContentRequest, + fileCoverageRequest, + fileCodeChangeRequest, + ]).then(([fileContent, fileCoverage, fileCodeChange]) => { + return { + fileContent: getDecode(fileContent.content), + fileCoverage: fileCoverage, + fileCodeChange: fileCodeChange.additions || [], + }; + }); } // @ts-ignore export const getCoverageSummaryMapService = ({ projectID, sha, reportID }) => - axios({ - url: "/api/coverage/summary/v2/map", - method: "GET", - params: { - reportID: reportID, - sha: sha, - projectID: projectID, - }, - }) - .then(({ data }) => data) - .then((r) => { - return Object.values(r).reduce((acc:any[], cur:any) => { - acc.push(cur); - return acc; - }, []); - }); + axios({ + url: "/api/coverage/summary/v2/map", + method: "GET", + params: { + reportID: reportID, + sha: sha, + projectID: projectID, + }, + }) + .then(({ data }) => data) + .then((r) => { + return Object.values(r).reduce((acc: any[], cur: any) => { + acc.push(cur); + return acc; + }, []); + }); diff --git a/packages/canyon-platform/src/pages/index/projects/[id]/getting-started.tsx b/packages/canyon-platform/src/pages/index/projects/[id]/getting-started.tsx index 78d3f2f7..03145595 100755 --- a/packages/canyon-platform/src/pages/index/projects/[id]/getting-started.tsx +++ b/packages/canyon-platform/src/pages/index/projects/[id]/getting-started.tsx @@ -8,43 +8,42 @@ const { Title, Text } = Typography; // import CopyCode from '../components/CopyCode.tsx'; // import { gettingStartedContent } from '../components/getting- started-content.ts'; const ProjectGettingStarted = () => { - const nav = useNavigate(); - const { data } = useRequest(() => - axios.get("/api/base").then(({ data }) => data), - ); - return ( -
- Configure Babel - - Install - - Add the canyon and istanbul babel plugin as a dependency using - npm: - - - Configure Babel - Add the following configuration to babel: - - Next Steps - - - - -
- ); + const nav = useNavigate(); + const { data } = useRequest(() => + axios.get("/api/base").then(({ data }) => data), + ); + return ( +
+ Configure Babel + + Install + + Add the canyon and istanbul babel plugin as a dependency using npm: + + + Configure Babel + Add the following configuration to babel: + + Next Steps + + + + +
+ ); }; export default ProjectGettingStarted; diff --git a/packages/canyon-platform/src/pages/index/projects/[id]/index.tsx b/packages/canyon-platform/src/pages/index/projects/[id]/index.tsx index 616868df..bf7cbba7 100755 --- a/packages/canyon-platform/src/pages/index/projects/[id]/index.tsx +++ b/packages/canyon-platform/src/pages/index/projects/[id]/index.tsx @@ -1,8 +1,8 @@ import Icon, { - AimOutlined, - BranchesOutlined, - EditOutlined, - QuestionCircleOutlined, + AimOutlined, + BranchesOutlined, + EditOutlined, + QuestionCircleOutlined, } from "@ant-design/icons"; import { useMutation, useQuery } from "@apollo/client"; import { TourProps } from "antd"; @@ -16,597 +16,557 @@ import { Link, useNavigate, useParams } from "react-router-dom"; import ProjectRecordDetailDrawer from "../../../../components/app/ProjectRecordDetailDrawer.tsx"; import MaterialSymbolsCommitSharp from "../../../../components/icons/MaterialSymbolsCommitSharp.tsx"; import { - DeleteProjectRecordDocument, - GetProjectByIdDocument, - GetProjectChartDataDocument, - GetProjectCompartmentDataDocument, - GetProjectRecordsDocument, - ProjectRecordsModel, + DeleteProjectRecordDocument, + GetProjectByIdDocument, + GetProjectChartDataDocument, + GetProjectCompartmentDataDocument, + GetProjectRecordsDocument, + ProjectRecordsModel, } from "@/helpers/backend/gen/graphql.ts"; const { useToken } = theme; const { Title, Text } = Typography; const ProjectOverviewPage = () => { - const { token } = useToken(); - const { t } = useTranslation(); - const [keyword, setKeyword] = useState(""); - const [current, setCurrent] = useState(1); - const [pageSize, setPageSize] = useState(10); - const [open, setOpen] = useState(false); - const [sha, setSha] = useState(""); - const initDefaultBranchOnly = Boolean( - localStorage.getItem("defaultBranchOnly"), - ); - const [defaultBranchOnly, setDefaultBranchOnly] = useState( - initDefaultBranchOnly, - ); - const onClose = () => { - setOpen(false); - }; + const { token } = useToken(); + const { t } = useTranslation(); + const [keyword, setKeyword] = useState(""); + const [current, setCurrent] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [open, setOpen] = useState(false); + const [sha, setSha] = useState(""); + const initDefaultBranchOnly = Boolean( + localStorage.getItem("defaultBranchOnly"), + ); + const [defaultBranchOnly, setDefaultBranchOnly] = useState( + initDefaultBranchOnly, + ); + const onClose = () => { + setOpen(false); + }; - const pam = useParams(); + const pam = useParams(); - const { data: projectsData, loading } = useQuery( - GetProjectRecordsDocument, - { - variables: { - projectID: pam.id as string, - current: current, - pageSize: pageSize, - keyword: keyword, - onlyDefault: defaultBranchOnly, - }, - fetchPolicy: "no-cache", - }, - ); + const { data: projectsData, loading } = useQuery(GetProjectRecordsDocument, { + variables: { + projectID: pam.id as string, + current: current, + pageSize: pageSize, + keyword: keyword, + onlyDefault: defaultBranchOnly, + }, + fetchPolicy: "no-cache", + }); - const { data: projectByIdData } = useQuery(GetProjectByIdDocument, { - variables: { - projectID: pam.id as string, - }, - fetchPolicy: "no-cache", - }); + const { data: projectByIdData } = useQuery(GetProjectByIdDocument, { + variables: { + projectID: pam.id as string, + }, + fetchPolicy: "no-cache", + }); - const { data: projectChartData, loading: projectChartDataLoading } = - useQuery(GetProjectChartDataDocument, { - variables: { - projectID: pam.id as string, - branch: "-", - }, - fetchPolicy: "no-cache", - }); + const { data: projectChartData, loading: projectChartDataLoading } = useQuery( + GetProjectChartDataDocument, + { + variables: { + projectID: pam.id as string, + branch: "-", + }, + fetchPolicy: "no-cache", + }, + ); - const { - data: projectCompartmentDataData, - loading: projectCompartmentDataLoading, - } = useQuery(GetProjectCompartmentDataDocument, { - variables: { - projectID: pam.id as string, - }, - fetchPolicy: "no-cache", - }); - const [deleteProjectRecord] = useMutation(DeleteProjectRecordDocument); - const ref1 = useRef(null); - const ref2 = useRef(null); - const [tourOpen, setTourOpen] = useState(false); - const steps: TourProps["steps"] = [ - { - title: t("projects.statements_tour_title"), - description: t("projects.statements_tour_description"), - target: () => ref1.current, - }, - { - title: t("projects.newlines_tour_title"), - description: t("projects.newlines_tour_description"), - target: () => ref2.current, - }, - ]; + const { + data: projectCompartmentDataData, + loading: projectCompartmentDataLoading, + } = useQuery(GetProjectCompartmentDataDocument, { + variables: { + projectID: pam.id as string, + }, + fetchPolicy: "no-cache", + }); + const [deleteProjectRecord] = useMutation(DeleteProjectRecordDocument); + const ref1 = useRef(null); + const ref2 = useRef(null); + const [tourOpen, setTourOpen] = useState(false); + const steps: TourProps["steps"] = [ + { + title: t("projects.statements_tour_title"), + description: t("projects.statements_tour_description"), + target: () => ref1.current, + }, + { + title: t("projects.newlines_tour_title"), + description: t("projects.newlines_tour_description"), + target: () => ref2.current, + }, + ]; - useEffect(() => { - if (!localStorage.getItem("touropen")) { - setTimeout(() => { - setTourOpen(true); - localStorage.setItem("touropen", "true"); - }, 2000); - } - }, []); + useEffect(() => { + if (!localStorage.getItem("touropen")) { + setTimeout(() => { + setTourOpen(true); + localStorage.setItem("touropen", "true"); + }, 2000); + } + }, []); - const columns: ColumnsType = [ - { - title: ( -
- - Sha -
- ), - dataIndex: "sha", - width: "100px", - render(_, { webUrl }): JSX.Element { - return ( - - {_?.slice(0, 7)} - - ); - }, - }, - { - title: ( - <> - - {t("projects.branch")} - - ), - dataIndex: "branch", - ellipsis: true, - width: "120px", - }, - { - title: ( - <> - - {t("projects.compare_target")} - - ), - dataIndex: "compareTarget", - width: "120px", - render(_, { compareUrl }): JSX.Element { - return ( - - {_.length === 40 ? _.slice(0, 7) : _} - - ); - }, - }, - { - title: <>{t("Build")}, - align: "center", - width: "70px", - render(_, record) { - return ( - <> - {record.buildID !== "-" ? ( - - - - ) : ( - - - )} - - ); - }, - }, - { - title: t("projects.message"), - dataIndex: "message", - width: "160px", - ellipsis: true, - }, - { - title: ( -
- - - - {t("projects.statements")} -
- ), - dataIndex: "statements", - // width: '148px', - render(_, { sha }) { - return ( - - {_}% - - ); - }, - // width: '150px', - // render(_, { sha }) { - // return ( - //
- // - // {_}% - // - // - // coverage - // - // - //
- // ); - // }, - }, - { - title: ( -
- - - - {t("projects.newlines")} -
- ), - dataIndex: "newlines", - // width: '130px', - render(_, { sha }) { - return ( - - {_}% - - ); - }, - }, - { - title: t("projects.report_times"), - dataIndex: "times", - width: "80px", - }, - { - title: t("projects.latest_report_time"), - dataIndex: "lastReportTime", - width: "135px", - render(_) { - return {dayjs(_).format("MM-DD HH:mm")}; - }, - }, - { - title: t("common.option"), - width: "125px", - render(_): JSX.Element { - return ( -
- { - setOpen(true); - setSha(_.sha); - }} - > - {t("projects.reported_details")} - - - - { - deleteProjectRecord({ - variables: { - projectID: pam.id as string, - sha: _.sha, - }, - }) - .then(() => { - window.location.reload(); - }) - .catch((err) => { - console.log(err); - }); - }} - onCancel={() => { - console.log("cancel"); - }} - okText="Yes" - cancelText="No" - > - - {t("common.delete")} - - -
- ); - }, - }, - ]; + const columns: ColumnsType = [ + { + title: ( +
+ + Sha +
+ ), + dataIndex: "sha", + width: "100px", + render(_, { webUrl }): JSX.Element { + return ( + + {_?.slice(0, 7)} + + ); + }, + }, + { + title: ( + <> + + {t("projects.branch")} + + ), + dataIndex: "branch", + ellipsis: true, + width: "120px", + }, + { + title: ( + <> + + {t("projects.compare_target")} + + ), + dataIndex: "compareTarget", + width: "120px", + render(_, { compareUrl }): JSX.Element { + return ( + + {_.length === 40 ? _.slice(0, 7) : _} + + ); + }, + }, + { + title: <>{t("Build")}, + align: "center", + width: "70px", + render(_, record) { + return ( + <> + {record.buildID !== "-" ? ( + + + + ) : ( + - + )} + + ); + }, + }, + { + title: t("projects.message"), + dataIndex: "message", + width: "160px", + ellipsis: true, + }, + { + title: ( +
+ + + + {t("projects.statements")} +
+ ), + dataIndex: "statements", + // width: '148px', + render(_, { sha }) { + return ( + + {_}% + + ); + }, + // width: '150px', + // render(_, { sha }) { + // return ( + //
+ // + // {_}% + // + // + // coverage + // + // + //
+ // ); + // }, + }, + { + title: ( +
+ + + + {t("projects.newlines")} +
+ ), + dataIndex: "newlines", + // width: '130px', + render(_, { sha }) { + return ( + + {_}% + + ); + }, + }, + { + title: t("projects.report_times"), + dataIndex: "times", + width: "80px", + }, + { + title: t("projects.latest_report_time"), + dataIndex: "lastReportTime", + width: "135px", + render(_) { + return {dayjs(_).format("MM-DD HH:mm")}; + }, + }, + { + title: t("common.option"), + width: "125px", + render(_): JSX.Element { + return ( +
+ { + setOpen(true); + setSha(_.sha); + }} + > + {t("projects.reported_details")} + + - const option = { - backgroundColor: "transparent", - grid: { - top: "30px", - left: "30px", - right: "10px", - bottom: "20px", - }, - tooltip: { - trigger: "axis", - }, - legend: { - x: "right", - data: [t("projects.statements"), t("projects.newlines")], - }, - xAxis: { - type: "category", - data: - projectChartData?.getProjectChartData.map(({ sha }) => - sha.slice(0, 7), - ) || [], - }, - yAxis: { - type: "value", - }, - series: [t("projects.statements"), t("projects.newlines")].map( - (_, index) => ({ - name: _, - data: - projectChartData?.getProjectChartData.map( - ({ statements, newlines }) => - index === 0 ? statements : newlines, - ) || [], - type: "line", - }), - ), - }; - const nav = useNavigate(); - return ( -
-
-
- - {projectByIdData?.getProjectByID.pathWithNamespace} - <EditOutlined - className={"ml-3 cursor-pointer text-[#0071c2]"} - style={{ fontSize: "20px" }} - onClick={() => { - nav(`/projects/${pam.id}/settings`); - }} - /> - -
+ { + deleteProjectRecord({ + variables: { + projectID: pam.id as string, + sha: _.sha, + }, + }) + .then(() => { + window.location.reload(); + }) + .catch((err) => { + console.log(err); + }); + }} + onCancel={() => { + console.log("cancel"); + }} + okText="Yes" + cancelText="No" + > + + {t("common.delete")} + + +
+ ); + }, + }, + ]; -
- - {t("projects.config.project.id")}:{" "} - {projectByIdData?.getProjectByID.id} - - - {t("projects.default.branch")}:{" "} - {projectByIdData?.getProjectByID.defaultBranch} - -
- {(projectByIdData?.getProjectByID.tags || []).length > 0 && ( -
- - {t("projects.config.tag")}: - - {projectByIdData?.getProjectByID.tags.map( - ({ color, name, link }, index) => ( - { - if (link) { - window.open(link); - } - }} - > - {name} - - ), - )} -
- )} -
+ const option = { + backgroundColor: "transparent", + grid: { + top: "30px", + left: "30px", + right: "10px", + bottom: "20px", + }, + tooltip: { + trigger: "axis", + }, + legend: { + x: "right", + data: [t("projects.statements"), t("projects.newlines")], + }, + xAxis: { + type: "category", + data: + projectChartData?.getProjectChartData.map(({ sha }) => + sha.slice(0, 7), + ) || [], + }, + yAxis: { + type: "value", + }, + series: [t("projects.statements"), t("projects.newlines")].map( + (_, index) => ({ + name: _, + data: + projectChartData?.getProjectChartData.map( + ({ statements, newlines }) => (index === 0 ? statements : newlines), + ) || [], + type: "line", + }), + ), + }; + const nav = useNavigate(); + return ( +
+
+
+ + {projectByIdData?.getProjectByID.pathWithNamespace} + <EditOutlined + className={"ml-3 cursor-pointer text-[#0071c2]"} + style={{ fontSize: "20px" }} + onClick={() => { + nav(`/projects/${pam.id}/settings`); + }} + /> + +
- - {t("projects.overview")} +
+ + {t("projects.config.project.id")}:{" "} + {projectByIdData?.getProjectByID.id} + + + {t("projects.default.branch")}:{" "} + {projectByIdData?.getProjectByID.defaultBranch} + +
+ {(projectByIdData?.getProjectByID.tags || []).length > 0 && ( +
+ + {t("projects.config.tag")}: - { - setTourOpen(false); - }} - steps={steps} - /> -
- -
- {( - projectCompartmentDataData?.getProjectCompartmentData || - [] - ).map((item, index) => { - return ( -
- - {t(item.label)} - - - {item.value} - -
- ); - })} -
-
+ {projectByIdData?.getProjectByID.tags.map( + ({ color, name, link }, index) => ( + { + if (link) { + window.open(link); + } + }} + > + {name} + + ), + )} +
+ )} +
-
- -
-
- - {t("projects.trends_in_coverage")} - - - {t("projects.trends.tooltip")} - -
- -
-
-
-
+ + {t("projects.overview")} + + { + setTourOpen(false); + }} + steps={steps} + /> +
+ +
+ {(projectCompartmentDataData?.getProjectCompartmentData || []).map( + (item, index) => { + return ( +
+ {t(item.label)} + {item.value} +
+ ); + }, + )} +
+
- - {t("projects.records")} - +
+
-
- { - setKeyword(value); - setCurrent(1); - }} - style={{ width: "600px" }} - /> - - - {t("projects.only.default.branch")}:{" "} - - { - if (v) { - localStorage.setItem( - "defaultBranchOnly", - "1", - ); - } else { - localStorage.removeItem( - "defaultBranchOnly", - ); - } - setDefaultBranchOnly(v); - }} - /> - -
- -
- {["#1f77b4", "#ff7f0e", "#2ca02c"].map((item, index) => { - return ( -
- - - {{ - 0: "手工测试", - 1: "UI自动化测试", - 2: "单元测试", - }[index] || "unknown"} - -
- ); - })} -
+
+ + {t("projects.trends_in_coverage")} + + + {t("projects.trends.tooltip")} + +
+
- {/*div*/} -
t("common.total_items", { total }), - total: projectsData?.getProjectRecords?.total, - current, - pageSize, - // current: projectsData?.getProjects?.current, - // pageSize: projectsData?.getProjects?.pageSize, - }} - dataSource={projectsData?.getProjectRecords?.data || []} - onChange={(val) => { - setCurrent(val.current || 1); - setPageSize(val.pageSize || 10); - // setKeyword(keyword); - }} - /> + + + - + {t("projects.records")} + +
+
+ { + setKeyword(value); + setCurrent(1); + }} + style={{ width: "600px" }} + /> + + + {t("projects.only.default.branch")}:{" "} + + { + if (v) { + localStorage.setItem("defaultBranchOnly", "1"); + } else { + localStorage.removeItem("defaultBranchOnly"); + } + setDefaultBranchOnly(v); + }} /> + +
+ +
+ {["#1f77b4", "#ff7f0e", "#2ca02c"].map((item, index) => { + return ( +
+ + + {{ + 0: "手工测试", + 1: "UI自动化测试", + 2: "单元测试", + }[index] || "unknown"} + +
+ ); + })}
- ); +
+ {/*div*/} +
t("common.total_items", { total }), + total: projectsData?.getProjectRecords?.total, + current, + pageSize, + // current: projectsData?.getProjects?.current, + // pageSize: projectsData?.getProjects?.pageSize, + }} + dataSource={projectsData?.getProjectRecords?.data || []} + onChange={(val) => { + setCurrent(val.current || 1); + setPageSize(val.pageSize || 10); + // setKeyword(keyword); + }} + /> + + + + ); }; export default ProjectOverviewPage; diff --git a/packages/canyon-platform/src/pages/index/projects/[id]/settings/helper/AutoInstrumentForm.tsx b/packages/canyon-platform/src/pages/index/projects/[id]/settings/helper/AutoInstrumentForm.tsx index 589c2291..ec601197 100644 --- a/packages/canyon-platform/src/pages/index/projects/[id]/settings/helper/AutoInstrumentForm.tsx +++ b/packages/canyon-platform/src/pages/index/projects/[id]/settings/helper/AutoInstrumentForm.tsx @@ -1,144 +1,182 @@ -import {Divider, Drawer, Form} from "antd"; -import {Editor} from "@monaco-editor/react"; -import {FileAddOutlined, FolderAddOutlined, PlusOutlined} from "@ant-design/icons"; +import { Divider, Drawer, Form } from "antd"; +import { Editor } from "@monaco-editor/react"; +import { + FileAddOutlined, + FolderAddOutlined, + PlusOutlined, +} from "@ant-design/icons"; +const AutoInstrumentForm = ({ onChange, defaultDataSource }) => { + // console.log(defaultDataSource) + const [open, setOpen] = useState(false); + const [activeContent, setActiveContent] = useState(""); + const [activeFilepath, setActiveFilepath] = useState(""); + const [dataSource, setDataSource] = useState(defaultDataSource); + const [mode, setMode] = useState("create"); -const AutoInstrumentForm = ({onChange,defaultDataSource}) => { - // console.log(defaultDataSource) - const [open, setOpen] = useState(false) - const [activeContent, setActiveContent] = useState('') - const [activeFilepath, setActiveFilepath] = useState('') - const [dataSource, setDataSource] = useState(defaultDataSource) - const [mode, setMode] = useState('create') + useEffect(() => { + onChange(defaultDataSource); + }, []); - useEffect(()=>{ - onChange(defaultDataSource) - },[]) + const columns = [ + { + title: "文件路径", + dataIndex: "filepath", + key: "filepath", + }, + { + title: "操作", + width: 200, + render: (_, record) => ( + + ), + }, + ]; - const columns = [ - { - title: '文件路径', - dataIndex: 'filepath', - key: 'filepath', - }, - { - title: '操作', - width: 200, - render: (_,record) => , - }, - ]; + function onClose() { + let newDataSource = []; + if (mode === "create") { + // newDataSource = [ + // ...dataSource, + // { + // filepath: activeFilepath, + // content: activeContent, + // } + // ] + // 去重,根据filepath,新的代替老的 - function onClose() { - let newDataSource = [] - if (mode === 'create') { - // newDataSource = [ - // ...dataSource, - // { - // filepath: activeFilepath, - // content: activeContent, - // } - // ] - - // 去重,根据filepath,新的代替老的 - - let exist = false - newDataSource = dataSource.map(item => { - if (item.filepath === activeFilepath) { - exist = true - return { - filepath: activeFilepath, - content: activeContent, - } - } - return item - }) - - if (!exist) { - newDataSource.push({ - filepath: activeFilepath, - content: activeContent, - }) - } + let exist = false; + newDataSource = dataSource.map((item) => { + if (item.filepath === activeFilepath) { + exist = true; + return { + filepath: activeFilepath, + content: activeContent, + }; + } + return item; + }); - } else { - newDataSource = dataSource.map(item => { - if (item.filepath === activeFilepath) { - return { - filepath: activeFilepath, - content: activeContent, - } - } - return item - }) + if (!exist) { + newDataSource.push({ + filepath: activeFilepath, + content: activeContent, + }); + } + } else { + newDataSource = dataSource.map((item) => { + if (item.filepath === activeFilepath) { + return { + filepath: activeFilepath, + content: activeContent, + }; } - setDataSource(newDataSource) - setOpen(false) - onChange(newDataSource) + return item; + }); } + setDataSource(newDataSource); + setOpen(false); + onChange(newDataSource); + } - function handleAdd() { - setOpen(true) - setMode('create') - setActiveFilepath('') - setActiveContent('') - } + function handleAdd() { + setOpen(true); + setMode("create"); + setActiveFilepath(""); + setActiveContent(""); + } - return
- -
- -
- - - { - setActiveFilepath(e.target.value) - }}/> - +
+ + + + { + setActiveFilepath(e.target.value); + }} + /> + - -
- { - setActiveContent(v||'') - }}/> -
-
- + +
+ { + setActiveContent(v || ""); + }} + /> +
+
+
- -} + + ); +}; -export default AutoInstrumentForm +export default AutoInstrumentForm; diff --git a/packages/canyon-platform/src/pages/index/projects/[id]/settings/helper/BasicForms.tsx b/packages/canyon-platform/src/pages/index/projects/[id]/settings/helper/BasicForms.tsx index bf3bd663..6847c737 100644 --- a/packages/canyon-platform/src/pages/index/projects/[id]/settings/helper/BasicForms.tsx +++ b/packages/canyon-platform/src/pages/index/projects/[id]/settings/helper/BasicForms.tsx @@ -7,80 +7,80 @@ import { UpdateProjectDocument } from "../../../../../../helpers/backend/gen/gra const { TextArea } = Input; const BasicForms: FC<{ data: any }> = ({ data }, ref) => { - const [updateProject] = useMutation(UpdateProjectDocument); - const prm: any = useParams(); - const { t } = useTranslation(); - const onFinish = (values: any) => { - updateProject({ - variables: { - projectID: prm.id, - coverage: "__null__", - description: values.description, - defaultBranch: "__null__", - instrumentCwd: values.instrumentCwd, - }, - }).then(() => { - message.success("成功"); - }); - }; - const [form] = useForm(); - const onSubmit = () => { - form.submit(); - }; - useImperativeHandle(ref, () => ({ - submit: onSubmit, - })); - // use - if (data) { - return ( - -
- - label={t("new.repository")} - name="pathWithNamespace" - className={"flex-1 mr-10"} - > - - + const [updateProject] = useMutation(UpdateProjectDocument); + const prm: any = useParams(); + const { t } = useTranslation(); + const onFinish = (values: any) => { + updateProject({ + variables: { + projectID: prm.id, + coverage: "__null__", + description: values.description, + defaultBranch: "__null__", + instrumentCwd: values.instrumentCwd, + }, + }).then(() => { + message.success("成功"); + }); + }; + const [form] = useForm(); + const onSubmit = () => { + form.submit(); + }; + useImperativeHandle(ref, () => ({ + submit: onSubmit, + })); + // use + if (data) { + return ( + +
+ + label={t("new.repository")} + name="pathWithNamespace" + className={"flex-1 mr-10"} + > + + - - className={"flex-3"} - label={t("projects.config.project.id")} - name="projectID" - > - - -
+ + className={"flex-3"} + label={t("projects.config.project.id")} + name="projectID" + > + + +
- label={t("common.language")} name="language"> - - + label={t("common.language")} name="language"> + + - - label={t("projects.config.project.desc")} - name="description" - > -