Skip to content

Chore/sp 4138 add status batch response#50

Merged
agustingroh merged 7 commits intomainfrom
chore/SP-4138-add-status-batch-response
Mar 20, 2026
Merged

Chore/sp 4138 add status batch response#50
agustingroh merged 7 commits intomainfrom
chore/SP-4138-add-status-batch-response

Conversation

@agustingroh
Copy link
Copy Markdown
Contributor

@agustingroh agustingroh commented Mar 9, 2026

Summary by CodeRabbit

  • New Features

    • Dependency responses now include per-component status with error codes/messages, resolved component versions, and richer license resolution (including compound license IDs).
  • Changed

    • Dependency queries return partial per-component results instead of failing entire requests; more complete dependency and license information is returned consistently.
  • Chores

    • Toolchain, libraries, CI, linting, and Makefile tooling updated.
  • Documentation

    • Changelog updated for v0.13.0.
image

scanoss.js

image

scanoss.py

image image

@agustingroh agustingroh requested a review from eeisegn March 9, 2026 15:19
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 9, 2026

Warning

Rate limit exceeded

@eeisegn has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 19 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3b2deef6-b3df-4271-9897-cbaca5a40380

📥 Commits

Reviewing files that changed from the base of the PR and between 18d42c7 and 51dfa92.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (41)
  • .github/workflows/go-ci.yml
  • .github/workflows/golangci-lint.yml
  • .github/workflows/release.yml
  • .golangci.yml
  • CHANGELOG.md
  • Makefile
  • go.mod
  • pkg/dtos/dependency_input.go
  • pkg/dtos/dependency_input_test.go
  • pkg/dtos/dependency_output.go
  • pkg/dtos/dependency_output_test.go
  • pkg/errors/error.go
  • pkg/models/all_urls.go
  • pkg/models/all_urls_test.go
  • pkg/models/common.go
  • pkg/models/common_test.go
  • pkg/models/dependencies.go
  • pkg/models/dependencies_test.go
  • pkg/models/golang_projects.go
  • pkg/models/golang_projects_test.go
  • pkg/models/licenses.go
  • pkg/models/licenses_test.go
  • pkg/models/mines.go
  • pkg/models/mines_test.go
  • pkg/models/projects.go
  • pkg/models/projects_test.go
  • pkg/models/versions.go
  • pkg/models/versions_test.go
  • pkg/protocol/rest/server.go
  • pkg/service/dependency_service.go
  • pkg/service/dependency_service_test.go
  • pkg/service/dependency_support.go
  • pkg/service/dependency_support_test.go
  • pkg/shared/index.go
  • pkg/transdep/dependency_collector.go
  • pkg/transdep/dependency_collector_test.go
  • pkg/transdep/dependency_graph.go
  • pkg/transdep/helper.go
  • pkg/transdep/utils_test.go
  • pkg/usecase/dependency.go
  • pkg/usecase/dependency_test.go
📝 Walkthrough

Walkthrough

Models migrated from per-request *sqlx.Conn to shared *sqlx.DB; dependency use-case now uses componenthelper for parallel component version/status resolution and resolves licenses via a new helper; service conversion directly builds protobuf responses including per-component error codes/messages and license entries; CI and module versions bumped.

Changes

