Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save egyjs/87c09567b5644bbff7127839fc1f0ab5 to your computer and use it in GitHub Desktop.
Save egyjs/87c09567b5644bbff7127839fc1f0ab5 to your computer and use it in GitHub Desktop.
Automating Postman to Swagger with Version Control using GitHub Actions and Laravel

Title: Automating Postman to Swagger with Version Control in Laravel Vapor


Hey folks! 👋 I wanted to share a neat workflow I've implemented for our Laravel Vapor project that automates the generation and upload of Swagger documentation with each Git tag, all seamlessly integrated with Postman. 🚀

🤖 GitHub Actions Workflow (deploy.yml)

With every push to a dev/* tag, GitHub Actions springs into action:

  • Checkout Code: Pulls in the latest source code.
  • Setup Node and PHP: Configures the environment.
  • Require Vapor CLI: Installs the Vapor CLI globally.
  • Install Project Dependencies: Uses Composer to fetch dependencies.
  • Deploy Environment: Vapor deploys our development environment with the commit details.

📦 Vapor Configuration (vapor.yml)

The magic happens during the build steps:

  • Convert Postman to OpenAPI: Downloads the Postman collection, removes unnecessary headers, and converts it to OpenAPI format.
  • Upload to SwaggerHub: The resulting Swagger file is then uploaded to SwaggerHub, ensuring our API documentation is always up-to-date.

🔄 Postman to OpenAPI Conversion (p2o.js)

This JavaScript script cleans up the Postman collection and leverages the postman-to-openapi package to perform the conversion. The OpenAPI file is then saved for deployment.

🚀 Laravel Artisan Command (UploadToSwaggerHub.php)

This PHP script is the backbone of SwaggerHub integration:

  • Create a New Version: Dynamically creates a new SwaggerHub version with each Git tag.
  • Upload to SwaggerHub: Sends an HTTP request to upload the OpenAPI file, ensuring our Swagger documentation is versioned and available.

🔄 Version Control and Automation

  • Postman Integration: We automate the download of a Postman collection on each deployment.
  • SwaggerHub Integration: With each Git tag, we generate a new Swagger version, ensuring version control and easy navigation in SwaggerHub.

The result? A fully automated pipeline where Swagger documentation evolves with our codebase, providing clear versioning and consistency. No more manual updates needed! 🌐✨

Feel free to adapt and integrate this workflow into your Laravel Vapor projects. Happy coding! 🚀👨‍💻

Free Palestine 🇵🇸

Contact me

el3zahaby @egyjs LinkedIn
# .github/workflows/deploy.yml
name: Deploy Development Environment
on:
push:
tags: [ dev/* ]
jobs:
deploy:
runs-on: ubuntu-22.04
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 14
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
tools: composer:v2
coverage: none
- name: Require Vapor CLI
run: composer global require laravel/vapor-cli
- name: Install Project Dependencies
run: composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Deploy Environment
run: vapor deploy development --commit="${{ github.event.head_commit.id }}" --message="${{ github.event.head_commit.message }} by ${{ github.actor }}"
env:
VAPOR_API_TOKEN: ${{ secrets.VAPOR_API_TOKEN }}
// ./p2o.js
// Require Package
const postmanToOpenApi = require('postman-to-openapi')
const env = require('dotenv').config()
const fs = require('fs/promises');
// Postman Collection Path
const postmanCollection = './postman_collection.json'
// remove `responses.headers` from postman collection and resave it
fs.readFile(postmanCollection, 'utf8').then(data => {
console.log('Removing `responses.headers` from postman collection')
const collection = JSON.parse(data)
// use recursiveRemoveHeaders(item) to remove `responses.headers` from postman collection
collection.collection.item.forEach(item => recursiveRemoveHeaders(item))
return fs.writeFile(postmanCollection, JSON.stringify(collection, null, 2)).then(() => {
// // Output OpenAPI Path
const outputFile = './swagger.yml'
// Promise callback style
postmanToOpenApi(postmanCollection, outputFile, {
defaultTag: 'General',
info: {
title: env.parsed.APP_NAME,
version: "{{version}}",
},
servers: [
{
url: env.parsed.APP_URL ?? 'https://api-dev.example.com',
description: (env.parsed.APP_ENV).toUpperCase() + " Environment"
}
],
security: [
{
"bearerAuth": []
}
],
replaceVars: true,
}).then().catch(err => {
console.error(err)
})
})
}).catch(err => {
console.error(err)
})
function recursiveRemoveHeaders(item) {
if (item.item) {
item.item.forEach(request => {
if (request.response) {
request.response.forEach(response => {
if (response.originalRequest) {
delete response.originalRequest.headers
}
})
}
recursiveRemoveHeaders(request)
})
}
}
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use GuzzleHttp\Client;
class UploadToSwaggerHub extends Command
{
protected $signature = 'swaggerhub:upload {file}';
protected $description = 'Create a new version in SwaggerHub and upload the file';
private string $api_key = '<YOUR_API_KEY>'; // Replace with your SwaggerHub API key
/**
* @throws \Exception
*/
public function handle()
{
$currentTag = str_replace('/', '', exec('git describe --tags --abbrev=0'));
$this->info("Creating a new version in SwaggerHub for tag: $currentTag");
$openApiPath = $this->argument('file');
// Call the method to upload the file to the new version
$this->uploadFile($currentTag, $openApiPath);
$this->info('File uploaded successfully.');
$this->setVersionToDefault($currentTag);
$this->info('Version set to default successfully.');
}
protected function uploadFile($version, $openApiPath)
{
// Your SwaggerHub API endpoint for uploading a file
$uploadUrl = "https://api.swaggerhub.com/apis/<VENDOR_NAME>/<API_NAME>?version=$version&isPrivate=false32&default=true";
// Make an HTTP request to upload the file
$client = new Client();
$response = $client->post($uploadUrl, [
'headers' => [
'accept' => 'application/json',
'Content-Type' => 'application/yaml',
'Authorization' => "Bearer $this->api_key", // Replace with your SwaggerHub API key
],
'body' => file_get_contents($openApiPath),
]);
if ($response->getStatusCode() < 200 || $response->getStatusCode() > 299) {
throw new \Exception('Error uploading file to SwaggerHub');
}
}
protected function setVersionToDefault(array|bool|string $currentTag){
// @see https://app.swaggerhub.com/apis-docs/swagger-hub/registry-api/1.0.67#/APIs/setApiDefaultVersion
$client = new Client();
$response = $client->put('https://api.swaggerhub.com/apis/egyjs/lockers/settings/default', [
'headers' => [
'accept' => 'application/json',
'Content-Type' => 'application/json',
'Authorization' => "Bearer $this->api_key",
],
'body' => json_encode([
'version' => $currentTag
]),
]);
if ($response->getStatusCode() < 200 || $response->getStatusCode() > 299) {
throw new \Exception('Error setting version to default');
}
}
}
# ./vapor.yml
id: <ID>
name: <NAME>
environments:
development:
memory: 1024
cli-memory: 512
storage: lockers-app
timeout: 900
domain: example.com
runtime: 'php-8.2:al2'
build:
- 'composer install'
- 'php artisan event:cache'
- 'npm i'
- 'curl -L https://api.postman.com/collections/9251113-de3e5249-080d-4a4a-a42f-810c35ca9b44?access_key=<POSTMAN_ACCESS_KEY> -o postman_collection.json'
- 'npm i postman-to-openapi -g'
- 'node p2o'
- 'php artisan swaggerhub:upload ./swagger.yml'
- 'echo "Cleaning up..."'
- 'rm -rf swagger.yml postman_collection.json node_modules'
deploy:
- 'php artisan db:show'
- 'php artisan migrate --force'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment