diff --git a/cli/clickup-folders.php b/cli/clickup-folders.php index cbab27a..8c58093 100755 --- a/cli/clickup-folders.php +++ b/cli/clickup-folders.php @@ -1,90 +1,109 @@ #!/usr/bin/php ClickUp bot # Copyright (C) 2023 Valerio Bozzolan, contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . /** * Get ClickUp folders */ // autoload libraries require __DIR__ . '/../autoload.php'; +// ask data +$populate = true; + $cache = ClickUpPhabricatorCache::instance(); // import all ClickUp Folders from all Spaces $clickup_spaces = ClickUpAPI::getSpaces(); foreach( $clickup_spaces as $space ) { // $space->id // $space->name // save each folder in the cache $cache->lock(); $folders = ClickUpAPI::getSpaceFolders( $space->id ); foreach( $folders as $folder ) { // $folder->id // $folder->name $cache->importClickupFolder( $folder ); } $cache->save(); } // loop cached folders foreach( $cache->getClickUpFolders() as $folder ) { $phab_tag_id = $cache->getPhabricatorTagIDFromClickupFolderID( $folder->id ); $phab_tag_phid = $cache->getPhabricatorTagPHIDFromClickupFolderID( $folder->id ); - if( $phab_tag_id ) { + + if( !$phab_tag_id && $populate ) { + $phab_tag_id = readline( "Specify the Phabricator Tag name for {$folder->name} (example: pippo_pluto): " ); + } + + if( $phab_tag_id && !$phab_tag_phid ) { // find fresh project from its slug $phab_project = PhabricatorAPI::querySingle( 'project.search', [ 'constraints' => [ 'slugs' => [ $phab_tag_id, ] ], ] ); - // associate the ClickUp folder to the Phabricator Tag PHID - $cache - ->lock() - ->associateClickupFolderIDPhabricatorPHID( $folder->id, $phab_project['phid'] ) - ->save(); - - /* - // find columns from this project - $phab_columns = PhabricatorAPI::query( 'project.column.search', [ - 'constraints' => [ - 'projects' => [ $phab_project['phid'] ], - ], - ] ); - - $cache->lock(); - foreach( $phab_columns['data'] as $phab_column ) { - $cache->associateClickupFolderIDPhabricatorColumnPHID( $folder->id, $phab_column['phid'] ); + if( $phab_project ) { + + // associate the ClickUp folder to the Phabricator Tag PHID + $cache + ->lock() + ->associateClickupFolderIDPhabricatorID( $folder->id, $phab_project['fields']['slug'] ) + ->associateClickupFolderIDPhabricatorPHID( $folder->id, $phab_project['phid'] ) + ->save(); + + /* + // find columns from this project + $phab_columns = PhabricatorAPI::query( 'project.column.search', [ + 'constraints' => [ + 'projects' => [ $phab_project['phid'] ], + ], + ] ); + + $cache->lock(); + foreach( $phab_columns['data'] as $phab_column ) { + $cache->associateClickupFolderIDPhabricatorColumnPHID( $folder->id, $phab_column['phid'] ); + } + $cache->save(); + */ } - $cache->save(); - */ } +} + +// loop cached folders +foreach( $cache->getClickUpFolders() as $folder ) { + + $phab_tag_id = $cache->getPhabricatorTagIDFromClickupFolderID( $folder->id ); + $phab_tag_phid = $cache->getPhabricatorTagPHIDFromClickupFolderID( $folder->id ); $phab_tag_id_msg = $phab_tag_id ? "HAS Phab ID" : "MISSING Phab ID"; // print something nice to see echo $folder->id . "\t|" . $phab_tag_id_msg . "\t|" . $folder->name . "\n"; } diff --git a/cli/clickup-users.php b/cli/clickup-users.php index 3183847..bc63402 100755 --- a/cli/clickup-users.php +++ b/cli/clickup-users.php @@ -1,47 +1,86 @@ #!/usr/bin/php ClickUp bot # Copyright (C) 2023 Valerio Bozzolan, contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . /** * Get ClickUp users */ // autoload libraries require __DIR__ . '/../autoload.php'; $cache = ClickUpPhabricatorCache::instance(); -$max = 5; -$i = 0; +$populate = true; -$clickup_users = $cache->getClickUpUsers(); -foreach( $clickup_users as $clickup_user ) { +if( $populate ) { + foreach( $cache->getIterableClickupTasks() as $clickup_task ) { + + $clickup_task_details = ClickUpAPI::queryTaskData( $clickup_task->id ); + if( $clickup_task_details ) { + echo "Import users from ClickUp Task {$clickup_task->id}\n"; + } + + sleep( 1 ); + } } -$clickup_tasks = $cache->getAllClickupTasks(); -foreach( $clickup_tasks as $clickup_task_id => $clickup_task ) { +foreach( $cache->getIterableClickupUsers() as $clickup_user ) { + + $clickup_user_id = $clickup_user->id; + $phab_username = $cache->getClickUpUserPhabUsernameByID( $clickup_user_id ); + $phab_phid = $cache->getClickUpUserPHIDByID( $clickup_user_id ); + + if( !$phab_username ) { + $phab_username = readline("# Type Phabricator nickname of user {$clickup_user->username} (example: ciccio.franco): "); + } + + if( !$phab_phid && $phab_username ) { + $phab_user = PhabricatorAPI::getUserByUsername( $phab_username ); + if( $phab_user ) { + + $phab_phid = $phab_user['phid']; + $phab_username = $phab_user['fields']['username']; + + $cache + ->lock() + ->setClickUpUserPhabUsernameByID( $clickup_user_id, $phab_username ) + ->setClickUpUserPHIDByID( $clickup_user_id, $phab_phid ) + ->save(); - $clickup_task_details = ClickUpAPI::queryTaskData( $clickup_task_id ); - if( $clickup_task_details ) { - print_r( $clickup_task_details->creator ); - print_r( $clickup_task_details->assignees ); - print_r( $clickup_task_details->watchers ); - exit; + echo "# Saved\n\n"; + } else { + echo "# Not found\n\n"; + } } +} + +foreach( $cache->getIterableClickupUsers() as $clickup_user ) { + + $clickup_user_id = $clickup_user->id; + $phab_username = $cache->getClickUpUserPhabUsernameByID( $clickup_user_id ); + $phab_phid = $cache->getClickUpUserPHIDByID( $clickup_user_id ); - sleep( 1 ); + echo $clickup_user->id; + echo "\t|"; + echo $clickup_user->username; + echo "\t|"; + echo $phab_username + ? 'id CONNECTED TO PHAB' + : 'id NOT CONNECTED TO PHAB'; + echo "\n"; } diff --git a/include/class-PhabricatorAPI.php b/include/class-PhabricatorAPI.php index 74c7777..5f63469 100644 --- a/include/class-PhabricatorAPI.php +++ b/include/class-PhabricatorAPI.php @@ -1,210 +1,228 @@ ClickUp bot # Copyright (C) 2023 Valerio Bozzolan, contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . /** * Utility to run HTTP queries against Phabricator / Phorge */ class PhabricatorAPI { public static function query( $entry_point, $query = [] ) { $client = new ConduitClient( PHABRICATOR_URL ); $client->setConduitToken( PHABRICATOR_CONDUIT_API_TOKEN ); return $client->callMethodSynchronous( $entry_point, $query ); } /** * Simplified query to the maniphest.edit Conduit Phabricator API * * https://sviluppo.erinformatica.it/conduit/method/maniphest.edit/ * * @param $transaction_values array Example: [ 'title' => 'ASD' ] * @param $extra_query array Example: ['objectIdentifier' => 'PHID--...'] to edit * @return mixed */ public static function createTask( $transaction_values = [], $query = [] ) { // build transactions $query['transactions'] = []; foreach( $transaction_values as $key => $value ) { $query['transactions'][] = self::transaction( $key, $value ); } return self::query( 'maniphest.edit', $query ); } /** * Simplified query to the maniphest.edit Conduit Phabricator API * * https://sviluppo.erinformatica.it/conduit/method/maniphest.edit/ * * @param $id string Task ID (e.g. 123 for T123) * @param $transaction_values array Example: [ 'title' => 'ASD' ] * @return mixed */ public static function editTask( $id, $transaction_values = [] ) { return self::createTask( $transaction_values, [ 'objectIdentifier' => $id, ] ); } /** * Check if a Phabricator status is equivalent to "closed" * * @return self */ public static function isStatusClosed( $task_status ) { // TODO: read from "maniphest.status.search" and set in cache and read from there $closed_statuses = [ 'resolved', 'wontfix', 'invalid', 'duplicate', 'spite', ]; return in_array( $task_status, $closed_statuses, true ); } /** * Convert a Phabricator status to a Clickup status * * @return "closed" or "open" */ public static function status2clickup( $task_status ) { $is_closed = self::isStatusClosed( $task_status ); if( $is_closed ) { return 'closed'; } return 'open'; } /** * Get a Task by its ID * * It returns just one element. * * https://sviluppo.erinformatica.it/conduit/method/maniphest.search/ * * @param $task_id mixed */ public static function getTask( $task_id ) { $task_id = self::sanitize_task_id( $task_id ); $query = [ 'constraints' => [ 'ids' => [ $task_id ], ], 'attachments' => [ 'columns' => true, 'projects' => true, ], ]; return self::searchSingleTask( $query ); } + public static function getUserByPHID( $phid ) { + $query = [ + 'constraints' => [ + 'phids' => [ $phid ], + ], + ]; + return self::querySingle( 'user.search', $query ); + } + + public static function getUserByUsername( $username ) { + $query = [ + 'constraints' => [ + 'usernames' => [ $username ], + ], + ]; + return self::querySingle( 'user.search', $query ); + } + public static function getTaskByPHID( $phid ) { $query = [ 'constraints' => [ 'phids' => [ $phid ], ], 'attachments' => [ 'columns' => true, 'projects' => true, ], ]; return self::searchSingleTask( $query ); } public static function searchSingleUserByPHID( $phid ) { $query = [ 'constraints' => [ 'phids' => [ $phid ], ], ]; return self::searchSingleUser( $query ); } public static function searchSingleTask( $query ) { return self::querySingle( 'maniphest.search', $query ); } public static function searchSingleUser( $query ) { return self::querySingle( 'user.search', $query ); } public static function querySingle( $method, $query ) { $results = self::query( $method, $query ); // just the first one is OK foreach( $results['data'] as $entry ) { return $entry; } throw new Exception( "Phabricator result not found from $method using: " . json_encode( $query ) ); } public static function searchObjectTransactionsFromTransactions( $phab_object_id, $transactions ) { $transaction_phids = []; foreach( $transactions as $transaction ) { $transaction_phids[] = $transaction['phid']; } return self::searchObjectTransactionsFromPHIDs( $phab_object_id, $transaction_phids ); } public static function searchObjectTransactionsFromPHIDs( $phab_object_id, $transaction_phids ) { $query = [ 'objectIdentifier' => $phab_object_id, 'constraints' => [ 'phids' => $transaction_phids, ], ]; $results = self::query( 'transaction.search', $query ); return $results['data']; } private static function transaction( $type, $value ) { return [ 'type' => $type, 'value' => $value, ]; } /** * Sanitize a Task ID * * @param $task_id mixed * @return int */ public static function sanitize_task_id( $task_id ) { // strip the damn 'T' since the 'id' API only accepts numeric $task_id = ltrim( $task_id, 'T' ); // no numeric no party $task_id = (int)$task_id; if( !$task_id ) { throw new Exception( "invalid Task ID" ); } return $task_id; } }