Skip to content

Commit

Permalink
feat: add a v1 and v2 handler to the proxy for different config cdn u…
Browse files Browse the repository at this point in the history
…rls (#21)

* add a v1 and v2 handler that uses different devcycle clients which fetch from v1 and v2 cdn endpoints respectively

* store an override config that's per instance level

* pass in v1 arg

* path fix

* path fix v2

* use released version of sdk

---------

Co-authored-by: Jamie Sinn <[email protected]>
  • Loading branch information
elliotCamblor and JamieSinn authored Aug 23, 2024
1 parent 508abab commit e5c3aab
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 40 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ module github.com/devcyclehq/sdk-proxy
go 1.20

require (
github.com/devcyclehq/go-server-sdk/v2 v2.17.0
github.com/devcyclehq/go-server-sdk/v2 v2.18.1
github.com/gin-gonic/gin v1.10.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/kr/pretty v0.3.1
github.com/launchdarkly/eventsource v1.7.1
github.com/stretchr/testify v1.9.0
)

Expand All @@ -27,7 +28,6 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/launchdarkly/eventsource v1.7.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
Expand Down
12 changes: 8 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/devcyclehq/go-server-sdk/v2 v2.16.1 h1:NZ0vHZhhrPOb2hbU/r5sIIDSG4tPc+TiO/ovJaP0Gk8=
github.com/devcyclehq/go-server-sdk/v2 v2.16.1/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24=
github.com/devcyclehq/go-server-sdk/v2 v2.17.0 h1:eBvoJesVPYvy7htJBjhIWRGbQq5fsmlcv4nvsrHqPDU=
github.com/devcyclehq/go-server-sdk/v2 v2.17.0/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24=
github.com/devcyclehq/go-server-sdk/v2 v2.18.0 h1:9STdu/bTnrjM1cFh3OJddO4nxAaAUqlOAO180x6SWpk=
github.com/devcyclehq/go-server-sdk/v2 v2.18.0/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24=
github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240822213335-a3201264237c h1:HABqfIWonwH731k8kd+yYLExjsuVH5S+xL9Ed3Kywhw=
github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240822213335-a3201264237c/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24=
github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240823134625-106c09774905 h1:tOTq2TbgeYtdFuaOl71Sups5p8setOA5VHan6DRGOnU=
github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240823134625-106c09774905/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24=
github.com/devcyclehq/go-server-sdk/v2 v2.18.1 h1:4Rwxr0Sx4S56zGgSqm0C06Xj88038iYVbWZinpPx8C4=
github.com/devcyclehq/go-server-sdk/v2 v2.18.1/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
Expand Down
69 changes: 37 additions & 32 deletions http_endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,53 +111,58 @@ func BatchEvents() gin.HandlerFunc {
}
}

