From c3a806011d8a809f1cf78fb6c3cba5be189ecdcf Mon Sep 17 00:00:00 2001 From: Chris Rodriguez Date: Wed, 13 Mar 2024 16:13:39 -0400 Subject: [PATCH] Reference pull request for full details. --- Api/Controllers/Collection.js | 7 + Api/Controllers/Import.js | 42 ++++- Api/Controllers/Label.js | 5 - Api/Models/assetLabels.model.js | 20 +++ Api/Models/label.model.js | 26 +++ Api/Services/mysql/assetLabelService.js | 100 +++++------ .../mysql/collectionApproverService.js | 2 - Api/Services/mysql/collectionService.js | 28 +++ Api/Services/mysql/labelService.js | 137 ++++++++------- Api/Services/mysql/poamApproverService.js | 2 - Api/specification/poam-manager.yaml | 159 +++++++++--------- Api/utils/sequelize.js | 2 + Database/POAM_Tracking_Tool_Data_Model.sql | 38 +---- .../poam-app/src/app/Shared/shared.service.ts | 11 +- Front End/poam-app/src/app/app.component.ts | 7 +- Front End/poam-app/src/app/app.module.ts | 1 - .../asset-processing.component.html | 70 ++++---- .../asset-processing.component.scss | 10 ++ .../asset-processing.component.ts | 106 ++++++++++-- .../asset-processing.module.ts | 10 +- .../asset-processing/asset/asset.component.ts | 35 ++-- .../pages/asset-processing/assets.service.ts | 9 +- .../label-processing.component.ts | 38 ++--- .../app/pages/label-processing/label.model.ts | 1 - .../pages/label-processing/label.service.ts | 37 ++-- .../label/label.component.html | 10 +- .../label-processing/label/label.component.ts | 60 ++++--- .../poam-details/poam-details.component.ts | 19 ++- .../poam-extend/poam-extend.component.ts | 46 ++++- .../poam-processing/poams.component.scss | 1 + .../app/pages/poam-processing/poams.module.ts | 1 - .../pages/poam-processing/poams.service.ts | 5 + 32 files changed, 635 insertions(+), 410 deletions(-) create mode 100644 Api/Models/assetLabels.model.js create mode 100644 Api/Models/label.model.js diff --git a/Api/Controllers/Collection.js b/Api/Controllers/Collection.js index bc5afe45..f1c3f648 100644 --- a/Api/Controllers/Collection.js +++ b/Api/Controllers/Collection.js @@ -26,6 +26,13 @@ module.exports.getCollection = async function getCollection(req, res, next){ res.status(201).json(getCollection) } +module.exports.getCollectionAssetLabel = async function getCollectionAssetLabel(req, res, next) { + + var getCollection = await collectionService.getCollectionAssetLabel(req, res, next) + + res.status(201).json(getCollection) +} + module.exports.getCollectionPoamStatus = async function getCollectionPoamStatus(req, res, next){ var getCollection = await collectionService.getCollectionPoamStatus(req, res, next) diff --git a/Api/Controllers/Import.js b/Api/Controllers/Import.js index 01af995a..b0a77006 100644 --- a/Api/Controllers/Import.js +++ b/Api/Controllers/Import.js @@ -285,6 +285,22 @@ module.exports.importCollectionAndAssets = async function importCollectionAndAss await collectionRecord.update(collectionData); } + if (collection.labels && Array.isArray(collection.labels)) { + for (const label of collection.labels) { + const labelData = { + collectionId: collectionRecord.collectionId, + labelName: label.name, + description: label.description, + stigmanLabelId: label.labelId, + }; + + await db.Label.findOrCreate({ + where: { stigmanLabelId: label.labelId, collectionId: collectionRecord.collectionId }, + defaults: labelData + }); + } + } + // Handle Assets for (const asset of assets) { const assetData = { @@ -307,12 +323,32 @@ module.exports.importCollectionAndAssets = async function importCollectionAndAss if (!assetCreated) { await assetRecord.update(assetData); } + if (asset.labelIds && Array.isArray(asset.labelIds)) { + for (const labelId of asset.labelIds) { + const labelRecord = await db.Label.findOne({ + where: { stigmanLabelId: labelId, collectionId: collectionRecord.collectionId }, + }); + + if (labelRecord) { + await db.AssetLabels.findOrCreate({ + where: { + assetId: assetRecord.assetId, + labelId: labelRecord.labelId + }, + defaults: { + assetId: assetRecord.assetId, + labelId: labelRecord.labelId, + collectionId: collectionRecord.collectionId + } + }); + } + } + } } - res.status(200).json({ message: 'Collection and Assets Imported Successfully' }); + res.status(200).json({ message: 'Collection, Assets, and Labels Imported Successfully' }); } catch (error) { - // Log the error and send a server error response console.error(error); res.status(500).json({ message: 'Internal Server Error' }); } -} +} \ No newline at end of file diff --git a/Api/Controllers/Label.js b/Api/Controllers/Label.js index 787aa5bc..58710c7b 100644 --- a/Api/Controllers/Label.js +++ b/Api/Controllers/Label.js @@ -12,31 +12,26 @@ const labelService = require('../Services/mysql/labelService') module.exports.getLabels = async function getLabels(req, res, next){ - // res.status(201).json({message: "getLabels Method called successfully"}) var labels = await labelService.getLabels(req,res,next); res.status(201).json(labels) } module.exports.getLabel = async function getLabel(req, res, next){ - // res.status(201).json({message: "getLabel Method called successfully"}); var label = await labelService.getLabel(req,res,next); res.status(201).json(label) } module.exports.postLabel = async function postLabel(req, res, next){ - //res.status(201).json({message: "post:Label Method called successfully"}); var label = await labelService.postLabel(req,res,next); res.status(201).json(label) } module.exports.putLabel = async function putLabel(req, res, next){ - // res.status(201).json({message: "putLabel Method called successfully"}); var label = await labelService.putLabel(req,res,next); res.status(201).json(label) } module.exports.deleteLabel= async function deleteLabel(req, res, next){ - // res.status(201).json({message: "deleteLabel Method called successfully"}); var label = await labelService.deleteLabel(req,res,next); res.status(201).json(label) } \ No newline at end of file diff --git a/Api/Models/assetLabels.model.js b/Api/Models/assetLabels.model.js new file mode 100644 index 00000000..2dbea8d5 --- /dev/null +++ b/Api/Models/assetLabels.model.js @@ -0,0 +1,20 @@ +module.exports = (sequelize, DataTypes) => { + const AssetLabels = sequelize.define('assetlabels', { + assetId: { + type: DataTypes.INTEGER, + primaryKey: true, + }, + collectionId: { + type: DataTypes.INTEGER, + }, + labelId: { + type: DataTypes.INTEGER, + primaryKey: true, + }, + }, { + tableName: 'assetlabels', + timestamps: false, + }); + + return AssetLabels; +}; \ No newline at end of file diff --git a/Api/Models/label.model.js b/Api/Models/label.model.js new file mode 100644 index 00000000..e0be86dc --- /dev/null +++ b/Api/Models/label.model.js @@ -0,0 +1,26 @@ +module.exports = (sequelize, DataTypes) => { + const Label = sequelize.define('label', { + labelId: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + collectionId: { + type: DataTypes.INTEGER, + }, + description: { + type: DataTypes.STRING(255), + }, + labelName: { + type: DataTypes.STRING(50), + }, + stigmanLabelId: { + type: DataTypes.STRING(50), + }, + }, { + tableName: 'label', + timestamps: false, + }); + + return Label; +}; \ No newline at end of file diff --git a/Api/Services/mysql/assetLabelService.js b/Api/Services/mysql/assetLabelService.js index 8d87e514..e784bcf4 100644 --- a/Api/Services/mysql/assetLabelService.js +++ b/Api/Services/mysql/assetLabelService.js @@ -14,17 +14,28 @@ const dbUtils = require('./utils') const mysql = require('mysql2') exports.getAssetLabels = async function getAssetLabels(req, res, next) { - + if (!req.params.collectionId) { + console.info('getAssetLabels collectionId not provided.'); + return next({ + status: 422, + errors: { + collectionId: 'is required', + } + }); + } try { let connection connection = await dbUtils.pool.getConnection() - let sql = "SELECT t1.assetId, assetName, t1.labelId, labelName FROM poamtracking.assetlabels t1 " + - "INNER JOIN poamtracking.asset t2 ON t1.assetId = t2.assetId " + - "INNER JOIN poamtracking.label t3 ON t1.labelId = t3.labelId " + - "ORDER BY t3.labelName" - - let [rowAssetLabels] = await connection.query(sql) - console.log("rowAssets: ", rowAssetLabels[0]) + let sql = ` + SELECT t1.assetId, assetName, t1.labelId, labelName + FROM poamtracking.assetlabels t1 + INNER JOIN poamtracking.asset t2 ON t1.assetId = t2.assetId + INNER JOIN poamtracking.label t3 ON t1.labelId = t3.labelId + WHERE t3.collectionId = ? + ORDER BY t3.labelName + `; + + let [rowAssetLabels] = await connection.query(sql, [req.params.collectionId]); await connection.release() var size = Object.keys(rowAssetLabels).length @@ -46,13 +57,11 @@ exports.getAssetLabels = async function getAssetLabels(req, res, next) { } catch (error) { let errorResponse = { null: "null" } - //await connection.release() return errorResponse; } } exports.getAssetLabelsByAsset = async function getAssetLabelsByAsset(req, res, next) { - //console.log("getAssetLabels (Service) ..."); if (!req.params.assetId) { console.info('getAssetLabelByAsset assetId not provided.'); return next({ @@ -200,19 +209,15 @@ exports.getAssetLabel = async function getAssetLabel(req, res, next) { } exports.postAssetLabel = async function posAssetLabel(req, res, next) { - // res.status(201).json({ message: "postAsset (Service) Method called successfully" }); - if (!req.body.assetId) { - console.info('postAssetLabel assetId not provided.'); + console.info('postAssetLabel assetId not provided.'); return next({ status: 422, errors: { assetId: 'is required', } }); - } - - if (!req.body.labelId) { + } else if (!req.body.labelId) { console.info('postAssetLabel labelId not provided.'); return next({ status: 422, @@ -220,6 +225,14 @@ exports.postAssetLabel = async function posAssetLabel(req, res, next) { labelId: 'is required', } }); + } else if (!req.body.collectionId) { + console.info('postAssetLabel collectionId not provided.'); + return next({ + status: 422, + errors: { + collectionId: 'is required', + } + }); } @@ -227,19 +240,18 @@ exports.postAssetLabel = async function posAssetLabel(req, res, next) { let connection connection = await dbUtils.pool.getConnection() - let sql_query = `INSERT INTO poamtracking.assetlabels (assetId, labelId) - values (?, ?)` + let sql_query = `INSERT INTO poamtracking.assetlabels (assetId, collectionId, labelId) + values (?, ?, ?)` - await connection.query(sql_query, [req.body.assetId, req.body.labelId]) + await connection.query(sql_query, [req.body.assetId, req.body.collectionId, req.body.labelId]) await connection.release() let sql = "SELECT t1.assetId, assetName, t1.labelId, labelName FROM poamtracking.assetlabels t1 " + "INNER JOIN poamtracking.asset t2 ON t1.assetId = t2.assetId " + "INNER JOIN poamtracking.label t3 ON t1.labelId = t3.labelId " + - "WHERE t1.assetId = " + req.body.assetId + " AND t1.labelId = " + req.body.labelId + - " ORDER BY t3.labelName" - let [rowAssetLabel] = await connection.query(sql) - console.log("rowAssetLabel: ", rowAssetLabel[0]) + "WHERE t1.assetId = ? AND t1.labelId = ? " + + "ORDER BY t3.labelName" + let [rowAssetLabel] = await connection.query(sql, [req.body.assetId, req.body.labelId]) await connection.release() var assetLabel = [rowAssetLabel[0]] @@ -255,31 +267,24 @@ exports.postAssetLabel = async function posAssetLabel(req, res, next) { } exports.putAssetLabel = async function putAssetLabel(req, res, next) { - // res.status(201).json({ message: "putAssetLabel(Service) Method called successfully" }); - - if (!req.body.assetId) { - console.info('putAssetLabel assetId not provided.'); - return next({ - status: 422, - errors: { - assetId: 'is required', - } - }); - } - - if (!req.body.labelId) { - console.info('putAssetLabel labelId not provided.'); - return next({ - status: 422, - errors: { - labelId: 'is required', - } - }); - } - + if (!req.body.assetId) { + console.info('postAssetLabel assetId not provided.'); + return next({ + status: 422, + errors: { + assetId: 'is required', + } + }); + } else if (!req.body.labelId) { + console.info('postAssetLabel labelId not provided.'); + return next({ + status: 422, + errors: { + labelId: 'is required', + } + }); + } try { - // Noting to update, only unique index, if we get here, just return what was sent in. - const message = new Object() message.assetId = req.body.assetId message.labelId = req.body.labelId @@ -294,7 +299,6 @@ exports.putAssetLabel = async function putAssetLabel(req, res, next) { } exports.deleteAssetLabel = async function deleteAssetLabel(req, res, next) { - // res.status(201).json({ message: "deletePermission (Service) Method called successfully" }); if (!req.params.assetId) { console.info('deleteAssetLabel assetId not provided.'); return next({ diff --git a/Api/Services/mysql/collectionApproverService.js b/Api/Services/mysql/collectionApproverService.js index 03ed0dbb..702f14e2 100644 --- a/Api/Services/mysql/collectionApproverService.js +++ b/Api/Services/mysql/collectionApproverService.js @@ -99,8 +99,6 @@ exports.postCollectionApprover = async function postCollectionApprover(req, res, let sql_query = `INSERT INTO poamtracking.collectionapprovers (collectionId, userId, status) values (?, ?, ?)` - //await connection.query(sql_query, [req.body.labelName, req.body.description, req.body.poamCount]) - //await connection.release() await connection.query(sql_query, [req.body.collectionId, req.body.userId, req.body.status]); await connection.release(); diff --git a/Api/Services/mysql/collectionService.js b/Api/Services/mysql/collectionService.js index f5226488..6d388384 100644 --- a/Api/Services/mysql/collectionService.js +++ b/Api/Services/mysql/collectionService.js @@ -240,6 +240,34 @@ exports.getCollection = async function getCollection(userName, collectionId, req +} + +exports.getCollectionAssetLabel = async function getCollectionAssetLabel(req, res, next) { + let connection; + try { + connection = await dbUtils.pool.getConnection(); + let sql = ` + SELECT l.labelName, COUNT(pl.labelId) AS labelCount + FROM poamtracking.assetlabels pl + INNER JOIN poamtracking.asset p ON pl.assetId = p.assetId + INNER JOIN poamtracking.label l ON pl.labelId = l.labelId + WHERE p.collectionId = ? + GROUP BY l.labelName; + `; + let [rows] = await connection.query(sql, [req.params.collectionId]); + + let assetLabel = rows.map(row => ({ + label: row.labelName, + labelCount: row.labelCount + })); + + return { assetLabel }; + } catch (error) { + console.error("Error fetching asset label counts: ", error); + throw new Error("Unable to fetch asset label counts"); + } finally { + if (connection) await connection.release(); + } } exports.getCollectionPoamStatus = async function getCollectionPoamStatus( req, res, next){ diff --git a/Api/Services/mysql/labelService.js b/Api/Services/mysql/labelService.js index 0587625e..9aadbb7d 100644 --- a/Api/Services/mysql/labelService.js +++ b/Api/Services/mysql/labelService.js @@ -14,15 +14,22 @@ const dbUtils = require('./utils') const mysql = require('mysql2') exports.getLabels = async function getLabels(req, res, next) { - console.log("getLabels (Service) ..."); + if (!req.params.collectionId) { + console.info('getLabel collectionId not provided.'); + return next({ + status: 422, + errors: { + collectionId: 'is required', + } + }); + } try { let connection connection = await dbUtils.pool.getConnection() - let sql = "SELECT * FROM poamtracking.label ORDER BY labelName;" - //console.log("getLabels sql: ", sql) + let sql = "SELECT * FROM poamtracking.label WHERE collectionId = ? ORDER BY labelName;"; + let [rowLabels] = await connection.query(sql, [req.params.collectionId]); - let [rowLabels] = await connection.query(sql) console.log("rowLabels: ", rowLabels[0]) await connection.release() @@ -31,16 +38,12 @@ exports.getLabels = async function getLabels(req, res, next) { var labels = [] for (let counter = 0; counter < size; counter++) { - // console.log("Before setting permissions size: ", size, ", counter: ",counter); labels.push({ "labelId": rowLabels[counter].labelId, "labelName": rowLabels[counter].labelName, "description": rowLabels[counter].description, - "poamCount": rowLabels[counter].poamCount }); - // console.log("After setting permissions size: ", size, ", counter: ",counter); - // if (counter + 1 >= size) break; } return { labels }; @@ -48,7 +51,6 @@ exports.getLabels = async function getLabels(req, res, next) { } catch (error) { let errorResponse = { null: "null" } - //await connection.release() return errorResponse; } } @@ -56,6 +58,14 @@ exports.getLabels = async function getLabels(req, res, next) { exports.getLabel = async function getLabel(req, res, next) { if (!req.params.labelId) { console.info('getLabel labelId not provided.'); + return next({ + status: 422, + errors: { + labelId: 'is required', + } + }); + } else if (!req.params.collectionId) { + console.info('getLabel collectionId not provided.'); return next({ status: 422, errors: { @@ -67,10 +77,9 @@ exports.getLabel = async function getLabel(req, res, next) { try { let connection connection = await dbUtils.pool.getConnection() - let sql = "SELECT * FROM poamtracking.label WHERE labelId=" + req.params.labelId + ";" - // console.log("getLabel sql: ", sql) + let sql = "SELECT * FROM poamtracking.label WHERE labelId = ? AND collectionId = ?"; + let [rowLabel] = await connection.query(sql, [req.params.labelId, req.params.collectionId]); - let [rowLabel] = await connection.query(sql) console.log("rowLabel: ", rowLabel[0]) await connection.release() @@ -80,16 +89,20 @@ exports.getLabel = async function getLabel(req, res, next) { } catch (error) { let errorResponse = { null: "null" } - //await connection.release() return errorResponse; } } exports.postLabel = async function postLabel(req, res, next) { - // res.status(201).json({ message: "postPermission (Service) Method called successfully" }); - // console.log("postPermission req.body: ", req.body) - - if (!req.body.labelName) { + if (!req.params.collectionId) { + console.info('getLabel collectionId not provided.'); + return next({ + status: 422, + errors: { + collectionId: 'is required', + } + }); + } else if (!req.body.labelName) { console.info('postLabel labelName not provided.'); return next({ status: 422, @@ -99,21 +112,18 @@ exports.postLabel = async function postLabel(req, res, next) { }); } - if (!req.body.poamCount) req.body.poamCount = 0; - try { let connection connection = await dbUtils.pool.getConnection() - let sql_query = `INSERT INTO poamtracking.label (labelName, description, poamCount) values (?, ?, ?)` + let sql_query = `INSERT INTO poamtracking.label (labelName, description, collectionId) VALUES (?, ?, ?)`; + await connection.query(sql_query, [req.body.labelName, req.body.description, req.params.collectionId]); - //await connection.query(sql_query, [req.body.labelName, req.body.description, req.body.poamCount]) - //await connection.release() - await connection.query(sql_query, [req.body.labelName, req.body.description, req.body.poamCount]) await connection.release() - let sql = "SELECT * FROM poamtracking.label WHERE labelName = '" + req.body.labelName + "';" - let [rowLabel] = await connection.query(sql) + let sql = "SELECT * FROM poamtracking.label WHERE labelName = ? AND collectionId = ?"; + let [rowLabel] = await connection.query(sql, [req.body.labelName, req.params.collectionId]); + console.log("rowLabel: ", rowLabel[0]) await connection.release() @@ -121,7 +131,6 @@ exports.postLabel = async function postLabel(req, res, next) { message.labelId = rowLabel[0].labelId message.labelName = rowLabel[0].labelName message.description = rowLabel[0].description - message.poamCount = rowLabel[0].poamCount return(message) } catch (error) { @@ -132,46 +141,47 @@ exports.postLabel = async function postLabel(req, res, next) { } exports.putLabel = async function putLabel(req, res, next) { - // res.status(201).json({ message: "putPermission (Service) Method called successfully" }); - // console.log("postPermission req.body: ", req.body) - if (!req.body.labelId) { - console.info('putLabel labelId not provided.'); - return next({ - status: 422, - errors: { - labelId: 'is required', - } - }); - } - - if (!req.body.labelName) { - console.info('putLabels labelName not provided.'); - return next({ - status: 422, - errors: { - labelName: 'is required', - } - }); - } - - if (!req.body.description) req.body.description = ""; - if (!req.body.poamCount) req.body.poamCount = 0; + if (!req.params.collectionId) { + console.info('getLabel collectionId not provided.'); + return next({ + status: 422, + errors: { + collectionId: 'is required', + } + }); + } else if (!req.body.labelId) { + console.info('putLabel labelId not provided.'); + return next({ + status: 422, + errors: { + labelId: 'is required', + } + }); + } else if (!req.body.labelName) { + console.info('putLabels labelName not provided.'); + return next({ + status: 422, + errors: { + labelName: 'is required', + } + }); + } else if (!req.body.description) { + req.body.description = ""; + } try { let connection connection = await dbUtils.pool.getConnection() - let sql_query = "UPDATE poamtracking.label SET labelName= ?, description= ?, " + - "poamCount= ? WHERE labelId = " + req.body.labelId + ";" - - await connection.query(sql_query, [req.body.labelName, req.body.description, req.body.poamCount]) + let sql_query = "UPDATE poamtracking.label SET labelName = ?, description = ? WHERE labelId = ? AND collectionId = ?"; + await connection.query(sql_query, [req.body.labelName, req.body.description, req.body.labelId, req.params.collectionId]); + await connection.release() const message = new Object() message.labelId = req.body.labelId message.labelName = req.body.labelName message.description = req.body.description - message.poamCount = req.body.poamCount return(message) } catch (error) { @@ -182,25 +192,30 @@ exports.putLabel = async function putLabel(req, res, next) { } exports.deleteLabel = async function deleteLabel(req, res, next) { - // res.status(201).json({ message: "deletePermission (Service) Method called successfully" }); if (!req.params.labelId) { - console.info('deleteLabel labelId not provided.'); + console.info('getLabel labelId not provided.'); return next({ status: 422, errors: { labelId: 'is required', } }); + } else if (!req.params.collectionId) { + console.info('getLabel collectionId not provided.'); + return next({ + status: 422, + errors: { + collectionId: 'is required', + } + }); } try { let connection connection = await dbUtils.pool.getConnection() - let sql = "DELETE FROM poamtracking.label WHERE labelId=" + req.params.labelId + ";" - //console.log("deleteLabel sql: ", sql) + let sql = "DELETE FROM poamtracking.label WHERE labelId = ? AND collectionId = ?"; + await connection.query(sql, [req.params.labelId, req.params.collectionId]); - await connection.query(sql) - // console.log("rowPermissions: ", rowPermissions[0]) await connection.release() var label = [] diff --git a/Api/Services/mysql/poamApproverService.js b/Api/Services/mysql/poamApproverService.js index 0b3e7a36..3b9ed82e 100644 --- a/Api/Services/mysql/poamApproverService.js +++ b/Api/Services/mysql/poamApproverService.js @@ -94,8 +94,6 @@ exports.postPoamApprover = async function postPoamApprover(req, res, next) { let sql_query = `INSERT INTO poamtracking.poamapprovers (poamId, userId, approved, approvedDate, comments) values (?, ?, ?, ?, ?)` - //await connection.query(sql_query, [req.body.labelName, req.body.description, req.body.poamCount]) - //await connection.release() await connection.query(sql_query, [req.body.poamId, req.body.userId, req.body.approved, req.body.approvedDate, req.body.comments]) await connection.release() diff --git a/Api/specification/poam-manager.yaml b/Api/specification/poam-manager.yaml index 362b808a..2193cad7 100644 --- a/Api/specification/poam-manager.yaml +++ b/Api/specification/poam-manager.yaml @@ -240,9 +240,15 @@ paths: - oauth: - 'c-pat:read' - /assetLabels: + /assetLabels/{collectionId}: + parameters: + - in: path + name: collectionId + required: true + schema: + type: integer get: - summary: Return all assets + summary: Return all assets with labels for a specific collection operationId: getAssetLabels responses: '200': @@ -734,6 +740,34 @@ paths: default: false - $ref: '#/components/parameters/benchmarkIdQuery' - $ref: '#/components/parameters/assetIdQuery' + + '/collection/{collectionId}/assetlabel': + parameters: + - $ref: '#/components/parameters/collectionIdPath' + get: + tags: + - Collection + security: + - oauth: + - 'c-pat:read' + summary: Return asset labels and label count by Collection + description: Return asset labels and label count by Collection + operationId: getCollectionAssetLabel + responses: + '200': + description: assetLabel response + content: + application/json: + schema: + $ref: '#/components/schemas/assetLabel' + '204': + description: No Content + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/error' '/collection/{collectionId}/poamlabel': parameters: @@ -1137,10 +1171,16 @@ paths: - oauth: - 'c-pat:read' - '/labels': + '/labels/{collectionId}': get: - summary: Return all labels + summary: Return all labels for a collection operationId: getLabels + parameters: + - in: path + name: collectionId + required: true + schema: + type: integer responses: '200': description: Collection Labels array response @@ -1164,15 +1204,20 @@ paths: - oauth: - 'c-pat:read' - '/label/{labelId}': + '/label/{collectionId}/{labelId}': parameters: + - in: path + name: collectionId + required: true + schema: + type: integer - in: path name: labelId required: true schema: type: integer get: - summary: Return a label by Id + summary: Return a label by labelId operationId: getLabel responses: '200': @@ -1218,7 +1263,13 @@ paths: - oauth: - 'c-pat:read' - '/label': + '/label/{collectionId}': + parameters: + - in: path + name: collectionId + required: true + schema: + type: integer post: summary: Add a label operationId: postLabel @@ -1273,77 +1324,6 @@ paths: security: - oauth: - 'c-pat:read' - - # '/permissions/user/{userId}': - # parameters: - # - in: path - # name: userId - # required: true - # schema: - # type: integer - # get: - # summary: Return all collection Permissionss for a user - # operationId: getPermissions_User - # responses: - # '200': - # description: Collection Permissions array response - # content: - # application/json: - # schema: - # type: array - # items: - # $ref: '#/components/schemas/user_Permissions' - # '403': - # $ref: '#/components/responses/forbidden' - # default: - # description: unexpected error - # content: - # application/json: - # schema: - # $ref: '#/components/schemas/error' - # tags: - # - Permissions - # security: - # - oauth: - # - 'c-pat:op' - - # '/permissions/user/{userId}/collection/{collectionId}': - # parameters: - # - in: path - # name: userId - # required: true - # schema: - # type: integer - # - in: path - # name: collectionId - # required: true - # schema: - # type: integer - # get: - # summary: Return unique user collection permission user - # operationId: getPermissions_UserCollection - # responses: - # '200': - # description: Collection Permissions array response - # content: - # application/json: - # schema: - # type: array - # items: - # $ref: '#/components/schemas/user_Permissions' - # '403': - # $ref: '#/components/responses/forbidden' - # default: - # description: unexpected error - # content: - # application/json: - # schema: - # $ref: '#/components/schemas/error' - # tags: - # - Permissions - # security: - # - oauth: - # - 'c-pat:op' '/permission': post: @@ -2903,11 +2883,14 @@ components: type: object required: - assetId + - collectionId - labelId additionalProperties: true properties: assetId: type: integer + collectionId: + type: integer labelId: type: integer @@ -3096,20 +3079,18 @@ components: label_Object: type: object required: - - labelId - labelName - - description - - poamCount - additionalProperties: false + - collectionId + additionalProperties: true properties: labelId: type: integer + collectionId: + type: integer labelName: type: string description: type: string - poamCount: - type: integer label: type: object @@ -3295,6 +3276,8 @@ components: properties: poamId: type: integer + collectionId: + type: integer labelId: type: integer @@ -3344,6 +3327,14 @@ components: type: string description: Schema for updating a milestone. All fields are optional, allowing partial updates. + assetLabel: + type: object + properties: + label: + type: string + labelCount: + type: integer + poamStatus: type: object properties: diff --git a/Api/utils/sequelize.js b/Api/utils/sequelize.js index 97ccdc3a..49af61c2 100644 --- a/Api/utils/sequelize.js +++ b/Api/utils/sequelize.js @@ -30,7 +30,9 @@ db.sequelize = sequelize; // Importing and initializing models db.Asset = require("../Models/asset.model.js")(sequelize, Sequelize.DataTypes); +db.AssetLabels = require("../Models/assetLabels.model.js")(sequelize, Sequelize.DataTypes); db.Collection = require("../Models/collection.model.js")(sequelize, Sequelize.DataTypes); +db.Label = require("../Models/label.model.js")(sequelize, Sequelize.DataTypes); db.Poam = require("../Models/poam.model.js")(sequelize, Sequelize.DataTypes); db.poamAsset = require("../Models/poamAsset.model.js")(sequelize, Sequelize.DataTypes); db.poamMilestone = require("../Models/poamMilestone.model.js")(sequelize, Sequelize.DataTypes); diff --git a/Database/POAM_Tracking_Tool_Data_Model.sql b/Database/POAM_Tracking_Tool_Data_Model.sql index da0ef0a9..20ca2829 100644 --- a/Database/POAM_Tracking_Tool_Data_Model.sql +++ b/Database/POAM_Tracking_Tool_Data_Model.sql @@ -66,6 +66,7 @@ CREATE TABLE `poamtracking`.`poamassignees` ( CREATE TABLE `poamtracking`.`assetlabels` ( `assetId` int NOT NULL, + `collectionId` int NOT NULL, `labelId` int NOT NULL, PRIMARY KEY (`assetId`,`labelId`) ) @@ -78,9 +79,10 @@ CREATE TABLE `poamtracking`.`assetlabels` ( CREATE TABLE `poamtracking`.`label` ( `labelId` int NOT NULL AUTO_INCREMENT, + `collectionId` int NOT NULL, `description` varchar(255) DEFAULT NULL, `labelName` varchar(50) NOT NULL, - `poamCount` int NOT NULL DEFAULT '0', + `stigmanLabelId` varchar(36) DEFAULT NULL, PRIMARY KEY (`labelId`), UNIQUE KEY `labelName_UNIQUE` (`labelName`) ); @@ -247,40 +249,6 @@ BEGIN END $$ DELIMITER ; -DELIMITER $$ -CREATE TRIGGER `after_poamasset_insert` -AFTER INSERT ON `poamtracking`.`poamassets` -FOR EACH ROW -BEGIN - UPDATE `label` - JOIN `assetlabels` ON `label`.`labelId` = `assetlabels`.`labelId` - SET `label`.`poamCount` = ( - SELECT COUNT(DISTINCT `pa`.`poamId`) - FROM `poamassets` `pa` - JOIN `assetlabels` `al` ON `pa`.`assetId` = `al`.`assetId` - WHERE `al`.`labelId` = `label`.`labelId` - ) - WHERE `assetlabels`.`assetId` = NEW.`assetId`; -END $$ -DELIMITER ; - -DELIMITER $$ -CREATE TRIGGER `after_poamasset_delete` -AFTER DELETE ON `poamtracking`.`poamassets` -FOR EACH ROW -BEGIN - UPDATE `label` - JOIN `assetlabels` ON `label`.`labelId` = `assetlabels`.`labelId` - SET `label`.`poamCount` = ( - SELECT COUNT(DISTINCT `pa`.`poamId`) - FROM `poamassets` `pa` - JOIN `assetlabels` `al` ON `pa`.`assetId` = `al`.`assetId` - WHERE `al`.`labelId` = `label`.`labelId` - ) - WHERE `assetlabels`.`assetId` = OLD.`assetId`; -END $$ -DELIMITER ; - DELIMITER $$ CREATE TRIGGER `after_poam_insert` AFTER INSERT ON `POAM` diff --git a/Front End/poam-app/src/app/Shared/shared.service.ts b/Front End/poam-app/src/app/Shared/shared.service.ts index f4e6c6ba..bbbb2f4a 100644 --- a/Front End/poam-app/src/app/Shared/shared.service.ts +++ b/Front End/poam-app/src/app/Shared/shared.service.ts @@ -1,12 +1,13 @@ import { Injectable } from '@angular/core'; import axios from 'axios'; -import { from, Observable, throwError } from 'rxjs'; +import { BehaviorSubject, from, Observable, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { environment } from '../../environments/environment'; @Injectable({ providedIn: 'root' }) export class SharedService { - + private _selectedCollection = new BehaviorSubject(null); + public readonly selectedCollection = this._selectedCollection.asObservable(); constructor() { } private getHeaders(token: string) { @@ -16,6 +17,10 @@ export class SharedService { }; } + public setSelectedCollection(collection: any) { + this._selectedCollection.next(collection); + } + getSTIGsFromSTIGMAN(token: string): Observable { const headers = this.getHeaders(token); return from(axios.get(environment.getSTIGsFromSTIGMANEndpoint, { headers }) @@ -38,7 +43,7 @@ export class SharedService { selectedCollectionFromSTIGMAN(collectionId: string, token: string): Observable { const headers = this.getHeaders(token); - const endpoint = environment.getCollectionsFromSTIGMANEndpoint + collectionId; + const endpoint = environment.getCollectionsFromSTIGMANEndpoint + collectionId + "?projection=labels"; return from(axios.get(endpoint, { headers }) .then(response => response.data) .catch(error => { diff --git a/Front End/poam-app/src/app/app.component.ts b/Front End/poam-app/src/app/app.component.ts index 3dbc4146..24af5439 100644 --- a/Front End/poam-app/src/app/app.component.ts +++ b/Front End/poam-app/src/app/app.component.ts @@ -16,6 +16,7 @@ import { FileUploadService } from './file-upload.service'; import { HttpEventType, HttpResponse } from '@angular/common/http'; import { ChangeDetectorRef } from '@angular/core'; import { StatusDialogComponent } from './Shared/components/status-dialog/status-dialog.component'; +import { SharedService } from './Shared/shared.service'; interface Permission { userId: number; @@ -66,6 +67,7 @@ export class AppComponent implements OnInit, OnDestroy { private themeService: NbThemeService, private authService: AuthService, private router: Router, + private sharedService: SharedService, private collectionService: CollectionsService, private userService: UsersService, private poamService: PoamService, @@ -169,7 +171,6 @@ export class AppComponent implements OnInit, OnDestroy { if (this.selectedCollection) { this.resetWorkspace(this.selectedCollection); - } if ((!this.payload.lastCollectionAccessedId) || this.payload.lastCollectionAccessedId == undefined) { @@ -253,13 +254,13 @@ export class AppComponent implements OnInit, OnDestroy { resetWorkspace(selectedCollection: any) { this.selectedCollection = selectedCollection; this.selectCollectionMsg = false; + this.sharedService.setSelectedCollection(parseInt(this.selectedCollection, 10)); let collection = this.collections.find((x: { collectionId: any; }) => x.collectionId == this.selectedCollection) if (collection) { var stWorkspace = document.getElementById('selectedCollection'); if (stWorkspace) { var att = stWorkspace.getElementsByTagName("BUTTON")[0]; - // console.log("collection: ",collection) att.textContent = "Collection - " + collection.collectionName } } @@ -278,9 +279,7 @@ export class AppComponent implements OnInit, OnDestroy { isAdmin: this.user.isAdmin, updateSettingsOnly: 1 } - // console.log("resetWorkspace payload.collections: ",this.payload.collections) let selectedPermissions = this.payload.collections.find((x: { collectionId: any; }) => x.collectionId == selectedCollection) - // console.log("resetWorkspace selectedPermission: ",selectedPermissions) let myRole = '' if (!selectedPermissions && !this.user.isAdmin) { diff --git a/Front End/poam-app/src/app/app.module.ts b/Front End/poam-app/src/app/app.module.ts index a0b0d499..6df8f57a 100644 --- a/Front End/poam-app/src/app/app.module.ts +++ b/Front End/poam-app/src/app/app.module.ts @@ -35,7 +35,6 @@ import { PoamApproveModule } from "./pages/poam-processing/poam-approve/poam-app import { PoamExtendModule } from "./pages/poam-processing/poam-extend/poam-extend.module"; import { FileUploadService } from './file-upload.service'; import { provideCharts, withDefaultRegisterables } from 'ng2-charts'; -import { NbDateFnsDateModule } from '@nebular/date-fns'; function initializeKeycloak(keycloak: KeycloakService) { return () => diff --git a/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.html b/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.html index 321e7494..3470e157 100644 --- a/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.html +++ b/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.html @@ -1,30 +1,43 @@ -
+
- - - -

