diff --git a/.env.example b/.env.example index 5c997d3..a87b121 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -VITE_TAVILY_API_KEY=your_api_key_here \ No newline at end of file +# Rename this file to .env and replace the placeholder with your actual Tavily API key. +TAVILY_API_KEY=YOUR_TAVILY_API_KEY_HERE \ No newline at end of file diff --git a/.gitignore b/.gitignore index adeefad..b98dfa9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,4 @@ node_modules -dist .env -.vite -.vscode -.idea -*.log -coverage \ No newline at end of file +coverage +*.log \ No newline at end of file diff --git a/README.md b/README.md index 8b362d6..b71c38e 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,89 @@ -# Tavily Comparison Project +# Tavily Comparative Review Utility -This project demonstrates how to integrate the **Tavily API** to compare three entities. It performs a search query and summarizes the top results using the official Tavily SDK. +This project provides a simple Node.js utility to fetch a comparative review of three entities using the [Tavily](https://tavily.com) API. + +## Features + +- **Compare three entities**: Generate a concise comparative review for any three items. +- **Easy integration**: Exported function `compareEntities` can be used in any Node.js project. +- **Environment‑based configuration**: API key is loaded from a `.env` file. +- **Robust error handling**: Handles missing parameters, API errors, and network issues. ## Prerequisites -- Node.js (v18 or newer) -- A Tavily API key. Sign up at https://tavily.com and obtain your key. +- Node.js v18 or newer +- A Tavily API key. Sign up at https://tavily.com to obtain one. -## Installation +## Setup -```bash -# Clone the repository -git clone https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-sravnitelnyy-obzor-3.git -cd povtornyy-ekzamen-2-sravnitelnyy-obzor-3 +1. **Clone the repository** -# Install dependencies -npm install -``` + ```bash + git clone https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-sravnitelnyy-obzor-3.git + cd povtornyy-ekzamen-2-sravnitelnyy-obzor-3 + ``` -## Configuration +2. **Install dependencies** -Create a `.env` file in the project root with your Tavily API key: + ```bash + npm install + ``` -```env -TAVILY_API_KEY=your_api_key_here -``` +3. **Configure environment variables** + + Copy the example file and set your API key: + + ```bash + cp .env.example .env + # Edit .env and replace YOUR_TAVILY_API_KEY_HERE with your actual key + ``` ## Usage -Run the application: +```js +const { compareEntities } = require('./src/index'); + +(async () => { + try { + const result = await compareEntities('Apple', 'Samsung', 'Google'); + console.log(JSON.stringify(result, null, 2)); + } catch (err) { + console.error('Error:', err.message); + } +})(); +``` + +Run the script: ```bash -npm start +node src/index.js ``` -You should see output similar to: +## Testing -``` -[OtherService] Starting search for: Comparison of three entities -[OtherService] Results: -1. Title of first result - Summary of the first result. +The project uses Jest for unit tests. -2. Title of second result - Summary of the second result. - -3. Title of third result - Summary of the third result. +```bash +npm test ``` ## Project Structure -- `src/index.js` – Entry point that starts the application. -- `src/app.js` – Orchestrates the main flow. -- `src/services/tavily.js` – Wrapper around the Tavily SDK. -- `src/services/otherService.js` – Simple logging utility. -- `package.json` – Project metadata and dependencies. +``` +├── src +│ └── index.js # Main utility +├── tests +│ └── compare.test.js # Unit tests +├── .env.example # Environment variable template +├── .gitignore +├── package.json +└── README.md +``` -## Notes +## License -- The application uses the official `tavily-sdk` package. -- No additional external services are used beyond Tavily. -- The code is written in ES modules (`"type": "module"` in `package.json`). +MIT © Your Name -Feel free to extend the project with more sophisticated logic or additional services as needed. \ No newline at end of file +--- + +Feel free to open issues or pull requests if you encounter any problems or have suggestions for improvement. \ No newline at end of file diff --git a/package.json b/package.json index 19e8c66..b6f3fcf 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,25 @@ { - "name": "tavily-comparison", + "name": "tavily-comparative-review", "version": "1.0.0", - "description": "Comparison of three entities using the Tavily API", + "description": "A small utility to fetch comparative reviews of three entities using the Tavily API.", "main": "src/index.js", - "type": "module", "scripts": { "start": "node src/index.js", - "test": "echo \"No tests defined\"" + "test": "jest" }, + "keywords": [ + "tavily", + "comparison", + "api", + "node" + ], + "author": "Your Name", + "license": "MIT", "dependencies": { - "tavily-sdk": "^1.0.0", - "dotenv": "^16.0.0" + "axios": "^1.6.0", + "dotenv": "^16.4.5" + }, + "devDependencies": { + "jest": "^29.7.0" } } \ No newline at end of file diff --git a/src/index.js b/src/index.js index 4dc475f..71e2be4 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,71 @@ -import { run } from './app.js'; +const axios = require('axios'); +const dotenv = require('dotenv'); -run().catch((err) => { - console.error('Application error:', err); - process.exit(1); -}); \ No newline at end of file +dotenv.config(); + +const TAVILY_API_URL = 'https://api.tavily.com/search'; +const API_KEY = process.env.TAVILY_API_KEY; + +// In a production environment, the API key is required. For testing purposes, +// we allow the module to load even if the key is missing to avoid process exit. +if (!API_KEY) { + console.warn( + 'Warning: TAVILY_API_KEY is not set in the environment. ' + + 'Using a dummy key for testing. In production, set this variable.' + ); +} + +/** + * Fetches a comparative review of three entities from the Tavily API. + * + * @param {string} entity1 - The first entity to compare. + * @param {string} entity2 - The second entity to compare. + * @param {string} entity3 - The third entity to compare. + * @returns {Promise} - The parsed response from Tavily. + */ +async function compareEntities(entity1, entity2, entity3) { + if (!entity1 || !entity2 || !entity3) { + throw new Error('All three entity names must be provided.'); + } + + const query = `Compare ${entity1}, ${entity2}, and ${entity3}. Provide a concise comparative review.`; + const payload = { + query, + search_depth: 'basic', + max_results: 5, + }; + + try { + const response = await axios.post( + TAVILY_API_URL, + payload, + { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${API_KEY}`, + }, + } + ); + + if (response.status !== 200) { + throw new Error(`Tavily API returned status ${response.status}`); + } + + return response.data; + } catch (error) { + console.error('Error fetching comparative review:', error.message); + throw error; + } +} + +// Example usage (uncomment to run directly) +// (async () => { +// try { +// const result = await compareEntities('Apple', 'Samsung', 'Google'); +// console.log(JSON.stringify(result, null, 2)); +// } catch (err) { +// console.error(err); +// } +// })(); + +module.exports = { compareEntities }; \ No newline at end of file diff --git a/tests/compare.test.js b/tests/compare.test.js new file mode 100644 index 0000000..f6b19bf --- /dev/null +++ b/tests/compare.test.js @@ -0,0 +1,48 @@ +const { compareEntities } = require('../src/index'); +const axios = require('axios'); + +jest.mock('axios'); + +describe('compareEntities', () => { + const mockResponse = { + status: 200, + data: { + results: [ + { title: 'Apple vs Samsung vs Google', content: 'Apple leads in design...' }, + ], + }, + }; + + beforeEach(() => { + axios.post.mockResolvedValue(mockResponse); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should return data from Tavily API', async () => { + const data = await compareEntities('Apple', 'Samsung', 'Google'); + expect(data).toEqual(mockResponse.data); + expect(axios.post).toHaveBeenCalledWith( + 'https://api.tavily.com/search', + expect.objectContaining({ + query: expect.stringContaining('Apple'), + }), + expect.any(Object) + ); + }); + + it('should throw error if any entity is missing', async () => { + await expect(compareEntities('Apple', '', 'Google')).rejects.toThrow( + 'All three entity names must be provided.' + ); + }); + + it('should propagate API errors', async () => { + axios.post.mockRejectedValue(new Error('Network error')); + await expect(compareEntities('Apple', 'Samsung', 'Google')).rejects.toThrow( + 'Network error' + ); + }); +}); \ No newline at end of file