Skip to content

Commit

Permalink
Add integrated API dashboard redoc swagger
Browse files Browse the repository at this point in the history
  • Loading branch information
cb-github-robot authored Aug 16, 2024
2 parents 6e54e9a + b1500c4 commit 4ce826d
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 2 deletions.
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ COPY ./package.json ./package-lock.json ./
RUN npm config set fetch-retries 5 \
&& npm config set fetch-retry-mintimeout 20000 \
&& npm config set fetch-retry-maxtimeout 120000 \
&& npm install --production --silent --no-audit --prefer-offline
&& npm install --silent --no-audit --prefer-offline

COPY . .

RUN npm run build

RUN npm prune --production

#############################################################
Expand All @@ -29,6 +31,7 @@ COPY --from=builder /app/dist ./dist
COPY --from=builder /app/img ./dist/img
COPY --from=builder /app/index.html ./
COPY --from=builder /app/index.js ./
COPY --from=builder /app/redoc-swagger-integration.html ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package-lock.json ./package-lock.json
COPY --from=builder /app/package.json ./package.json
Expand Down
2 changes: 2 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -227,13 +227,15 @@
<a class="dropdown-item" href="javascript:void(0)" onclick="getConnection();">Show all Clouds</a>
<a class="dropdown-item" href="javascript:void(0)" onclick="hideMCI();">Hide/Show MCI</a>
<a class="dropdown-item" href="javascript:void(0)" onclick="clearMap();">Clear Map</a>
<a class="dropdown-item" href="javascript:void(0)" onclick="window.open('redoc-swagger-integration.html', '_blank');">API Dashboard</a>
<a class="dropdown-item" href="javascript:void(0)" onclick="toggleView();">Toggle Map/API</a>
</div>
</div>

<!-- Create toggleView Field -->
<div class="form-group col">
<button type="button" onClick="toggleView();" class="btn btn-info w-100">Toggle Map/API</button>
<!-- <button type="button" onclick="window.open('redoc-swagger-integration.html', '_blank')" class="btn btn-info w-100">Redoc & Swagger</button> -->
</div>

</form>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"scripts": {
"start": "parcel -p 1324 --host 0.0.0.0 index.html",
"build": "echo 'Removing temporal cache files' && rm -rf dist/* .parcel-cache/* && mkdir -p dist/img/ && cp -r img/ dist/ && parcel build --public-url . index.html"
"build": "echo 'Removing temporal cache files' && rm -rf dist/* .parcel-cache/* && mkdir -p dist/img/ && cp -r img/ dist/ && cp redoc-swagger-integration.html dist/ && parcel build --public-url . index.html"
},
"description": "MapUI Client for CB-Tumblebug (display multi-cloud infra service)",
"repository": {
Expand Down
220 changes: 220 additions & 0 deletions redoc-swagger-integration.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CB-Tumblebug API Dashboard</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
height: 100vh;
width: 100vw;
overflow: hidden;
}

#title-bar {
height: 50px; /* Height for the title bar */
background-color: #262626;
color: #fcfcfc;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: bold;
}

#content-container {
display: flex;
height: calc(100vh - 50px); /* Remaining height after title bar */
width: 100%;
}

#redoc-container {
width: 65%; /* Redoc is now on the left side */
height: 100%;
overflow-y: scroll;
border-right: 1px solid #ddd;
}


#swagger-container {
width: 35%; /* Swagger UI is now on the right side */
height: 100%;
display: flex;
flex-direction: column;
}
#swagger-header {
height: 40px;
background-color: #ffffff;
color: rgb(0, 0, 0);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
position: sticky; /* Make header stick to the top */
top: 0;
}
#swagger-ui-container {
height: calc(100% - 40px); /* Adjust height based on header */
overflow-y: scroll;
}
</style>

<!-- Load Swagger UI styles -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.17.14/swagger-ui.css">
</head>
<body>
<div id="title-bar"> CB-Tumblebug API Dashboard</div>

