Skip to content

Commit 381f39c

Browse files
committed
Add request lifecycle devdock proof
1 parent b1eb712 commit 381f39c

2 files changed

Lines changed: 248 additions & 0 deletions

File tree

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
//go:build api2devdock
2+
3+
package main
4+
5+
import (
6+
"context"
7+
"fmt"
8+
"net/http"
9+
"time"
10+
11+
tusgo "github.com/bdragon300/tusgo"
12+
"github.com/bdragon300/tusgo/examples/api2devdock"
13+
)
14+
15+
func assertRequestMethods(label string, actual []string, expected []string) error {
16+
if len(actual) != len(expected) {
17+
return fmt.Errorf("%s expected request methods %v, got %v", label, expected, actual)
18+
}
19+
20+
for index, method := range expected {
21+
if actual[index] != method {
22+
return fmt.Errorf(
23+
"%s expected request method %s at index %d, got %s",
24+
label,
25+
method,
26+
index,
27+
actual[index],
28+
)
29+
}
30+
}
31+
32+
return nil
33+
}
34+
35+
func assertStatusCodes(actual []int, expected []int) error {
36+
if len(actual) != len(expected) {
37+
return fmt.Errorf(
38+
"request lifecycle hooks expected status codes %v, got %v",
39+
expected,
40+
actual,
41+
)
42+
}
43+
44+
for index, statusCode := range expected {
45+
if actual[index] != statusCode {
46+
return fmt.Errorf(
47+
"request lifecycle hooks expected status code %d at index %d, got %d",
48+
statusCode,
49+
index,
50+
actual[index],
51+
)
52+
}
53+
}
54+
55+
return nil
56+
}
57+
58+
func uploadWithLifecycleHooks(
59+
ctx context.Context,
60+
scenario map[string]interface{},
61+
createResponse map[string]interface{},
62+
) (map[string]interface{}, error) {
63+
requestLifecycleHooks, err := api2devdock.RequestLifecycleHooks(scenario)
64+
if err != nil {
65+
return nil, err
66+
}
67+
endpointURL, err := api2devdock.TusURL(scenario, createResponse)
68+
if err != nil {
69+
return nil, err
70+
}
71+
content, err := api2devdock.ScenarioBytes(scenario)
72+
if err != nil {
73+
return nil, err
74+
}
75+
if err := api2devdock.RequireFullFileChunkSize(scenario); err != nil {
76+
return nil, err
77+
}
78+
metadata, err := api2devdock.UploadMetadata(scenario, createResponse)
79+
if err != nil {
80+
return nil, err
81+
}
82+
83+
afterResponseMethods := []string{}
84+
afterResponseStatusCodes := []int{}
85+
beforeRequestMethods := []string{}
86+
client := tusgo.NewClient(http.DefaultClient, endpointURL).WithRequestLifecycleHooks(
87+
tusgo.RequestLifecycleHooks{
88+
BeforeRequest: func(request *http.Request) error {
89+
beforeRequestMethods = append(beforeRequestMethods, request.Method)
90+
91+
return nil
92+
},
93+
AfterResponse: func(request *http.Request, response *http.Response) error {
94+
afterResponseMethods = append(afterResponseMethods, request.Method)
95+
afterResponseStatusCodes = append(afterResponseStatusCodes, response.StatusCode)
96+
97+
return nil
98+
},
99+
},
100+
).WithContext(ctx)
101+
102+
upload := tusgo.Upload{}
103+
if _, err := client.CreateUpload(&upload, int64(len(content)), false, metadata); err != nil {
104+
return nil, err
105+
}
106+
if upload.Location == "" {
107+
return nil, fmt.Errorf("request lifecycle hooks TUS upload did not expose an upload URL")
108+
}
109+
110+
stream := tusgo.NewUploadStream(client, &upload)
111+
stream.ChunkSize = tusgo.NoChunked
112+
written, err := stream.Write(content)
113+
if err != nil {
114+
return nil, err
115+
}
116+
if written != len(content) {
117+
return nil, fmt.Errorf("wrote %d bytes, expected %d", written, len(content))
118+
}
119+
if upload.RemoteOffset != int64(len(content)) {
120+
return nil, fmt.Errorf("remote offset %d, expected %d", upload.RemoteOffset, len(content))
121+
}
122+
if err := assertRequestMethods(
123+
"before request lifecycle hooks",
124+
beforeRequestMethods,
125+
requestLifecycleHooks.ExpectedBeforeRequestMethods,
126+
); err != nil {
127+
return nil, err
128+
}
129+
if err := assertRequestMethods(
130+
"after response lifecycle hooks",
131+
afterResponseMethods,
132+
requestLifecycleHooks.ExpectedAfterResponseMethods,
133+
); err != nil {
134+
return nil, err
135+
}
136+
if err := assertStatusCodes(
137+
afterResponseStatusCodes,
138+
requestLifecycleHooks.ExpectedAfterResponseStatusCodes,
139+
); err != nil {
140+
return nil, err
141+
}
142+
143+
return map[string]interface{}{
144+
"afterResponseMethods": afterResponseMethods,
145+
"afterResponseStatusCodes": afterResponseStatusCodes,
146+
"beforeRequestMethods": beforeRequestMethods,
147+
"uploadUrl": upload.Location,
148+
}, nil
149+
}
150+
151+
func main() {
152+
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
153+
defer cancel()
154+
155+
scenario, err := api2devdock.LoadScenario(
156+
"examples/api2-devdock-tus-request-lifecycle-hooks/api2-scenario.json",
157+
)
158+
if err != nil {
159+
api2devdock.Fail("load scenario: %v", err)
160+
}
161+
createResponse, err := api2devdock.CreateResponseFromScenario(scenario)
162+
if err != nil {
163+
api2devdock.Fail("read prepared create response: %v", err)
164+
}
165+
166+
result, err := uploadWithLifecycleHooks(ctx, scenario, createResponse)
167+
if err != nil {
168+
api2devdock.Fail("request lifecycle hooks: %v", err)
169+
}
170+
if err := api2devdock.WriteResult(result); err != nil {
171+
api2devdock.Fail("write result: %v", err)
172+
}
173+
174+
scenarioID, err := api2devdock.ScenarioID(scenario)
175+
if err != nil {
176+
api2devdock.Fail("read scenario id: %v", err)
177+
}
178+
fmt.Printf(
179+
"Go TUS SDK devdock scenario %s observed lifecycle hooks for %s\n",
180+
scenarioID,
181+
result["uploadUrl"],
182+
)
183+
}

