Skip to content

Commit 5d7f98b

Browse files
authored
Merge pull request #1 from bwaidelich/feature/initial-version
feat: Initial version
2 parents 363fc2e + db2baf4 commit 5d7f98b

31 files changed

+1326
-0
lines changed

.github/FUNDING.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github: [bwaidelich]
2+
custom: ['https://www.paypal.me/bwaidelich']

.github/workflows/php.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
build:
14+
15+
strategy:
16+
matrix:
17+
php-versions: [ '8.3', '8.4' ]
18+
19+
runs-on: ubuntu-latest
20+
21+
steps:
22+
- name: Setup PHP
23+
uses: shivammathur/setup-php@v2
24+
with:
25+
php-version: ${{ matrix.php-versions }}
26+
27+
- name: Checkout code
28+
uses: actions/checkout@v3
29+
30+
# - name: Validate composer.json and composer.lock
31+
# run: composer validate --strict
32+
33+
- name: Cache Composer packages
34+
id: composer-cache
35+
uses: actions/cache@v3
36+
with:
37+
path: vendor
38+
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
39+
restore-keys: |
40+
${{ runner.os }}-php-
41+
42+
- name: Install dependencies
43+
run: composer install --prefer-dist --no-progress
44+
45+
- name: Run tests
46+
run: composer run-script test

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
composer.lock
2+
vendor

composer.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"name": "wwwision/subscription-engine",
3+
"description": "Subscription engine for event-sourced systems",
4+
"type": "package",
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
"name": "Bastian Waidelich",
9+
"email": "b.waidelich@wwwision.de"
10+
}
11+
],
12+
"funding": [
13+
{
14+
"type": "github",
15+
"url": "https://github.com/sponsors/bwaidelich"
16+
},
17+
{
18+
"type": "paypal",
19+
"url": "https://www.paypal.me/bwaidelich"
20+
}
21+
],
22+
"require": {
23+
"php": ">=8.3",
24+
"psr/log": "^1 || ^2 || ^3"
25+
},
26+
"require-dev": {
27+
"roave/security-advisories": "dev-latest",
28+
"phpstan/phpstan": "^2",
29+
"squizlabs/php_codesniffer": "^4.0.x-dev",
30+
"phpunit/phpunit": "^11"
31+
},
32+
"autoload": {
33+
"psr-4": {
34+
"Wwwision\\SubscriptionEngine\\": "src/"
35+
}
36+
},
37+
"autoload-dev": {
38+
"psr-4": {
39+
"Wwwision\\SubscriptionEngine\\Tests\\": "tests/"
40+
}
41+
},
42+
"scripts": {
43+
"test:phpstan": "phpstan",
44+
"test:cs": "phpcs --colors src",
45+
"test:cs:fix": "phpcbf --colors src",
46+
"test": [
47+
"@test:phpstan",
48+
"@test:cs"
49+
]
50+
}
51+
}

phpcs.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" ?>
2+
<ruleset name="PSR2">
3+
<description>The PSR2 coding standard – without line length limitation</description>
4+
<rule ref="PSR2">
5+
<exclude name="Generic.Files.LineLength.TooLong" />
6+
</rule>
7+
</ruleset>

phpstan.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
includes:
2+
- phpstan.neon.dist
3+
parameters:
4+
editorUrlTitle: 'file://%%file%%:%%line%%'
5+
editorUrl: 'file://%%file%%:%%line%%'

phpstan.neon.dist

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
parameters:
2+
level: 9
3+
treatPhpDocTypesAsCertain: false
4+
paths:
5+
- src

phpunit.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd"
4+
bootstrap="vendor/autoload.php"
5+
cacheDirectory=".phpunit.cache"
6+
executionOrder="depends,defects"
7+
requireCoverageMetadata="true"
8+
beStrictAboutCoverageMetadata="true"
9+
beStrictAboutOutputDuringTests="true"
10+
failOnRisky="true"
11+
failOnWarning="true">
12+
<testsuites>
13+
<testsuite name="default">
14+
<directory>tests/unit</directory>
15+
</testsuite>
16+
</testsuites>
17+
18+
<coverage includeUncoveredFiles="false" />
19+
20+
<source restrictDeprecations="true" restrictNotices="true" restrictWarnings="true">
21+
<include>
22+
<directory>src</directory>
23+
</include>
24+
</source>
25+
</phpunit>

src/Engine/Error.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wwwision\SubscriptionEngine\Engine;
6+
7+
use Throwable;
8+
use Wwwision\SubscriptionEngine\Subscription\Position;
9+
use Wwwision\SubscriptionEngine\Subscription\SubscriptionId;
10+
11+
final readonly class Error
12+
{
13+
private function __construct(
14+
public SubscriptionId $subscriptionId,
15+
public string $message,
16+
public Throwable|null $throwable,
17+
public Position|null $position,
18+
) {
19+
}
20+
21+
public static function create(
22+
SubscriptionId $subscriptionId,
23+
string $message,
24+
Throwable|null $throwable,
25+
Position|null $position,
26+
): self {
27+
return new self(
28+
$subscriptionId,
29+
$message,
30+
$throwable,
31+
$position
32+
);
33+
}
34+
}

src/Engine/Errors.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wwwision\SubscriptionEngine\Engine;
6+
7+
use InvalidArgumentException;
8+
9+
/**
10+
* @implements \IteratorAggregate<Error>
11+
*/
12+
final readonly class Errors implements \IteratorAggregate, \Countable
13+
{
14+
private const int CLAMP_ERRORS = 5;
15+
16+
/**
17+
* @var non-empty-array<Error>
18+
*/
19+
private array $errors;
20+
21+
private function __construct(Error ...$errors)
22+
{
23+
if ($errors === []) {
24+
throw new InvalidArgumentException('Errors must not be empty.', 1736159619);
25+
}
26+
$this->errors = array_values($errors);
27+
}
28+
29+
/**
30+
* @param array<Error> $errors
31+
*/
32+
public static function fromArray(array $errors): self
33+
{
34+
return new self(...$errors);
35+
}
36+
37+
public function getIterator(): \Traversable
38+
{
39+
yield from $this->errors;
40+
}
41+
42+
public function count(): int
43+
{
44+
return count($this->errors);
45+
}
46+
47+
public function first(): Error
48+
{
49+
return $this->errors[array_key_first($this->errors)];
50+
}
51+
52+
public function getClampedMessage(): string
53+
{
54+
$additionalMessage = '';
55+
$lines = [];
56+
foreach ($this->errors as $error) {
57+
$lines[] = sprintf('%s"%s": %s', $error->position ? 'Event ' . $error->position->value . ' in ' : '', $error->subscriptionId->value, $error->message);
58+
if (count($lines) >= self::CLAMP_ERRORS) {
59+
$additionalMessage = sprintf('%sAnd %d other exceptions, see log.', ";\n", count($this->errors) - self::CLAMP_ERRORS);
60+
break;
61+
}
62+
}
63+
return implode(";\n", $lines) . $additionalMessage;
64+
}
65+
}

0 commit comments

Comments
 (0)