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
23 changes: 19 additions & 4 deletions sql/reports/topcoder/weekly-member-participation.sql
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
WITH window_bounds AS (
WITH provided_dates AS (
SELECT
(DATE_TRUNC('week', CURRENT_DATE) - INTERVAL '4 weeks') AS window_start,
(DATE_TRUNC('week', CURRENT_DATE) + INTERVAL '1 week') AS window_end
NULLIF($1, '')::timestamptz AS start_date,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
Casting directly from NULLIF($1, '') to timestamptz might lead to runtime errors if the input is not a valid timestamp. Consider validating the input before casting.

NULLIF($2, '')::timestamptz AS end_date
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
Casting directly from NULLIF($2, '') to timestamptz might lead to runtime errors if the input is not a valid timestamp. Consider validating the input before casting.

),
window_bounds AS (
SELECT
COALESCE(
pd.start_date,
CASE
WHEN pd.end_date IS NOT NULL THEN pd.end_date - INTERVAL '5 weeks'
ELSE DATE_TRUNC('week', CURRENT_DATE) - INTERVAL '4 weeks'
END
) AS window_start,
COALESCE(
pd.end_date,
DATE_TRUNC('week', CURRENT_DATE) + INTERVAL '1 week'
) AS window_end
FROM provided_dates pd
),
billing AS (
SELECT
Expand All @@ -18,7 +33,7 @@ billing AS (
LEFT JOIN projects.projects proj
ON proj.id::text = NULLIF(TRIM(c."projectId"::text), '')
LEFT JOIN "billing-accounts"."BillingAccount" project_ba
ON project_ba.id = proj."billingAccountId"
ON project_ba.id::text = NULLIF(TRIM(proj."billingAccountId"::text), '')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ correctness]
Using NULLIF(TRIM(proj."billingAccountId"::text), '') might lead to unexpected behavior if proj."billingAccountId" is NULL. Consider handling NULL values explicitly.

GROUP BY c.id
),
project_clients AS (
Expand Down
3 changes: 1 addition & 2 deletions src/auth/auth.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ function decodeTokenPayload(token: string): Record<string, unknown> | null {
return null;
}
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
const padded =
payload + "=".repeat((4 - (payload.length % 4)) % 4);
const padded = payload + "=".repeat((4 - (payload.length % 4)) % 4);
const decoded = Buffer.from(padded, "base64").toString("utf8");
return JSON.parse(decoded);
} catch {
Expand Down
4 changes: 3 additions & 1 deletion src/common/validation.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ export function transformArray({ value }: { value: unknown }) {
return [v];
};

return Array.isArray(value) ? value.flatMap(splitIfString) : splitIfString(value);
return Array.isArray(value)
? value.flatMap(splitIfString)
: splitIfString(value);
}
3 changes: 2 additions & 1 deletion src/reports/report-directory.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,8 @@ export const REPORTS_DIRECTORY: ReportsDirectory = {
report(
"Weekly Member Participation",
"/topcoder/weekly-member-participation",
"Weekly distinct registrants and submitters for the last five weeks",
"Weekly distinct registrants and submitters for the provided date range (defaults to last five weeks)",
[paymentsStartDateParam, paymentsEndDateParam],
),
report(
"Member Payment Accrual",
Expand Down
6 changes: 1 addition & 5 deletions src/reports/sfdc/sfdc-reports.dto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -732,11 +732,7 @@ describe("TaasResourceBookingsReportQueryDto validation", () => {
billingAccountIds: "80001012,80002012 , 80003012",
});
expect(errors).toHaveLength(0);
expect(dto.billingAccountIds).toEqual([
"80001012",
"80002012",
"80003012",
]);
expect(dto.billingAccountIds).toEqual(["80001012", "80002012", "80003012"]);
});

it("accepts ISO date strings for startDate and endDate", async () => {
Expand Down
22 changes: 22 additions & 0 deletions src/reports/topcoder/dto/weekly-member-participation.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ApiPropertyOptional } from "@nestjs/swagger";
import { IsDateString, IsOptional } from "class-validator";

export class WeeklyMemberParticipationQueryDto {
@ApiPropertyOptional({
description:
"Start date (inclusive) for filtering challenge posting date in ISO 8601 format",
example: "2024-01-01T00:00:00.000Z",
})
@IsOptional()
@IsDateString()
startDate?: string;

@ApiPropertyOptional({
description:
"End date (exclusive) for filtering challenge posting date in ISO 8601 format",
example: "2024-02-01T00:00:00.000Z",
})
@IsOptional()
@IsDateString()
endDate?: string;
}
10 changes: 7 additions & 3 deletions src/reports/topcoder/topcoder-reports.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { TopcoderReportsService } from "./topcoder-reports.service";
import { RegistrantCountriesQueryDto } from "./dto/registrant-countries.dto";
import { MemberPaymentAccrualQueryDto } from "./dto/member-payment-accrual.dto";
import { RecentMemberDataQueryDto } from "./dto/recent-member-data.dto";
import { WeeklyMemberParticipationQueryDto } from "./dto/weekly-member-participation.dto";
import { TopcoderReportsGuard } from "../../auth/guards/topcoder-reports.guard";
import { CsvResponseInterceptor } from "../../common/interceptors/csv-response.interceptor";

Expand Down Expand Up @@ -63,10 +64,13 @@ export class TopcoderReportsController {
@Get("/weekly-member-participation")
@ApiOperation({
summary:
"Weekly distinct registrants and submitters for the last five weeks",
"Weekly distinct registrants and submitters for the provided date range (defaults to last five weeks)",
})
getWeeklyMemberParticipation() {
return this.reports.getWeeklyMemberParticipation();
getWeeklyMemberParticipation(
@Query() query: WeeklyMemberParticipationQueryDto,
) {
const { startDate, endDate } = query;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ correctness]
Consider validating startDate and endDate to ensure they are valid dates and that startDate is not after endDate. This will prevent potential errors or unexpected behavior in the report generation.

return this.reports.getWeeklyMemberParticipation(startDate, endDate);
}

@Get("/member-payment-accrual")
Expand Down
9 changes: 3 additions & 6 deletions src/reports/topcoder/topcoder-reports.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,15 +448,15 @@ export class TopcoderReportsService {
}));
}

