Skip to content

Commit d8a8fd0

Browse files
Fix Codacy linting issues and resolve flaky http test infrastructure bug
Signed-off-by: Steve Springett <steve@springett.us>
1 parent 9d884a3 commit d8a8fd0

31 files changed

+159
-83
lines changed

backend/src/db/connection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { KyselyPGlite } from 'kysely-pglite';
33
import { Pool } from 'pg';
44
import fs from 'node:fs';
55
import { getConfig } from '../config/index.js';
6-
import { Database } from './types.js';
6+
import type { Database } from './types.js';
77
import { logger } from '../utils/logger.js';
88

99
let db: Kysely<Database> | null = null;

backend/src/db/migrate.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -815,12 +815,14 @@ export async function runMigrations(): Promise<void> {
815815
for (const statement of statements) {
816816
if (statement.trim()) {
817817
try {
818+
// biome-ignore lint/suspicious/noExplicitAny: Kysely executeQuery requires CompiledQuery but we build raw SQL
818819
await db.executeQuery({
819-
sql: statement + ';',
820+
sql: `${statement};`,
820821
parameters: [],
821822
} as any);
822-
} catch (error: any) {
823-
const msg = error?.message || '';
823+
} catch (error: unknown) {
824+
const errorMessage = (error as Record<string, unknown> | null)?.message || '';
825+
const msg = typeof errorMessage === 'string' ? errorMessage : '';
824826
// Ignore "already exists" errors from CREATE INDEX / ALTER TABLE
825827
if (msg.includes('already exists')) {
826828
continue;

backend/src/events/channels/email.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export class EmailChannel implements NotificationChannel {
101101

102102
// Validate required fields
103103
const required: (keyof SmtpConfig)[] = ['SMTP_HOST', 'SMTP_PORT', 'SMTP_FROM'];
104-
const missing = required.filter((key) => !this.config![key]);
104+
const missing = required.filter((key) => !this.config?.[key]);
105105
if (missing.length > 0) {
106106
throw new Error(
107107
`SMTP_ENABLED is true but required configuration is missing: ${missing.join(', ')}`,
@@ -337,7 +337,7 @@ export class EmailChannel implements NotificationChannel {
337337
if (!message) return;
338338

339339
await this.transporter.sendMail({
340-
from: this.config!.SMTP_FROM,
340+
from: this.config?.SMTP_FROM || 'noreply@assessors-studio.local',
341341
to: message.to,
342342
subject: message.subject,
343343
text: message.text,

backend/src/events/webhook-channel.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ export class WebhookChannel implements NotificationChannel {
299299

300300
// Increment consecutive failures
301301
const newFailureCount = webhook.consecutive_failures + 1;
302-
const updates: Record<string, any> = {
302+
const updates: Record<string, unknown> = {
303303
consecutive_failures: newFailureCount,
304304
updated_at: new Date(),
305305
};
@@ -370,7 +370,7 @@ export class WebhookChannel implements NotificationChannel {
370370
await db
371371
.updateTable('webhook_delivery')
372372
.set({
373-
attempt: (row.attempt as number) + 1,
373+
attempt: typeof row.attempt === 'number' ? row.attempt + 1 : 2,
374374
status: 'pending',
375375
next_retry_at: null,
376376
})

backend/src/middleware/auth.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,17 +84,17 @@ async function authenticateCookie(req: AuthRequest): Promise<AuthRequest['user']
8484
const token = req.cookies?.token;
8585
if (!token) return null;
8686

87-
let decoded: any;
87+
let decoded: Record<string, unknown>;
8888
try {
89-
decoded = jwt.verify(token, config.JWT_SECRET);
89+
decoded = jwt.verify(token, config.JWT_SECRET) as Record<string, unknown>;
9090
} catch {
9191
return null;
9292
}
9393

9494
const db = getDatabase();
9595
const session = await db
9696
.selectFrom('session')
97-
.where('id', '=', decoded.sessionId)
97+
.where('id', '=', decoded.sessionId as string)
9898
.where('expires_at', '>', new Date())
9999
.selectAll()
100100
.executeTakeFirst();

backend/src/middleware/camelCase.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function transformKeys(obj: unknown): unknown {
3434
export function camelCaseResponse(_req: Request, res: Response, next: NextFunction): void {
3535
const originalJson = res.json.bind(res);
3636

37-
res.json = function (body: unknown) {
37+
res.json = (body: unknown) => {
3838
return originalJson(transformKeys(body));
3939
};
4040

backend/src/routes/admin-notification-rules.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import { z } from 'zod';
1111
import { v4 as uuidv4 } from 'uuid';
1212
import { getDatabase } from '../db/connection.js';
1313
import { logger } from '../utils/logger.js';
14-
import { AuthRequest, requireAuth, requirePermission } from '../middleware/auth.js';
14+
import type { AuthRequest } from '../middleware/auth.js';
15+
import { requireAuth, requirePermission } from '../middleware/auth.js';
1516
import { asyncHandler, handleValidationError } from '../utils/route-helpers.js';
1617

1718
const router = Router();
@@ -32,7 +33,7 @@ const updateRuleSchema = createRuleSchema.partial();
3233
* GET /admin/notification-rules
3334
* List all system notification rules
3435
*/
35-
router.get('/', requireAuth, requirePermission('admin.notification_rules'), asyncHandler(async (req: AuthRequest, res: Response): Promise<void> => {
36+
router.get('/', requireAuth, requirePermission('admin.notification_rules'), asyncHandler(async (_req: AuthRequest, res: Response): Promise<void> => {
3637
const db = getDatabase();
3738
const rules = await db
3839
.selectFrom('notification_rule')
@@ -41,7 +42,7 @@ router.get('/', requireAuth, requirePermission('admin.notification_rules'), asyn
4142
.orderBy('created_at', 'desc')
4243
.execute();
4344

44-
res.json(rules);
45+
res.json(rules as Record<string, unknown>[]);
4546
}));
4647

4748
/**
@@ -197,7 +198,7 @@ router.put('/:id', requireAuth, requirePermission('admin.notification_rules'), a
197198
}
198199

199200
// Update rule
200-
const updates: Record<string, any> = {};
201+
const updates: Record<string, unknown> = {};
201202
if (data.name) updates.name = data.name;
202203
if (data.channel) updates.channel = data.channel;
203204
if (data.eventTypes) updates.event_types = JSON.stringify(data.eventTypes);

backend/src/routes/assessors.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { v4 as uuidv4 } from 'uuid';
55
import { getDatabase } from '../db/connection.js';
66
import { asyncHandler, handleValidationError } from '../utils/route-helpers.js';
77
import { logger } from '../utils/logger.js';
8-
import { AuthRequest, requireAuth, requirePermission } from '../middleware/auth.js';
8+
import type { AuthRequest } from '../middleware/auth.js';
9+
import { requireAuth, requirePermission } from '../middleware/auth.js';
910

1011
const router = Router();
1112

@@ -22,14 +23,16 @@ const updateAssessorSchema = z.object({
2223
});
2324

2425
// GET / - List all assessors
25-
router.get('/', requireAuth, asyncHandler(async (req: AuthRequest, res: Response): Promise<void> => {
26+
router.get('/', requireAuth, asyncHandler(async (_req: AuthRequest, res: Response): Promise<void> => {
2627
const db = getDatabase();
2728

2829
const assessors = (await db
2930
.selectFrom('assessor')
31+
// biome-ignore lint/suspicious/noExplicitAny: Kysely cross-table join refs require type cast
3032
.leftJoin('entity', (join) =>
3133
join.onRef('entity.id' as any, '=', 'assessor.entity_id' as any)
3234
)
35+
// biome-ignore lint/suspicious/noExplicitAny: Kysely cross-table join refs require type cast
3336
.leftJoin('app_user', (join) =>
3437
join.onRef('app_user.id' as any, '=', 'assessor.user_id' as any)
3538
)
@@ -46,7 +49,7 @@ router.get('/', requireAuth, asyncHandler(async (req: AuthRequest, res: Response
4649
'app_user.display_name as user_display_name',
4750
])
4851
.orderBy('assessor.created_at', 'desc')
49-
.execute()) as any[];
52+
.execute()) as Record<string, unknown>[];
5053

5154
res.json({ data: assessors });
5255
}));
@@ -57,9 +60,11 @@ router.get('/:id', requireAuth, asyncHandler(async (req: AuthRequest, res: Respo
5760

5861
const assessor = await db
5962
.selectFrom('assessor')
63+
// biome-ignore lint/suspicious/noExplicitAny: Kysely cross-table join refs require type cast
6064
.leftJoin('entity', (join) =>
6165
join.onRef('entity.id' as any, '=', 'assessor.entity_id' as any)
6266
)
67+
// biome-ignore lint/suspicious/noExplicitAny: Kysely cross-table join refs require type cast
6368
.leftJoin('app_user', (join) =>
6469
join.onRef('app_user.id' as any, '=', 'assessor.user_id' as any)
6570
)
@@ -86,6 +91,7 @@ router.get('/:id', requireAuth, asyncHandler(async (req: AuthRequest, res: Respo
8691
// Get attestations by this assessor
8792
const attestations = (await db
8893
.selectFrom('attestation')
94+
// biome-ignore lint/suspicious/noExplicitAny: Kysely cross-table join refs require type cast
8995
.leftJoin('assessment', (join) =>
9096
join.onRef('assessment.id' as any, '=', 'attestation.assessment_id' as any)
9197
)
@@ -98,7 +104,7 @@ router.get('/:id', requireAuth, asyncHandler(async (req: AuthRequest, res: Respo
98104
'assessment.id as assessment_id',
99105
])
100106
.orderBy('attestation.created_at', 'desc')
101-
.execute()) as any[];
107+
.execute()) as Record<string, unknown>[];
102108

103109
res.json({ ...assessor, attestations });
104110
}));
@@ -154,7 +160,7 @@ router.put(
154160
return;
155161
}
156162

157-
const updateData: any = { updated_at: new Date() };
163+
const updateData: Record<string, unknown> = { updated_at: new Date() };
158164
if (data.thirdParty !== undefined) updateData.third_party = data.thirdParty;
159165
if (data.entityId !== undefined) updateData.entity_id = data.entityId;
160166
if (data.userId !== undefined) updateData.user_id = data.userId;

backend/src/routes/attestations.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { z } from 'zod';
44
import { v4 as uuidv4 } from 'uuid';
55
import { getDatabase } from '../db/connection.js';
66
import { logger } from '../utils/logger.js';
7-
import { AuthRequest, requireAuth, requirePermission } from '../middleware/auth.js';
7+
import type { AuthRequest } from '../middleware/auth.js';
8+
import { requireAuth, requirePermission } from '../middleware/auth.js';
89
import { toSnakeCase } from '../middleware/camelCase.js';
910
import { asyncHandler, handleValidationError } from '../utils/route-helpers.js';
1011

@@ -14,7 +15,7 @@ const router = Router();
1415
* Check if an attestation's parent assessment is read-only.
1516
* Returns error message if read-only, null if mutable.
1617
*/
17-
async function checkAttestationAssessmentReadOnly(db: any, assessmentId: string): Promise<string | null> {
18+
async function checkAttestationAssessmentReadOnly(db: ReturnType<typeof getDatabase>, assessmentId: string): Promise<string | null> {
1819
const assessment = await db
1920
.selectFrom('assessment')
2021
.where('id', '=', assessmentId)
@@ -70,9 +71,11 @@ router.get('/', requireAuth, asyncHandler(async (req: AuthRequest, res: Response
7071
.selectFrom('attestation')
7172
.leftJoin('assessment', 'assessment.id', 'attestation.assessment_id')
7273
.leftJoin('signatory', 'signatory.id', 'attestation.signatory_id')
74+
// biome-ignore lint/suspicious/noExplicitAny: Kysely cross-table join refs require type cast
7375
.leftJoin('assessor', (join) =>
7476
join.onRef('assessor.id' as any, '=', 'attestation.assessor_id' as any)
7577
)
78+
// biome-ignore lint/suspicious/noExplicitAny: Kysely cross-table join refs require type cast
7679
.leftJoin('entity as assessor_entity', (join) =>
7780
join.onRef('assessor_entity.id' as any, '=', 'assessor.entity_id' as any)
7881
)
@@ -92,7 +95,7 @@ router.get('/', requireAuth, asyncHandler(async (req: AuthRequest, res: Response
9295
])
9396
.limit(limit)
9497
.offset(offset)
95-
.execute()) as any[];
98+
.execute()) as Record<string, unknown>[];
9699

97100
res.json({
98101
data: attestations,
@@ -231,7 +234,7 @@ router.put(
231234
return;
232235
}
233236

234-
const updateData: any = {};
237+
const updateData: Record<string, unknown> = {};
235238

236239
if (data.summary !== undefined) updateData.summary = data.summary;
237240
if (data.signatoryId !== undefined) updateData.signatoryId = data.signatoryId;

backend/src/routes/export.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import PDFDocument from 'pdfkit';
55
import { getDatabase } from '../db/connection.js';
66
import { asyncHandler } from '../utils/route-helpers.js';
77
import { logger } from '../utils/logger.js';
8-
import { AuthRequest, requireAuth, requirePermission } from '../middleware/auth.js';
8+
import type { AuthRequest } from '../middleware/auth.js';
9+
import { requireAuth, requirePermission } from '../middleware/auth.js';
910

1011
const router = Router();
1112

0 commit comments

Comments
 (0)