Skip to content

Commit 90c55e3

Browse files
chore: added preview url and live url
1 parent d931e29 commit 90c55e3

6 files changed

Lines changed: 108 additions & 62 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12-
- [Swagger] Refactored `publish_portal_product` to build live URLs dynamically from `SWAGGER_PORTAL_BASE_PATH`, support preview and section/table-of-contents paths, and return the resolved `liveUrl` in the publish response.
12+
- [Swagger] Refactored `publish_portal_product` to build published URLs dynamically from `SWAGGER_PORTAL_BASE_PATH`, support preview and section/table-of-contents paths, and return the resolved `liveUrl` or `previewUrl` in the publish response.
1313

1414
### Added
1515

docs/products/SmartBear MCP Server/swagger-portal-integration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ The Swagger Portal client provides comprehensive portal and product management c
116116
#### `publish_portal_product`
117117

118118
- Purpose: Publish a product's content to make it live or as preview. This endpoint publishes the current content of a product, making it visible to portal visitors. Use preview mode to test before going live.
119-
- Returns: Publication status information, including the generated live URL when the product can be resolved.
119+
- Returns: Publication status information, including the generated `liveUrl` for live publication or `previewUrl` for preview publication when the product can be resolved.
120120
- Use case: Make product content visible to portal visitors, either as live content or preview for testing.
121121
- Parameters:
122122

@@ -126,7 +126,7 @@ The Swagger Portal client provides comprehensive portal and product management c
126126
| `preview` | Whether to publish as preview (true) or live (false). Preview allows testing before going live. Defaults to false (live publication) | boolean | No |
127127
| `tableOfContentsId` | Optional table of contents UUID, or identifier in the format 'portal-subdomain:product-slug:section-slug:table-of-contents-slug' used to resolve a specific live URL path | string | No |
128128

129-
The publish response now includes `liveUrl` when available. The live URL is built dynamically from the configured `portalBasePath`, so it works across dev2, int, and production environments.
129+
The publish response now includes `liveUrl` for live publication and `previewUrl` for preview publication. The URL is built dynamically from the configured `portalBasePath`, so it works across dev2, int, and production environments. When `tableOfContentsId` resolves, the returned URL includes the section and table-of-contents path.
130130

131131
### Product Sections Management
132132

src/swagger/client/api.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -361,11 +361,12 @@ export class SwaggerAPI {
361361
}
362362

