Skip to content

Commit c57383e

Browse files
authored
Merge pull request #28 from topcoder-platform/develop
[v6 PROD RELEASE] - dev -> master
2 parents 20261d4 + 73a547b commit c57383e

File tree

20 files changed

+1836
-159
lines changed

20 files changed

+1836
-159
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ workflows:
103103
- feature/top-262-projectid-non-mandatory
104104
- TOP-2364
105105
- PM-2097
106-
- pm-2456
106+
- pm-2539
107107

108108
- "build-qa":
109109
context: org-global

.github/workflows/trivy.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Trivy Scanner
2+
3+
permissions:
4+
contents: read
5+
security-events: write
6+
on:
7+
push:
8+
branches:
9+
- main
10+
- dev
11+
pull_request:
12+
jobs:
13+
trivy-scan:
14+
name: Use Trivy
15+
runs-on: ubuntu-24.04
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
20+
- name: Run Trivy scanner in repo mode
21+
uses: aquasecurity/trivy-action@0.33.1
22+
with:
23+
scan-type: "fs"
24+
ignore-unfixed: true
25+
format: "sarif"
26+
output: "trivy-results.sarif"
27+
severity: "CRITICAL,HIGH,UNKNOWN"
28+
scanners: vuln,secret,misconfig,license
29+
github-pat: ${{ secrets.GITHUB_TOKEN }}
30+
31+
- name: Upload Trivy scan results to GitHub Security tab
32+
uses: github/codeql-action/upload-sarif@v3
33+
with:
34+
sarif_file: "trivy-results.sarif"

app-constants.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ const Topics = {
6565
ChallengeTimelineTemplateDeleted: "challenge.action.challenge.timeline.deleted",
6666
ChallengePhaseUpdated: "challenge.action.phase.updated",
6767
ChallengePhaseDeleted: "challenge.action.phase.deleted",
68+
DefaultChallengeReviewerCreated: "challenge.action.default.reviewer.created",
69+
DefaultChallengeReviewerUpdated: "challenge.action.default.reviewer.updated",
70+
DefaultChallengeReviewerDeleted: "challenge.action.default.reviewer.deleted",
6871
// Self Service topics
6972
Notifications: "notifications.action.create",
7073
};
@@ -95,6 +98,9 @@ const DisabledTopics = [
9598
Topics.ChallengeTimelineTemplateDeleted,
9699
Topics.ChallengePhaseUpdated,
97100
Topics.ChallengePhaseDeleted,
101+
Topics.DefaultChallengeReviewerCreated,
102+
Topics.DefaultChallengeReviewerUpdated,
103+
Topics.DefaultChallengeReviewerDeleted,
98104
];
99105