Cohort / File(s) Summary
Changelog & Modules
CHANGELOG.md, go.mod
Added 0.13.0 release notes; bumped Go toolchain and many dependencies (gRPC, protobuf, OTEL, sqlite, scanoss modules); adjusted indirect deps and changelog links.
DTOs
pkg/dtos/dependency_input.go, pkg/dtos/dependency_output.go
Replaced local ComponentDTO with componenthelper.ComponentDTO; removed exported ComponentDTO and ParseTransitiveReqDTOS; added Status domain.ComponentStatus to DependenciesOutput.
Models — DB handle migration
pkg/models/all_urls.go, pkg/models/golang_projects.go, pkg/models/licenses.go, pkg/models/mines.go, pkg/models/projects.go, pkg/models/versions.go, pkg/models/dependencies.go
Switched model structs/constructors from *sqlx.Conn*sqlx.DB; updated internal calls from m.conn.* to m.db.*; removed per-call conn acquisition/close and related error branches; adjusted API signatures where applicable.
Model tests
pkg/models/*_test.go
Updated tests to construct models with db instead of conn; adjusted calls to new signatures and made related assertions/fixtures changes.
Usecase (dependency)
pkg/usecase/dependency.go, pkg/usecase/dependency_test.go
DependencyUseCase holds *sqlx.DB; NewDependencies drops conn param; integrates componenthelper.GetComponentsVersion (MaxWorkers 5) for parallel processing; extracted resolveLicenses helper; updated version/requirement checks and per-component status handling.
Service & Conversion
pkg/service/dependency_service.go, pkg/service/dependency_support.go, pkg/service/dependency_support_test.go
Service no longer opens per-call conn; convertDependencyOutput signature simplified and now directly constructs pb.DependencyResponse, mapping per-component error_code/error_message and licenses; removed conversion error branch and logger parameter.
Transitive deps & helpers
pkg/transdep/...
Minor comment additions, import alias rename, small refactor in graph string building and error variable naming.
CI / Lint / Makefile
.github/workflows/..., .golangci.yml, Makefile
Bumped GitHub Actions and golangci-lint action versions; workflows now derive Go version from go.mod; migrated golangci config to v2 format; added LINT_VERSION and updated lint_docker.
Misc & tests
pkg/errors/error.go, pkg/models/common.go, pkg/models/common_test.go, pkg/protocol/rest/server.go, pkg/shared/index.go, many pkg/*_test.go
Doc/comment updates, test helper reorganizations (sqlite setup/conn helpers moved), small variable renames and lint cleanups; no public API behavioral changes beyond those noted.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Service as "dependency_service"
    participant Usecase as "DependencyUseCase"
    participant ComponentHelper as "componenthelper"
    participant AllURLs as "AllUrlsModel"
    participant License as "LicenseModel"
    participant Response as "pb.DependencyResponse"

    Client->>Service: GetDependencies(request)
    Service->>Usecase: GetDependencies(dtoRequest)
    Usecase->>ComponentHelper: GetComponentsVersion(PURLs, MaxWorkers=5)
    ComponentHelper-->>Usecase: processedComponents (purl,name,type,version,status)
    loop per component
        alt status == Success
            Usecase->>AllURLs: GetURLsByPurlString(component)
            AllURLs-->>Usecase: allUrls
            Usecase->>License: resolveLicenses(allUrls)
            License-->>Usecase: licenseEntries
            Usecase->>Usecase: assemble DependencyOutput (Success, version, licenses, url)
        else status != Success
            Usecase->>Usecase: assemble DependencyOutput (status with error_code/error_message)
        end
    end
    Usecase-->>Service: DependenciesOutput
    Service->>Service: convertDependencyOutput(dto) -> pb.DependencyResponse
    Service-->>Client: return pb.DependencyResponse
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Chore/sp 4138 add status batch response #50 — appears strongly related: makes the same code-level changes (per-component status fields, direct proto response construction, migration from *sqlx.Conn to *sqlx.DB, and ComponentDTO replacement).

Suggested reviewers

  • isasmendiagus
  • scanoss-qg

Poem

🐰 I hopped from Conn to DB with nimble feet,

Each component now reports its tale complete.
Licenses and errors tucked in every line,
CI refreshed and modules aligned fine.
A rabbit cheers this tidy release design.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Chore/sp 4138 add status batch response' directly describes the main change: adding status information to batch API responses, which aligns with the PR's primary objective of including error_code and error_message fields in dependency responses.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/SP-4138-add-status-batch-response

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
pkg/models/versions.go (1)

80-84: ⚠️ Potential issue | 🔴 Critical

Remove the extra bind arguments from the INSERT statement.

The query has 2 placeholders ($1, $2) but 4 arguments are passed (name, "", false, false). This will fail at runtime in StructScan() with "sql: expected 2 arguments, got 4".

Suggested fix
 	err := m.db.QueryRowxContext(m.ctx,
 		"INSERT INTO versions (version_name, semver) VALUES($1, $2)"+
 			" RETURNING id, version_name, semver",
-		name, "", false, false,
+		name, "",
 	).StructScan(&version)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/models/versions.go` around lines 80 - 84, The INSERT call in
m.db.QueryRowxContext inside pkg/models/versions.go is passing too many bind
parameters — the SQL has two placeholders ($1, $2) but the call provides name,
"", false, false; update the call in the function where QueryRowxContext and
.StructScan(&version) are used so only the arguments matching the placeholders
are passed (e.g., pass just name and the semver value or the appropriate two
variables) and remove the extra boolean arguments; ensure the values correspond
to the INSERT columns (version_name, semver) so StructScan(&version) receives
the returned row correctly.
pkg/service/dependency_service.go (1)

77-85: ⚠️ Potential issue | 🟠 Major

Remove the now-unused dedicated DB connection.

After dropping conn from usecase.NewDependencies, Lines 77-83 only reserve a pool connection and can fail or throttle requests for no functional benefit.

Suggested cleanup
-	conn, err := d.db.Connx(ctx)                    // Get a connection from the pool
-	if err != nil {
-		s.Errorf("Failed to get a database connection from the pool: %v", err)
-		statusResp := common.StatusResponse{Status: common.StatusCode_FAILED, Message: "Failed to get database pool connection"}
-		return &pb.DependencyResponse{Status: &statusResp}, errors.NewServiceUnavailableError("problem getting database pool connection", err)
-	}
-	defer gd.CloseSQLConnection(conn)
 	// Search the KB for information about each dependency
 	depUc := usecase.NewDependencies(ctx, s, d.db, d.config)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_service.go` around lines 77 - 85, Remove the unused
dedicated DB connection acquisition: delete the call to d.db.Connx(ctx), the
associated error handling that returns a StatusResponse, and the deferred
gd.CloseSQLConnection(conn) since usecase.NewDependencies no longer needs a raw
connection; keep calling usecase.NewDependencies(ctx, s, d.db, d.config) as
before. Ensure any imports or variables only used for conn are cleaned up to
avoid unused symbol warnings (e.g., remove conn and related error handling code
paths).
pkg/usecase/dependency_test.go (1)

44-54: ⚠️ Potential issue | 🟠 Major

Replace :memory: DSN with shared-cache DSN to fix fixture visibility.

These tests load fixtures through a reserved conn but query through db. With SQLite :memory:, each pooled connection is isolated, so queries through db won't see fixtures loaded via conn. Use the shared-cache DSN (file::memory:?cache=shared) instead, like the sqliteSetup() helper in common.go already does.

Also applies to: 89, 151–161, 169

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/usecase/dependency_test.go` around lines 44 - 54, The test uses
sqlx.Connect with the ":memory:" DSN so fixtures loaded via the reserved conn
(conn from db.Connx) aren't visible to queries made on db; change the in-memory
DSN to the shared-cache DSN ("file::memory:?cache=shared") or reuse the existing
sqliteSetup() helper so the connection pool shares the same in-memory DB, and
ensure the code path around sqlx.Connect, db, conn, and models.LoadTestSQLData
uses that shared DSN so fixtures are visible across connections.
pkg/service/dependency_support.go (1)

222-223: ⚠️ Potential issue | 🟠 Major

Remove the unconditional stdout dump of component data.

Unlike the existing debug logger, Lines 222-223 always print request-derived components to stdout. That bypasses log-level/redaction controls and can leak dependency inventory in production logs.

🧹 Suggested fix
-	fmt.Printf("Valid components: %v\n", validComponents)
-	fmt.Printf("Invalid components: %v\n", invalidPurls)
+	s.Debugf("Filtered transitive dependency components: valid=%d invalid=%d", len(validComponents), len(invalidPurls))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_support.go` around lines 222 - 223, Remove the
unconditional stdout prints that leak component data by deleting the two
fmt.Printf calls (the ones printing validComponents and invalidPurls) and
replace them with a controlled debug log call using the package's logger (e.g.,
logger.Debugf or similar) so output respects log levels and redaction; locate
the prints by searching for fmt.Printf that references validComponents and
invalidPurls in dependency_support.go and emit the same information via the
service's debug logger instead of stdout.
🧹 Nitpick comments (1)
CHANGELOG.md (1)

16-17: Keep the changelog focused on external behavior.

These bullets read like implementation notes, not release notes. convertDependencyOutput and the *sqlx.DB constructor migration will age quickly and are harder for consumers to map to impact. I'd summarize the observable effect instead and keep the code-level refactor in the PR description/internal notes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 16 - 17, The changelog entries are too
implementation-focused; replace the two code-level bullets with consumer-facing
descriptions of observable behavior changes: for the convertDependencyOutput
change, describe the user-visible outcome (e.g., "Dependency scan responses now
include dependency metadata, license info, and explicit error status in
responses") instead of naming the function; for the DB constructor change,
describe the impact (e.g., "Data models now use pooled DB connections for
improved stability/performance") rather than mentioning `*sqlx.DB` or
`*sqlx.Conn`; move the detailed notes about `convertDependencyOutput` and the
constructor refactor into the PR description or internal changelog for
developers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@go.mod`:
- Line 3: go.mod now declares "go 1.25.0" but CI workflows still set
"go-version: 1.24.x", causing mismatched toolchains; update every CI workflow
entry that contains the key "go-version: 1.24.x" to "go-version: 1.25.x" so the
YAML actions use the same Go version as declared in go.mod (search for the
literal "go-version: 1.24.x" in workflow YAML files and replace with
"go-version: 1.25.x").
- Around line 18-19: The Go modules for OpenTelemetry are skewed (root requires
otel v1.42.0 while transitive go-grpc-helper pins otel/sdk and exporters at
v1.40.0); fix by explicitly adding require entries for
go.opentelemetry.io/otel/sdk, go.opentelemetry.io/otel/sdk/metric and the OTLP
exporter modules at v1.42.0 in go.mod to override the transitive constraint,
include a short comment stating this override is needed until go-grpc-helper
updates, and run `go mod tidy` and tests (noting
pkg/service/dependency_support.go and pkg/protocol/grpc/server.go depend on this
stack) to verify compatibility.

In `@pkg/dtos/dependency_output.go`:
- Line 47: The DependenciesOutput struct's Status field (type
domain.ComponentStatus) lacks a JSON tag and will marshal as "Status" instead of
the intended "status"; update the struct field declaration for Status to include
`json:"status"` so it matches the sibling fields and preserves the API response
shape.

In `@pkg/service/dependency_support_test.go`:
- Around line 37-38: The test currently only prints the result of
convertDependencyOutput(outputDto); replace the print with assertions that
validate the converted value for an empty dtos.DependencyOutput: assert the
returned value from convertDependencyOutput(outputDto) is non-nil, any string
fields are empty, numeric fields are zero, boolean fields are false, and any
slices/maps have length zero. Use the test helper (t or require/assert) to check
these conditions so a broken convertDependencyOutput implementation will fail
the test.

In `@pkg/service/dependency_support.go`:
- Around line 91-97: The mapping currently skips setting d.ErrorCode when
dep.Status.StatusCode != domain.Success but dep.Status.Message is empty and also
takes &dep.Status.Message which binds to the loop variable; update the logic in
the loop around domain.StatusCodeToErrorCode, d.ErrorCode, d.ErrorMessage and
deps append so that you always set d.ErrorCode when dep.Status.StatusCode !=
domain.Success (regardless of message presence) and if you need a pointer to the
message, allocate a new string variable (e.g. msg := dep.Status.Message) and
assign &msg to d.ErrorMessage to avoid reusing the loop variable's address
before appending to deps.

In `@pkg/usecase/dependency_test.go`:
- Around line 127-139: The test currently expects every dependency returned by
depUc.GetDependencies to have an error status, but the input includes a valid
purl (pkg:npm/isbinaryfile) so the new component-level status model allows mixed
success/error results; update the assertions in the test (in
pkg/usecase/dependency_test.go) to stop failing on successes — instead verify
that (1) at least one dependency has a success status (compare
dep.Status.StatusCode to domain.Success for the known valid purl or by checking
dep.Purl == "pkg:npm/isbinaryfile"), and (2) at least one dependency has an
error status, or that warn is set appropriately, rather than asserting every dep
in dependencies.Files has an error.

In `@pkg/usecase/dependency.go`:
- Around line 90-93: The code currently skips appending a result when
d.allUrls.GetURLsByPurlString fails; instead of just logging and continue,
construct and append a failed DependenciesOutput for the current
processedComponent (include identifying fields like
processedComponent.Purl/Version and file.File) with a non-success Status (e.g.,
FAILED or ERROR) and any error message/details, then continue; update the logic
around d.allUrls.GetURLsByPurlString and depOutput to always push a
DependenciesOutput entry on error so batch semantics remain one-entry-per-input.
- Around line 43-49: The constructor NewDependencies/DependencyUseCase was
migrated to store *sqlx.DB but the service layer still calls db.Conn (checkout)
per RPC and never uses the connection; this pins pool connections. Remove
per-request conn checkout in the dependency service (remove any db.Conn/db.Connx
or conn variable usage in dependency_service.go), use the stored *sqlx.DB on
DependencyUseCase or pass *sqlx.DB into the called methods instead, and ensure
all model/service calls accept/use *sqlx.DB directly; also delete any unused
conn.Close/Release calls and fix call sites to stop expecting *sqlx.Conn.

---

Outside diff comments:
In `@pkg/models/versions.go`:
- Around line 80-84: The INSERT call in m.db.QueryRowxContext inside
pkg/models/versions.go is passing too many bind parameters — the SQL has two
placeholders ($1, $2) but the call provides name, "", false, false; update the
call in the function where QueryRowxContext and .StructScan(&version) are used
so only the arguments matching the placeholders are passed (e.g., pass just name
and the semver value or the appropriate two variables) and remove the extra
boolean arguments; ensure the values correspond to the INSERT columns
(version_name, semver) so StructScan(&version) receives the returned row
correctly.

In `@pkg/service/dependency_service.go`:
- Around line 77-85: Remove the unused dedicated DB connection acquisition:
delete the call to d.db.Connx(ctx), the associated error handling that returns a
StatusResponse, and the deferred gd.CloseSQLConnection(conn) since
usecase.NewDependencies no longer needs a raw connection; keep calling
usecase.NewDependencies(ctx, s, d.db, d.config) as before. Ensure any imports or
variables only used for conn are cleaned up to avoid unused symbol warnings
(e.g., remove conn and related error handling code paths).

In `@pkg/service/dependency_support.go`:
- Around line 222-223: Remove the unconditional stdout prints that leak
component data by deleting the two fmt.Printf calls (the ones printing
validComponents and invalidPurls) and replace them with a controlled debug log
call using the package's logger (e.g., logger.Debugf or similar) so output
respects log levels and redaction; locate the prints by searching for fmt.Printf
that references validComponents and invalidPurls in dependency_support.go and
emit the same information via the service's debug logger instead of stdout.

In `@pkg/usecase/dependency_test.go`:
- Around line 44-54: The test uses sqlx.Connect with the ":memory:" DSN so
fixtures loaded via the reserved conn (conn from db.Connx) aren't visible to
queries made on db; change the in-memory DSN to the shared-cache DSN
("file::memory:?cache=shared") or reuse the existing sqliteSetup() helper so the
connection pool shares the same in-memory DB, and ensure the code path around
sqlx.Connect, db, conn, and models.LoadTestSQLData uses that shared DSN so
fixtures are visible across connections.

---

Nitpick comments:
In `@CHANGELOG.md`:
- Around line 16-17: The changelog entries are too implementation-focused;
replace the two code-level bullets with consumer-facing descriptions of
observable behavior changes: for the convertDependencyOutput change, describe
the user-visible outcome (e.g., "Dependency scan responses now include
dependency metadata, license info, and explicit error status in responses")
instead of naming the function; for the DB constructor change, describe the
impact (e.g., "Data models now use pooled DB connections for improved
stability/performance") rather than mentioning `*sqlx.DB` or `*sqlx.Conn`; move
the detailed notes about `convertDependencyOutput` and the constructor refactor
into the PR description or internal changelog for developers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 37fd348f-1e95-41b2-93b6-cb2e76f6a751

📥 Commits

Reviewing files that changed from the base of the PR and between 31c4fa6 and fd5d205.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (21)
  • CHANGELOG.md
  • go.mod
  • pkg/dtos/dependency_input.go
  • pkg/dtos/dependency_output.go
  • pkg/models/all_urls.go
  • pkg/models/all_urls_test.go
  • pkg/models/golang_projects.go
  • pkg/models/golang_projects_test.go
  • pkg/models/licenses.go
  • pkg/models/licenses_test.go
  • pkg/models/mines.go
  • pkg/models/mines_test.go
  • pkg/models/projects.go
  • pkg/models/projects_test.go
  • pkg/models/versions.go
  • pkg/models/versions_test.go
  • pkg/service/dependency_service.go
  • pkg/service/dependency_support.go
  • pkg/service/dependency_support_test.go
  • pkg/usecase/dependency.go
  • pkg/usecase/dependency_test.go

Comment thread go.mod
Comment thread go.mod
Comment thread pkg/dtos/dependency_output.go
Comment thread pkg/service/dependency_support_test.go
Comment thread pkg/service/dependency_support.go Outdated
Comment thread pkg/usecase/dependency_test.go Outdated
Comment thread pkg/usecase/dependency.go
Comment thread pkg/usecase/dependency.go Outdated
@agustingroh agustingroh force-pushed the chore/SP-4138-add-status-batch-response branch from 6813088 to c185e4c Compare March 9, 2026 16:03
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/models/versions.go (1)

80-84: ⚠️ Potential issue | 🟠 Major

Parameter count mismatch in INSERT statement.

The SQL query has 2 placeholders ($1, $2) but 4 arguments are passed (name, "", false, false). The extra arguments will be silently ignored, indicating either incomplete refactoring or accidental extra parameters.

🐛 Suggested fix
 	err := m.db.QueryRowxContext(m.ctx,
 		"INSERT INTO versions (version_name, semver) VALUES($1, $2)"+
 			" RETURNING id, version_name, semver",
-		name, "", false, false,
+		name, "",
 	).StructScan(&version)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/models/versions.go` around lines 80 - 84, The INSERT call in
m.db.QueryRowxContext within pkg/models/versions.go passes four arguments (name,
"", false, false) but the SQL has only two placeholders ($1, $2), causing a
parameter mismatch; update the SQL to include the additional columns if the
booleans and empty semver are intended (e.g., add the appropriate column names
and $3, $4 placeholders) or remove the extra arguments and only pass name and
semver (""), ensuring the call to QueryRowxContext and the subsequent
StructScan(&version) match the final column list and returned fields.
♻️ Duplicate comments (2)
pkg/service/dependency_support.go (1)

91-99: ⚠️ Potential issue | 🟠 Major

Error code not set when message is empty; pointer aliasing on loop variable.

Two issues persist:

  1. The condition dep.Status.StatusCode != domain.Success && dep.Status.Message != "" skips setting ErrorCode when status indicates failure but message is empty.

  2. &dep.Status.Message takes the address of a field within the loop variable dep. Since dep is reused across iterations, all error items will share the same pointer, causing the last message to appear in all.

🐛 Suggested fix
-			if dep.Status.StatusCode != domain.Success && dep.Status.Message != "" {
-				errorCode := domain.StatusCodeToErrorCode(dep.Status.StatusCode)
-				d.ErrorCode = errorCode
-				if dep.Status.Message != "" {
-					d.ErrorMessage = &dep.Status.Message
-				}
+			if dep.Status.StatusCode != domain.Success {
+				d.ErrorCode = domain.StatusCodeToErrorCode(dep.Status.StatusCode)
+				if dep.Status.Message != "" {
+					msg := dep.Status.Message
+					d.ErrorMessage = &msg
+				}
 				deps = append(deps, d)
 				continue
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_support.go` around lines 91 - 99, The code currently
only sets d.ErrorCode when dep.Status.Message is non-empty and takes
&dep.Status.Message which aliases the loop variable; change the logic to check
failure solely by dep.Status.StatusCode != domain.Success, always set
d.ErrorCode = domain.StatusCodeToErrorCode(dep.Status.StatusCode), and if
dep.Status.Message != "" create a new local string variable (e.g., msg :=
dep.Status.Message) and set d.ErrorMessage = &msg to avoid pointer aliasing
before appending d to deps; ensure these changes are applied around the existing
dep, d, dep.Status.Message, ErrorCode, ErrorMessage,
domain.StatusCodeToErrorCode and deps append usage.
pkg/service/dependency_support_test.go (1)

37-38: ⚠️ Potential issue | 🟡 Minor

Test lacks assertions.

The test only prints the converted value without asserting expected properties. A broken convertDependencyOutput implementation would still pass.

💚 Suggested fix
 	output := convertDependencyOutput(outputDto)
-	fmt.Printf("Output: %v\n", output)
+	if output == nil {
+		t.Error("expected non-nil output")
+	}
+	if len(output.Files) != 0 {
+		t.Errorf("expected empty Files slice, got %d", len(output.Files))
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_support_test.go` around lines 37 - 38, The test
currently only prints the result of convertDependencyOutput(outputDto) without
verifying behavior; replace or supplement the fmt.Printf with assertions that
validate the converted output's expected fields and values (e.g., check non-nil
result, expected length, specific fields match input from outputDto, and any
error/status fields). Locate the call to convertDependencyOutput and use the
test framework's assertion helpers (t.Fatalf/t.Errorf or testify/require as used
elsewhere) to assert correctness of the returned structure and key properties so
the test fails when conversion is incorrect.
🧹 Nitpick comments (4)
.golangci.yml (1)

84-86: Consider excluding test files more precisely.

The paths: - tests exclusion only matches a directory named tests. If test files follow the Go convention of *_test.go within packages, they may not be excluded. Consider using generated: lax or adding *_test.go patterns if needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.golangci.yml around lines 84 - 86, The current .golangci.yml exclusions
block uses paths: - tests which only matches a directory named tests; update the
exclusions to precisely exclude Go test files by adding a glob pattern for
*_test.go (e.g., add a paths entry like **/*_test.go) or enable generated: lax
as needed; modify the exclusions.paths list where 'paths' and 'tests' are
defined so that it includes the test-file glob (and/or set generated: lax) to
ensure package-level *_test.go files are excluded.
pkg/dtos/dependency_input.go (1)

