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
+95 -239
View File
@@ -364,174 +364,25 @@ function getDeviceStatus(item)
return "Unknown status"
}
// -----------------------------------------------------------------------------
function initializeDatatable_n (status) {
console.log(tableColumnVisible);
// Build GraphQL query dynamically based on tableColumnVisible
let requiredColumns = ['devMac', 'devName', 'devIsNew', 'devPresentLastScan', 'devAlertDown', 'devIsArchived']
let columnsToFetch = [
'devMac', 'devName', 'devLastConnection', 'devIsArchived', 'devOwner', 'devType',
'devIcon', 'devFavorite', 'devGroup', 'devFirstConnection', 'devLastIP', 'devNetworkNodeMAC',
'devLocation', 'devVendor', 'devNetworkNodePort', 'devGUID', 'devSyncHubNodeName',
'devNetworkSite', 'devSSID', 'devSourcePlugin'
];
let selectedColumns = columnsToFetch.filter(col => tableColumnVisible.includes(col));
// 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);
}
});
// Map column index to column name for GraphQL query
function mapColumnIndexToFieldName(index) {
const columnNames = [
"rowid", "devMac", "devName", "devOwner", "devType", "devVendor",
"devFavorite", "devGroup", "devComments", "devFirstConnection",
"devLastConnection", "devLastIP", "devStaticIP", "devScan", "devLogEvents",
"devAlertEvents", "devAlertDown", "devSkipRepeated", "devLastNotification",
"devPresentLastScan", "devIsNew", "devLocation", "devIsArchived",
"devParentMAC", "devParentPort", "devIcon", "devGUID", "devSite", "devSSID",
"devSyncHubNode", "devSourcePlugin"
];
return columnNames[index] || null;
}
// ---------------------------------------------------------
function initializeDatatable (status) {
if(!status)
{
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 = `
query {
query devices($options: PageQueryOptionsInput) {
devices(options: $options) {
devices {
rowid
devMac
@@ -615,86 +476,75 @@ function initializeDatatable (status) {
devSyncHubNode
devSourcePlugin
}
count
}
}
`;
console.log(graphqlQuery);
console.log(d);
$.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(result) {
// Prepare query variables for pagination, sorting, and search
let query = {
"operationName": null,
"query": graphqlQuery,
"variables": {
"options": {
"page": Math.floor(d.start / d.length) + 1, // Page number (1-based)
"limit": parseInt(d.length, 10), // Page size (ensure it's an integer)
"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
devicesListAll_JSON = result["devices"];
console.log(devicesListAll_JSON);
return JSON.stringify(query); // Send the JSON request
},
"dataSrc": function (json) {
console.log(json);
devicesListAll_JSON_str = JSON.stringify(devicesListAll_JSON)
setCache('devicesListAll_JSON', devicesListAll_JSON_str)
// query data
getDevicesTotals(result.devices);
// Filter the data based on deviceStatus
var filteredData = filterDataByStatus(result.devices, deviceStatus);
// Convert JSON data into the desired format
var dataArray = {
data: filteredData.map(function(item) {
var originalRow = [
item.devName || "",
item.devOwner || "",
item.devType || "",
item.devIcon || "",
item.devFavorite || "",
item.devGroup || "",
item.devFirstConnection || "",
item.devLastConnection || "",
item.devLastIP || "",
(isRandomMAC(item.devMac)) || "", // Check if randomized MAC
getDeviceStatus(item) || "",
item.devMac || "", // hidden
formatIPlong(item.devLastIP) || "", // IP orderable
item.rowid || "",
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 || ""
return json.devices.devices.map(device => {
// Convert each device record into the required DataTable row format
const originalRow = [
device.devName || "",
device.devOwner || "",
device.devType || "",
device.devIcon || "",
device.devFavorite || "",
device.devGroup || "",
device.devFirstConnection || "",
device.devLastConnection || "",
device.devLastIP || "",
(isRandomMAC(device.devMac)) || "", // Custom logic for randomized MAC
getDeviceStatus(device) || "",
device.devMac || "", // hidden
formatIPlong(device.devLastIP) || "", // IP orderable
device.rowid || "",
device.devParentMAC || "",
getNumberOfChildren(device.devMac, json.devices.devices) || 0,
device.devLocation || "",
device.devVendor || "",
device.devParentPort || 0,
device.devGUID || "",
device.devSyncHubNode || "",
device.devSite || "",
device.devSSID || "",
device.devSourcePlugin || ""
];
var newRow = [];
// reorder data based on user-defined columns order
for (index = 0; index < tableColumnOrder.length; index++) {
const newRow = [];
// Reorder data based on user-defined columns order
for (let index = 0; index < tableColumnOrder.length; index++) {
newRow.push(originalRow[tableColumnOrder[index]]);
}
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,
'lengthChange' : true,
'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
{targets: [mapIndx(15)],
'createdCell': function (td, cellData, rowData, row, col) {
// check if this is a network device
if(getSetting("NETWORK_DEVICE_TYPES").includes(`'${rowData[mapIndx(2)]}'`) )
{
@@ -742,6 +594,7 @@ function initializeDatatable (status) {
// Icon
{targets: [mapIndx(3)],
'createdCell': function (td, cellData, rowData, row, col) {
if (!emptyArr.includes(cellData)){
$(td).html (atob(cellData));
} else {
@@ -849,7 +702,6 @@ function initializeDatatable (status) {
// Processing
'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');?>: ",
@@ -858,9 +710,9 @@ function initializeDatatable (status) {
"previous": "<?= lang('Device_Table_nav_prev');?>"
},
"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
$('#tableDevices').on( 'length.dt', function ( e, settings, len ) {
setCookie ("nax_parTableRows", len, 129600); // save for 90 days
@@ -884,18 +736,22 @@ function initializeDatatable (status) {
// Toggle visibility of element with ID 'multiEdit'
$('#multiEdit').toggle(anyRowSelected);
}, 200);
}, 100);
});
hideSpinner();
}
});
}
}
}
);
};
// -----------------------------------------------------------------------------
+14 -14
View File
@@ -970,6 +970,19 @@ function getGuid() {
// -----------------------------------------------------------------------------
// 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')
{
@@ -988,20 +1001,7 @@ function showSpinner(stringKey='Loading')
$("#loadingSpinner").show();
}
else{
html = `
<!-- 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)
$(".wrapper").append(spinnerHtml.replace('_text_',text))
}
}
// -----------------------------------------------------------------------------
+46 -37
View File
@@ -1,9 +1,8 @@
import graphene
from graphene import ObjectType, String, Int, Boolean, Field, List
from graphene import ObjectType, String, Int, Boolean, List, Field, InputObjectType
import json
import sys
# Register NetAlertX directories
INSTALL_PATH="/app"
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
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
class Device(ObjectType):
rowid = Int()
@@ -48,50 +58,49 @@ class Device(ObjectType):
devSyncHubNode = String()
devSourcePlugin = String()
class Query(ObjectType):
class DeviceResult(ObjectType):
devices = List(Device)
count = Int()
def resolve_devices(self, info):
# Load JSON data only when the query executes
# Define Query Type with Pagination Support
class Query(ObjectType):
devices = Field(DeviceResult, options=PageQueryOptionsInput())
def resolve_devices(self, info, options=None):
try:
with open(folder + 'table_devices.json', 'r') as f:
devices_data = json.load(f)["data"]
except (FileNotFoundError, json.JSONDecodeError) as e:
mylog('error', f'[graphql_schema] Error loading devices data: {e}')
return []
mylog('none', f'[graphql_schema] Error loading devices data: {e}')
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
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
# }
# });