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`.
|
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
|
> This project has been started and quickly written in my spare time to solve one exact problem in one NDA-project,
|
||||||
> contain stupid errors and (for sure) doesn't cover all possible cases according to collection schema.
|
> 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.
|
> 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
|
## Supported features
|
||||||
|
|
||||||
* [collection schema **v2.1**](https://schema.postman.com/json/collection/v2.1.0/collection.json);
|
* 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);
|
||||||
* `Bearer` auth;
|
|
||||||
* replace vars in requests by stored in collection and environment file;
|
* 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;
|
* 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);
|
* all headers (including disabled for `http`-format);
|
||||||
@ -25,7 +28,7 @@ These formats are supported for now: `http`, `curl`, `wget`.
|
|||||||
## Planned features
|
## Planned features
|
||||||
|
|
||||||
- support as many as possible/necessary of authentication kinds (_currently only `Bearer` supported_);
|
- 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);
|
- documentation generation support (markdown) with response examples (if present) (#6);
|
||||||
- maybe some another convert formats (like httpie or something...);
|
- maybe some another convert formats (like httpie or something...);
|
||||||
- better logging;
|
- better logging;
|
||||||
@ -65,6 +68,7 @@ Possible ARGUMENTS:
|
|||||||
-e, --env - use environment file with variables to replace in requests
|
-e, --env - use environment file with variables to replace in requests
|
||||||
--var "NAME=VALUE" - force replace specified env variable called NAME with custom VALUE
|
--var "NAME=VALUE" - force replace specified env variable called NAME with custom VALUE
|
||||||
-p, --preserve - do not delete OUTPUT_PATH (if exists)
|
-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
|
-h, --help - show this help message and exit
|
||||||
-v, --version - show version info 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.
|
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
|
## How to implement a new format
|
||||||
|
|
||||||
1. Create new namespace in `./src/Converters` and name it according to format of your choice.
|
1. Create new namespace in `./src/Converters` and name it according to format of your choice.
|
||||||
|
@ -14,7 +14,8 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^8.1",
|
"php": "^8.1",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-mbstring": "*"
|
"ext-mbstring": "*",
|
||||||
|
"ext-readline": "*"
|
||||||
},
|
},
|
||||||
"bin": ["pm-convert"],
|
"bin": ["pm-convert"],
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -24,10 +24,10 @@ is_null($file) && throw new RuntimeException('Unable to locate autoload.php file
|
|||||||
|
|
||||||
$processor = new Processor($argv);
|
$processor = new Processor($argv);
|
||||||
try {
|
try {
|
||||||
$processor->convert();
|
$processor->handle();
|
||||||
} catch (InvalidArgumentException $e) {
|
} catch (InvalidArgumentException $e) {
|
||||||
fwrite(STDERR, sprintf('ERROR: %s%s', $e->getMessage(), EOL));
|
fwrite(STDERR, sprintf('ERROR: %s%s', $e->getMessage(), EOL));
|
||||||
print(implode(EOL, $processor->usage()));
|
print(implode(EOL, Processor::usage()));
|
||||||
die(1);
|
die(1);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
fwrite(STDERR, sprintf('ERROR: %s%s', $e->getMessage(), EOL));
|
fwrite(STDERR, sprintf('ERROR: %s%s', $e->getMessage(), EOL));
|
||||||
|
@ -4,6 +4,8 @@ declare(strict_types = 1);
|
|||||||
|
|
||||||
namespace PmConverter;
|
namespace PmConverter;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Generator;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
use Stringable;
|
use Stringable;
|
||||||
|
|
||||||
@ -16,6 +18,8 @@ use Stringable;
|
|||||||
*/
|
*/
|
||||||
class Collection implements Stringable
|
class Collection implements Stringable
|
||||||
{
|
{
|
||||||
|
public readonly CollectionVersion $version;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closed constructor so that we could use factory methods
|
* Closed constructor so that we could use factory methods
|
||||||
*
|
*
|
||||||
@ -24,10 +28,11 @@ class Collection implements Stringable
|
|||||||
private function __construct(protected object $json)
|
private function __construct(protected object $json)
|
||||||
{
|
{
|
||||||
// specific case when collection has been exported via postman api
|
// specific case when collection has been exported via postman api
|
||||||
if (isset($json->collection)) {
|
if (property_exists($json, 'collection')) {
|
||||||
$json = $json->collection;
|
$json = $json->collection;
|
||||||
}
|
}
|
||||||
$this->json = $json;
|
$this->json = $json;
|
||||||
|
$this->version = $this->detectVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,7 +93,7 @@ class Collection implements Stringable
|
|||||||
*
|
*
|
||||||
* @return CollectionVersion
|
* @return CollectionVersion
|
||||||
*/
|
*/
|
||||||
public function version(): CollectionVersion
|
protected function detectVersion(): CollectionVersion
|
||||||
{
|
{
|
||||||
return match (true) {
|
return match (true) {
|
||||||
str_contains($this->json->info->schema, '/v2.0.') => CollectionVersion::Version20,
|
str_contains($this->json->info->schema, '/v2.0.') => CollectionVersion::Version20,
|
||||||
@ -96,4 +101,72 @@ class Collection implements Stringable
|
|||||||
default => CollectionVersion::Unknown
|
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;
|
namespace PmConverter\Converters\Abstract;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Iterator;
|
||||||
use PmConverter\Collection;
|
use PmConverter\Collection;
|
||||||
use PmConverter\Converters\{
|
use PmConverter\Converters\RequestContract;
|
||||||
ConverterContract,
|
|
||||||
RequestContract};
|
|
||||||
use PmConverter\Environment;
|
use PmConverter\Environment;
|
||||||
use PmConverter\Exceptions\{
|
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
||||||
CannotCreateDirectoryException,
|
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
||||||
DirectoryIsNotWriteableException,
|
use PmConverter\Exceptions\InvalidHttpVersionException;
|
||||||
InvalidHttpVersionException};
|
|
||||||
use PmConverter\FileSystem;
|
use PmConverter\FileSystem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
abstract class AbstractConverter implements ConverterContract
|
abstract class AbstractConverter
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Collection|null
|
* @var Collection|null
|
||||||
@ -32,53 +30,66 @@ abstract class AbstractConverter implements ConverterContract
|
|||||||
protected string $outputPath;
|
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
|
* @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;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new directory to save a converted collection into
|
* Converts requests from collection
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*
|
*
|
||||||
* @param Collection $collection
|
* @param Collection $collection
|
||||||
* @param string $outputPath
|
* @return static
|
||||||
* @return void
|
|
||||||
* @throws CannotCreateDirectoryException
|
* @throws CannotCreateDirectoryException
|
||||||
* @throws DirectoryIsNotWriteableException
|
* @throws DirectoryIsNotWriteableException
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function convert(Collection $collection, string $outputPath): void
|
public function convert(Collection $collection): static
|
||||||
{
|
{
|
||||||
$this->prepareOutputDir($outputPath);
|
|
||||||
$this->collection = $collection;
|
$this->collection = $collection;
|
||||||
$this->setVariables();
|
$this->outputPath = FileSystem::makeDir($this->outputPath);
|
||||||
foreach ($collection->item as $item) {
|
$this->setCollectionVars();
|
||||||
$this->convertItem($item);
|
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
|
* @return $this
|
||||||
*/
|
*/
|
||||||
protected function setVariables(): static
|
protected function setCollectionVars(): static
|
||||||
{
|
{
|
||||||
empty($this->env) && $this->env = new Environment($this->collection?->variable);
|
foreach ($this->collection?->variable ?? [] as $var) {
|
||||||
if (!empty($this->collection?->variable)) {
|
Environment::instance()->setCustomVar($var->key, $var->value);
|
||||||
foreach ($this->collection->variable as $var) {
|
|
||||||
$this->env[$var->key] = $var->value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -121,30 +129,6 @@ abstract class AbstractConverter implements ConverterContract
|
|||||||
&& empty($item->request);
|
&& 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
|
* Initialiazes request object to be written in file
|
||||||
*
|
*
|
||||||
@ -152,17 +136,18 @@ abstract class AbstractConverter implements ConverterContract
|
|||||||
* @return RequestContract
|
* @return RequestContract
|
||||||
* @throws InvalidHttpVersionException
|
* @throws InvalidHttpVersionException
|
||||||
*/
|
*/
|
||||||
protected function initRequest(object $item): RequestContract
|
protected function makeRequest(object $item): RequestContract
|
||||||
{
|
{
|
||||||
$request_class = static::REQUEST_CLASS;
|
$request_class = static::REQUEST_CLASS;
|
||||||
|
|
||||||
/** @var RequestContract $request */
|
/** @var RequestContract $request */
|
||||||
$request = new $request_class();
|
$request = new $request_class();
|
||||||
$request->setName($item->name);
|
$request->setName($item->name);
|
||||||
|
$request->setVersion($this->collection->version);
|
||||||
$request->setHttpVersion(1.1); //TODO http version?
|
$request->setHttpVersion(1.1); //TODO http version?
|
||||||
$request->setDescription($item->request?->description ?? null);
|
$request->setDescription($item->request?->description ?? null);
|
||||||
$request->setVerb($item->request->method);
|
$request->setVerb($item->request->method);
|
||||||
$request->setUrl($item->request->url->raw);
|
$request->setUrl($item->request->url);
|
||||||
$request->setHeaders($item->request->header);
|
$request->setHeaders($item->request->header);
|
||||||
$request->setAuth($item->request?->auth ?? $this->collection?->auth ?? null);
|
$request->setAuth($item->request?->auth ?? $this->collection?->auth ?? null);
|
||||||
if ($item->request->method !== 'GET' && !empty($item->request->body)) {
|
if ($item->request->method !== 'GET' && !empty($item->request->body)) {
|
||||||
@ -196,18 +181,9 @@ abstract class AbstractConverter implements ConverterContract
|
|||||||
*/
|
*/
|
||||||
protected function interpolate(string $content): string
|
protected function interpolate(string $content): string
|
||||||
{
|
{
|
||||||
if (!$this->env?->hasVars()) {
|
$replace = static fn ($a) => Environment::instance()->var($var = $a[0]) ?: $var;
|
||||||
return $content;
|
return Environment::instance()->hasVars()
|
||||||
}
|
? preg_replace_callback('/\{\{.*}}/m', $replace, $content)
|
||||||
$matches = [];
|
: $content;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace PmConverter\Converters\Abstract;
|
namespace PmConverter\Converters\Abstract;
|
||||||
|
|
||||||
|
use PmConverter\CollectionVersion;
|
||||||
use PmConverter\Converters\RequestContract;
|
use PmConverter\Converters\RequestContract;
|
||||||
use PmConverter\Exceptions\{
|
use PmConverter\Exceptions\EmptyHttpVerbException;
|
||||||
EmptyHttpVerbException,
|
use PmConverter\Exceptions\InvalidHttpVersionException;
|
||||||
InvalidHttpVersionException};
|
|
||||||
use PmConverter\HttpVersion;
|
use PmConverter\HttpVersion;
|
||||||
use Stringable;
|
use Stringable;
|
||||||
|
|
||||||
@ -22,9 +22,9 @@ abstract class AbstractRequest implements Stringable, RequestContract
|
|||||||
protected string $verb;
|
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
|
* @var float HTTP protocol version
|
||||||
@ -56,6 +56,15 @@ abstract class AbstractRequest implements Stringable, RequestContract
|
|||||||
*/
|
*/
|
||||||
protected string $bodymode = 'raw';
|
protected string $bodymode = 'raw';
|
||||||
|
|
||||||
|
|
||||||
|
protected CollectionVersion $version;
|
||||||
|
|
||||||
|
public function setVersion(CollectionVersion $version): static
|
||||||
|
{
|
||||||
|
$this->version = $version;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@ -133,7 +142,7 @@ abstract class AbstractRequest implements Stringable, RequestContract
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function setUrl(string $url): static
|
public function setUrl(object|string $url): static
|
||||||
{
|
{
|
||||||
$this->url = $url;
|
$this->url = $url;
|
||||||
return $this;
|
return $this;
|
||||||
@ -142,9 +151,9 @@ abstract class AbstractRequest implements Stringable, RequestContract
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @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)) {
|
if (!empty($auth)) {
|
||||||
switch ($auth->type) {
|
switch ($auth->type) {
|
||||||
case 'bearer':
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -5,12 +5,11 @@ declare(strict_types=1);
|
|||||||
namespace PmConverter\Converters;
|
namespace PmConverter\Converters;
|
||||||
|
|
||||||
|
|
||||||
use PmConverter\Converters\{
|
use PmConverter\Converters\Curl\CurlConverter;
|
||||||
Curl\CurlConverter,
|
use PmConverter\Converters\Http\HttpConverter;
|
||||||
Http\HttpConverter,
|
use PmConverter\Converters\Postman20\Postman20Converter;
|
||||||
Postman20\Postman20Converter,
|
use PmConverter\Converters\Postman21\Postman21Converter;
|
||||||
Postman21\Postman21Converter,
|
use PmConverter\Converters\Wget\WgetConverter;
|
||||||
Wget\WgetConverter};
|
|
||||||
|
|
||||||
enum ConvertFormat: string
|
enum ConvertFormat: string
|
||||||
{
|
{
|
||||||
@ -19,4 +18,26 @@ enum ConvertFormat: string
|
|||||||
case Wget = WgetConverter::class;
|
case Wget = WgetConverter::class;
|
||||||
case Postman20 = Postman20Converter::class;
|
case Postman20 = Postman20Converter::class;
|
||||||
case Postman21 = Postman21Converter::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;
|
namespace PmConverter\Converters\Curl;
|
||||||
|
|
||||||
use PmConverter\Converters\{
|
use PmConverter\Converters\Abstract\AbstractConverter;
|
||||||
Abstract\AbstractConverter,
|
|
||||||
ConverterContract};
|
|
||||||
|
|
||||||
class CurlConverter extends AbstractConverter implements ConverterContract
|
class CurlConverter extends AbstractConverter
|
||||||
{
|
{
|
||||||
protected const FILE_EXT = 'sh';
|
protected const FILE_EXT = 'sh';
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ class CurlRequest extends AbstractRequest
|
|||||||
"curl \ ",
|
"curl \ ",
|
||||||
"\t--http1.1 \ ", //TODO proto
|
"\t--http1.1 \ ", //TODO proto
|
||||||
"\t--request $this->verb \ ",
|
"\t--request $this->verb \ ",
|
||||||
"\t--location $this->url \ ",
|
"\t--location {$this->getRawUrl()} \ ",
|
||||||
],
|
],
|
||||||
$this->prepareHeaders(),
|
$this->prepareHeaders(),
|
||||||
$this->prepareBody()
|
$this->prepareBody()
|
||||||
|
@ -4,11 +4,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace PmConverter\Converters\Http;
|
namespace PmConverter\Converters\Http;
|
||||||
|
|
||||||
use PmConverter\Converters\{
|
use PmConverter\Converters\Abstract\AbstractConverter;
|
||||||
Abstract\AbstractConverter,
|
|
||||||
ConverterContract};
|
|
||||||
|
|
||||||
class HttpConverter extends AbstractConverter implements ConverterContract
|
class HttpConverter extends AbstractConverter
|
||||||
{
|
{
|
||||||
protected const FILE_EXT = 'http';
|
protected const FILE_EXT = 'http';
|
||||||
|
|
||||||
|
@ -5,8 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace PmConverter\Converters\Http;
|
namespace PmConverter\Converters\Http;
|
||||||
|
|
||||||
use PmConverter\Converters\Abstract\AbstractRequest;
|
use PmConverter\Converters\Abstract\AbstractRequest;
|
||||||
use PmConverter\Exceptions\{
|
use PmConverter\Exceptions\EmptyHttpVerbException;
|
||||||
EmptyHttpVerbException};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to determine file content with http request format
|
* Class to determine file content with http request format
|
||||||
@ -29,7 +28,7 @@ class HttpRequest extends AbstractRequest
|
|||||||
*/
|
*/
|
||||||
protected function prepareHeaders(): array
|
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) {
|
foreach ($this->headers as $name => $data) {
|
||||||
$output[] = sprintf('%s%s: %s', $data['disabled'] ? '# ' : '', $name, $data['value']);
|
$output[] = sprintf('%s%s: %s', $data['disabled'] ? '# ' : '', $name, $data['value']);
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,7 @@ namespace PmConverter\Converters\Postman20;
|
|||||||
|
|
||||||
use PmConverter\Collection;
|
use PmConverter\Collection;
|
||||||
use PmConverter\CollectionVersion;
|
use PmConverter\CollectionVersion;
|
||||||
use PmConverter\Converters\{
|
use PmConverter\Converters\Abstract\AbstractConverter;
|
||||||
Abstract\AbstractConverter,
|
|
||||||
ConverterContract};
|
|
||||||
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
||||||
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
||||||
use PmConverter\FileSystem;
|
use PmConverter\FileSystem;
|
||||||
@ -16,7 +14,7 @@ use PmConverter\FileSystem;
|
|||||||
/**
|
/**
|
||||||
* Converts Postman Collection v2.1 to v2.0
|
* 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';
|
protected const FILE_EXT = 'v20.postman_collection.json';
|
||||||
|
|
||||||
@ -26,25 +24,25 @@ class Postman20Converter extends AbstractConverter implements ConverterContract
|
|||||||
* Converts collection requests
|
* Converts collection requests
|
||||||
*
|
*
|
||||||
* @param Collection $collection
|
* @param Collection $collection
|
||||||
* @param string $outputPath
|
* @return static
|
||||||
* @return void
|
|
||||||
* @throws CannotCreateDirectoryException
|
* @throws CannotCreateDirectoryException
|
||||||
* @throws DirectoryIsNotWriteableException
|
* @throws DirectoryIsNotWriteableException
|
||||||
*/
|
*/
|
||||||
public function convert(Collection $collection, string $outputPath): void
|
public function convert(Collection $collection): static
|
||||||
{
|
{
|
||||||
$this->collection = $collection;
|
$this->collection = $collection;
|
||||||
// if data was exported from API, here is already valid json to
|
// if data was exported from API, here is already valid json to
|
||||||
// just flush it in file, otherwise we need to convert it deeper
|
// 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->collection->info->schema = str_replace('/v2.1.', '/v2.0.', $this->collection->info->schema);
|
||||||
$this->convertAuth($this->collection->raw());
|
$this->convertAuth($this->collection->raw());
|
||||||
foreach ($this->collection->item as $item) {
|
foreach ($this->collection->item as $item) {
|
||||||
$this->convertItem($item);
|
$this->convertItem($item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->prepareOutputDir($outputPath);
|
$this->outputPath = FileSystem::makeDir($this->outputPath);
|
||||||
$this->writeCollection();
|
$this->writeCollection();
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,11 +95,11 @@ class Postman20Converter extends AbstractConverter implements ConverterContract
|
|||||||
if (empty($request->auth)) {
|
if (empty($request->auth)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$type = $request->auth->type;
|
$auth = ['type' => 'noauth'];
|
||||||
if ($type !== 'noauth' && is_array($request->auth->$type)) {
|
$type = strtolower($request->auth->type);
|
||||||
$auth = [];
|
if ($type !== 'noauth') {
|
||||||
foreach ($request->auth->$type as $param) {
|
foreach ($request->auth->$type as $param) {
|
||||||
$auth[$param->key] = $param->value;
|
$auth[$param->key] = $param->value ?? '';
|
||||||
}
|
}
|
||||||
$request->auth->$type = (object)$auth;
|
$request->auth->$type = (object)$auth;
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,7 @@ namespace PmConverter\Converters\Postman21;
|
|||||||
|
|
||||||
use PmConverter\Collection;
|
use PmConverter\Collection;
|
||||||
use PmConverter\CollectionVersion;
|
use PmConverter\CollectionVersion;
|
||||||
use PmConverter\Converters\{
|
use PmConverter\Converters\Abstract\AbstractConverter;
|
||||||
Abstract\AbstractConverter,
|
|
||||||
ConverterContract};
|
|
||||||
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
||||||
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
||||||
use PmConverter\FileSystem;
|
use PmConverter\FileSystem;
|
||||||
@ -16,7 +14,7 @@ use PmConverter\FileSystem;
|
|||||||
/**
|
/**
|
||||||
* Converts Postman Collection v2.0 to v2.1
|
* 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';
|
protected const FILE_EXT = 'v21.postman_collection.json';
|
||||||
|
|
||||||
@ -26,25 +24,25 @@ class Postman21Converter extends AbstractConverter implements ConverterContract
|
|||||||
* Converts collection requests
|
* Converts collection requests
|
||||||
*
|
*
|
||||||
* @param Collection $collection
|
* @param Collection $collection
|
||||||
* @param string $outputPath
|
* @return static
|
||||||
* @return void
|
|
||||||
* @throws CannotCreateDirectoryException
|
* @throws CannotCreateDirectoryException
|
||||||
* @throws DirectoryIsNotWriteableException
|
* @throws DirectoryIsNotWriteableException
|
||||||
*/
|
*/
|
||||||
public function convert(Collection $collection, string $outputPath): void
|
public function convert(Collection $collection): static
|
||||||
{
|
{
|
||||||
$this->collection = $collection;
|
$this->collection = $collection;
|
||||||
// if data was exported from API, here is already valid json to
|
// if data was exported from API, here is already valid json to
|
||||||
// just flush it in file, otherwise we need to convert it deeper
|
// 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->collection->info->schema = str_replace('/v2.0.', '/v2.1.', $this->collection->info->schema);
|
||||||
$this->convertAuth($this->collection->raw());
|
$this->convertAuth($this->collection->raw());
|
||||||
foreach ($this->collection->item as $item) {
|
foreach ($this->collection->item as $item) {
|
||||||
$this->convertItem($item);
|
$this->convertItem($item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->prepareOutputDir($outputPath);
|
$this->outputPath = FileSystem::makeDir($this->outputPath);
|
||||||
$this->writeCollection();
|
$this->writeCollection();
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,7 +86,7 @@ interface RequestContract
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getUrl(): string;
|
public function getRawUrl(): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets headers from collection item to request object
|
* Sets headers from collection item to request object
|
||||||
@ -116,7 +116,7 @@ interface RequestContract
|
|||||||
/**
|
/**
|
||||||
* Sets authorization headers
|
* Sets authorization headers
|
||||||
*
|
*
|
||||||
* @param object|null $auth
|
* @param object $auth
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setAuth(object $auth): static;
|
public function setAuth(object $auth): static;
|
||||||
|
@ -4,11 +4,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace PmConverter\Converters\Wget;
|
namespace PmConverter\Converters\Wget;
|
||||||
|
|
||||||
use PmConverter\Converters\{
|
use PmConverter\Converters\Abstract\AbstractConverter;
|
||||||
Abstract\AbstractConverter,
|
|
||||||
ConverterContract};
|
|
||||||
|
|
||||||
class WgetConverter extends AbstractConverter implements ConverterContract
|
class WgetConverter extends AbstractConverter
|
||||||
{
|
{
|
||||||
protected const FILE_EXT = 'sh';
|
protected const FILE_EXT = 'sh';
|
||||||
|
|
||||||
|
@ -77,16 +77,16 @@ class WgetRequest extends AbstractRequest
|
|||||||
if ($this->getBodymode() === 'formdata') {
|
if ($this->getBodymode() === 'formdata') {
|
||||||
if ($this->getBody()) {
|
if ($this->getBody()) {
|
||||||
if ($this->getVerb() === 'GET') {
|
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 {
|
} else {
|
||||||
$output[] = sprintf("\t--body-data '%s' \ ", http_build_query($this->prepareBody()));
|
$output[] = sprintf("\t--body-data '%s' \ ", http_build_query($this->prepareBody()));
|
||||||
$output[] = sprintf("\t%s", $this->getUrl());
|
$output[] = sprintf("\t%s", $this->getRawUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($this->getVerb() !== 'GET') {
|
if ($this->getVerb() !== 'GET') {
|
||||||
$output[] = sprintf("\t--body-data '%s' \ ", implode("\n", $this->prepareBody()));
|
$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, ['']));
|
return implode(EOL, array_merge($output, ['']));
|
||||||
|
@ -4,23 +4,138 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace PmConverter;
|
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
|
* @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)) {
|
return static::$instance ??= new static();
|
||||||
foreach ($env->values as $var) {
|
|
||||||
$this->vars[static::formatKey($var->key)] = $var->value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
public function hasVars(): bool
|
||||||
{
|
{
|
||||||
return !empty($this->vars);
|
return !empty($this->ownVars) && !empty($this->customVars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closed constructor
|
||||||
|
*/
|
||||||
|
protected function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function offsetExists(mixed $offset): bool
|
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
|
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
|
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
|
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;
|
namespace PmConverter;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
use PmConverter\Exceptions\{
|
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
||||||
CannotCreateDirectoryException,
|
use PmConverter\Exceptions\DirectoryIsNotReadableException;
|
||||||
DirectoryIsNotReadableException,
|
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
||||||
DirectoryIsNotWriteableException,
|
use PmConverter\Exceptions\DirectoryNotExistsException;
|
||||||
DirectoryNotExistsException};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to work with files and directories
|
* Helper class to work with files and directories
|
||||||
@ -24,7 +24,7 @@ class FileSystem
|
|||||||
*/
|
*/
|
||||||
public static function normalizePath(string $path): string
|
public static function normalizePath(string $path): string
|
||||||
{
|
{
|
||||||
$path = str_replace('~', $_SERVER['HOME'], $path);
|
$path = str_replace('~/', "{$_SERVER['HOME']}/", $path);
|
||||||
return rtrim($path, DS);
|
return rtrim($path, DS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,13 +112,14 @@ class FileSystem
|
|||||||
* @param string $path
|
* @param string $path
|
||||||
* @return bool
|
* @return bool
|
||||||
* @throws JsonException
|
* @throws JsonException
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static function isCollectionFile(string $path): bool
|
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')
|
&& str_ends_with($path, '.postman_collection.json')
|
||||||
&& file_exists($path)
|
&& file_exists($path)
|
||||||
&& is_readable($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;
|
namespace PmConverter;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Generator;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
|
use JetBrains\PhpStorm\NoReturn;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
use PmConverter\Converters\{
|
use PmConverter\Converters\Abstract\AbstractConverter;
|
||||||
ConverterContract,
|
use PmConverter\Converters\ConverterContract;
|
||||||
ConvertFormat};
|
use PmConverter\Converters\ConvertFormat;
|
||||||
use PmConverter\Exceptions\{
|
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
||||||
CannotCreateDirectoryException,
|
use PmConverter\Exceptions\DirectoryIsNotReadableException;
|
||||||
DirectoryIsNotReadableException,
|
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
||||||
DirectoryIsNotWriteableException,
|
use PmConverter\Exceptions\DirectoryNotExistsException;
|
||||||
DirectoryNotExistsException};
|
use PmConverter\Exceptions\IncorrectSettingsFileException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main class
|
||||||
|
*/
|
||||||
class Processor
|
class Processor
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Converter version
|
* Converter version
|
||||||
*/
|
*/
|
||||||
public const VERSION = '1.5.0';
|
public const VERSION = '2.0.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 = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int Initial timestamp
|
* @var int Initial timestamp
|
||||||
*/
|
*/
|
||||||
protected int $initTime;
|
protected readonly int $initTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int Initial RAM usage
|
* @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
|
* @var Environment
|
||||||
*/
|
*/
|
||||||
protected Environment $env;
|
public Environment $env;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool Flag to output some debug-specific messages
|
|
||||||
*/
|
|
||||||
protected bool $devMode = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param array $argv Arguments came from cli
|
* @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->initTime = hrtime(true);
|
||||||
$this->initRam = memory_get_usage(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
|
protected function parseArgs(): void
|
||||||
{
|
{
|
||||||
if (count($this->argv) < 2) {
|
|
||||||
die(implode(EOL, $this->usage()) . EOL);
|
|
||||||
}
|
|
||||||
foreach ($this->argv as $idx => $arg) {
|
foreach ($this->argv as $idx => $arg) {
|
||||||
switch ($arg) {
|
switch ($arg) {
|
||||||
case '-f':
|
case '-f':
|
||||||
case '--file':
|
case '--file':
|
||||||
$rawpath = $this->argv[$idx + 1];
|
$this->settings->addFilePath($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];
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '-o':
|
case '-o':
|
||||||
@ -124,7 +95,7 @@ class Processor
|
|||||||
if (empty($this->argv[$idx + 1])) {
|
if (empty($this->argv[$idx + 1])) {
|
||||||
throw new InvalidArgumentException('-o is required');
|
throw new InvalidArgumentException('-o is required');
|
||||||
}
|
}
|
||||||
$this->outputPath = $this->argv[$idx + 1];
|
$this->settings->setOutputPath($this->argv[$idx + 1]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '-d':
|
case '-d':
|
||||||
@ -132,54 +103,57 @@ class Processor
|
|||||||
if (empty($this->argv[$idx + 1])) {
|
if (empty($this->argv[$idx + 1])) {
|
||||||
throw new InvalidArgumentException('a directory path is expected for -d (--dir)');
|
throw new InvalidArgumentException('a directory path is expected for -d (--dir)');
|
||||||
}
|
}
|
||||||
$rawpath = $this->argv[$idx + 1];
|
$this->settings->addDirPath($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));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '-e':
|
case '-e':
|
||||||
case '--env':
|
case '--env':
|
||||||
$this->envFile = FileSystem::normalizePath($this->argv[$idx + 1]);
|
$this->settings->setEnvFilepath($this->argv[$idx + 1]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '-p':
|
case '-p':
|
||||||
case '--preserve':
|
case '--preserve':
|
||||||
$this->preserveOutput = true;
|
$this->settings->setPreserveOutput(true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '--http':
|
case '--http':
|
||||||
$this->formats[ConvertFormat::Http->name] = ConvertFormat::Http;
|
$this->settings->addFormat(ConvertFormat::Http);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '--curl':
|
case '--curl':
|
||||||
$this->formats[ConvertFormat::Curl->name] = ConvertFormat::Curl;
|
$this->settings->addFormat(ConvertFormat::Curl);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '--wget':
|
case '--wget':
|
||||||
$this->formats[ConvertFormat::Wget->name] = ConvertFormat::Wget;
|
$this->settings->addFormat(ConvertFormat::Wget);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '--v2.0':
|
case '--v2.0':
|
||||||
$this->formats[ConvertFormat::Postman20->name] = ConvertFormat::Postman20;
|
$this->settings->addFormat(ConvertFormat::Postman20);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '--v2.1':
|
case '--v2.1':
|
||||||
$this->formats[ConvertFormat::Postman21->name] = ConvertFormat::Postman21;
|
$this->settings->addFormat(ConvertFormat::Postman21);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '-a':
|
case '-a':
|
||||||
case '--all':
|
case '--all':
|
||||||
foreach (ConvertFormat::cases() as $format) {
|
foreach (ConvertFormat::cases() as $format) {
|
||||||
$this->formats[$format->name] = $format;
|
$this->settings->addFormat($format);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '--var':
|
case '--var':
|
||||||
[$var, $value] = explode('=', trim($this->argv[$idx + 1]));
|
//TODO split by first equal sign
|
||||||
$this->vars[$var] = $value;
|
$this->env->setCustomVar(...explode('=', trim($this->argv[$idx + 1])));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '--dev':
|
||||||
|
$this->settings->setDevMode(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '--dump':
|
||||||
|
$this->needDumpSettings = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '-v':
|
case '-v':
|
||||||
@ -189,23 +163,63 @@ class Processor
|
|||||||
case '-h':
|
case '-h':
|
||||||
case '--help':
|
case '--help':
|
||||||
die(implode(EOL, $this->usage()) . EOL);
|
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');
|
throw new InvalidArgumentException('there are no collections to convert');
|
||||||
}
|
}
|
||||||
if (empty($this->outputPath)) {
|
if (empty($this->settings->outputPath())) {
|
||||||
throw new InvalidArgumentException('-o is required');
|
throw new InvalidArgumentException('-o is required');
|
||||||
}
|
}
|
||||||
if (empty($this->formats)) {
|
if (empty($this->settings->formats())) {
|
||||||
$this->formats = [ConvertFormat::Http->name => ConvertFormat::Http];
|
$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
|
* Initializes output directory
|
||||||
*
|
*
|
||||||
@ -215,100 +229,74 @@ class Processor
|
|||||||
* @throws DirectoryNotExistsException
|
* @throws DirectoryNotExistsException
|
||||||
* @throws DirectoryIsNotReadableException
|
* @throws DirectoryIsNotReadableException
|
||||||
*/
|
*/
|
||||||
protected function initOutputDirectory(): void
|
protected function prepareOutputDirectory(): void
|
||||||
{
|
{
|
||||||
if (isset($this?->outputPath) && !$this->preserveOutput) {
|
if (!$this->settings->isPreserveOutput()) {
|
||||||
FileSystem::removeDir($this->outputPath);
|
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
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function initConverters(): void
|
protected function initConverters(): void
|
||||||
{
|
{
|
||||||
foreach ($this->formats as $type) {
|
foreach ($this->settings->formats() as $type) {
|
||||||
$this->converters[$type->name] = new $type->value($this->preserveOutput);
|
$this->converters[$type->name] = new $type->value($this->settings->isPreserveOutput());
|
||||||
}
|
}
|
||||||
unset($this->formats);
|
unset($this->formats);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes collection objects
|
* Generates collections from settings
|
||||||
*
|
*
|
||||||
|
* @return Generator<Collection>
|
||||||
* @throws JsonException
|
* @throws JsonException
|
||||||
*/
|
*/
|
||||||
protected function initCollections(): void
|
protected function newCollection(): Generator
|
||||||
{
|
{
|
||||||
foreach ($this->collectionPaths as $collectionPath) {
|
foreach ($this->settings->collectionPaths() as $collectionPath) {
|
||||||
$collection = Collection::fromFile($collectionPath);
|
yield Collection::fromFile($collectionPath);
|
||||||
$this->collections[$collection->name()] = $collection;
|
|
||||||
}
|
}
|
||||||
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
|
* Begins a conversion
|
||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws JsonException
|
||||||
*/
|
*/
|
||||||
public function convert(): void
|
public function convert(): void
|
||||||
{
|
{
|
||||||
$this->parseArgs();
|
$count = count($this->settings->collectionPaths());
|
||||||
$this->initOutputDirectory();
|
|
||||||
$this->initConverters();
|
|
||||||
$this->initCollections();
|
|
||||||
$this->initEnv();
|
|
||||||
$count = count($this->collections);
|
|
||||||
$current = $success = 0;
|
$current = $success = 0;
|
||||||
|
$collection = null;
|
||||||
print(implode(EOL, array_merge($this->version(), $this->copyright())) . EOL . EOL);
|
print(implode(EOL, array_merge($this->version(), $this->copyright())) . EOL . EOL);
|
||||||
foreach ($this->collections as $collectionName => $collection) {
|
foreach ($this->newCollection() as $collection) {
|
||||||
++$current;
|
++$current;
|
||||||
printf("Converting '%s' (%d/%d):%s", $collectionName, $current, $count, EOL);
|
printf("Converting '%s' (%d/%d):%s", $collection->name(), $current, $count, EOL);
|
||||||
foreach ($this->converters as $type => $exporter) {
|
foreach ($this->converters as $type => $converter) {
|
||||||
|
/** @var AbstractConverter $converter */
|
||||||
printf('> %s%s', strtolower($type), EOL);
|
printf('> %s%s', strtolower($type), EOL);
|
||||||
$outputPath = sprintf('%s%s%s', $this->outputPath, DS, $collectionName);
|
$outputPath = sprintf('%s%s%s', $this->settings->outputPath(), DS, $collection->name());
|
||||||
if (!empty($this->env)) {
|
|
||||||
$exporter->withEnv($this->env);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
$exporter->convert($collection, $outputPath);
|
$converter = $converter->to($outputPath);
|
||||||
printf(' OK: %s%s', $exporter->getOutputPath(), EOL);
|
$converter = $converter->convert($collection);
|
||||||
|
$converter->flush();
|
||||||
|
printf(' OK: %s%s', $converter->getOutputPath(), EOL);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
printf(' ERROR %s: %s%s', $e->getCode(), $e->getMessage(), EOL);
|
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);
|
print(EOL);
|
||||||
++$success;
|
++$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);
|
$this->printStats($success, $current);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,18 +322,19 @@ class Processor
|
|||||||
/**
|
/**
|
||||||
* @return string[]
|
* @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[]
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
public function copyright(): array
|
public static function copyright(): array
|
||||||
{
|
{
|
||||||
|
$years = ($year = (int)date('Y')) > 2023 ? "2023 - $year" : $year;
|
||||||
return [
|
return [
|
||||||
'Anthony Axenov (c) ' . date('Y') . ", MIT license",
|
"Anthony Axenov (c) $years, MIT license",
|
||||||
'https://git.axenov.dev/anthony/pm-convert'
|
'https://git.axenov.dev/anthony/pm-convert'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -353,9 +342,9 @@ class Processor
|
|||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function usage(): array
|
public static function usage(): array
|
||||||
{
|
{
|
||||||
return array_merge($this->version(), [
|
return array_merge(static::version(), [
|
||||||
'Usage:',
|
'Usage:',
|
||||||
"\t./pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]",
|
"\t./pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]",
|
||||||
"\tphp 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-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--var \"NAME=VALUE\" - force replace specified env variable called NAME with custom VALUE",
|
||||||
"\t-p, --preserve - do not delete OUTPUT_PATH (if exists)",
|
"\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-h, --help - show this help message and exit",
|
||||||
"\t-v, --version - show version info and exit",
|
"\t-v, --version - show version info and exit",
|
||||||
'',
|
'',
|
||||||
@ -401,6 +391,6 @@ class Processor
|
|||||||
" -o ~/postman_export \ ",
|
" -o ~/postman_export \ ",
|
||||||
" --all",
|
" --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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use PmConverter\Exceptions\{
|
use PmConverter\Exceptions\EmptyHttpVerbException;
|
||||||
EmptyHttpVerbException,
|
use PmConverter\Exceptions\InvalidHttpVersionException;
|
||||||
InvalidHttpVersionException};
|
|
||||||
|
|
||||||
class AbstractRequestTest extends TestCase
|
class AbstractRequestTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -71,7 +70,7 @@ class AbstractRequestTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @covers PmConverter\Converters\Abstract\AbstractRequest
|
* @covers PmConverter\Converters\Abstract\AbstractRequest
|
||||||
* @covers PmConverter\Converters\Abstract\AbstractRequest::setUrl()
|
* @covers PmConverter\Converters\Abstract\AbstractRequest::setUrl()
|
||||||
* @covers PmConverter\Converters\Abstract\AbstractRequest::getUrl()
|
* @covers PmConverter\Converters\Abstract\AbstractRequest::getRawUrl()
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function testUrl(): void
|
public function testUrl(): void
|
||||||
@ -79,7 +78,7 @@ class AbstractRequestTest extends TestCase
|
|||||||
$request = new \PmConverter\Converters\Http\HttpRequest();
|
$request = new \PmConverter\Converters\Http\HttpRequest();
|
||||||
$request->setUrl('http://localhost');
|
$request->setUrl('http://localhost');
|
||||||
|
|
||||||
$this->assertSame('http://localhost', $request->getUrl());
|
$this->assertSame('http://localhost', $request->getRawUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user