Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions constants/interface-routes/elevate-project/configs.json
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,34 @@
],
"service": "project"
},
{
"sourceRoute": "/project/v1/library/categories/delete/:id",
"type": "DELETE",
"priority": "MUST_HAVE",
"inSequence": false,
"orchestrated": false,
"targetPackages": [
{
"basePackageName": "project",
"packageName": "elevate-project"
}
],
"service": "project"
},
{
"sourceRoute": "/project/v1/library/categories/details/:id",
"type": "GET",
"priority": "MUST_HAVE",
"inSequence": false,
"orchestrated": false,
"targetPackages": [
{
"basePackageName": "project",
"packageName": "elevate-project"
}
],
"service": "project"
},
{
"sourceRoute": "/project/v1/programs/create",
"type": "POST",
Expand Down
67 changes: 63 additions & 4 deletions controllers/v1/library/categories.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module.exports = class LibraryCategories extends Abstract {
}

/**
* @api {get} /improvement-project/api/v1/library/categories/projects/:categoryExternalId?page=:page&limit=:limit&search=:search&sort=:sort
* @api {get} /improvement-project/api/v1/library/categories/projects/:categoryExternalId?page=:page&limit=:limit&search=:search&sort=:sort
* List of library projects.
* @apiVersion 1.0.0
* @apiGroup Library Categories
Expand All @@ -56,7 +56,7 @@ module.exports = class LibraryCategories extends Abstract {
"description" : "Test template description",
"createdAt": "2020-08-31T05:59:12.230Z"
}
],
],
"count": 7
}
}
Expand Down Expand Up @@ -179,7 +179,7 @@ module.exports = class LibraryCategories extends Abstract {
}

/**
* @api {get} /improvement-project/api/v1/library/categories/list
* @api {get} /improvement-project/api/v1/library/categories/list
* List of library categories.
* @apiVersion 1.0.0
* @apiGroup Library Categories
Expand Down Expand Up @@ -240,7 +240,66 @@ module.exports = class LibraryCategories extends Abstract {
async list(req) {
return new Promise(async (resolve, reject) => {
try {
let projectCategories = await libraryCategoriesHelper.list(req)
let projectCategories = await libraryCategoriesHelper.list(req.searchText, req.query, req.userDetails)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vaivk369 why do we need user detail to get category listing?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should get list of categories for the tenant user is belonging to right?
user categories model has tenant.
Also we should fetch categories which are having visibleToOrganizations set to orgId of the user

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


projectCategories.result = projectCategories.data

return resolve(projectCategories)
} catch (error) {
return reject({
status: error.status || HTTP_STATUS_CODE.internal_server_error.status,
message: error.message || HTTP_STATUS_CODE.internal_server_error.message,
errorObject: error,
})
}
})
}

/**
* delete a library category
* @method
* @name delete
* @param {Object} req - requested data
* @returns {Array} Library categories.
*/

Comment thread
vaivk369 marked this conversation as resolved.
async delete(req) {
return new Promise(async (resolve, reject) => {
try {
const filterQuery = {
_id: req.params._id,
}
let projectCategories = await libraryCategoriesHelper.delete(filterQuery, req.userDetails)
Comment thread
vaivk369 marked this conversation as resolved.

projectCategories.result = projectCategories.data

return resolve(projectCategories)
} catch (error) {
return reject({
status: error.status || HTTP_STATUS_CODE.internal_server_error.status,
message: error.message || HTTP_STATUS_CODE.internal_server_error.message,
errorObject: error,
})
}
})
}

/**
* read a library category
* @method
* @name details
* @param {Object} req - requested data
* @returns {Array} Library categories.
*/

async details(req) {
return new Promise(async (resolve, reject) => {
try {
const filterQuery = {
_id: req.params._id,
getChildren: req.query.getChildren === 'true',
}
let projectCategories = await libraryCategoriesHelper.details(filterQuery, req.userDetails)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vaivk369 why do we need user detail to get category details?

Copy link
Copy Markdown
Author

@vaivk369 vaivk369 Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aks30 to return category only if its tenantId = user's tenant Id t


projectCategories.result = projectCategories.data

Expand Down
17 changes: 10 additions & 7 deletions controllers/v1/project/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,12 @@ module.exports = class ProjectTemplates extends Abstract {
}

/**
* @api {post} /project/v1/project/templates/importProjectTemplate/:projectTemplateExternalId
* @api {post} /project/v1/project/templates/importProjectTemplate/:projectTemplateExternalId
* Import templates from existsing project templates.
* @apiVersion 1.0.0
* @apiGroup Project Templates
* @apiSampleRequest /project/v1/project/templates/importProjectTemplate/template-1
* @apiParamExample {json} Request:
* @apiParamExample {json} Request:
* {
* "externalId" : "template1",
"isReusable" : false,
Expand Down Expand Up @@ -187,7 +187,7 @@ module.exports = class ProjectTemplates extends Abstract {
* @apiVersion 1.0.0
* @apiGroup Project Templates
* @apiSampleRequest /project/v1/project/templates/listByIds
* @apiParamExample {json} Request:
* @apiParamExample {json} Request:
* {
* "externalIds" : ["IDEAIMP 4"]
* }
Expand Down Expand Up @@ -680,13 +680,13 @@ module.exports = class ProjectTemplates extends Abstract {
}

/**
* @api {post} /project/v1/project/templates/update/:templateId
* @api {post} /project/v1/project/templates/update/:templateId
* Update projects template.
* @apiVersion 1.0.0
* @apiGroup Project Templates
* @apiSampleRequest /project/v1/project/templates/update/6006b5cca1a95727dbcdf648
* @apiHeader {String} internal-access-token internal access token
* @apiHeader {String} X-authenticated-user-token Authenticity token
* @apiHeader {String} internal-access-token internal access token
* @apiHeader {String} X-authenticated-user-token Authenticity token
* @apiUse successBody
* @apiUse errorBody
* @apiParamExample {json} Response:
Expand Down Expand Up @@ -822,7 +822,10 @@ module.exports = class ProjectTemplates extends Abstract {
req.pageSize,
req.searchText,
req.query.currentOrgOnly ? req.query.currentOrgOnly : false,
req.userDetails
req.userDetails,
req.query.categoryIds ? req.query.categoryIds : '',
req.query.groupByCategory ? req.query.groupByCategory : false,
req.query.taskDetails ? req.query.taskDetails : false
)

// Assign the 'data' property of 'projectTemplates' to 'result'.
Expand Down
2 changes: 2 additions & 0 deletions generics/constants/api-responses.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ module.exports = {
PROJECT_CATEGORIES_ADDED: 'Successfully created project categories',
PROJECT_CATEGORIES_NOT_UPDATED: 'Could not updated project categories',
PROJECT_CATEGORIES_NOT_ADDED: 'Could not create project categories',
PROJECT_CATEGORIES_DELETED: 'Successfully deleted project categories',
PROJECT_CATEGORIES_NOT_DELETED: 'Could not delete project categories',
PROJECT_TEMPLATE_NOT_UPDATED: 'Not found project template',
COULD_NOT_CREATE_ASSESSMENT_SOLUTION: 'Could not create assessment solution',
FAILED_TO_ADD_ENTITY_TO_SOLUTION: 'Failed to add entity to solution',
Expand Down
97 changes: 97 additions & 0 deletions migrations/addHierarchyFieldsToCategories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* Migration: addHierarchyFieldsToCategories
* Description: Add hierarchical fields to existing project categories
* Fields added:
* - parentId: ObjectId (null by default)
* - hasChildCategories: Boolean (false by default)
* - sequenceNumber: Number (0 by default)
* - metaInformation: Object ({} by default)
*/

const mongoose = require('mongoose')

module.exports = {
async up(db, client) {
const session = client.startSession()
try {
await session.withTransaction(async () => {
const categoriesCollection = db.collection('projectCategories')

// Update all existing documents to add the new fields with default values
// Only update documents where parentId doesn't exist (for idempotency)
const result = await categoriesCollection.updateMany(
{
parentId: { $exists: false },
},
{
$set: {
parentId: null,
hasChildCategories: false,
sequenceNumber: 0,
metaInformation: {},
},
},
{ session }
)

console.log(
`Migration: addHierarchyFieldsToCategories - Updated ${result.modifiedCount} category documents`
)

// Create indexes if they don't exist
await categoriesCollection.createIndex({ parentId: 1, tenantId: 1 }, { session })
console.log('Created compound index on parentId and tenantId')

return result
})
} catch (error) {
console.error('Error during migration addHierarchyFieldsToCategories:', error)
throw error
} finally {
await session.endSession()
}
},

async down(db, client) {
const session = client.startSession()
try {
await session.withTransaction(async () => {
const categoriesCollection = db.collection('projectCategories')

// Remove the added fields from all documents
const result = await categoriesCollection.updateMany(
{},
{
$unset: {
parentId: '',
hasChildCategories: '',
sequenceNumber: '',
metaInformation: '',
},
},
{ session }
)

console.log(
`Migration rollback: addHierarchyFieldsToCategories - Reverted ${result.modifiedCount} category documents`
)

// Drop the compound index if it exists
try {
await categoriesCollection.dropIndex('parentId_1_tenantId_1', { session })
console.log('Dropped compound index on parentId and tenantId')
} catch (err) {
// Index might not exist, which is fine
console.log('Compound index not found during rollback')
}

return result
})
} catch (error) {
console.error('Error during migration rollback addHierarchyFieldsToCategories:', error)
throw error
} finally {
await session.endSession()
}
},
}
33 changes: 33 additions & 0 deletions models/project-categories.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,44 @@ module.exports = {
default: [],
index: true,
},
description: {
type: String,
required: true,
default: 'default',
},
keywords: {
type: Array,
default: [],
index: true,
},
parentId: {
type: 'ObjectId',
default: null,
index: true, // CRITICAL for hierarchy queries
},
hasChildCategories: {
type: Boolean,
default: false,
index: true, // Quick leaf identification
},
sequenceNumber: {
type: Number,
default: 0,
index: true,
},
Comment thread
vaivk369 marked this conversation as resolved.
metaInformation: {
type: Object,
default: {},
},
},
compoundIndex: [
{
name: { externalId: 1, tenantId: 1 },
indexType: { unique: true },
},
// For Query (parentId + tenantId queries)
{
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vaivk369 Not sure what is the use of these compound indexes, can you highlight please

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Query (parentId + tenantId queries) @aks30

name: { parentId: 1, tenantId: 1 },
},
],
}
1 change: 0 additions & 1 deletion models/project-templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ module.exports = {
type: String,
index: true,
},
name: String,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vaivk369 Any reason to remove this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if category name is updated, the name in project-templates become stale and then we would need to write sync job to update the names in project templates and projects. As we are alredy haaving id and external Id of the category, we can safely remove name.

},
],
description: {
Expand Down
Loading