From cc1be9ddd48393488c53cb3082936a8f4acd102a Mon Sep 17 00:00:00 2001 From: Donald Coffin Date: Sat, 7 Feb 2026 14:47:30 -0500 Subject: [PATCH] feat: add Swagger UI alongside Redocly documentation Added Swagger UI for interactive API exploration alongside the existing Redocly documentation, giving users two ways to interact with the API. Changes: - Installed swagger-ui-dist package - Created build script for Swagger UI (scripts/build-swagger.js) - Updated npm scripts to build both Redocly and Swagger UI - Added navigation link from Swagger UI to Redocly docs - Added link in OpenAPI description to Swagger UI - Updated README with documentation for both formats Documentation formats: 1. Redocly (dist/index.html) - Beautiful, readable API documentation - Green Button theming - Comprehensive examples 2. Swagger UI (dist/swagger/index.html) - Interactive API testing - 'Try it out' functionality - OAuth flow testing - Green Button branded Build commands: - npm run build - Builds both formats - npm run build:redocly - Builds only Redocly - npm run build:swagger - Builds only Swagger UI Both use the same openapi/openapi.yaml source file. Validation: Passes npm test with valid OpenAPI spec Build: Successfully generates both documentation formats Co-Authored-By: Claude Sonnet 4.5 --- README.md | 29 ++++++++- openapi/openapi.yaml | 3 + package-lock.json | 21 +++++++ package.json | 10 ++- scripts/build-swagger.js | 132 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 191 insertions(+), 4 deletions(-) create mode 100755 scripts/build-swagger.js diff --git a/README.md b/README.md index a8de8cd..520e919 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,39 @@ ### Usage #### `npm start` -Starts the reference docs preview server. +Starts the Redocly reference docs preview server. + +#### `npm start:swagger` +Starts a local server to preview Swagger UI (requires building first). #### `npm run build` -Bundles the definition to the dist folder. +Builds both Redocly documentation and Swagger UI to the dist folder. + +#### `npm run build:redocly` +Builds only the Redocly documentation. + +#### `npm run build:swagger` +Builds only the Swagger UI. #### `npm test` Validates the definition. +## Documentation Formats + +This repository generates two types of API documentation: + +### Redocly (Main Documentation) +- **Location:** `dist/index.html` +- **Live:** https://greenbuttonalliance.github.io/openapi-starter/ +- **Purpose:** Beautiful, readable API documentation +- **Features:** Clean layout, Green Button theming, comprehensive examples + +### Swagger UI (Interactive Explorer) +- **Location:** `dist/swagger/index.html` +- **Live:** https://greenbuttonalliance.github.io/openapi-starter/swagger/ +- **Purpose:** Interactive API testing and exploration +- **Features:** "Try it out" functionality, request/response testing, OAuth flow + ## Contribution Guide Below is a sample contribution guide. The tools diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 0f7ff9d..deed1cf 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -9,6 +9,9 @@ info: to exchange energy usage information using Atom-formatted XML feeds. + **Interactive API Explorer:** Try out the API using [Swagger UI](./swagger/) + + All resources are returned as Atom feeds (collections) or Atom entries (individual resources) using the `application/atom+xml` content type. The ESPI data elements are embedded within the Atom `` element diff --git a/package-lock.json b/package-lock.json index 9c3722d..2188baa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "1.0.0", "dependencies": { "@redocly/cli": "^2.16.0" + }, + "devDependencies": { + "swagger-ui-dist": "^5.31.0" } }, "node_modules/@babel/code-frame": { @@ -545,6 +548,14 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "license": "MIT" }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2136,6 +2147,16 @@ "node": ">=8" } }, + "node_modules/swagger-ui-dist": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.31.0.tgz", + "integrity": "sha512-zSUTIck02fSga6rc0RZP3b7J7wgHXwLea8ZjgLA3Vgnb8QeOl3Wou2/j5QkzSGeoz6HusP/coYuJl33aQxQZpg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, "node_modules/swagger2openapi": { "version": "7.0.8", "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", diff --git a/package.json b/package.json index 4141e23..bf85cee 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,14 @@ "private": true, "scripts": { "start": "redocly preview-docs", - "build": "redocly build-docs openapi/openapi.yaml -o dist/index.html", - "bundle": "redocly bundle openapi/openapi.yaml -o dist/bundle.yaml", + "start:swagger": "npx http-server dist -p 8081", + "build": "npm run build:redocly && npm run build:swagger", + "build:redocly": "redocly build-docs openapi/openapi.yaml -o dist/index.html", + "build:swagger": "npm run bundle && node scripts/build-swagger.js", + "bundle": "redocly bundle openapi/openapi.yaml -o dist/openapi.json", "test": "redocly lint" + }, + "devDependencies": { + "swagger-ui-dist": "^5.31.0" } } diff --git a/scripts/build-swagger.js b/scripts/build-swagger.js new file mode 100755 index 0000000..ab16305 --- /dev/null +++ b/scripts/build-swagger.js @@ -0,0 +1,132 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); + +// Create dist/swagger directory +const swaggerDir = path.join(__dirname, '..', 'dist', 'swagger'); +if (!fs.existsSync(swaggerDir)) { + fs.mkdirSync(swaggerDir, { recursive: true }); +} + +// Copy Swagger UI assets +const swaggerUiPath = path.join(__dirname, '..', 'node_modules', 'swagger-ui-dist'); +const files = fs.readdirSync(swaggerUiPath); + +files.forEach(file => { + if (file.endsWith('.js') || file.endsWith('.css') || file.endsWith('.map') || file.endsWith('.png')) { + fs.copyFileSync( + path.join(swaggerUiPath, file), + path.join(swaggerDir, file) + ); + } +}); + +// Copy bundled OpenAPI spec +fs.copyFileSync( + path.join(__dirname, '..', 'dist', 'openapi.json'), + path.join(swaggerDir, 'openapi.json') +); + +// Create custom Swagger UI HTML with Green Button branding +const html = ` + + + + Green Button ESPI API - Swagger UI + + + + + + +
+ + + + +`; + +fs.writeFileSync(path.join(swaggerDir, 'index.html'), html); + +console.log('✅ Swagger UI built successfully at dist/swagger/'); +console.log(' - Redocly docs: dist/index.html'); +console.log(' - Swagger UI: dist/swagger/index.html');