Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/Cli/WorkReportCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
Banner::render($output, $this->getApplication()?->getVersion() ?? 'dev');

try {
$config = $this->configProvider->get();
} catch (ConfigException $e) {
$config = $this->configProvider->getConfig();
$timeEntriesSource = $this->timeEntriesSourceFactory->build($config->source);
$this->destination = $this->destinationFactory->build($config->destination);
} catch (SourceException | DestinationException | ConfigException $e) {
$this->io->error($e->getMessage());
return Command::FAILURE;
}

$timeEntriesSource = $this->timeEntriesSourceFactory->build($config->source);
$this->destination = $this->destinationFactory->build($config->destination);

try {
$timeEntries = $timeEntriesSource->fetchTimeEntries($this->from, $this->to);
} catch (SourceException $e) {
Expand Down
17 changes: 17 additions & 0 deletions src/Config/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Igancev\WorkReporter\Config\SourceConfig\SourcesConfig;
use Igancev\WorkReporter\Source\SourceType;
use Igancev\WorkReporter\Destination\DestinationType;
use LogicException;

readonly class Config
{
Expand All @@ -15,5 +16,21 @@ public function __construct(
public SourcesConfig $sources,
public DestinationsConfig $destinations,
) {
// @phpstan-ignore function.alreadyNarrowedType
if (!property_exists($this->destinations, $this->destination->value)) {
throw new LogicException("Destination {$this->destination->value} is not supported");
}

if (!property_exists($this->sources, $this->source->value)) {
throw new LogicException("Source {$this->source->value} is not supported");
}

if ($this->destinations->{$this->destination->value} === null) {
throw new ConfigException("Destination {$this->destination->value} is not configured");
}

if ($this->sources->{$this->source->value} === null) {
throw new ConfigException("Source {$this->source->value} is not configured");
}
}
}
5 changes: 4 additions & 1 deletion src/Config/ConfigProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@

interface ConfigProvider
{
public function get(): Config;
/**
* @throws ConfigException
*/
public function getConfig(): Config;
}
2 changes: 1 addition & 1 deletion src/Config/YamlConfigProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function __construct(
) {
}

public function get(): Config
public function getConfig(): Config
{
if ($this->config !== null) {
return $this->config;
Expand Down
14 changes: 8 additions & 6 deletions src/Destination/ConcreteDestinationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use Amp\Socket\DnsSocketConnector;
use Igancev\WorkReporter\Config\ConfigProvider;
use Igancev\WorkReporter\Destination\YouTrack\YouTrackDestination;
use RuntimeException;

final readonly class ConcreteDestinationFactory implements DestinationFactory
{
Expand All @@ -26,11 +25,14 @@ public function build(DestinationType $destination): Destination
};
}

/**
* @throws DestinationException
*/
private function buildYouTrackDestination(): YouTrackDestination
{
$config = $this->configProvider->get()->destinations->youTrack;
if ($config === null) {
throw new RuntimeException("YouTrack destination configuration is missing");
$youtrackConfigDestinations = $this->configProvider->getConfig()->destinations->youTrack;
if ($youtrackConfigDestinations === null) {
throw new DestinationException("Definition of YouTrack destination missing in configuration");
}

return new YouTrackDestination(
Expand All @@ -39,8 +41,8 @@ private function buildYouTrackDestination(): YouTrackDestination
new UnlimitedConnectionPool(new DefaultConnectionFactory(new DnsSocketConnector()))
)
->build(),
$config->url,
$config->token,
$youtrackConfigDestinations->url,
$youtrackConfigDestinations->token,
);
}
}
3 changes: 3 additions & 0 deletions src/Destination/DestinationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@

interface DestinationFactory
{
/**
* @throws DestinationException
*/
public function build(DestinationType $destination): Destination;
}
15 changes: 10 additions & 5 deletions src/Source/ConcreteSourceFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use Igancev\WorkReporter\Config\ConfigProvider;
use Igancev\WorkReporter\Source\PlainJson\PlainJsonTimeEntriesSource;
use Igancev\WorkReporter\Source\SuperProductivity\SuperProductivitySyncSource;
use RuntimeException;

final readonly class ConcreteSourceFactory implements TimeEntriesSourceFactory
{
Expand All @@ -24,21 +23,27 @@ public function build(SourceType $source): TimeEntriesSource
};
}

