6
6
"encoding/json"
7
7
"io"
8
8
"log/slog"
9
+ "maps"
9
10
"net/http"
10
11
"strconv"
11
12
"time"
@@ -34,16 +35,19 @@ type OperationCompletion interface {
34
35
// OperationCompletionSuccessful is input for [NewCompletionHTTPRequest], used to deliver successful operation results.
35
36
type OperationCompletionSuccessful struct {
36
37
// Header to send in the completion request.
37
- Header http.Header
38
- // Body to send in the completion HTTP request.
39
- // If it implements `io.Closer` it will automatically be closed by the client.
40
- Body io.Reader
38
+ // Note that this is a Nexus header, not an HTTP header.
39
+ Header Header
40
+
41
+ // A [Reader] that may be directly set on the completion or constructed when instantiating via
42
+ // [NewOperationCompletionSuccessful].
43
+ // Automatically closed when the completion is delivered.
44
+ Reader * Reader
41
45
// OperationID is the unique ID for this operation. Used when a completion callback is received before a started response.
42
46
OperationID string
43
47
// StartTime is the time the operation started. Used when a completion callback is received before a started response.
44
48
StartTime time.Time
45
- // StartLinks are used to link back to the operation when a completion callback is received before a started response.
46
- StartLinks []Link
49
+ // Links are used to link back to the operation when a completion callback is received before a started response.
50
+ Links []Link
47
51
}
48
52
49
53
// OperationCompletionSuccessfulOptions are options for [NewOperationCompletionSuccessful].
@@ -55,48 +59,56 @@ type OperationCompletionSuccessfulOptions struct {
55
59
OperationID string
56
60
// StartTime is the time the operation started. Used when a completion callback is received before a started response.
57
61
StartTime time.Time
58
- // StartLinks are used to link back to the operation when a completion callback is received before a started response.
59
- StartLinks []Link
62
+ // Links are used to link back to the operation when a completion callback is received before a started response.
63
+ Links []Link
60
64
}
61
65
62
66
// NewOperationCompletionSuccessful constructs an [OperationCompletionSuccessful] from a given result.
63
67
func NewOperationCompletionSuccessful (result any , options OperationCompletionSuccessfulOptions ) (* OperationCompletionSuccessful , error ) {
64
- if reader , ok := result .(* Reader ); ok {
65
- return & OperationCompletionSuccessful {
66
- Header : addContentHeaderToHTTPHeader (reader .Header , make (http.Header )),
67
- Body : reader .ReadCloser ,
68
- OperationID : options .OperationID ,
69
- StartTime : options .StartTime ,
70
- StartLinks : options .StartLinks ,
71
- }, nil
72
- } else {
68
+ reader , ok := result .(* Reader )
69
+ if ! ok {
73
70
content , ok := result .(* Content )
74
71
if ! ok {
75
- var err error
76
72
serializer := options .Serializer
77
73
if serializer == nil {
78
74
serializer = defaultSerializer
79
75
}
76
+ var err error
80
77
content , err = serializer .Serialize (result )
81
78
if err != nil {
82
79
return nil , err
83
80
}
84
81
}
85
- header := http.Header {"Content-Length" : []string {strconv .Itoa (len (content .Data ))}}
82
+ header := maps .Clone (content .Header )
83
+ if header == nil {
84
+ header = make (Header , 1 )
85
+ }
86
+ header ["length" ] = strconv .Itoa (len (content .Data ))
86
87
87
- return & OperationCompletionSuccessful {
88
- Header : addContentHeaderToHTTPHeader (content .Header , header ),
89
- Body : bytes .NewReader (content .Data ),
90
- OperationID : options .OperationID ,
91
- StartTime : options .StartTime ,
92
- StartLinks : options .StartLinks ,
93
- }, nil
88
+ reader = & Reader {
89
+ Header : header ,
90
+ ReadCloser : io .NopCloser (bytes .NewReader (content .Data )),
91
+ }
94
92
}
93
+
94
+ return & OperationCompletionSuccessful {
95
+ Header : make (Header ),
96
+ Reader : reader ,
97
+ OperationID : options .OperationID ,
98
+ StartTime : options .StartTime ,
99
+ Links : options .Links ,
100
+ }, nil
95
101
}
96
102
97
103
func (c * OperationCompletionSuccessful ) applyToHTTPRequest (request * http.Request ) error {
104
+ if request .Header == nil {
105
+ request .Header = make (http.Header , len (c .Header )+ len (c .Reader .Header )+ 1 ) // +1 for headerOperationState
106
+ }
107
+ if c .Reader .Header != nil {
108
+ addContentHeaderToHTTPHeader (c .Reader .Header , request .Header )
109
+ }
98
110
if c .Header != nil {
99
- request .Header = c .Header . Clone ( )
111
+ addNexusHeaderToHTTPHeader ( c .Header , request .Header )
100
112
}
101
113
request .Header .Set (headerOperationState , string (OperationStateSucceeded ))
102
114
if c .Header .Get (HeaderOperationID ) == "" && c .OperationID != "" {
@@ -106,39 +118,39 @@ func (c *OperationCompletionSuccessful) applyToHTTPRequest(request *http.Request
106
118
request .Header .Set (headerOperationStartTime , c .StartTime .Format (http .TimeFormat ))
107
119
}
108
120
if c .Header .Get (headerLink ) == "" {
109
- if err := addLinksToHTTPHeader (c .StartLinks , request .Header ); err != nil {
121
+ if err := addLinksToHTTPHeader (c .Links , request .Header ); err != nil {
110
122
return err
111
123
}
112
124
}
113
125
114
- if closer , ok := c .Body .(io.ReadCloser ); ok {
115
- request .Body = closer
116
- } else {
117
- request .Body = io .NopCloser (c .Body )
118
- }
126
+ request .Body = c .Reader .ReadCloser
119
127
return nil
120
128
}
121
129
122
130
// OperationCompletionUnsuccessful is input for [NewCompletionHTTPRequest], used to deliver unsuccessful operation
123
131
// results.
124
132
type OperationCompletionUnsuccessful struct {
125
133
// Header to send in the completion request.
126
- Header http.Header
134
+ // Note that this is a Nexus header, not an HTTP header.
135
+ Header Header
127
136
// State of the operation, should be failed or canceled.
128
137
State OperationState
129
138
// OperationID is the unique ID for this operation. Used when a completion callback is received before a started response.
130
139
OperationID string
131
140
// StartTime is the time the operation started. Used when a completion callback is received before a started response.
132
141
StartTime time.Time
133
- // StartLinks are used to link back to the operation when a completion callback is received before a started response.
134
- StartLinks []Link
142
+ // Links are used to link back to the operation when a completion callback is received before a started response.
143
+ Links []Link
135
144
// Failure object to send with the completion.
136
145
Failure * Failure
137
146
}
138
147
139
148
func (c * OperationCompletionUnsuccessful ) applyToHTTPRequest (request * http.Request ) error {
149
+ if request .Header == nil {
150
+ request .Header = make (http.Header , len (c .Header )+ 2 ) // +2 for headerOperationState and content-type
151
+ }
140
152
if c .Header != nil {
141
- request .Header = c .Header . Clone ( )
153
+ addNexusHeaderToHTTPHeader ( c .Header , request .Header )
142
154
}
143
155
request .Header .Set (headerOperationState , string (c .State ))
144
156
request .Header .Set ("Content-Type" , contentTypeJSON )
@@ -149,7 +161,7 @@ func (c *OperationCompletionUnsuccessful) applyToHTTPRequest(request *http.Reque
149
161
request .Header .Set (headerOperationStartTime , c .StartTime .Format (http .TimeFormat ))
150
162
}
151
163
if c .Header .Get (headerLink ) == "" {
152
- if err := addLinksToHTTPHeader (c .StartLinks , request .Header ); err != nil {
164
+ if err := addLinksToHTTPHeader (c .Links , request .Header ); err != nil {
153
165
return err
154
166
}
155
167
}
0 commit comments