Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement telemetry settings #225

Merged
merged 2 commits into from
Jul 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -25,6 +26,7 @@ services:
$distsDir: '%dists_dir%'
$resetPasswordTokenTtl: 86400 # 24h
Symfony\Component\HttpFoundation\Session\Session $session: '@session'
$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');
}
akondas marked this conversation as resolved.
Show resolved Hide resolved
}
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>
akondas marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -96,4 +96,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);
}
}