code: qt6,frontends,qmldir (3 files)

This commit is contained in:
Richard Ward
2025-12-30 21:39:40 +00:00
parent 05ece8ac26
commit ac53a10698
3 changed files with 419 additions and 0 deletions

View File

@@ -0,0 +1,217 @@
import QtQuick
import QtQuick.Controls.Material
/**
* QML DBAL Client Component
*
* Provides database operations for QML UI components.
* Wraps the C++ DBALClient for easy QML integration.
*
* Example:
* ```qml
* import "../qmllib/dbal"
*
* DBALProvider {
* id: dbal
* baseUrl: "http://localhost:3001/api/dbal"
* tenantId: "default"
*
* onConnectedChanged: {
* if (connected) {
* loadUsers()
* }
* }
* }
*
* function loadUsers() {
* dbal.list("User", { take: 20 }, function(result) {
* userModel.clear()
* for (var i = 0; i < result.items.length; i++) {
* userModel.append(result.items[i])
* }
* })
* }
* ```
*/
Item {
id: root
// Configuration
property string baseUrl: "http://localhost:3001/api/dbal"
property string tenantId: "default"
property string authToken: ""
// State
property bool connected: false
property bool loading: false
property string lastError: ""
// Signals
signal errorOccurred(string message)
signal operationCompleted(string operation, var result)
// Internal HTTP client (simplified - would use XMLHttpRequest in real impl)
QtObject {
id: internal
function request(method, endpoint, body, callback) {
root.loading = true
root.lastError = ""
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
root.loading = false
if (xhr.status >= 200 && xhr.status < 300) {
try {
var result = JSON.parse(xhr.responseText)
if (callback) callback(result, null)
root.operationCompleted(endpoint, result)
} catch (e) {
var err = "Failed to parse response: " + e.message
root.lastError = err
root.errorOccurred(err)
if (callback) callback(null, err)
}
} else {
var error = xhr.statusText || "Request failed"
root.lastError = error
root.errorOccurred(error)
if (callback) callback(null, error)
}
}
}
var url = root.baseUrl + endpoint
xhr.open(method, url)
xhr.setRequestHeader("Content-Type", "application/json")
xhr.setRequestHeader("X-Tenant-ID", root.tenantId)
if (root.authToken) {
xhr.setRequestHeader("Authorization", "Bearer " + root.authToken)
}
if (body) {
xhr.send(JSON.stringify(body))
} else {
xhr.send()
}
}
}
// Public API
/**
* Create a new record
* @param {string} entity - Entity name (e.g., "User", "AuditLog")
* @param {object} data - Record data
* @param {function} callback - Callback(result, error)
*/
function create(entity, data, callback) {
internal.request("POST", "/create", {
entity: entity,
data: data,
tenantId: tenantId
}, callback)
}
/**
* Read a single record by ID
* @param {string} entity - Entity name
* @param {string} id - Record ID
* @param {function} callback - Callback(result, error)
*/
function read(entity, id, callback) {
internal.request("GET", "/read/" + entity + "/" + id, null, callback)
}
/**
* Update an existing record
* @param {string} entity - Entity name
* @param {string} id - Record ID
* @param {object} data - Updated fields
* @param {function} callback - Callback(result, error)
*/
function update(entity, id, data, callback) {
internal.request("PUT", "/update", {
entity: entity,
id: id,
data: data
}, callback)
}
/**
* Delete a record
* @param {string} entity - Entity name
* @param {string} id - Record ID
* @param {function} callback - Callback(success, error)
*/
function remove(entity, id, callback) {
internal.request("DELETE", "/delete/" + entity + "/" + id, null, callback)
}
/**
* List records with pagination and filtering
* @param {string} entity - Entity name
* @param {object} options - { take, skip, where, orderBy }
* @param {function} callback - Callback({ items, total }, error)
*/
function list(entity, options, callback) {
var body = {
entity: entity,
tenantId: tenantId
}
if (options.take !== undefined) body.take = options.take
if (options.skip !== undefined) body.skip = options.skip
if (options.where !== undefined) body.where = options.where
if (options.orderBy !== undefined) body.orderBy = options.orderBy
internal.request("POST", "/list", body, callback)
}
/**
* Find first record matching filter
* @param {string} entity - Entity name
* @param {object} filter - Filter criteria
* @param {function} callback - Callback(result, error)
*/
function findFirst(entity, filter, callback) {
internal.request("POST", "/findFirst", {
entity: entity,
tenantId: tenantId,
filter: filter
}, callback)
}
/**
* Execute a named operation
* @param {string} operation - Operation name
* @param {object} params - Operation parameters
* @param {function} callback - Callback(result, error)
*/
function execute(operation, params, callback) {
internal.request("POST", "/execute", {
operation: operation,
params: params,
tenantId: tenantId
}, callback)
}
/**
* Check connection to DBAL backend
* @param {function} callback - Callback(success, error)
*/
function ping(callback) {
internal.request("GET", "/ping", null, function(result, error) {
root.connected = !error
if (callback) callback(!error, error)
})
}
// Auto-ping on component ready
Component.onCompleted: {
ping()
}
}

View File

@@ -0,0 +1,3 @@
module dbal
DBALProvider 1.0 DBALProvider.qml

View File

