170 lines
5.4 KiB
TypeScript
170 lines
5.4 KiB
TypeScript
import {
|
|
getFilteredRowsForGrouping,
|
|
getIssueFieldsFragment,
|
|
getMetaKeys,
|
|
getOrderByFragment,
|
|
singleFilterConstructor,
|
|
translateQueryParams,
|
|
} from "./query.utils";
|
|
import { log } from "./utils";
|
|
export const SPECIAL_ORDER_BY = {
|
|
labels__name: "labels",
|
|
"-labels__name": "labels",
|
|
assignees__first_name: "members",
|
|
"-assignees__first_name": "members",
|
|
issue_module__module__name: "modules",
|
|
"-issue_module__module__name": "modules",
|
|
issue_cycle__cycle__name: "cycles",
|
|
"-issue_cycle__cycle__name": "cycles",
|
|
state__name: "states",
|
|
"-state__name": "states",
|
|
estimate_point__key: "estimate_point",
|
|
"-estimate_point__key": "estimate_point",
|
|
};
|
|
export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: string, queries: any) => {
|
|
const {
|
|
cursor,
|
|
per_page,
|
|
group_by,
|
|
sub_group_by,
|
|
order_by = "-created_at",
|
|
...otherProps
|
|
} = translateQueryParams(queries);
|
|
|
|
const [pageSize, page, offset] = cursor.split(":");
|
|
|
|
let sql = "";
|
|
|
|
const fieldsFragment = getIssueFieldsFragment();
|
|
|
|
if (sub_group_by) {
|
|
const orderByString = getOrderByFragment(order_by);
|
|
sql = getFilteredRowsForGrouping(projectId, queries);
|
|
sql += `, ranked_issues AS ( SELECT fi.*,
|
|
ROW_NUMBER() OVER (PARTITION BY group_id, sub_group_id ${orderByString}) as rank,
|
|
COUNT(*) OVER (PARTITION by group_id, sub_group_id) as total_issues from fi)
|
|
SELECT ri.*, ${fieldsFragment}
|
|
FROM ranked_issues ri
|
|
JOIN issues i ON ri.id = i.id
|
|
WHERE rank <= ${per_page}
|
|
|
|
`;
|
|
|
|
return sql;
|
|
}
|
|
if (group_by) {
|
|
const orderByString = getOrderByFragment(order_by);
|
|
sql = getFilteredRowsForGrouping(projectId, queries);
|
|
sql += `, ranked_issues AS ( SELECT fi.*,
|
|
ROW_NUMBER() OVER (PARTITION BY group_id ${orderByString}) as rank,
|
|
COUNT(*) OVER (PARTITION by group_id) as total_issues FROM fi)
|
|
SELECT ri.*, ${fieldsFragment}
|
|
FROM ranked_issues ri
|
|
JOIN issues i ON ri.id = i.id
|
|
WHERE rank <= ${per_page}
|
|
`;
|
|
|
|
return sql;
|
|
}
|
|
|
|
if (order_by && Object.keys(SPECIAL_ORDER_BY).includes(order_by)) {
|
|
const name = order_by.replace("-", "");
|
|
const orderByString = getOrderByFragment(order_by, "i.");
|
|
|
|
sql = `WITH sorted_issues AS (`;
|
|
sql += getFilteredRowsForGrouping(projectId, queries);
|
|
sql += `SELECT fi.* , `;
|
|
if (order_by.includes("assignee")) {
|
|
sql += ` s.first_name as ${name} `;
|
|
} else if (order_by.includes("estimate")) {
|
|
sql += ` s.key as ${name} `;
|
|
} else {
|
|
sql += ` s.name as ${name} `;
|
|
}
|
|
sql += `FROM fi `;
|
|
if (order_by && Object.keys(SPECIAL_ORDER_BY).includes(order_by)) {
|
|
if (order_by.includes("cycle")) {
|
|
sql += `
|
|
LEFT JOIN cycles s on fi.cycle_id = s.id`;
|
|
}
|
|
if (order_by.includes("estimate_point__key")) {
|
|
sql += `
|
|
LEFT JOIN estimate_points s on fi.estimate_point = s.id`;
|
|
}
|
|
if (order_by.includes("state")) {
|
|
sql += `
|
|
LEFT JOIN states s on fi.state_id = s.id`;
|
|
}
|
|
if (order_by.includes("label")) {
|
|
sql += `
|
|
LEFT JOIN issue_meta sm ON fi.id = sm.issue_id AND sm.key = 'label_ids'
|
|
LEFT JOIN labels s ON s.id = sm.value`;
|
|
}
|
|
if (order_by.includes("module")) {
|
|
sql += `
|
|
LEFT JOIN issue_meta sm ON fi.id = sm.issue_id AND sm.key = 'module_ids'
|
|
LEFT JOIN modules s ON s.id = sm.value`;
|
|
}
|
|
|
|
if (order_by.includes("assignee")) {
|
|
sql += `
|
|
LEFT JOIN issue_meta sm ON fi.id = sm.issue_id AND sm.key = 'assignee_ids'
|
|
LEFT JOIN members s ON s.id = sm.value`;
|
|
}
|
|
|
|
sql += ` ORDER BY ${name} ASC NULLS LAST`;
|
|
}
|
|
sql += `)`;
|
|
|
|
sql += `SELECT ${fieldsFragment}, group_concat(si.${name}) as ${name} from sorted_issues si JOIN issues i ON si.id = i.id
|
|
`;
|
|
sql += ` group by i.id ${orderByString} LIMIT ${pageSize} OFFSET ${offset * 1 + page * pageSize};`;
|
|
|
|
return sql;
|
|
}
|
|
|
|
const filterJoinFields = getMetaKeys(queries);
|
|
const orderByString = getOrderByFragment(order_by);
|
|
|
|
sql = `SELECT ${fieldsFragment}`;
|
|
if (otherProps.state_group) {
|
|
sql += `, states.'group' as state_group`;
|
|
}
|
|
sql += ` from issues i
|
|
`;
|
|
|
|
if (otherProps.state_group) {
|
|
sql += `LEFT JOIN states ON i.state_id = states.id `;
|
|
}
|
|
filterJoinFields.forEach((field: string) => {
|
|
const value = otherProps[field] || "";
|
|
sql += ` INNER JOIN issue_meta ${field} ON i.id = ${field}.issue_id AND ${field}.key = '${field}' AND ${field}.value IN ('${value.split(",").join("','")}')
|
|
`;
|
|
});
|
|
|
|
sql += ` WHERE 1=1 `;
|
|
if (projectId) {
|
|
sql += ` AND i.project_id = '${projectId}' `;
|
|
}
|
|
sql += ` ${singleFilterConstructor(otherProps)} group by i.id `;
|
|
sql += orderByString;
|
|
|
|
// Add offset and paging to query
|
|
sql += ` LIMIT ${pageSize} OFFSET ${offset * 1 + page * pageSize};`;
|
|
|
|
return sql;
|
|
};
|
|
|
|
export const issueFilterCountQueryConstructor = (workspaceSlug: string, projectId: string, queries: any) => {
|
|
//@todo Very crude way to extract count from the actual query. Needs to be refactored
|
|
// Remove group by from the query to fallback to non group query
|
|
const { group_by, sub_group_by, order_by, ...otherProps } = queries;
|
|
let sql = issueFilterQueryConstructor(workspaceSlug, projectId, otherProps);
|
|
const fieldsFragment = getIssueFieldsFragment();
|
|
|
|
sql = sql.replace(`SELECT ${fieldsFragment}`, "SELECT COUNT(DISTINCT i.id) as total_count");
|
|
// Remove everything after group by i.id
|
|
sql = `${sql.split("group by i.id")[0]};`;
|
|
return sql;
|
|
};
|