Assets

- - - - - - - - - - - - - - -
{{customColumn}} - {{row.data[customColumn]}} - {{column}}{{row.data[column] || '-'}}
- -
-
+ + + + + + +

Assets

+ + + + + + + + + + + + + + +
{{customColumn}} + {{row.data[customColumn]}} + {{column}}{{row.data[column] || '-'}}
+ + +
+ + +
+ +
+
+
+
+
+
@@ -87,14 +100,7 @@

Assets

- - -

- Collections processing may require role of admin. -

-

-
diff --git a/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.scss b/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.scss index 1669d608..0df4d9ea 100644 --- a/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.scss +++ b/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.scss @@ -8,6 +8,16 @@ !######################################################################## */ +.asset-label-chart { + overflow: hidden; + height: 50vh; + width: 100vw; +} + +.canvas { + max-height: 50vh; + max-width: 100vw; +} .add-asset-button { margin-top: 20px; diff --git a/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.ts b/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.ts index 2b4c82b0..17defd5c 100644 --- a/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.ts +++ b/Front End/poam-app/src/app/pages/asset-processing/asset-processing.component.ts @@ -24,6 +24,7 @@ import { UsersService } from '../user-processing/users.service'; import { environment } from '../../../environments/environment'; import { ChangeDetectorRef } from '@angular/core'; import { Assets } from './asset.model'; +import { Chart, registerables, ChartData } from 'chart.js'; interface Permission { userId: number; @@ -60,11 +61,41 @@ interface FSEntry { styleUrls: ['./asset-processing.component.scss'] }) export class AssetProcessingComponent implements OnInit, AfterViewInit { + @ViewChild('assetLabelsChart') assetLabelsChart!: ElementRef; @ViewChild('assetScrollListener', { read: ElementRef }) select!: ElementRef; offset = 0; limit = 50; isListFull = false; + public assetLabel: any[] = []; + public selectedLabel: any = 'All'; + assetLabelChart!: Chart; + assetLabelChartData: ChartData<'bar'> = { + labels: [''], + datasets: [], + }; + public selectedPosition: any = 'bottom'; + barChartOptions = { + responsive: true, + maintainAspectRatio: false, + scales: { + x: { grid: { display: true } }, + y: { beginAtZero: true, grid: { display: false } }, + }, + plugins: { + legend: { + display: true, + position: this.selectedPosition, + labels: { + font: { + size: 13, + family: 'sans-serif', + weight: 600, + } + } + }, + }, + }; customColumn = 'asset'; defaultColumns = ['Asset Name', 'Description', 'Collection', 'IP Address', 'Domain', 'MAC Address']; allColumns = [this.customColumn, ...this.defaultColumns]; @@ -80,8 +111,8 @@ export class AssetProcessingComponent implements OnInit, AfterViewInit { availableAssets: any[] = []; selectedAssets: string[] = []; - collections: any[] = []; // Array to hold collections - selectedCollection: string = ''; // Selected collection ID + collections: any[] = []; + selectedCollection: string = ''; assets: any; asset: any = {}; data: any = []; @@ -113,7 +144,8 @@ export class AssetProcessingComponent implements OnInit, AfterViewInit { private readonly keycloak: KeycloakService, private userService: UsersService, private dataSourceBuilder: NbTreeGridDataSourceBuilder) { - } + Chart.register(...registerables); +} onSubmit() { this.resetData(); @@ -138,6 +170,8 @@ export class AssetProcessingComponent implements OnInit, AfterViewInit { } }, 0); }); + this.initializeChart(); + this.cdr.detectChanges(); } getSelectPanel(): HTMLElement | null { @@ -145,6 +179,23 @@ export class AssetProcessingComponent implements OnInit, AfterViewInit { return dropdownPanel as HTMLElement; } + private initializeChart(): void { + this.cdr.detectChanges(); + if (this.assetLabelsChart?.nativeElement) { + this.assetLabelChart = new Chart(this.assetLabelsChart.nativeElement, { + type: 'bar', + data: this.assetLabelChartData, + options: this.barChartOptions, + }); + if (this.assetLabel) { + this.updateLabelChartData(this.assetLabel); + } + } else { + console.error('Unable to initialize chart: Element not available.'); + } + + } + fetchCollections() { this.keycloak.getToken().then(token => { this.sharedService.getCollectionsFromSTIGMAN(token).subscribe({ @@ -296,16 +347,33 @@ export class AssetProcessingComponent implements OnInit, AfterViewInit { if (this.payload == undefined) return; - this.subs.sink = this.assetService.getAssetsByCollection(this.user.lastCollectionAccessedId, this.offset, this.limit) - .subscribe((assetData: any) => { - console.log(assetData); + this.subs.sink = forkJoin( + this.assetService.getAssetsByCollection( + this.user.lastCollectionAccessedId, + this.offset, + this.limit + ), + this.assetService.getCollectionAssetLabel( + this.payload.lastCollectionAccessedId + ) + ).subscribe(([assetData, assetLabelResponse]: any) => { if (!Array.isArray(assetData)) { - console.error('Unexpected response format:', assetData); + console.error('Unexpected response format:', assetData + ); this.isLoading = false; - return; } + else if (!Array.isArray(assetLabelResponse.assetLabel)) { + console.log("assetLabelResponse: ", assetLabelResponse); + console.error( + 'assetLabelResponse.assetLabel is not an array', + assetLabelResponse.assetLabel + ); + return; + } this.assets = loadMore ? [...this.assets, ...assetData] : assetData; - + this.assetLabel = assetLabelResponse.assetLabel; + this.setLabelChartData(this.assetLabel); + this.data = this.assets.map((asset: any) => ({ assetId: asset.assetId, assetName: asset.assetName, @@ -327,6 +395,24 @@ export class AssetProcessingComponent implements OnInit, AfterViewInit { }); } + setLabelChartData(assetLabel: any[]) { + this.updateLabelChartData(assetLabel); + } + + updateLabelChartData(assetLabel: any[]): void { + if (!this.assetLabelChart) { + console.warn("Asset Label chart is not initialized."); + return; + } + const datasets = assetLabel.map((item) => ({ + label: item.label, + data: [item.labelCount] + })); + this.assetLabelChart.data.datasets = datasets; + this.assetLabelChart.update(); + } + + updateDataSource() { let treeNodes: AssetTreeNode[] = this.assets.map((asset: Assets) => { return { @@ -381,8 +467,6 @@ export class AssetProcessingComponent implements OnInit, AfterViewInit { let selectedData = this.data.filter((asset: { assetId: any; }) => asset.assetId === assetId) this.asset = selectedData[0]; - //console.log("asset: ", this.asset); - //console.log("assets: ", this.assets); this.allowSelectAssets = false; } diff --git a/Front End/poam-app/src/app/pages/asset-processing/asset-processing.module.ts b/Front End/poam-app/src/app/pages/asset-processing/asset-processing.module.ts index 627f04cc..585ee871 100644 --- a/Front End/poam-app/src/app/pages/asset-processing/asset-processing.module.ts +++ b/Front End/poam-app/src/app/pages/asset-processing/asset-processing.module.ts @@ -15,9 +15,11 @@ import { AssetProcessingComponent } from './asset-processing.component'; import { AssetProcessingRoutingModule } from './asset-processing.routing'; import { SharedModule } from '../../Shared/shared.module'; import { AssetComponent } from './asset/asset.component'; -import { NbButtonModule, NbInputModule, NbCardModule,NbLayoutModule, NbTreeGridModule, NbSpinnerModule, NbSelectModule, NbIconModule, NbTableModule, NbTooltipModule } from '@nebular/theme'; +import { NbButtonModule, NbInputModule, NbCardModule,NbLayoutModule, NbTreeGridModule, NbSpinnerModule, NbSelectModule, NbIconModule, NbTableModule, NbTooltipModule, NbTabsetModule } from '@nebular/theme'; import { Angular2SmartTableModule } from 'angular2-smart-table'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'; +import { AppComponent } from '../../app.component'; +import { provideCharts, withDefaultRegisterables } from 'ng2-charts'; @NgModule({ declarations: [ @@ -37,13 +39,15 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll'; NbLayoutModule, NbSelectModule, NbSpinnerModule, + NbTabsetModule, NbTooltipModule, NbTreeGridModule, Angular2SmartTableModule, SharedModule, ], - exports: [ - ] + providers: [ + provideCharts(withDefaultRegisterables()), + ], }) export class AssetProcessingModule { } diff --git a/Front End/poam-app/src/app/pages/asset-processing/asset/asset.component.ts b/Front End/poam-app/src/app/pages/asset-processing/asset/asset.component.ts index 30904434..f1e97d5c 100644 --- a/Front End/poam-app/src/app/pages/asset-processing/asset/asset.component.ts +++ b/Front End/poam-app/src/app/pages/asset-processing/asset/asset.component.ts @@ -12,17 +12,17 @@ import { Component, EventEmitter, Input, OnInit, Output, TemplateRef } from '@an import { NbDialogService, NbWindowRef } from '@nebular/theme'; import { ConfirmationDialogComponent, ConfirmationDialogOptions } from '../../../Shared/components/confirmation-dialog/confirmation-dialog.component'; import { SubSink } from 'subsink'; -import { Observable } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; import { AssetService } from '../assets.service' import { AuthService } from '../../../auth'; import { Settings } from 'angular2-smart-table'; import { SmartTableSelectComponent } from '../../../Shared/components/smart-table/smart-table-select.component'; +import { SharedService } from '../../../Shared/shared.service'; interface Label { labelId?: number; labelName?: string; description?: string; - poamCount?: number; } interface LabelsResponse { @@ -37,7 +37,6 @@ interface Collection { created?: string; grantCount?: number; assetCount?: number; - poamCount?: number; } interface CollectionsResponse { @@ -63,9 +62,10 @@ export class AssetComponent implements OnInit { assetLabels: any[] = []; data: any = []; tcollectionName: string = ""; - modalWindow: NbWindowRef | undefined dialog!: TemplateRef; + selectedCollection: any; + private subscriptions = new Subscription(); assetLabelsSettings: Settings = { add: { @@ -119,14 +119,21 @@ export class AssetComponent implements OnInit { constructor(private dialogService: NbDialogService, private assetService: AssetService, private authService: AuthService, - // private iconLibraries: NbIconLibraries + private sharedService: SharedService, ) { } ngOnInit(): void { if (this.payload === undefined) return; - this.data = []; - this.data = this.asset; - this.getData(); + else { + this.data = []; + this.data = this.asset; + this.getData(); + } + this.subscriptions.add( + this.sharedService.selectedCollection.subscribe(collectionId => { + this.selectedCollection = collectionId; + }) + ); } onSubmit() { @@ -189,7 +196,7 @@ export class AssetComponent implements OnInit { } getLabelData() { - this.subs.sink = this.assetService.getLabels().subscribe((labels: any) => { + this.subs.sink = this.assetService.getLabels(this.selectedCollection).subscribe((labels: any) => { this.labelList = labels.labels; this.updateLabelEditorConfig(); }); @@ -249,12 +256,8 @@ this.assetLabelsSettings = Object.assign({}, labelSettings); confirmCreate(event: any) { - // console.log("Attempting to confirmCreate()..."); if (this.asset.assetId === "ADDASSET") { - // nothing to do, when the asset is submitted, we'll push the array of label id's to so they can be - // associated properly to the asset - event.confirm.resolve(); return; } @@ -274,7 +277,8 @@ this.assetLabelsSettings = Object.assign({}, labelSettings); let assetLabel = { assetId: +this.asset.assetId, - labelId: +event.newData.labelId + collectionId: this.selectedCollection, + labelId: +event.newData.labelId, } this.isLoading = true; @@ -350,7 +354,8 @@ this.assetLabelsSettings = Object.assign({}, labelSettings); }).onClose; ngOnDestroy() { - this.subs.unsubscribe() + this.subs.unsubscribe(); + this.subscriptions.unsubscribe(); } } diff --git a/Front End/poam-app/src/app/pages/asset-processing/assets.service.ts b/Front End/poam-app/src/app/pages/asset-processing/assets.service.ts index cbe3f2a0..9a5f9685 100644 --- a/Front End/poam-app/src/app/pages/asset-processing/assets.service.ts +++ b/Front End/poam-app/src/app/pages/asset-processing/assets.service.ts @@ -53,9 +53,9 @@ export class AssetService { .pipe(catchError(this.handleError)); } - getLabels() { + getLabels(collectionId: string) { return this.http - .get(`${this.uri}/labels`) + .get(`${this.uri}/labels/${collectionId}`) .pipe(catchError(this.handleError)); } @@ -78,6 +78,11 @@ export class AssetService { .pipe(catchError(this.handleError)); } + getCollectionAssetLabel(id: string) { + return this.http.get(`${this.uri}/collection/${id}/assetlabel`, this.httpOptions) + .pipe(catchError(this.handleError)); + } + postAssetLabel(assetLabel: any) { return this.http .post(`${this.uri}/assetLabel`, assetLabel, this.httpOptions); diff --git a/Front End/poam-app/src/app/pages/label-processing/label-processing.component.ts b/Front End/poam-app/src/app/pages/label-processing/label-processing.component.ts index 373abaa8..820ef0df 100644 --- a/Front End/poam-app/src/app/pages/label-processing/label-processing.component.ts +++ b/Front End/poam-app/src/app/pages/label-processing/label-processing.component.ts @@ -13,12 +13,13 @@ import { Router } from '@angular/router'; import { NbDialogService, NbTreeGridDataSource, NbTreeGridDataSourceBuilder } from '@nebular/theme'; import { KeycloakService } from 'keycloak-angular'; import { KeycloakProfile } from 'keycloak-js'; -import { Observable } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; import { SubSink } from "subsink"; import { ConfirmationDialogComponent, ConfirmationDialogOptions } from '../../Shared/components/confirmation-dialog/confirmation-dialog.component'; import { AuthService } from '../../auth'; import { UsersService } from '../user-processing/users.service'; import { LabelService } from './label.service'; +import { SharedService } from '../../Shared/shared.service'; interface Permission { userId: number; @@ -38,8 +39,6 @@ interface FSEntry { labelId?: string; labelName?: string; description?: string; - poamCount?: string; - } @Component({ @@ -49,7 +48,7 @@ interface FSEntry { }) export class LabelProcessingComponent implements OnInit { customColumn = 'label'; - defaultColumns = [ 'Name', 'Description', 'POAM Count' ]; + defaultColumns = [ 'Name', 'Description' ]; allColumns = [ this.customColumn, ...this.defaultColumns ]; dataSource!: NbTreeGridDataSource; @@ -57,18 +56,16 @@ export class LabelProcessingComponent implements OnInit { user: any; public isLoggedIn = false; public userProfile: KeycloakProfile | null = null; - labels: any; label: any={}; data: any= []; - allowSelectLabels = true; isLoading = true; - selected: any selectedRole: string = 'admin'; payload: any; - + selectedCollection: any; + private subscriptions = new Subscription(); get hideCollectionEntry() { return (this.label.labelId && this.label.labelId != "LABEL") ? { display: 'block' } @@ -84,11 +81,11 @@ export class LabelProcessingComponent implements OnInit { private authService: AuthService, private readonly keycloak: KeycloakService, private userService: UsersService, + private sharedService: SharedService, private dataSourceBuilder: NbTreeGridDataSourceBuilder) { } onSubmit() { - console.log("Attempting to onSubmit()..."); this.resetData(); } @@ -96,11 +93,13 @@ export class LabelProcessingComponent implements OnInit { this.isLoggedIn = await this.keycloak.isLoggedIn(); if (this.isLoggedIn) { this.userProfile = await this.keycloak.loadUserProfile(); - console.log("Poams component userProfile: ",this.userProfile.email) - console.log("Poams component userProfile: ",this.userProfile.username) this.setPayload(); } - + this.subscriptions.add( + this.sharedService.selectedCollection.subscribe(collectionId => { + this.selectedCollection = collectionId; + }) + ); } setPayload() { @@ -141,11 +140,10 @@ export class LabelProcessingComponent implements OnInit { getLabelData() { this.isLoading = true; this.labels = null; - this.labelService.getLabels().subscribe((result: any) => { + this.labelService.getLabels(this.selectedCollection).subscribe((result: any) => { this.data = result.labels.sort((a: { labelId: number; }, b: { labelId: number; }) => a.labelId - b.labelId); this.labels = this.data; - console.log("Labels: ",this.data) this.getLabelsGrid(""); this.isLoading = false; }); @@ -156,17 +154,15 @@ export class LabelProcessingComponent implements OnInit { let labelData = this.data; var treeViewData: TreeNode[] = labelData.map((label: { labelId: number | any[]; labelName: any; - description: any; poamCount: any; }) => { + description: any;}) => { let myChildren: never[] = []; return { - data: { label: label.labelId, 'Name': label.labelName, 'Description': label.description, - 'POAM Count': label.poamCount}, + data: { label: label.labelId, 'Name': label.labelName, 'Description': label.description}, children: myChildren }; }) - console.log("treeViewData: ", treeViewData) this.dataSource = this.dataSourceBuilder.create(treeViewData); } @@ -176,8 +172,6 @@ export class LabelProcessingComponent implements OnInit { let selectedData = this.data.filter((label: { labelId: any; }) => label.labelId === labelId) this.label = selectedData[0]; - console.log("label: ",this.label); - console.log("labels: ",this.labels); this.allowSelectLabels = false; } @@ -192,12 +186,12 @@ export class LabelProcessingComponent implements OnInit { this.label.labelId = "ADDLABEL"; this.label.labelName = ""; this.label.description = "" - this.label.poamCount = 0; this.allowSelectLabels = false; } ngOnDestroy() { - this.subs.unsubscribe() + this.subs.unsubscribe(); + this.subscriptions.unsubscribe(); } confirm = (dialogOptions: ConfirmationDialogOptions): Observable => diff --git a/Front End/poam-app/src/app/pages/label-processing/label.model.ts b/Front End/poam-app/src/app/pages/label-processing/label.model.ts index a729a9a7..c8b7891c 100644 --- a/Front End/poam-app/src/app/pages/label-processing/label.model.ts +++ b/Front End/poam-app/src/app/pages/label-processing/label.model.ts @@ -13,5 +13,4 @@ export interface Label { labelId?: number; labelName?: string; description?: string; - poamCount?: number; } diff --git a/Front End/poam-app/src/app/pages/label-processing/label.service.ts b/Front End/poam-app/src/app/pages/label-processing/label.service.ts index b96ee214..a7fad4be 100644 --- a/Front End/poam-app/src/app/pages/label-processing/label.service.ts +++ b/Front End/poam-app/src/app/pages/label-processing/label.service.ts @@ -9,8 +9,8 @@ */ import { Injectable } from '@angular/core'; -import { HttpClient, HttpParams, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; -import { Observable, throwError } from 'rxjs'; +import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; +import { throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { environment } from '../../../environments/environment'; import { Label } from './label.model'; @@ -30,52 +30,41 @@ export class LabelService { private handleError(error: HttpErrorResponse) { if (error.error instanceof ErrorEvent) { - // A client-side or network error occurred. Handle it accordingly. console.error('An error occurred:', error.error.message); } else { - // The backend returned an unsuccessful response code. - // The response body may contain clues as to what went wrong, console.error(`Backend returned code ${error.status}, ` + `body was: ${error.error}`); } - // return an observable with a user-facing error message return throwError('Something bad happened; please try again later.'); } - getLabels() { - // console.log("Label Service Call attempted: getLabels()..."); - + getLabels(collectionId: string) { return this.http - .get(`${this.uri}/labels` ) + .get(`${this.uri}/labels/${collectionId}` ) .pipe(catchError(this.handleError)); } - getLabel(id: string) { - // console.log("Collectons Service Call attempted: getCollectionById()...Id:" + id); + getLabel(collectionId: string, labelId: string) { return this.http - .get(`${this.uri}/label/${id}`) + .get(`${this.uri}/label/${collectionId}/${labelId}`) .pipe(catchError(this.handleError)); } - addLabel(label: any) { - //console.log("Label Service Call attempted: addLabel(label) label: ",label); + addLabel(collectionId: string, label: any) { return this.http - .post