diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index fdef999..4b99b96 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -2,9 +2,9 @@ name: 'Unit Tests & Static Analysis' on: push: - branches: [ main ] + branches: [ release-1.x, release-2.x ] pull_request: - branches: [ main ] + branches: [ release-1.x, release-2.x ] jobs: continuous-integration: diff --git a/.gitignore b/.gitignore index b053998..449488a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .phpunit.cache .phpunit.result.cache +architectural-decisions.xml.bak vendor/ \ No newline at end of file diff --git a/README.md b/README.md index 703f397..5682cda 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ namespace Acme\ArchitecturalDecisions; use Cspray\ArchitecturalDecision\DecisionStatus; use Cspray\ArchitecturalDecision\DocBlockArchitecturalDecision; use Attribute; +use DateTimeImmutable; /** * Explain the decision and its potential business impact. @@ -46,11 +47,11 @@ use Attribute; #[Attribute] final class MyFirstDecision extends DocBlockArchitecturalDecision { - public function getDate() : string { - return '2022-07-19'; + public function date() : DateTimeImmutable { + return new DateTimeImmutable('2022-07-19'); } - public function getStatus() : string|DecisionStatus { + public function status() : string|DecisionStatus { return DecisionStatus::Draft; } @@ -67,7 +68,7 @@ If successful a file named `architectural-decisions.xml` will be generated and s ### Setting Custom Meta Data -There might be additional information you'd like to include with an ArchitecturalDecisionRecord that doesn't fit into the contents of the decision. Perhaps it is additional data that can be used with static analysis. Perhaps you like to include information about who authored the decision or some other meta-data. You can implement the `ArchitecturalDecisionRecord::setMetaData(DOMElement $meta)` method to add whatever data you'd like to the generated XML document. Please review the [DOMDocument]() documentation for how to appropriately add elements and attribute to the `` element. +There might be additional information you'd like to include with an ArchitecturalDecisionRecord that doesn't fit into the contents of the decision. Perhaps it is additional data that can be used with static analysis. Perhaps you like to include information about who authored the decision or some other meta-data. You can implement the `ArchitecturalDecisionRecord::setMetaData(DOMElement $meta)` method to add whatever data you'd like to the generated XML document. Please review the [DOMDocument](https://www.php.net/domdocument) documentation for how to appropriately add elements and attribute to the `` element. ## Example XML Document diff --git a/bin/architectural-decisions b/bin/architectural-decisions index eae5e6e..f7ed9ec 100755 --- a/bin/architectural-decisions +++ b/bin/architectural-decisions @@ -3,7 +3,6 @@ use Cspray\AnnotatedTarget\PhpParserAnnotatedTargetParser; use Cspray\ArchitecturalDecision\ArchitecturalDecisionAttributeGatherer; -use Cspray\ArchitecturalDecision\Initializer; use Cspray\ArchitecturalDecision\SourceArchitecturalDecisionAttributeRegistry; use Cspray\ArchitecturalDecision\XmlDocumentGenerator; @@ -35,25 +34,13 @@ foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($composerDirs) $paths[] = $rootDir . '/' . $composerDir; } -$initializers = $composer['extra']['$architecturalDecision']['initializers'] ?? []; +$additionalScanPaths = $composer['extra']['$architecturalDecision']['additionalScanPaths'] ?? []; -foreach ($initializers as $initializerClass) { - $initializerClass = (string) $initializerClass; - if (!class_exists($initializerClass)) { - echo 'Your configured initializer ' . $initializerClass . ' is not an autoloadable class.'; - exit(255); - } - - if (!is_subclass_of($initializerClass, Initializer::class)) { - echo 'Your configured initializer ' . $initializerClass . ' MUST extend ' . Initializer::class; - exit(255); - } - - $initializer = new $initializerClass(); - $paths = [...$paths, ...$initializer->getAdditionalScanPaths()]; +foreach ($additionalScanPaths as $additionalScanPath) { + $paths[] = $rootDir . '/' . $additionalScanPath; } -$attributes = (new SourceArchitecturalDecisionAttributeRegistry($paths))->getArchitecturalDecisionAttributes(); +$attributes = (new SourceArchitecturalDecisionAttributeRegistry($paths))->architecturalDecisionAttributes(); if (count($attributes) === 0) { echo 'You must implement an ArchitecturalDecisionRecord in your codebase before running this command!'; exit(255); diff --git a/src/ArchitecturalDecisionAttributeGatherer.php b/src/ArchitecturalDecisionAttributeGatherer.php index 55bb642..9cc8a46 100644 --- a/src/ArchitecturalDecisionAttributeGatherer.php +++ b/src/ArchitecturalDecisionAttributeGatherer.php @@ -21,7 +21,7 @@ public function __construct(private readonly AnnotatedTargetParser $annotatedTar /** * @return list */ - public function getRegisteredAttributes() : array { + public function registeredAttributes() : array { return $this->attributeTypes; } diff --git a/src/ArchitecturalDecisionAttributeRegistry.php b/src/ArchitecturalDecisionAttributeRegistry.php index 8a9ef50..d1ec684 100644 --- a/src/ArchitecturalDecisionAttributeRegistry.php +++ b/src/ArchitecturalDecisionAttributeRegistry.php @@ -9,6 +9,6 @@ interface ArchitecturalDecisionAttributeRegistry { /** * @return list */ - public function getArchitecturalDecisionAttributes() : array; + public function architecturalDecisionAttributes() : array; } \ No newline at end of file diff --git a/src/ArchitecturalDecisionRecord.php b/src/ArchitecturalDecisionRecord.php index 7687d14..ef4f886 100644 --- a/src/ArchitecturalDecisionRecord.php +++ b/src/ArchitecturalDecisionRecord.php @@ -5,6 +5,7 @@ use Cspray\ArchitecturalDecision\ArchitecturalDecisionRecords\ExplicitArchitecturalDecisionStatus; use Cspray\ArchitecturalDecision\ArchitecturalDecisionRecords\PreferCustomArchitecturalDecisionOverGeneric; use Cspray\ArchitecturalDecision\ArchitecturalDecisionRecords\UsingAttributesForArchitecturalDecisions; +use DateTimeImmutable; use DOMElement; #[ExplicitArchitecturalDecisionStatus] @@ -14,14 +15,14 @@ interface ArchitecturalDecisionRecord { const SCHEMA = 'https://architectural-decision.cspray.io/schema/architectural-decision.xsd'; - public function getId() : string; + public function id() : string; - public function getDate() : string; + public function date() : DateTimeImmutable; - public function getStatus() : string|DecisionStatus; + public function status() : string|DecisionStatus; - public function getContents() : string; + public function contents() : string; - public function setMetaData(DOMElement $meta) : void; + public function addMetaData(DOMElement $meta) : void; } \ No newline at end of file diff --git a/src/ArchitecturalDecisionRecords/ExplicitArchitecturalDecisionStatus.php b/src/ArchitecturalDecisionRecords/ExplicitArchitecturalDecisionStatus.php index d505d2d..5d0adea 100644 --- a/src/ArchitecturalDecisionRecords/ExplicitArchitecturalDecisionStatus.php +++ b/src/ArchitecturalDecisionRecords/ExplicitArchitecturalDecisionStatus.php @@ -5,6 +5,8 @@ use Attribute; use Cspray\ArchitecturalDecision\DecisionStatus; use Cspray\ArchitecturalDecision\DocBlockArchitecturalDecision; +use DateTimeImmutable; +use DateTimeZone; /** * # Explicit Architectural Decision Status @@ -37,11 +39,11 @@ #[Attribute(Attribute::TARGET_CLASS)] final class ExplicitArchitecturalDecisionStatus extends DocBlockArchitecturalDecision { - public function getDate() : string { - return '2022-07-19'; + public function date() : DateTimeImmutable { + return new DateTimeImmutable('2022-07-19', new DateTimeZone('America/New_York')); } - public function getStatus() : DecisionStatus { + public function status() : DecisionStatus { return DecisionStatus::Accepted; } } \ No newline at end of file diff --git a/src/ArchitecturalDecisionRecords/PreferCustomArchitecturalDecisionOverGeneric.php b/src/ArchitecturalDecisionRecords/PreferCustomArchitecturalDecisionOverGeneric.php index fc9f28c..e871543 100644 --- a/src/ArchitecturalDecisionRecords/PreferCustomArchitecturalDecisionOverGeneric.php +++ b/src/ArchitecturalDecisionRecords/PreferCustomArchitecturalDecisionOverGeneric.php @@ -5,6 +5,7 @@ use Attribute; use Cspray\ArchitecturalDecision\DecisionStatus; use Cspray\ArchitecturalDecision\DocBlockArchitecturalDecision; +use DateTimeImmutable; /** * # Prefer Custom ArchitecturalDecisionRecord Over Generic @@ -85,11 +86,11 @@ #[Attribute(Attribute::TARGET_CLASS)] final class PreferCustomArchitecturalDecisionOverGeneric extends DocBlockArchitecturalDecision { - public function getDate() : string { - return '2022-07-19'; + public function date() : DateTimeImmutable { + return new DateTimeImmutable('2022-07-19', new \DateTimeZone('America/New_York')); } - public function getStatus() : DecisionStatus { + public function status() : DecisionStatus { return DecisionStatus::Accepted; } } \ No newline at end of file diff --git a/src/ArchitecturalDecisionRecords/UsingAttributesForArchitecturalDecisions.php b/src/ArchitecturalDecisionRecords/UsingAttributesForArchitecturalDecisions.php index 2664c6c..eae4071 100644 --- a/src/ArchitecturalDecisionRecords/UsingAttributesForArchitecturalDecisions.php +++ b/src/ArchitecturalDecisionRecords/UsingAttributesForArchitecturalDecisions.php @@ -5,6 +5,7 @@ use Attribute; use Cspray\ArchitecturalDecision\DecisionStatus; use Cspray\ArchitecturalDecision\DocBlockArchitecturalDecision; +use DateTimeImmutable; /** * # Use Attributes for Architectural Decisions @@ -15,11 +16,11 @@ */ #[Attribute(Attribute::TARGET_ALL)] final class UsingAttributesForArchitecturalDecisions extends DocBlockArchitecturalDecision { - public function getDate() : string { - return '2022-07-19'; + public function date() : DateTimeImmutable { + return new DateTimeImmutable('2022-07-19', new \DateTimeZone('America/New_York')); } - public function getStatus() : DecisionStatus { + public function status() : DecisionStatus { return DecisionStatus::Accepted; } } \ No newline at end of file diff --git a/src/DocBlockArchitecturalDecision.php b/src/DocBlockArchitecturalDecision.php index a80425d..48a6eba 100644 --- a/src/DocBlockArchitecturalDecision.php +++ b/src/DocBlockArchitecturalDecision.php @@ -9,12 +9,12 @@ abstract class DocBlockArchitecturalDecision implements ArchitecturalDecisionRec private ?string $contents = null; - public function getId() : string { + public function id() : string { $parts = explode('\\', static::class); return array_pop($parts); } - final public function getContents() : string { + final public function contents() : string { if (!isset($this->contents)) { $reflection = new \ReflectionClass(static::class); $content = $reflection->getDocComment(); @@ -37,7 +37,7 @@ final public function getContents() : string { return $this->contents; } - public function setMetaData(DOMElement $meta) : void { + public function addMetaData(DOMElement $meta) : void { // noop, override to set your custom meta data } } \ No newline at end of file diff --git a/src/Initializer.php b/src/Initializer.php deleted file mode 100644 index 65b0248..0000000 --- a/src/Initializer.php +++ /dev/null @@ -1,14 +0,0 @@ - - */ - abstract public function getAdditionalScanPaths() : array; - -} diff --git a/src/SourceArchitecturalDecisionAttributeRegistry.php b/src/SourceArchitecturalDecisionAttributeRegistry.php index 2491a0f..54f8995 100644 --- a/src/SourceArchitecturalDecisionAttributeRegistry.php +++ b/src/SourceArchitecturalDecisionAttributeRegistry.php @@ -26,7 +26,7 @@ public function __construct( $this->parser = (new ParserFactory())->createForNewestSupportedVersion(); } - public function getArchitecturalDecisionAttributes() : array { + public function architecturalDecisionAttributes() : array { $gatherer = new class extends NodeVisitorAbstract { /** diff --git a/src/XmlDocumentGenerator.php b/src/XmlDocumentGenerator.php index 164a83c..f2fe6a7 100644 --- a/src/XmlDocumentGenerator.php +++ b/src/XmlDocumentGenerator.php @@ -25,7 +25,7 @@ public function generateDocument(string $file, array $scanDirs) : void { /** @var array}> $decisionAnnotations */ $decisionAnnotations = []; - foreach ($this->gatherer->getRegisteredAttributes() as $attributeType) { + foreach ($this->gatherer->registeredAttributes() as $attributeType) { /** @psalm-var class-string $type */ $type = $attributeType->getName(); $decisionAnnotations[$type] = [ @@ -55,14 +55,14 @@ public function generateDocument(string $file, array $scanDirs) : void { $decisionElement = $dom->createElementNS(ArchitecturalDecisionRecord::SCHEMA, 'architecturalDecision') ); - $decisionElement->setAttribute('id', $attribute->getId()); + $decisionElement->setAttribute('id', $attribute->id()); $decisionElement->setAttribute('attribute', $attribute::class); $decisionElement->appendChild( - $dom->createElementNS(ArchitecturalDecisionRecord::SCHEMA, 'date', $attribute->getDate()) + $dom->createElementNS(ArchitecturalDecisionRecord::SCHEMA, 'date', $attribute->date()->format('Y-m-d')) ); - $status = $attribute->getStatus(); + $status = $attribute->status(); if ($status instanceof DecisionStatus) { $status = $status->value; } @@ -75,7 +75,7 @@ public function generateDocument(string $file, array $scanDirs) : void { ); $contentsNode->appendChild( - $contentsNode->ownerDocument->createCDATASection($attribute->getContents()) + $contentsNode->ownerDocument->createCDATASection($attribute->contents()) ); if ($decisionAnnotation['targets'] !== []) { @@ -158,7 +158,7 @@ public function generateDocument(string $file, array $scanDirs) : void { $meta = $dom->createElementNS(ArchitecturalDecisionRecord::SCHEMA, 'meta') ); - $attribute->setMetaData($meta); + $attribute->addMetaData($meta); } $dom->schemaValidate(dirname(__DIR__) . '/architectural-decision.xsd'); diff --git a/tests/DocBlockArchitecturalDecisionTest.php b/tests/DocBlockArchitecturalDecisionTest.php index 8f7dbbf..d2b8f0c 100644 --- a/tests/DocBlockArchitecturalDecisionTest.php +++ b/tests/DocBlockArchitecturalDecisionTest.php @@ -24,13 +24,13 @@ public function testGetContentsReturnsDocBlock() : void { displayed in the CLI tool explaining the reason for the Architectural Decision. DOC; - self::assertSame($expected, $subject->getContents()); + self::assertSame($expected, $subject->contents()); } public function testGetTitleReturnsConstructorArgument() : void { $subject = new StubDocBlockArchitecturalDecision(); - self::assertSame('stub-attr-id', $subject->getId()); + self::assertSame('stub-attr-id', $subject->id()); } public function testGetContentsMissingDocBlockThrowsException() : void { @@ -39,7 +39,7 @@ public function testGetContentsMissingDocBlockThrowsException() : void { self::expectException(MissingDocBlock::class); self::expectExceptionMessage('Expected to find a DocBlock associated with ' . MissingDocBlockArchitecturalDecision::class); - $subject->getContents(); + $subject->contents(); } } \ No newline at end of file diff --git a/tests/SourceArchitecturalDecisionAttributeRegistryTest.php b/tests/SourceArchitecturalDecisionAttributeRegistryTest.php index 644edb7..5d8213b 100644 --- a/tests/SourceArchitecturalDecisionAttributeRegistryTest.php +++ b/tests/SourceArchitecturalDecisionAttributeRegistryTest.php @@ -18,7 +18,7 @@ final class SourceArchitecturalDecisionAttributeRegistryTest extends TestCase { public function testSourceDirectoryContainsAttributes() : void { $subject = new SourceArchitecturalDecisionAttributeRegistry([__DIR__]); - $attributes = $subject->getArchitecturalDecisionAttributes(); + $attributes = $subject->architecturalDecisionAttributes(); self::assertCount(4, $attributes); diff --git a/tests/Stub/Adr/AnotherDocBlockArchitecturalDecision.php b/tests/Stub/Adr/AnotherDocBlockArchitecturalDecision.php index 60bf90b..b83efbb 100644 --- a/tests/Stub/Adr/AnotherDocBlockArchitecturalDecision.php +++ b/tests/Stub/Adr/AnotherDocBlockArchitecturalDecision.php @@ -4,6 +4,7 @@ use Attribute; use Cspray\ArchitecturalDecision\DocBlockArchitecturalDecision; +use DateTimeImmutable; /** * Another doc block. @@ -11,15 +12,15 @@ #[Attribute(Attribute::TARGET_ALL)] final class AnotherDocBlockArchitecturalDecision extends DocBlockArchitecturalDecision { - public function getId() : string { + public function id() : string { return 'Another DocBlock'; } - public function getDate() : string { - return '1984-01-01'; + public function date() : DateTimeImmutable { + return new DateTimeImmutable('1984-01-01', new \DateTimeZone('America/New_York')); } - public function getStatus() : string { + public function status() : string { return 'Draft'; } } \ No newline at end of file diff --git a/tests/Stub/Adr/StubDocBlockArchitecturalDecision.php b/tests/Stub/Adr/StubDocBlockArchitecturalDecision.php index e317bba..4a5c727 100644 --- a/tests/Stub/Adr/StubDocBlockArchitecturalDecision.php +++ b/tests/Stub/Adr/StubDocBlockArchitecturalDecision.php @@ -15,15 +15,15 @@ #[Attribute(Attribute::TARGET_ALL)] final class StubDocBlockArchitecturalDecision extends DocBlockArchitecturalDecision { - public function getId() : string { + public function id() : string { return 'stub-attr-id'; } - public function getDate() : string { - return '2022-01-01'; + public function date() : \DateTimeImmutable { + return new \DateTimeImmutable('2022-01-01', new \DateTimeZone('America/New_York')); } - public function getStatus() : DecisionStatus { + public function status() : DecisionStatus { return DecisionStatus::Accepted; } } \ No newline at end of file diff --git a/tests/Stub/Adr/StubMetaDataArchitecturalDecision.php b/tests/Stub/Adr/StubMetaDataArchitecturalDecision.php index d4cb6b0..9efd5ed 100644 --- a/tests/Stub/Adr/StubMetaDataArchitecturalDecision.php +++ b/tests/Stub/Adr/StubMetaDataArchitecturalDecision.php @@ -4,6 +4,7 @@ use Cspray\ArchitecturalDecision\DecisionStatus; use Cspray\ArchitecturalDecision\DocBlockArchitecturalDecision; +use DateTimeImmutable; use DOMElement; /** @@ -12,15 +13,15 @@ #[\Attribute(\Attribute::TARGET_CLASS)] final class StubMetaDataArchitecturalDecision extends DocBlockArchitecturalDecision { - public function getDate() : string { - return '2022-07-20'; + public function date() : DateTimeImmutable { + return new DateTimeImmutable('2022-07-20', new \DateTimeZone('America/New_York')); } - public function getStatus() : string|DecisionStatus { + public function status() : string|DecisionStatus { return DecisionStatus::Draft; } - public function setMetaData(DOMElement $meta) : void { + public function addMetaData(DOMElement $meta) : void { $dom = $meta->ownerDocument; $foo = $meta->appendChild( diff --git a/tests/Stub/BadAdr/MissingDocBlockArchitecturalDecision.php b/tests/Stub/BadAdr/MissingDocBlockArchitecturalDecision.php index 1f38dcc..5a53b5c 100644 --- a/tests/Stub/BadAdr/MissingDocBlockArchitecturalDecision.php +++ b/tests/Stub/BadAdr/MissingDocBlockArchitecturalDecision.php @@ -4,15 +4,16 @@ use Attribute; use Cspray\ArchitecturalDecision\DocBlockArchitecturalDecision; +use DateTimeImmutable; #[Attribute(Attribute::TARGET_ALL)] final class MissingDocBlockArchitecturalDecision extends DocBlockArchitecturalDecision { - public function getDate() : string { - return '2016-01-01'; + public function date() : DateTimeImmutable { + return new DateTimeImmutable('2016-01-01', new \DateTimeZone('America/New_York')); } - public function getStatus() : string { + public function status() : string { return 'Rejected'; } } \ No newline at end of file