Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions validate-pages/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This project iterates over one or more Port organizations, lists every page in each organization, and validates them via the Port API. It includes two related flows:
- **Validate** — only checks pages and reports any validation errors found
- **Fix** — validates pages and, for any page with errors, immediately calls the Port API fix endpoint on it, then reports which fixes were applied
- **Fix** — validates pages and, for any page with errors, applies client-side fixes (including setting `displayMode: "widget"` on `table-entities-explorer` widgets inside `dashboard-widget` containers), calls the Port API fix endpoint, then reports which fixes were applied

Each flow can be run as console-only output or as a report (HTML + JSON) generator.

Expand Down Expand Up @@ -83,8 +83,9 @@ The script will:
1. Authenticate against each organization using its Port API credentials
2. List all pages in the organization (in compact form)
3. Validate each page via the Port API
4. For any page with validation errors, immediately call the Port API fix endpoint on it
5. Report the fixes that were applied to each page
4. Apply client-side fixes on every page (including setting missing `displayMode` on table widgets inside dashboard widgets), then validate each page
5. For any page that is still invalid, call the Port API fix endpoint on it
6. Report the fixes that were applied to each page

Validation and fixing are interleaved page-by-page (rather than fixing everything in a second pass after all pages are validated), so partial progress is preserved if the script is interrupted.

Expand All @@ -101,6 +102,7 @@ For each organization the script prints progress per page, then a summary listin
```
[my-org] 1 page(s) with fixes applied / 42 pages
FIXES APPLIED some-broken-page:
- Set displayMode to 'widget' for table-entities-explorer 'relatedTable' (was (missing))
- Removed the id key from links in a links widget
NO FIXES APPLIED another-page
```
Expand Down
2 changes: 1 addition & 1 deletion validate-pages/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"test": "node pageFixes.test.js",
"validate": "node index.js",
"validate:report": "node report.js",
"fix": "node fix.js",
Expand Down
92 changes: 92 additions & 0 deletions validate-pages/pageFixes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
const TABLE_ENTITIES_EXPLORER = "table-entities-explorer";
const DASHBOARD_WIDGET = "dashboard-widget";
const REQUIRED_DISPLAY_MODE = "widget";

/**
* Sets displayMode to "widget" on table-entities-explorer widgets that are
* direct children of dashboard-widget containers. Mirrors the port-api
* validateDashboardWidgetChildDisplayMode guardrail.
*
* @param {Array<Record<string, unknown>> | undefined} widgets
* @returns {Array<{name: string, message: string}>}
*/
function normalizeDashboardWidgetChildDisplayMode(widgets) {
const fixes = [];

function fixDashboardWidgetChildren(dashboardWidget) {
if (!Array.isArray(dashboardWidget.widgets)) {
return;
}

for (const child of dashboardWidget.widgets) {
if (
child.type === TABLE_ENTITIES_EXPLORER &&
child.displayMode !== REQUIRED_DISPLAY_MODE
) {
const previous = child.displayMode ?? "(missing)";
child.displayMode = REQUIRED_DISPLAY_MODE;
const label = child.id || child.title || "unknown";
fixes.push({
name: "normalizeDashboardWidgetChildDisplayMode",
message: `Set displayMode to '${REQUIRED_DISPLAY_MODE}' for table-entities-explorer '${label}' (was ${previous})`,
});
}

walkWidget(child);
}
}

function walkWidget(widget) {
if (!widget || typeof widget !== "object") {
return;
}

if (widget.type === DASHBOARD_WIDGET) {
fixDashboardWidgetChildren(widget);
return;
}

if (Array.isArray(widget.widgets)) {
for (const nestedWidget of widget.widgets) {
walkWidget(nestedWidget);
}
}

if (Array.isArray(widget.groups)) {
for (const group of widget.groups) {
if (Array.isArray(group.widgets)) {
for (const nestedWidget of group.widgets) {
walkWidget(nestedWidget);
}
}
}
}
}

if (Array.isArray(widgets)) {
for (const widget of widgets) {
walkWidget(widget);
}
}

return fixes;
}

/**
* Applies all client-side page fixes and returns the fix messages produced.
* Mutates the page object in place.
*
* @param {Record<string, unknown>} page
* @returns {Array<{name: string, message: string}>}
*/
function applyClientSidePageFixes(page) {
return normalizeDashboardWidgetChildDisplayMode(page.widgets);
}

module.exports = {
TABLE_ENTITIES_EXPLORER,
DASHBOARD_WIDGET,
REQUIRED_DISPLAY_MODE,
normalizeDashboardWidgetChildDisplayMode,
applyClientSidePageFixes,
};
55 changes: 55 additions & 0 deletions validate-pages/pageFixes.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const assert = require("assert");
const {
normalizeDashboardWidgetChildDisplayMode,
} = require("./pageFixes");

function runTests() {
const nestedTable = {
id: "relatedTable",
type: "table-entities-explorer",
title: "Related Entities",
};

const pageWidgets = [
{
id: "entityPageGrouper",
type: "grouper",
groups: [
{
title: "Overview",
widgets: [
{
id: "overviewDashboard",
type: "dashboard-widget",
widgets: [nestedTable],
},
],
},
],
},
];

const fixes = normalizeDashboardWidgetChildDisplayMode(pageWidgets);

assert.strictEqual(fixes.length, 1);
assert.strictEqual(nestedTable.displayMode, "widget");
assert.match(fixes[0].message, /relatedTable/);

const unchanged = normalizeDashboardWidgetChildDisplayMode(pageWidgets);
assert.strictEqual(unchanged.length, 0);

const catalogWidgets = [
{
id: "catalogTable",
type: "table-entities-explorer",
displayMode: "tabs",
},
];
const catalogFixes = normalizeDashboardWidgetChildDisplayMode(catalogWidgets);
assert.strictEqual(catalogFixes.length, 0);
assert.strictEqual(catalogWidgets[0].displayMode, "tabs");

console.log("pageFixes.test.js: all tests passed");
}

runTests();
Loading