diff --git a/.env.dist b/.env.dist index 4bcb1b6..811cf8a 100644 --- a/.env.dist +++ b/.env.dist @@ -14,7 +14,7 @@ FORECAST_PROJECT_ID=94336 FORECAST_PERSON_ID=211932 FORECAST_FALLBACK_TASK_ID=2306965 -GITLAB_TOKEN=xxxx +GITLAB_TOKEN=xxxx # read_api access GITLAB_URL=https://gitlab.com GITLAB_PATTERN=ticketnrpattern @@ -22,11 +22,13 @@ MATTERMOST_PATTERN=pow MATTERMOST_HOST=https://mattermost.nxs360.com MATTERMOST_USER_ID=xxxx MATTERMOST_PASSWORD=xxxxx +# https://mattermost.xxxx.com/api/v4/teams + bearer src/ForecastAutomation/MattermostClient/Business/MattermostApi.php:102 MATTERMOST_TEAM_ID=xxxxx LOGGLY_CUSTOMER_TOKEN=xxxxx PERIODICAL_ACTIVITY_CONFIG=[{"note":"Daily","duration":30,"project_id":"94336","frequency":"1,2,3,4,5,6,7"},{"note":"TeamMeeting","duration":30,"project_id":"94336","frequency":"1,2,3,4,5,6,7"}] +#PERIODICAL_ACTIVITY_CONFIG=[{"note":"Daily","duration":15,"project_id":"1701894153411_JTask","frequency":"1,2,3,4,5"},{"note":"Daily","duration":15,"project_id":"1684267402625_JTask","frequency":"1,2,3,4,5"},{"note":"Refinement","duration":60,"project_id":"1684267402625_JTask","frequency":"1"},{"note":"Refinement","duration":60,"project_id":"1701894153411_JTask","frequency":"1"},{"note":"Weekly","duration":60,"project_id":"1684267402625_JTask","frequency":"1"},{"note":"Offene Fragen","duration":60,"project_id":"1722812861432_JTask","frequency":"4"},{"note":"Abstimmung Projekte <> Dennis","duration":30,"project_id":"1650658262819_JTask","frequency":"4"}] KAFKA_BROKER_LIST=localhost:9092 @@ -35,3 +37,7 @@ AMQP_PORT=5672 AMQP_VHOST=/ AMQP_USER=guest AMQP_PASS=guest + +PROJEKTRON_API_ENDPOINT=https://projektxxxxxxxxom/bcs/taskdetail/effortrecording/edit +PROJEKTRON_USERNAME=Patxxxxxeld@cexxxxxxxc.com +PROJEKTRON_COOKIE_HEADER_VALUE="_gcl_au=1.1.1981414287.1723202755; __qca=P0-1366456151-1723202755461; TABLET=false; COLOR_SCHEME=Light; BROWSER_ENCRYPTION_KEY=vtzQfWGcFnlGthQeytSHMA4JK7KsRufo88WdH+Wq0WQ=; OAuthCSRFState=ON37WtPOoO2RHsmEH1-UUzQTYfbcek8awCA2Z414eDk; JSESSIONID=2A5D40360E279xxxxxxx; CSRF_Token=ui81iqm7pLqSpxxxxxxx" diff --git a/README.md b/README.md index b0fa113..30699de 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ With this application, you can automate your time accounting and have clear fair (will create an activity if you [comment or approve](src/ForecastAutomation/GitlabClient/Shared/Plugin/GitlabActivityPlugin.php#L25) a MR by consuming gitlab event api) - [mattermost](src/ForecastAutomation/MattermostClient/Shared/Plugin/MattermostActivityPlugin.php) (will create an activity if you [direct message](src/ForecastAutomation/MattermostClient/Business/MattermostApi.php#L112) a text that [contains a ticketnumber](src/ForecastAutomation/MattermostClient/Shared/Plugin/MattermostActivityPlugin.php#L53)) - - [periodical activity](src/ForecastAutomation/PeriodicalActivityDataImport/Shared/Plugin/PeriodicalActivityDataImportConsoleCommandPlugin.php) + - [periodical activity](src/ForecastAutomation/PeriodicalActivity/Shared/Plugin/PeriodicalActivityDataImportConsoleCommandPlugin.php) (will create weekly repeating activities based on [configuration](.env.dist#L29)) ### ✨ How to use - run `composer run import` at the end of the day (date('Y-m-d 00:00') used by default) to fill your forecast.it timesheet with your [enabled activity plugins](src/ForecastAutomation/Activity/ActivityDependencyProvider.php) diff --git a/composer.json b/composer.json index 3dc3e35..473a19c 100644 --- a/composer.json +++ b/composer.json @@ -6,10 +6,14 @@ "platform": { "php": "8.0", "ext-amqp": "1.11.0beta" + }, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true, + "infection/extension-installer": true } }, "require": { - "lesstif/php-jira-rest-client": "^2.5", + "lesstif/php-jira-rest-client": "^5.8", "guzzlehttp/guzzle": "^7.3", "symfony/dotenv": "^5.3", "vlucas/phpdotenv": "^5.3", diff --git a/composer.lock b/composer.lock index 988459b..8b493fe 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b97834e3b3572557f2088e2d5468a291", + "content-hash": "2f49d6051a899ac30b6c85c40691f715", "packages": [ { "name": "enqueue/amqp-ext", @@ -551,31 +551,33 @@ }, { "name": "lesstif/php-jira-rest-client", - "version": "2.6.0", + "version": "5.8.0", "source": { "type": "git", "url": "https://github.com/lesstif/php-jira-rest-client.git", - "reference": "e650cf1eb9eddbae7f9560b2b161a643caaad4ca" + "reference": "db8b8d0a8b1aaf30cfe9872633d5e43056f4d7de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lesstif/php-jira-rest-client/zipball/e650cf1eb9eddbae7f9560b2b161a643caaad4ca", - "reference": "e650cf1eb9eddbae7f9560b2b161a643caaad4ca", + "url": "https://api.github.com/repos/lesstif/php-jira-rest-client/zipball/db8b8d0a8b1aaf30cfe9872633d5e43056f4d7de", + "reference": "db8b8d0a8b1aaf30cfe9872633d5e43056f4d7de", "shasum": "" }, "require": { "ext-curl": "*", "ext-json": "*", - "monolog/monolog": "~1.12|^2.0", - "netresearch/jsonmapper": "^2.0|^3.0|^4.0", - "php": "^7.1|^8.0", - "vlucas/phpdotenv": "^3.0|^4.0|^5.0" + "monolog/monolog": "^2.0|^3.0", + "netresearch/jsonmapper": "^4.2", + "php": "^8.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpstan/phpstan": "^0.12.23", - "phpunit/phpunit": "^7.0 || ^8.0", - "symfony/var-dumper": "~2.8|~3.0" + "mockery/mockery": "^1.0|^2.0", + "phpstan/phpstan": "^1.0|^2.0", + "phpunit/phpunit": "^9.0|^10.0", + "symfony/var-dumper": "^5.0|^6.0|^7.0" + }, + "suggest": { + "vlucas/phpdotenv": "^5.0|^6.0" }, "type": "library", "extra": { @@ -610,22 +612,22 @@ ], "support": { "issues": "https://github.com/lesstif/php-jira-rest-client/issues", - "source": "https://github.com/lesstif/php-jira-rest-client/tree/2.6.0" + "source": "https://github.com/lesstif/php-jira-rest-client/tree/5.8.0" }, - "time": "2021-09-21T14:42:49+00:00" + "time": "2024-03-24T12:31:56+00:00" }, { "name": "monolog/monolog", - "version": "2.3.5", + "version": "2.9.3", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "fd4380d6fc37626e2f799f29d91195040137eba9" + "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd4380d6fc37626e2f799f29d91195040137eba9", - "reference": "fd4380d6fc37626e2f799f29d91195040137eba9", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/a30bfe2e142720dfa990d0a7e573997f5d884215", + "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215", "shasum": "" }, "require": { @@ -638,18 +640,22 @@ "require-dev": { "aws/aws-sdk-php": "^2.4.9 || ^3.0", "doctrine/couchdb": "~1.0@dev", - "elasticsearch/elasticsearch": "^7", - "graylog2/gelf-php": "^1.4.2", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2@dev", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", "mongodb/mongodb": "^1.8", "php-amqplib/php-amqplib": "~2.4 || ^3", - "php-console/php-console": "^3.1.3", - "phpspec/prophecy": "^1.6.1", - "phpstan/phpstan": "^0.12.91", - "phpunit/phpunit": "^8.5", - "predis/predis": "^1.1", - "rollbar/rollbar": "^1.3", - "ruflin/elastica": ">=0.90@dev", - "swiftmailer/swiftmailer": "^5.3|^6.0" + "phpspec/prophecy": "^1.15", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.38 || ^9.6.19", + "predis/predis": "^1.1 || ^2.0", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" }, "suggest": { "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", @@ -664,7 +670,6 @@ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", "rollbar/rollbar": "Allow sending log messages to Rollbar", "ruflin/elastica": "Allow sending log messages to an Elastic Search server" }, @@ -699,7 +704,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.3.5" + "source": "https://github.com/Seldaek/monolog/tree/2.9.3" }, "funding": [ { @@ -711,20 +716,20 @@ "type": "tidelift" } ], - "time": "2021-10-01T21:08:31+00:00" + "time": "2024-04-12T20:52:51+00:00" }, { "name": "netresearch/jsonmapper", - "version": "v4.0.0", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d" + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", - "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0", "shasum": "" }, "require": { @@ -735,7 +740,7 @@ "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", "squizlabs/php_codesniffer": "~3.5" }, "type": "library", @@ -760,9 +765,9 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1" }, - "time": "2020-12-01T19:48:11+00:00" + "time": "2024-01-31T06:18:54+00:00" }, { "name": "phpoption/phpoption", @@ -7458,5 +7463,5 @@ "php": "8.0", "ext-amqp": "1.11.0beta" }, - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.6.0" } diff --git a/src/ForecastAutomation/Activity/ActivityDependencyProvider.php b/src/ForecastAutomation/Activity/ActivityDependencyProvider.php index 5e22d0f..5abbb68 100644 --- a/src/ForecastAutomation/Activity/ActivityDependencyProvider.php +++ b/src/ForecastAutomation/Activity/ActivityDependencyProvider.php @@ -19,19 +19,24 @@ use ForecastAutomation\MattermostClient\Shared\Plugin\Filter\HasMessageChannelFilter; use ForecastAutomation\MattermostClient\Shared\Plugin\Filter\IsDirectChannelFilter; use ForecastAutomation\MattermostClient\Shared\Plugin\MattermostActivityPlugin; +use ForecastAutomation\PeriodicalActivity\Shared\Plugin\PeriodicalActivityPlugin; +use ForecastAutomation\QueueClient\QueueClientFacade; class ActivityDependencyProvider extends AbstractDependencyProvider { public const ACTIVITY_PLUGINS = 'ACTIVITY_PLUGINS'; + public const QUEUE_CLIENT_FACADE = 'QUEUE_CLIENT_FACADE'; public function provideDependencies(Locator $locator): void { $this->set(self::ACTIVITY_PLUGINS, $this->getActivityPlugins()); + $this->set(self::QUEUE_CLIENT_FACADE, new QueueClientFacade()); } public function getActivityPlugins(): ActivityPluginCollection { return new ActivityPluginCollection( + new PeriodicalActivityPlugin(), new JiraActivityPlugin(), new MattermostActivityPlugin( new HasMessageChannelFilter(new \DateTime(date('Y-m-d'))), @@ -42,4 +47,6 @@ public function getActivityPlugins(): ActivityPluginCollection ; } + + //ToDo getActivityOutputPlugins -> queueActivityOutputPlugin } diff --git a/src/ForecastAutomation/Activity/ActivityFacade.php b/src/ForecastAutomation/Activity/ActivityFacade.php index d17d642..fb40ea9 100644 --- a/src/ForecastAutomation/Activity/ActivityFacade.php +++ b/src/ForecastAutomation/Activity/ActivityFacade.php @@ -23,4 +23,9 @@ public function collect(): ActivityDtoCollection { return $this->getFactory()->createActivityCollector()->collect(); } + + public function send(ActivityDtoCollection $activityDtoCollection): int + { + return $this->getFactory()->createActivitySendQueueProcess()->send($activityDtoCollection); + } } diff --git a/src/ForecastAutomation/Activity/ActivityFactory.php b/src/ForecastAutomation/Activity/ActivityFactory.php index 739fa11..7ebf565 100644 --- a/src/ForecastAutomation/Activity/ActivityFactory.php +++ b/src/ForecastAutomation/Activity/ActivityFactory.php @@ -12,8 +12,11 @@ namespace ForecastAutomation\Activity; use ForecastAutomation\Activity\Business\ActivityCollector; +use ForecastAutomation\Activity\Business\ActivitySendQueueProcess; use ForecastAutomation\Activity\Shared\Plugin\ActivityPluginCollection; +use ForecastAutomation\ForecastDataImport\ForecastDataImportDependencyProvider; use ForecastAutomation\Kernel\AbstractFactory; +use ForecastAutomation\QueueClient\QueueClientFacade; class ActivityFactory extends AbstractFactory { @@ -26,4 +29,16 @@ public function createActivityCollector(): ActivityCollector { return new ActivityCollector($this->getActivityPlugins()); } + + public function createActivitySendQueueProcess(): ActivitySendQueueProcess + { + return new ActivitySendQueueProcess( + $this->getQueueClientFacade(), + ); + } + + public function getQueueClientFacade(): QueueClientFacade + { + return $this->getProvidedDependency(ActivityDependencyProvider::QUEUE_CLIENT_FACADE); + } } diff --git a/src/ForecastAutomation/Activity/Business/ActivityCollector.php b/src/ForecastAutomation/Activity/Business/ActivityCollector.php index 6b96318..3e7428c 100644 --- a/src/ForecastAutomation/Activity/Business/ActivityCollector.php +++ b/src/ForecastAutomation/Activity/Business/ActivityCollector.php @@ -32,7 +32,7 @@ private function mergeActivityDtoCollections(array $activityDtoCollections): Act { $mergedActivityDtoCollection = new ActivityDtoCollection(); foreach ($activityDtoCollections as $activityDtoCollection) { - $mergedActivityDtoCollection->merge($activityDtoCollection); + $mergedActivityDtoCollection->mergeFreedom($activityDtoCollection); } return $mergedActivityDtoCollection; diff --git a/src/ForecastAutomation/ForecastDataImport/Business/ForecastDataImportProcess.php b/src/ForecastAutomation/Activity/Business/ActivitySendQueueProcess.php similarity index 69% rename from src/ForecastAutomation/ForecastDataImport/Business/ForecastDataImportProcess.php rename to src/ForecastAutomation/Activity/Business/ActivitySendQueueProcess.php index 5fdfc6d..152f640 100644 --- a/src/ForecastAutomation/ForecastDataImport/Business/ForecastDataImportProcess.php +++ b/src/ForecastAutomation/Activity/Business/ActivitySendQueueProcess.php @@ -9,28 +9,25 @@ * with this source code in the file LICENSE. */ -namespace ForecastAutomation\ForecastDataImport\Business; +namespace ForecastAutomation\Activity\Business; -use ForecastAutomation\Activity\ActivityFacade; use ForecastAutomation\Activity\Shared\Dto\ActivityDtoCollection; -use ForecastAutomation\ForecastClient\Shared\Config\ForecastClientQueueConstants; +use ForecastAutomation\ProjektronClient\Shared\Config\ProjektronClientQueueConstants; use ForecastAutomation\QueueClient\QueueClientFacade; use ForecastAutomation\QueueClient\Shared\Dto\MessageCollectionDto; use ForecastAutomation\QueueClient\Shared\Dto\MessageDto; -class ForecastDataImportProcess +class ActivitySendQueueProcess { public function __construct( - private ActivityFacade $activityFacade, private QueueClientFacade $queueClientFacade, ) { } - public function start(): int + public function send(ActivityDtoCollection $activityDtoCollection): int { - $activityDtoCollection = $this->activityFacade->collect(); $this->queueClientFacade->sendMessages( - ForecastClientQueueConstants::QUEUE_NAME, + ProjektronClientQueueConstants::QUEUE_NAME, $this->createMessageCollectionDto($activityDtoCollection) ); @@ -44,8 +41,8 @@ private function createMessageCollectionDto(ActivityDtoCollection $activityDtoCo $messages[] = new MessageDto( ['created' => $activityDto->created->format('c')] + (array) $activityDto, - ForecastClientQueueConstants::QUEUE_NAME, - ForecastClientQueueConstants::IMPORT_EVENT + ProjektronClientQueueConstants::QUEUE_NAME, // ToDo: Move to output plugin + ProjektronClientQueueConstants::IMPORT_EVENT ); } diff --git a/src/ForecastAutomation/Activity/Shared/Dto/ActivityDtoCollection.php b/src/ForecastAutomation/Activity/Shared/Dto/ActivityDtoCollection.php index 0c52f98..0183c13 100644 --- a/src/ForecastAutomation/Activity/Shared/Dto/ActivityDtoCollection.php +++ b/src/ForecastAutomation/Activity/Shared/Dto/ActivityDtoCollection.php @@ -89,6 +89,15 @@ public function merge(self $activityDtoCollection): self return $this; } + public function mergeFreedom(self $activityDtoCollection): self + { + foreach ($activityDtoCollection as $activityDto) { + $this->activityDtos[] = $activityDto; + } + + return $this; + } + private function sumDurationIfExist(ActivityDto $activityDto): bool { $exist = false; diff --git a/src/ForecastAutomation/ForecastDataImport/Shared/Plugin/ForecastImportActivityConsoleCommandPlugin.php b/src/ForecastAutomation/Activity/Shared/Plugin/ImportActivityConsoleCommandPlugin.php similarity index 62% rename from src/ForecastAutomation/ForecastDataImport/Shared/Plugin/ForecastImportActivityConsoleCommandPlugin.php rename to src/ForecastAutomation/Activity/Shared/Plugin/ImportActivityConsoleCommandPlugin.php index b507425..df5f458 100644 --- a/src/ForecastAutomation/ForecastDataImport/Shared/Plugin/ForecastImportActivityConsoleCommandPlugin.php +++ b/src/ForecastAutomation/Activity/Shared/Plugin/ImportActivityConsoleCommandPlugin.php @@ -9,19 +9,19 @@ * with this source code in the file LICENSE. */ -namespace ForecastAutomation\ForecastDataImport\Shared\Plugin; +namespace ForecastAutomation\Activity\Shared\Plugin; use ForecastAutomation\Kernel\Shared\Plugin\AbstractCommandPlugin; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; /** - * @method \ForecastAutomation\ForecastDataImport\ForecastDataImportFacade getFacade() + * @method \ForecastAutomation\Activity\ActivityFacade getFacade() */ -class ForecastImportActivityConsoleCommandPlugin extends AbstractCommandPlugin +class ImportActivityConsoleCommandPlugin extends AbstractCommandPlugin { - public const COMMAND_NAME = 'forecast:import:activity'; - public const DESCRIPTION = 'This command will import your forecast.it activity, based on activated plugins.'; + public const COMMAND_NAME = 'import:activity'; + public const DESCRIPTION = 'This command will import your personal activity based on activated plugins.'; protected function configure(): void { @@ -33,7 +33,9 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $this->getFacade()->startImportProcess(); + $this->getFacade()->send( + $this->getFacade()->collect() + ); return self::SUCCESS; } diff --git a/src/ForecastAutomation/AmqpClient/AmqpClientDependencyProvider.php b/src/ForecastAutomation/AmqpClient/AmqpClientDependencyProvider.php index 21512d1..cf01697 100644 --- a/src/ForecastAutomation/AmqpClient/AmqpClientDependencyProvider.php +++ b/src/ForecastAutomation/AmqpClient/AmqpClientDependencyProvider.php @@ -15,6 +15,7 @@ use ForecastAutomation\Kernel\AbstractDependencyProvider; use ForecastAutomation\Kernel\Locator; use ForecastAutomation\Log\LogFacade; +use ForecastAutomation\ProjektronClient\Shared\Plugin\ProjektronClientQueuePluginPlugin; use ForecastAutomation\QueueClient\Shared\Plugin\QueuePluginCollection; use ForecastAutomation\Serializer\SerializerFacade; @@ -36,7 +37,8 @@ public function provideDependencies(Locator $locator): void protected function createQueuePluginCollection(): QueuePluginCollection { return new QueuePluginCollection( - new ForecastClientQueuePluginPlugin(), +// new ForecastClientQueuePluginPlugin(), + new ProjektronClientQueuePluginPlugin(), ); } } diff --git a/src/ForecastAutomation/Console/ConsoleDependencyProvider.php b/src/ForecastAutomation/Console/ConsoleDependencyProvider.php index e2d2b90..3603af7 100644 --- a/src/ForecastAutomation/Console/ConsoleDependencyProvider.php +++ b/src/ForecastAutomation/Console/ConsoleDependencyProvider.php @@ -11,10 +11,9 @@ namespace ForecastAutomation\Console; -use ForecastAutomation\ForecastDataImport\Shared\Plugin\ForecastImportActivityConsoleCommandPlugin; +use ForecastAutomation\Activity\Shared\Plugin\ImportActivityConsoleCommandPlugin; use ForecastAutomation\Kernel\AbstractDependencyProvider; use ForecastAutomation\Kernel\Locator; -use ForecastAutomation\PeriodicalActivityDataImport\Shared\Plugin\PeriodicalActivityDataImportConsoleCommandPlugin; use ForecastAutomation\QueueClient\Shared\Plugin\QueueClientConsumerConsoleCommandPlugin; class ConsoleDependencyProvider extends AbstractDependencyProvider @@ -26,25 +25,19 @@ public function provideDependencies(Locator $locator): void $this->set( self::CONSOLE_PLUGINS, [ - $this->createForecastImportActivityConsoleCommandPlugin(), - $this->creatPeriodicalActivityConsoleCommandPlugin(), + $this->createImportActivityConsoleCommandPlugin(), $this->createQueueClientConsumerConsoleCommandPlugin(), ] ); } - protected function createForecastImportActivityConsoleCommandPlugin(): ForecastImportActivityConsoleCommandPlugin + protected function createImportActivityConsoleCommandPlugin(): ImportActivityConsoleCommandPlugin { - return new ForecastImportActivityConsoleCommandPlugin(); + return new ImportActivityConsoleCommandPlugin(); } protected function createQueueClientConsumerConsoleCommandPlugin(): QueueClientConsumerConsoleCommandPlugin { return new QueueClientConsumerConsoleCommandPlugin(); } - - protected function creatPeriodicalActivityConsoleCommandPlugin(): PeriodicalActivityDataImportConsoleCommandPlugin - { - return new PeriodicalActivityDataImportConsoleCommandPlugin(); - } } diff --git a/src/ForecastAutomation/ForecastClient/ForecastClientFactory.php b/src/ForecastAutomation/ForecastClient/ForecastClientFactory.php index 447a4a7..90a8343 100644 --- a/src/ForecastAutomation/ForecastClient/ForecastClientFactory.php +++ b/src/ForecastAutomation/ForecastClient/ForecastClientFactory.php @@ -12,7 +12,7 @@ namespace ForecastAutomation\ForecastClient; use ForecastAutomation\Cache\CacheFacade; -use ForecastAutomation\ForecastClient\Business\ForecastApi; +use ForecastAutomation\ForecastClient\Business\ProjektronApi; use ForecastAutomation\ForecastClient\Shared\Dto\ForecastConfigDto; use ForecastAutomation\Kernel\AbstractFactory; use ForecastAutomation\Log\LogFacade; @@ -31,9 +31,9 @@ public function createForecastConfigDto(): ForecastConfigDto ); } - public function createForecastApi(): ForecastApi + public function createForecastApi(): ProjektronApi { - return new ForecastApi( + return new ProjektronApi( $this->createClient(), $this->createForecastConfigDto(), $this->getLogFacade(), diff --git a/src/ForecastAutomation/ForecastDataImport/ForecastDataImportDependencyProvider.php b/src/ForecastAutomation/ForecastDataImport/ForecastDataImportDependencyProvider.php deleted file mode 100644 index 1df7f3f..0000000 --- a/src/ForecastAutomation/ForecastDataImport/ForecastDataImportDependencyProvider.php +++ /dev/null @@ -1,29 +0,0 @@ - - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace ForecastAutomation\ForecastDataImport; - -use ForecastAutomation\Activity\ActivityFacade; -use ForecastAutomation\Kernel\AbstractDependencyProvider; -use ForecastAutomation\Kernel\Locator; -use ForecastAutomation\QueueClient\QueueClientFacade; - -class ForecastDataImportDependencyProvider extends AbstractDependencyProvider -{ - public const ACTIVITY_FACADE = 'ACTIVITY_FACADE'; - public const QUEUE_CLIENT_FACADE = 'QUEUE_CLIENT_FACADE'; - - public function provideDependencies(Locator $locator): void - { - $this->set(self::ACTIVITY_FACADE, new ActivityFacade()); - $this->set(self::QUEUE_CLIENT_FACADE, new QueueClientFacade()); - } -} diff --git a/src/ForecastAutomation/ForecastDataImport/ForecastDataImportFacade.php b/src/ForecastAutomation/ForecastDataImport/ForecastDataImportFacade.php deleted file mode 100644 index b7b749d..0000000 --- a/src/ForecastAutomation/ForecastDataImport/ForecastDataImportFacade.php +++ /dev/null @@ -1,25 +0,0 @@ - - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace ForecastAutomation\ForecastDataImport; - -use ForecastAutomation\Kernel\AbstractFacade; - -/** - * @method \ForecastAutomation\ForecastDataImport\ForecastDataImportFactory getFactory() - */ -class ForecastDataImportFacade extends AbstractFacade -{ - public function startImportProcess(): int - { - return $this->getFactory()->createForecastDataImportProcess()->start(); - } -} diff --git a/src/ForecastAutomation/ForecastDataImport/ForecastDataImportFactory.php b/src/ForecastAutomation/ForecastDataImport/ForecastDataImportFactory.php deleted file mode 100644 index 5ed89f7..0000000 --- a/src/ForecastAutomation/ForecastDataImport/ForecastDataImportFactory.php +++ /dev/null @@ -1,38 +0,0 @@ - - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace ForecastAutomation\ForecastDataImport; - -use ForecastAutomation\Activity\ActivityFacade; -use ForecastAutomation\ForecastDataImport\Business\ForecastDataImportProcess; -use ForecastAutomation\Kernel\AbstractFactory; -use ForecastAutomation\QueueClient\QueueClientFacade; - -class ForecastDataImportFactory extends AbstractFactory -{ - public function createForecastDataImportProcess(): ForecastDataImportProcess - { - return new ForecastDataImportProcess( - $this->getActivityFacade(), - $this->getQueueClientFacade(), - ); - } - - public function getActivityFacade(): ActivityFacade - { - return $this->getProvidedDependency(ForecastDataImportDependencyProvider::ACTIVITY_FACADE); - } - - public function getQueueClientFacade(): QueueClientFacade - { - return $this->getProvidedDependency(ForecastDataImportDependencyProvider::QUEUE_CLIENT_FACADE); - } -} diff --git a/src/ForecastAutomation/JiraClient/Business/JiraCollector.php b/src/ForecastAutomation/JiraClient/Business/JiraCollector.php index 10bbb5f..729bde7 100644 --- a/src/ForecastAutomation/JiraClient/Business/JiraCollector.php +++ b/src/ForecastAutomation/JiraClient/Business/JiraCollector.php @@ -40,7 +40,8 @@ public function getComments(string $startDate): array foreach ($ticketList as $issueKey) { $comments = $this->jiraClient->getComments($issueKey); foreach ($comments->comments as $comment) { - if (date('Y-m-d H:i', strtotime($comment->updated)) >= $startDate + $passedDate = \DateTime::createFromFormat('Y-m-d H:i', $startDate); + if (($comment->updated >= $passedDate) && (isset($comment->author->emailAddress) && $comment->author->emailAddress === $this->jiraConfigDto->jiraUser)) { $jiraActivities[$issueKey][] = $comment; } diff --git a/src/ForecastAutomation/JiraClient/JiraClientFactory.php b/src/ForecastAutomation/JiraClient/JiraClientFactory.php index 9de8995..e8d63b1 100644 --- a/src/ForecastAutomation/JiraClient/JiraClientFactory.php +++ b/src/ForecastAutomation/JiraClient/JiraClientFactory.php @@ -22,9 +22,10 @@ class JiraClientFactory extends AbstractFactory public function createJiraConfigDto(): JiraConfigDto { return new JiraConfigDto( - $_ENV['JIRA_TOKEN'], $_ENV['JIRA_HOST'], $_ENV['JIRA_USER'], + true, + $_ENV['JIRA_TOKEN'], (int) $_ENV['JIRA_MAX_RESULTS'], $_ENV['JIRA_QUERY'], ); @@ -32,7 +33,13 @@ public function createJiraConfigDto(): JiraConfigDto public function createJiraClient(): IssueService { - return new IssueService(new ArrayConfiguration((array) $this->createJiraConfigDto())); + return new IssueService(new ArrayConfiguration( + [ + 'jiraHost' => $this->createJiraConfigDto()->jiraHost, + 'useTokenBasedAuth' => $this->createJiraConfigDto()->useTokenBasedAuth, + 'personalAccessToken' => $this->createJiraConfigDto()->personalAccessToken, + ] + )); } public function createJiraCollector(): JiraCollector diff --git a/src/ForecastAutomation/JiraClient/Shared/Dto/JiraConfigDto.php b/src/ForecastAutomation/JiraClient/Shared/Dto/JiraConfigDto.php index 715c2bf..d54700b 100644 --- a/src/ForecastAutomation/JiraClient/Shared/Dto/JiraConfigDto.php +++ b/src/ForecastAutomation/JiraClient/Shared/Dto/JiraConfigDto.php @@ -16,9 +16,10 @@ class JiraConfigDto extends AbstractDto { public function __construct( - public string $jiraPassword, public string $jiraHost, public string $jiraUser, + public bool $useTokenBasedAuth, + public string $personalAccessToken, public int $jiraMaxResults, public string $jiraQuery ) { diff --git a/src/ForecastAutomation/JiraClient/Shared/Plugin/JiraActivityPlugin.php b/src/ForecastAutomation/JiraClient/Shared/Plugin/JiraActivityPlugin.php index f8b65a9..d17ac1f 100644 --- a/src/ForecastAutomation/JiraClient/Shared/Plugin/JiraActivityPlugin.php +++ b/src/ForecastAutomation/JiraClient/Shared/Plugin/JiraActivityPlugin.php @@ -50,7 +50,7 @@ private function createActivityDtoCollection(array $jiraComments): ActivityDtoCo $jiraTicketNumber, sprintf('%s...', substr(preg_replace('/\[[^)]+\]/', '', $jiraComment[0]->body), 0, 60)) ), - new \DateTime($jiraComment[0]->updated), + $jiraComment[0]->updated, self::ACTIVITY_DURATION * \count($jiraComment) ); } diff --git a/src/ForecastAutomation/MattermostClient/Shared/Plugin/MattermostActivityPlugin.php b/src/ForecastAutomation/MattermostClient/Shared/Plugin/MattermostActivityPlugin.php index fc1cad5..d0e270a 100644 --- a/src/ForecastAutomation/MattermostClient/Shared/Plugin/MattermostActivityPlugin.php +++ b/src/ForecastAutomation/MattermostClient/Shared/Plugin/MattermostActivityPlugin.php @@ -94,10 +94,10 @@ private function mapEventsToActivity(array $filteredPostsCollection): ActivityDt private function getNeedle(string $target_title): string { - $matchPattern = sprintf('(%s-[0-9]{1,})i', $_ENV['GITLAB_PATTERN']); + $matchPattern = sprintf('(%s-[0-9]{1,})i', $_ENV['MATTERMOST_PATTERN']); $resultMatch = preg_match($matchPattern, $target_title, $match); if (0 === $resultMatch || ! isset($match[0])) { - throw new \Exception('gitlab needle not found for target_title: '.$target_title); + throw new \Exception('needle not found for target_title: '.$target_title); } return strtoupper($match[0]); diff --git a/src/ForecastAutomation/PeriodicalActivityDataImport/Business/PeriodicalActivityConfigReader.php b/src/ForecastAutomation/PeriodicalActivity/Business/PeriodicalActivityConfigReader.php similarity index 95% rename from src/ForecastAutomation/PeriodicalActivityDataImport/Business/PeriodicalActivityConfigReader.php rename to src/ForecastAutomation/PeriodicalActivity/Business/PeriodicalActivityConfigReader.php index 4514243..57e02c7 100644 --- a/src/ForecastAutomation/PeriodicalActivityDataImport/Business/PeriodicalActivityConfigReader.php +++ b/src/ForecastAutomation/PeriodicalActivity/Business/PeriodicalActivityConfigReader.php @@ -9,10 +9,10 @@ * with this source code in the file LICENSE. */ -namespace ForecastAutomation\PeriodicalActivityDataImport\Business; +namespace ForecastAutomation\PeriodicalActivity\Business; use ForecastAutomation\Log\LogFacade; -use ForecastAutomation\PeriodicalActivityDataImport\Shared\Dto\PeriodicalActivityConfigDto; +use ForecastAutomation\PeriodicalActivity\Shared\Dto\PeriodicalActivityConfigDto; use JsonSchema\Validator; class PeriodicalActivityConfigReader diff --git a/src/ForecastAutomation/PeriodicalActivityDataImport/Business/PeriodicalActivityDataImportProcess.php b/src/ForecastAutomation/PeriodicalActivity/Business/PeriodicalActivityDataImportProcess.php similarity index 81% rename from src/ForecastAutomation/PeriodicalActivityDataImport/Business/PeriodicalActivityDataImportProcess.php rename to src/ForecastAutomation/PeriodicalActivity/Business/PeriodicalActivityDataImportProcess.php index 3b83c04..4b4c87a 100644 --- a/src/ForecastAutomation/PeriodicalActivityDataImport/Business/PeriodicalActivityDataImportProcess.php +++ b/src/ForecastAutomation/PeriodicalActivity/Business/PeriodicalActivityDataImportProcess.php @@ -9,12 +9,12 @@ * with this source code in the file LICENSE. */ -namespace ForecastAutomation\PeriodicalActivityDataImport\Business; +namespace ForecastAutomation\PeriodicalActivity\Business; use ForecastAutomation\Activity\Shared\Dto\ActivityDto; use ForecastAutomation\Activity\Shared\Dto\ActivityDtoCollection; use ForecastAutomation\ForecastClient\Shared\Config\ForecastClientQueueConstants; -use ForecastAutomation\PeriodicalActivityDataImport\Shared\Dto\PeriodicalActivityConfigDto; +use ForecastAutomation\PeriodicalActivity\Shared\Dto\PeriodicalActivityConfigDto; use ForecastAutomation\QueueClient\QueueClientFacade; use ForecastAutomation\QueueClient\Shared\Dto\MessageCollectionDto; use ForecastAutomation\QueueClient\Shared\Dto\MessageDto; @@ -29,19 +29,12 @@ public function __construct( ) { } - public function start(string $periodicalDate): int + public function generateActivityToDate(string $periodicalDate): ActivityDtoCollection { - $activityDtoCollection = $this->generateActivity( + return $this->generateActivity( $this->periodicalActivityConfigReader->readPeriodicalConfig($periodicalDate), $periodicalDate ); - - $this->queueClientFacade->sendMessages( - ForecastClientQueueConstants::QUEUE_NAME, - $this->createMessageCollectionDto($activityDtoCollection) - ); - - return \count($activityDtoCollection->activityDtos); } private function createMessageCollectionDto(ActivityDtoCollection $activityDtoCollection): MessageCollectionDto diff --git a/src/ForecastAutomation/PeriodicalActivityDataImport/Business/Schema/periodical_activity_config_schema.json b/src/ForecastAutomation/PeriodicalActivity/Business/Schema/periodical_activity_config_schema.json similarity index 100% rename from src/ForecastAutomation/PeriodicalActivityDataImport/Business/Schema/periodical_activity_config_schema.json rename to src/ForecastAutomation/PeriodicalActivity/Business/Schema/periodical_activity_config_schema.json diff --git a/src/ForecastAutomation/PeriodicalActivityDataImport/PeriodicalActivityDataImportDependencyProvider.php b/src/ForecastAutomation/PeriodicalActivity/PeriodicalActivityDependencyProvider.php similarity index 83% rename from src/ForecastAutomation/PeriodicalActivityDataImport/PeriodicalActivityDataImportDependencyProvider.php rename to src/ForecastAutomation/PeriodicalActivity/PeriodicalActivityDependencyProvider.php index 3daf30c..4520e25 100644 --- a/src/ForecastAutomation/PeriodicalActivityDataImport/PeriodicalActivityDataImportDependencyProvider.php +++ b/src/ForecastAutomation/PeriodicalActivity/PeriodicalActivityDependencyProvider.php @@ -9,14 +9,14 @@ * with this source code in the file LICENSE. */ -namespace ForecastAutomation\PeriodicalActivityDataImport; +namespace ForecastAutomation\PeriodicalActivity; use ForecastAutomation\Kernel\AbstractDependencyProvider; use ForecastAutomation\Kernel\Locator; use ForecastAutomation\Log\LogFacade; use ForecastAutomation\QueueClient\QueueClientFacade; -class PeriodicalActivityDataImportDependencyProvider extends AbstractDependencyProvider +class PeriodicalActivityDependencyProvider extends AbstractDependencyProvider { public const LOG_FACADE = 'LOG_FACADE'; public const QUEUE_CLIENT_FACADE = 'QUEUE_CLIENT_FACADE'; diff --git a/src/ForecastAutomation/PeriodicalActivity/PeriodicalActivityFacade.php b/src/ForecastAutomation/PeriodicalActivity/PeriodicalActivityFacade.php new file mode 100644 index 0000000..a4c3624 --- /dev/null +++ b/src/ForecastAutomation/PeriodicalActivity/PeriodicalActivityFacade.php @@ -0,0 +1,26 @@ + + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace ForecastAutomation\PeriodicalActivity; + +use ForecastAutomation\Activity\Shared\Dto\ActivityDtoCollection; +use ForecastAutomation\Kernel\AbstractFacade; + +/** + * @method \ForecastAutomation\PeriodicalActivity\PeriodicalActivityFactory getFactory() + */ +class PeriodicalActivityFacade extends AbstractFacade +{ + public function generateActivityToDate(string $periodicalDate): ActivityDtoCollection + { + return $this->getFactory()->createPeriodicalActivityDataImportProcess()->generateActivityToDate($periodicalDate); + } +} diff --git a/src/ForecastAutomation/PeriodicalActivityDataImport/PeriodicalActivityDataImportFactory.php b/src/ForecastAutomation/PeriodicalActivity/PeriodicalActivityFactory.php similarity index 73% rename from src/ForecastAutomation/PeriodicalActivityDataImport/PeriodicalActivityDataImportFactory.php rename to src/ForecastAutomation/PeriodicalActivity/PeriodicalActivityFactory.php index 9841d35..aa02f8e 100644 --- a/src/ForecastAutomation/PeriodicalActivityDataImport/PeriodicalActivityDataImportFactory.php +++ b/src/ForecastAutomation/PeriodicalActivity/PeriodicalActivityFactory.php @@ -9,16 +9,16 @@ * with this source code in the file LICENSE. */ -namespace ForecastAutomation\PeriodicalActivityDataImport; +namespace ForecastAutomation\PeriodicalActivity; use ForecastAutomation\Kernel\AbstractFactory; use ForecastAutomation\Log\LogFacade; -use ForecastAutomation\PeriodicalActivityDataImport\Business\PeriodicalActivityConfigReader; -use ForecastAutomation\PeriodicalActivityDataImport\Business\PeriodicalActivityDataImportProcess; +use ForecastAutomation\PeriodicalActivity\Business\PeriodicalActivityConfigReader; +use ForecastAutomation\PeriodicalActivity\Business\PeriodicalActivityDataImportProcess; use ForecastAutomation\QueueClient\QueueClientFacade; use JsonSchema\Validator; -class PeriodicalActivityDataImportFactory extends AbstractFactory +class PeriodicalActivityFactory extends AbstractFactory { public function createPeriodicalActivityDataImportProcess(): PeriodicalActivityDataImportProcess { @@ -52,11 +52,11 @@ public function createValidator(): Validator public function getLogFacade(): LogFacade { - return $this->getProvidedDependency(PeriodicalActivityDataImportDependencyProvider::LOG_FACADE); + return $this->getProvidedDependency(PeriodicalActivityDependencyProvider::LOG_FACADE); } public function getQueueClientFacade(): QueueClientFacade { - return $this->getProvidedDependency(PeriodicalActivityDataImportDependencyProvider::QUEUE_CLIENT_FACADE); + return $this->getProvidedDependency(PeriodicalActivityDependencyProvider::QUEUE_CLIENT_FACADE); } } diff --git a/src/ForecastAutomation/PeriodicalActivityDataImport/Shared/Dto/PeriodicalActivityConfigDto.php b/src/ForecastAutomation/PeriodicalActivity/Shared/Dto/PeriodicalActivityConfigDto.php similarity index 88% rename from src/ForecastAutomation/PeriodicalActivityDataImport/Shared/Dto/PeriodicalActivityConfigDto.php rename to src/ForecastAutomation/PeriodicalActivity/Shared/Dto/PeriodicalActivityConfigDto.php index f54c807..e2d5550 100644 --- a/src/ForecastAutomation/PeriodicalActivityDataImport/Shared/Dto/PeriodicalActivityConfigDto.php +++ b/src/ForecastAutomation/PeriodicalActivity/Shared/Dto/PeriodicalActivityConfigDto.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace ForecastAutomation\PeriodicalActivityDataImport\Shared\Dto; +namespace ForecastAutomation\PeriodicalActivity\Shared\Dto; use ForecastAutomation\Kernel\Shared\Dto\AbstractDto; diff --git a/src/ForecastAutomation/PeriodicalActivity/Shared/Plugin/PeriodicalActivityPlugin.php b/src/ForecastAutomation/PeriodicalActivity/Shared/Plugin/PeriodicalActivityPlugin.php new file mode 100644 index 0000000..83f4ea6 --- /dev/null +++ b/src/ForecastAutomation/PeriodicalActivity/Shared/Plugin/PeriodicalActivityPlugin.php @@ -0,0 +1,35 @@ + + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace ForecastAutomation\PeriodicalActivity\Shared\Plugin; + +use ForecastAutomation\Activity\Shared\Plugin\ActivityPluginInterface; +use ForecastAutomation\Kernel\Shared\Plugin\AbstractPlugin; +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\PromiseInterface; + +/** + * @method \ForecastAutomation\PeriodicalActivity\PeriodicalActivityFacade getFacade() + */ +class PeriodicalActivityPlugin extends AbstractPlugin implements ActivityPluginInterface +{ + public function collect(): PromiseInterface + { + $wrapPromise = new Promise( + function () use (&$wrapPromise) { + $periodicalActivity = $this->getFacade()->generateActivityToDate(date('Y-m-d 00:00')); + $wrapPromise->resolve($periodicalActivity); + } + ); + + return $wrapPromise; + } +} diff --git a/src/ForecastAutomation/PeriodicalActivityDataImport/PeriodicalActivityDataImportFacade.php b/src/ForecastAutomation/PeriodicalActivityDataImport/PeriodicalActivityDataImportFacade.php deleted file mode 100644 index dafc0d3..0000000 --- a/src/ForecastAutomation/PeriodicalActivityDataImport/PeriodicalActivityDataImportFacade.php +++ /dev/null @@ -1,25 +0,0 @@ - - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace ForecastAutomation\PeriodicalActivityDataImport; - -use ForecastAutomation\Kernel\AbstractFacade; - -/** - * @method \ForecastAutomation\PeriodicalActivityDataImport\PeriodicalActivityDataImportFactory getFactory() - */ -class PeriodicalActivityDataImportFacade extends AbstractFacade -{ - public function startImportProcess(string $periodicalDate): int - { - return $this->getFactory()->createPeriodicalActivityDataImportProcess()->start($periodicalDate); - } -} diff --git a/src/ForecastAutomation/PeriodicalActivityDataImport/Shared/Plugin/PeriodicalActivityDataImportConsoleCommandPlugin.php b/src/ForecastAutomation/PeriodicalActivityDataImport/Shared/Plugin/PeriodicalActivityDataImportConsoleCommandPlugin.php deleted file mode 100644 index e8f9e5d..0000000 --- a/src/ForecastAutomation/PeriodicalActivityDataImport/Shared/Plugin/PeriodicalActivityDataImportConsoleCommandPlugin.php +++ /dev/null @@ -1,40 +0,0 @@ - - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -namespace ForecastAutomation\PeriodicalActivityDataImport\Shared\Plugin; - -use ForecastAutomation\Kernel\Shared\Plugin\AbstractCommandPlugin; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * @method \ForecastAutomation\PeriodicalActivityDataImport\PeriodicalActivityDataImportFacade getFacade() - */ -class PeriodicalActivityDataImportConsoleCommandPlugin extends AbstractCommandPlugin -{ - public const COMMAND_NAME = 'forecast:import:periodical:activity'; - public const DESCRIPTION = 'This command will generate forecast.it activity, based on your personal configuration.'; - - protected function configure(): void - { - $this->setName(self::COMMAND_NAME); - $this->setDescription(self::DESCRIPTION); - - parent::configure(); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $this->getFacade()->startImportProcess(date('Y-m-d')); - - return self::SUCCESS; - } -} diff --git a/src/ForecastAutomation/ProjektronClient/Business/PayloadDto.php b/src/ForecastAutomation/ProjektronClient/Business/PayloadDto.php new file mode 100644 index 0000000..d2069e1 --- /dev/null +++ b/src/ForecastAutomation/ProjektronClient/Business/PayloadDto.php @@ -0,0 +1,49 @@ + + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace ForecastAutomation\ProjektronClient\Business; + +class PayloadDto { + private $data; + + public function __construct(string $task, string $csrfToken, string $username, string $day, string $hours, string $minutes, string $description) { + $this->data = [ + 'daytimerecording,formsubmitted' => 'true', + 'daytimerecording,Content,singleeffort,TaskSelector,fixedtask,Data_CustomTitle' => 'Einzelbuchen', + 'daytimerecording,Content,singleeffort,TaskSelector,fixedtask,Data_FirstOnPage' => 'daytimerecording', + 'daytimerecording,Content,singleeffort,TaskSelector,fixedtask,Data_FirstOnPage' => 'daytimerecording', + 'daytimerecording,Content,singleeffort,TaskSelector,fixedtask,task,task' => $task, // task + 'daytimerecording,Content,singleeffort,EffortEditor,effort1,effortStart,effortStart_hour' => '', + 'daytimerecording,Content,singleeffort,EffortEditor,effort1,effortStart,effortStart_minute' => '', + 'daytimerecording,Content,singleeffort,EffortEditor,effort1,effortEnd,effortEnd_hour' => '', + 'daytimerecording,Content,singleeffort,EffortEditor,effort1,effortEnd,effortEnd_minute' => '', + 'daytimerecording,Content,singleeffort,EffortEditor,effort1,effortExpense,effortExpense_hour' => $hours, + 'daytimerecording,Content,singleeffort,EffortEditor,effort1,effortExpense,effortExpense_minute' => $minutes, + 'daytimerecording,Content,singleeffort,EffortEditor,effort1,description,description' => $description, + 'daytimerecording,Content,singleeffort,recordType' => 'neweffort', + 'daytimerecording,Content,singleeffort,recordOid' => '', + 'daytimerecording,Content,singleeffort,recordDate' => $day, // day + 'daytimerecording,editableComponentNames' => 'daytimerecording,Content,singleeffort daytimerecording,Content,daytimerecordingAllBookingsOfTheDay', + 'oid' => $task, + 'user' => $username, + 'action,MultiComponentDayTimeRecordingAction,daytimerecording' => '0', + 'BCS.ConfirmDiscardChangesDialog,InitialApplyButtonsOnError' => 'daytimerecording,Apply', + 'CSRF_Token' => $csrfToken, + 'daytimerecording,Apply' => 'daytimerecording,Apply', + 'submitButtonPressed' => 'daytimerecording,Apply', + ]; + } + + public function getEncodedData() { + // URL-encode the data for the POST request + return http_build_query($this->data); + } +} diff --git a/src/ForecastAutomation/ProjektronClient/Business/ProjektronApi.php b/src/ForecastAutomation/ProjektronClient/Business/ProjektronApi.php new file mode 100644 index 0000000..98c0b46 --- /dev/null +++ b/src/ForecastAutomation/ProjektronClient/Business/ProjektronApi.php @@ -0,0 +1,98 @@ + + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace ForecastAutomation\ProjektronClient\Business; + +use ForecastAutomation\Activity\Shared\Dto\ActivityDto; +use ForecastAutomation\Activity\Shared\Dto\ActivityDtoCollection; + +class ProjektronApi +{ + private string $csrf; + + public function __construct( + private string $projektronApiEndpoint, + private string $projektronCookieHeaderValue, + private string $username, + private string $catchAllTask + ) { $this->setCsrfFromCookie(); } + + public function writeActivities(ActivityDtoCollection $activityDtoCollection): int + { + $savedActivities = 0; + foreach ($activityDtoCollection as $activityDto) { + $activityDto->needle = $this->setCatchAllOnUnknownTask($activityDto->needle); + $activityDto->created->sub(new \DateInterval('P1D')); // give projektron a date, and it adds +1 day, so remove one day of the time entry, HAHAHA + $activityDto->created->setTime(22, 0, 0); // projektron doesnt save the entry if the time is not 22:00 HAHAHA + $payloadDto = new PayloadDto( + $activityDto->needle, + $this->csrf,$this->username, + $activityDto->created->getTimestamp().'000', + (string)intdiv($activityDto->duration, 60), + (string)($activityDto->duration%60), + $activityDto->description + ); + $this->sendActivity($this->projektronApiEndpoint.'?oid='.$activityDto->needle, $payloadDto); + ++$savedActivities; + } + + return $savedActivities; + } + + private function sendActivity(string $path, PayloadDto $payloadDto): string + { + $this->headers = [ + 'Cookie: '.$this->projektronCookieHeaderValue, + 'User-Agent: ArchUser/1337', + ]; + + $ch = curl_init($path); + curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $payloadDto->getEncodedData()); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $response = curl_exec($ch); + + if (!$this->isProjektronActivitySuccess($response)) { + throw new \Exception('Could not send activity to projektron.'); + } + + if (curl_errno($ch)) { + throw new \Exception(curl_error($ch)); + } + curl_close($ch); + + return $response; + } + + private function isProjektronActivitySuccess(string $response): bool + { + return strpos($response, '