First basic ready-to-use implementation
This commit is contained in:
parent
5cc681de63
commit
3b94911895
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/.idea
|
||||||
|
/.vscode
|
||||||
|
/vendor
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Антон Аксенов (aka Anthony Axenov)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
114
README.md
Normal file
114
README.md
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
# Postman collection converter
|
||||||
|
|
||||||
|
Convert your Postman collections into different formats.
|
||||||
|
|
||||||
|
Very fast.
|
||||||
|
Offline.
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Supported features
|
||||||
|
|
||||||
|
* [collection schema **v2.1**](https://schema.postman.com/json/collection/v2.1.0/collection.json);
|
||||||
|
* 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);
|
||||||
|
* `json` body (forces header `Content-Type` to `application/json`);
|
||||||
|
* `formdata` body (including disabled fields for `http`-format; forces header `Content-Type` to `multipart/form-data`)
|
||||||
|
|
||||||
|
## Planned features
|
||||||
|
|
||||||
|
- support as many as possible/necessary of authentication kinds (_currently no ones_);
|
||||||
|
- support as many as possible/necessary of body formats (_currently json and formdata_);
|
||||||
|
- documentation generation support (markdown) with responce examples (if present);
|
||||||
|
- maybe some another convert formats (like httpie or something...);
|
||||||
|
- replace `{{vars}}` from folder;
|
||||||
|
- replace `{{vars}}` from environment;
|
||||||
|
- performance measurement;
|
||||||
|
- better logging;
|
||||||
|
- tests, phpcs, psalm, etc.;
|
||||||
|
- web version.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
composer global r axenov/pm-convert
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure your `~/.config/composer/vendor/bin` is in `$PATH` env:
|
||||||
|
|
||||||
|
```
|
||||||
|
echo $PATH | grep --color=auto 'composer'
|
||||||
|
# if not then execute this command and add it into ~/.profile:
|
||||||
|
export PATH="$PATH:~/.config/composer/vendor/bin"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
$ pm-convert --help
|
||||||
|
Postman collection converter
|
||||||
|
Usage:
|
||||||
|
./pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]
|
||||||
|
php pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]
|
||||||
|
composer pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]
|
||||||
|
./vendor/bin/pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]
|
||||||
|
|
||||||
|
Possible ARGUMENTS:
|
||||||
|
-f, --file - a PATH to single collection located in PATH to convert from
|
||||||
|
-d, --dir - a directory with collections located in COLLECTION_FILEPATH to convert from
|
||||||
|
-o, --output - a directory OUTPUT_PATH to put results in
|
||||||
|
-p, --preserve - do not delete OUTPUT_PATH (if exists)
|
||||||
|
-h, --help - show this help message and exit
|
||||||
|
-v, --version - show version info and exit
|
||||||
|
|
||||||
|
If both -c and -d are specified then only unique set of files will be converted.
|
||||||
|
-f or -d are required to be specified at least once, but each may be specified multiple times.
|
||||||
|
PATH must be a valid path to readable json-file or directory.
|
||||||
|
OUTPUT_PATH must be a valid path to writeable directory.
|
||||||
|
If -o is specified several times then only last one will be applied.
|
||||||
|
|
||||||
|
Possible FORMATS:
|
||||||
|
--http - generate raw *.http files (default)
|
||||||
|
--curl - generate shell scripts with curl command
|
||||||
|
--wget - generate shell scripts with wget command
|
||||||
|
If no FORMATS specified then --http implied.
|
||||||
|
Any of FORMATS can be specified at the same time.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
./pm-convert \
|
||||||
|
-f ~/dir1/first.postman_collection.json \
|
||||||
|
--directory ~/team \
|
||||||
|
--file ~/dir2/second.postman_collection.json \
|
||||||
|
-d ~/personal \
|
||||||
|
-o ~/postman_export
|
||||||
|
```
|
||||||
|
### Notice
|
||||||
|
|
||||||
|
Make sure every (I mean _every_) collection (not collection file), its folders and/or requests has unique names.
|
||||||
|
If not, you can rename them in Postman or convert collections with similar names into different directories.
|
||||||
|
Otherwise converted files may be overwritten by each other.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
You can use, share and develop this project according to [MIT License](LICENSE).
|
||||||
|
|
||||||
|
Postman is [protected legal trademark](https://www.postman.com/legal/trademark-policy/) of Postman, Inc.
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
I'm **not** affiliated with Postman, Inc. in any way.
|
||||||
|
|
||||||
|
I'm just a backend developer who is forced to use this javascripted gigachad-shitmonster.
|
||||||
|
|
||||||
|
So the goal of this project is to:
|
||||||
|
* take the data and its synchronization under own transparent control;
|
||||||
|
* easily migrate to something more RAM tolerant and productive, easier and free to use;
|
||||||
|
* get off the needle of the vendor lock, strict restrictions for teams and not to pay incredible $$$ for heavy useless WYSIWYGs;
|
||||||
|
* give YOU these opportunities.
|
34
composer.json
Normal file
34
composer.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "axenov/pm-convert",
|
||||||
|
"type": "library",
|
||||||
|
"description": "Postman collection coverter",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://axenov.dev/",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Anthony Axenov",
|
||||||
|
"homepage": "https://axenov.dev"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keywords": ["postman", "collection", "converter", "http", "wget", "curl", "api", "convert"],
|
||||||
|
"require": {
|
||||||
|
"php": "^8.1",
|
||||||
|
"ext-json": "*"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"pm-convert"
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PmConverter\\": "src\\"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"pm-convert": "@php ./pm-convert"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"optimize-autoloader": true,
|
||||||
|
"preferred-install": "dist",
|
||||||
|
"sort-packages": true
|
||||||
|
}
|
||||||
|
}
|
21
composer.lock
generated
Normal file
21
composer.lock
generated
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "b49bb03a97ad612632a24ebc59dfafcb",
|
||||||
|
"packages": [],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": {
|
||||||
|
"php": "^8.1",
|
||||||
|
"ext-json": "*"
|
||||||
|
},
|
||||||
|
"platform-dev": [],
|
||||||
|
"plugin-api-version": "2.3.0"
|
||||||
|
}
|
28
pm-convert
Executable file
28
pm-convert
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
use PmConverter\Processor;
|
||||||
|
|
||||||
|
$paths = [
|
||||||
|
__DIR__ . '/../../autoload.php',
|
||||||
|
__DIR__ . '/../autoload.php',
|
||||||
|
__DIR__ . '/vendor/autoload.php'
|
||||||
|
];
|
||||||
|
$file = null;
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
if (file_exists($path)) {
|
||||||
|
require_once $file = $path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_null($file) && throw new RuntimeException('Unable to locate autoload.php file.');
|
||||||
|
|
||||||
|
try {
|
||||||
|
(new Processor($argv))->start();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
fwrite(STDERR, sprintf('ERROR: %s%s', $e->getMessage(), PHP_EOL));
|
||||||
|
die(1);
|
||||||
|
}
|
11
src/Exceptions/CannotCreateDirectoryException.php
Normal file
11
src/Exceptions/CannotCreateDirectoryException.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class CannotCreateDirectoryException extends Exception
|
||||||
|
{
|
||||||
|
}
|
11
src/Exceptions/DirectoryIsNotReadableException.php
Normal file
11
src/Exceptions/DirectoryIsNotReadableException.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class DirectoryIsNotReadableException extends Exception
|
||||||
|
{
|
||||||
|
}
|
11
src/Exceptions/DirectoryIsNotWriteableException.php
Normal file
11
src/Exceptions/DirectoryIsNotWriteableException.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class DirectoryIsNotWriteableException extends Exception
|
||||||
|
{
|
||||||
|
}
|
11
src/Exceptions/DirectoryNotExistsException.php
Normal file
11
src/Exceptions/DirectoryNotExistsException.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class DirectoryNotExistsException extends Exception
|
||||||
|
{
|
||||||
|
}
|
111
src/Exporters/Abstract/AbstractConverter.php
Normal file
111
src/Exporters/Abstract/AbstractConverter.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exporters\Abstract;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use PmConverter\Exporters\{
|
||||||
|
RequestContract};
|
||||||
|
use PmConverter\FileSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
abstract class AbstractConverter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var object|null
|
||||||
|
*/
|
||||||
|
protected ?object $collection = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected string $outputPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function convert(object $collection, string $outputPath): void
|
||||||
|
{
|
||||||
|
$outputPath = sprintf('%s%s%s', $outputPath, DIRECTORY_SEPARATOR, static::OUTPUT_DIR);
|
||||||
|
$this->outputPath = FileSystem::makeDir($outputPath);
|
||||||
|
$this->collection = $collection;
|
||||||
|
foreach ($collection->item as $item) {
|
||||||
|
$this->convertItem($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getOutputPath(): string
|
||||||
|
{
|
||||||
|
return $this->outputPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param object $item
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function isItemFolder(object $item): bool
|
||||||
|
{
|
||||||
|
return !empty($item->item) && empty($item->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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(DIRECTORY_SEPARATOR, $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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param object $item
|
||||||
|
* @return RequestContract
|
||||||
|
*/
|
||||||
|
protected function initRequest(object $item): RequestContract
|
||||||
|
{
|
||||||
|
$request_class = static::REQUEST;
|
||||||
|
$result = new $request_class();
|
||||||
|
$result->setName($item->name);
|
||||||
|
$result->setDescription($item->request?->description ?? null);
|
||||||
|
$result->setVerb($item->request->method);
|
||||||
|
$result->setUrl($item->request->url->raw);
|
||||||
|
$result->setHeaders($item->request->header);
|
||||||
|
if ($item->request->method !== 'GET' && !empty($item->request->body)) {
|
||||||
|
$result->setBody($item->request->body);
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestContract $request
|
||||||
|
* @param string|null $subpath
|
||||||
|
* @return bool
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function writeRequest(RequestContract $request, string $subpath = null): bool
|
||||||
|
{
|
||||||
|
$filedir = sprintf('%s%s%s', $this->outputPath, DIRECTORY_SEPARATOR, $subpath);
|
||||||
|
$filedir = FileSystem::makeDir($filedir);
|
||||||
|
$filepath = sprintf('%s%s%s.%s', $filedir, DIRECTORY_SEPARATOR, $request->getName(), static::FILE_EXT);
|
||||||
|
return file_put_contents($filepath, (string)$request) > 0;
|
||||||
|
}
|
||||||
|
}
|
164
src/Exporters/Abstract/AbstractRequest.php
Normal file
164
src/Exporters/Abstract/AbstractRequest.php
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exporters\Abstract;
|
||||||
|
|
||||||
|
use PmConverter\Exporters\Http\HttpRequest;
|
||||||
|
use PmConverter\Exporters\RequestContract;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
abstract class AbstractRequest implements RequestContract
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected string $http = 'HTTP/1.1'; //TODO verb
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected string $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
protected ?string $description = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected array $headers = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var mixed
|
||||||
|
*/
|
||||||
|
protected mixed $body = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected string $bodymode = 'raw';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected string $verb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected string $url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @return HttpRequest
|
||||||
|
*/
|
||||||
|
public function setName(string $name): static
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return str_replace(DIRECTORY_SEPARATOR, '_', $this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $description
|
||||||
|
* @return HttpRequest
|
||||||
|
*/
|
||||||
|
public function setDescription(?string $description): static
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $verb
|
||||||
|
* @return HttpRequest
|
||||||
|
*/
|
||||||
|
public function setVerb(string $verb): static
|
||||||
|
{
|
||||||
|
$this->verb = $verb;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $url
|
||||||
|
* @return HttpRequest
|
||||||
|
*/
|
||||||
|
public function setUrl(string $url): static
|
||||||
|
{
|
||||||
|
$this->url = $url;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param object[]|null $headers
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setHeaders(?array $headers): static
|
||||||
|
{
|
||||||
|
foreach ($headers as $header) {
|
||||||
|
$this->headers[$header->key] = [
|
||||||
|
'value' => $header->value,
|
||||||
|
'disabled' => $header?->disabled ?? false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $bodymode
|
||||||
|
* @return HttpRequest
|
||||||
|
*/
|
||||||
|
public function setBodymode(string $bodymode): static
|
||||||
|
{
|
||||||
|
$this->bodymode = $bodymode;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $body
|
||||||
|
* @return HttpRequest
|
||||||
|
*/
|
||||||
|
public function setBody(object $body): static
|
||||||
|
{
|
||||||
|
$this->setBodymode($body->mode);
|
||||||
|
if (!empty($body->options) && $body->options->{$this->bodymode}->language === 'json') {
|
||||||
|
empty($this->headers['Content-Type']) && $this->setHeaders([
|
||||||
|
(object)[
|
||||||
|
'key' => 'Content-Type',
|
||||||
|
'value' => 'application/json',
|
||||||
|
'disabled' => false,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$body->mode === 'formdata' && $this->setHeaders([
|
||||||
|
(object)[
|
||||||
|
'key' => 'Content-Type',
|
||||||
|
'value' => 'multipart/form-data',
|
||||||
|
'disabled' => false,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$this->body = $body->{$body->mode};
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract protected function prepareBody(): ?string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract public function __toString(): string;
|
||||||
|
}
|
17
src/Exporters/ConvertFormat.php
Normal file
17
src/Exporters/ConvertFormat.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exporters;
|
||||||
|
|
||||||
|
|
||||||
|
use PmConverter\Exporters\Curl\CurlConverter;
|
||||||
|
use PmConverter\Exporters\Http\HttpConverter;
|
||||||
|
use PmConverter\Exporters\Wget\WgetConverter;
|
||||||
|
|
||||||
|
enum ConvertFormat: string
|
||||||
|
{
|
||||||
|
case Http = HttpConverter::class;
|
||||||
|
case Curl = CurlConverter::class;
|
||||||
|
case Wget = WgetConverter::class;
|
||||||
|
}
|
11
src/Exporters/ConverterContract.php
Normal file
11
src/Exporters/ConverterContract.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exporters;
|
||||||
|
|
||||||
|
interface ConverterContract
|
||||||
|
{
|
||||||
|
public function convert(object $collection, string $outputPath): void;
|
||||||
|
public function getOutputPath(): string;
|
||||||
|
}
|
17
src/Exporters/Curl/CurlConverter.php
Normal file
17
src/Exporters/Curl/CurlConverter.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exporters\Curl;
|
||||||
|
|
||||||
|
use PmConverter\Exporters\{
|
||||||
|
Abstract\AbstractConverter,
|
||||||
|
ConverterContract};
|
||||||
|
|
||||||
|
class CurlConverter extends AbstractConverter implements ConverterContract
|
||||||
|
{
|
||||||
|
protected const FILE_EXT = 'sh';
|
||||||
|
protected const OUTPUT_DIR = 'curl';
|
||||||
|
|
||||||
|
protected const REQUEST = CurlRequest::class;
|
||||||
|
}
|
65
src/Exporters/Curl/CurlRequest.php
Normal file
65
src/Exporters/Curl/CurlRequest.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exporters\Curl;
|
||||||
|
|
||||||
|
use PmConverter\Exporters\Abstract\AbstractRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class CurlRequest extends AbstractRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function prepareBody(): ?string
|
||||||
|
{
|
||||||
|
switch ($this->bodymode) {
|
||||||
|
case 'formdata':
|
||||||
|
$body = [];
|
||||||
|
foreach ($this->body as $data) {
|
||||||
|
$body[] = sprintf(
|
||||||
|
"%s\t--form '%s=%s' \ ",
|
||||||
|
isset($data->disabled) ? '# ' : '',
|
||||||
|
$data->key,
|
||||||
|
$data->type === 'file' ? "@$data->src" : $data->value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return implode(PHP_EOL, $body);
|
||||||
|
default:
|
||||||
|
return $this->body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
$output[] = '#!/bin/sh';
|
||||||
|
if ($this->description) {
|
||||||
|
$output[] = '# ' . str_replace("\n", "\n# ", $this->description);
|
||||||
|
$output[] = '';
|
||||||
|
}
|
||||||
|
$output[] = "curl \ ";
|
||||||
|
$output[] = "\t--http1.1 \ "; //TODO verb
|
||||||
|
$output[] = "\t--request $this->verb \ ";
|
||||||
|
$output[] = "\t--location $this->url \ ";
|
||||||
|
foreach ($this->headers as $header_key => $header) {
|
||||||
|
if ($header['disabled']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$output[] = sprintf("\t--header '%s=%s' \ ", $header_key, $header['value']);
|
||||||
|
}
|
||||||
|
if (!is_null($body = $this->prepareBody())) {
|
||||||
|
$output[] = match ($this->bodymode) {
|
||||||
|
'formdata' => $body,
|
||||||
|
default => "\t--data '$body'",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$output[] = rtrim(array_pop($output), '\ ');
|
||||||
|
return implode(PHP_EOL, $output);
|
||||||
|
}
|
||||||
|
}
|
17
src/Exporters/Http/HttpConverter.php
Normal file
17
src/Exporters/Http/HttpConverter.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exporters\Http;
|
||||||
|
|
||||||
|
use PmConverter\Exporters\{
|
||||||
|
Abstract\AbstractConverter,
|
||||||
|
ConverterContract};
|
||||||
|
|
||||||
|
class HttpConverter extends AbstractConverter implements ConverterContract
|
||||||
|
{
|
||||||
|
protected const FILE_EXT = 'http';
|
||||||
|
protected const OUTPUT_DIR = 'http';
|
||||||
|
|
||||||
|
protected const REQUEST = HttpRequest::class;
|
||||||
|
}
|
53
src/Exporters/Http/HttpRequest.php
Normal file
53
src/Exporters/Http/HttpRequest.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exporters\Http;
|
||||||
|
|
||||||
|
use PmConverter\Exporters\Abstract\AbstractRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class HttpRequest extends AbstractRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function prepareBody(): ?string
|
||||||
|
{
|
||||||
|
switch ($this->bodymode) {
|
||||||
|
case 'formdata':
|
||||||
|
$body = [];
|
||||||
|
foreach ($this->body as $data) {
|
||||||
|
$body[] = sprintf(
|
||||||
|
'%s%s=%s',
|
||||||
|
empty($data->disabled) ? '' : '# ',
|
||||||
|
$data->key,
|
||||||
|
$data->type === 'file' ? "$data->src" : $data->value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return implode(PHP_EOL, $body);
|
||||||
|
default:
|
||||||
|
return $this->body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
if ($this->description) {
|
||||||
|
$output[] = '# ' . str_replace("\n", "\n# ", $this->description);
|
||||||
|
$output[] = '';
|
||||||
|
}
|
||||||
|
$output[] = "$this->verb $this->url $this->http";
|
||||||
|
foreach ($this->headers as $header_key => $header) {
|
||||||
|
$output[] = sprintf('%s%s: %s', $header['disabled'] ? '# ' : '', $header_key, $header['value']);
|
||||||
|
}
|
||||||
|
$output[] = '';
|
||||||
|
$output[] = (string)$this->prepareBody();
|
||||||
|
return implode(PHP_EOL, $output);
|
||||||
|
}
|
||||||
|
}
|
18
src/Exporters/RequestContract.php
Normal file
18
src/Exporters/RequestContract.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exporters;
|
||||||
|
|
||||||
|
interface RequestContract
|
||||||
|
{
|
||||||
|
public function setName(string $name): static;
|
||||||
|
public function getName(): string;
|
||||||
|
public function setDescription(?string $description): static;
|
||||||
|
public function setVerb(string $verb): static;
|
||||||
|
public function setUrl(string $url): static;
|
||||||
|
public function setHeaders(?array $headers): static;
|
||||||
|
public function setBodymode(string $bodymode): static;
|
||||||
|
public function setBody(object $body): static;
|
||||||
|
public function __toString(): string;
|
||||||
|
}
|
17
src/Exporters/Wget/WgetConverter.php
Normal file
17
src/Exporters/Wget/WgetConverter.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exporters\Wget;
|
||||||
|
|
||||||
|
use PmConverter\Exporters\{
|
||||||
|
Abstract\AbstractConverter,
|
||||||
|
ConverterContract};
|
||||||
|
|
||||||
|
class WgetConverter extends AbstractConverter implements ConverterContract
|
||||||
|
{
|
||||||
|
protected const FILE_EXT = 'sh';
|
||||||
|
protected const OUTPUT_DIR = 'wget';
|
||||||
|
|
||||||
|
protected const REQUEST = WgetRequest::class;
|
||||||
|
}
|
63
src/Exporters/Wget/WgetRequest.php
Normal file
63
src/Exporters/Wget/WgetRequest.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter\Exporters\Wget;
|
||||||
|
|
||||||
|
use PmConverter\Exporters\Abstract\AbstractRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class WgetRequest extends AbstractRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function prepareBody(): ?string
|
||||||
|
{
|
||||||
|
switch ($this->bodymode) {
|
||||||
|
case 'formdata':
|
||||||
|
$lines = [];
|
||||||
|
foreach ($this->body as &$data) {
|
||||||
|
if ($data->type === 'file') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$lines[$data->key] = $data->value;
|
||||||
|
}
|
||||||
|
$body[] = http_build_query($lines);
|
||||||
|
return implode(PHP_EOL, $body);
|
||||||
|
default:
|
||||||
|
return $this->body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
$output[] = '#!/bin/sh';
|
||||||
|
if ($this->description) {
|
||||||
|
$output[] = '# ' . str_replace("\n", "\n# ", $this->description);
|
||||||
|
$output[] = '';
|
||||||
|
}
|
||||||
|
$output[] = 'wget \ ';
|
||||||
|
$output[] = "\t--no-check-certificate \ ";
|
||||||
|
$output[] = "\t--quiet \ ";
|
||||||
|
$output[] = "\t--timeout=0 \ ";
|
||||||
|
$output[] = "\t--method $this->verb \ ";
|
||||||
|
foreach ($this->headers as $header_key => $header) {
|
||||||
|
if ($header['disabled']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$output[] = sprintf("\t--header '%s=%s' \ ", $header_key, $header['value']);
|
||||||
|
}
|
||||||
|
if (!is_null($body = $this->prepareBody())) {
|
||||||
|
$output[] = "\t--body-data '$body' \ ";
|
||||||
|
}
|
||||||
|
$output[] = rtrim(array_pop($output), '\ ');
|
||||||
|
$output[] = "\t'$this->url'";
|
||||||
|
return implode(PHP_EOL, $output);
|
||||||
|
}
|
||||||
|
}
|
95
src/FileSystem.php
Normal file
95
src/FileSystem.php
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace PmConverter;
|
||||||
|
|
||||||
|
use PmConverter\Exceptions\{
|
||||||
|
CannotCreateDirectoryException,
|
||||||
|
DirectoryIsNotReadableException,
|
||||||
|
DirectoryIsNotWriteableException,
|
||||||
|
DirectoryNotExistsException};
|
||||||
|
|
||||||
|
class FileSystem
|
||||||
|
{
|
||||||
|
public static function normalizePath(string $path): string
|
||||||
|
{
|
||||||
|
$path = str_replace('~', $_SERVER['HOME'], $path);
|
||||||
|
return rtrim($path, DIRECTORY_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
* @return string
|
||||||
|
* @throws CannotCreateDirectoryException
|
||||||
|
* @throws DirectoryIsNotWriteableException
|
||||||
|
*/
|
||||||
|
public static function makeDir(string $path): string
|
||||||
|
{
|
||||||
|
$path = static::normalizePath($path);
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
mkdir($path, recursive: true)
|
||||||
|
|| throw new CannotCreateDirectoryException("cannot create output directory: $path");
|
||||||
|
}
|
||||||
|
if (!is_writable($path)) {
|
||||||
|
throw new DirectoryIsNotWriteableException("output directory permissions are not valid: $path");
|
||||||
|
}
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
* @return void
|
||||||
|
* @throws DirectoryIsNotReadableException
|
||||||
|
* @throws DirectoryIsNotWriteableException
|
||||||
|
* @throws DirectoryNotExistsException
|
||||||
|
*/
|
||||||
|
public static function removeDir(string $path): void
|
||||||
|
{
|
||||||
|
$path = static::normalizePath($path);
|
||||||
|
$dir_contents = static::dirContents($path);
|
||||||
|
foreach ($dir_contents as $record) {
|
||||||
|
is_dir($record) ? static::removeDir($record) : @unlink($record);
|
||||||
|
}
|
||||||
|
file_exists($path) && @rmdir($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
* @return bool
|
||||||
|
* @throws DirectoryIsNotWriteableException
|
||||||
|
* @throws DirectoryNotExistsException
|
||||||
|
* @throws DirectoryIsNotReadableException
|
||||||
|
*/
|
||||||
|
public static function checkDir(string $path): bool
|
||||||
|
{
|
||||||
|
$path = static::normalizePath($path);
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
throw new DirectoryNotExistsException("output directory is not exist: $path");
|
||||||
|
}
|
||||||
|
if (!is_readable($path)) {
|
||||||
|
throw new DirectoryIsNotReadableException("output directory permissions are not valid: $path");
|
||||||
|
}
|
||||||
|
if (!is_writable($path)) {
|
||||||
|
throw new DirectoryIsNotWriteableException("output directory permissions are not valid: $path");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
* @return array
|
||||||
|
* @throws DirectoryIsNotReadableException
|
||||||
|
* @throws DirectoryIsNotWriteableException
|
||||||
|
* @throws DirectoryNotExistsException
|
||||||
|
*/
|
||||||
|
public static function dirContents(string $path): array
|
||||||
|
{
|
||||||
|
$path = static::normalizePath($path);
|
||||||
|
$records = array_diff(@scandir($path) ?: [], ['.', '..']);
|
||||||
|
foreach ($records as &$record) {
|
||||||
|
$record = sprintf('%s%s%s', $path, DIRECTORY_SEPARATOR, $record);
|
||||||
|
}
|
||||||
|
return $records;
|
||||||
|
}
|
||||||
|
}
|
264
src/Processor.php
Normal file
264
src/Processor.php
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PmConverter;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use JsonException;
|
||||||
|
use PmConverter\Exceptions\CannotCreateDirectoryException;
|
||||||
|
use PmConverter\Exceptions\DirectoryIsNotReadableException;
|
||||||
|
use PmConverter\Exceptions\DirectoryIsNotWriteableException;
|
||||||
|
use PmConverter\Exceptions\DirectoryNotExistsException;
|
||||||
|
use PmConverter\Exporters\ConverterContract;
|
||||||
|
use PmConverter\Exporters\ConvertFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class Processor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Converter version
|
||||||
|
*/
|
||||||
|
public const VERSION = '1.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 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 object[] Collections that will be converted into choosen formats
|
||||||
|
*/
|
||||||
|
protected array $collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param array $argv Arguments came from cli
|
||||||
|
*/
|
||||||
|
public function __construct(protected array $argv)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an array of arguments came from cli
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws DirectoryIsNotWriteableException
|
||||||
|
* @throws DirectoryNotExistsException
|
||||||
|
* @throws DirectoryIsNotReadableException
|
||||||
|
*/
|
||||||
|
protected function parseArgs(): void
|
||||||
|
{
|
||||||
|
if (count($this->argv) < 2) {
|
||||||
|
die(implode(PHP_EOL, $this->usage()) . PHP_EOL);
|
||||||
|
}
|
||||||
|
foreach ($this->argv as $idx => $arg) {
|
||||||
|
switch ($arg) {
|
||||||
|
case '-f':
|
||||||
|
case '--file':
|
||||||
|
$path = $this->argv[$idx + 1];
|
||||||
|
if (empty($path) || !str_ends_with($path, '.json') || !file_exists($path) || !is_readable($path)) {
|
||||||
|
throw new InvalidArgumentException('a valid json-file path is expected for -f (--file)');
|
||||||
|
}
|
||||||
|
$this->collectionPaths[] = $this->argv[$idx + 1];
|
||||||
|
break;
|
||||||
|
case '-o':
|
||||||
|
case '--output':
|
||||||
|
if (empty($this->argv[$idx + 1])) {
|
||||||
|
throw new InvalidArgumentException('-o expected');
|
||||||
|
}
|
||||||
|
$this->outputPath = $this->argv[$idx + 1];
|
||||||
|
break;
|
||||||
|
case '-d':
|
||||||
|
case '--dir':
|
||||||
|
if (empty($this->argv[$idx + 1])) {
|
||||||
|
throw new InvalidArgumentException('a directory path is expected for -d (--dir)');
|
||||||
|
}
|
||||||
|
$path = $this->argv[$idx + 1];
|
||||||
|
if (FileSystem::checkDir($path)) {
|
||||||
|
$files = array_filter(
|
||||||
|
FileSystem::dirContents($path),
|
||||||
|
static fn($filename) => str_ends_with($filename, '.json')
|
||||||
|
);
|
||||||
|
$this->collectionPaths = array_unique(array_merge($this?->collectionPaths ?? [], $files));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '-p':
|
||||||
|
case '--preserve':
|
||||||
|
$this->preserveOutput = true;
|
||||||
|
break;
|
||||||
|
case '--http':
|
||||||
|
$this->formats[ConvertFormat::Http->name] = ConvertFormat::Http;
|
||||||
|
break;
|
||||||
|
case '--curl':
|
||||||
|
$this->formats[ConvertFormat::Curl->name] = ConvertFormat::Curl;
|
||||||
|
break;
|
||||||
|
case '--wget':
|
||||||
|
$this->formats[ConvertFormat::Wget->name] = ConvertFormat::Wget;
|
||||||
|
break;
|
||||||
|
case '-v':
|
||||||
|
case '--version':
|
||||||
|
die(implode(PHP_EOL, $this->version()) . PHP_EOL);
|
||||||
|
case '-h':
|
||||||
|
case '--help':
|
||||||
|
die(implode(PHP_EOL, $this->usage()) . PHP_EOL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($this->formats)) {
|
||||||
|
$this->formats = [ConvertFormat::Http->name => ConvertFormat::Http];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
* @throws CannotCreateDirectoryException
|
||||||
|
* @throws DirectoryIsNotWriteableException
|
||||||
|
* @throws DirectoryNotExistsException
|
||||||
|
* @throws DirectoryIsNotReadableException
|
||||||
|
*/
|
||||||
|
protected function initOutputDirectory(): void
|
||||||
|
{
|
||||||
|
if (isset($this?->outputPath) && !$this->preserveOutput) {
|
||||||
|
FileSystem::removeDir($this->outputPath);
|
||||||
|
}
|
||||||
|
FileSystem::makeDir($this->outputPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes converters according to choosen formats
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function initConverters(): void
|
||||||
|
{
|
||||||
|
foreach ($this->formats as $type) {
|
||||||
|
$this->converters[$type->name] = new $type->value($this->preserveOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws JsonException
|
||||||
|
*/
|
||||||
|
protected function initCollections(): void
|
||||||
|
{
|
||||||
|
foreach ($this->collectionPaths as $collectionPath) {
|
||||||
|
$content = file_get_contents(FileSystem::normalizePath($collectionPath));
|
||||||
|
$content = json_decode($content, flags: JSON_THROW_ON_ERROR);
|
||||||
|
if (!property_exists($content, 'collection') || empty($content?->collection)) {
|
||||||
|
throw new JsonException("not a valid collection: $collectionPath");
|
||||||
|
}
|
||||||
|
$this->collections[$content->collection->info->name] = $content->collection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins a conversion
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function start(): void
|
||||||
|
{
|
||||||
|
$this->parseArgs();
|
||||||
|
$this->initOutputDirectory();
|
||||||
|
$this->initConverters();
|
||||||
|
$this->initCollections();
|
||||||
|
print(implode(PHP_EOL, array_merge($this->version(), $this->copyright())) . PHP_EOL . PHP_EOL);
|
||||||
|
foreach ($this->collections as $collectionName => $collection) {
|
||||||
|
print("Converting '$collectionName':" . PHP_EOL);
|
||||||
|
foreach ($this->converters as $type => $exporter) {
|
||||||
|
print("\t-> " . strtolower($type));
|
||||||
|
$outputPath = sprintf('%s%s%s', $this->outputPath, DIRECTORY_SEPARATOR, $collectionName);
|
||||||
|
$exporter->convert($collection, $outputPath);
|
||||||
|
printf("\t- OK: %s%s", $exporter->getOutputPath(), PHP_EOL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function version(): array
|
||||||
|
{
|
||||||
|
return ["Postman collection converter v" . self::VERSION];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function copyright(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Anthony Axenov (c) ' . date('Y') . ", MIT license",
|
||||||
|
'https://git.axenov.dev/anthony/pm-convert'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function usage(): array
|
||||||
|
{
|
||||||
|
return array_merge($this->version(), [
|
||||||
|
'Usage:',
|
||||||
|
"\t./pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]",
|
||||||
|
"\tphp pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]",
|
||||||
|
"\tcomposer pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]",
|
||||||
|
"\t./vendor/bin/pm-convert -f|-d PATH -o OUTPUT_PATH [ARGUMENTS] [FORMATS]",
|
||||||
|
'',
|
||||||
|
'Possible ARGUMENTS:',
|
||||||
|
"\t-f, --file - a PATH to single collection located in PATH to convert from",
|
||||||
|
"\t-d, --dir - a directory with collections located in COLLECTION_FILEPATH to convert from",
|
||||||
|
"\t-o, --output - a directory OUTPUT_PATH to put results in",
|
||||||
|
"\t-p, --preserve - do not delete OUTPUT_PATH (if exists)",
|
||||||
|
"\t-h, --help - show this help message and exit",
|
||||||
|
"\t-v, --version - show version info and exit",
|
||||||
|
'',
|
||||||
|
'If no ARGUMENTS passed then --help implied.',
|
||||||
|
'If both -c and -d are specified then only unique set of files will be converted.',
|
||||||
|
'-f or -d are required to be specified at least once, but each may be specified multiple times.',
|
||||||
|
'PATH must be a valid path to readable json-file or directory.',
|
||||||
|
'OUTPUT_PATH must be a valid path to writeable directory.',
|
||||||
|
'If -o is specified several times then only last one will be used.',
|
||||||
|
'',
|
||||||
|
'Possible FORMATS:',
|
||||||
|
"\t--http - generate raw *.http files (default)",
|
||||||
|
"\t--curl - generate shell scripts with curl command",
|
||||||
|
"\t--wget - generate shell scripts with wget command",
|
||||||
|
'If no FORMATS specified then --http implied.',
|
||||||
|
'Any of FORMATS can be specified at the same time.',
|
||||||
|
'',
|
||||||
|
'Example:',
|
||||||
|
" ./pm-convert \ ",
|
||||||
|
" -f ~/dir1/first.postman_collection.json \ ",
|
||||||
|
" --directory ~/team \ ",
|
||||||
|
" --file ~/dir2/second.postman_collection.json \ ",
|
||||||
|
" -d ~/personal \ ",
|
||||||
|
" -o ~/postman_export ",
|
||||||
|
"",
|
||||||
|
], $this->copyright());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user