Skip to content

Commit

Permalink
Switch to Laravel Prompts, PERSCOM PHP SDK and local config instead o…
Browse files Browse the repository at this point in the history
…f database/models. (#1)
  • Loading branch information
jonerickson authored Oct 8, 2023
1 parent c0efd62 commit 9b86f6d
Show file tree
Hide file tree
Showing 27 changed files with 4,813 additions and 5,986 deletions.
26 changes: 26 additions & 0 deletions app/Commands/Command.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App\Commands;

use App\Repositories\ConfigRepository;
use App\Repositories\PerscomRepository;
use LaravelZero\Framework\Commands\Command as BaseCommand;

abstract class Command extends BaseCommand
{
protected $aliases = [];

protected ConfigRepository $config;

protected PerscomRepository $perscom;

public function __construct(ConfigRepository $config, PerscomRepository $perscom)
{
parent::__construct();

$this->config = $config;
$this->perscom = $perscom;

$this->setAliases($this->aliases);
}
}
118 changes: 23 additions & 95 deletions app/Commands/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,110 +2,38 @@

namespace App\Commands;

use App\Models\Setting;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Sleep;
use LaravelZero\Framework\Commands\Command;
use Phar;
use function Laravel\Prompts\info;
use function Laravel\Prompts\text;

class InstallCommand extends Command
{
/**
* The signature of the command.
*
* @var string
*/
protected $signature = 'install';

/**
* The description of the command.
*
* @var string
*/
protected $description = 'Installs and configures the PERSCOM CLI to connect and interact with your online dashboard.';

/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if (Phar::running()) {
if (! File::exists($_SERVER['HOME'].'/.perscom/framework/views') ||
! File::exists($_SERVER['HOME'].'/.perscom/app') ||
! File::exists($_SERVER['HOME'].'/.perscom/logs')) {
$this->task('Setting up the proper file structure', function () {
if (! File::exists($_SERVER['HOME'].'/.perscom/framework/views')) {
File::makeDirectory($_SERVER['HOME'].'/.perscom/framework/views', 0755, true);
}

if (! File::exists($_SERVER['HOME'].'/.perscom/app')) {
File::makeDirectory($_SERVER['HOME'].'/.perscom/app', 0755, true);
}

if (! File::exists($_SERVER['HOME'].'/.perscom/logs')) {
File::makeDirectory($_SERVER['HOME'].'/.perscom/logs', 0755, true);
}

Sleep::for(1)->seconds();

return true;
});
}

if (! File::exists($_SERVER['HOME'].'/.perscom/database/database.sqlite')) {
$this->task('Provisioning a database on the local system', function () {
if (! File::exists($_SERVER['HOME'].'/.perscom/database')) {
File::makeDirectory($_SERVER['HOME'].'/.perscom/database', 0755, true);
}
File::put($_SERVER['HOME'].'/.perscom/database/database.sqlite', '');

Sleep::for(1)->seconds();

return true;
});

$this->task('Setting up and migrating the database', function () {
Artisan::call('migrate --force');

Sleep::for(1)->seconds();

return true;
});
}
}

$found = Setting::query()->whereIn('key', ['perscom_id', 'api_key'])->whereNotNull('value')->exists();

$continue = true;
if ($found) {
$continue = $this->confirm('We found your saved PERSCOM credentials. Do you wish to overwrite these credentials and continue?', true);
}

if (! $continue) {
return Command::SUCCESS;
}

$perscomId = $this->ask('What is your PERSCOM ID');
$apiKey = $this->ask('What is your PERSCOM API key');

$this->task('Saving settings to the database', function () use ($perscomId, $apiKey) {
Setting::updateOrCreate([
'key' => 'perscom_id',
], ['value' => $perscomId]);

Setting::updateOrCreate([
'key' => 'api_key',
], ['value' => $apiKey]);

Sleep::for(1)->seconds();

return true;
});

$this->info('The PERSCOM CLI has been successfully configured.');
$perscomId = text(
label: 'Please enter your PERSCOM ID:',
required: true,
validate: fn (string $value) => match (true) {
! is_numeric($value) => 'The PERSCOM ID must be a number.',
default => null
},
hint: 'You can find this located in the settings of your PERSCOM dashboard.'
);

$this->config->set('perscomId', $perscomId);

$apiKey = text(
label: 'Please enter your PERSCOM API key:',
required: true,
hint: 'You must have the "manage:api" permissions to create and view your API keys.'
);

$this->config->set('apiKey', $apiKey);

info('<fg=green>==></> <options=bold>You have successfully setup the PERSCOM CLI.');

return Command::SUCCESS;
}
Expand Down
114 changes: 23 additions & 91 deletions app/Commands/ResourceCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,110 +2,42 @@

namespace App\Commands;

use App\Contracts\PerscomApiServiceContract;
use App\Contracts\ResourceCommandContract;
use App\Models\Setting;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use Phar;
use Symfony\Component\HttpFoundation\Request;
use function Termwind\render;
use Saloon\Exceptions\Request\RequestException;

use function Laravel\Prompts\error;
use function Laravel\Prompts\info;
use function Laravel\Prompts\spin;
use function Laravel\Prompts\table;

abstract class ResourceCommand extends Command implements ResourceCommandContract
{
/**
* @var string
*/
protected $method = 'GET';

/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if (Phar::running() && ! File::exists($_SERVER['HOME'].'/.perscom/database/database.sqlite')) {
$this->error('Unable to find the local database. Please run the install command to perform the PERSCOM CLI setup.');

return Command::FAILURE;
}

if (! Setting::query()->whereIn('key', ['perscom_id', 'api_key'])->whereNotNull('value')->exists()) {
$this->error('Unable to load saved PERSCOM credentials. Please run the install command to perform the PERSCOM CLI setup.');

return Command::FAILURE;
}

$apiService = app()->make(PerscomApiServiceContract::class);

$id = null;
if ($this->hasArgument('id')) {
$id = $this->argument('id');
}
try {
$response = spin(
fn () => $this->performApiCall(),
'Performing operation...'
);

$body = null;
if ($this->hasOption('body')) {
$body = json_decode($this->option('body'), true);
$transformer = app()->make($this->transformer);
[$keys, $data] = $transformer->transform($response);

if (is_null($body)) {
$this->error('The body provided is not proper JSON. Please try again.');
info('<fg=green>==></> <options=bold>The operation has successfully been performed.');

return Command::FAILURE;
}
}

$response = $apiService->api(optional($id, function ($id) {
return "$this->endpoint/$id";
}) ?: $this->endpoint, $this->method, $body);

if (! $response->successful()) {
$this->error($response->json('error.message', 'There was an error with your last request. Please try again.'));

return Command::FAILURE;
}

$resource = Str::lower(Str::singular($this->endpoint));

if ($this->method === Request::METHOD_POST) {
$this->info("The $resource has been successfully created.");
}

if ($this->method === Request::METHOD_PUT) {
$this->info("The $resource has been successfully updated.");
}

if ($this->method === Request::METHOD_DELETE) {
$this->info("The $resource has been successfully deleted.");
table($keys, $data);

return Command::SUCCESS;
}

$transformer = app()->make($this->transformer);

if ($this->hasOption('keys')) {
$keys = explode(',', $this->option('keys'));
}
} catch (\Exception $exception) {
$message = match (true) {
$exception instanceof RequestException => $exception->getResponse()->json('error.message') ?? null,
$exception->getMessage() !== '' => $exception->getMessage(),
default => 'There was an error with the last operation.'
};

switch ($this->option('output')) {
case 'json':
$this->line($response);
break;
error($message);

case 'html':
$this->line(view('api.view', [
'transformer' => $transformer->transform($response->json('data'), $keys),
])->render());
break;

default:
render(view('api.view', [
'transformer' => $transformer->transform($response->json('data'), $keys),
]));
break;
return Command::FAILURE;
}

return Command::SUCCESS;
}
}
21 changes: 21 additions & 0 deletions app/Commands/UninstallCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace App\Commands;

use function Laravel\Prompts\info;

class UninstallCommand extends Command
{
protected $signature = 'uninstall';

protected $description = 'Uninstalls the PERSCOM CLI and removes all stored credentials.';

public function handle()
{
$this->config->flush();

info('<fg=green>==></> <options=bold>The PERSCOM CLI has been successfully uninstalled.');

return Command::SUCCESS;
}
}
49 changes: 20 additions & 29 deletions app/Commands/Users/UsersCreateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,34 @@

use App\Commands\ResourceCommand;
use App\Transformers\UsersTransformer;
use Illuminate\Contracts\Console\PromptsForMissingInput;

class UsersCreateCommand extends ResourceCommand
class UsersCreateCommand extends ResourceCommand implements PromptsForMissingInput
{
/**
* The signature of the command.
*
* @var string
*/
protected $signature = 'users:create
{name : The name of the user being updated}
{email : The email of the user being updated}
{--body= : The JSON payload containing the new user data}
{--keys= : A comma-delimited list of additional attributes to include (optional)}
{--include= : A comma-delimited list of resource relationships to include (optional)}
{--output=table : The intended output of the command (options: table, json, html)}';
{--include= : A comma-delimited list of resource relationships to include (optional)}';

/**
* The description of the command.
*
* @var string
*/
protected $description = 'Create a new user';

/**
* The API endpoint
*
* @var string
*/
protected $endpoint = 'users';
protected string $transformer = UsersTransformer::class;

/**
* The transformer to use
*
* @var string
*/
protected $transformer = UsersTransformer::class;
public function performApiCall(): array
{
return $this->perscom->users()->create([
'name' => $this->option('name'),
'email' => $this->option('email'),
])->json('data');
}

/**
* @var string
*/
protected $method = 'POST';
protected function promptForMissingArgumentsUsing(): array
{
return [
'name' => 'What is the user\'s name?',
'email' => 'Which is the user\'s email?',
];
}
}
Loading

0 comments on commit 9b86f6d

Please sign in to comment.