23-26: Minor: Import grouping convention.

The componenthelper import is grouped with stdlib imports. Consider separating external imports from stdlib for consistency.

♻️ Suggested grouping
 import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/scanoss/go-component-helper/componenthelper"
 
+	"github.com/scanoss/go-component-helper/componenthelper"
 	"go.uber.org/zap"
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/dtos/dependency_input.go` around lines 23 - 26, The import grouping in
dependency_input.go incorrectly places the external package
"github.com/scanoss/go-component-helper/componenthelper" with standard library
imports; update the import block to follow conventional grouping: keep stdlib
imports together, place third-party imports (e.g.,
"github.com/scanoss/go-component-helper/componenthelper" and "go.uber.org/zap")
in a separate group, and then local/project imports last so functions/types
referencing componenthelper and zap remain unchanged (no code changes to
functions like any using componenthelper).
pkg/usecase/dependency.go (2)

91-99: Components are now correctly retained on URL lookup failure.

The error handling now appends a DependenciesOutput with ComponentNotFound status instead of silently dropping the component. This maintains one-entry-per-input semantics.

Minor style note: fmt.Sprintf("component not found") can be simplified to just "component not found" since there are no format arguments.

🧹 Simplify string literal
 			depOutput.Status = domain.ComponentStatus{
-				Message:    fmt.Sprintf("component not found"),
+				Message:    "component not found",
 				StatusCode: domain.ComponentNotFound,
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/usecase/dependency.go` around lines 91 - 99, Replace the unnecessary use
of fmt.Sprintf in the error branch of GetURLsByPurlString handling: in the block
inside the loop where url, err := d.allUrls.GetURLsByPurlString(...) leads to
setting depOutput.Status = domain.ComponentStatus{ Message:
fmt.Sprintf("component not found"), ... }, simplify Message to the plain string
"component not found" (update the assignment in the code that constructs
depOutput.Status inside that error branch in pkg/usecase/dependency.go).

69-75: Consider making MaxWorkers configurable.

The MaxWorkers: 5 is hardcoded. If this service handles variable load or needs tuning for different deployment environments, exposing this via config would provide more flexibility.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/usecase/dependency.go` around lines 69 - 75, The code hardcodes
MaxWorkers: 5 in the call to componentHelper.GetComponentsVersion which reduces
configurability; update the call to read MaxWorkers from configuration (e.g., a
field on the usecase/dependency receiver such as d.cfg.MaxWorkers or a passed-in
options value) and fall back to a sensible default if unset, so change the
ComponentVersionCfg construction in the GetComponentsVersion call to use that
config value instead of the literal 5 (refer to MaxWorkers,
componentHelper.GetComponentsVersion, ComponentVersionCfg, and the receiver
fields like d.db/d.ctx/d.s/file.Purls to locate the call).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.golangci.yml:
- Around line 65-68: Remove the unused G204 exclusion from .golangci.yml or add
a short justification comment explaining why it is present; specifically, delete
the G204 entry unless you intend to run shell commands in the future, or if you
want to keep it add a comment referencing the relevant code (e.g., loadSQLData
in pkg/models/common.go and current exec usage via db.Exec/ExecContext which are
SQL executions, not shell exec) stating the rationale and intended future
patterns that would require G204. Ensure the justification mentions there are no
shell exec calls today and points to db.Exec/ExecContext as examples so
reviewers know the exclusion is deliberate.

In `@CHANGELOG.md`:
- Line 18: Update the version format on the changelog line containing "Upgraded
`package-url/packageurl-go` to v.1.5" by removing the extra dot after the "v" so
it reads "v1.5" to match the project's other version references; edit that exact
string in CHANGELOG.md to "Upgraded `package-url/packageurl-go` to v1.5".

---

Outside diff comments:
In `@pkg/models/versions.go`:
- Around line 80-84: The INSERT call in m.db.QueryRowxContext within
pkg/models/versions.go passes four arguments (name, "", false, false) but the
SQL has only two placeholders ($1, $2), causing a parameter mismatch; update the
SQL to include the additional columns if the booleans and empty semver are
intended (e.g., add the appropriate column names and $3, $4 placeholders) or
remove the extra arguments and only pass name and semver (""), ensuring the call
to QueryRowxContext and the subsequent StructScan(&version) match the final
column list and returned fields.

---

Duplicate comments:
In `@pkg/service/dependency_support_test.go`:
- Around line 37-38: The test currently only prints the result of
convertDependencyOutput(outputDto) without verifying behavior; replace or
supplement the fmt.Printf with assertions that validate the converted output's
expected fields and values (e.g., check non-nil result, expected length,
specific fields match input from outputDto, and any error/status fields). Locate
the call to convertDependencyOutput and use the test framework's assertion
helpers (t.Fatalf/t.Errorf or testify/require as used elsewhere) to assert
correctness of the returned structure and key properties so the test fails when
conversion is incorrect.

In `@pkg/service/dependency_support.go`:
- Around line 91-99: The code currently only sets d.ErrorCode when
dep.Status.Message is non-empty and takes &dep.Status.Message which aliases the
loop variable; change the logic to check failure solely by dep.Status.StatusCode
!= domain.Success, always set d.ErrorCode =
domain.StatusCodeToErrorCode(dep.Status.StatusCode), and if dep.Status.Message
!= "" create a new local string variable (e.g., msg := dep.Status.Message) and
set d.ErrorMessage = &msg to avoid pointer aliasing before appending d to deps;
ensure these changes are applied around the existing dep, d, dep.Status.Message,
ErrorCode, ErrorMessage, domain.StatusCodeToErrorCode and deps append usage.

---

Nitpick comments:
In @.golangci.yml:
- Around line 84-86: The current .golangci.yml exclusions block uses paths: -
tests which only matches a directory named tests; update the exclusions to
precisely exclude Go test files by adding a glob pattern for *_test.go (e.g.,
add a paths entry like **/*_test.go) or enable generated: lax as needed; modify
the exclusions.paths list where 'paths' and 'tests' are defined so that it
includes the test-file glob (and/or set generated: lax) to ensure package-level
*_test.go files are excluded.

In `@pkg/dtos/dependency_input.go`:
- Around line 23-26: The import grouping in dependency_input.go incorrectly
places the external package
"github.com/scanoss/go-component-helper/componenthelper" with standard library
imports; update the import block to follow conventional grouping: keep stdlib
imports together, place third-party imports (e.g.,
"github.com/scanoss/go-component-helper/componenthelper" and "go.uber.org/zap")
in a separate group, and then local/project imports last so functions/types
referencing componenthelper and zap remain unchanged (no code changes to
functions like any using componenthelper).

In `@pkg/usecase/dependency.go`:
- Around line 91-99: Replace the unnecessary use of fmt.Sprintf in the error
branch of GetURLsByPurlString handling: in the block inside the loop where url,
err := d.allUrls.GetURLsByPurlString(...) leads to setting depOutput.Status =
domain.ComponentStatus{ Message: fmt.Sprintf("component not found"), ... },
simplify Message to the plain string "component not found" (update the
assignment in the code that constructs depOutput.Status inside that error branch
in pkg/usecase/dependency.go).
- Around line 69-75: The code hardcodes MaxWorkers: 5 in the call to
componentHelper.GetComponentsVersion which reduces configurability; update the
call to read MaxWorkers from configuration (e.g., a field on the
usecase/dependency receiver such as d.cfg.MaxWorkers or a passed-in options
value) and fall back to a sensible default if unset, so change the
ComponentVersionCfg construction in the GetComponentsVersion call to use that
config value instead of the literal 5 (refer to MaxWorkers,
componentHelper.GetComponentsVersion, ComponentVersionCfg, and the receiver
fields like d.db/d.ctx/d.s/file.Purls to locate the call).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0f6fc5cc-413a-4f7e-a9bb-854d45d152b5

📥 Commits

Reviewing files that changed from the base of the PR and between 6813088 and c185e4c.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (26)
  • .github/workflows/go-ci.yml
  • .github/workflows/golangci-lint.yml
  • .github/workflows/release.yml
  • .golangci.yml
  • CHANGELOG.md
  • Makefile
  • go.mod
  • pkg/dtos/dependency_input.go
  • pkg/dtos/dependency_output.go
  • pkg/models/all_urls.go
  • pkg/models/all_urls_test.go
  • pkg/models/golang_projects.go
  • pkg/models/golang_projects_test.go
  • pkg/models/licenses.go
  • pkg/models/licenses_test.go
  • pkg/models/mines.go
  • pkg/models/mines_test.go
  • pkg/models/projects.go
  • pkg/models/projects_test.go
  • pkg/models/versions.go
  • pkg/models/versions_test.go
  • pkg/service/dependency_service.go
  • pkg/service/dependency_support.go
  • pkg/service/dependency_support_test.go
  • pkg/usecase/dependency.go
  • pkg/usecase/dependency_test.go
🚧 Files skipped from review as they are similar to previous changes (9)
  • pkg/models/mines_test.go
  • pkg/models/all_urls.go
  • pkg/models/golang_projects_test.go
  • pkg/models/projects_test.go
  • pkg/models/versions_test.go
  • pkg/dtos/dependency_output.go
  • pkg/models/golang_projects.go
  • Makefile
  • .github/workflows/release.yml

Comment thread .golangci.yml
Comment thread CHANGELOG.md
@agustingroh agustingroh force-pushed the chore/SP-4138-add-status-batch-response branch 2 times, most recently from 68ca2d1 to ae46224 Compare March 9, 2026 16:24
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
pkg/transdep/dependency_graph.go (1)

77-88: ⚠️ Potential issue | 🟠 Major

Use the sorted keys here; the current output is still non-deterministic.

Line 77 ranges over dg.dependenciesOf, so the sort done above is effectively dead code. String() can still emit a different order for the same graph across runs.

🛠️ Proposed fix
-	for key, value := range dg.dependenciesOf {
-		children := value
+	for _, dep := range deps {
+		key := *dep
+		children := dg.dependenciesOf[key]
 
 		if len(children) == 0 {
 			_, _ = fmt.Fprintf(&result, "%s --> null\n", key.Purl)
 			continue
 		}
 
 		for _, child := range children {
 			_, _ = fmt.Fprintf(&result, "%s --> %s\n", key.Purl, child.Purl)
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/transdep/dependency_graph.go` around lines 77 - 88, The String() method
currently ranges directly over dg.dependenciesOf which is non-deterministic;
instead collect the map keys (the package keys used as loop variable), sort them
deterministically (e.g., by key.Purl), then iterate over that sorted slice when
building result; also consider sorting each children slice (the value used as
children) before iterating to ensure fully deterministic output. Locate the loop
referencing dg.dependenciesOf and replace it with iterating the sorted keys and
sorted children while writing to result.
pkg/service/dependency_support.go (1)

