From 4944d3560d18a81da1f2586412f107aa8a735c65 Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Sun, 21 Apr 2019 19:01:08 +0200 Subject: [PATCH] Implemented recursive type loader for API types --- src/api/index.js | 144 ++------------------------- src/api/types/block-device.js | 37 +++++++ src/api/types/drive.js | 67 +++++++++++++ src/api/types/lvm-physical-volume.js | 32 ++++++ src/api/types/lvm-volume-group.js | 13 +++ src/graphql/type-loader.js | 11 ++ 6 files changed, 171 insertions(+), 133 deletions(-) create mode 100644 src/api/types/block-device.js create mode 100644 src/api/types/drive.js create mode 100644 src/api/types/lvm-physical-volume.js create mode 100644 src/api/types/lvm-volume-group.js create mode 100644 src/graphql/type-loader.js diff --git a/src/api/index.js b/src/api/index.js index 63673e2..8651286 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -5,11 +5,9 @@ const graphql = require("graphql"); const fs = require("fs"); const path = require("path"); -const matchOrError = require("../match-or-error"); -const upperSnakeCase = require("../upper-snake-case"); -const All = require("../graphql/symbols/all"); const createGraphQLInterface = require("../graphql/index"); -const {ID, LocalProperties, createDataObject} = require("../graphql/data-object"); +const All = require("../graphql/symbols/all"); +const loadTypes = require("../graphql/type-loader"); const createLoaders = require("./loaders"); @@ -44,132 +42,12 @@ new graphql.GraphQLScalarType({ let schema = graphql.buildSchema(fs.readFileSync(path.resolve(__dirname, "../schemas/main.gql"), "utf8")); -function createBlockDevice({ name, path }) { - if (name != null) { - path = `/dev/${name}`; - } else if (path != null) { - let match = matchOrError(/^\/dev\/(.+)$/, path); - name = match[0]; - } - - /* FIXME: parent */ - - return createDataObject({ - [LocalProperties]: { - path: path - }, - lsblk: { - [ID]: name, - name: "name", - size: "size", - mountpoint: "mountpoint", - deviceNumber: "deviceNumber", - removable: "removable", - readOnly: "readOnly", - children: (device) => { - return device.children.map((child) => { - return createBlockDevice({ name: child.name }); - }); - } - } - }); -} - -function createPhysicalVolume({ path }) { - return createDataObject({ - [LocalProperties]: { - path: path, - blockDevice: () => { - return createBlockDevice({ path: path }); - } - }, - lvmPhysicalVolumes: { - [ID]: path, - volumeGroup: (volume) => { - if (volume.volumeGroup != null) { - return createVolumeGroup({ name: volume.volumeGroup }); - } - }, - format: "format", - size: "totalSpace", - freeSpace: "freeSpace", - duplicate: "isDuplicate", - allocatable: "isAllocatable", - used: "isUsed", - exported: "isExported", - missing: "isMissing" - } - }); -} - -function createVolumeGroup({ name }) { - return createDataObject({ - [LocalProperties]: { - name: name - } - }); -} - -function createDrive({ path }) { - return createDataObject({ - [LocalProperties]: { - path: path, - blockDevice: () => { - return createBlockDevice({ path: path }); - }, - /* FIXME: allBlockDevices, for representing every single block device that's hosted on this physical drive, linearly. Need to figure out how that works with representation of mdraid arrays, LVM volumes, etc. */ - }, - smartctlScan: { - [ID]: path, - interface: "interface" - }, - smartctlInfo: { - [ID]: path, - model: "model", - modelFamily: "modelFamily", - smartAvailable: "smartAvailable", - smartEnabled: "smartEnabled", - serialNumber: "serialNumber", - wwn: "wwn", - firmwareVersion: "firmwareVersion", - size: "size", - rpm: "rpm", - logicalSectorSize: (device) => device.sectorSizes.logical, - physicalSectorSize: (device) => device.sectorSizes.physical, - formFactor: "formFactor", - ataVersion: "ataVersion", - sataVersion: "sataVersion" - }, - smartctlAttributes: { - [ID]: path, - smartAttributes: (attributes) => { - return attributes.map((attribute) => { - return Object.assign({}, attribute, { - type: upperSnakeCase(attribute.type), - updatedWhen: upperSnakeCase(attribute.updatedWhen) - }); - }); - }, - smartHealth: (attributes) => { - let failed = attributes.filter((item) => { - return (item.failingNow === true || item.failedBefore === true); - }); - - let deteriorating = attributes.filter((item) => { - return (item.type === "preFail" && item.worstValueSeen < 100); - }); - - if (failed.length > 0) { - return "FAILING"; - } else if (deteriorating.length > 0) { - return "DETERIORATING"; - } else { - return "HEALTHY"; - } - } - } - }); -} +let types = loadTypes({ + Drive: require("./types/drive"), + BlockDevice: require("./types/block-device"), + LVMPhysicalVolume: require("./types/lvm-physical-volume"), + LVMVolumeGroup: require("./types/lvm-volume-group"), +}); module.exports = function () { return createGraphQLInterface(schema, { loaderFactory: createLoaders }, { @@ -183,7 +61,7 @@ module.exports = function () { } }).then((devices) => { return devices.map((device) => { - return createDrive({ path: device.path }); + return types.Drive({ path: device.path }); }); }); } @@ -198,7 +76,7 @@ module.exports = function () { } }).then((devices) => { return devices.map((device) => { - return createBlockDevice({ name: device.name }); + return types.BlockDevice({ name: device.name }); }); }); }, @@ -212,7 +90,7 @@ module.exports = function () { } }).then((volumes) => { return volumes.map((volume) => { - return createPhysicalVolume({ path: volume.path }); + return types.LVMPhysicalVolume({ path: volume.path }); }); }); } diff --git a/src/api/types/block-device.js b/src/api/types/block-device.js new file mode 100644 index 0000000..3d7c97d --- /dev/null +++ b/src/api/types/block-device.js @@ -0,0 +1,37 @@ +"use strict"; + +const {createDataObject, LocalProperties, ID} = require("../../graphql/data-object"); +const matchOrError = require("../../match-or-error"); + +module.exports = function (_types) { + return function BlockDevice({ name, path }) { + if (name != null) { + path = `/dev/${name}`; + } else if (path != null) { + let match = matchOrError(/^\/dev\/(.+)$/, path); + name = match[0]; + } + + /* FIXME: parent */ + + return createDataObject({ + [LocalProperties]: { + path: path + }, + lsblk: { + [ID]: name, + name: "name", + size: "size", + mountpoint: "mountpoint", + deviceNumber: "deviceNumber", + removable: "removable", + readOnly: "readOnly", + children: (device) => { + return device.children.map((child) => { + return BlockDevice({ name: child.name }); + }); + } + } + }); + }; +}; \ No newline at end of file diff --git a/src/api/types/drive.js b/src/api/types/drive.js new file mode 100644 index 0000000..a19dbd5 --- /dev/null +++ b/src/api/types/drive.js @@ -0,0 +1,67 @@ +"use strict"; + +const {createDataObject, LocalProperties, ID} = require("../../graphql/data-object"); +const upperSnakeCase = require("../../upper-snake-case"); + +module.exports = function (types) { + return function Drive({ path }) { + return createDataObject({ + [LocalProperties]: { + path: path, + blockDevice: () => { + return types.BlockDevice({ path: path }); + }, + /* FIXME: allBlockDevices, for representing every single block device that's hosted on this physical drive, linearly. Need to figure out how that works with representation of mdraid arrays, LVM volumes, etc. */ + }, + smartctlScan: { + [ID]: path, + interface: "interface" + }, + smartctlInfo: { + [ID]: path, + model: "model", + modelFamily: "modelFamily", + smartAvailable: "smartAvailable", + smartEnabled: "smartEnabled", + serialNumber: "serialNumber", + wwn: "wwn", + firmwareVersion: "firmwareVersion", + size: "size", + rpm: "rpm", + logicalSectorSize: (device) => device.sectorSizes.logical, + physicalSectorSize: (device) => device.sectorSizes.physical, + formFactor: "formFactor", + ataVersion: "ataVersion", + sataVersion: "sataVersion" + }, + smartctlAttributes: { + [ID]: path, + smartAttributes: (attributes) => { + return attributes.map((attribute) => { + return Object.assign({}, attribute, { + type: upperSnakeCase(attribute.type), + updatedWhen: upperSnakeCase(attribute.updatedWhen) + }); + }); + }, + smartHealth: (attributes) => { + let failed = attributes.filter((item) => { + return (item.failingNow === true || item.failedBefore === true); + }); + + let deteriorating = attributes.filter((item) => { + return (item.type === "preFail" && item.worstValueSeen < 100); + }); + + if (failed.length > 0) { + return "FAILING"; + } else if (deteriorating.length > 0) { + return "DETERIORATING"; + } else { + return "HEALTHY"; + } + } + } + }); + }; +}; \ No newline at end of file diff --git a/src/api/types/lvm-physical-volume.js b/src/api/types/lvm-physical-volume.js new file mode 100644 index 0000000..038c146 --- /dev/null +++ b/src/api/types/lvm-physical-volume.js @@ -0,0 +1,32 @@ +"use strict"; + +const {createDataObject, LocalProperties, ID} = require("../../graphql/data-object"); + +module.exports = function (types) { + return function LVMPhysicalVolume({ path }) { + return createDataObject({ + [LocalProperties]: { + path: path, + blockDevice: () => { + return types.BlockDevice({ path: path }); + } + }, + lvmPhysicalVolumes: { + [ID]: path, + volumeGroup: (volume) => { + if (volume.volumeGroup != null) { + return types.LVMVolumeGroup({ name: volume.volumeGroup }); + } + }, + format: "format", + size: "totalSpace", + freeSpace: "freeSpace", + duplicate: "isDuplicate", + allocatable: "isAllocatable", + used: "isUsed", + exported: "isExported", + missing: "isMissing" + } + }); + }; +}; \ No newline at end of file diff --git a/src/api/types/lvm-volume-group.js b/src/api/types/lvm-volume-group.js new file mode 100644 index 0000000..be5d4df --- /dev/null +++ b/src/api/types/lvm-volume-group.js @@ -0,0 +1,13 @@ +"use strict"; + +const {createDataObject, LocalProperties} = require("../../graphql/data-object"); + +module.exports = function (_types) { + return function createVolumeGroup({ name }) { + return createDataObject({ + [LocalProperties]: { + name: name + } + }); + }; +}; \ No newline at end of file diff --git a/src/graphql/type-loader.js b/src/graphql/type-loader.js new file mode 100644 index 0000000..06d96c9 --- /dev/null +++ b/src/graphql/type-loader.js @@ -0,0 +1,11 @@ +"use strict"; + +module.exports = function loadTypes(types) { + let loadedTypes = {}; + + for (let [name, module_] of Object.entries(types)) { + loadedTypes[name] = module_(loadedTypes); + } + + return loadedTypes; +}; \ No newline at end of file