[PHP] Simple Postman Collection to http converter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/php | |
<?php | |
/*********************************************************************************************************************** | |
* Simple Postman Collection Converter | |
* | |
* Author: Anthony Axenov (c) 2021 | |
* Version: v1.1 | |
* License: MIT | |
* Dependecies: php8.0, php-json | |
* | |
* This script converts Postman Collection v2.1 to http-files: | |
* 1) builds same directory tree as in collection | |
* 2) builds ready to run *.http files (except you use Postman's environment vars) | |
* 3) builds *.md file with description and examples | |
* | |
* Usage: | |
* | |
* 1) Export collection from Postman as described here using 'Collection v2.1': | |
* https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections | |
* | |
* 2) Run: | |
* | |
* php convert.php /path/to/collection.json | |
* | |
* 3) Optional: you can make script executable first and then run as usual: | |
* | |
* sudo chmod a+x convert.php | |
* ./convert.php /path/to/collection.json | |
* | |
* Note: '~' alias is allowed, it will be replaced to current user home dir. | |
* | |
* Note: this script was quickly written to solve one exact problem in one NDA-project, so it may contain | |
* stupid errors and (for sure) doesn't cover ALL of possible cases according to collection scheme. So feel | |
* free to fork and change this script according your needs and to propose your fixes here. | |
*********************************************************************************************************************** | |
* Version history: | |
* | |
* v1.0: initial | |
* v1.1: improved output directory structure | |
* | |
**********************************************************************************************************************/ | |
declare(strict_types = 1); | |
/** | |
* Writes all arguments in stdout in proper human-readable format | |
* | |
* @param mixed ...$data | |
* @return void | |
* @see https://gist.github.com/anthonyaxenov/6eafac478528ac6d300d8f9b4460c286 output() | |
*/ | |
function out(mixed ...$data): void | |
{ | |
$result = []; | |
foreach ($data as $something) { | |
is_array($something) && $something = var_export($something, true); | |
empty($something) && $something = ''; | |
if (is_object($something)) { | |
if (method_exists($something, 'toArray')) { | |
$something = $something->toArray(); | |
} elseif (method_exists($something, 'jsonSerialize')) { | |
$something = $something->jsonSerialize(); | |
} else { | |
$something = var_export($something, true); | |
} | |
} | |
$result[] = $something; | |
} | |
printf('[' . date('H:i:s') . '] ' . implode(' ', $result) . PHP_EOL); | |
} | |
/** | |
* Read Postman collection file | |
* | |
* @param string $path Path to file (`~` allowed) | |
* @return object JSON-decoded object | |
* @throws Exception | |
*/ | |
function read_collection_file(string $path): object | |
{ | |
$content = file_get_contents(str_replace('~/', $_SERVER['HOME'] . '/', $path)); | |
$json = json_decode($content); | |
return json_last_error() === JSON_ERROR_NONE | |
? $json | |
: throw new Exception( | |
"File '$path' does not contain a valid json. Error: [" . json_last_error() . "] " . json_last_error_msg() | |
); | |
} | |
/** | |
* Writes data from $request as *.http-file into $dir_tree | |
* | |
* @param object $request Request data | |
* @param string|null $dir_tree Directory path | |
* @return void | |
*/ | |
function write_request(object $request, ?string $dir_tree = null) | |
{ | |
$filepath = OUTPUT_DIR . '/' . ($dir_tree ? $dir_tree . '/' : '') . $request->name; | |
$httpfile = "$filepath.http"; | |
$mdfile = "$filepath.md"; | |
// name | |
//file_put_contents($httpfile, "# $request->name\n\n", FILE_APPEND); | |
file_put_contents($mdfile, "# $request->name\n\n", FILE_APPEND); | |
// description (if exists) | |
isset($request->request->description) && /*file_put_contents( | |
$httpfile, | |
'# ' . str_replace("\n", "\n# ", $request->request->description) . "\n\n", | |
FILE_APPEND | |
) &&*/ | |
file_put_contents($mdfile, "{$request->request->description}\n\n", FILE_APPEND); | |
// generating request data | |
$url = "{$request->request->method} {$request->request->url->raw} HTTP/1.1"; | |
$headers = []; | |
$body = ''; | |
if (count($request->request->header) > 0) { | |
foreach ($request->request->header as $header) { | |
$headers[(empty($header->disabled) ? '' : '# ') . $header->key] = $header->value; | |
} | |
} | |
if (isset($request->request->body)) { | |
$bodymode = $request->request->body->mode; | |
if (($request->request->body->options->$bodymode->language ?? '') === 'json') { | |
empty($headers['Content-Type']) && $headers['Content-Type'] = 'application/json'; | |
empty($headers['Accept']) && $headers['Accept'] = 'application/json'; | |
} | |
array_walk($headers, fn(&$value, $key) => $value = $key . ': ' . $value); | |
switch ($bodymode) { | |
case 'formdata': | |
foreach ($request->request->body->$bodymode as $data) { | |
if ($data->type === 'file' && !empty($data->src)) { | |
foreach ($data->src as $src) { | |
$body .= (empty($data->disabled) ? '' : '# ') . "$data->key=$src\n"; | |
} | |
} else { | |
$body .= (empty($data->disabled) ? '' : '# ') . "$data->key=$data->value\n"; | |
} | |
} | |
break; | |
default: | |
$body = $request->request->body->$bodymode; | |
} | |
} | |
$headers = trim(implode("\n", $headers)); | |
$body = trim($body); | |
file_put_contents($httpfile, trim("$url\n$headers\n\n$body\n"), FILE_APPEND); | |
file_put_contents($mdfile, "```\n" . trim("$url\n$headers\n\n$body") . "\n```", FILE_APPEND); | |
// response examples | |
if (!empty($request->response)) { | |
file_put_contents($mdfile, "\n# Examples\n", FILE_APPEND); | |
foreach ($request->response as $response) { | |
file_put_contents($mdfile, "## $response->name\n```\n$url\n", FILE_APPEND); | |
foreach ($response->header as $header) { | |
file_put_contents($mdfile, "$header->key=$header->value\n", FILE_APPEND); | |
} | |
file_put_contents($mdfile, "\n$body\n```\nResult:\n```\n$response->body\n```\n", FILE_APPEND); | |
} | |
} | |
out('[OK] ' . $httpfile); | |
} | |
/** | |
* Processes one item in Postman collection | |
* If $item is nested directory then function processes it recursively, otherwise writes *.http-file | |
* | |
* @param $item | |
* @return void | |
*/ | |
function process_item($item = null) | |
{ | |
if (empty($item)) { | |
return; | |
} | |
if (is_array($item)) { | |
static $dir_tree; | |
static $path; | |
foreach ($item as $subitem) { | |
if (empty($subitem->item)) { | |
write_request($subitem, $path); | |
} else { | |
$dir_tree[] = $subitem->name; | |
$path = implode('/', $dir_tree); | |
!file_exists(OUTPUT_DIR . "/$path/") && mkdir(OUTPUT_DIR . "/$path/", recursive: true); | |
isset($subitem->description) && file_put_contents( | |
OUTPUT_DIR . "/$path/README.md", | |
$subitem->description | |
); | |
process_item($subitem->item); | |
array_pop($dir_tree); | |
$path = implode('/', $dir_tree); | |
} | |
} | |
} else { | |
write_request($item, ''); | |
} | |
} | |
/** | |
* Recursively removes directory | |
* | |
* @param string $path | |
* @return void | |
*/ | |
function remove_directory(string $path): void | |
{ | |
$files = glob($path . '/*'); | |
foreach ($files as $file) { | |
is_dir($file) ? remove_directory($file) : unlink($file); | |
} | |
file_exists($path) && rmdir($path); | |
} | |
try { | |
$json = read_collection_file($argv[1] ?? throw new Exception('ERROR: No collection file specified')); | |
define('OUTPUT_DIR', './files/' . $json->info->name); | |
remove_directory(OUTPUT_DIR); | |
!file_exists(OUTPUT_DIR) && mkdir(OUTPUT_DIR, recursive: true) || throw new Exception( | |
'ERROR: Cannot create output directory: ' . OUTPUT_DIR | |
); | |
process_item($json->item); | |
} catch (Exception $e) { | |
out($e->getMessage()); | |
return 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment