Skip to content

Latest commit

 

History

History
531 lines (369 loc) · 20.7 KB

README.md

File metadata and controls

531 lines (369 loc) · 20.7 KB

Build Status Code Coverage Scrutinizer Code Quality

User facing application for the Wikimedia Deutschland fundraising.

Installation

First setup

For development, you need to have Docker and docker compose installed. You need at least Docker version >= 20 and the docker compose plugin (compose version >= 2). If your OS does not come with the right version, please use the official installation instructions for Docker. You don't need to install other dependencies (PHP, Node.js, MariaDB) on your machine.

Get a clone of our git repository and then run:

make setup

This will

Installing the current dependencies

make install-php

Will install the dependencies currently specified in composer.lock. Use this command whenever you check out a branch that has changes to composer.lock.

Running the application

The command

make up-app

will run the application in Docker containers. You can access it at http://localhost:8082/.

To stop the application, run

make down-app

Running without the Makefile

The up-app task will also make sure that your local configuration is correct and will stop previous instances of the application. You can run the application without the Makefile, in the foreground, with the command

docker compose up

Configuration

The web and CLI entry points of the application check for the APP_ENV environment variable. If it not set, the application assumes the value dev. Each environment must have a corresponding configuration file in app/config, following the name pattern of config.ENVIRONMENTNAME.json. See the section "Running in different environments" below to see how to set APP_ENV. Running the command make default-config will automatically set up the configuration file.

You can add local modifications by adding a file that follows the name pattern of config.ENVIRONMENTNAME.local.json.

The application merges the values from the configuration files with the default values from the file app/config/config.dist.json.

.env files

They mostly contain credentials. Symfony requires those files and will pick the file depending on the APP_ENV environment variable.

  • .env: Sets default values for all environments, overwritten by all following files.
  • .env.dev: Sets default values for the development environment
  • .env.test: Sets default values for unit test environment
  • .env.dev.local: Overrides the default values for all environments. See Payments section below.

.env.dev and .env.test contain defaults and should never contain actual credentials. If you want to override credentials, create the file .env.dev.local. DO NOT CHECK IT IN!

When we deploy the application with our deployment scripts, the deployment script will create a file called .env with the configuration data for production.

Fronted development

If you want to work on the client-side code of the application, you need to load it from a different source, e.g. the development server of fundraising-app-frontend running on port 7072. The configuration has the setting assets-path that you can point to a different path or even a URL.

The following setting will point your application to the frontend development server:

"assets-path": "http://localhost:7072"

The application will show a message if the browser can't find the assets.

Create a test configuration that uses the MariaDB database

To speed up the tests when running them locally, the tests use SQLite instead of MariaDB. To run the tests with the real database, add the file app/config/config.test.local.json with the following content:

{
    "db": {
        "driver": "pdo_mysql",
        "user": "fundraising",
        "password": "INSECURE PASSWORD",
        "dbname": "fundraising",
        "host": "database",
        "port": 3306
    }
}

Payments

For a fully working instance with all payment types and working templates you need to fill out the following configuration data in app/config/config.dev.json:

"operator-email"
"operator-displayname-organization"
"operator-displayname-suborganization"
"paypal-donation"
"paypal-membership"
"creditcard"

To be able to go to the PayPal page for payments, you also need to fill the PayPal credentials in the file .env.dev.local. The .env.dev.local file will override the example data given in .env.dev. See the file .env.dev for an explanation of the PayPal configuration entries.

Content

The application needs a copy of the content repository at https://github.com/wmde/fundraising-frontend-content to work properly. In development, the content repository is a composer dev-dependency. If you want to put the content repository in another place, you need to configure the i18n-base-path to point to it. The following example shows the configuration when the content repository is at the same level as the application directory:

"i18n-base-path": "../fundraising-frontend-content/i18n"

A/B test campaigns.

For more information on how to set up the campaigns see "How to Create an A/B Test".

The campaign definitions are in the app/config directory. You can tell the application which files to use by editing the campaigns value in app/config/config.ENVIRONMENTNAME.json. The application will merge the campaign configuration files on top of each other.

Running in different environments

By default, the configuration environment is dev and the configuration file is config.dev.json. If you want to change that, you have to pass the environment variable to make, docker and docker compose commands.

make ci APP_ENV=prod

For docker compose you can create a file called .env in the application directory with the contents of

APP_ENV=prod

If you want to override the defaults in the .env file, you set the variable in your shell like this:

