refactor: modify Listen method to receive metadata via functional options

MyungSub0519 committed Nov 12, 2025 at 23:25 UTC 5afb3cac08ee30ab8eb5913666bcf0557881b2e8
3 files changed +40 -20
cmd/demo-app/main.go
+2 -8
@@ -186,15 +186,9 @@ func runPaint() error {
186 return fmt.Errorf("new client: %w", err)
187 }
188 defer client.Close()
189 -
189 +
190 // 3) Register lease and obtain a net.Listener that accepts relayed connections
191 - metadata := &sdk.Metadata{
192 - Description: "Portal demo paint app",
193 - Tags: []string{"demo", "paint"},
194 - Owner: "PortalApp Developer",
195 - Country: "KR",
196 - }
197 - listener, err := client.Listen(cred, flagName, []string{"http/1.1"}, metadata)
191 + listener, err := client.Listen(cred, flagName, []string{"http/1.1"}, sdk.WithDescription("Portal demo paint app"), sdk.WithTags([]string{"demo", "paint"}), sdk.WithOwner("PortalApp Developer"), sdk.WithCountry("KR"))
192 if err != nil {
193 return fmt.Errorf("listen: %w", err)
194 }
sdk/sdk.go
+36 -10
@@ -2,7 +2,6 @@ package sdk
2
3 import (
4 "context"
5 - "encoding/json"
5 "errors"
6 "fmt"
7 "io"
@@ -177,6 +176,38 @@ type Metadata struct {
176 Country string `json:"country"`
177 }
178
179 +type MetadataOption func(*Metadata)
180 +
181 +func WithDescription(description string) MetadataOption {
182 + return func(m *Metadata) {
183 + m.Description = description
184 + }
185 +}
186 +
187 +func WithTags(tags []string) MetadataOption {
188 + return func(m *Metadata) {
189 + m.Tags = tags
190 + }
191 +}
192 +
193 +func WithThumbnail(thumbnail string) MetadataOption {
194 + return func(m *Metadata) {
195 + m.Thumbnail = thumbnail
196 + }
197 +}
198 +
199 +func WithOwner(owner string) MetadataOption {
200 + return func(m *Metadata) {
201 + m.Owner = owner
202 + }
203 +}
204 +
205 +func WithCountry(country string) MetadataOption {
206 + return func(m *Metadata) {
207 + m.Country = country
208 + }
209 +}
210 +
211 type RDClient struct {
212 mu sync.Mutex
213
@@ -316,12 +347,11 @@ func (g *RDClient) Dial(cred *cryptoops.Credential, leaseID string, alpn string)
347 return nil, ErrNoAvailableRelay
348 }
349
319 -func (g *RDClient) Listen(cred *cryptoops.Credential, name string, alpns []string, metadata *Metadata) (*RDListener, error) {
350 +func (g *RDClient) Listen(cred *cryptoops.Credential, name string, alpns []string, options ...MetadataOption) (*RDListener, error) {
351 log.Debug().
352 Str("lease_id", cred.ID()).
353 Str("name", name).
354 Strs("alpns", alpns).
324 - Bool("has_metadata", metadata != nil).
355 Msg("[SDK] Creating listener")
356
357 // Validate name is URL-safe
@@ -334,13 +364,9 @@ func (g *RDClient) Listen(cred *cryptoops.Credential, name string, alpns []strin
364
365 metadataValue := ""
366
337 - if metadata != nil {
338 - metadataJSON, err := json.Marshal(metadata)
339 - if err != nil {
340 - log.Error().Err(err).Msg("[SDK] Failed to marshal metadata")
341 - return nil, ErrInvalidMetadata
342 - }
343 - metadataValue = string(metadataJSON)
367 + for _, option := range options {
368 + metadata := &Metadata{}
369 + option(metadata)
370 }
371
372 g.mu.Lock()
sdk/sdk_e2e_test.go
+2 -2
@@ -102,7 +102,7 @@ func TestE2E_ClientToAppThroughRelay(t *testing.T) {
102
103 // 5. Register listener on app side
104 log.Info().Msg("[TEST] Step 5: Registering app listener")
105 - appListener, err := appClient.Listen(appCred, "test-app", []string{"http/1.1"}, nil)
105 + appListener, err := appClient.Listen(appCred, "test-app", []string{"http/1.1"})
106 if err != nil {
107 t.Fatalf("Failed to create app listener: %v", err)
108 }
@@ -276,7 +276,7 @@ func TestE2E_MultipleConnections(t *testing.T) {
276 }
277 defer appClient.Close()
278
279 - appListener, err := appClient.Listen(appCred, "multi-test-app", []string{"http/1.1"}, nil)
279 + appListener, err := appClient.Listen(appCred, "multi-test-app", []string{"http/1.1"})
280 if err != nil {
281 t.Fatalf("Failed to create app listener: %v", err)
282 }