1 Commits

Author SHA1 Message Date
0f95fab755 WIP 2026-04-08 17:40:06 +08:00
12 changed files with 620 additions and 360 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@
/.vscode
/vendor
/nbproject
/tmp
.phpunit.result.cache
.phpunit.cache

View File

@@ -12,7 +12,7 @@
],
"keywords": ["postman", "collection", "converter", "http", "wget", "curl", "api", "convert"],
"require": {
"php": "^8.2",
"php": "^8.4",
"ext-json": "*",
"ext-mbstring": "*",
"ext-readline": "*"
@@ -32,6 +32,6 @@
"sort-packages": true
},
"require-dev": {
"phpunit/phpunit": "^10.3"
"phpunit/phpunit": "^12.5"
}
}

728
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ use PmConverter\Handler;
const EOL = PHP_EOL;
const DS = DIRECTORY_SEPARATOR;
const PM_VERSION = '1.7';
const PM_VERSION = '1.8';
$paths = [
__DIR__ . '/../../autoload.php',

10
pm-convert-settings.json Normal file
View File

@@ -0,0 +1,10 @@
{
"directories": [
"tmp"
],
"output": "tmp/output",
"preserveOutput": false,
"formats": [
"opencollection"
]
}

View File

@@ -18,7 +18,7 @@ class ArgumentParser
/**
* @var array Parsed and ready to use
*/
protected array $parsed = [];
protected ?array $parsed = null;
/**
* Constructor
@@ -103,6 +103,11 @@ class ArgumentParser
$this->parsed[AN::Formats][] = ConvertFormat::Wget;
break;
case '--openc':
case '--opencollection':
$this->parsed[AN::Formats][] = ConvertFormat::OpenCollection;
break;
case '--v2.0':
$this->parsed[AN::Formats][] = ConvertFormat::Postman20;
break;
@@ -163,7 +168,7 @@ class ArgumentParser
}
}
return $this->parsed;
return $this->parsed ?? [];
}
/**

View File

@@ -118,7 +118,7 @@ class Collection implements Stringable
throw new Exception("cannot read file: $filepath");
}
$json = json_decode($content, true);
$json = json_decode($content, true, flags: JSON_THROW_ON_ERROR);
$schema = $json['info']['schema'] ?? '';
if (str_ends_with($schema, 'v2.0.0/collection.json')) {

View File

@@ -7,6 +7,7 @@ namespace PmConverter\Converters;
use PmConverter\Converters\Curl\CurlConverter;
use PmConverter\Converters\Http\HttpConverter;
use PmConverter\Converters\OpenCollection\OpenCollectionConverter;
use PmConverter\Converters\Postman20\Postman20Converter;
use PmConverter\Converters\Postman21\Postman21Converter;
use PmConverter\Converters\Wget\WgetConverter;
@@ -16,6 +17,7 @@ enum ConvertFormat: string
case Http = HttpConverter::class;
case Curl = CurlConverter::class;
case Wget = WgetConverter::class;
case OpenCollection = OpenCollectionConverter::class;
case Postman20 = Postman20Converter::class;
case Postman21 = Postman21Converter::class;
@@ -25,6 +27,7 @@ enum ConvertFormat: string
'http' => ConvertFormat::Http,
'curl' => ConvertFormat::Curl,
'wget' => ConvertFormat::Wget,
'opencollection' => ConvertFormat::OpenCollection,
'v2.0' => ConvertFormat::Postman20,
'v2.1' => ConvertFormat::Postman21,
};
@@ -36,6 +39,7 @@ enum ConvertFormat: string
ConvertFormat::Http => 'http',
ConvertFormat::Curl => 'curl',
ConvertFormat::Wget => 'wget',
ConvertFormat::OpenCollection => 'opencollection',
ConvertFormat::Postman20 => 'v2.0',
ConvertFormat::Postman21 => 'v2.1',
};

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace PmConverter\Converters\OpenCollection;
use PmConverter\Collection;
use PmConverter\Converters\Abstract\AbstractConverter;
use PmConverter\FileSystem;
class OpenCollectionConverter extends AbstractConverter
{
protected const FILE_EXT = 'yml';
protected const OUTPUT_DIR = 'opencollection';
protected const REQUEST_CLASS = OpenCollectionRequest::class;
/**
* @inheritDoc
*/
public function convert(Collection $collection): static
{
$this->collection = $collection;
$this->outputPath = FileSystem::makeDir($this->outputPath);
$this->setCollectionVars();
// Write main collection file
$this->writeCollectionFile($collection);
foreach ($collection->iterate() as $path => $item) {
$request = $this->makeRequest($item);
$this->writeRequest($request, $path);
}
return $this;
}
/**
* Writes main OpenCollection collection file
*
* @param Collection $collection
* @return void
*/
protected function writeCollectionFile(Collection $collection): void
{
$filepath = sprintf('%s%scollection.%s', $this->outputPath, DS, static::FILE_EXT);
$content = $this->generateCollectionContent($collection);
file_put_contents($filepath, $content);
}
/**
* Generates collection YAML content
*
* @param Collection $collection
* @return string
*/
protected function generateCollectionContent(Collection $collection): string
{
$output = [
'name: ' . ($collection->name ?? 'Untitled Collection'),
'type: collection',
'',
];
if (!empty($collection->info?->description)) {
$output[] = 'description: |';
$output[] = ' ' . str_replace("\n", "\n ", $collection->info->description);
$output[] = '';
}
$output[] = 'version: 1';
$output[] = '';
$output[] = 'baseVars: []';
$output[] = '';
$output[] = 'environments: []';
$output[] = '';
$output[] = 'requests:';
foreach ($collection->iterate() as $path => $item) {
$requestRef = str_replace(DS, '/', $path . '/' . $item->name);
$output[] = ' - $ref: ./' . $requestRef . '.yml';
}
return implode("\n", $output);
}
}

