Skip to content
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

Merged
merged 18 commits into from
Mar 19, 2018

Conversation

simonbtomlinson
Copy link
Contributor

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.

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.
@simonbtomlinson
Copy link
Contributor Author

I've separated the parts of the Api class in commit 5b5b891.

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';
Copy link
Contributor

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?

Copy link
Contributor Author

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 {
Copy link
Contributor

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});
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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)
Copy link
Contributor

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?

Copy link
Contributor Author

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 () => {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

@@ -110,9 +110,8 @@ router.post('/create', async (req, res) => {
const validGroup = {
type: 'object',
maxProperties: 4,
Copy link
Contributor

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
Copy link
Contributor

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.
Copy link
Contributor

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()) {
Copy link
Contributor

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?

Copy link
Contributor Author

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();
Copy link
Contributor

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?

Copy link
Contributor Author

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).

Copy link
Contributor

@buip buip left a 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!

Copy link
Contributor

@buip buip left a 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

@@ -157,9 +156,8 @@ router.put('/edit', async (req, res) => {
const validGroup = {
type: 'object',
maxProperties: 5,
Copy link
Contributor

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

@@ -227,9 +225,8 @@ router.post('/delete', async (req, res) => {
const validParams = {
type: 'object',
maxProperties: 2,
Copy link
Contributor

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

@simonbtomlinson simonbtomlinson merged commit 6f70e6c into development Mar 19, 2018
@simonbtomlinson simonbtomlinson deleted the extract-api branch March 19, 2018 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants