Introducing settings file, some refactorings and stabilization
This commit is contained in:
parent
5666702ccc
commit
0cdaaf4880
72
README.md
72
README.md
@ -8,14 +8,17 @@ Without 3rd-party dependencies.
|
||||
|
||||
These formats are supported for now: `http`, `curl`, `wget`.
|
||||
|
||||
> This project was quickly written in my spare time to solve one exact problem in one NDA-project, so it may
|
||||
> contain stupid errors and (for sure) doesn't cover all possible cases according to collection schema.
|
||||
> So feel free to propose your improvements.
|
||||
> This project has been started and quickly written in my spare time to solve one exact problem in one NDA-project,
|
||||
> so it may contain stupid errors and (for sure) doesn't cover all possible cases according to collection schema.
|
||||
> Feel free to propose your improvements.
|
||||
|
||||
Versions older than the latest are not supported, only current one is.
|
||||
If you found an error in old version please ensure if an error you found has been fixed in latest version.
|
||||
So please always use the latest version of `pm-convert`.
|
||||
|
||||
## Supported features
|
||||
|
||||
* [collection schema **v2.1**](https://schema.postman.com/json/collection/v2.1.0/collection.json);
|
||||
* `Bearer` auth;
|
||||
* collection schemas [**v2.1**](https://schema.postman.com/json/collection/v2.1.0/collection.json) and [**v2.0**](https://schema.postman.com/json/collection/v2.0.0/collection.json);
|
||||
* replace vars in requests by stored in collection and environment file;
|
||||
* export one or several collections (or even whole directories) into one or all of formats supported at the same time;
|
||||
* all headers (including disabled for `http`-format);
|
||||
@ -25,7 +28,7 @@ These formats are supported for now: `http`, `curl`, `wget`.
|
||||
## Planned features
|
||||
|
||||
- support as many as possible/necessary of authentication kinds (_currently only `Bearer` supported_);
|
||||
- support as many as possible/necessary of body formats (_currently only `json` and `formdata`_);
|
||||
- support as many as possible/necessary of body formats (_currently only `json` and `formdata` supported_);
|
||||
- documentation generation support (markdown) with response examples (if present) (#6);
|
||||
- maybe some another convert formats (like httpie or something...);
|
||||
- better logging;
|
||||
@ -65,6 +68,7 @@ Possible ARGUMENTS:
|
||||
-e, --env - use environment file with variables to replace in requests
|
||||
--var "NAME=VALUE" - force replace specified env variable called NAME with custom VALUE
|
||||
-p, --preserve - do not delete OUTPUT_PATH (if exists)
|
||||
--dump - convert provided arguments into settings file in `pwd`
|
||||
-h, --help - show this help message and exit
|
||||
-v, --version - show version info and exit
|
||||
|
||||
@ -134,6 +138,62 @@ In such case collection itself places in single root object called `collection`
|
||||
|
||||
So, pm-convert will just raise actual data up on top level and write into disk.
|
||||
|
||||
## Settings file
|
||||
|
||||
You may want to specify parameters once and just use them everytime without explicit defining arguments to `pm-convert`.
|
||||
|
||||
This might be done in several ways.
|
||||
|
||||
1. Save this file as `pm-convert-settings.json` in your project directory:
|
||||
|
||||
```json
|
||||
{
|
||||
"directories": [],
|
||||
"files": [],
|
||||
"environment": "",
|
||||
"output": "",
|
||||
"preserveOutput": false,
|
||||
"formats": [],
|
||||
"vars": {}
|
||||
}
|
||||
```
|
||||
|
||||
Fill it with values you need.
|
||||
|
||||
2. Add `--dump` at the end of your command and all arguments you provided will be converted and saved as
|
||||
`pm-convert-settings.json` in your curent working directory. For example in `--help` file will contain this:
|
||||
|
||||
```json
|
||||
{
|
||||
"directories": [
|
||||
"~/team",
|
||||
"~/personal"
|
||||
],
|
||||
"files": [
|
||||
"~/dir1/first.postman_collection.json",
|
||||
"~/dir2/second.postman_collection.json"
|
||||
],
|
||||
"environment": "~/localhost.postman_environment.json",
|
||||
"output": "~/postman_export",
|
||||
"preserveOutput": false,
|
||||
"formats": [
|
||||
"http",
|
||||
"curl",
|
||||
"wget",
|
||||
"v2.0",
|
||||
"v2.1"
|
||||
],
|
||||
"vars": {
|
||||
"myvar": "some value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If settings file already exists then you will be asked what to do: overwrite it, back it up or exit.
|
||||
|
||||
Once settings file saved in current you can just run `pm-convert`.
|
||||
Settings will be applied like if you pass them explicitly via arguments.
|
||||
|
||||
## How to implement a new format
|
||||
|
||||
1. Create new namespace in `./src/Converters` and name it according to format of your choice.
|
||||
|
@ -14,7 +14,8 @@
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*"
|
||||
"ext-mbstring": "*",
|
||||
"ext-readline": "*"
|
||||
},
|
||||
"bin": ["pm-convert"],
|
||||
"autoload": {
|
||||
|
@ -24,10 +24,10 @@ is_null($file) && throw new RuntimeException('Unable to locate autoload.php file
|
||||
|
||||
$processor = new Processor($argv);
|
||||
try {
|
||||
$processor->convert();
|
||||
$processor->handle();
|
||||
} catch (InvalidArgumentException $e) {
|
||||
fwrite(STDERR, sprintf('ERROR: %s%s', $e->getMessage(), EOL));
|
||||
print(implode(EOL, $processor->usage()));
|
||||
print(implode(EOL, Processor::usage()));
|
||||
die(1);
|
||||
} catch (Exception $e) {
|
||||
fwrite(STDERR, sprintf('ERROR: %s%s', $e->getMessage(), EOL));
|
||||
|
@ -4,6 +4,8 @@ declare(strict_types = 1);
|
||||
|
||||
namespace PmConverter;
|
||||
|
||||
use Exception;
|
||||
use Generator;
|
||||
use JsonException;
|
||||
use Stringable;
|
||||
|
||||
@ -16,6 +18,8 @@ use Stringable;
|
||||
*/
|
||||
class Collection implements Stringable
|
||||
{
|
||||
public readonly CollectionVersion $version;
|
||||
|
||||
/**
|
||||
* Closed constructor so that we could use factory methods
|
||||
*
|
||||
@ -24,10 +28,11 @@ class Collection implements Stringable
|
||||
private function __construct(protected object $json)
|
||||
{
|
||||
// specific case when collection has been exported via postman api
|
||||
if (isset($json->collection)) {
|
||||
if (property_exists($json, 'collection')) {
|
||||
$json = $json->collection;
|
||||
}
|
||||
$this->json = $json;
|
||||
$this->version = $this->detectVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,7 +93,7 @@ class Collection implements Stringable
|
||||
*
|
||||
* @return CollectionVersion
|
||||
*/
|
||||
public function version(): CollectionVersion
|
||||
protected function detectVersion(): CollectionVersion
|
||||
{
|
||||
return match (true) {
|
||||
str_contains($this->json->info->schema, '/v2.0.') => CollectionVersion::Version20,
|
||||
@ -96,4 +101,72 @@ class Collection implements Stringable
|
||||
default => CollectionVersion::Unknown
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collection version from raw file
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return CollectionVersion
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function detectFileVersion(string $filepath): CollectionVersion
|
||||
{
|
||||
$handle = fopen($filepath, 'r');
|
||||
if ($handle === false) {
|
||||
throw new Exception("Cannot open file for reading: $filepath");
|
||||
}
|
||||
$content = '';
|
||||
// Postman collection files may be HUGE and I don't need to parse
|
||||
// them here to find value .info.schema field because normally it
|
||||
// is stored at the beginning of a file, so if it's not then this
|
||||
// is a user problem, not mine.
|
||||
while (\mb_strlen($content) <= 2048) {
|
||||
$content .= fgets($handle, 50);
|
||||
if (str_contains($content, 'https://schema.getpostman.com/json/collection')) {
|
||||
if (str_contains($content, '/v2.0.')) {
|
||||
return CollectionVersion::Version20;
|
||||
}
|
||||
if (str_contains($content, '/v2.1.')) {
|
||||
return CollectionVersion::Version21;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CollectionVersion::Unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over collection request items and returns item associated by its path in folder
|
||||
*
|
||||
* @param mixed|null $item
|
||||
* @return Generator
|
||||
*/
|
||||
public function iterate(mixed $item = null): Generator
|
||||
{
|
||||
$is_recursive = !is_null($item);
|
||||
$folder = $is_recursive ? $item : $this->json;
|
||||
static $dir_tree;
|
||||
$path = DS . ($is_recursive ? implode(DS, $dir_tree ?? []) : '');
|
||||
foreach ($folder->item as $subitem) {
|
||||
if ($this->isItemFolder($subitem)) {
|
||||
$dir_tree[] = $subitem->name;
|
||||
yield from $this->iterate($subitem);
|
||||
continue;
|
||||
}
|
||||
yield $path => $subitem;
|
||||
}
|
||||
$is_recursive && array_pop($dir_tree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether item contains another items or not
|
||||
*
|
||||
* @param object $item
|
||||
* @return bool
|
||||
*/
|
||||
protected function isItemFolder(object $item): bool
|
||||
{
|
||||
return !empty($item->item)
|
||||
&& is_array($item->item)
|
||||
&& empty($item->request);
|
||||
}
|
||||
}
|
||||
|
@ -5,21 +5,19 @@ declare(strict_types=1);
|
||||
namespace PmConverter\Converters\Abstract;
|
||||
|
||||
use Exception;
|
||||
use Iterator;
|
||||
use PmConverter\Collection;
|
||||
use PmConverter\Converters\{
|
||||
ConverterContract,
|
||||
RequestContract};
|
||||
use PmConverter\Converters\RequestContract;
|
||||
use PmConverter\Environment;
|
||||
use PmConverter\Exceptions\{
|
||||
CannotCreateDirectoryException,
|
||||
DirectoryIsNotWriteableException,
|
||||
InvalidHttpVersionException};
|
||||
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
||||
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
||||
use PmConverter\Exceptions\InvalidHttpVersionException;
|
||||
use PmConverter\FileSystem;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
abstract class AbstractConverter implements ConverterContract
|
||||
abstract class AbstractConverter
|
||||
{
|
||||
/**
|
||||
* @var Collection|null
|
||||
@ -32,53 +30,66 @@ abstract class AbstractConverter implements ConverterContract
|
||||
protected string $outputPath;
|
||||
|
||||
/**
|
||||
* @var Environment|null
|
||||
* @var RequestContract[] Converted requests
|
||||
*/
|
||||
protected ?Environment $env = null;
|
||||
protected array $requests = [];
|
||||
|
||||
/**
|
||||
* Sets an environment with vars
|
||||
* Sets output path
|
||||
*
|
||||
* @param Environment $env
|
||||
* @param string $outputPath
|
||||
* @return $this
|
||||
*/
|
||||
public function withEnv(Environment $env): static
|
||||
public function to(string $outputPath): self
|
||||
{
|
||||
$this->env = $env;
|
||||
$this->outputPath = sprintf('%s%s%s', $outputPath, DS, static::OUTPUT_DIR);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new directory to save a converted collection into
|
||||
*
|
||||
* @param string $outputPath
|
||||
* @return void
|
||||
* @throws CannotCreateDirectoryException
|
||||
* @throws DirectoryIsNotWriteableException
|
||||
*/
|
||||
protected function prepareOutputDir(string $outputPath): void
|
||||
{
|
||||
$outputPath = sprintf('%s%s%s', $outputPath, DS, static::OUTPUT_DIR);
|
||||
$this->outputPath = FileSystem::makeDir($outputPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts collection requests
|
||||
* Converts requests from collection
|
||||
*
|
||||
* @param Collection $collection
|
||||
* @param string $outputPath
|
||||
* @return void
|
||||
* @return static
|
||||
* @throws CannotCreateDirectoryException
|
||||
* @throws DirectoryIsNotWriteableException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function convert(Collection $collection, string $outputPath): void
|
||||
public function convert(Collection $collection): static
|
||||
{
|
||||
$this->prepareOutputDir($outputPath);
|
||||
$this->collection = $collection;
|
||||
$this->setVariables();
|
||||
foreach ($collection->item as $item) {
|
||||
$this->convertItem($item);
|
||||
$this->outputPath = FileSystem::makeDir($this->outputPath);
|
||||
$this->setCollectionVars();
|
||||
foreach ($collection->iterate() as $path => $item) {
|
||||
// $this->requests[$path][] = $this->makeRequest($item);
|
||||
$this->writeRequest($this->makeRequest($item), $path);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns converted requests
|
||||
*
|
||||
* @return Iterator<string, RequestContract>
|
||||
*/
|
||||
public function converted(): Iterator
|
||||
{
|
||||
foreach ($this->requests as $path => $requests) {
|
||||
foreach ($requests as $request) {
|
||||
yield $path => $request;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes requests on disk
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function flush(): void
|
||||
{
|
||||
foreach ($this->converted() as $path => $request) {
|
||||
$this->writeRequest($request, $path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,13 +98,10 @@ abstract class AbstractConverter implements ConverterContract
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function setVariables(): static
|
||||
protected function setCollectionVars(): static
|
||||
{
|
||||
empty($this->env) && $this->env = new Environment($this->collection?->variable);
|
||||
if (!empty($this->collection?->variable)) {
|
||||
foreach ($this->collection->variable as $var) {
|
||||
$this->env[$var->key] = $var->value;
|
||||
}
|
||||
foreach ($this->collection?->variable ?? [] as $var) {
|
||||
Environment::instance()->setCustomVar($var->key, $var->value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
@ -121,30 +129,6 @@ abstract class AbstractConverter implements ConverterContract
|
||||
&& empty($item->request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an item to request object and writes it into file
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function convertItem(mixed $item): void
|
||||
{
|
||||
if ($this->isItemFolder($item)) {
|
||||
static $dir_tree;
|
||||
foreach ($item->item as $subitem) {
|
||||
$dir_tree[] = $item->name;
|
||||
$path = implode(DS, $dir_tree);
|
||||
if ($this->isItemFolder($subitem)) {
|
||||
$this->convertItem($subitem);
|
||||
} else {
|
||||
$this->writeRequest($this->initRequest($subitem), $path);
|
||||
}
|
||||
array_pop($dir_tree);
|
||||
}
|
||||
} else {
|
||||
$this->writeRequest($this->initRequest($item));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialiazes request object to be written in file
|
||||
*
|
||||
@ -152,17 +136,18 @@ abstract class AbstractConverter implements ConverterContract
|
||||
* @return RequestContract
|
||||
* @throws InvalidHttpVersionException
|
||||
*/
|
||||
protected function initRequest(object $item): RequestContract
|
||||
protected function makeRequest(object $item): RequestContract
|
||||
{
|
||||
$request_class = static::REQUEST_CLASS;
|
||||
|
||||
/** @var RequestContract $request */
|
||||
$request = new $request_class();
|
||||
$request->setName($item->name);
|
||||
$request->setVersion($this->collection->version);
|
||||
$request->setHttpVersion(1.1); //TODO http version?
|
||||
$request->setDescription($item->request?->description ?? null);
|
||||
$request->setVerb($item->request->method);
|
||||
$request->setUrl($item->request->url->raw);
|
||||
$request->setUrl($item->request->url);
|
||||
$request->setHeaders($item->request->header);
|
||||
$request->setAuth($item->request?->auth ?? $this->collection?->auth ?? null);
|
||||
if ($item->request->method !== 'GET' && !empty($item->request->body)) {
|
||||
@ -196,18 +181,9 @@ abstract class AbstractConverter implements ConverterContract
|
||||
*/
|
||||
protected function interpolate(string $content): string
|
||||
{
|
||||
if (!$this->env?->hasVars()) {
|
||||
return $content;
|
||||
}
|
||||
$matches = [];
|
||||
if (preg_match_all('/\{\{.*}}/m', $content, $matches, PREG_PATTERN_ORDER) > 0) {
|
||||
foreach ($matches[0] as $key => $var) {
|
||||
if (str_contains($content, $var)) {
|
||||
$content = str_replace($var, $this->env[$var] ?: $var, $content);
|
||||
unset($matches[0][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
$replace = static fn ($a) => Environment::instance()->var($var = $a[0]) ?: $var;
|
||||
return Environment::instance()->hasVars()
|
||||
? preg_replace_callback('/\{\{.*}}/m', $replace, $content)
|
||||
: $content;
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace PmConverter\Converters\Abstract;
|
||||
|
||||
use PmConverter\CollectionVersion;
|
||||
use PmConverter\Converters\RequestContract;
|
||||
use PmConverter\Exceptions\{
|
||||
EmptyHttpVerbException,
|
||||
InvalidHttpVersionException};
|
||||
use PmConverter\Exceptions\EmptyHttpVerbException;
|
||||
use PmConverter\Exceptions\InvalidHttpVersionException;
|
||||
use PmConverter\HttpVersion;
|
||||
use Stringable;
|
||||
|
||||
@ -22,9 +22,9 @@ abstract class AbstractRequest implements Stringable, RequestContract
|
||||
protected string $verb;
|
||||
|
||||
/**
|
||||
* @var string URL where to send a request
|
||||
* @var object|string URL where to send a request
|
||||
*/
|
||||
protected string $url;
|
||||
protected object|string $url;
|
||||
|
||||
/**
|
||||
* @var float HTTP protocol version
|
||||
@ -56,6 +56,15 @@ abstract class AbstractRequest implements Stringable, RequestContract
|
||||
*/
|
||||
protected string $bodymode = 'raw';
|
||||
|
||||
|
||||
protected CollectionVersion $version;
|
||||
|
||||
public function setVersion(CollectionVersion $version): static
|
||||
{
|
||||
$this->version = $version;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@ -133,7 +142,7 @@ abstract class AbstractRequest implements Stringable, RequestContract
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setUrl(string $url): static
|
||||
public function setUrl(object|string $url): static
|
||||
{
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
@ -142,9 +151,9 @@ abstract class AbstractRequest implements Stringable, RequestContract
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getUrl(): string
|
||||
public function getRawUrl(): string
|
||||
{
|
||||
return $this->url ?: '<empty url>';
|
||||
return is_object($this->url) ? $this->url->raw : $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,7 +195,11 @@ abstract class AbstractRequest implements Stringable, RequestContract
|
||||
if (!empty($auth)) {
|
||||
switch ($auth->type) {
|
||||
case 'bearer':
|
||||
$this->setHeader('Authorization', 'Bearer ' . $auth->{$auth->type}[0]->value);
|
||||
$this->setHeader('Authorization', 'Bearer ' . match ($this->version) {
|
||||
CollectionVersion::Version20 => $auth->{$auth->type}->token,
|
||||
CollectionVersion::Version21 => $auth->{$auth->type}[0]->value,
|
||||
default => null
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -5,12 +5,11 @@ declare(strict_types=1);
|
||||
namespace PmConverter\Converters;
|
||||
|
||||
|
||||
use PmConverter\Converters\{
|
||||
Curl\CurlConverter,
|
||||
Http\HttpConverter,
|
||||
Postman20\Postman20Converter,
|
||||
Postman21\Postman21Converter,
|
||||
Wget\WgetConverter};
|
||||
use PmConverter\Converters\Curl\CurlConverter;
|
||||
use PmConverter\Converters\Http\HttpConverter;
|
||||
use PmConverter\Converters\Postman20\Postman20Converter;
|
||||
use PmConverter\Converters\Postman21\Postman21Converter;
|
||||
use PmConverter\Converters\Wget\WgetConverter;
|
||||
|
||||
enum ConvertFormat: string
|
||||
{
|
||||
@ -19,4 +18,26 @@ enum ConvertFormat: string
|
||||
case Wget = WgetConverter::class;
|
||||
case Postman20 = Postman20Converter::class;
|
||||
case Postman21 = Postman21Converter::class;
|
||||
|
||||
public static function fromArg(string $arg): self
|
||||
{
|
||||
return match ($arg) {
|
||||
'http' => ConvertFormat::Http,
|
||||
'curl' => ConvertFormat::Curl,
|
||||
'wget' => ConvertFormat::Wget,
|
||||
'v2.0' => ConvertFormat::Postman20,
|
||||
'v2.1' => ConvertFormat::Postman21,
|
||||
};
|
||||
}
|
||||
|
||||
public function toArg(): string
|
||||
{
|
||||
return match ($this) {
|
||||
ConvertFormat::Http => 'http',
|
||||
ConvertFormat::Curl => 'curl',
|
||||
ConvertFormat::Wget => 'wget',
|
||||
ConvertFormat::Postman20 => 'v2.0',
|
||||
ConvertFormat::Postman21 => 'v2.1',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace PmConverter\Converters\Curl;
|
||||
|
||||
use PmConverter\Converters\{
|
||||
Abstract\AbstractConverter,
|
||||
ConverterContract};
|
||||
use PmConverter\Converters\Abstract\AbstractConverter;
|
||||
|
||||
class CurlConverter extends AbstractConverter implements ConverterContract
|
||||
class CurlConverter extends AbstractConverter
|
||||
{
|
||||
protected const FILE_EXT = 'sh';
|
||||
|
||||
|
@ -72,7 +72,7 @@ class CurlRequest extends AbstractRequest
|
||||
"curl \ ",
|
||||
"\t--http1.1 \ ", //TODO proto
|
||||
"\t--request $this->verb \ ",
|
||||
"\t--location $this->url \ ",
|
||||
"\t--location {$this->getRawUrl()} \ ",
|
||||
],
|
||||
$this->prepareHeaders(),
|
||||
$this->prepareBody()
|
||||
|
@ -4,11 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace PmConverter\Converters\Http;
|
||||
|
||||
use PmConverter\Converters\{
|
||||
Abstract\AbstractConverter,
|
||||
ConverterContract};
|
||||
use PmConverter\Converters\Abstract\AbstractConverter;
|
||||
|
||||
class HttpConverter extends AbstractConverter implements ConverterContract
|
||||
class HttpConverter extends AbstractConverter
|
||||
{
|
||||
protected const FILE_EXT = 'http';
|
||||
|
||||
|
@ -5,8 +5,7 @@ declare(strict_types=1);
|
||||
namespace PmConverter\Converters\Http;
|
||||
|
||||
use PmConverter\Converters\Abstract\AbstractRequest;
|
||||
use PmConverter\Exceptions\{
|
||||
EmptyHttpVerbException};
|
||||
use PmConverter\Exceptions\EmptyHttpVerbException;
|
||||
|
||||
/**
|
||||
* Class to determine file content with http request format
|
||||
@ -29,7 +28,7 @@ class HttpRequest extends AbstractRequest
|
||||
*/
|
||||
protected function prepareHeaders(): array
|
||||
{
|
||||
$output[] = sprintf('%s %s HTTP/%s', $this->getVerb(), $this->getUrl(), $this->getHttpVersion());
|
||||
$output[] = sprintf('%s %s HTTP/%s', $this->getVerb(), $this->getRawUrl(), $this->getHttpVersion());
|
||||
foreach ($this->headers as $name => $data) {
|
||||
$output[] = sprintf('%s%s: %s', $data['disabled'] ? '# ' : '', $name, $data['value']);
|
||||
}
|
||||
|
@ -6,9 +6,7 @@ namespace PmConverter\Converters\Postman20;
|
||||
|
||||
use PmConverter\Collection;
|
||||
use PmConverter\CollectionVersion;
|
||||
use PmConverter\Converters\{
|
||||
Abstract\AbstractConverter,
|
||||
ConverterContract};
|
||||
use PmConverter\Converters\Abstract\AbstractConverter;
|
||||
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
||||
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
||||
use PmConverter\FileSystem;
|
||||
@ -16,7 +14,7 @@ use PmConverter\FileSystem;
|
||||
/**
|
||||
* Converts Postman Collection v2.1 to v2.0
|
||||
*/
|
||||
class Postman20Converter extends AbstractConverter implements ConverterContract
|
||||
class Postman20Converter extends AbstractConverter
|
||||
{
|
||||
protected const FILE_EXT = 'v20.postman_collection.json';
|
||||
|
||||
@ -26,25 +24,25 @@ class Postman20Converter extends AbstractConverter implements ConverterContract
|
||||
* Converts collection requests
|
||||
*
|
||||
* @param Collection $collection
|
||||
* @param string $outputPath
|
||||
* @return void
|
||||
* @return static
|
||||
* @throws CannotCreateDirectoryException
|
||||
* @throws DirectoryIsNotWriteableException
|
||||
*/
|
||||
public function convert(Collection $collection, string $outputPath): void
|
||||
public function convert(Collection $collection): static
|
||||
{
|
||||
$this->collection = $collection;
|
||||
// if data was exported from API, here is already valid json to
|
||||
// just flush it in file, otherwise we need to convert it deeper
|
||||
if ($this->collection->version() === CollectionVersion::Version21) {
|
||||
if ($this->collection->version === CollectionVersion::Version21) {
|
||||
$this->collection->info->schema = str_replace('/v2.1.', '/v2.0.', $this->collection->info->schema);
|
||||
$this->convertAuth($this->collection->raw());
|
||||
foreach ($this->collection->item as $item) {
|
||||
$this->convertItem($item);
|
||||
}
|
||||
}
|
||||
$this->prepareOutputDir($outputPath);
|
||||
$this->outputPath = FileSystem::makeDir($this->outputPath);
|
||||
$this->writeCollection();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,11 +95,11 @@ class Postman20Converter extends AbstractConverter implements ConverterContract
|
||||
if (empty($request->auth)) {
|
||||
return;
|
||||
}
|
||||
$type = $request->auth->type;
|
||||
if ($type !== 'noauth' && is_array($request->auth->$type)) {
|
||||
$auth = [];
|
||||
$auth = ['type' => 'noauth'];
|
||||
$type = strtolower($request->auth->type);
|
||||
if ($type !== 'noauth') {
|
||||
foreach ($request->auth->$type as $param) {
|
||||
$auth[$param->key] = $param->value;
|
||||
$auth[$param->key] = $param->value ?? '';
|
||||
}
|
||||
$request->auth->$type = (object)$auth;
|
||||
}
|
||||
|
@ -6,9 +6,7 @@ namespace PmConverter\Converters\Postman21;
|
||||
|
||||
use PmConverter\Collection;
|
||||
use PmConverter\CollectionVersion;
|
||||
use PmConverter\Converters\{
|
||||
Abstract\AbstractConverter,
|
||||
ConverterContract};
|
||||
use PmConverter\Converters\Abstract\AbstractConverter;
|
||||
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
||||
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
||||
use PmConverter\FileSystem;
|
||||
@ -16,7 +14,7 @@ use PmConverter\FileSystem;
|
||||
/**
|
||||
* Converts Postman Collection v2.0 to v2.1
|
||||
*/
|
||||
class Postman21Converter extends AbstractConverter implements ConverterContract
|
||||
class Postman21Converter extends AbstractConverter
|
||||
{
|
||||
protected const FILE_EXT = 'v21.postman_collection.json';
|
||||
|
||||
@ -26,25 +24,25 @@ class Postman21Converter extends AbstractConverter implements ConverterContract
|
||||
* Converts collection requests
|
||||
*
|
||||
* @param Collection $collection
|
||||
* @param string $outputPath
|
||||
* @return void
|
||||
* @return static
|
||||
* @throws CannotCreateDirectoryException
|
||||
* @throws DirectoryIsNotWriteableException
|
||||
*/
|
||||
public function convert(Collection $collection, string $outputPath): void
|
||||
public function convert(Collection $collection): static
|
||||
{
|
||||
$this->collection = $collection;
|
||||
// if data was exported from API, here is already valid json to
|
||||
// just flush it in file, otherwise we need to convert it deeper
|
||||
if ($this->collection->version() === CollectionVersion::Version20) {
|
||||
if ($this->collection->version === CollectionVersion::Version20) {
|
||||
$this->collection->info->schema = str_replace('/v2.0.', '/v2.1.', $this->collection->info->schema);
|
||||
$this->convertAuth($this->collection->raw());
|
||||
foreach ($this->collection->item as $item) {
|
||||
$this->convertItem($item);
|
||||
}
|
||||
}
|
||||
$this->prepareOutputDir($outputPath);
|
||||
$this->outputPath = FileSystem::makeDir($this->outputPath);
|
||||
$this->writeCollection();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,7 +86,7 @@ interface RequestContract
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl(): string;
|
||||
public function getRawUrl(): string;
|
||||
|
||||
/**
|
||||
* Sets headers from collection item to request object
|
||||
@ -116,7 +116,7 @@ interface RequestContract
|
||||
/**
|
||||
* Sets authorization headers
|
||||
*
|
||||
* @param object|null $auth
|
||||
* @param object $auth
|
||||
* @return $this
|
||||
*/
|
||||
public function setAuth(object $auth): static;
|
||||
|
@ -4,11 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace PmConverter\Converters\Wget;
|
||||
|
||||
use PmConverter\Converters\{
|
||||
Abstract\AbstractConverter,
|
||||
ConverterContract};
|
||||
use PmConverter\Converters\Abstract\AbstractConverter;
|
||||
|
||||
class WgetConverter extends AbstractConverter implements ConverterContract
|
||||
class WgetConverter extends AbstractConverter
|
||||
{
|
||||
protected const FILE_EXT = 'sh';
|
||||
|
||||
|
@ -77,16 +77,16 @@ class WgetRequest extends AbstractRequest
|
||||
if ($this->getBodymode() === 'formdata') {
|
||||
if ($this->getBody()) {
|
||||
if ($this->getVerb() === 'GET') {
|
||||
$output[] = sprintf("\t%s?%s", $this->getUrl(), http_build_query($this->prepareBody()));
|
||||
$output[] = sprintf("\t%s?%s", $this->getRawUrl(), http_build_query($this->prepareBody()));
|
||||
} else {
|
||||
$output[] = sprintf("\t--body-data '%s' \ ", http_build_query($this->prepareBody()));
|
||||
$output[] = sprintf("\t%s", $this->getUrl());
|
||||
$output[] = sprintf("\t%s", $this->getRawUrl());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($this->getVerb() !== 'GET') {
|
||||
$output[] = sprintf("\t--body-data '%s' \ ", implode("\n", $this->prepareBody()));
|
||||
$output[] = sprintf("\t%s", $this->getUrl());
|
||||
$output[] = sprintf("\t%s", $this->getRawUrl());
|
||||
}
|
||||
}
|
||||
return implode(EOL, array_merge($output, ['']));
|
||||
|
@ -4,23 +4,138 @@ declare(strict_types=1);
|
||||
|
||||
namespace PmConverter;
|
||||
|
||||
class Environment implements \ArrayAccess
|
||||
use ArrayAccess;
|
||||
use JsonException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Environment implements ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @var string Path to env file
|
||||
*/
|
||||
protected static string $filepath = '';
|
||||
|
||||
/**
|
||||
* @var Environment
|
||||
*/
|
||||
protected static Environment $instance;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $vars = [];
|
||||
protected array $ownVars = [];
|
||||
|
||||
/**
|
||||
* @param object|null $env
|
||||
* @var array
|
||||
*/
|
||||
public function __construct(protected ?object $env)
|
||||
protected array $customVars = [];
|
||||
|
||||
public static function instance(): static
|
||||
{
|
||||
if (!empty($env->values)) {
|
||||
foreach ($env->values as $var) {
|
||||
$this->vars[static::formatKey($var->key)] = $var->value;
|
||||
return static::$instance ??= new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filepath
|
||||
* @return $this
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function readFromFile(string $filepath): static
|
||||
{
|
||||
$content = file_get_contents(static::$filepath = $filepath);
|
||||
$content = json_decode($content, flags: JSON_THROW_ON_ERROR); //TODO try-catch
|
||||
$content || throw new JsonException("not a valid environment: $filepath");
|
||||
property_exists($content, 'environment') && $content = $content->environment;
|
||||
if (!property_exists($content, 'id') && !property_exists($content, 'name')) {
|
||||
throw new JsonException("not a valid environment: $filepath");
|
||||
}
|
||||
return $this->setOwnVars($content->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $vars
|
||||
* @return $this
|
||||
*/
|
||||
protected function setOwnVars(array $vars): static
|
||||
{
|
||||
foreach ($vars as $key => $value) {
|
||||
is_object($value) && [$key, $value] = [$value->key, $value->value];
|
||||
$this->setOwnVar($key, $value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value to some environment own variable
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @return $this
|
||||
*/
|
||||
protected function setOwnVar(string $name, string $value): static
|
||||
{
|
||||
$this->ownVars[static::formatKey($name)] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $vars
|
||||
* @return $this
|
||||
*/
|
||||
public function setCustomVars(array $vars): static
|
||||
{
|
||||
foreach ($vars as $key => $value) {
|
||||
is_object($value) && [$key, $value] = [$value->key, $value->value];
|
||||
$this->setCustomVar($key, $value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value to some environment own variable
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @return $this
|
||||
*/
|
||||
public function setCustomVar(string $name, string $value): static
|
||||
{
|
||||
$this->customVars[static::formatKey($name)] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value of specific variable
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function var(string $name): mixed
|
||||
{
|
||||
$format_key = static::formatKey($name);
|
||||
return $this->ownVars[$format_key] ?? $this->customVars[$format_key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of own and custom variables
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function vars(): array
|
||||
{
|
||||
return array_merge($this->ownVars, $this->customVars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of custom variables
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function customVars(): array
|
||||
{
|
||||
return $this->customVars;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -30,15 +145,35 @@ class Environment implements \ArrayAccess
|
||||
*/
|
||||
public function hasVars(): bool
|
||||
{
|
||||
return !empty($this->vars);
|
||||
return !empty($this->ownVars) && !empty($this->customVars);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Closed constructor
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetExists(mixed $offset): bool
|
||||
{
|
||||
return array_key_exists(static::formatKey($offset), $this->vars);
|
||||
return array_key_exists(static::formatKey($offset), $this->vars());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,7 +181,7 @@ class Environment implements \ArrayAccess
|
||||
*/
|
||||
public function offsetGet(mixed $offset): mixed
|
||||
{
|
||||
return $this->vars[static::formatKey($offset)] ?? null;
|
||||
return $this->var($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,7 +189,7 @@ class Environment implements \ArrayAccess
|
||||
*/
|
||||
public function offsetSet(mixed $offset, mixed $value): void
|
||||
{
|
||||
$this->vars[static::formatKey($offset)] = $value;
|
||||
$this->customVars[static::formatKey($offset)] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,7 +197,7 @@ class Environment implements \ArrayAccess
|
||||
*/
|
||||
public function offsetUnset(mixed $offset): void
|
||||
{
|
||||
unset($this->vars[static::formatKey($offset)]);
|
||||
unset($this->customVars[static::formatKey($offset)]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
11
src/Exceptions/IncorrectSettingsFileException.php
Normal file
11
src/Exceptions/IncorrectSettingsFileException.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PmConverter\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class IncorrectSettingsFileException extends Exception
|
||||
{
|
||||
}
|
@ -4,12 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace PmConverter;
|
||||
|
||||
use Exception;
|
||||
use JsonException;
|
||||
use PmConverter\Exceptions\{
|
||||
CannotCreateDirectoryException,
|
||||
DirectoryIsNotReadableException,
|
||||
DirectoryIsNotWriteableException,
|
||||
DirectoryNotExistsException};
|
||||
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
||||
use PmConverter\Exceptions\DirectoryIsNotReadableException;
|
||||
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
||||
use PmConverter\Exceptions\DirectoryNotExistsException;
|
||||
|
||||
/**
|
||||
* Helper class to work with files and directories
|
||||
@ -24,7 +24,7 @@ class FileSystem
|
||||
*/
|
||||
public static function normalizePath(string $path): string
|
||||
{
|
||||
$path = str_replace('~', $_SERVER['HOME'], $path);
|
||||
$path = str_replace('~/', "{$_SERVER['HOME']}/", $path);
|
||||
return rtrim($path, DS);
|
||||
}
|
||||
|
||||
@ -112,13 +112,14 @@ class FileSystem
|
||||
* @param string $path
|
||||
* @return bool
|
||||
* @throws JsonException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function isCollectionFile(string $path): bool
|
||||
{
|
||||
return (!empty($path = trim(static::normalizePath($path))))
|
||||
return (!empty($path = static::normalizePath($path)))
|
||||
&& str_ends_with($path, '.postman_collection.json')
|
||||
&& file_exists($path)
|
||||
&& is_readable($path)
|
||||
&& Collection::fromFile($path)->version() !== CollectionVersion::Unknown;
|
||||
&& Collection::detectFileVersion($path) !== CollectionVersion::Unknown;
|
||||
}
|
||||
}
|
||||
|
@ -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 = '2.0.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());
|
||||
}
|
||||
}
|
||||
|
313
src/Settings.php
Normal file
313
src/Settings.php
Normal file
@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PmConverter;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use JsonException;
|
||||
use PmConverter\Converters\ConvertFormat;
|
||||
use PmConverter\Exceptions\IncorrectSettingsFileException;
|
||||
|
||||
/**
|
||||
* Class responsible for settings storage and dumping
|
||||
*/
|
||||
class Settings
|
||||
{
|
||||
/**
|
||||
* @var string Full path to settings file
|
||||
*/
|
||||
protected static string $filepath;
|
||||
|
||||
/**
|
||||
* @var bool Flag to output some debug-specific messages
|
||||
*/
|
||||
protected bool $devMode = false;
|
||||
|
||||
/**
|
||||
* @var string[] Paths to collection directories
|
||||
*/
|
||||
protected array $directories = [];
|
||||
|
||||
/**
|
||||
* @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 string Path to environment file
|
||||
*/
|
||||
protected string $envFilepath = '';
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function fileExists(): bool
|
||||
{
|
||||
return file_exists(self::$filepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
* @throws IncorrectSettingsFileException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public static function init(): self
|
||||
{
|
||||
$content = '{}';
|
||||
self::$filepath = sprintf('%s%spm-convert-settings.json', $_SERVER['PWD'], DS);
|
||||
if (self::fileExists()) {
|
||||
$content = trim(file_get_contents(self::$filepath));
|
||||
}
|
||||
try {
|
||||
$settings = json_decode($content ?: '{}', flags: JSON_THROW_ON_ERROR);
|
||||
} catch (JsonException $e) {
|
||||
throw new IncorrectSettingsFileException('Incorrect settings file: ' . $e->getMessage(), $e->getCode());
|
||||
}
|
||||
return new self($settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns full path to settings file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function filepath(): string
|
||||
{
|
||||
return self::$filepath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $settings
|
||||
* @throws JsonException
|
||||
*/
|
||||
protected function __construct(object $settings)
|
||||
{
|
||||
foreach ($settings->directories ?? [] as $path) {
|
||||
$this->addDirPath($path);
|
||||
}
|
||||
foreach ($settings->files ?? [] as $path) {
|
||||
$this->addFilePath($path);
|
||||
}
|
||||
$this->setDevMode(!empty($settings->devMode));
|
||||
$this->setPreserveOutput(!empty($settings->preserveOutput));
|
||||
isset($settings->environment) && $this->setEnvFilepath($settings->environment);
|
||||
isset($settings->output) && $this->setOutputPath($settings->output);
|
||||
foreach ($settings->formats ?? [] as $format) {
|
||||
$this->addFormat(ConvertFormat::fromArg($format));
|
||||
}
|
||||
foreach ($settings->vars ?? [] as $name => $value) {
|
||||
$this->vars[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return void
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function addDirPath(string $path): void
|
||||
{
|
||||
$this->directories = array_unique(array_merge(
|
||||
$this->directories ?? [],
|
||||
[FileSystem::normalizePath($path)]
|
||||
));
|
||||
$files = array_filter(
|
||||
FileSystem::dirContents($path),
|
||||
static fn ($filename) => FileSystem::isCollectionFile($filename)
|
||||
);
|
||||
$this->collectionPaths = array_unique(array_merge($this->collectionPaths ?? [], $files));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return void
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function addFilePath(string $path): void
|
||||
{
|
||||
$normpath = FileSystem::normalizePath($path);
|
||||
if (!FileSystem::isCollectionFile($normpath)) {
|
||||
throw new InvalidArgumentException("not a valid collection: $path");
|
||||
}
|
||||
in_array($path, $this->collectionPaths) || $this->collectionPaths[] = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $outputPath
|
||||
* @return void
|
||||
*/
|
||||
public function setOutputPath(string $outputPath): void
|
||||
{
|
||||
$this->outputPath = $outputPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $devMode
|
||||
* @return void
|
||||
*/
|
||||
public function setDevMode(bool $devMode): void
|
||||
{
|
||||
$this->devMode = $devMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ConvertFormat $format
|
||||
* @return void
|
||||
*/
|
||||
public function addFormat(ConvertFormat $format): void
|
||||
{
|
||||
$this->formats[$format->name] = $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of variables
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function vars(): array
|
||||
{
|
||||
return $this->vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $preserveOutput
|
||||
* @return void
|
||||
*/
|
||||
public function setPreserveOutput(bool $preserveOutput): void
|
||||
{
|
||||
$this->preserveOutput = $preserveOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filepath
|
||||
* @return void
|
||||
*/
|
||||
public function setEnvFilepath(string $filepath): void
|
||||
{
|
||||
$this->envFilepath = FileSystem::normalizePath($filepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDevMode(): bool
|
||||
{
|
||||
return $this->devMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function collectionPaths(): array
|
||||
{
|
||||
return $this->collectionPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function outputPath(): string
|
||||
{
|
||||
return $this->outputPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isPreserveOutput(): bool
|
||||
{
|
||||
return $this->preserveOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ConvertFormat[]
|
||||
*/
|
||||
public function formats(): array
|
||||
{
|
||||
return $this->formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function envFilepath(): string
|
||||
{
|
||||
return $this->envFilepath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines fieldset of settings JSON
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __serialize(): array
|
||||
{
|
||||
return [
|
||||
'dev' => $this->isDevMode(),
|
||||
'directories' => $this->directories,
|
||||
'files' => $this->collectionPaths(),
|
||||
'environment' => $this->envFilepath(),
|
||||
'output' => $this->outputPath(),
|
||||
'preserve-output' => $this->isPreserveOutput(),
|
||||
'formats' => array_values(array_map(
|
||||
static fn (ConvertFormat $format) => $format->toArg(),
|
||||
$this->formats(),
|
||||
)),
|
||||
'vars' => $this->vars,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts settings into JSON format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return json_encode($this->__serialize(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes settings in JSON format into settings file
|
||||
*
|
||||
* @param array $vars
|
||||
* @return bool
|
||||
*/
|
||||
public function dump(array $vars = []): bool
|
||||
{
|
||||
count($vars) > 0 && $this->vars = $vars;
|
||||
return file_put_contents(self::$filepath, (string)$this) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a backup file of current settings file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function backup(): string
|
||||
{
|
||||
copy(self::$filepath, $newfilepath = self::$filepath . '.bak.' . time());
|
||||
return $newfilepath;
|
||||
}
|
||||
}
|
@ -3,9 +3,8 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PmConverter\Exceptions\{
|
||||
EmptyHttpVerbException,
|
||||
InvalidHttpVersionException};
|
||||
use PmConverter\Exceptions\EmptyHttpVerbException;
|
||||
use PmConverter\Exceptions\InvalidHttpVersionException;
|
||||
|
||||
class AbstractRequestTest extends TestCase
|
||||
{
|
||||
@ -71,7 +70,7 @@ class AbstractRequestTest extends TestCase
|
||||
/**
|
||||
* @covers PmConverter\Converters\Abstract\AbstractRequest
|
||||
* @covers PmConverter\Converters\Abstract\AbstractRequest::setUrl()
|
||||
* @covers PmConverter\Converters\Abstract\AbstractRequest::getUrl()
|
||||
* @covers PmConverter\Converters\Abstract\AbstractRequest::getRawUrl()
|
||||
* @return void
|
||||
*/
|
||||
public function testUrl(): void
|
||||
@ -79,7 +78,7 @@ class AbstractRequestTest extends TestCase
|
||||
$request = new \PmConverter\Converters\Http\HttpRequest();
|
||||
$request->setUrl('http://localhost');
|
||||
|
||||
$this->assertSame('http://localhost', $request->getUrl());
|
||||
$this->assertSame('http://localhost', $request->getRawUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user