-
Notifications
You must be signed in to change notification settings - Fork 0
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
Fleshing out client-side #17
Conversation
Review status: 0 of 26 files reviewed at latest revision, 1 unresolved discussion. todo-app-production/client/app/api/index.js, line 35 at r1 (raw file):
Flow doesn't like the way I'm handling payload here:
Should I listen to flow or shut it up? Comments from Reviewable |
Reviewed 26 of 26 files at r1. todo-app-production/client/.bootstraprc, line 22 at r1 (raw file):
Are we sure this is correct? I thought this was true in todo-app-production/client/package.json, line 135 at r1 (raw file):
let's drop the todo-app-production/client/app/api/index.js, line 24 at r1 (raw file):
jsdoc shouldn't be necessary if we use flow IMO, flow makes the code self-documenting so you are guaranteed it doesn't get out of date todo-app-production/client/app/api/index.js, line 25 at r1 (raw file):
what if I want to upload multiple entities? Then this isn't named correctly. We should have two levels of the abstraction. The first one only knows about http method and seeing up the CSRF token etc., as well as setting the pending_ajax_requests helper (see F&G), but needs to know nothing about redux. It's presumptuous to think the only time the app ever needs to make an API call that it's one-to-one mapped to a redux action. If that's the case, we probably didn't need to be using saga in the first place because the app has such simple requirements. Then there is a second set of methods which are specific to each controller. Check out F&G's api folder for an example of what I mean for this second level of abstraction. todo-app-production/client/app/api/index.js, line 35 at r1 (raw file): Previously, Judahmeek (Judah Meek) wrote…
See above comment, we're unnecessarily coupling this implementation to redux todo-app-production/client/app/api/routes.js, line 1 at r1 (raw file):
if you're getting errors here then it's because eslint's import resolver isn't set up correctly, this shouldn't error todo-app-production/client/app/bundles/todosIndex/actions/todos/actionTypes.js, line 1 at r1 (raw file):
Sorry didn't notice this before, we need to move this to its own folder called either todo-app-production/client/app/bundles/todosIndex/reducers/errorsReducer.js, line 19 at r1 (raw file):
I think we should go with this style (note use of aliases and use of // @flow
import { handleActions } from 'redux-actions';
import { List as $$List } from 'immutable';
import type { errorPayload } from 'types';
import { addTodoFailure, removeTodoFailure } from 'actions/todos/actionTypes';
// types
export type State = $$List<Error>;
// initial state
export const errorsInitialState = new $$List();
// helpers
const pushPayload = (state: State, { payload }: errorPayload) => state.push(payload);
// handlers
const handlers = {
[addTodoFailure]: pushPayload,
[removeTodoFailure]: pushPayload,
};
// reducer
export default handleActions(handlers, errorsInitialState); todo-app-production/client/app/bundles/todosIndex/reducers/errorsReducer.test.js, line 1 at r1 (raw file):
try to use flow even in your tests todo-app-production/client/app/bundles/todosIndex/reducers/todosReducer.js, line 18 at r1 (raw file):
when we implement normalize it will change this so we can just use todo-app-production/client/app/bundles/todosIndex/sagas/index.js, line 5 at r1 (raw file):
need a line break above todo-app-production/client/app/bundles/todosIndex/sagas/index.js, line 21 at r1 (raw file):
That's okay, the following is my personal opinion: Redux also can act as sort of a log of what is going on, so it's useful to see that it was successful and then maybe we respond to it maybe we don't. todo-app-production/client/app/libs/constants/httpVerbs.js, line 4 at r1 (raw file):
Can we solve whatever problem this is solving just by using flow to ensure we're using a valid http verb? todo-app-production/client/app/libs/utils/normalizr/index.js, line 4 at r1 (raw file):
I always like to keep lodash/fp at the bottom of the imports as I need to quickly ascertain whether it's in the file or not because I use it so frequently, and it provides a nice sort of end cap for the third-party libs. Sorry if this is wrong in the source code todo-app-production/client/app/libs/utils/normalizr/index.test.js, line 1 at r1 (raw file):
let's try and always use flow even in our tests todo-app-production/client/app/libs/utils/normalizr/index.test.js, line 5 at r1 (raw file):
we can drop the todo-app-production/client/webpack-helpers/set-plugins.js, line 32 at r1 (raw file):
@alexfedoseev can you double check this real fast? I'm not up to date with the latest bootstrap-loader best practices todo-app-production/config/routes.rb, line 5 at r1 (raw file):
we don't allow edit? Comments from Reviewable |
Review status: 19 of 34 files reviewed at latest revision, 18 unresolved discussions. todo-app-production/client/.bootstraprc, line 22 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
Hmmm, this entire section is commented out in F&G so I'm going to go ahead and copy that. todo-app-production/client/package.json, line 135 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
Done. todo-app-production/client/app/api/index.js, line 24 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
Flow only documents type, not purpose. Since you'll be using this code as an example while consulting, wouldn't further documentation make things easier for your clients to identify specific concepts? todo-app-production/client/app/api/index.js, line 25 at r1 (raw file):
todo-app-production/client/app/api/routes.js, line 1 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
We match F&G's import resolver, which means this must be a webpack issue. I'll investigate further. Comments from Reviewable |
Review status: 14 of 40 files reviewed at latest revision, 18 unresolved discussions, some commit checks failed. todo-app-production/client/app/bundles/todosIndex/reducers/errorsReducer.js, line 19 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
Done. todo-app-production/client/app/bundles/todosIndex/reducers/errorsReducer.test.js, line 1 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
I tried to, but get 104 errors because Jest and Flow don't get along even after updating flow-typed. todo-app-production/client/app/bundles/todosIndex/sagas/index.js, line 5 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
Done. todo-app-production/client/app/libs/utils/normalizr/index.js, line 4 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
By "at the bottom", do you mean below all the imports or just below the other third party libraries? todo-app-production/client/app/libs/utils/normalizr/index.test.js, line 1 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
Tried. Doesn't work with Jest even after updating flow-typed. todo-app-production/client/app/libs/utils/normalizr/index.test.js, line 5 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
Done. todo-app-production/client/webpack-helpers/set-plugins.js, line 32 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
I pulled this straight from bootstrap-loader's readme so I sure hope it's up-to-date. todo-app-production/config/routes.rb, line 5 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
Should we? I assumed that React would just create the edit form. Besides, if necessary, we could always pull any necessary information through a #show request, right? todo-app-production/client/app/bundles/todosIndex/actions/todos/actionTypes.js, line 1 at r1 (raw file): Previously, robwise (Rob Wise) wrote…
What's the benefit of that file structure? Less duplication of action types? Comments from Reviewable |
@@ -25,12 +25,13 @@ describe('addTodo Saga', () => { | |||
it('handles async errors', () => { | |||
const description = 'todo description'; | |||
const id = 'todoId'; | |||
const payload = { description, id }; | |||
|
|||
const action = todosActions.addTodo(description, id); | |||
const generator = sagas.addTodo(action); | |||
|
|||
let nextGen = generator.next(); |
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.
Try to avoid using let
s. You don't need to keep reassigning the variable like this. Just do:
const gen = sagas.addTodo(action);
// ....
gen.next();
@@ -1,22 +1,22 @@ | |||
// @flow | |||
import { call, put, fork, takeEvery } from 'redux-saga/effects'; | |||
import type { putEffect, IOEffect } from 'redux-saga/effects'; | |||
import { callApi } from 'app/api'; | |||
import * as api from 'app/api/todos'; |
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.
Needs a blank line above this
export const removeTodo = (todoId: number) => { | ||
const url = todosScope(`/${todoId}`); | ||
return apiCall.delete({ url }); | ||
}; |
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
export function isObject(subject: any) { | ||
return typeof subject === 'object' && Object.prototype.toString.call(subject) === '[object Object]'; | ||
} | ||
|
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 must have written this a long time ago, I think we can just use _.isPlainObject
instead?
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.
Done
export const isProduction = !!RAILS_ENV && RAILS_ENV === Environment.PRODUCTION; | ||
export const isStaging = !!RAILS_ENV && RAILS_ENV === Environment.STAGING; | ||
export const isProductionLike = !isDev && !isTest; | ||
export const isServerRendering = SERVER_RENDERING; |
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.
Where are we using these? These have never really worked right on F&G because of Heroku Preboot, so even when you're in production, it still thinks you're in staging. Are we sure we need these?
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.
They were referenced at https://github.com/shakacode/react-on-rails-pro/pull/17/files#diff-167e965b00696dfa51189e22e267cb84, but I doubt they're absolutely necessary.
It's pretty simple code though, so I wouldn't say it doesn't work right. More like you might need an additional env_var to signal when Heroku Preboot is active?
|
||
const ATTRIBUTE_NAME = 'data-are_ajax_requests_pending'; | ||
|
||
let $$pendingAjaxRequestUuids = new $$Set(); |
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 must have wrote this a long time ago, let's drop the $$
except on $$Set
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.
Done
Review status: 8 of 122 files reviewed at latest revision, 19 unresolved discussions, some commit checks failed. todo-app-production/client/.flowconfig, line 8 at r3 (raw file):
Craziest thing: adding Comments from Reviewable |
const updateBodyAttribute = () => bodyEl.setAttribute(ATTRIBUTE_NAME, hasPendingAjaxRequests()); | ||
if (bodyEl == null) { | ||
throw new Error('document.body is undefined!'); | ||
} |
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 would this ever be the case?
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.
Flow wants null checks or it gives a Method cannot be called on possibly null value
error.
} | ||
|
||
const hasPendingAjaxRequests = () => !pendingAjaxRequestUuids.isEmpty(); | ||
const updateBodyAttribute = () => bodyEl.setAttribute(ATTRIBUTE_NAME, hasPendingAjaxRequests().toString()); |
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.
J/C why toSring
?
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 flow demands a string for setAttribute.
return this.callApi('PUT', params); | ||
}, | ||
|
||
delete(params) { | ||
delete(params: any) { |
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.
these are string | {}
not any
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.
Will fix.
if (response.ok) return response; | ||
|
||
return response.json().then(errData => { | ||
const isBadCsrfToken = response.status === 401 && response.message === 'FnG: Bad Authenticity Token'; | ||
const isBadCsrfToken = response.status === 401 && errData.message === 'Bad Authenticity Token'; |
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.
J/C on change from response
to errData
. Do we need to change this on F&G?
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.
Probably. Flow says that response
doesn't have a message
parameter. What I did passes flow, but I won't be able to say that it actually works until I'm able to test it. I'm not well acquainted with Fetch API or Promises yet and the documentation isn't as clear as I would like.
import { stringify } from 'qs'; | ||
import _ from 'lodash/fp'; | ||
|
||
import Environment from 'app/libs/constants/Environment'; | ||
import * as env from 'app/libs/utils/env'; | ||
|
||
export function buildUrl(path, query) { | ||
export function buildUrl(path: string, query: Object) { |
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.
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 see if I can't get more specific. Suggestions would be appreciated.
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.
The only option I'm currently aware of is using an exact option type for query: https://flowtype.org/docs/objects.html#exact-object-types
import { buildUrl } from './index'; | ||
|
||
describe('libs/utils/api', () => { | ||
test('{ buildUrl }', () => { | ||
describe('{ buildUrl }', () => { | ||
it('combines a path and an object of key values into a url with a query', () => { |
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.
does the it
need to become test
?
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.
No, it
is an alias for test
. The problem was that nested test
's automatically pass without any complaint.
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('creates an object with numeric ids as the keys', () => { | ||
const array = [{ id: 1 }]; | ||
|
||
const actual = normalizeArray(array); | ||
const expected = { 1: { id: 1 } }; // eslint-disable-line no-useless-computed-key | ||
const expected = { [1]: { id: 1 } }; // eslint-disable-line no-useless-computed-key |
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.
J/C why you needed to add the brackets?
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.
Flow again: facebook/flow#380
it('creates a map with numeric ids as the keys', () => { | ||
const array = [{ id: 1 }]; | ||
|
||
const actual = normalizeArrayToMap(array); | ||
const expected = normalizeMapIdKeys({ 1: { id: 1 } }); // eslint-disable-line no-useless-computed-key,max-len | ||
const expected = normalizeMapIdKeys({ [1]: { id: 1 } }); // eslint-disable-line no-useless-computed-key,max-len |
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 Q as above?
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 A
const setDevtool = require('./set-devtool'); | ||
|
||
describe('webpack-helpers/set-devtool', () => { | ||
test('when builderConfig.devtool is not defined', () => { | ||
describe('when builderConfig.devtool is not defined', () => { | ||
it('outputs to a public path', () => { |
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 are these all still it
instead of test
?
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 it
is an alias for test
: http://facebook.github.io/jest/docs/api.html#testname-fn
const actual = setEntry(builderConfig, {}).entry.vendor; | ||
|
||
expect(actual).toBe(expect.stringContaining(expected)); | ||
expect(actual).toEqual(expect.arrayContaining(expected)); |
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.
Can't you just do
const expected = expect.arrayContaining('react-addons-perf');
const actual = setEntry(builderConfig, {}).entry.vendor;
expect(actual).toEqual(expected);
String matching is for when you have a regex I think, simple equality will do here
Same goes for others below
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're right. stringMatching()
is unnecessary here.
This change is