async getWeeklyMemberParticipation() {
async getWeeklyMemberParticipation(startDate?: string, endDate?: string) {
const query = this.sql.load(
"reports/topcoder/weekly-member-participation.sql",
);
const rows = await this.db.query<{
"challenge_stats.posting_date_week": string;
"challenge_stats.count_distinct_registrant": string | number;
"challenge_stats.count_distinct_submitter": string | number;
}>(query);
}>(query, [startDate ?? null, endDate ?? null]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ correctness]
The use of startDate ?? null and endDate ?? null as query parameters is potentially problematic if the SQL query expects specific date formats. Ensure that the SQL query and database can handle null values appropriately, or consider validating and formatting the dates before passing them to the query.


return rows.map((row) => ({
"challenge_stats.posting_date_week":
Expand All @@ -470,10 +470,7 @@ export class TopcoderReportsService {
}));
}

async getMemberPaymentAccrual(
startDate?: string,
endDate?: string,
) {
async getMemberPaymentAccrual(startDate?: string, endDate?: string) {
const query = this.sql.load("reports/topcoder/member-payment-accrual.sql");
const rows = await this.db.query<MemberPaymentAccrualRow>(query, [
startDate ?? null,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ correctness]
Similar to the previous method, ensure that startDate ?? null and endDate ?? null are compatible with the SQL query and database expectations. If the query cannot handle null values, this could lead to unexpected results or errors.

Expand Down
3 changes: 2 additions & 1 deletion src/reports/topgear/topgear-reports.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export class TopgearReportsService {
}

async getTopgearHandles(opts: { startDate?: string }) {
const startDate = parseOptionalDate(opts.startDate) ?? subDays(new Date(), 90);
const startDate =
parseOptionalDate(opts.startDate) ?? subDays(new Date(), 90);
const query = this.sql.load("reports/topgear/topgear-handles.sql");
return this.db.query(query, [startDate.toISOString()]);
}
Expand Down
Loading