diff --git a/README.md b/README.md index 59023ae..bad62de 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,13 @@ These formats are supported for now: `http`, `curl`, `wget`. ## Planned features +- conversion between postman schema v2.1 <-> v2.0 (#11); - 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); +- documentation generation support (markdown) with responce examples (if present) (#6); - maybe some another convert formats (like httpie or something...); - better logging; -- tests, phpcs, psalm, etc.; +- 90%+ test coverage, phpcs, psalm, etc.; - web version. ## Install and upgrade @@ -59,30 +60,40 @@ Usage: ./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 - -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 + -f, --file - a PATH to a single collection file to convert from + -d, --dir - a PATH to a directory with collections to convert from + -o, --output - a directory OUTPUT_PATH to put results in + -e, --env - use environment file with variables to replace in requests + --var "NAME=VALUE" - force replace specified env variable called NAME with custom VALUE + (see interpolation notes below) + -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 no ARGUMENTS passed then --help implied. -If both -f 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 from both arguments 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). +If -o or -e was specified several times then only last one will be used. 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. +Notes about variable interpolation: + 1. You can use -e to tell where to find variables to replace in requests. + 2. You can use one or several --var to replace specific env variables to your own value. + 3. Correct syntax is `--var "NAME=VALUE". NAME may be in curly braces like {{NAME}}. + 4. Since -e is optional, a bunch of --var will emulate an environment. Also it does not + matter if there is --var in environment file you provided or not. + 5. Even if you (not) provided -e and/or --var, any of variable may still be overridden + from collection (if any), so last ones has top priority. + Example: ./pm-convert \ -f ~/dir1/first.postman_collection.json \ @@ -90,7 +101,9 @@ Example: --file ~/dir2/second.postman_collection.json \ --env ~/localhost.postman_environment.json \ -d ~/personal \ - -o ~/postman_export + --var "myvar=some value" \ + -o ~/postman_export + ``` ### Notices diff --git a/src/Converters/Abstract/AbstractConverter.php b/src/Converters/Abstract/AbstractConverter.php index 12a7c45..d049fe1 100644 --- a/src/Converters/Abstract/AbstractConverter.php +++ b/src/Converters/Abstract/AbstractConverter.php @@ -27,11 +27,6 @@ abstract class AbstractConverter implements ConverterContract */ protected string $outputPath; - /** - * @var string[] - */ - protected array $vars = []; - /** * @var Environment|null */ @@ -72,9 +67,10 @@ abstract class AbstractConverter implements ConverterContract */ protected function setVariables(): static { - if (isset($this->collection?->variable)) { + empty($this->env) && $this->env = new Environment($this->collection?->variable); + if (!empty($this->collection?->variable)) { foreach ($this->collection->variable as $var) { - $this->vars["{{{$var->key}}}"] = $var->value; + $this->env[$var->key] = $var->value; } } return $this; @@ -176,21 +172,18 @@ abstract class AbstractConverter implements ConverterContract */ protected function interpolate(string $content): string { - if (empty($this->vars) && !$this->env?->hasVars()) { + if (!$this->env?->hasVars()) { return $content; } $matches = []; - if (preg_match_all('/\{\{[a-zA-Z][a-zA-Z0-9]*}}/m', $content, $matches, PREG_PATTERN_ORDER) > 0) { + 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->vars[$var] ?? $this->env[$var] ?? $var, $content); + $content = str_replace($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/Environment.php b/src/Environment.php index c0c76da..b0d1164 100644 --- a/src/Environment.php +++ b/src/Environment.php @@ -17,7 +17,7 @@ class Environment implements \ArrayAccess public function __construct(protected object $env) { foreach ($env->values as $var) { - $this->vars["{{{$var->key}}}"] = $var->value; + $this->vars[static::formatKey($var->key)] = $var->value; } } @@ -36,7 +36,7 @@ class Environment implements \ArrayAccess */ public function offsetExists(mixed $offset): bool { - return array_key_exists($offset, $this->vars); + return array_key_exists(static::formatKey($offset), $this->vars); } /** @@ -44,7 +44,7 @@ class Environment implements \ArrayAccess */ public function offsetGet(mixed $offset): mixed { - return $this->vars[$offset]; + return $this->vars[static::formatKey($offset)] ?? null; } /** @@ -52,7 +52,7 @@ class Environment implements \ArrayAccess */ public function offsetSet(mixed $offset, mixed $value): void { - $this->vars[$offset] = $value; + $this->vars[static::formatKey($offset)] = $value; } /** @@ -60,6 +60,17 @@ class Environment implements \ArrayAccess */ public function offsetUnset(mixed $offset): void { - unset($this->vars[$offset]); + unset($this->vars[static::formatKey($offset)]); + } + + /** + * Returns correct variable {{name}} + * + * @param string $key + * @return string + */ + public static function formatKey(string $key): string + { + return sprintf('{{%s}}', str_replace(['{', '}'], '', $key)); } } diff --git a/src/Processor.php b/src/Processor.php index dea94af..ac9e22e 100644 --- a/src/Processor.php +++ b/src/Processor.php @@ -38,6 +38,11 @@ class Processor */ protected bool $preserveOutput = false; + /** + * @var string[] Additional variables + */ + protected array $vars; + /** * @var ConvertFormat[] Formats to convert a collections into */ @@ -64,7 +69,7 @@ class Processor protected int $initRam; /** - * @var string + * @var string Path to environment file */ protected string $envFile; @@ -151,6 +156,11 @@ class Processor $this->formats[ConvertFormat::Wget->name] = ConvertFormat::Wget; break; + case '--var': + [$var, $value] = explode('=', trim($this->argv[$idx + 1])); + $this->vars[$var] = $value; + break; + case '-v': case '--version': die(implode(PHP_EOL, $this->version()) . PHP_EOL); @@ -198,6 +208,7 @@ class Processor foreach ($this->formats as $type) { $this->converters[$type->name] = new $type->value($this->preserveOutput); } + unset($this->formats); } /** @@ -215,6 +226,7 @@ class Processor } $this->collections[$content->collection->info->name] = $content->collection; } + unset($this->collectionPaths, $content); } /** @@ -234,6 +246,10 @@ class Processor 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); } /** @@ -267,6 +283,7 @@ class Processor print(PHP_EOL); ++$success; } + unset($this->converters, $type, $exporter, $outputPath, $this->collections, $collectionName, $collection); $this->printStats($success, $current); } @@ -281,7 +298,7 @@ class Processor { $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%s', $success, $count, $time, $ram, PHP_EOL); + printf('Converted %d of %d in %.3f ms using up to %.3f MiB RAM%s', $success, $count, $time, $ram, PHP_EOL); } /** @@ -316,22 +333,22 @@ class Processor "\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 PATH 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", + "\t-f, --file - a PATH to a single collection file to convert from", + "\t-d, --dir - a PATH to a directory with collections to convert from", + "\t-o, --output - a directory OUTPUT_PATH to put results in", + "\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 (see interpolation notes below)", + "\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 -f 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 from both arguments 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).', + 'If -o or -e was specified several times then only last one will be used.', '', 'Possible FORMATS:', "\t--http - generate raw *.http files (default)", @@ -341,6 +358,15 @@ class Processor 'If no FORMATS specified then --http implied.', 'Any of FORMATS can be specified at the same time.', '', + 'Notes about variable interpolation:', + "\t1. You can use -e to tell where to find variables to replace in requests.", + "\t2. You can use one or several --var to replace specific env variables to your own value.", + "\t3. Correct syntax is `--var \"NAME=VALUE\". NAME may be in curly braces like {{NAME}}.", + "\t4. Since -e is optional, a bunch of --var will emulate an environment. Also it does not", + "\t matter if there is --var in environment file you provided or not.", + "\t5. Even if you (not) provided -e and/or --var, any of variable may still be overridden", + "\t from collection (if any), so last ones has top priority.", + '', 'Example:', " ./pm-convert \ ", " -f ~/dir1/first.postman_collection.json \ ", @@ -348,6 +374,7 @@ class Processor " --file ~/dir2/second.postman_collection.json \ ", " --env ~/localhost.postman_environment.json \ ", " -d ~/personal \ ", + " --var \"myvar=some value\" \ ", " -o ~/postman_export ", "", ], $this->copyright());