Browse Source

refactoring

update-deps
Alex Mikhalev 7 years ago
parent
commit
8a6d501cda
  1. 10
      client/components/App.tsx
  2. 4
      client/components/DeviceView.tsx
  3. 10
      client/components/NavBar.tsx
  4. 4
      client/components/ProgramTable.tsx
  5. 2
      client/pages/LoginPage.tsx
  6. 2
      client/pages/LogoutPage.tsx
  7. 4
      client/pages/ProgramPage.tsx
  8. 7
      client/sprinklersRpc/WebSocketRpcClient.ts
  9. 24
      client/state/AppState.ts
  10. 35
      client/state/HttpApi.ts
  11. 40
      client/state/TokenStore.ts

10
client/components/App.tsx

@ -5,7 +5,7 @@ import { Container } from "semantic-ui-react"; @@ -5,7 +5,7 @@ import { Container } from "semantic-ui-react";
import { MessagesView, NavBar } from "@client/components";
import * as p from "@client/pages";
import * as rp from "@client/routePaths";
import * as route from "@client/routePaths";
// tslint:disable:ordered-imports
import "font-awesome/css/font-awesome.css";
@ -18,8 +18,8 @@ function NavContainer() { @@ -18,8 +18,8 @@ function NavContainer() {
<NavBar/>
<Switch>
<Route path={rp.device(":deviceId")} component={p.DevicePage}/>
<Route path={rp.messagesTest} component={p.MessagesTestPage}/>
<Route path={route.device(":deviceId")} component={p.DevicePage}/>
<Route path={route.messagesTest} component={p.MessagesTestPage}/>
<Redirect to="/"/>
</Switch>
@ -31,8 +31,8 @@ function NavContainer() { @@ -31,8 +31,8 @@ function NavContainer() {
export default function App() {
return (
<Switch>
<Route path={rp.login} component={p.LoginPage}/>
<Route path={rp.logout} component={p.LogoutPage}/>
<Route path={route.login} component={p.LoginPage}/>
<Route path={route.logout} component={p.LogoutPage}/>
<NavContainer/>
</Switch>
);

4
client/components/DeviceView.tsx

@ -4,7 +4,7 @@ import * as React from "react"; @@ -4,7 +4,7 @@ import * as React from "react";
import { Grid, Header, Icon, Item, SemanticICONS } from "semantic-ui-react";
import * as p from "@client/pages";
import * as rp from "@client/routePaths";
import * as route from "@client/routePaths";
import { AppState, injectState } from "@client/state";
import { ConnectionState as ConState } from "@common/sprinklersRpc";
import { Route, RouteComponentProps, withRouter } from "react-router";
@ -67,7 +67,7 @@ class DeviceView extends React.Component<DeviceViewProps & RouteComponentProps<a @@ -67,7 +67,7 @@ class DeviceView extends React.Component<DeviceViewProps & RouteComponentProps<a
</Grid.Column>
</Grid>
<ProgramTable device={device} routerStore={routerStore} />
<Route path={rp.program(":deviceId", ":programId")} component={p.ProgramPage} />
<Route path={route.program(":deviceId", ":programId")} component={p.ProgramPage} />
</React.Fragment>
);
return (

10
client/components/NavBar.tsx

@ -3,7 +3,7 @@ import * as React from "react"; @@ -3,7 +3,7 @@ import * as React from "react";
import { Link } from "react-router-dom";
import { Menu } from "semantic-ui-react";
import * as rp from "@client/routePaths";
import * as route from "@client/routePaths";
import { AppState, ConsumeState, injectState } from "@client/state";
interface NavItemProps {
@ -26,17 +26,17 @@ function NavBar({ appState }: { appState: AppState }) { @@ -26,17 +26,17 @@ function NavBar({ appState }: { appState: AppState }) {
let loginMenu;
if (appState.isLoggedIn) {
loginMenu = (
<NavItem to={rp.logout}>Logout</NavItem>
<NavItem to={route.logout}>Logout</NavItem>
);
} else {
loginMenu = (
<NavItem to={rp.login}>Login</NavItem>
<NavItem to={route.login}>Login</NavItem>
);
}
return (
<Menu>
<NavItem to={rp.device("grinklers")}>Device grinklers</NavItem>
<NavItem to={rp.messagesTest}>Messages test</NavItem>
<NavItem to={route.device("grinklers")}>Device grinklers</NavItem>
<NavItem to={route.messagesTest}>Messages test</NavItem>
<Menu.Menu position="right">
{loginMenu}
</Menu.Menu>

4
client/components/ProgramTable.tsx

@ -5,7 +5,7 @@ import { Link } from "react-router-dom"; @@ -5,7 +5,7 @@ import { Link } from "react-router-dom";
import { Button, ButtonProps, Form, Icon, Table } from "semantic-ui-react";
import { ProgramSequenceView, ScheduleView } from "@client/components";
import * as rp from "@client/routePaths";
import * as route from "@client/routePaths";
import { Program, SprinklersDevice } from "@common/sprinklersRpc";
@observer
@ -21,7 +21,7 @@ class ProgramRows extends React.Component<{ @@ -21,7 +21,7 @@ class ProgramRows extends React.Component<{
const { name, running, enabled, schedule, sequence } = program;
const buttonStyle: ButtonProps = { size: "small", compact: false };
const detailUrl = rp.program(device.id, program.id);
const detailUrl = route.program(device.id, program.id);
const stopStartButton = (
<Button onClick={this.cancelOrRun} {...buttonStyle} positive={!running} negative={running}>

2
client/pages/LoginPage.tsx

@ -30,7 +30,7 @@ class LoginPageState { @@ -30,7 +30,7 @@ class LoginPageState {
login(appState: AppState) {
this.loading = true;
this.error = null;
appState.tokenStore.grantPassword(this.username, this.password)
appState.httpApi.grantPassword(this.username, this.password)
.then(() => {
this.loading = false;
log.info("logged in");

2
client/pages/LogoutPage.tsx

@ -7,7 +7,7 @@ export function LogoutPage() { @@ -7,7 +7,7 @@ export function LogoutPage() {
function consumeState(appState: AppState) {
appState.tokenStore.clear();
return (
<Redirect to="/" />
<Redirect to="/login" />
);
}

4
client/pages/ProgramPage.tsx

@ -6,7 +6,7 @@ import { RouteComponentProps } from "react-router"; @@ -6,7 +6,7 @@ import { RouteComponentProps } from "react-router";
import { Button, CheckboxProps, Form, Icon, Input, InputOnChangeData, Menu, Modal } from "semantic-ui-react";
import { ProgramSequenceView, ScheduleView } from "@client/components";
import * as rp from "@client/routePaths";
import * as route from "@client/routePaths";
import { AppState, injectState } from "@client/state";
import log from "@common/logger";
import { Program, SprinklersDevice } from "@common/sprinklersRpc";
@ -189,7 +189,7 @@ class ProgramPage extends React.Component<ProgramPageProps> { @@ -189,7 +189,7 @@ class ProgramPage extends React.Component<ProgramPageProps> {
private close = () => {
const { deviceId } = this.props.match.params;
this.props.history.push({ pathname: rp.device(deviceId), search: "" });
this.props.history.push({ pathname: route.device(deviceId), search: "" });
}
private onNameChange = (e: any, p: InputOnChangeData) => {

7
client/sprinklersRpc/WebSocketRpcClient.ts

@ -105,7 +105,6 @@ export class WebSocketRpcClient implements s.SprinklersRPC { @@ -105,7 +105,6 @@ export class WebSocketRpcClient implements s.SprinklersRPC {
}
start() {
log.debug({ url: this.webSocketUrl }, "connecting to websocket");
this._connect();
}
@ -222,6 +221,12 @@ export class WebSocketRpcClient implements s.SprinklersRPC { @@ -222,6 +221,12 @@ export class WebSocketRpcClient implements s.SprinklersRPC {
}
private _connect() {
if (this.socket != null &&
(this.socket.readyState === WebSocket.CLOSED)) {
this.tryAuthenticate();
return;
}
log.debug({ url: this.webSocketUrl }, "connecting to websocket");
this.socket = new WebSocket(this.webSocketUrl);
this.socket.onopen = this.onOpen.bind(this);
this.socket.onclose = this.onClose.bind(this);

24
client/state/AppState.ts

@ -25,13 +25,23 @@ export default class AppState { @@ -25,13 +25,23 @@ export default class AppState {
async start() {
syncHistoryWithStore(this.history, this.routerStore);
this.tokenStore.loadLocalStorage();
if (!this.httpApi.tokenStore.accessToken.isValid) {
if (this.httpApi.tokenStore.refreshToken.isValid) {
await this.checkToken();
await this.sprinklersRpc.start();
}
async checkToken() {
const { tokenStore: { accessToken, refreshToken } } = this.httpApi;
if (accessToken.isValid) { // if the access token is valid, we are good
return;
}
if (!refreshToken.isValid) { // if the refresh token is not valid, need to login again
this.history.push("/login");
return;
}
try {
await this.httpApi.tokenStore.grantRefresh();
await this.httpApi.grantRefresh();
} catch (err) {
if (err instanceof ApiError && err.code === ErrorCode.BadToken) {
log.warn({ err }, "refresh is bad for some reason, erasing");
@ -42,11 +52,5 @@ export default class AppState { @@ -42,11 +52,5 @@ export default class AppState {
// TODO: some kind of error page?
}
}
} else {
this.history.push("/login");
}
}
this.sprinklersRpc.start();
}
}

35
client/state/HttpApi.ts

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
import { TokenStore } from "@client/state/TokenStore";
import ApiError from "@common/ApiError";
import { ErrorCode } from "@common/ErrorCode";
import { TokenGrantPasswordRequest, TokenGrantRefreshRequest, TokenGrantResponse } from "@common/httpApi";
import log from "@common/logger";
export { ApiError };
@ -22,7 +24,7 @@ export default class HttpApi { @@ -22,7 +24,7 @@ export default class HttpApi {
}
this.baseUrl = baseUrl;
this.tokenStore = new TokenStore(this);
this.tokenStore = new TokenStore();
}
async makeRequest(url: string, options?: RequestInit, body?: any): Promise<any> {
@ -49,4 +51,35 @@ export default class HttpApi { @@ -49,4 +51,35 @@ export default class HttpApi {
return responseBody;
}
async grantPassword(username: string, password: string) {
const request: TokenGrantPasswordRequest = {
grant_type: "password", username, password,
};
const response: TokenGrantResponse = await this.makeRequest("/token/grant", {
method: "POST",
}, request);
this.tokenStore.accessToken.token = response.access_token;
this.tokenStore.refreshToken.token = response.refresh_token;
this.tokenStore.saveLocalStorage();
const { accessToken } = this.tokenStore;
log.debug({ aud: accessToken.claims!.aud }, "got password grant tokens");
}
async grantRefresh() {
const { refreshToken } = this.tokenStore;
if (!refreshToken.isValid) {
throw new ApiError("can not grant refresh with invalid refresh_token");
}
const request: TokenGrantRefreshRequest = {
grant_type: "refresh", refresh_token: refreshToken.token!,
};
const response: TokenGrantResponse = await this.makeRequest("/token/grant", {
method: "POST",
}, request);
this.tokenStore.accessToken.token = response.access_token;
this.tokenStore.refreshToken.token = response.refresh_token;
this.tokenStore.saveLocalStorage();
const { accessToken } = this.tokenStore;
log.debug({ aud: accessToken.claims!.aud }, "got refresh grant tokens");
}
}

40
client/state/TokenStore.ts

@ -1,11 +1,6 @@ @@ -1,11 +1,6 @@
import { observable } from "mobx";
import HttpApi, { ApiError } from "@client/state/HttpApi";
import { Token } from "@client/state/Token";
import { TokenGrantPasswordRequest, TokenGrantRefreshRequest, TokenGrantResponse } from "@common/httpApi";
import logger from "@common/logger";
const log = logger.child({ source: "TokenStore"});
const LOCAL_STORAGE_KEY = "TokenStore";
@ -13,12 +8,6 @@ export class TokenStore { @@ -13,12 +8,6 @@ export class TokenStore {
@observable accessToken: Token = new Token();
@observable refreshToken: Token = new Token();
private api: HttpApi;
constructor(api: HttpApi) {
this.api = api;
}
clear() {
this.accessToken.token = null;
this.refreshToken.token = null;
@ -37,35 +26,6 @@ export class TokenStore { @@ -37,35 +26,6 @@ export class TokenStore {
}
}
async grantPassword(username: string, password: string) {
const request: TokenGrantPasswordRequest = {
grant_type: "password", username, password,
};
const response: TokenGrantResponse = await this.api.makeRequest("/token/grant", {
method: "POST",
}, request);
this.accessToken.token = response.access_token;
this.refreshToken.token = response.refresh_token;
this.saveLocalStorage();
log.debug({ aud: this.accessToken.claims!.aud }, "got password grant tokens");
}
async grantRefresh() {
if (!this.refreshToken.isValid) {
throw new ApiError("can not grant refresh with invalid refresh_token");
}
const request: TokenGrantRefreshRequest = {
grant_type: "refresh", refresh_token: this.refreshToken.token!,
};
const response: TokenGrantResponse = await this.api.makeRequest("/token/grant", {
method: "POST",
}, request);
this.accessToken.token = response.access_token;
this.refreshToken.token = response.refresh_token;
this.saveLocalStorage();
log.debug({ aud: this.accessToken.claims!.aud }, "got refresh grant tokens");
}
toJSON() {
return { accessToken: this.accessToken.toJSON(), refreshToken: this.refreshToken.toJSON() };
}

Loading…
Cancel
Save