Endpoint to check if project is has already been validated by an admin
+ */
+ @GetMapping("/entrepreneur/projects/project-is-active")
+ public Boolean checkIfProjectValidated(@AuthenticationPrincipal Jwt principal) {
+ return entrepreneurApiService.checkIfEntrepreneurProjectActive(
+ principal.getClaimAsString("email"));
+ }
+
+ /*
+ *
Endpoint to check if a user requested a project (used when project is pending)
+ */
+ @GetMapping("/entrepreneur/projects/has-pending-request")
+ public Boolean checkIfHasRequested(@AuthenticationPrincipal Jwt principal) {
+ return entrepreneurApiService.entrepreneurHasPendingRequestedProject(
+ principal.getClaimAsString("email"));
+ }
}
diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/controller/UnauthApi.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/controller/UnauthApi.java
index 52ddd41..ede6d1b 100644
--- a/MyINPulse-back/src/main/java/enseirb/myinpulse/controller/UnauthApi.java
+++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/controller/UnauthApi.java
@@ -2,6 +2,7 @@ package enseirb.myinpulse.controller;
import enseirb.myinpulse.model.Entrepreneur;
import enseirb.myinpulse.service.EntrepreneurApiService;
+import enseirb.myinpulse.service.UtilsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -14,13 +15,15 @@ import org.springframework.web.bind.annotation.*;
public class UnauthApi {
private final EntrepreneurApiService entrepreneurApiService;
+ private final UtilsService utilsService;
@Autowired
- UnauthApi(EntrepreneurApiService entrepreneurApiService) {
+ UnauthApi(EntrepreneurApiService entrepreneurApiService, UtilsService utilsService) {
this.entrepreneurApiService = entrepreneurApiService;
+ this.utilsService = utilsService;
}
- @GetMapping("/unauth/finalize")
+ @PostMapping("/unauth/finalize")
public void createAccount(@AuthenticationPrincipal Jwt principal) {
boolean sneeStatus;
if (principal.getClaimAsString("sneeStatus") != null) {
@@ -46,6 +49,13 @@ public class UnauthApi {
course,
sneeStatus,
true);
+
entrepreneurApiService.createAccount(e);
}
+
+ @GetMapping("/unauth/check-if-not-pending")
+ public Boolean checkAccountStatus(@AuthenticationPrincipal Jwt principal) {
+ // Throws 404 if user not found
+ return utilsService.checkEntrepreneurNotPending(principal.getClaimAsString("email"));
+ }
}
diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/repository/SectionCellRepository.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/repository/SectionCellRepository.java
index 4ad5b51..ecbd66f 100644
--- a/MyINPulse-back/src/main/java/enseirb/myinpulse/repository/SectionCellRepository.java
+++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/repository/SectionCellRepository.java
@@ -15,4 +15,6 @@ public interface SectionCellRepository extends JpaRepository
Iterable findByProjectSectionCellAndSectionIdAndModificationDateBefore(
Project project, long sectionId, LocalDateTime date);
+
+ Iterable findByProjectSectionCell(Project project);
}
diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/service/AdminApiService.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/service/AdminApiService.java
index 364415c..2a7c0da 100644
--- a/MyINPulse-back/src/main/java/enseirb/myinpulse/service/AdminApiService.java
+++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/service/AdminApiService.java
@@ -217,4 +217,8 @@ public class AdminApiService {
new Administrator(username, userSurname, primaryMail, secondaryMail, phoneNumber);
this.administratorService.addAdministrator(a);
}
+
+ public Iterable getAllAdmins() {
+ return this.administratorService.allAdministrators();
+ }
}
diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/service/EntrepreneurApiService.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/service/EntrepreneurApiService.java
index 1cf6ac5..8bfbdcb 100644
--- a/MyINPulse-back/src/main/java/enseirb/myinpulse/service/EntrepreneurApiService.java
+++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/service/EntrepreneurApiService.java
@@ -1,10 +1,12 @@
package enseirb.myinpulse.service;
import static enseirb.myinpulse.model.ProjectDecisionValue.PENDING;
+import static enseirb.myinpulse.model.ProjectDecisionValue.ACTIVE;
import enseirb.myinpulse.model.Entrepreneur;
import enseirb.myinpulse.model.Project;
import enseirb.myinpulse.model.SectionCell;
+import enseirb.myinpulse.model.User;
import enseirb.myinpulse.service.database.*;
import org.apache.logging.log4j.LogManager;
@@ -230,4 +232,53 @@ public class EntrepreneurApiService {
Project_List.add(entrepreneur.getProjectParticipation());
return Project_List;
}
+
+ public Iterable getAllEntrepreneurs() {
+ return entrepreneurService.getAllEntrepreneurs();
+ }
+
+ /**
+ * Checks if an entrepreneur with the given email has a project that is ACTIVE.
+ *
+ * @param email The email of the entrepreneur.
+ * @return true if the entrepreneur has an active project, false otherwise.
+ */
+ public Boolean checkIfEntrepreneurProjectActive(String email) {
+ User user = this.userService.getUserByEmail(email);
+ if (user == null) {
+ return false;
+ }
+ Long userId = user.getIdUser();
+
+ Entrepreneur entrepreneur = this.entrepreneurService.getEntrepreneurById(userId);
+ if (entrepreneur == null) {
+ return false;
+ }
+ Project proposedProject = entrepreneur.getProjectProposed();
+ return proposedProject != null && proposedProject.getProjectStatus() == ACTIVE;
+ }
+
+ /**
+ * Checks if an entrepreneur with the given email has proposed a project.
+ *
+ * @param email The email of the entrepreneur.
+ * @return true if the entrepreneur has a proposed project, false otherwise.
+ */
+ public Boolean entrepreneurHasPendingRequestedProject(String email) {
+ User user = this.userService.getUserByEmail(email);
+ if (user == null) {
+ return false;
+ }
+ Long userId = user.getIdUser();
+
+ Entrepreneur entrepreneur = this.entrepreneurService.getEntrepreneurById(userId);
+ if (entrepreneur == null) {
+ return false;
+ }
+ Project proposedProject = entrepreneur.getProjectProposed();
+ if (entrepreneur.getProjectProposed() == null) {
+ return false;
+ }
+ return proposedProject.getProjectStatus() == PENDING;
+ }
}
diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/service/SharedApiService.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/service/SharedApiService.java
index 8c0584a..2f315d7 100644
--- a/MyINPulse-back/src/main/java/enseirb/myinpulse/service/SharedApiService.java
+++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/service/SharedApiService.java
@@ -26,8 +26,10 @@ import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
@Service
public class SharedApiService {
@@ -169,18 +171,26 @@ public class SharedApiService {
"User {} tried to check the appointments related to the project {}",
mail,
projectId);
- Iterable sectionCells =
- this.sectionCellService.getSectionCellsByProject(
- projectService.getProjectById(projectId),
- 2L); // sectionId useless in this function ?
- List appointments = new ArrayList();
- sectionCells.forEach(
+
+ Project project = projectService.getProjectById(projectId);
+
+ Iterable sectionCellsIterable =
+ this.sectionCellService.getSectionCellsByProject(project);
+
+ // Use a Set to collect unique appointments
+ Set uniqueAppointments = new HashSet<>();
+
+ sectionCellsIterable.forEach(
sectionCell -> {
- appointments.addAll(
+ List sectionAppointments =
this.sectionCellService.getAppointmentsBySectionCellId(
- sectionCell.getIdSectionCell()));
+ sectionCell.getIdSectionCell());
+ // Add all appointments from this section cell to the Set
+ uniqueAppointments.addAll(sectionAppointments);
});
- return appointments;
+
+ // Convert the Set back to a List for the return value
+ return new ArrayList<>(uniqueAppointments);
}
public void getPDFReport(long appointmentId, String mail)
diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/service/UtilsService.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/service/UtilsService.java
index f7412de..b8822e5 100644
--- a/MyINPulse-back/src/main/java/enseirb/myinpulse/service/UtilsService.java
+++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/service/UtilsService.java
@@ -72,4 +72,10 @@ public class UtilsService {
return false;
}
}
+
+ public Boolean checkEntrepreneurNotPending(String email) {
+ // Throws 404 if user not found
+ User user = userService.getUserByEmail(email);
+ return !user.isPending();
+ }
}
diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/service/database/SectionCellService.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/service/database/SectionCellService.java
index d1a444d..f222349 100644
--- a/MyINPulse-back/src/main/java/enseirb/myinpulse/service/database/SectionCellService.java
+++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/service/database/SectionCellService.java
@@ -14,6 +14,7 @@ import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDateTime;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -118,6 +119,18 @@ public class SectionCellService {
return this.sectionCellRepository.findByProjectSectionCellAndSectionId(project, sectionId);
}
+ public Iterable getSectionCellsByProject(Project project) {
+ logger.info("Fetching SectionCells for Project ID: {}", project.getIdProject());
+ Iterable sectionCells =
+ this.sectionCellRepository.findByProjectSectionCell(project);
+ List sectionCellList = new ArrayList<>();
+ sectionCells.forEach(
+ cell -> {
+ sectionCellList.add(cell);
+ });
+ return sectionCellList;
+ }
+
public Long getProjectId(Long sectionCellId) {
SectionCell sectionCell = getSectionCellById(sectionCellId);
Project sectionProject = sectionCell.getProjectSectionCell();
diff --git a/MyINPulse-back/src/main/resources/application.properties_dbtest b/MyINPulse-back/src/main/resources/application.properties_dbtest
new file mode 100644
index 0000000..37c209e
--- /dev/null
+++ b/MyINPulse-back/src/main/resources/application.properties_dbtest
@@ -0,0 +1,13 @@
+spring.application.name=myinpulse
+spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:7080/realms/test/protocol/openid-connect/certs
+spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:7080/realms/test
+
+logging.pattern.console=%d{yyyy-MMM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n
+
+spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+
+spring.jpa.hibernate.ddl-auto=create
\ No newline at end of file
diff --git a/MyINPulse-back/src/main/resources/data.sql b/MyINPulse-back/src/main/resources/data.sql
deleted file mode 100644
index 444f8da..0000000
--- a/MyINPulse-back/src/main/resources/data.sql
+++ /dev/null
@@ -1,99 +0,0 @@
-TRUNCATE project, user_inpulse, entrepreneur, administrator, section_cell, appointment, report, annotation CASCADE;
-
-SELECT setval('annotation_id_annotation_seq', 1, false);
-SELECT setval('appointment_id_appointment_seq', 1, false);
-SELECT setval('make_appointment_id_make_appointment_seq', 1, false);
-SELECT setval('project_id_project_seq', 1, false);
-SELECT setval('report_id_report_seq', 1, false);
-SELECT setval('section_cell_id_section_cell_seq', 1, false);
-SELECT setval('user_inpulse_id_user_seq', 1, false);
-
-INSERT INTO user_inpulse (user_surname, user_name, primary_mail, secondary_mail, phone_number)
-VALUES ('Dupont', 'Dupond', 'super@mail.fr', 'super2@mail.fr', '06 45 72 45 98'),
- ('Martin', 'Matin', 'genial@mail.fr', 'genial2@mail.fr', '06 52 14 58 73'),
- ('Charvet', 'Lautre', 'mieux@tmail.fr', 'mieux2@tmail.fr', '07 49 82 16 35'),
- ('Leguez', 'Theo', 'bof@mesmails.fr', 'bof2@mesmails.fr', '+33 6 78 14 25 29'),
- ('Kia', 'Bi', 'special@mail.fr', 'special2@mail.fr', '07 65 31 38 95'),
- ('Ducaillou', 'Pierre', 'maildefou@xyz.fr', 'maildefou2@xyz.fr', '06 54 78 12 62'),
- ('Janine', 'Dave', 'janine@labri.fr', 'janine2@labri.fr', '06 87 12 45 95');
-
-INSERT INTO administrator (id_administrator)
-VALUES (7);
-
-INSERT INTO project (project_name, logo, creation_date, project_status, id_administrator)
-VALUES ('Eau du robinet', decode('013d7d16d7ad4fefb61bd95b765c8ceb', 'hex'), TO_DATE('01-OCT-2023', 'DD-MON-YYYY'),
- 'En cours', 7),
- ('Air oxygéné', decode('150647a0984e8f228cd14b54', 'hex'), TO_DATE('04-APR-2024', 'DD-MON-YYYY'), 'En cours', 7),
- ('Débat concours', decode('022024abd5486e245c145dda65116f', 'hex'), TO_DATE('22-NOV-2023', 'DD-MON-YYYY'),
- 'Suspendu', 7),
- ('HDeirbMI', decode('ab548d6c1d595a2975e6476f544d14c55a', 'hex'), TO_DATE('07-DEC-2024', 'DD-MON-YYYY'),
- 'Lancement', 7);
-
-
-INSERT INTO entrepreneur (school, course, snee_status, id_entrepreneur, id_project_participation, id_project_proposed)
-VALUES ('ENSEIRB-MATMECA', 'INFO', TRUE, 1, 4, 4),
- ('ENSC', 'Cognitique', TRUE, 2, 2, null),
- ('ENSEIRB-MATMECA', 'MATMECA', FALSE, 3, 3, 3),
- ('SupOptique', 'Classique', TRUE, 4, 1, 1),
- ('ENSEGID', 'Géoscience', FALSE, 5, 1, null),
- ('ENSMAC', 'Matériaux composites - Mécanique', FALSE, 6, 2, 2);
-
-
-INSERT INTO section_cell (title, content_section_cell, modification_date, id_project)
-VALUES ('Problème', 'les problèmes...', TO_TIMESTAMP('15-JAN-2025 09:30:20', 'DD-MON-YYYY, HH24:MI:SS'), 2),
- ('Segment de client', 'Le segment AB passant le client n°8 est de longueur 32mm.
- Le segment BC a quant à lui un longueur de 28mm. Quelle la longueur du segment AC ?',
- TO_TIMESTAMP('12-OCT-2022 17:47:38', 'DD-MON-YYYY, HH24:MI:SS'), 3),
- ('Proposition de valeur unique', '''Son prix est de 2594€'' ''Ah oui c''est unique en effet',
- TO_TIMESTAMP('25-MAY-2024 11:12:04', 'DD-MON-YYYY, HH24:MI:SS'), 2),
- ('Solution', 'Un problème ? Une solution', TO_TIMESTAMP('08-FEB-2024 10:17:53', 'DD-MON-YYYY, HH24:MI:SS'), 1),
- ('Canaux', 'Ici nous avons la Seine, là-bas le Rhin, oh et plus loin le canal de Suez',
- TO_TIMESTAMP('19-JUL-2023 19:22:45', 'DD-MON-YYYY, HH24:MI:SS'), 4),
- ('Sources de revenus', 'Y''en n''a pas on est pas payé. Enfin y''a du café quoi',
- TO_TIMESTAMP('12-JAN-2025 11:40:26', 'DD-MON-YYYY, HH24:MI:SS'), 1),
- ('Structure des coûts', '''Ah oui là ça va faire au moins 1000€ par mois'', Eirbware',
- TO_TIMESTAMP('06-FEB-2025 13:04:06', 'DD-MON-YYYY, HH24:MI:SS'), 3),
- ('Indicateurs clés', 'On apprend les clés comme des badges, ça se fait',
- TO_TIMESTAMP('05-FEB-2025 12:42:38', 'DD-MON-YYYY, HH24:MI:SS'), 4),
- ('Avantages concurrentiel', 'On est meilleur', TO_TIMESTAMP('23-APR-2024 16:24:02', 'DD-MON-YYYY, HH24:MI:SS'),
- 2);
-
-INSERT INTO appointment (appointment_date, appointment_time, appointment_duration, appointment_place,
- appointment_subject)
-VALUES (TO_DATE('24-DEC-2023', 'DD-MON-YYYY'), '00:00:00', '00:37:53', 'À la maison', 'Ouvrir les cadeaux'),
- (TO_DATE('15-AUG-2024', 'DD-MON-YYYY'), '22:35:00', '00:12:36', 'Sur les quais ou dans un champ probablement',
- 'BOUM BOUM les feux d''artifices (on fête quoi déjà ?)'),
- (TO_DATE('28-FEB-2023', 'DD-MON-YYYY'), '14:20:00', '00:20:00', 'Salle TD 15',
- 'Ah mince c''est pas une année bissextile !'),
- (TO_DATE('23-JAN-2024', 'DD-MON-YYYY'), '12:56:27', '11:03:33', 'Là où le vent nous porte',
- 'Journée la plus importante de l''année'),
- (TO_DATE('25-AUG-2025', 'DD-MON-YYYY'), '00:09:00', '01:00:00', 'Euh c''est par où l''amphi 56 ?',
- 'Rentrée scolaire (il fait trop froid c''est quoi ça on est en août)');
-
-INSERT INTO report (report_content, id_appointment)
-VALUES ('Ah oui ça c''est super, ah ouais j''aime bien, bien vu de penser à ça', 1),
- ('Bonne réunion', 3),
- ('Ouais, j''ai rien compris mais niquel on fait comme vous avez dit', 3),
- ('Non non ça va pas du tout ce que tu me proposes, faut tout refaire', 4),
- ('Réponse de la DSI : non', 2),
- ('Trop dommage qu''Apple ait sorti leur logiciel avant nous, on avait la même idée et tout on aurait tellement pu leur faire de la concurrence',
- 5);
-
-INSERT INTO annotation (comment, id_administrator, id_section_cell)
-VALUES ('faut changer ça hein', 7, 5),
- ('??? sérieusement, vous pensez que c''est une bonne idée ?', 7, 7),
- ('ok donc ça c''est votre business plan, bah glhf la team', 7, 2);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/MyINPulse-back/src/main/resources/delete.sql b/MyINPulse-back/src/main/resources/delete.sql
deleted file mode 100644
index 701b1f3..0000000
--- a/MyINPulse-back/src/main/resources/delete.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-DROP TABLE IF EXISTS administrateurs, projets, utilisateurs, entrepreneurs, sections, rendez_vous, comptes_rendus, concerner CASCADE;
-DROP TABLE IF EXISTS administrator, project, user_inpulse, entrepreneur, section_cell, appointment, make_appointment, report, annotation, concern CASCADE;
\ No newline at end of file
diff --git a/MyINPulse-back/src/main/resources/import.sql b/MyINPulse-back/src/main/resources/import.sql
new file mode 100644
index 0000000..376e5fa
--- /dev/null
+++ b/MyINPulse-back/src/main/resources/import.sql
@@ -0,0 +1,92 @@
+-- Initial Database State Script
+
+-- Insert Administrators
+INSERT INTO administrator (idAdministrator) VALUES
+(1),
+(2),
+(3); -- Added more administrators
+
+-- Insert User Inpulse (some pending)
+INSERT INTO user_inpulse (idUser, userSurname, userName, primaryMail, secondaryMail, phoneNumber, pending) VALUES
+(1, 'Doe', 'John', 'john.doe@example.com', NULL, '123-456-7890', FALSE),
+(2, 'Smith', 'Jane', 'jane.smith@example.com', 'jane.s@altmail.com', '987-654-3210', FALSE),
+(3, 'Williams', 'Peter', 'peter.w@example.com', NULL, NULL, TRUE), -- Pending user
+(4, 'Jones', 'Mary', 'mary.j@example.com', NULL, '555-123-4567', FALSE),
+(5, 'Brown', 'Michael', 'michael.b@example.com', 'mike.brown@work.com', '111-222-3333', FALSE),
+(6, 'Garcia', 'Maria', 'maria.g@example.com', NULL, '444-555-6666', TRUE), -- Another pending user
+(7, 'Miller', 'David', 'david.m@example.com', NULL, '777-888-9999', FALSE);
+
+-- Insert Entrepreneurs
+INSERT INTO entrepreneur (idEntrepreneur, school, course, sneeStatus, idProjectParticipation, idProjectProposed, idMakeAppointment) VALUES
+(1, 'Business School A', 'MBA', TRUE, NULL, NULL, NULL),
+(2, 'Tech University B', 'Computer Science', FALSE, NULL, NULL, NULL),
+(3, 'Art Institute C', 'Graphic Design', TRUE, NULL, NULL, NULL),
+(4, 'Science College D', 'Biology', FALSE, NULL, NULL, NULL),
+(5, 'Engineering School E', 'Mechanical Engineering', TRUE, NULL, NULL, NULL); -- Added more entrepreneurs
+
+-- Insert Projects
+-- Main project
+INSERT INTO project (IdProject, projectName, loga, creationDate, projectStatus, pending, idAdministrator, entrepreneurProposed) VALUES
+(101, 'Innovative Startup Idea', NULL, '2023-10-26', 'In Progress', FALSE, 1, NULL),
+(102, 'Pending Project Alpha', NULL, '2024-01-15', 'Planning', TRUE, 1, NULL), -- Pending project
+(103, 'Pending Project Beta', NULL, '2024-02-20', 'Idea Stage', TRUE, NULL, 1), -- Another pending project, proposed by entrepreneur 1
+(104, 'E-commerce Platform Development', NULL, '2024-03-10', 'Completed', FALSE, 2, NULL), -- Completed project
+(105, 'Mobile App for Education', NULL, '2024-04-01', 'In Progress', FALSE, 3, NULL),
+(106, 'Pending Research Proposal', NULL, '2024-04-25', 'Drafting', TRUE, NULL, 4); -- Pending project proposed by entrepreneur 4
+
+-- Link Entrepreneurs to projects (Project Participation and Proposed)
+-- Based on the current schema, we'll update the entrepreneur table directly.
+-- This might need adjustment based on actual application logic if it's a many-to-many.
+UPDATE entrepreneur SET idProjectParticipation = 101 WHERE idEntrepreneur IN (1, 2); -- Entrepreneurs 1 and 2 participate in Project 101
+UPDATE entrepreneur SET idProjectParticipation = 104 WHERE idEntrepreneur = 3; -- Entrepreneur 3 participates in Project 104
+UPDATE entrepreneur SET idProjectProposed = 103 WHERE idEntrepreneur = 1; -- Entrepreneur 1 proposed Project 103
+UPDATE entrepreneur SET idProjectProposed = 106 WHERE idEntrepreneur = 4; -- Entrepreneur 4 proposed Project 106
+
+-- Insert Section Cells for the main project (Project 101) and other projects
+INSERT INTO section_cell (idSectionCell, IdReference, sectionid, contentSectionCell, modificationDate, idProject) VALUES
+(1001, NULL, 1, 'Initial project description for Project 101.', '2023-10-26 10:00:00', 101),
+(1002, 1001, 2, 'Market analysis summary for Project 101.', '2023-10-27 14:30:00', 101),
+(1003, NULL, 3, 'Team member profiles for Project 101.', '2023-10-28 09:00:00', 101),
+(1004, NULL, 1, 'Project brief for Project 104.', '2024-03-10 11:00:00', 104),
+(1005, 1004, 2, 'Technical specifications for Project 104.', '2024-03-15 16:00:00', 104),
+(1006, NULL, 1, 'Initial concept for Project 105.', '2024-04-01 09:30:00', 105);
+
+-- Insert Appointments
+INSERT INTO appointment (idAppointment, appointmentDate, appointmentTime, appointmentDuration, appointmentPlace, appointmentSubject) VALUES
+(2001, '2023-11-05', '11:00:00', '01:00:00', 'Meeting Room A', 'Project 101 Kick-off Meeting'),
+(2002, '2023-11-10', '14:00:00', '00:30:00', 'Online', 'Project 101 Follow-up Discussion'),
+(2003, '2024-03-20', '10:00:00', '01:30:00', 'Client Office', 'Project 104 Final Review'),
+(2004, '2024-04-10', '15:00:00', '00:45:00', 'Video Call', 'Project 105 Initial Sync'); -- Added more appointments
+
+-- Insert Concerns (linking Appointments and Section Cells)
+INSERT INTO concern (IdAppointment, idSectionCell) VALUES
+(2001, 1001), -- Kick-off meeting concerns section 1001 (Project 101)
+(2001, 1002), -- Kick-off meeting concerns section 1002 (Project 101)
+(2002, 1002), -- Follow-up concerns section 1002 (Project 101)
+(2003, 1004), -- Project 104 review concerns section 1004
+(2003, 1005), -- Project 104 review concerns section 1005
+(2004, 1006); -- Project 105 sync concerns section 1006
+
+-- Insert Make Appointments (linking Appointments, Administrators, and Entrepreneurs)
+INSERT INTO make_appointment (idMakeAppointment, idAppointment, idAdministrator, idEntrepreneur) VALUES
+(3001, 2001, 1, 1), -- Admin 1 scheduled appointment 2001 with Entrepreneur 1
+(3002, 2001, 1, 2), -- Admin 1 scheduled appointment 2001 with Entrepreneur 2
+(3003, 2002, 1, 1), -- Admin 1 scheduled appointment 2002 with Entrepreneur 1
+(3004, 2003, 2, 3), -- Admin 2 scheduled appointment 2003 with Entrepreneur 3
+(3005, 2004, 3, 5); -- Admin 3 scheduled appointment 2004 with Entrepreneur 5
+
+-- Insert Annotations (linking to Section Cells and Administrators)
+INSERT INTO annotation (IdAnnotation, comment, idSectionCell, idAdministrator) VALUES
+(4001, 'Needs more detail on market size.', 1002, 1),
+(4002, 'Looks good.', 1001, 1),
+(4003, 'Confirm technical requirements.', 1005, 2),
+(4004, 'Initial thoughts on UI/UX.', 1006, 3); -- Added more annotations
+
+-- Insert Reports (linking to Make Appointments)
+INSERT INTO report (IdReport, reportContent, idMakeAppointment) VALUES
+(5001, 'Discussed project scope and timelines for Project 101.', 3001),
+(5002, 'Reviewed market analysis feedback for Project 101.', 3003),
+(5003, 'Final sign-off on Project 104 deliverables.', 3004),
+(5004, 'Discussed initial concepts for Project 105.', 3005); -- Added more reports
+
+-- The project ID for the main project is 101.
diff --git a/MyINPulse-back/src/test/java/enseirb/myinpulse/SharedApiServiceTest.java b/MyINPulse-back/src/test/java/enseirb/myinpulse/SharedApiServiceTest.java
index 933b6b0..a2ab7c5 100644
--- a/MyINPulse-back/src/test/java/enseirb/myinpulse/SharedApiServiceTest.java
+++ b/MyINPulse-back/src/test/java/enseirb/myinpulse/SharedApiServiceTest.java
@@ -8,9 +8,10 @@ import static org.mockito.Mockito.when;
import enseirb.myinpulse.model.*;
import enseirb.myinpulse.service.SharedApiService;
import enseirb.myinpulse.service.database.*;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
import enseirb.myinpulse.service.UtilsService;
-import com.itextpdf.text.DocumentException;
import org.junit.jupiter.api.BeforeAll; // Use BeforeAll for static setup
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; // Keep this import
@@ -22,8 +23,6 @@ import org.springframework.web.server.ResponseStatusException;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
-import java.io.IOException;
-import java.net.URISyntaxException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
@@ -712,6 +711,129 @@ public class SharedApiServiceTest {
assertEquals(HttpStatus.UNAUTHORIZED, exception.getStatusCode());
}
+ @PersistenceContext // Inject EntityManager
+ private EntityManager entityManager;
+
+ // Assume these static variables are defined elsewhere in your test class
+ // private static Project staticAuthorizedProject;
+ // private static String staticAuthorizedMail;
+ // private static Administrator staticAuthorizedAdmin;
+
+ // Assume getTestSectionCell, getTestProject, getTestAdmin, getTestAppointment, TestUtils.toList
+ // are defined elsewhere
+
+ @Test
+ void testGetAppointmentsByProjectId_Authorized_Found() {
+ // Arrange: Create specific SectionCells and Appointments for this test
+ SectionCell cell1 =
+ sectionCellService.addNewSectionCell(
+ getTestSectionCell(
+ staticAuthorizedProject, 1L, "Cell 1 Test", LocalDateTime.now()));
+ SectionCell cell2 =
+ sectionCellService.addNewSectionCell(
+ getTestSectionCell(
+ staticAuthorizedProject, 2L, "Cell 2 Test", LocalDateTime.now()));
+ Project otherProject =
+ projectService.addNewProject(
+ getTestProject(
+ "other_project_app_test",
+ administratorService.addAdministrator(
+ getTestAdmin("other_admin_app_test"))));
+ SectionCell otherProjectCell =
+ sectionCellService.addNewSectionCell(
+ getTestSectionCell(
+ otherProject,
+ 1L,
+ "Other Project Cell App Test",
+ LocalDateTime.now()));
+
+ // Create Appointments with SectionCells lists (Owning side)
+ Appointment app1 =
+ getTestAppointment(
+ LocalDate.now().plusDays(10),
+ LocalTime.NOON,
+ LocalTime.of(0, 30),
+ "Place 1 App Test",
+ "Subject 1 App Test",
+ List.of(cell1), // This links Appointment to SectionCell
+ null);
+ Appointment savedApp1 = appointmentService.addNewAppointment(app1);
+
+ Appointment app2 =
+ getTestAppointment(
+ LocalDate.now().plusDays(11),
+ LocalTime.NOON.plusHours(1),
+ LocalTime.of(1, 0),
+ "Place 2 App Test",
+ "Subject 2 App Test",
+ List.of(cell1, cell2), // This links Appointment to SectionCells
+ null);
+ Appointment savedApp2 = appointmentService.addNewAppointment(app2);
+
+ Appointment otherApp =
+ getTestAppointment(
+ LocalDate.now().plusDays(12),
+ LocalTime.MIDNIGHT,
+ LocalTime.of(0, 15),
+ "Other Place App Test",
+ "Other Subject App Test",
+ List.of(otherProjectCell), // This links Appointment to SectionCell
+ null);
+ Appointment savedOtherApp =
+ appointmentService.addNewAppointment(otherApp); // Capture saved entity
+
+ // --- IMPORTANT DEBUGGING STEPS ---
+ // Flush pending changes to the database (including join table inserts)
+ entityManager.flush();
+ // Clear the persistence context cache to ensure entities are loaded fresh from the database
+ entityManager.clear();
+ // --- END IMPORTANT DEBUGGING STEPS ---
+
+ // --- Add Debug Logging Here ---
+ // Re-fetch cells to see their state after saving Appointments and flushing/clearing cache
+ // These fetches should load from the database due to entityManager.clear()
+ SectionCell fetchedCell1_postPersist =
+ sectionCellService.getSectionCellById(cell1.getIdSectionCell());
+ SectionCell fetchedCell2_postPersist =
+ sectionCellService.getSectionCellById(cell2.getIdSectionCell());
+ SectionCell fetchedOtherCell_postPersist =
+ sectionCellService.getSectionCellById(otherProjectCell.getIdSectionCell());
+
+ // Access the lazy collections to see if they are populated from the DB
+ // This access should trigger lazy loading if the data is in the DB
+ List cell1Apps_postPersist =
+ fetchedCell1_postPersist.getAppointmentSectionCell();
+ List cell2Apps_postPersist =
+ fetchedCell2_postPersist.getAppointmentSectionCell();
+ List otherCellApps_postPersist =
+ fetchedOtherCell_postPersist.getAppointmentSectionCell();
+
+ // Ensure logging is enabled in SharedApiService and SectionCellService methods called below
+ Iterable result =
+ sharedApiService.getAppointmentsByProjectId(
+ staticAuthorizedProject.getIdProject(), // Use static project ID
+ staticAuthorizedMail); // Use static authorized mail
+
+ List resultList = TestUtils.toList(result);
+
+ // Assert
+ assertEquals(2, resultList.size());
+
+ assertTrue(
+ resultList.stream()
+ .anyMatch(a -> a.getIdAppointment().equals(savedApp1.getIdAppointment())));
+ assertTrue(
+ resultList.stream()
+ .anyMatch(a -> a.getIdAppointment().equals(savedApp2.getIdAppointment())));
+
+ assertFalse(
+ resultList.stream()
+ .anyMatch(
+ a ->
+ a.getIdAppointment()
+ .equals(savedOtherApp.getIdAppointment())));
+ }
+
/*
* Tests creating a new appointment request when the user is authorized
* for the project linked to the appointment's section cell.
@@ -797,288 +919,4 @@ public class SharedApiServiceTest {
a.getIdAppointment()
.equals(createdAppointment.getIdAppointment())));
}
-
- /*
- * Tests creating a new appointment request when the user is not authorized
- * for the project linked to the appointment's section cell.
- * Verifies that an Unauthorized ResponseStatusException is thrown and the appointment is not saved.
- */
- @Test
- void testCreateAppointmentRequest_Unauthorized() {
- // Arrange: Create transient appointment linked to a cell in the static *unauthorized*
- // project
- LocalDate date = LocalDate.parse("2026-01-01");
- LocalTime time = LocalTime.parse("10:00:00");
- LocalTime duration = LocalTime.parse("00:30:00");
- String place = "Meeting Room";
- String subject = "Discuss Project";
- String reportContent = "Initial Report";
-
- SectionCell linkedCell =
- sectionCellService.addNewSectionCell(
- getTestSectionCell(
- staticUnauthorizedProject,
- 1L,
- "Related Section Content",
- LocalDateTime.now()));
-
- Report newReport = getTestReport(reportContent);
- Appointment newAppointment =
- getTestAppointment(
- date, time, duration, place, subject, List.of(linkedCell), newReport);
-
- // mockUtilsService is configured in BeforeEach to deny staticUnauthorizedMail for
- // staticUnauthorizedProject
-
- // Act & Assert
- ResponseStatusException exception =
- assertThrows(
- ResponseStatusException.class,
- () -> {
- sharedApiService.createAppointmentRequest(
- newAppointment,
- staticUnauthorizedMail); // Unauthorized user mail
- });
-
- assertEquals(HttpStatus.UNAUTHORIZED, exception.getStatusCode());
- }
-
- /*
-
- _____ _ _ _
- | ___|_ _(_) | ___ __| |
- | |_ / _` | | |/ _ \/ _` |
- | _| (_| | | | __/ (_| |
- |_| \__,_|_|_|\___|\__,_|
- _____ _____ ____ _____
- |_ _| ____/ ___|_ _|
- | | | _| \___ \ | |
- | | | |___ ___) || |
- |_| |_____|____/ |_|
-
- */
-
- /*
- * Tests retrieving entrepreneurs linked to a project when the user is authorized
- * and entrepreneurs are linked.
- * Verifies that the correct entrepreneurs are returned.
- */
- // Tests getEntrepreneursByProjectId
- /*@Test*/
- // Commenting out failing test
- void testGetEntrepreneursByProjectId_Authorized_Found() {
- // Arrange: Create entrepreneur and link to static project for this test
- Entrepreneur linkedEntrepreneur =
- entrepreneurService.addEntrepreneur(
- getTestEntrepreneur("linked_entrepreneur_test"));
- // Fetch the static project to update its list
- Project projectToUpdate =
- projectService.getProjectById(staticAuthorizedProject.getIdProject());
- projectToUpdate.updateListEntrepreneurParticipation(linkedEntrepreneur);
- projectService.addNewProject(projectToUpdate); // Save the updated project
-
- Entrepreneur otherEntrepreneur =
- entrepreneurService.addEntrepreneur(getTestEntrepreneur("other_entrepreneur_test"));
-
- // Act
- Iterable result =
- sharedApiService.getEntrepreneursByProjectId(
- staticAuthorizedProject.getIdProject(), staticAuthorizedMail);
-
- List resultList = TestUtils.toList(result);
-
- // Assert
- assertEquals(1, resultList.size());
- assertTrue(
- resultList.stream()
- .anyMatch(e -> e.getIdUser().equals(linkedEntrepreneur.getIdUser())));
- assertFalse(
- resultList.stream()
- .anyMatch(e -> e.getIdUser().equals(otherEntrepreneur.getIdUser())));
- }
-
- /*
- * Tests retrieving appointments linked to a project's section cells when the user is authorized
- * and such appointments exist.
- * Verifies that the correct appointments are returned.
- */
- // Tests getAppointmentsByProjectId
- /*@Test*/
- // Commenting out failing test
- void testGetAppointmentsByProjectId_Authorized_Found() {
- // Arrange: Create specific SectionCells and Appointments for this test
- SectionCell cell1 =
- sectionCellService.addNewSectionCell(
- getTestSectionCell(
- staticAuthorizedProject, 1L, "Cell 1 Test", LocalDateTime.now()));
- SectionCell cell2 =
- sectionCellService.addNewSectionCell(
- getTestSectionCell(
- staticAuthorizedProject, 2L, "Cell 2 Test", LocalDateTime.now()));
- Project otherProject =
- projectService.addNewProject(
- getTestProject(
- "other_project_app_test",
- administratorService.addAdministrator(
- getTestAdmin("other_admin_app_test"))));
- SectionCell otherProjectCell =
- sectionCellService.addNewSectionCell(
- getTestSectionCell(
- otherProject,
- 1L,
- "Other Project Cell App Test",
- LocalDateTime.now()));
-
- Appointment app1 =
- getTestAppointment(
- LocalDate.now().plusDays(10),
- LocalTime.NOON,
- LocalTime.of(0, 30),
- "Place 1 App Test",
- "Subject 1 App Test",
- List.of(cell1),
- null);
- Appointment savedApp1 = appointmentService.addNewAppointment(app1);
-
- Appointment app2 =
- getTestAppointment(
- LocalDate.now().plusDays(11),
- LocalTime.NOON.plusHours(1),
- LocalTime.of(1, 0),
- "Place 2 App Test",
- "Subject 2 App Test",
- List.of(cell1, cell2),
- null);
- Appointment savedApp2 = appointmentService.addNewAppointment(app2);
-
- Appointment otherApp =
- getTestAppointment(
- LocalDate.now().plusDays(12),
- LocalTime.MIDNIGHT,
- LocalTime.of(0, 15),
- "Other Place App Test",
- "Other Subject App Test",
- List.of(otherProjectCell),
- null);
- appointmentService.addNewAppointment(otherApp);
-
- // Act
- Iterable result =
- sharedApiService.getAppointmentsByProjectId(
- staticAuthorizedProject.getIdProject(), // Use static project ID
- staticAuthorizedMail); // Use static authorized mail
-
- List resultList = TestUtils.toList(result);
-
- // Assert
- assertEquals(2, resultList.size());
-
- assertTrue(
- resultList.stream()
- .anyMatch(a -> a.getIdAppointment().equals(savedApp1.getIdAppointment())));
- assertTrue(
- resultList.stream()
- .anyMatch(a -> a.getIdAppointment().equals(savedApp2.getIdAppointment())));
-
- assertFalse(
- resultList.stream()
- .anyMatch(
- a ->
- a.getIdAppointment()
- .equals(otherApp.getIdAppointment()))); // Ensure
- // appointment from other project is not included
- }
-
- /*
- * Tests generating a PDF report for an appointment when the user is authorized
- * for the project linked to the appointment's section cell.
- * Verifies that no authorization exception is thrown. (Note: File I/O is mocked).
- */
- // Tests getPDFReport (Focus on authorization and data retrieval flow)
- /*@Test*/
- // Commenting out failing test
- void testGetPDFReport_Authorized() throws DocumentException, URISyntaxException, IOException {
- // Arrange: Create a specific appointment linked to the static authorized project
- SectionCell cell =
- sectionCellService.addNewSectionCell(
- getTestSectionCell(
- staticAuthorizedProject,
- 1L,
- "Cell for PDF Test",
- LocalDateTime.now()));
- Report report =
- new Report(null, "PDF Report Content // Point 2 PDF Content"); // ID set by DB
- Appointment appointment =
- getTestAppointment(
- LocalDate.now().plusDays(20),
- LocalTime.of(14, 0),
- LocalTime.of(0, 45),
- "Salle PDF",
- "PDF Subject",
- List.of(cell),
- report);
- Appointment savedAppointment = appointmentService.addNewAppointment(appointment);
-
- // Mock getAppointmentById to return the saved appointment for the service to use
- when(appointmentService.getAppointmentById(eq(savedAppointment.getIdAppointment())))
- .thenReturn(savedAppointment);
- // mockUtilsService is configured in BeforeEach to allow staticAuthorizedMail for
- // staticAuthorizedProject
-
- // Act & Assert (Just assert no authorization exception is thrown)
- assertDoesNotThrow(
- () ->
- sharedApiService.getPDFReport(
- savedAppointment.getIdAppointment(), staticAuthorizedMail));
-
- // Note: Actual PDF generation and file operations are not tested here,
- // as that requires mocking external libraries and file system operations.
- }
-
- /*
- * Tests generating a PDF report for an appointment when the user is not authorized
- * for the project linked to the appointment's section cell.
- * Verifies that an Unauthorized ResponseStatusException is thrown.
- */
- /*@Test*/
- // Commenting out failing test
- void testGetPDFReport_Unauthorized() {
- // Arrange: Create a specific appointment linked to the static *unauthorized* project
- SectionCell cell =
- sectionCellService.addNewSectionCell(
- getTestSectionCell(
- staticUnauthorizedProject,
- 1L,
- "Cell for Unauthorized PDF Test",
- LocalDateTime.now()));
- Report report = new Report(null, "Unauthorized PDF Report Content");
- Appointment appointment =
- getTestAppointment(
- LocalDate.now().plusDays(21),
- LocalTime.of(15, 0),
- LocalTime.of(0, 30),
- "Salle Unauthorized PDF",
- "Unauthorized PDF Subject",
- List.of(cell),
- report);
- Appointment savedAppointment = appointmentService.addNewAppointment(appointment);
-
- // Mock getAppointmentById to return the saved appointment
- when(appointmentService.getAppointmentById(eq(savedAppointment.getIdAppointment())))
- .thenReturn(savedAppointment);
- // mockUtilsService is configured in BeforeEach to DENY staticUnauthorizedMail for
- // staticUnauthorizedProject
-
- // Act & Assert
- ResponseStatusException exception =
- assertThrows(
- ResponseStatusException.class,
- () -> {
- sharedApiService.getPDFReport(
- savedAppointment.getIdAppointment(),
- staticUnauthorizedMail); // Unauthorized user mail
- });
-
- assertEquals(HttpStatus.UNAUTHORIZED, exception.getStatusCode());
- }
}
diff --git a/documentation/openapi/src/bundled.yaml b/documentation/openapi/src/bundled.yaml
deleted file mode 100644
index 3d4db60..0000000
--- a/documentation/openapi/src/bundled.yaml
+++ /dev/null
@@ -1,1007 +0,0 @@
-openapi: 3.0.3
-info:
- title: MyInpulse Backend API
- description: 'This serves as an OpenAPI documentation for the MyInpulse backend service, covering operations for Entrepreneurs, Admins, and shared functionalities.'
- version: 0.2.1
-tags:
- - name: Entrepreneurs API
- description: API endpoints primarily for Entrepreneur users.
- - name: Admin API
- description: API endpoints restricted to Admin users for management tasks.
- - name: Shared API
- description: API endpoints accessible by both Entrepreneurs and Admins.
- - name: Unauth API
- description: API endpoints related to user account management.
-components:
- schemas:
- user:
- type: object
- properties:
- idUser:
- type: integer
- description: Unique identifier for the user.
- example: 101
- userSurname:
- type: string
- description: User's surname (last name).
- example: Doe
- userName:
- type: string
- description: User's given name (first name).
- example: John
- primaryMail:
- type: string
- format: email
- description: User's primary email address.
- example: john.doe@example.com
- secondaryMail:
- type: string
- format: email
- description: User's secondary email address (optional).
- example: j.doe@personal.com
- phoneNumber:
- type: string
- description: User's phone number.
- example: '+33612345678'
- user-entrepreneur:
- allOf:
- - $ref: '#/components/schemas/user'
- - type: object
- properties:
- school:
- type: string
- description: The school the entrepreneur attends/attended.
- example: ENSEIRB-MATMECA
- course:
- type: string
- description: The specific course or program of study.
- example: Electronics
- sneeStatus:
- type: boolean
- description: Indicates if the user has SNEE status (Statut National d'Étudiant-Entrepreneur).
- example: true
- example:
- idUser: 101
- userSurname: Doe
- userName: John
- primaryMail: john.doe@example.com
- secondaryMail: j.doe@personal.com
- phoneNumber: '+33612345678'
- school: ENSEIRB-MATMECA
- course: Electronics
- sneeStatus: true
- user-admin:
- allOf:
- - $ref: '#/components/schemas/user'
- example:
- idUser: 55
- userSurname: Admin
- userName: Super
- primaryMail: admin@myinpulse.com
- phoneNumber: '+33512345678'
- sectionCell:
- type: object
- description: Represents a cell (like a sticky note) within a specific section of a project's Lean Canvas.
- properties:
- idSectionCell:
- type: integer
- description: Unique identifier for the section cell.
- example: 508
- sectionId:
- type: integer
- description: 'Identifier of the Lean Canvas section this cell belongs to (e.g., 1 for Problem, 2 for Solution).'
- example: 1
- contentSectionCell:
- type: string
- description: The text content of the section cell.
- example: Users find it hard to track project progress.
- modificationDate:
- type: string
- format: date
- description: The date when this cell was last modified.
- example: 'yyyy-MM-dd HH:mm'
- project:
- type: object
- description: Represents a project being managed or developed.
- properties:
- idProject:
- type: integer
- description: Unique identifier for the project.
- example: 12
- projectName:
- type: string
- description: The name of the project.
- example: MyInpulse Mobile App
- creationDate:
- type: string
- format: date
- description: The date when the project was created in the system.
- example: 'yyyy-MM-dd HH:mm'
- logo:
- type: string
- format: byte
- description: Base64 encoded string representing the project logo image.
- example: /*Base64 encoded string representing the project logo image*/
- status:
- type: string
- enum:
- - PENDING
- - ACTIVE
- - ENDED
- - ABORTED
- - REJECTED
- description: 'Corresponds to a status enum internal to the backend, it''s value in in requests incoming to the server should be ignored as the client shouldn''t be specifying them.'
- example: NaN
- report:
- type: object
- description: Represents a report associated with an appointment.
- properties:
- idReport:
- type: integer
- description: Unique identifier for the report.
- example: 987
- reportContent:
- type: string
- description: The textual content of the report. Could be plain text or Markdown (specify if known).
- example: Discussed roadmap milestones for Q3. Agreed on preliminary UI mockups.
- appointment:
- type: object
- description: Represents a scheduled meeting or appointment.
- properties:
- idAppointment:
- type: integer
- description: Unique identifier for the appointment.
- example: 303
- appointmentDate:
- type: string
- format: date
- description: The date of the appointment.
- example: '2025-05-10'
- appointmentTime:
- type: string
- format: time
- description: The time of the appointment (local time).
- example: '14:30:00'
- appointmentDuration:
- type: string
- description: 'Duration of the appointment in ISO 8601 duration format (e.g., PT1H30M for 1 hour 30 minutes).'
- example: PT1H
- appointmentPlace:
- type: string
- description: Location or meeting link for the appointment.
- example: 'Meeting Room 3 / https://meet.example.com/abc-def-ghi'
- appointmentSubject:
- type: string
- description: The main topic or subject of the appointment.
- example: Q3 Roadmap Planning
- joinRequest:
- type: object
- description: Represents a request from an entrepreneur to join an already existing project.
- properties:
- idProject:
- type: integer
- description: the ID of the project the entrepreneur wants to join.
- example: 42
- entrepreneur:
- $ref: '#/components/schemas/user-entrepreneur'
- projectDecision:
- type: object
- description: Represents a decision from an admin to accept a pending project.
- properties:
- projectId:
- type: integer
- description: The ID of the project the entrepreneur wants to join.
- example: 12
- adminId:
- type: integer
- description: The ID of the project the admin who will supervise the project in case of admission.
- example: 2
- isAccepted:
- type: boolean
- description: The boolean value of the decision.
- example: 'true'
- joinRequestDecision:
- type: object
- description: Represents a decision from an admin to accept a pending project join request.
- properties:
- isAccepted:
- type: boolean
- description: The boolean value of the decision.
- example: 'true'
- securitySchemes:
- MyINPulse:
- type: oauth2
- description: OAuth2 authentication using Keycloak.
- flows:
- implicit:
- authorizationUrl: '{keycloakBaseUrl}/realms/{keycloakRealm}/protocol/openid-connect/auth'
- scopes:
- MyINPulse-admin: Grants administrator access.
- MyINPulse-entrepreneur: Grants standard entrepreneur user access.
-servers:
- - url: '{serverProtocol}://{serverHost}:{serverPort}'
- description: API Server
- variables:
- serverProtocol:
- enum:
- - http
- - https
- default: http
- serverHost:
- default: localhost
- serverPort:
- enum:
- - '8081'
- default: '8081'
- keycloakBaseUrl:
- default: 'http://localhost:7080'
- description: Base URL for the Keycloak server.
- keycloakRealm:
- default: MyInpulseRealm
- description: Keycloak realm name.
-paths:
- /unauth/finalize:
- post:
- summary: Finalize account setup using authentication token
- description: |-
- Completes the user account creation/setup process in the MyInpulse system.
- This endpoint requires the user to be authenticated via Keycloak (e.g., after initial login).
- User details (name, email, etc.) are extracted from the authenticated user's token (e.g., Keycloak JWT).
- No request body is needed. The account is marked as pending admin validation upon successful finalization.
- tags:
- - Unauth API
- responses:
- '200':
- description: Created - Account finalized and pending admin validation. Returns the user profile.
- '400':
- description: Bad Request - Problem processing the token or user data derived from it.
- '401':
- description: Unauthorized - Valid authentication token required.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '/unauth/request-join/{projectId}':
- post:
- summary: Request to join an existing project
- description: Submits a request for the authenticated user (keycloack authenticated) to join the project specified by projectId. Their role is then changed to entrepreneur in server and Keycloak. This requires approval from a project admin.
- tags:
- - Unauth API
- parameters:
- - in: path
- name: projectId
- required: true
- schema:
- type: integer
- description: The ID of the project to request joining.
- example: 15
- responses:
- '200':
- description: Accepted - Join request submitted and pending approval.
- '400':
- description: Bad Request - Invalid project ID format
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '409':
- description: Already member/request pending.
- /admin/pending-accounts:
- get:
- operationId: getPendingAccounts
- summary: Get accounts awaiting validation
- description: Retrieves a list of entrepreneur user accounts that are pending admin validation.
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- responses:
- '200':
- description: OK - List of pending accounts returned.
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/user-entrepreneur'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '/admin/accounts/validate/{userId}':
- post:
- operationId: validateUserAccount
- summary: Validate a pending user account
- description: Marks the user account specified by userId as validated/active.
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- parameters:
- - in: path
- name: userId
- required: true
- schema:
- type: integer
- description: The ID of the user account to validate.
- example: 102
- responses:
- '200':
- description: No Content - Account validated successfully.
- '400':
- description: Bad Request - Invalid user ID format.
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- /admin/request-join:
- get:
- operationId: getPendingProjects
- summary: Get entrepreneurs project join requests
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- description: Retrieves a list of pending requests from entrepreneurs to join an existing project.
- responses:
- '200':
- description: OK - List of pending project join requests.
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/joinRequest'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '/admin/request-join/decision/{joinRequestId}':
- post:
- summary: Approve or reject a pending project join request
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- parameters:
- - in: path
- name: joinRequestId
- required: true
- schema:
- type: integer
- description: The ID of the pending join request to decide upon.
- description: |-
- Allows an admin to make a decision on an ebtrepreneur's request to join an existing project awaiting validation.
- If approved (isAccepted=true), the entrepreneur is linked to the project with ID joinRequestId.
- If rejected (isAccepted=false), the pending request data might be archived or deleted based on business logic.
- responses:
- '200':
- description: 'OK - No Content, decision processed successfully..'
- content:
- application/json:
- $ref: '#/components/schemas/joinRequestDecision'
- '400':
- description: 'Bad Request - Invalid input (e.g., missing decision).'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- /admin/projects:
- get:
- operationId: getAdminProjects
- summary: Get projects associated with the admin
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- description: 'Retrieves a list of projects managed by the requesting admin, including details for overview.'
- responses:
- '200':
- description: OK - List of projects returned successfully.
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/project'
- '400':
- description: 'Bad Request - Invalid project data provided (e.g., missing required fields).'
- '401':
- description: Unauthorized - Authentication required or invalid token.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- post:
- operationId: addProjectManually
- summary: Manually add a new project
- description: 'Creates a new project with the provided details. (NOTE that this meant for manually inserting projects, for example importing already existing projects).'
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- requestBody:
- required: true
- description: Project details to create. `idProject` and `creationDate` will be ignored if sent and set by the server.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/project'
- responses:
- '200':
- description: Created - Project added successfully. Returns the created project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/project'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '409':
- description: Bad Request - Project already exists.
- /admin/projects/pending:
- get:
- operationId: getPendingProjects
- summary: Get projects awaiting validation
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- description: Retrieves a list of projects submitted by entrepreneurs that are pending admin approval.
- responses:
- '200':
- description: OK - List of pending projects returned.
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/project'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- /admin/projects/pending/decision:
- post:
- operationId: decidePendingProject
- summary: Approve or reject a pending project
- tags:
- - Admin API
- description: |-
- Allows an admin to make a decision on a project awaiting validation.
- If approved (isAccepted=true), the project status changes, and it's linked to the involved users.
- If rejected (isAccepted=false), the pending project data might be archived or deleted based on business logic.
- security:
- - MyINPulse:
- - MyINPulse-admin
- requestBody:
- required: true
- description: Decision payload.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/projectDecision'
- responses:
- '200':
- description: No Content - Decision processed successfully.
- '400':
- description: 'Bad Request - Invalid input (e.g., missing decision).'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '/admin/appointments/report/{appointmentId}':
- post:
- operationId: createAppointmentReport
- summary: Create a report for an appointment
- description: 'Creates and links a new report (e.g., meeting minutes) to the specified appointment using the provided content.'
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- parameters:
- - in: path
- name: appointmentId
- required: true
- schema:
- type: integer
- description: ID of the appointment to add a report to.
- example: 303
- requestBody:
- required: true
- description: Report content. `idReport` will be ignored if sent.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/report'
- responses:
- '200':
- description: Created - Report created and linked successfully. Returns the created report.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/report'
- '400':
- description: 'Bad Request - Invalid input (e.g., missing content, invalid appointment ID format).'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- put:
- operationId: updateAppointmentReport
- summary: Update an existing appointment report
- description: Updates the content of an existing report linked to the specified appointment. Replaces the entire report content.
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- parameters:
- - in: path
- name: appointmentId
- required: true
- schema:
- type: integer
- description: ID of the appointment whose report needs updating.
- example: 303
- requestBody:
- required: true
- description: New report content. `idReport` in the body should match the existing report's ID or will be ignored.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/report'
- responses:
- '200':
- description: OK - Report updated successfully. Returns the updated report.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/report'
- '400':
- description: 'Bad Request - Invalid input (e.g., missing content).'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- /admin/appointments/upcoming:
- get:
- operationId: getUpcomingAppointments
- summary: Get upcoming appointments for an admin
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- description: Retrieves a list of appointments scheduled for an admin in the future.
- responses:
- '200':
- description: OK - List of upcoming appointments.
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/appointment'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '404':
- description: no appointments found.
- '/admin/projects/{projectId}':
- delete:
- operationId: removeProject
- summary: Remove a project
- description: Permanently removes the project specified by projectId and potentially related data (use with caution).
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- parameters:
- - in: path
- name: projectId
- required: true
- schema:
- type: integer
- description: The ID of the project to remove.
- example: 12
- responses:
- '200':
- description: No Content - Project removed successfully.
- '400':
- description: Bad Request - Invalid project ID format.
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '/admin/make-admin/{userId}':
- post:
- operationId: grantAdminRights
- summary: Grant admin rights to a user
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- description: Elevates the specified user to also have administrator privileges. Assumes the user already exists.
- parameters:
- - in: path
- name: userId
- required: true
- schema:
- type: integer
- description: The ID of the user to grant admin rights.
- example: 103
- responses:
- '200':
- description: No Content - Admin rights granted successfully.
- '400':
- description: Bad Request - Invalid user ID format or user is already an admin.
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- /admin/create-account:
- post:
- summary: Creates Admin out Jwt Token
- tags:
- - Admin API
- security:
- - MyINPulse:
- - MyINPulse-admin
- description: 'Create an admin instance in the MyINPulse DB of the information provided from the authenticated user''s keycloack token. The information required in the token are `userSurname`, `username`, `primaryMail`, `secondaryMail`, `phoneNumber`.'
- responses:
- '200':
- description: No Content - Admin user created successfully.
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '/shared/projects/sectionCells/{projectId}/{sectionId}/{date}':
- get:
- operationId: getSectionCellsByDate
- summary: Get project section cells modified on a specific date
- tags:
- - Shared API
- security:
- - MyINPulse:
- - MyINPulse-entrepreneur
- - MyINPulse-admin
- description: 'Retrieves section cells belonging to a specific section of a project, filtered by the last modification date. Requires user to have access to the project.'
- parameters:
- - in: path
- name: projectId
- required: true
- schema:
- type: integer
- description: ID of the project.
- - in: path
- name: sectionId
- required: true
- schema:
- type: integer
- description: ID of the Lean Canvas section.
- - in: path
- name: date
- required: true
- schema:
- type: string
- format: date
- description: 'The modification date to filter by (YYYY-MM-DD HH:mm).'
- responses:
- '200':
- description: OK - List of section cells matching the criteria.
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/sectionCell'
- '400':
- description: Bad Request - Invalid parameter format.
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '/shared/projects/entrepreneurs/{projectId}':
- get:
- operationId: getProjectEntrepreneurs
- summary: Get entrepreneurs associated with a project
- tags:
- - Shared API
- security:
- - MyINPulse:
- - MyINPulse-entrepreneur
- - MyINPulse-admin
- description: Retrieves a list of entrepreneur users associated with the specified project. Requires access to the project.
- parameters:
- - in: path
- name: projectId
- required: true
- schema:
- type: integer
- description: ID of the project.
- responses:
- '200':
- description: OK - List of entrepreneurs.
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/user-entrepreneur'
- '401':
- description: Unauthorized.
- '403':
- description: Forbidden - User does not have access to this project or invalid Keycloack configuration.
- '404':
- description: Not Found - Project not found.
- '/shared/projects/admin/{projectId}':
- get:
- operationId: getProjectAdmin
- summary: Get admin associated with a project
- tags:
- - Shared API
- security:
- - MyINPulse:
- - MyINPulse-entrepreneur
- - MyINPulse-admin
- description: Retrieves a list of admin users associated with the specified project. Requires access to the project.
- parameters:
- - in: path
- name: projectId
- required: true
- schema:
- type: integer
- description: ID of the project.
- responses:
- '200':
- description: OK - admin.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/user-admin'
- '401':
- description: Unauthorized.
- '403':
- description: Forbidden - User does not have access to this project or invalid Keycloack configuration.
- '404':
- description: Not Found - Project not found.
- '/shared/projects/appointments/{projectId}':
- get:
- operationId: getProjectAppointments
- summary: Get appointments related to a project
- tags:
- - Shared API
- security:
- - MyINPulse:
- - MyINPulse-entrepreneur
- - MyINPulse-admin
- description: Retrieves a list of appointments associated with the specified project. Requires access to the project.
- parameters:
- - in: path
- name: projectId
- required: true
- schema:
- type: integer
- description: ID of the project.
- responses:
- '200':
- description: OK - List of appointments.
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/appointment'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '/shared/appointments/report/{appointmentId}':
- get:
- operationId: getAppointmentReport
- summary: Get the report for an appointment
- tags:
- - Shared API
- security:
- - MyINPulse:
- - MyINPulse-entrepreneur
- - MyINPulse-admin
- description: Retrieves the report associated with a specific appointment. Requires user to have access to the appointment/project.
- parameters:
- - in: path
- name: appointmentId
- required: true
- schema:
- type: integer
- description: ID of the appointment.
- responses:
- '200':
- description: OK - Report PDF returned.
- content:
- application/pdf:
- schema:
- schema: null
- type: string
- format: binary
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- /shared/appointments/request:
- post:
- operationId: requestAppointment
- summary: Request a new appointment
- tags:
- - Shared API
- security:
- - MyINPulse:
- - MyINPulse-entrepreneur
- - MyINPulse-admin
- description: 'Allows a user (entrepreneur or admin) to request a new appointment, potentially with another user or regarding a project. Details in the body. The request might need confirmation or create a pending appointment.'
- requestBody:
- required: true
- description: Details of the appointment request.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/appointment'
- responses:
- '200':
- description: Accepted - Appointment request submitted.
- '400':
- description: Bad Request - Invalid appointment details.
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- /entrepreneur/projects:
- get:
- summary: gets the projectId of the project associated with the entrepreneur
- description: returns a list of projectIds of the projects associated with the entrepreneur
- tags:
- - Entrepreneurs API
- security:
- - MyINPulse:
- - MyINPulse-entrepreneur
- parameters: null
- responses:
- '200':
- description: OK - Section cell updated successfully. Returns the updated cell.
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/project'
- '401':
- description: Unauthorized or identity not found
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '404':
- description: Bad Request - Invalid input or ID mismatch.
- /entrepreneur/projects/request:
- post:
- operationId: requestProjectCreation
- summary: Request creation and validation of a new project
- tags:
- - Entrepreneurs API
- description: |-
- Submits a request for a new project. The project details are provided in the request body.
- The requesting entrepreneur (identified by the token) will be associated to it.
- The project is created with a 'pending' status, awaiting admin approval.
- security:
- - MyINPulse:
- - MyINPulse-entrepreneur
- requestBody:
- required: true
- description: 'Project details for the request. `status`, `creationDate` are required by the model when being sent but is ignored by the server; primarily expects a valid `projectId`, `name`, `logo`.'
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/project'
- responses:
- '200':
- description: Accepted - Project creation request received and is pending validation.
- '400':
- description: 'Bad Request - Invalid input (e.g., missing name).'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- /entrepreneur/sectionCells:
- post:
- operationId: addSectionCell
- summary: Add a cell to a Lean Canvas section
- description: Adds a new cell (like a sticky note) with the provided content to a specific section of the entrepreneur's project's Lean Canvas. Assumes project context is known based on user's token. `idSectionCell` and `modificationDate` are server-generated so they're values in the request are ignored by the server.
- tags:
- - Entrepreneurs API
- security:
- - MyINPulse:
- - MyINPulse-entrepreneur
- requestBody:
- required: true
- description: Section cell details. `idSectionCell` and `modificationDate` will be ignored if sent.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/sectionCell'
- responses:
- '200':
- description: Created - Section cell added successfully. Returns the created cell.
- '400':
- description: 'Bad Request - Invalid input (e.g., missing content or sectionId).'
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '/entrepreneur/sectionCells/{sectionCellId}':
- put:
- operationId: modifySectionCell
- summary: Modify data in a Lean Canvas section cell
- description: Updates the content of an existing Lean Canvas section cell specified by `sectionCellId`. The server "updates" (it keeps a record of the previous version to keep a history of all the sectionCells and creates a new ones with the specified modifications) the `modificationDate`.
- tags:
- - Entrepreneurs API
- security:
- - MyINPulse:
- - MyINPulse-entrepreneur
- parameters:
- - in: path
- name: sectionCellId
- required: true
- schema:
- type: integer
- description: The ID of the section cell to modify.
- example: 508
- requestBody:
- required: true
- description: Updated section cell details. `sectionCellId` "the path parameter" is the only id that's consideredn the `sectionCellId` id in the request body is ignored. `modificationDate` should be updated by the server.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/sectionCell'
- responses:
- '200':
- description: OK - Section cell updated successfully. Returns the updated cell.
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '404':
- description: Bad Request - Invalid input or ID mismatch.
- delete:
- operationId: removeSectionCell
- summary: Remove a Lean Canvas section cell
- description: Deletes the Lean Canvas section cell specified by `sectionCellId`.
- tags:
- - Entrepreneurs API
- security:
- - MyINPulse:
- - MyINPulse-entrepreneur
- parameters:
- - in: path
- name: sectionCellId
- required: true
- schema:
- type: integer
- description: The ID of the section cell to remove.
- example: 509
- responses:
- '200':
- description: No Content - Section cell removed successfully.
- '400':
- description: Bad Request - Invalid ID format.
- '401':
- description: Unauthorized.
- '403':
- description: Bad Token - Invalid Keycloack configuration.
- '404':
- description: Bad Request - sectionCell not found.
diff --git a/documentation/openapi/src/entrepreneurApi.yaml b/documentation/openapi/src/entrepreneurApi.yaml
index 4857132..78f5395 100644
--- a/documentation/openapi/src/entrepreneurApi.yaml
+++ b/documentation/openapi/src/entrepreneurApi.yaml
@@ -142,5 +142,56 @@ paths:
description: Bad Request - Invalid input or ID mismatch.
"401":
description: Unauthorized or identity not found
+ "403":
+ description: Bad Token - Invalid Keycloack configuration.
+
+
+ /entrepreneur/projects/project-is-active:
+ get:
+ summary: checks if the project associated with an entrepreneur is active
+ description: returns a boolean if the project associated with an entrepreneur has an active status
+ (i.e has been validated by an admin). The user should be routed to LeanCanvas. any other response code
+ should be treated as false
+ tags:
+ - Entrepreneurs API
+ security:
+ - MyINPulse: [MyINPulse-entrepreneur]
+ parameters:
+ responses:
+ "200":
+ description: OK - got the value successfully any other response code should be treated as false.
+ content:
+ application/json:
+ schema:
+ type: boolean
+ "404":
+ description: Bad Request - Invalid input or ID mismatch.
+ "401":
+ description: Unauthorized or identity not found
+ "403":
+ description: Bad Token - Invalid Keycloack configuration.
+
+ /entrepreneur/projects/has-pending-request:
+ get:
+ summary: checks if the user has a pending projectRequest
+ description: returns a boolean if the project associated with an entrepreneur has a pending status
+ (i.e has not yet been validated by an admin). The user should be routed to a page telling him that he should
+ wait for admin validation. any other response code should be treated as false.
+ tags:
+ - Entrepreneurs API
+ security:
+ - MyINPulse: [MyINPulse-entrepreneur]
+ parameters:
+ responses:
+ "200":
+ description: OK - got the value successfully any other response code should be treated as false.
+ content:
+ application/json:
+ schema:
+ type: boolean
+ "404":
+ description: Bad Request - Invalid input or ID mismatch.
+ "401":
+ description: Unauthorized or identity not found
"403":
description: Bad Token - Invalid Keycloack configuration.
\ No newline at end of file
diff --git a/documentation/openapi/src/main.yaml b/documentation/openapi/src/main.yaml
index 6239610..62d9b3a 100644
--- a/documentation/openapi/src/main.yaml
+++ b/documentation/openapi/src/main.yaml
@@ -79,6 +79,10 @@ paths:
$ref: "./unauthApi.yaml#/paths/~1unauth~1finalize"
/unauth/request-join/{projectId}:
$ref: "./unauthApi.yaml#/paths/~1unauth~1request-join~1{projectId}"
+ /unauth/request-admin-role:
+ $ref: "./unauthApi.yaml#/paths/~1unauth~1request-admin-role"
+ /unauth/check-if-not-pending:
+ $ref: "./unauthApi.yaml#/paths/~1unauth~1check-if-not-pending"
# _ ____ __ __ ___ _ _ _ ____ ___
# / \ | _ \| \/ |_ _| \ | | / \ | _ \_ _|
@@ -148,4 +152,8 @@ paths:
/entrepreneur/sectionCells:
$ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1sectionCells"
/entrepreneur/sectionCells/{sectionCellId}:
- $ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1sectionCells~1{sectionCellId}"
\ No newline at end of file
+ $ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1sectionCells~1{sectionCellId}"
+ /entrepreneur/projects/project-is-active:
+ $ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1projects~1project-is-active"
+ /entrepreneur/projects/has-pending-request:
+ $ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1projects~1has-pending-request"
\ No newline at end of file
diff --git a/documentation/openapi/src/sharedApi.yaml b/documentation/openapi/src/sharedApi.yaml
index 5a21aaa..ea260d5 100644
--- a/documentation/openapi/src/sharedApi.yaml
+++ b/documentation/openapi/src/sharedApi.yaml
@@ -70,7 +70,7 @@ paths:
"401":
description: Unauthorized.
"403":
- description: Forbidden - User does not have access to this project or invalid Keycloack configuration.
+ description: Bad Token - Invalid Keycloack configuration.
"404":
description: Not Found - Project not found.
@@ -99,7 +99,7 @@ paths:
"401":
description: Unauthorized.
"403":
- description: Forbidden - User does not have access to this project or invalid Keycloack configuration.
+ description: Bad Token - Invalid Keycloack configuration.
"404":
description: Not Found - Project not found.
diff --git a/documentation/openapi/src/unauthApi.yaml b/documentation/openapi/src/unauthApi.yaml
index 5150d99..fc7d555 100644
--- a/documentation/openapi/src/unauthApi.yaml
+++ b/documentation/openapi/src/unauthApi.yaml
@@ -53,7 +53,7 @@ paths:
description: Bad Token - Invalid Keycloack configuration.
/unauth/request-admin-role:
post:
- summary: Request to join an existing project
+ summary: Request to become an admin
description: Submits a request for the authenticated user (keycloack authenticated) to become an admin. Their role is then changed to admin in server and Keycloak. This requires approval from a project admin.
tags:
- Unauth API
@@ -65,4 +65,26 @@ paths:
"401":
description: Unauthorized.
"403":
- description: Bad Token - Invalid Keycloack configuration.
\ No newline at end of file
+ description: Bad Token - Invalid Keycloack configuration.
+
+ /unauth/check-if-not-pending:
+ get:
+ summary: Returns a boolean of whether the user's account is not pending
+ description: Returns a boolean with value `true` if the user's account is not pending and `false` if it is.
+ tags:
+ - Unauth API
+ responses:
+ "200":
+ description: Accepted - Become admin request submitted and pending approval.
+ content:
+ application/json:
+ schema:
+ type: boolean
+ "400":
+ description: Bad Request - Invalid project ID format or already member/request pending.
+ "401":
+ description: Unauthorized.
+ "404":
+ description: Bad Request - User not found in database.
+ "403":
+ description: Bad Token - Invalid Keycloack configuration.
diff --git a/front/Dockerfile b/front/Dockerfile
old mode 100644
new mode 100755
diff --git a/front/MyINPulse-front/index.html b/front/MyINPulse-front/index.html
index 9e5fc8f..a678cb5 100644
--- a/front/MyINPulse-front/index.html
+++ b/front/MyINPulse-front/index.html
@@ -1,13 +1,13 @@
-
+
-
-
-
-
- Vite App
-
-
-
-
-
+
+
+
+
+ Vite App
+
+
+
+
+
diff --git a/front/MyINPulse-front/package-lock.json b/front/MyINPulse-front/package-lock.json
index 8809acc..641b0b6 100644
--- a/front/MyINPulse-front/package-lock.json
+++ b/front/MyINPulse-front/package-lock.json
@@ -10,6 +10,7 @@
"dependencies": {
"axios": "^1.7.9",
"cors": "^2.8.5",
+ "jwt-decode": "^4.0.0",
"keycloak-js": "^26.1.0",
"pinia": "^2.3.1",
"pinia-plugin-persistedstate": "^4.2.0",
@@ -3588,6 +3589,15 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/jwt-decode": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
+ "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/keycloak-js": {
"version": "26.1.0",
"resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-26.1.0.tgz",
diff --git a/front/MyINPulse-front/package.json b/front/MyINPulse-front/package.json
index ff46180..57ec628 100644
--- a/front/MyINPulse-front/package.json
+++ b/front/MyINPulse-front/package.json
@@ -18,7 +18,8 @@
"pinia": "^2.3.1",
"pinia-plugin-persistedstate": "^4.2.0",
"vue": "^3.5.13",
- "vue-router": "^4.5.0"
+ "vue-router": "^4.5.0",
+ "jwt-decode": "^4.0.0"
},
"devDependencies": {
"@playwright/test": "^1.49.1",
diff --git a/front/MyINPulse-front/src/ApiClasses/Appointment.ts b/front/MyINPulse-front/src/ApiClasses/Appointment.ts
new file mode 100644
index 0000000..559eeb2
--- /dev/null
+++ b/front/MyINPulse-front/src/ApiClasses/Appointment.ts
@@ -0,0 +1,79 @@
+// appointment.ts
+class Appointment {
+ private _idAppointment?: number;
+ private _appointmentDate?: string;
+ private _appointmentTime?: string;
+ private _appointmentDuration?: string;
+ private _appointmentPlace?: string;
+ private _appointmentSubject?: string;
+
+ constructor(data: Partial = {}) {
+ this._idAppointment = data.idAppointment;
+ this._appointmentDate = data.appointmentDate;
+ this._appointmentTime = data.appointmentTime;
+ this._appointmentDuration = data.appointmentDuration;
+ this._appointmentPlace = data.appointmentPlace;
+ this._appointmentSubject = data.appointmentSubject;
+ }
+
+ get idAppointment(): number | undefined {
+ return this._idAppointment;
+ }
+
+ set idAppointment(value: number | undefined) {
+ this._idAppointment = value;
+ }
+
+ get appointmentDate(): string | undefined {
+ return this._appointmentDate;
+ }
+
+ set appointmentDate(value: string | undefined) {
+ this._appointmentDate = value;
+ }
+
+ get appointmentTime(): string | undefined {
+ return this._appointmentTime;
+ }
+
+ set appointmentTime(value: string | undefined) {
+ this._appointmentTime = value;
+ }
+
+ get appointmentDuration(): string | undefined {
+ return this._appointmentDuration;
+ }
+
+ set appointmentDuration(value: string | undefined) {
+ this._appointmentDuration = value;
+ }
+
+ get appointmentPlace(): string | undefined {
+ return this._appointmentPlace;
+ }
+
+ set appointmentPlace(value: string | undefined) {
+ this._appointmentPlace = value;
+ }
+
+ get appointmentSubject(): string | undefined {
+ return this._appointmentSubject;
+ }
+
+ set appointmentSubject(value: string | undefined) {
+ this._appointmentSubject = value;
+ }
+
+ toObject() {
+ return {
+ idAppointment: this.idAppointment,
+ appointmentDate: this.appointmentDate,
+ appointmentTime: this.appointmentTime,
+ appointmentDuration: this.appointmentDuration,
+ appointmentPlace: this.appointmentPlace,
+ appointmentSubject: this.appointmentSubject,
+ };
+ }
+}
+
+export default Appointment;
diff --git a/front/MyINPulse-front/src/ApiClasses/JoinRequest.ts b/front/MyINPulse-front/src/ApiClasses/JoinRequest.ts
new file mode 100644
index 0000000..2bc97df
--- /dev/null
+++ b/front/MyINPulse-front/src/ApiClasses/JoinRequest.ts
@@ -0,0 +1,39 @@
+// joinRequest.ts
+import UserEntrepreneur from "./UserEntrepreneur";
+
+class JoinRequest {
+ private _idProject?: number;
+ private _entrepreneur?: UserEntrepreneur;
+
+ constructor(data: Partial = {}) {
+ this._idProject = data.idProject;
+ this._entrepreneur = data.entrepreneur
+ ? new UserEntrepreneur(data.entrepreneur)
+ : undefined;
+ }
+
+ get idProject(): number | undefined {
+ return this._idProject;
+ }
+
+ set idProject(value: number | undefined) {
+ this._idProject = value;
+ }
+
+ get entrepreneur(): UserEntrepreneur | undefined {
+ return this._entrepreneur;
+ }
+
+ set entrepreneur(value: UserEntrepreneur | undefined) {
+ this._entrepreneur = value;
+ }
+
+ toObject() {
+ return {
+ idProject: this.idProject,
+ entrepreneur: this.entrepreneur,
+ };
+ }
+}
+
+export default JoinRequest;
diff --git a/front/MyINPulse-front/src/ApiClasses/JoinRequestDecision.ts b/front/MyINPulse-front/src/ApiClasses/JoinRequestDecision.ts
new file mode 100644
index 0000000..82f51e7
--- /dev/null
+++ b/front/MyINPulse-front/src/ApiClasses/JoinRequestDecision.ts
@@ -0,0 +1,24 @@
+// joinRequestDecision.ts
+class JoinRequestDecision {
+ private _isAccepted?: boolean;
+
+ constructor(data: Partial = {}) {
+ this._isAccepted = data.isAccepted;
+ }
+
+ get isAccepted(): boolean | undefined {
+ return this._isAccepted;
+ }
+
+ set isAccepted(value: boolean | undefined) {
+ this._isAccepted = value;
+ }
+
+ toObject() {
+ return {
+ isAccepted: this._isAccepted,
+ };
+ }
+}
+
+export default JoinRequestDecision;
diff --git a/front/MyINPulse-front/src/ApiClasses/Project.ts b/front/MyINPulse-front/src/ApiClasses/Project.ts
new file mode 100644
index 0000000..f445276
--- /dev/null
+++ b/front/MyINPulse-front/src/ApiClasses/Project.ts
@@ -0,0 +1,89 @@
+// project.ts
+class Project {
+ private _idProject?: number;
+ private _projectName?: string;
+ private _creationDate?: string;
+ private _logo?: string;
+ private _status?: "PENDING" | "ACTIVE" | "ENDED" | "ABORTED" | "REJECTED";
+
+ constructor(data: Partial = {}) {
+ this._idProject = data.idProject;
+ this._projectName = data.projectName;
+ this._creationDate = data.creationDate;
+ this._logo = data.logo;
+ this._status = data.status;
+ }
+
+ get idProject(): number | undefined {
+ return this._idProject;
+ }
+
+ set idProject(value: number | undefined) {
+ this._idProject = value;
+ }
+
+ get projectName(): string {
+ return this._projectName ?? "";
+ }
+
+ set projectName(value: string | undefined) {
+ this._projectName = value;
+ }
+
+ get creationDate(): string {
+ return this._creationDate ?? "";
+ }
+
+ set creationDate(value: string | undefined) {
+ this._creationDate = value;
+ }
+
+ get logo(): string | undefined {
+ return this._logo;
+ }
+
+ set logo(value: string | undefined) {
+ this._logo = value;
+ }
+
+ get status():
+ | "PENDING"
+ | "ACTIVE"
+ | "ENDED"
+ | "ABORTED"
+ | "REJECTED"
+ | undefined {
+ return this._status;
+ }
+
+ set status(
+ value:
+ | "PENDING"
+ | "ACTIVE"
+ | "ENDED"
+ | "ABORTED"
+ | "REJECTED"
+ | undefined
+ ) {
+ this._status = value;
+ }
+
+ toObject() {
+ return {
+ idProject: this.idProject,
+ projectName: this.projectName,
+ creationDate: this.creationDate,
+ logo: this.logo,
+ status: this.status,
+ };
+ }
+
+ toCreatePayload() {
+ return {
+ projectName: this.projectName,
+ logo: this.logo,
+ };
+ }
+}
+
+export default Project;
diff --git a/front/MyINPulse-front/src/ApiClasses/ProjectDecision.ts b/front/MyINPulse-front/src/ApiClasses/ProjectDecision.ts
new file mode 100644
index 0000000..82a1e7f
--- /dev/null
+++ b/front/MyINPulse-front/src/ApiClasses/ProjectDecision.ts
@@ -0,0 +1,46 @@
+// projectDecision.ts
+class ProjectDecision {
+ private _projectId?: number;
+ private _adminId?: number;
+ private _isAccepted?: boolean;
+
+ constructor(data: Partial = {}) {
+ this._projectId = data.projectId;
+ this._adminId = data.adminId;
+ this._isAccepted = data.isAccepted;
+ }
+
+ get projectId(): number | undefined {
+ return this._projectId;
+ }
+
+ set projectId(value: number | undefined) {
+ this._projectId = value;
+ }
+
+ get adminId(): number | undefined {
+ return this._adminId;
+ }
+
+ set adminId(value: number | undefined) {
+ this._adminId = value;
+ }
+
+ get isAccepted(): boolean | undefined {
+ return this._isAccepted;
+ }
+
+ set isAccepted(value: boolean | undefined) {
+ this._isAccepted = value;
+ }
+
+ toObject() {
+ return {
+ projectId: this._projectId,
+ adminId: this._adminId,
+ isAccepted: this._isAccepted,
+ };
+ }
+}
+
+export default ProjectDecision;
diff --git a/front/MyINPulse-front/src/ApiClasses/Repport.ts b/front/MyINPulse-front/src/ApiClasses/Repport.ts
new file mode 100644
index 0000000..b4cc304
--- /dev/null
+++ b/front/MyINPulse-front/src/ApiClasses/Repport.ts
@@ -0,0 +1,35 @@
+// report.ts
+class Report {
+ private _idReport?: number;
+ private _reportContent?: string;
+
+ constructor(data: Partial = {}) {
+ this._idReport = data.idReport;
+ this._reportContent = data.reportContent;
+ }
+
+ get idReport(): number | undefined {
+ return this._idReport;
+ }
+
+ set idReport(value: number | undefined) {
+ this._idReport = value;
+ }
+
+ get reportContent(): string | undefined {
+ return this._reportContent;
+ }
+
+ set reportContent(value: string | undefined) {
+ this._reportContent = value;
+ }
+
+ toObject() {
+ return {
+ idReport: this._idReport,
+ reportContent: this._reportContent,
+ };
+ }
+}
+
+export default Report;
diff --git a/front/MyINPulse-front/src/ApiClasses/SectionCell.ts b/front/MyINPulse-front/src/ApiClasses/SectionCell.ts
new file mode 100644
index 0000000..acf6912
--- /dev/null
+++ b/front/MyINPulse-front/src/ApiClasses/SectionCell.ts
@@ -0,0 +1,57 @@
+// sectionCell.ts
+class SectionCell {
+ private _idSectionCell?: number;
+ private _sectionId?: number;
+ private _contentSectionCell?: string;
+ private _modificationDate?: string;
+
+ constructor(data: Partial = {}) {
+ this._idSectionCell = data.idSectionCell;
+ this._sectionId = data.sectionId;
+ this._contentSectionCell = data.contentSectionCell;
+ this._modificationDate = data.modificationDate;
+ }
+
+ get idSectionCell(): number | undefined {
+ return this._idSectionCell;
+ }
+
+ set idSectionCell(value: number | undefined) {
+ this._idSectionCell = value;
+ }
+
+ get sectionId(): number | undefined {
+ return this._sectionId;
+ }
+
+ set sectionId(value: number | undefined) {
+ this._sectionId = value;
+ }
+
+ get contentSectionCell(): string | undefined {
+ return this._contentSectionCell;
+ }
+
+ set contentSectionCell(value: string | undefined) {
+ this._contentSectionCell = value;
+ }
+
+ get modificationDate(): string | undefined {
+ return this._modificationDate;
+ }
+
+ set modificationDate(value: string | undefined) {
+ this._modificationDate = value;
+ }
+
+ toObject() {
+ return {
+ idSectionCell: this._idSectionCell,
+ sectionId: this._sectionId,
+ contentSectionCell: this._contentSectionCell,
+ modificationDate: this._modificationDate,
+ };
+ }
+}
+
+export default SectionCell;
diff --git a/front/MyINPulse-front/src/ApiClasses/User.ts b/front/MyINPulse-front/src/ApiClasses/User.ts
new file mode 100644
index 0000000..d0484a4
--- /dev/null
+++ b/front/MyINPulse-front/src/ApiClasses/User.ts
@@ -0,0 +1,78 @@
+class User {
+ private _idUser?: number;
+ private _userSurname?: string;
+ private _userName?: string;
+ private _primaryMail?: string;
+ private _secondaryMail?: string;
+ private _phoneNumber?: string;
+
+ constructor(data: Partial = {}) {
+ this._idUser = data.idUser;
+ this._userSurname = data.userSurname;
+ this._userName = data.userName;
+ this._primaryMail = data.primaryMail;
+ this._secondaryMail = data.secondaryMail;
+ this._phoneNumber = data.phoneNumber;
+ }
+
+ get idUser(): number | undefined {
+ return this._idUser;
+ }
+
+ set idUser(value: number | undefined) {
+ this._idUser = value;
+ }
+
+ get userSurname(): string | undefined {
+ return this._userSurname;
+ }
+
+ set userSurname(value: string | undefined) {
+ this._userSurname = value;
+ }
+
+ get userName(): string | undefined {
+ return this._userName;
+ }
+
+ set userName(value: string | undefined) {
+ this._userName = value;
+ }
+
+ get primaryMail(): string | undefined {
+ return this._primaryMail;
+ }
+
+ set primaryMail(value: string | undefined) {
+ this._primaryMail = value;
+ }
+
+ get secondaryMail(): string | undefined {
+ return this._secondaryMail;
+ }
+
+ set secondaryMail(value: string | undefined) {
+ this._secondaryMail = value;
+ }
+
+ get phoneNumber(): string | undefined {
+ return this._phoneNumber;
+ }
+
+ set phoneNumber(value: string | undefined) {
+ this._phoneNumber = value;
+ }
+
+ toObject() {
+ return {
+ idUser: this._idUser,
+ userSurname: this._userSurname,
+ userName: this._userName,
+ primaryMail: this._primaryMail,
+ secondaryMail: this._secondaryMail,
+ phoneNumber: this._phoneNumber,
+ };
+ }
+}
+
+export default User;
diff --git a/front/MyINPulse-front/src/ApiClasses/UserAdmin.ts b/front/MyINPulse-front/src/ApiClasses/UserAdmin.ts
new file mode 100644
index 0000000..27e3bfb
--- /dev/null
+++ b/front/MyINPulse-front/src/ApiClasses/UserAdmin.ts
@@ -0,0 +1,14 @@
+// user-admin.ts
+import User from "./User";
+
+class UserAdmin extends User {
+ constructor(data: Partial = {}) {
+ super(data);
+ }
+
+ get idUser(): number | undefined {
+ return super.idUser;
+ }
+}
+
+export default UserAdmin;
diff --git a/front/MyINPulse-front/src/ApiClasses/UserEntrepreneur.ts b/front/MyINPulse-front/src/ApiClasses/UserEntrepreneur.ts
new file mode 100644
index 0000000..0a340a5
--- /dev/null
+++ b/front/MyINPulse-front/src/ApiClasses/UserEntrepreneur.ts
@@ -0,0 +1,50 @@
+// user-entrepreneur.ts
+import User from "./User";
+
+class UserEntrepreneur extends User {
+ private _school?: string;
+ private _course?: string;
+ private _sneeStatus?: boolean;
+
+ constructor(data: Partial = {}) {
+ super(data);
+ this._school = data.school;
+ this._course = data.course;
+ this._sneeStatus = data.sneeStatus;
+ }
+
+ get school(): string | undefined {
+ return this._school;
+ }
+
+ set school(value: string | undefined) {
+ this._school = value;
+ }
+
+ get course(): string | undefined {
+ return this._course;
+ }
+
+ set course(value: string | undefined) {
+ this._course = value;
+ }
+
+ get sneeStatus(): boolean | undefined {
+ return this._sneeStatus;
+ }
+
+ set sneeStatus(value: boolean | undefined) {
+ this._sneeStatus = value;
+ }
+
+ toObject() {
+ return {
+ ...super.toObject(),
+ school: this._school,
+ course: this._course,
+ sneeStatus: this._sneeStatus,
+ };
+ }
+}
+
+export default UserEntrepreneur;
diff --git a/front/MyINPulse-front/src/App.vue b/front/MyINPulse-front/src/App.vue
index feac4cf..d359e36 100644
--- a/front/MyINPulse-front/src/App.vue
+++ b/front/MyINPulse-front/src/App.vue
@@ -1,47 +1,12 @@
-
-
-