diff --git a/dev/Application/Messaging/Impl/EventHandler.php b/dev/Application/Messaging/Impl/EventHandler.php index 65ce423..b71c2e4 100644 --- a/dev/Application/Messaging/Impl/EventHandler.php +++ b/dev/Application/Messaging/Impl/EventHandler.php @@ -24,11 +24,9 @@ public function handle(Message $message, string $channel): void if ($this->translator){ $message = $this->translator->translate($message); } - echo "Handling message with body: " - .$message->getBody().", headers: " - .print_r($message->getHeaders(), true).", properties: " - .print_r($message->getProperties(), true)."\n"; + echo "Handling message with id: ".$message->getProperties()['id']." from channel: ".$channel."...\n"; $this->store->add(message: $message, channel: $channel); + echo "Successfully added message to store.\n"; } } diff --git a/dev/tests/features/bootstrap/PingContext.php b/dev/tests/features/bootstrap/PingContext.php index e3755ae..448fd1f 100644 --- a/dev/tests/features/bootstrap/PingContext.php +++ b/dev/tests/features/bootstrap/PingContext.php @@ -15,6 +15,8 @@ class PingContext implements Context { protected int $port; + protected string $host; + protected Curl $curl; /** @@ -34,6 +36,7 @@ public function __construct() public function theServicePortIsDefined() { $this->port = getenv('HTTP_PORT'); + $this->host = getenv('HTTP_HOST')?:'localhost'; } /** @@ -42,7 +45,7 @@ public function theServicePortIsDefined() public function iDoHttpGet() { $this->curl = new Curl(); - $this->curl->get('http://event-listener:'.$this->port); + $this->curl->get('http://'.$this->host.':'.$this->port); } /** diff --git a/ops/envs/concurrent/comp/.dist.env b/ops/envs/concurrent/comp/.dist.env new file mode 100644 index 0000000..c499d83 --- /dev/null +++ b/ops/envs/concurrent/comp/.dist.env @@ -0,0 +1,24 @@ +COMPOSE_PROJECT_NAME=evdobe-event-listener +TIME_ZONE=Europe/Athens +HTTP_PORT=9501 +MESSAGE_BROKER_HOST=kafka +MESSAGE_BROKER_PORT=29092 +MESSAGE_CONSUMER_GROUP=my-event-listener +EVENT_CHANNEL_1=my-event-channel +EVENT_CHANNEL_2=another-channel +EVENT_CHANNEL_3=third-channel +EVENT_CHANNELS=" + ${EVENT_CHANNEL_1};\Application\Messaging\Impl\HeaderNameFilter|submitted|deleted;\Application\Messaging\Plugin\ExampleTranslator1 + ${EVENT_CHANNEL_2};\Application\Messaging\Plugin\InvalidFilter;\Application\Messaging\Plugin\ExampleTranslator2 + ${EVENT_CHANNEL_3} +" +INVALID_CHANNEL=global.invalid +MESSAGE_TOPICS=" + ${EVENT_CHANNEL_1}:1:1 + ${EVENT_CHANNEL_2}:1:1 + ${INVALID_CHANNEL}:1:1 + ${EVENT_CHANNEL_3}:1:1 +" +STORE_DB_USER=adbuser +STORE_DB_PASSWORD=adbpass +STORE_DB_NAME=eventstore \ No newline at end of file diff --git a/ops/envs/concurrent/comp/.gitignore b/ops/envs/concurrent/comp/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/ops/envs/concurrent/comp/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/ops/envs/concurrent/comp/docker-compose.yaml b/ops/envs/concurrent/comp/docker-compose.yaml new file mode 100644 index 0000000..5a8de9e --- /dev/null +++ b/ops/envs/concurrent/comp/docker-compose.yaml @@ -0,0 +1,196 @@ +services: + event-listener-1: + build: ../cont/event-listener + environment: + HOST_GID: ${HOST_UID} + HOST_UID: ${HOST_UID} + HTTP_PORT: ${HTTP_PORT} + TZ: ${TIME_ZONE} + AUTORELOAD_PROGRAMS: "swoole" + AUTORELOAD_ANY_FILES: 0 + MESSAGE_BROKER_HOST: kafka-1 + MESSAGE_BROKER_PORT: ${MESSAGE_BROKER_PORT} + MESSAGE_CONSUMER_GROUP: ${MESSAGE_CONSUMER_GROUP} + EVENT_CHANNELS: ${EVENT_CHANNELS} + INVALID_CHANNEL: ${INVALID_CHANNEL} + STORE_DB_HOST: db + STORE_DB_USER: ${STORE_DB_USER} + STORE_DB_PASSWORD: ${STORE_DB_PASSWORD} + STORE_DB_NAME: ${STORE_DB_NAME} + expose: + - ${HTTP_PORT} + ports: + - 9501:${HTTP_PORT} + volumes: + - ../../../../dev:/var/www + depends_on: + - kafka-1 + - db + event-listener-2: + build: ../cont/event-listener + environment: + HOST_GID: ${HOST_UID} + HOST_UID: ${HOST_UID} + HTTP_PORT: ${HTTP_PORT} + TZ: ${TIME_ZONE} + AUTORELOAD_PROGRAMS: "swoole" + AUTORELOAD_ANY_FILES: 0 + MESSAGE_BROKER_HOST: kafka-2 + MESSAGE_BROKER_PORT: ${MESSAGE_BROKER_PORT} + MESSAGE_CONSUMER_GROUP: ${MESSAGE_CONSUMER_GROUP} + EVENT_CHANNELS: ${EVENT_CHANNELS} + INVALID_CHANNEL: ${INVALID_CHANNEL} + STORE_DB_HOST: db + STORE_DB_USER: ${STORE_DB_USER} + STORE_DB_PASSWORD: ${STORE_DB_PASSWORD} + STORE_DB_NAME: ${STORE_DB_NAME} + expose: + - ${HTTP_PORT} + ports: + - 9502:${HTTP_PORT} + volumes: + - ../../../../dev:/var/www + depends_on: + - kafka-2 + - db + event-listener-3: + build: ../cont/event-listener + environment: + HOST_GID: ${HOST_UID} + HOST_UID: ${HOST_UID} + HTTP_PORT: ${HTTP_PORT} + TZ: ${TIME_ZONE} + AUTORELOAD_PROGRAMS: "swoole" + AUTORELOAD_ANY_FILES: 0 + MESSAGE_BROKER_HOST: kafka-3 + MESSAGE_BROKER_PORT: ${MESSAGE_BROKER_PORT} + MESSAGE_CONSUMER_GROUP: ${MESSAGE_CONSUMER_GROUP} + EVENT_CHANNELS: ${EVENT_CHANNELS} + INVALID_CHANNEL: ${INVALID_CHANNEL} + STORE_DB_HOST: db + STORE_DB_USER: ${STORE_DB_USER} + STORE_DB_PASSWORD: ${STORE_DB_PASSWORD} + STORE_DB_NAME: ${STORE_DB_NAME} + expose: + - ${HTTP_PORT} + ports: + - 9503:${HTTP_PORT} + volumes: + - ../../../../dev:/var/www + depends_on: + - kafka-3 + - db + + zookeeper-1: + image: evdobe/kafka:strimzi-0.27.0-kafka-3.0.0-ev2 + command: [ + "sh", "-c", + "/opt/scripts/zookeeper-entrypoint.sh" + ] + expose: + - 2181 + - 2888 + - 3888 + environment: + LOG_DIR: /tmp/logs + ZOOKEEPER_ID: 1 + ZOOKEEPER_SERVERS: 1=zookeeper-1:2888:3888;2=zookeeper-2:2888:3888;3=zookeeper-3:2888:3888 + zookeeper-2: + image: evdobe/kafka:strimzi-0.27.0-kafka-3.0.0-ev2 + command: [ + "sh", "-c", + "/opt/scripts/zookeeper-entrypoint.sh" + ] + expose: + - 2181 + - 2888 + - 3888 + environment: + LOG_DIR: /tmp/logs + ZOOKEEPER_ID: 2 + ZOOKEEPER_SERVERS: 1=zookeeper-1:2888:3888;2=zookeeper-2:2888:3888;3=zookeeper-3:2888:3888 + zookeeper-3: + image: evdobe/kafka:strimzi-0.27.0-kafka-3.0.0-ev2 + command: [ + "sh", "-c", + "/opt/scripts/zookeeper-entrypoint.sh" + ] + expose: + - 2181 + - 2888 + - 3888 + environment: + LOG_DIR: /tmp/logs + ZOOKEEPER_ID: 3 + ZOOKEEPER_SERVERS: 1=zookeeper-1:2888:3888;2=zookeeper-2:2888:3888;3=zookeeper-3:2888:3888 + kafka-1: + image: evdobe/kafka:strimzi-0.27.0-kafka-3.0.0-ev2 + command: [ + "/opt/scripts/entrypoint.sh" + ] + depends_on: + - zookeeper-1 + expose: + - 9092 + - 29092 + environment: + LOG_DIR: "/tmp/logs" + KAFKA_BROKER_ID: 1 + KAFKA_ADVERTISED_LISTENERS: EXTERNAL://kafka-1:29092,INTERNAL://kafka-1:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: EXTERNAL:PLAINTEXT,INTERNAL:PLAINTEXT + KAFKA_LISTENERS: EXTERNAL://:29092,INTERNAL://:9092 + KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL + KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:2181,zookeeper-2:2181,zookeeper-3:2181 + KAFKA_CREATE_TOPICS_SEPARATOR: "$$'\n'" + KAFKA_CREATE_TOPICS: ${MESSAGE_TOPICS} + kafka-2: + image: evdobe/kafka:strimzi-0.27.0-kafka-3.0.0-ev2 + command: [ + "/opt/scripts/entrypoint.sh" + ] + depends_on: + - zookeeper-2 + expose: + - 9092 + - 29092 + environment: + LOG_DIR: "/tmp/logs" + KAFKA_BROKER_ID: 2 + KAFKA_ADVERTISED_LISTENERS: EXTERNAL://kafka-2:29092,INTERNAL://kafka-2:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: EXTERNAL:PLAINTEXT,INTERNAL:PLAINTEXT + KAFKA_LISTENERS: EXTERNAL://:29092,INTERNAL://:9092 + KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL + KAFKA_ZOOKEEPER_CONNECT: zookeeper-2:2181,zookeeper-3:2181,zookeeper-1:2181 + KAFKA_CREATE_TOPICS_SEPARATOR: "$$'\n'" + KAFKA_CREATE_TOPICS: ${MESSAGE_TOPICS} + kafka-3: + image: evdobe/kafka:strimzi-0.27.0-kafka-3.0.0-ev2 + command: [ + "/opt/scripts/entrypoint.sh" + ] + depends_on: + - zookeeper-3 + expose: + - 9092 + - 29092 + environment: + LOG_DIR: "/tmp/logs" + KAFKA_BROKER_ID: 3 + KAFKA_ADVERTISED_LISTENERS: EXTERNAL://kafka-3:29092,INTERNAL://kafka-3:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: EXTERNAL:PLAINTEXT,INTERNAL:PLAINTEXT + KAFKA_LISTENERS: EXTERNAL://:29092,INTERNAL://:9092 + KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL + KAFKA_ZOOKEEPER_CONNECT: zookeeper-3:2181,zookeeper-1:2181,zookeeper-2:2181 + KAFKA_CREATE_TOPICS_SEPARATOR: "$$'\n'" + KAFKA_CREATE_TOPICS: ${MESSAGE_TOPICS} + + db: + build: ../cont/db/ + environment: + POSTGRES_PASSWORD: "${STORE_DB_PASSWORD}" + POSTGRES_USER: "${STORE_DB_USER}" + POSTGRES_DB: "${STORE_DB_NAME}" + POSTGRES_HOST_AUTH_METHOD: trust + expose: + - 5432 + \ No newline at end of file diff --git a/ops/envs/concurrent/cont/db/Dockerfile b/ops/envs/concurrent/cont/db/Dockerfile new file mode 100644 index 0000000..362335f --- /dev/null +++ b/ops/envs/concurrent/cont/db/Dockerfile @@ -0,0 +1,4 @@ +FROM postgres:9.6.24 +RUN localedef -i el_GR -c -f UTF-8 -A /usr/share/locale/locale.alias el_GR.UTF-8 +ENV LANG el_GR.utf8 +COPY target / \ No newline at end of file diff --git a/ops/envs/concurrent/cont/db/target/docker-entrypoint-initdb.d/event_table.sql b/ops/envs/concurrent/cont/db/target/docker-entrypoint-initdb.d/event_table.sql new file mode 100644 index 0000000..7786a6c --- /dev/null +++ b/ops/envs/concurrent/cont/db/target/docker-entrypoint-initdb.d/event_table.sql @@ -0,0 +1,85 @@ + +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 9.6.24 +-- Dumped by pg_dump version 9.6.24 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +SET default_tablespace = ''; + +SET default_with_oids = false; + +-- +-- Name: event; Type: TABLE; Schema: public; Owner: adbuser +-- + +CREATE TABLE public.event ( + id integer NOT NULL, + "name" text NOT NULL, + channel text NOT NULL, + correlation_id text NOT NULL, + aggregate_id integer NOT NULL, + aggregate_version integer NOT NULL, + data jsonb NOT NULL, + "timestamp" timestamp(3) without time zone DEFAULT now() NOT NULL, + dispatched boolean DEFAULT false NOT NULL, + dispatched_at timestamp(3) without time zone, + received_at timestamp(3) without time zone, + projected boolean DEFAULT false NOT NULL +); + + +ALTER TABLE public.event OWNER TO adbuser; + +-- +-- Name: event_id_seq; Type: SEQUENCE; Schema: public; Owner: adbuser +-- + +CREATE SEQUENCE public.event_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.event_id_seq OWNER TO adbuser; + +-- +-- Name: event_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: adbuser +-- + +ALTER SEQUENCE public.event_id_seq OWNED BY public.event.id; + + +-- +-- Name: event id; Type: DEFAULT; Schema: public; Owner: adbuser +-- + +ALTER TABLE ONLY public.event ALTER COLUMN id SET DEFAULT nextval('public.event_id_seq'::regclass); + + +-- +-- Name: event event_pkey; Type: CONSTRAINT; Schema: public; Owner: adbuser +-- + +ALTER TABLE ONLY public.event + ADD CONSTRAINT event_pkey PRIMARY KEY (id); + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/ops/envs/concurrent/cont/event-listener/Dockerfile b/ops/envs/concurrent/cont/event-listener/Dockerfile new file mode 100644 index 0000000..e781d74 --- /dev/null +++ b/ops/envs/concurrent/cont/event-listener/Dockerfile @@ -0,0 +1,41 @@ +FROM phpswoole/swoole:4.8-php8.1 + +ENV LIBRDKAFKA_VERSION v1.8.2 +ENV BUILD_DEPS \ + autoconf \ + build-essential \ + git \ + libpcre3-dev \ + python \ + libzip-dev \ + zip \ + git \ + libpq-dev \ + inotify-tools + +RUN apt-get clean && apt-get update \ + && apt-get install -y ${BUILD_DEPS} \ + && docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \ + && docker-php-ext-install zip pgsql pdo_pgsql + +RUN cd /tmp \ + && git clone \ + --branch ${LIBRDKAFKA_VERSION} \ + --depth 1 \ + https://github.com/edenhill/librdkafka.git \ + && cd librdkafka \ + && ./configure \ + && make \ + && make install \ + && pecl install rdkafka \ + && docker-php-ext-enable rdkafka \ + && rm -rf /tmp/librdkafka \ + && apt remove -y build-essential \ + && apt autoremove -y + +COPY target / + +ENV TZ=Europe/Athens +RUN mv /usr/local/etc/php/php.ini-development /usr/local/etc/php/php.ini +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +RUN sed -ri -e 's!;date.timezone =!date.timezone = "Europe/Athens"!g' /usr/local/etc/php/php.ini \ No newline at end of file diff --git a/ops/envs/concurrent/cont/event-listener/target/.gitkeep b/ops/envs/concurrent/cont/event-listener/target/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/01-create-host-user.sh b/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/01-create-host-user.sh new file mode 100644 index 0000000..d0cd332 --- /dev/null +++ b/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/01-create-host-user.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -e +if [[ -z "$HOST_UID" ]]; then + echo "ERROR: please set HOST_UID" >&2 + exit 1 +fi +if [[ -z "$HOST_GID" ]]; then + echo "ERROR: please set HOST_GID" >&2 + exit 1 +fi +if grep -q '^hostuser:' /etc/passwd; then + userdel hostuser +fi +if grep -q "hostgroup" /etc/group; then + groupdel hostgroup +fi +addgroup --gid "$HOST_GID" hostgroup +adduser --uid "$HOST_UID" --gid "$HOST_GID" --gecos "" --home /var/www --disabled-password hostuser diff --git a/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/02-composer-install.sh b/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/02-composer-install.sh new file mode 100755 index 0000000..19d9da5 --- /dev/null +++ b/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/02-composer-install.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +PARENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +if [ ! -f composer.json ]; then + runuser -l hostuser -c "cp $PARENT_PATH/assets/composer.json composer.json" + runuser -l hostuser -c "composer require php-di/php-di doctrine/annotations" + runuser -l hostuser -c "composer require --dev phpunit/phpunit" +fi +runuser -l hostuser -c "composer install --ignore-platform-req=ext-rdkafka" \ No newline at end of file diff --git a/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/03-behat-init.sh b/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/03-behat-init.sh new file mode 100755 index 0000000..1c40d89 --- /dev/null +++ b/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/03-behat-init.sh @@ -0,0 +1,4 @@ +#!/bin/sh +if [ -f vendor/bin/behat ]; then + runuser -l hostuser -c "vendor/bin/behat --init" +fi \ No newline at end of file diff --git a/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/assets/composer.json b/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/assets/composer.json new file mode 100644 index 0000000..57c86f0 --- /dev/null +++ b/ops/envs/concurrent/cont/event-listener/target/usr/local/boot/assets/composer.json @@ -0,0 +1,18 @@ +{ + "name": "evdobe/event-listener", + "description": "A process that listens to a domain event channel, translates and persists events of interest to an event store.", + "authors": [ + { + "name": "Konstantinos Togias", + "email": "info@ktogias.gr" + } + ], + "require": {}, + "autoload": { + "psr-4": { + "Infrastructure\\": "Infrastructure", + "Application\\": "Application", + "Domain\\": "Domain" + } + } +} diff --git a/ops/envs/test/comp/docker-compose.yaml b/ops/envs/test/comp/docker-compose.yaml index 9992f74..7a20e6b 100644 --- a/ops/envs/test/comp/docker-compose.yaml +++ b/ops/envs/test/comp/docker-compose.yaml @@ -69,6 +69,7 @@ services: HOST_GID: ${HOST_UID} HOST_UID: ${HOST_UID} HTTP_PORT: ${HTTP_PORT} + HTTP_HOST: event-listener TZ: ${TIME_ZONE} MESSAGE_BROKER_HOST: ${MESSAGE_BROKER_HOST} MESSAGE_BROKER_PORT: ${MESSAGE_BROKER_PORT}