examples/api2devdock/scenario.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ type RetryOffsetRecoveryPlan struct {
4646
RecoveryResponse RetryOffsetRecoveryResponsePlan
4747
}
4848

49+
type RequestLifecycleHooksPlan struct {
50+
ExpectedAfterResponseMethods []string
51+
ExpectedAfterResponseStatusCodes []int
52+
ExpectedBeforeRequestMethods []string
53+
}
54+
4955
func Fail(format string, args ...interface{}) {
5056
panic(fmt.Sprintf(format, args...))
5157
}
@@ -132,6 +138,24 @@ func StringArrayValue(value interface{}, label string) ([]string, error) {
132138
return strings, nil
133139
}
134140

141+
func IntArrayValue(value interface{}, label string) ([]int, error) {
142+
array, err := ArrayValue(value, label)
143+
if err != nil {
144+
return nil, err
145+
}
146+
147+
ints := make([]int, 0, len(array))
148+
for index, item := range array {
149+
number, err := IntValue(item, fmt.Sprintf("%s[%d]", label, index))
150+
if err != nil {
151+
return nil, err
152+
}
153+
ints = append(ints, number)
154+
}
155+
156+
return ints, nil
157+
}
158+
135159
func ScalarString(value interface{}) string {
136160
switch typed := value.(type) {
137161
case bool:
@@ -579,6 +603,47 @@ func RetryOffsetRecovery(scenario map[string]interface{}) (RetryOffsetRecoveryPl
579603
}, nil
580604
}
581605

606+
func RequestLifecycleHooks(scenario map[string]interface{}) (RequestLifecycleHooksPlan, error) {
607+
upload, err := ObjectValue(scenario["upload"], "upload")
608+
if err != nil {
609+
return RequestLifecycleHooksPlan{}, err
610+
}
611+
requestLifecycleHooks, err := ObjectValue(
612+
upload["requestLifecycleHooks"],
613+
"upload.requestLifecycleHooks",
614+
)
615+
if err != nil {
616+
return RequestLifecycleHooksPlan{}, err
617+
}
618+
expectedAfterResponseMethods, err := StringArrayValue(
619+
requestLifecycleHooks["expectedAfterResponseMethods"],
620+
"upload.requestLifecycleHooks.expectedAfterResponseMethods",
621+
)
622+
if err != nil {
623+
return RequestLifecycleHooksPlan{}, err
624+
}
625+
expectedAfterResponseStatusCodes, err := IntArrayValue(
626+
requestLifecycleHooks["expectedAfterResponseStatusCodes"],
627+
"upload.requestLifecycleHooks.expectedAfterResponseStatusCodes",
628+
)
629+
if err != nil {
630+
return RequestLifecycleHooksPlan{}, err
631+
}
632+
expectedBeforeRequestMethods, err := StringArrayValue(
633+
requestLifecycleHooks["expectedBeforeRequestMethods"],
634+
"upload.requestLifecycleHooks.expectedBeforeRequestMethods",
635+
)
636+
if err != nil {
637+
return RequestLifecycleHooksPlan{}, err
638+
}
639+
640+
return RequestLifecycleHooksPlan{
641+
ExpectedAfterResponseMethods: expectedAfterResponseMethods,
642+
ExpectedAfterResponseStatusCodes: expectedAfterResponseStatusCodes,
643+
ExpectedBeforeRequestMethods: expectedBeforeRequestMethods,
644+
}, nil
645+
}
646+
582647
func ScenarioID(scenario map[string]interface{}) (string, error) {
583648
return StringValue(scenario["scenarioId"], "scenarioId")
584649
}

0 commit comments

Comments
 (0)