WIP work on youtrack rest api plugin.
parent
4e3477c4a0
commit
519c0b074a
|
@ -17,7 +17,7 @@
|
|||
|
||||
** Install/Getting started
|
||||
|
||||
Get ~.phar~ file from r, Run configuration wizzard (planned for version 1.0).
|
||||
Get ~.phar~ file from r, Run configruration wizzard (planned for version 1.0).
|
||||
|
||||
Before version 1.0 is released you have to navigate into ~/app~ directory
|
||||
and call command through ~.rprt.php~ file.
|
||||
|
@ -75,6 +75,11 @@ curl 'https://drunomics.myjetbrains.com/youtrack/api/reports?$top=-1&fields=id,n
|
|||
5 days of development.
|
||||
- remove errors from reports
|
||||
|
||||
* Support the work
|
||||
|
||||
If you find this Free Software useful you can consider donating to my
|
||||
[[https://liberapay.com/tehnoklistir/][@tehno-klistir liberapay account.]]
|
||||
|
||||
* Development
|
||||
|
||||
** Testing
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\YoutrackRestApiClient;
|
||||
use function DI\create;
|
||||
use function DI\get;
|
||||
use function DI\factory;
|
||||
|
@ -60,6 +61,8 @@ return [
|
|||
get('config.service'),
|
||||
get('mpdf')
|
||||
),
|
||||
YoutrackRestApiClient::class => create()->constructor(get('config.service')),
|
||||
'youtrack_rest_api.client' => get(YoutrackRestApiClient::class),
|
||||
'pdf_export.service' => get(PdfExportInterface::class),
|
||||
// 'locale' => get('config.service')->method('get', 'en'),
|
||||
// Translator::class => create()->constructor('sl')->method('addLoader', 'po', new PoFileLoader),
|
||||
|
|
|
@ -21,4 +21,5 @@ $reportCommand = $container->get(ReportCommand::class);
|
|||
$application->add($reportCommand);
|
||||
|
||||
// eval(\Psy\sh());
|
||||
|
||||
$application->run();
|
||||
|
|
|
@ -51,6 +51,9 @@ class InvoiceCommand extends Command
|
|||
|
||||
protected PdfExportInterface $pdfExport;
|
||||
|
||||
/**
|
||||
* Mailer service.
|
||||
*/
|
||||
protected MailerInterface $mailer;
|
||||
|
||||
protected const TYPE_WORK = 1;
|
||||
|
@ -177,9 +180,12 @@ class InvoiceCommand extends Command
|
|||
}
|
||||
$output->writeln("report: <info>{$report_name}</info>");
|
||||
$data = $this->csv->getInvoiceData($file);
|
||||
if (! empty($expenses)) {
|
||||
if (!empty($expenses)) {
|
||||
$data = array_merge($data, $expenses);
|
||||
}
|
||||
if (!empty($custom)) {
|
||||
$data = array_merge($data, $custom);
|
||||
}
|
||||
$table = $this->getTable($output, $data);
|
||||
$table->render();
|
||||
if ($input->getOption('pdf')) {
|
||||
|
|
|
@ -145,7 +145,7 @@ class ReportCsv implements ReportCsvInterface
|
|||
$rows[] = [
|
||||
null,
|
||||
null,
|
||||
'Kosten',
|
||||
'Extra', // Kosten
|
||||
'EUR',
|
||||
];
|
||||
// Don't make next line bold. See RprtCli\PdfExport\PdfExportService::parsedDataToHtml.
|
||||
|
@ -161,6 +161,7 @@ class ReportCsv implements ReportCsvInterface
|
|||
];
|
||||
$totalPrice += $invoice_element->getValue();
|
||||
}
|
||||
// @todo Add Extra time as well!
|
||||
}
|
||||
if ($add_separator) {
|
||||
$rows[] = ReportCsvInterface::SEPARATOR_MEDIUM;
|
||||
|
|
|
@ -14,35 +14,63 @@ use Attribute;
|
|||
#[Attribute]
|
||||
class EntityDefinition {
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param string $provider
|
||||
* @param string $name
|
||||
* @param array $fields
|
||||
* @param array $filters
|
||||
* @param array $resources
|
||||
*/
|
||||
public function __construct(
|
||||
private string $id,
|
||||
private string $provider,
|
||||
private string $name,
|
||||
private array $fields,
|
||||
private array $filters
|
||||
private array $filters,
|
||||
private array $resources
|
||||
) { }
|
||||
|
||||
public function getId() {
|
||||
public function getId() :string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getProvider() {
|
||||
public function getProvider() :string {
|
||||
return $this->provider;
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
public function getName() :string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getFields() {
|
||||
public function getFields() :array {
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
public function getFilters() {
|
||||
public function getFilters() :array {
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
public function getDefinition() {
|
||||
/**
|
||||
* Should be of type ResourceInterface.
|
||||
*/
|
||||
public function getResources() :array {
|
||||
return $this->resources;
|
||||
}
|
||||
|
||||
public function getResource(string $key) :?Resource {
|
||||
return $this->resources[$key] ?? NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get definition as array.
|
||||
*
|
||||
* @todo I'm not really sure why I implemented this method. Definition
|
||||
* is a definition already.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDefinition() :array {
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'provider' => $this->provider,
|
||||
|
|
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace RprtCli\Utils\TimeTrackingServices;
|
||||
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityDefinition;
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityInterface;
|
||||
|
||||
/**
|
||||
|
@ -14,6 +13,8 @@ interface EntityManagerInterface {
|
|||
|
||||
/**
|
||||
* List supported entity types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function list(): array;
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\Utils\TimeTrackingServices;
|
||||
|
||||
|
||||
/**
|
||||
* Defines resources for entity type.
|
||||
*
|
||||
* This class could be readonly after php82.
|
||||
*/
|
||||
final class Resource {
|
||||
|
||||
public const GET = 'GET';
|
||||
public const POST = 'POST';
|
||||
|
||||
public function __construct(
|
||||
private string $id,
|
||||
private string $method,
|
||||
private string $path,
|
||||
private string $description = ''
|
||||
) { }
|
||||
|
||||
public function getId() :string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getPath() :string {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function getMethod() :string {
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
public function getDescription() :string {
|
||||
return $this->description;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace RprtCli\Utils\TimeTrackingServices\YoutrackRestApi;
|
||||
|
||||
use ReflectionAttribute;
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityManagerInterface;
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityDefinition;
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityInterface;
|
||||
|
@ -30,8 +31,11 @@ class EntityManager implements EntityManagerInterface {
|
|||
|
||||
/**
|
||||
* Returns all the entity definitions.
|
||||
*
|
||||
* @return array
|
||||
* List of entity definitions keyed with their id.
|
||||
*/
|
||||
public function listEntityDefinitions() {
|
||||
public function listEntityDefinitions() :array {
|
||||
if (!$this->entityDefinitions) {
|
||||
$this->discoverEntities();
|
||||
}
|
||||
|
@ -40,8 +44,10 @@ class EntityManager implements EntityManagerInterface {
|
|||
|
||||
/**
|
||||
* Discovers entity definitions.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function discoverEntities() {
|
||||
private function discoverEntities() :?array {
|
||||
if (!empty($this->entityDefinitions)) {
|
||||
return $this->entityDefinitions;
|
||||
}
|
||||
|
@ -49,8 +55,8 @@ class EntityManager implements EntityManagerInterface {
|
|||
$definitions = [];
|
||||
// @todo create a proxy service for finder.
|
||||
$this->finder->files()->in($path);
|
||||
/** @var SplFileInfo $file */
|
||||
foreach ($this->finder as $file) {
|
||||
/** @var SplFileInfo $file */
|
||||
$class = self::ENTITIES_NAMESPACE . '\\' . self::ENTITIES_DIR . '\\' . $file->getBasename('.php');
|
||||
$reflection = new \ReflectionClass($class);
|
||||
$attribute = $this->getAttributeOfInstance($reflection, EntityDefinition::class);
|
||||
|
@ -61,8 +67,10 @@ class EntityManager implements EntityManagerInterface {
|
|||
}
|
||||
$instance = $attribute->newInstance();
|
||||
$id = $instance->getId();
|
||||
$content = $instance->getDefinition();
|
||||
$content['class'] = $class;
|
||||
$content = [
|
||||
'definition' => $instance,
|
||||
'class' => $class,
|
||||
];
|
||||
$definitions[$id] = $content;
|
||||
}
|
||||
$this->entityDefinitions = $definitions;
|
||||
|
@ -77,19 +85,11 @@ class EntityManager implements EntityManagerInterface {
|
|||
* @param string $instance
|
||||
* The instance the attribute should be of.
|
||||
*
|
||||
* @return
|
||||
* @return ?ReflectionAttribute
|
||||
* The attribute instance.
|
||||
*/
|
||||
protected function getAttributeOfInstance(\ReflectionClass $reflection, string $instance) {
|
||||
$t = $reflection->getAttributes();
|
||||
var_dump($t);
|
||||
var_dump($t[0]->getName());
|
||||
var_dump($t[0]->getArguments());
|
||||
var_dump($t[0]->newInstance());
|
||||
$s = $reflection->getAttributes(EntityDefinition::class, \ReflectionAttribute::IS_INSTANCEOF);
|
||||
var_dump($s);
|
||||
protected function getAttributeOfInstance(\ReflectionClass $reflection, string $instance) :?ReflectionAttribute {
|
||||
$attributes = $reflection->getAttributes($instance, \ReflectionAttribute::IS_INSTANCEOF);
|
||||
var_dump($attributes);
|
||||
if (empty($attributes)) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -97,6 +97,15 @@ class EntityManager implements EntityManagerInterface {
|
|||
return reset($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entity definition by id.
|
||||
*
|
||||
* @param string $id
|
||||
* Id of an entity definition (work_item, issue, project, comment).
|
||||
*
|
||||
* @return ?EntityDefinition
|
||||
* Entity definition (maybe even EntityDefinition object).
|
||||
*/
|
||||
public function getDefinition(string $id) :?array {
|
||||
if (!isset($this->entityDefinitions)) {
|
||||
$this->list();
|
||||
|
@ -108,6 +117,20 @@ class EntityManager implements EntityManagerInterface {
|
|||
return $this->entityDefinitions[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create entity instance.
|
||||
*
|
||||
* This method should be used for transfering the data between report cli
|
||||
* tools and the youtrack rest api.
|
||||
*
|
||||
* @param string $id
|
||||
* Instance id (issue, project, work_item, comment ...).
|
||||
* @param array $values
|
||||
* Time, description ...
|
||||
*
|
||||
* @return ?EntityInterface
|
||||
*
|
||||
*/
|
||||
public function createInstance(string $id, array $values) :?EntityInterface {
|
||||
if (!isset($this->entityDefinitions)) {
|
||||
$this->list();
|
||||
|
@ -117,8 +140,10 @@ class EntityManager implements EntityManagerInterface {
|
|||
return NULL;
|
||||
}
|
||||
$definition = $this->getDefinition($id);
|
||||
// This is not ok. definition is of type EntityDefinition but we want
|
||||
// EntityInterface. This is where $class would come in handy.
|
||||
$reflection = new \ReflectionClass($definition['class']);
|
||||
return new $reflection->newInstanceArgs($values);
|
||||
return $reflection->newInstanceArgs($values);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\Utils\TimeTrackingServices;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* Define filter properties.
|
||||
*/
|
||||
#[Attribute]
|
||||
class FilterAttribute {
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param string $provider
|
||||
* @param string $name
|
||||
* @param array $allowedFields
|
||||
* @param array $allowedEntities
|
||||
* @param array $resources
|
||||
*/
|
||||
public function __construct(
|
||||
private string $id,
|
||||
private string $provider,
|
||||
private string $name,
|
||||
private array $allowedFields = [],
|
||||
private array $allowedEntities = [],
|
||||
) { }
|
||||
|
||||
public function getId() :string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getProvider() :string {
|
||||
return $this->provider;
|
||||
}
|
||||
|
||||
public function getName() :string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getAllowedFields() :array {
|
||||
return $this->allowedFields;
|
||||
}
|
||||
|
||||
public function getAllowedEntities() :array {
|
||||
return $this->allowedEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get definition as array.
|
||||
*
|
||||
* @todo I'm not really sure why I implemented this method. Definition
|
||||
* is a definition already.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDefinition() :array {
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'provider' => $this->provider,
|
||||
'name' => $this->name,
|
||||
'allowedFields' => $this->allowedFields,
|
||||
'allowedEntities' => $this->allowedEntities,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\Filters;
|
||||
|
||||
/**
|
||||
* Defines a filter attribute and some constants.
|
||||
*/
|
||||
interface YoutrackFilterInterface {
|
||||
|
||||
const OPTIONAL = 0;
|
||||
const REQUIRED = 1;
|
||||
|
||||
}
|
|
@ -4,11 +4,22 @@ declare(strict_types=1);
|
|||
|
||||
namespace RprtCli\Utils\TimeTrackingServices\YoutrackRestApi;
|
||||
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityDefinition;
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityInterface;
|
||||
|
||||
/**
|
||||
* Abstract class for youtrack entities.
|
||||
*/
|
||||
abstract class YoutrackEntity implements EntityInterface {
|
||||
|
||||
protected function getDefintion() :?EntityDefinition {
|
||||
$reflection = new \ReflectionClass(self::class);
|
||||
$attributes = $reflection->getAttributes(EntityDefinition::class, \ReflectionAttribute::IS_INSTANCEOF);
|
||||
if (empty($attributes)) {
|
||||
return NULL;
|
||||
}
|
||||
return reset($attributes);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\YoutrackEntityTypes;
|
||||
|
||||
use RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\YoutrackEntity;
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityDefinition;
|
||||
use RprtCli\Utils\TimeTrackingServices\Resource;
|
||||
use RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\Filters\YoutarckFilterInterface;
|
||||
|
||||
/**
|
||||
* https://www.jetbrains.com/help/youtrack/devportal/api-entity-IssueComment.html
|
||||
* https://www.jetbrains.com/help/youtrack/devportal/resource-api-issues-issueID-comments.html
|
||||
*/
|
||||
#[EntityDefinition(
|
||||
id: 'issue_comment',
|
||||
provider: 'youtrack_rest_api',
|
||||
name: 'IssueComment',
|
||||
fields: [
|
||||
'id',
|
||||
'text',
|
||||
'textPreview',
|
||||
'created',
|
||||
'author' => [
|
||||
'id',
|
||||
'fullName',
|
||||
'email',
|
||||
],
|
||||
'issue' => [
|
||||
'id',
|
||||
'idReadable',
|
||||
'project' => [
|
||||
'id',
|
||||
'shortName',
|
||||
],
|
||||
'summary',
|
||||
],
|
||||
],
|
||||
filters: [
|
||||
'issue' => [
|
||||
'required' => YoutrackFilterInterface::REQUIRED,
|
||||
],
|
||||
],
|
||||
resources: [
|
||||
'list' => new Resource(
|
||||
id: 'list',
|
||||
method: Resource::GET,
|
||||
path: '/api/issues/{issueID}/comments',
|
||||
description: 'Lists comments of issue.'
|
||||
),
|
||||
]
|
||||
)]
|
||||
class YoutrackComment extends YoutrackEntity {
|
||||
|
||||
}
|
|
@ -6,16 +6,38 @@ namespace RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\YoutrackEntityTypes
|
|||
|
||||
use RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\YoutrackEntity;
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityDefinition;
|
||||
use RprtCli\Utils\TimeTrackingServices\Resource;
|
||||
|
||||
/**
|
||||
* https://www.jetbrains.com/help/youtrack/devportal/api-entity-Issue.html
|
||||
* https://www.jetbrains.com/help/youtrack/devportal/resource-api-issues.html
|
||||
*/
|
||||
#[EntityDefinition(
|
||||
'issue',
|
||||
'youtrack_rest_api',
|
||||
'Issue',
|
||||
[],
|
||||
[]
|
||||
id: 'issue',
|
||||
provider: 'youtrack_rest_api',
|
||||
name: 'Issue',
|
||||
fields: [
|
||||
'id',
|
||||
'idReadable',
|
||||
'project' => [
|
||||
'id',
|
||||
'shortName',
|
||||
],
|
||||
'summary',
|
||||
],
|
||||
filters: [
|
||||
'state',
|
||||
'project',
|
||||
'assignee',
|
||||
],
|
||||
resources: [
|
||||
'list' => new Resource(
|
||||
id: 'list',
|
||||
method: Resource::GET,
|
||||
path: '/api/issues',
|
||||
description: 'Lists issues.'
|
||||
),
|
||||
]
|
||||
)]
|
||||
class YoutrackIssue extends YoutrackEntity {
|
||||
const ID = 'issue';
|
||||
|
|
|
@ -4,16 +4,39 @@ declare(strict_types=1);
|
|||
|
||||
namespace RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\YoutrackEntityTypes;
|
||||
|
||||
use RprtCli\Utils\TimeTrackingServices\Resource;
|
||||
use RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\YoutrackEntity;
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityDefinition;
|
||||
|
||||
/**
|
||||
* https://www.jetbrains.com/help/youtrack/devportal/resource-api-admin-projects.html
|
||||
*/
|
||||
#[EntityDefinition(
|
||||
'project',
|
||||
'youtrack_rest_api',
|
||||
'Youtrack Project',
|
||||
[],
|
||||
[]
|
||||
id: 'project',
|
||||
provider: 'youtrack_rest_api',
|
||||
name: 'Youtrack Project',
|
||||
fields: [
|
||||
'id',
|
||||
'shortName',
|
||||
'description',
|
||||
'leader' => ['id', 'fullName'],
|
||||
],
|
||||
filters: [],
|
||||
resources: [
|
||||
'list' => new Resource(
|
||||
id: 'list',
|
||||
method: Resource::GET,
|
||||
path: '/api/admin/projects',
|
||||
description: 'List projects'
|
||||
),
|
||||
'read' => new Resource(
|
||||
id: 'read',
|
||||
method: Resource::GET,
|
||||
path: '/api/admin/projects/{projectId}',
|
||||
description: 'Sprecific project'
|
||||
),
|
||||
]
|
||||
)]
|
||||
class YoutrackProject extends YoutrackEntity {
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -4,29 +4,59 @@ declare(strict_types=1);
|
|||
|
||||
namespace RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\YoutrackEntityTypes;
|
||||
|
||||
use RprtCli\Utils\TimeTrackingServices\Resource;
|
||||
use RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\YoutrackEntity;
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityDefinition;
|
||||
|
||||
/**
|
||||
* https://www.jetbrains.com/help/youtrack/devportal/api-entity-IssueWorkItem.html
|
||||
* https://www.jetbrains.com/help/youtrack/devportal/resource-api-workItems.html
|
||||
*/
|
||||
#[EntityDefinition(
|
||||
'work_item',
|
||||
'youtrack_rest_api',
|
||||
'Issue Work Item',
|
||||
[
|
||||
id: 'work_item',
|
||||
provider: 'youtrack_rest_api',
|
||||
name: 'Issue Work Item',
|
||||
fields: [
|
||||
'id',
|
||||
'author',
|
||||
'author' => [
|
||||
'id',
|
||||
'fullName',
|
||||
'email'
|
||||
],
|
||||
'text',
|
||||
'type',
|
||||
'duration',
|
||||
'duration' => [
|
||||
'id',
|
||||
'minutes',
|
||||
'presentation'
|
||||
],
|
||||
'date',
|
||||
'issue',
|
||||
'created',
|
||||
'issue' => [
|
||||
'id',
|
||||
'idReadable',
|
||||
'project' => [
|
||||
'id',
|
||||
'shortName'
|
||||
],
|
||||
'summary',
|
||||
],
|
||||
],
|
||||
[
|
||||
filters: [
|
||||
'project',
|
||||
'issue',
|
||||
'user',
|
||||
'date',
|
||||
],
|
||||
resources: [
|
||||
'list' => new Resource(
|
||||
id: 'list',
|
||||
method: Resource::GET,
|
||||
path: '/api/workItems',
|
||||
description: 'List workItems'
|
||||
),
|
||||
]
|
||||
)]
|
||||
class YoutrackWorkItem extends YoutrackEntity {
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\Utils\TimeTrackingServices\YoutrackRestApi;
|
||||
|
||||
use Cog\YouTrack\Rest\Client\YouTrackClient;
|
||||
use Cog\YouTrack\Rest\Authorizer\TokenAuthorizer;
|
||||
use Cog\YouTrack\Rest\HttpClient\GuzzleHttpClient;
|
||||
use RprtCli\Utils\Configuration\ConfigurationInterface;
|
||||
|
||||
|
||||
/**
|
||||
* YouTrack php sdk abstraction service.
|
||||
*
|
||||
* Returns the client that takes care of authentication.
|
||||
*/
|
||||
class YoutrackRestApiClient {
|
||||
|
||||
protected ConfigurationInterface $config;
|
||||
protected YouTrackClient $client;
|
||||
|
||||
// config
|
||||
public function __construct(ConfigurationInterface $config) {
|
||||
$this->config = $config;
|
||||
$this->client = $this->createYoutrackClient();
|
||||
}
|
||||
|
||||
protected function createYoutrackClient() :YouTrackClient {
|
||||
// Could this all go into __construct method?
|
||||
$apiBaseUri = $this->config->get('tracking_service.youtrack.base_url', false);
|
||||
$apiToken = $this->config->get('tracking_service.youtrack.auth_token', false);
|
||||
// Instantiate PSR-7 HTTP Client
|
||||
$psrHttpClient = new \GuzzleHttp\Client([
|
||||
'base_uri' => $apiBaseUri,
|
||||
'debug' => true,
|
||||
]);
|
||||
// Instantiate YouTrack API HTTP Client
|
||||
$httpClient = new GuzzleHttpClient($psrHttpClient);
|
||||
// Instantiate YouTrack API Token Authorizer
|
||||
$authorizer = new TokenAuthorizer($apiToken);
|
||||
// Instantiate YouTrack API Client
|
||||
$client = new YouTrackClient($httpClient, $authorizer);
|
||||
return $client;
|
||||
}
|
||||
|
||||
public function getClient() :YouTrackClient {
|
||||
return $this->client;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\Utils\TimeTrackingServices\YoutrackRestApi;
|
||||
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityManagerInterface;
|
||||
use RprtCli\Utils\TimeTrackingServices\YoutrackRestApi\YoutrackRestApiClient;
|
||||
|
||||
class YoutrackRestApiRequestBuilder {
|
||||
|
||||
protected EntityManagerInterface $entityManager;
|
||||
protected YoutrackRestApiClient $clientFactory;
|
||||
|
||||
public function __construct(EntityManagerInterface $entity_manager, YoutrackRestApiClient $clientFactory) {
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->clientFactory = $clientFactory;
|
||||
// EntityMangerInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that creates path to youtrack entity resource.
|
||||
*
|
||||
* Examples:
|
||||
* list issue => GET '/api/issues?fields=id,idReadable,summary,..'
|
||||
* create issue => POST '/api/issues'
|
||||
* but create action is only allowed on worktItems.
|
||||
* list work_item => GET '/api/workItem?fields=...'
|
||||
*
|
||||
* First impression seems simple. We need path and method.
|
||||
* What bothers me whether it would be better to create a new attribute
|
||||
* wehere these "mappings" would live: allowed actions and then method and path.
|
||||
*
|
||||
* @TODO Add filter system?
|
||||
*
|
||||
* @param string $action
|
||||
* Name of the action: list, create, read, update, delete.
|
||||
* @param string $entity_id
|
||||
* Name of the entity id (project, issue, comment, work_item).
|
||||
*
|
||||
* @return ?string
|
||||
* Path to the resource.
|
||||
*/
|
||||
public function buildPath(string $action, string $entity_id) :?string {
|
||||
$defintion = $this->entityManager->getDefinition($entity_id);
|
||||
if (!isset($defintion['definition'])) {
|
||||
// Missing entity definition!
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
$defintion = $defintion['definition'];
|
||||
}
|
||||
$fields = $definition->getFields();
|
||||
$fieldsQuery = $this->fieldsQuery($fields);
|
||||
/** @var RprtCli\Utils\TimeTrackingServices\Resource $resource */
|
||||
$resource = $defintion->getResource($action);
|
||||
if (!$resource) {
|
||||
// Missing resource!
|
||||
return NULL;
|
||||
}
|
||||
$path = $resource->getPath();
|
||||
// @TODO add query, create client request.
|
||||
// @see https://github.com/cybercog/youtrack-rest-php/blob/eb0315133d1d3d161da23d26537201afb253dec9/src/Client/YouTrackClient.php#L89
|
||||
if ($fieldsQuery) {
|
||||
$path .= '?' . $fieldsQuery;
|
||||
}
|
||||
// @TODO create request and handle request.
|
||||
|
||||
}
|
||||
|
||||
protected function fieldsQuery(array $fields) :?string {
|
||||
$query = implode(',', $this->stringifyFields($fields));
|
||||
if (!empty($query)) {
|
||||
return "fields=${query}";
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function stringifyFields(array $fields) :array {
|
||||
foreach ($fields as $key => &$field) {
|
||||
if (is_array($field)) {
|
||||
$field = $key . '(' . implode(',', $this->stringifyFields($field)) . ')';
|
||||
}
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ namespace RprtCli\Tests\Unit\YoutrackRestApi;
|
|||
|
||||
use DI\ContainerBuilder;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityDefinition;
|
||||
use RprtCli\Utils\TimeTrackingServices\EntityInterface;
|
||||
|
||||
/**
|
||||
* Test the entity manager service and system.
|
||||
|
@ -24,15 +26,26 @@ class EntityManagerTest extends TestCase {
|
|||
// Instance of EntityMangerInterface;
|
||||
// $this->assertInstanceOf('EntityManagerInterface', $entityManagerService);
|
||||
$definitions = $entityManagerService->list();
|
||||
var_dump($definitions);
|
||||
// var_dump($definitions);
|
||||
$this->assertCount(3, $definitions);
|
||||
$entities = ['project', 'issue', 'work_item'];
|
||||
foreach ($entities as $entity) {
|
||||
$this->assertArrayHasKey($entity, $definitions, 'Check that key exists in entity types definitions.');
|
||||
foreach ($entities as $entity_id) {
|
||||
$this->assertArrayHasKey($entity_id, $definitions, 'Check that key exists in entity types definitions.');
|
||||
var_dump($definitions[$entity_id]);
|
||||
|
||||
$this->assertInstanceOf(EntityDefinition::class, $definitions[$entity_id]['definition'], 'Check that definiton is of type entity definition');
|
||||
$definition = $entityManagerService->getDefinition($entity_id);
|
||||
$entity = $entityManagerService->createInstance($entity_id, []);
|
||||
$this->assertArrayHasKey('definition', $definition);
|
||||
$this->assertArrayHasKey('class', $definition);
|
||||
$this->assertInstanceOf(EntityDefinition::class, $definition['definition']);
|
||||
$this->assertInstanceOf(EntityInterface::class, $entity);
|
||||
}
|
||||
// list method returns definitions: project, issue, worktItem
|
||||
// Check one definition, compare.
|
||||
// Test creating the instance
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
7
todo.txt
7
todo.txt
|
@ -15,6 +15,13 @@ TODO:
|
|||
- invoice command improvements
|
||||
- pdf and output option could be combined
|
||||
|
||||
TODO: (start of 2023)
|
||||
- youtrack rest api:
|
||||
- resources class and parameter in EntityDefinition
|
||||
- compose fields method
|
||||
- create filter system
|
||||
- connect filter system to youtrack rest api command
|
||||
|
||||
TODO (end of 2022):
|
||||
- DONE fix project list output (first is missing)
|
||||
- DONE report selection trait
|
||||
|
|
Loading…
Reference in New Issue