From 807f805f498c62f94780476805f4d76280c26a05 Mon Sep 17 00:00:00 2001 From: Yana Pechenenko Date: Wed, 18 Mar 2026 18:27:43 +0100 Subject: [PATCH 1/4] fix main function --- package-lock.json | 3 -- task-1/cocktail.js | 70 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65aa212..a26d152 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,6 @@ "": { "name": "core-assignment-week-10", "version": "1.0.0", - "hasInstallScript": true, "license": "CC BY-NC-SA 4.0", "dependencies": { "@inquirer/prompts": "^7.0.0", @@ -1585,7 +1584,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -1822,7 +1820,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", diff --git a/task-1/cocktail.js b/task-1/cocktail.js index 6fd5a62..131bcd9 100644 --- a/task-1/cocktail.js +++ b/task-1/cocktail.js @@ -1,14 +1,66 @@ // API documentation: https://www.thecocktaildb.com/api.php -import path from 'path'; +import path from "path"; +import fsPromises from "node:fs/promises"; -const BASE_URL = 'https://www.thecocktaildb.com/api/json/v1/1'; +const BASE_URL = "https://www.thecocktaildb.com/api/json/v1/1"; // Add helper functions as needed here +function generateMarkdownContent(data) { + if (!data.drinks) { + return `Sorry, we haven't recipe for this cocktail +`; + } + + const cocktails = data.drinks.map((drink) => { + const heading = `## ${drink.strDrink}`; + + const thumbnailImage = `![${drink.strDrink}](${drink.strDrinkThumb}/medium)`; + + const category = `**Category**: ${drink.strCategory}`; + + const idAlcoholic = + drink.strAlcoholic === "Alcoholic" + ? "**Alcoholic**: Yes" + : "**Alcoholic**: No"; + + const listOfIngredients = cocktailIngredients(drink) + .map((ingredient) => `- ${ingredient}`) + .join("\n"); + + const instructions = `### Instructions\n\n${drink.strInstructions}`; + + const serveIn = `Serve in: ${drink.strGlass}`; + + const drinkOutput = `${heading}\n\n${thumbnailImage}\n\n${category}\n\n${idAlcoholic}\n\n### Ingredients\n\n${listOfIngredients}\n\n${instructions}\n\n${serveIn}\n\n`; + + return drinkOutput; + }); + + return cocktails.join("\n"); +} + +function cocktailIngredients(drink) { + if (!drink) return; + + let ingredients = []; + + for (let i = 1; i <= 15; i++) { + const ingredient = drink[`strIngredient${i}`]; + const measure = drink[`strMeasure${i}`]; + + if (!ingredient) break; + + ingredients.push(`${measure || ""} ${ingredient}`.trim()); + } + + return ingredients; +} + export async function main() { if (process.argv.length < 3) { - console.error('Please provide a cocktail name as a command line argument.'); + console.error("Please provide a cocktail name as a command line argument."); return; } @@ -22,8 +74,18 @@ export async function main() { // 1. Fetch data from the API at the given URL // 2. Generate markdown content to match the examples // 3. Write the generated content to a markdown file as given by outPath + + const response = await fetch(url); + + if (!response.ok) { + throw new Error(`Request failed with status ${response.status}`); + } + + const data = await response.json(); + const markdownContent = generateMarkdownContent(data); + await fsPromises.writeFile(outPath, markdownContent); } catch (error) { - // 4. Handle errors + console.error(`Something went wrong: ${error.message}`); } } From 884dbcfb2eedb5989daf9cf2ced7ef5580de60db Mon Sep 17 00:00:00 2001 From: Yana Pechenenko Date: Wed, 18 Mar 2026 19:02:16 +0100 Subject: [PATCH 2/4] finish task-1 --- task-1/cocktail.js | 16 +++++++++------- task-1/output/margarita.md | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 task-1/output/margarita.md diff --git a/task-1/cocktail.js b/task-1/cocktail.js index 131bcd9..183dc42 100644 --- a/task-1/cocktail.js +++ b/task-1/cocktail.js @@ -8,11 +8,6 @@ const BASE_URL = "https://www.thecocktaildb.com/api/json/v1/1"; // Add helper functions as needed here function generateMarkdownContent(data) { - if (!data.drinks) { - return `Sorry, we haven't recipe for this cocktail -`; - } - const cocktails = data.drinks.map((drink) => { const heading = `## ${drink.strDrink}`; @@ -52,7 +47,7 @@ function cocktailIngredients(drink) { if (!ingredient) break; - ingredients.push(`${measure || ""} ${ingredient}`.trim()); + ingredients.push(`${measure || ""}${ingredient}`.trim()); } return ingredients; @@ -82,7 +77,14 @@ export async function main() { } const data = await response.json(); - const markdownContent = generateMarkdownContent(data); + + if (!data.drinks) { + console.error("No cocktails found with that name."); + return; + } + + const markdownContent = `# Cocktail Recipes\n\n${generateMarkdownContent(data)}`; + await fsPromises.writeFile(outPath, markdownContent); } catch (error) { console.error(`Something went wrong: ${error.message}`); diff --git a/task-1/output/margarita.md b/task-1/output/margarita.md new file mode 100644 index 0000000..d33d005 --- /dev/null +++ b/task-1/output/margarita.md @@ -0,0 +1,23 @@ +# Cocktail Recipes + +## Margarita + +![Margarita](https://www.thecocktaildb.com/images/media/drink/5noda61589575158.jpg/medium) + +**Category**: Ordinary Drink + +**Alcoholic**: Yes + +### Ingredients + +- 1 1/2 oz Tequila +- 1/2 oz Triple sec +- 1 oz Lime juice +- Salt + +### Instructions + +Rub the rim of the glass. + +Serve in: Cocktail glass + From 97648e2c36abc4445e1a12664c8e0081ae684e54 Mon Sep 17 00:00:00 2001 From: Yana Pechenenko Date: Wed, 18 Mar 2026 21:18:24 +0100 Subject: [PATCH 3/4] finish task-2 --- task-2/post-cli/src/services.js | 146 +++++++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 12 deletions(-) diff --git a/task-2/post-cli/src/services.js b/task-2/post-cli/src/services.js index 2601f57..2ca7570 100644 --- a/task-2/post-cli/src/services.js +++ b/task-2/post-cli/src/services.js @@ -1,5 +1,5 @@ // Change base URL for API requests to the local IP of the Post Central API server -const BASE_URL = 'http://localhost:3000'; +const BASE_URL = "http://localhost:3000"; // ============================================================================ // AUTH TOKEN - Stored after login/register, sent with every request @@ -37,7 +37,7 @@ const getHello = async () => { const response = await fetch(`${BASE_URL}/posts/hello`); if (!response.ok) { throw new Error( - `Failed to get hello: HTTP ${response.status} ${response.statusText}` + `Failed to get hello: HTTP ${response.status} ${response.statusText}`, ); } return await response.json(); @@ -53,7 +53,25 @@ const getHello = async () => { * Response: { user: string } */ const getMe = async () => { - // TODO + try { + const token = getToken(); + const response = await fetch(`${BASE_URL}/users/me`, { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (!response.ok) { + throw new Error( + `Failed to getMe : HTTP ${response.status} ${response.statusText}`, + ); + } + + return await response.json(); + } catch (error) { + throw new Error(`Error: ${error.message}`); + } }; // ============================================================================ @@ -68,15 +86,15 @@ const getMe = async () => { */ const createUser = async (name, password) => { const response = await fetch(`${BASE_URL}/users/register`, { - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: JSON.stringify({ name, password }), }); if (!response.ok) { throw new Error( - `Failed to create user: HTTP ${response.status} ${response.statusText}` + `Failed to create user: HTTP ${response.status} ${response.statusText}`, ); } return await response.json(); @@ -89,7 +107,23 @@ const createUser = async (name, password) => { * Response: { user: string, token: string } */ const loginUser = async (name, password) => { - // TODO + try { + const response = await fetch(`${BASE_URL}/users/login`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ name, password }), + }); + + if (!response.ok) { + throw new Error( + `Failed to login user: HTTP ${response.status} ${response.statusText}`, + ); + } + + return await response.json(); + } catch (error) { + throw new Error(`Error: ${error.message}`); + } }; // ============================================================================ @@ -103,7 +137,27 @@ const loginUser = async (name, password) => { * Response: { id: number, text: string, user: string } */ const createPost = async (text) => { - // TODO + try { + const token = getToken(); + + const response = await fetch(`${BASE_URL}/posts`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ text }), + }); + if (!response.ok) { + throw new Error( + `Failed to create a post: HTTP ${response.status} ${response.statusText}`, + ); + } + + return await response.json(); + } catch (error) { + throw new Error(`Error: ${error.message}`); + } }; /** @@ -112,7 +166,23 @@ const createPost = async (text) => { * Response: Array of { id, text, user } */ const getPosts = async () => { - // TODO + try { + const token = getToken(); + const response = await fetch(`${BASE_URL}/posts/me`, { + method: "GET", + headers: { Authorization: `Bearer ${token}` }, + }); + + if (!response.ok) { + throw new Error( + `Failed to get posts: HTTP ${response.status} ${response.statusText}`, + ); + } + + return await response.json(); + } catch (error) { + throw new Error(`Error: ${error.message}`); + } }; /** @@ -122,7 +192,27 @@ const getPosts = async () => { * Response: { id: number, text: string } */ const updatePost = async (id, text) => { - // TODO + try { + const token = getToken(); + const response = await fetch(`${BASE_URL}/posts/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ text }), + }); + + if (!response.ok) { + throw new Error( + `Failed to update post: HTTP ${response.status} ${response.statusText}`, + ); + } + + return await response.json(); + } catch (error) { + throw new Error(`Error: ${error.message}`); + } }; /** @@ -131,7 +221,23 @@ const updatePost = async (id, text) => { * Response: { user: string, message: string } */ const deleteUser = async () => { - // TODO + try { + const token = getToken(); + const response = await fetch(`${BASE_URL}/users/me`, { + method: "DELETE", + headers: { Authorization: `Bearer ${token}` }, + }); + + if (!response.ok) { + throw new Error( + `Failed to delete user: HTTP ${response.status} ${response.statusText}`, + ); + } + + return await response.json(); + } catch (error) { + throw new Error(`Error: ${error.message}`); + } }; /** @@ -140,7 +246,23 @@ const deleteUser = async () => { * Response: { id: number, text: string, message: string } */ const deletePost = async (id) => { - // TODO + try { + const token = getToken(); + const response = await fetch(`${BASE_URL}/posts/${id}`, { + method: "DELETE", + headers: { Authorization: `Bearer ${token}` }, + }); + + if (!response.ok) { + throw new Error( + `Failed to delete post: HTTP ${response.status} ${response.statusText}`, + ); + } + + return await response.json(); + } catch (error) { + throw new Error(`Error: ${error.message}`); + } }; // ============================================================================ From 1d8df02eb0a46ff55e76fdc140ba36366f29b7c5 Mon Sep 17 00:00:00 2001 From: Yana Pechenenko Date: Thu, 26 Mar 2026 17:11:21 +0100 Subject: [PATCH 4/4] change functions for task 1 and task 2 --- task-1/cocktail.js | 113 +++++++++++------ task-1/output/margarita.md | 1 - task-2/post-cli/src/services.js | 212 +++++++++++--------------------- 3 files changed, 143 insertions(+), 183 deletions(-) diff --git a/task-1/cocktail.js b/task-1/cocktail.js index 183dc42..f7c85fc 100644 --- a/task-1/cocktail.js +++ b/task-1/cocktail.js @@ -7,33 +7,30 @@ const BASE_URL = "https://www.thecocktaildb.com/api/json/v1/1"; // Add helper functions as needed here -function generateMarkdownContent(data) { - const cocktails = data.drinks.map((drink) => { - const heading = `## ${drink.strDrink}`; - - const thumbnailImage = `![${drink.strDrink}](${drink.strDrinkThumb}/medium)`; - - const category = `**Category**: ${drink.strCategory}`; - - const idAlcoholic = - drink.strAlcoholic === "Alcoholic" - ? "**Alcoholic**: Yes" - : "**Alcoholic**: No"; - - const listOfIngredients = cocktailIngredients(drink) - .map((ingredient) => `- ${ingredient}`) - .join("\n"); +function formatHeading(drink) { + return `## ${drink.strDrink}`; +} - const instructions = `### Instructions\n\n${drink.strInstructions}`; +function formatThumbnail(drink) { + return `![${drink.strDrink}](${drink.strDrinkThumb}/medium)`; +} - const serveIn = `Serve in: ${drink.strGlass}`; +function formatCategory(drink) { + return `**Category**: ${drink.strCategory}`; +} - const drinkOutput = `${heading}\n\n${thumbnailImage}\n\n${category}\n\n${idAlcoholic}\n\n### Ingredients\n\n${listOfIngredients}\n\n${instructions}\n\n${serveIn}\n\n`; +function formatAlcoholic(drink) { + return drink.strAlcoholic === "Alcoholic" + ? "**Alcoholic**: Yes" + : "**Alcoholic**: No"; +} - return drinkOutput; - }); +function formatInstructions(drink) { + return `### Instructions\n\n${drink.strInstructions}`; +} - return cocktails.join("\n"); +function formatServeIn(drink) { + return `Serve in: ${drink.strGlass}`; } function cocktailIngredients(drink) { @@ -53,6 +50,55 @@ function cocktailIngredients(drink) { return ingredients; } +function generateMarkdownContent(data) { + return data.drinks + .map((drink) => { + const ingredients = cocktailIngredients(drink) + .map((ingredient) => `- ${ingredient}`) + .join("\n"); + + return [ + formatHeading(drink), + "", + formatThumbnail(drink), + "", + formatCategory(drink), + "", + formatAlcoholic(drink), + "", + "### Ingredients", + "", + ingredients, + "", + formatInstructions(drink), + "", + formatServeIn(drink), + "", + ].join("\n"); + }) + .join("\n"); +} + +async function fetchCocktailData(url) { + const response = await fetch(url); + + if (!response.ok) { + throw new Error(`Request failed with status ${response.status}`); + } + + return await response.json(); +} + +function validateCocktailData(data) { + if (!data.drinks) { + throw new Error("No cocktails found with that name."); + } +} + +async function saveMarkdown(path, content) { + await fsPromises.writeFile(path, content); +} + export async function main() { if (process.argv.length < 3) { console.error("Please provide a cocktail name as a command line argument."); @@ -66,26 +112,11 @@ export async function main() { const outPath = path.join(__dirname, `./output/${cocktailName}.md`); try { - // 1. Fetch data from the API at the given URL - // 2. Generate markdown content to match the examples - // 3. Write the generated content to a markdown file as given by outPath - - const response = await fetch(url); - - if (!response.ok) { - throw new Error(`Request failed with status ${response.status}`); - } - - const data = await response.json(); - - if (!data.drinks) { - console.error("No cocktails found with that name."); - return; - } - - const markdownContent = `# Cocktail Recipes\n\n${generateMarkdownContent(data)}`; + const data = await fetchCocktailData(url); + validateCocktailData(data); - await fsPromises.writeFile(outPath, markdownContent); + const markdown = `# Cocktail Recipes\n\n${generateMarkdownContent(data)}`; + await saveMarkdown(outPath, markdown); } catch (error) { console.error(`Something went wrong: ${error.message}`); } diff --git a/task-1/output/margarita.md b/task-1/output/margarita.md index d33d005..eb835ca 100644 --- a/task-1/output/margarita.md +++ b/task-1/output/margarita.md @@ -20,4 +20,3 @@ Rub the rim of the glass. Serve in: Cocktail glass - diff --git a/task-2/post-cli/src/services.js b/task-2/post-cli/src/services.js index 2ca7570..cfabf8e 100644 --- a/task-2/post-cli/src/services.js +++ b/task-2/post-cli/src/services.js @@ -1,6 +1,30 @@ // Change base URL for API requests to the local IP of the Post Central API server const BASE_URL = "http://localhost:3000"; +const friendlyMessages = { + 400: "Bed request - check your input", + 401: "You must be logged in", + 403: "You can modify only your own posts", + 404: "Resource not found", + 500: "Server error - try again later", +}; + +function getFriendlyErrorMessage(status, defaultText) { + return friendlyMessages[status] || defaultText; +} + +async function apiRequest(endpoint, options = {}) { + const response = await fetch(`${BASE_URL}${endpoint}`, options); + + if (!response.ok) { + throw new Error( + `${response.status} ${getFriendlyErrorMessage(response.status, response.statusText)}`, + ); + } + + return await response.json(); +} + // ============================================================================ // AUTH TOKEN - Stored after login/register, sent with every request // ============================================================================ @@ -34,13 +58,7 @@ const getToken = () => authToken; * Response: { id: number, user: string, text: string, timestamp: string } */ const getHello = async () => { - const response = await fetch(`${BASE_URL}/posts/hello`); - if (!response.ok) { - throw new Error( - `Failed to get hello: HTTP ${response.status} ${response.statusText}`, - ); - } - return await response.json(); + return apiRequest("/posts/hello"); }; // ============================================================================ @@ -53,25 +71,14 @@ const getHello = async () => { * Response: { user: string } */ const getMe = async () => { - try { - const token = getToken(); - const response = await fetch(`${BASE_URL}/users/me`, { - method: "GET", - headers: { - Authorization: `Bearer ${token}`, - }, - }); - - if (!response.ok) { - throw new Error( - `Failed to getMe : HTTP ${response.status} ${response.statusText}`, - ); - } + const token = getToken(); - return await response.json(); - } catch (error) { - throw new Error(`Error: ${error.message}`); - } + return apiRequest("/users/me", { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + }, + }); }; // ============================================================================ @@ -85,19 +92,13 @@ const getMe = async () => { * Response: { user: string, token: string } */ const createUser = async (name, password) => { - const response = await fetch(`${BASE_URL}/users/register`, { + return apiRequest("/users/register", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ name, password }), }); - if (!response.ok) { - throw new Error( - `Failed to create user: HTTP ${response.status} ${response.statusText}`, - ); - } - return await response.json(); }; /** @@ -107,23 +108,11 @@ const createUser = async (name, password) => { * Response: { user: string, token: string } */ const loginUser = async (name, password) => { - try { - const response = await fetch(`${BASE_URL}/users/login`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ name, password }), - }); - - if (!response.ok) { - throw new Error( - `Failed to login user: HTTP ${response.status} ${response.statusText}`, - ); - } - - return await response.json(); - } catch (error) { - throw new Error(`Error: ${error.message}`); - } + return apiRequest("/users/login", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ name, password }), + }); }; // ============================================================================ @@ -137,27 +126,15 @@ const loginUser = async (name, password) => { * Response: { id: number, text: string, user: string } */ const createPost = async (text) => { - try { - const token = getToken(); - - const response = await fetch(`${BASE_URL}/posts`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify({ text }), - }); - if (!response.ok) { - throw new Error( - `Failed to create a post: HTTP ${response.status} ${response.statusText}`, - ); - } - - return await response.json(); - } catch (error) { - throw new Error(`Error: ${error.message}`); - } + const token = getToken(); + return apiRequest("/posts", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ text }), + }); }; /** @@ -166,23 +143,11 @@ const createPost = async (text) => { * Response: Array of { id, text, user } */ const getPosts = async () => { - try { - const token = getToken(); - const response = await fetch(`${BASE_URL}/posts/me`, { - method: "GET", - headers: { Authorization: `Bearer ${token}` }, - }); - - if (!response.ok) { - throw new Error( - `Failed to get posts: HTTP ${response.status} ${response.statusText}`, - ); - } - - return await response.json(); - } catch (error) { - throw new Error(`Error: ${error.message}`); - } + const token = getToken(); + return apiRequest("/posts/me", { + method: "GET", + headers: { Authorization: `Bearer ${token}` }, + }); }; /** @@ -192,27 +157,16 @@ const getPosts = async () => { * Response: { id: number, text: string } */ const updatePost = async (id, text) => { - try { - const token = getToken(); - const response = await fetch(`${BASE_URL}/posts/${id}`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify({ text }), - }); + const token = getToken(); - if (!response.ok) { - throw new Error( - `Failed to update post: HTTP ${response.status} ${response.statusText}`, - ); - } - - return await response.json(); - } catch (error) { - throw new Error(`Error: ${error.message}`); - } + return apiRequest(`/posts/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ text }), + }); }; /** @@ -221,23 +175,11 @@ const updatePost = async (id, text) => { * Response: { user: string, message: string } */ const deleteUser = async () => { - try { - const token = getToken(); - const response = await fetch(`${BASE_URL}/users/me`, { - method: "DELETE", - headers: { Authorization: `Bearer ${token}` }, - }); - - if (!response.ok) { - throw new Error( - `Failed to delete user: HTTP ${response.status} ${response.statusText}`, - ); - } - - return await response.json(); - } catch (error) { - throw new Error(`Error: ${error.message}`); - } + const token = getToken(); + return apiRequest("/users/me", { + method: "DELETE", + headers: { Authorization: `Bearer ${token}` }, + }); }; /** @@ -246,23 +188,11 @@ const deleteUser = async () => { * Response: { id: number, text: string, message: string } */ const deletePost = async (id) => { - try { - const token = getToken(); - const response = await fetch(`${BASE_URL}/posts/${id}`, { - method: "DELETE", - headers: { Authorization: `Bearer ${token}` }, - }); - - if (!response.ok) { - throw new Error( - `Failed to delete post: HTTP ${response.status} ${response.statusText}`, - ); - } - - return await response.json(); - } catch (error) { - throw new Error(`Error: ${error.message}`); - } + const token = getToken(); + return apiRequest(`/posts/${id}`, { + method: "DELETE", + headers: { Authorization: `Bearer ${token}` }, + }); }; // ============================================================================