export APP_ENV=prod

If you run a single docker container, you can pass the variable with the -e flag:

docker run -e APP_ENV=prod php some_script.php

Valid environment names are

  • dev - development environment, mostly for local development
  • test - unit testing environment
  • uat - user acceptance testing
  • prod - production

Note: PHPUnit tests are always run in the test environment configuration, regardless of APP_ENV!

Running the tests, code style checks and static analysis

Running all checks

make ci

This will run the tests, check the code style, do the static analysis and check the configuration files.

Run only tests

make test

If you want to run a specific folder with tests or just one file, use the TEST_DIR parameter. Examples:

# Run the unit tests
make phpunit TEST_DIR=tests/Unit

# Run a specific test file
make phpunit TEST_DIR=tests/EdgeToEdge/Routes/AddDonationRouteTest.php

Run only code style checks

make cs

If you want to fix the code style violations, run

make fix-cs

Static analysis

We perform static code analysis with PHPStan during runs of make ci.

In the absence of dev-dependencies (i.e. to simulate the vendor/ code on production) you can run PHPStan with the commands

docker build -t wmde/fundraising-frontend-phpstan build/phpstan
docker run -v $PWD:/app --rm wmde/fundraising-frontend-phpstan analyse -c phpstan.neon --level 1 --no-progress cli/ contexts/ src/

These tasks are also performed during the DroneCI runs.

Emails

In the development environment we use mailhog to capture all emails sent by the application. You can access the mailhog web interface at http://localhost:8025/

You can find more information on how we test the email templates and how to render them without using the application in the Email Templates document.

Database

Resetting the database in your local environment

To drop the database and rebuild it from scratch the database, you need to stop the database container, delete the volume db-storage defined in docker-compose.yml and start the database container again.

You can shut down all containers and delete all volumes with the command

docker compose down -v

The next time you run make up-app, the database container will process all SQL files in .docker/database.

Accessing the database with the command line client

To start the command line client, use the following commands:

docker compose up -d database
docker compose exec database mysql -u fundraising -p"INSECURE PASSWORD" fundraising

Accessing the database from your host machine

If you want to use a different client for accessing the database, you need to connect to port 3307.

Database migrations

Out of the box, the database should be in a usable state for local development.

If you make changes to the database schema, you have to do two things:

  1. Create a Doctrine migration script for the production database. Store the migration scripts in the migrations directory of the bounded context where you made the changes.
  2. In your development environment, create the new database schema definitions with the make generate-database-schema command. This will refresh the file ./docker/database/01_Database_Schema.sql. Then restart the container environment while dropping the database volume. See section "Resetting the database in your local environment" above.

Migrations CLI and configuration

The configuration file for migrations is in app/config/migrations.php

The bin/doctrine CLI command comes with the pre-configured migrations command for the Fundraising App. Wherever the Doctrine migrations documentation mentions running the command vendor/bin/doctrine-migrations, use the command bin/doctrine instead. E.g. bin/doctrine migrations:status.

In your Docker-based development environment, run the command in the app container, using docker compose exec. The container environment must be running for this to work. Example:

docker compose exec app bin/doctrine migrations:status

Running migrations on the server

Have a look the deployment documentation on how to run the migrations on the server.

Note: If you're getting errors that the configuration file was not found, make sure to set APP_ENV to the right value. See section "Running in different environments" in this document.

Accessing the database from a Docker image

If you want to connect to the database container from another docker container that's not part of the docker-compose.yml configuration (for example to use a tool like Adminer or PHPMyAdmin), you need to put that container in the same virtual network as the rest of the application containers. With the command

docker network ls

you can list all networks. There will be one network name ending in fundraising_proxy (the prefix is probably the directory name where you checked out this repository).

Next up is finding out the full name of the database container with the command

docker ps

The database container will be the one ending in _database_1. The prefix is probably the directory name where you checked out this repository.

Copy the full network name and container name and use them instead of the placeholders __CONTAINER_NAME__ and __NETWORK_NAME__ in the following command to run PHPMyAdmin, port 8099:

docker run -it --link __CONTAINER_NAME__:db --net __NETWORK_NAME__ -p 8099:80 phpmyadmin/phpmyadmin

Importing the address completion data

To import the German postcode database, you need to place it in .docker/database/00.postcodes.sql. The database container will pick it up when running for the first time (when it creates the volume db-storage). This happens when you run docker compose up for the first time or when you reset the database (see above). Depending on the speed of you machine, the import will take up to 10 minutes. Watch the output of the database container so see when the database has finished importing.