363363
/**
364-
* Publish a portal product and generate live URL with environment-specific domain
364+
* Publish a portal product and generate a published URL with environment-specific domain.
365+
* Returns `liveUrl` for live publishes and `previewUrl` for preview publishes.
365366
* @param productId - ID of the product to publish
366367
* @param preview - Whether to publish in preview mode (default: false)
367-
* @param tableOfContentsId - Optional tableOfContentsId The table of contents UUID, or identifier in the format 'portal-subdomain:product-slug:section-slug:table-of-contents-slug'
368-
* @returns Complete publish response with product details and live URL
368+
* @param tableOfContentsId - Optional table of contents UUID, or identifier in the format 'portal-subdomain:product-slug:section-slug:table-of-contents-slug'
369+
* @returns Complete publish response with product details and the resolved published URL
369370
*/
370371
async publishPortalProduct(
371372
productId: string,
@@ -399,12 +400,13 @@ export class SwaggerAPI {
399400

400401
// Build live URL using environment-specific domain
401402
const host = portalDetails.customDomain ?? portalDetails.subdomain;
402-
const liveUrl = buildPortalLiveUrl(
403+
const publicationUrl = buildPortalLiveUrl(
403404
this.config,
404405
host,
405406
productDetails.slug,
406407
targetSection,
407408
targetTocItem,
409+
preview,
408410
);
409411

410412
// Publish the product to the portal
@@ -422,7 +424,9 @@ export class SwaggerAPI {
422424

423425
return {
424426
...result,
425-
liveUrl,
427+
...(preview
428+
? { previewUrl: publicationUrl }
429+
: { liveUrl: publicationUrl }),
426430
} as PublishPortalProductResponse;
427431
}
428432

src/swagger/client/portal-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ export type UpdateDocumentBody = Omit<UpdateDocumentArgs, "documentId">;
432432

433433
export type PublishPortalProductResponse = SuccessResponse & {
434434
liveUrl?: string;
435+
previewUrl?: string;
435436
};
436437

437438
// Response types for better type safety

src/swagger/client/utils.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,27 @@ export function findTableOfContentsItem(
2626
}
2727

2828
/**
29-
* Build the live URL for a published portal product.
29+
* Build the live URL or previewUrl for a published portal product.
3030
*/
3131
export function buildPortalLiveUrl(
3232
config: SwaggerConfiguration,
3333
host: string | undefined,
3434
productSlug: string | undefined,
3535
section: UrlSegmentSource,
3636
tocItem: UrlSegmentSource,
37+
preview: boolean = false,
3738
): string {
3839
if (!host || !productSlug) {
3940
return "";
4041
}
4142

4243
const portalUiDomain = config.getPortalUiDomainSuffix();
4344
const baseUrl = `https://${host}${portalUiDomain}/${productSlug}`;
45+
const previewSuffix = preview ? "?preview=product" : "";
4446

4547
if (section?.slug && tocItem?.slug) {
46-
return `${baseUrl}/${section.slug}/${tocItem.slug}`;
48+
return `${baseUrl}/${section.slug}/${tocItem.slug}${previewSuffix}`;
4749
}
4850

49-
return baseUrl;
51+
return `${baseUrl}${previewSuffix}`;
5052
}

src/tests/unit/swagger/api.test.ts

Lines changed: 90 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -243,59 +243,73 @@ describe("SwaggerAPI", () => {
243243
});
244244

245245
describe("publishPortalProduct", () => {
246-
it("should publish product and return resolved liveUrl", async () => {
247-
const headers = {
248-
Authorization: "Bearer test-token",
249-
"Content-Type": "application/json",
250-
"User-Agent": "SmartBear-MCP/1.0.0",
251-
};
252-
const productId = "prod-123";
253-
const portalId = "portal-123";
254-
const productSlug = "test-product";
255-
const tocId = "toc-1";
256-
257-
const productResponse = {
258-
id: productId,
259-
name: "Test Product",
260-
slug: productSlug,
261-
portalId,
262-
};
263-
const portalResponse = {
264-
id: portalId,
265-
name: "Test Portal",
266-
subdomain: "testportal",
267-
};
268-
const sectionsResponse = {
269-
page: {
270-
number: 0,
271-
size: 20,
272-
totalElements: 1,
273-
totalPages: 1,
246+
const headers = {
247+
Authorization: "Bearer test-token",
248+
"Content-Type": "application/json",
249+
"User-Agent": "SmartBear-MCP/1.0.0",
250+
};
251+
252+
const productId = "prod-123";
253+
const portalId = "portal-123";
254+
const productSlug = "test-product";
255+
const tocId = "toc-1";
256+
257+
const productResponse = {
258+
id: productId,
259+
name: "Test Product",
260+
slug: productSlug,
261+
portalId,
262+
};
263+
264+
const portalResponse = {
265+
id: portalId,
266+
name: "Test Portal",
267+
subdomain: "testportal",
268+
};
269+
270+
const sectionsResponse = {
271+
page: {
272+
number: 0,
273+
size: 20,
274+
totalElements: 1,
275+
totalPages: 1,
276+
},
277+
items: [
278+
{
279+
id: "section-1",
280+
productId,
281+
title: "Docs",
282+
slug: "docs",
283+
tableOfContents: [
284+
{
285+
id: tocId,
286+
slug: "getting-started",
287+
title: "Getting Started",
288+
order: 0,
289+
parentId: null,
290+
children: [],
291+
swaggerhubApi: null,
292+
content: null,
293+
},
294+
],
295+
order: 0,
274296
},
275-
items: [
276-
{
277-
id: "section-1",
278-
productId,
279-
title: "Docs",
280-
slug: "docs",
281-
tableOfContents: [
282-
{
283-
id: tocId,
284-
slug: "getting-started",
285-
title: "Getting Started",
286-
order: 0,
287-
parentId: null,
288-
children: [],
289-
swaggerhubApi: null,
290-
content: null,
291-
},
292-
],
293-
order: 0,
294-
},
295-
],
296-
};
297-
const publishResponse = { success: true };
297+
],
298+
};
299+
300+
const previewSectionsResponse = {
301+
page: {
302+
number: 0,
303+
size: 20,
304+
totalElements: 1,
305+
totalPages: 1,
306+
},
307+
items: [],
308+
};
298309

310+
const publishResponse = { success: true };
311+
312+
it("should publish product and return resolved liveUrl", async () => {
299313
fetchMock
300314
.mockResponseOnce(JSON.stringify(productResponse))
301315
.mockResponseOnce(JSON.stringify(sectionsResponse))
@@ -343,6 +357,31 @@ describe("SwaggerAPI", () => {
343357
`https://testportal.portal.swaggerhub.com/${productSlug}/docs/getting-started`,
344358
});
345359
});
360+
361+
it("should publish preview product and return previewUrl", async () => {
362+
fetchMock
363+
.mockResponseOnce(JSON.stringify(productResponse))
364+
.mockResponseOnce(JSON.stringify(previewSectionsResponse))
365+
.mockResponseOnce(JSON.stringify(portalResponse))
366+
.mockResponseOnce(JSON.stringify(publishResponse));
367+
368+
const result = await api.publishPortalProduct(productId, true);
369+
370+
expect(fetchMock).toHaveBeenNthCalledWith(
371+
4,
372+
`https://api.portal.swaggerhub.com/v1/products/${productId}/published-content?preview=true`,
373+
{
374+
method: "PUT",
375+
headers,
376+
},
377+
);
378+
379+
expect(result).toEqual({
380+
success: true,
381+
previewUrl:
382+
`https://testportal.portal.swaggerhub.com/${productSlug}?preview=product`,
383+
});
384+
});
346385
});
347386

348387
describe("error handling", () => {

0 commit comments

Comments
 (0)