diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index 38e10d5f5..d0efa16cf 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -70,4 +70,9 @@ class Api::V1::BaseController < ApplicationController @project = load_project return render_forbidden if !@project.is_public && !current_user.admin? && !@project.member?(current_user) end + + def verification_with_setting(key) + @project = load_project + return render_forbidden unless EduSetting.get(key).split(",").include?(current_user.id.to_s) + end end \ No newline at end of file diff --git a/app/controllers/api/v1/projects/code_check_controller.rb b/app/controllers/api/v1/projects/code_check_controller.rb new file mode 100644 index 000000000..288617012 --- /dev/null +++ b/app/controllers/api/v1/projects/code_check_controller.rb @@ -0,0 +1,72 @@ +class Api::V1::Projects::CodeCheckController < Api::V1::BaseController + before_action :require_public_and_member_above + before_action :load_project + before_action :verification_with_setting('open_huawei_check_users') + + before_action do + @client = Api::V1::Projects::CodecheckService.new(@project) + end + def index + + end + def create + @result = @client.create_task(params["branch"]) + render :index + end + + def run_task + @result = @client.run_task + render :index + end + + def query_task + @result = @client.query_task + render :index + end + #----- 缺陷管理 ------ + def defects_summary + @result = @client.defects_summary(request.query_string) + render :index + end + + def metrics_summary + @result = @client.metrics_summary + render :index + end + + def defects_detail + @result = @client.defects_detail(request.query_string) + render :index + end + + def defects_statistic + @result = @client.defects_statistic + render :index + end + + def defect_status #put + @result = @client.defect_status(request.request_parameters.deep_dup) + render :index + end + #------ 代码问题 ---- + def issue_status #post + @result = @client.issue_status(request.request_parameters) + render :index + end + + def issue_list_by_filter #post + @result = @client.issue_list_by_filter(request.request_parameters) + render :index + end + + def issue_filter #post + @result = @client.issue_filter(request.request_parameters) + render :index + end + + def defect_metric_trend #get + @result = @client.defect_metric_trend(request.query_string) + render :index + end + +end \ No newline at end of file diff --git a/app/services/api/v1/projects/codecheck_service.rb b/app/services/api/v1/projects/codecheck_service.rb new file mode 100644 index 000000000..4a60ea99a --- /dev/null +++ b/app/services/api/v1/projects/codecheck_service.rb @@ -0,0 +1,73 @@ +class Api::V1::Projects::CodecheckService < ApplicationService + def initialize(project) + @project = project + @client = HuaweiCloud::CodeCheck.new + end + + def create_task(branch) + language = if @project.project_language.present? + @project.project_language.name.upcase + else + "TYPESCRIPT" + end + + result = @client.create_task( + git_url: @project.repository.url, + git_branch: branch, + languages: [language], + username: @project.owner.login, + access_token: "this is opensource" + ) + @project.update(huawei_code_check_task_id: result["task_id"]) + end + + def run_task + @client.run_task(@project.huawei_code_check_task_id) + end + + def query_task + @client.query_task(@project.huawei_code_check_task_id) + end + + def defects_summary(query) + @client.defects_summary(@project.huawei_code_check_task_id,query) + end + + def defects_detail(query) + @client.defects_detail(@project.huawei_code_check_task_id,query) + end + + def defects_statistic + @client.defects_statistic(@project.huawei_code_check_task_id) + end + + def metrics_summary + @client.metrics_summary(@project.huawei_code_check_task_id) + end + + def defect_status(query) + @client.defect_status(@project.huawei_code_check_task_id,query) + end + + def issue_status(body) #更新代码问题状态 post + @client.issue_status(@project.huawei_code_check_task_id,body) + end + + def issue_list_by_filter(body) #获取代码问题状态 + @client.issue_list_by_filter(@project.huawei_code_check_task_id,body) + end + + def issue_filter(body) #获取代码问题状态 + @client.issue_filter(@project.huawei_code_check_task_id,body) + end + + def defect_metric_trend(query) + @client.defect_metric_trend(@project.huawei_code_check_task_id,query) + end + def next_status + @client.next_status(@project.huawei_code_check_task_id) + end + + + +end \ No newline at end of file diff --git a/app/services/huawei_cloud/code_check.rb b/app/services/huawei_cloud/code_check.rb new file mode 100644 index 000000000..21133c047 --- /dev/null +++ b/app/services/huawei_cloud/code_check.rb @@ -0,0 +1,152 @@ +require 'rest-client' +require 'json' +require 'redis' + +module HuaweiCloud + class CodeCheck + CONFIG = Rails.application.config_for(:configuration)['huawei_code_check'] + PROJECT_ID = CONFIG['product_id'] + REGION = CONFIG['region'] + + def initialize + @region = REGION + @project_id = PROJECT_ID + @token = HuaweiCloud::ImaAccount.new.token + @headers = { + "Content-Type" => "application/json;charset=utf8", + "X-Auth-Token" => @token + } + end + + + # 创建检查任务 + # params: git_url, git_branch, languages (array), optional rule_sets etc + def create_task(git_url:, git_branch:, languages: nil, rule_sets: nil, task_type: "full", endpoint_id: nil, username: nil, access_token: nil, resource_pool_id: nil) + url = "https://codecheck-ext.#{@region}.myhuaweicloud.com/v2/#{@project_id}/task" + Rails.logger.info "################### create_task url #{url}" + + + body = { + git_url: git_url, + git_branch: git_branch, + task_type:task_type + } + body[:language] = languages if languages + body[:rule_sets] = rule_sets if rule_sets + body[:endpoint_id] = endpoint_id if endpoint_id + body[:username] = username if username + body[:access_token] = access_token if access_token + body[:resource_pool_id] = resource_pool_id if resource_pool_id + Rails.logger.info body.to_json + resp = RestClient.post(url, body.to_json, @headers) + JSON.parse(resp.body) + rescue RestClient::ExceptionWithResponse => e + # 你可以根据 e.response.code 处理 401 (Token失效) 或其它错误 + raise "CodeCheck create_task failed: #{e.response}" + end + + def run_task(task_id) + url = "https://codecheck-ext.#{@region}.myhuaweicloud.com/v2/tasks/#{task_id}/run" + body = { + } + resp = RestClient.post(url, body.to_json, @headers) + JSON.parse(resp.body) + rescue RestClient::ExceptionWithResponse => e + # 你可以根据 e.response.code 处理 401 (Token失效) 或其它错误 + raise "CodeCheck create_task failed: #{e.response}" + end + + def query_task(task_id) + url = "https://codecheck-ext.#{@region}.myhuaweicloud.com/v2/tasks/#{task_id}/progress" + resp = RestClient.get(url, @headers) + JSON.parse(resp.body) + rescue RestClient::ExceptionWithResponse => e + # 你可以根据 e.response.code 处理 401 (Token失效) 或其它错误 + raise "CodeCheck create_task failed: #{e.response}" + end + + def defects_summary(task_id,query) + url = "https://codecheck-ext.#{@region}.myhuaweicloud.com/v2/tasks/#{task_id}/defects-summary?#{query}" + resp = RestClient.get(url, @headers) + JSON.parse(resp.body) + rescue RestClient::ExceptionWithResponse => e + # 你可以根据 e.response.code 处理 401 (Token失效) 或其它错误 + raise "CodeCheck create_task failed: #{e.response}" + end + + + def defects_detail(task_id,query) + url = "https://codecheck-ext.#{@region}.myhuaweicloud.com/v2/tasks/#{task_id}/defects-detail?#{query}" + resp = RestClient.get(url, @headers) + JSON.parse(resp.body) + rescue RestClient::ExceptionWithResponse => e + # 你可以根据 e.response.code 处理 401 (Token失效) 或其它错误 + raise "CodeCheck create_task failed: #{e.response}" + end + + def defects_statistic(task_id) + url = "https://codecheck-ext.#{@region}.myhuaweicloud.com/v2/tasks/#{task_id}/defects-statistic" + resp = RestClient.get(url, @headers) + JSON.parse(resp.body) + rescue RestClient::ExceptionWithResponse => e + # 你可以根据 e.response.code 处理 401 (Token失效) 或其它错误 + raise "CodeCheck create_task failed: #{e.response}" + end + + def metrics_summary(task_id) + url = "https://codecheck-ext.#{@region}.myhuaweicloud.com/v2/#{@project_id}/tasks/#{task_id}/metrics-summary" + resp = RestClient.get(url, @headers) + JSON.parse(resp.body) + rescue RestClient::ExceptionWithResponse => e + # 你可以根据 e.response.code 处理 401 (Token失效) 或其它错误 + raise "CodeCheck create_task failed: #{e.response}" + end + + def defect_status(task_id,body) + url = "https://codecheck-ext.#{@region}.myhuaweicloud.com/v2/tasks/#{task_id}/defect-status" + resp = RestClient.put(url, body.to_json, @headers) + JSON.parse(resp.body) + end + + def issue_status(task_id,body) + url = "ttps://codecheck-ext.#{@region}.myhuaweicloud.com/v1/defect/issue-status" + body["taskId"] = task_id + resp = RestClient.post(url, body.to_json, @headers) + JSON.parse(resp.body) + rescue RestClient::ExceptionWithResponse => e + # 你可以根据 e.response.code 处理 401 (Token失效) 或其它错误 + raise "CodeCheck create_task failed: #{e.response}" + end + + def issue_list_by_filter(task_id,body) + url = "ttps://codecheck-ext.#{@region}.myhuaweicloud.com/v1/defect/issue-list-by-filter" + body["taskId"] = task_id + resp = RestClient.post(url, body.to_json, @headers) + JSON.parse(resp.body) + rescue RestClient::ExceptionWithResponse => e + # 你可以根据 e.response.code 处理 401 (Token失效) 或其它错误 + raise "CodeCheck create_task failed: #{e.response}" + end + + def issue_filter(task_id,body) + url = "ttps://codecheck-ext.#{@region}.myhuaweicloud.com/v1/defect/issue-filter" + body["taskId"] = task_id + resp = RestClient.post(url, body.to_json, @headers) + JSON.parse(resp.body) + rescue RestClient::ExceptionWithResponse => e + # 你可以根据 e.response.code 处理 401 (Token失效) 或其它错误 + raise "CodeCheck create_task failed: #{e.response}" + end + + def defect_metric_trend(task_id,query) + url = "https://codecheck-ext.#{@region}.myhuaweicloud.com/v1/history/defect-metric-trend?task_id=#{task_id}&&#{query}" + resp = RestClient.get(url, @headers) + JSON.parse(resp.body) + rescue RestClient::ExceptionWithResponse => e + # 你可以根据 e.response.code 处理 401 (Token失效) 或其它错误 + raise "CodeCheck create_task failed: #{e.response}" + end + + + end +end diff --git a/app/services/huawei_cloud/ima_account.rb b/app/services/huawei_cloud/ima_account.rb new file mode 100644 index 000000000..3a35ef5e6 --- /dev/null +++ b/app/services/huawei_cloud/ima_account.rb @@ -0,0 +1,59 @@ +module HuaweiCloud + class ImaAccount + IAM_TOKEN_KEY = "huaweicloud:iam_token".freeze + CONFIG = Rails.application.config_for(:configuration)['huawei_code_check'] + PROJECT_ID = CONFIG['project_id'] + REGION = CONFIG['region'] + DOMAIN_NAME = CONFIG['domain_name'] + USER_NAME = CONFIG['user_name'] + USER_PASSWORD = CONFIG['user_password'] + + def initialize + @region = REGION + @domain_name = DOMAIN_NAME + @user_name = USER_NAME + @user_password = USER_PASSWORD + @project_id = PROJECT_ID + end + + # 获取或缓存 Token + def token + cached = Rails.cache.fetch(IAM_TOKEN_KEY) + return cached if cached + + new_token = fetch_token + Rails.cache.write(IAM_TOKEN_KEY,new_token,expires_in: 23.hour) + new_token + end + private + + def fetch_token + url = "https://iam.#{@region}.myhuaweicloud.com/v3/auth/tokens" + headers = { "Content-Type" => "application/json;charset=utf8" } + + body = { + auth: { + identity: { + methods: ["password"], + password: { + user: { + domain: { name: @domain_name }, + name: @user_name, + password: @user_password + } + } + }, + scope: { + project: { id: @project_id } + } + } + } + response = RestClient.post(url, body.to_json, headers) + token = response.headers[:x_subject_token] + raise "Failed to obtain IAM token" unless token + token + rescue RestClient::ExceptionWithResponse => e + raise "IAM token fetch failed: #{e.response}" + end + end +end \ No newline at end of file diff --git a/app/views/api/v1/projects/code_check/index.json.jbuilder b/app/views/api/v1/projects/code_check/index.json.jbuilder new file mode 100644 index 000000000..86a3f08fd --- /dev/null +++ b/app/views/api/v1/projects/code_check/index.json.jbuilder @@ -0,0 +1 @@ +json.data @result \ No newline at end of file diff --git a/app/views/projects/show.json.jbuilder b/app/views/projects/show.json.jbuilder index aa66808b4..ab0742c85 100644 --- a/app/views/projects/show.json.jbuilder +++ b/app/views/projects/show.json.jbuilder @@ -7,6 +7,7 @@ json.repo_id @project.repository.id json.repo_identifier @project.repository.identifier json.is_need_apply @project.is_need_apply json.sca_project_id @project.sca_project_id +json.huawei_code_check_task_id @project.huawei_code_check_task_id user_apply_signatures = @project.apply_signatures.with_user_id(current_user.id) json.user_apply_signatures user_apply_signatures do |signature| json.id signature.id diff --git a/config/routes/api.rb b/config/routes/api.rb index 8e007d6b4..1fd09fe53 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -252,6 +252,23 @@ defaults format: :json do get :scan_issues end end + + resource :code_check, only: [:index,:create] do + collection do + post :run_task + get :query_task + get :defects_summary + get :defects_detail + get :defects_statistic + get :metrics_summary + put :defect_status + post :issue_status + post :issue_list_by_filter + post :issue_filter + get :defect_metric_trend + + end + end resources :pipelines do post :build_yaml, on: :collection post :save_yaml, on: :collection diff --git a/db/migrate/20251112060641_add_huawei_code_check_task_to_projects.rb b/db/migrate/20251112060641_add_huawei_code_check_task_to_projects.rb new file mode 100644 index 000000000..a28dbc779 --- /dev/null +++ b/db/migrate/20251112060641_add_huawei_code_check_task_to_projects.rb @@ -0,0 +1,5 @@ +class AddHuaweiCodeCheckTaskToProjects < ActiveRecord::Migration[5.2] + def change + add_column :projects , :huawei_code_check_task_id, :string + end +end