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
1 change: 1 addition & 0 deletions sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ sonar.organization=trycatch-formatch

# Encoding of the source code. Default is default system encoding
#sonar.sourceEncoding=UTF-8
sonar.javascript.lcov.reportPaths=coverage/lcov.info
173 changes: 130 additions & 43 deletions src/app/api/team-project/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { prisma } from '@/lib/prisma';
import { ProjectStatus } from '@prisma/client';
import type { Prisma } from '@prisma/client';
import { NextRequest, NextResponse } from 'next/server';
import { checkAuth } from '@/lib/check-auth';
import { z } from 'zod';
Expand Down Expand Up @@ -32,6 +33,103 @@ const updateProjectSchema = z.object({
.optional(),
});

type StructuralProjectState = {
name: string;
deadline: Date;
totalValue: number;
skills: { skillId: string }[];
stacks: { stackId: string; percentage: number }[];
};

type IncomingProjectState = z.infer<typeof updateProjectSchema>;

function sortedValues(values: string[]) {
return [...values].sort((first, second) => first.localeCompare(second));
}

function hasSameSkills(
existingSkills: StructuralProjectState['skills'],
incomingSkills: string[]
) {
const existing = sortedValues(existingSkills.map((skill) => skill.skillId));
const incoming = sortedValues(incomingSkills);

return (
existing.length === incoming.length &&
existing.every((skillId, index) => skillId === incoming[index])
);
}

function hasSameStacks(
existingStacks: StructuralProjectState['stacks'],
incomingStacks: NonNullable<IncomingProjectState['stacks']>
) {
if (existingStacks.length !== incomingStacks.length) return false;

const incomingByStackId = new Map(
incomingStacks.map((stack) => [stack.stackId, stack.percentage])
);

return existingStacks.every(
(stack) => incomingByStackId.get(stack.stackId) === stack.percentage
);
}

function hasStructuralProjectChanges(
existing: StructuralProjectState,
incoming: IncomingProjectState
) {
const incomingStacks = incoming.stacks ?? [];

return (
existing.name !== incoming.name ||
existing.deadline.getTime() !== new Date(incoming.deadline).getTime() ||
existing.totalValue !== incoming.totalValue ||
!hasSameSkills(existing.skills, incoming.skills) ||
!hasSameStacks(existing.stacks, incomingStacks)
);
}

async function applyProjectStackChanges(
tx: Prisma.TransactionClient,
projectId: string,
incomingStacks: NonNullable<IncomingProjectState['stacks']>
) {
const existingStacks = await tx.projectStack.findMany({
where: { projectId },
select: { id: true, stackId: true, percentage: true },
});

const { toUpdate, toCreate, toDelete } = planProjectStackChanges(
existingStacks,
incomingStacks
);

for (const stackToDelete of toDelete) {
await tx.stackTaken.deleteMany({
where: { projectStackId: stackToDelete.id },
});
await tx.projectStack.delete({ where: { id: stackToDelete.id } });
}

for (const stackToUpdate of toUpdate) {
await tx.projectStack.update({
where: { id: stackToUpdate.id },
data: { percentage: stackToUpdate.percentage },
});
}

for (const stackToCreate of toCreate) {
await tx.projectStack.create({
data: {
projectId,
stackId: stackToCreate.stackId,
percentage: stackToCreate.percentage,
},
});
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

export async function GET(
request: NextRequest,
context: { params: { id: string } }
Expand Down Expand Up @@ -163,6 +261,11 @@ export async function PUT(
try {
const existing = await prisma.project.findUnique({
where: { id: projectId },
include: {
skills: { select: { skillId: true } },
stacks: { select: { stackId: true, percentage: true } },
stacksTaken: { select: { id: true }, take: 1 },
},
});

if (!existing) {
Expand Down Expand Up @@ -193,56 +296,40 @@ export async function PUT(
github,
} = parse.data;

const existingStacks = await prisma.projectStack.findMany({
where: { projectId },
select: { id: true, stackId: true, percentage: true },
});

const { toUpdate, toCreate, toDelete } = planProjectStackChanges(
existingStacks,
stacks ?? []
);

for (const stackToDelete of toDelete) {
await prisma.stackTaken.deleteMany({
where: { projectStackId: stackToDelete.id },
if (
existing.stacksTaken.length > 0 &&
hasStructuralProjectChanges(existing, parse.data)
) {
return buildResponse({
success: false,
message: MESSAGES.AUTH.UNAUTHORIZED,
status: 403,
errors: [
'Não é permitido alterar estrutura do projeto após formação da equipe.',
],
});
await prisma.projectStack.delete({ where: { id: stackToDelete.id } });
}

for (const stackToUpdate of toUpdate) {
await prisma.projectStack.update({
where: { id: stackToUpdate.id },
data: { percentage: stackToUpdate.percentage },
});
}
const updated = await prisma.$transaction(async (tx) => {
await applyProjectStackChanges(tx, projectId, stacks ?? []);

for (const stackToCreate of toCreate) {
await prisma.projectStack.create({
return tx.project.update({
where: { id: projectId },
data: {
projectId,
stackId: stackToCreate.stackId,
percentage: stackToCreate.percentage,
name,
description,
deadline: new Date(deadline),
totalValue,
status,
github: github || null,
skills: {
deleteMany: {},
create: skills?.map((skillId: string) => ({
skill: { connect: { id: skillId } },
})),
},
},
});
}

const updated = await prisma.project.update({
where: { id: projectId },
data: {
name,
description,
deadline: new Date(deadline),
totalValue,
status,
github: github || null,
skills: {
deleteMany: {},
create: skills?.map((skillId: string) => ({
skill: { connect: { id: skillId } },
})),
},
},
});

await checkProjectStatus(projectId);
Expand Down
Loading
Loading