diff --git a/pkg/qldbstore/qldbstore_hepc.go b/pkg/qldbstore/qldbstore_hepc.go new file mode 100644 index 0000000..9c630f1 --- /dev/null +++ b/pkg/qldbstore/qldbstore_hepc.go @@ -0,0 +1,121 @@ +package qldbstore + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/hohn/mrvacommander/pkg/common" +) + +type HepcStore struct { + Endpoint string +} + +type HepcResult struct { + GitBranch string `json:"git_branch"` + GitCommitID string `json:"git_commit_id"` + GitRepo string `json:"git_repo"` + IngestionDatetime string `json:"ingestion_datetime_utc"` + ResultURL string `json:"result_url"` + ToolID string `json:"tool_id"` + ToolName string `json:"tool_name"` + ToolVersion string `json:"tool_version"` + Projname string `json:"projname"` +} + +func NewHepcStore(endpoint string) *HepcStore { + return &HepcStore{Endpoint: endpoint} +} + +func (h *HepcStore) FindAvailableDBs(analysisReposRequested []common.NameWithOwner) ( + notFoundRepos []common.NameWithOwner, + foundRepos []common.NameWithOwner) { + + // Fetch the metadata.json from the Hepc server + url := fmt.Sprintf("%s/index", h.Endpoint) + resp, err := http.Get(url) + if err != nil { + fmt.Printf("Error fetching metadata: %v\n", err) + return analysisReposRequested, nil + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + fmt.Printf("Non-OK HTTP status: %s\n", resp.Status) + return analysisReposRequested, nil + } + + // Decode the response + var results []HepcResult + decoder := json.NewDecoder(resp.Body) + for { + var result HepcResult + if err := decoder.Decode(&result); err == io.EOF { + break + } else if err != nil { + fmt.Printf("Error decoding JSON: %v\n", err) + return analysisReposRequested, nil + } + results = append(results, result) + } + + // Compare against requested repos + repoSet := make(map[string]struct{}) + for _, result := range results { + repoSet[result.Projname] = struct{}{} + } + + for _, reqRepo := range analysisReposRequested { + repoKey := fmt.Sprintf("%s/%s", reqRepo.Owner, reqRepo.Repo) + if _, exists := repoSet[repoKey]; exists { + foundRepos = append(foundRepos, reqRepo) + } else { + notFoundRepos = append(notFoundRepos, reqRepo) + } + } + + return notFoundRepos, foundRepos +} + +func (h *HepcStore) GetDatabase(location common.NameWithOwner) ([]byte, error) { + // Fetch the latest results for the specified repository + url := fmt.Sprintf("%s/api/v1/latest_results/codeql-all", h.Endpoint) + resp, err := http.Get(url) + if err != nil { + return nil, fmt.Errorf("error fetching database metadata: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("non-OK HTTP status: %s", resp.Status) + } + + var latestResults []HepcResult + decoder := json.NewDecoder(resp.Body) + if err := decoder.Decode(&latestResults); err != nil { + return nil, fmt.Errorf("error decoding JSON: %w", err) + } + + // Find the correct result for the requested repo + repoKey := fmt.Sprintf("%s/%s", location.Owner, location.Repo) + for _, result := range latestResults { + if result.Projname == repoKey { + // Fetch the database as a byte slice + dbResp, err := http.Get(result.ResultURL) + if err != nil { + return nil, fmt.Errorf("error fetching database: %w", err) + } + defer dbResp.Body.Close() + + if dbResp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("non-OK HTTP status for database fetch: %s", dbResp.Status) + } + + return io.ReadAll(dbResp.Body) + } + } + + return nil, fmt.Errorf("database not found for repository: %s", repoKey) +}