221-222: ⚠️ Potential issue | 🟡 Minor

Remove debug print statements before merging.

fmt.Printf calls for debugging should be removed or replaced with structured logging via the logger parameter.

🔧 Suggested fix
-	fmt.Printf("Valid components: %v\n", validComponents)
-	fmt.Printf("Invalid components: %v\n", invalidPurls)
+	s.Debugf("Valid components: %v", validComponents)
+	s.Debugf("Invalid components: %v", invalidPurls)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_support.go` around lines 221 - 222, Remove the two
debug fmt.Printf calls that print validComponents and invalidPurls in
pkg/service/dependency_support.go and replace them with structured logging using
the function's logger parameter (e.g., logger.Debug/Info/Error) to record the
same information; locate the lines referencing validComponents and invalidPurls
and change them to logger calls that include context (e.g., "valid components"
and "invalid purls") and the respective variable values so debug output goes
through the structured logger instead of fmt.Printf.
♻️ Duplicate comments (1)
pkg/service/dependency_support.go (1)

90-98: ⚠️ Potential issue | 🟠 Major

Two bugs remain in error-mapping logic.

  1. Lost error codes: The condition dep.Status.StatusCode != domain.Success && dep.Status.Message != "" skips the entire block when status is non-success but message is empty, so ErrorCode is never set for those cases.

  2. Pointer aliasing risk: &dep.Status.Message takes the address of the loop variable's field. In Go versions prior to 1.22, all iterations share the same dep variable address, causing all error messages to point to the last value. Copy the string first:

🔧 Suggested fix
-			if dep.Status.StatusCode != domain.Success && dep.Status.Message != "" {
-				errorCode := domain.StatusCodeToErrorCode(dep.Status.StatusCode)
-				d.ErrorCode = errorCode
-				if dep.Status.Message != "" {
-					d.ErrorMessage = &dep.Status.Message
-				}
+			if dep.Status.StatusCode != domain.Success {
+				d.ErrorCode = domain.StatusCodeToErrorCode(dep.Status.StatusCode)
+				if dep.Status.Message != "" {
+					msg := dep.Status.Message
+					d.ErrorMessage = &msg
+				}
 				deps = append(deps, d)
 				continue
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_support.go` around lines 90 - 98, The error-mapping
block currently skips setting ErrorCode when dep.Status.StatusCode !=
domain.Success but dep.Status.Message == "", and it takes the address of
dep.Status.Message which can alias across loop iterations; update the condition
to only check dep.Status.StatusCode != domain.Success so ErrorCode is always set
(use domain.StatusCodeToErrorCode(dep.Status.StatusCode) and assign to
d.ErrorCode), and when assigning ErrorMessage copy the string into a new local
(e.g. msg := dep.Status.Message) and set d.ErrorMessage = &msg only if msg != ""
to avoid pointer aliasing and keep nil for empty messages before appending d to
deps.
🧹 Nitpick comments (5)
pkg/models/common_test.go (1)

28-42: Mark both setup functions as test helpers.

Adding t.Helper() at the top of sqliteSetup and sqliteConn will report setup failures at the calling test instead of inside common_test.go.

