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
SB-10000: Adding app- and pipeline-ids to server #290
Closed
Closed
Changes from 3 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
7a5aa17
adding app- and pipeline-ids to server
82be581
fixing docs
cc47054
using test-tables in TestPipelineIDHandler
2deab0d
Using t.Run() in table tests
0b987da
Removing request ID assignment in ID handlers
06b5993
Adding a random b64 ID generator
joemadeus bd51966
Merge remote-tracking branch 'upstream/master' into jr-sb10000-create…
joemadeus 630633e
Adding docs, tests for RandB64ID
joemadeus File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package server | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
uuid "github.com/nu7hatch/gouuid" | ||
) | ||
|
||
// IDer generates a new ID | ||
type IDer interface { | ||
ID() (string, error) | ||
} | ||
|
||
// AppUUID generates IDs based on v4 UUIDs | ||
type AppUUID struct { | ||
formatter func(string) string | ||
} | ||
|
||
// NewAppUUID creates a new AppUUID generator | ||
func NewAppUUID(appname string) *AppUUID { | ||
uuider := &AppUUID{ | ||
formatter: func(u string) string { | ||
// default formatter does no formatting at all, simply | ||
// returning the generated UUID as a string | ||
return u | ||
}, | ||
} | ||
|
||
if len(appname) > 0 { | ||
// ...if an appname is supplied, use that instead of the default | ||
format := appname + "-%s" | ||
uuider.formatter = func(u string) string { | ||
return fmt.Sprintf(format, u) | ||
} | ||
} | ||
|
||
return uuider | ||
} | ||
|
||
// ID returns a random (v4) UUID using the prefix supplied to NewAppUUID() | ||
func (i *AppUUID) ID() (string, error) { | ||
u, err := uuid.NewV4() | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return i.formatter(u.String()), nil | ||
} | ||
|
||
// A PipelineID identifies requests all the way through some system by concatenating the | ||
// IDs generated by each individual app, each separated by a separator string, "|" | ||
type PipelineID struct { | ||
AppIDer IDer | ||
} | ||
|
||
const fullIDerSep = "|" | ||
|
||
// ID generates a new pipeline ID based on the current ID and its app IDer | ||
func (f *PipelineID) ID(current string) (string, error) { | ||
next, err := f.AppIDer.ID() | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
if len(current) == 0 { | ||
return next, nil | ||
} | ||
|
||
return strings.Join([]string{current, next}, fullIDerSep), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package server | ||
|
||
import ( | ||
"regexp" | ||
"testing" | ||
) | ||
|
||
func TestAppUUID_ID(t *testing.T) { | ||
// thanks to https://adamscheller.com/regular-expressions/uuid-regex/ for saving me from writing this | ||
uuidRegex := "([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}){1}" | ||
t.Run("NoAppName", func(t *testing.T) { | ||
ider := NewAppUUID("") | ||
id, err := ider.ID() | ||
if err != nil { | ||
t.Error("failed to get mock app ID", "err", err) | ||
} | ||
|
||
match, err := regexp.MatchString(uuidRegex, id) | ||
if err != nil { | ||
t.Error("err matching generated ID", "err", err) | ||
} | ||
if !match { | ||
t.Error("ID did not match", "got", id) | ||
} | ||
}) | ||
|
||
t.Run("WithAppName", func(t *testing.T) { | ||
ider := NewAppUUID("blapp") | ||
id, err := ider.ID() | ||
if err != nil { | ||
t.Error("failed to get mock app ID", "err", err) | ||
} | ||
|
||
match, err := regexp.MatchString("blapp-"+uuidRegex, id) | ||
if err != nil { | ||
t.Error("err matching generated ID", "err", err) | ||
} | ||
if !match { | ||
t.Error("ID did not match", "got", id) | ||
} | ||
}) | ||
} | ||
|
||
type MockIDer struct { | ||
sendThis string | ||
} | ||
|
||
func (m *MockIDer) ID() (string, error) { | ||
return m.sendThis, nil | ||
} | ||
|
||
func TestPipelineID_ID(t *testing.T) { | ||
first := "antleeb" | ||
second := "babananab" | ||
third := "canola" | ||
|
||
mockIDer := &MockIDer{} | ||
pipeIDer := &PipelineID{AppIDer: mockIDer} | ||
|
||
mockIDer.sendThis = first | ||
id, err := pipeIDer.ID("") | ||
if err != nil { | ||
t.Error("failed to get pipeline ID", "err", err) | ||
} | ||
if first != id { | ||
t.Error("frist ID call did not match", "got", id, "expected", first) | ||
} | ||
|
||
mockIDer.sendThis = second | ||
id, err = pipeIDer.ID(id) | ||
exp := first + fullIDerSep + second | ||
if err != nil { | ||
t.Error("failed to get pipeline ID", "err", err) | ||
} | ||
if exp != id { | ||
t.Error("second ID call did not match", "got", id, "expected", exp) | ||
} | ||
|
||
mockIDer.sendThis = third | ||
id, err = pipeIDer.ID(id) | ||
exp = first + fullIDerSep + second + fullIDerSep + third | ||
if err != nil { | ||
t.Error("failed to get pipeline ID", "err", err) | ||
} | ||
if exp != id { | ||
t.Error("third ID call did not match", "got", id, "expected", exp) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -177,3 +177,77 @@ func TestNoCacheHandler(t *testing.T) { | |
t.Errorf("expected no-cache Expires header to be '%#v', got '%#v'", want, got) | ||
} | ||
} | ||
|
||
func TestAppIDHandler(t *testing.T) { | ||
r, err := http.NewRequest("GET", "", nil) | ||
if err != nil { | ||
t.Error("failed to create mock request", "err", err) | ||
} | ||
w := httptest.NewRecorder() | ||
|
||
id := "flambe" | ||
AppIDHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
requestID := GetRequestID(r.Context()) | ||
// write the ID to the response body so we can test it on the recorder | ||
_, _ = w.Write([]byte(requestID)) | ||
w.WriteHeader(http.StatusOK) | ||
}), &MockIDer{sendThis: id}).ServeHTTP(w, r) | ||
|
||
headVal, ok := w.Result().Header[RequestIDHeader] | ||
if !ok { | ||
t.Error("header value was not found") | ||
} | ||
if len(headVal) != 1 { | ||
t.Error("expected one value in request ID header", "got", len(headVal)) | ||
} | ||
if headVal[0] != id { | ||
t.Error("unexpected value in request ID header", "got", headVal[0], "expected", id) | ||
} | ||
if w.Body.String() != id { | ||
t.Error("unexpected value in body", "got", w.Body.String(), "expected", id) | ||
} | ||
} | ||
|
||
func TestPipelineIDHandler(t *testing.T) { | ||
tests := []struct { | ||
prev, next, expected string | ||
}{ | ||
{"", "roger", "roger"}, | ||
{"roger", "roderick", "roger|roderick"}, | ||
{"roger|roderick", "brian", "roger|roderick|brian"}, | ||
} | ||
|
||
for _, test := range tests { | ||
r, err := http.NewRequest("GET", "", nil) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for table driven test stuff each test case should be in a t.run, ie: func TestPipelineIDHandler(t *testing.T) {
tests := []struct {
desc, prev, next, expected string
}{
{"new", "", "roger", "roger"},
{"existing", "roger", "roderick", "roger|roderick"},
{"chained", "roger|roderick", "brian", "roger|roderick|brian"},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
r, err := http.NewRequest("GET", "", nil)
...
})
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks! fixed. |
||
if err != nil { | ||
t.Error("failed to create mock request", "err", err) | ||
} | ||
r.Header.Set(RequestIDHeader, test.prev) | ||
w := httptest.NewRecorder() | ||
|
||
pipeIDer := &PipelineID{ | ||
AppIDer: &MockIDer{sendThis: test.next}, | ||
} | ||
|
||
PipelineIDHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
requestID := GetRequestID(r.Context()) | ||
// write the ID to the response body so we can test it on the recorder | ||
_, _ = w.Write([]byte(requestID)) | ||
w.WriteHeader(http.StatusOK) | ||
}), pipeIDer).ServeHTTP(w, r) | ||
|
||
headVal, ok := w.Result().Header[RequestIDHeader] | ||
if !ok { | ||
t.Error("header value was not found") | ||
} | ||
if len(headVal) != 1 { | ||
t.Error("expected one value in request ID header", "got", len(headVal)) | ||
} | ||
if headVal[0] != test.expected { | ||
t.Error("unexpected value in request ID header", "got", headVal[0], "expected", test.expected) | ||
} | ||
if w.Body.String() != test.expected { | ||
t.Error("unexpected value in body", "got", w.Body.String(), "expected", test.expected) | ||
} | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
I think this is fine, we don't want to set the request ID to empty string, better to not set it at all.
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.
Agreed, but a) I shouldn't have let this TODO slip into the commit, and b) I think of this code as being important but not critical, and the idea that something that isn't critical blitzing stuff that is annoys me. In this case, this err is here solely because the UUID generator.
If you think this is fine I'll remove the TODO and leave the return.