Skip to content

Commit 210e9e6

Browse files
authored
Merge branch 'fixing-cors-issues' into copilot/sub-pr-67-again
2 parents 225483f + 1b34a52 commit 210e9e6

File tree

2 files changed

+64
-135
lines changed

2 files changed

+64
-135
lines changed

packages/yasgui/src/Tab.ts

Lines changed: 12 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,8 @@ WHERE {
772772
} LIMIT 1000`;
773773

774774
// Execute query in background without changing editor content
775-
this.executeBackgroundQuery(constructQuery);
775+
// Note: void operator is intentional - errors are handled in the catch block of executeBackgroundQuery
776+
void this.executeBackgroundQuery(constructQuery);
776777
};
777778

778779
private async executeBackgroundQuery(query: string) {
@@ -783,127 +784,21 @@ WHERE {
783784
this.yasr.showLoading();
784785
this.emit("queryBefore", this);
785786

786-
// Get the request configuration
787-
const requestConfig = this.yasqe.config.requestConfig;
788-
const config = typeof requestConfig === "function" ? requestConfig(this.yasqe) : requestConfig;
789-
790-
if (!config.endpoint) {
791-
throw new Error("No endpoint configured");
792-
}
793-
794-
const endpoint = typeof config.endpoint === "function" ? config.endpoint(this.yasqe) : config.endpoint;
795-
const method = typeof config.method === "function" ? config.method(this.yasqe) : config.method || "POST";
796-
let headers = typeof config.headers === "function" ? config.headers(this.yasqe) : config.headers || {};
797-
const withCredentials =
798-
typeof config.withCredentials === "function" ? config.withCredentials(this.yasqe) : config.withCredentials;
799-
800-
// Process authentication and add to headers
801-
const finalHeaders = { ...headers };
802-
let hasAuthConfigured = false;
803-
804-
try {
805-
// Check for Bearer Token authentication
806-
const bearerAuth = typeof config.bearerAuth === "function" ? config.bearerAuth(this.yasqe) : config.bearerAuth;
807-
const trimmedBearerToken = bearerAuth && bearerAuth.token ? bearerAuth.token.trim() : "";
808-
if (bearerAuth && trimmedBearerToken.length > 0) {
809-
hasAuthConfigured = true;
810-
if (finalHeaders["Authorization"] === undefined) {
811-
finalHeaders["Authorization"] = `Bearer ${trimmedBearerToken}`;
812-
}
813-
}
814-
815-
// Check for API Key authentication
816-
const apiKeyAuth = typeof config.apiKeyAuth === "function" ? config.apiKeyAuth(this.yasqe) : config.apiKeyAuth;
817-
const trimmedHeaderName = apiKeyAuth && apiKeyAuth.headerName ? apiKeyAuth.headerName.trim() : "";
818-
const trimmedApiKey = apiKeyAuth && apiKeyAuth.apiKey ? apiKeyAuth.apiKey.trim() : "";
819-
if (apiKeyAuth && trimmedHeaderName.length > 0 && trimmedApiKey.length > 0) {
820-
hasAuthConfigured = true;
821-
if (finalHeaders[trimmedHeaderName] === undefined) {
822-
finalHeaders[trimmedHeaderName] = trimmedApiKey;
823-
}
824-
}
825-
826-
// Check for Basic Authentication
827-
const basicAuth = typeof config.basicAuth === "function" ? config.basicAuth(this.yasqe) : config.basicAuth;
828-
if (basicAuth && basicAuth.username && basicAuth.password) {
829-
hasAuthConfigured = true;
830-
if (finalHeaders["Authorization"] === undefined) {
831-
const credentials = `${basicAuth.username}:${basicAuth.password}`;
832-
const encoded = Yasqe.Sparql.base64EncodeUnicode(credentials);
833-
finalHeaders["Authorization"] = `Basic ${encoded}`;
834-
}
835-
}
836-
} catch (error) {
837-
console.warn("Failed to configure authentication for background query:", error);
838-
}
839-
840-
// Determine credentials mode - enable if auth is configured or explicitly set
841-
const shouldIncludeCredentials = hasAuthConfigured || withCredentials;
842-
843-
// Prepare request
844-
const searchParams = new URLSearchParams();
845-
searchParams.append("query", query);
846-
847-
// Add any additional args
848-
if (config.args && Array.isArray(config.args)) {
849-
config.args.forEach((arg: any) => {
850-
if (arg.name && arg.value) {
851-
searchParams.append(arg.name, arg.value);
852-
}
853-
});
854-
}
787+
// Track query execution time
788+
const startTime = Date.now();
855789

856-
const fetchOptions: RequestInit = {
857-
method: method,
858-
headers: {
859-
Accept: "text/turtle",
860-
...finalHeaders,
790+
// Use yasqe's executeQuery with custom query and accept header
791+
const queryResponse = await Yasqe.Sparql.executeQuery(
792+
this.yasqe,
793+
undefined, // Use default config
794+
{
795+
customQuery: query,
796+
customAccept: "text/turtle",
861797
},
862-
credentials: shouldIncludeCredentials ? "include" : "same-origin",
863-
mode: "cors",
864-
};
865-
866-
let url = endpoint;
867-
if (method === "POST") {
868-
fetchOptions.headers = {
869-
...fetchOptions.headers,
870-
"Content-Type": "application/x-www-form-urlencoded",
871-
};
872-
fetchOptions.body = searchParams.toString();
873-
} else {
874-
const urlObj = new URL(endpoint);
875-
searchParams.forEach((value, key) => {
876-
urlObj.searchParams.append(key, value);
877-
});
878-
url = urlObj.toString();
879-
}
798+
);
880799

881-
const startTime = Date.now();
882-
const response = await fetch(url, fetchOptions);
883800
const duration = Date.now() - startTime;
884801

885-
const result = await response.text();
886-
887-
// Create a query response object similar to what Yasqe produces
888-
// This includes headers so the Parser can detect the content type
889-
const queryResponse = {
890-
ok: response.ok,
891-
status: response.status,
892-
statusText: response.statusText,
893-
headers: response.headers,
894-
type: response.type,
895-
content: result,
896-
};
897-
898-
if (!response.ok) {
899-
// For HTTP errors (4xx, 5xx), pass the error with status to yasr
900-
const error: any = new Error(result || response.statusText);
901-
error.status = response.status;
902-
error.statusText = response.statusText;
903-
error.response = queryResponse;
904-
throw error;
905-
}
906-
907802
// Set the response in Yasr
908803
this.yasr.setResponse(queryResponse, duration);
909804

@@ -923,15 +818,6 @@ WHERE {
923818
const errorObj: any = error;
924819
let errorText = error instanceof Error ? error.message : String(error);
925820

926-
// Enhance error message for fetch failures
927-
if (
928-
error instanceof Error &&
929-
!errorObj.status &&
930-
(error.message.includes("Failed to fetch") || error.message.includes("NetworkError"))
931-
) {
932-
errorText = `${error.message}. The server may have returned an error response (check browser dev tools), but CORS headers are preventing JavaScript from accessing it. Ensure the endpoint returns proper CORS headers even for error responses (Access-Control-Allow-Origin, etc.).`;
933-
}
934-
935821
this.yasr.setResponse(
936822
{
937823
error: {

packages/yasqe/src/sparql.ts

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,16 @@ export function getAjaxConfig(
150150
*/
151151
}
152152

153-
export async function executeQuery(yasqe: Yasqe, config?: YasqeAjaxConfig): Promise<any> {
153+
export interface ExecuteQueryOptions {
154+
customQuery?: string;
155+
customAccept?: string;
156+
}
157+
158+
export async function executeQuery(
159+
yasqe: Yasqe,
160+
config?: YasqeAjaxConfig,
161+
options?: ExecuteQueryOptions,
162+
): Promise<any> {
154163
const queryStart = Date.now();
155164
try {
156165
yasqe.emit("queryBefore", yasqe, config);
@@ -160,10 +169,13 @@ export async function executeQuery(yasqe: Yasqe, config?: YasqeAjaxConfig): Prom
160169
}
161170
const abortController = new AbortController();
162171

172+
// Use custom accept header if provided, otherwise use the default
173+
const acceptHeader = options?.customAccept || populatedConfig.accept;
174+
163175
const fetchOptions: RequestInit = {
164176
method: populatedConfig.reqMethod,
165177
headers: {
166-
Accept: populatedConfig.accept,
178+
Accept: acceptHeader,
167179
...(populatedConfig.headers || {}),
168180
},
169181
credentials: populatedConfig.withCredentials ? "include" : "same-origin",
@@ -174,14 +186,45 @@ export async function executeQuery(yasqe: Yasqe, config?: YasqeAjaxConfig): Prom
174186
(fetchOptions.headers as Record<string, string>)["Content-Type"] = "application/x-www-form-urlencoded";
175187
}
176188
const searchParams = new URLSearchParams();
177-
for (const key in populatedConfig.args) {
178-
const value = populatedConfig.args[key];
179-
if (Array.isArray(value)) {
180-
value.forEach((v) => searchParams.append(key, v));
181-
} else {
182-
searchParams.append(key, value);
189+
190+
// Helper function to append args to search params
191+
const appendArgsToParams = (args: RequestArgs, excludeKeys: string[] = []) => {
192+
for (const key in args) {
193+
if (!excludeKeys.includes(key)) {
194+
const value = args[key];
195+
if (Array.isArray(value)) {
196+
value.forEach((v) => searchParams.append(key, v));
197+
} else {
198+
searchParams.append(key, value);
199+
}
200+
}
201+
}
202+
};
203+
204+
// Helper function to determine the query parameter name
205+
// SPARQL queries use 'query' parameter, updates use 'update' parameter
206+
const getQueryParameterName = (args: RequestArgs): string => {
207+
if (args.query !== undefined) {
208+
return "query";
209+
} else if (args.update !== undefined) {
210+
return "update";
183211
}
212+
// Default to 'query' for standard SPARQL SELECT/CONSTRUCT/DESCRIBE/ASK queries
213+
return "query";
214+
};
215+
216+
// Use custom query if provided, otherwise use the args from config
217+
if (options?.customQuery) {
218+
const queryArg = getQueryParameterName(populatedConfig.args);
219+
searchParams.append(queryArg, options.customQuery);
220+
221+
// Add other args except the query/update parameter
222+
appendArgsToParams(populatedConfig.args, ["query", "update"]);
223+
} else {
224+
// Add all args from config
225+
appendArgsToParams(populatedConfig.args);
184226
}
227+
185228
if (populatedConfig.reqMethod === "POST") {
186229
fetchOptions.body = searchParams.toString();
187230
} else {
@@ -261,7 +304,7 @@ export function getUrlArguments(yasqe: Yasqe, _config: Config["requestConfig"]):
261304
const defaultGraphs = isFunction(config.defaultGraphs) ? config.defaultGraphs(yasqe) : config.defaultGraphs;
262305
if (defaultGraphs && defaultGraphs.length > 0) {
263306
let argName = queryMode == "query" ? "default-graph-uri" : "using-graph-uri ";
264-
data[argName] = namedGraphs;
307+
data[argName] = defaultGraphs;
265308
}
266309

267310
/**

0 commit comments

Comments
 (0)