Skip to content

Commit 7ccbd31

Browse files
authored
fix: remove policy simulation from upstream resolution preview (#36)
* fix: remove policy simulation from upstream resolution preview - Strip policy fetch path from previewResolution(), now only loads local package state and upstream configs - Delete dead simulatePolicies() helper from upstreamChecker.js - Add WebView rendering tests for upstream content and error states without policy data * chore: add changelog entry for v2.1.1
1 parent 8d8293d commit 7ccbd31

6 files changed

Lines changed: 125 additions & 51 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.1.1 - March 2026
2+
### Fixed
3+
- Upstream Resolution Preview no longer throws an error when policy simulation endpoint returns 404
4+
15
## 2.1.0 - March 2026
26
### Terraform Export
37

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"publisher": "Cloudsmith",
44
"displayName": "Cloudsmith VS Code",
55
"description": "Access packages from a Cloudsmith instance.",
6-
"version": "2.1.0",
6+
"version": "2.1.1",
77
"license": "SEE LICENSE IN LICENSE",
88
"homepage": "https://github.com/cloudsmith-io/cloudsmith-vscode-extension/blob/main/README.md",
99
"bugs": {

test/upstreamChecker.test.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,3 +553,43 @@ suite("UpstreamChecker shared helper and format handling", () => {
553553
assert.strictEqual(updates.length, 0);
554554
});
555555
});
556+
557+
suite("UpstreamChecker preview resolution", () => {
558+
test("previewResolution does not trigger policy fetches and still returns upstream data", async () => {
559+
const checker = new UpstreamChecker({});
560+
let policyFetchCount = 0;
561+
562+
checker.existsLocally = async () => ({
563+
data: null,
564+
error: null,
565+
});
566+
checker.getUpstreamsForFormat = async () => ({
567+
data: [
568+
{
569+
name: "PyPI",
570+
upstream_url: "https://pypi.org/simple/",
571+
is_active: true,
572+
},
573+
{
574+
name: "Disabled mirror",
575+
upstream_url: "https://disabled.example/python",
576+
is_active: false,
577+
},
578+
],
579+
error: null,
580+
});
581+
checker.api.getV2 = async () => {
582+
policyFetchCount += 1;
583+
return [];
584+
};
585+
586+
const result = await checker.previewResolution("acme", "example-repo", "flask", "python");
587+
588+
assert.strictEqual(policyFetchCount, 0);
589+
assert.strictEqual("policies" in result, false);
590+
assert.strictEqual(result.canResolveViaUpstream, true);
591+
assert.strictEqual(result.upstreams.data.total, 2);
592+
assert.strictEqual(result.upstreams.data.active, 1);
593+
assert.strictEqual(result.upstreams.data.configs[0].name, "PyPI");
594+
});
595+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
const assert = require("assert");
2+
const { UpstreamPreviewProvider } = require("../views/upstreamPreviewProvider");
3+
4+
suite("UpstreamPreviewProvider Test Suite", () => {
5+
test("renders upstream resolution details without any policy section", () => {
6+
const provider = new UpstreamPreviewProvider({});
7+
8+
const html = provider._getHtmlContent({
9+
name: "flask",
10+
format: "python",
11+
workspace: "acme",
12+
repo: "example-repo",
13+
local: {
14+
data: null,
15+
error: null,
16+
},
17+
upstreams: {
18+
data: {
19+
total: 2,
20+
active: 1,
21+
configs: [
22+
{
23+
name: "PyPI",
24+
upstream_url: "https://pypi.org/simple/",
25+
is_active: true,
26+
},
27+
{
28+
name: "Legacy mirror",
29+
upstream_url: "https://legacy.example/python",
30+
is_active: false,
31+
},
32+
],
33+
},
34+
error: null,
35+
},
36+
canResolveViaUpstream: true,
37+
});
38+
39+
assert.ok(html.includes("Upstream resolution preview"));
40+
assert.ok(html.includes("PyPI"));
41+
assert.ok(html.includes("Legacy mirror"));
42+
assert.ok(html.includes("Upstreams (1 active of 2)"));
43+
assert.ok(!html.includes("Active policies"));
44+
assert.ok(!html.includes("policy simulation"));
45+
assert.ok(!html.includes("Block Until Scan"));
46+
assert.ok(!html.includes("policy evaluation"));
47+
});
48+
49+
test("renders upstream errors without expecting policy data", () => {
50+
const provider = new UpstreamPreviewProvider({});
51+
52+
const html = provider._getHtmlContent({
53+
name: "flask",
54+
format: "python",
55+
workspace: "acme",
56+
repo: "example-repo",
57+
local: {
58+
data: null,
59+
error: null,
60+
},
61+
upstreams: {
62+
data: {
63+
total: 0,
64+
active: 0,
65+
configs: [],
66+
},
67+
error: "Response status: 503 - Service Unavailable",
68+
},
69+
canResolveViaUpstream: false,
70+
});
71+
72+
assert.ok(html.includes("Could not load upstream data"));
73+
assert.ok(!html.includes("Active policies"));
74+
});
75+
});

util/upstreamChecker.js

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -450,44 +450,24 @@ class UpstreamChecker {
450450
return state.upstreams;
451451
}
452452

453-
/**
454-
* Simulate policy evaluation for a workspace.
455-
* Uses the v2 API endpoint.
456-
*
457-
* @param {string} workspace Workspace slug.
458-
* @returns {Object|null} Policy simulation results, or null on error.
459-
*/
460-
async simulatePolicies(workspace) {
461-
const result = await this.api.getV2(
462-
`workspaces/${workspace}/policies/simulate/`
463-
);
464-
if (typeof result === "string") {
465-
console.warn(`[UpstreamChecker] Policy simulation error: ${result}`);
466-
return { data: null, error: result };
467-
}
468-
return { data: result, error: null };
469-
}
470-
471453
/**
472454
* Orchestrate a full upstream resolution preview.
473-
* Checks local existence, upstream configs, and active policies.
455+
* Checks local existence and upstream configs for a package preview.
474456
*
475457
* @param {string} workspace Workspace slug.
476458
* @param {string} repo Repository slug.
477459
* @param {string} name Package name.
478460
* @param {string} format Package format.
479-
* @returns {Object} Combined result with local, upstream, and policy info.
461+
* @returns {Object} Combined result with local and upstream info.
480462
*/
481463
async previewResolution(workspace, repo, name, format) {
482-
// Run all checks in parallel
483-
const [localPkg, upstreams, policies] = await Promise.all([
464+
const [localPkg, upstreams] = await Promise.all([
484465
this.existsLocally(workspace, repo, name, format),
485466
this.getUpstreamsForFormat(workspace, repo, format),
486-
this.simulatePolicies(workspace),
487467
]);
488468

489469
const upstreamList = Array.isArray(upstreams.data) ? upstreams.data : [];
490-
const activeUpstreams = upstreamList.filter(u => u.is_active !== false);
470+
const activeUpstreams = upstreamList.filter((upstream) => upstream.is_active !== false);
491471

492472
return {
493473
name,
@@ -503,7 +483,6 @@ class UpstreamChecker {
503483
},
504484
error: upstreams.error,
505485
},
506-
policies,
507486
canResolveViaUpstream: activeUpstreams.length > 0,
508487
};
509488
}

views/upstreamPreviewProvider.js

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -59,29 +59,8 @@ class UpstreamPreviewProvider {
5959
upstreamHtml += "</tbody></table>";
6060
}
6161

62-
let policyHtml = "";
63-
if (result.policies.error) {
64-
policyHtml = `<p class="error-banner">Could not load policy simulation: ${this._escapeHtml(result.policies.error)}</p>`;
65-
} else {
66-
const pols = Array.isArray(result.policies.data) ? result.policies.data : (result.policies.data && result.policies.data.results) || [];
67-
if (pols.length === 0) {
68-
policyHtml = '<p class="muted">No active policies found.</p>';
69-
} else {
70-
policyHtml = '<table class="data-table"><thead><tr><th>Policy</th><th>Type</th><th>Action</th></tr></thead><tbody>';
71-
for (const p of pols) {
72-
policyHtml += `<tr>
73-
<td>${this._escapeHtml(p.name || p.slug_perm || "Unknown")}</td>
74-
<td>${this._escapeHtml(p.policy_type || p.type || "")}</td>
75-
<td>${this._escapeHtml(p.on_violation_quarantine ? "Quarantine" : (p.action || "Tag or warn"))}</td>
76-
</tr>`;
77-
}
78-
policyHtml += "</tbody></table>";
79-
}
80-
}
81-
8262
const resolutionSummary = result.canResolveViaUpstream
83-
? `<div class="resolution-yes">This package can likely resolve through ${result.upstreams.data.active} active upstream${result.upstreams.data.active === 1 ? "" : "s"}. ` +
84-
"If Block Until Scan is enabled, the package stays blocked until policy evaluation completes.</div>"
63+
? `<div class="resolution-yes">This package can likely resolve through ${result.upstreams.data.active} active upstream${result.upstreams.data.active === 1 ? "" : "s"}.</div>`
8564
: '<div class="resolution-no">No active upstreams for this format. Upload the package directly.</div>';
8665

8766
return `<!DOCTYPE html>
@@ -124,9 +103,6 @@ class UpstreamPreviewProvider {
124103
125104
<h3>Upstreams (${result.upstreams.data.active} active of ${result.upstreams.data.total})</h3>
126105
${upstreamHtml}
127-
128-
<h3>Active policies</h3>
129-
${policyHtml}
130106
</body>
131107
</html>`;
132108
}

0 commit comments

Comments
 (0)