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
10 changes: 10 additions & 0 deletions .changeset/endpoint-describe-panel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@matdata/yasgui": minor
"@matdata/yasqe": minor
---

Add an "Endpoint overview" panel to get to know a SPARQL endpoint while writing queries (Describe endpoint button or `F8`).

The panel shows the endpoint's SPARQL Service Description and VoID description (when published) and offers bounded overview queries for named graphs, triple counts, namespaces, classes, properties, SKOS schemes, shapes, sample instances, hubs, languages, links and time/geo properties. Results are remembered per endpoint, also when other queries are executed or the page is reloaded. The panel docks next to the editor, can be pinned, collapsed and resized, and follows the endpoint of the active tab. Queries and categories can be extended through the new `endpointDescribe` configuration.

YASQE's `executeQuery` gets a `skipGraphArgs` option to leave out the tab's default/named graph arguments.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Matgui provides a complete SPARQL development environment with powerful features
- **[Query Formatting](https://github.com/Matdata-eu/Matgui/blob/main/docs/user-guide.md#query-formatting)** - One-click query beautification with configurable formatters
- **[Prefix Management](https://github.com/Matdata-eu/Matgui/blob/main/docs/user-guide.md#prefix-management)** - Auto-capture and reuse PREFIX declarations
- **[URI Explorer](https://github.com/Matdata-eu/Matgui/blob/main/docs/user-guide.md#uri-explorer)** - Ctrl+Click URIs to explore connections
- **[Endpoint Overview](https://github.com/Matdata-eu/Matgui/blob/main/docs/user-guide.md#endpoint-overview)** - Explore an unfamiliar endpoint: service description, VoID and overview queries (classes, properties, graphs, languages, links, geo) in a side panel
- **[Keyboard Shortcuts](https://github.com/Matdata-eu/Matgui/blob/main/docs/user-guide.md#keyboard-shortcuts)** - Efficient query development workflow

### 📊 Powerful Visualizations
Expand Down Expand Up @@ -248,6 +249,7 @@ const yasgui = new Yasgui(document.getElementById('yasgui'), {
theme: 'dark', // 'light' or 'dark'
orientation: 'horizontal', // 'horizontal' or 'vertical'
showSnippetsBar: true, // Show code snippets
endpointDescribe: { enabled: true }, // "Describe endpoint" panel (F8)

// Persistence
persistenceId: 'my-yasgui-instance', // Custom storage ID
Expand Down
120 changes: 120 additions & 0 deletions docs/developer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ This comprehensive guide covers everything developers need to know to integrate,
- [Examples](#examples)
- [Security Best Practices](#security-best-practices)
- [Endpoint Buttons Configuration](#endpoint-buttons-configuration)
- [Endpoint Describe Configuration](#endpoint-describe-configuration)
- [Theme Configuration](#theme-configuration)
- [API Reference](#api-reference)
- [Yasgui Class](#yasgui-class)
Expand All @@ -69,6 +70,7 @@ This comprehensive guide covers everything developers need to know to integrate,
- [`setTheme(theme: 'light' | 'dark'): void`](#setthemetheme-light--dark-void)
- [`getTheme(): 'light' | 'dark'`](#gettheme-light--dark)
- [`toggleTheme(): 'light' | 'dark'`](#toggletheme-light--dark)
- [`endpointDescribe: EndpointDescribePanel | undefined`](#endpointdescribe-endpointdescribepanel--undefined)
- [Tab Class](#tab-class)
- [Methods](#methods-1)
- [`getName(): string`](#getname-string)
Expand All @@ -79,6 +81,8 @@ This comprehensive guide covers everything developers need to know to integrate,
- [`query(): Promise<void>`](#query-promisevoid)
- [`setQuery(query: string): void`](#setqueryquery-string-void)
- [`getQuery(): string`](#getquery-string)
- [`runBackgroundQuery(query: string, options?): Promise<any>`](#runbackgroundqueryquery-string-options-promiseany)
- [`getRequestInit(): Promise<{ headers, withCredentials } | undefined>`](#getrequestinit-promise-headers-withcredentials---undefined)
- [Yasqe Class](#yasqe-class)
- [Methods](#methods-2)
- [`getValue(): string`](#getvalue-string)
Expand Down Expand Up @@ -689,6 +693,9 @@ interface Config {

// Layout orientation: 'vertical' or 'horizontal'
orientation?: 'vertical' | 'horizontal'; // default: 'vertical'

// "Describe endpoint" panel (see Endpoint Describe Configuration)
endpointDescribe: EndpointDescribeConfig;
}
```

Expand Down Expand Up @@ -1955,6 +1962,81 @@ You can customize button appearance using CSS variables:
}
```

### Endpoint Describe Configuration

The **Endpoint overview** panel shows the SPARQL service description and VoID description of the current endpoint, and a set of predefined overview queries (classes, properties, named graphs, languages, links, time and geo). Results are cached per endpoint under their own localStorage key (`<persistenceId>_endpointDescribe`), separately from the main configuration.

```typescript
interface EndpointDescribeConfig {
// Show the "Describe endpoint" button and panel
enabled: boolean; // default: true

// Replace or extend the default describe queries
queries?: DescribeQuery[] | ((defaults: DescribeQuery[]) => DescribeQuery[]);

// Replace or extend the default categories
categories?: DescribeCategory[] | ((defaults: DescribeCategory[]) => DescribeCategory[]);

// Timeout of a single describe query, in milliseconds
timeoutMs: number; // default: 60000

// Maximum number of describe queries running at the same time
maxConcurrentQueries: number; // default: 2

// Fetch the service description and VoID when the panel opens
fetchMetadata: boolean; // default: true
}

interface DescribeQuery {
id: string;
category: string; // id of a DescribeCategory
label: string;
description?: string;
// SPARQL SELECT query, or a function that builds it (use ctx.limit and ctx.offset for paginated queries)
query: string | ((ctx: { limit: number; offset: number; getResult: (queryId: string) => any }) => string);
paginated?: boolean; // show "Load more"
pageSize?: number; // default: 25
expensive?: boolean; // show a "may be slow" hint
dependsOn?: string; // id of a query whose results are needed to build this one
}

interface DescribeCategory {
id: string;
label: string;
icon: string; // Font Awesome icon class, e.g. "fa-train"
}
```

**Example**: add a category with endpoint specific queries:

```javascript
const yasgui = new Yasgui(document.getElementById("yasgui"), {
endpointDescribe: {
categories: (defaults) => [
...defaults,
{ id: "railway", label: "Railway infrastructure", icon: "fa-train" },
],
queries: (defaults) => [
...defaults,
{
id: "operational-points",
category: "railway",
label: "Operational points per country",
query: `PREFIX era: <http://data.europa.eu/949/>
SELECT ?country (COUNT(?op) AS ?operationalPoints) WHERE {
?op a era:OperationalPoint ; era:inCountry ?country .
}
GROUP BY ?country
ORDER BY DESC(?operationalPoints)
LIMIT 50`,
},
],
},
});
```

Disable the panel with `endpointDescribe: { enabled: false }`. The panel can also be controlled programmatically through `yasgui.endpointDescribe` (`open()`, `close()`, `toggle()`, `collapse()`, `setPinned(pinned)`, `runQuery(queryId)`).

### Theme Configuration

YASGUI supports both light and dark themes with comprehensive customization options.
Expand Down Expand Up @@ -2152,6 +2234,18 @@ const newTheme = yasgui.toggleTheme();
console.log('Switched to:', newTheme);
```

##### `endpointDescribe: EndpointDescribePanel | undefined`

The endpoint overview panel (see [Endpoint Describe Configuration](#endpoint-describe-configuration)). It is `undefined` when the panel is disabled with `endpointDescribe: { enabled: false }`.

```javascript
yasgui.endpointDescribe?.open(); // open (or expand) the panel
yasgui.endpointDescribe?.setPinned(true); // keep it open, also after a reload
await yasgui.endpointDescribe?.runQuery("classes"); // run a describe query for the current endpoint
yasgui.endpointDescribe?.collapse(); // collapse it to a thin bar
yasgui.endpointDescribe?.close();
```

### Tab Class

Represents a query tab.
Expand Down Expand Up @@ -2227,6 +2321,32 @@ const query = tab.getQuery();
console.log('Current query:', query);
```

##### `runBackgroundQuery(query: string, options?): Promise<any>`

Execute a SPARQL query against the tab's endpoint without changing the editor or the results view. The tab's request configuration and authentication are used (an expired OAuth 2.0 token is refreshed first) and no query events are emitted. Rejects on HTTP errors.

Options: `accept` (Accept header), `signal` (an `AbortSignal`) and `skipGraphArgs` (don't send the tab's default/named graphs).

```javascript
const response = await tab.runBackgroundQuery("SELECT (COUNT(*) AS ?n) WHERE { ?s ?p ?o }", {
accept: "application/sparql-results+json",
skipGraphArgs: true,
});
const count = JSON.parse(response.content).results.bindings[0].n.value;
```

##### `getRequestInit(): Promise<{ headers, withCredentials } | undefined>`

Get the request headers (including authentication headers) and credentials mode used for requests to the tab's endpoint, for example to fetch other resources from the same server.

```javascript
const init = await tab.getRequestInit();
const response = await fetch(tab.getEndpoint(), {
headers: { ...init.headers, Accept: "text/turtle" },
credentials: init.withCredentials ? "include" : "same-origin",
});
```

### Yasqe Class

SPARQL query editor.
Expand Down
50 changes: 50 additions & 0 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
- [Endpoint Quick Switch](#endpoint-quick-switch)
- [Configuration Import/Export](#configuration-importexport)
- [URI Explorer](#uri-explorer)
- [Endpoint Overview](#endpoint-overview)
- [Query Tabs](#query-tabs)
- [Settings Modal](#settings-modal)
- [SPARQL Endpoints Management](#sparql-endpoints-management)
Expand Down Expand Up @@ -726,6 +727,54 @@ Alternatively, use `Ctrl`+`Shift` and click on the URI to executes a CONSTRUCT q
**Example:**
Ctrl+clicking on `http://dbpedia.org/resource/European_Union` automatically queries for all triples where the European Union is the subject of.

### Endpoint Overview

Get to know an unfamiliar SPARQL endpoint while you write your queries. Click the **Describe endpoint** button in the control bar or press `F8` to open the **Endpoint overview** panel next to the editor.

**Service description and VoID**

When the panel opens, MatGUI looks for metadata that the endpoint publishes about itself:

- The [SPARQL Service Description](https://www.w3.org/TR/sparql11-service-description/), returned by the endpoint URL when it is requested without a query. It lists the supported SPARQL features, extension functions, result formats and named graphs.
- A [VoID](https://www.w3.org/TR/void/) dataset description at `/.well-known/void` (or embedded in the service description), with statistics such as the number of triples, classes, vocabularies and class/property partitions.

Some descriptions are very large (Wikidata's includes statistics for every class and property). Only the first 5 MB are read; the panel mentions it when a description was cut off.

Many endpoints publish neither (for example GraphDB has no service description). The panel then simply says so and you can use the overview queries instead. If you run an endpoint yourself, publishing a service description and VoID makes it much easier to explore.

**Overview queries**

The panel contains predefined queries, grouped in categories:

| Category | Queries |
| ----------------------------- | ------------------------------------------------------------------------------------------ |
| Endpoint & dataset overview | Named graphs, triples per named graph, total number of triples, most used namespaces |
| Vocabulary & schema | Classes and properties with usage counts, SKOS concept schemes, SHACL/ShEx shapes |
| Instances | Sample instances of the most used classes, most connected nodes (hubs) |
| Labels, languages & literals | Labels per language, properties with language-tagged literals |
| Interlinking & external links | Referenced hosts (e.g. Wikidata, GeoNames, DBpedia), linking properties, linked entities |
| Time & geo | Temporal properties, geospatial properties, coordinate reference systems, bounding boxes |

- Queries only run when you click **Run** (▶) or **Run all** for a category, so large endpoints are never queried unexpectedly. Queries marked **may be slow** scan the whole dataset and may time out on large endpoints.
- Every query is limited. Lists such as the named graphs are loaded page by page with **Load more**.
- Some endpoints stop long-running queries at a time limit and return whatever they found so far (for example Virtuoso's "anytime queries", used by DBpedia). Such results are marked as **Partial result**: counts may be too low and an empty result does not mean there is no such data.
- Public endpoints limit how many (expensive) queries you can run. When an endpoint answers that you are sending too many requests, wait a moment before running more queries. Errors of the endpoint are shown below the query (e.g. "estimated execution time exceeds the limit").
- Describe queries run in the background with the endpoint and authentication settings of the current tab. They do not change your query or the results view. The default and named graphs configured for the tab are not applied, so the whole endpoint is described.
- Click an IRI in the results to insert it into your query. MatGUI uses a prefixed name when it knows the prefix and adds the missing `PREFIX` declaration.
- Click the **Open query in a new tab** button next to a query to open it in a new tab, to adapt it further.

**Remembered results**

Results are remembered per endpoint, also when you execute other queries, switch tabs or reload the page. The panel always follows the endpoint of the active tab. Use the **Run again** button to refresh a result; the time it was fetched is shown next to it.

**Keeping the panel out of the way**

- **Move out of the way** (») collapses the panel to a thin bar at the side of the screen. Click the bar to expand it again.
- An unpinned panel collapses automatically when you click into the query editor.
- The **pin** button pins the panel: it stays open while you write queries and is restored when you reload the page.
- Drag the left edge of the panel to change its width.
- On small screens the panel opens as a sheet at the bottom of the screen.

### Query Tabs

Manage multiple queries simultaneously with tabs.
Expand Down Expand Up @@ -1830,6 +1879,7 @@ Master Matgui with these keyboard shortcuts for faster querying.
| `F11` | Toggle YASQE (editor) fullscreen |
| `F10` | Toggle YASR (results) fullscreen |
| `F9` | Switch between YASQE and YASR fullscreen |
| `F8` | Toggle the endpoint overview panel |
| `Esc` | Exit fullscreen mode |

### Matgui Tabs
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/yasgui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"es6-object-assign": "^1.1.0",
"jsuri": "^1.3.1",
"lodash-es": "^4.17.15",
"n3": "^1.3.5",
"sortablejs": "^1.10.2",
"@matdata/yasgui-graph-plugin": "^1.6.1",
"@matdata/yasgui-table-plugin": "^1.3.0"
Expand Down
38 changes: 38 additions & 0 deletions packages/yasgui/src/Tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,13 @@ export class Tab extends EventEmitter {
}
}

// F8 - Toggle the endpoint describe panel
if (event.key === "F8" && this.yasgui.endpointDescribe) {
event.preventDefault();
this.yasgui.endpointDescribe.toggle();
return;
}

// F11 - Toggle Yasqe fullscreen
if (event.key === "F11") {
event.preventDefault();
Expand Down Expand Up @@ -1581,6 +1588,37 @@ export class Tab extends EventEmitter {
void this.executeBackgroundQuery(constructQuery);
};

/**
* Execute a SPARQL query against this tab's endpoint without touching the editor or the results view.
* The request configuration and authentication of the tab are used.
*/
public async runBackgroundQuery(
query: string,
options: { accept?: string; signal?: AbortSignal; skipGraphArgs?: boolean } = {},
): Promise<any> {
if (!this.yasqe) throw new Error("No yasqe editor initialized");
const tokenValid = await this.ensureOAuth2TokenValid();
if (!tokenValid) throw new Error("OAuth 2.0 authentication failed");
return Yasqe.Sparql.executeQuery(this.yasqe, undefined, {
customQuery: query,
customAccept: options.accept,
signal: options.signal,
silent: true,
skipGraphArgs: options.skipGraphArgs,
});
}

/**
* Request headers (including authentication) and credentials mode used for requests to this tab's endpoint.
*/
public async getRequestInit(): Promise<{ headers: { [key: string]: string }; withCredentials: boolean } | undefined> {
if (!this.yasqe) return undefined;
await this.ensureOAuth2TokenValid();
const ajaxConfig = Yasqe.Sparql.getAjaxConfig(this.yasqe);
if (!ajaxConfig) return undefined;
return { headers: { ...(ajaxConfig.headers || {}) }, withCredentials: ajaxConfig.withCredentials };
}

private async executeBackgroundQuery(query: string) {
if (!this.yasqe || !this.yasr) return;

Expand Down
22 changes: 22 additions & 0 deletions packages/yasgui/src/TabSettingsModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@ export default class TabSettingsModal {
controlBarEl.appendChild(this.mapButton);
this.mapButton.onclick = (event: MouseEvent) => this.openMapWidget(event);

// Describe endpoint button
if (this.tab.yasgui.config.endpointDescribe?.enabled) {
const describeButton = document.createElement("button");
describeButton.setAttribute("aria-label", "Describe endpoint");
describeButton.title = "Describe endpoint (F8)";
describeButton.innerHTML = '<i class="fas fa-magnifying-glass-chart"></i>';
addClass(describeButton, "tabContextButton", "describeEndpointButton", "desktopOnly");
describeButton.onclick = () => this.tab.yasgui.endpointDescribe?.toggle();
controlBarEl.appendChild(describeButton);
}

// Hamburger menu button and dropdown (mobile only)
const hamburgerContainer = document.createElement("div");
addClass(hamburgerContainer, "hamburgerContainer", "mobileOnly");
Expand Down Expand Up @@ -167,6 +178,17 @@ export default class TabSettingsModal {
};
this.hamburgerDropdown.appendChild(mapItem);

if (this.tab.yasgui.config.endpointDescribe?.enabled) {
const describeItem = document.createElement("button");
addClass(describeItem, "hamburgerMenuItem");
describeItem.innerHTML = '<i class="fas fa-magnifying-glass-chart"></i><span>Describe endpoint</span>';
describeItem.onclick = () => {
this.tab.yasgui.endpointDescribe?.toggle();
this.closeHamburgerMenu();
};
this.hamburgerDropdown.appendChild(describeItem);
}

// Theme toggle menu item (if enabled)
if (this.tab.yasgui.config.showThemeToggle) {
const themeItem = document.createElement("button");
Expand Down
6 changes: 6 additions & 0 deletions packages/yasgui/src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export default function initialize(): Config<CatalogueItem> {
showThemeToggle: true,
orientation: "vertical",
showSnippetsBar: true,
endpointDescribe: {
enabled: true,
timeoutMs: 60000,
maxConcurrentQueries: 2,
fetchMetadata: true,
},
endpointButtons: undefined,
endpointCatalogueOptions: {
getData: () => {
Expand Down
Loading
Loading