Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
70abe16
fix: data refreshing on first render is restricted to avoid multiple …
yoganandaness Oct 20, 2025
8d86eee
fix: experiment table query params persisting
yoganandaness Oct 20, 2025
e296a44
People table enhancement and query persisting
yoganandaness Oct 20, 2025
db25e46
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Oct 27, 2025
0003da9
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
jekabs-karklins Oct 28, 2025
8ea968b
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Oct 29, 2025
93e347b
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Oct 30, 2025
6cb9c3d
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Oct 30, 2025
4dfae27
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Nov 4, 2025
38ab5a4
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Nov 4, 2025
5e80d16
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Nov 5, 2025
26170ac
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Nov 5, 2025
ea251a2
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Nov 18, 2025
0b3d065
Eliminating security issue by avoiding order by and validating the so…
yoganandaness Nov 18, 2025
390f7b8
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Jan 9, 2026
3390365
Merge branch 'SWAP-4929-user-officer-experiment-table-filter' of gith…
yoganandaness Jan 15, 2026
f854fe5
fix: handling json columns sorting without using raw queries
yoganandaness Jan 15, 2026
3e99b20
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Jan 15, 2026
d57b8c7
fix: persistence of search query had been not made optional
yoganandaness Jan 15, 2026
dbd1ed4
Merge branch 'SWAP-4929-user-officer-experiment-table-filter' of gith…
yoganandaness Jan 15, 2026
8a6df25
Merge branch 'develop' of github.com:UserOfficeProject/user-office-co…
yoganandaness Jan 15, 2026
992eed4
fix: add persistqueryparams flag for flexibility
yoganandaness Jan 16, 2026
d8175d7
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Jan 16, 2026
7d7b303
fix: fix filters on stfc datasource
yoganandaness Jan 19, 2026
a43a088
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Jan 19, 2026
5c96ca4
fix: fix filters on stfc datasource
yoganandaness Jan 19, 2026
f28619d
Merge branch 'SWAP-4929-user-officer-experiment-table-filter' of gith…
yoganandaness Jan 19, 2026
fca88c0
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Jan 20, 2026
58ed3de
Merge branch 'develop' of github.com:UserOfficeProject/user-office-co…
yoganandaness Jan 21, 2026
4ca0f44
Merge branch 'develop' into SWAP-4929-user-officer-experiment-table-f…
yoganandaness Jan 21, 2026
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
3 changes: 2 additions & 1 deletion apps/backend/src/datasources/ExperimentDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ExperimentsFilter,
UserExperimentsFilter,
} from '../resolvers/queries/ExperimentsQuery';
import { PaginationSortDirection } from '../utils/pagination';
import { ExperimentSafetyEventsRecord } from './postgres/records';

export interface ExperimentDataSource {
Expand Down Expand Up @@ -88,7 +89,7 @@ export interface ExperimentDataSource {
first?: number,
offset?: number,
sortField?: string,
sortDirection?: string,
sortDirection?: PaginationSortDirection,
searchText?: string
): Promise<{
totalCount: number;
Expand Down
5 changes: 3 additions & 2 deletions apps/backend/src/datasources/ProposalDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { TechnicalReview } from '../models/TechnicalReview';
import { UserWithRole } from '../models/User';
import { UpdateTechnicalReviewAssigneeInput } from '../resolvers/mutations/UpdateTechnicalReviewAssigneeMutation';
import { UserProposalsFilter } from '../resolvers/types/User';
import { PaginationSortDirection } from '../utils/pagination';
import { ProposalsFilter } from './../resolvers/queries/ProposalsQuery';
import { ProposalEventsRecord } from './postgres/records';

Expand All @@ -15,7 +16,7 @@ export interface ProposalDataSource {
first?: number,
offset?: number,
sortField?: string,
sortDirection?: string,
sortDirection?: PaginationSortDirection,
searchText?: string
): Promise<{ totalCount: number; proposalViews: ProposalView[] }>;
// Read
Expand Down Expand Up @@ -89,7 +90,7 @@ export interface ProposalDataSource {
first?: number,
offset?: number,
sortField?: string,
sortDirection?: string,
sortDirection?: PaginationSortDirection,
searchText?: string
): Promise<{ totalCount: number; proposals: ProposalView[] }>;
getInvitedProposal(inviteId: number): Promise<InvitedProposal | null>;
Expand Down
5 changes: 4 additions & 1 deletion apps/backend/src/datasources/UserDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BasicUserDetails, User, UserRole } from '../models/User';
import { AddUserRoleArgs } from '../resolvers/mutations/AddUserRoleMutation';
import { UpdateUserByIdArgs } from '../resolvers/mutations/UpdateUserMutation';
import { UsersArgs } from '../resolvers/queries/UsersQuery';
import { PaginationSortDirection } from '../utils/pagination';

export interface UserDataSource {
delete(id: number): Promise<User | null>;
Expand Down Expand Up @@ -35,9 +36,11 @@ export interface UserDataSource {
): Promise<{ totalCount: number; users: BasicUserDetails[] }>;
getPreviousCollaborators(
user_id: number,
filter?: string,
first?: number,
offset?: number,
sortField?: string,
sortDirection?: PaginationSortDirection,
searchText?: string,
userRole?: UserRole,
subtractUsers?: [number]
): Promise<{ totalCount: number; users: BasicUserDetails[] }>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
UserExperimentsFilter,
ExperimentsFilter,
} from '../../resolvers/queries/ExperimentsQuery';
import { PaginationSortDirection } from '../../utils/pagination';
import { ExperimentDataSource } from '../ExperimentDataSource';
import { ExperimentSafetyEventsRecord } from '../postgres/records';

Expand Down Expand Up @@ -122,7 +123,7 @@ export class ExperimentDataSourceMock implements ExperimentDataSource {
first?: number,
offset?: number,
sortField?: string,
sortDirection?: string,
sortDirection?: PaginationSortDirection,
searchText?: string
): Promise<{ totalCount: number; experiments: Experiment[] }> {
return {
Expand Down
5 changes: 3 additions & 2 deletions apps/backend/src/datasources/mockups/ProposalDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from '../../models/TechnicalReview';
import { UserWithRole } from '../../models/User';
import { UpdateTechnicalReviewAssigneeInput } from '../../resolvers/mutations/UpdateTechnicalReviewAssigneeMutation';
import { PaginationSortDirection } from '../../utils/pagination';
import { ProposalEventsRecord } from '../postgres/records';
import { ProposalDataSource } from '../ProposalDataSource';
import { ProposalsFilter } from './../../resolvers/queries/ProposalsQuery';
Expand Down Expand Up @@ -143,7 +144,7 @@ export class ProposalDataSourceMock implements ProposalDataSource {
first?: number | undefined,
offset?: number | undefined,
sortField?: string | undefined,
sortDirection?: string | undefined,
sortDirection?: PaginationSortDirection | undefined,
searchText?: string | undefined
): Promise<{ totalCount: number; proposalViews: ProposalView[] }> {
return { totalCount: 0, proposalViews: [] };
Expand Down Expand Up @@ -430,7 +431,7 @@ export class ProposalDataSourceMock implements ProposalDataSource {
first?: number,
offset?: number,
sortField?: string,
sortDirection?: string,
sortDirection?: PaginationSortDirection,
searchText?: string
) {
return { totalCount: 1, proposals: [dummyProposalView] };
Expand Down
9 changes: 7 additions & 2 deletions apps/backend/src/datasources/mockups/UserDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { AddUserRoleArgs } from '../../resolvers/mutations/AddUserRoleMutation';
import { UpdateUserByIdArgs } from '../../resolvers/mutations/UpdateUserMutation';
import { UsersArgs } from '../../resolvers/queries/UsersQuery';
import { PaginationSortDirection } from '../../utils/pagination';
import { UserDataSource } from '../UserDataSource';

export const basicDummyUser = new BasicUserDetails(
Expand Down Expand Up @@ -390,9 +391,13 @@ export class UserDataSourceMock implements UserDataSource {

async getPreviousCollaborators(
user_id: number,
filter?: string,
first?: number,
offset?: number
offset?: number,
sortField?: string,
sortDirection?: PaginationSortDirection,
searchText?: string,
userRole?: UserRole,
subtractUsers?: [number]
): Promise<{ totalCount: number; users: BasicUserDetails[] }> {
return {
totalCount: 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
ExperimentsFilter,
UserExperimentsFilter,
} from '../../resolvers/queries/ExperimentsQuery';
import { PaginationSortDirection } from '../../utils/pagination';
import { ExperimentDataSource } from '../ExperimentDataSource';
import database from './database';
import {
Expand Down Expand Up @@ -571,7 +572,7 @@ export default class PostgresExperimentDataSource
first?: number,
offset?: number,
sortField?: string,
sortDirection?: string,
sortDirection?: PaginationSortDirection,
searchText?: string
): Promise<{ totalCount: number; experiments: Experiment[] }> {
//print all arguments
Expand Down Expand Up @@ -673,7 +674,7 @@ export default class PostgresExperimentDataSource
throw new GraphQLError(`Bad sort field given: ${sortField}`);
}
sortField = fieldMap[sortField];
query.orderByRaw(`${sortField} ${sortDirection}`);
query.orderBy(sortField, sortDirection);
}

if (first) {
Expand Down
39 changes: 28 additions & 11 deletions apps/backend/src/datasources/postgres/ProposalDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { UserWithRole } from '../../models/User';
import { WorkflowConnectionWithStatus } from '../../models/WorkflowConnections';
import { UpdateTechnicalReviewAssigneeInput } from '../../resolvers/mutations/UpdateTechnicalReviewAssigneeMutation';
import { UserProposalsFilter } from '../../resolvers/types/User';
import { PaginationSortDirection } from '../../utils/pagination';
import { AdminDataSource } from '../AdminDataSource';
import { ProposalDataSource } from '../ProposalDataSource';
import { WorkflowDataSource } from '../WorkflowDataSource';
Expand Down Expand Up @@ -46,17 +47,16 @@ import {

const fieldMap: { [key: string]: string } = {
finalStatus: 'final_status',
'technicalReviews.status': "technical_reviews->0->'status'",
'technicalReviews.timeAllocation': "technical_reviews->0->'timeAllocation'",
'technicalReviews.status': 'technical_review_status',
'technicalReviews.timeAllocation': 'technical_review_time_allocation',
// NOTE: For now sorting by first name only is completly fine because the full name is constructed from frist + last
technicalReviewAssigneesFullName:
"technical_reviews->0->'technicalReviewAssignee'->'firstname'",
'faps.code': "faps->0->'code'",
technicalReviewAssigneesFullName: 'technical_review_assignee',
'faps.code': 'fap_code',
callShortCode: 'call_short_code',
'instruments.name': "instruments->0->'name'",
'instruments.name': 'instrument_name',
statusName: 'proposal_status_id',
'instruments.managementTimeAllocation':
"instruments->0->'managementTimeAllocation'",
'instrument_management_time_allocation',
proposalId: 'proposal_id',
title: 'title',
submitted: 'submitted',
Expand Down Expand Up @@ -384,7 +384,7 @@ export default class PostgresProposalDataSource implements ProposalDataSource {
first?: number,
offset?: number,
sortField?: string,
sortDirection?: string,
sortDirection?: PaginationSortDirection,
searchText?: string,
principleInvestigator?: number[]
): Promise<{ totalCount: number; proposalViews: ProposalView[] }> {
Expand All @@ -393,7 +393,24 @@ export default class PostgresProposalDataSource implements ProposalDataSource {
: [];

return database
.select(['*', database.raw('count(*) OVER() AS full_count')])
.select([
'*',
database.raw("instruments->0->'name' AS instrument_name"),
database.raw(
"instruments->0->'managementTimeAllocation' AS instrument_management_time_allocation"
),
database.raw(
"technical_reviews->0->'status' AS technical_review_status"
),
database.raw(
"technical_reviews->0->'timeAllocation' AS technical_review_time_allocation"
),
database.raw(
"technical_reviews->0->'technicalReviewAssignee'->'firstname' AS technical_review_assignee"
),
database.raw("faps->0->'code' AS fap_code"),
database.raw('count(*) OVER() AS full_count'),
])
.from('proposal_table_view')
.join(
'users',
Expand Down Expand Up @@ -461,7 +478,7 @@ export default class PostgresProposalDataSource implements ProposalDataSource {
throw new GraphQLError(`Bad sort field given: ${sortField}`);
}
sortField = fieldMap[sortField];
query.orderByRaw(`${sortField} ${sortDirection}`);
query.orderBy(sortField, sortDirection);
}

if (filter?.referenceNumbers) {
Expand Down Expand Up @@ -1069,7 +1086,7 @@ export default class PostgresProposalDataSource implements ProposalDataSource {
first?: number,
offset?: number,
sortField?: string,
sortDirection?: string,
sortDirection?: PaginationSortDirection,
searchText?: string
): Promise<{ totalCount: number; proposals: ProposalView[] }> {
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,7 @@ export default class PostgresStatusActionsLogsDataSource
}
if (args.sortField && args.sortDirection) {
if (args.sortField in fieldMap) {
query.orderByRaw(
`${fieldMap[args.sortField]} ${args.sortDirection}`
);
query.orderBy(fieldMap[args.sortField], args.sortDirection);
} else {
throw new GraphQLError(`Bad sort field given: ${args.sortField}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export default class PostgresTemplateDataSource implements TemplateDataSource {
throw new GraphQLError(`Bad sort field given: ${sortField}`);
}

query.orderByRaw(`${this.fieldMap[sortField]} ${sortDirection}`);
query.orderBy(this.fieldMap[sortField], sortDirection);
}

if (first) {
Expand Down
67 changes: 49 additions & 18 deletions apps/backend/src/datasources/postgres/UserDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { AddUserRoleArgs } from '../../resolvers/mutations/AddUserRoleMutation';
import { UpdateUserByIdArgs } from '../../resolvers/mutations/UpdateUserMutation';
import { UsersArgs } from '../../resolvers/queries/UsersQuery';
import { PaginationSortDirection } from '../../utils/pagination';
import { UserDataSource } from '../UserDataSource';
import database, { isUniqueConstraintError } from './database';
import {
Expand All @@ -29,6 +30,14 @@ import {
createUserObject,
} from './records';

const fieldMap: { [key: string]: string } = {
created_at: 'created_at',
firstname: 'firstname',
preferredname: 'preferredname',
lastname: 'lastname',
institution: 'i.institution',
};

export default class PostgresUserDataSource implements UserDataSource {
async delete(id: number): Promise<User | null> {
return database('users')
Expand Down Expand Up @@ -376,25 +385,25 @@ export default class PostgresUserDataSource implements UserDataSource {
}

async getUsers({
filter,
searchText,
first,
offset,
userRole,
subtractUsers,
orderBy,
orderDirection = 'desc',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

question: Is that intended to remove the default value? I would suggest keeping it because if you only specify orderBy, the ordering will not take place, which is a bit unexpecged.

And if this has the default value below we don't need to check if it is set

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure, if i got your suggestion in a right way

But, the changes includes

  1. Renaming orderBy to sortField and orderDirection to sortDirection.
  2. sortField has been defaulted to created_at, which loads results based on creation date in the ascending order.
  3. When sortDirection is not provided, the default sort direction is Desc.

sortField = 'created_at',
sortDirection,
}: UsersArgs): Promise<{ totalCount: number; users: BasicUserDetails[] }> {
return database
.select(['*', database.raw('count(*) OVER() AS full_count')])
.from('users')
.join('institutions as i', { 'users.institution_id': 'i.institution_id' })
.modify((query) => {
if (filter) {
if (searchText) {
query.andWhere((qb) => {
qb.whereILikeEscaped('institution', '%?%', filter)
.orWhereILikeEscaped('firstname', '%?%', filter)
.orWhereILikeEscaped('preferredname', '%?%', filter)
.orWhereILikeEscaped('lastname', '%?%', filter);
qb.whereILikeEscaped('institution', '%?%', searchText)
.orWhereILikeEscaped('firstname', '%?%', searchText)
.orWhereILikeEscaped('preferredname', '%?%', searchText)
.orWhereILikeEscaped('lastname', '%?%', searchText);
});
}
if (first) {
Expand All @@ -411,8 +420,12 @@ export default class PostgresUserDataSource implements UserDataSource {
if (subtractUsers && subtractUsers.length > 0) {
query.whereNotIn('users.user_id', subtractUsers);
}
if (orderBy) {
query.orderBy(orderBy, orderDirection);
if (sortField && sortDirection) {
if (!fieldMap.hasOwnProperty(sortField)) {
throw new GraphQLError(`Bad sort field given: ${sortField}`);
}
sortField = fieldMap[sortField];
query.orderBy(sortField, sortDirection);
}
})
.then(
Expand All @@ -431,14 +444,24 @@ export default class PostgresUserDataSource implements UserDataSource {

async getPreviousCollaborators(
userId: number,
filter?: string,
first?: number,
offset?: number,
sortField?: string,
sortDirection?: PaginationSortDirection,
searchText?: string,
userRole?: UserRole,
subtractUsers?: [number]
): Promise<{ totalCount: number; users: BasicUserDetails[] }> {
if (userId == -1) {
return this.getUsers({ filter, first, offset, userRole, subtractUsers });
return this.getUsers({
searchText,
first,
offset,
userRole,
subtractUsers,
sortField,
sortDirection,
});
}

const lastCollaborators = await this.getMostRecentCollaborators(userId);
Expand All @@ -455,14 +478,22 @@ export default class PostgresUserDataSource implements UserDataSource {
.join('institutions as i', { 'users.institution_id': 'i.institution_id' })
.whereIn('users.user_id', userIds)
.modify((query) => {
if (filter) {
if (searchText) {
query.andWhere((qb) => {
qb.whereILikeEscaped('institution', '%?%', filter)
.orWhereILikeEscaped('firstname', '%?%', filter)
.orWhereILikeEscaped('preferredname', '%?%', filter)
.orWhereILikeEscaped('lastname', '%?%', filter);
qb.whereILikeEscaped('institution', '%?%', searchText)
.orWhereILikeEscaped('firstname', '%?%', searchText)
.orWhereILikeEscaped('preferredname', '%?%', searchText)
.orWhereILikeEscaped('lastname', '%?%', searchText);
});
}
if (sortField && sortDirection) {
if (!fieldMap.hasOwnProperty(sortField)) {
throw new GraphQLError(`Bad sort field given: ${sortField}`);
}
sortField = fieldMap[sortField];
query.orderBy(sortField, sortDirection);
}

if (first) {
query.limit(first);
}
Expand Down Expand Up @@ -553,7 +584,7 @@ export default class PostgresUserDataSource implements UserDataSource {
.from('pu')
.whereIn('pu.proposal_pk', proposals)
.groupBy('pu.user_id')
.orderByRaw('count(pu.user_id) DESC')
.orderBy('pu.user_id', 'desc')
.limit(10)
.then((users: { user_id: number }[]) => users.map((uid) => uid.user_id));
}
Expand Down
Loading
Loading