Skip to content

Commit 04f1409

Browse files
joe4devclaude
andcommitted
refactor(ls-api): extract shared LS↔RIE API types into internal/lsapi
InvokeRequest, LogResponse, and ErrorResponse were duplicated across cmd/localstack and cmd/ls-api. Moving them to internal/lsapi/types.go makes both packages use the same definitions and removes the duplication flagged in the PR review. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 73001c5 commit 04f1409

6 files changed

Lines changed: 47 additions & 51 deletions

File tree

cmd/localstack/custom_interop.go

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda/interop"
1919
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda/rapidcore"
2020
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda/rapidcore/standalone"
21+
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lsapi"
2122
"github.com/go-chi/chi/v5"
2223
log "github.com/sirupsen/logrus"
2324
)
@@ -51,7 +52,7 @@ func (l *LocalStackAdapter) SendStatus(status LocalStackStatus, payload []byte)
5152
}
5253

5354
// SendLogs posts the captured invocation logs to LocalStack.
54-
func (l *LocalStackAdapter) SendLogs(invokeId string, logs LogResponse) error {
55+
func (l *LocalStackAdapter) SendLogs(invokeId string, logs lsapi.LogResponse) error {
5556
serialized, err := json.Marshal(logs)
5657
if err != nil {
5758
return err
@@ -81,22 +82,6 @@ func (l *LocalStackAdapter) SendResult(invokeId string, body []byte, isError boo
8182
return err
8283
}
8384

84-
// The InvokeRequest is sent by LocalStack to trigger an invocation
85-
type InvokeRequest struct {
86-
InvokeId string `json:"invoke-id"`
87-
InvokedFunctionArn string `json:"invoked-function-arn"`
88-
Payload string `json:"payload"`
89-
TraceId string `json:"trace-id"`
90-
}
91-
92-
// The ErrorResponse is sent TO LocalStack when encountering an error
93-
type ErrorResponse struct {
94-
ErrorMessage string `json:"errorMessage"`
95-
ErrorType string `json:"errorType,omitempty"`
96-
RequestId string `json:"requestId,omitempty"`
97-
StackTrace []string `json:"stackTrace,omitempty"`
98-
}
99-
10085
func NewCustomInteropServer(lsOpts *LsOpts, delegate interop.Server, logCollector *LogCollector) (server *CustomInteropServer) {
10186
server = &CustomInteropServer{
10287
delegate: delegate.(*rapidcore.Server),
@@ -112,7 +97,7 @@ func NewCustomInteropServer(lsOpts *LsOpts, delegate interop.Server, logCollecto
11297
go func() {
11398
r := chi.NewRouter()
11499
r.Post("/invoke", func(w http.ResponseWriter, r *http.Request) {
115-
invokeR := InvokeRequest{}
100+
invokeR := lsapi.InvokeRequest{}
116101
bytess, err := io.ReadAll(r.Body)
117102
if err != nil {
118103
log.Error(err)
@@ -154,7 +139,7 @@ func NewCustomInteropServer(lsOpts *LsOpts, delegate interop.Server, logCollecto
154139
case errors.Is(err, rapidcore.ErrInvokeTimeout):
155140
log.Debugf("Got invoke timeout")
156141
isErr = true
157-
errorResponse := ErrorResponse{
142+
errorResponse := lsapi.ErrorResponse{
158143
ErrorMessage: fmt.Sprintf(
159144
"%s %s Task timed out after %d.00 seconds",
160145
time.Now().Format("2006-01-02T15:04:05Z"),

cmd/localstack/custom_interop_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http/httptest"
88
"testing"
99

10+
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lsapi"
1011
"github.com/stretchr/testify/assert"
1112
"github.com/stretchr/testify/require"
1213
)
@@ -28,7 +29,7 @@ func TestInvokeRequestContract(t *testing.T) {
2829
"trace-id": "Root=1-abc;Parent=def;Sampled=1"
2930
}`
3031

31-
var req InvokeRequest
32+
var req lsapi.InvokeRequest
3233
require.NoError(t, json.Unmarshal([]byte(raw), &req))
3334

3435
assert.Equal(t, "abc-123", req.InvokeId)
@@ -42,7 +43,7 @@ func TestInvokeRequestContract(t *testing.T) {
4243
func TestLogResponseContract(t *testing.T) {
4344
raw := `{"logs":"START RequestId: abc\nEND RequestId: abc\n"}`
4445

45-
var lr LogResponse
46+
var lr lsapi.LogResponse
4647
require.NoError(t, json.Unmarshal([]byte(raw), &lr))
4748

4849
assert.Equal(t, "START RequestId: abc\nEND RequestId: abc\n", lr.Logs)
@@ -84,7 +85,7 @@ func TestSendStatus_ErrorSendsToCorrectPath(t *testing.T) {
8485

8586
func TestSendLogs_SendsJSONWithLogsKey(t *testing.T) {
8687
var capturedPath string
87-
var capturedBody LogResponse
88+
var capturedBody lsapi.LogResponse
8889
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
8990
capturedPath = r.URL.Path
9091
body, _ := io.ReadAll(r.Body)
@@ -94,7 +95,7 @@ func TestSendLogs_SendsJSONWithLogsKey(t *testing.T) {
9495
defer srv.Close()
9596

9697
adapter := &LocalStackAdapter{UpstreamEndpoint: srv.URL}
97-
logs := LogResponse{Logs: "START RequestId: invoke-1\nEND RequestId: invoke-1\n"}
98+
logs := lsapi.LogResponse{Logs: "START RequestId: invoke-1\nEND RequestId: invoke-1\n"}
9899
require.NoError(t, adapter.SendLogs("invoke-1", logs))
99100

100101
assert.Equal(t, "/invocations/invoke-1/logs", capturedPath)

cmd/localstack/logs.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ package main
33
import (
44
"strings"
55
"sync"
6-
)
76

8-
type LogResponse struct {
9-
Logs string `json:"logs"`
10-
}
7+
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lsapi"
8+
)
119

1210
type LogCollector struct {
1311
mutex *sync.Mutex
@@ -37,10 +35,10 @@ func (lc *LogCollector) reset() {
3735
lc.RuntimeLogs = []string{}
3836
}
3937

40-
func (lc *LogCollector) getLogs() LogResponse {
38+
func (lc *LogCollector) getLogs() lsapi.LogResponse {
4139
lc.mutex.Lock()
4240
defer lc.mutex.Unlock()
43-
response := LogResponse{
41+
response := lsapi.LogResponse{
4442
Logs: strings.Join(lc.RuntimeLogs, ""),
4543
}
4644
lc.RuntimeLogs = []string{}

cmd/ls-api/main.go

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import (
55
"bytes"
66
"encoding/json"
77
"fmt"
8+
"io"
9+
"net/http"
10+
11+
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lsapi"
812
"github.com/go-chi/chi"
913
"github.com/go-chi/chi/middleware"
1014
log "github.com/sirupsen/logrus"
11-
"io"
12-
"net/http"
1315
)
1416

1517
const apiPort = 9563
@@ -30,7 +32,7 @@ func main() {
3032
router.Post("/status/{runtime_id}/{status}", statusHandler)
3133

3234
router.Get("/success", func(w http.ResponseWriter, r *http.Request) {
33-
invokeRequest, _ := json.Marshal(InvokeRequest{InvokeId: uid, Payload: "{\"counter\":0}"})
35+
invokeRequest, _ := json.Marshal(lsapi.InvokeRequest{InvokeId: uid, Payload: "{\"counter\":0}"})
3436
_, err := http.Post(invokeUrl, "application/json", bytes.NewReader(invokeRequest))
3537
if err != nil {
3638
log.Error(err)
@@ -44,7 +46,7 @@ func main() {
4446
})
4547

4648
router.Get("/fail", func(w http.ResponseWriter, r *http.Request) {
47-
invokeRequest, _ := json.Marshal(InvokeRequest{InvokeId: uid, Payload: "{\"counter\":0, \"fail\": \"yes\"}"})
49+
invokeRequest, _ := json.Marshal(lsapi.InvokeRequest{InvokeId: uid, Payload: "{\"counter\":0, \"fail\": \"yes\"}"})
4850
_, err := http.Post(invokeUrl, "application/json", bytes.NewReader(invokeRequest))
4951
if err != nil {
5052
log.Error(err)
@@ -67,7 +69,7 @@ func main() {
6769
func invokeLogsHandler(w http.ResponseWriter, r *http.Request) {
6870
invokeId := chi.URLParam(r, "invoke_id")
6971
log.Println(invokeId)
70-
var logResponse LogResponse
72+
var logResponse lsapi.LogResponse
7173
if err := json.NewDecoder(r.Body).Decode(&logResponse); err != nil {
7274
log.Error("invalid logs payload: ", err)
7375
} else {
@@ -76,26 +78,13 @@ func invokeLogsHandler(w http.ResponseWriter, r *http.Request) {
7678
w.WriteHeader(http.StatusAccepted)
7779
}
7880

79-
// InvokeRequest is sent by LocalStack to trigger an invocation.
80-
type InvokeRequest struct {
81-
InvokeId string `json:"invoke-id"`
82-
InvokedFunctionArn string `json:"invoked-function-arn"`
83-
Payload string `json:"payload"`
84-
TraceId string `json:"trace-id"`
85-
}
86-
87-
// LogResponse is sent by the runtime to report logs for a completed invocation.
88-
type LogResponse struct {
89-
Logs string `json:"logs"`
90-
}
91-
9281
func statusHandler(w http.ResponseWriter, r *http.Request) {
9382
runtime_id := chi.URLParam(r, "runtime_id")
9483
status := chi.URLParam(r, "status")
9584
log.Println(runtime_id + " + " + status)
9685
if status == "ready" {
9786
go func() {
98-
invokeRequest, _ := json.Marshal(InvokeRequest{InvokeId: "12345", Payload: "{\"counter\":0}"})
87+
invokeRequest, _ := json.Marshal(lsapi.InvokeRequest{InvokeId: "12345", Payload: "{\"counter\":0}"})
9988
_, err := http.Post(invokeUrl, "application/json", bytes.NewReader(invokeRequest))
10089
if err != nil {
10190
log.Error(err)

cmd/ls-api/main_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99
"time"
1010

11+
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lsapi"
1112
"github.com/go-chi/chi"
1213
"github.com/stretchr/testify/assert"
1314
"github.com/stretchr/testify/require"
@@ -71,7 +72,7 @@ func TestInvocationLogsReturns202(t *testing.T) {
7172
srv := httptest.NewServer(newTestRouter())
7273
defer srv.Close()
7374

74-
logPayload, err := json.Marshal(LogResponse{
75+
logPayload, err := json.Marshal(lsapi.LogResponse{
7576
Logs: "START RequestId: " + testInvokeID + " Version: $LATEST\nEND RequestId: " + testInvokeID + "\n",
7677
})
7778
require.NoError(t, err)
@@ -91,9 +92,9 @@ func TestInvocationLogsReturns202(t *testing.T) {
9192
// - returns 202 Accepted (matching LocalStack executor_endpoint.py status_ready)
9293
// - asynchronously sends a POST to the invoke endpoint with a valid InvokeRequest body
9394
func TestStatusReadyReturns202AndTriggersInvoke(t *testing.T) {
94-
invokeCh := make(chan InvokeRequest, 1)
95+
invokeCh := make(chan lsapi.InvokeRequest, 1)
9596
captureServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
96-
var req InvokeRequest
97+
var req lsapi.InvokeRequest
9798
require.NoError(t, json.NewDecoder(r.Body).Decode(&req))
9899
invokeCh <- req
99100
w.WriteHeader(http.StatusOK)

internal/lsapi/types.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package lsapi
2+
3+
// InvokeRequest is sent by LocalStack to trigger an invocation.
4+
type InvokeRequest struct {
5+
InvokeId string `json:"invoke-id"`
6+
InvokedFunctionArn string `json:"invoked-function-arn"`
7+
Payload string `json:"payload"`
8+
TraceId string `json:"trace-id"`
9+
}
10+
11+
// LogResponse is sent by the runtime to report logs for a completed invocation.
12+
type LogResponse struct {
13+
Logs string `json:"logs"`
14+
}
15+
16+
// ErrorResponse is sent to LocalStack when encountering an error.
17+
type ErrorResponse struct {
18+
ErrorMessage string `json:"errorMessage"`
19+
ErrorType string `json:"errorType,omitempty"`
20+
RequestId string `json:"requestId,omitempty"`
21+
StackTrace []string `json:"stackTrace,omitempty"`
22+
}

0 commit comments

Comments
 (0)