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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"license": "MIT",
"scripts": {
"lint": "eslint src --ext ts,tsx",
"dev:server": "ts-node-dev -r tsconfig-paths/register --inspect --transpile-only --ignore-watch node_modules src/shared/infra/http/server.ts",
"dev:server": "cross-env ts-node-dev -r dotenv/config -r tsconfig-paths/register --inspect --transpile-only --ignore-watch node_modules src/shared/infra/http/server.ts",
"build": "babel src --extensions \".js,.ts\" --out-dir dist --copy-files",
"docs": "npx insomnia-documenter --output docs"
},
Expand All @@ -33,6 +33,7 @@
"@typescript-eslint/parser": "^4.15.1",
"babel-plugin-module-resolver": "^5.0.0",
"babel-plugin-transform-typescript-metadata": "^0.3.1",
"cross-env": "^7.0.3",
"eslint": "^7.20.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-import-resolver-typescript": "^2.4.0",
Expand Down
3 changes: 1 addition & 2 deletions src/config/upload.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* eslint-disable no-nested-ternary */
/* eslint-disable no-param-reassign */
import { S3 } from '@aws-sdk/client-s3';
import multer, { FileFilterCallback } from 'multer';
import path from 'path';
Expand All @@ -12,6 +10,7 @@ const storageTypes = (type: 'audio' | 'user' | 'music') => ({
local: multer.diskStorage({
destination: tmpFolder,
filename(req, file, cb) {
// eslint-disable-next-line no-param-reassign
file.key = `${Date.now()}-${file.originalname.replace(/\s/g, '_')}`;

return cb(null, file.key);
Expand Down
16 changes: 16 additions & 0 deletions src/modules/events/dtos/IEventsGoogleDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { calendar_v3 } from 'googleapis';

export interface ICustomEventDTO extends calendar_v3.Schema$Event {
accepted?: calendar_v3.Schema$Event[]
declined?: calendar_v3.Schema$Event[]
tentative?:calendar_v3.Schema$Event[]
needsAction?: calendar_v3.Schema$Event[]
}
export interface IEventWithAttendeesStatus {
id?: string;
summary?: string;
accepted: string[];
declined: string[];
tentative: string[];
needsAction: string[];
}
31 changes: 24 additions & 7 deletions src/modules/invites/infra/http/controller/InvitesController.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-console */
import { Request, Response } from 'express';
import { container } from 'tsyringe';

Expand All @@ -9,6 +10,7 @@ import ListEventsByWeekService from '@modules/invites/services/ListEventsByWeekS
import UpdateInviteService from '@modules/invites/services/UpdateInviteService';
import OutlookUpdateInviteState from '@modules/invites/services/OutlookUpdateInviteStateService';
import UpdateInviteStateService from '@modules/invites/services/UpdateInviteStateService';
import AppError from '@shared/errors/AppError';

export default class InviteController {
// public async create(req: Request, res: Response): Promise<Response> {
Expand Down Expand Up @@ -43,17 +45,32 @@ export default class InviteController {
public async listEventsByUser(req: Request, res: Response): Promise<Response> {
const list = container.resolve(ListEventsService);
const { email } = req.body;
const invites = await list.execute(email);

return res.status(201).json(invites);
const events = await list.getEventsUser(email);
console.log(`InvitesController 50: Events${JSON.stringify(events)}`);
return res.status(201).json(events);
}

public async listInvitesByUser(req: Request, res: Response): Promise<Response> {
const list = container.resolve(ListInvitesService);
console.log('InvitesController → req.body:', req.body);
console.log('InvitesController → Content-Type:', req.headers['content-type']);
const { email } = req.body;
const invites = await list.execute(email);

return res.status(201).json(invites);
if (!email) {
return res.status(400).json({ message: 'É preciso enviar um email no corpo da requisição.' });
}

try {
const invites = await container
.resolve(ListInvitesService)
.getInvites(email);

return res.status(200).json(invites);
} catch (err) {
if (err instanceof AppError) {
return res.status(err.statusCode).json({ message: err.message });
}
console.error(err);
return res.status(500).json({ message: 'Erro interno no servidor.' });
}
}

public async listEventsInAWeekByUser(req: Request, res: Response): Promise<Response> {
Expand Down
164 changes: 146 additions & 18 deletions src/modules/invites/infra/prisma/repositories/InvitesRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import ICreateInviteDTO from '@modules/invites/dtos/ICreateInviteDTO';

interface IInviteWithConfirmation {
element: Invite; // Replace 'YourElementType' with the actual type of 'element'
yes: {amount: number, ateendees:User[], pseudoAttendes : PseudoUser[]};
no: {amount: number, ateendees:User[], pseudoAttendes : PseudoUser[]};
maybe: {amount: number, ateendees:User[], pseudoAttendes : PseudoUser[]};
yes: { amount: number, ateendees: User[], pseudoAttendes: PseudoUser[] };
no: { amount: number, ateendees: User[], pseudoAttendes: PseudoUser[] };
maybe: { amount: number, ateendees: User[], pseudoAttendes: PseudoUser[] };
}
export default class InvitesRepository implements IInvitesRepository {
private ormRepository: Prisma.InviteDelegate<Prisma.RejectOnNotFound | Prisma.RejectPerOperation | undefined>
Expand Down Expand Up @@ -43,19 +43,19 @@ export default class InvitesRepository implements IInvitesRepository {
description,
guests: {
create:
guests.map((guest) => ({
Status: 'needsAction',
optional: false,
User: { connect: { email: guest } },
})),
guests.map((guest) => ({
Status: 'needsAction',
optional: false,
User: { connect: { email: guest } },
})),
},
pseudoGuests: {
create:
pseudoGuests.map((pseudoGuest) => ({
Status: 'pseudoUser',
optional: false,
pseudoUser: { connect: { id: pseudoGuest } },
})),
pseudoGuests.map((pseudoGuest) => ({
Status: 'pseudoUser',
optional: false,
pseudoUser: { connect: { id: pseudoGuest } },
})),

},
address,
Expand Down Expand Up @@ -184,7 +184,7 @@ export default class InvitesRepository implements IInvitesRepository {
inviteId: element.id,
},
});
const yesAteendees1:User[] = [];
const yesAteendees1: User[] = [];
yesAttendees.map(async (ateendee) => {
const y = await this.ormRepository2.findUnique({
where: {
Expand All @@ -207,7 +207,7 @@ export default class InvitesRepository implements IInvitesRepository {
inviteId: element.id,
},
});
const noAteendees1:User[] = [];
const noAteendees1: User[] = [];
noAttendees.map(async (ateendee) => {
const n = await this.ormRepository2.findUnique({
where: {
Expand Down Expand Up @@ -272,7 +272,7 @@ export default class InvitesRepository implements IInvitesRepository {
return invitedWithConfirmation;
}

public async UpdatedInviteStatusById(id: string, state:string, email:string): Promise<Invite|null> {
public async UpdatedInviteStatusById(id: string, state: string, email: string): Promise<Invite | null> {
const invit = await this.ormRepository.update({
where: {
id,
Expand All @@ -293,7 +293,7 @@ export default class InvitesRepository implements IInvitesRepository {
return invit;
}

public async UpdatedInviteById(eventId:string, begin:string, end:string, phone:string): Promise<Invite|null> {
public async UpdatedInviteById(eventId: string, begin: string, end: string, phone: string): Promise<Invite | null> {
const inviteUser = await this.ormRepository.findFirst({
where: {
id: eventId,
Expand All @@ -317,7 +317,7 @@ export default class InvitesRepository implements IInvitesRepository {
return invit;
}

public async listEventsInAWeekByUser(phone: string, beginWeek:string, endWeek:string): Promise<Invite[]> {
public async listEventsInAWeekByUser(phone: string, beginWeek: string, endWeek: string): Promise<Invite[]> {
const events = await this.ormRepository.findMany({
where: {
phone, state: 'accepted', begin: { gte: beginWeek }, end: { lte: endWeek },
Expand Down Expand Up @@ -414,4 +414,132 @@ export default class InvitesRepository implements IInvitesRepository {

return invite;
}
private async filterNewInvites(invites: ICreateInviteDTO[]): Promise<ICreateInviteDTO[]> {
const validInvites = invites.filter((i) => i && typeof i.googleId === 'string' && i.googleId.trim() !== '');

const googleIds = invites
.map((i) => i?.googleId)
.filter((id): id is string => !!id);

const createdInvites = await this.ormRepository.findMany({
where: {
googleId: { in: googleIds },
},
});

if (validInvites.length < invites.length) {
console.warn(
'⚠️ Alguns invites estavam inválidos e foram ignorados:',
invites.filter(i => !i || !i.googleId)
);
}
const existingIds = new Set(createdInvites.map(i => i.googleId));
return validInvites.filter(i => !existingIds.has(i.googleId));
}

private async createInvitesWithoutRelations(invites: ICreateInviteDTO[]): Promise<Invite[]> {
const baseData = invites.map((invite) => ({
name: invite.name,
begin: invite.begin,
end: invite.end,
beginSearch: invite.beginSearch,
endSearch: invite.endSearch,
phone: invite.phone,
description: invite.description,
address: invite.address,
link: invite.link,
state: invite.state,
googleId: invite.googleId,
organizerPhoto: invite.organizerPhoto,
organizerName: invite.organizerName,
}));

await this.ormRepository.createMany({ data: baseData });

return this.ormRepository.findMany({
where: {
googleId: { in: invites.map((i) => i.googleId) },
},
});
}
private async prepareRelations(invites: ICreateInviteDTO[], createdInvites: Invite[]) {
const inviteUserData: Prisma.InviteUserCreateManyInput[] = [];
const pseudoInviteUserData: Prisma.PseudoInviteUserCreateManyInput[] = [];

for (const invite of invites) {
const created = createdInvites.find(i => i.googleId === invite.googleId);
if (!created) continue;

invite.guests.forEach(email => {
inviteUserData.push({
inviteId: created.id,
userEmail: email,
optional: false,
Status: 'needsAction',
});
});

invite.optionalGuests.forEach(email => {
inviteUserData.push({
inviteId: created.id,
userEmail: email,
optional: true,
Status: 'needsAction',
});
});

invite.pseudoGuests.forEach(id => {
pseudoInviteUserData.push({
inviteId: created.id,
pseudoUserId: id,
optional: false,
Status: 'pseudoUser',
});
});

invite.pseudoOptionalGuests.forEach(id => {
pseudoInviteUserData.push({
inviteId: created.id,
pseudoUserId: id,
optional: true,
Status: 'pseudoUser',
});
});

const organizer = await this.ormRepository2.findUnique({ where: { phone: invite.phone } });
if (organizer?.email) {
inviteUserData.push({
inviteId: created.id,
userEmail: organizer.email,
optional: false,
Status: 'accepted',
});
}
}

return { inviteUserData, pseudoInviteUserData };
}
private async createRelations(
inviteUserData: Prisma.InviteUserCreateManyInput[],
pseudoInviteUserData: Prisma.PseudoInviteUserCreateManyInput[]
) {
if (inviteUserData.length) {
await this.ormRepository3.createMany({ data: inviteUserData, skipDuplicates: true });
}

if (pseudoInviteUserData.length) {
await this.ormRepository4.createMany({ data: pseudoInviteUserData, skipDuplicates: true });
}
}
public async createMany(invites: ICreateInviteDTO[]): Promise<Invite[]> {
const newInvites = await this.filterNewInvites(invites);
if (!newInvites.length) return [];

const createdInvites = await this.createInvitesWithoutRelations(newInvites);
const { inviteUserData, pseudoInviteUserData } = await this.prepareRelations(newInvites, createdInvites);
await this.createRelations(inviteUserData, pseudoInviteUserData);

return createdInvites;
}

}
2 changes: 2 additions & 0 deletions src/modules/invites/repositories/IInvitesRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ interface IInvitesRepository {
connect(user: User, invite: Invite): Promise<InviteUser>
findInvitesByOrganizerName(eventId:string): Promise<(Invite & { pseudoGuests: PseudoInviteUser[] })[] | null>;
delete(id:string): Promise<Invite|null>;
createMany(data: ICreateInviteDTO[]): Promise<Invite[]>;

}

export default IInvitesRepository;
12 changes: 11 additions & 1 deletion src/modules/invites/services/CheckUserAvailabilityService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,17 @@ export default class CheckUserAvailabilityService {
auth: {
clientId: process.env.OUTLOOK_CLIENT_ID as string,
clientSecret: process.env.OUTLOOK_CLIENT_SECRET,
authority: 'https://login.microsoftonline.com/common',
},
system: {
loggerOptions: {
loggerCallback(loglevel: any, message: any, containsPii: any) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: 3,
}
}
};

const cca = new msal.ConfidentialClientApplication(clientConfig);
Expand Down Expand Up @@ -69,7 +79,7 @@ export default class CheckUserAvailabilityService {
return true;
}

const oAuth2Client = new google.auth.OAuth2(process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, process.env.GOOGLE_CLIENT_URI);
const oAuth2Client = new google.auth.OAuth2(process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, `${process.env.GOOGLE_CLIENT_URI}`);

oAuth2Client.setCredentials({ access_token: user.tokens });

Expand Down
Loading
Loading