Frontend development

Using compiled assets

By default, the application uses an old version of pre-built frontend assets from fundraising-app-frontend. To download the assets, run

make download-assets

To get the pre-built assets from a specific branch of that repository, run

make download-assets ASSET_BRANCH=your_branch_name

We have plans to use the most recent version of the assets fom the GitHub fundraising-app-frontend, but that involves an extension of our infrastructure.

Development with live assets

If you want to load the assets from a different source, e.g. the development server of fundraising-app-frontend running on port 7072, you need to add the following line to your config.dev.json file:

"assets-path": "http://localhost:7072"

The HTML templates will prefix every asset (CSS, JavaScript) reference with the value of assets-path.

Updating the dependencies

To update all the PHP dependencies, run

make update-php

To update only the messages in the application and emails, update the fundraising-frontend-content dependency with the command

make update-content

For updating an individual PHP dependency, use the command line

docker run --rm -it -v $(pwd):/app -u $(id -u):$(id -g) registry.gitlab.com/fun-tech/fundraising-frontend-docker:composer composer update PACKAGE_NAME

and replace the PACKAGE_NAME placeholder with the name of your package.

Deployment

For an in-depth documentation how to deploy the application on our servers, see the deployment documentation.

Troubleshooting

Cache

The CLI commands in the container and the PHP-FPM process both share the same cache location inside the container - /tmp/symfony. When running CLI commands, the owner of the files will be root:root. This will prevent the PHP-FPM process from writing to the cache. If you get a cache-related error in your browser, run the following command while the docker compose environment is running:

make clear

The command will delete all cache files inside the app container. After running the command, make sure to access the web site first, before running another CLI command.

Project structure

This app and its used Bounded Contexts follow the architecture rules outlined in Clean Architecture + Bounded Contexts.

Architecture diagram

Used Bounded Contexts:

Production code layout

  • src/: code not belonging to any Bounded Context, framework agnostic if possible
    • Factories/: application factories used by the framework, including top level factory FFFactory
    • Presentation/: presentation code, including the Presenters/
    • Validation/: validation code
  • vendor/wmde/$ContextName/src/: framework agnostic code belonging to a specific Bounded Context
    • Domain/: domain model and domain services
    • UseCases/: one directory per use case
    • DataAccess/: implementations of services that binds to database, network, etc
    • Infrastructure/: implementations of services binding to cross cutting concerns, i.e. logging
  • web/: web accessible code
    • index.php: HTTP entry point
    • skins: Asset files (CSS, JavaScript, images, fonts) for different skins
  • app/: contains application-specific configuration and all framework (Symfony) dependent code
    • Controllers/: Symfony Controllers
    • EventHandlers: "Middleware" code that performs tasks before or after HTTP request handling
    • config/: configuration files
      • config.dist.json: default configuration
      • config.test.json: configuration used by integration and system tests (gets merged into default config)
      • config.test.local.json: instance specific (gitignored) test config (gets merged into config.test.json)
      • config.development.json: instance specific (gitignored) production configuration (gets merged into default config)
  • config/: Symfony configuration files
  • cli/: Command line commands, integrated into the Symfony console
  • var/: Ephemeral application data
    • log/: Log files (in debug mode, every request creates a log file)
    • cache/: Cache directory for Twig templates and Symfony DI containers

Test code layout

The test directory structure (and namespace structure) mirrors the production code. Tests for code in src/ and app/ is in tests/.

Tests are categorized by their type. To run only tests of a given type, you can use one of the test suites defined in phpunit.xml.dist.

  • Unit/: small isolated tests (one class or a small number of related classes)
  • Integration/: tests combining several units
  • EdgeToEdge/: edge-to-edge tests (fake HTTP requests to the framework)
  • System/: tests involving outside systems (i.e., beyond our PHP app and database)
  • Fixtures/: test doubles (stubs, spies and mocks)

If you need access the FunFunFactory in your non-unit tests, for instance to interact with persistence, you should inherit from KernelTestCase and get the Factory from the container.

Test type restrictions

Network Framework (Symfony) Top level factory Database and disk
Unit No No No No
Integration No No Discouraged Yes
EdgeToEdge No Yes Yes Yes
System Yes Yes Yes Yes

Other directories

  • .docker/: Configuration and Dockerfiles for the development environment

See also