Extracts the inline validation code from the settings module to a
dedicated deserialize module, so that the validation rules can be better
re-used elsewhere.
Uses functions rather than newtype structs, for 2 reasons:
1. There's an issue in the config crate where newtype structs aren't
deserialized. An unmerged PR exists to fix it, but in the meantime
this changeset works round the problem.
2. When using newtype structs, the tests aren't able to create fresh
instances of the Settings struct unless it is flattened to a single
level.
Maybe we can revisit the newtype struct approach down the line when both
of the above are resolved.
https://github.com/mozilla/fxa-email-service/pull/15
r=rfk
Imitates, as closely as possible, the convict-based approach we use in
our node repos. The major difference is that validation here has to
happen in the code, we can't define it in config.
Settings are applied in the following order:
* config/default.json
* config/${NODE_ENV}.json (optional)
* config/local.json (optional, git-ignored)
* environment variables
I opted to re-use $NODE_ENV instead of some rust-specific environment
variable so that our dev setups will work with minimal changes. If that
seems too weird though I can change it.
Uses the `config` crate, which was the convictiest dependency I could
find.
Not wired in to any behaviour, just does some crude validation and then
returns a hard-coded message id. There is some test coverage, but also
some gaps where I haven't figured out how we should do stuff yet:
* The validation code is punishingly verbose. I evauated a couple of
crates to implement validation, `accord` and `validator`, and opted
for `validator`. However they both seem quite simple, for instance
there's no support for nested objects or values packed inside arrays.
This meant we couldn't lean on the nice, readable macro syntax that's
available and have to explitly validate fields using Rocket's FromData
trait instead.
* Rocket has a feature called error catchers, which I couldn't get to
work. These are supposed to let you return custom error data but, no
matter what I tried, it always seemed to return the default error
response for each type (which is an html string). And even if I could
get those to work, I couldn't see a way to propagate rich error
information, e.g. names of invalid parameters, into the catcher
without having to validate all of the parameters a second time inside
the catcher.
* I tried to write a test case that fired unicode at the endpoint and it
failed.
I'm sure the root cause of these problems is just my own lack of
expertise and we'll figure it out eventually. In the meantime, this
seemed like enough to get us started. I'll open issues to cover the
above.
In terms of code structure, I've just lumped most stuff in to the send
module for now. As we add more functionality, I expect the natural
boundaries to reveal themselves. No point agonising over it yet.