fix: filter changes#811
Conversation
|
PR review rate limit exceeded |
There was a problem hiding this comment.
Pull request overview
This PR extends the “user updates” history endpoint to support query-based filtering (user/type/date range) and adjusts agent update history logging so agent-level changes can be recorded per version.
Changes:
- Parse filter query params in
getAllUserUpdatesand pass them to the DB service. - Add
filterssupport toconversationDbService.getUserUpdates()and return a users list alongside updates. - When updating an agent without a
version_id, log certain changes across all agent versions.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| src/services/commonService/configServices.js | Adds parsing of filter params from the request query and forwards them to getUserUpdates. |
| src/db_services/conversation.service.js | Adds filter-aware where construction for history queries and extends the response payload. |
| src/controllers/agentConfig.controller.js | Expands agent-level history logging across agent versions when no version_id is provided. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return { | ||
| success: true, | ||
| updates: updatedHistory, | ||
| users: userData | ||
| .filter((user) => user?.meta?.type !== "embed") | ||
| .map((user) => ({ | ||
| id: user.id, | ||
| name: user.name | ||
| })) |
There was a problem hiding this comment.
Returning users for every version_id query can significantly increase payload size for orgs with many users (even with caching), impacting response time and bandwidth. If the UI only needs users relevant to the current result set, consider returning distinct users referenced in updatedHistory, or gating this behind an explicit query flag (e.g. include_users=true).
| return { | |
| success: true, | |
| updates: updatedHistory, | |
| users: userData | |
| .filter((user) => user?.meta?.type !== "embed") | |
| .map((user) => ({ | |
| id: user.id, | |
| name: user.name | |
| })) | |
| // Only include users that are actually referenced in the updated history | |
| const referencedUserIds = new Set( | |
| Array.isArray(updatedHistory) | |
| ? updatedHistory | |
| .map((entry) => entry && entry.user_id) | |
| .filter((id) => id !== null && id !== undefined) | |
| : [] | |
| ); | |
| const relevantUsers = Array.isArray(userData) | |
| ? userData.filter( | |
| (user) => | |
| user && | |
| referencedUserIds.has(user.id) && | |
| user.meta?.type !== "embed" | |
| ) | |
| : []; | |
| return { | |
| success: true, | |
| updates: updatedHistory, | |
| users: relevantUsers.map((user) => ({ | |
| id: user.id, | |
| name: user.name | |
| })) |
| if (key === "configuration") { | ||
| for (const config_key in value) { | ||
| user_history.push({ ...history_entry, type: config_key }); | ||
| } | ||
| } else if (!version_id && agent_versions.length > 0) { | ||
| // Agent-level update (e.g. name): push history for each version | ||
| for (const vid of agent_versions) { | ||
| user_history.push({ ...history_entry, version_id: String(vid), type: key }); | ||
| } |
There was a problem hiding this comment.
When updating an agent via the /:agent_id route (no version_id), the configuration branch still pushes history entries with version_id set to undefined (from history_entry). Given user_bridge_config_history.version_id is modeled as non-nullable, this can lead to failed inserts or inconsistent history. Consider mirroring the per-version expansion here too (push one entry per agent_versions), or otherwise ensure version_id is always set on entries before addBulkUserEntries.
| user_ids: req.query.user_ids ? req.query.user_ids.split(",").filter((id) => id.trim()) : [], | ||
| types: req.query.types ? req.query.types.split(",").filter((type) => type.trim()) : [], |
There was a problem hiding this comment.
user_ids / types are filtered for non-empty values but not trimmed, so values like " 123" or " name" will be passed through and may not match DB values. Consider trimming during parsing (and for user_ids, coercing to integers) before passing to the DB layer.
| user_ids: req.query.user_ids ? req.query.user_ids.split(",").filter((id) => id.trim()) : [], | |
| types: req.query.types ? req.query.types.split(",").filter((type) => type.trim()) : [], | |
| user_ids: req.query.user_ids | |
| ? req.query.user_ids | |
| .split(",") | |
| .map((id) => id.trim()) | |
| .filter((id) => id !== "") | |
| .map((id) => parseInt(id, 10)) | |
| .filter((id) => Number.isFinite(id)) | |
| : [], | |
| types: req.query.types | |
| ? req.query.types | |
| .split(",") | |
| .map((type) => type.trim()) | |
| .filter((type) => type !== "") | |
| : [], |
|
|
||
| // Apply filters if provided | ||
| if (filters.user_ids && filters.user_ids.length > 0) { | ||
| whereConditions.user_id = { [Sequelize.Op.in]: filters.user_ids }; |
There was a problem hiding this comment.
filters.user_ids comes from query-string parsing and is an array of strings, but user_bridge_config_history.user_id is an INTEGER (see model definition). Passing strings (especially with whitespace) can cause type mismatch/casting issues and missed matches. Convert user_ids to integers (and drop non-numeric values) before building the [Op.in] predicate.
| whereConditions.user_id = { [Sequelize.Op.in]: filters.user_ids }; | |
| const userIds = filters.user_ids | |
| .map((id) => (typeof id === "string" ? id.trim() : id)) | |
| .map((id) => Number(id)) | |
| .filter((id) => Number.isInteger(id)); | |
| if (userIds.length > 0) { | |
| whereConditions.user_id = { [Sequelize.Op.in]: userIds }; | |
| } |
Husainbw786
left a comment
There was a problem hiding this comment.
PR Review: FilterInActivity (Node Backend)
✅ What's Good
- Clean filter extraction in
configServices.js - Proper Sequelize
Op.inusage for array filters - Good pattern for building dynamic where conditions
- Nice addition of agent-level history tracking across all versions
🔧 Optimization Suggestions
1. Date filter conflict (BUG)
// Lines ~436-443: date_to overwrites date_from condition
if (filters.date_from) {
whereConditions.time = { [Sequelize.Op.gte]: new Date(filters.date_from) };
}
if (filters.date_to) {
whereConditions.time = {
...whereConditions.time, // This works, but spread might be undefined
[Sequelize.Op.lte]: new Date(filters.date_to)
};
}Fix: Consolidate into one block:
if (filters.date_from || filters.date_to) {
whereConditions.time = {};
if (filters.date_from) whereConditions.time[Sequelize.Op.gte] = new Date(filters.date_from);
if (filters.date_to) whereConditions.time[Sequelize.Op.lte] = new Date(filters.date_to);
}2. Add index hint for performance
If user_bridge_config_history is large, ensure composite index exists:
CREATE INDEX idx_history_version_time ON user_bridge_config_history(version_id, time DESC);3. Validate/sanitize filter inputs
// In configServices.js, validate date formats
const isValidDate = (d) => !isNaN(Date.parse(d));
date_from: req.query.date_from && isValidDate(req.query.date_from) ? req.query.date_from : null,4. Consider pagination metadata
Return total count for better UX:
const total = await models.pg.user_bridge_config_history.count({ where: whereConditions });
return { success: true, updates: updatedHistory, users: ..., total, page, pageSize };5. Empty array check
// Before querying with Op.in, ensure array has values (already done, but good to note)
if (filters.user_ids && filters.user_ids.length > 0)Overall
Solid implementation! The date filter edge case is the main thing to fix before merge.
No description provided.