diff --git a/apps/client/cypress/e2e/specs/create-project.e2e.ts b/apps/client/cypress/e2e/specs/create-project.e2e.ts index c9054e678..08a893070 100644 --- a/apps/client/cypress/e2e/specs/create-project.e2e.ts +++ b/apps/client/cypress/e2e/specs/create-project.e2e.ts @@ -7,7 +7,6 @@ const orgMi = organizations.find(({ name }) => name === 'mi') as Organization describe('Create Project', () => { const project = { orgId: orgMi.id, - orgName: orgMi.name, name: 'project01', slug: 'mi-project01', description: 'Application de prise de rendez-vous en préfécture.', @@ -39,23 +38,23 @@ describe('Create Project', () => { cy.getByDataTestid('createProjectBtn').should('be.enabled').click() cy.wait('@postProject').its('response.statusCode').should('match', /^20\d$/) - cy.url().should('contain', `/projects/${project.slug}/dashboard`) + cy.url().should('match', /projects\/.*\/dashboard/) cy.wait('@listProjects').its('response.statusCode').should('match', /^20\d$/) cy.assertCreateProjects([project.slug]) }) - it('Should create a project even if name is already taken', () => { + it('Should not create a project if name is already taken', () => { cy.intercept('POST', '/api/v1/projects').as('postProject') cy.intercept('GET', '/api/v1/projects?filter=member&statusNotIn=archived').as('listProjects') cy.goToProjects() .getByDataTestid('createProjectLink').click() .get('select#organizationId-select').select(project.orgId) - .getByDataTestid('nameInput').type(project.name) + .getByDataTestid('nameInput').type(`${project.name}`) cy.getByDataTestid('createProjectBtn').should('be.enabled').click() - cy.wait('@postProject').its('response.statusCode').should('match', /^20\d$/) - cy.url().should('contain', `/projects/${project.orgName}-${project.name}-1/dashboard`) + cy.wait('@postProject').its('response.statusCode').should('not.match', /^20\d$/) + cy.getByDataTestid('snackbar').should('contain', `Le projet "${project.name}" existe déjà`) }) }) diff --git a/apps/server/src/resources/project/business.ts b/apps/server/src/resources/project/business.ts index 0173af0c3..335f0186d 100644 --- a/apps/server/src/resources/project/business.ts +++ b/apps/server/src/resources/project/business.ts @@ -9,6 +9,7 @@ import { deleteAllRepositoryForProject, getAllProjectsDataForExport, getOrganizationById, + getProjectByNames, getProjectOrThrow, getSlugs, initializeProject, @@ -74,6 +75,11 @@ export async function createProject(dataDto: typeof projectContract.createProjec if (!organization) return new BadRequest400('Organisation introuvable') if (!organization.active) return new BadRequest400('Organisation inactive') + const projectSearch = await getProjectByNames({ name: dataDto.name, organizationName: organization.name }) + if (projectSearch) { + return new BadRequest400(`Le projet "${dataDto.name}" existe déjà`) + } + let slug = `${organization.name}-${dataDto.name}` const projectsWithSamePrefix = await getSlugs(slug) slug = generateSlug(slug, projectsWithSamePrefix?.map(project => project.slug)) diff --git a/apps/server/src/resources/project/queries.ts b/apps/server/src/resources/project/queries.ts index 3423e2b29..853a48697 100644 --- a/apps/server/src/resources/project/queries.ts +++ b/apps/server/src/resources/project/queries.ts @@ -187,6 +187,20 @@ export function getProjectInfosAndRepos(id: Project['id']) { }) } +interface GetProjectByNameParams { + name: Project['name'] + organizationName: Organization['name'] +} + +export function getProjectByNames({ name, organizationName }: GetProjectByNameParams) { + return prisma.project.findFirst({ + where: { + name, + organization: { name: organizationName }, + }, + }) +} + export function getSlugs(slugPrefix: string) { return prisma.project.findMany({ where: {