diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 842c9ae..868293e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,36 +1,22 @@
-# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
-# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
-
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-
name: Build Project
on:
- push:
- branches: [ "main", "develop" ]
pull_request:
- branches: [ "main" ]
+ branches: [ "main", "develop", "release" ]
jobs:
- build:
-
+ Build:
runs-on: self-hosted
-
steps:
- - uses: actions/checkout@v4
- - name: Install Maven
- run: sudo apt-get install -y maven
- - name: Set up JDK 21
- uses: actions/setup-java@v4
- with:
- java-version: '21'
- distribution: 'temurin'
- cache: maven
- - name: Build with Maven
- env:
- DATASOURCE_PASSWORD: ${{ secrets.DATASOURCE_PASSWORD }}
- DATASOURCE_URL: ${{ secrets.DATASOURCE_URL }}
- DATASOURCE_USERNAME: ${{ secrets.DATASOURCE_USERNAME }}
- run: mvn clean install
+ - uses: actions/checkout@v4
+
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'temurin'
+ cache: maven
+
+ - name: Build with Maven
+ env:
+ CONFIG_IP: ${{ secrets.DEV_INTEG_HOST }}
+ run: mvn clean install
diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml
new file mode 100644
index 0000000..8a79107
--- /dev/null
+++ b/.github/workflows/deploy-dev.yml
@@ -0,0 +1,36 @@
+name: Deploy Develop
+on:
+ pull_request:
+ branches:
+ - develop
+ types:
+ - closed
+ workflow_dispatch:
+jobs:
+ Deploy:
+ name: Deploy on Develop
+ if: ${{ github.event.pull_request.merged == true }}
+ runs-on: self-hosted
+ steps:
+ - name: executing remote ssh commands using ssh key
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.DEV_HOST }}
+ username: ${{ secrets.HOSTS_USERNAME }}
+ key: ${{ secrets.DEV_SSH_KEY }}
+ port: ${{ secrets.SSH_PORT }}
+ script: |
+ cd AllConnected/${{ github.event.repository.name }}
+ echo "Fetching latest code..."
+ git fetch
+ git checkout develop
+ git pull
+ echo "Building Docker image..."
+ docker build -t ${{ github.event.repository.name }} .
+ echo "Creating .env file..."
+ echo "PROFILE=dev" >> .env
+ echo "CONFIG_IP=10.43.101.114" >> .env
+ docker rm -f ${{ github.event.repository.name }}
+ docker run --name ${{ github.event.repository.name }} --network all_connected -d -p ${{ secrets.SERVICE_PORT }}:8080 --env-file .env ${{ github.event.repository.name }}
+ echo "Docker container running..."
+ rm .env
diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml
new file mode 100644
index 0000000..b292e56
--- /dev/null
+++ b/.github/workflows/deploy-prod.yml
@@ -0,0 +1,33 @@
+name: Deploy PROD
+on:
+ push:
+ tags:
+ - '*'
+ workflow_dispatch:
+jobs:
+ Deploy:
+ name: Deploy on PROD
+ runs-on: self-hosted
+ steps:
+ - name: executing remote ssh commands using ssh key
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.PROD_G1_HOST }}
+ username: ${{ secrets.HOSTS_USERNAME }}
+ key: ${{ secrets.PROD_G1_SSH_KEY }}
+ port: ${{ secrets.SSH_PORT }}
+ script: |
+ cd AllConnected/${{ github.event.repository.name }}
+ echo "Fetching latest code..."
+ git fetch
+ git checkout main
+ git pull
+ echo "Building Docker image..."
+ docker build -t ${{ github.event.repository.name }} .
+ echo "Creating .env file..."
+ echo "PROFILE=prod1" >> .env
+ echo "CONFIG_IP=10.43.101.72" >> .env
+ docker rm -f ${{ github.event.repository.name }}
+ docker run --name ${{ github.event.repository.name }} --network all_connected -d -p ${{ secrets.SERVICE_PORT }}:8080 --env-file .env ${{ github.event.repository.name }}
+ echo "Docker container running..."
+ rm .env
diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml
new file mode 100644
index 0000000..12b532f
--- /dev/null
+++ b/.github/workflows/deploy-qa.yml
@@ -0,0 +1,36 @@
+name: Deploy QA
+on:
+ pull_request:
+ branches:
+ - release
+ types:
+ - closed
+ workflow_dispatch:
+jobs:
+ Deploy:
+ name: Deploy on QA
+ if: ${{ github.event.pull_request.merged == true }}
+ runs-on: self-hosted
+ steps:
+ - name: executing remote ssh commands using ssh key
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.QA_G1_HOST }}
+ username: ${{ secrets.HOSTS_USERNAME }}
+ key: ${{ secrets.QA_G1_SSH_KEY }}
+ port: ${{ secrets.SSH_PORT }}
+ script: |
+ cd AllConnected/${{ github.event.repository.name }}
+ echo "Fetching latest code..."
+ git fetch
+ git checkout release
+ git pull
+ echo "Building Docker image..."
+ docker build -t ${{ github.event.repository.name }} .
+ echo "Creating .env file..."
+ echo "PROFILE=qa1" >> .env
+ echo "CONFIG_IP=10.43.100.223" >> .env
+ docker rm -f ${{ github.event.repository.name }}
+ docker run --name ${{ github.event.repository.name }} --network all_connected -d -p ${{ secrets.SERVICE_PORT }}:8080 --env-file .env ${{ github.event.repository.name }}
+ echo "Docker container running..."
+ rm .env
diff --git a/.gitignore b/.gitignore
index 549e00a..8c68a52 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,9 @@ build/
### VS Code ###
.vscode/
+
+### Firebase ###
+spring-firebase-key.json
+
+### Enviroment variables ###
+.env
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..d8ac2ee
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,24 @@
+# Etapa 1: Build
+FROM maven:3.9.4-eclipse-temurin-21 AS build
+
+WORKDIR /app
+
+# Copiar el pom y código fuente
+COPY pom.xml ./
+RUN mvn dependency:go-offline
+
+COPY src ./src
+
+RUN mvn clean package -DskipTests
+
+# Etapa 2: Run
+FROM eclipse-temurin:21-jdk-jammy
+
+WORKDIR /app
+
+# Copiar el jar generado desde la etapa de build
+COPY --from=build /app/target/*.jar app.jar
+
+EXPOSE 8080
+
+ENTRYPOINT ["java", "-jar", "app.jar"]
diff --git a/README.md b/README.md
index aec6ca9..79467bd 100644
--- a/README.md
+++ b/README.md
@@ -21,12 +21,23 @@ Microservicio encargado del CRUD de usuarios de la aplicación AllConnected
Para ejecutar este proyecto, necesitarás agregar las siguientes variables de entorno a tu archivo .env
-`DATASOURCE_URL`
+### Conexion a la base de datos
+`DATASOURCE_URL`
`DATASOURCE_USERNAME`
-
`DATASOURCE_PASSWORD`
+### Configuración de Firebase
+Estas variables son extraidas del archivo de configuración de Firebase en formato JSON
+
+`GOOGLE_ADMIN_CONFIG_TYPE`
+`GOOGLE_ADMIN_CONFIG_PROJECT_ID`
+`GOOGLE_ADMIN_CONFIG_PRIVATE_KEY`
+`GOOGLE_ADMIN_CONFIG_PRIVATE_KEY_ID`
+`GOOGLE_ADMIN_CONFIG_CLIENT_EMAIL`
+`GOOGLE_ADMIN_CONFIG_CLIENT_ID`
+`GOOGLE_ADMIN_CONFIG_CLIENT_X509_CERT_URL`
+
---
## Ejecutar Localmente 💻
@@ -54,6 +65,23 @@ Inicia el servidor
mvn spring-boot:run
```
+---
+## Ejecutar con Docker 🐋
+
+```bash
+ git clone https://github.com/FusionTech-2430/users-service
+```
+
+Ve al directorio del proyecto
+
+```bash
+ cd users-service
+```
+
+```bash
+ docker-compose up
+```
+
---
## Autores 🧑🏻💻
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..e6de309
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,20 @@
+version: '3.8'
+
+services:
+ spring-app:
+ build: .
+ ports:
+ - "8080:8080"
+ env_file:
+ - .env
+ environment:
+ - DATASOURCE_URL=${DATASOURCE_URL}
+ - DATASOURCE_USERNAME=${DATASOURCE_USERNAME}
+ - DATASOURCE_PASSWORD=${DATASOURCE_PASSWORD}
+ - GOOGLE_ADMIN_CONFIG_TYPE=${GOOGLE_ADMIN_CONFIG_TYPE}
+ - GOOGLE_ADMIN_CONFIG_PROJECT_ID=${GOOGLE_ADMIN_CONFIG_PROJECT_ID}
+ - GOOGLE_ADMIN_CONFIG_PRIVATE_KEY=${GOOGLE_ADMIN_CONFIG_PRIVATE_KEY}
+ - GOOGLE_ADMIN_CONFIG_PRIVATE_KEY_ID=${GOOGLE_ADMIN_CONFIG_PRIVATE_KEY_ID}
+ - GOOGLE_ADMIN_CONFIG_CLIENT_EMAIL=${GOOGLE_ADMIN_CONFIG_CLIENT_EMAIL}
+ - GOOGLE_ADMIN_CONFIG_CLIENT_ID=${GOOGLE_ADMIN_CONFIG_CLIENT_ID}
+ - GOOGLE_ADMIN_CONFIG_CLIENT_X509_CERT_URL=${GOOGLE_ADMIN_CONFIG_CLIENT_X509_CERT_URL}
diff --git a/pom.xml b/pom.xml
index 38890cf..2873cd8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,7 +47,14 @@
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
-
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.cloud
+ spring-cloud-starter-config
+
org.springframework.boot
spring-boot-devtools
@@ -64,6 +71,14 @@
lombok
true
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.cloud
+ spring-cloud-starter-config
+
org.springframework.boot
spring-boot-starter-test
@@ -74,6 +89,23 @@
reactor-test
test
+
+
+ com.google.firebase
+ firebase-admin
+ 9.1.0
+
+
+ com.google.cloud
+ google-cloud-storage
+ 2.11.0
+
+
+
+ commons-io
+ commons-io
+ 2.16.1
+
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/config/FirebaseConfig.java b/src/main/java/co/allconnected/fussiontech/usersservice/config/FirebaseConfig.java
new file mode 100644
index 0000000..b8a89ec
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/config/FirebaseConfig.java
@@ -0,0 +1,43 @@
+package co.allconnected.fussiontech.usersservice.config;
+
+import com.google.auth.oauth2.GoogleCredentials;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.FirebaseOptions;
+import com.google.gson.Gson;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+@Configuration
+public class FirebaseConfig {
+
+ private final FirebaseConfigProperties firebaseConfigProperties;
+
+ public FirebaseConfig(FirebaseConfigProperties firebaseConfigProperties) {
+ this.firebaseConfigProperties = firebaseConfigProperties;
+ }
+
+ @PostConstruct
+ public FirebaseApp initializeFirebase() throws IOException {
+ firebaseConfigProperties.setPrivate_key(
+ firebaseConfigProperties.getPrivate_key().replace("\\n", "\n")
+ );
+
+ String json = new Gson().toJson(firebaseConfigProperties);
+
+ GoogleCredentials credentials = GoogleCredentials.fromStream(new ByteArrayInputStream(json.getBytes()));
+
+ FirebaseOptions options = FirebaseOptions.builder()
+ .setCredentials(credentials)
+ .setStorageBucket(firebaseConfigProperties.getProject_id()+".appspot.com")
+ .build();
+
+ if(FirebaseApp.getApps().isEmpty()) {
+ FirebaseApp.initializeApp(options);
+ }
+
+ return FirebaseApp.getInstance();
+ }
+}
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/config/FirebaseConfigProperties.java b/src/main/java/co/allconnected/fussiontech/usersservice/config/FirebaseConfigProperties.java
new file mode 100644
index 0000000..80482e0
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/config/FirebaseConfigProperties.java
@@ -0,0 +1,22 @@
+package co.allconnected.fussiontech.usersservice.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "firebase")
+@Data
+public class FirebaseConfigProperties {
+ private String type;
+ private String project_id;
+ private String private_key;
+ private String private_key_id;
+ private String client_email;
+ private String client_id;
+ private String auth_uri;
+ private String token_uri;
+ private String auth_provider_x509_cert_url;
+ private String client_x509_cert_url;
+ private String universe_domain;
+}
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/controllers/UsersController.java b/src/main/java/co/allconnected/fussiontech/usersservice/controllers/UsersController.java
new file mode 100644
index 0000000..2ff4bfd
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/controllers/UsersController.java
@@ -0,0 +1,155 @@
+package co.allconnected.fussiontech.usersservice.controllers;
+
+import co.allconnected.fussiontech.usersservice.dtos.*;
+import co.allconnected.fussiontech.usersservice.services.UserService;
+import co.allconnected.fussiontech.usersservice.utils.OperationException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+@RestController
+@RequestMapping("/api/v1/users")
+public class UsersController {
+
+ private final UserService userService;
+
+ @Autowired
+ public UsersController(UserService userService) {
+ this.userService = userService;
+ }
+
+ @PostMapping
+ public ResponseEntity> createUser(
+ @ModelAttribute UserDTO user,
+ @RequestParam(value = "photo", required = false) MultipartFile photo) {
+ try {
+ UserDTO userDTO = userService.createUser(user, photo);
+ return ResponseEntity.status(HttpStatus.CREATED).body(userDTO);
+ } catch (OperationException e) {
+ return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage()));
+ } catch (RuntimeException e) {
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()));
+ }
+ }
+
+ @PostMapping("/from-admin")
+ public ResponseEntity> createUserFromAdmin(
+ @ModelAttribute UserCreateDTO user,
+ @RequestParam(value = "photo", required = false) MultipartFile photo) {
+ try {
+ UserDTO userDTO = userService.createUserFromAdmin(user, photo);
+ return ResponseEntity.status(HttpStatus.CREATED).body(userDTO);
+ } catch (OperationException e) {
+ return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage()));
+ } catch (RuntimeException e) {
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()));
+ }
+ }
+
+ @PostMapping("/guest")
+ public ResponseEntity> createGuestUser() {
+ try {
+ UserDTO userDTO = userService.createGuestUser();
+ return ResponseEntity.status(HttpStatus.CREATED).body(userDTO);
+ } catch (OperationException e) {
+ return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage()));
+ }
+ }
+
+ @GetMapping
+ public ResponseEntity> getUsers(
+ @RequestParam(value = "fullname", required = false) String fullname,
+ @RequestParam(value = "username", required = false) String username,
+ @RequestParam(value = "mail", required = false) String mail,
+ @RequestParam(value = "rol", required = false) String rol,
+ @RequestParam(value = "active", required = false) Boolean active
+ ) {
+ try {
+ UserDTO[] listUsersDTO = userService.getUsers(fullname, username, mail, rol, active);
+ return ResponseEntity.status(HttpStatus.OK).body(listUsersDTO);
+ } catch (OperationException e) {
+ return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage()));
+ } catch (RuntimeException e) {
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()));
+ }
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity> getUser(@PathVariable String id) {
+ try {
+ return ResponseEntity.status(HttpStatus.OK).body(userService.getUser(id));
+ } catch (OperationException e) {
+ return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage()));
+ }
+ }
+
+ @PutMapping("/{id}")
+ public ResponseEntity> updateUser(
+ @PathVariable String id,
+ @ModelAttribute UserCreateDTO user,
+ @RequestParam(value = "photo", required = false) MultipartFile photo) {
+ try {
+ System.out.println("Actualizando usuario");
+ UserDTO userDTO = userService.updateUser(id, user, photo);
+ ResponseEntity response = ResponseEntity.status(HttpStatus.OK).body(userDTO);
+ System.out.println(response.getHeaders());
+ return response;
+ } catch (OperationException e) {
+ return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage()));
+ } catch (RuntimeException e) {
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()));
+ }
+ }
+
+ @DeleteMapping("/{id}")
+ public ResponseEntity> deleteUser(@PathVariable String id) {
+ try {
+ userService.deleteUser(id);
+ return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
+ } catch (OperationException e) {
+ return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage()));
+ }
+ }
+
+ @PostMapping("/{id}/deactivate")
+ public ResponseEntity> deactivateUser(@PathVariable String id, @RequestBody DeleteRequestDTO deleteRequest) {
+ try {
+ DeletedDTO deletedDTO = userService.deactivateUser(id, deleteRequest.delete_reason());
+ return ResponseEntity.status(HttpStatus.OK).body(deletedDTO);
+ } catch (OperationException e) {
+ return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage()));
+ }
+ }
+
+ @PostMapping("/{id}/activate")
+ public ResponseEntity> activateUser(@PathVariable String id) {
+ try {
+ UserDTO user = userService.activateUser(id);
+ return ResponseEntity.status(HttpStatus.OK).body(user);
+ } catch (OperationException e) {
+ return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage()));
+ }
+ }
+
+ @PostMapping("/{id}/roles")
+ public ResponseEntity> addRole(@PathVariable String id, @RequestBody RolesDTO roles) {
+ try {
+ UserDTO userDTO = userService.addRoles(id, roles.roles());
+ return ResponseEntity.status(HttpStatus.CREATED).body(userDTO);
+ } catch (OperationException e) {
+ return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage()));
+ }
+ }
+
+ @DeleteMapping("/{id}/roles/{rol}")
+ public ResponseEntity> removeRole(@PathVariable String id, @PathVariable String rol) {
+ try {
+ UserDTO userDTO = userService.removeRoles(id, rol);
+ return ResponseEntity.status(HttpStatus.OK).body(userDTO);
+ } catch (OperationException e) {
+ return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/controllers/fileToRemove.txt b/src/main/java/co/allconnected/fussiontech/usersservice/controllers/fileToRemove.txt
deleted file mode 100644
index 7755816..0000000
--- a/src/main/java/co/allconnected/fussiontech/usersservice/controllers/fileToRemove.txt
+++ /dev/null
@@ -1 +0,0 @@
-Este archivo es para dejar la estructura de carpetas del proyecto. Eliminar cuando se tenga el proyecto.
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/dtos/DeleteRequestDTO.java b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/DeleteRequestDTO.java
new file mode 100644
index 0000000..092bb2c
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/DeleteRequestDTO.java
@@ -0,0 +1,3 @@
+package co.allconnected.fussiontech.usersservice.dtos;
+public record DeleteRequestDTO(String delete_reason) {
+}
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/dtos/DeletedDTO.java b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/DeletedDTO.java
new file mode 100644
index 0000000..44d8c0c
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/DeletedDTO.java
@@ -0,0 +1,8 @@
+// src/main/java/co/allconnected/fussiontech/usersservice/dtos/DeletedDTO.java
+package co.allconnected.fussiontech.usersservice.dtos;
+
+import java.io.Serializable;
+import java.time.Instant;
+
+public record DeletedDTO(String id_user, String delete_reason, Instant delete_date) implements Serializable {
+}
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/dtos/InactiveUserDTO.java b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/InactiveUserDTO.java
new file mode 100644
index 0000000..0525dcf
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/InactiveUserDTO.java
@@ -0,0 +1,21 @@
+package co.allconnected.fussiontech.usersservice.dtos;
+
+import co.allconnected.fussiontech.usersservice.model.User;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.time.Instant;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class InactiveUserDTO extends UserDTO{
+ private String delete_reason;
+ private Instant delete_date;
+ public InactiveUserDTO(User user){
+ super(user);
+ this.delete_reason = user.getDeleted().getReason();
+ this.delete_date = user.getDeleted().getDeleteDate();
+ }
+}
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/dtos/Response.java b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/Response.java
new file mode 100644
index 0000000..85a6af6
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/Response.java
@@ -0,0 +1,4 @@
+package co.allconnected.fussiontech.usersservice.dtos;
+
+public record Response(int code, String message) {
+}
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/dtos/RolesDTO.java b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/RolesDTO.java
new file mode 100644
index 0000000..a1a7073
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/RolesDTO.java
@@ -0,0 +1,4 @@
+package co.allconnected.fussiontech.usersservice.dtos;
+
+public record RolesDTO(String[] roles) {
+}
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/dtos/UserCreateDTO.java b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/UserCreateDTO.java
new file mode 100644
index 0000000..4c5915d
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/UserCreateDTO.java
@@ -0,0 +1,4 @@
+package co.allconnected.fussiontech.usersservice.dtos;
+
+public record UserCreateDTO(String fullname, String username, String password, String mail, String[] roles) {
+}
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/dtos/UserDTO.java b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/UserDTO.java
new file mode 100644
index 0000000..2757db0
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/UserDTO.java
@@ -0,0 +1,30 @@
+package co.allconnected.fussiontech.usersservice.dtos;
+
+import co.allconnected.fussiontech.usersservice.model.Rol;
+import co.allconnected.fussiontech.usersservice.model.User;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class UserDTO {
+ private String id_user;
+ private String fullname;
+ private String username;
+ private String mail;
+ private String photo_url;
+ private String[] roles;
+ private boolean active;
+
+ public UserDTO(User user){
+ this.id_user = user.getIdUser();
+ this.fullname = user.getFullname();
+ this.username = user.getUsername();
+ this.mail = user.getMail();
+ this.photo_url = user.getPhotoUrl();
+ this.roles = user.getRoles().stream().map(Rol::getIdRol).toArray(String[]::new);
+ this.active = user.getActive();
+ }
+}
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/dtos/fileToRemove.txt b/src/main/java/co/allconnected/fussiontech/usersservice/dtos/fileToRemove.txt
deleted file mode 100644
index 7755816..0000000
--- a/src/main/java/co/allconnected/fussiontech/usersservice/dtos/fileToRemove.txt
+++ /dev/null
@@ -1 +0,0 @@
-Este archivo es para dejar la estructura de carpetas del proyecto. Eliminar cuando se tenga el proyecto.
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/model/Deleted.java b/src/main/java/co/allconnected/fussiontech/usersservice/model/Deleted.java
new file mode 100644
index 0000000..8f5c71d
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/model/Deleted.java
@@ -0,0 +1,35 @@
+package co.allconnected.fussiontech.usersservice.model;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.time.Instant;
+
+@Getter
+@Setter
+@Entity
+@NoArgsConstructor
+@Table(name = "deleted", schema = "all_connected_users")
+public class Deleted {
+ public Deleted(String idUser, String reason) {
+ this.idUser = idUser;
+ this.reason = reason;
+ this.deleteDate = Instant.now();
+ }
+
+ @Id
+ @Column(name = "id_user", nullable = false, length = 50)
+ private String idUser;
+
+ @OneToOne
+ @JoinColumn(name = "id_user")
+ private User user;
+
+ @Column(name = "reason", nullable = false, length = 200)
+ private String reason;
+
+ @Column(name = "delete_date", nullable = false)
+ private Instant deleteDate;
+}
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/model/Rol.java b/src/main/java/co/allconnected/fussiontech/usersservice/model/Rol.java
new file mode 100644
index 0000000..371d3b4
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/model/Rol.java
@@ -0,0 +1,21 @@
+package co.allconnected.fussiontech.usersservice.model;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+@Getter
+@Setter
+@Entity
+@Table(name = "rol", schema = "all_connected_users")
+public class Rol {
+ @Id
+ @Column(name = "id_rol", nullable = false, length = Integer.MAX_VALUE)
+ private String idRol;
+
+ @ManyToMany(mappedBy = "roles")
+ private Set users = new LinkedHashSet<>();
+}
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/model/User.java b/src/main/java/co/allconnected/fussiontech/usersservice/model/User.java
new file mode 100644
index 0000000..7e79832
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/model/User.java
@@ -0,0 +1,64 @@
+package co.allconnected.fussiontech.usersservice.model;
+
+import co.allconnected.fussiontech.usersservice.dtos.UserCreateDTO;
+import co.allconnected.fussiontech.usersservice.dtos.UserDTO;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+@Getter
+@Setter
+@Entity
+@NoArgsConstructor
+@Table(name = "\"user\"", schema = "all_connected_users")
+public class User {
+
+ public User(UserCreateDTO dto){
+ this.fullname = dto.fullname();
+ this.username = dto.username();
+ this.mail = dto.mail();
+ this.active = true;
+ }
+
+ public User(UserDTO dto){
+ this.idUser = dto.getId_user();
+ this.fullname = dto.getFullname();
+ this.username = dto.getUsername();
+ this.mail = dto.getMail();
+ this.photoUrl = dto.getMail();
+ this.active = true;
+ }
+
+ @Id
+ @Column(name = "id_user", nullable = false, length = 28)
+ private String idUser;
+
+ @Column(name = "fullname", length = 100)
+ private String fullname;
+
+ @Column(name = "username", nullable = false, length = 45)
+ private String username;
+
+ @Column(name = "mail", length = 45)
+ private String mail;
+
+ @Column(name = "photo_url", length = 200)
+ private String photoUrl;
+
+ @Column(name = "active", nullable = false)
+ private Boolean active = false;
+
+ @ManyToMany
+ @JoinTable(name = "user_rol",
+ schema = "all_connected_users",
+ joinColumns = @JoinColumn(name = "id_user"),
+ inverseJoinColumns = @JoinColumn(name = "id_rol"))
+ private Set roles = new LinkedHashSet<>();
+
+ @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
+ private Deleted deleted;
+}
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/model/fileToRemove.txt b/src/main/java/co/allconnected/fussiontech/usersservice/model/fileToRemove.txt
deleted file mode 100644
index 7755816..0000000
--- a/src/main/java/co/allconnected/fussiontech/usersservice/model/fileToRemove.txt
+++ /dev/null
@@ -1 +0,0 @@
-Este archivo es para dejar la estructura de carpetas del proyecto. Eliminar cuando se tenga el proyecto.
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/repository/DeletedRepository.java b/src/main/java/co/allconnected/fussiontech/usersservice/repository/DeletedRepository.java
new file mode 100644
index 0000000..86dc627
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/repository/DeletedRepository.java
@@ -0,0 +1,7 @@
+package co.allconnected.fussiontech.usersservice.repository;
+
+import co.allconnected.fussiontech.usersservice.model.Deleted;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface DeletedRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/repository/RolRepository.java b/src/main/java/co/allconnected/fussiontech/usersservice/repository/RolRepository.java
new file mode 100644
index 0000000..1950f71
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/repository/RolRepository.java
@@ -0,0 +1,7 @@
+package co.allconnected.fussiontech.usersservice.repository;
+
+import co.allconnected.fussiontech.usersservice.model.Rol;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface RolRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/repository/UserRepository.java b/src/main/java/co/allconnected/fussiontech/usersservice/repository/UserRepository.java
new file mode 100644
index 0000000..6939a9f
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/repository/UserRepository.java
@@ -0,0 +1,24 @@
+package co.allconnected.fussiontech.usersservice.repository;
+
+import co.allconnected.fussiontech.usersservice.model.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface UserRepository extends JpaRepository {
+ @Query("SELECT u FROM User u " +
+ "LEFT JOIN u.roles r " +
+ "WHERE (:fullname IS NULL OR u.fullname LIKE %:fullname%) " +
+ "AND (:username IS NULL OR u.username LIKE %:username%) " +
+ "AND (:mail IS NULL OR u.mail LIKE %:mail%) " +
+ "AND (:rol IS NULL OR r.idRol LIKE %:rol%) " +
+ "AND (:active IS NULL OR u.active = :active)")
+ List findUsersByFilters(@Param("fullname") String fullname,
+ @Param("username") String username,
+ @Param("mail") String mail,
+ @Param("rol") String rol,
+ @Param("active") Boolean active);
+
+}
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/repository/fileToRemove.txt b/src/main/java/co/allconnected/fussiontech/usersservice/repository/fileToRemove.txt
deleted file mode 100644
index 7755816..0000000
--- a/src/main/java/co/allconnected/fussiontech/usersservice/repository/fileToRemove.txt
+++ /dev/null
@@ -1 +0,0 @@
-Este archivo es para dejar la estructura de carpetas del proyecto. Eliminar cuando se tenga el proyecto.
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/services/FirebaseService.java b/src/main/java/co/allconnected/fussiontech/usersservice/services/FirebaseService.java
new file mode 100644
index 0000000..23ff78e
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/services/FirebaseService.java
@@ -0,0 +1,77 @@
+package co.allconnected.fussiontech.usersservice.services;
+
+import com.google.cloud.storage.Bucket;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseAuthException;
+import com.google.firebase.auth.UserRecord;
+import com.google.firebase.cloud.StorageClient;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+public class FirebaseService {
+
+ public String uploadImg(String imageName, String extension, MultipartFile imageFile) throws IOException {
+ InputStream inputStream = imageFile.getInputStream();
+ Bucket bucket = StorageClient.getInstance().bucket();
+ bucket.create("user_photos/"+imageName, inputStream, "image/"+extension);
+ return bucket.get("user_photos/"+imageName)
+ .signUrl(360, java.util.concurrent.TimeUnit.DAYS).toString();
+ }
+
+ public void deleteImg(String imageName) {
+ Bucket bucket = StorageClient.getInstance().bucket();
+ bucket.get("user_photos/"+imageName).delete();
+ }
+
+ public String createUser(String email, String password, String[] roles) throws FirebaseAuthException {
+ UserRecord.CreateRequest request = new UserRecord.CreateRequest()
+ .setEmail(email)
+ .setPassword(password);
+
+ // Add custom claims
+ Map claims = new HashMap<>();
+ claims.put("roles", roles);
+
+ // Create user and set custom claims
+ UserRecord userRecord = FirebaseAuth.getInstance().createUser(request);
+ FirebaseAuth.getInstance().setCustomUserClaims(userRecord.getUid(), claims);
+ return userRecord.getUid();
+ }
+
+ public void deleteUser(String uid) throws FirebaseAuthException {
+ FirebaseAuth.getInstance().deleteUser(uid);
+ }
+
+ public void disableUser(String uid) throws FirebaseAuthException {
+ UserRecord.UpdateRequest request = new UserRecord.UpdateRequest(uid)
+ .setDisabled(true);
+ FirebaseAuth.getInstance().updateUser(request);
+ }
+
+ public void enableUser(String uid) throws FirebaseAuthException {
+ UserRecord.UpdateRequest request = new UserRecord.UpdateRequest(uid)
+ .setDisabled(false);
+ FirebaseAuth.getInstance().updateUser(request);
+ }
+
+ public void updateUser(String uid, String email, String password) throws FirebaseAuthException {
+ UserRecord.UpdateRequest request = new UserRecord.UpdateRequest(uid);
+ if (email != null)
+ request.setEmail(email);
+ if (password != null)
+ request.setPassword(password);
+ FirebaseAuth.getInstance().updateUser(request);
+ }
+
+ public void updateCustomClaims(String uid, String[] roles) throws FirebaseAuthException {
+ Map claims = new HashMap<>();
+ claims.put("roles", roles);
+ FirebaseAuth.getInstance().setCustomUserClaims(uid, claims); }
+}
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/services/RolService.java b/src/main/java/co/allconnected/fussiontech/usersservice/services/RolService.java
new file mode 100644
index 0000000..caff223
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/services/RolService.java
@@ -0,0 +1,40 @@
+package co.allconnected.fussiontech.usersservice.services;
+
+import co.allconnected.fussiontech.usersservice.model.Rol;
+import co.allconnected.fussiontech.usersservice.repository.RolRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class RolService {
+
+ private final RolRepository rolRepository;
+
+ @Autowired
+ public RolService(RolRepository rolRepository) {
+ this.rolRepository = rolRepository;
+ }
+
+ public Rol createRol(Rol rol) {
+ return rolRepository.save(rol);
+ }
+
+ public Optional getRol(String id) {
+ return rolRepository.findById(id);
+ }
+
+ public List getAllRoles() {
+ return rolRepository.findAll();
+ }
+
+ public Rol updateRol(Rol rol) {
+ return rolRepository.save(rol);
+ }
+
+ public void deleteRol(String id) {
+ rolRepository.deleteById(id);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/services/UserService.java b/src/main/java/co/allconnected/fussiontech/usersservice/services/UserService.java
new file mode 100644
index 0000000..e6e686f
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/services/UserService.java
@@ -0,0 +1,243 @@
+package co.allconnected.fussiontech.usersservice.services;
+
+import co.allconnected.fussiontech.usersservice.dtos.DeletedDTO;
+import co.allconnected.fussiontech.usersservice.dtos.InactiveUserDTO;
+import co.allconnected.fussiontech.usersservice.dtos.UserCreateDTO;
+import co.allconnected.fussiontech.usersservice.dtos.UserDTO;
+import co.allconnected.fussiontech.usersservice.model.Deleted;
+import co.allconnected.fussiontech.usersservice.model.Rol;
+import co.allconnected.fussiontech.usersservice.model.User;
+import co.allconnected.fussiontech.usersservice.repository.DeletedRepository;
+import co.allconnected.fussiontech.usersservice.repository.UserRepository;
+import co.allconnected.fussiontech.usersservice.utils.OperationException;
+import com.google.firebase.auth.FirebaseAuthException;
+import org.apache.commons.io.FilenameUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+@Service
+public class UserService {
+
+ private final UserRepository userRepository;
+ private final DeletedRepository deletedRepository;
+ private final RolService rolService;
+ private final FirebaseService firebaseService;
+
+ @Autowired
+ public UserService(UserRepository userRepository, DeletedRepository deletedRepository, RolService rolService, FirebaseService firebaseService) {
+ this.userRepository = userRepository;
+ this.firebaseService = firebaseService;
+ this.rolService = rolService;
+ this.deletedRepository = deletedRepository;
+ }
+
+ public UserDTO createUser(UserDTO userDto, MultipartFile photo) {
+ User user = new User(userDto);
+ // Add roles to user
+ for (String rol : userDto.getRoles()) {
+ Optional rolEntity = rolService.getRol(rol);
+ if(rolEntity.isEmpty()) throw new OperationException(404, "Rol not found");
+ user.getRoles().add(rolEntity.get());
+ }
+
+ // Update claims
+ try{
+ firebaseService.updateCustomClaims(user.getIdUser(), userDto.getRoles());
+ } catch (FirebaseAuthException e) {
+ throw new OperationException(500, "Firebase authentication error: " + e.getMessage());
+ }
+
+ // Upload photo to firebase
+ if (photo != null) {
+ String photoName = user.getIdUser();
+ String extension = FilenameUtils.getExtension(photo.getOriginalFilename());
+ try{
+ user.setPhotoUrl(firebaseService.uploadImg(photoName, extension, photo));
+ } catch (IOException e) {
+ throw new OperationException(500, "Error uploading photo: " + e.getMessage());
+ }
+ }
+ return new UserDTO(userRepository.save(user));
+ }
+
+ public UserDTO createUserFromAdmin(UserCreateDTO userDto, MultipartFile photo) {
+ User user = new User(userDto);
+
+ // Create user in firebase
+ try {
+ user.setIdUser(firebaseService.createUser(userDto.mail(), userDto.password(), userDto.roles()));
+ } catch (FirebaseAuthException e) {
+ throw new OperationException(500, "Firebase authentication error: " + e.getMessage());
+ }
+
+ // Add roles to user
+ for (String rol : userDto.roles()) {
+ Optional rolEntity = rolService.getRol(rol);
+ if(rolEntity.isEmpty()) throw new OperationException(404, "Rol not found");
+ user.getRoles().add(rolEntity.get());
+ }
+
+ // Upload photo to firebase
+ if (photo != null) {
+ String photoName = user.getIdUser();
+ String extension = FilenameUtils.getExtension(photo.getOriginalFilename());
+ try{
+ user.setPhotoUrl(firebaseService.uploadImg(photoName, extension, photo));
+ } catch (IOException e) {
+ throw new OperationException(500, "Error uploading photo: " + e.getMessage());
+ }
+ }
+ return new UserDTO(userRepository.save(user));
+ }
+
+ public UserDTO createGuestUser(){
+ User user = new User();
+ user.setIdUser(UUID.randomUUID().toString().replace("-", "").substring(0, 28));
+
+ user.setUsername("Guest<" + user.getIdUser() + ">");
+ user.setFullname("Guest");
+ user.setActive(true);
+
+ Rol rolEntity = rolService.getRol("guest").orElseThrow();
+ user.getRoles().add(rolEntity);
+
+ return new UserDTO(userRepository.save(user));
+ }
+
+ public void deleteUser(String id) {
+ Optional userOptional = userRepository.findById(id);
+ if (userOptional.isPresent()) {
+ User user = userOptional.get();
+ if (user.getPhotoUrl() != null)
+ firebaseService.deleteImg(user.getIdUser());
+ // check if user is guest
+ if (user.getRoles().stream().noneMatch(rol -> rol.getIdRol().equals("guest"))) try {
+ firebaseService.deleteUser(user.getIdUser());
+ } catch (FirebaseAuthException e) {
+ throw new OperationException(500, "Firebase authentication error: " + e.getMessage());
+ }
+ userRepository.delete(user);
+ } else {
+ throw new OperationException(404, "User not found");
+ }
+ }
+
+ public DeletedDTO deactivateUser(String id, String reason) {
+ return userRepository.findById(id).map(user -> {
+ Deleted deleted = new Deleted(user.getIdUser(), reason);
+ user.setActive(false);
+ user.setDeleted(deletedRepository.save(deleted));
+ userRepository.save(user);
+ if (user.getRoles().stream().noneMatch(rol -> rol.getIdRol().equals("guest"))) try {
+ firebaseService.disableUser(user.getIdUser());
+ } catch (FirebaseAuthException e) {
+ throw new OperationException(500, e.getMessage());
+ }
+ return new DeletedDTO(deleted.getIdUser(), deleted.getReason(), deleted.getDeleteDate());
+ }).orElseThrow(() -> new OperationException(404, "User not found"));
+ }
+
+ public UserDTO activateUser(String id){
+ return userRepository.findById(id).map(user -> {
+ user.setActive(true);
+ user.setDeleted(null);
+ userRepository.save(user);
+ if (user.getRoles().stream().noneMatch(rol -> rol.getIdRol().equals("guest"))) try {
+ firebaseService.enableUser(user.getIdUser());
+ } catch (FirebaseAuthException e) {
+ throw new OperationException(500, e.getMessage());
+ }
+ return new UserDTO(user);
+ }).orElseThrow(() -> new OperationException(404, "User not found"));
+ }
+
+ public UserDTO[] getUsers(String fullname, String username, String mail, String rol, Boolean active) {
+ return userRepository.findUsersByFilters(fullname, username, mail, rol, active).stream()
+ .map(user -> user.getActive() ? new UserDTO(user) : new InactiveUserDTO(user))
+ .toArray(UserDTO[]::new);
+ }
+
+ public UserDTO getUser(String id) {
+ return userRepository.findById(id)
+ .map(user -> user.getActive() ? new UserDTO(user) : new InactiveUserDTO(user))
+ .orElseThrow(() -> new OperationException(404, "User not found"));
+ }
+
+ public UserDTO updateUser(String id, UserCreateDTO userDTO, MultipartFile photo) {
+ User user = userRepository.findById(id).orElseThrow(() -> new OperationException(404, "User not found"));
+
+ if(userDTO.fullname() != null && !user.getFullname().equals(userDTO.fullname()))
+ user.setFullname(userDTO.fullname());
+
+ if(userDTO.username() != null && !user.getUsername().equals(userDTO.username()))
+ user.setUsername(userDTO.username());
+
+ // Update mail and password in firebase if they changed
+ if (userDTO.mail() != null && !user.getMail().equals(userDTO.mail()) || userDTO.password() != null) {
+ try {
+ firebaseService.updateUser(user.getIdUser(), userDTO.mail(), userDTO.password());
+ user.setMail(userDTO.mail());
+ } catch (FirebaseAuthException e) {
+ throw new OperationException(500, "Firebase authentication error: " + e.getMessage());
+ }
+ }
+
+ // Update photo if not null
+ if (photo != null) {
+ if (user.getPhotoUrl() != null)
+ firebaseService.deleteImg(user.getIdUser());
+ String photoName = user.getIdUser();
+ String extension = FilenameUtils.getExtension(photo.getOriginalFilename());
+ try{
+ user.setPhotoUrl(firebaseService.uploadImg(photoName, extension, photo));
+ } catch (IOException e) {
+ throw new OperationException(500, "Error uploading photo: " + e.getMessage());
+ }
+ }
+ return new UserDTO(userRepository.save(user));
+ }
+
+ public UserDTO addRoles(String id, String[] roles) {
+ User user = userRepository.findById(id).orElseThrow(() -> new OperationException(404, "User not found"));
+ for (String rol : roles) {
+ Rol rolEntity = rolService.getRol(rol).orElseThrow();
+ user.getRoles().add(rolEntity);
+ }
+
+ // Update custom claims in firebase
+ try {
+ firebaseService.updateCustomClaims(user.getIdUser(), roles);
+ } catch (FirebaseAuthException e) {
+ throw new OperationException(500, "Firebase authentication error: " + e.getMessage());
+ }
+
+ return new UserDTO(userRepository.save(user));
+ }
+
+ public UserDTO removeRoles(String id, String role) {
+ User user = userRepository.findById(id).orElseThrow(() -> new OperationException(404, "User not found"));
+ Rol rolEntity = rolService.getRol(role).orElseThrow(() -> new OperationException(404, "Rol not found"));
+ user.getRoles().remove(rolEntity);
+ user = userRepository.save(user);
+
+ // Update custom claims in firebase
+ String[] roles = user.getRoles().stream()
+ .map(Rol::getIdRol)
+ .toArray(String[]::new);
+
+ try {
+ firebaseService.updateCustomClaims(user.getIdUser(), roles);
+ } catch (FirebaseAuthException e) {
+ throw new OperationException(500, "Firebase authentication error: " + e.getMessage());
+ }
+
+ return new UserDTO(user);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/services/fileToRemove.txt b/src/main/java/co/allconnected/fussiontech/usersservice/services/fileToRemove.txt
deleted file mode 100644
index 7755816..0000000
--- a/src/main/java/co/allconnected/fussiontech/usersservice/services/fileToRemove.txt
+++ /dev/null
@@ -1 +0,0 @@
-Este archivo es para dejar la estructura de carpetas del proyecto. Eliminar cuando se tenga el proyecto.
\ No newline at end of file
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/utils/OperationException.java b/src/main/java/co/allconnected/fussiontech/usersservice/utils/OperationException.java
new file mode 100644
index 0000000..e826e6f
--- /dev/null
+++ b/src/main/java/co/allconnected/fussiontech/usersservice/utils/OperationException.java
@@ -0,0 +1,14 @@
+package co.allconnected.fussiontech.usersservice.utils;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class OperationException extends RuntimeException {
+ private final Integer code;
+ public OperationException(Integer code, String message) {
+ super(message);
+ this.code = code;
+ }
+}
diff --git a/src/main/java/co/allconnected/fussiontech/usersservice/utils/fileToRemove.txt b/src/main/java/co/allconnected/fussiontech/usersservice/utils/fileToRemove.txt
deleted file mode 100644
index 7755816..0000000
--- a/src/main/java/co/allconnected/fussiontech/usersservice/utils/fileToRemove.txt
+++ /dev/null
@@ -1 +0,0 @@
-Este archivo es para dejar la estructura de carpetas del proyecto. Eliminar cuando se tenga el proyecto.
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 8024e8d..4884193 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,12 +1,7 @@
-
spring:
application:
name: users-service
- datasource:
- url: ${DATASOURCE_URL}
- username: ${DATASOURCE_USERNAME}
- password: ${DATASOURCE_PASSWORD}
- driver-class-name: org.postgresql.Driver
-eureka:
- client:
- enabled: false
+ config:
+ import: configserver:http://${CONFIG_IP:localhost}:8888
+ profiles:
+ active: ${PROFILE:keveldev}