Skip to content
Merged
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
155 changes: 155 additions & 0 deletions dev/test-powershell-share.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test PowerShell Share</title>
<style>
body {
font-family: monospace;
padding: 20px;
background: #f5f5f5;
}
#yasqe {
height: 300px;
border: 1px solid #ccc;
background: white;
margin-bottom: 20px;
}
#output {
margin-top: 20px;
padding: 15px;
background: white;
border: 1px solid #ddd;
white-space: pre-wrap;
font-family: 'Courier New', monospace;
font-size: 12px;
}
button {
margin: 10px 5px;
padding: 10px 20px;
cursor: pointer;
background: #4CAF50;
color: white;
border: none;
border-radius: 4px;
}
button:hover {
background: #45a049;
}
.validation {
margin-top: 20px;
padding: 10px;
background: #e8f5e9;
border: 1px solid #4CAF50;
border-radius: 4px;
}
.validation.fail {
background: #ffebee;
border-color: #f44336;
}
.validation h3 {
margin-top: 0;
}
.check {
margin: 5px 0;
}
.check.pass::before {
content: '✓ ';
color: green;
font-weight: bold;
}
.check.fail::before {
content: '✗ ';
color: red;
font-weight: bold;
}
</style>
</head>
<body>
<h1>PowerShell Share Format Test</h1>
<p>This page tests the new PowerShell share format with multi-line query and sparql-generated output file.</p>

<div id="yasqe"></div>
<button id="testBtn">Generate PowerShell Command</button>

<div id="output"></div>
<div id="validation"></div>

<script type="module">
import Yasqe from '../packages/yasqe/src/index.ts';

// Initialize YASQE
const yasqe = new Yasqe(document.getElementById('yasqe'), {
value: `SELECT ?s ?p ?o WHERE {
?s ?p ?o .
FILTER(?p = rdf:type)
}
ORDER BY ?s DESC(?o)
LIMIT 10`,
requestConfig: {
endpoint: 'https://dbpedia.org/sparql',
method: 'POST'
}
});

function testPowerShellShare() {
try {
const psCommand = yasqe.getAsPowerShellString();
const output = document.getElementById('output');
const validationDiv = document.getElementById('validation');
output.textContent = psCommand;

// Validation checks
const checks = [
['Contains $query or $update variable', /\$(query|update) = @"/g.test(psCommand)],
['Contains closing @"', /^"@$/gm.test(psCommand)],
['Contains actual query text (not encoded)', psCommand.includes('SELECT ?s ?p ?o WHERE')],
['Contains ORDER BY clause', psCommand.includes('ORDER BY')],
['Contains Body with variable reference', /Body = ".*\$(query|update).*"/g.test(psCommand)],
['Contains sparql-generated output file', psCommand.includes('sparql-generated')],
['Does NOT contain URL-encoded query', !psCommand.includes('SELECT%20')],
['Does NOT contain encoded equals', !psCommand.includes('%3D')],
];

let validationHTML = '<h3>Validation Results:</h3>';
let allPassed = true;
checks.forEach(([name, passed]) => {
validationHTML += `<div class="check ${passed ? 'pass' : 'fail'}">${name}</div>`;
if (!passed) allPassed = false;
});

validationDiv.innerHTML = validationHTML;
validationDiv.className = allPassed ? 'validation pass' : 'validation fail';

if (allPassed) {
output.style.borderColor = 'green';
output.style.backgroundColor = '#f1f8f4';
} else {
output.style.borderColor = 'red';
output.style.backgroundColor = '#fff5f5';
}

console.log('\nGenerated PowerShell Command:');
console.log(psCommand);
console.log('\nValidation Results:');
checks.forEach(([name, passed]) => {
console.log(`${passed ? '✓' : '✗'} ${name}`);
});
} catch (error) {
document.getElementById('output').textContent = 'Error: ' + error.message;
document.getElementById('validation').innerHTML = '<h3 class="fail">Error occurred!</h3><p>' + error.message + '</p>';
console.error(error);
}
}

// Setup button click handler
document.getElementById('testBtn').addEventListener('click', testPowerShellShare);

// Auto-run test on load
window.addEventListener('load', () => {
setTimeout(testPowerShellShare, 1000);
});
</script>
</body>
</html>
33 changes: 32 additions & 1 deletion packages/yasqe/src/__tests__/share-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ describe("Share Functionality", () => {
" }",
' ContentType = "application/x-www-form-urlencoded"',
' Body = "query=SELECT"',
' OutFile = "result.json"',
' OutFile = "sparql-generated.json"',
"}",
"",
"Invoke-WebRequest @params",
Expand All @@ -117,6 +117,37 @@ describe("Share Functionality", () => {
expect(psString).to.include("Headers");
expect(psString).to.include("OutFile");
expect(psString).to.include("Accept");
expect(psString).to.include("sparql-generated");
});

it("should format PowerShell commands with here-string for query", () => {
const query = "SELECT * WHERE { ?s ?p ?o }";
const lines = [
'$query = @"',
query,
'"@',
"",
"$params = @{",
' Uri = "https://example.com/sparql"',
' Method = "Post"',
" Headers = @{",
' "Accept" = "application/sparql-results+json"',
" }",
' ContentType = "application/x-www-form-urlencoded"',
' Body = "query=$([System.Net.WebUtility]::UrlEncode($query))"',
' OutFile = "sparql-generated.json"',
"}",
"",
"Invoke-WebRequest @params",
];
const psString = lines.join("\n");

expect(psString).to.include('$query = @"');
expect(psString).to.include('"@');
expect(psString).to.include(query);
expect(psString).to.include('Body = "query=$([System.Net.WebUtility]::UrlEncode($query))"');
expect(psString).to.not.include('Body = "query=`$query"'); // Should NOT escape the variable
expect(psString).to.include("sparql-generated");
});

it("should format wget commands with proper line breaks", () => {
Expand Down
48 changes: 43 additions & 5 deletions packages/yasqe/src/sparql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -520,10 +520,48 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
lines.push(headersLines.join("\n"));
lines.push(" }");
}
lines.push(` OutFile = "result.${fileExtension}"`);
lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
lines.push("}");
} else if (ajaxConfig.reqMethod === "POST") {
const body = queryString.stringify(ajaxConfig.args);
// Extract the query/update parameter and other parameters separately
// Determine the query parameter name first (query takes precedence over update)
const queryParamName = ajaxConfig.args.query !== undefined ? "query" : "update";
const queryParam = ajaxConfig.args[queryParamName];

const otherArgs: RequestArgs = {};
for (const key in ajaxConfig.args) {
if (key !== "query" && key !== "update") {
otherArgs[key] = ajaxConfig.args[key];
}
}

// Build the query string using here-string for easy editing
if (queryParam) {
// Handle both string and string[] cases - use first element if array
const queryText = Array.isArray(queryParam) ? queryParam[0] : queryParam;
lines.push(`$${queryParamName} = @"`);
lines.push(queryText);
lines.push(`"@`);
lines.push("");
}

// Build the body with the query variable and any other parameters
// The query must be URL-encoded for application/x-www-form-urlencoded
let bodyExpression: string;
const urlEncodeExpr = `[System.Net.WebUtility]::UrlEncode($${queryParamName})`;
if (queryParam && Object.keys(otherArgs).length > 0) {
// Both query variable and other args
const otherArgsString = queryString.stringify(otherArgs);
bodyExpression = `"${queryParamName}=$(${urlEncodeExpr})&${escapePowerShellString(otherArgsString)}"`;
} else if (queryParam) {
// Only query variable - use subexpression for URL encoding
bodyExpression = `"${queryParamName}=$(${urlEncodeExpr})"`;
} else {
// Only other args (shouldn't happen, but handle it)
const otherArgsString = queryString.stringify(otherArgs);
bodyExpression = `"${escapePowerShellString(otherArgsString)}"`;
}

lines.push("$params = @{");
lines.push(` Uri = "${escapePowerShellString(url)}"`);
lines.push(` Method = "Post"`);
Expand All @@ -533,8 +571,8 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
lines.push(" }");
}
lines.push(` ContentType = "application/x-www-form-urlencoded"`);
lines.push(` Body = "${escapePowerShellString(body)}"`);
lines.push(` OutFile = "result.${fileExtension}"`);
lines.push(` Body = ${bodyExpression}`);
lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
lines.push("}");
} else {
// Handle other methods (PUT, DELETE, etc.)
Expand All @@ -552,7 +590,7 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
lines.push(` ContentType = "application/x-www-form-urlencoded"`);
lines.push(` Body = "${body.replace(/"/g, '`"')}"`);
}
lines.push(` OutFile = "result.${fileExtension}"`);
lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
lines.push("}");
}

Expand Down