Skip to content
Merged
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
3 changes: 3 additions & 0 deletions backend/src/api/components/organization/examples.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ components:
createdById: debc3c7f-4c5d-4bec-9130-17bb0aea8b75
updatedById: debc3c7f-4c5d-4bec-9130-17bb0aea8b75
memberCount: 2
activityCount: 4

Organization:
value:
Expand Down Expand Up @@ -84,6 +85,7 @@ components:
createdById: debc3c7f-4c5d-4bec-9130-17bb0aea8b75
updatedById: debc3c7f-4c5d-4bec-9130-17bb0aea8b75
memberCount: 2
activityCount: 4

Organization2:
value:
Expand Down Expand Up @@ -130,6 +132,7 @@ components:
createdById: debc3c7f-4c5d-4bec-9130-17bb0aea8b75
updatedById: debc3c7f-4c5d-4bec-9130-17bb0aea8b75
memberCount: 0
activityCount: 0

OrganizationList:
value:
Expand Down
8 changes: 8 additions & 0 deletions backend/src/api/components/organization/query.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ components:
enum:
- createdAt_ASC
- createdAt_DESC
- memberCount_ASC
- memberCount_DESC
- activityCount_ASC
- activityCount_DESC
- joinedAt_ASC
- joinedAt_DESC
- lastActive_ASC
- lastActive_DESC

limit:
description: >-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1531,12 +1531,14 @@ describe('MemberRepository tests', () => {
'identities',
'activeOn',
'joinedAt',
'activityCount',
]),
SequelizeTestUtils.objectWithoutKey(org2Plain, [
'lastActive',
'identities',
'activeOn',
'joinedAt',
'activityCount',
]),
],
noMerge: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import SequelizeTestUtils from '../../utils/sequelizeTestUtils'
import Error404 from '../../../errors/Error404'
import MemberRepository from '../memberRepository'
import { PlatformType } from '../../../types/integrationEnums'
import ActivityRepository from '../activityRepository'

const db = null

