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
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Db, ObjectId } from "mongodb";

const HEX24 = /^[0-9a-fA-F]{24}$/;

export async function up(db: Db) {
try {
const historiesToChange = await db
.collection("histories")
.find({ user: { $type: "string", $regex: HEX24 } })
.toArray();

if (historiesToChange.length === 0) {
console.log("No histories with string users to change.");
} else {
const bulkOps = historiesToChange.map((history) => ({
updateOne: {
filter: { _id: history._id },
update: {
$set: {
user: new ObjectId(history.user as string),
migration_revert_flag: true,
},
},
},
}));

const result = await db.collection("histories").bulkWrite(bulkOps);
console.log(`Converted ${result.modifiedCount} history.user fields to ObjectId.`);
}
} catch (error) {
console.error("Migration UP failed:", error);
throw error;
}
}

export async function down(db: Db) {
try {
const historiesToRedefine = await db
.collection("histories")
.find({ migration_revert_flag: true })
.toArray();

if (historiesToRedefine.length === 0) return;

const bulkOps = historiesToRedefine.map((history) => {
const userIdString = history.user ? String(history.user) : null;

return {
updateOne: {
filter: { _id: history._id },
update: { $set: { user: userIdString } },
$unset: { migration_revert_flag: "" },
},
};
})

const result = await db.collection("histories").bulkWrite(bulkOps);
console.log(`Reverted ${result.modifiedCount} fields back to strings.`);

} catch (error) {
console.error("Migration DOWN failed:", error);
throw error;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Db } from "mongodb";

export async function up(db: Db) {
const historyCollection = db.collection("histories");

const historiesFound = historyCollection.find({
user: { $type: "string", $regex: "-" },
});

let count = 0;

while (await historiesFound.hasNext()) {
const history = await historiesFound.next();
if (!history) continue;
const oldUserValue = history.user as string;

const newUser = {
isM2M: true,
clientId: oldUserValue,
subject: "chatbot-service",
scopes: ["read", "write"],
role: { main: "integration" },
namespace: "main",
};

await historyCollection.updateOne(
{ _id: history._id },
{ $set: { user: newUser } }
);

count++;
}

console.log(`Migration complete. Updated ${count} documents.`);
}

export async function down(db: Db) {
const historyCollection = db.collection("histories");

const historiesFound = historyCollection.find({
"user.isM2M": true,
"user.subject": "chatbot-service",
"user.clientId": { $exists: true }
});

let count = 0;

while (await historiesFound.hasNext()) {
const history = await historiesFound.next();
if (!history) continue;
const clientId = history.user.clientId;

await historyCollection.updateOne(
{ _id: history._id },
{ $set: { user: clientId } }
);

count++;
}

console.log(`Rollback complete. Reverted ${count} documents.`);
}
4 changes: 3 additions & 1 deletion public/locales/en/history.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"anonymousUserName": "Anonymous",
"automatedMonitoring": "Automated Monitoring",
"virtualAssistant": "Virtual Assistant",
"create": "created",
"update": "updated",
"delete": "deleted",
Expand All @@ -8,4 +10,4 @@
"personality": "profile",
"claim": "claim",
"historyItem": "{{username}} {{type}} {{title}} {{targetModel}}"
}
}
4 changes: 3 additions & 1 deletion public/locales/pt/history.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"anonymousUserName": "Anônimo",
"automatedMonitoring": "Monitoramento Automatizado",
"virtualAssistant": "Assistente Virtual",
"create": "criou",
"update": "atualizou",
"delete": "deletou",
Expand All @@ -8,4 +10,4 @@
"personality": "o perfil de",
"claim": "a afirmação",
"historyItem": "{{username}} {{type}} {{targetModel}} {{title}}"
}
}
4 changes: 2 additions & 2 deletions server/chat-bot/chat-bot.machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const createChatBotMachine = (
verificationRequestStateMachineService: VerificationRequestStateMachineService,
value?,
context?,
chatbotStateId?
M2MUser?,
) => {
const chatBotMachine = createMachine<ChatBotContext>(
{
Expand Down Expand Up @@ -199,7 +199,7 @@ export const createChatBotMachine = (

verificationRequestStateMachineService.request(
verificationRequestBody,
chatbotStateId
M2MUser
);
},
},
Expand Down
17 changes: 16 additions & 1 deletion server/chat-bot/chat-bot.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { createChatBotMachine } from "./chat-bot.machine";
import { ConfigService } from "@nestjs/config";
import { ChatBotStateService } from "../chat-bot-state/chat-bot-state.service";
import { VerificationRequestStateMachineService } from "../verification-request/state-machine/verification-request.state-machine.service";
import { Roles } from "../auth/ability/ability.factory";
import { M2M } from "../entities/m2m.entity";

const diacriticsRegex = /[\u0300-\u036f]/g;
const MESSAGE_MAP = {
Expand All @@ -22,6 +24,19 @@ interface ChatBotContext {
sourceChannel?: string;
}

function M2MUser(clientId): M2M {
return {
isM2M: true,
clientId,
subject: "chatbot-service",
scopes: ["read", "write"],
role: {
main: Roles.Integration,
},
namespace: "main",
};
}

@Injectable({ scope: Scope.REQUEST })
export class ChatbotService {
constructor(
Expand Down Expand Up @@ -99,7 +114,7 @@ export class ChatbotService {
...chatbotState.machine.context,
sourceChannel: channel,
},
chatbotState._id
M2MUser(chatbotState._id)
);

chatBotMachineService.start(chatbotState.machine.value);
Expand Down
2 changes: 1 addition & 1 deletion server/claim-review/claim-review.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ export class ClaimReviewService {
const history = this.historyService.getHistoryParams(
newReview._id,
TargetModel.ClaimReview,
this.req?.user,
this.req.user?._id,
hide ? HistoryType.Hide : HistoryType.Unhide,
after,
before
Expand Down
8 changes: 4 additions & 4 deletions server/claim/claim.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export class ClaimService {
newClaim.latestRevision = newClaimRevision._id;
newClaim.slug = newClaimRevision.slug;

const user = this.req.user;
const user = this.req.user?._id;

const history = this.historyService.getHistoryParams(
newClaim._id,
Expand Down Expand Up @@ -193,7 +193,7 @@ export class ClaimService {
claim.latestRevision = newClaimRevision._id;
claim.slug = newClaimRevision.slug;

const user = this.req.user;
const user = this.req.user?._id;

const history = this.historyService.getHistoryParams(
claimId,
Expand All @@ -218,7 +218,7 @@ export class ClaimService {
* @returns Returns the claim with the param isDeleted equal to true
*/
async delete(claimId) {
const user = this.req.user;
const user = this.req.user?._id;
const previousClaim = await this.getById(claimId);
const history = this.historyService.getHistoryParams(
claimId,
Expand Down Expand Up @@ -251,7 +251,7 @@ export class ClaimService {
const history = this.historyService.getHistoryParams(
newClaim._id,
TargetModel.Claim,
this.req?.user,
this.req.user?._id,
isHidden ? HistoryType.Hide : HistoryType.Unhide,
after,
before
Expand Down
2 changes: 1 addition & 1 deletion server/claim/types/debate/debate.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class DebateService {
const history = this.historyService.getHistoryParams(
newDebate._id,
TargetModel.Debate,
this.req.user,
this.req.user?._id,
HistoryType.Update,
newDebate,
previousDebate
Expand Down
2 changes: 1 addition & 1 deletion server/claim/types/image/image.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class ImageService {
const history = this.historyService.getHistoryParams(
newImage._id,
TargetModel.Image,
this.req.user,
this.req.user?._id,
HistoryType.Create,
newImage
);
Expand Down
14 changes: 9 additions & 5 deletions server/entities/m2m.entity.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { Roles } from "../auth/ability/ability.factory";
export class M2M {
isM2M: boolean;
role: {
main: string;
};
scopes: string[];
isM2M: boolean;
clientId: string;
subject: string;
scopes: string[];
role: {
main: Roles.Integration;
};
namespace: string;
}
56 changes: 56 additions & 0 deletions server/history/history.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Test } from "@nestjs/testing";
import { HistoryController } from "./history.controller";
import { HistoryService } from "./history.service";
import { historyServiceMock, mockHistoryItem } from "../mocks/HistoryMock";
import { TargetModel } from "./schema/history.schema";

describe("HistoryController (Unit)", () => {
let controller: HistoryController;
let historyService: typeof historyServiceMock;

beforeEach(async () => {
const testingModule = await Test.createTestingModule({
controllers: [HistoryController],
providers: [{ provide: HistoryService, useValue: historyServiceMock }],
}).compile();

controller = testingModule.get(HistoryController);
historyService = testingModule.get(HistoryService);
});

beforeEach(() => {
jest.clearAllMocks();
});

describe("getHistory", () => {
it("should return history correctly (happy path)", async () => {
historyService.getHistoryForTarget.mockResolvedValue({
history: [mockHistoryItem],
totalChanges: 1,
totalPages: 1,
page: 1,
pageSize: 10,
});

const response = await controller.getHistory(
{ targetId: "id", targetModel: TargetModel.Claim },
{}
);
expect(response.history.length).toBeGreaterThan(0);
expect(response.totalChanges).toBeGreaterThanOrEqual(1);
});

it("should throw error if targetId is empty", async () => {
await expect(
controller.getHistory({ targetId: "", targetModel: TargetModel.Claim }, {})
).rejects.toThrow();
});

it("should throw error if service fails", async () => {
historyService.getHistoryForTarget.mockRejectedValue(new Error("fail"));
await expect(
controller.getHistory({ targetId: "id", targetModel: TargetModel.Claim }, {})
).rejects.toThrow("fail");
});
});
});
Loading
Loading