• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fogfish / swarm / 11426008059

20 Oct 2024 11:40AM UTC coverage: 60.333% (-0.2%) from 60.498%
11426008059

push

github

web-flow
(fix): pass IOContext from WebSocket (#106)

22 of 42 new or added lines in 4 files covered. (52.38%)

1 existing line in 1 file now uncovered.

1016 of 1684 relevant lines covered (60.33%)

0.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/broker/websocket/awscdk.go
1
//
2
// Copyright (C) 2021 - 2022 Dmitry Kolesnikov
3
//
4
// This file may be modified and distributed under the terms
5
// of the Apache License Version 2.0. See the LICENSE file for details.
6
// https://github.com/fogfish/swarm
7
//
8

9
package websocket
10

11
import (
12
        "strconv"
13
        "strings"
14

15
        "github.com/aws/aws-cdk-go/awscdk/v2"
16
        "github.com/aws/aws-cdk-go/awscdk/v2/awsapigatewayv2"
17
        authorizers "github.com/aws/aws-cdk-go/awscdk/v2/awsapigatewayv2authorizers"
18
        integrations "github.com/aws/aws-cdk-go/awscdk/v2/awsapigatewayv2integrations"
19
        "github.com/aws/aws-cdk-go/awscdk/v2/awscertificatemanager"
20
        "github.com/aws/aws-cdk-go/awscdk/v2/awslambda"
21
        "github.com/aws/aws-cdk-go/awscdk/v2/awsroute53"
22
        "github.com/aws/aws-cdk-go/awscdk/v2/awsroute53targets"
23
        "github.com/aws/aws-sdk-go-v2/aws"
24
        "github.com/aws/constructs-go/constructs/v10"
25
        "github.com/aws/jsii-runtime-go"
26
        "github.com/fogfish/scud"
27
)
28

29
//------------------------------------------------------------------------------
30
//
31
// AWS CDK Sink Construct
32
//
33
//------------------------------------------------------------------------------
34

35
const stage = "ws"
36

37
type Sink struct {
38
        constructs.Construct
39
        Handler awslambda.IFunction
40
}
41

42
type SinkProps struct {
43
        Route    string
44
        Function scud.FunctionProps
45
        Gateway  awsapigatewayv2.WebSocketApi
46
}
47

48
func NewSink(scope constructs.Construct, id *string, props *SinkProps) *Sink {
×
49
        sink := &Sink{Construct: constructs.NewConstruct(scope, id)}
×
50

×
51
        props.Function.Setenv(EnvConfigEventType, props.Route)
×
52
        props.Function.Setenv(EnvConfigSourceWebSocket, aws.ToString(props.Gateway.ApiEndpoint())+"/"+stage)
×
53

×
54
        sink.Handler = scud.NewFunction(sink.Construct, jsii.String("Func"), props.Function)
×
55

×
56
        it := integrations.NewWebSocketLambdaIntegration(jsii.String(props.Route), sink.Handler,
×
57
                &integrations.WebSocketLambdaIntegrationProps{},
×
58
        )
×
59

×
60
        props.Gateway.AddRoute(jsii.String(props.Route),
×
61
                &awsapigatewayv2.WebSocketRouteOptions{
×
62
                        Integration: it,
×
63
                },
×
64
        )
×
65

×
66
        props.Gateway.GrantManageConnections(sink.Handler)
×
67

×
68
        return sink
×
69
}
×
70

71
//------------------------------------------------------------------------------
72
//
73
// AWS CDK Stack Construct
74
//
75
//------------------------------------------------------------------------------
76

77
type BrokerProps struct {
78
        System string
79
}
80

81
type Broker struct {
82
        constructs.Construct
83
        Gateway    awsapigatewayv2.WebSocketApi
84
        Authorizer awsapigatewayv2.IWebSocketRouteAuthorizer
85
        domain     awsapigatewayv2.DomainName
86
        dns        awsroute53.ARecord
87
        acc        int
88
}
89

90
func NewBroker(scope constructs.Construct, id *string, props *BrokerProps) *Broker {
×
91
        broker := &Broker{Construct: constructs.NewConstruct(scope, id)}
×
92

×
93
        return broker
×
94
}
×
95

96
type AuthorizerApiKeyProps struct {
97
        Access string
98
        Secret string
99
        Scope  []string
100
}
101

102
func (broker *Broker) NewAuthorizerApiKey(props *AuthorizerApiKeyProps) awsapigatewayv2.IWebSocketRouteAuthorizer {
×
103
        if broker.Gateway != nil {
×
104
                panic("Authorizer MUST be defined before the gateway is instantiated.")
×
105
        }
106

107
        if props.Access == "" || props.Secret == "" {
×
108
                panic("Authorizer MUST define access and secret api keys")
×
109
        }
110

111
        handler := scud.NewFunctionGo(broker.Construct, jsii.String("Authorizer"),
×
112
                &scud.FunctionGoProps{
×
113
                        SourceCodeModule: "github.com/fogfish/swarm",
×
114
                        SourceCodeLambda: "broker/websocket/lambda/auth",
×
115
                        FunctionProps: &awslambda.FunctionProps{
×
116
                                Environment: &map[string]*string{
×
117
                                        "CONFIG_SWARM_WS_AUTHORIZER_ACCESS": jsii.String(props.Access),
×
118
                                        "CONFIG_SWARM_WS_AUTHORIZER_SECRET": jsii.String(props.Secret),
×
NEW
119
                                        "CONFIG_SWARM_WS_AUTHORIZER_SCOPE":  jsii.String(strings.Join(props.Scope, " ")),
×
120
                                },
×
121
                        },
×
122
                },
×
123
        )
×
124

×
125
        broker.Authorizer = authorizers.NewWebSocketLambdaAuthorizer(
×
126
                jsii.String("default"),
×
127
                handler,
×
128
                &authorizers.WebSocketLambdaAuthorizerProps{
×
129
                        IdentitySource: jsii.Strings("route.request.querystring.apikey"),
×
130
                },
×
131
        )
×
132

×
133
        return broker.Authorizer
×
134
}
135

136
type AuthorizerJwtProps struct {
137
        Issuer   string
138
        Audience string
139
}
140

141
func (broker *Broker) NewAuthorizerJwt(props *AuthorizerJwtProps) awsapigatewayv2.IWebSocketRouteAuthorizer {
×
142
        if broker.Gateway != nil {
×
143
                panic("Authorizer MUST be defined before the gateway is instantiated.")
×
144
        }
145

146
        if !strings.HasPrefix(props.Issuer, "https://") {
×
147
                panic("Issuer URL MUST start with https://")
×
148
        }
149

150
        if !strings.HasSuffix(props.Issuer, "/") {
×
151
                props.Issuer += "/"
×
152
        }
×
153

154
        handler := scud.NewFunctionGo(broker.Construct, jsii.String("Authorizer"),
×
155
                &scud.FunctionGoProps{
×
156
                        SourceCodeModule: "github.com/fogfish/swarm",
×
157
                        SourceCodeLambda: "broker/websocket/lambda/auth",
×
158
                        FunctionProps: &awslambda.FunctionProps{
×
159
                                Environment: &map[string]*string{
×
160
                                        "CONFIG_SWARM_WS_AUTHORIZER_ISS": jsii.String(props.Issuer),
×
161
                                        "CONFIG_SWARM_WS_AUTHORIZER_AUD": jsii.String(props.Audience),
×
162
                                },
×
163
                        },
×
164
                },
×
165
        )
×
166

×
167
        broker.Authorizer = authorizers.NewWebSocketLambdaAuthorizer(
×
168
                jsii.String("default"),
×
169
                handler,
×
170
                &authorizers.WebSocketLambdaAuthorizerProps{
×
171
                        IdentitySource: jsii.Strings("route.request.querystring.token"),
×
172
                },
×
173
        )
×
174

×
175
        return broker.Authorizer
×
176
}
177

178
type AuthorizerUniversalProps struct {
179
        AuthorizerApiKey *AuthorizerApiKeyProps
180
        AuthorizerJwt    *AuthorizerJwtProps
181
}
182

183
func (broker *Broker) NewAuthorizerUniversal(props *AuthorizerUniversalProps) awsapigatewayv2.IWebSocketRouteAuthorizer {
×
184
        if broker.Gateway != nil {
×
185
                panic("Authorizer MUST be defined before the gateway is instantiated.")
×
186
        }
187

188
        if props.AuthorizerApiKey == nil || props.AuthorizerJwt == nil {
×
189
                panic("Universal Authorizer requires definition of all members")
×
190
        }
191

192
        if props.AuthorizerApiKey.Access == "" || props.AuthorizerApiKey.Secret == "" {
×
193
                panic("Authorizer MUST define access and secret api keys")
×
194
        }
195

196
        if !strings.HasPrefix(props.AuthorizerJwt.Issuer, "https://") {
×
197
                panic("Issuer URL MUST start with https://")
×
198
        }
199

200
        if !strings.HasSuffix(props.AuthorizerJwt.Issuer, "/") {
×
201
                props.AuthorizerJwt.Issuer += "/"
×
202
        }
×
203

204
        handler := scud.NewFunctionGo(broker.Construct, jsii.String("Authorizer"),
×
205
                &scud.FunctionGoProps{
×
206
                        SourceCodeModule: "github.com/fogfish/swarm",
×
207
                        SourceCodeLambda: "broker/websocket/lambda/auth",
×
208
                        FunctionProps: &awslambda.FunctionProps{
×
209
                                Environment: &map[string]*string{
×
210
                                        "CONFIG_SWARM_WS_AUTHORIZER_ACCESS": jsii.String(props.AuthorizerApiKey.Access),
×
211
                                        "CONFIG_SWARM_WS_AUTHORIZER_SECRET": jsii.String(props.AuthorizerApiKey.Secret),
×
212
                                        "CONFIG_SWARM_WS_AUTHORIZER_ISS":    jsii.String(props.AuthorizerJwt.Issuer),
×
213
                                        "CONFIG_SWARM_WS_AUTHORIZER_AUD":    jsii.String(props.AuthorizerJwt.Audience),
×
214
                                },
×
215
                        },
×
216
                },
×
217
        )
×
218

×
219
        broker.Authorizer = authorizers.NewWebSocketLambdaAuthorizer(
×
220
                jsii.String("default"),
×
221
                handler,
×
222
                &authorizers.WebSocketLambdaAuthorizerProps{
×
223
                        IdentitySource: jsii.Strings("route.request.querystring.token"),
×
224
                },
×
225
        )
×
226

×
227
        return broker.Authorizer
×
228
}
229

230
type WebSocketApiProps struct {
231
        *awsapigatewayv2.WebSocketApiProps
232
        Throttle *awsapigatewayv2.ThrottleSettings
233
        Host     *string
234
        TlsArn   *string
235
}
236

237
func (broker *Broker) NewGateway(props *WebSocketApiProps) awsapigatewayv2.WebSocketApi {
×
238
        if props.WebSocketApiProps == nil {
×
239
                props.WebSocketApiProps = &awsapigatewayv2.WebSocketApiProps{}
×
240
        }
×
241

242
        if props.WebSocketApiProps.ApiName == nil {
×
243
                props.ApiName = awscdk.Aws_STACK_NAME()
×
244
        }
×
245

246
        if props.WebSocketApiProps.ConnectRouteOptions == nil && broker.Authorizer != nil {
×
247
                connector := scud.NewFunctionGo(broker.Construct, jsii.String("Connector"),
×
248
                        &scud.FunctionGoProps{
×
249
                                SourceCodeModule: "github.com/fogfish/swarm",
×
250
                                SourceCodeLambda: "broker/websocket/lambda/connector",
×
251
                        },
×
252
                )
×
253

×
254
                props.WebSocketApiProps.ConnectRouteOptions = &awsapigatewayv2.WebSocketRouteOptions{
×
255
                        Integration: integrations.NewWebSocketLambdaIntegration(jsii.String("defcon"), connector,
×
256
                                &integrations.WebSocketLambdaIntegrationProps{},
×
257
                        ),
×
258
                        Authorizer: broker.Authorizer,
×
259
                }
×
260
        }
×
261

262
        broker.Gateway = awsapigatewayv2.NewWebSocketApi(broker.Construct, jsii.String("Gateway"), props.WebSocketApiProps)
×
263

×
264
        var domain *awsapigatewayv2.DomainMappingOptions
×
265
        if props.Host != nil && props.TlsArn != nil {
×
266
                broker.domain = awsapigatewayv2.NewDomainName(broker.Construct, jsii.String("DomainName"),
×
267
                        &awsapigatewayv2.DomainNameProps{
×
268
                                EndpointType: awsapigatewayv2.EndpointType_REGIONAL,
×
269
                                DomainName:   props.Host,
×
270
                                Certificate:  awscertificatemanager.Certificate_FromCertificateArn(broker.Construct, jsii.String("X509"), props.TlsArn),
×
271
                        },
×
272
                )
×
273

×
274
                domain = &awsapigatewayv2.DomainMappingOptions{
×
275
                        DomainName: broker.domain,
×
276
                }
×
277
        }
×
278

279
        awsapigatewayv2.NewWebSocketStage(broker.Construct, jsii.String("Stage"),
×
280
                &awsapigatewayv2.WebSocketStageProps{
×
281
                        AutoDeploy:    jsii.Bool(true),
×
282
                        StageName:     jsii.String(stage),
×
283
                        Throttle:      props.Throttle,
×
284
                        WebSocketApi:  broker.Gateway,
×
285
                        DomainMapping: domain,
×
286
                },
×
287
        )
×
288

×
289
        if props.Host != nil && props.TlsArn != nil {
×
290
                broker.createRoute53(*props.Host)
×
291
        }
×
292

293
        return broker.Gateway
×
294
}
295

296
func (broker *Broker) createRoute53(host string) {
×
297
        domain := strings.Join(strings.Split(host, ".")[1:], ".")
×
298
        zone := awsroute53.HostedZone_FromLookup(broker.Construct, jsii.String("HZone"),
×
299
                &awsroute53.HostedZoneProviderProps{
×
300
                        DomainName: jsii.String(domain),
×
301
                },
×
302
        )
×
303

×
304
        broker.dns = awsroute53.NewARecord(broker.Construct, jsii.String("ARecord"),
×
305
                &awsroute53.ARecordProps{
×
306
                        RecordName: jsii.String(host),
×
307
                        Target: awsroute53.RecordTarget_FromAlias(
×
308
                                awsroute53targets.NewApiGatewayv2DomainProperties(
×
309
                                        broker.domain.RegionalDomainName(),
×
310
                                        broker.domain.RegionalHostedZoneId(),
×
311
                                ),
×
312
                        ),
×
313
                        Ttl:  awscdk.Duration_Seconds(jsii.Number(60)),
×
314
                        Zone: zone,
×
315
                },
×
316
        )
×
317

×
318
}
×
319

320
func (broker *Broker) NewSink(props *SinkProps) *Sink {
×
321
        if broker.Gateway == nil {
×
322
                panic("Gatewaye is not defined.")
×
323
        }
324

325
        props.Gateway = broker.Gateway
×
326

×
327
        broker.acc++
×
328
        name := "Sink" + strconv.Itoa(broker.acc)
×
329
        sink := NewSink(broker.Construct, jsii.String(name), props)
×
330

×
331
        return sink
×
332
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc