Skip to content

Commit 0883a80

Browse files
Isaac Teuscherclaude
andcommitted
Add missing endpoints and collapse sidebar sections
Adds ten GET endpoints from the 0.6.0 spec that were never exposed in the UI, restructures the sidebar so each resource lives in its own collapsible section, and refactors the button click handler to a data-driven template substitution loop. New endpoints: GET /assessment/{assessmentId} GET /assessment/{assessmentId}/cycle GET /audit-assessments GET /audit-assessments/{auditAssessmentId} GET /audit-assessments/{auditAssessmentId}/control-implementations GET /elements/{elementId} GET /evidence/{evidenceId} GET /evidence/{evidenceId}/artifacts GET /issues/{issueId} GET /solution-capabilities/{solutionCapabilityId} Skipped for now (each needs two IDs and would clutter the UI): the by-sub-ID lookups under /assessment/*/cycle/*, /evidence/*/artifacts/*, and /issues/*/{recommendations,deviations,milestones}/*. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent ff53366 commit 0883a80

3 files changed

Lines changed: 189 additions & 106 deletions

File tree

public/main.html

Lines changed: 113 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -42,60 +42,119 @@ <h2>Login</h2>
4242
<div class="container">
4343
<div class="api-list">
4444
<h2>API Calls</h2>
45-
46-
<h3 style="margin-top: 0; margin-bottom: 10px; color: #1467FF; border-bottom: 2px solid #1467FF; padding-bottom: 5px;">Workspace Endpoints</h3>
47-
<button class="api-button" data-endpoint="/projects">Projects</button>
48-
<button class="api-button" data-endpoint="/solution-capabilities">Solution Capabilities</button>
49-
<button class="api-button" data-endpoint="/elements">Elements</button>
50-
<button class="api-button" data-endpoint="/assessment">Assessments</button>
51-
<button class="api-button" data-endpoint="/evidence">Evidence</button>
52-
<button class="api-button" data-endpoint="/issues/oscal">Issues OSCAL</button>
53-
54-
<h3 style="margin-top: 20px; margin-bottom: 10px; color: #1467FF; border-bottom: 2px solid #1467FF; padding-bottom: 5px;">Project Endpoints</h3>
55-
<div style="margin-bottom: 10px;">
56-
<label for="projectId" style="display: block; margin-bottom: 5px; font-weight: bold;">Project:</label>
57-
<select id="projectId" name="projectId" style="width: 100%; padding: 5px; margin-bottom: 10px; box-sizing: border-box;">
58-
<option value="">Loading projects...</option>
59-
</select>
60-
<button type="button" id="refreshProjects" style="width: 100%; padding: 5px; margin-bottom: 10px; color: white; border: none; border-radius: 3px; cursor: pointer;">Refresh Projects</button>
61-
</div>
62-
<button class="api-button project-endpoint" data-endpoint-template="/projects/{projectId}" data-requires-project="true">Get Project by ID</button>
63-
<button class="api-button project-endpoint" data-endpoint-template="/projects/{projectId}/control-implementations" data-requires-project="true">Control Implementations</button>
64-
<button class="api-button project-endpoint" data-endpoint-template="/issues?projectId={projectId}" data-requires-project="true">Issues</button>
65-
66-
<h3 style="margin-top: 20px; margin-bottom: 10px; color: #1467FF; border-bottom: 2px solid #1467FF; padding-bottom: 5px;">Issue Endpoints</h3>
67-
<div style="margin-bottom: 10px;">
68-
<label for="issueId" style="display: block; margin-bottom: 5px; font-weight: bold;">Issue ID:</label>
69-
<input type="text" id="issueId" name="issueId" placeholder="Enter issue UUID" style="width: 100%; padding: 5px; margin-bottom: 10px; box-sizing: border-box;">
70-
</div>
71-
<button class="api-button issue-endpoint" data-endpoint-template="/issues/{issueId}/recommendations" data-requires-issue="true">Recommendations</button>
72-
<button class="api-button issue-endpoint" data-endpoint-template="/issues/{issueId}/deviations" data-requires-issue="true">Deviations</button>
73-
<button class="api-button issue-endpoint" data-endpoint-template="/issues/{issueId}/milestones" data-requires-issue="true">Milestones</button>
74-
75-
<h3 style="margin-top: 20px; margin-bottom: 10px; color: #1467FF; border-bottom: 2px solid #1467FF; padding-bottom: 5px;">Validator Endpoints</h3>
76-
<button class="api-button" data-endpoint="/validators">Validators</button>
77-
<div style="margin-bottom: 10px;">
78-
<label for="validatorId" style="display: block; margin-bottom: 5px; font-weight: bold;">Validator ID:</label>
79-
<input type="text" id="validatorId" name="validatorId" placeholder="Enter validator UUID" style="width: 100%; padding: 5px; margin-bottom: 10px; box-sizing: border-box;">
80-
</div>
81-
<button class="api-button validator-endpoint" data-endpoint-template="/validators/{validatorId}" data-requires-validator="true">Get Validator by ID</button>
82-
83-
<h3 style="margin-top: 20px; margin-bottom: 10px; color: #1467FF; border-bottom: 2px solid #1467FF; padding-bottom: 5px;">Stack Endpoints</h3>
84-
<button class="api-button" data-endpoint="/stacks">Stacks</button>
85-
<div style="margin-bottom: 10px;">
86-
<label for="stackId" style="display: block; margin-bottom: 5px; font-weight: bold;">Stack ID:</label>
87-
<input type="text" id="stackId" name="stackId" placeholder="Enter stack UUID" style="width: 100%; padding: 5px; margin-bottom: 10px; box-sizing: border-box;">
88-
</div>
89-
<button class="api-button stack-endpoint" data-endpoint-template="/stacks/{stackId}" data-requires-stack="true">Get Stack by ID</button>
90-
91-
<h3 style="margin-top: 20px; margin-bottom: 10px; color: #1467FF; border-bottom: 2px solid #1467FF; padding-bottom: 5px;">Script Endpoints</h3>
92-
<button class="api-button" data-endpoint="/scripts">Scripts</button>
93-
<div style="margin-bottom: 10px;">
94-
<label for="scriptId" style="display: block; margin-bottom: 5px; font-weight: bold;">Script ID:</label>
95-
<input type="text" id="scriptId" name="scriptId" placeholder="Enter script UUID" style="width: 100%; padding: 5px; margin-bottom: 10px; box-sizing: border-box;">
96-
</div>
97-
<button class="api-button script-endpoint" data-endpoint-template="/scripts/{scriptId}" data-requires-script="true">Get Script by ID</button>
98-
<br/>
45+
46+
<details class="endpoint-group" open>
47+
<summary>Project Endpoints</summary>
48+
<button class="api-button" data-endpoint="/projects">Projects</button>
49+
<div style="margin-bottom: 10px;">
50+
<label for="projectId" class="id-label">Project:</label>
51+
<select id="projectId" name="projectId" class="id-input">
52+
<option value="">Loading projects...</option>
53+
</select>
54+
<button type="button" id="refreshProjects" style="width: 100%; padding: 5px; margin-bottom: 10px; color: white; border: none; border-radius: 3px; cursor: pointer;">Refresh Projects</button>
55+
</div>
56+
<button class="api-button project-endpoint" data-endpoint-template="/projects/{projectId}" data-requires-project="true">Get Project by ID</button>
57+
<button class="api-button project-endpoint" data-endpoint-template="/projects/{projectId}/control-implementations" data-requires-project="true">Control Implementations</button>
58+
<button class="api-button project-endpoint" data-endpoint-template="/issues?projectId={projectId}" data-requires-project="true">Issues (by Project)</button>
59+
</details>
60+
61+
<details class="endpoint-group">
62+
<summary>Assessment Endpoints</summary>
63+
<button class="api-button" data-endpoint="/assessment">Assessments</button>
64+
<div style="margin-bottom: 10px;">
65+
<label for="assessmentId" class="id-label">Assessment ID:</label>
66+
<input type="text" id="assessmentId" name="assessmentId" placeholder="Enter assessment UUID" class="id-input">
67+
</div>
68+
<button class="api-button assessment-endpoint" data-endpoint-template="/assessment/{assessmentId}" data-requires-assessment="true">Get Assessment by ID</button>
69+
<button class="api-button assessment-endpoint" data-endpoint-template="/assessment/{assessmentId}/cycle" data-requires-assessment="true">Assessment Cycles</button>
70+
</details>
71+
72+
<details class="endpoint-group">
73+
<summary>Audit Assessment Endpoints</summary>
74+
<button class="api-button" data-endpoint="/audit-assessments">Audit Assessments</button>
75+
<div style="margin-bottom: 10px;">
76+
<label for="auditAssessmentId" class="id-label">Audit Assessment ID:</label>
77+
<input type="text" id="auditAssessmentId" name="auditAssessmentId" placeholder="Enter audit assessment UUID" class="id-input">
78+
</div>
79+
<button class="api-button audit-assessment-endpoint" data-endpoint-template="/audit-assessments/{auditAssessmentId}" data-requires-audit-assessment="true">Get Audit Assessment by ID</button>
80+
<button class="api-button audit-assessment-endpoint" data-endpoint-template="/audit-assessments/{auditAssessmentId}/control-implementations" data-requires-audit-assessment="true">Control Implementations</button>
81+
</details>
82+
83+
<details class="endpoint-group">
84+
<summary>Evidence Endpoints</summary>
85+
<button class="api-button" data-endpoint="/evidence">Evidence</button>
86+
<div style="margin-bottom: 10px;">
87+
<label for="evidenceId" class="id-label">Evidence ID:</label>
88+
<input type="text" id="evidenceId" name="evidenceId" placeholder="Enter evidence UUID" class="id-input">
89+
</div>
90+
<button class="api-button evidence-endpoint" data-endpoint-template="/evidence/{evidenceId}" data-requires-evidence="true">Get Evidence by ID</button>
91+
<button class="api-button evidence-endpoint" data-endpoint-template="/evidence/{evidenceId}/artifacts" data-requires-evidence="true">Evidence Artifacts</button>
92+
</details>
93+
94+
<details class="endpoint-group">
95+
<summary>Solution Capability Endpoints</summary>
96+
<button class="api-button" data-endpoint="/solution-capabilities">Solution Capabilities</button>
97+
<div style="margin-bottom: 10px;">
98+
<label for="solutionCapabilityId" class="id-label">Solution Capability ID:</label>
99+
<input type="text" id="solutionCapabilityId" name="solutionCapabilityId" placeholder="Enter solution capability UUID" class="id-input">
100+
</div>
101+
<button class="api-button solution-capability-endpoint" data-endpoint-template="/solution-capabilities/{solutionCapabilityId}" data-requires-solution-capability="true">Get Solution Capability by ID</button>
102+
</details>
103+
104+
<details class="endpoint-group">
105+
<summary>Element Endpoints</summary>
106+
<button class="api-button" data-endpoint="/elements">Elements</button>
107+
<div style="margin-bottom: 10px;">
108+
<label for="elementId" class="id-label">Element ID:</label>
109+
<input type="text" id="elementId" name="elementId" placeholder="Enter element UUID" class="id-input">
110+
</div>
111+
<button class="api-button element-endpoint" data-endpoint-template="/elements/{elementId}" data-requires-element="true">Get Element by ID</button>
112+
</details>
113+
114+
<details class="endpoint-group">
115+
<summary>Issue Endpoints</summary>
116+
<button class="api-button" data-endpoint="/issues/oscal">Issues OSCAL</button>
117+
<div style="margin-bottom: 10px;">
118+
<label for="issueId" class="id-label">Issue ID:</label>
119+
<input type="text" id="issueId" name="issueId" placeholder="Enter issue UUID" class="id-input">
120+
</div>
121+
<button class="api-button issue-endpoint" data-endpoint-template="/issues/{issueId}" data-requires-issue="true">Get Issue by ID</button>
122+
<button class="api-button issue-endpoint" data-endpoint-template="/issues/{issueId}/recommendations" data-requires-issue="true">Recommendations</button>
123+
<button class="api-button issue-endpoint" data-endpoint-template="/issues/{issueId}/deviations" data-requires-issue="true">Deviations</button>
124+
<button class="api-button issue-endpoint" data-endpoint-template="/issues/{issueId}/milestones" data-requires-issue="true">Milestones</button>
125+
</details>
126+
127+
<details class="endpoint-group">
128+
<summary>Validator Endpoints</summary>
129+
<button class="api-button" data-endpoint="/validators">Validators</button>
130+
<div style="margin-bottom: 10px;">
131+
<label for="validatorId" class="id-label">Validator ID:</label>
132+
<input type="text" id="validatorId" name="validatorId" placeholder="Enter validator UUID" class="id-input">
133+
</div>
134+
<button class="api-button validator-endpoint" data-endpoint-template="/validators/{validatorId}" data-requires-validator="true">Get Validator by ID</button>
135+
</details>
136+
137+
<details class="endpoint-group">
138+
<summary>Stack Endpoints</summary>
139+
<button class="api-button" data-endpoint="/stacks">Stacks</button>
140+
<div style="margin-bottom: 10px;">
141+
<label for="stackId" class="id-label">Stack ID:</label>
142+
<input type="text" id="stackId" name="stackId" placeholder="Enter stack UUID" class="id-input">
143+
</div>
144+
<button class="api-button stack-endpoint" data-endpoint-template="/stacks/{stackId}" data-requires-stack="true">Get Stack by ID</button>
145+
</details>
146+
147+
<details class="endpoint-group">
148+
<summary>Script Endpoints</summary>
149+
<button class="api-button" data-endpoint="/scripts">Scripts</button>
150+
<div style="margin-bottom: 10px;">
151+
<label for="scriptId" class="id-label">Script ID:</label>
152+
<input type="text" id="scriptId" name="scriptId" placeholder="Enter script UUID" class="id-input">
153+
</div>
154+
<button class="api-button script-endpoint" data-endpoint-template="/scripts/{scriptId}" data-requires-script="true">Get Script by ID</button>
155+
</details>
156+
157+
<br/>
99158
<form id="logout-form" action="/logout">
100159
<button type="submit">Logout</button>
101160
</form>

public/script.js

Lines changed: 23 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -89,64 +89,35 @@ loginForm.addEventListener('submit', async (event) => {
8989
}
9090
});
9191

92+
// Turn "assessmentId" into "an Assessment"; "projectId" into "a Project".
93+
function humanizePlaceholder(name) {
94+
const base = name.replace(/Id$/, '');
95+
const spaced = base.replace(/([A-Z])/g, ' $1').trim();
96+
const words = spaced.split(/\s+/).map(w => w[0].toUpperCase() + w.slice(1)).join(' ');
97+
const article = /^[aeiou]/i.test(words) ? 'an' : 'a';
98+
return { words, article };
99+
}
100+
92101
apiButtons.forEach(button => {
93102
button.addEventListener('click', () => {
94103
let endpoint = button.getAttribute('data-endpoint');
95104
const endpointTemplate = button.getAttribute('data-endpoint-template');
96-
const requiresProject = button.getAttribute('data-requires-project') === 'true';
97-
const requiresIssue = button.getAttribute('data-requires-issue') === 'true';
98-
const requiresValidator = button.getAttribute('data-requires-validator') === 'true';
99-
const requiresStack = button.getAttribute('data-requires-stack') === 'true';
100-
const requiresScript = button.getAttribute('data-requires-script') === 'true';
101-
102-
// If button requires a project ID, use the template and replace {projectId}
103-
if (requiresProject && endpointTemplate) {
104-
const projectId = document.getElementById('projectId').value.trim();
105-
if (!projectId) {
106-
alert('Please select a Project first');
107-
return;
108-
}
109-
endpoint = endpointTemplate.replace('{projectId}', encodeURIComponent(projectId));
110-
}
111-
112-
// If button requires an issue ID, use the template and replace {issueId}
113-
if (requiresIssue && endpointTemplate) {
114-
const issueId = document.getElementById('issueId').value.trim();
115-
if (!issueId) {
116-
alert('Please enter an Issue ID first');
117-
return;
118-
}
119-
endpoint = endpointTemplate.replace('{issueId}', encodeURIComponent(issueId));
120-
}
121105

122-
// If button requires a validator ID, use the template and replace {validatorId}
123-
if (requiresValidator && endpointTemplate) {
124-
const validatorId = document.getElementById('validatorId').value.trim();
125-
if (!validatorId) {
126-
alert('Please enter a Validator ID first');
127-
return;
128-
}
129-
endpoint = endpointTemplate.replace('{validatorId}', encodeURIComponent(validatorId));
130-
}
131-
132-
// If button requires a stack ID, use the template and replace {stackId}
133-
if (requiresStack && endpointTemplate) {
134-
const stackId = document.getElementById('stackId').value.trim();
135-
if (!stackId) {
136-
alert('Please enter a Stack ID first');
137-
return;
138-
}
139-
endpoint = endpointTemplate.replace('{stackId}', encodeURIComponent(stackId));
140-
}
141-
142-
// If button requires a script ID, use the template and replace {scriptId}
143-
if (requiresScript && endpointTemplate) {
144-
const scriptId = document.getElementById('scriptId').value.trim();
145-
if (!scriptId) {
146-
alert('Please enter a Script ID first');
147-
return;
106+
// Substitute every {placeholderId} in the template from an input with the same id.
107+
if (endpointTemplate) {
108+
endpoint = endpointTemplate;
109+
const placeholders = [...endpointTemplate.matchAll(/\{(\w+)\}/g)].map(m => m[1]);
110+
for (const name of placeholders) {
111+
const el = document.getElementById(name);
112+
const value = el ? el.value.trim() : '';
113+
if (!value) {
114+
const { words, article } = humanizePlaceholder(name);
115+
const verb = el && el.tagName === 'SELECT' ? 'select' : 'enter';
116+
alert(`Please ${verb} ${article} ${words} first`);
117+
return;
118+
}
119+
endpoint = endpoint.replace(`{${name}}`, encodeURIComponent(value));
148120
}
149-
endpoint = endpointTemplate.replace('{scriptId}', encodeURIComponent(scriptId));
150121
}
151122

152123
fetch('/execute', {

public/style.css

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,58 @@ h3 {
7777
background-color: #5a6268;
7878
}
7979

80+
.endpoint-group {
81+
margin-bottom: 6px;
82+
border: 1px solid #d5d5d5;
83+
border-radius: 4px;
84+
background-color: #fafafa;
85+
padding: 0 8px;
86+
}
87+
88+
.endpoint-group[open] {
89+
padding-bottom: 8px;
90+
}
91+
92+
.endpoint-group > summary {
93+
cursor: pointer;
94+
padding: 8px 4px;
95+
font-weight: bold;
96+
font-size: 13px;
97+
color: #1467FF;
98+
list-style: none;
99+
outline: none;
100+
}
101+
102+
.endpoint-group > summary::-webkit-details-marker {
103+
display: none;
104+
}
105+
106+
.endpoint-group > summary::before {
107+
content: "▸";
108+
display: inline-block;
109+
width: 12px;
110+
color: #1467FF;
111+
}
112+
113+
.endpoint-group[open] > summary::before {
114+
content: "▾";
115+
}
116+
117+
.endpoint-group .id-label {
118+
display: block;
119+
margin-bottom: 5px;
120+
font-weight: bold;
121+
font-size: 12px;
122+
}
123+
124+
.endpoint-group .id-input {
125+
width: 100%;
126+
padding: 5px;
127+
margin-top: 4px;
128+
margin-bottom: 10px;
129+
box-sizing: border-box;
130+
font-size: 13px;
131+
}
132+
80133
/* Add more CSS for styling as needed */
81134

0 commit comments

Comments
 (0)