/**
* @throws SourceException
*/
private function buildPlainJsonSource(): PlainJsonTimeEntriesSource
{
$config = $this->configProvider->get()->sources->plainJson;
$config = $this->configProvider->getConfig()->sources->plainJson;
if ($config === null) {
throw new RuntimeException("PlainJson source configuration is missing");
throw new SourceException("PlainJson source configuration is missing");
}

return new PlainJsonTimeEntriesSource($config->filePath);
}

/**
* @throws SourceException
*/
private function buildFromSuperProductivitySource(): SuperProductivitySyncSource
{
$config = $this->configProvider->get()->sources->superProductivity;
$config = $this->configProvider->getConfig()->sources->superProductivity;
if ($config === null) {
throw new RuntimeException("SuperProductivity source configuration is missing");
throw new SourceException("SuperProductivity source configuration is missing");
}

return new SuperProductivitySyncSource($config->syncFilePath);
Expand Down
26 changes: 18 additions & 8 deletions src/Source/SuperProductivity/Storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

namespace Igancev\WorkReporter\Source\SuperProductivity;

use Igancev\WorkReporter\Source\SourceException;
use JsonException;
use RuntimeException;

/**
* @internal
Expand All @@ -16,6 +16,9 @@
/** @var array<mixed> */
private array $jsonData;

/**
* @throws SourceException
*/
public function __construct(string $syncMetaPath)
{
$this->syncMetaPath = $syncMetaPath;
Expand Down Expand Up @@ -48,6 +51,7 @@ public function getTaskIds(): iterable
* subTaskIds: string[],
* tagIds: string[],
* }
* @throws SourceException
*/
public function getTaskById(string $taskId): array
{
Expand All @@ -61,24 +65,27 @@ public function getTaskById(string $taskId): array
return $this->jsonData['mainModelData']['archiveOld']['task']['entities'][$taskId];
}

throw new RuntimeException('SuperProductivitySyncDataSource: Unable to find task with id ' . $taskId);
throw new SourceException('SuperProductivitySyncDataSource: Unable to find task with id ' . $taskId);
}

/** @return array<mixed> */
/**
* @return array<mixed>
* @throws SourceException
*/
private function parseJson(): array
{
$content = @file_get_contents($this->syncMetaPath);
if ($content === false) {
throw new RuntimeException(
'SuperProductivitySyncDataSource: Unable to read sync data file ' . $this->syncMetaPath,
throw new SourceException(
'SuperProductivitySyncDataSource: Unable to read sync data file: ' . $this->syncMetaPath,
);
}

// specific format of SuperProductivity sync data
// json file starts with prefix like `pf_4.4__`, example: pf_4.4__{"revMap":{"menuTree":"1770462116462", ...
$startPos = strpos($content, '{');
if ($startPos === false) {
throw new RuntimeException(
throw new SourceException(
'SuperProductivitySyncDataSource: Unable to parse start position "{" ' . $this->syncMetaPath,
);
}
Expand All @@ -88,16 +95,19 @@ private function parseJson(): array
try {
$jsonData = json_decode($jsonString, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
throw new RuntimeException('SuperProductivitySyncDataSource: Unable to parse JSON: ' . $e->getMessage());
throw new SourceException('SuperProductivitySyncDataSource: Unable to parse JSON: ' . $e->getMessage());
}

return $jsonData;
}

/**
* @throws SourceException
*/
public function getTagById(string $tagId): Tag
{
if (!array_key_exists($tagId, $this->jsonData['mainModelData']['tag']['entities'])) {
throw new RuntimeException('SuperProductivitySyncDataSource: Unable to find tag with id ' . $tagId);
throw new SourceException('SuperProductivitySyncDataSource: Unable to find tag with id ' . $tagId);
}

$tagName = $this->jsonData['mainModelData']['tag']['entities'][$tagId]['title'];
Expand Down
4 changes: 4 additions & 0 deletions src/Source/SuperProductivity/SuperProductivitySyncSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Igancev\WorkReporter\Source\SuperProductivity;

use Igancev\WorkReporter\Source\SourceException;
use Igancev\WorkReporter\Source\TimeEntriesSource;
use Igancev\WorkReporter\TimeEntry;
use DateTimeImmutable;
Expand All @@ -13,6 +14,9 @@
private Storage $storage;
private TaskFactory $taskFactory;

/**
* @throws SourceException
*/
public function __construct(
string $syncMetaPath,
) {
Expand Down
3 changes: 3 additions & 0 deletions src/Source/TimeEntriesSourceFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@

interface TimeEntriesSourceFactory
{
/**
* @throws SourceException
*/
public function build(SourceType $source): TimeEntriesSource;
}
Loading
Loading