@@ -0,0 +1,199 @@
#include "DBALClient.h"
#include <QJsonDocument>
#include <QNetworkRequest>
#include <QUrl>
#include <QUrlQuery>
DBALClient::DBALClient(QObject *parent)
: QObject(parent)
, m_networkManager(new QNetworkAccessManager(this))
, m_baseUrl("http://localhost:3001/api/dbal")
, m_tenantId("default")
, m_connected(false)
{
connect(m_networkManager, &QNetworkAccessManager::finished,
this, &DBALClient::handleNetworkReply);
}
DBALClient::~DBALClient()
{
// Cleanup pending callbacks
m_pendingCallbacks.clear();
}
void DBALClient::setBaseUrl(const QString &url)
{
if (m_baseUrl != url) {
m_baseUrl = url;
emit baseUrlChanged();
}
}
void DBALClient::setTenantId(const QString &id)
{
if (m_tenantId != id) {
m_tenantId = id;
emit tenantIdChanged();
}
}
void DBALClient::setAuthToken(const QString &token)
{
if (m_authToken != token) {
m_authToken = token;
emit authTokenChanged();
}
}
void DBALClient::setError(const QString &error)
{
m_lastError = error;
emit errorOccurred(error);
}
void DBALClient::sendRequest(const QString &method, const QString &endpoint,
const QJsonObject &body, const QJSValue &callback)
{
QUrl url(m_baseUrl + endpoint);
QNetworkRequest request(url);
// Set headers
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("X-Tenant-ID", m_tenantId.toUtf8());
if (!m_authToken.isEmpty()) {
request.setRawHeader("Authorization", ("Bearer " + m_authToken).toUtf8());
}
QNetworkReply *reply = nullptr;
QByteArray jsonData = QJsonDocument(body).toJson(QJsonDocument::Compact);
if (method == "GET") {
reply = m_networkManager->get(request);
} else if (method == "POST") {
reply = m_networkManager->post(request, jsonData);
} else if (method == "PUT") {
reply = m_networkManager->put(request, jsonData);
} else if (method == "DELETE") {
reply = m_networkManager->deleteResource(request);
}
if (reply && callback.isCallable()) {
m_pendingCallbacks[reply] = callback;
}
}
void DBALClient::handleNetworkReply(QNetworkReply *reply)
{
reply->deleteLater();
QJSValue callback = m_pendingCallbacks.take(reply);
if (reply->error() != QNetworkReply::NoError) {
setError(reply->errorString());
if (callback.isCallable()) {
QJSValueList args;
args << QJSValue::NullValue;
args << reply->errorString();
callback.call(args);
}
return;
}
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (callback.isCallable()) {
QJSValueList args;
// Convert QJsonObject to QJSValue through QVariant
if (doc.isObject()) {
args << callback.engine()->toScriptValue(doc.object().toVariantMap());
} else if (doc.isArray()) {
args << callback.engine()->toScriptValue(doc.array().toVariantList());
} else {
args << QJSValue::NullValue;
}
callback.call(args);
}
// Update connected status
if (!m_connected) {
m_connected = true;
emit connectedChanged();
}
}
// CRUD Operations
void DBALClient::create(const QString &entity, const QJsonObject &data, const QJSValue &callback)
{
QJsonObject body;
body["entity"] = entity;
body["data"] = data;
body["tenantId"] = m_tenantId;
sendRequest("POST", "/create", body, callback);
}
void DBALClient::read(const QString &entity, const QString &id, const QJSValue &callback)
{
QString endpoint = QString("/read/%1/%2").arg(entity, id);
sendRequest("GET", endpoint, QJsonObject(), callback);
}
void DBALClient::update(const QString &entity, const QString &id,
const QJsonObject &data, const QJSValue &callback)
{
QJsonObject body;
body["entity"] = entity;
body["id"] = id;
body["data"] = data;
sendRequest("PUT", "/update", body, callback);
}
void DBALClient::remove(const QString &entity, const QString &id, const QJSValue &callback)
{
QString endpoint = QString("/delete/%1/%2").arg(entity, id);
sendRequest("DELETE", endpoint, QJsonObject(), callback);
}
void DBALClient::list(const QString &entity, const QJsonObject &options, const QJSValue &callback)
{
QJsonObject body;
body["entity"] = entity;
body["tenantId"] = m_tenantId;
if (options.contains("take")) body["take"] = options["take"];
if (options.contains("skip")) body["skip"] = options["skip"];
if (options.contains("where")) body["where"] = options["where"];
if (options.contains("orderBy")) body["orderBy"] = options["orderBy"];
sendRequest("POST", "/list", body, callback);
}
void DBALClient::findFirst(const QString &entity, const QJsonObject &filter, const QJSValue &callback)
{
QJsonObject body;
body["entity"] = entity;
body["tenantId"] = m_tenantId;
body["filter"] = filter;
sendRequest("POST", "/findFirst", body, callback);
}
void DBALClient::execute(const QString &operation, const QJsonObject &params, const QJSValue &callback)
{
QJsonObject body;
body["operation"] = operation;
body["params"] = params;
body["tenantId"] = m_tenantId;
sendRequest("POST", "/execute", body, callback);
}
void DBALClient::ping()
{
sendRequest("GET", "/ping", QJsonObject(), QJSValue());
}