Skip to content

Commit 8030024

Browse files
committed
fix: comment after review
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
1 parent f9ef818 commit 8030024

4 files changed

Lines changed: 29 additions & 32 deletions

File tree

backend/src/api/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as http from 'http'
77
import os from 'os'
88
import { QueryTypes } from 'sequelize'
99

10+
import { BadRequestError } from '@crowd/common'
1011
import { getDbConnection } from '@crowd/data-access-layer/src/database'
1112
import { getServiceLogger } from '@crowd/logging'
1213
import { getOpensearchClient } from '@crowd/opensearch'
@@ -149,8 +150,7 @@ setImmediate(async () => {
149150

150151
app.use((err: any, req: any, res: any, next: any) => {
151152
if (err.type === 'entity.parse.failed') {
152-
res.status(400).json({ error: { code: 'BAD_REQUEST', message: 'Invalid JSON body' } })
153-
return
153+
return next(new BadRequestError('Invalid JSON body'))
154154
}
155155
next(err)
156156
})

backend/src/api/public/v1/dev-stats/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { getAffiliations } from './getAffiliations'
99

1010
const rateLimiter = createRateLimiter({ max: 60, windowMs: 60 * 1000 })
1111

12-
export function devStatsRouter(): Router {
12+
export function memberOrganizationAffiliationsRouter(): Router {
1313
const router = Router()
1414

1515
router.use(rateLimiter)

backend/src/api/public/v1/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { AUTH0_CONFIG } from '../../../conf'
66
import { oauth2Middleware } from '../middlewares/oauth2Middleware'
77
import { staticApiKeyMiddleware } from '../middlewares/staticApiKeyMiddleware'
88

9-
import { devStatsRouter } from './dev-stats'
9+
import { memberOrganizationAffiliationsRouter } from './dev-stats'
1010
import { membersRouter } from './members'
1111
import { organizationsRouter } from './organizations'
1212

@@ -15,7 +15,11 @@ export function v1Router(): Router {
1515

1616
router.use('/members', oauth2Middleware(AUTH0_CONFIG), membersRouter())
1717
router.use('/organizations', oauth2Middleware(AUTH0_CONFIG), organizationsRouter())
18-
router.use('/member-organization-affiliations', staticApiKeyMiddleware(), devStatsRouter())
18+
router.use(
19+
'/member-organization-affiliations',
20+
staticApiKeyMiddleware(),
21+
memberOrganizationAffiliationsRouter(),
22+
)
1923

2024
router.use(() => {
2125
throw new NotFoundError()

services/libs/data-access-layer/src/affiliations/index.ts

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { getLongestDateRange } from '@crowd/common'
2+
import { IMemberOrganization } from '@crowd/types'
3+
14
import { BLACKLISTED_MEMBER_TITLES } from '../members/base'
25
import { QueryExecutor } from '../queryExecutor'
36

@@ -7,7 +10,7 @@ export interface IAffiliationPeriod {
710
endDate: string | null
811
}
912

10-
interface IWorkRow {
13+
interface IWorkExperience {
1114
id: string
1215
memberId: string
1316
organizationId: string
@@ -28,8 +31,8 @@ interface IWorkRow {
2831
export async function findWorkExperiencesBulk(
2932
qx: QueryExecutor,
3033
memberIds: string[],
31-
): Promise<IWorkRow[]> {
32-
const rows: IWorkRow[] = await qx.select(
34+
): Promise<IWorkExperience[]> {
35+
const rows: IWorkExperience[] = await qx.select(
3336
`
3437
WITH relevant_orgs AS (
3538
SELECT DISTINCT "organizationId"
@@ -75,7 +78,7 @@ export async function findWorkExperiencesBulk(
7578
export async function findManualAffiliationsBulk(
7679
qx: QueryExecutor,
7780
memberIds: string[],
78-
): Promise<IWorkRow[]> {
81+
): Promise<IWorkExperience[]> {
7982
return qx.select(
8083
`
8184
SELECT
@@ -98,26 +101,16 @@ export async function findManualAffiliationsBulk(
98101
)
99102
}
100103

101-
function durationMs(org: IWorkRow): number {
102-
const start = new Date(org.dateStart ?? '').getTime()
103-
const end = new Date(org.dateEnd ?? '9999-12-31').getTime()
104-
return end - start
105-
}
106-
107-
function longestDateRange(orgs: IWorkRow[]): IWorkRow {
108-
const withDates = orgs.filter((r) => r.dateStart)
109-
const candidates = withDates.length > 0 ? withDates : orgs
110-
return candidates.reduce((best, org) => (durationMs(org) > durationMs(best) ? org : best))
111-
}
112-
113-
function selectPrimaryWorkExperience(orgs: IWorkRow[]): IWorkRow {
104+
function selectPrimaryWorkExperience(orgs: IWorkExperience[]) {
114105
if (orgs.length === 1) return orgs[0]
115106

116107
// 1. Manual affiliations (segmentId non-null) always win
117108
const manual = orgs.filter((r) => r.segmentId !== null)
118109
if (manual.length > 0) {
119110
if (manual.length === 1) return manual[0]
120-
return longestDateRange(manual)
111+
return getLongestDateRange(
112+
manual as unknown as IMemberOrganization[],
113+
) as unknown as IWorkExperience
121114
}
122115

123116
// 2. isPrimaryWorkExperience = true — prefer those with a dateStart
@@ -135,11 +128,11 @@ function selectPrimaryWorkExperience(orgs: IWorkRow[]): IWorkRow {
135128
}
136129

137130
// 5. Longest date range as final tiebreaker
138-
return longestDateRange(orgs)
131+
return getLongestDateRange(orgs as unknown as IMemberOrganization[]) as unknown as IWorkExperience
139132
}
140133

141134
/** Returns the org used to fill gaps — primary undated wins, then earliest-created undated. */
142-
function findFallbackOrg(rows: IWorkRow[]): IWorkRow | null {
135+
function findFallbackOrg(rows: IWorkExperience[]): IWorkExperience | null {
143136
const primaryUndated = rows.find((r) => r.isPrimaryWorkExperience && !r.dateStart && !r.dateEnd)
144137
if (primaryUndated) return primaryUndated
145138

@@ -155,7 +148,7 @@ function findFallbackOrg(rows: IWorkRow[]): IWorkRow | null {
155148
* Collects all date boundaries from the dated rows, capped at today.
156149
* Each dateStart and (dateEnd + 1 day) marks a point where active orgs can change.
157150
*/
158-
function collectBoundaries(datedRows: IWorkRow[]): Date[] {
151+
function collectBoundaries(datedRows: IWorkExperience[]): Date[] {
159152
const today = startOfDay(new Date())
160153

161154
const ms = new Set<number>([today.getTime()])
@@ -176,7 +169,7 @@ function collectBoundaries(datedRows: IWorkRow[]): Date[] {
176169
.map((t) => new Date(t))
177170
}
178171

179-
function orgsActiveAt(rows: IWorkRow[], boundaryDate: Date): IWorkRow[] {
172+
function orgsActiveAt(rows: IWorkExperience[], boundaryDate: Date): IWorkExperience[] {
180173
return rows.filter((role) => {
181174
if (!role.dateStart && !role.dateEnd) return true // truly undated: active at every boundary
182175

@@ -202,12 +195,12 @@ function dayBefore(date: Date): Date {
202195

203196
/** Iterates boundary intervals and builds non-overlapping affiliation windows. */
204197
function buildTimeline(
205-
allRows: IWorkRow[],
206-
fallbackOrg: IWorkRow | null,
198+
allRows: IWorkExperience[],
199+
fallbackOrg: IWorkExperience | null,
207200
boundaries: Date[],
208201
): IAffiliationPeriod[] {
209202
const affiliations: IAffiliationPeriod[] = []
210-
let currentOrg: IWorkRow = null
203+
let currentOrg: IWorkExperience = null
211204
let currentWindowStart: Date = null
212205
let uncoveredPeriodStart: Date = null
213206

@@ -288,7 +281,7 @@ function buildTimeline(
288281
return affiliations
289282
}
290283

291-
function resolveAffiliationsForMember(rows: IWorkRow[]): IAffiliationPeriod[] {
284+
function resolveAffiliationsForMember(rows: IWorkExperience[]): IAffiliationPeriod[] {
292285
// If one undated work-experience org is marked primary, drop other undated work-experience orgs
293286
// to avoid infinite conflicts. Manual affiliations (segmentId !== null) are never dropped.
294287
const primaryUndated = rows.find((r) => r.isPrimaryWorkExperience && !r.dateStart && !r.dateEnd)
@@ -327,7 +320,7 @@ export async function resolveAffiliationsByMemberIds(
327320
findManualAffiliationsBulk(qx, memberIds),
328321
])
329322

330-
const byMember = new Map<string, IWorkRow[]>()
323+
const byMember = new Map<string, IWorkExperience[]>()
331324
for (const row of [...workExperiences, ...manualAffiliations]) {
332325
const list = byMember.get(row.memberId) ?? []
333326
list.push(row)

0 commit comments

Comments
 (0)