From 9947987c20ab34baf5c0fa5c7ffaa86edec9a397 Mon Sep 17 00:00:00 2001 From: Anthony Axenov Date: Thu, 10 Aug 2023 15:43:18 +0800 Subject: [PATCH 1/4] Some refactorings in content generation --- src/Exporters/Abstract/AbstractConverter.php | 2 +- src/Exporters/Abstract/AbstractRequest.php | 46 ++++++++-- src/Exporters/Curl/CurlConverter.php | 3 +- src/Exporters/Curl/CurlRequest.php | 88 ++++++++++++-------- src/Exporters/Http/HttpConverter.php | 3 +- src/Exporters/Http/HttpRequest.php | 69 +++++++++------ src/Exporters/Wget/WgetConverter.php | 3 +- src/Exporters/Wget/WgetRequest.php | 83 +++++++++++------- 8 files changed, 194 insertions(+), 103 deletions(-) diff --git a/src/Exporters/Abstract/AbstractConverter.php b/src/Exporters/Abstract/AbstractConverter.php index 307f7ae..b3351ec 100644 --- a/src/Exporters/Abstract/AbstractConverter.php +++ b/src/Exporters/Abstract/AbstractConverter.php @@ -82,7 +82,7 @@ abstract class AbstractConverter */ protected function initRequest(object $item): RequestContract { - $request_class = static::REQUEST; + $request_class = static::REQUEST_CLASS; $result = new $request_class(); $result->setName($item->name); $result->setDescription($item->request?->description ?? null); diff --git a/src/Exporters/Abstract/AbstractRequest.php b/src/Exporters/Abstract/AbstractRequest.php index 365a2ab..5049461 100644 --- a/src/Exporters/Abstract/AbstractRequest.php +++ b/src/Exporters/Abstract/AbstractRequest.php @@ -8,14 +8,14 @@ use PmConverter\Exporters\Http\HttpRequest; use PmConverter\Exporters\RequestContract; /** - * + * Class to determine file content with any request format */ abstract class AbstractRequest implements RequestContract { /** * @var string */ - protected string $http = 'HTTP/1.1'; //TODO verb + protected string $http = 'HTTP/1.1'; //TODO proto /** * @var string @@ -53,6 +53,8 @@ abstract class AbstractRequest implements RequestContract protected string $url; /** + * Sets name from collection item to request object + * * @param string $name * @return HttpRequest */ @@ -63,6 +65,8 @@ abstract class AbstractRequest implements RequestContract } /** + * Returns name of request + * * @return string */ public function getName(): string @@ -71,6 +75,8 @@ abstract class AbstractRequest implements RequestContract } /** + * Sets description from collection item to request object + * * @param string|null $description * @return HttpRequest */ @@ -81,6 +87,8 @@ abstract class AbstractRequest implements RequestContract } /** + * Sets HTTP verb from collection item to request object + * * @param string $verb * @return HttpRequest */ @@ -91,6 +99,8 @@ abstract class AbstractRequest implements RequestContract } /** + * Sets URL from collection item to request object + * * @param string $url * @return HttpRequest */ @@ -101,6 +111,8 @@ abstract class AbstractRequest implements RequestContract } /** + * Sets headers from collection item to request object + * * @param object[]|null $headers * @return $this */ @@ -116,6 +128,8 @@ abstract class AbstractRequest implements RequestContract } /** + * Sets body mode from collection item to request object + * * @param string $bodymode * @return HttpRequest */ @@ -126,8 +140,10 @@ abstract class AbstractRequest implements RequestContract } /** - * @param string $body - * @return HttpRequest + * Sets body from collection item to request object + * + * @param object $body + * @return $this */ public function setBody(object $body): static { @@ -153,11 +169,29 @@ abstract class AbstractRequest implements RequestContract } /** - * @return string + * Returns array of description lines + * + * @return array */ - abstract protected function prepareBody(): ?string; + abstract protected function prepareDescription(): array; /** + * Returns array of headers + * + * @return array + */ + abstract protected function prepareHeaders(): array; + + /** + * Returns array of request body lines + * + * @return array + */ + abstract protected function prepareBody(): array; + + /** + * Converts request object to string to be written in result file + * * @return string */ abstract public function __toString(): string; diff --git a/src/Exporters/Curl/CurlConverter.php b/src/Exporters/Curl/CurlConverter.php index 0415eb3..88e2d22 100644 --- a/src/Exporters/Curl/CurlConverter.php +++ b/src/Exporters/Curl/CurlConverter.php @@ -11,7 +11,8 @@ use PmConverter\Exporters\{ class CurlConverter extends AbstractConverter implements ConverterContract { protected const FILE_EXT = 'sh'; + protected const OUTPUT_DIR = 'curl'; - protected const REQUEST = CurlRequest::class; + protected const REQUEST_CLASS = CurlRequest::class; } diff --git a/src/Exporters/Curl/CurlRequest.php b/src/Exporters/Curl/CurlRequest.php index 6b9dc0b..fe7fe71 100644 --- a/src/Exporters/Curl/CurlRequest.php +++ b/src/Exporters/Curl/CurlRequest.php @@ -7,59 +7,77 @@ namespace PmConverter\Exporters\Curl; use PmConverter\Exporters\Abstract\AbstractRequest; /** - * + * Class to determine file content with curl request format */ class CurlRequest extends AbstractRequest { /** - * @return string + * @inheritDoc */ - protected function prepareBody(): ?string + protected function prepareDescription(): array { - 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 empty($this->description) + ? [] + : ['# ' . str_replace("\n", "\n# ", $this->description), '']; } /** - * @return string + * @inheritDoc */ - public function __toString(): string + protected function prepareHeaders(): array { - $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 \ "; + $output = []; 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'", - }; + return $output; + } + + /** + * @inheritDoc + */ + protected function prepareBody(): array + { + $output = []; + switch ($this->bodymode) { + case 'formdata': + foreach ($this->body as $data) { + $output[] = sprintf( + "%s\t--form '%s=%s' \ ", + isset($data->disabled) ? '# ' : '', + $data->key, + $data->type === 'file' ? "@$data->src" : $data->value + ); + } + break; + default: + $output = ["\t--data '$this->body'"]; + break; } + return $output; + } + + /** + * @inheritDoc + */ + public function __toString(): string + { + $output = array_merge( + ['#!/bin/sh'], + $this->prepareDescription(), + [ + "curl \ ", + "\t--http1.1 \ ", //TODO proto + "\t--request $this->verb \ ", + "\t--location $this->url \ ", + ], + $this->prepareHeaders(), + $this->prepareBody() + ); $output[] = rtrim(array_pop($output), '\ '); - return implode(PHP_EOL, $output); + return implode(PHP_EOL, array_merge($output, [''])); } } diff --git a/src/Exporters/Http/HttpConverter.php b/src/Exporters/Http/HttpConverter.php index 1a4ac42..73dc042 100644 --- a/src/Exporters/Http/HttpConverter.php +++ b/src/Exporters/Http/HttpConverter.php @@ -11,7 +11,8 @@ use PmConverter\Exporters\{ class HttpConverter extends AbstractConverter implements ConverterContract { protected const FILE_EXT = 'http'; + protected const OUTPUT_DIR = 'http'; - protected const REQUEST = HttpRequest::class; + protected const REQUEST_CLASS = HttpRequest::class; } diff --git a/src/Exporters/Http/HttpRequest.php b/src/Exporters/Http/HttpRequest.php index 62cd108..adc4118 100644 --- a/src/Exporters/Http/HttpRequest.php +++ b/src/Exporters/Http/HttpRequest.php @@ -7,47 +7,64 @@ namespace PmConverter\Exporters\Http; use PmConverter\Exporters\Abstract\AbstractRequest; /** - * + * Class to determine file content with http request format */ class HttpRequest extends AbstractRequest { /** - * @return string + * @inheritDoc */ - protected function prepareBody(): ?string + protected function prepareDescription(): array { - 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 empty($this->description) + ? [] + : ['# ' . str_replace("\n", "\n# ", $this->description), '']; } /** - * @return string + * @inheritDoc */ - public function __toString(): string + protected function prepareHeaders(): array { - 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 $output; + } + + /** + * @inheritDoc + */ + protected function prepareBody(): array + { + switch ($this->bodymode) { + case 'formdata': + $output = ['']; + foreach ($this->body as $data) { + $output[] = sprintf( + '%s%s=%s', + empty($data->disabled) ? '' : '# ', + $data->key, + $data->type === 'file' ? $data->src : $data->value + ); + } + return $output; + default: + return ['', $this->body]; + } + } + + /** + * @inheritDoc + */ + public function __toString(): string + { + $output = array_merge( + $this->prepareDescription(), + $this->prepareHeaders(), + $this->prepareBody() + ); return implode(PHP_EOL, $output); } } diff --git a/src/Exporters/Wget/WgetConverter.php b/src/Exporters/Wget/WgetConverter.php index 064a7f7..8217519 100644 --- a/src/Exporters/Wget/WgetConverter.php +++ b/src/Exporters/Wget/WgetConverter.php @@ -11,7 +11,8 @@ use PmConverter\Exporters\{ class WgetConverter extends AbstractConverter implements ConverterContract { protected const FILE_EXT = 'sh'; + protected const OUTPUT_DIR = 'wget'; - protected const REQUEST = WgetRequest::class; + protected const REQUEST_CLASS = WgetRequest::class; } diff --git a/src/Exporters/Wget/WgetRequest.php b/src/Exporters/Wget/WgetRequest.php index 48bcff7..f5023fb 100644 --- a/src/Exporters/Wget/WgetRequest.php +++ b/src/Exporters/Wget/WgetRequest.php @@ -7,57 +7,76 @@ namespace PmConverter\Exporters\Wget; use PmConverter\Exporters\Abstract\AbstractRequest; /** - * + * Class to determine file content with wget request format */ class WgetRequest extends AbstractRequest { /** - * @return string + * @inheritDoc */ - protected function prepareBody(): ?string + protected function prepareDescription(): array { - 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 empty($this->description) + ? [] + : ['# ' . str_replace("\n", "\n# ", $this->description), '']; } /** - * @return string + * @inheritDoc */ - public function __toString(): string + protected function prepareHeaders(): array { - $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 \ "; + $output = []; 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' \ "; + return $output; + } + + /** + * @inheritDoc + */ + protected function prepareBody(): array + { + switch ($this->bodymode) { + case 'formdata': + $params = []; + foreach ($this->body as $data) { + if ($data->type === 'file') { + continue; + } + $params[$data->key] = $data->value; + } + $output[] = http_build_query($params); + return $output; + default: + return ["\t$this->body"]; } + } + + /** + * @inheritDoc + */ + public function __toString(): string + { + $output = array_merge( + ['#!/bin/sh'], + $this->prepareDescription(), + [ + 'wget \ ', + "\t--no-check-certificate \ ", + "\t--quiet \ ", + "\t--timeout=0 \ ", + "\t--method $this->verb \ ", + ], + $this->prepareHeaders(), + $this->prepareBody() + ); $output[] = rtrim(array_pop($output), '\ '); $output[] = "\t'$this->url'"; - return implode(PHP_EOL, $output); + return implode(PHP_EOL, array_merge($output, [''])); } } -- 2.46.GIT From c630af8795ed92fba8a77199eb34a149b7d37c6a Mon Sep 17 00:00:00 2001 From: Anthony Axenov Date: Thu, 10 Aug 2023 15:44:21 +0800 Subject: [PATCH 2/4] Little output edit to prepare to some in-progress messages --- src/Processor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Processor.php b/src/Processor.php index 3a39e0c..87d8d92 100644 --- a/src/Processor.php +++ b/src/Processor.php @@ -218,10 +218,10 @@ class Processor ++$current; printf("Converting '%s' (%d/%d):%s", $collectionName, $current, $count, PHP_EOL); foreach ($this->converters as $type => $exporter) { - printf(' > %s', strtolower($type)); + printf('> %s%s', strtolower($type), PHP_EOL); $outputPath = sprintf('%s%s%s', $this->outputPath, DIRECTORY_SEPARATOR, $collectionName); $exporter->convert($collection, $outputPath); - printf(' - OK: %s%s', $exporter->getOutputPath(), PHP_EOL); + printf(' OK: %s%s', $exporter->getOutputPath(), PHP_EOL); } print(PHP_EOL); ++$success; -- 2.46.GIT From c4961b1238634a61ef61100bf665fc1b4aac0c0d Mon Sep 17 00:00:00 2001 From: Anthony Axenov Date: Sun, 13 Aug 2023 10:26:22 +0800 Subject: [PATCH 3/4] Several cool features - replace vars from collection by default - replace vars from environment file with -e - bearer auth header --- README.md | 54 ++++++------ src/Environment.php | 65 +++++++++++++++ src/Exporters/Abstract/AbstractConverter.php | 88 +++++++++++++++++++- src/Exporters/Abstract/AbstractRequest.php | 24 ++++++ src/Exporters/RequestContract.php | 1 + src/Processor.php | 61 ++++++++++++-- 6 files changed, 257 insertions(+), 36 deletions(-) create mode 100644 src/Environment.php diff --git a/README.md b/README.md index 85a1fbc..3e25ba1 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ These formats are supported for now: `http`, `curl`, `wget`. ## Supported features * [collection schema **v2.1**](https://schema.postman.com/json/collection/v2.1.0/collection.json); +* `Bearer` auth; +* replace vars in requests by stored in collection and environment file; * export one or several collections (or even whole directories) into one or all of formats supported at the same time; * all headers (including disabled for `http`-format); * `json` body (forces header `Content-Type` to `application/json`); @@ -22,12 +24,10 @@ These formats are supported for now: `http`, `curl`, `wget`. ## Planned features -- support as many as possible/necessary of authentication kinds (_currently no ones_); +- 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`_); - 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; - better logging; - tests, phpcs, psalm, etc.; - web version. @@ -52,39 +52,45 @@ export PATH="$PATH:~/.config/composer/vendor/bin" $ 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] + ./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 + -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 + -e, --env - use environment file with variable values to replace in request + -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. +If no ARGUMENTS passed then --help implied. +If both -f 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. +If -o is specified several times then only last one will be used. +If -e is specified several times then only last one will be used. +If -e is not specified then only collection vars will be replaced (if any). Possible FORMATS: - --http - generate raw *.http files (default) - --curl - generate shell scripts with curl command - --wget - generate shell scripts with wget command + --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 + ./pm-convert \ + -f ~/dir1/first.postman_collection.json \ + --directory ~/team \ + --file ~/dir2/second.postman_collection.json \ + --env ~/localhost.postman_environment.json \ + -d ~/personal \ + -o ~/postman_export + ``` ### Notice diff --git a/src/Environment.php b/src/Environment.php new file mode 100644 index 0000000..c0c76da --- /dev/null +++ b/src/Environment.php @@ -0,0 +1,65 @@ +values as $var) { + $this->vars["{{{$var->key}}}"] = $var->value; + } + } + + /** + * Tells if there are some vars or not + * + * @return bool + */ + public function hasVars(): bool + { + return !empty($this->vars); + } + + /** + * @inheritDoc + */ + public function offsetExists(mixed $offset): bool + { + return array_key_exists($offset, $this->vars); + } + + /** + * @inheritDoc + */ + public function offsetGet(mixed $offset): mixed + { + return $this->vars[$offset]; + } + + /** + * @inheritDoc + */ + public function offsetSet(mixed $offset, mixed $value): void + { + $this->vars[$offset] = $value; + } + + /** + * @inheritDoc + */ + public function offsetUnset(mixed $offset): void + { + unset($this->vars[$offset]); + } +} diff --git a/src/Exporters/Abstract/AbstractConverter.php b/src/Exporters/Abstract/AbstractConverter.php index b3351ec..2c7f620 100644 --- a/src/Exporters/Abstract/AbstractConverter.php +++ b/src/Exporters/Abstract/AbstractConverter.php @@ -5,14 +5,16 @@ declare(strict_types=1); namespace PmConverter\Exporters\Abstract; use Exception; +use PmConverter\Environment; use PmConverter\Exporters\{ + ConverterContract, RequestContract}; use PmConverter\FileSystem; /** * */ -abstract class AbstractConverter +abstract class AbstractConverter implements ConverterContract { /** * @var object|null @@ -25,6 +27,30 @@ abstract class AbstractConverter protected string $outputPath; /** + * @var string[] + */ + protected array $vars = []; + + /** + * @var Environment + */ + protected Environment $env; + + /** + * Sets an environment with vars + * + * @param Environment $env + * @return $this + */ + public function withEnv(Environment $env): static + { + $this->env = $env; + return $this; + } + + /** + * Converts collection requests + * * @throws Exception */ public function convert(object $collection, string $outputPath): void @@ -32,12 +58,30 @@ abstract class AbstractConverter $outputPath = sprintf('%s%s%s', $outputPath, DIRECTORY_SEPARATOR, static::OUTPUT_DIR); $this->outputPath = FileSystem::makeDir($outputPath); $this->collection = $collection; + $this->setVariables(); foreach ($collection->item as $item) { $this->convertItem($item); } } /** + * Prepares collection variables + * + * @return $this + */ + protected function setVariables(): static + { + if ($this->collection->variable) { + foreach ($this->collection->variable as $var) { + $this->vars["{{{$var->key}}}"] = $var->value; + } + } + return $this; + } + + /** + * Returns output path + * * @return string */ public function getOutputPath(): string @@ -46,6 +90,8 @@ abstract class AbstractConverter } /** + * Checks whether item contains another items or not + * * @param object $item * @return bool */ @@ -55,6 +101,8 @@ abstract class AbstractConverter } /** + * Converts an item to request object and writes it into file + * * @throws Exception */ protected function convertItem(mixed $item): void @@ -77,18 +125,23 @@ abstract class AbstractConverter } /** + * Initialiazes request object to be written in file + * * @param object $item * @return RequestContract */ protected function initRequest(object $item): RequestContract { $request_class = static::REQUEST_CLASS; - $result = new $request_class(); + + /** @var RequestContract $result */ + $result = new $request_class($this->vars); $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); + $result->setAuth($item->request?->auth ?? $this->collection?->auth ?? null); if ($item->request->method !== 'GET' && !empty($item->request->body)) { $result->setBody($item->request->body); } @@ -96,6 +149,8 @@ abstract class AbstractConverter } /** + * Writes converted request object to file + * * @param RequestContract $request * @param string|null $subpath * @return bool @@ -106,6 +161,33 @@ abstract class AbstractConverter $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; + $content = $this->interpolate((string)$request); + return file_put_contents($filepath, $content) > 0; + } + + /** + * Replaces variables in request with values from collection or environment + * + * @param string $content + * @return string + */ + protected function interpolate(string $content): string + { + if (empty($this->vars) && $this->env->hasVars()) { + return $content; + } + $matches = []; + if (preg_match_all('/\{\{[a-zA-Z][a-zA-Z0-9]+}}/', $content, $matches, PREG_PATTERN_ORDER) > 0) { + foreach ($matches[0] as $key => $var) { + if (str_contains($content, $var)) { + $content = str_replace($var, $this->vars[$var] ?? $this->env[$var] ?? $var, $content); + unset($matches[0][$key]); + } + } + } + // if (!empty($matches[0])) { + // fwrite(STDERR, sprintf(' No values found: %s%s', implode(', ', $matches[0]), PHP_EOL)); + // } + return $content; } } diff --git a/src/Exporters/Abstract/AbstractRequest.php b/src/Exporters/Abstract/AbstractRequest.php index 5049461..dcb7cd4 100644 --- a/src/Exporters/Abstract/AbstractRequest.php +++ b/src/Exporters/Abstract/AbstractRequest.php @@ -127,6 +127,30 @@ abstract class AbstractRequest implements RequestContract return $this; } + /** + * Sets authorization headers + * + * @param object|null $auth + * @return $this + */ + public function setAuth(?object $auth): static + { + if (!empty($auth)) { + switch ($auth->type) { + case 'bearer': + $this->headers['Authorization'] = [ + 'value' => 'Bearer ' . $auth->{$auth->type}[0]->value, + 'disabled' => false, + ]; + break; + default: + break; + } + } + return $this; + } + + /** * Sets body mode from collection item to request object * diff --git a/src/Exporters/RequestContract.php b/src/Exporters/RequestContract.php index 7fb4aa7..b0c277e 100644 --- a/src/Exporters/RequestContract.php +++ b/src/Exporters/RequestContract.php @@ -11,6 +11,7 @@ interface RequestContract public function setDescription(?string $description): static; public function setVerb(string $verb): static; public function setUrl(string $url): static; + public function setAuth(object $auth): static; public function setHeaders(?array $headers): static; public function setBodymode(string $bodymode): static; public function setBody(object $body): static; diff --git a/src/Processor.php b/src/Processor.php index 87d8d92..7ab4547 100644 --- a/src/Processor.php +++ b/src/Processor.php @@ -57,12 +57,22 @@ class Processor /** * @var int Initial timestamp */ - protected int $init_time; + protected int $initTime; /** * @var int Initial RAM usage */ - protected int $init_ram; + protected int $initRam; + + /** + * @var string + */ + protected string $envFile; + + /** + * @var Environment + */ + protected Environment $env; /** * Constructor @@ -71,8 +81,8 @@ class Processor */ public function __construct(protected array $argv) { - $this->init_time = hrtime(true); - $this->init_ram = memory_get_usage(true); + $this->initTime = hrtime(true); + $this->initRam = memory_get_usage(true); } /** @@ -115,14 +125,19 @@ class Processor if (empty($this->argv[$idx + 1])) { throw new InvalidArgumentException('a directory path is expected for -d (--dir)'); } - $normpath = $this->argv[$idx + 1]; + $rawpath = $this->argv[$idx + 1]; $files = array_filter( - FileSystem::dirContents($normpath), + FileSystem::dirContents($rawpath), static fn($filename) => FileSystem::isCollectionFile($filename) ); $this->collectionPaths = array_unique(array_merge($this?->collectionPaths ?? [], $files)); break; + case '-e': + case '--env': + $this->envFile = FileSystem::normalizePath($this->argv[$idx + 1]); + break; + case '-p': case '--preserve': $this->preserveOutput = true; @@ -158,6 +173,8 @@ class Processor } /** + * Initializes output directory + * * @return void * @throws CannotCreateDirectoryException * @throws DirectoryIsNotWriteableException @@ -185,6 +202,8 @@ class Processor } /** + * Initializes collection objects + * * @throws JsonException */ protected function initCollections(): void @@ -199,6 +218,22 @@ class Processor } } + /** + * Initializes environment object + * + * @return void + * @throws JsonException + */ + protected function initEnv(): void + { + $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); + } + /** * Begins a conversion * @@ -210,6 +245,7 @@ class Processor $this->initOutputDirectory(); $this->initConverters(); $this->initCollections(); + $this->initEnv(); $count = count($this->collections); $current = 0; $success = 0; @@ -220,6 +256,9 @@ class Processor foreach ($this->converters as $type => $exporter) { printf('> %s%s', strtolower($type), PHP_EOL); $outputPath = sprintf('%s%s%s', $this->outputPath, DIRECTORY_SEPARATOR, $collectionName); + if (!empty($this->env)) { + $exporter->withEnv($this->env); + } $exporter->convert($collection, $outputPath); printf(' OK: %s%s', $exporter->getOutputPath(), PHP_EOL); } @@ -238,8 +277,8 @@ class Processor */ protected function printStats(int $success, int $count): void { - $time = (hrtime(true) - $this->init_time) / 1_000_000; - $ram = (memory_get_peak_usage(true) - $this->init_ram) / 1024 / 1024; + $time = (hrtime(true) - $this->initTime) / 1_000_000; + $ram = (memory_get_peak_usage(true) - $this->initRam) / 1024 / 1024; printf('Converted %d of %d in %.3f ms using %.3f MiB RAM', $success, $count, $time, $ram); } @@ -278,16 +317,19 @@ class Processor "\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-e, --env - use environment file with variable values to replace in request", "\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.', + 'If both -f 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.', + 'If -e is specified several times then only last one will be used.', + 'If -e is not specified then only collection vars will be replaced (if any).', '', 'Possible FORMATS:', "\t--http - generate raw *.http files (default)", @@ -301,6 +343,7 @@ class Processor " -f ~/dir1/first.postman_collection.json \ ", " --directory ~/team \ ", " --file ~/dir2/second.postman_collection.json \ ", + " --env ~/localhost.postman_environment.json \ ", " -d ~/personal \ ", " -o ~/postman_export ", "", -- 2.46.GIT From 40cc852328d32f9b8ccbf8ce2be158437b9c9123 Mon Sep 17 00:00:00 2001 From: Anthony Axenov Date: Sun, 13 Aug 2023 10:26:46 +0800 Subject: [PATCH 4/4] Bump version --- src/Processor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Processor.php b/src/Processor.php index 7ab4547..b99a4d4 100644 --- a/src/Processor.php +++ b/src/Processor.php @@ -22,7 +22,7 @@ class Processor /** * Converter version */ - public const VERSION = '1.1.1'; + public const VERSION = '1.2.0'; /** * @var string[] Paths to collection files -- 2.46.GIT