Skip to content

Commit

Permalink
Implement telemetry settings (repman-io#225)
Browse files Browse the repository at this point in the history
* Implement telemetry

* Show telemetry documentation link
  • Loading branch information
karniv00l committed Jul 24, 2020
1 parent aec3462 commit 07a5395
Show file tree
Hide file tree
Showing 13 changed files with 212 additions and 6 deletions.
2 changes: 2 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ parameters:
router.request_context.host: '%env(default:domain:APP_PUBLIC_HOST)%'
security_advisories_db_dir: '%env(resolve:SECURITY_ADVISORIES_DB_DIR)%'
security_advisories_db_repo: 'https://github.com/FriendsOfPHP/security-advisories.git'
instance_id_file: '%kernel.project_dir%/var/instance-id'

services:
# default configuration for services in *this* file
Expand All @@ -26,6 +27,7 @@ services:
$resetPasswordTokenTtl: 86400 # 24h
Symfony\Component\HttpFoundation\Session\Session $session: '@session'
$proxyFilesystem: '@proxy.storage'
$instanceIdFile: '%instance_id_file%'

# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
Expand Down
1 change: 1 addition & 0 deletions config/services_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ parameters:
repo_dir: '%kernel.project_dir%/tests/Resources'
security_advisories_db_dir: '%kernel.project_dir%/tests/Resources/fixtures/security/security-advisories'
security_advisories_db_repo: 'bogus'
instance_id_file: '%kernel.cache_dir%/test-instance-id'

services:
Buddy\Repman\Service\Downloader:
Expand Down
24 changes: 23 additions & 1 deletion src/Command/CreateAdminCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,28 @@

namespace Buddy\Repman\Command;

use Buddy\Repman\Message\Admin\ChangeConfig;
use Buddy\Repman\Message\User\CreateUser;
use Buddy\Repman\Service\Telemetry;
use Ramsey\Uuid\Uuid;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Messenger\MessageBusInterface;

final class CreateAdminCommand extends Command
{
private MessageBusInterface $bus;
private Telemetry $telemetry;

public function __construct(MessageBusInterface $bus)
public function __construct(MessageBusInterface $bus, Telemetry $telemetry)
{
$this->bus = $bus;
$this->telemetry = $telemetry;

parent::__construct();
}

Expand Down Expand Up @@ -53,6 +59,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int
['ROLE_ADMIN']
));

if (!$this->telemetry->isInstanceIdPresent()) {
$question = new ConfirmationQuestion(
"Allow for sending anonymous usage statistic? [{$this->telemetry->docsUrl()}] (y/n)",
true
);
$answer = $this
->getHelper('question')
->ask($input, $output, $question);

$this->bus->dispatch(new ChangeConfig([
'telemetry' => $answer ? 'enabled' : 'disabled',
]));

$this->telemetry->generateInstanceId();
}

$output->writeln(sprintf('Created admin user with id: %s', $id));

return 0;
Expand Down
31 changes: 30 additions & 1 deletion src/Controller/Admin/ConfigController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Buddy\Repman\Form\Type\Admin\ConfigType;
use Buddy\Repman\Message\Admin\ChangeConfig;
use Buddy\Repman\Service\Config;
use Buddy\Repman\Service\Telemetry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
Expand All @@ -15,10 +16,12 @@
final class ConfigController extends AbstractController
{
private Config $config;
private Telemetry $telemetry;

public function __construct(Config $config)
public function __construct(Config $config, Telemetry $telemetry)
{
$this->config = $config;
$this->telemetry = $telemetry;
}

/**
Expand All @@ -39,4 +42,30 @@ public function edit(Request $request): Response
'form' => $form->createView(),
]);
}

/**
* @Route("/admin/config/telemetry", name="admin_config_telemetry_enable", methods={"POST"})
*/
public function enableTelemetry(Request $request): Response
{
$this->telemetry->generateInstanceId();
$this->dispatchMessage(new ChangeConfig([
'telemetry' => 'enable',
]));

return $this->redirectToRoute('index');
}

/**
* @Route("/admin/config/telemetry", name="admin_config_telemetry_disable", methods={"DELETE"})
*/
public function disableTelemetry(Request $request): Response
{
$this->telemetry->generateInstanceId();
$this->dispatchMessage(new ChangeConfig([
'telemetry' => 'disable',
]));

return $this->redirectToRoute('index');
}
}
15 changes: 14 additions & 1 deletion src/Controller/IndexController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,30 @@

namespace Buddy\Repman\Controller;

use Buddy\Repman\Service\Telemetry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

final class IndexController extends AbstractController
{
private Telemetry $telemetry;

public function __construct(Telemetry $telemetry)
{
$this->telemetry = $telemetry;
}

/**
* @Route(path="/", name="index", methods={"GET"})
*/
public function index(): Response
{
return $this->render('index.html.twig');
$showTelemetryPrompt = !$this->telemetry->isInstanceIdPresent();

return $this->render('index.html.twig', [
'showTelemetryPrompt' => $showTelemetryPrompt,
'telemetryDocsUrl' => $this->telemetry->docsUrl(),
]);
}
}
11 changes: 11 additions & 0 deletions src/Form/Type/Admin/ConfigType.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'data-style' => 'btn-secondary',
],
])
->add('telemetry', ChoiceType::class, [
'choices' => [
'enabled' => 'enabled',
'disabled' => 'disabled',
],
'help' => 'Enable collecting and sending anonymous usage data',
'attr' => [
'class' => 'form-control selectpicker',
'data-style' => 'btn-secondary',
],
])
->add('save', SubmitType::class, ['label' => 'Save'])
;
}
Expand Down
35 changes: 35 additions & 0 deletions src/Migrations/Version20200716105216.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Buddy\Repman\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20200716105216 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');

