From a97d7058ff639d27aa65263363215999d94ae17b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 21 Mar 2020 21:32:17 +0100 Subject: [PATCH] Telemetry cron job. --- app/Console/Commands/Tools/Cron.php | 42 ++++++ app/Jobs/SubmitTelemetryData.php | 170 ++++++++++++++++++++++ app/Support/Cronjobs/TelemetryCronjob.php | 90 ++++++++++++ 3 files changed, 302 insertions(+) create mode 100644 app/Jobs/SubmitTelemetryData.php create mode 100644 app/Support/Cronjobs/TelemetryCronjob.php diff --git a/app/Console/Commands/Tools/Cron.php b/app/Console/Commands/Tools/Cron.php index f633b5d207..328b473b07 100644 --- a/app/Console/Commands/Tools/Cron.php +++ b/app/Console/Commands/Tools/Cron.php @@ -29,6 +29,7 @@ use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Support\Cronjobs\AutoBudgetCronjob; use FireflyIII\Support\Cronjobs\RecurringCronjob; +use FireflyIII\Support\Cronjobs\TelemetryCronjob; use Illuminate\Console\Command; use InvalidArgumentException; use Log; @@ -92,6 +93,17 @@ class Cron extends Command $this->error($e->getMessage()); } + /* + * Fire telemetry cron job (disabled): + */ + try { + //$this->telemetryCronJob($force, $date); + } catch (FireflyException $e) { + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); + $this->error($e->getMessage()); + } + $this->info('More feedback on the cron jobs can be found in the log files.'); // app('telemetry')->feature('executed-command', $this->signature); @@ -150,4 +162,34 @@ class Cron extends Command $this->line('The recurring transaction cron job fired successfully.'); } } + + /** + * @param bool $force + * @param Carbon|null $date + */ + private function telemetryCronJob(bool $force, ?Carbon $date): void + { + if (false === config('firefly.send_telemetry') || false === config('firefly.feature_flags.telemetry')) { + // if not configured to do anything with telemetry, do nothing. + return; + } + $telemetry = new TelemetryCronJob; + $telemetry->setForce($force); + + // set date in cron job: + if (null !== $date) { + $telemetry->setDate($date); + } + + $result = $telemetry->fire(); + + if (false === $result) { + $this->line('The telemetry cron job did not fire.'); + } + if (true === $result) { + $this->line('The telemetry cron job fired successfully.'); + } + + } + } diff --git a/app/Jobs/SubmitTelemetryData.php b/app/Jobs/SubmitTelemetryData.php new file mode 100644 index 0000000000..5da30f9e28 --- /dev/null +++ b/app/Jobs/SubmitTelemetryData.php @@ -0,0 +1,170 @@ +. + */ + +namespace FireflyIII\Jobs; + + +use Carbon\Carbon; +use FireflyIII\Models\Telemetry; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; +use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Collection; +use Log; + +/** + * Class SubmitTelemetryData + */ +class SubmitTelemetryData implements ShouldQueue +{ + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + /** @var Carbon The current date */ + private $date; + /** @var bool Force the transaction to be created no matter what. */ + private $force; + + /** + * Create a new job instance. + * + * @codeCoverageIgnore + * + * @param Carbon $date + */ + public function __construct(?Carbon $date) + { + $this->date = $date; + Log::debug('Created new SubmitTelemetryData'); + } + + /** + * + */ + public function handle(): void + { + $url = sprintf('%s/submit', config('firefly.telemetry_endpoint')); + Log::debug(sprintf('Will submit telemetry to endpoint: %s', $url)); + + $telemetry = $this->collectTelemetry(); + + if (0 === $telemetry->count()) { + Log::debug('Nothing to submit.'); + + return; + } + + + $json = $this->parseJson($telemetry); + + $client = new Client; + $options = [ + 'body' => json_encode($json, JSON_THROW_ON_ERROR, 512), + 'headers' => [ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'connect_timeout' => 3.14, + 'User-Agent' => sprintf('FireflyIII/%s', config('firefly.version')), + ], + ]; + try { + $result = $client->post($url, $options); + } catch (GuzzleException $e) { + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); + + return; + } + $body = (string) $result->getBody(); + $statusCode = $result->getStatusCode(); + Log::info(sprintf('Result of submission [%d]: %s', $statusCode, $body)); + if (200 === $statusCode) { + // mark as submitted: + $this->markAsSubmitted($telemetry); + } + } + + /** + * @param Carbon $date + */ + public function setDate(Carbon $date): void + { + $this->date = $date; + } + + /** + * @param bool $force + */ + public function setForce(bool $force): void + { + $this->force = $force; + } + + /** + * @return Collection + */ + private function collectTelemetry(): Collection + { + $collection = Telemetry::whereNull('submitted')->get(); + Log::debug(sprintf('Found %d entry(s) to submit', $collection->count())); + + return $collection; + } + + /** + * @param Collection $telemetry + */ + private function markAsSubmitted(Collection $telemetry): void + { + $telemetry->each( + static function (Telemetry $entry) { + $entry->submitted = new Carbon; + $entry->save(); + } + ); + } + + /** + * @param Collection $telemetry + * + * @return array + */ + private function parseJson(Collection $telemetry): array + { + $array = []; + /** @var Telemetry $entry */ + foreach ($telemetry as $entry) { + $array[] = [ + 'installation_id' => $entry->installation_id, + 'type' => $entry->type, + 'key' => $entry->key, + 'value' => $entry->value, + ]; + } + + return $array; + } + +} \ No newline at end of file diff --git a/app/Support/Cronjobs/TelemetryCronjob.php b/app/Support/Cronjobs/TelemetryCronjob.php new file mode 100644 index 0000000000..8efd8c889f --- /dev/null +++ b/app/Support/Cronjobs/TelemetryCronjob.php @@ -0,0 +1,90 @@ +. + */ + +namespace FireflyIII\Support\Cronjobs; + +use Carbon\Carbon; +use FireflyIII\Jobs\SubmitTelemetryData; +use FireflyIII\Models\Configuration; +use Log; + +/** + * Class TelemetryCronjob + */ +class TelemetryCronjob extends AbstractCronjob +{ + + /** + * @inheritDoc + */ + public function fire(): bool + { + /** @var Configuration $config */ + $config = app('fireflyconfig')->get('last_tm_job', 0); + $lastTime = (int) $config->data; + $diff = time() - $lastTime; + $diffForHumans = Carbon::now()->diffForHumans(Carbon::createFromTimestamp($lastTime), true); + if (0 === $lastTime) { + Log::info('Telemetry cron-job has never fired before.'); + } + // less than half a day ago: + if ($lastTime > 0 && $diff <= 43200) { + Log::info(sprintf('It has been %s since the telemetry cron-job has fired.', $diffForHumans)); + if (false === $this->force) { + Log::info('The cron-job will not fire now.'); + + return false; + } + + // fire job regardless. + if (true === $this->force) { + Log::info('Execution of the telemetry cron-job has been FORCED.'); + } + } + + if ($lastTime > 0 && $diff > 43200) { + Log::info(sprintf('It has been %s since the telemetry cron-job has fired. It will fire now!', $diffForHumans)); + } + + $this->fireTelemetry(); + + app('preferences')->mark(); + + return true; + } + + + /** + * + */ + private function fireTelemetry(): void + { + Log::info(sprintf('Will now fire telemetry cron job task for date "%s".', $this->date->format('Y-m-d'))); + /** @var SubmitTelemetryData $job */ + $job = app(SubmitTelemetryData::class); + $job->setDate($this->date); + $job->setForce($this->force); + $job->handle(); + app('fireflyconfig')->set('last_tm_job', (int) $this->date->format('U')); + Log::info('Done with telemetry cron job task.'); + } +} \ No newline at end of file