Skip to content

Cadidate 1 diff from zip #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
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
220 changes: 198 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,211 @@
# Refactoring & unit testing challenge
# Old Doctor Slots Synchronizer: Analysis
To add unit tests for `DoctorSlotsSynchronizer.php`, I will first analyze the existing code to understand the business logic.
Then, I will refactor the code where needed to improve testability and follow best practices. Finally, I will write unit tests using PHPUnit to cover the business logic.

## How to use this repsitory and complete the assignment
Hello! First of all - welcome and congrats that we can meet on this stage of the process!
### 1. **Understanding the Business Logic**

Below you will find the home assignment we prepared for you. Some guidelines:
1. To create your copy, Use the green "Use this template" button on the top right. It should be set as private repo. Do not use Fork feature.
2. Complete the task as described below.
3. When done, give access to the repo to the hiring manager and other people provided.
4. Send us the link to the PULL REQUEST in your repo, so we can review your work.
The class `DoctorSlotsSynchronizer` handles the synchronization of doctor slots from an external API. Here's a breakdown of its functionality:

Good luck!
1. **Fetching Doctors Data**: It retrieves a list of doctors from an external API endpoint.
2. **Normalizing Doctor Names**: The names of doctors are normalized, e.g., handling special cases like surnames with prefixes.
3. **Handling Existing Doctors**: For each doctor fetched from the API, it checks if they exist in the database; if not, a new `Doctor` entity is created.
4. **Synchronizing Slots**: For each doctor, it fetches their available slots from another API endpoint and updates the database with these slots.
5. **Error Handling**: If any slot cannot be fetched or parsed, it marks the doctor with an error status.

### 2. **Refactoring for Better Testability**

## Task description
To make the code more testable, I will introduce some changes:

Please look at [`src/DoctorSlotsSynchronizer.php`](src/DoctorSlotsSynchronizer.php). Add unit tests using your favourite framework to test its business logic. Refactor any part of the code if you find it useful for architectural and testing purposes.
- **Extract Methods**: Break down large methods into smaller, more focused methods.
- **Dependency Injection for External Services**: Use dependency injection for services like fetching data from external APIs and logging, which makes it easier to mock them in tests.
- **Single Responsibility Principle**: Ensure that each method has a single responsibility to simplify unit testing.

The business requirements are not given, you need to reverse-engineer them from the code. There are no hidden bugs (as far as we know), you don't have to focus on fixing the behaviour, but rather on refactoring the code and proving using unit tests that it is correct.
### 3. **Refactoring `DoctorSlotsSynchronizer.php`**

The aim is to use unit testing, but if you'd like to propose a solution, we're curious about it as well.
### 4. **Writing Unit Tests**

I will use PHPUnit to write unit tests for the refactored `DoctorSlotsSynchronizer` class.
I will mock dependencies like `ApiClientInterface`, `EntityManagerInterface`, and `Logger` to isolate the tests and focus on the business logic.

### 5. **Conclusion**

- **Refactoring Benefits**: The refactoring improves the readability, maintainability, and testability of the `DoctorSlotsSynchronizer` class.
- **Mocking Dependencies**: Mocking external dependencies allows us to write isolated unit tests that focus on business logic rather than implementation details.
- **Unit Testing**: The tests cover various scenarios, such as creating new doctors, updating existing ones, handling errors, and managing slots synchronization.

---

# New Doctor Slots Synchronizer

This project is a PHP refactor of an application designed to synchronize doctor information and their available slots from an external API into a local database.
It includes features such as retry mechanisms for transient failures, improved logging for better observability, unit and integration tests, and adherence to coding standards using PHP-CS-Fixer.


## Table of Contents

- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Configuration](#configuration)
- [Running Tests](#running-tests)
- [Project Structure](#project-structure)
- [Code Standards](#code-standards)

## Prerequisites

- PHP 8.0 or higher
- Composer
- Docker (optional, for containerization)
- A database supported by Doctrine ORM (e.g., MySQL, PostgreSQL, SQLite)

## Installation
Only the vendor API is dockerized and configured to work with `docker-compose`. However, feel free to dockerize the rest of the project if you find it helpful.

To run the container, use `docker-compose up -d`.
After a while, the vendor API serving doctors will be accessible on `http://localhost:2137`.
1. **Clone the repository:**

```bash
git clone https://github.com/yourusername/doctor-slots-synchronizer.git
```

2. **Navigate to the project directory:**

```bash
cd doctor-slots-synchronizer
```

3. **Install dependencies using Composer:**

```bash
composer install
```

4. **Set up the environment variables:**

Copy the `.env.example` file to `.env` and configure it accordingly.

```bash
cp .env.example .env
```

## Configuration

### Environment Variables

The application uses environment variables for configuration. These are defined in the `.env` file.

- **API_ENDPOINT**: The endpoint of the external API to fetch doctor and slot data.
- **API_USERNAME**: Username for API authentication.
- **API_PASSWORD**: Password for API authentication.

Example `.env` file:

```env
API_ENDPOINT=http://localhost:2137/api/doctors
API_USERNAME=docplanner
API_PASSWORD=docplanner
```
### Docker Setup

1. **Build and Start Containers:**

Run the following command to build the Docker images and start the containers:

```bash
docker-compose up --build
```

This will start the application at `http://localhost:2137/api/doctors`.

```bash
curl --location 'http://localhost:2137/api/doctors' --header 'Authorization: Basic ZG9jcGxhbm5lcjpkb2NwbGFubmVy'
```

2. **Stop Containers:**

To stop the running containers, press `Ctrl+C` or run:

```bash
docker-compose down
```

### Application Flow

1. **Fetch Doctors**: Retrieves a list of doctors from the external API.
2. **Process Each Doctor**:
- Normalizes the doctor's name.
- Checks if the doctor exists in the local database; creates or updates as necessary.
3. **Fetch and Process Slots**:
- Fetches available slots for each doctor.
- Processes each slot, saving it to the database.
4. **Error Handling**:
- Implements retry mechanisms for transient API failures.
- Logs errors with detailed context.

## Running Tests

The project includes both unit and integration tests using PHPUnit.

### Unit Tests

To run unit tests:

```bash
composer test -- --testsuite Unit
```
## Project Structure

```
project_root/
├── .env
├── bin/
├── docker/
│ ├── Dockerfile
│ ├── entrypoint.sh
│ ├── docker-compose.yml
├── src/
│ ├── Entity/
│ │ ├── Doctor.php
│ │ └── Slot.php
│ ├── Service/
│ │ ├── DoctorSlotsSynchronizer.php
│ │ └── StaticDoctorSlotsSynchronizer.php
├── tests/
│ └── Unit/
│ ├── Entity/
│ │ ├── DoctoTest.php
│ │ └── SlotTest.php
│ └── Service/
│ ├── DoctorSlotsSynchronizerTest.php
│ └── StaticDoctorSlotsSynchronizerTest.php
├── vendor/
├── .php-cs-fixer.dist.php
├── composer.json
├── composer.lock
├── phpunit.xml
├── public/
│ └── index.php
```

## Code Standards

The project adheres to the PSR-12 coding standard and uses PHP-CS-Fixer for code formatting.

### Running PHP-CS-Fixer

To check for coding standard violations:

```bash
composer cs-fix -- --dry-run --diff
```

To automatically fix coding standard violations:

```bash
composer cs-fix
```

### Configuration

The `.php-cs-fixer.dist.php` file contains the configuration for PHP-CS-Fixer.

## Hints
---

- Show us your skills.
- Remember to write tests that test business logic, not implementation.
- It's up to you how much time you want to spend.
- If you could do something better, but it's too much work, please put a comment on what you would improve.
- If something is unclear, don't hesitate to ask.
Rafa Terrero
53 changes: 41 additions & 12 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
{
"require": {
"doctrine/orm": "^2.10",
"monolog/monolog": "^2.3",
"symfony/cache": "^6.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
{
"name": "rterrero/doctor-slots-synchronizer",
"description": "Synchronising doctors and their slots from an external API to a local database.",
"type": "project",
"require": {
"php": "^8.0",
"doctrine/annotations": "^2.0",
"doctrine/cache": "^2.1",
"doctrine/dbal": "^3.1",
"doctrine/orm": "^2.19",
"guzzlehttp/guzzle": "^7.9",
"monolog/monolog": "^2.3",
"psr/log": "^1.1 || ^2.0 || ^3.0",
"symfony/cache": "^7.1",
"vlucas/phpdotenv": "^5.4"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"friendsofphp/php-cs-fixer": "^3.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"test": "phpunit",
"cs-fix": "php-cs-fixer fix"
},
"config": {
"optimize-autoloader": true,
"sort-packages": true
},
"minimum-stability": "stable",
"license": "MIT"
}
Loading