From 3267a2d245d5299db8ba486d94a4c4b30a8b253a Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Thu, 13 Nov 2025 15:10:24 +0100 Subject: [PATCH 1/2] UpstreamRegistry type, converters and test code Signed-off-by: Daniele Martinoli --- .../converters/registry_converters.go | 96 +++++ .../converters/registry_converters_test.go | 345 ++++++++++++++++++ pkg/registry/types/upstream_registry.go | 50 +++ 3 files changed, 491 insertions(+) create mode 100644 pkg/registry/converters/registry_converters.go create mode 100644 pkg/registry/converters/registry_converters_test.go create mode 100644 pkg/registry/types/upstream_registry.go diff --git a/pkg/registry/converters/registry_converters.go b/pkg/registry/converters/registry_converters.go new file mode 100644 index 000000000..334de9830 --- /dev/null +++ b/pkg/registry/converters/registry_converters.go @@ -0,0 +1,96 @@ +package converters + +import ( + "fmt" + "time" + + upstreamv0 "github.com/modelcontextprotocol/registry/pkg/api/v0" + + "github.com/stacklok/toolhive/pkg/registry/types" +) + +// NewServerRegistryFromUpstream creates a ServerRegistry from upstream ServerJSON array. +// This is used when ingesting data from upstream MCP Registry API endpoints. +func NewServerRegistryFromUpstream(servers []upstreamv0.ServerJSON) *types.ServerRegistry { + return &types.ServerRegistry{ + Version: "1.0.0", + LastUpdated: time.Now().Format(time.RFC3339), + Servers: servers, + } +} + +// NewServerRegistryFromToolhive creates a ServerRegistry from ToolHive Registry. +// This converts ToolHive format to upstream ServerJSON using the converters package. +// Used when ingesting data from ToolHive-format sources (Git, File, API). +func NewServerRegistryFromToolhive(toolhiveReg *types.Registry) (*types.ServerRegistry, error) { + if toolhiveReg == nil { + return nil, fmt.Errorf("toolhive registry cannot be nil") + } + + servers := make([]upstreamv0.ServerJSON, 0, len(toolhiveReg.Servers)+len(toolhiveReg.RemoteServers)) + + // Convert container servers using converters package + for name, imgMeta := range toolhiveReg.Servers { + serverJSON, err := ImageMetadataToServerJSON(name, imgMeta) + if err != nil { + return nil, fmt.Errorf("failed to convert server %s: %w", name, err) + } + servers = append(servers, *serverJSON) + } + + // Convert remote servers using converters package + for name, remoteMeta := range toolhiveReg.RemoteServers { + serverJSON, err := RemoteServerMetadataToServerJSON(name, remoteMeta) + if err != nil { + return nil, fmt.Errorf("failed to convert remote server %s: %w", name, err) + } + servers = append(servers, *serverJSON) + } + + return &types.ServerRegistry{ + Version: toolhiveReg.Version, + LastUpdated: toolhiveReg.LastUpdated, + Servers: servers, + }, nil +} + +// ToToolhive converts ServerRegistry back to ToolHive Registry format. +// Used for backward compatibility with v0 API. +func ToToolhive(sr *types.ServerRegistry) (*types.Registry, error) { + if sr == nil { + return nil, fmt.Errorf("server registry cannot be nil") + } + + toolhiveReg := &types.Registry{ + Version: sr.Version, + LastUpdated: sr.LastUpdated, + Servers: make(map[string]*types.ImageMetadata), + RemoteServers: make(map[string]*types.RemoteServerMetadata), + } + + for i := range sr.Servers { + serverJSON := &sr.Servers[i] + name := types.ExtractSimpleName(serverJSON.Name) + + // Detect server type by presence of packages vs remotes + if len(serverJSON.Packages) > 0 { + // Container server + imgMeta, err := ServerJSONToImageMetadata(serverJSON) + if err != nil { + return nil, fmt.Errorf("failed to convert server %s: %w", serverJSON.Name, err) + } + toolhiveReg.Servers[name] = imgMeta + } else if len(serverJSON.Remotes) > 0 { + // Remote server + remoteMeta, err := ServerJSONToRemoteServerMetadata(serverJSON) + if err != nil { + return nil, fmt.Errorf("failed to convert remote server %s: %w", serverJSON.Name, err) + } + toolhiveReg.RemoteServers[name] = remoteMeta + } + // Note: Servers with neither packages nor remotes are skipped + // This shouldn't happen with valid ServerJSON data + } + + return toolhiveReg, nil +} diff --git a/pkg/registry/converters/registry_converters_test.go b/pkg/registry/converters/registry_converters_test.go new file mode 100644 index 000000000..3daeeb506 --- /dev/null +++ b/pkg/registry/converters/registry_converters_test.go @@ -0,0 +1,345 @@ +package converters + +import ( + "testing" + "time" + + upstreamv0 "github.com/modelcontextprotocol/registry/pkg/api/v0" + "github.com/modelcontextprotocol/registry/pkg/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/stacklok/toolhive/pkg/registry/types" +) + +func TestNewServerRegistryFromToolhive(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + toolhiveReg *types.Registry + expectError bool + validate func(*testing.T, *types.ServerRegistry) + }{ + { + name: "successful conversion with container servers", + toolhiveReg: &types.Registry{ + Version: "1.0.0", + LastUpdated: "2024-01-01T00:00:00Z", + Servers: map[string]*types.ImageMetadata{ + "test-server": { + BaseServerMetadata: types.BaseServerMetadata{ + Name: "test-server", + Description: "A test server", + Tier: "Community", + Status: "Active", + Transport: "stdio", + Tools: []string{"test_tool"}, + }, + Image: "test/image:latest", + }, + }, + RemoteServers: make(map[string]*types.RemoteServerMetadata), + }, + expectError: false, + validate: func(t *testing.T, sr *types.ServerRegistry) { + t.Helper() + assert.Equal(t, "1.0.0", sr.Version) + assert.Equal(t, "2024-01-01T00:00:00Z", sr.LastUpdated) + assert.Len(t, sr.Servers, 1) + assert.Contains(t, sr.Servers[0].Name, "test-server") + assert.Equal(t, "A test server", sr.Servers[0].Description) + }, + }, + { + name: "successful conversion with remote servers", + toolhiveReg: &types.Registry{ + Version: "1.0.0", + LastUpdated: "2024-01-01T00:00:00Z", + Servers: make(map[string]*types.ImageMetadata), + RemoteServers: map[string]*types.RemoteServerMetadata{ + "remote-server": { + BaseServerMetadata: types.BaseServerMetadata{ + Name: "remote-server", + Description: "A remote server", + Tier: "Community", + Status: "Active", + Transport: "sse", + Tools: []string{"remote_tool"}, + }, + URL: "https://example.com", + }, + }, + }, + expectError: false, + validate: func(t *testing.T, sr *types.ServerRegistry) { + t.Helper() + assert.Len(t, sr.Servers, 1) + assert.Contains(t, sr.Servers[0].Name, "remote-server") + }, + }, + { + name: "empty registry", + toolhiveReg: &types.Registry{ + Version: "1.0.0", + LastUpdated: "2024-01-01T00:00:00Z", + Servers: make(map[string]*types.ImageMetadata), + RemoteServers: make(map[string]*types.RemoteServerMetadata), + }, + expectError: false, + validate: func(t *testing.T, sr *types.ServerRegistry) { + t.Helper() + assert.Empty(t, sr.Servers) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + result, err := NewServerRegistryFromToolhive(tt.toolhiveReg) + + if tt.expectError { + assert.Error(t, err) + assert.Nil(t, result) + } else { + assert.NoError(t, err) + assert.NotNil(t, result) + if tt.validate != nil { + tt.validate(t, result) + } + } + }) + } +} + +func TestNewServerRegistryFromUpstream(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + servers []upstreamv0.ServerJSON + validate func(*testing.T, *types.ServerRegistry) + }{ + { + name: "create from upstream servers", + servers: []upstreamv0.ServerJSON{ + { + Schema: "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json", + Name: "io.test/server1", + Description: "Test server 1", + Version: "1.0.0", + Packages: []model.Package{ + { + RegistryType: "oci", + Identifier: "test/image:latest", + Transport: model.Transport{Type: "stdio"}, + }, + }, + }, + }, + validate: func(t *testing.T, sr *types.ServerRegistry) { + t.Helper() + assert.Equal(t, "1.0.0", sr.Version) + assert.NotEmpty(t, sr.LastUpdated) + assert.Len(t, sr.Servers, 1) + assert.Equal(t, "io.test/server1", sr.Servers[0].Name) + }, + }, + { + name: "create from empty slice", + servers: []upstreamv0.ServerJSON{}, + validate: func(t *testing.T, sr *types.ServerRegistry) { + t.Helper() + assert.Empty(t, sr.Servers) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + result := NewServerRegistryFromUpstream(tt.servers) + + assert.NotNil(t, result) + if tt.validate != nil { + tt.validate(t, result) + } + }) + } +} + +func TestServerRegistry_ToToolhive(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + serverReg *types.ServerRegistry + expectError bool + validateFunc func(*testing.T, *types.Registry) + }{ + { + name: "convert to toolhive with servers", + serverReg: &types.ServerRegistry{ + Version: "1.0.0", + LastUpdated: "2024-01-01T00:00:00Z", + Servers: []upstreamv0.ServerJSON{ + { + Schema: "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json", + Name: "io.test/server1", + Description: "Test server", + Version: "1.0.0", + Packages: []model.Package{ + { + RegistryType: "oci", + Identifier: "test/image:latest", + Transport: model.Transport{Type: "stdio"}, + }, + }, + }, + }, + }, + expectError: false, + validateFunc: func(t *testing.T, reg *types.Registry) { + t.Helper() + assert.Equal(t, "1.0.0", reg.Version) + assert.Equal(t, "2024-01-01T00:00:00Z", reg.LastUpdated) + assert.Len(t, reg.Servers, 1) + assert.Equal(t, "test/image:latest", reg.Servers["server1"].Image) + assert.Equal(t, "stdio", reg.Servers["server1"].Transport) + assert.Equal(t, "Test server", reg.Servers["server1"].Description) + assert.Equal(t, "io.test/server1", reg.Servers["server1"].Name) + }, + }, + { + name: "convert to toolhive with remote servers", + serverReg: &types.ServerRegistry{ + Version: "1.0.0", + LastUpdated: "2024-01-01T00:00:00Z", + Servers: []upstreamv0.ServerJSON{ + { + Schema: "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json", + Name: "io.test/server1", + Description: "Test server", + Version: "1.0.0", + Remotes: []model.Transport{ + { + Type: "sse", + URL: "https://example.com", + }, + }, + }, + }, + }, + expectError: false, + validateFunc: func(t *testing.T, reg *types.Registry) { + t.Helper() + assert.Equal(t, "1.0.0", reg.Version) + assert.Equal(t, "2024-01-01T00:00:00Z", reg.LastUpdated) + assert.Len(t, reg.Servers, 0) + assert.Len(t, reg.RemoteServers, 1) + assert.Equal(t, "https://example.com", reg.RemoteServers["server1"].URL) + assert.Equal(t, "sse", reg.RemoteServers["server1"].Transport) + assert.Equal(t, "Test server", reg.RemoteServers["server1"].Description) + assert.Equal(t, "io.test/server1", reg.RemoteServers["server1"].Name) + }, + }, + { + name: "convert empty registry", + serverReg: &types.ServerRegistry{ + Version: "1.0.0", + LastUpdated: "2024-01-01T00:00:00Z", + Servers: []upstreamv0.ServerJSON{}, + }, + expectError: false, + validateFunc: func(t *testing.T, reg *types.Registry) { + t.Helper() + assert.Empty(t, reg.Servers) + assert.Empty(t, reg.RemoteServers) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + result, err := ToToolhive(tt.serverReg) + + if tt.expectError { + assert.Error(t, err) + assert.Nil(t, result) + } else { + assert.NoError(t, err) + assert.NotNil(t, result) + if tt.validateFunc != nil { + tt.validateFunc(t, result) + } + } + }) + } +} + +func TestServerRegistry_RoundTripConversion(t *testing.T) { + t.Parallel() + + originalToolhive := &types.Registry{ + Version: "1.0.0", + LastUpdated: "2024-01-01T00:00:00Z", + Servers: map[string]*types.ImageMetadata{ + "test-server": { + BaseServerMetadata: types.BaseServerMetadata{ + Name: "test-server", + Description: "A test server", + Tier: "Community", + Status: "Active", + Transport: "stdio", + Tools: []string{"test_tool"}, + }, + Image: "test/image:latest", + }, + }, + RemoteServers: make(map[string]*types.RemoteServerMetadata), + } + + // Convert to ServerRegistry + serverReg, err := NewServerRegistryFromToolhive(originalToolhive) + require.NoError(t, err) + require.NotNil(t, serverReg) + + // Convert back to ToolHive + convertedBack, err := ToToolhive(serverReg) + require.NoError(t, err) + require.NotNil(t, convertedBack) + + // Verify key fields match + assert.Equal(t, originalToolhive.Version, convertedBack.Version) + assert.Equal(t, originalToolhive.LastUpdated, convertedBack.LastUpdated) + assert.Len(t, convertedBack.Servers, 1) +} + +func TestNewServerRegistryFromUpstream_DefaultValues(t *testing.T) { + t.Parallel() + + servers := []upstreamv0.ServerJSON{ + { + Schema: "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json", + Name: "io.test/server1", + Description: "Test server", + Version: "1.0.0", + }, + } + + result := NewServerRegistryFromUpstream(servers) + + // Verify defaults + assert.Equal(t, "1.0.0", result.Version) + assert.NotEmpty(t, result.LastUpdated) + + // Verify timestamp is recent (within last minute) + parsedTime, err := time.Parse(time.RFC3339, result.LastUpdated) + require.NoError(t, err) + assert.WithinDuration(t, time.Now(), parsedTime, time.Minute) +} diff --git a/pkg/registry/types/upstream_registry.go b/pkg/registry/types/upstream_registry.go new file mode 100644 index 000000000..6fa4c6625 --- /dev/null +++ b/pkg/registry/types/upstream_registry.go @@ -0,0 +1,50 @@ +package types + +import ( + "strings" + + upstreamv0 "github.com/modelcontextprotocol/registry/pkg/api/v0" +) + +// ServerRegistry is the unified internal registry format. +// It stores servers in upstream ServerJSON format while maintaining +// ToolHive-compatible metadata fields for backward compatibility. +type ServerRegistry struct { + // Version is the schema version (ToolHive compatibility) + Version string `json:"version"` + + // LastUpdated is the timestamp when registry was last updated (ToolHive compatibility) + LastUpdated string `json:"last_updated"` + + // Servers contains the server definitions in upstream MCP format + Servers []upstreamv0.ServerJSON `json:"servers"` +} + +// GetServerByName retrieves a server by its name. +// Supports both reverse-DNS format (e.g., "io.github.user/server") and simple names (e.g., "server"). +func (sr *ServerRegistry) GetServerByName(name string) (*upstreamv0.ServerJSON, bool) { + if sr == nil { + return nil, false + } + + for i := range sr.Servers { + serverName := sr.Servers[i].Name + if serverName == name || ExtractSimpleName(serverName) == name { + return &sr.Servers[i], true + } + } + return nil, false +} + +// ExtractSimpleName extracts the simple server name from reverse-DNS format. +// Examples: +// - "io.github.user/server" -> "server" +// - "com.example/my-server" -> "my-server" +// - "simple-name" -> "simple-name" (no change if not reverse-DNS) +func ExtractSimpleName(reverseDNS string) string { + idx := strings.LastIndex(reverseDNS, "/") + if idx >= 0 && idx < len(reverseDNS)-1 { + return reverseDNS[idx+1:] + } + return reverseDNS +} From a7c5a326fbed55864c7c83bcfcb73cc6653c3c2c Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Mon, 17 Nov 2025 12:21:22 +0100 Subject: [PATCH 2/2] removed unnecessary functions Signed-off-by: Daniele Martinoli --- .../converters/registry_converters.go | 41 ----- .../converters/registry_converters_test.go | 149 ------------------ pkg/registry/types/upstream_registry.go | 31 ---- 3 files changed, 221 deletions(-) diff --git a/pkg/registry/converters/registry_converters.go b/pkg/registry/converters/registry_converters.go index 334de9830..5fc7784e5 100644 --- a/pkg/registry/converters/registry_converters.go +++ b/pkg/registry/converters/registry_converters.go @@ -53,44 +53,3 @@ func NewServerRegistryFromToolhive(toolhiveReg *types.Registry) (*types.ServerRe Servers: servers, }, nil } - -// ToToolhive converts ServerRegistry back to ToolHive Registry format. -// Used for backward compatibility with v0 API. -func ToToolhive(sr *types.ServerRegistry) (*types.Registry, error) { - if sr == nil { - return nil, fmt.Errorf("server registry cannot be nil") - } - - toolhiveReg := &types.Registry{ - Version: sr.Version, - LastUpdated: sr.LastUpdated, - Servers: make(map[string]*types.ImageMetadata), - RemoteServers: make(map[string]*types.RemoteServerMetadata), - } - - for i := range sr.Servers { - serverJSON := &sr.Servers[i] - name := types.ExtractSimpleName(serverJSON.Name) - - // Detect server type by presence of packages vs remotes - if len(serverJSON.Packages) > 0 { - // Container server - imgMeta, err := ServerJSONToImageMetadata(serverJSON) - if err != nil { - return nil, fmt.Errorf("failed to convert server %s: %w", serverJSON.Name, err) - } - toolhiveReg.Servers[name] = imgMeta - } else if len(serverJSON.Remotes) > 0 { - // Remote server - remoteMeta, err := ServerJSONToRemoteServerMetadata(serverJSON) - if err != nil { - return nil, fmt.Errorf("failed to convert remote server %s: %w", serverJSON.Name, err) - } - toolhiveReg.RemoteServers[name] = remoteMeta - } - // Note: Servers with neither packages nor remotes are skipped - // This shouldn't happen with valid ServerJSON data - } - - return toolhiveReg, nil -} diff --git a/pkg/registry/converters/registry_converters_test.go b/pkg/registry/converters/registry_converters_test.go index 3daeeb506..2de75409e 100644 --- a/pkg/registry/converters/registry_converters_test.go +++ b/pkg/registry/converters/registry_converters_test.go @@ -171,155 +171,6 @@ func TestNewServerRegistryFromUpstream(t *testing.T) { } } -func TestServerRegistry_ToToolhive(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - serverReg *types.ServerRegistry - expectError bool - validateFunc func(*testing.T, *types.Registry) - }{ - { - name: "convert to toolhive with servers", - serverReg: &types.ServerRegistry{ - Version: "1.0.0", - LastUpdated: "2024-01-01T00:00:00Z", - Servers: []upstreamv0.ServerJSON{ - { - Schema: "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json", - Name: "io.test/server1", - Description: "Test server", - Version: "1.0.0", - Packages: []model.Package{ - { - RegistryType: "oci", - Identifier: "test/image:latest", - Transport: model.Transport{Type: "stdio"}, - }, - }, - }, - }, - }, - expectError: false, - validateFunc: func(t *testing.T, reg *types.Registry) { - t.Helper() - assert.Equal(t, "1.0.0", reg.Version) - assert.Equal(t, "2024-01-01T00:00:00Z", reg.LastUpdated) - assert.Len(t, reg.Servers, 1) - assert.Equal(t, "test/image:latest", reg.Servers["server1"].Image) - assert.Equal(t, "stdio", reg.Servers["server1"].Transport) - assert.Equal(t, "Test server", reg.Servers["server1"].Description) - assert.Equal(t, "io.test/server1", reg.Servers["server1"].Name) - }, - }, - { - name: "convert to toolhive with remote servers", - serverReg: &types.ServerRegistry{ - Version: "1.0.0", - LastUpdated: "2024-01-01T00:00:00Z", - Servers: []upstreamv0.ServerJSON{ - { - Schema: "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json", - Name: "io.test/server1", - Description: "Test server", - Version: "1.0.0", - Remotes: []model.Transport{ - { - Type: "sse", - URL: "https://example.com", - }, - }, - }, - }, - }, - expectError: false, - validateFunc: func(t *testing.T, reg *types.Registry) { - t.Helper() - assert.Equal(t, "1.0.0", reg.Version) - assert.Equal(t, "2024-01-01T00:00:00Z", reg.LastUpdated) - assert.Len(t, reg.Servers, 0) - assert.Len(t, reg.RemoteServers, 1) - assert.Equal(t, "https://example.com", reg.RemoteServers["server1"].URL) - assert.Equal(t, "sse", reg.RemoteServers["server1"].Transport) - assert.Equal(t, "Test server", reg.RemoteServers["server1"].Description) - assert.Equal(t, "io.test/server1", reg.RemoteServers["server1"].Name) - }, - }, - { - name: "convert empty registry", - serverReg: &types.ServerRegistry{ - Version: "1.0.0", - LastUpdated: "2024-01-01T00:00:00Z", - Servers: []upstreamv0.ServerJSON{}, - }, - expectError: false, - validateFunc: func(t *testing.T, reg *types.Registry) { - t.Helper() - assert.Empty(t, reg.Servers) - assert.Empty(t, reg.RemoteServers) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - result, err := ToToolhive(tt.serverReg) - - if tt.expectError { - assert.Error(t, err) - assert.Nil(t, result) - } else { - assert.NoError(t, err) - assert.NotNil(t, result) - if tt.validateFunc != nil { - tt.validateFunc(t, result) - } - } - }) - } -} - -func TestServerRegistry_RoundTripConversion(t *testing.T) { - t.Parallel() - - originalToolhive := &types.Registry{ - Version: "1.0.0", - LastUpdated: "2024-01-01T00:00:00Z", - Servers: map[string]*types.ImageMetadata{ - "test-server": { - BaseServerMetadata: types.BaseServerMetadata{ - Name: "test-server", - Description: "A test server", - Tier: "Community", - Status: "Active", - Transport: "stdio", - Tools: []string{"test_tool"}, - }, - Image: "test/image:latest", - }, - }, - RemoteServers: make(map[string]*types.RemoteServerMetadata), - } - - // Convert to ServerRegistry - serverReg, err := NewServerRegistryFromToolhive(originalToolhive) - require.NoError(t, err) - require.NotNil(t, serverReg) - - // Convert back to ToolHive - convertedBack, err := ToToolhive(serverReg) - require.NoError(t, err) - require.NotNil(t, convertedBack) - - // Verify key fields match - assert.Equal(t, originalToolhive.Version, convertedBack.Version) - assert.Equal(t, originalToolhive.LastUpdated, convertedBack.LastUpdated) - assert.Len(t, convertedBack.Servers, 1) -} - func TestNewServerRegistryFromUpstream_DefaultValues(t *testing.T) { t.Parallel() diff --git a/pkg/registry/types/upstream_registry.go b/pkg/registry/types/upstream_registry.go index 6fa4c6625..9d87efb83 100644 --- a/pkg/registry/types/upstream_registry.go +++ b/pkg/registry/types/upstream_registry.go @@ -1,8 +1,6 @@ package types import ( - "strings" - upstreamv0 "github.com/modelcontextprotocol/registry/pkg/api/v0" ) @@ -19,32 +17,3 @@ type ServerRegistry struct { // Servers contains the server definitions in upstream MCP format Servers []upstreamv0.ServerJSON `json:"servers"` } - -// GetServerByName retrieves a server by its name. -// Supports both reverse-DNS format (e.g., "io.github.user/server") and simple names (e.g., "server"). -func (sr *ServerRegistry) GetServerByName(name string) (*upstreamv0.ServerJSON, bool) { - if sr == nil { - return nil, false - } - - for i := range sr.Servers { - serverName := sr.Servers[i].Name - if serverName == name || ExtractSimpleName(serverName) == name { - return &sr.Servers[i], true - } - } - return nil, false -} - -// ExtractSimpleName extracts the simple server name from reverse-DNS format. -// Examples: -// - "io.github.user/server" -> "server" -// - "com.example/my-server" -> "my-server" -// - "simple-name" -> "simple-name" (no change if not reverse-DNS) -func ExtractSimpleName(reverseDNS string) string { - idx := strings.LastIndex(reverseDNS, "/") - if idx >= 0 && idx < len(reverseDNS)-1 { - return reverseDNS[idx+1:] - } - return reverseDNS -}