func GetConfig() gin.HandlerFunc {
func GetConfig(client *devcycle.Client, version ...string) gin.HandlerFunc {
return func(c *gin.Context) {
instance := c.Value("instance").(*ProxyInstance)
client := c.Value("devcycle").(*devcycle.Client)

if c.Param("sdkKey") == "" || !strings.HasSuffix(c.Param("sdkKey"), ".json") {
c.AbortWithStatus(http.StatusForbidden)
return
}
var ret []byte
rawConfig, etag, err := client.GetRawConfig()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{})
return
}
if instance.SSEEnabled {
config := map[string]interface{}{}
err = json.Unmarshal(rawConfig, &config)
var ret, rawConfig []byte
var etag, lm string
var err error
if client != nil {
rawConfig, etag, lm, err = client.GetRawConfig()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{})
return
}
hostname := fmt.Sprintf("http://%s:%d", instance.SSEHostname, instance.HTTPPort)
// This is the only indicator that a unix socket request was made
if c.Request.RemoteAddr == "" {
hostname = fmt.Sprintf("unix:%s", instance.UnixSocketPath)
}
fmt.Println(c.Request)
if val, ok := config["sse"]; ok {
path := val.(map[string]interface{})["path"].(string)

config["sse"] = devcycle_api.SSEHost{
Hostname: hostname,
Path: path,
if instance.SSEEnabled {
config := map[string]interface{}{}
err = json.Unmarshal(rawConfig, &config)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{})
return
}
hostname := fmt.Sprintf("http://%s:%d", instance.SSEHostname, instance.HTTPPort)
// This is the only indicator that a unix socket request was made
if c.Request.RemoteAddr == "" {
hostname = fmt.Sprintf("unix:%s", instance.UnixSocketPath)
}
fmt.Println(c.Request)
if val, ok := config["sse"]; ok {
path := val.(map[string]interface{})["path"].(string)

config["sse"] = devcycle_api.SSEHost{
Hostname: hostname,
Path: path,
}
}
}

ret, err = json.Marshal(config)
} else {
ret = rawConfig
}

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{})
return
ret, err = json.Marshal(config)
} else {
ret = rawConfig
}
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{})
return
}
} else if client == nil && len(version) > 0 {
ret, etag, lm = instance.BypassSDKConfig(version[0])
}
c.Header("ETag", etag)
c.Header("Last-Modified", lm)
c.Data(http.StatusOK, "application/json", ret)
}
}
Expand Down
35 changes: 35 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"fmt"
"github.com/devcyclehq/go-server-sdk/v2/api"
"github.com/launchdarkly/eventsource"
"io"
"log"
"net/http"
"os"
"runtime"
"time"
Expand Down Expand Up @@ -38,6 +40,7 @@ type ProxyInstance struct {
dvcClient *devcycle.Client
sseServer *eventsource.Server
sseEvents chan api.ClientEvent
bypassConfig []byte
}

type SDKConfig struct {
Expand Down Expand Up @@ -73,13 +76,45 @@ func (i *ProxyInstance) BuildDevCycleOptions() *devcycle.Options {
EnableBetaRealtimeUpdates: i.SSEEnabled,
AdvancedOptions: devcycle.AdvancedOptions{
OverridePlatformData: &i.PlatformData,
OverrideConfigWithV1: false,
},
ClientEventHandler: i.sseEvents,
}
options.CheckDefaults()
return &options
}

func (i *ProxyInstance) BypassSDKConfig(version string) (config []byte, etag, lastModified string) {

request, err := http.NewRequest("GET", fmt.Sprintf("https://config-cdn.devcycle.com/config/%s/server/%s.json", version, i.SDKKey), nil)
if err != nil {
return i.bypassConfig, "", ""
}

resp, err := http.DefaultClient.Do(request)

if err != nil {
return i.bypassConfig, "", ""
}

if resp.StatusCode == http.StatusNotModified {
return i.bypassConfig, resp.Header.Get("ETag"), resp.Header.Get("Last-Modified")
}

if resp.StatusCode != http.StatusOK {
return i.bypassConfig, "", ""
}

body, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return i.bypassConfig, "", ""
}

i.bypassConfig = body
return i.bypassConfig, resp.Header.Get("ETag"), resp.Header.Get("Last-Modified")
}

func (i *ProxyInstance) EventRebroadcaster() {
for event := range i.sseEvents {
if event.EventType == api.ClientEventType_RealtimeUpdates {
Expand Down
10 changes: 8 additions & 2 deletions proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ func NewBucketingProxyInstance(instance *ProxyInstance) (*ProxyInstance, error)

options := instance.BuildDevCycleOptions()
client, err := devcycle.NewClient(instance.SDKKey, options)
if err != nil {
return nil, fmt.Errorf("error creating DevCycle client: %v", err)
}
instance.dvcClient = client

r := newRouter(client, instance)
Expand Down Expand Up @@ -116,9 +119,12 @@ func newRouter(client *devcycle.Client, instance *ProxyInstance) *gin.Engine {
}
configCDNv1 := r.Group("/config/v1")
{
configCDNv1.GET("/server/:sdkKey", GetConfig())
configCDNv1.GET("/server/:sdkKey", GetConfig(nil, "v1"))
}
configCDNv2 := r.Group("/config/v2")
{
configCDNv2.GET("/server/:sdkKey", GetConfig(client))
}

r.GET("/event-stream", SSE())

return r
Expand Down

0 comments on commit e5c3aab

Please sign in to comment.