Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions chat-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.DS_Store
39 changes: 31 additions & 8 deletions chat-app/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,38 @@
# Write and Deploy Chat Application Frontend and Backend

### To start the program

First install dependencies:

```bash
npm install
```

From `/backend` directory:

Option 1. Production mode

```bash
npm start
```

Option 2. Development mode

```bash
npm run dev
```

### Link to the coursework

https://sdc.codeyourfuture.io/decomposition/sprints/2/prep/

You must complete and deploy a chat application. You have two weeks to complete this.

It must support at least the following requirements:
* As a user, I can send add a message to the chat.
* As a user, when I open the chat I see the messages that have been sent by any user.
* As a user, when someone sends a message, it gets added to what I see.

- As a user, I can send add a message to the chat.
- As a user, when I open the chat I see the messages that have been sent by any user.
- As a user, when someone sends a message, it gets added to what I see.

It must also support at least one additional feature.

Expand All @@ -27,8 +50,8 @@ Exploring and understanding different ways of sending information between a clie

### How to submit

* Fork the Module-Decomposition repository
* Develop and deploy your chat app
* Create a pull request back into the original Module-Decomposition repo, including:
* A link to the deployed frontend on the CYF hosting environment
* A link to the deployed backend on the CYF hosting environment
- Fork the Module-Decomposition repository
- Develop and deploy your chat app
- Create a pull request back into the original Module-Decomposition repo, including:
- A link to the deployed frontend on the CYF hosting environment
- A link to the deployed backend on the CYF hosting environment
114 changes: 114 additions & 0 deletions chat-app/backend/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import express from "express";
import cors from "cors";

const port = process.env.PORT || 3000;
// Auto-increment IDS
let nextMessageId = 1;
// In-memory message store, shared by all clients
const messages = [];
// Stores pending longPoll requests waiting for new messages
const callbacksForNewMessages = [];

// App setup
const app = express();
app.use(cors());
app.use(express.json());

// Routes
/**
* GET /
* Returns messages to client
*
* Query params:
* since - timestamps (ms). If provided, only messages newer than this are returned
* longPoll - If true and there are no new messages, holds the connection open
* until there are new messages instead of returning empty array.
*/
app.get("/", (req, res) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed this in my previous review. Could you use an AI to find out

Why /messages would make a better URI than / for a service that get and add messages?

let since = Number(req.query.since);
let longPoll = req.query.longPoll === "true";

if (since) {
const messagesToSend = messages.filter((msg) => msg.time > since);

// callback until a new message is posted
// No new messages and client wants long-polling - park the response
if (messagesToSend.length === 0 && longPoll) {
callbacksForNewMessages.push((val) => res.json(val));
return;
}
res.json(messagesToSend);
return;
}
// No "since" param, return the full message history
res.json(messages);
});

/**
* POST /
* Accepts a new message from a client.
* Trims whitespace from user and message before saving so that
* values like " hello " are stored as "hello".
*/
app.post("/", (req, res) => {
const { message, user } = req.body;
Comment thread
cjyuan marked this conversation as resolved.

if (
typeof message !== "string" ||
typeof user !== "string" ||
!message.trim() ||
!user.trim()
) {
res.status(400).json({ error: `Message and user are required!` });
return;
}

const newMessage = {
id: nextMessageId++,
message: message.trim(),
user: user.trim(),
time: Date.now(),
likes: 0,
dislikes: 0,
};

messages.push(newMessage);

// Resolve all parked long-poll clients with new messages
while (callbacksForNewMessages.length > 0) {
const callback = callbacksForNewMessages.pop();
callback([newMessage]);
}
res.status(201).json({ success: true });
});

/**
* POST /:id/:reaction
* Increments the like or dislike count on a message.
* Returns the updated message object.
*/
app.post("/:id/:reaction", (req, res) => {
const id = Number(req.params.id);
const reaction = req.params.reaction;

const message = messages.find((msg) => msg.id === id);
if (!message) {
res.status(404).json({ error: "Message not found" });
return;
}

if (reaction === "like") {
message.likes++;
} else if (reaction === "dislike") {
message.dislikes++;
} else {
res.status(400).json({ error: "Invalid reaction!" });
return;
}
res.json(message);
});
Comment on lines +90 to +109

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could consider rearrange the code this way to prevent unnecessary operations from being performed when input is invalid:

app.post("/:id/:reaction", (req, res) => {
  const id = Number(req.params.id);
  const reaction = req.params.reaction;

  // Validate id and reaction first

  // if reaction is invalid
  if (...) {
    res.status(400).json({ error: "Invalid reaction!" });
    return;
  }

  const message = messages.find((msg) => msg.id === id);
  if (!message) {
    res.status(404).json({ error: "Message not found" });
    return;
  }

  if (reaction === "like") {
    message.likes++;
  } else if (reaction === "dislike") {
    message.dislikes++;
  }

  res.json(message);
});


// start the server
app.listen(port, () => {
console.log(`Chat server listening on port ${port}`);
});
Loading
Loading