Completed
Push — master ( 0c0dcf...a1d6a9 )
by Bernhard
01:22
created

Push::promptSilent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
/**
4
 * \Wicked\Timely\Command\Push
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Bernhard Wick <[email protected]>
15
 * @copyright 2016 Bernhard Wick
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/wick-ed/timely
18
 */
19
20
namespace Wicked\Timely\Command;
21
22
use JiraRestApi\Configuration\ConfigurationInterface;
23
use Wicked\Timely\DotEnvConfiguration;
24
use JiraRestApi\Issue\IssueService;
25
use JiraRestApi\Issue\Worklog;
26
use JiraRestApi\JiraException;
27
use Symfony\Component\Console\Input\InputInterface;
28
use Symfony\Component\Console\Output\OutputInterface;
29
use Wicked\Timely\Entities\TaskFactory;
30
use Wicked\Timely\Formatter\FormatterFactory;
31
use Wicked\Timely\Helper\Date;
32
use Wicked\Timely\Storage\StorageFactory;
33
34
/**
35
 * Class for the "track" command. Command is used to track time bookings
36
 *
37
 * @author    Bernhard Wick <[email protected]>
38
 * @copyright 2016 Bernhard Wick
39
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
40
 * @link      https://github.com/wick-ed/timely
41
 */
42
class Push extends AbstractReadCommand
43
{
44
45
    /**
46
     * Constants used within this command
47
     *
48
     * @var string
49
     */
50
    const COMMAND_NAME = 'push';
51
    const KEYCHAIN_NAME = 'osxkeychain';
52
    const KEYCHAIN_SAVE = 'timely jira access';
53
54
    /**
55
     * Configures the "track" command
56
     *
57
     * @return void
58
     *
59
     * {@inheritDoc}
60
     * @see \Symfony\Component\Console\Command\Command::configure()
61
     */
62
    protected function configure()
63
    {
64
        $this
65
        ->setName(static::COMMAND_NAME)
66
        ->setDescription('Pushes booked times against the configured remote');
67
68
        // add all the read options from the abstract super class
69
        parent::configure();
70
    }
71
72
    /**
73
     * Execute the command
74
     *
75
     * @param \Symfony\Component\Console\Input\InputInterface   $input  The command input
76
     * @param \Symfony\Component\Console\Output\OutputInterface $output The command output
77
     *
78
     * @return void
79
     *
80
     * {@inheritDoc}
81
     * @throws JiraException
82
     * @throws \JsonMapper_Exception
83
     * @see \Symfony\Component\Console\Command\Command::execute()
84
     */
85
    protected function execute(InputInterface $input, OutputInterface $output)
86
    {
87
//        $passwd = $this->promptSilent($output);
88
//        $output->writeln('fertig für heute:'.$passwd.'#');
89
90
        // get the ticket
91
        $ticket = $input->getArgument('ticket');
92
93
        // we might need framing dates
94
        $toDate = null;
95
        $fromDate = null;
96
97
        // there might be a limit
98
        $limit = null;
99
100
        // prepare all the input options
101
        $this->prepareInputParams($input, $ticket, $fromDate, $toDate, $limit);
0 ignored issues
show
Bug introduced by
It seems like $ticket defined by $input->getArgument('ticket') on line 91 can also be of type array<integer,string> or null; however, Wicked\Timely\Command\Ab...d::prepareInputParams() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
102
103
        // filter by ticket if given
104
        /** @var \Wicked\Timely\Storage\StorageInterface $storage */
105
        $storage = StorageFactory::getStorage();
106
        $bookings = $storage->retrieve($ticket, $toDate, $fromDate, $limit);
107
108
        // return if we did not find any bookings
109
        if (empty($bookings)) {
110
            $output->write('No bookings found, nothing to push ...', true);
111
            return;
112
        }
113
114
        // get tasks from bookings
115
        $tasks = TaskFactory::getTasksFromBookings($bookings);
116
117
        // retrieve configuration
118
        $configuration = new DotEnvConfiguration();
119
        $password = $configuration->getJiraPassword();
120
        if (empty($password) || strtolower($password) === self::KEYCHAIN_NAME) {
121
            $password = $this->getPasswordFromKeychain($output, $configuration);
122
            if (empty($password)) {
123
                return;
124
            }
125
            $configuration->setJiraPassword($password);
126
127
        }
128
        unset($password);
129
        $bookingsPushed = array();
130
        // get our issue service and push the tasks
131
        $issueService = new IssueService($configuration);
132
        foreach ($tasks as $task) {
133
            // Allready pushed to jira? take next one
134
            if ($task->isPushed()) {
135
                continue;
136
            }
137
            // create a worklog from the task
138
            $workLog = new Worklog();
139
            $workLog->setComment($task->getComment())
140
                ->setStarted($task->getStartTime())
141
                ->setTimeSpent(Date::secondsToUnits(Date::roundByInterval($task->getDuration(), 900)));
142
143
            // check the sanity of our worklog and discard it if there is something missing
144
            if (!$task->getTicketId() || empty($workLog->timeSpent) || empty($workLog->comment)) {
145
                $output->writeln('Not pushing one worklog as it misses vital information');
146
                continue;
147
            }
148
149
            // log the worklog about to being pushed if output is verbose
150
            if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
151
                $output->writeln(
152
                    sprintf(
153
                        '%s : %s > %s',
154
                        $task->getTicketId(),
155
                        $workLog->timeSpent,
156
                        $workLog->comment
157
                    )
158
                );
159
            }
160
161
            try {
162
                // push to remote
163
                $issueService->addWorklog($task->getTicketId(), $workLog);
164
165
                $output->writeln(
166
                    sprintf(
167
                        'PUSHED %s : %s > %s',
168
                        $task->getTicketId(),
169
                        $workLog->timeSpent,
170
                        $workLog->comment
171
                    )
172
                );
173
174
                $bookingsPushed[] = $task->getStartBooking();
175
176
            } catch (JiraException $e) {
177
                $output->write(
178
                    sprintf(
179
                        'Error while pushing. Status %s, with message: "%s"',
180
                        $e->getCode(),
181
                        $e->getMessage()
182
                    )
183
                );
184
            }
185
        }
186
187
        foreach ($bookingsPushed as $booking) {
188
            $storage->storePush($booking);
189
            $formatter = FormatterFactory::getFormatter();
190
            $bookString = $formatter->toString($booking);
191
            $output->write($bookString, true);
192
        }
193
194
        // write output
195
        $output->write(sprintf('Successfully pushed %s tasks.', count($bookingsPushed)), true);
196
197
    }