$this->addSql("INSERT INTO config (key, value) VALUES ('telemetry', 'disabled')");
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');

$this->addSql("DELETE FROM config WHERE key = 'telemetry'");
}
}
34 changes: 34 additions & 0 deletions src/Service/Telemetry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Buddy\Repman\Service;

use Ramsey\Uuid\Uuid;

final class Telemetry
{
private string $instanceIdFile;

public function __construct(string $instanceIdFile)
{
$this->instanceIdFile = $instanceIdFile;
}

public function docsUrl(): string
{
return 'https://repman.io/docs/telemetry';
}

public function generateInstanceId(): void
{
if (!$this->isInstanceIdPresent()) {
\file_put_contents($this->instanceIdFile, Uuid::uuid4());
}
}

public function isInstanceIdPresent(): bool
{
return \file_exists($this->instanceIdFile);
}
}
2 changes: 1 addition & 1 deletion templates/admin/config/edit.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

{% block content %}
<div class="row">
<div class="col-md-6 col-lg-4">
<div class="col-md-6 col-lg-5">
{{ form(form) }}
</div>
</div>
Expand Down
17 changes: 17 additions & 0 deletions templates/component/telemetryPrompt.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div class="alert alert-info">
<h4>Telemetry</h4>
<p>
Help us improve <strong>Repman</strong> by enabling sending anonymous usage statistic
(<a href="{{ telemetryDocsUrl }}" target="_blank" rel="noopener noreferrer">more info</a>).
</p>
<div class="btn-list">
<form class="card" action="{{ path('admin_config_telemetry_enable') }}" method="POST">
<input type="hidden" name="_method" value="POST">
<button class="btn btn-info" type="submit">Okay</button>
</form>
<form class="card" action="{{ path('admin_config_telemetry_disable') }}" method="POST">
<input type="hidden" name="_method" value="DELETE">
<button class="btn btn-secondary" type="submit">No, thanks</button>
</form>
</div>
</div>
5 changes: 4 additions & 1 deletion templates/index.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
{% block page %}
<div class="content-page">
<main class="container my-4 flex-fill">

{% include 'component/flash.html.twig' %}

{% if is_granted('ROLE_ADMIN') and showTelemetryPrompt %}
{% include 'component/telemetryPrompt.html.twig' %}
{% endif %}

<div class="row justify-content-center">
<div class="col-lg-6">
<div class="card">
Expand Down
5 changes: 4 additions & 1 deletion tests/Functional/Command/CreateAdminCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@

use Buddy\Repman\Command\CreateAdminCommand;
use Buddy\Repman\Tests\Functional\FunctionalTestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;

final class CreateAdminCommandTest extends FunctionalTestCase
{
public function testCreateAdmin(): void
{
$commandTester = new CommandTester($this->container()->get(CreateAdminCommand::class));
$command = $this->container()->get(CreateAdminCommand::class);
$command->setApplication(new Application());
$commandTester = new CommandTester($command);
$commandTester->execute([
'email' => 'test@buddy.works',
'password' => 'password',
Expand Down
36 changes: 36 additions & 0 deletions tests/Functional/Controller/Admin/ConfigControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,40 @@ public function testToggleAuthenticationOptions(): void
$this->lastResponseBody()
);
}

public function testEnableTelemetry(): void
{
$prompt = 'Help us improve <strong>Repman</strong> by enabling sending anonymous usage statistic';
$instanceIdFile = $this->container()->getParameter('instance_id_file');
@unlink($instanceIdFile);
$this->client->request('GET', $this->urlTo('index'));
self::assertStringContainsString($prompt, $this->lastResponseBody());

$this->client->request('POST', $this->urlTo('admin_config_telemetry_enable'));

self::assertTrue($this->client->getResponse()->isRedirect($this->urlTo('index')));
$this->client->followRedirect();

self::assertStringNotContainsString($prompt, $this->lastResponseBody());
self::assertFileExists($instanceIdFile);
@unlink($instanceIdFile);
}

public function testDisableTelemetry(): void
{
$prompt = 'Help us improve <strong>Repman</strong> by enabling sending anonymous usage statistic';
$instanceIdFile = $this->container()->getParameter('instance_id_file');
@unlink($instanceIdFile);
$this->client->request('GET', $this->urlTo('index'));
self::assertStringContainsString($prompt, $this->lastResponseBody());

$this->client->request('DELETE', $this->urlTo('admin_config_telemetry_enable'));

self::assertTrue($this->client->getResponse()->isRedirect($this->urlTo('index')));
$this->client->followRedirect();

self::assertStringNotContainsString($prompt, $this->lastResponseBody());
self::assertFileExists($instanceIdFile);
@unlink($instanceIdFile);
}
}

0 comments on commit 07a5395

Please sign in to comment.