Skip to content
Open

task #182

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
110 changes: 105 additions & 5 deletions src/createServer.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,110 @@
'use strict';

const http = require('node:http');
const fs = require('node:fs');
const path = require('node:path');
const querystring = require('node:querystring');

const dataPath = path.resolve(__dirname, '../db/expense.json');

function createServer() {
/* Write your code here */
// Return instance of http.Server class
return http.createServer((req, res) => {
/* ---------- GET FORM ---------- */
if (req.method === 'GET' && req.url === '/') {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');

res.end(`
<h1>Add expense</h1>
<form method="POST" action="/add-expense">
<label>Date: <input name="date" type="date" required /></label><br />
<label>Title: <input name="title" type="text" required /></label><br />
<label>Amount: <input name="amount" type="number" required /></label><br />
<button type="submit">Save</button>
</form>
`);

return;
}

/* ---------- ONLY POST /add-expense ---------- */
if (req.method !== 'POST' || req.url !== '/add-expense') {

Choose a reason for hiding this comment

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

The requirements state that the application should show an HTML form. This server logic only accepts POST requests to /add-expense and returns a 404 Not Found for all other requests, including GET requests that would typically be used to serve the form.

res.statusCode = 404;
res.end('Not Found');

return;
}

const chunks = [];

req.on('data', (chunk) => chunks.push(chunk));

req.on('end', () => {
const body = Buffer.concat(chunks).toString('utf-8');

let expense;

try {
if (req.headers['content-type']?.includes('application/json')) {
expense = JSON.parse(body);
} else {
expense = querystring.parse(body);
}
} catch {
res.statusCode = 400;
res.end('<h2>Invalid data</h2>');

return;
}

if (!expense.date || !expense.title || !expense.amount) {
res.statusCode = 400;
res.setHeader('Content-Type', 'text/html');
res.end('<h2>Missing required fields</h2>');

return;
}

/* ---------- READ EXISTING FILE ---------- */
fs.readFile(dataPath, 'utf-8', (readErr, fileData) => {
let expenses = [];

if (!readErr && fileData) {
try {
expenses = JSON.parse(fileData);
} catch {
expenses = [];
}
}

if (!Array.isArray(expenses)) {
expenses = [];
}

expenses.push(expense);

/* ---------- WRITE UPDATED ARRAY ---------- */
fs.writeFile(
dataPath,
JSON.stringify(expenses, null, 2),
(writeErr) => {
if (writeErr) {
res.statusCode = 500;
res.end('<h2>Server error</h2>');

return;
}

/* ---------- HTML RESPONSE ---------- */
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');

res.end(JSON.stringify(expense, null, 2));

Choose a reason for hiding this comment

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

Great job setting the Content-Type header to text/html. However, the task requires returning an HTML page that contains the well-formatted JSON, not just the raw JSON string. To ensure the JSON is displayed correctly in the browser as specified, please wrap it in <pre> tags.

},
);
});
});
});
}

module.exports = {
createServer,
};
module.exports = { createServer };
4 changes: 2 additions & 2 deletions tests/formDataServer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('Form Data Server', () => {

const savedData = JSON.parse(fs.readFileSync(dataPath));

expect(savedData).toStrictEqual(expense);
expect(savedData).toStrictEqual([expense]);

Choose a reason for hiding this comment

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

This test correctly verifies that a single expense is saved. To fully confirm that the data overwriting issue from the previous review is fixed, consider adding another test case that checks if a new expense is appended to a file that already contains existing expenses.

});

it('should reject request without all params on "POST /submit-expense" request', async () => {
Expand Down Expand Up @@ -83,7 +83,7 @@ describe('Form Data Server', () => {
};
const response = await axios.post(`${HOST}/add-expense`, expense);

expect(response.headers['content-type']).toBe('application/json');
expect(response.headers['content-type']).toBe('text/html');
expect(response.data).toStrictEqual(expense);

Choose a reason for hiding this comment

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

This assertion doesn't align with the task requirements. The server is expected to return an HTML page containing the JSON (ideally wrapped in <pre> tags), so the response data will be a string. This test incorrectly expects a JavaScript object. It should be updated to verify that the response is the correct HTML string.

});

Expand Down