Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/fraud 3.1 #2681

Merged
merged 6 commits into from
Nov 15, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class CarpoolStatusService {
async findByOperatorJourneyId(
operator_id: number,
operator_journey_id: string,
api_version: string,
): Promise<
{
created_at: Date;
Expand All @@ -38,6 +39,7 @@ export class CarpoolStatusService {
operator_journey_id,
);
const fraud = await this.labelRepository.findFraudByOperatorJourneyId(
api_version,
operator_id,
operator_journey_id,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,65 +10,98 @@ describe("Carpool Label Repository", () => {
let labelRepository: CarpoolLabelRepository;
let db: DbContext;
let carpool_id: number;
const label = "test";
const another_label = "another_test_label";

// anomaly
const anomaly_label_1 = "temporal_overlap_anomaly";

// terms
const terms_label_1 = "distance_too_short";

// frauds
const fraud_label_1_V3 = "interoperator_fraud";

const fraud_label_1_V3_1 = "interoperator_overlap_trip";
const fraud_label_2_V3_1 = "interoperator_too_many_trips_by_day";

const { before, after } = makeDbBeforeAfter();

beforeAll(async () => {
db = await before();
repository = new CarpoolRepository(db.connection);
labelRepository = new CarpoolLabelRepository(db.connection);
const data = await repository.register(insertableCarpool);
carpool_id = data._id;
const insertTermLabels = async (carpool_id: number) => {
await db.connection.getClient().query(
sql`INSERT INTO ${raw(labelRepository.anomalyTable)} (carpool_id, label) VALUES (${carpool_id}, ${label})`,
sql`INSERT INTO ${raw(labelRepository.termsTable)} (carpool_id, labels) VALUES (${carpool_id}, ${[
terms_label_1,
]})`,
);
};

const insertFraudLabels = async (carpool_id: number) => {
await db.connection.getClient().query(
sql`INSERT INTO ${raw(labelRepository.fraudTable)} (carpool_id, label) VALUES (${carpool_id}, ${label})`,
sql`INSERT INTO ${
raw(labelRepository.fraudTable)
} (carpool_id, label) VALUES (${carpool_id}, ${fraud_label_1_V3_1})`,
);
await db.connection.getClient().query(
sql`INSERT INTO ${raw(labelRepository.fraudTable)} (carpool_id, label) VALUES (${carpool_id},${another_label})`,
sql`INSERT INTO ${
raw(labelRepository.fraudTable)
} (carpool_id, label) VALUES (${carpool_id},${fraud_label_2_V3_1})`,
);
};

const insertAnomalyLabel = async (carpool_id: number) => {
await db.connection.getClient().query(
sql`INSERT INTO ${raw(labelRepository.termsTable)} (carpool_id, labels) VALUES (${carpool_id}, ${[label]})`,
sql`INSERT INTO ${
raw(labelRepository.anomalyTable)
} (carpool_id, label) VALUES (${carpool_id}, ${anomaly_label_1})`,
);
};

beforeAll(async () => {
db = await before();
repository = new CarpoolRepository(db.connection);
labelRepository = new CarpoolLabelRepository(db.connection);
const { _id: carpool_id } = await repository.register(insertableCarpool);
await insertAnomalyLabel(carpool_id);
await insertFraudLabels(carpool_id);
await insertTermLabels(carpool_id);
});

afterAll(async () => {
await after(db);
});

// anomaly
it("Should read carpool anomaly label", async () => {
const result = await labelRepository.findAnomalyByOperatorJourneyId(
insertableCarpool.operator_id,
insertableCarpool.operator_journey_id,
);
assertEquals(result, [{
label,
label: anomaly_label_1,
metas: {
conflicting_operator_journey_id: null,
overlap_duration_ratio: null,
},
}]);
});

it("Should read carpool fraud label and return single entry with 2 labels", async () => {
const result = await labelRepository.findFraudByOperatorJourneyId(
// terms
it("Should read carpool terms label", async () => {
const result = await labelRepository.findTermsByOperatorJourneyId(
insertableCarpool.operator_id,
insertableCarpool.operator_journey_id,
);
assertEquals(result, [{
label: "interoperator_fraud",
label: terms_label_1,
}]);
});

it("Should read carpool fraud label and return empty array if none", async () => {
// fraud V3
it("Should read carpool fraud label and returns empty array if none for api v3", async () => {
// Arrange
await repository.register({ ...insertableCarpool, operator_journey_id: "operator_journey_id-4" });

// Act
const result = await labelRepository.findFraudByOperatorJourneyId(
"v3",
insertableCarpool.operator_id,
"operator_journey_id-4",
);
Expand All @@ -77,13 +110,42 @@ describe("Carpool Label Repository", () => {
assertEquals(result, []);
});

it("Should read carpool terms label", async () => {
const result = await labelRepository.findTermsByOperatorJourneyId(
it("Should read carpool fraud label and returns single entry with 2 labels for api v3", async () => {
const result = await labelRepository.findFraudByOperatorJourneyId(
"v3.0",
insertableCarpool.operator_id,
insertableCarpool.operator_journey_id,
);
assertEquals(result, [{
label,
label: fraud_label_1_V3,
}]);
});

// fraud V3.1
it("Should read carpool fraud label and returns 2 labels for api v3.1", async () => {
const result = await labelRepository.findFraudByOperatorJourneyId(
"v3.1",
insertableCarpool.operator_id,
insertableCarpool.operator_journey_id,
);
assertEquals(result, [
{ label: fraud_label_1_V3_1 },
{ label: fraud_label_2_V3_1 },
]);
});

it("Should read carpool fraud label and returns empty array if none for api v3", async () => {
// Arrange
await repository.register({ ...insertableCarpool, operator_journey_id: "operator_journey_id-4" });

// Act
const result = await labelRepository.findFraudByOperatorJourneyId(
"v3.1",
insertableCarpool.operator_id,
"operator_journey_id-4",
);

// Assert
assertEquals(result, []);
});
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { semver } from "@/deps.ts";
import { provider } from "@/ilos/common/index.ts";
import { PoolClient, PostgresConnection } from "@/ilos/connection-postgres/index.ts";
import sql, { raw } from "@/lib/pg/sql.ts";
Expand Down Expand Up @@ -32,6 +33,7 @@ export class CarpoolLabelRepository {
}

async findFraudByOperatorJourneyId(
api_version: string,
operator_id: number,
operator_journey_id: string,
client?: PoolClient,
Expand All @@ -50,7 +52,12 @@ export class CarpoolLabelRepository {
if (result.rowCount == 0) {
return [];
}
return [{ label: "interoperator_fraud" }];
// = "3.0.0" or "3.0" or "3"
if (semver.satisfies(semver.parse("3.0.0"), semver.parseRange(api_version))) {
return [{ label: "interoperator_fraud" }];
}
// >= "3.1" or "3.1.0"
return result.rows.map(({ label }) => ({ label: label }));
}

async findAnomalyByOperatorJourneyId(
Expand Down
2 changes: 1 addition & 1 deletion api/src/pdc/proxy/HttpTransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ export class HttpTransport implements TransportInterface {
limit: 20_000,
windowMinute: 1,
},
rpcAnswerOnSuccess: false,
rpcAnswerOnSuccess: true,
rpcAnswerOnFailure: true,
});

Expand Down
11 changes: 4 additions & 7 deletions api/src/pdc/services/acquisition/actions/StatusJourneyAction.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { handler, NotFoundException } from "@/ilos/common/index.ts";
import { ContextType, handler, NotFoundException } from "@/ilos/common/index.ts";
import { Action as AbstractAction } from "@/ilos/core/index.ts";
import { copyGroupIdAndApplyGroupPermissionMiddlewares } from "@/pdc/providers/middleware/index.ts";

import { castToStatusEnum } from "@/pdc/providers/carpool/helpers/castStatus.ts";
import { CarpoolStatusService } from "@/pdc/providers/carpool/providers/CarpoolStatusService.ts";
import {
handlerConfig,
ParamsInterface,
ResultInterface,
} from "@/shared/acquisition/status.contract.ts";
import { handlerConfig, ParamsInterface, ResultInterface } from "@/shared/acquisition/status.contract.ts";
import { alias } from "@/shared/acquisition/status.schema.ts";

@handler({
Expand All @@ -27,11 +23,12 @@ export class StatusJourneyAction extends AbstractAction {
super();
}

protected async handle(params: ParamsInterface): Promise<ResultInterface> {
protected async handle(params: ParamsInterface, context: ContextType): Promise<ResultInterface> {
const { operator_journey_id, operator_id } = params;
const result = await this.statusService.findByOperatorJourneyId(
operator_id,
operator_journey_id,
context.call?.api_version_range || "3.1",
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider centralizing API version configuration

The hardcoded version string "3.1" should be moved to a central configuration. This would make version management more maintainable and reduce the risk of inconsistencies.

Consider creating a shared constants file:

// src/shared/constants/api.ts
export const API_VERSIONS = {
  DEFAULT: '3.1',
  V3: 'v3',
  V3_1: 'v3.1',
} as const;

Then update the code:

- context.call?.api_version_range || "3.1",
+ context.call?.api_version_range || API_VERSIONS.DEFAULT,

if (!result) {
throw new NotFoundException();
Expand Down
4 changes: 2 additions & 2 deletions api/src/shared/acquisition/status.contract.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export interface ParamsInterface {
operator_journey_id: string;
operator_id: number;
api_version: "v3" | "v3.1";
}

export interface AnomalyErrorDetails {
Expand Down Expand Up @@ -38,5 +39,4 @@ export const handlerConfig = {
method: "status",
};

export const signature =
`${handlerConfig.service}:${handlerConfig.method}` as const;
export const signature = `${handlerConfig.service}:${handlerConfig.method}` as const;
Loading