100106
const challengeTextSortField = {

config/default.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,5 @@ module.exports = {
129129
// Database schemas for direct counts (shared DB)
130130
RESOURCES_DB_SCHEMA: process.env.RESOURCES_DB_SCHEMA || "resources",
131131
REVIEW_DB_SCHEMA: process.env.REVIEW_DB_SCHEMA || "reviews",
132+
CHALLENGE_SERVICE_PRISMA_TIMEOUT: process.env.CHALLENGE_SERVICE_PRISMA_TIMEOUT ? parseInt(process.env.CHALLENGE_SERVICE_PRISMA_TIMEOUT, 10) : 10000,
132133
};

data-migration/src/config.js

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,81 @@ const parseMigrationMode = value => {
1414
return ALLOWED_MIGRATION_MODES.has(normalized) ? normalized : 'full';
1515
};
1616

17-
// Default configuration with fallbacks
18-
module.exports = {
17+
const RECOMMENDED_INCREMENTAL_FIELDS = ['updatedAt', 'updatedBy'];
18+
19+
const collectKnownSchemaFields = migratorConfig => {
20+
const knownFields = new Set();
21+
if (!migratorConfig) {
22+
return knownFields;
23+
}
24+
25+
Object.values(migratorConfig).forEach(definition => {
26+
if (!definition || typeof definition !== 'object') {
27+
return;
28+
}
29+
(definition.requiredFields || []).forEach(field => knownFields.add(field));
30+
(definition.optionalFields || []).forEach(field => knownFields.add(field));
31+
const defaults = definition.hasDefaults || [];
32+
defaults.forEach(field => knownFields.add(field));
33+
});
34+
35+
return knownFields;
36+
};
37+
38+
const validateIncrementalFieldConfiguration = (config) => {
39+
const incrementalFields = Array.isArray(config.INCREMENTAL_FIELDS) ? config.INCREMENTAL_FIELDS : [];
40+
const isIncrementalMode = config.MIGRATION_MODE === 'incremental';
41+
const knownFields = collectKnownSchemaFields(config.migrator);
42+
43+
const result = {
44+
warnings: [],
45+
errors: []
46+
};
47+
48+
if (isIncrementalMode && incrementalFields.length === 0) {
49+
result.errors.push('INCREMENTAL_FIELDS must be configured when running incremental migrations.');
50+
}
51+
52+
incrementalFields.forEach(field => {
53+
if (!knownFields.has(field)) {
54+
result.warnings.push(`Incremental field "${field}" is not present in any migrator schema and will be ignored.`);
55+
}
56+
});
57+
58+
const recommendedMissing = RECOMMENDED_INCREMENTAL_FIELDS.filter(field => incrementalFields.length > 0 && !incrementalFields.includes(field));
59+
if (recommendedMissing.length) {
60+
result.warnings.push(`Consider including ${recommendedMissing.join(', ')} in INCREMENTAL_FIELDS to preserve audit columns during updates.`);
61+
}
62+
63+
if (incrementalFields.length) {
64+
console.info(`[config] Incremental fields configured: ${incrementalFields.join(', ')}`);
65+
}
66+
67+
result.warnings.forEach(message => console.warn(`[config] ${message}`));
68+
result.errors.forEach(message => console.error(`[config] ${message}`));
69+
70+
return result;
71+
};
72+
73+
/**
74+
* Migration configuration with environment-driven overrides.
75+
*
76+
* Environment variables:
77+
* - DATABASE_URL: Postgres connection string.
78+
* - DATA_DIRECTORY: Root directory where migration payloads are stored.
79+
* - MIGRATION_MODE: 'full' or 'incremental' (defaults to 'full').
80+
* - INCREMENTAL_SINCE_DATE: ISO-8601 date used to filter source records.
81+
* - INCREMENTAL_FIELDS: Comma-separated list of fields updated during incremental runs.
82+
* - INCREMENTAL_FIELDS examples:
83+
* INCREMENTAL_FIELDS=updatedAt,updatedBy,status
84+
* INCREMENTAL_FIELDS=updatedAt,updatedBy,status,currentPhaseNames
85+
* Fields not listed remain unchanged during incremental updates.
86+
* - INCREMENTAL_DATE_FIELDS: Ordered list of timestamp fields to evaluate for incremental filtering (default: updatedAt,updated).
87+
* - MISSING_DATE_FIELD_BEHAVIOR: Behaviour when timestamps are missing (skip, include, warn-and-skip, warn-and-include).
88+
* - INVALID_DATE_FIELD_BEHAVIOR: Behaviour when timestamps are invalid or suspicious (same options as above).
89+
* - CREATED_BY / UPDATED_BY: Attribution columns written during migration.
90+
*/
91+
const config = {
1992
// Database connection
2093
DATABASE_URL: process.env.DATABASE_URL,
2194

@@ -24,6 +97,9 @@ module.exports = {
2497
BATCH_SIZE: parseInt(process.env.BATCH_SIZE || '100', 10),
2598
CONCURRENCY_LIMIT: parseInt(process.env.CONCURRENCY_LIMIT || '10', 10),
2699
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
100+
LOG_VERBOSITY: (process.env.LOG_VERBOSITY || 'normal').toLowerCase(),
101+
SUMMARY_LOG_LIMIT: parseInt(process.env.SUMMARY_LOG_LIMIT || '5', 10),
102+
COLLECT_UPSERT_STATS: process.env.COLLECT_UPSERT_STATS === 'true',
27103

28104
// Migration behavior
29105
SKIP_MISSING_REQUIRED: process.env.SKIP_MISSING_REQUIRED === 'true',
@@ -34,7 +110,15 @@ module.exports = {
34110
// Incremental migration settings
35111
MIGRATION_MODE: parseMigrationMode(process.env.MIGRATION_MODE),
36112
INCREMENTAL_SINCE_DATE: process.env.INCREMENTAL_SINCE_DATE || null,
113+
/**
114+
* Fields that should be mutated when MIGRATION_MODE=incremental.
115+
* Only columns listed here are included in UPDATE operations; omitted columns retain their existing values.
116+
* Works in tandem with INCREMENTAL_SINCE_DATE to scope the incremental window.
117+
*/
37118
INCREMENTAL_FIELDS: parseListEnv(process.env.INCREMENTAL_FIELDS),
119+
INCREMENTAL_DATE_FIELDS: parseListEnv(process.env.INCREMENTAL_DATE_FIELDS) || ['updatedAt', 'updated'],
120+
MISSING_DATE_FIELD_BEHAVIOR: (process.env.MISSING_DATE_FIELD_BEHAVIOR || 'warn-and-skip').toLowerCase(),
121+
INVALID_DATE_FIELD_BEHAVIOR: (process.env.INVALID_DATE_FIELD_BEHAVIOR || 'warn-and-skip').toLowerCase(),
38122

39123
// Migration attribution
40124
CREATED_BY: process.env.CREATED_BY || 'migration',
@@ -424,7 +508,16 @@ module.exports = {
424508
},
425509
};
426510

511+
config.incrementalFieldValidation = validateIncrementalFieldConfiguration(config);
512+
513+
module.exports = config;
514+
427515
Object.defineProperty(module.exports, 'parseMigrationMode', {
428516
value: parseMigrationMode,
429517
enumerable: false
430518
});
519+
520+
Object.defineProperty(module.exports, 'validateIncrementalFieldConfiguration', {
521+
value: validateIncrementalFieldConfiguration,
522+
enumerable: false
523+
});

0 commit comments

Comments
 (0)