Expand Down Expand Up @@ -91,6 +92,7 @@ describe('OrganizationRepository tests', () => {
id: organizationCreated.id,
...toCreate,
memberCount: 0,
activityCount: 0,
activeOn: [],
identities: [],
importHash: null,
Expand Down Expand Up @@ -136,6 +138,7 @@ describe('OrganizationRepository tests', () => {
id: organizationCreated.id,
...toCreate,
memberCount: 2,
activityCount: 0,
lastActive: null,
joinedAt: null,
activeOn: [],
Expand Down Expand Up @@ -173,6 +176,7 @@ describe('OrganizationRepository tests', () => {
id: organizationCreated.id,
...toCreate,
memberCount: 0,
activityCount: 0,
activeOn: [],
identities: [],
lastActive: null,
Expand Down Expand Up @@ -560,7 +564,17 @@ describe('OrganizationRepository tests', () => {
async function createOrganization(organization: any, options, members = []) {
const memberIds = []
for (const member of members) {
const memberCreated = await MemberRepository.create(member, options)
const memberCreated = await MemberRepository.create(
SequelizeTestUtils.objectWithoutKey(member, 'activities'),
options,
)

if (member.activities) {
for (const activity of member.activities) {
await ActivityRepository.create({ ...activity, member: memberCreated.id }, options)
}
}

memberIds.push(memberCreated.id)
}
organization.members = memberIds
Expand Down Expand Up @@ -777,6 +791,14 @@ describe('OrganizationRepository tests', () => {
username: { github: 'joan' },
displayName: 'Joan',
joinedAt: moment().toDate(),
activities: [
{
type: 'activity',
timestamp: '2020-05-27T15:13:30Z',
platform: PlatformType.GITHUB,
sourceId: '#sourceId1',
},
],
},
])
await createOrganization(piedpiper, mockIRepositoryOptions)
Expand All @@ -801,6 +823,193 @@ describe('OrganizationRepository tests', () => {
expect(found.rows[0].name).toBe('crowd.dev')
})

it('Should filter by activityCount', async () => {
const mockIRepositoryOptions = await SequelizeTestUtils.getTestIRepositoryOptions(db)
const org1 = await createOrganization(crowddev, mockIRepositoryOptions, [
{
username: { github: 'joan' },
displayName: 'Joan',
joinedAt: moment().toDate(),
activities: [
{
type: 'activity',
timestamp: '2020-05-27T15:13:30Z',
platform: PlatformType.GITHUB,
sourceId: '#sourceId1',
},
],
},
{
username: { github: 'anil' },
displayName: 'anil',
joinedAt: moment().toDate(),
activities: [
{
type: 'activity',
timestamp: '2020-06-27T15:13:30Z',
platform: PlatformType.TWITTER,
sourceId: '#sourceId2',
},
],
},
])
await createOrganization(piedpiper, mockIRepositoryOptions)
await createOrganization(hooli, mockIRepositoryOptions)

const found = await OrganizationRepository.findAndCountAll(
{
advancedFilter: {
activityCount: {
gte: 2,
},
},
},
mockIRepositoryOptions,
)

expect(found.count).toBe(1)
expect(found.rows).toStrictEqual([org1])
})

it('Should filter by memberCount', async () => {
const mockIRepositoryOptions = await SequelizeTestUtils.getTestIRepositoryOptions(db)
const org1 = await createOrganization(crowddev, mockIRepositoryOptions, [
{
username: { github: 'joan' },
displayName: 'Joan',
joinedAt: moment().toDate(),
},
{
username: { github: 'anil' },
displayName: 'anil',
joinedAt: moment().toDate(),
},
{
username: { github: 'uros' },
displayName: 'uros',
joinedAt: moment().toDate(),
},
])
const org2 = await createOrganization(piedpiper, mockIRepositoryOptions, [
{
username: { github: 'mario' },
displayName: 'mario',
joinedAt: moment().toDate(),
},
{
username: { github: 'igor' },
displayName: 'igor',
joinedAt: moment().toDate(),
},
])
await createOrganization(hooli, mockIRepositoryOptions)

const found = await OrganizationRepository.findAndCountAll(
{
advancedFilter: {
memberCount: {
gte: 2,
},
},
},
mockIRepositoryOptions,
)

expect(found.count).toBe(2)
expect(found.rows.sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1))).toStrictEqual([
org1,
org2,
])
})

it('Should filter by joinedAt', async () => {
const mockIRepositoryOptions = await SequelizeTestUtils.getTestIRepositoryOptions(db)
await createOrganization(crowddev, mockIRepositoryOptions, [
{
username: { github: 'joan' },
displayName: 'Joan',
joinedAt: moment().toDate(),
activities: [
{
type: 'activity',
timestamp: '2020-05-27T15:13:30Z',
platform: PlatformType.GITHUB,
sourceId: '#sourceId1',
},
],
},
{
username: { github: 'anil' },
displayName: 'anil',
joinedAt: moment().toDate(),
activities: [
{
type: 'activity',
timestamp: '2020-04-27T15:13:30Z',
platform: PlatformType.SLACK,
sourceId: '#sourceId2',
},
],
},
{
username: { github: 'uros' },
displayName: 'uros',
joinedAt: moment().toDate(),
activities: [
{
type: 'activity',
timestamp: '2020-03-27T15:13:30Z',
platform: PlatformType.TWITTER,
sourceId: '#sourceId3',
},
],
},
])
const org2 = await createOrganization(piedpiper, mockIRepositoryOptions, [
{
username: { github: 'mario' },
displayName: 'mario',
joinedAt: moment().toDate(),
activities: [
{
type: 'activity',
timestamp: '2022-03-27T15:13:30Z',
platform: PlatformType.DEVTO,
sourceId: '#sourceId4',
},
],
},
{
username: { github: 'igor' },
displayName: 'igor',
joinedAt: moment().toDate(),
activities: [
{
type: 'activity',
timestamp: '2022-02-27T15:13:30Z',
platform: PlatformType.DEVTO,
sourceId: '#sourceId4',
},
],
},
])
await createOrganization(hooli, mockIRepositoryOptions)

const found = await OrganizationRepository.findAndCountAll(
{
advancedFilter: {
joinedAt: {
gte: '2022-02-27',
},
},
},
mockIRepositoryOptions,
)

expect(found.count).toBe(1)
expect(found.rows.sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1))).toStrictEqual([org2])
})

it('Should work with advanced filters', async () => {
const mockIRepositoryOptions = await SequelizeTestUtils.getTestIRepositoryOptions(db)
await createOrganization(crowddev, mockIRepositoryOptions, [
Expand Down Expand Up @@ -940,6 +1149,7 @@ describe('OrganizationRepository tests', () => {
id: organizationCreated.id,
...toCreate,
memberCount: 0,
activityCount: 0,
activeOn: [],
identities: [],
lastActive: null,
Expand Down
12 changes: 12 additions & 0 deletions backend/src/database/repositories/activityRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,18 @@ class ActivityRepository {
nestedFields: {
sentiment: 'sentiment.sentiment',
},
manyToMany: {
organizations: {
table: 'activities',
model: 'activity',
overrideJoinField: 'memberId',
relationTable: {
name: 'memberOrganizations',
from: 'memberId',
to: 'organizationId',
},
},
},
},
options,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,14 +376,14 @@ describe('QueryParser tests', () => {
Sequelize.literal(`"task"."id"`),
Op.in,
Sequelize.literal(
`(SELECT "tasks".id FROM "tasks" INNER JOIN "memberTasks" ON "memberTasks"."taskId" = "tasks".id WHERE "memberTasks"."memberId" = '${mid1}')`,
`(SELECT "tasks"."id" FROM "tasks" INNER JOIN "memberTasks" ON "memberTasks"."taskId" = "tasks"."id" WHERE "memberTasks"."memberId" = '${mid1}')`,
),
),
Sequelize.where(
Sequelize.literal(`"task"."id"`),
Op.in,
Sequelize.literal(
`(SELECT "tasks".id FROM "tasks" INNER JOIN "activityTasks" ON "activityTasks"."taskId" = "tasks".id WHERE "activityTasks"."activityId" = '${aid1}' OR "activityTasks"."activityId" = '${aid2}')`,
`(SELECT "tasks"."id" FROM "tasks" INNER JOIN "activityTasks" ON "activityTasks"."taskId" = "tasks"."id" WHERE "activityTasks"."activityId" = '${aid1}' OR "activityTasks"."activityId" = '${aid2}')`,
),
),
],
Expand Down Expand Up @@ -522,7 +522,7 @@ describe('QueryParser tests', () => {
Sequelize.literal(`"task"."id"`),
Op.in,
Sequelize.literal(
`(SELECT "tasks".id FROM "tasks" INNER JOIN "activityTasks" ON "activityTasks"."taskId" = "tasks".id WHERE "activityTasks"."activityId" = '${aid1}' OR "activityTasks"."activityId" = '${aid2}')`,
`(SELECT "tasks"."id" FROM "tasks" INNER JOIN "activityTasks" ON "activityTasks"."taskId" = "tasks"."id" WHERE "activityTasks"."activityId" = '${aid1}' OR "activityTasks"."activityId" = '${aid2}')`,
),
),
],
Expand Down
8 changes: 5 additions & 3 deletions backend/src/database/repositories/filters/queryParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,21 +265,23 @@ class QueryParser {
}" = '${QueryParser.uuid(item)}'`
}, '')

const joinField = mapping.overrideJoinField ?? 'id'

// Find all the rows in the table that have the items we are filtering on
// For example, find all members that have the tags with id1 or id2
const literal = Sequelize.literal(
`(SELECT "${mapping.table}".id FROM "${mapping.table}" INNER JOIN "${mapping.relationTable.name}" ON "${mapping.relationTable.name}"."${mapping.relationTable.from}" = "${mapping.table}".id WHERE ${items})`,
`(SELECT "${mapping.table}"."${joinField}" FROM "${mapping.table}" INNER JOIN "${mapping.relationTable.name}" ON "${mapping.relationTable.name}"."${mapping.relationTable.from}" = "${mapping.table}"."${joinField}" WHERE ${items})`,
)

// It coudl be that we have more than 1 many to many filter, so we could need to append. For example:
// {tags: [id1, id2], organizations: [id3, id4]}
if (query[Op.and]) {
query[Op.and].push(
Sequelize.where(Sequelize.literal(`"${mapping.model}"."id"`), Op.in, literal),
Sequelize.where(Sequelize.literal(`"${mapping.model}"."${joinField}"`), Op.in, literal),
)
} else {
query[Op.and] = [
Sequelize.where(Sequelize.literal(`"${mapping.model}"."id"`), Op.in, literal),
Sequelize.where(Sequelize.literal(`"${mapping.model}"."${joinField}"`), Op.in, literal),
]
}

Expand Down
Loading