Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions internal/api/handlers/v0/publish_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,25 @@ func TestPublishEndpoint(t *testing.T) {
},
expectedStatus: http.StatusOK,
},
{
name: "reject publish with empty repository",
requestBody: apiv0.ServerJSON{
Schema: model.CurrentSchemaURL,
Name: "example/test-server",
Description: "A test server",
Repository: &model.Repository{},
Version: "1.0.0",
},
tokenClaims: &auth.JWTClaims{
AuthMethod: auth.MethodNone,
Permissions: []auth.Permission{
{Action: auth.PermissionActionPublish, ResourcePattern: "example/*"},
},
},
setupRegistryService: func(_ service.RegistryService) {},
expectedStatus: http.StatusUnprocessableEntity,
expectedError: "Failed to publish server, invalid schema: call /validate for details",
},
{
name: "missing authorization header",
requestBody: apiv0.ServerJSON{},
Expand Down
28 changes: 26 additions & 2 deletions internal/validators/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,32 @@ func ValidateServerJSON(serverJSON *apiv0.ServerJSON, opts ValidationOptions) *V
func validateRepository(ctx *ValidationContext, obj *model.Repository) *ValidationResult {
result := &ValidationResult{Valid: true, Issues: []ValidationIssue{}}

// Skip validation if repository is nil or empty (optional field)
if obj == nil || (obj.URL == "" && obj.Source == "") {
// The repository field is optional, but its required fields must be present
// when the repository object itself is provided.
if obj == nil {
return result
}
if obj.URL == "" {
issue := NewValidationIssue(
ValidationIssueTypeSemantic,
ctx.Field("url").String(),
"repository url is required when repository is provided",
ValidationIssueSeverityError,
"repository-url-required",
)
result.AddIssue(issue)
}
if obj.Source == "" {
issue := NewValidationIssue(
ValidationIssueTypeSemantic,
ctx.Field("source").String(),
"repository source is required when repository is provided",
ValidationIssueSeverityError,
"repository-source-required",
)
result.AddIssue(issue)
}
if !result.Valid {
return result
}

Expand Down
47 changes: 47 additions & 0 deletions internal/validators/validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,16 @@ func TestValidate(t *testing.T) {
},
expectedError: validators.ErrMultipleSlashesInServerName.Error(),
},
{
name: "server without repository",
serverDetail: apiv0.ServerJSON{
Schema: model.CurrentSchemaURL,
Name: "com.example/test-server",
Description: "A test server",
Version: "1.0.0",
},
expectedError: "",
},
{
name: "valid server detail with all fields",
serverDetail: apiv0.ServerJSON{
Expand Down Expand Up @@ -281,6 +291,43 @@ func TestValidate(t *testing.T) {
},
expectedError: "",
},
{
name: "server with empty repository",
serverDetail: apiv0.ServerJSON{
Schema: model.CurrentSchemaURL,
Name: "com.example/test-server",
Description: "A test server",
Repository: &model.Repository{},
Version: "1.0.0",
},
expectedError: "repository url is required when repository is provided",
},
{
name: "server with repository missing URL",
serverDetail: apiv0.ServerJSON{
Schema: model.CurrentSchemaURL,
Name: "com.example/test-server",
Description: "A test server",
Repository: &model.Repository{
Source: "github",
},
Version: "1.0.0",
},
expectedError: "repository url is required when repository is provided",
},
{
name: "server with repository missing source",
serverDetail: apiv0.ServerJSON{
Schema: model.CurrentSchemaURL,
Name: "com.example/test-server",
Description: "A test server",
Repository: &model.Repository{
URL: "https://github.com/owner/repo",
},
Version: "1.0.0",
},
expectedError: "repository source is required when repository is provided",
},
{
name: "server with invalid repository source",
serverDetail: apiv0.ServerJSON{
Expand Down