From d28a8a66f01296dcbd70d7178e4ea1ec2376391f Mon Sep 17 00:00:00 2001 From: Agustin Groh Date: Mon, 9 Mar 2026 10:58:03 -0300 Subject: [PATCH] chore(deps):SP-4138 add error_message and error_code fields to Dependency batch response --- CHANGELOG.md | 6 ++ api/dependenciesv2/scanoss-dependencies.pb.go | 81 ++++++++++++------- .../scanoss/api/dependencies/v2/README.md | 37 ++++++++- .../v2/scanoss-dependencies.proto | 6 +- .../v2/scanoss-dependencies.swagger.json | 22 ++++- 5 files changed, 122 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a7ea11..7000721 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.32.0] - 2026-03-09 +### Added +- Added `error_message` and `error_code` fields to `Dependencies` message in `DependencyResponse` for error handling at the individual dependency level +- Updated `DependencyResponse` JSON schema description to reflect error fields inside the dependency block + ## [0.31.0] - 2026-02-24 ### Added - Added gRPC `GetComponentStatus` and REST endpoint GET `/v2/components/status/component` for retrieving lifecycle status of a single component @@ -238,6 +243,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Vulnerabilities - Added REST endpoint support for each service also +[0.32.0]: https://github.com/scanoss/papi/compare/v0.31.0...v0.32.0 [0.31.0]: https://github.com/scanoss/papi/compare/v0.30.0...v0.31.0 [0.30.0]: https://github.com/scanoss/papi/compare/v0.29.0...v0.30.0 [0.29.0]: https://github.com/scanoss/papi/compare/v0.28.0...v0.29.0 diff --git a/api/dependenciesv2/scanoss-dependencies.pb.go b/api/dependenciesv2/scanoss-dependencies.pb.go index 2fb059e..3e57fcb 100644 --- a/api/dependenciesv2/scanoss-dependencies.pb.go +++ b/api/dependenciesv2/scanoss-dependencies.pb.go @@ -464,14 +464,18 @@ func (x *DependencyResponse_Licenses) GetUrl() string { } type DependencyResponse_Dependencies struct { - state protoimpl.MessageState `protogen:"open.v1"` - Component string `protobuf:"bytes,1,opt,name=component,proto3" json:"component,omitempty"` - Purl string `protobuf:"bytes,2,opt,name=purl,proto3" json:"purl,omitempty"` - Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` - Licenses []*DependencyResponse_Licenses `protobuf:"bytes,4,rep,name=licenses,proto3" json:"licenses,omitempty"` - Url string `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"` - Comment string `protobuf:"bytes,6,opt,name=comment,proto3" json:"comment,omitempty"` - Requirement string `protobuf:"bytes,7,opt,name=requirement,proto3" json:"requirement,omitempty"` // string scope = 7; + state protoimpl.MessageState `protogen:"open.v1"` + Component string `protobuf:"bytes,1,opt,name=component,proto3" json:"component,omitempty"` + Purl string `protobuf:"bytes,2,opt,name=purl,proto3" json:"purl,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + Licenses []*DependencyResponse_Licenses `protobuf:"bytes,4,rep,name=licenses,proto3" json:"licenses,omitempty"` + Url string `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"` + Comment string `protobuf:"bytes,6,opt,name=comment,proto3" json:"comment,omitempty"` + Requirement string `protobuf:"bytes,7,opt,name=requirement,proto3" json:"requirement,omitempty"` + // Optional error message describing what went wrong during component processing + ErrorMessage *string `protobuf:"bytes,8,opt,name=error_message,proto3,oneof" json:"error_message,omitempty"` + // Optional error code indicating the type of error encountered + ErrorCode *commonv2.ErrorCode `protobuf:"varint,9,opt,name=error_code,proto3,enum=scanoss.api.common.v2.ErrorCode,oneof" json:"error_code,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -555,6 +559,20 @@ func (x *DependencyResponse_Dependencies) GetRequirement() string { return "" } +func (x *DependencyResponse_Dependencies) GetErrorMessage() string { + if x != nil && x.ErrorMessage != nil { + return *x.ErrorMessage + } + return "" +} + +func (x *DependencyResponse_Dependencies) GetErrorCode() commonv2.ErrorCode { + if x != nil && x.ErrorCode != nil { + return *x.ErrorCode + } + return commonv2.ErrorCode(0) +} + type DependencyResponse_Files struct { state protoimpl.MessageState `protogen:"open.v1"` File string `protobuf:"bytes,1,opt,name=file,proto3" json:"file,omitempty"` @@ -697,7 +715,7 @@ const file_scanoss_api_dependencies_v2_scanoss_dependencies_proto_rawDesc = "" + "\x05Files\x12\x12\n" + "\x04file\x18\x01 \x01(\tR\x04file\x12J\n" + "\x05purls\x18\x02 \x03(\v24.scanoss.api.dependencies.v2.DependencyRequest.PurlsR\x05purls:i\x92Ad\n" + - "bJ`{\"files\":[{\"file\":\"package.json\",\"purls\":[{\"purl\":\"pkg:npm/express\",\"requirement\":\"^4.18.0\"}]}]}\x18\x01\"\xfe\b\n" + + "bJ`{\"files\":[{\"file\":\"package.json\",\"purls\":[{\"purl\":\"pkg:npm/express\",\"requirement\":\"^4.18.0\"}]}]}\x18\x01\"\xa3\r\n" + "\x12DependencyResponse\x12K\n" + "\x05files\x18\x01 \x03(\v25.scanoss.api.dependencies.v2.DependencyResponse.FilesR\x05files\x12=\n" + "\x06status\x18\x02 \x01(\v2%.scanoss.api.common.v2.StatusResponseR\x06status\x1av\n" + @@ -705,7 +723,7 @@ const file_scanoss_api_dependencies_v2_scanoss_dependencies_proto_rawDesc = "" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" + "\aspdx_id\x18\x02 \x01(\tR\aspdx_id\x12*\n" + "\x10is_spdx_approved\x18\x03 \x01(\bR\x10is_spdx_approved\x12\x10\n" + - "\x03url\x18\x04 \x01(\tR\x03url\x1a\xfe\x01\n" + + "\x03url\x18\x04 \x01(\tR\x03url\x1a\x91\x03\n" + "\fDependencies\x12\x1c\n" + "\tcomponent\x18\x01 \x01(\tR\tcomponent\x12\x12\n" + "\x04purl\x18\x02 \x01(\tR\x04purl\x12\x18\n" + @@ -713,13 +731,19 @@ const file_scanoss_api_dependencies_v2_scanoss_dependencies_proto_rawDesc = "" + "\blicenses\x18\x04 \x03(\v28.scanoss.api.dependencies.v2.DependencyResponse.LicensesR\blicenses\x12\x10\n" + "\x03url\x18\x05 \x01(\tR\x03url\x12\x18\n" + "\acomment\x18\x06 \x01(\tR\acomment\x12 \n" + - "\vrequirement\x18\a \x01(\tR\vrequirement\x1a\xa5\x01\n" + + "\vrequirement\x18\a \x01(\tR\vrequirement\x12)\n" + + "\rerror_message\x18\b \x01(\tH\x00R\rerror_message\x88\x01\x01\x12E\n" + + "\n" + + "error_code\x18\t \x01(\x0e2 .scanoss.api.common.v2.ErrorCodeH\x01R\n" + + "error_code\x88\x01\x01B\x10\n" + + "\x0e_error_messageB\r\n" + + "\v_error_code\x1a\xa5\x01\n" + "\x05Files\x12\x12\n" + "\x04file\x18\x01 \x01(\tR\x04file\x12\x0e\n" + "\x02id\x18\x02 \x01(\tR\x02id\x12\x16\n" + "\x06status\x18\x03 \x01(\tR\x06status\x12`\n" + - "\fdependencies\x18\x04 \x03(\v2<.scanoss.api.dependencies.v2.DependencyResponse.DependenciesR\fdependencies:\xba\x03\x92A\xb4\x03\n" + - "\xb1\x03J\xae\x03{\"files\":[{\"file\":\"package.json\",\"id\":\"dependency\",\"status\":\"pending\",\"dependencies\":[{\"component\":\"express\",\"purl\":\"pkg:npm/express\",\"version\":\"4.18.2\",\"requirement\":\"^4.18.0\",\"licenses\":[{\"name\":\"MIT\",\"spdx_id\":\"MIT\",\"is_spdx_approved\":true,\"url\":\"https://opensource.org/licenses/MIT\"}],\"url\":\"https://www.npmjs.com/package/express\",\"comment\":\"\"}]}],\"status\":{\"status\":\"SUCCESS\",\"message\":\"Dependencies successfully retrieved\"}}\x18\x01\"\xa7\x02\n" + + "\fdependencies\x18\x04 \x03(\v2<.scanoss.api.dependencies.v2.DependencyResponse.DependenciesR\fdependencies:\xcc\x06\x92A\xc6\x06\n" + + "\xc3\x062\x8f\x03Success example. For error cases, dependency block includes error_message and error_code fields, e.g.: {\\\"files\\\":[{\\\"file\\\":\\\"package.json\\\",\\\"id\\\":\\\"dependency\\\",\\\"status\\\":\\\"pending\\\",\\\"dependencies\\\":[{\\\"component\\\":\\\"\\\",\\\"purl\\\":\\\"pkg:npm/express\\\",\\\"error_message\\\":\\\"Component not found\\\",\\\"error_code\\\":\\\"COMPONENT_NOT_FOUND\\\"}]}],\\\"status\\\":{\\\"status\\\":\\\"SUCCESS\\\",\\\"message\\\":\\\"Success\\\"}}J\xae\x03{\"files\":[{\"file\":\"package.json\",\"id\":\"dependency\",\"status\":\"pending\",\"dependencies\":[{\"component\":\"express\",\"purl\":\"pkg:npm/express\",\"version\":\"4.18.2\",\"requirement\":\"^4.18.0\",\"licenses\":[{\"name\":\"MIT\",\"spdx_id\":\"MIT\",\"is_spdx_approved\":true,\"url\":\"https://opensource.org/licenses/MIT\"}],\"url\":\"https://www.npmjs.com/package/express\",\"comment\":\"\"}]}],\"status\":{\"status\":\"SUCCESS\",\"message\":\"Dependencies successfully retrieved\"}}\x18\x01\"\xa7\x02\n" + "\x1bTransitiveDependencyRequest\x12\x14\n" + "\x05depth\x18\x01 \x01(\x05R\x05depth\x12\x14\n" + "\x05limit\x18\x02 \x01(\x05R\x05limit\x12G\n" + @@ -771,8 +795,9 @@ var file_scanoss_api_dependencies_v2_scanoss_dependencies_proto_goTypes = []any{ (*TransitiveDependencyResponse_Component)(nil), // 9: scanoss.api.dependencies.v2.TransitiveDependencyResponse.Component (*commonv2.StatusResponse)(nil), // 10: scanoss.api.common.v2.StatusResponse (*commonv2.ComponentRequest)(nil), // 11: scanoss.api.common.v2.ComponentRequest - (*commonv2.EchoRequest)(nil), // 12: scanoss.api.common.v2.EchoRequest - (*commonv2.EchoResponse)(nil), // 13: scanoss.api.common.v2.EchoResponse + (commonv2.ErrorCode)(0), // 12: scanoss.api.common.v2.ErrorCode + (*commonv2.EchoRequest)(nil), // 13: scanoss.api.common.v2.EchoRequest + (*commonv2.EchoResponse)(nil), // 14: scanoss.api.common.v2.EchoResponse } var file_scanoss_api_dependencies_v2_scanoss_dependencies_proto_depIdxs = []int32{ 5, // 0: scanoss.api.dependencies.v2.DependencyRequest.files:type_name -> scanoss.api.dependencies.v2.DependencyRequest.Files @@ -783,18 +808,19 @@ var file_scanoss_api_dependencies_v2_scanoss_dependencies_proto_depIdxs = []int3 10, // 5: scanoss.api.dependencies.v2.TransitiveDependencyResponse.status:type_name -> scanoss.api.common.v2.StatusResponse 4, // 6: scanoss.api.dependencies.v2.DependencyRequest.Files.purls:type_name -> scanoss.api.dependencies.v2.DependencyRequest.Purls 6, // 7: scanoss.api.dependencies.v2.DependencyResponse.Dependencies.licenses:type_name -> scanoss.api.dependencies.v2.DependencyResponse.Licenses - 7, // 8: scanoss.api.dependencies.v2.DependencyResponse.Files.dependencies:type_name -> scanoss.api.dependencies.v2.DependencyResponse.Dependencies - 12, // 9: scanoss.api.dependencies.v2.Dependencies.Echo:input_type -> scanoss.api.common.v2.EchoRequest - 0, // 10: scanoss.api.dependencies.v2.Dependencies.GetDependencies:input_type -> scanoss.api.dependencies.v2.DependencyRequest - 2, // 11: scanoss.api.dependencies.v2.Dependencies.GetTransitiveDependencies:input_type -> scanoss.api.dependencies.v2.TransitiveDependencyRequest - 13, // 12: scanoss.api.dependencies.v2.Dependencies.Echo:output_type -> scanoss.api.common.v2.EchoResponse - 1, // 13: scanoss.api.dependencies.v2.Dependencies.GetDependencies:output_type -> scanoss.api.dependencies.v2.DependencyResponse - 3, // 14: scanoss.api.dependencies.v2.Dependencies.GetTransitiveDependencies:output_type -> scanoss.api.dependencies.v2.TransitiveDependencyResponse - 12, // [12:15] is the sub-list for method output_type - 9, // [9:12] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 12, // 8: scanoss.api.dependencies.v2.DependencyResponse.Dependencies.error_code:type_name -> scanoss.api.common.v2.ErrorCode + 7, // 9: scanoss.api.dependencies.v2.DependencyResponse.Files.dependencies:type_name -> scanoss.api.dependencies.v2.DependencyResponse.Dependencies + 13, // 10: scanoss.api.dependencies.v2.Dependencies.Echo:input_type -> scanoss.api.common.v2.EchoRequest + 0, // 11: scanoss.api.dependencies.v2.Dependencies.GetDependencies:input_type -> scanoss.api.dependencies.v2.DependencyRequest + 2, // 12: scanoss.api.dependencies.v2.Dependencies.GetTransitiveDependencies:input_type -> scanoss.api.dependencies.v2.TransitiveDependencyRequest + 14, // 13: scanoss.api.dependencies.v2.Dependencies.Echo:output_type -> scanoss.api.common.v2.EchoResponse + 1, // 14: scanoss.api.dependencies.v2.Dependencies.GetDependencies:output_type -> scanoss.api.dependencies.v2.DependencyResponse + 3, // 15: scanoss.api.dependencies.v2.Dependencies.GetTransitiveDependencies:output_type -> scanoss.api.dependencies.v2.TransitiveDependencyResponse + 13, // [13:16] is the sub-list for method output_type + 10, // [10:13] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name } func init() { file_scanoss_api_dependencies_v2_scanoss_dependencies_proto_init() } @@ -802,6 +828,7 @@ func file_scanoss_api_dependencies_v2_scanoss_dependencies_proto_init() { if File_scanoss_api_dependencies_v2_scanoss_dependencies_proto != nil { return } + file_scanoss_api_dependencies_v2_scanoss_dependencies_proto_msgTypes[7].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/protobuf/scanoss/api/dependencies/v2/README.md b/protobuf/scanoss/api/dependencies/v2/README.md index f5869ca..6bcdf01 100644 --- a/protobuf/scanoss/api/dependencies/v2/README.md +++ b/protobuf/scanoss/api/dependencies/v2/README.md @@ -139,8 +139,12 @@ Each dependency object includes: - `licenses`: Array of license information - `url`: Component homepage or repository URL - `comment`: Additional analysis notes +- `error_message` (optional): Error message describing what went wrong during component processing +- `error_code` (optional): Error code indicating the type of error encountered -### Response Example +### Response Examples + +#### Success ```json { "files": [ @@ -175,6 +179,37 @@ Each dependency object includes: } ``` +#### Error in dependency +When a component cannot be processed, the dependency block includes `error_message` and `error_code` fields. The remaining fields will be empty since the component could not be resolved: +```json +{ + "files": [ + { + "file": "conanfile.txt", + "id": "dependency", + "status": "pending", + "dependencies": [ + { + "component": "", + "purl": "pkg:conan/gtest", + "version": "", + "licenses": [], + "url": "", + "comment": "", + "requirement": "v1.17.0", + "error_message": "Component version not found", + "error_code": "VERSION_NOT_FOUND" + } + ] + } + ], + "status": { + "status": "SUCCESS", + "message": "Dependencies successfully retrieved" + } +} +``` + ## Echo Standard service health check endpoint for testing connectivity and API key validation. diff --git a/protobuf/scanoss/api/dependencies/v2/scanoss-dependencies.proto b/protobuf/scanoss/api/dependencies/v2/scanoss-dependencies.proto index d7c7143..a4036e5 100644 --- a/protobuf/scanoss/api/dependencies/v2/scanoss-dependencies.proto +++ b/protobuf/scanoss/api/dependencies/v2/scanoss-dependencies.proto @@ -131,6 +131,7 @@ message DependencyResponse { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { json_schema: { example: "{\"files\":[{\"file\":\"package.json\",\"id\":\"dependency\",\"status\":\"pending\",\"dependencies\":[{\"component\":\"express\",\"purl\":\"pkg:npm/express\",\"version\":\"4.18.2\",\"requirement\":\"^4.18.0\",\"licenses\":[{\"name\":\"MIT\",\"spdx_id\":\"MIT\",\"is_spdx_approved\":true,\"url\":\"https://opensource.org/licenses/MIT\"}],\"url\":\"https://www.npmjs.com/package/express\",\"comment\":\"\"}]}],\"status\":{\"status\":\"SUCCESS\",\"message\":\"Dependencies successfully retrieved\"}}"; + description: "Success example. For error cases, dependency block includes error_message and error_code fields, e.g.: {\\\"files\\\":[{\\\"file\\\":\\\"package.json\\\",\\\"id\\\":\\\"dependency\\\",\\\"status\\\":\\\"pending\\\",\\\"dependencies\\\":[{\\\"component\\\":\\\"\\\",\\\"purl\\\":\\\"pkg:npm/express\\\",\\\"error_message\\\":\\\"Component not found\\\",\\\"error_code\\\":\\\"COMPONENT_NOT_FOUND\\\"}]}],\\\"status\\\":{\\\"status\\\":\\\"SUCCESS\\\",\\\"message\\\":\\\"Success\\\"}}"; } }; @@ -148,7 +149,10 @@ message DependencyResponse { string url = 5; string comment = 6; string requirement = 7; -// string scope = 7; + // Optional error message describing what went wrong during component processing + optional string error_message = 8 [json_name = "error_message"]; + // Optional error code indicating the type of error encountered + optional common.v2.ErrorCode error_code = 9 [json_name = "error_code"]; } message Files { string file = 1; diff --git a/protobuf/scanoss/api/dependencies/v2/scanoss-dependencies.swagger.json b/protobuf/scanoss/api/dependencies/v2/scanoss-dependencies.swagger.json index 40fddf8..c212e7f 100644 --- a/protobuf/scanoss/api/dependencies/v2/scanoss-dependencies.swagger.json +++ b/protobuf/scanoss/api/dependencies/v2/scanoss-dependencies.swagger.json @@ -173,8 +173,15 @@ "type": "string" }, "requirement": { + "type": "string" + }, + "error_message": { "type": "string", - "title": "string scope = 7;" + "title": "Optional error message describing what went wrong during component processing" + }, + "error_code": { + "$ref": "#/definitions/v2ErrorCode", + "title": "Optional error code indicating the type of error encountered" } } }, @@ -394,6 +401,7 @@ "title": "Response status (required?)" } }, + "description": "Success example. For error cases, dependency block includes error_message and error_code fields, e.g.: {\\\"files\\\":[{\\\"file\\\":\\\"package.json\\\",\\\"id\\\":\\\"dependency\\\",\\\"status\\\":\\\"pending\\\",\\\"dependencies\\\":[{\\\"component\\\":\\\"\\\",\\\"purl\\\":\\\"pkg:npm/express\\\",\\\"error_message\\\":\\\"Component not found\\\",\\\"error_code\\\":\\\"COMPONENT_NOT_FOUND\\\"}]}],\\\"status\\\":{\\\"status\\\":\\\"SUCCESS\\\",\\\"message\\\":\\\"Success\\\"}}", "title": "Dependency response data (JSON payload)" }, "v2DependencyResponseFiles": { @@ -435,6 +443,18 @@ }, "description": "Echo Message Response." }, + "v2ErrorCode": { + "type": "string", + "enum": [ + "INVALID_PURL", + "COMPONENT_NOT_FOUND", + "NO_INFO", + "INVALID_SEMVER", + "VERSION_NOT_FOUND" + ], + "default": "INVALID_PURL", + "description": "Error code enum for component analysis operations.\nRepresents the various error conditions that can occur during component processing and validation.\n\n - INVALID_PURL: The provided Package URL (PURL) is invalid or malformed\n - COMPONENT_NOT_FOUND: The requested component could not be found in the database\n - NO_INFO: No information is available for the requested component\n - INVALID_SEMVER: The provided semantic version (SemVer) is invalid or malformed\n - VERSION_NOT_FOUND: Component version not found" + }, "v2StatusCode": { "type": "string", "enum": [