Skip to content

Commit

Permalink
Plugins can now have options which will be hand over to plugin methods
Browse files Browse the repository at this point in the history
  • Loading branch information
DZunke committed Apr 8, 2024
1 parent 382ab8c commit e767baf
Show file tree
Hide file tree
Showing 20 changed files with 209 additions and 60 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ config file with the CLI Command like `vendor/bin/panaly -c my-own-config.yaml`.

```yaml
plugins: # Registered plugins that deliver single metrics that could be utilized for metric groups
- Namespace/Of/The/Project/FilesystemPlugin # registers a "filesystem_directory_count" and a "fielsystem_file_count" metric
- Namespace/Of/Another/Project/PHPStanBaselinePlugin # registers a simple "phpstan_baseline_total_count" metric
- I/Have/A/Storage/Engine/LocalJsonStoragePlugin # registers a "local_json" storage and also a "metric_history_timeframe" metric that shows from / to string of alltime metric reading
- My/Own/Plugin/HtmlReportPlugin # registers the "my_own_html_reporting" reporting that takes the result collection of the metrics and does something with it
Namespace/Of/The/Project/FilesystemPlugin: ~ # registers a "filesystem_directory_count" and a "fielsystem_file_count" metric
Namespace/Of/Another/Project/PHPStanBaselinePlugin: ~ # registers a simple "phpstan_baseline_total_count" metric
I/Have/A/Storage/Engine/LocalJsonStoragePlugin: ~ # registers a "local_json" storage and also a "metric_history_timeframe" metric that shows from / to string of alltime metric reading
My/Own/Plugin/HtmlReportPlugin: ~ # registers the "my_own_html_reporting" reporting that takes the result collection of the metrics and does something with it

groups:
group1:
Expand All @@ -45,7 +45,7 @@ groups:
paths:
- src
- tests
i_am_a_free_name_that_will_be_lost:
i_am_a_custom_identifier:
metric: fielsystem_file_count # This overwrites the key and is the metric to be utilized
title: "Just test files"
paths:
Expand Down
4 changes: 2 additions & 2 deletions panaly.dist.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins:
- DZunke\PanalyFiles\FilesPlugin
- DZunke\PanalySymfonyDump\SymfonyDumpPlugin
DZunke\PanalyFiles\FilesPlugin: ~
DZunke\PanalySymfonyDump\SymfonyDumpPlugin: ~

groups:
filesystem:
Expand Down
2 changes: 1 addition & 1 deletion src/Collector/Collector.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function collect(): Result
$group = new Group($title);

foreach ($executingGroup->metrics as $executingMetric) {
$metricHandler = $this->runtimeConfiguration->getMetric($executingMetric->identifier);
$metricHandler = $this->runtimeConfiguration->getMetric($executingMetric->metric);
$metricResult = $metricHandler->calculate($executingMetric->options);

$group->addMetric(new Metric(
Expand Down
14 changes: 9 additions & 5 deletions src/Configuration/ConfigurationFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
use Panaly\Configuration\ConfigurationFile\Storage;

use function array_key_exists;
use function array_keys;
use function array_map;
use function array_values;

readonly class ConfigurationFile
{
Expand Down Expand Up @@ -40,15 +42,16 @@ public static function fromArray(array $config): ConfigurationFile
}

/**
* @param list<class-string> $pluginConfig
* @param array<class-string, array|null> $pluginConfig
*
* @return list<Plugin>
*/
private static function buildPluginConfig(array $pluginConfig): array
{
return array_map(
static fn (string $class) => new Plugin($class),
$pluginConfig,
static fn (string $class, array|null $options) => new Plugin($class, $options ?? []),
array_keys($pluginConfig),
array_values($pluginConfig),
);
}

Expand Down Expand Up @@ -115,13 +118,14 @@ private static function convertToMetricConfig(array $metricConfig): array
$options ??= [];
unset($options['title']);

$metric = $identifier;
if (array_key_exists('metric', $options)) {
// Take the metric not from the key but from the option
$identifier = $options['metric'];
$metric = $options['metric'];
unset($options['metric']);
}

$metrics[] = new Metric($identifier, $title, $options);
$metrics[] = new Metric($identifier, $metric, $title, $options);
}

return $metrics;
Expand Down
7 changes: 6 additions & 1 deletion src/Configuration/ConfigurationFile/Metric.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@
{
public function __construct(
public string $identifier,
public string $metric,
public string|null $title,
public array $options,
) {
if ($this->identifier === '') {
throw InvalidConfigurationFile::metricMustNotHaveABlankName();
throw InvalidConfigurationFile::metricMustNotHaveABlankIdentifier();
}

if ($this->metric === '') {
throw InvalidConfigurationFile::metricMustNotHaveABlankMetric();
}
}
}
6 changes: 5 additions & 1 deletion src/Configuration/ConfigurationFile/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@

readonly class Plugin
{
/** @param class-string $class */
/**
* @param class-string $class
* @param array<string, mixed> $options
*/
public function __construct(
public string $class,
public array $options = [],
) {
if (! class_exists($this->class)) {
throw InvalidConfigurationFile::pluginClassNotExists($this->class);
Expand Down
9 changes: 7 additions & 2 deletions src/Configuration/Exception/InvalidConfigurationFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,13 @@ public static function metricGroupMustNotHaveABlankTitle(): InvalidConfiguration
return new self('A metric group configuration must have an non-empty title option.');
}

public static function metricMustNotHaveABlankName(): InvalidConfigurationFile
public static function metricMustNotHaveABlankIdentifier(): InvalidConfigurationFile
{
return new self('A metric configuration must have an non-empty name.');
return new self('A metric configuration must have an non-empty identifier.');
}

public static function metricMustNotHaveABlankMetric(): InvalidConfigurationFile
{
return new self('A metric configuration must have an non-empty metric name.');
}
}
10 changes: 6 additions & 4 deletions src/Configuration/PluginLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,27 @@ public function load(ConfigurationFile $configurationFile, RuntimeConfiguration
throw PluginLoadingFailed::instantiationFailed($plugin->class, $e);
}

$loadedPlugin->initialize($configurationFile, $runtimeConfiguration);
$loadedPlugin->initialize($configurationFile, $runtimeConfiguration, $plugin->options);

$loadedPluginMetrics = $loadedPlugin->getAvailableMetrics();
$loadedPluginMetrics = $loadedPlugin->getAvailableMetrics($plugin->options);
array_walk(
$loadedPluginMetrics,
static fn (Plugin\Metric $metric) => $runtimeConfiguration->addMetric($metric),
);

$loadedPluginStorages = $loadedPlugin->getAvailableStorages();
$loadedPluginStorages = $loadedPlugin->getAvailableStorages($plugin->options);
array_walk(
$loadedPluginStorages,
static fn (Plugin\Storage $storage) => $runtimeConfiguration->addStorage($storage),
);

$loadedPluginReports = $loadedPlugin->getAvailableReporting();
$loadedPluginReports = $loadedPlugin->getAvailableReporting($plugin->options);
array_walk(
$loadedPluginReports,
static fn (Plugin\Reporting $reporting) => $runtimeConfiguration->addReporting($reporting),
);

$runtimeConfiguration->addPlugin($loadedPlugin);
}
}
}
14 changes: 14 additions & 0 deletions src/Configuration/RuntimeConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Panaly\Configuration;

use Panaly\Configuration\Exception\InvalidRuntimeConfiguration;
use Panaly\Plugin\Plugin;
use Panaly\Plugin\Plugin\Metric;
use Panaly\Plugin\Plugin\Reporting;
use Panaly\Plugin\Plugin\Storage;
Expand All @@ -17,6 +18,8 @@ class RuntimeConfiguration
{
private EventDispatcherInterface $eventDispatcher;

/** @var list<Plugin> */
private array $loadedPlugins = [];
/** @var array<string, Metric> */
private array $metrics = [];
/** @var array<string, Storage> */
Expand All @@ -34,6 +37,17 @@ public function getEventDispatcher(): EventDispatcherInterface
return $this->eventDispatcher;
}

public function addPlugin(Plugin $plugin): void
{
$this->loadedPlugins[] = $plugin;
}

/** @return list<Plugin> */
public function getPlugins(): array
{
return $this->loadedPlugins;
}

public function getMetric(string $identifier): Metric
{
if (! $this->hasMetric($identifier)) {
Expand Down
24 changes: 11 additions & 13 deletions src/Plugin/BasePlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,30 @@

use Panaly\Configuration\ConfigurationFile;
use Panaly\Configuration\RuntimeConfiguration;
use Panaly\Plugin\Plugin\Metric;
use Panaly\Plugin\Plugin\Reporting;
use Panaly\Plugin\Plugin\Storage;

abstract class BasePlugin implements Plugin
{
public function initialize(ConfigurationFile $configurationFile, RuntimeConfiguration $runtimeConfiguration): void
{
// Do nothing by design ... it just has to be overwritten when there is something to do
// Idea: Maybe allow also a plugin configuration that could be given here in the future?
public function initialize(
ConfigurationFile $configurationFile,
RuntimeConfiguration $runtimeConfiguration,
array $options,
): void {
}

/** @return list<Metric> */
public function getAvailableMetrics(): array
/** @inheritDoc */
public function getAvailableMetrics(array $options): array
{
return [];
}

/** @return list<Storage> */
public function getAvailableStorages(): array
/** @inheritDoc */
public function getAvailableStorages(array $options): array
{
return [];
}

/** @return list<Reporting> */
public function getAvailableReporting(): array
/** @inheritDoc */
public function getAvailableReporting(array $options): array
{
return [];
}
Expand Down
17 changes: 8 additions & 9 deletions src/Plugin/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,18 @@

interface Plugin
{
/**
* The method is called to give a plugin the possibility to do something with the configuration file and runtime
* configuration on loading. Enables, for example, access to the event dispatcher which is given in the runtime
* configuration and so allows to register listeners or subscriber to it.
*/
public function initialize(ConfigurationFile $configurationFile, RuntimeConfiguration $runtimeConfiguration): void;
public function initialize(
ConfigurationFile $configurationFile,
RuntimeConfiguration $runtimeConfiguration,
array $options,
): void;

/** @return list<Metric> */
public function getAvailableMetrics(): array;
public function getAvailableMetrics(array $options): array;

/** @return list<Storage> */
public function getAvailableStorages(): array;
public function getAvailableStorages(array $options): array;

/** @return list<Reporting> */
public function getAvailableReporting(): array;
public function getAvailableReporting(array $options): array;
}
17 changes: 13 additions & 4 deletions tests/Configuration/ConfigurationFile/MetricTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,27 @@ class MetricTest extends TestCase
{
public function testThatTheObjectCanBeBuild(): void
{
$metric = new Metric('foo', 'bar', []);
$metric = new Metric('baz', 'foo', 'bar', []);

self::assertSame('foo', $metric->identifier);
self::assertSame('baz', $metric->identifier);
self::assertSame('foo', $metric->metric);
self::assertSame('bar', $metric->title);
self::assertSame([], $metric->options);
}

public function testThatTheMetricMustHaveAnIdentifier(): void
{
$this->expectException(InvalidConfigurationFile::class);
$this->expectExceptionMessage('A metric configuration must have an non-empty name.');
$this->expectExceptionMessage('A metric configuration must have an non-empty identifier.');

new Metric('', null, []);
new Metric('', '', null, []);
}

public function testThatTheMetricMustHaveAnMetricDefined(): void
{
$this->expectException(InvalidConfigurationFile::class);
$this->expectExceptionMessage('A metric configuration must have an non-empty metric name.');

new Metric('foo', '', null, []);
}
}
10 changes: 10 additions & 0 deletions tests/Configuration/ConfigurationFile/PluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ public function testThatAPluginCanBeCreated(): void
self::assertSame($pluginClass::class, $plugin->class);
}

public function testThatAPluginCanBeCreatedWithOptions(): void
{
$pluginClass = self::createStub(PluginInterface::class);

$plugin = new Plugin($pluginClass::class, ['foo' => 'bar']);

self::assertSame($pluginClass::class, $plugin->class);
self::assertSame(['foo' => 'bar'], $plugin->options);
}

public function testThatAnExistingClassMustImplementThePluginInterface(): void
{
$this->expectException(InvalidConfigurationFile::class);
Expand Down
4 changes: 2 additions & 2 deletions tests/Configuration/ConfigurationFileLoaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ public function testThatLoadingAValidConfigurationFileWillWork(): void
self::assertSame('Foo Bar Baz', $configuration->metricGroups[0]->title);

self::assertCount(2, $configuration->metricGroups[0]->metrics);
self::assertSame('a_static_integer', $configuration->metricGroups[0]->metrics[0]->identifier);
self::assertSame('a_static_integer', $configuration->metricGroups[0]->metrics[0]->metric);
self::assertSame([], $configuration->metricGroups[0]->metrics[0]->options);

self::assertSame('a_static_integer', $configuration->metricGroups[0]->metrics[1]->identifier);
self::assertSame('a_static_integer', $configuration->metricGroups[0]->metrics[1]->metric);
self::assertSame([], $configuration->metricGroups[0]->metrics[1]->options);

self::assertCount(1, $configuration->storage);
Expand Down
24 changes: 21 additions & 3 deletions tests/Configuration/ConfigurationFileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,24 @@ public function testThatCreatingFromArrayWithoutInputIsValid(): void
self::assertSame([], $configurationFile->reporting);
}

public function testThatCreatingWithPluginsIsValid(): void
public function testThatCreatingWithPluginsButWithoutOptionsIsValid(): void
{
$plugin = self::createStub(Plugin::class);
$configurationFile = ConfigurationFile::fromArray(['plugins' => [$plugin::class]]);
$configurationFile = ConfigurationFile::fromArray(['plugins' => [$plugin::class => null]]);

self::assertCount(1, $configurationFile->plugins);
self::assertSame($plugin::class, $configurationFile->plugins[0]->class);
self::assertSame([], $configurationFile->plugins[0]->options);
}

public function testThatCreatingWithPluginsWithOptionsIsValid(): void
{
$plugin = self::createStub(Plugin::class);
$configurationFile = ConfigurationFile::fromArray(['plugins' => [$plugin::class => ['foo' => 'bar']]]);

self::assertCount(1, $configurationFile->plugins);
self::assertSame($plugin::class, $configurationFile->plugins[0]->class);
self::assertSame(['foo' => 'bar'], $configurationFile->plugins[0]->options);
}

public function testThatCreatingWithMetricGroupsIsValid(): void
Expand All @@ -67,14 +78,21 @@ public function testThatCreatingWithMetricGroupsIsValid(): void

self::assertCount(3, $configurationFile->metricGroups[0]->metrics);

// The "baz" metric
self::assertSame('baz', $configurationFile->metricGroups[0]->metrics[0]->identifier);
self::assertSame('baz', $configurationFile->metricGroups[0]->metrics[0]->metric);
self::assertSame('foo_baz', $configurationFile->metricGroups[0]->metrics[0]->title);
self::assertSame(['foo' => 'bar'], $configurationFile->metricGroups[0]->metrics[0]->options);

// The "quo" metric
self::assertSame('quo', $configurationFile->metricGroups[0]->metrics[1]->identifier);
self::assertSame('quo', $configurationFile->metricGroups[0]->metrics[1]->metric);
self::assertNull($configurationFile->metricGroups[0]->metrics[1]->title);
self::assertSame(['foo' => 'baz'], $configurationFile->metricGroups[0]->metrics[1]->options);

self::assertSame('baz', $configurationFile->metricGroups[0]->metrics[2]->identifier);
// The "also_a_baz_metric"
self::assertSame('also_a_baz_metric', $configurationFile->metricGroups[0]->metrics[2]->identifier);
self::assertSame('baz', $configurationFile->metricGroups[0]->metrics[2]->metric);
}

public function testThatCreatingWithStorageIsValid(): void
Expand Down
Loading

0 comments on commit e767baf

Please sign in to comment.