198
199
    /**
200
     * Prompt silent
201
     *
202
     * Interactively prompts for input without echoing to the terminal.
203
     * Requires a bash shell or Windows and won't work with
204
     * safe_mode settings (Uses `shell_exec`)
205
     *
206
     * Source: http://www.sitepoint.com/interactive-cli-password-prompt-in-php/
207
     *
208
     * @param OutputInterface $output Console output interface
209
     * @param string          $prompt The message to the user
210
     *
211
     * @return string
212
     */
213
    protected function promptSilent(OutputInterface $output, $prompt = "Enter Password:")
214
    {
215
        $command = "/usr/bin/env bash -c 'echo OK'";
216
        if (rtrim(shell_exec($command)) !== 'OK') {
217
            trigger_error("Can't invoke bash");
218
            return '';
219
        }
220
        $command = "/usr/bin/env bash -c 'read -s -p \""
221
                   . addslashes($prompt)
222
                   . "\" mypassword && echo \$mypassword'";
223
        $password = rtrim(shell_exec($command));
224
        $output->writeln('');
225
        return $password;
226
    }
227
228
    /**
229
     * Will retrieve a stored password from the OSX keychain
230
     *
231
     * @param OutputInterface        $output        Console output interface
232
     * @param ConfigurationInterface $configuration Jira configuration
233
     *
234
     * @return string
235
     */
236
    private function getPasswordFromKeychain(OutputInterface $output, ConfigurationInterface $configuration)
237
    {
238
        $password = rtrim(shell_exec("security find-generic-password -s '".self::KEYCHAIN_SAVE."' -w"));
239
        if (empty($password)) {
240
            $password = $this->promptSilent($output, 'Please enter password for your jira account "'.$configuration->getJiraUser().'":');
241
            if (empty($password)) {
242
                $output->writeln('Empty password is not possible. Stop push ...');
243
                return '';
244
            }
245
            shell_exec('security add-generic-password -a "'.$configuration->getJiraUser().'" -s "'.self::KEYCHAIN_SAVE.'" -w "'.$password.'"');
246
        }
247
        return $password;
248
    }
249
}
250