{"id":23177648,"url":"https://github.com/dimitrisniras/simpler-products","last_synced_at":"2025-04-05T01:42:27.148Z","repository":{"id":256767368,"uuid":"856282443","full_name":"dimitrisniras/simpler-products","owner":"dimitrisniras","description":"Simple CRUD API in Go managing products in a store","archived":false,"fork":false,"pushed_at":"2024-12-12T00:16:04.000Z","size":119,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-10T10:15:08.590Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dimitrisniras.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-12T10:04:08.000Z","updated_at":"2024-09-15T13:26:35.000Z","dependencies_parsed_at":"2024-09-13T00:51:59.495Z","dependency_job_id":"ce1c0fd7-7b06-4b68-bf97-4b26cf550ce8","html_url":"https://github.com/dimitrisniras/simpler-products","commit_stats":null,"previous_names":["dimitrisniras/simpler-products"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimitrisniras%2Fsimpler-products","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimitrisniras%2Fsimpler-products/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimitrisniras%2Fsimpler-products/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimitrisniras%2Fsimpler-products/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dimitrisniras","download_url":"https://codeload.github.com/dimitrisniras/simpler-products/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247276097,"owners_count":20912287,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-18T06:35:58.596Z","updated_at":"2025-04-05T01:42:27.125Z","avatar_url":"https://github.com/dimitrisniras.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Simpler Products API\n\nThis is a Go-based RESTful API for managing products in a store. It provides endpoints for creating, reading, updating, and deleting products, along with basic authentication and pagination support. The API is built using the Gin web framework and interacts with a MySQL database for data storage.\n\n## Features\n\n* **Product CRUD operations:**\n  * `GET /api/v1/products`: Retrieve a list of products with pagination support.\n  * `GET /api/v1/products/:id`: Retrieve a specific product by its ID.\n  * `POST /api/v1/products`: Create a new product.\n  * `PUT /api/v1/products/:id`: Update an existing product.\n  * `DELETE /api/v1/products/:id`: Delete a product.\n* **Authentication:**\n  * `JWT_SECRET_KEY` and `AUTH_ENABLED` environment variables control JWT authentication.\n  * Product endpoints require a valid JWT token in the `Authorization` header when authentication is enabled.\n* **Pagination:**\n  * The `GET /api/v1/products` endpoint supports pagination using `limit` and `offset` query parameters.\n* **Response handling:**\n  * Centralized response and error handling middleware provides consistent and structured responses.\n* **Logging:**\n  * Uses `logrus` for structured logging.\n* **Validation:**\n  * Basic input validation is implemented using Gin's binding and validation features.\n* **Testing:**\n  * Unit tests are included for services, handlers, validators, and middleware.\n* **Docker support:**\n  * A `Dockerfile` and `docker-compose.yml` file are provided for easy containerization and deployment.\n\n## Prerequisites\n\n* **Go:** Make sure you have Go installed on your system. You can download it from the official website: [https://golang.org/dl/](https://golang.org/dl/)\n* **MySQL:** You'll need a running MySQL server. You can install it locally or use a Docker container (see the \"Running with Docker Compose\" section below).\n* **Docker (optional):** If you want to run the application using Docker, make sure you have Docker installed. You can download it from the official website: [https://www.docker.com/get-started](https://www.docker.com/get-started)\n* **Docker Compose (optional):** If you want to use Docker Compose for easier setup, make sure you have it installed. You can find instructions on the official website: [https://docs.docker.com/compose/install/](https://docs.docker.com/compose/install/)\n\n## Getting Started\n\n1. **Clone the repository:**\n\n    ```bash\n    git clone https://github.com/dimitrisniras/simpler-products.git\n    cd simpler-products\n    ```\n\n2. **Set up environment variables:**\n\n    * Create a `.env` file in the project root directory.\n\n    * Add the following environment variables, replacing the placeholders with your actual values:\n\n        ```yml\n        PORT=8080\n        LOG_LEVEL=debug # or 'trace', 'info', 'warn', 'error', 'release'\n        DB_USER=your_db_user\n        DB_PASSWORD=your_db_password\n        DB_HOST=localhost # or the hostname/IP of your MySQL server\n        DB_PORT=3306\n        DB_NAME=your_db_name\n        JWT_SECRET_KEY=your_strong_secret_key\n        AUTH_ENABLED=true # or 'false' to disable authentication\n        ```\n\n3. **Create the database and table:**\n\n    * Connect to your MySQL server using a tool like the MySQL command-line client or MySQL Workbench.\n\n    * Create the database specified in your `.env` file (e.g., `your_db_name`).\n\n    * Execute the following SQL query to create the `products` table:\n\n        ```sql\n        CREATE TABLE Products (\n            id VARCHAR(255) PRIMARY KEY,\n            name VARCHAR(255) NOT NULL,\n            description VARCHAR(255) NOT NULL,\n            price DECIMAL(10, 2) NOT NULL\n        );\n        ```\n\n4. **Install dependencies:**\n\n    ```bash\n    make install-deps\n    ```\n\n## Running the Application\n\n### Locally\n\n1. **Build the executable:**\n\n    ```bash\n    make build\n    ```\n\n2. **Run the application:**\n\n    ```bash\n    make run\n    ```\n\n    The API will be accessible at `http://localhost:8080`.\n\n### With Docker\n\n1. **Build and run the containers:**\n\n    ```bash\n    docker-compose up --build\n    ```\n\n    This will build the Go application image, pull the MySQL image, and start both containers. The API will be accessible at `http://localhost:8080`.\n    Remember if you're a Mac OS user you'll need to specify the DB_HOST as `docker.for.mac.localhost`\n\n## Testing\n\n### Unit Tests\n\n* **Run unit tests:**\n\n    ```bash\n    make test\n    ```\n\n    This will execute all the unit tests in the `tests` directory.\n\n## Authentication\n\nThis API utilizes JWT (JSON Web Token) for authentication. You'll need to generate an RSA key pair (private and public keys) and set the public key as an environment variable.\n\n### Generating Keys\n\nYou can generate an RSA key pair using OpenSSL:\n\n```bash\nopenssl genrsa -out private.pem 2048\nopenssl rsa -in private.pem -pubout -out public.pem\n```\n\n* The `private.pem` file contains your private key, which should be kept secure and not shared.\n* The `public.pem` file contains your public key, which will be used by the API to verify JWTs.\n\n### Setting up the Public Key\n\n1. **Encode the public key to Base64:**\n\n    ```bash\n    export JWT_PUBLIC_KEY=$(cat public.pem | base64 -w 0)\n    ```\n\n2. **Set the environment variable:**\n\n* **Locally:** Include the following line in your `.env` file, replacing `your_base64_encoded_public_key` with the actual Base64-encoded value of your public key:\n\n    ```bash\n    JWT_PUBLIC_KEY=your_base64_encoded_public_key\n    ```\n\n* **Docker Compose:** Add the following environment variable to the app service in your `docker-compose.yml` file:\n\n    ```yaml\n    environment:\n    - JWT_PUBLIC_KEY=your_base64_encoded_public_key\n    # ... other environment variables\n    ```\n\n## API Endpoints\n\n* **`GET /api/ping`**\n\n  * A simple health check endpoint that returns 200 OK.\n  * Does not require authentication.\n\n  * **Success Response:**\n\n    ```json\n    {\n        \"status\": 200,\n    }\n    ```\n\n* **`GET /api/v1/products`**\n\n  * Retrieves a list of products.\n  * Supports pagination using limit and offset query parameters.\n  * Requires authentication (when AUTH_ENABLED is true).\n\n  * **Success Response (with pagination):**\n\n    ```json\n    {\n        \"status\": 200,\n        \"data\": [\n            {\n                \"id\": \"uuid1\",\n                \"name\": \"Product A\",\n                \"description\": \"Description of Product A\",\n                \"price\": 10.99\n            },\n            // ... other products\n        ],\n        \"pagination\": {\n            \"limit\": 10,\n            \"offset\": 0,\n            \"total\": 25 \n        }\n    }\n    ```\n\n    * **Error Response (e.g., Invalid parameters):**\n\n    ```json\n    {\n        \"status\": 400,\n        \"errors\": [\n            {\n                \"message\": \"Invalid limit parameter\"\n            }\n        ]\n    }\n    ```\n\n* **`GET /api/v1/products/:id`**\n\n  * Retrieves a specific product by its ID.\n  * Requires authentication.\n\n  * **Success Response:**\n\n    ```json\n    {\n        \"status\": 200,\n        \"data\": [{\n            \"id\": \"uuid1\",\n            \"name\": \"Product A\",\n            \"description\": \"Description of Product A\",\n            \"price\": 10.99\n        }]\n    }\n    ```\n\n    * **Error Response (e.g., Product not found):**\n\n    ```json\n    {\n        \"status\": 404,\n        \"errors\": [\n            {\n                \"message\": \"product not found\"\n            }\n        ]\n    }\n    ```\n\n* **`POST /api/v1/products`**\n\n  * Creates a new product.\n  * Requires authentication.\n  \n  * **Success Response:**\n\n    ```json\n    {\n        \"status\": 201,\n        \"data\": [{\n            \"id\": \"uuid1\",\n            \"name\": \"New Product\",\n            \"description\": \"This is a new product\",\n            \"price\": 19.99\n        }]\n    }\n    ```\n\n  * **Error Response (e.g., Validation errors):**\n\n    ```json\n    {\n        \"status\": 400,\n        \"errors\": [\n            {\n                \"message\": \"Name is required\"\n            },\n            {\n                \"message\": \"Price must be greater than 0\"\n            }\n        ]\n    }\n    ```\n\n* **`PUT /api/v1/products/:id`**\n\n  * Updates an existing product.\n  * Requires authentication.\n\n  * **Success Response:**\n\n    ```json\n    {\n        \"status\": 200,\n        \"data\": [{\n            \"id\": \"uuid1\",\n            \"name\": \"Updated Product\",\n            \"description\": \"Updated description\",\n            \"price\": 24.95\n        }]\n    }\n    ```\n\n    * **Error Response (e.g., Product not found):**\n\n    ```json\n    {\n        \"status\": 404,\n        \"errors\": [\n            {\n                \"message\": \"product not found\"\n            }\n        ]\n    }\n    ```\n\n* **`DELETE /api/v1/products/:id`**\n\n  * Deletes a product.\n  * Requires authentication.\n\n  * **Success Response:**\n\n    ```json\n    {}\n    ```\n\n  * **Error Response (e.g., Product not found):**\n\n    ```json\n    {\n        \"status\": 404,\n        \"errors\": [\n            {\n                \"message\": \"product not found\"\n            }\n        ]\n    }\n    ```\n\n## Examples\n\n### Creating a Product\n\n```bash\ncurl -X POST -H \"Content-Type: application/json\" \\\n-H \"Authorization: Bearer your_jwt_token\" \\\n-d '{\"name\": \"New Product\", \"description\": \"This is a new product\", \"price\": 19.99}' \\\nhttp://localhost:8080/api/v1/products\n```\n\n### Retrieving Products\n\n```bash\ncurl -H \"Authorization: Bearer your_jwt_token\" \\\nhttp://localhost:8080/api/v1/products?limit=5\u0026offset=0\n```\n\nRemember to replace `your_jwt_token` with an actual valid JWT token if authentication is enabled.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimitrisniras%2Fsimpler-products","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdimitrisniras%2Fsimpler-products","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimitrisniras%2Fsimpler-products/lists"}