-
Notifications
You must be signed in to change notification settings - Fork 345
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
Collect all frontend api requests in one place #311
Conversation
This removes validation for the token in the groups routes because the token is not actually necessary (it might be passed by a header) and in fact the Api class exclusively passes it as a header.
Splits the api into an `ApiBackend` that handles actual networking and a class for each category of api actions. For example, `MetersApi` contains methods for interacting with meters.
I've separated the parts of the |
src/client/app/actions/admin.ts
Outdated
import { ChartTypes } from '../types/redux/graph'; | ||
import { PreferenceRequestItem } from '../types/items'; | ||
import * as t from '../types/redux/admin'; | ||
import { ActionType, Dispatch, GetState, Thunk } from '../types/redux/actions'; | ||
import { State } from '../types/redux/state'; | ||
import {preferencesApi} from '../utils/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.
We aren't using this spacing style in the project. Perhaps we need to add a tslint rule?
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 there's a tslint rule for this. I do like { foo }
style better though, and a rule would be nice. See here: palantir/tslint#1044. I'll fix it manually.
* | ||
* Should probably only be used by other api classes. | ||
*/ | ||
class ApiBackend { |
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 like the name ApiBackend, as it signifies something server-side. How about ApiInternal.ts
, Api.ts
, or ApiDispatcher.ts
?
} | ||
|
||
public async delete(groupID: number) { | ||
return await this.backend.doPostRequest('api/groups/delete', {id: groupID}); |
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.
Why is this a POST instead of DELETE request? Recommend changing the server to correspond to be a DELETE request.
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 didn't actually change the way we do any networking, as I didn't think it was in-scope for this pr. We can definitely change that in a different pr though.
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.
Yeah, I'm on board for changing this in a future PR.
token: getToken() | ||
} | ||
}) | ||
metersApi.submitNewReadings(this.props.selectedImportMeter.value, file) |
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 reason directly below this line that you used a .then
instead of async
/await
? Or would that get ugly fast because await
needs to be in an async function?
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's cleaner than wrapping it in an async IIFE in this case, and it was already like that, so I decided not to change it.
// If there was a problem other than a lack of authorization, the user can't fix it. | ||
// Log it to the console for developer use. | ||
console.error(err); // tslint:disable-line no-console | ||
(async () => { |
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 it be possible to mark handleSubmit
as async instead of using an async IFFE? Or does that not work with React?
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 can't call setState() inside an async function, so this has to stay an IFFE.
src/server/routes/groups.js
Outdated
@@ -110,9 +110,8 @@ router.post('/create', async (req, res) => { | |||
const validGroup = { | |||
type: 'object', | |||
maxProperties: 4, |
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.
Don't forget to change maxProperties here and on all the other validation routes you changed.
} else { | ||
// If there was a problem other than a lack of authorization, the user can't fix it. | ||
// Log it to the console for developer use. | ||
console.error(err); // tslint:disable-line no-console |
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.
Another use of console.eror -> throw new Error
/** | ||
* Handles the actual sending and receiving of network requests. | ||
* | ||
* Should probably only be used by other api classes. |
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.
Recommend removing "probably", we definitely don't want it used outside other API classes.
headers: { [key: string]: string}, | ||
extraConfig: AxiosRequestConfig | ||
): AxiosRequestConfig { | ||
if (hasToken()) { |
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.
Doesn't this always send a token regardless of the route? Is this best practice?
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 does. I think this is a good way to do it. It makes things available to the server more often and there's less of a downside. We can just make the server send a 401 unauthorized if the user doesn't have the appropriate authorization level.
dispatch2(changeBarStacking()); | ||
} | ||
}); | ||
const preferences = await preferencesApi.getPreferences(); |
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 know the old code did not have a catch condition, but should we be explicit and add a catch to every request?
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.
Maybe? I feel like we want to throw an error here, at least until we have more robust error handling (which isn't in scope for this pr, I don't 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.
I don't know about the client side but the server side looks good to 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.
Sorry for the last comment! I did not look carefully
src/server/routes/groups.js
Outdated
@@ -157,9 +156,8 @@ router.put('/edit', async (req, res) => { | |||
const validGroup = { | |||
type: 'object', | |||
maxProperties: 5, |
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 maxProperties should be 4 here
src/server/routes/groups.js
Outdated
@@ -227,9 +225,8 @@ router.post('/delete', async (req, res) => { | |||
const validParams = { | |||
type: 'object', | |||
maxProperties: 2, |
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 maxProperties should be 1 here
Pulls all api requests into a single
Api.ts
file that takes care of things like authentication.I'd be happy to receive feedback about how
Api.ts
can be split up in a way that's easier to read while still easy to use.This required modifying some server-side validation code in places that expected a token parameter. The token can be passed in a number of ways (see
src/server/routes/authenticator.js
for the relevant code), but lots of validation code assumed it would be passed in a specific way.