Skip to content

Commit be80ade

Browse files
authored
Merge branch 'main' into feat/CM-361-part-1
2 parents 10adda6 + 5fc149a commit be80ade

10 files changed

Lines changed: 96 additions & 21 deletions

File tree

backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"script:continue-run": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true tsx src/bin/scripts/continue-run.ts",
2020
"script:trigger-webhook": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true tsx src/bin/scripts/trigger-webhook.ts",
2121
"script:send-weekly-analytics-email": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true tsx src/bin/scripts/send-weekly-analytics-email.ts",
22+
"script:merge-members": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true tsx src/bin/scripts/merge-members.ts",
2223
"script:merge-organizations": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true tsx src/bin/scripts/merge-organizations.ts",
2324
"script:refresh-materialized-views": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true tsx src/bin/scripts/refresh-materialized-views.ts",
2425
"script:unmerge-members": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true tsx src/bin/scripts/unmerge-members.ts",

backend/src/api/public/v1/stewardships/getMyActivity.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export async function getMyActivityHandler(req: Request, res: Response): Promise
6565
description: r.content,
6666
actor: r.actor,
6767
createdAt: r.createdAt,
68-
suggestedAction: SUGGESTED_ACTIONS[r.stewardshipStatus] ?? null,
68+
suggestedAction: SUGGESTED_ACTIONS[r.currentStewardshipStatus] ?? null,
6969
})),
7070
meta: {
7171
total,

backend/src/bin/scripts/merge-members.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import commandLineUsage from 'command-line-usage'
33
import * as fs from 'fs'
44
import path from 'path'
55

6+
import { generateUUIDv1 } from '@crowd/common'
67
import { CommonMemberService } from '@crowd/common_services'
78
import { optionsQx } from '@crowd/data-access-layer'
89
import { MemberField, findMemberById } from '@crowd/data-access-layer/src/members'
@@ -33,6 +34,13 @@ const options = [
3334
description:
3435
'The unique ID of a member that will be merged into the first one. This one will be destroyed. You can provide multiple ids here separated by comma.',
3536
},
37+
{
38+
name: 'userId',
39+
alias: 'u',
40+
typeLabel: '{underline userId}',
41+
type: String,
42+
description: 'User ID of the user performing the merge.',
43+
},
3644
{
3745
name: 'help',
3846
alias: 'h',
@@ -65,6 +73,8 @@ if (parameters.help || !parameters.originalId || !parameters.targetId) {
6573
const originalId = parameters.originalId
6674
const targetIds = parameters.targetId.split(',')
6775

76+
const userId = parameters.userId
77+
6878
const options = await SequelizeRepository.getDefaultIRepositoryOptions()
6979
const qx = SequelizeRepository.getQueryExecutor(options)
7080

@@ -74,6 +84,16 @@ if (parameters.help || !parameters.originalId || !parameters.targetId) {
7484
])
7585

7686
options.currentTenant = { id: originalMember.tenantId }
87+
options.currentUser = { id: userId }
88+
89+
const ctx = {
90+
...options,
91+
requestId: generateUUIDv1(),
92+
userData: {
93+
ip: '127.0.0.1',
94+
userAgent: 'merge-members-script',
95+
},
96+
}
7797

7898
for (const targetId of targetIds) {
7999
const targetMember = await findMemberById(qx, targetId, [
@@ -89,7 +109,7 @@ if (parameters.help || !parameters.originalId || !parameters.targetId) {
89109
log.info(`Merging ${targetId} into ${originalId}...`)
90110
const service = new CommonMemberService(optionsQx(options), options.temporal, log)
91111
try {
92-
await service.merge(originalId, targetId)
112+
await service.merge(originalId, targetId, ctx)
93113
} catch (err) {
94114
log.error(`Error merging members: ${err.message}`)
95115
process.exit(1)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Store the stewardship status as it was at the moment each activity was logged.
2+
-- Existing rows get NULL (no history to reconstruct); queries fall back to s.status for them.
3+
ALTER TABLE stewardship_activity
4+
ADD COLUMN IF NOT EXISTS status_at_time TEXT;

services/apps/packages_worker/src/maven/extract.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,10 @@ async function resolveWithInheritance(groupId: string, artifactId: string, versi
276276

277277
const missingLicense = licenses.length === 0
278278
const missingScm = !scmUrl
279+
const missingDevelopers = developers.length === 0 || contributors.length === 0
279280
const parent = extractParent(pom)
280281

281-
if (parent && (missingLicense || missingScm)) {
282+
if (parent && (missingLicense || missingScm || missingDevelopers)) {
282283
const parentKey = `${parent.groupId}:${parent.artifactId}:${parent.version}`
283284
if (depth >= MAX_PARENT_DEPTH || visited.has(parentKey)) {
284285
log.warn(

services/apps/script_executor_worker/src/activities.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
findMemberById,
3838
findMemberIdentitiesGroupedByPlatform,
3939
findMemberMergeActions,
40+
findMergeActionUnmergeBackup,
4041
} from './activities/dissect-member'
4142
import {
4243
getBotMembersWithOrgAffiliation,
@@ -66,6 +67,7 @@ export {
6667
findMembersWithSamePlatformIdentitiesDifferentCapitalization,
6768
mergeMembers,
6869
findMemberMergeActions,
70+
findMergeActionUnmergeBackup,
6971
unmergeMembers,
7072
unmergeMembersPreview,
7173
waitForTemporalWorkflowExecutionFinish,

services/apps/script_executor_worker/src/activities/dissect-member/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ export async function findMemberMergeActions(
3030
return mergeActions
3131
}
3232

33+
export async function findMergeActionUnmergeBackup(
34+
mergeActionId: string,
35+
): Promise<IMergeAction['unmergeBackup'] | null> {
36+
try {
37+
const mergeActionRepo = new MergeActionRepository(svc.postgres.reader.connection(), svc.log)
38+
return await mergeActionRepo.findMergeActionUnmergeBackup(mergeActionId)
39+
} catch (err) {
40+
throw new Error(err)
41+
}
42+
}
43+
3344
export async function findMemberIdentitiesGroupedByPlatform(
3445
memberId: string,
3546
): Promise<IFindMemberIdentitiesGroupedByPlatformResult[]> {

services/apps/script_executor_worker/src/workflows/dissectMember.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
continueAsNew,
55
proxyActivities,
66
startChild,
7-
workflowInfo,
87
} from '@temporalio/workflow'
98

109
import { IMemberUnmergeBackup, IUnmergeBackup, MemberIdentityType } from '@crowd/types'
@@ -24,8 +23,6 @@ const common = proxyActivities<typeof commonActivities>({
2423
})
2524

2625
export async function dissectMember(args: IDissectMemberArgs): Promise<void> {
27-
const info = workflowInfo()
28-
2926
// check if memberId exist in db before unmerging
3027
const member = await activity.findMemberById(args.memberId)
3128

@@ -92,17 +89,26 @@ export async function dissectMember(args: IDissectMemberArgs): Promise<void> {
9289
// 2. wait for temporal async stuff to complete
9390
// 3. call the same workflow again for the unmerged secondary member
9491

92+
// Fetch each backup in its own activity — listing 10 backups in one result can exceed
93+
// Temporal's 2MB activity payload limit on polluted members.
94+
const unmergeBackup = await activity.findMergeActionUnmergeBackup(mergeAction.id)
95+
96+
if (!unmergeBackup) {
97+
console.log(`Merge action ${mergeAction.id} has no unmerge backup, skipping!`)
98+
continue
99+
}
100+
95101
await common.unmergeMembers(
96102
mergeAction.primaryId,
97-
mergeAction.unmergeBackup as IUnmergeBackup<IMemberUnmergeBackup>,
103+
unmergeBackup as IUnmergeBackup<IMemberUnmergeBackup>,
98104
)
99105

100106
const workflowId = `finishMemberUnmerging/${mergeAction.primaryId}/${mergeAction.secondaryId}`
101107

102108
await common.waitForTemporalWorkflowExecutionFinish(workflowId)
103109

104110
await startChild(dissectMember, {
105-
workflowId: `${info.workflowId}/${mergeAction.secondaryId}`,
111+
workflowId: `dissectMember/${mergeAction.secondaryId}`,
106112
cancellationType: ChildWorkflowCancellationType.ABANDON,
107113
parentClosePolicy: ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON,
108114
retry: {

services/libs/data-access-layer/src/old/apps/script_executor_worker/mergeAction.repo.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,16 @@ class MergeActionRepository {
1919
) {
2020
let rows: IMergeAction[] = []
2121
let query = `
22-
select * from "mergeActions" ma
22+
select
23+
ma.id,
24+
ma.type,
25+
ma."primaryId",
26+
ma."secondaryId",
27+
ma."createdAt",
28+
ma."updatedAt",
29+
ma.step,
30+
ma.state
31+
from "mergeActions" ma
2332
where
2433
ma."state" = 'merged' and
2534
ma."primaryId" = $(memberId) and
@@ -62,6 +71,20 @@ class MergeActionRepository {
6271
return rows
6372
}
6473

74+
async findMergeActionUnmergeBackup(mergeActionId: string) {
75+
const rows = await this.connection.query(
76+
`
77+
select ma."unmergeBackup"
78+
from "mergeActions" ma
79+
where ma.id = $(mergeActionId)
80+
and ma."unmergeBackup" is not null
81+
`,
82+
{ mergeActionId },
83+
)
84+
85+
return rows[0]?.unmergeBackup ?? null
86+
}
87+
6588
async findMergeActionsWithDeletedSecondaryEntities(
6689
limit: number,
6790
offset: number,

services/libs/data-access-layer/src/osspckgs/stewardships.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,9 @@ export async function openStewardshipByPurl(
203203
),
204204
_log AS (
205205
INSERT INTO stewardship_activity
206-
(stewardship_id, actor_user_id, actor_type, activity_type, content, actor_username, actor_display_name, actor_avatar_url)
206+
(stewardship_id, actor_user_id, actor_type, activity_type, content, actor_username, actor_display_name, actor_avatar_url, status_at_time)
207207
SELECT upserted.id, $(actorUserId), 'user', 'state_changed', 'Opened for stewardship',
208-
$(actorUsername), $(actorDisplayName), $(actorAvatarUrl)
208+
$(actorUsername), $(actorDisplayName), $(actorAvatarUrl), 'open'
209209
FROM upserted
210210
WHERE NOT EXISTS (SELECT 1 FROM prev WHERE prev.old_status = 'open')
211211
)
@@ -278,16 +278,17 @@ export async function assignSteward(
278278

279279
await tx.result(
280280
`INSERT INTO stewardship_activity
281-
(stewardship_id, actor_user_id, actor_type, activity_type, content, metadata, actor_username, actor_display_name, actor_avatar_url)
281+
(stewardship_id, actor_user_id, actor_type, activity_type, content, metadata, actor_username, actor_display_name, actor_avatar_url, status_at_time)
282282
VALUES ($(stewardshipId), $(actorUserId), 'user', 'steward_added', $(content), $(metadata)::jsonb,
283-
$(actorUsername), $(actorDisplayName), $(actorAvatarUrl))`,
283+
$(actorUsername), $(actorDisplayName), $(actorAvatarUrl), $(statusAtTime))`,
284284
{
285285
stewardshipId,
286286
actorUserId: data.assignedBy,
287287
actorUsername: data.actorUsername ?? null,
288288
actorDisplayName: data.actorDisplayName ?? null,
289289
actorAvatarUrl: data.actorAvatarUrl ?? null,
290290
content: `Assigned steward ${data.userId} as ${data.role}`,
291+
statusAtTime: stewardship.status,
291292
metadata: JSON.stringify({
292293
userId: data.userId,
293294
role: data.role,
@@ -316,10 +317,10 @@ export async function assignSteward(
316317
),
317318
_log AS (
318319
INSERT INTO stewardship_activity
319-
(stewardship_id, actor_user_id, actor_type, activity_type, content, metadata, actor_username, actor_display_name, actor_avatar_url)
320+
(stewardship_id, actor_user_id, actor_type, activity_type, content, metadata, actor_username, actor_display_name, actor_avatar_url, status_at_time)
320321
SELECT id, $(actorUserId), 'user', 'state_changed',
321322
'Status updated to assessing', $(metadata)::jsonb,
322-
$(actorUsername), $(actorDisplayName), $(actorAvatarUrl)
323+
$(actorUsername), $(actorDisplayName), $(actorAvatarUrl), 'assessing'
323324
FROM upd
324325
)
325326
SELECT * FROM upd
@@ -380,6 +381,7 @@ export interface ActivityFeedRow {
380381
content: string | null
381382
metadata: Record<string, unknown> | null
382383
stewardshipStatus: string
384+
currentStewardshipStatus: string
383385
createdAt: string
384386
total: string
385387
}
@@ -406,7 +408,8 @@ export async function listStewardshipActivity(
406408
sa.activity_type AS "activityType",
407409
sa.content AS content,
408410
${STEWARD_DISPLAY_NAME_METADATA} AS metadata,
409-
s.status AS "stewardshipStatus",
411+
COALESCE(sa.status_at_time, s.status) AS "stewardshipStatus",
412+
s.status AS "currentStewardshipStatus",
410413
sa.created_at AS "createdAt",
411414
COUNT(*) OVER()::text AS total
412415
FROM stewardship_activity sa
@@ -454,6 +457,7 @@ export async function listStewardshipActivity(
454457
),
455458
metadata: row.metadata as Record<string, unknown> | null,
456459
stewardshipStatus: row.stewardshipStatus as string,
460+
currentStewardshipStatus: row.currentStewardshipStatus as string,
457461
createdAt: toIso(row.createdAt),
458462
})),
459463
total,
@@ -797,6 +801,7 @@ export async function listMyActivity(
797801
sub.content,
798802
sub.metadata,
799803
sub."stewardshipStatus",
804+
sub."currentStewardshipStatus",
800805
sub."createdAt",
801806
COUNT(*) OVER()::text AS total
802807
FROM (
@@ -814,7 +819,8 @@ export async function listMyActivity(
814819
sa.activity_type AS "activityType",
815820
sa.content AS content,
816821
${STEWARD_DISPLAY_NAME_METADATA} AS metadata,
817-
s.status AS "stewardshipStatus",
822+
COALESCE(sa.status_at_time, s.status) AS "stewardshipStatus",
823+
s.status AS "currentStewardshipStatus",
818824
sa.created_at AS "createdAt"
819825
FROM stewardship_activity sa
820826
JOIN stewardships s ON s.id = sa.stewardship_id
@@ -879,6 +885,7 @@ export async function listMyActivity(
879885
),
880886
metadata: row.metadata as Record<string, unknown> | null,
881887
stewardshipStatus: row.stewardshipStatus as string,
888+
currentStewardshipStatus: row.currentStewardshipStatus as string,
882889
createdAt: toIso(row.createdAt),
883890
})),
884891
total,
@@ -961,9 +968,9 @@ export async function escalateStewardship(
961968
),
962969
_log AS (
963970
INSERT INTO stewardship_activity
964-
(stewardship_id, actor_user_id, actor_type, activity_type, content, metadata, actor_username, actor_display_name, actor_avatar_url)
971+
(stewardship_id, actor_user_id, actor_type, activity_type, content, metadata, actor_username, actor_display_name, actor_avatar_url, status_at_time)
965972
SELECT id, $(actorUserId), 'user', 'escalation',
966-
$(content), $(metadata)::jsonb, $(actorUsername), $(actorDisplayName), $(actorAvatarUrl)
973+
$(content), $(metadata)::jsonb, $(actorUsername), $(actorDisplayName), $(actorAvatarUrl), 'escalated'
967974
FROM upd
968975
)
969976
SELECT * FROM upd
@@ -1037,9 +1044,9 @@ export async function updateStewardshipStatus(
10371044
),
10381045
_log AS (
10391046
INSERT INTO stewardship_activity
1040-
(stewardship_id, actor_user_id, actor_type, activity_type, content, metadata, actor_username, actor_display_name, actor_avatar_url)
1047+
(stewardship_id, actor_user_id, actor_type, activity_type, content, metadata, actor_username, actor_display_name, actor_avatar_url, status_at_time)
10411048
SELECT id, $(actorUserId), 'user', 'state_changed',
1042-
$(content), $(metadata)::jsonb, $(actorUsername), $(actorDisplayName), $(actorAvatarUrl)
1049+
$(content), $(metadata)::jsonb, $(actorUsername), $(actorDisplayName), $(actorAvatarUrl), $(status)
10431050
FROM upd
10441051
)
10451052
SELECT * FROM upd

0 commit comments

Comments
 (0)