Skip to content

Commit

Permalink
Use strictNullChecks (#118)
Browse files Browse the repository at this point in the history
* Use strictNullChecks

* strictNullChecks in ui
  • Loading branch information
seanchas116 authored and minamorl committed Jul 22, 2016
1 parent ad02298 commit c773753
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 56 deletions.
2 changes: 1 addition & 1 deletion common/data.ts
Expand Up @@ -9,6 +9,6 @@ interface IMessage {
export
interface IUser {
name: string
iconUrl: string
iconUrl: string|null
connecting?: boolean
}
4 changes: 2 additions & 2 deletions server/api.ts
Expand Up @@ -14,12 +14,12 @@ app.get("/messages", async (req, res) => {
order: '"message"."id" DESC', // <- TODO: fix Sequelize
where, limit
})
const data = messages.map(m => messageToJSON(m, m.user)).reverse()
const data = messages.map(m => messageToJSON(m, m.user!)).reverse()
res.json(data)
})

app.get("/user", (req, res) => {
const user: User = req.user
const user: User|undefined = req.user
if (user) {
const json: IUser = {
name: user.name,
Expand Down
6 changes: 4 additions & 2 deletions server/auth.ts
Expand Up @@ -60,8 +60,10 @@ passport.use(new TwitterStrategy({
user = await User.create({name: profile.username});
integration = await TwitterIntegration.create({twitterId: id, userId: user.id});
}
const iconUrl = profile.photos[0].value
user.iconUrl = iconUrl.replace("_normal.", ".")
if (profile.photos) {
const iconUrl = profile.photos[0].value
user.iconUrl = iconUrl.replace("_normal.", ".")
}
await user.save()
done(null, user);
} catch (error) {
Expand Down
41 changes: 29 additions & 12 deletions server/events.ts
Expand Up @@ -10,28 +10,33 @@ export function setConnectionNumber(num: number) {
}

export
abstract class BaseReceiveEvent {
interface ReceiveEvent {
ev: SendEventType
constructor(protected user: IUser, protected value?: string) {
}
abstract prepare(): Promise<void>
abstract response(): Promise<string>

prepare(): Promise<void>
response(): Promise<string|undefined>
}
export
class WhoamiEvent extends BaseReceiveEvent{
class WhoamiEvent implements ReceiveEvent {
ev = SendEventType.WHOAMI

constructor(public user: IUser) {
}

async prepare() {
}
async response() {
return newMessage(this.ev, this.user)
}
}
export
class CreateMessageEvent extends BaseReceiveEvent{
class CreateMessageEvent implements ReceiveEvent {
ev = SendEventType.NEW_MESSAGE
message: Message

constructor(public user: IUser, public value: string) {
}

async prepare() {
const user = await User.findOne({
where: {
Expand All @@ -55,19 +60,27 @@ class CreateMessageEvent extends BaseReceiveEvent{
}
}
export
class DeleteMessageEvent extends BaseReceiveEvent{
class DeleteMessageEvent implements ReceiveEvent {
ev = SendEventType.DELETE_MESSAGE

constructor(public id: number) {
}

async prepare() {
await Message.destroy({where: {id: this.value}})
await Message.destroy({where: {id: this.id}})
}
async response() {
await this.prepare()
return newMessage(this.ev, this.value)
return newMessage(this.ev, this.id)
}
}
export
class JoinEvent extends BaseReceiveEvent {
class JoinEvent implements ReceiveEvent {
ev = SendEventType.USER_JOIN

constructor(public user: IUser) {
}

async prepare() {
}
async response() {
Expand All @@ -80,8 +93,12 @@ class JoinEvent extends BaseReceiveEvent {
}
}
export
class LeftEvent extends BaseReceiveEvent {
class LeftEvent implements ReceiveEvent {
ev = SendEventType.USER_LEAVE

constructor(public user: IUser) {
}

async prepare() {
}
async response() {
Expand Down
51 changes: 32 additions & 19 deletions server/models.ts
Expand Up @@ -5,13 +5,17 @@ const config = require("../../config/db")[process.env.NODE_ENV || "development"]
export let sequelize = new Sequelize(config.url)

interface MessageParams {
text?: string;
userId?: number;
id?: number;
text?: string
userId?: number
id?: number
createdAt?: Date
}
export interface Message extends Sequelize.Instance<MessageParams>, MessageParams {
user?: User;
export interface Message extends Sequelize.Instance<MessageParams> {
user?: User
text: string
userId: number
id: number
createdAt: Date
}

export let Message = sequelize.define<Message, {}>('message', {
Expand All @@ -35,10 +39,13 @@ interface UserParams {
id?: number
iconUrl?: string
}
export interface User extends Sequelize.Instance<UserParams>, UserParams {
messages?: Message[];
connections?: Connection[];
twitterIntegration?: TwitterIntegration;
export interface User extends Sequelize.Instance<UserParams> {
messages?: Message[]
connections?: Connection[]
twitterIntegration?: TwitterIntegration
name: string
id: number
iconUrl: string|null
}

export let User = sequelize.define<User, UserParams>('user', {
Expand All @@ -47,25 +54,31 @@ export let User = sequelize.define<User, UserParams>('user', {
})

interface ConnectionParams {
available?: boolean;
userId?: number;
id?: number;
available?: boolean
userId?: number
id?: number
}
export interface Connection extends Sequelize.Instance<ConnectionParams>, ConnectionParams {
user?: User;
export interface Connection extends Sequelize.Instance<ConnectionParams> {
user?: User
available: boolean
userId: number
id: number
}

export let Connection = sequelize.define<Connection, ConnectionParams>('connection', {
available: Sequelize.BOOLEAN
})

interface TwitterIntegrationParams {
twitterId?: string;
userId?: number;
id?: number;
twitterId?: string
userId?: number
id?: number
}
export interface TwitterIntegration extends Sequelize.Instance<TwitterIntegrationParams>, TwitterIntegrationParams {
user?: User;
export interface TwitterIntegration extends Sequelize.Instance<TwitterIntegrationParams> {
user?: User
twitterId: string
userId: number
id: number
}

export let TwitterIntegration = sequelize.define<TwitterIntegration, TwitterIntegrationParams>('twitterIntegration', {
Expand Down
25 changes: 16 additions & 9 deletions server/wsserver.ts
Expand Up @@ -5,7 +5,7 @@ import { Message, User, Connection } from "./models"
import { IMessage, IUser } from "../common/data";
import { app, server } from "./app";
import { ReceiveEventType, SendEventType } from "../common/eventType"
import { newMessage, WhoamiEvent, BaseReceiveEvent, JoinEvent, CreateMessageEvent, DeleteMessageEvent, LeftEvent } from "./events"
import { newMessage, WhoamiEvent, ReceiveEvent, JoinEvent, CreateMessageEvent, DeleteMessageEvent, LeftEvent } from "./events"

const expressWs = require('express-ws')(app, server);
const wss: WebSocket.Server = expressWs.getWss();
Expand All @@ -22,9 +22,9 @@ const broadcast = (message: string): void => {
}

app["ws"]("/", async (ws: WebSocket, req: express.Request) => {
let user: User = req.user
let userData: IUser
let connection: Connection
let user: User|undefined = req.user
let userData: IUser|undefined
let connection: Connection|undefined
// validate connection
if(!req.headers["origin"]) {
ws.close()
Expand Down Expand Up @@ -92,9 +92,9 @@ app["ws"]("/", async (ws: WebSocket, req: express.Request) => {
try {
let json = JSON.parse(undecoded_json)
let {ev, value} = json
let messageEvent: BaseReceiveEvent
let messageEvent: ReceiveEvent|undefined;

if(user) {
if (userData) {
if (ev === ReceiveEventType[ReceiveEventType.CREATE_MESSAGE]) {
if (messageCount < messageLimitPerHour) {
messageEvent = new CreateMessageEvent(userData, value)
Expand All @@ -107,19 +107,26 @@ app["ws"]("/", async (ws: WebSocket, req: express.Request) => {
ping_available = true
}
}
broadcast(await messageEvent.response())
if (messageEvent) {
const res = await messageEvent.response();
if (res) {
broadcast(res);
}
}
} catch(e) {
// failed
}
})

let onClose = async () => {
if(user) {
if (userData) {
let event = new LeftEvent(userData)
try {
broadcast(await event.response())
// destroy connection
await connection.destroy()
if (connection) {
await connection.destroy()
}
} catch(e) {
// failed
}
Expand Down
4 changes: 2 additions & 2 deletions test/test_events.ts
Expand Up @@ -60,7 +60,7 @@ describe("events", () => {

it("returns correct message", async () => {
const val = await event.response()
let parsed = JSON.parse(val)
let parsed = JSON.parse(val!)
const expected: IMessage = {
id: event.message.id,
text: message,
Expand All @@ -74,7 +74,7 @@ describe("events", () => {
})
})
describe("DeleteMessageEvent", () => {
let event = new events.DeleteMessageEvent(user, "1")
let event = new events.DeleteMessageEvent(1)
// override prepare methods for testing
let prepare_called: boolean
event.prepare = async () => { prepare_called = true }
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Expand Up @@ -4,6 +4,7 @@
"target": "es6",
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"strictNullChecks": true,
"sourceMap": true,
"outDir": "./lib"
},
Expand Down
2 changes: 1 addition & 1 deletion ui/src/index.tsx
Expand Up @@ -8,7 +8,7 @@ import * as ReactDOM from "react-dom";
import ThreadView from "./views/ThreadView";

window.addEventListener("DOMContentLoaded", () => {
ReactDOM.render(<ThreadView></ThreadView>, document.getElementById("app"));
ReactDOM.render(<ThreadView></ThreadView>, document.getElementById("app")!);
});

const {GA_TRACKING_ID} = process.env
Expand Down
8 changes: 4 additions & 4 deletions ui/src/thread.ts
Expand Up @@ -14,10 +14,10 @@ export
class Thread extends EventEmitter {
connetion = new ReconnectingWebSocket(WS_URL);
messages: IMessage[] = [];
latestMessage: IMessage = null;
latestMessage: IMessage|undefined
connectionCount = 0;
availableUsers: IUser[] = [];
currentUser: IUser = null;
currentUser: IUser|undefined
hasOlderMessages = true
fetchingOlderMessages = false

Expand Down Expand Up @@ -69,7 +69,7 @@ class Thread extends EventEmitter {
const response = await fetch(`${API_URL}/messages?limit=${MESSAGE_PER_PAGE}`);
const messages: IMessage[] = await response.json();
this.messages = messages;
this.latestMessage = null;
this.latestMessage = undefined
this.emit("messageAppend");
}

Expand All @@ -91,7 +91,7 @@ class Thread extends EventEmitter {
this.hasOlderMessages = false
} else {
this.messages.unshift(...messages)
this.latestMessage = null
this.latestMessage = undefined
this.emit("messagePrepend")
}
this.fetchingOlderMessages = false
Expand Down
8 changes: 4 additions & 4 deletions ui/src/views/ThreadView.tsx
Expand Up @@ -29,7 +29,7 @@ class UserList extends React.Component<{}, UserListState> {

render() {
const MAX_USERS = 10
const {users} = this.state;
const users = this.state.users!;
return (
<div className="user-list">
<ul>
Expand All @@ -50,7 +50,7 @@ class UserView extends React.Component<{}, UserLoginState> {
constructor() {
super();
this.state = {
user: null,
user: undefined,
loggedOut: false
};
auth.on("change", () => {
Expand Down Expand Up @@ -115,7 +115,7 @@ class MessageForm extends React.Component<{}, UserLoginState> {
constructor() {
super()
this.state = {
user: null,
user: undefined,
loggedOut: true
}
auth.on("change", () => {
Expand Down Expand Up @@ -231,7 +231,7 @@ class ThreadView extends React.Component<{}, ThreadViewState> {
}

render() {
const {messages} = this.state;
const messages = this.state.messages!;
return (
<div className="app-container" ref="appContainer">
<HeaderView />
Expand Down
1 change: 1 addition & 0 deletions ui/tsconfig.json
Expand Up @@ -4,6 +4,7 @@
"target": "es6",
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"strictNullChecks": true,
"sourceMap": false,
"jsx": "react",
"outDir": "./lib",
Expand Down

0 comments on commit c773753

Please sign in to comment.