Skip to content

Add /backend endpoint with content negotiation (JSON/HTML) #2743

@WolfgangFahl

Description

@WolfgangFahl

Description:
To facilitate debugging, monitoring, and integration with external tools, Scholia should expose a /backend endpoint. This endpoint should provide information about the currently running instance, including the version, the configured SPARQL endpoint, and feature flags (e.g., whether text-to-topic is enabled).

Requirements:

  1. Endpoint: /backend
  2. Content Negotiation:
    • If the client requests application/json (via the Accept header), return a JSON object containing backend details.
    • Otherwise (default), return a simple HTML page displaying this information in a table.
  3. Information to expose:
    • Scholia Version
    • SPARQL Endpoint URL
    • SPARQL Endpoint Name
    • Feature flags (e.g., text_to_topic_q_text_enabled, third_parties_enabled)

Acceptance Criteria:

  • curl -H "Accept: application/json" https://scholia.../backend returns valid JSON.
  • Visiting /backend in a browser renders a human-readable page extending base.html.

Implementation Proposal

1. Update scholia/app/views.py

Note: The file scholia/app/views.py was not provided in the context, but referenced in __init__.py. Assuming standard Flask blueprint structure, we add the route there.

Add the following logic to handle the route and content negotiation. This utilizes scholia.__version__, scholia.config, and flask.current_app.

from flask import jsonify, render_template, request, current_app
from .. import __version__
from ..config import config

# ... existing imports and blueprint setup ...

@main.route('/backend')
def backend():
    """Show backend information."""
    sparql_endpoint = config['query-server'].get('sparql_endpoint', '')
    sparql_endpoint_name = config['query-server'].get('sparql_endpoint_name', '')
    
    # Gather backend information
    data = {
        'version': __version__,
        'sparql_endpoint': sparql_endpoint,
        'sparql_endpoint_name': sparql_endpoint_name,
        'text_to_topic_q_text_enabled': getattr(current_app, 'text_to_topic_q_text_enabled', False),
        'third_parties_enabled': getattr(current_app, 'third_parties_enabled', False),
    }

    # Content negotiation
    # Best practice for JSON APIs to check Accept header
    if request.accept_mimetypes.accept_json and \
       not request.accept_mimetypes.accept_html:
        return jsonify(data)
    
    # Default to HTML representation
    return render_template('backend.html', **data)

2. Create scholia/app/templates/backend.html

Create a new template file to display the information for browser users.

{% extends "base.html" %}

{% block title %}Backend Information - Scholia{% endblock %}

{% block page_content %}
<div class="container">
    <h1>Backend Information</h1>
    <p>Configuration details for this Scholia instance.</p>

    <table class="table table-striped table-hover">
        <thead>
            <tr>
                <th>Key</th>
                <th>Value</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td><strong>Version</strong></td>
                <td><code>{{ version }}</code></td>
            </tr>
            <tr>
                <td><strong>SPARQL Endpoint Name</strong></td>
                <td>{{ sparql_endpoint_name }}</td>
            </tr>
            <tr>
                <td><strong>SPARQL Endpoint URL</strong></td>
                <td><a href="{{ sparql_endpoint }}">{{ sparql_endpoint }}</a></td>
            </tr>
            <tr>
                <td><strong>Text-to-Topic Enabled</strong></td>
                <td>
                    {% if text_to_topic_q_text_enabled %}
                    <span class="badge badge-success">Yes</span>
                    {% else %}
                    <span class="badge badge-secondary">No</span>
                    {% endif %}
                </td>
            </tr>
            <tr>
                <td><strong>Third Parties Enabled</strong></td>
                <td>
                    {% if third_parties_enabled %}
                    <span class="badge badge-success">Yes</span>
                    {% else %}
                    <span class="badge badge-secondary">No</span>
                    {% endif %}
                </td>
            </tr>
        </tbody>
    </table>
    
    <h3>JSON Output</h3>
    <p>
        To retrieve this data programmatically, send a request with the header:
        <br>
        <code>Accept: application/json</code>
    </p>

</div>
{% endblock %}

3. Add Test Case (Optional but Recommended)

Add a test case to tests/test_backend.py (or existing test file) to verify content negotiation.

import pytest
from scholia.app import create_app

@pytest.fixture
def client():
    app = create_app()
    app.config['TESTING'] = True
    with app.test_client() as client:
        yield client

def test_backend_html(client):
    """Test that the backend page returns HTML by default."""
    rv = client.get('/backend')
    assert rv.status_code == 200
    assert b'Backend Information' in rv.data
    assert b'Version' in rv.data

def test_backend_json(client):
    """Test that the backend page returns JSON when requested."""
    rv = client.get('/backend', headers={'Accept': 'application/json'})
    assert rv.status_code == 200
    assert rv.is_json
    data = rv.get_json()
    assert 'version' in data
    assert 'sparql_endpoint' in data
    assert 'text_to_topic_q_text_enabled' in data

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementsome suggestions to improve Scholia

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions