-
Notifications
You must be signed in to change notification settings - Fork 53
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
refactor: Improve the HTTP API implementation #382
Conversation
Codecov Report
@@ Coverage Diff @@
## develop #382 +/- ##
===========================================
- Coverage 65.66% 64.53% -1.14%
===========================================
Files 80 86 +6
Lines 9481 9866 +385
===========================================
+ Hits 6226 6367 +141
- Misses 2624 2867 +243
- Partials 631 632 +1
|
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.
Awesome work @fredcarle. Just a few minor things:
-
Just a bit concerned about not handling inconsistencies between different GQL clients (more detail here: Defra Server GQL Endpoint Bugs Out Using GQL Clients #367)
-
We use the
Git Rebase
Flow vsGit Merge
Flow. Just to avoid those merge commit pollutions fromdevelop
branch into thefeature
branch and also to keep a cleaner history for the reviewers (happy to go over my flow if you'd like =D). -
You should update the
README.md
of the repo which currently says:
Once you've confirmed your node is running correctly, if you're using the GraphiQL client to interact with the database, then make sure you set the GraphQL Endpoint to http://localhost:9181/graphql and the Method to GET
-
Add your name in the
README.md
under theContributors
section. WOOOT WOOOOT!!!!!! -
You can use resolving words in the PR description to auto link the PR to an issue so it closes on merge (Linking a pull request to an issue). So your first line can be something like "Resolves Refactor: HTTP API #372".
-
A slightly more descriptive PR title, where the first word is an action of some sort (here is a nice list: https://gist.github.com/scmx/411f6fea4ee3832806720d536a7d5d8f). btw not sure if you stumbled across these yet or not but the subset of conventional commit labels we use can be found here: https://github.com/sourcenetwork/defradb/blob/develop/tools/configs/chglog/config.yml.
api/http/handler.go
Outdated
if c.req.Method == "GET" { | ||
query = c.req.URL.Query().Get("query") | ||
} else { | ||
body, err := io.ReadAll(c.req.Body) | ||
if err != nil { | ||
http.Error(c.res, err.Error(), http.StatusBadRequest) | ||
return | ||
} | ||
query = string(body) | ||
} | ||
|
||
if query == "" { | ||
http.Error(c.res, "missing GraphQL query", http.StatusBadRequest) | ||
return | ||
} |
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 tested it with Altair
and Insomnia
,
This keeps Altair
broken for POST
requests and Insomnia
broken for GET
requests.
I made a more detailed comment on why and gave a suggestion here: #367
eac7ea5
to
dd46ebd
Compare
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.
Code reads really quite nicely - thanks! Quite comment heavy, but a lot are just questions and I'm guessing a fair few would have been resolved before converting to a full-PR. Glad you are getting stuck in already :)
api/http/handler.go
Outdated
|
||
// HandlerConfig holds the handler configurable parameters | ||
type HandlerConfig struct { | ||
Logger logging.Logger |
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.
suggestion: It feels strange to me that a 'config' object contains a configured instance of something (here Logger) - suggest this object hosts log config alongside any http related stuff and whatever consumes this config also configures the logger.
Be careful with this as you'll need to handle config for all defra-packages that this lib consumes.
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.
Fair point. I'm not 100% sure I understand your suggestion but I'll give it a try and you can tell me if that's what you meant.
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.
Was thinking the config object should be something similar to the below:
type HandlerConfig struct {
loggingConfig {
Level:...
EnableStackTrace:...
....
},
httpConfig {
port:...
...
},
dbConfig {
Address:...,
Badger:...
},
}
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.
A continuation of my comments elsewhere, I dont think its necessary to pass in the logger to the handler package, and we certainly wouldnt need to define the loglevel/stacktrace/config info for the logger within this config obj, as the logger stuff is defined elsewhere (at the entry point, eg CLI package).
api/api.go
Outdated
) | ||
|
||
// NewHTTPServer returns a Server loaded with the HTTP API Handler. | ||
func NewHTTPServer(db client.DB, c ...*http.HandlerConfig) *http.Server { |
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.
question: Why would you have many configs?
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.
You wouldn't. It's a pattern used to make the config optional.
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.
Would suggest using a type that only permits a single config value then, or use 2 functions (one with config, one without). Silently dropping configs 1..n might not be so fun for consumers.
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.
Im guilty of also using this pattern in Go to create an "optional" function argument, im 50/50 on if it needs to be split into two funcs, one with no config, and one with a single config.
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 have seen this pattern in gRPC
and a few other Go projects too. This was also one of the recommendations in the "100 Go Mistakes and How to Avoid Them" book. I think it's fine to have one function.
api/api_test.go
Outdated
func TestServer(t *testing.T) { | ||
s := NewHTTPServer(nil, nil) | ||
|
||
if ok := assert.NotNil(t, s); ok { |
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.
question: why the if? And on further thought, is the if ok := assert.NotNil(t, s); ok {
worth it when calling assert.Error(t, s.Listen(":303000"))
if s
is null should provide a very clear error anyway?
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.
Because assert doesn't return or break the process if s
is nil. Also, calling s.Listen
with s
being nil will pollute the terminal with a stack trace while checking with assert.NotNil
will give a clean and concise error message.
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.
this is an issue the plagues most of the test packages. I accidentally started using assert
package from that repo but it turns out they have a more appropriate package called "require" which will automatically fail the test at that point in code without any handling on our part.
So assert.NotNil
becomes require.NotNil
, etc
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 remembered that convo and couldnt remember/find-where-we-fixed-this. Might be good to go through the hassle of deciding to migrate (and then migrating) sooner rather than later.
api/api.go
Outdated
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package api |
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.
question: What do you see this package containing? It's reason-to-be seems unclear at the moment (would suggest package-level docs when converting from draft-PR to normal-PR)
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.
Not sure yet. Maybe the GRPC API could be here too. Maybe a messaging service for queues. Maybe we could have a discussion about it.
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.
What's the benefit of having the package exist now?
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.
To be honest, what initiated this change on my side was seeing
import (
api "github.com/sourcenetwork/defradb/api/http"
)
I'm not a fan of "importing as" especially if the package name has no conflict. So I resorted to create the api package to return the api we want. Maybe I should have just removed the api
name and use http
to call NewServer
. What do you think? This way we could avoid the api package and the import as
.
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 was a preemptive organization descision in case we has differnt transports/protocols for accessing the API beyond HTTP. But, for example, the gRPC API is defined/instanciated elsewhere
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.
Would vote for dropping the API package now (keeping it simple), and then add it when we need it. Cost to add is low, and it makes things easier to understand in the meantime
api/http/handler.go
Outdated
|
||
h.logger = defaultLogger() | ||
|
||
h.setRoutes() |
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.
todo: this looks like a bug? If you provide a logger via c
then setRoutes
is never called. Would suggest replacing the if-early-return with an if-else, otherwise similar issues will be easily introduced (if solved e.g. by adding a h.setRoutes
call within the if)
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.
Good catch. It was indeed a bug. Fixed
api/http/handler.go
Outdated
} | ||
} | ||
|
||
func getBlock(c *context) { |
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.
thought: Didnt realize we touched so many internal concepts here! This looks largely like a copy-paste from the old code, but please dont shy away from cleaning this up a tad if you feel like it (but really no pressure, might be worth creating an issue though if you dont) - really don't think the http package should be going anywhere near cid instantiation and protobuf....
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 was thinking the same. I wanted to refactor the general http package before touching the handler functions so I thought I would create a separate issue to take care of that. Like phase 1: http package architecture, phase 2: refactoring handlers.
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 on approach, structure then handlers:)
api/http/handler_test.go
Outdated
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestHandler(t *testing.T) { |
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.
nitpick: took me a few seconds to realise this wasnt a test utility, but actually a test. Might be worth renaming.
Also (more seriously) the name implies to me that it is and will be the only test for the handler (with default config) - might be worth a rethink here as you get nearer non-draft PR status
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.
Fair enough. I'll rename to be more specific.
api/http/handler.go
Outdated
db: db, | ||
} | ||
|
||
if c != nil { |
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.
suggestion: I am very very against passing nil into a function, I didn't realize you were planning on doing this until I saw the tests. Suggest removing this if c != nil {
check and dont pass nil (if this is why c
is a pointer, would suggest de-pointering the param too).
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.
A more idiomatic way of doing this is to have NewHandler(db client.DB)
and NewHandlerWithConfig(db client.DB, c config)
or just NewHandler(c config)
with db client.DB
passed in the config
. Which one do you prefer?
It's not uncommon in Go to pass nil to a function that behaves differently depending on nil or not nil. Probably not using it in the most appropriate way here though. However, if our preference is to avoid that, fine by me.
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.
Lots of people like to do it in other languages that permit this too, and every now and then users get null point exceptions at runtime... Is easy enough to avoid these, and if you want the occasional optional param you can easily wrap it (even easier with generics):
type OptionalFoo struct {
Foo Foo,
HasValue bool
}
That way everyone is aware that it can be nil, instead of having to worry about the validity of every pointer in the codebase. A few newer langs have this built in (rust/C#/I-think-typescript-but-we-might-have-done-that-manually-at-my-old-place).
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'll reiterate Fred's point that its common to have pointers as arguments (for several reasons) and then having appropriate nil checks within the funcs. Moreover, the compiler is able to make several optimizations if it knows there are specific nil
checks within a func, and ensure that all future pointer access is optimized without the need for runtime nil checks.
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.
Not fussed about performance here (is almost always peanuts), but the needless risk of trying to call a member on a nil pointer.
api/http/handler_test.go
Outdated
|
||
} | ||
|
||
func TestHandlerWithConfig(t *testing.T) { |
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.
nitpick: Similar points to the comment RE the other test function's name, this function also suggests it should be the only test for any/all config and whilst maybe a bit hard to do atm as config is fairly empty, would suggest the name reflects the contents of the config under test
api/http/server.go
Outdated
} | ||
|
||
// NewServer instantiated a new server. | ||
func NewServer() *Server { |
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.
question: Why return a pointer here?
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.
Habit of constructors in Go generally returning a pointer. Here in doesn't have to be.
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.
Happy as is, was just curious
Thanks Shahzad!
I'll investigate further and give those GQL clients a try myself so I can get a feel for it.
Oh yes John had mentioned that and I forgot to do it. I fixed that but I'd still like to see your flow. Let me know when that works for you.
Done!
Yes!!
I edited the initial comment to include
I updated the title. Let me know if that fits the bill better. |
@AndrewSisley I've made a few changes based on your feedback and some extra as well. Let me know what you think. |
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.
Added a few thoughts and changes. Most notably is the logger flow, route names/prefix, and the new context
object.
api/http/handler.go
Outdated
db client.DB | ||
|
||
*chi.Mux | ||
*logger |
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.
At the moment, the practise weve been using in the other packages, is a "package-level" logger with whatever appropriate namespace/name based on the package.
An example of that is here:
Line 20 in 27ffcf3
log = logging.MustNewLogger("defra.net") |
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 noticed that. I thought I would try it as a config see what the thought was. I'm happy to revert to package level logger.
api/http/handler.go
Outdated
|
||
// HandlerConfig holds the handler configurable parameters | ||
type HandlerConfig struct { | ||
Logger logging.Logger |
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.
A continuation of my comments elsewhere, I dont think its necessary to pass in the logger to the handler package, and we certainly wouldnt need to define the loglevel/stacktrace/config info for the logger within this config obj, as the logger stuff is defined elsewhere (at the entry point, eg CLI package).
api/http/handler.go
Outdated
db: db, | ||
} | ||
|
||
if c != nil { |
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'll reiterate Fred's point that its common to have pointers as arguments (for several reasons) and then having appropriate nil checks within the funcs. Moreover, the compiler is able to make several optimizations if it knows there are specific nil
checks within a func, and ensure that all future pointer access is optimized without the need for runtime nil checks.
api/http/handler.go
Outdated
res http.ResponseWriter | ||
req *http.Request | ||
db client.DB | ||
log *logger |
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.
Same thing mentioned else, should use package level logger :)
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'll try something out with package level logger. I think it might be fun.
api/http/handler.go
Outdated
return h | ||
} | ||
|
||
type context struct { |
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.
Although I do see that it also passes in the client.DB
object.
One naming option is handlerContext
// setup logger middleware | ||
h.Use(h.loggerMiddleware) | ||
|
||
// define routes |
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.
We can prob namespace all these routes under /api/v1
or something for best practises :)
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 agree
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.
should probably then also update the information in all these docs
and Makefile
files in the respective lines to http://localhost:9181/api/v1/
:
README.md
123:This will start a node with the default settings (running at http://localhost:9181), and create a configuration file at $HOME/.defra/config.yaml. Where $HOME is your operating system user home directory.
139:Once you've confirmed your node is running correctly, if you're using the GraphiQL client to interact with the database, then make sure you set the `GraphQL Endpoint` to `http://localhost:9181/api/v1/graphql`.
docs/cmd/defradb_server-dump.md
21: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb_client_rpc.md
22: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb_client_rpc_addreplicator.md
27: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb_client_schema.md
24: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb_client_dump.md
20: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb_client_ping.md
20: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb_client_blocks_get.md
20: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb_client_query.md
31: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb_client_blocks.md
16: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb_client_schema_add.md
26: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb_start.md
30: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb.md
21: --url string url of the target database (default "http://localhost:9181")
docs/cmd/defradb_client.md
22: --url string url of the target database (default "http://localhost:9181")
and just double checking that we don't need to change these ?:
cli/defradb/cmd/root.go
89: "http://localhost:9181",
cli/defradb/cmd/config.go
118: Address: "localhost:9181",
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.
discovering new parts of the code base every day 🤘
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.
@shahzadlone I don't think we should update these URLs. The API endpoint and the database URL are two different things. So saying that the URL of the target database is http://localhost:9181
is correct in my opinion.
// test with no config | ||
s := NewServer() | ||
if ok := assert.NotNil(t, s); ok { | ||
assert.Error(t, s.Listen(":303000")) |
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 just testing to make sure you cant listen on an invalid port/addr combo? If so, should rename the Test func.
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.
At the moment it's pretty useless if you ask me as this is just a utility function using the standard library Listener. I did it mainly for code coverage and to have it as a place holder later on if that function ever becomes more complex and it becomes important to test.
api/http/logger.go
Outdated
"github.com/sourcenetwork/defradb/logging" | ||
) | ||
|
||
type logger struct { |
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 don't think I see a point of having this type. As mentioned in other comments, the package should have a package-level logger that can be directly accessed.
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 the logger is at the package level, 100% agree.
} | ||
} | ||
|
||
type loggingResponseWriter struct { |
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.
This middleware handler can either accept logger as a parameter, or just call the package level logger, but doesn't need to use a custom logger
type native to this package.
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.
indeed.
api/http/logger.go
Outdated
return lrw.ResponseWriter.Write(b) | ||
} | ||
|
||
func (l *logger) loggerMiddleware(next http.Handler) http.Handler { |
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.
Reiteration of the logger design, this can be a regular func that can either take a logger.Logger or just call the package level logger 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.
having the package level logger has a lot of impact on the rest of the code. I'll do the changes :)
cf2ea6d
to
05c9389
Compare
@AndrewSisley and @jsimnz, I made quite a few changes that cover your concerns and suggestions. Mainly the logger is now set at the package level, the context struct has been removed and no more passing around configs. I've also added some more http error handling. Overall the architecture is simpler than my original commit. @shahzadlone I still haven't tested with the gql clients but I think my latest changes cover the issue that you were seeing. |
Title looks good! I believe the new change of removing the However, I tested this branch at this commit Client Error:
Server Error:
|
Found what the bug is. You forgot to update the cobra commands under The db address is still:
Which needs to be updated to:
However maybe you can export the "/api/v1" const string from the |
94e5570
to
67dc73e
Compare
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 :) Cheers for putting up with all our comments 😁
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 ✅ Great work, WOOOT WOOOOT!!!
Tested these and can confirm they all work:
- ✅ Mutation Operation with
GET
onInsomnia
- ✅ Mutation Operation with
GET
onAltair
- ✅ Mutation Operation with
POST
onInsomnia
- ✅ Mutation Operation with
POST
onAltair
- ✅ Query Operation with
GET
onInsomnia
- ✅ Query Operation with
GET
onAltair
- ✅ Query Operation with
POST
onInsomnia
- ✅ Query Operation with
POST
onAltair
No worries. One of the fastest way to improve team efficiency and personal proficiency is to have constructive exchanges. It's always constructive to get feedback:
I love it. Specially when those interactions are with rock star devs 🤘 |
c509a55
to
2365b9c
Compare
2365b9c
to
1264525
Compare
Co-authored-by: Shahzad Lone <[email protected]>
Rename test functions to be more specific. Remove api package and use http.NewServer. Put handlerFuncs in their own file. Change the way options are set on the handler.
Add better error handling. Remove the context struct. Use context.Context to pass db client. Remove handler options.
Move log to api.go Add more specific unit test Add ctx to handleErr Add suffix to handlerfuncs name Make Handler local Fix JoinPaths
1264525
to
56862b5
Compare
Resolves sourcenetwork#372 Resolves sourcenetwork#367 This PR restructures the api/http package with idiomatic patterns and a few useful utility functions.
Resolves #372
Resolves #367
Creating this draft PR to get a bit of feedback. Let me know what you think.
The server test functions are there mainly for code coverage and are kinda useless at the moment. Will be useful eventually if the server instantiation becomes more involved.
Let me know if you find any value in unit testing all the handler functions. I find it not so worth it for the trivial ones and those that use the db connection need to be decoupled (create a service interface to be able to mock the DB methods).