Small cleanup
 func sqliteSetup(t *testing.T) *sqlx.DB {
+	t.Helper()
 	db, err := sqlx.Connect("sqlite", "file::memory:?cache=shared")
@@
 func sqliteConn(t *testing.T, ctx context.Context, db *sqlx.DB) *sqlx.Conn {
+	t.Helper()
 	conn, err := db.Connx(ctx) // Get a connection from the pool
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/models/common_test.go` around lines 28 - 42, The test helper functions
sqliteSetup and sqliteConn should call t.Helper() so failures are reported at
the caller; add t.Helper() as the first line inside both sqliteSetup(t
*testing.T) and sqliteConn(t *testing.T, ctx context.Context, db *sqlx.DB) to
mark them as test helpers and improve error reporting.
pkg/service/dependency_service.go (1)

59-60: Defer request-time telemetry so failures are measured too.

The early returns on Line 67, Line 72, and Line 83 bypass Line 88, so failed dependency requests disappear from the latency histogram. GetTransitiveDependencies already uses the safer defer pattern.

📈 Proposed change
 func (d dependencyServer) GetDependencies(ctx context.Context, request *pb.DependencyRequest) (*pb.DependencyResponse, error) {
 	requestStartTime := time.Now() // Capture the scan start time
+	defer telemetryRequestTime(ctx, d.config, requestStartTime)
 	s := ctxzap.Extract(ctx).Sugar()
 	s.Info("Processing dependency request...")
@@
-	depResponse := convertDependencyOutput(dtoDependencies) // Convert the internal data into a response object
-	telemetryRequestTime(ctx, d.config, requestStartTime)   // Record the request processing time
+	depResponse := convertDependencyOutput(dtoDependencies) // Convert the internal data into a response object

Also applies to: 87-88

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_service.go` around lines 59 - 60, The latency
observation for dependency requests is currently done after early returns so
failures aren't recorded; instead, capture the start time (requestStartTime :=
time.Now()) as before and add a deferred function right after creating the
logger (s := ctxzap.Extract(ctx).Sugar()) that computes elapsed since
requestStartTime and records the latency/telemetry (same pattern used in
GetTransitiveDependencies), ensuring the defer runs on all return paths; update
or remove the existing non-deferred observe call so latency is only recorded by
the new defer.
.github/workflows/golangci-lint.yml (1)

27-28: Pin the actual golangci-lint binary version to match local configuration.

The Makefile pins local Docker lint to v2.10.1, but this workflow only pins the action wrapper. Without an explicit version input, the action defaults to latest, creating a drift risk between local and CI lint results. Since go.mod does not specify a golangci-lint dependency, the action cannot auto-detect the version.

🧹 Proposed change
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v9
+       with:
+         version: v2.10.1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/golangci-lint.yml around lines 27 - 28, The workflow
currently uses the golangci-lint action wrapper (uses:
golangci/golangci-lint-action@v9) but doesn't pin the actual golangci-lint
binary version, causing drift with the Makefile (which uses v2.10.1); update the
golangci-lint step (name: golangci-lint / uses:
golangci/golangci-lint-action@v9) to include a with: block that sets version:
"v2.10.1" so the CI binary matches local configuration.
pkg/usecase/dependency.go (2)

68-74: Consider making MaxWorkers configurable.

The worker pool size is hardcoded to 5. For production flexibility, consider exposing this via ServerConfig to allow tuning based on deployment environment and database connection pool limits.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/usecase/dependency.go` around lines 68 - 74, The call to
componentHelper.GetComponentsVersion hardcodes ComponentVersionCfg.MaxWorkers =
5; make MaxWorkers configurable by adding a MaxWorkers (or WorkerPoolSize) field
to your ServerConfig struct and use that value when constructing
ComponentVersionCfg (e.g., replace the literal 5 with
d.s.ServerConfig.MaxWorkers or similar), ensure sensible default/fallback if
config is zero and document/update any config parsing where ServerConfig is
populated; touch the ComponentVersionCfg type only if necessary to align naming.

108-113: Simplify version assignment.

The conditional can be replaced with a direct assignment since an empty string is an acceptable default.

♻️ Suggested simplification
-			// Avoids empty version
-			if len(url.Version) > 0 {
-				depOutput.Version = url.Version
-			} else {
-				depOutput.Version = ""
-			}
+			// Prefer URL version over component version when available
+			if url.Version != "" {
+				depOutput.Version = url.Version
+			}

Note: This preserves processedComponent.Version (set at line 80) when url.Version is empty, which may be more useful than overwriting with an empty string.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/usecase/dependency.go` around lines 108 - 113, Replace the conditional
block that sets depOutput.Version so it only assigns when url.Version is
non-empty (i.e., if url.Version != "" { depOutput.Version = url.Version }),
leaving depOutput.Version as previously set (e.g., from
processedComponent.Version) when url.Version is empty; this avoids overwriting a
useful default with an empty string and keeps the logic centered on depOutput,
url.Version, and processedComponent.Version.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/models/common_test.go`:
- Around line 27-33: sqliteSetup is using a global in-memory URI which leaks
state across tests; update sqliteSetup to call t.Helper() and construct a
per-test unique SQLite URI using the test name (e.g. "file:" +
url.PathEscape(t.Name()) + "?mode=memory&cache=shared") when calling
sqlx.Connect so each test gets its own named in-memory DB; also add t.Helper()
to sqliteConn so failures point to the test, not the helper, and keep the
existing cache=shared/mode=memory options to preserve connection pooling
semantics.

---

Outside diff comments:
In `@pkg/service/dependency_support.go`:
- Around line 221-222: Remove the two debug fmt.Printf calls that print
validComponents and invalidPurls in pkg/service/dependency_support.go and
replace them with structured logging using the function's logger parameter
(e.g., logger.Debug/Info/Error) to record the same information; locate the lines
referencing validComponents and invalidPurls and change them to logger calls
that include context (e.g., "valid components" and "invalid purls") and the
respective variable values so debug output goes through the structured logger
instead of fmt.Printf.

In `@pkg/transdep/dependency_graph.go`:
- Around line 77-88: The String() method currently ranges directly over
dg.dependenciesOf which is non-deterministic; instead collect the map keys (the
package keys used as loop variable), sort them deterministically (e.g., by
key.Purl), then iterate over that sorted slice when building result; also
consider sorting each children slice (the value used as children) before
iterating to ensure fully deterministic output. Locate the loop referencing
dg.dependenciesOf and replace it with iterating the sorted keys and sorted
children while writing to result.

---

Duplicate comments:
In `@pkg/service/dependency_support.go`:
- Around line 90-98: The error-mapping block currently skips setting ErrorCode
when dep.Status.StatusCode != domain.Success but dep.Status.Message == "", and
it takes the address of dep.Status.Message which can alias across loop
iterations; update the condition to only check dep.Status.StatusCode !=
domain.Success so ErrorCode is always set (use
domain.StatusCodeToErrorCode(dep.Status.StatusCode) and assign to d.ErrorCode),
and when assigning ErrorMessage copy the string into a new local (e.g. msg :=
dep.Status.Message) and set d.ErrorMessage = &msg only if msg != "" to avoid
pointer aliasing and keep nil for empty messages before appending d to deps.

---

Nitpick comments:
In @.github/workflows/golangci-lint.yml:
- Around line 27-28: The workflow currently uses the golangci-lint action
wrapper (uses: golangci/golangci-lint-action@v9) but doesn't pin the actual
golangci-lint binary version, causing drift with the Makefile (which uses
v2.10.1); update the golangci-lint step (name: golangci-lint / uses:
golangci/golangci-lint-action@v9) to include a with: block that sets version:
"v2.10.1" so the CI binary matches local configuration.

In `@pkg/models/common_test.go`:
- Around line 28-42: The test helper functions sqliteSetup and sqliteConn should
call t.Helper() so failures are reported at the caller; add t.Helper() as the
first line inside both sqliteSetup(t *testing.T) and sqliteConn(t *testing.T,
ctx context.Context, db *sqlx.DB) to mark them as test helpers and improve error
reporting.

In `@pkg/service/dependency_service.go`:
- Around line 59-60: The latency observation for dependency requests is
currently done after early returns so failures aren't recorded; instead, capture
the start time (requestStartTime := time.Now()) as before and add a deferred
function right after creating the logger (s := ctxzap.Extract(ctx).Sugar()) that
computes elapsed since requestStartTime and records the latency/telemetry (same
pattern used in GetTransitiveDependencies), ensuring the defer runs on all
return paths; update or remove the existing non-deferred observe call so latency
is only recorded by the new defer.

In `@pkg/usecase/dependency.go`:
- Around line 68-74: The call to componentHelper.GetComponentsVersion hardcodes
ComponentVersionCfg.MaxWorkers = 5; make MaxWorkers configurable by adding a
MaxWorkers (or WorkerPoolSize) field to your ServerConfig struct and use that
value when constructing ComponentVersionCfg (e.g., replace the literal 5 with
d.s.ServerConfig.MaxWorkers or similar), ensure sensible default/fallback if
config is zero and document/update any config parsing where ServerConfig is
populated; touch the ComponentVersionCfg type only if necessary to align naming.
- Around line 108-113: Replace the conditional block that sets depOutput.Version
so it only assigns when url.Version is non-empty (i.e., if url.Version != "" {
depOutput.Version = url.Version }), leaving depOutput.Version as previously set
(e.g., from processedComponent.Version) when url.Version is empty; this avoids
overwriting a useful default with an empty string and keeps the logic centered
on depOutput, url.Version, and processedComponent.Version.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ba5ca02-40fa-4d1d-a6a1-a985836db9c1

📥 Commits

Reviewing files that changed from the base of the PR and between c185e4c and 3724dd7.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (22)
  • .github/workflows/go-ci.yml
  • .github/workflows/golangci-lint.yml
  • .github/workflows/release.yml
  • .golangci.yml
  • CHANGELOG.md
  • Makefile
  • go.mod
  • pkg/dtos/dependency_input.go
  • pkg/dtos/dependency_output.go
  • pkg/errors/error.go
  • pkg/models/all_urls.go
  • pkg/models/common.go
  • pkg/models/common_test.go
  • pkg/models/golang_projects.go
  • pkg/protocol/rest/server.go
  • pkg/service/dependency_service.go
  • pkg/service/dependency_support.go
  • pkg/shared/index.go
  • pkg/transdep/dependency_collector.go
  • pkg/transdep/dependency_graph.go
  • pkg/transdep/helper.go
  • pkg/usecase/dependency.go
💤 Files with no reviewable changes (1)
  • pkg/models/common.go
✅ Files skipped from review due to trivial changes (3)
  • pkg/transdep/dependency_collector.go
  • pkg/shared/index.go
  • pkg/errors/error.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/dtos/dependency_output.go
  • CHANGELOG.md
  • pkg/models/all_urls.go

Comment thread pkg/models/common_test.go
@agustingroh agustingroh force-pushed the chore/SP-4138-add-status-batch-response branch 2 times, most recently from 83c9436 to 18d42c7 Compare March 10, 2026 10:21
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/models/versions.go (1)

80-84: ⚠️ Potential issue | 🟡 Minor

Excess arguments in INSERT statement.

The SQL statement has 2 placeholders ($1, $2) but 4 arguments are passed (name, "", false, false). The extra arguments (false, false) are silently ignored by the database driver, which may mask an intent error or indicate leftover code from a previous schema version.

Based on the Version struct (which has id, version_name, semver), only name and the empty string for semver should be passed.

🔧 Proposed fix
 	err := m.db.QueryRowxContext(m.ctx,
 		"INSERT INTO versions (version_name, semver) VALUES($1, $2)"+
 			" RETURNING id, version_name, semver",
-		name, "", false, false,
+		name, "",
 	).StructScan(&version)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/models/versions.go` around lines 80 - 84, The INSERT call in the method
using m.db.QueryRowxContext in pkg/models/versions.go passes four arguments
(name, "", false, false) while the SQL has only two placeholders ($1, $2);
remove the extraneous false, false arguments so the call supplies exactly name
and the empty semver string to match the SQL, keeping the RETURNING and
StructScan(&version) unchanged.
♻️ Duplicate comments (4)
CHANGELOG.md (1)

18-18: ⚠️ Potential issue | 🟡 Minor

Fix version format for consistency.

The version format v.1.5 includes an extra dot after the "v" prefix. For consistency with other version references in this changelog (e.g., v1.79.2, v0.32.0), it should be v1.5.

📝 Proposed fix
-- Upgraded `package-url/packageurl-go` to v.1.5
+- Upgraded `package-url/packageurl-go` to v1.5
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` at line 18, Update the version string for
package-url/packageurl-go: replace the incorrect "v.1.5" with the consistent
format "v1.5" in the CHANGELOG entry that currently reads `Upgraded
package-url/packageurl-go to v.1.5`.
pkg/service/dependency_support_test.go (1)

37-38: ⚠️ Potential issue | 🟡 Minor

Assert the converted value instead of printing it.

This still passes even if convertDependencyOutput regresses, because nothing is asserted. At minimum, check that the result is non-nil and that an empty dtos.DependencyOutput produces no files.

🧪 Minimal assertion update
 	output := convertDependencyOutput(outputDto)
-	fmt.Printf("Output: %v\n", output)
+	if output == nil {
+		t.Fatal("expected non-nil response")
+	}
+	if len(output.Files) != 0 {
+		t.Fatalf("expected no files, got %d", len(output.Files))
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_support_test.go` around lines 37 - 38, Replace the
debug print with real assertions: inside the test that calls
convertDependencyOutput(outputDto) assert that the returned value (output) is
non-nil, and add a case where you pass an empty dtos.DependencyOutput and assert
that convertDependencyOutput returns an output with zero files (or empty
slice/length 0). Use the test's *testing.T assertion helpers (t.Fatalf/t.Errorf
or testify/require if available) to fail the test on regression; reference the
existing variables outputDto and output and the convertDependencyOutput function
to locate where to add these assertions.
pkg/service/dependency_support.go (1)

90-95: ⚠️ Potential issue | 🟠 Major

Don't drop error_code or reuse the loop variable's message pointer.

Failed components with an empty message currently fall through as successes, and &dep.Status.Message aliases the reused range variable, so later iterations can overwrite earlier error_message values.