<div id="content-container">
<div id="redoc-container"></div>
<div id="swagger-container">
<div id="swagger-header">Try out API with Swagger UI</div>
<div id="swagger-ui-container"></div>
</div>

</div>

<!-- Load the latest Redoc and Swagger UI scripts -->
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.17.14/swagger-ui-bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.17.14/swagger-ui-standalone-preset.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js-yaml.min.js"></script>

<script>
// Basic authentication details
const username = 'default';
const password = 'default';
const credentials = btoa(username + ':' + password);
const authHeader = 'Basic ' + credentials;

console.log('Starting to fetch YAML');

// Fetch the YAML file with authentication
fetch('http://localhost:1323/tumblebug/api/doc.yaml', {
headers: {
'Authorization': authHeader
}
})
.then(response => {
console.log('Fetch response:', response);
if (!response.ok) {
throw new Error('Authentication failed: ' + response.statusText);
}
return response.text();
})
.then(yamlText => {
console.log('YAML fetched successfully');
const spec = jsyaml.load(yamlText);

console.log('YAML parsed successfully');

// Initialize Swagger UI (placed on the right side)
const ui = SwaggerUIBundle({
url: 'http://localhost:1323/tumblebug/api/doc.yaml',
dom_id: '#swagger-ui-container',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
layout: "BaseLayout",
deepLinking: true, // Enable deep linking
displayOperationId: true, // Display operationId in Swagger UI
requestInterceptor: (request) => {
request.headers['Authorization'] = authHeader;
return request;
},
validatorUrl: null,
onComplete: () => {
console.log('Swagger UI loaded');
}
});

console.log('Swagger UI initialized');

// Initialize Redoc (placed on the left side)
Redoc.init(spec, {
scrollYOffset: 50,
hideLoading: true,
theme: {
colors: {
primary: {
main: '#dd5522'
}
},
typography: {
fontFamily: 'Arial, sans-serif',
fontSize: '18px',
headings: {
fontFamily: 'Arial, sans-serif'
}
},
}
}, document.getElementById('redoc-container'));

console.log('Redoc initialized');

// Function to remove special characters from a string
function removeSpecialCharacters(str) {
return str.replace(/[^a-zA-Z0-9]/g, '');
}

// Use MutationObserver to detect URL hash changes
const observer = new MutationObserver(() => {
const hash = window.location.hash;
if (hash.startsWith('#tag')) { // Only proceed if the hash starts with #tag
const operationId = hash.split('/').pop();

// Find the corresponding operation in Swagger UI based on the operationId
const allOperations = document.querySelectorAll('#swagger-ui-container [id]');
let targetOperation = null;

allOperations.forEach(op => {
const idAttribute = op.getAttribute('id');
const idParts = idAttribute.split('-');
const lastIdPart = idParts.pop();

// Check if the last part of the id matches the operationId
if (lastIdPart === operationId) {
targetOperation = op;
}

if (idAttribute.includes('operations-tag-')) {
const idWithoutPrefix = idAttribute.replace('operations-tag-', '');
const idWithoutSpecialChars = removeSpecialCharacters(idWithoutPrefix);
const operationIdWithoutSpecialChars = removeSpecialCharacters(operationId);
if (!targetOperation && idWithoutSpecialChars === operationIdWithoutSpecialChars) {
targetOperation = op;
}
}
});

if (targetOperation) {
console.log('Target found for operationId:', operationId);
targetOperation.scrollIntoView({ behavior: 'smooth' });
} else {
console.log('Target not found for operationId:', operationId);
}
}
});

// Observe changes to the URL hash
observer.observe(document.querySelector('body'), {
attributes: true,
childList: true,
subtree: true
});

})
.catch(error => {
console.error('Failed to load API specification:', error);
alert('Failed to load API specification: ' + error.message);
});
</script>
</body>
</html>


0 comments on commit 4ce826d

Please sign in to comment.