-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Headers improvements #23
base: master
Are you sure you want to change the base?
Headers improvements #23
Conversation
- timeout is changed to time.Duration - usage of 'comma ok' idiom - add constant for ditto specific content type Signed-off-by: Antonia Trifonova antonia.trifonova@bosch.io
@konstantina-gramatova please review this PR |
- Update the doc according the refactoring - Convert timeout in time.Duration after unmarshaling JSON - Add more tests scenarios Signed-off-by: Antonia Trifonova antonia.trifonova@bosch.io
protocol/headers.go
Outdated
func parseTimeout(timeout string) (time.Duration, error) { | ||
l := len(timeout) | ||
if l > 0 { | ||
t := time.Duration(-1) | ||
switch timeout[l-1] { | ||
case 'm': | ||
if i, err := strconv.Atoi(timeout[:l-1]); err == nil { | ||
t = time.Duration(i) * time.Minute | ||
} | ||
case 's': | ||
if timeout[l-2] == 'm' { | ||
if i, err := strconv.Atoi(timeout[:l-2]); err == nil { | ||
t = time.Duration(i) * time.Millisecond | ||
} | ||
} else { | ||
if i, err := strconv.Atoi(timeout[:l-1]); err == nil { | ||
t = time.Duration(i) * time.Second | ||
} | ||
} | ||
default: | ||
if i, err := strconv.Atoi(timeout); err == nil { | ||
t = time.Duration(i) * time.Second | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a specific reasoning behind implementing a custom duration string parsing instead of using the one from the Go SDK - i.e. time.ParseDuration?
protocol/headers.go
Outdated
func inTimeoutRange(timeout time.Duration) bool { | ||
return timeout >= 0 && timeout < time.Hour | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are about to perform header values validation here - are we about to introduce them for all known headers limitations and expected values - e.g. requested-acks header value validation, etc.? Otherwise, it is not really consistent.
protocol/headers.go
Outdated
} | ||
return 60 * time.Second |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the client supposed to handle default values of the headers? Or by spec, the cloud backend is supposed to interpret missing values to their defaults?
In general, the aim is always to send the smallest possible payload from the device - i.e. omit defaulting in this case.
Another topic is that if we add this default values handling here - it should be added everywhere for consistency. Otherwise - it's hard to use if it's randomly implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case the value is only returned/read and not set explicitly.
"In general, the aim is always to send the smallest possible payload from the device - i.e. omit defaulting in this case."
I'm totally agree with this statement, so it's good all getters/readers to return the default value no matter it's explicitly set or not (as it's done for the "response-required" value).
In order to make it consistent (and keep the payload compact), the setters/modifiers should remove the header if it is with its default value, i.e. no need to check a local variable before calling WithResponseRequired(required) - the Lib will do this for me.
For advanced users the Values map is exported so they can check any header existence or set it with its default value.
protocol/headers.go
Outdated
func (h *Headers) Timeout() time.Duration { | ||
if value, ok := h.Values[HeaderTimeout]; ok { | ||
if duration, err := parseTimeout(value.(string)); err == nil { | ||
return duration | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we decide to change the API from string to time.Duration - the value in the Headers.Values map must be of type time.Duration as well - not a string one parsed every time. Otherwise, we introduce an inconsistency.
This of course, will requires some marshaling/unmarshaling customizations but it's important to keep the usage of the header the same regardless how the value was accessed - via the getter or via the Headers.Values map directly.
- change header type to map - provide the functionality for case-insensitive - change timeout getter and setter method to expect time.Duration type - improve the getter methods to return default value (if this value is presented) - improve the test scenarios Signed-off-by: Antonia Trifonova antonia.trifonova@bosch.io
Signed-off-by: Antonia Trifonova antonia.trifonova@bosch.io
06e361c
to
79f4756
Compare
protocol/headers.go
Outdated
@@ -90,10 +94,10 @@ func (h Headers) CorrelationID() string { | |||
return v.(string) | |||
} | |||
} | |||
return "" | |||
return uuid.New().String() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If initially missing, on every invocation of CorrelationID() a newly generated id would be returned.
Also if there are more than one headers differing only in capitalization, should we make sure that the one that is initially returned(the first met one) would be returned on every next invocation as well?
protocol/headers.go
Outdated
// See https://www.eclipse.org/ditto/protocol-specification.html | ||
type Headers map[string]interface{} | ||
|
||
// CorrelationID returns the 'correlation-id' header value if it is presented. | ||
// If the header value is not presented, the CorrelationID returns empty string. | ||
// If the header value is not presented, the 'correlation-id' header value will be generated in UUID format. | ||
// | ||
// If there are two headers differing only in capitalization CorrelationID returns the first value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there are more than one headers differing only in capitalization CorrelationID returns the first met value.
protocol/headers.go
Outdated
if err := json.Unmarshal(data, &temp); err != nil { | ||
return err | ||
} | ||
for k := range temp { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this cleaning needed? In the generic case the initial headers are empty.
protocol/headers_opts.go
Outdated
func WithGeneric(headerID string, value interface{}) HeaderOpt { | ||
return func(headers Headers) error { | ||
for k := range headers { | ||
if strings.EqualFold(k, headerID) { | ||
delete(headers, k) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The user might want to add several different headers with keys only differing in the capitalization. In this case the user should directly use the exported map?
Signed-off-by: Antonia Trifonova antonia.trifonova@bosch.io
Signed-off-by: Antonia Trifonova antonia.trifonova@bosch.io
Signed-off-by: Antonia Trifonova antonia.trifonova@bosch.io
protocol/headers.go
Outdated
// | ||
// If there are more than one headers differing only in capitalization, the CorrelationID returns the first met value. | ||
// To use the provided key to get the value, access the map directly. | ||
func (h Headers) CorrelationID() (string, bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return only the correlation id, skip boolean flag for the unexpected type. Keep it consistent with other methods.
protocol/headers.go
Outdated
func sortHeadersKey(h Headers) []string { | ||
var keys []string | ||
for k := range h { | ||
keys = append(keys, k) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be more efficient to allocate the []string in advance as the size is known, than using append.
protocol/headers.go
Outdated
} | ||
|
||
// With sets new Headers to the existing. | ||
func (h Headers) With(opts ...HeaderOpt) Headers { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove it.
protocol/headers.go
Outdated
// ContentType returns the 'content-type' header value if it is presented. | ||
// | ||
// If the header value is not presented, the ContentType returns the empty string. | ||
// If the header value is not presented, the ContentType returns the empty string. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The line duplicated, copy paste error.
protocol/headers.go
Outdated
// If the header value is presented, but the type is not a string or the value is not valid, the Timeout returns the default value. | ||
// | ||
// If there are more than one headers differing only in capitalization, the Timeout returns the first met value. | ||
// To use the provided key to get the value, access the map directly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API doc might be more optimized and punctual.
protocol/headers_opts.go
Outdated
// If there aren't any headers for 'correlation-id', WithCorrelationID sets a new header with | ||
// key 'correlation-id' and the provided value. | ||
// | ||
// To use the provided key to set a new value, access the map directly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API doc might be more optimized and punctual.
Signed-off-by: Antonia Trifonova antonia.trifonova@bosch.io
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Can/should this be merged? With most of the commit messages containing (only) a "PoC" message? |
It's undergoing a last review currently - will leave a comment to confirm merge-readiness. |
Signed-off-by: Antonia Trifonova antonia.trifonova@bosch.io