Strategies (task 06 debt, task 12 dependency) - ResourcePicker orders candidates; it deliberately does not choose. Only the engine knows which resource actually fits this slot and which was already taken by another role, and a strategy that picked would have to duplicate both checks - Four implementations behind a tagged iterator: first_available (name order, the previous behaviour and still the default because it is predictable), least_gap, least_loaded, same_as_previous - least_gap and least_loaded are deliberate opposites and both are correct; choosing between them is a business decision, so it lives in settings - same_as_previous lifts a course's preferred resource to the front and keeps everyone else behind it. A preference, not a filter: forcing the same operator would make the patient wait two weeks, which is worse than a different operator - Availability accepts course_uuid to supply that preference, closing the dependency task 12 recorded against task 06 - An unknown strategy falls back at search time but is rejected at save time. Stale settings must not stop bookings; a user typing a wrong value must not believe it took effect Test suite flake createUser() retries on a mobile-number collision — db_test is never reset and holds tens of thousands of users, so the random draw does collide. The failed INSERT closes the EntityManager, and the retry asked the container for it again, which hands back the *same closed instance*. So the retry threw, and every later test in that process inherited a dead manager. That is the intermittent "EntityManager is closed" on an unrelated, always-different test that made roughly half of full runs red and never reproduced in a subset. Resetting the registry gives a live manager back. UserCollisionRetryTest pins it by closing the manager on purpose. Two consecutive full runs are green: 1334 tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
177 lines
6.3 KiB
YAML
177 lines
6.3 KiB
YAML
# yaml-language-server: $schema=../vendor/symfony/dependency-injection/Loader/schema/services.schema.json
|
|
|
|
# This file is the entry point to configure your own services.
|
|
# Files in the packages/ subdirectory configure your dependencies.
|
|
# See also https://symfony.com/doc/current/service_container/import.html
|
|
|
|
# Put parameters here that don't need to change on each machine where the app is deployed
|
|
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
|
|
parameters:
|
|
default_api_ir_base_url: 'https://s.api.ir'
|
|
|
|
# Optional ISR webhook for the public site. Without defaults, a deploy that
|
|
# does not define these variables makes BlogCacheInvalidator un-instantiable,
|
|
# which takes the WHOLE blog module down with HTTP 500 — reads included.
|
|
# BlogCacheInvalidator is fail-open on an empty string: no webhook, no cache
|
|
# invalidation, everything else keeps working.
|
|
env(REVALIDATE_WEBHOOK_URL): ''
|
|
env(REVALIDATE_WEBHOOK_SECRET): ''
|
|
|
|
services:
|
|
# default configuration for services in *this* file
|
|
_defaults:
|
|
autowire: true # Automatically injects dependencies in your services.
|
|
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
|
|
|
|
# Tag every room-deletion reason so RoomService can iterate them without knowing
|
|
# who they are. Tasks 02 (active resources) and 07 (future appointments) each add
|
|
# one implementation and RoomService itself stays untouched.
|
|
_instanceof:
|
|
App\Branch\Service\RoomDeletionGuardInterface:
|
|
tags: ['app.room_deletion_guard']
|
|
|
|
# Ordering strategies for resource assignment (task 06). The engine asks the
|
|
# registry by code, so adding a strategy means adding one class — nothing
|
|
# in the engine or the settings controller changes.
|
|
App\Appointment\Availability\Picker\ResourcePicker:
|
|
tags: ['app.resource_picker']
|
|
|
|
# makes classes in src/ available to be used as services
|
|
# this creates a service per class whose id is the fully-qualified class name
|
|
App\:
|
|
resource: '../src/'
|
|
|
|
App\Category\Command\SeedCategoriesCommand:
|
|
arguments:
|
|
$projectDir: '%kernel.project_dir%'
|
|
|
|
App\Shared\Command\SeedDemoDataCommand:
|
|
arguments:
|
|
$environment: '%kernel.environment%'
|
|
|
|
App\Shared\Service\FileUploadService:
|
|
arguments:
|
|
$projectDir: '%kernel.project_dir%'
|
|
|
|
App\Doctor\Controller\DoctorController:
|
|
arguments:
|
|
$projectDir: '%kernel.project_dir%'
|
|
|
|
App\Clinic\Controller\ClinicController:
|
|
arguments:
|
|
$projectDir: '%kernel.project_dir%'
|
|
|
|
App\Settlement\Controller\SettlementController:
|
|
arguments:
|
|
$projectDir: '%kernel.project_dir%'
|
|
|
|
App\UserProfile\Controller\UserProfileController:
|
|
arguments:
|
|
$projectDir: '%kernel.project_dir%'
|
|
|
|
App\Shared\Service\ApiIrService:
|
|
arguments:
|
|
$baseUrl: '%env(default:default_api_ir_base_url:API_IR_BASE_URL)%'
|
|
$token: '%env(default::API_IR_TOKEN)%'
|
|
|
|
App\Shared\Captcha\AltchaService:
|
|
arguments:
|
|
$hmacKey: '%env(ALTCHA_HMAC_KEY)%'
|
|
$envEnabled: '%env(bool:ALTCHA_ENABLED)%'
|
|
$maxNumber: '%env(int:ALTCHA_MAX_NUMBER)%'
|
|
$expireSeconds: '%env(int:ALTCHA_EXPIRE_SECONDS)%'
|
|
$altchaPool: '@altcha.pool'
|
|
|
|
App\Shared\Controller\HealthController:
|
|
arguments:
|
|
$healthPool: '@health.pool'
|
|
|
|
# Persist warning+ logs to the app_log table while keeping stderr output.
|
|
App\Shared\Logging\DbLogger:
|
|
decorates: 'logger'
|
|
arguments:
|
|
$inner: '@.inner'
|
|
$conn: '@doctrine.dbal.default_connection'
|
|
|
|
App\Auth\Service\OtpService:
|
|
arguments:
|
|
$otpTtl: '%env(int:OTP_TTL)%'
|
|
$appEnv: '%kernel.environment%'
|
|
|
|
App\Auth\Service\TokenService:
|
|
arguments:
|
|
$refreshTokenTtl: '%env(int:REFRESH_TOKEN_TTL)%'
|
|
|
|
App\Auth\Security\PasswordAuthenticator:
|
|
arguments:
|
|
$refreshTokenTtl: '%env(int:REFRESH_TOKEN_TTL)%'
|
|
$crawlerServiceToken: '%env(default::CRAWLER_SERVICE_TOKEN)%'
|
|
$loginLimiter: '@limiter.login'
|
|
|
|
App\Auth\Controller\AuthController:
|
|
arguments:
|
|
$sendCodeLimiter: '@limiter.send_code'
|
|
$verifyCodeLimiter: '@limiter.verify_code'
|
|
$tokenIssueLimiter: '@limiter.token_issue'
|
|
$passwordResetLimiter: '@limiter.password_reset'
|
|
|
|
App\Payment\Gateway\MellatGateway:
|
|
arguments:
|
|
$terminalId: '%env(default::MELLAT_TERMINAL_ID)%'
|
|
$username: '%env(default::MELLAT_USERNAME)%'
|
|
$password: '%env(default::MELLAT_PASSWORD)%'
|
|
|
|
App\Payment\Gateway\SepGateway:
|
|
arguments:
|
|
$terminalId: '%env(default::SEP_TERMINAL_ID)%'
|
|
|
|
App\Payment\Controller\PaymentController:
|
|
arguments:
|
|
$appBaseUrl: '%env(APP_BASE_URL)%'
|
|
$allowedFrontendHosts: '%env(ALLOWED_FRONTEND_HOSTS)%'
|
|
|
|
App\Payment\Service\PaymentManager:
|
|
arguments:
|
|
$appBaseUrl: '%env(APP_BASE_URL)%'
|
|
|
|
App\Sms\Provider\KavehNegarProvider:
|
|
arguments:
|
|
$apiKey: '%env(default::KAVENEGAR_API_KEY)%'
|
|
|
|
App\Blog\Controller\BlogController:
|
|
arguments:
|
|
$projectDir: '%kernel.project_dir%'
|
|
|
|
App\Blog\Service\BlogCacheInvalidator:
|
|
arguments:
|
|
$webhookUrl: '%env(REVALIDATE_WEBHOOK_URL)%'
|
|
$webhookSecret: '%env(REVALIDATE_WEBHOOK_SECRET)%'
|
|
|
|
App\Insurance\Controller\InsuranceController:
|
|
arguments:
|
|
$projectDir: '%kernel.project_dir%'
|
|
|
|
App\ClinicInvitation\Service\ClinicInvitationService:
|
|
arguments:
|
|
$appUrl: '%env(APP_BASE_URL)%'
|
|
|
|
App\ClinicInvitation\Controller\ClinicInvitationWebController:
|
|
arguments:
|
|
$appUrl: '%env(APP_BASE_URL)%'
|
|
|
|
App\Secretary\Controller\SecretaryController:
|
|
arguments:
|
|
$appUrl: '%env(APP_BASE_URL)%'
|
|
|
|
App\Secretary\Service\SecretaryService:
|
|
arguments:
|
|
$appUrl: '%env(APP_BASE_URL)%'
|
|
|
|
App\Auth\Controller\PreRegistrationController:
|
|
arguments:
|
|
$appUrl: '%env(APP_BASE_URL)%'
|
|
|
|
App\Sms\Controller\SmsWalletController:
|
|
arguments:
|
|
$appBaseUrl: '%env(APP_BASE_URL)%'
|