Skip to content

Commit 59ea97e

Browse files
committed
Merge branch 'main' of github.com:Matdata-eu/Yasgui
2 parents 7ab6f2e + d31612a commit 59ea97e

3 files changed

Lines changed: 230 additions & 6 deletions

File tree

dev/test-powershell-share.html

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Test PowerShell Share</title>
7+
<style>
8+
body {
9+
font-family: monospace;
10+
padding: 20px;
11+
background: #f5f5f5;
12+
}
13+
#yasqe {
14+
height: 300px;
15+
border: 1px solid #ccc;
16+
background: white;
17+
margin-bottom: 20px;
18+
}
19+
#output {
20+
margin-top: 20px;
21+
padding: 15px;
22+
background: white;
23+
border: 1px solid #ddd;
24+
white-space: pre-wrap;
25+
font-family: 'Courier New', monospace;
26+
font-size: 12px;
27+
}
28+
button {
29+
margin: 10px 5px;
30+
padding: 10px 20px;
31+
cursor: pointer;
32+
background: #4CAF50;
33+
color: white;
34+
border: none;
35+
border-radius: 4px;
36+
}
37+
button:hover {
38+
background: #45a049;
39+
}
40+
.validation {
41+
margin-top: 20px;
42+
padding: 10px;
43+
background: #e8f5e9;
44+
border: 1px solid #4CAF50;
45+
border-radius: 4px;
46+
}
47+
.validation.fail {
48+
background: #ffebee;
49+
border-color: #f44336;
50+
}
51+
.validation h3 {
52+
margin-top: 0;
53+
}
54+
.check {
55+
margin: 5px 0;
56+
}
57+
.check.pass::before {
58+
content: '✓ ';
59+
color: green;
60+
font-weight: bold;
61+
}
62+
.check.fail::before {
63+
content: '✗ ';
64+
color: red;
65+
font-weight: bold;
66+
}
67+
</style>
68+
</head>
69+
<body>
70+
<h1>PowerShell Share Format Test</h1>
71+
<p>This page tests the new PowerShell share format with multi-line query and sparql-generated output file.</p>
72+
73+
<div id="yasqe"></div>
74+
<button id="testBtn">Generate PowerShell Command</button>
75+
76+
<div id="output"></div>
77+
<div id="validation"></div>
78+
79+
<script type="module">
80+
import Yasqe from '../packages/yasqe/src/index.ts';
81+
82+
// Initialize YASQE
83+
const yasqe = new Yasqe(document.getElementById('yasqe'), {
84+
value: `SELECT ?s ?p ?o WHERE {
85+
?s ?p ?o .
86+
FILTER(?p = rdf:type)
87+
}
88+
ORDER BY ?s DESC(?o)
89+
LIMIT 10`,
90+
requestConfig: {
91+
endpoint: 'https://dbpedia.org/sparql',
92+
method: 'POST'
93+
}
94+
});
95+
96+
function testPowerShellShare() {
97+
try {
98+
const psCommand = yasqe.getAsPowerShellString();
99+
const output = document.getElementById('output');
100+
const validationDiv = document.getElementById('validation');
101+
output.textContent = psCommand;
102+
103+
// Validation checks
104+
const checks = [
105+
['Contains $query or $update variable', /\$(query|update) = @"/g.test(psCommand)],
106+
['Contains closing @"', /^"@$/gm.test(psCommand)],
107+
['Contains actual query text (not encoded)', psCommand.includes('SELECT ?s ?p ?o WHERE')],
108+
['Contains ORDER BY clause', psCommand.includes('ORDER BY')],
109+
['Contains Body with variable reference', /Body = ".*\$(query|update).*"/g.test(psCommand)],
110+
['Contains sparql-generated output file', psCommand.includes('sparql-generated')],
111+
['Does NOT contain URL-encoded query', !psCommand.includes('SELECT%20')],
112+
['Does NOT contain encoded equals', !psCommand.includes('%3D')],
113+
];
114+
115+
let validationHTML = '<h3>Validation Results:</h3>';
116+
let allPassed = true;
117+
checks.forEach(([name, passed]) => {
118+
validationHTML += `<div class="check ${passed ? 'pass' : 'fail'}">${name}</div>`;
119+
if (!passed) allPassed = false;
120+
});
121+
122+
validationDiv.innerHTML = validationHTML;
123+
validationDiv.className = allPassed ? 'validation pass' : 'validation fail';
124+
125+
if (allPassed) {
126+
output.style.borderColor = 'green';
127+
output.style.backgroundColor = '#f1f8f4';
128+
} else {
129+
output.style.borderColor = 'red';
130+
output.style.backgroundColor = '#fff5f5';
131+
}
132+
133+
console.log('\nGenerated PowerShell Command:');
134+
console.log(psCommand);
135+
console.log('\nValidation Results:');
136+
checks.forEach(([name, passed]) => {
137+
console.log(`${passed ? '✓' : '✗'} ${name}`);
138+
});
139+
} catch (error) {
140+
document.getElementById('output').textContent = 'Error: ' + error.message;
141+
document.getElementById('validation').innerHTML = '<h3 class="fail">Error occurred!</h3><p>' + error.message + '</p>';
142+
console.error(error);
143+
}
144+
}
145+
146+
// Setup button click handler
147+
document.getElementById('testBtn').addEventListener('click', testPowerShellShare);
148+
149+
// Auto-run test on load
150+
window.addEventListener('load', () => {
151+
setTimeout(testPowerShellShare, 1000);
152+
});
153+
</script>
154+
</body>
155+
</html>

packages/yasqe/src/__tests__/share-test.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ describe("Share Functionality", () => {
105105
" }",
106106
' ContentType = "application/x-www-form-urlencoded"',
107107
' Body = "query=SELECT"',
108-
' OutFile = "result.json"',
108+
' OutFile = "sparql-generated.json"',
109109
"}",
110110
"",
111111
"Invoke-WebRequest @params",
@@ -117,6 +117,37 @@ describe("Share Functionality", () => {
117117
expect(psString).to.include("Headers");
118118
expect(psString).to.include("OutFile");
119119
expect(psString).to.include("Accept");
120+
expect(psString).to.include("sparql-generated");
121+
});
122+
123+
it("should format PowerShell commands with here-string for query", () => {
124+
const query = "SELECT * WHERE { ?s ?p ?o }";
125+
const lines = [
126+
'$query = @"',
127+
query,
128+
'"@',
129+
"",
130+
"$params = @{",
131+
' Uri = "https://example.com/sparql"',
132+
' Method = "Post"',
133+
" Headers = @{",
134+
' "Accept" = "application/sparql-results+json"',
135+
" }",
136+
' ContentType = "application/x-www-form-urlencoded"',
137+
' Body = "query=$([System.Net.WebUtility]::UrlEncode($query))"',
138+
' OutFile = "sparql-generated.json"',
139+
"}",
140+
"",
141+
"Invoke-WebRequest @params",
142+
];
143+
const psString = lines.join("\n");
144+
145+
expect(psString).to.include('$query = @"');
146+
expect(psString).to.include('"@');
147+
expect(psString).to.include(query);
148+
expect(psString).to.include('Body = "query=$([System.Net.WebUtility]::UrlEncode($query))"');
149+
expect(psString).to.not.include('Body = "query=`$query"'); // Should NOT escape the variable
150+
expect(psString).to.include("sparql-generated");
120151
});
121152

122153
it("should format wget commands with proper line breaks", () => {

packages/yasqe/src/sparql.ts

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -520,10 +520,48 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
520520
lines.push(headersLines.join("\n"));
521521
lines.push(" }");
522522
}
523-
lines.push(` OutFile = "result.${fileExtension}"`);
523+
lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
524524
lines.push("}");
525525
} else if (ajaxConfig.reqMethod === "POST") {
526-
const body = queryString.stringify(ajaxConfig.args);
526+
// Extract the query/update parameter and other parameters separately
527+
// Determine the query parameter name first (query takes precedence over update)
528+
const queryParamName = ajaxConfig.args.query !== undefined ? "query" : "update";
529+
const queryParam = ajaxConfig.args[queryParamName];
530+
531+
const otherArgs: RequestArgs = {};
532+
for (const key in ajaxConfig.args) {
533+
if (key !== "query" && key !== "update") {
534+
otherArgs[key] = ajaxConfig.args[key];
535+
}
536+
}
537+
538+
// Build the query string using here-string for easy editing
539+
if (queryParam) {
540+
// Handle both string and string[] cases - use first element if array
541+
const queryText = Array.isArray(queryParam) ? queryParam[0] : queryParam;
542+
lines.push(`$${queryParamName} = @"`);
543+
lines.push(queryText);
544+
lines.push(`"@`);
545+
lines.push("");
546+
}
547+
548+
// Build the body with the query variable and any other parameters
549+
// The query must be URL-encoded for application/x-www-form-urlencoded
550+
let bodyExpression: string;
551+
const urlEncodeExpr = `[System.Net.WebUtility]::UrlEncode($${queryParamName})`;
552+
if (queryParam && Object.keys(otherArgs).length > 0) {
553+
// Both query variable and other args
554+
const otherArgsString = queryString.stringify(otherArgs);
555+
bodyExpression = `"${queryParamName}=$(${urlEncodeExpr})&${escapePowerShellString(otherArgsString)}"`;
556+
} else if (queryParam) {
557+
// Only query variable - use subexpression for URL encoding
558+
bodyExpression = `"${queryParamName}=$(${urlEncodeExpr})"`;
559+
} else {
560+
// Only other args (shouldn't happen, but handle it)
561+
const otherArgsString = queryString.stringify(otherArgs);
562+
bodyExpression = `"${escapePowerShellString(otherArgsString)}"`;
563+
}
564+
527565
lines.push("$params = @{");
528566
lines.push(` Uri = "${escapePowerShellString(url)}"`);
529567
lines.push(` Method = "Post"`);
@@ -533,8 +571,8 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
533571
lines.push(" }");
534572
}
535573
lines.push(` ContentType = "application/x-www-form-urlencoded"`);
536-
lines.push(` Body = "${escapePowerShellString(body)}"`);
537-
lines.push(` OutFile = "result.${fileExtension}"`);
574+
lines.push(` Body = ${bodyExpression}`);
575+
lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
538576
lines.push("}");
539577
} else {
540578
// Handle other methods (PUT, DELETE, etc.)
@@ -552,7 +590,7 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
552590
lines.push(` ContentType = "application/x-www-form-urlencoded"`);
553591
lines.push(` Body = "${body.replace(/"/g, '`"')}"`);
554592
}
555-
lines.push(` OutFile = "result.${fileExtension}"`);
593+
lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
556594
lines.push("}");
557595
}
558596

0 commit comments

Comments
 (0)