Introducing settings file, some refactorings and stabilization

This commit is contained in:
2024-07-07 23:43:15 +08:00
parent 5666702ccc
commit 47930b010f
22 changed files with 910 additions and 328 deletions

View File

@@ -5,93 +5,74 @@ declare(strict_types=1);
namespace PmConverter;
use Exception;
use Generator;
use InvalidArgumentException;
use JetBrains\PhpStorm\NoReturn;
use JsonException;
use PmConverter\Converters\{
ConverterContract,
ConvertFormat};
use PmConverter\Exceptions\{
CannotCreateDirectoryException,
DirectoryIsNotReadableException,
DirectoryIsNotWriteableException,
DirectoryNotExistsException};
use PmConverter\Converters\Abstract\AbstractConverter;
use PmConverter\Converters\ConverterContract;
use PmConverter\Converters\ConvertFormat;
use PmConverter\Exceptions\CannotCreateDirectoryException;
use PmConverter\Exceptions\DirectoryIsNotReadableException;
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
use PmConverter\Exceptions\DirectoryNotExistsException;
use PmConverter\Exceptions\IncorrectSettingsFileException;
/**
* Main class
*/
class Processor
{
/**
* Converter version
*/
public const VERSION = '1.5.0';
/**
* @var string[] Paths to collection files
*/
protected array $collectionPaths = [];
/**
* @var string Output path where to put results in
*/
protected string $outputPath;
/**
* @var bool Flag to remove output directories or not before conversion started
*/
protected bool $preserveOutput = false;
/**
* @var string[] Additional variables
*/
protected array $vars;
/**
* @var ConvertFormat[] Formats to convert a collections into
*/
protected array $formats;
/**
* @var ConverterContract[] Converters will be used for conversion according to choosen formats
*/
protected array $converters = [];
/**
* @var Collection[] Collections that will be converted into choosen formats
*/
protected array $collections = [];
public const VERSION = '1.6.0';
/**
* @var int Initial timestamp
*/
protected int $initTime;
protected readonly int $initTime;
/**
* @var int Initial RAM usage
*/
protected int $initRam;
protected readonly int $initRam;
/**
* @var string Path to environment file
* @var Settings Settings (lol)
*/
protected string $envFile;
protected Settings $settings;
/**
* @var ConverterContract[] Converters will be used for conversion according to chosen formats
*/
protected array $converters = [];
/**
* @var bool Do we need to save settings file and exit or not?
*/
protected bool $needDumpSettings = false;
/**
* @var Environment
*/
protected Environment $env;
/**
* @var bool Flag to output some debug-specific messages
*/
protected bool $devMode = false;
public Environment $env;
/**
* Constructor
*
* @param array $argv Arguments came from cli
*/
public function __construct(protected array $argv)
public function __construct(protected readonly array $argv)
{
$this->initTime = hrtime(true);
$this->initRam = memory_get_usage(true);
$this->settings = Settings::init();
$this->env = Environment::instance()
->readFromFile($this->settings->envFilepath())
->setCustomVars($this->settings->vars());
$this->parseArgs();
$this->needDumpSettings && $this->dumpSettingsFile();
}
/**
@@ -102,21 +83,11 @@ class Processor
*/
protected function parseArgs(): void
{
if (count($this->argv) < 2) {
die(implode(EOL, $this->usage()) . EOL);
}
foreach ($this->argv as $idx => $arg) {
switch ($arg) {
case '-f':
case '--file':
$rawpath = $this->argv[$idx + 1];
$normpath = FileSystem::normalizePath($rawpath);
if (!FileSystem::isCollectionFile($normpath)) {
throw new InvalidArgumentException(
sprintf("not a valid collection:%s\t%s %s", EOL, $arg, $rawpath)
);
}
$this->collectionPaths[] = $this->argv[$idx + 1];
$this->settings->addFilePath($this->argv[$idx + 1]);
break;
case '-o':
@@ -124,7 +95,7 @@ class Processor
if (empty($this->argv[$idx + 1])) {
throw new InvalidArgumentException('-o is required');
}
$this->outputPath = $this->argv[$idx + 1];
$this->settings->setOutputPath($this->argv[$idx + 1]);
break;
case '-d':
@@ -132,54 +103,57 @@ class Processor
if (empty($this->argv[$idx + 1])) {
throw new InvalidArgumentException('a directory path is expected for -d (--dir)');
}
$rawpath = $this->argv[$idx + 1];
$files = array_filter(
FileSystem::dirContents($rawpath),
static fn($filename) => FileSystem::isCollectionFile($filename)
);
$this->collectionPaths = array_unique(array_merge($this?->collectionPaths ?? [], $files));
$this->settings->addDirPath($this->argv[$idx + 1]);
break;
case '-e':
case '--env':
$this->envFile = FileSystem::normalizePath($this->argv[$idx + 1]);
$this->settings->setEnvFilepath($this->argv[$idx + 1]);
break;
case '-p':
case '--preserve':
$this->preserveOutput = true;
$this->settings->setPreserveOutput(true);
break;
case '--http':
$this->formats[ConvertFormat::Http->name] = ConvertFormat::Http;
$this->settings->addFormat(ConvertFormat::Http);
break;
case '--curl':
$this->formats[ConvertFormat::Curl->name] = ConvertFormat::Curl;
$this->settings->addFormat(ConvertFormat::Curl);
break;
case '--wget':
$this->formats[ConvertFormat::Wget->name] = ConvertFormat::Wget;
$this->settings->addFormat(ConvertFormat::Wget);
break;
case '--v2.0':
$this->formats[ConvertFormat::Postman20->name] = ConvertFormat::Postman20;
$this->settings->addFormat(ConvertFormat::Postman20);
break;
case '--v2.1':
$this->formats[ConvertFormat::Postman21->name] = ConvertFormat::Postman21;
$this->settings->addFormat(ConvertFormat::Postman21);
break;
case '-a':
case '--all':
foreach (ConvertFormat::cases() as $format) {
$this->formats[$format->name] = $format;
$this->settings->addFormat($format);
}
break;
case '--var':
[$var, $value] = explode('=', trim($this->argv[$idx + 1]));
$this->vars[$var] = $value;
//TODO split by first equal sign
$this->env->setCustomVar(...explode('=', trim($this->argv[$idx + 1])));
break;
case '--dev':
$this->settings->setDevMode(true);
break;
case '--dump':
$this->needDumpSettings = true;
break;
case '-v':
@@ -189,23 +163,63 @@ class Processor
case '-h':
case '--help':
die(implode(EOL, $this->usage()) . EOL);
case '--dev':
$this->devMode = true;
break;
}
}
if (empty($this->collectionPaths)) {
if (empty($this->settings->collectionPaths())) {
throw new InvalidArgumentException('there are no collections to convert');
}
if (empty($this->outputPath)) {
if (empty($this->settings->outputPath())) {
throw new InvalidArgumentException('-o is required');
}
if (empty($this->formats)) {
$this->formats = [ConvertFormat::Http->name => ConvertFormat::Http];
if (empty($this->settings->formats())) {
$this->settings->addFormat(ConvertFormat::Http);
}
}
/**
* Handles input command
*
* @return void
* @throws CannotCreateDirectoryException
* @throws DirectoryIsNotReadableException
* @throws DirectoryIsNotWriteableException
* @throws DirectoryNotExistsException
* @throws JsonException
* @throws IncorrectSettingsFileException
*/
public function handle(): void
{
$this->prepareOutputDirectory();
$this->initConverters();
$this->convert();
}
/**
* Writes all settings into file if --dump provided
*
* @return never
*/
#[NoReturn]
protected function dumpSettingsFile(): never
{
$answer = 'o';
if ($this->settings::fileExists()) {
echo 'Settings file already exists: ' . $this->settings::filepath() . EOL;
echo 'Do you want to (o)verwrite it, (b)ackup it and create new one or (c)ancel (default)?' . EOL;
$answer = strtolower(trim(readline('> ')));
}
if (!in_array($answer, ['o', 'b'])) {
die('Current settings file has not been changed' . EOL);
}
if ($answer === 'b') {
$filepath = $this->settings->backup();
printf("Settings file has been backed up to file:%s\t%s%s", EOL, $filepath, EOL);
}
$this->settings->dump($this->env->customVars());
printf("Arguments has been converted into settings file:%s\t%s%s", EOL, $this->settings::filepath(), EOL);
die('Review and edit it if needed.' . EOL);
}
/**
* Initializes output directory
*
@@ -215,100 +229,74 @@ class Processor
* @throws DirectoryNotExistsException
* @throws DirectoryIsNotReadableException
*/
protected function initOutputDirectory(): void
protected function prepareOutputDirectory(): void
{
if (isset($this?->outputPath) && !$this->preserveOutput) {
FileSystem::removeDir($this->outputPath);
if (!$this->settings->isPreserveOutput()) {
FileSystem::removeDir($this->settings->outputPath());
}
FileSystem::makeDir($this->outputPath);
FileSystem::makeDir($this->settings->outputPath());
}
/**
* Initializes converters according to choosen formats
* Initializes converters according to chosen formats
*
* @return void
*/
protected function initConverters(): void
{
foreach ($this->formats as $type) {
$this->converters[$type->name] = new $type->value($this->preserveOutput);
foreach ($this->settings->formats() as $type) {
$this->converters[$type->name] = new $type->value($this->settings->isPreserveOutput());
}
unset($this->formats);
}
/**
* Initializes collection objects
* Generates collections from settings
*
* @return Generator<Collection>
* @throws JsonException
*/
protected function initCollections(): void
protected function newCollection(): Generator
{
foreach ($this->collectionPaths as $collectionPath) {
$collection = Collection::fromFile($collectionPath);
$this->collections[$collection->name()] = $collection;
foreach ($this->settings->collectionPaths() as $collectionPath) {
yield Collection::fromFile($collectionPath);
}
unset($this->collectionPaths, $content);
}
/**
* Initializes environment object
*
* @return void
* @throws JsonException
*/
protected function initEnv(): void
{
if (!isset($this->envFile)) {
return;
}
$content = file_get_contents(FileSystem::normalizePath($this->envFile));
$content = json_decode($content, flags: JSON_THROW_ON_ERROR);
if (!property_exists($content, 'environment') || empty($content?->environment)) {
throw new JsonException("not a valid environment: $this->envFile");
}
$this->env = new Environment($content->environment);
foreach ($this->vars as $var => $value) {
$this->env[$var] = $value;
}
unset($this->vars, $this->envFile, $content, $var, $value);
}
/**
* Begins a conversion
*
* @throws Exception
* @throws JsonException
*/
public function convert(): void
{
$this->parseArgs();
$this->initOutputDirectory();
$this->initConverters();
$this->initCollections();
$this->initEnv();
$count = count($this->collections);
$count = count($this->settings->collectionPaths());
$current = $success = 0;
$collection = null;
print(implode(EOL, array_merge($this->version(), $this->copyright())) . EOL . EOL);
foreach ($this->collections as $collectionName => $collection) {
foreach ($this->newCollection() as $collection) {
++$current;
printf("Converting '%s' (%d/%d):%s", $collectionName, $current, $count, EOL);
foreach ($this->converters as $type => $exporter) {
printf("Converting '%s' (%d/%d):%s", $collection->name(), $current, $count, EOL);
foreach ($this->converters as $type => $converter) {
/** @var AbstractConverter $converter */
printf('> %s%s', strtolower($type), EOL);
$outputPath = sprintf('%s%s%s', $this->outputPath, DS, $collectionName);
if (!empty($this->env)) {
$exporter->withEnv($this->env);
}
$outputPath = sprintf('%s%s%s', $this->settings->outputPath(), DS, $collection->name());
try {
$exporter->convert($collection, $outputPath);
printf(' OK: %s%s', $exporter->getOutputPath(), EOL);
$converter = $converter->to($outputPath);
$converter = $converter->convert($collection);
$converter->flush();
printf(' OK: %s%s', $converter->getOutputPath(), EOL);
} catch (Exception $e) {
printf(' ERROR %s: %s%s', $e->getCode(), $e->getMessage(), EOL);
$this->devMode && array_map(static fn ($line) => printf(" %s%s", $line, EOL), $e->getTrace());
if ($this->settings->isDevMode()) {
array_map(static fn ($line) => printf(' %s%s', $line, EOL), $e->getTrace());
}
}
}
print(EOL);
++$success;
}
unset($this->converters, $type, $exporter, $outputPath, $this->collections, $collectionName, $collection);
unset($this->converters, $type, $converter, $outputPath, $this->collections, $collectionName, $collection);
$this->printStats($success, $current);
}
@@ -334,18 +322,19 @@ class Processor
/**
* @return string[]
*/
public function version(): array
public static function version(): array
{
return ["Postman collection converter v" . self::VERSION];
return ['Postman collection converter v' . self::VERSION];
}
/**
* @return string[]
*/
public function copyright(): array
public static function copyright(): array
{
$years = ($year = (int)date('Y')) > 2023 ? "2023 - $year" : $year;
return [
'Anthony Axenov (c) ' . date('Y') . ", MIT license",
"Anthony Axenov (c) $years, MIT license",
'https://git.axenov.dev/anthony/pm-convert'
];
}
@@ -353,9 +342,9 @@ class Processor
/**
* @return array
*/
public function usage(): array
public static function usage(): array
{
return array_merge($this->version(), [
return array_merge(static::version(), [
'Usage:',
"\t./pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]",
"\tphp pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]",
@@ -369,6 +358,7 @@ class Processor
"\t-e, --env - use environment file with variables to replace in requests",
"\t--var \"NAME=VALUE\" - force replace specified env variable called NAME with custom VALUE",
"\t-p, --preserve - do not delete OUTPUT_PATH (if exists)",
"\t --dump - convert provided arguments into settings file in `pwd",
"\t-h, --help - show this help message and exit",
"\t-v, --version - show version info and exit",
'',
@@ -401,6 +391,6 @@ class Processor
" -o ~/postman_export \ ",
" --all",
"",
], $this->copyright());
], static::copyright());
}
}