{"action":"created","issue":{"url":"https://api.github.com/repos/18F/cms-hitech-apd/issues/63","repository_url":"https://api.github.com/repos/18F/cms-hitech-apd","labels_url":"https://api.github.com/repos/18F/cms-hitech-apd/issues/63/labels{/name}","comments_url":"https://api.github.com/repos/18F/cms-hitech-apd/issues/63/comments","events_url":"https://api.github.com/repos/18F/cms-hitech-apd/issues/63/events","html_url":"https://github.com/18F/cms-hitech-apd/pull/63","id":284210011,"number":63,"title":"Add endpoints for local user management","user":{"login":"mgwalker","id":1775733,"avatar_url":"https://avatars3.githubusercontent.com/u/1775733?v=4","gravatar_id":"","url":"https://api.github.com/users/mgwalker","html_url":"https://github.com/mgwalker","followers_url":"https://api.github.com/users/mgwalker/followers","following_url":"https://api.github.com/users/mgwalker/following{/other_user}","gists_url":"https://api.github.com/users/mgwalker/gists{/gist_id}","starred_url":"https://api.github.com/users/mgwalker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mgwalker/subscriptions","organizations_url":"https://api.github.com/users/mgwalker/orgs","repos_url":"https://api.github.com/users/mgwalker/repos","events_url":"https://api.github.com/users/mgwalker/events{/privacy}","received_events_url":"https://api.github.com/users/mgwalker/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2017-12-22T17:04:07Z","updated_at":"2017-12-22T17:09:22Z","closed_at":null,"author_association":"MEMBER","pull_request":{"url":"https://api.github.com/repos/18F/cms-hitech-apd/pulls/63","html_url":"https://github.com/18F/cms-hitech-apd/pull/63","diff_url":"https://github.com/18F/cms-hitech-apd/pull/63.diff","patch_url":"https://github.com/18F/cms-hitech-apd/pull/63.patch"},"body":"## The endpoints\r\n\r\n### Create a new user\r\n\r\nAn HTTP `POST` to `/user` allows an authenticated user to add a new user.  Expects a JSON body with an email address and password.  The password will be hashed with bcrypt before it is stored.  Sends an HTTP status 400 if the body is invalid or if the email address already exists in the database along with an error message token.  Otherwise it sends back an HTTP status 200 and an empty body.  Relies on the `loggedIn` middleware.\r\n\r\n### Get a list of all users\r\n\r\nAn HTTP `GET` to `/users` allows an authenticated user to get a list of all users.  Sends the results back as JSON.  Relies on the `loggedIn` middleware.\r\n\r\n### Get a specific user\r\n\r\nAn HTTP `GET` to `/users/:id` allows an authenticated user to get the user specified by the `:id` parameter (e.g., `/users/57` would get the user whose ID is 57).  Sends an HTTP status 400 if the ID is invalid (e.g., missing or not a number) along with an error message token.  Sends a status 404 and empty body if the user ID is not found.  Otherwise it sends back the user as JSON.  Relies on the `loggedIn` middleware.\r\n\r\n## Middleware\r\n\r\nTo make it easier to lock routes to only authenticated users (and hopefully, down the road, to let us specific authorization requirements), I've added a `loggedIn` middleware.  Inserting this into the list of endpoint handlers will make sure the \"main\" handler is only called if the user is logged in.  E.g.:\r\n\r\n```js\r\napp.post('/user', loggedIn, (req, res) => { ...`\r\n```\r\n\r\nIn the future, I'm envisioning extending this model so we also have an `authenticated` (or something else; I'm not a fan of that name, but can't think of a better one offhand) middleware:\r\n\r\n```js\r\napp.post('/user', authenticated('admin'), (req, res) => { ...`\r\n```\r\n\r\nThe idea is simply that the various endpoint handlers shouldn't have to check user credentials themselves - that should be centralized.\r\n\r\n## Error message tokens\r\n\r\nThis is me just making stuff up.  Instead of sending back full English strings describing the errors, I've made these endpoints send back token-ish strings like `add-user-email-exists`.  The idea is that it's readable for debugging purposes, but for displaying to users, it can be mapped to localized strings in the client.  This feels like an experiment to me.  Feedback welcomed and encouraged.  😆 \r\n\r\nIf we decide this is a good approach, we should probably capture all of the error \"tokens\" in a doc somewhere.  For #45, I was planning to create a `docs` folder to put development documentation in.  That might be a place for it.  However we do it, let's keep it lightweight.  😬 \r\n\r\n## Random fixes\r\n\r\n* The email field in the users table was not unique.  Oops!  There's a new migration to handle that.\r\n* The API dockerfile tried to helpful migrate and seed the database with dev data on *build*, but that didn't work because the local development volume isn't mounted into the container until the container is *started*.  So knex was complaining that it couldn't find its config file and that was breaking the entire container build.  For now, migrations and seeds will just have to be run manually.  That's not too hard, though: once the containers are running, you can do:\r\n   ```bash\r\n   docker-compose exec api npm run migrate\r\n   docker-compose exec api npm run seed\r\n   ```\r\n   For folks not using docker...  no change.  😉 \r\n\r\n## Tasks\r\n\r\n* Closes #33"},"comment":{"url":"https://api.github.com/repos/18F/cms-hitech-apd/issues/comments/353638949","html_url":"https://github.com/18F/cms-hitech-apd/pull/63#issuecomment-353638949","issue_url":"https://api.github.com/repos/18F/cms-hitech-apd/issues/63","id":353638949,"user":{"login":"codecov-io","id":8655789,"avatar_url":"https://avatars0.githubusercontent.com/u/8655789?v=4","gravatar_id":"","url":"https://api.github.com/users/codecov-io","html_url":"https://github.com/codecov-io","followers_url":"https://api.github.com/users/codecov-io/followers","following_url":"https://api.github.com/users/codecov-io/following{/other_user}","gists_url":"https://api.github.com/users/codecov-io/gists{/gist_id}","starred_url":"https://api.github.com/users/codecov-io/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/codecov-io/subscriptions","organizations_url":"https://api.github.com/users/codecov-io/orgs","repos_url":"https://api.github.com/users/codecov-io/repos","events_url":"https://api.github.com/users/codecov-io/events{/privacy}","received_events_url":"https://api.github.com/users/codecov-io/received_events","type":"User","site_admin":false},"created_at":"2017-12-22T17:09:21Z","updated_at":"2017-12-22T17:09:21Z","author_association":"NONE","body":"# [Codecov](https://codecov.io/gh/18F/cms-hitech-apd/pull/63?src=pr&el=h1) Report\n> Merging [#63](https://codecov.io/gh/18F/cms-hitech-apd/pull/63?src=pr&el=desc) into [master](https://codecov.io/gh/18F/cms-hitech-apd/commit/cb4f5480a34a66033c2ef02ad0d8230872d078a2?src=pr&el=desc) will **not change** coverage.\n> The diff coverage is `100%`.\n\n[![Impacted file tree graph](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/graphs/tree.svg?width=650&height=150&src=pr&token=3LGr7FwcxN)](https://codecov.io/gh/18F/cms-hitech-apd/pull/63?src=pr&el=tree)\n\n```diff\n@@          Coverage Diff           @@\n##           master    #63    +/-   ##\n======================================\n  Coverage     100%   100%            \n======================================\n  Files          18     28    +10     \n  Lines         266    537   +271     \n  Branches        6      6            \n======================================\n+ Hits          266    537   +271\n```\n\n\n| [Impacted Files](https://codecov.io/gh/18F/cms-hitech-apd/pull/63?src=pr&el=tree) | Coverage Δ | |\n|---|---|---|\n| [api/tests/auth/middleware.js](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/diff?src=pr&el=tree#diff-YXBpL3Rlc3RzL2F1dGgvbWlkZGxld2FyZS5qcw==) | `100% <100%> (ø)` | |\n| [api/tests/routes/index.js](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/diff?src=pr&el=tree#diff-YXBpL3Rlc3RzL3JvdXRlcy9pbmRleC5qcw==) | `100% <100%> (ø)` | |\n| [api/tests/routes/users/index.js](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/diff?src=pr&el=tree#diff-YXBpL3Rlc3RzL3JvdXRlcy91c2Vycy9pbmRleC5qcw==) | `100% <100%> (ø)` | |\n| [api/tests/routes/users/get.js](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/diff?src=pr&el=tree#diff-YXBpL3Rlc3RzL3JvdXRlcy91c2Vycy9nZXQuanM=) | `100% <100%> (ø)` | |\n| [api/auth/middleware.js](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/diff?src=pr&el=tree#diff-YXBpL2F1dGgvbWlkZGxld2FyZS5qcw==) | `100% <100%> (ø)` | |\n| [api/tests/routes/users/post.js](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/diff?src=pr&el=tree#diff-YXBpL3Rlc3RzL3JvdXRlcy91c2Vycy9wb3N0Lmpz) | `100% <100%> (ø)` | |\n| [api/routes/index.js](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/diff?src=pr&el=tree#diff-YXBpL3JvdXRlcy9pbmRleC5qcw==) | `100% <100%> (ø)` | |\n| [api/routes/users/post.js](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/diff?src=pr&el=tree#diff-YXBpL3JvdXRlcy91c2Vycy9wb3N0Lmpz) | `100% <100%> (ø)` | |\n| [api/routes/users/index.js](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/diff?src=pr&el=tree#diff-YXBpL3JvdXRlcy91c2Vycy9pbmRleC5qcw==) | `100% <100%> (ø)` | |\n| [api/routes/users/get.js](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/diff?src=pr&el=tree#diff-YXBpL3JvdXRlcy91c2Vycy9nZXQuanM=) | `100% <100%> (ø)` | |\n| ... and [10 more](https://codecov.io/gh/18F/cms-hitech-apd/pull/63/diff?src=pr&el=tree-more) | |\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/18F/cms-hitech-apd/pull/63?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/18F/cms-hitech-apd/pull/63?src=pr&el=footer). Last update [cb4f548...70f23a8](https://codecov.io/gh/18F/cms-hitech-apd/pull/63?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n"}}
{"actor":{"display_login":"codecov-io"}}