GraphQl 0.121 - Pagination working

This commit is contained in:
jokob-sk
2024-11-12 23:17:20 +11:00
parent 79fe759470
commit f007eac656
3 changed files with 375 additions and 510 deletions
+93 -237
View File
@@ -364,174 +364,25 @@ function getDeviceStatus(item)
return "Unknown status" return "Unknown status"
} }
// ----------------------------------------------------------------------------- // Map column index to column name for GraphQL query
function initializeDatatable_n (status) { function mapColumnIndexToFieldName(index) {
const columnNames = [
console.log(tableColumnVisible); "rowid", "devMac", "devName", "devOwner", "devType", "devVendor",
"devFavorite", "devGroup", "devComments", "devFirstConnection",
"devLastConnection", "devLastIP", "devStaticIP", "devScan", "devLogEvents",
// Build GraphQL query dynamically based on tableColumnVisible "devAlertEvents", "devAlertDown", "devSkipRepeated", "devLastNotification",
let requiredColumns = ['devMac', 'devName', 'devIsNew', 'devPresentLastScan', 'devAlertDown', 'devIsArchived'] "devPresentLastScan", "devIsNew", "devLocation", "devIsArchived",
let columnsToFetch = [ "devParentMAC", "devParentPort", "devIcon", "devGUID", "devSite", "devSSID",
'devMac', 'devName', 'devLastConnection', 'devIsArchived', 'devOwner', 'devType', "devSyncHubNode", "devSourcePlugin"
'devIcon', 'devFavorite', 'devGroup', 'devFirstConnection', 'devLastIP', 'devNetworkNodeMAC',
'devLocation', 'devVendor', 'devNetworkNodePort', 'devGUID', 'devSyncHubNodeName',
'devNetworkSite', 'devSSID', 'devSourcePlugin'
]; ];
let selectedColumns = columnsToFetch.filter(col => tableColumnVisible.includes(col)); return columnNames[index] || null;
// Construct the GraphQL query
let graphqlQuery = `
query {
devices {
${selectedColumns.join('\n')}
}
}
`;
console.log(graphqlQuery);
$.ajax({
url: 'php/server/query_graphql.php', // PHP endpoint that proxies to the GraphQL server
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
query: graphqlQuery,
variables: {} // Optional: pass variables if needed
}),
success: function(response) {
console.log('GraphQL Response:', response);
// Handle the GraphQL response
let devicesListAll_JSON = response.data.devices;
let devicesListAll_JSON_str = JSON.stringify(devicesListAll_JSON);
setCache('devicesListAll_JSON', devicesListAll_JSON_str);
// Query data
getDevicesTotals(devicesListAll_JSON);
// Filter the data based on deviceStatus
var filteredData = filterDataByStatus(devicesListAll_JSON, deviceStatus);
// Convert JSON data into the desired format
var dataArray = {
data: filteredData.map(function(item) {
var originalRow = selectedColumns.map(function(column) {
return item[column] || "";
});
var newRow = [];
// Reorder data based on user-defined columns order
for (index = 0; index < tableColumnOrder.length; index++) {
newRow.push(originalRow[tableColumnOrder[index]]);
}
return newRow;
})
};
// Initialize DataTable
if ($.fn.dataTable.isDataTable('#tableDevices')) {
var table = $('#tableDevices').DataTable();
table.clear().destroy();
}
var table = $('#tableDevices').DataTable({
'data': dataArray["data"],
'paging': true,
'lengthChange': true,
'lengthMenu': [[10, 25, 50, 100, 500, 100000], [10, 25, 50, 100, 500, getString('Device_Tablelenght_all')]],
'searching': true,
'ordering': true,
'info': true,
'autoWidth': false,
'pageLength': tableRows,
'order': tableOrder,
'select': true,
'columnDefs': [
{ visible: false, targets: tableColumnHide },
{ className: 'text-center', targets: [mapIndx(3), mapIndx(4), mapIndx(9), mapIndx(10), mapIndx(15), mapIndx(18)] },
{ width: '80px', targets: [mapIndx(6), mapIndx(7), mapIndx(15)] },
{ width: '30px', targets: [mapIndx(10), mapIndx(13), mapIndx(18)] },
{ orderData: [mapIndx(12)], targets: mapIndx(8) },
// Device Name
{ targets: [mapIndx(0)], 'createdCell': function (td, cellData, rowData) {
$(td).html ('<b class="anonymizeDev"><a href="deviceDetails.php?mac='+ rowData[mapIndx(11)] +'" class="">'+ cellData +'</a></b>');
}},
// Handle other column customizations (similar to the original code)
// Example for IP address formatting:
{ targets: [mapIndx(8)], 'createdCell': function (td, cellData) {
if (!emptyArr.includes(cellData)) {
$(td).html (`<span class="anonymizeIp">
<a href="http://${cellData}" class="pointer" target="_blank">${cellData}</a>
<a href="https://${cellData}" class="pointer" target="_blank"><i class="fa fa-lock "></i></a>
</span>`);
} else {
$(td).html('');
}
}}
// Other columns (Status, MAC, Date, etc.) can be similarly customized.
],
'processing': true,
'language': {
processing: '<table><td width="130px" align="middle">Loading...</td><td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td></table>',
emptyTable: 'No data',
"lengthMenu": "<?= lang('Device_Tablelenght');?>",
"search": "<?= lang('Device_Searchbox');?>: ",
"paginate": {
"next": "<?= lang('Device_Table_nav_next');?>",
"previous": "<?= lang('Device_Table_nav_prev');?>"
},
"info": "<?= lang('Device_Table_info');?>",
}
});
// Event listeners for row selection and saving parameters (same as the original code)
$('#tableDevices').on('length.dt', function (e, settings, len) {
setCookie ("nax_parTableRows", len, 129600); // save for 90 days
});
$('#tableDevices').on('order.dt', function () {
setCookie ("nax_parTableOrder", JSON.stringify(table.order()), 129600); // save for 90 days
});
// Add multi-edit button and row selection functionality
$('#multiEditPlc').append(
`<button type="submit" id="multiEdit" class="btn btn-primary" style="display:none" onclick="multiEditDevices();">
<i class="fa fa-pencil pointer"></i> ${getString("Device_MultiEdit")}
</button>`
);
$('#tableDevices').on('click', 'tr', function () {
setTimeout(function(){
var anyRowSelected = $('#tableDevices tr.selected').length > 0;
$('#multiEdit').toggle(anyRowSelected);
}, 200);
});
hideSpinner(); // Hide the loading spinner
},
error: function(xhr, status, error) {
console.error('AJAX Error:', error);
}
});
} }
// --------------------------------------------------------- // ---------------------------------------------------------
function initializeDatatable (status) { function initializeDatatable (status) {
if(!status) if(!status)
{ {
status = 'my_devices' status = 'my_devices'
@@ -579,9 +430,19 @@ function initializeDatatable (status) {
} }
} }
// Construct the GraphQL query
var table = $('#tableDevices').DataTable({
"serverSide": true,
"processing": true,
"ajax": {
"url": 'php/server/query_graphql.php', // PHP endpoint that proxies to the GraphQL server
"type": "POST",
"contentType": "application/json",
"data": function (d) {
// Construct GraphQL query with pagination and sorting options
let graphqlQuery = ` let graphqlQuery = `
query { query devices($options: PageQueryOptionsInput) {
devices(options: $options) {
devices { devices {
rowid rowid
devMac devMac
@@ -615,86 +476,75 @@ function initializeDatatable (status) {
devSyncHubNode devSyncHubNode
devSourcePlugin devSourcePlugin
} }
count
}
} }
`; `;
console.log(graphqlQuery); console.log(d);
$.ajax({ // Prepare query variables for pagination, sorting, and search
url: 'php/server/query_graphql.php', // PHP endpoint that proxies to the GraphQL server let query = {
type: 'POST', "operationName": null,
contentType: 'application/json', "query": graphqlQuery,
data: JSON.stringify({ "variables": {
query: graphqlQuery, "options": {
variables: {} // Optional: pass variables if needed "page": Math.floor(d.start / d.length) + 1, // Page number (1-based)
}), "limit": parseInt(d.length, 10), // Page size (ensure it's an integer)
success: function(result) { "sort": d.order && d.order[0] ? [{
"field": mapColumnIndexToFieldName(d.order[0].column), // Sort field from DataTable column
"order": d.order[0].dir.toUpperCase() // Sort direction (ASC/DESC)
}] : [], // Default to an empty array if no sorting is defined
"search": d.search.value // Search query
}
}
};
// refresh devices cache return JSON.stringify(query); // Send the JSON request
devicesListAll_JSON = result["devices"]; },
console.log(devicesListAll_JSON); "dataSrc": function (json) {
console.log(json);
devicesListAll_JSON_str = JSON.stringify(devicesListAll_JSON) return json.devices.devices.map(device => {
setCache('devicesListAll_JSON', devicesListAll_JSON_str) // Convert each device record into the required DataTable row format
const originalRow = [
// query data device.devName || "",
getDevicesTotals(result.devices); device.devOwner || "",
device.devType || "",
// Filter the data based on deviceStatus device.devIcon || "",
var filteredData = filterDataByStatus(result.devices, deviceStatus); device.devFavorite || "",
device.devGroup || "",
// Convert JSON data into the desired format device.devFirstConnection || "",
var dataArray = { device.devLastConnection || "",
data: filteredData.map(function(item) { device.devLastIP || "",
var originalRow = [ (isRandomMAC(device.devMac)) || "", // Custom logic for randomized MAC
item.devName || "", getDeviceStatus(device) || "",
item.devOwner || "", device.devMac || "", // hidden
item.devType || "", formatIPlong(device.devLastIP) || "", // IP orderable
item.devIcon || "", device.rowid || "",
item.devFavorite || "", device.devParentMAC || "",
item.devGroup || "", getNumberOfChildren(device.devMac, json.devices.devices) || 0,
item.devFirstConnection || "", device.devLocation || "",
item.devLastConnection || "", device.devVendor || "",
item.devLastIP || "", device.devParentPort || 0,
(isRandomMAC(item.devMac)) || "", // Check if randomized MAC device.devGUID || "",
getDeviceStatus(item) || "", device.devSyncHubNode || "",
item.devMac || "", // hidden device.devSite || "",
formatIPlong(item.devLastIP) || "", // IP orderable device.devSSID || "",
item.rowid || "", device.devSourcePlugin || ""
item.devParentMAC || "",
getNumberOfChildren(item.devMac, result.devices) || 0,
item.devLocation || "",
item.devVendor || "",
item.devParentPort || 0,
item.devGUID || "",
item.devSyncHubNode || "",
item.devSite || "",
item.devSSID || "",
item.devSourcePlugin || ""
]; ];
var newRow = []; const newRow = [];
// Reorder data based on user-defined columns order
// reorder data based on user-defined columns order for (let index = 0; index < tableColumnOrder.length; index++) {
for (index = 0; index < tableColumnOrder.length; index++) {
newRow.push(originalRow[tableColumnOrder[index]]); newRow.push(originalRow[tableColumnOrder[index]]);
} }
return newRow; return newRow;
}) });
};
// Check if the DataTable already exists
if ($.fn.dataTable.isDataTable('#tableDevices')) {
// The DataTable exists, so destroy it
var table = $('#tableDevices').DataTable();
table.clear().destroy();
} }
},
var table =
$('#tableDevices').DataTable({
'data' : dataArray["data"],
'paging' : true, 'paging' : true,
'lengthChange' : true, 'lengthChange' : true,
'lengthMenu' : [[10, 25, 50, 100, 500, 100000], [10, 25, 50, 100, 500, getString('Device_Tablelenght_all')]], 'lengthMenu' : [[10, 25, 50, 100, 500, 100000], [10, 25, 50, 100, 500, getString('Device_Tablelenght_all')]],
@@ -727,6 +577,8 @@ function initializeDatatable (status) {
// Connected Devices // Connected Devices
{targets: [mapIndx(15)], {targets: [mapIndx(15)],
'createdCell': function (td, cellData, rowData, row, col) { 'createdCell': function (td, cellData, rowData, row, col) {
// check if this is a network device // check if this is a network device
if(getSetting("NETWORK_DEVICE_TYPES").includes(`'${rowData[mapIndx(2)]}'`) ) if(getSetting("NETWORK_DEVICE_TYPES").includes(`'${rowData[mapIndx(2)]}'`) )
{ {
@@ -742,6 +594,7 @@ function initializeDatatable (status) {
// Icon // Icon
{targets: [mapIndx(3)], {targets: [mapIndx(3)],
'createdCell': function (td, cellData, rowData, row, col) { 'createdCell': function (td, cellData, rowData, row, col) {
if (!emptyArr.includes(cellData)){ if (!emptyArr.includes(cellData)){
$(td).html (atob(cellData)); $(td).html (atob(cellData));
} else { } else {
@@ -849,7 +702,6 @@ function initializeDatatable (status) {
// Processing // Processing
'processing' : true, 'processing' : true,
'language' : { 'language' : {
processing: '<table> <td width="130px" align="middle">Loading...</td><td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td> </table>',
emptyTable: 'No data', emptyTable: 'No data',
"lengthMenu": "<?= lang('Device_Tablelenght');?>", "lengthMenu": "<?= lang('Device_Tablelenght');?>",
"search": "<?= lang('Device_Searchbox');?>: ", "search": "<?= lang('Device_Searchbox');?>: ",
@@ -858,9 +710,9 @@ function initializeDatatable (status) {
"previous": "<?= lang('Device_Table_nav_prev');?>" "previous": "<?= lang('Device_Table_nav_prev');?>"
}, },
"info": "<?= lang('Device_Table_info');?>", "info": "<?= lang('Device_Table_info');?>",
} },
}); initComplete: function (settings, JSON) {
// Handle any additional interactions or event listeners as required
// Save cookie Rows displayed, and Parameters rows & order // Save cookie Rows displayed, and Parameters rows & order
$('#tableDevices').on( 'length.dt', function ( e, settings, len ) { $('#tableDevices').on( 'length.dt', function ( e, settings, len ) {
setCookie ("nax_parTableRows", len, 129600); // save for 90 days setCookie ("nax_parTableRows", len, 129600); // save for 90 days
@@ -884,18 +736,22 @@ function initializeDatatable (status) {
// Toggle visibility of element with ID 'multiEdit' // Toggle visibility of element with ID 'multiEdit'
$('#multiEdit').toggle(anyRowSelected); $('#multiEdit').toggle(anyRowSelected);
}, 200); }, 100);
}); });
hideSpinner(); hideSpinner();
}
});
} }
}
);
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
+14 -14
View File
@@ -970,6 +970,19 @@ function getGuid() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Loading Spinner overlay // Loading Spinner overlay
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
spinnerHtml = `
<!-- spinner -->
<div id="loadingSpinner" style="display: block">
<div class="pa_semitransparent-panel"></div>
<div class="panel panel-default pa_spinner">
<table>
<td width="130px" align="middle">_text_</td>
<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td>
</table>
</div>
</div>
`
function showSpinner(stringKey='Loading') function showSpinner(stringKey='Loading')
{ {
@@ -988,20 +1001,7 @@ function showSpinner(stringKey='Loading')
$("#loadingSpinner").show(); $("#loadingSpinner").show();
} }
else{ else{
html = ` $(".wrapper").append(spinnerHtml.replace('_text_',text))
<!-- spinner -->
<div id="loadingSpinner" style="display: block">
<div class="pa_semitransparent-panel"></div>
<div class="panel panel-default pa_spinner">
<table>
<td width="130px" align="middle">${text}</td>
<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td>
</table>
</div>
</div>
`
$(".wrapper").append(html)
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
+46 -37
View File
@@ -1,9 +1,8 @@
import graphene import graphene
from graphene import ObjectType, String, Int, Boolean, Field, List from graphene import ObjectType, String, Int, Boolean, List, Field, InputObjectType
import json import json
import sys import sys
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH="/app"
sys.path.extend([f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/server"])
@@ -14,6 +13,17 @@ from const import apiPath
# Define a base URL with the user's home directory # Define a base URL with the user's home directory
folder = apiPath folder = apiPath
# Pagination and Sorting Input Types
class SortOptionsInput(InputObjectType):
field = String()
order = String()
class PageQueryOptionsInput(InputObjectType):
page = Int()
limit = Int()
sort = List(SortOptionsInput)
search = String()
# Device ObjectType # Device ObjectType
class Device(ObjectType): class Device(ObjectType):
rowid = Int() rowid = Int()
@@ -48,50 +58,49 @@ class Device(ObjectType):
devSyncHubNode = String() devSyncHubNode = String()
devSourcePlugin = String() devSourcePlugin = String()
class DeviceResult(ObjectType):
class Query(ObjectType):
devices = List(Device) devices = List(Device)
count = Int()
def resolve_devices(self, info): # Define Query Type with Pagination Support
# Load JSON data only when the query executes class Query(ObjectType):
devices = Field(DeviceResult, options=PageQueryOptionsInput())
def resolve_devices(self, info, options=None):
try: try:
with open(folder + 'table_devices.json', 'r') as f: with open(folder + 'table_devices.json', 'r') as f:
devices_data = json.load(f)["data"] devices_data = json.load(f)["data"]
except (FileNotFoundError, json.JSONDecodeError) as e: except (FileNotFoundError, json.JSONDecodeError) as e:
mylog('error', f'[graphql_schema] Error loading devices data: {e}') mylog('none', f'[graphql_schema] Error loading devices data: {e}')
return [] return DeviceResult(devices=[], count=0)
return devices_data # Directly return the data without mapping total_count = len(devices_data)
# Apply pagination and sorting if options are provided
if options:
# Implement pagination and sorting here
if options.page and options.limit:
start = (options.page - 1) * options.limit
end = start + options.limit
devices_data = devices_data[start:end]
if options.sort:
for sort_option in options.sort:
devices_data = sorted(
devices_data,
key=lambda x: x.get(sort_option.field),
reverse=(sort_option.order.lower() == "desc")
)
# Filter data if a search term is provided
if options.search:
devices_data = [
device for device in devices_data
if options.search.lower() in device.get("devName", "").lower()
]
return DeviceResult(devices=devices_data, count=total_count)
# Schema Definition # Schema Definition
devicesSchema = graphene.Schema(query=Query) devicesSchema = graphene.Schema(query=Query)
# # Sample query
# $.ajax({
# url: 'php/server/query_graphql.php', // The PHP endpoint that proxies to the GraphQL server
# type: 'POST',
# contentType: 'application/json', // Send the data as JSON
# data: JSON.stringify({
# query: `
# query {
# devices {
# devMac
# devName
# devLastConnection
# devArchived
# }
# }
# `, // GraphQL query for plugins
# variables: {} // Optional, pass variables if needed
# }),
# success: function(response) {
# console.log('GraphQL Response:', response);
# // Handle the GraphQL response here
# },
# error: function(xhr, status, error) {
# console.error('AJAX Error:', error);
# // Handle errors here
# }
# });