View File

@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
namespace PmConverter\Converters\OpenCollection;
use PmConverter\Converters\Abstract\AbstractRequest;
use PmConverter\Exceptions\EmptyHttpVerbException;
/**
* Class to determine file content with OpenCollection format
*/
class OpenCollectionRequest extends AbstractRequest
{
/**
* @inheritDoc
*/
protected function prepareDescription(): array
{
return empty($this->description)
? []
: ['description: |', ' ' . str_replace("\n", "\n ", $this->description), ''];
}
/**
* @inheritDoc
* @throws EmptyHttpVerbException
*/
protected function prepareHeaders(): array
{
$output = ['headers:'];
foreach ($this->headers as $name => $data) {
$prefix = $data['disabled'] ? '# ' : '';
$output[] = sprintf('%s %s: %s', $prefix, $name, $data['value']);
}
return $output;
}
/**
* @inheritDoc
*/
protected function prepareBody(): array
{
$output = ['body:'];
switch ($this->getBodymode()) {
case 'formdata':
$output[] = ' type: formdata';
$output[] = ' files:';
foreach ($this->body as $key => $data) {
$disabled = $data['disabled'] ?? false;
$prefix = $disabled ? '# ' : ' ';
if ($data['type'] === 'file') {
$output[] = sprintf('%s- name: %s', $prefix, $key);
$output[] = sprintf('%s type: file', $prefix);
$output[] = sprintf('%s value: %s', $prefix, $data['value']);
} else {
$output[] = sprintf('%s- name: %s', $prefix, $key);
$output[] = sprintf('%s type: text', $prefix);
$output[] = sprintf('%s value: %s', $prefix, $data['value']);
}
}
break;
default:
$output[] = ' type: raw';
if (!empty($this->body)) {
$output[] = ' content: |';
$bodyContent = is_string($this->body) ? $this->body : json_encode($this->body, JSON_PRETTY_PRINT);
foreach (explode("\n", $bodyContent) as $line) {
$output[] = ' ' . $line;
}
}
break;
}
return $output;
}
/**
* @inheritDoc
* @throws EmptyHttpVerbException
*/
public function __toString(): string
{
$output = [
'name: ' . $this->getName(),
'type: request',
'',
];
$output[] = 'method: ' . strtolower($this->getVerb());
$output[] = '';
$url = $this->getRawUrl();
$output[] = 'url: ' . $url;
$output[] = '';
$output = array_merge($output, $this->prepareDescription());
if (!empty($this->headers)) {
$output = array_merge($output, $this->prepareHeaders());
$output[] = '';
}
if ($this->verb !== 'GET' && !empty($this->body)) {
$output = array_merge($output, $this->prepareBody());
$output[] = '';
}
return implode("\n", $output);
}
}

View File

@@ -150,6 +150,7 @@ class Handler
"\t--http - generate raw *.http files (default)",
"\t--curl - generate shell scripts with curl command",
"\t--wget - generate shell scripts with wget command",
"\t--opencollection - generate OpenCollection YAML files",
"\t--v2.0 - convert from Postman Collection Schema v2.1 into v2.0",
"\t--v2.1 - convert from Postman Collection Schema v2.0 into v2.1",
"\t-a, --all - convert to all of formats listed above",

View File

@@ -82,18 +82,9 @@ class Settings
}
$filePath = trim($filePath);
if (!file_exists($filePath)) {
throw new Exception("file does not exist: $filePath");
}
if (!is_file($filePath)) {
throw new Exception("not a file: $filePath");
}
if (!is_readable($filePath)) {
throw new Exception("file is not readable: $filePath");
}
file_exists($filePath) || throw new Exception("file does not exist: $filePath");
is_file($filePath) || throw new Exception("not a file: $filePath");
is_readable($filePath) || throw new Exception("file is not readable: $filePath");
$content = file_get_contents($filePath);
$settings = json_decode($content ?: '{}', true, JSON_THROW_ON_ERROR);