🔧 Suggested fix
-			if dep.Status.StatusCode != domain.Success && dep.Status.Message != "" {
-				errorCode := domain.StatusCodeToErrorCode(dep.Status.StatusCode)
-				d.ErrorCode = errorCode
-				if dep.Status.Message != "" {
-					d.ErrorMessage = &dep.Status.Message
-				}
+			if dep.Status.StatusCode != domain.Success {
+				d.ErrorCode = domain.StatusCodeToErrorCode(dep.Status.StatusCode)
+				if dep.Status.Message != "" {
+					msg := dep.Status.Message
+					d.ErrorMessage = &msg
+				}
 				deps = append(deps, d)
 				continue
 			}
In Go, is it safe to take the address of a field on the range variable (for example `&dep.Status.Message`) inside a `for _, dep := range ...` loop, or is the range variable reused across iterations?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_support.go` around lines 90 - 95, The loop currently
skips failed dependencies with empty dep.Status.Message and assigns
&dep.Status.Message which aliases the reused range variable; change the logic so
any non-success StatusCode sets d.ErrorCode using
domain.StatusCodeToErrorCode(dep.Status.StatusCode) regardless of Message
content, and when assigning d.ErrorMessage create a fresh string copy (e.g., tmp
:= dep.Status.Message; d.ErrorMessage = &tmp) instead of taking the address of
the range variable; update the code paths in the loop that reference dep,
d.ErrorCode, and d.ErrorMessage accordingly.
pkg/usecase/dependency_test.go (1)

131-143: ⚠️ Potential issue | 🟡 Minor

Also assert the known valid purl succeeds.

This only proves there is at least one error. A regression where every component is marked failed would still pass, which weakens the new mixed-batch contract for pkg:npm/isbinaryfile.

🧪 Suggested assertion
-	errorCount := 0
+	errorCount := 0
+	successCount := 0
 	for _, file := range dependencies.Files {
 		for _, dep := range file.Dependencies {
+			if dep.Purl == "pkg:npm/isbinaryfile" && dep.Status.StatusCode == domain.Success {
+				successCount++
+			}
 			if dep.Status.StatusCode != "" && dep.Status.StatusCode != domain.Success {
 				errorCount++
 			}
 		}
 	}
+	if successCount == 0 {
+		t.Errorf("expected pkg:npm/isbinaryfile to succeed in mixed batch")
+	}
 	if errorCount == 0 {
 		t.Errorf("expected at least one component-level error in mixed batch, got none")
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/usecase/dependency_test.go` around lines 131 - 143, Add an assertion that
the known-valid package URL "pkg:npm/isbinaryfile" is marked successful in the
mixed-batch test: after computing errorCount over dependencies.Files ->
file.Dependencies (checking dep.Status.StatusCode against domain.Success),
locate the dependency whose PURL equals "pkg:npm/isbinaryfile" (use the existing
dependency struct field for the package URL) and assert its
dep.Status.StatusCode == domain.Success (fail the test if not), so the test
ensures at least that that specific valid component succeeded as well as there
being some failures.
🧹 Nitpick comments (3)
pkg/transdep/dependency_graph.go (1)

68-87: Use the sorted keys when rendering the graph.

Line 77 still ranges over dg.dependenciesOf directly, so String() remains nondeterministic even though deps is sorted above. That makes output unstable for tests and diffs.

Proposed fix
-	deps := make([]*Dependency, 0, len(dg.dependenciesOf))
+	deps := make([]Dependency, 0, len(dg.dependenciesOf))
 	for dep := range dg.dependenciesOf {
-		deps = append(deps, &dep)
+		deps = append(deps, dep)
 	}

 	sort.Slice(deps, func(i, j int) bool {
 		return deps[i].Purl < deps[j].Purl
 	})

-	for key, value := range dg.dependenciesOf {
-		children := value
+	for _, key := range deps {
+		children := dg.dependenciesOf[key]

 		if len(children) == 0 {
 			_, _ = fmt.Fprintf(&result, "%s --> null\n", key.Purl)
 			continue
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/transdep/dependency_graph.go` around lines 68 - 87, The graph rendering
currently ranges over dg.dependenciesOf (making output nondeterministic); change
the second loop to iterate over the sorted deps slice instead. Build deps as a
stable list (avoid taking &dep of the range variable — append a copy or use a
newDep := dep then append(&newDep) or simply append dep values) and then replace
"for key, value := range dg.dependenciesOf" with "for _, key := range deps" and
fetch children via dg.dependenciesOf[key] (or dg.dependenciesOf[*key] if you
keep pointers); ensure you use the same Purl/key used when sorting so
String()/rendering is deterministic.
.golangci.yml (1)

87-89: Consider using a more specific exclusion pattern for test files.

The current exclusion path tests only matches a directory literally named "tests". If test files follow the Go convention of *_test.go files alongside source files (as seen in pkg/models/golang_projects_test.go), this exclusion won't apply to them.

Since run.tests: false already excludes test files from linting, this exclusions.paths entry may be redundant or intended for a different purpose.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.golangci.yml around lines 87 - 89, The exclusion entry under
exclusions.paths currently lists a literal "tests" directory which won't match
Go test files like pkg/models/golang_projects_test.go; either remove that
redundant entry because run.tests: false already skips *_test.go files, or
replace "tests" with a more specific pattern that matches Go test filenames
(e.g., a glob/regex targeting *_test.go) so exclusions.paths actually excludes
test files—look for the exclusions.paths key in .golangci.yml and update or
delete it accordingly.
pkg/service/dependency_service.go (1)

87-88: Record latency for failed requests too.

telemetryRequestTime only runs on the success/warning path here, so bad requests and hard failures disappear from the latency metric. GetTransitiveDependencies already solves this with a defer; doing the same here keeps observability consistent.

📈 Suggested change
 func (d dependencyServer) GetDependencies(ctx context.Context, request *pb.DependencyRequest) (*pb.DependencyResponse, error) {
 	requestStartTime := time.Now() // Capture the scan start time
+	defer telemetryRequestTime(ctx, d.config, requestStartTime)
 	s := ctxzap.Extract(ctx).Sugar()
 	s.Info("Processing dependency request...")
@@
 	}
 	depResponse := convertDependencyOutput(dtoDependencies) // Convert the internal data into a response object
-	telemetryRequestTime(ctx, d.config, requestStartTime)   // Record the request processing time
 	// Set the status and respond with the data
 	return &pb.DependencyResponse{Files: depResponse.Files, Status: &statusResp}, nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_service.go` around lines 87 - 88, Move the
telemetryRequestTime call into a deferred call at the start of the function so
latency is recorded for all outcomes (success, warning, and failures);
specifically, add a defer telemetryRequestTime(ctx, d.config, requestStartTime)
near the beginning of the function that contains convertDependencyOutput and
depResponse (mirroring how GetTransitiveDependencies uses defer) so the request
processing time is always captured even if an error is returned before
depResponse is created.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/models/all_urls_test.go`:
- Around line 53-54: Update the test constructors that build the DB select
context so they exercise the migrated nil-conn path: replace the active
connection argument with nil when calling database.NewDBSelectContext within the
NewAllURLModel (and other model) test setups (i.e., change
database.NewDBSelectContext(s, db, conn, myConfig.Database.Trace) to
database.NewDBSelectContext(s, db, nil, myConfig.Database.Trace)) in all
occurrences in this test file so the code paths used in production (which pass
nil) are exercised; look for NewAllURLModel and the other model constructor
calls that wrap database.NewDBSelectContext and adjust their third parameter to
nil.

In `@pkg/usecase/dependency_test.go`:
- Line 89: The test is hitting a different in-memory SQLite instance because
NewDependencies uses *sqlx.DB while fixtures were loaded via Connx; switch the
test to use a shared-memory DSN and a single connection so both sides see the
same DB. Update the test setup that calls LoadTestSQLData and NewDependencies to
open the DB with a shared-memory DSN (e.g. "file::memory:?cache=shared" or
"file:memdb?mode=memory&cache=shared") and call db.SetMaxOpenConns(1) (and
SetConnMaxIdleTime/SetConnMaxLifetime as needed) so the *sqlx.DB and Connx use
the same underlying in-memory database; ensure Connx and the *sqlx.DB use that
same DSN when creating connections so NewDependencies reads the fixture data.

In `@pkg/usecase/dependency.go`:
- Around line 90-97: The code currently treats any error from
GetURLsByPurlString(processedComponent.Purl, processedComponent.Version) as
domain.ComponentNotFound; instead, if err != nil this is an operational/db error
and should be handled separately (log with d.s.Warnf including err and either
return/propagate the error to caller or set depOutput.Status to an
internal/lookup-failure status rather than ComponentNotFound), and only when the
call succeeds but returns no URLs (nil/empty url result) should you set
depOutput.Status to domain.ComponentNotFound; update the logic around
GetURLsByPurlString, d.s.Warnf, depOutput and depOutputs to reflect this
distinction.
- Around line 68-74: GetComponentsVersion is being called with parallelism
(ComponentVersionCfg{MaxWorkers: 5, Input: file.Purls}), which yields results in
completion order and breaks positional stability; fix by either setting
MaxWorkers to 1 so processing is sequential (preserve input order) or modify
GetComponentsVersion to return a stable ordering: have workers emit results with
the original input index (or use a map keyed by component identity) and then
reconstruct the returned slice in the same order as file.Purls before returning;
update the call site around GetComponentsVersion/ComponentVersionCfg (and fields
Input, MaxWorkers) accordingly so callers receive results in input order.

---

Outside diff comments:
In `@pkg/models/versions.go`:
- Around line 80-84: The INSERT call in the method using m.db.QueryRowxContext
in pkg/models/versions.go passes four arguments (name, "", false, false) while
the SQL has only two placeholders ($1, $2); remove the extraneous false, false
arguments so the call supplies exactly name and the empty semver string to match
the SQL, keeping the RETURNING and StructScan(&version) unchanged.

---

Duplicate comments:
In `@CHANGELOG.md`:
- Line 18: Update the version string for package-url/packageurl-go: replace the
incorrect "v.1.5" with the consistent format "v1.5" in the CHANGELOG entry that
currently reads `Upgraded package-url/packageurl-go to v.1.5`.

In `@pkg/service/dependency_support_test.go`:
- Around line 37-38: Replace the debug print with real assertions: inside the
test that calls convertDependencyOutput(outputDto) assert that the returned
value (output) is non-nil, and add a case where you pass an empty
dtos.DependencyOutput and assert that convertDependencyOutput returns an output
with zero files (or empty slice/length 0). Use the test's *testing.T assertion
helpers (t.Fatalf/t.Errorf or testify/require if available) to fail the test on
regression; reference the existing variables outputDto and output and the
convertDependencyOutput function to locate where to add these assertions.

In `@pkg/service/dependency_support.go`:
- Around line 90-95: The loop currently skips failed dependencies with empty
dep.Status.Message and assigns &dep.Status.Message which aliases the reused
range variable; change the logic so any non-success StatusCode sets d.ErrorCode
using domain.StatusCodeToErrorCode(dep.Status.StatusCode) regardless of Message
content, and when assigning d.ErrorMessage create a fresh string copy (e.g., tmp
:= dep.Status.Message; d.ErrorMessage = &tmp) instead of taking the address of
the range variable; update the code paths in the loop that reference dep,
d.ErrorCode, and d.ErrorMessage accordingly.

In `@pkg/usecase/dependency_test.go`:
- Around line 131-143: Add an assertion that the known-valid package URL
"pkg:npm/isbinaryfile" is marked successful in the mixed-batch test: after
computing errorCount over dependencies.Files -> file.Dependencies (checking
dep.Status.StatusCode against domain.Success), locate the dependency whose PURL
equals "pkg:npm/isbinaryfile" (use the existing dependency struct field for the
package URL) and assert its dep.Status.StatusCode == domain.Success (fail the
test if not), so the test ensures at least that that specific valid component
succeeded as well as there being some failures.

---

Nitpick comments:
In @.golangci.yml:
- Around line 87-89: The exclusion entry under exclusions.paths currently lists
a literal "tests" directory which won't match Go test files like
pkg/models/golang_projects_test.go; either remove that redundant entry because
run.tests: false already skips *_test.go files, or replace "tests" with a more
specific pattern that matches Go test filenames (e.g., a glob/regex targeting
*_test.go) so exclusions.paths actually excludes test files—look for the
exclusions.paths key in .golangci.yml and update or delete it accordingly.

In `@pkg/service/dependency_service.go`:
- Around line 87-88: Move the telemetryRequestTime call into a deferred call at
the start of the function so latency is recorded for all outcomes (success,
warning, and failures); specifically, add a defer telemetryRequestTime(ctx,
d.config, requestStartTime) near the beginning of the function that contains
convertDependencyOutput and depResponse (mirroring how GetTransitiveDependencies
uses defer) so the request processing time is always captured even if an error
is returned before depResponse is created.

In `@pkg/transdep/dependency_graph.go`:
- Around line 68-87: The graph rendering currently ranges over dg.dependenciesOf
(making output nondeterministic); change the second loop to iterate over the
sorted deps slice instead. Build deps as a stable list (avoid taking &dep of the
range variable — append a copy or use a newDep := dep then append(&newDep) or
simply append dep values) and then replace "for key, value := range
dg.dependenciesOf" with "for _, key := range deps" and fetch children via
dg.dependenciesOf[key] (or dg.dependenciesOf[*key] if you keep pointers); ensure
you use the same Purl/key used when sorting so String()/rendering is
deterministic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7baac4b0-cc33-4490-9c20-69d60be1ed16

📥 Commits

Reviewing files that changed from the base of the PR and between 3724dd7 and 18d42c7.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (35)
  • .github/workflows/go-ci.yml
  • .github/workflows/golangci-lint.yml
  • .github/workflows/release.yml
  • .golangci.yml
  • CHANGELOG.md
  • Makefile
  • go.mod
  • pkg/dtos/dependency_input.go
  • pkg/dtos/dependency_output.go
  • pkg/errors/error.go
  • pkg/models/all_urls.go
  • pkg/models/all_urls_test.go
  • pkg/models/common.go
  • pkg/models/common_test.go
  • pkg/models/dependencies.go
  • pkg/models/golang_projects.go
  • pkg/models/golang_projects_test.go
  • pkg/models/licenses.go
  • pkg/models/licenses_test.go
  • pkg/models/mines.go
  • pkg/models/mines_test.go
  • pkg/models/projects.go
  • pkg/models/projects_test.go
  • pkg/models/versions.go
  • pkg/models/versions_test.go
  • pkg/protocol/rest/server.go
  • pkg/service/dependency_service.go
  • pkg/service/dependency_support.go
  • pkg/service/dependency_support_test.go
  • pkg/shared/index.go
  • pkg/transdep/dependency_collector.go
  • pkg/transdep/dependency_graph.go
  • pkg/transdep/helper.go
  • pkg/usecase/dependency.go
  • pkg/usecase/dependency_test.go
💤 Files with no reviewable changes (1)
  • pkg/models/common.go
✅ Files skipped from review due to trivial changes (1)
  • pkg/errors/error.go
🚧 Files skipped from review as they are similar to previous changes (14)
  • pkg/shared/index.go
  • pkg/models/licenses_test.go
  • pkg/models/versions_test.go
  • pkg/transdep/helper.go
  • pkg/dtos/dependency_output.go
  • pkg/models/common_test.go
  • pkg/transdep/dependency_collector.go
  • Makefile
  • .github/workflows/go-ci.yml
  • pkg/models/projects.go
  • pkg/models/mines_test.go
  • pkg/protocol/rest/server.go
  • .github/workflows/golangci-lint.yml
  • pkg/models/projects_test.go

Comment thread pkg/models/all_urls_test.go
Comment thread pkg/usecase/dependency_test.go
Comment thread pkg/usecase/dependency.go
Comment thread pkg/usecase/dependency.go Outdated
@agustingroh agustingroh force-pushed the chore/SP-4138-add-status-batch-response branch 2 times, most recently from d6986ef to 7097286 Compare March 10, 2026 10:57
eeisegn
eeisegn previously approved these changes Mar 10, 2026
@agustingroh agustingroh force-pushed the chore/SP-4138-add-status-batch-response branch 3 times, most recently from 5a157f4 to ffe037b Compare March 18, 2026 14:19
scanoss-qg
scanoss-qg previously approved these changes Mar 18, 2026
Copy link
Copy Markdown

@scanoss-qg scanoss-qg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@agustingroh agustingroh force-pushed the chore/SP-4138-add-status-batch-response branch 4 times, most recently from df0d2e2 to 9595bb0 Compare March 19, 2026 14:26
Comment thread pkg/models/common.go
Comment thread pkg/service/dependency_support.go Outdated
Comment thread pkg/service/dependency_support.go Outdated
Comment thread pkg/usecase/dependency.go Outdated
Comment thread pkg/usecase/dependency.go
@agustingroh agustingroh force-pushed the chore/SP-4138-add-status-batch-response branch 2 times, most recently from 18738fd to 0f0e094 Compare March 19, 2026 16:27
@agustingroh agustingroh force-pushed the chore/SP-4138-add-status-batch-response branch 2 times, most recently from b5cb6ea to 228b35b Compare March 19, 2026 16:41
@agustingroh agustingroh force-pushed the chore/SP-4138-add-status-batch-response branch from 228b35b to 45d52fe Compare March 19, 2026 17:06
@agustingroh agustingroh force-pushed the chore/SP-4138-add-status-batch-response branch 6 times, most recently from 09ab2e3 to b0b1b7e Compare March 20, 2026 12:55
@agustingroh agustingroh force-pushed the chore/SP-4138-add-status-batch-response branch from b0b1b7e to 5fb99d1 Compare March 20, 2026 13:06
@agustingroh agustingroh requested a review from scanoss-qg March 20, 2026 13:07
@eeisegn
Copy link
Copy Markdown
Contributor

eeisegn commented Mar 20, 2026

@coderabbitai resume

@eeisegn
Copy link
Copy Markdown
Contributor

eeisegn commented Mar 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@eeisegn eeisegn dismissed isasmendiagus’s stale review March 20, 2026 17:31

already addressed

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (4)
pkg/dtos/dependency_output.go (1)

47-47: ⚠️ Potential issue | 🟠 Major

Add the json:"status" tag to maintain API consistency.

The Status field lacks a JSON tag and will serialize as "Status" (capital S), while all sibling fields use lowercase tags ("component", "purl", etc.). This inconsistency will break the API response shape.

Suggested fix
-	Status      domain.ComponentStatus
+	Status      domain.ComponentStatus `json:"status"`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/dtos/dependency_output.go` at line 47, The Status field in the DTO struct
is missing a JSON tag causing inconsistent serialization; update the struct
definition to add the JSON tag json:"status" to the Status field (e.g., the
Status field in the DependencyOutput/DTO struct) so it serializes as "status"
like its sibling fields.
pkg/service/dependency_support_test.go (1)

37-38: ⚠️ Potential issue | 🟡 Minor

Re-add assertions to validate the conversion result.

This test only prints the converted value without any assertions. A broken convertDependencyOutput implementation would still pass. Assert the expected shape: non-nil result, empty Files slice for empty input.

Suggested fix
 	output := convertDependencyOutput(outputDto)
-	fmt.Printf("Output: %v\n", output)
+	if output == nil {
+		t.Fatal("expected non-nil output")
+	}
+	if len(output.Files) != 0 {
+		t.Errorf("expected empty Files slice, got %d", len(output.Files))
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_support_test.go` around lines 37 - 38, The test
currently only prints the result of convertDependencyOutput; update the test to
assert the conversion result instead: after calling output :=
convertDependencyOutput(outputDto) add assertions that output is not nil (e.g.,
non-nil pointer or non-empty struct as appropriate) and that output.Files is an
empty slice (len(output.Files) == 0 or equivalent) to validate the expected
shape for empty input; reference the convertDependencyOutput function and the
output variable in your assertions.
pkg/service/dependency_support.go (1)

90-94: ⚠️ Potential issue | 🔴 Critical

Pointer to loop variable field causes shared pointer bug.

Line 93 takes &dep.Status.Message where dep is the range loop variable. Since range variables are reused across iterations, all d.ErrorMessage pointers will reference the same memory location, causing all error messages to show the last iteration's value.

🐛 Proposed fix
 		if dep.Status.StatusCode != domain.Success {
 			errorCode := domain.StatusCodeToErrorCode(dep.Status.StatusCode)
 			d.ErrorCode = errorCode
-			d.ErrorMessage = &dep.Status.Message
+			msg := dep.Status.Message
+			d.ErrorMessage = &msg
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/service/dependency_support.go` around lines 90 - 94, In the range loop
where you inspect each dep (the range variable named dep) you currently assign
d.ErrorMessage = &dep.Status.Message which takes the address of the reused loop
variable causing all ErrorMessage pointers to alias the same memory; fix by
creating a new local copy of the message inside the loop (e.g. msg :=
dep.Status.Message) and assign d.ErrorMessage = &msg (or assign the string value
instead of a pointer) so each d.ErrorMessage points to its own distinct memory;
update the code paths in the function containing this loop accordingly.
pkg/usecase/dependency.go (1)

96-103: ⚠️ Potential issue | 🟠 Major

Don't translate lookup errors into “component not found”.

A real miss does not hit this branch: pkg/models/all_urls.go already returns a fallback AllURL with nil error for that case. An error here means invalid input or a lookup/query failure, so converting it to ComponentWithoutInfo masks operational problems and makes the per-component status misleading.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/usecase/dependency.go` around lines 96 - 103, The error from
d.allUrls.GetURLsByPurlString is an operational/lookup failure and must not be
translated to domain.ComponentWithoutInfo; update the error branch in the
GetURLsByPurlString handling so you log the full error (use d.s.Errorf) and set
depOutput.Status.Message = err.Error() and depOutput.Status.StatusCode =
domain.ComponentLookupError (create this constant if it doesn't exist, or use an
existing internal/error status code rather than ComponentWithoutInfo), then
append depOutput and continue—do not treat this path as a legitimate "component
not found" success case.
🧹 Nitpick comments (1)
pkg/models/dependencies_test.go (1)

27-31: Consider simplifying the deferred closure.

The closure doesn't need to take db as a parameter since it can capture it directly from the enclosing scope. This would also make the explicit *sqlx.DB type annotation unnecessary.

Additionally, this cleanup pattern differs from other test files (e.g., licenses_test.go uses defer CloseDB(db)). Consider using a consistent pattern across test files for maintainability.

♻️ Optional: Simplified closure
-	defer func(db *sqlx.DB) {
-		if errDB := db.Close(); errDB != nil {
-			t.Fatalf("failed to close db: %v", errDB)
-		}
-	}(db)
+	defer func() {
+		if errDB := db.Close(); errDB != nil {
+			t.Fatalf("failed to close db: %v", errDB)
+		}
+	}()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/models/dependencies_test.go` around lines 27 - 31, The deferred closure
that closes the database (currently using defer func(db *sqlx.DB) { ... }(db))
is overly verbose; replace it with a simpler deferred call that captures db from
the outer scope (e.g., defer func() { if err := db.Close(); err != nil {
t.Fatalf(...) } }()) or, better, use the existing test helper CloseDB(db) used
in licenses_test.go to keep patterns consistent—update the test to call
CloseDB(db) (or the simplified anonymous defer) and remove the explicit *sqlx.DB
parameter and type annotation from the closure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@go.mod`:
- Line 13: Update the module version to match the CHANGELOG: change the go.mod
dependency for github.com/scanoss/go-component-helper from v0.4.0 to v0.5.0 (or
alternatively, if v0.4.0 is correct, update the CHANGELOG entry to reflect
v0.4.0); locate the dependency line referencing
github.com/scanoss/go-component-helper in go.mod and set it to v0.5.0 so the
declared version and CHANGELOG.md are consistent.
- Line 21: Update the gRPC module from google.golang.org/grpc v1.79.2 to v1.79.3
or later to address the authorization bypass vulnerability; edit the dependency
version for google.golang.org/grpc in the go.mod entry (replace v1.79.2 with
v1.79.3 or a newer safe release), then run the module update commands (e.g., go
get google.golang.org/grpc@v1.79.3 and go mod tidy) and run your test
suite/build to ensure compatibility.

In `@pkg/models/all_urls.go`:
- Around line 90-100: Get rid of using PurlName == "" as the golang miss
sentinel: change GetURLsByPurlNameType to return an explicit "found" signal
(e.g., (AllURL, bool, error) or set a Found bool on AllURL) and update the call
sites in this function to check that explicit flag instead of PurlName when
deciding to call getURLsByGolangPurl; update any other callers (including the
golang_projects/GitHub-conversion paths) to honor the new found signal so
fallback results that populate PurlName no longer block the golang fallback.

In `@pkg/usecase/dependency.go`:
- Around line 125-135: The code treats processedComponent.Requirement as an
exact pin by comparing trimmed strings (using
purlutils.GetVersionFromReqOperator) which misflags ranged requirements; replace
the string equality check with a proper range evaluation (e.g., use a
semver/range check utility) to test whether url.Version satisfies
processedComponent.Requirement, and only set depOutput.Status to VersionNotFound
when that range check fails; do not overwrite depOutput.Version with the
requested requirement—leave depOutput.Version as url.Version and only modify
depOutput.Status (domain.ComponentStatus) and Message when the range check
indicates a real mismatch.

---

Duplicate comments:
In `@pkg/dtos/dependency_output.go`:
- Line 47: The Status field in the DTO struct is missing a JSON tag causing
inconsistent serialization; update the struct definition to add the JSON tag
json:"status" to the Status field (e.g., the Status field in the
DependencyOutput/DTO struct) so it serializes as "status" like its sibling
fields.

In `@pkg/service/dependency_support_test.go`:
- Around line 37-38: The test currently only prints the result of
convertDependencyOutput; update the test to assert the conversion result
instead: after calling output := convertDependencyOutput(outputDto) add
assertions that output is not nil (e.g., non-nil pointer or non-empty struct as
appropriate) and that output.Files is an empty slice (len(output.Files) == 0 or
equivalent) to validate the expected shape for empty input; reference the
convertDependencyOutput function and the output variable in your assertions.

In `@pkg/service/dependency_support.go`:
- Around line 90-94: In the range loop where you inspect each dep (the range
variable named dep) you currently assign d.ErrorMessage = &dep.Status.Message
which takes the address of the reused loop variable causing all ErrorMessage
pointers to alias the same memory; fix by creating a new local copy of the
message inside the loop (e.g. msg := dep.Status.Message) and assign
d.ErrorMessage = &msg (or assign the string value instead of a pointer) so each
d.ErrorMessage points to its own distinct memory; update the code paths in the
function containing this loop accordingly.

In `@pkg/usecase/dependency.go`:
- Around line 96-103: The error from d.allUrls.GetURLsByPurlString is an
operational/lookup failure and must not be translated to
domain.ComponentWithoutInfo; update the error branch in the GetURLsByPurlString
handling so you log the full error (use d.s.Errorf) and set
depOutput.Status.Message = err.Error() and depOutput.Status.StatusCode =
domain.ComponentLookupError (create this constant if it doesn't exist, or use an
existing internal/error status code rather than ComponentWithoutInfo), then
append depOutput and continue—do not treat this path as a legitimate "component
not found" success case.

---

Nitpick comments:
In `@pkg/models/dependencies_test.go`:
- Around line 27-31: The deferred closure that closes the database (currently
using defer func(db *sqlx.DB) { ... }(db)) is overly verbose; replace it with a
simpler deferred call that captures db from the outer scope (e.g., defer func()
{ if err := db.Close(); err != nil { t.Fatalf(...) } }()) or, better, use the
existing test helper CloseDB(db) used in licenses_test.go to keep patterns
consistent—update the test to call CloseDB(db) (or the simplified anonymous
defer) and remove the explicit *sqlx.DB parameter and type annotation from the
closure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8ccba456-ac1d-466c-8bc2-02961d70ec26

📥 Commits

Reviewing files that changed from the base of the PR and between 18d42c7 and 5fb99d1.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (41)
  • .github/workflows/go-ci.yml
  • .github/workflows/golangci-lint.yml
  • .github/workflows/release.yml
  • .golangci.yml
  • CHANGELOG.md
  • Makefile
  • go.mod
  • pkg/dtos/dependency_input.go
  • pkg/dtos/dependency_input_test.go
  • pkg/dtos/dependency_output.go
  • pkg/dtos/dependency_output_test.go
  • pkg/errors/error.go
  • pkg/models/all_urls.go
  • pkg/models/all_urls_test.go
  • pkg/models/common.go
  • pkg/models/common_test.go
  • pkg/models/dependencies.go
  • pkg/models/dependencies_test.go
  • pkg/models/golang_projects.go
  • pkg/models/golang_projects_test.go
  • pkg/models/licenses.go
  • pkg/models/licenses_test.go
  • pkg/models/mines.go
  • pkg/models/mines_test.go
  • pkg/models/projects.go
  • pkg/models/projects_test.go
  • pkg/models/versions.go
  • pkg/models/versions_test.go
  • pkg/protocol/rest/server.go
  • pkg/service/dependency_service.go
  • pkg/service/dependency_service_test.go
  • pkg/service/dependency_support.go
  • pkg/service/dependency_support_test.go
  • pkg/shared/index.go
  • pkg/transdep/dependency_collector.go
  • pkg/transdep/dependency_collector_test.go
  • pkg/transdep/dependency_graph.go
  • pkg/transdep/helper.go
  • pkg/transdep/utils_test.go
  • pkg/usecase/dependency.go
  • pkg/usecase/dependency_test.go
💤 Files with no reviewable changes (1)
  • pkg/models/common.go
✅ Files skipped from review due to trivial changes (9)
  • pkg/shared/index.go
  • pkg/transdep/utils_test.go
  • pkg/dtos/dependency_input_test.go
  • pkg/protocol/rest/server.go
  • pkg/errors/error.go
  • pkg/dtos/dependency_output_test.go
  • pkg/transdep/dependency_collector_test.go
  • pkg/transdep/dependency_collector.go
  • pkg/transdep/helper.go
🚧 Files skipped from review as they are similar to previous changes (19)
  • pkg/transdep/dependency_graph.go
  • .github/workflows/golangci-lint.yml
  • pkg/models/common_test.go
  • Makefile
  • pkg/models/versions_test.go
  • .github/workflows/go-ci.yml
  • pkg/models/dependencies.go
  • pkg/models/projects_test.go
  • pkg/models/mines_test.go
  • pkg/models/versions.go
  • .github/workflows/release.yml
  • pkg/models/all_urls_test.go
  • .golangci.yml
  • pkg/models/golang_projects_test.go
  • pkg/models/licenses.go
  • pkg/usecase/dependency_test.go
  • pkg/models/projects.go
  • pkg/service/dependency_service.go
  • CHANGELOG.md

Comment thread go.mod Outdated
Comment thread go.mod Outdated
Comment thread pkg/models/all_urls.go
Comment on lines +90 to +100
if result.PurlName == "" && component.PurlType == "golang" {
return m.getURLsByGolangPurl(component)
}
return result, nil
}
result, err := m.GetURLsByPurlNameType(component.Name, component.PurlType)
if err != nil {
return AllURL{}, err
}
if result.PurlName == "" && component.PurlType == "golang" {
return m.getURLsByGolangPurl(component)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't use PurlName as the golang miss sentinel anymore.

Line 216 now fills PurlName on fallback results, so after a normal all_urls miss this branch no longer calls getURLsByGolangPurl. Most Go components will return the generic fallback immediately and never reach golang_projects or the GitHub-conversion path. Please switch this to an explicit “found” signal instead of inferring it from PurlName.

Also applies to: 216-216

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/models/all_urls.go` around lines 90 - 100, Get rid of using PurlName ==
"" as the golang miss sentinel: change GetURLsByPurlNameType to return an
explicit "found" signal (e.g., (AllURL, bool, error) or set a Found bool on
AllURL) and update the call sites in this function to check that explicit flag
instead of PurlName when deciding to call getURLsByGolangPurl; update any other
callers (including the golang_projects/GitHub-conversion paths) to honor the new
found signal so fallback results that populate PurlName no longer block the
golang fallback.

Comment thread pkg/usecase/dependency.go
Comment on lines +125 to 135
if processedComponent.Requirement != "" {
v := purlutils.GetVersionFromReqOperator(processedComponent.Requirement)
// Compare versions ignoring the "v" prefix (e.g., "v1.2.3" == "1.2.3")
// If the version does not satisfy the requirement, mark the status accordingly.
if strings.TrimPrefix(url.Version, "v") != strings.TrimPrefix(v, "v") {
depOutput.Status = domain.ComponentStatus{
StatusCode: domain.VersionNotFound,
Message: fmt.Sprintf("Requirement not met, showing information for version '%s'", url.Version),
}
depOutput.Version = v
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't treat ranged requirements as exact pins, and keep the resolved version.

This exact compare will flag valid ranges like the PR's ^1.5.5 -> 1.14.3 example as VERSION_NOT_FOUND. On real mismatches, Line 134 then replaces the resolved version with the requested one, which also conflicts with the PR's boost 0.1 does not satisfy requirement 1.83.0 response shape. Evaluate the requirement with proper range semantics and leave depOutput.Version set to url.Version.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/usecase/dependency.go` around lines 125 - 135, The code treats
processedComponent.Requirement as an exact pin by comparing trimmed strings
(using purlutils.GetVersionFromReqOperator) which misflags ranged requirements;
replace the string equality check with a proper range evaluation (e.g., use a
semver/range check utility) to test whether url.Version satisfies
processedComponent.Requirement, and only set depOutput.Status to VersionNotFound
when that range check fails; do not overwrite depOutput.Version with the
requested requirement—leave depOutput.Version as url.Version and only modify
depOutput.Status (domain.ComponentStatus) and Message when the range check
indicates a real mismatch.

@agustingroh agustingroh merged commit 52a41aa into main Mar 20, 2026
2 of 3 checks passed
@agustingroh agustingroh deleted the chore/SP-4138-add-status-batch-response branch March 20, 2026 17:33
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 20, 2026

✅ Actions performed

Reviews resumed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants