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..38acb12 100644 --- a/task-1/cocktail.js +++ b/task-1/cocktail.js @@ -1,14 +1,45 @@ -// API documentation: https://www.thecocktaildb.com/api.php +import path from "path"; +import fs from "fs/promises"; -import path from 'path'; +const BASE_URL = "https://www.thecocktaildb.com/api/json/v1/1"; -const BASE_URL = 'https://www.thecocktaildb.com/api/json/v1/1'; +function formatDrinkToMarkdown(drink) { + let markdown = `## ${drink.strDrink}\n\n`; -// Add helper functions as needed here + markdown += `![${drink.strDrink}](${drink.strDrinkThumb}/medium)\n\n`; + + let isAlcoholic = "No"; + if (drink.strAlcoholic === "Alcoholic") { + isAlcoholic = "Yes"; + } + + markdown += `**Category**: ${drink.strCategory}\n\n`; + markdown += `**Alcoholic**: ${isAlcoholic}\n\n`; + + markdown += `### Ingredients\n\n`; + + for (let i = 1; i <= 15; i++) { + const ingredient = drink["strIngredient" + i]; + const measure = drink["strMeasure" + i]; + + if (ingredient !== null && ingredient !== "") { + let displayMeasure = ""; + if (measure !== null && measure !== "") { + displayMeasure = measure.trim() + " "; + } + markdown += "- " + displayMeasure + ingredient + "\n"; + } + } + + markdown += `\n### Instructions\n\n${drink.strInstructions}\n\n`; + markdown += `Serve in: ${drink.strGlass}\n\n`; + + return markdown; +} 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; } @@ -19,15 +50,31 @@ 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(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + if (data.drinks === null) { + console.error("No cocktails found with that name."); + return; + } + + let markdownContent = "# Cocktail Recipes\n\n"; + + for (const drink of data.drinks) { + markdownContent += formatDrinkToMarkdown(drink); + } + + await fs.writeFile(outPath, markdownContent); + console.log(`Markdown file created at: ${outPath}`); } catch (error) { - // 4. Handle errors + console.error(`Error: ${error.message}`); } } -// Do not change the code below if (!process.env.VITEST) { main(); } 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 + diff --git a/task-2/post-cli/package-lock.json b/task-2/post-cli/package-lock.json index 8c03ed4..8071efc 100644 --- a/task-2/post-cli/package-lock.json +++ b/task-2/post-cli/package-lock.json @@ -7,8 +7,7 @@ "": { "name": "post-central-cli", "version": "1.0.0", - "hasInstallScript": true, - "license": "ISC", + "license": "CC BY-NC-SA 4.0", "dependencies": { "@inquirer/prompts": "^7.0.0", "chalk": "^5.6.2", @@ -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-2/post-cli/src/services.js b/task-2/post-cli/src/services.js index 2601f57..9386c01 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 @@ -34,13 +34,18 @@ const getToken = () => authToken; * Response: { id: number, user: string, text: string, timestamp: string } */ const getHello = async () => { - const response = await fetch(`${BASE_URL}/posts/hello`); + const response = await fetch(`${BASE_URL}/posts/hello`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + const data = await response.json().catch(() => ({})); if (!response.ok) { - throw new Error( - `Failed to get hello: HTTP ${response.status} ${response.statusText}` - ); + throw new Error(data.message || `Failed to get hello: ${response.status}`); } - return await response.json(); + + return data; }; // ============================================================================ @@ -53,7 +58,21 @@ const getHello = async () => { * Response: { user: string } */ const getMe = async () => { - // TODO + const response = await fetch(`${BASE_URL}/users/me`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getToken()}`, + }, + }); + const data = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error( + data.message || `Failed to get user info: ${response.status}`, + ); + } + + return data; }; // ============================================================================ @@ -68,18 +87,17 @@ 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 }), }); + const data = await response.json().catch(() => ({})); if (!response.ok) { - throw new Error( - `Failed to create user: HTTP ${response.status} ${response.statusText}` - ); + throw new Error(data.message || "Could not register user"); } - return await response.json(); + return data; }; /** @@ -89,7 +107,18 @@ const createUser = async (name, password) => { * Response: { user: string, token: string } */ const loginUser = async (name, password) => { - // TODO + const response = await fetch(`${BASE_URL}/users/login`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ name, password }), + }); + const data = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(data.message || "Could not log in"); + } + return data; }; // ============================================================================ @@ -103,7 +132,19 @@ const loginUser = async (name, password) => { * Response: { id: number, text: string, user: string } */ const createPost = async (text) => { - // TODO + const response = await fetch(`${BASE_URL}/posts`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getToken()}`, + }, + body: JSON.stringify({ text }), + }); + const data = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(data.message || "Could not create post"); + } + return data; }; /** @@ -112,7 +153,18 @@ const createPost = async (text) => { * Response: Array of { id, text, user } */ const getPosts = async () => { - // TODO + const response = await fetch(`${BASE_URL}/posts/me`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getToken()}`, + }, + }); + const data = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(data.message || "Could not retrieve posts"); + } + return data; }; /** @@ -122,7 +174,19 @@ const getPosts = async () => { * Response: { id: number, text: string } */ const updatePost = async (id, text) => { - // TODO + const response = await fetch(`${BASE_URL}/posts/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getToken()}`, + }, + body: JSON.stringify({ text }), + }); + const data = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(data.message || "Could not update post"); + } + return data; }; /** @@ -131,7 +195,18 @@ const updatePost = async (id, text) => { * Response: { user: string, message: string } */ const deleteUser = async () => { - // TODO + const response = await fetch(`${BASE_URL}/users/me`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getToken()}`, + }, + }); + const data = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(data.message || "Could not delete user"); + } + return data; }; /** @@ -140,7 +215,18 @@ const deleteUser = async () => { * Response: { id: number, text: string, message: string } */ const deletePost = async (id) => { - // TODO + const response = await fetch(`${BASE_URL}/posts/${id}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getToken()}`, + }, + }); + const data = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(data.message || "Could not delete post"); + } + return data; }; // ============================================================================ diff --git a/task-2/post-cli/tests/test-crud.test.js b/task-2/post-cli/tests/test-crud.test.js index fbf7ea4..893880c 100644 --- a/task-2/post-cli/tests/test-crud.test.js +++ b/task-2/post-cli/tests/test-crud.test.js @@ -152,6 +152,7 @@ describe('Complete CRUD Operations', () => { fetchMock.mockResolvedValueOnce({ ok: true, status: 200, + json: async () => ({ id: 1, text: 'Updated post text!', message: 'Post deleted' }), }); // DELETE: Remove the post @@ -170,6 +171,7 @@ describe('Complete CRUD Operations', () => { fetchMock.mockResolvedValueOnce({ ok: true, status: 200, + json: async () => ({ user: 'Alice', message: 'User deleted' }), }); // DELETE: Remove the user @@ -218,6 +220,7 @@ describe('Complete CRUD Operations', () => { fetchMock.mockResolvedValueOnce({ ok: true, status: 200, + json: async () => ({ user: 'mock-user', message: 'User deleted' }), }); await deleteUser(); @@ -240,6 +243,7 @@ describe('Complete CRUD Operations', () => { fetchMock.mockResolvedValueOnce({ ok: true, status: 200, + json: async () => ({ id: 5, text: 'some text', message: 'Post deleted' }), }); await deletePost(5);