refactor: replace Spring Boot with Jooby framework #1
@@ -1,5 +1,3 @@
|
||||
SPRING_PROFILES_ACTIVE=dev
|
||||
|
||||
APP_ALLOWED_ORIGINS='http://127.0.0.1:3000, http://localhost:3000'
|
||||
|
||||
DB_NAME=EXAMPLE_DB
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@
|
||||
target
|
||||
.env
|
||||
Icon?
|
||||
.docker
|
||||
@@ -26,11 +26,14 @@ FROM eclipse-temurin:21-jre-alpine
|
||||
WORKDIR /app
|
||||
|
||||
# Seguridad: Usuario no-root
|
||||
RUN addgroup -S spring && adduser -S spring -G spring
|
||||
USER spring:spring
|
||||
RUN addgroup -S app && adduser -S app -G app
|
||||
|
||||
RUN mkdir -p /app/tmp && chown app:app /app/tmp && chmod 777 /app/tmp
|
||||
|
||||
USER app:app
|
||||
|
||||
# Copiamos solo el JAR final (ajustado a tu módulo bootstrap)
|
||||
COPY --from=build /app/bootstrap/target/*.jar app.jar
|
||||
COPY --from=build /app/bootstrap/target/bootstrap-*.jar app.jar
|
||||
|
||||
# Configuración de Memoria y Rendimiento para Microservicios
|
||||
# -XX:+UseSerialGC: Menos consumo de RAM para apps < 1GB
|
||||
|
||||
31
HELP.md
31
HELP.md
@@ -1,31 +0,0 @@
|
||||
# Getting Started
|
||||
|
||||
### Reference Documentation
|
||||
For further reference, please consider the following sections:
|
||||
|
||||
* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
|
||||
* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/maven-plugin)
|
||||
* [Create an OCI image](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/maven-plugin/build-image.html)
|
||||
* [Spring Web](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/reference/web/servlet.html)
|
||||
* [Spring Data JPA](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/reference/data/sql.html#data.sql.jpa-and-spring-data)
|
||||
* [Validation](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/reference/io/validation.html)
|
||||
* [Spring Boot DevTools](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/reference/using/devtools.html)
|
||||
* [Spring Boot Actuator](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/reference/actuator/index.html)
|
||||
|
||||
### Guides
|
||||
The following guides illustrate how to use some features concretely:
|
||||
|
||||
* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
|
||||
* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
|
||||
* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)
|
||||
* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/)
|
||||
* [Validation](https://spring.io/guides/gs/validating-form-input/)
|
||||
* [Building a RESTful Web Service with Spring Boot Actuator](https://spring.io/guides/gs/actuator-service/)
|
||||
|
||||
### Maven Parent overrides
|
||||
|
||||
Due to Maven's design, elements are inherited from the parent POM to the project POM.
|
||||
While most of the inheritance is fine, it also inherits unwanted elements like `<license>` and `<developers>` from the parent.
|
||||
To prevent this, the project POM contains empty overrides for these elements.
|
||||
If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides.
|
||||
|
||||
@@ -12,9 +12,18 @@
|
||||
<artifactId>domain</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -3,7 +3,6 @@ package com.pablotj.portfolio.application.project;
|
||||
import com.pablotj.portfolio.domain.project.Project;
|
||||
import com.pablotj.portfolio.domain.project.ProjectId;
|
||||
import com.pablotj.portfolio.domain.project.port.ProjectRepositoryPort;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
<project>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.pablotj</groupId>
|
||||
<artifactId>portfolio-api</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>bootstrap</artifactId>
|
||||
|
||||
<properties>
|
||||
<jooby.version>3.6.1</jooby.version>
|
||||
<postgresql.version>42.7.2</postgresql.version>
|
||||
<testcontainers.version>1.19.7</testcontainers.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.pablotj</groupId>
|
||||
@@ -14,40 +24,90 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Drivers DB -->
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-netty</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-jackson</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-jackson</artifactId>
|
||||
<version>3.6.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-hibernate-validator</artifactId>
|
||||
<version>3.6.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-openapi</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-swagger-ui</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-logback</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<version>${postgresql.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<version>2.2.224</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger/OpenAPI -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-hikari</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Actuator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-guice</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<!--
|
||||
install(new DotenvModule());
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-dotenv</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</dependency>-->
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-flyway</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-test</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>${testcontainers.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -55,15 +115,77 @@
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-maven-plugin</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
<goal>openapi</goal> <!-- Este goal genera openapi.json/yaml -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<mainClass>com.pablotj.portfolio.bootstrap.PortfolioApplication</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>jooby-shade</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<!-- 1. GENÉRICO: Elimina TODO en META-INF excepto servicios y archivos ebean -->
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
<exclude>META-INF/MANIFEST.MF</exclude>
|
||||
<exclude>META-INF/DEPENDENCIES*</exclude>
|
||||
<exclude>META-INF/LICENSE*</exclude>
|
||||
<exclude>META-INF/NOTICE*</exclude>
|
||||
<exclude>META-INF/*.txt</exclude>
|
||||
<exclude>META-INF/*.md</exclude>
|
||||
|
||||
<!-- 2. ESPECÍFICO: Limpia los avisos de Netty, JSON Schema y Módulos -->
|
||||
<exclude>META-INF/io.netty.versions.properties</exclude>
|
||||
<exclude>draftv3/schema</exclude>
|
||||
<exclude>draftv4/schema</exclude>
|
||||
<exclude>**/module-info.class</exclude>
|
||||
<exclude>META-INF/versions/**</exclude>
|
||||
|
||||
<!-- 3. GENÉRICO: Archivos de licencia sueltos en la raíz -->
|
||||
<exclude>LICENSE*</exclude>
|
||||
<exclude>NOTICE*</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/ebean.mf</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>com.pablotj.portfolio.bootstrap.PortfolioApplication</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -1,16 +1,67 @@
|
||||
package com.pablotj.portfolio.bootstrap;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import com.pablotj.portfolio.bootstrap.certification.CertificationApplicationConfig;
|
||||
import com.pablotj.portfolio.bootstrap.education.EducationApplicationConfig;
|
||||
import com.pablotj.portfolio.bootstrap.experience.ExperienceApplicationConfig;
|
||||
import com.pablotj.portfolio.bootstrap.profile.ProfileApplicationConfig;
|
||||
import com.pablotj.portfolio.bootstrap.project.ProjectApplicationConfig;
|
||||
import com.pablotj.portfolio.bootstrap.skill.SkillApplicationConfig;
|
||||
import com.pablotj.portfolio.infrastructure.config.CorsConfig;
|
||||
import com.pablotj.portfolio.infrastructure.rest.api.ApiErrorController;
|
||||
import com.pablotj.portfolio.infrastructure.rest.api.ApiRootController;
|
||||
import com.pablotj.portfolio.infrastructure.rest.certification.CertificationController;
|
||||
import com.pablotj.portfolio.infrastructure.rest.education.EducationController;
|
||||
import com.pablotj.portfolio.infrastructure.rest.experience.ExperienceController;
|
||||
import com.pablotj.portfolio.infrastructure.rest.profile.ProfileController;
|
||||
import com.pablotj.portfolio.infrastructure.rest.project.ProjectController;
|
||||
import com.pablotj.portfolio.infrastructure.rest.skill.SkillController;
|
||||
import io.jooby.Jooby;
|
||||
import io.jooby.OpenAPIModule;
|
||||
import io.jooby.ebean.EbeanModule;
|
||||
import io.jooby.flyway.FlywayModule;
|
||||
import io.jooby.guice.GuiceModule;
|
||||
import io.jooby.hibernate.validator.HibernateValidatorModule;
|
||||
import io.jooby.hikari.HikariModule;
|
||||
import io.jooby.jackson.JacksonModule;
|
||||
import io.jooby.netty.NettyServer;
|
||||
|
||||
@SpringBootApplication(scanBasePackages = "com.pablotj")
|
||||
@EnableJpaRepositories(basePackages = {"com.pablotj.portfolio.infrastructure.persistence.*.repo"})
|
||||
@EntityScan(basePackages = {"com.pablotj.portfolio.infrastructure.persistence.*.entity"})
|
||||
public class PortfolioApplication {
|
||||
public class PortfolioApplication extends Jooby {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PortfolioApplication.class, args);
|
||||
{
|
||||
install(new NettyServer());
|
||||
|
||||
install(new JacksonModule());
|
||||
install(new HibernateValidatorModule());
|
||||
install(new HikariModule());
|
||||
install(new FlywayModule());
|
||||
install(new EbeanModule());
|
||||
install(new CorsConfig());
|
||||
|
||||
error(ApiErrorController.getHandler());
|
||||
|
||||
install(new ProfileApplicationConfig());
|
||||
install(new ProjectApplicationConfig());
|
||||
install(new CertificationApplicationConfig());
|
||||
install(new EducationApplicationConfig());
|
||||
install(new ExperienceApplicationConfig());
|
||||
install(new SkillApplicationConfig());
|
||||
|
||||
install(new GuiceModule()); // Esto habilita la resolución de @Inject y @Named
|
||||
|
||||
path("/api", () -> {
|
||||
mvc(ApiRootController.class);
|
||||
mvc(CertificationController.class);
|
||||
mvc(EducationController.class);
|
||||
mvc(ExperienceController.class);
|
||||
mvc(ProfileController.class);
|
||||
mvc(ProjectController.class);
|
||||
mvc(SkillController.class);
|
||||
});
|
||||
install(new OpenAPIModule().swaggerUI("/docs"));
|
||||
|
||||
}
|
||||
|
||||
public static void main(final String[] args) {
|
||||
runApp(args, PortfolioApplication::new);
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.certification;
|
||||
import com.pablotj.portfolio.application.certification.CreateCertificationUseCase;
|
||||
import com.pablotj.portfolio.application.certification.GetCertificationUseCase;
|
||||
import com.pablotj.portfolio.domain.certification.port.CertificationRepositoryPort;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.certification.adapter.CertificationRepositoryAdapter;
|
||||
import io.jooby.Extension;
|
||||
import io.jooby.Jooby;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
@Configuration
|
||||
public class CertificationApplicationConfig {
|
||||
public class CertificationApplicationConfig implements Extension {
|
||||
|
||||
@Bean
|
||||
public GetCertificationUseCase getCertificationUseCase(CertificationRepositoryPort repo) {
|
||||
return new GetCertificationUseCase(repo);
|
||||
}
|
||||
@Override
|
||||
public void install(@Nonnull Jooby app) {
|
||||
CertificationRepositoryAdapter adapter = new CertificationRepositoryAdapter();
|
||||
|
||||
@Bean
|
||||
public CreateCertificationUseCase createCertificationUseCase(CertificationRepositoryPort repo) {
|
||||
return new CreateCertificationUseCase(repo);
|
||||
app.getServices().put(CertificationRepositoryPort.class, adapter);
|
||||
|
||||
app.getServices().put(GetCertificationUseCase.class, new GetCertificationUseCase(adapter));
|
||||
app.getServices().put(CreateCertificationUseCase.class, new CreateCertificationUseCase(adapter));
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.education;
|
||||
import com.pablotj.portfolio.application.education.CreateEducationUseCase;
|
||||
import com.pablotj.portfolio.application.education.GetEducationUseCase;
|
||||
import com.pablotj.portfolio.domain.education.port.EducationRepositoryPort;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.education.adapter.EducationRepositoryAdapter;
|
||||
import io.jooby.Extension;
|
||||
import io.jooby.Jooby;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
@Configuration
|
||||
public class EducationApplicationConfig {
|
||||
public class EducationApplicationConfig implements Extension {
|
||||
|
||||
@Bean
|
||||
public GetEducationUseCase getEducationUseCase(EducationRepositoryPort repo) {
|
||||
return new GetEducationUseCase(repo);
|
||||
}
|
||||
@Override
|
||||
public void install(@Nonnull Jooby app) {
|
||||
EducationRepositoryAdapter adapter = new EducationRepositoryAdapter();
|
||||
|
||||
@Bean
|
||||
public CreateEducationUseCase createEducationUseCase(EducationRepositoryPort repo) {
|
||||
return new CreateEducationUseCase(repo);
|
||||
app.getServices().put(EducationRepositoryPort.class, adapter);
|
||||
|
||||
app.getServices().put(GetEducationUseCase.class, new GetEducationUseCase(adapter));
|
||||
app.getServices().put(CreateEducationUseCase.class, new CreateEducationUseCase(adapter));
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.experience;
|
||||
import com.pablotj.portfolio.application.experience.CreateExperienceUseCase;
|
||||
import com.pablotj.portfolio.application.experience.GetExperienceUseCase;
|
||||
import com.pablotj.portfolio.domain.experience.port.ExperienceRepositoryPort;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.adapter.ExperienceRepositoryAdapter;
|
||||
import io.jooby.Extension;
|
||||
import io.jooby.Jooby;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
@Configuration
|
||||
public class ExperienceApplicationConfig {
|
||||
public class ExperienceApplicationConfig implements Extension {
|
||||
@Override
|
||||
public void install(@Nonnull Jooby app) {
|
||||
|
||||
@Bean
|
||||
public GetExperienceUseCase getExperienceUseCase(ExperienceRepositoryPort repo) {
|
||||
return new GetExperienceUseCase(repo);
|
||||
}
|
||||
ExperienceRepositoryAdapter adapter = new ExperienceRepositoryAdapter();
|
||||
|
||||
@Bean
|
||||
public CreateExperienceUseCase createExperienceUseCase(ExperienceRepositoryPort repo) {
|
||||
return new CreateExperienceUseCase(repo);
|
||||
app.getServices().put(ExperienceRepositoryPort.class, adapter);
|
||||
|
||||
app.getServices().put(GetExperienceUseCase.class, new GetExperienceUseCase(adapter));
|
||||
app.getServices().put(CreateExperienceUseCase.class, new CreateExperienceUseCase(adapter));
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.profile;
|
||||
import com.pablotj.portfolio.application.profile.CreateProfileUseCase;
|
||||
import com.pablotj.portfolio.application.profile.GetProfileUseCase;
|
||||
import com.pablotj.portfolio.domain.profile.port.ProfileRepositoryPort;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.adapter.ProfileRepositoryAdapter;
|
||||
import io.jooby.Extension;
|
||||
import io.jooby.Jooby;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
@Configuration
|
||||
public class ProfileApplicationConfig {
|
||||
public class ProfileApplicationConfig implements Extension {
|
||||
@Override
|
||||
public void install(@Nonnull Jooby app) {
|
||||
|
||||
@Bean
|
||||
public GetProfileUseCase getHomeUseCase(ProfileRepositoryPort repo) {
|
||||
return new GetProfileUseCase(repo);
|
||||
}
|
||||
ProfileRepositoryAdapter adapter = new ProfileRepositoryAdapter();
|
||||
|
||||
@Bean
|
||||
public CreateProfileUseCase createHomeUseCase(ProfileRepositoryPort repo) {
|
||||
return new CreateProfileUseCase(repo);
|
||||
app.getServices().put(ProfileRepositoryPort.class, adapter);
|
||||
|
||||
app.getServices().put(GetProfileUseCase.class, new GetProfileUseCase(adapter));
|
||||
app.getServices().put(CreateProfileUseCase.class, new CreateProfileUseCase(adapter));
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.project;
|
||||
import com.pablotj.portfolio.application.project.CreateProjectUseCase;
|
||||
import com.pablotj.portfolio.application.project.GetProjectUseCase;
|
||||
import com.pablotj.portfolio.domain.project.port.ProjectRepositoryPort;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.adapter.ProjectRepositoryAdapter;
|
||||
import io.jooby.Extension;
|
||||
import io.jooby.Jooby;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
@Configuration
|
||||
public class ProjectApplicationConfig {
|
||||
public class ProjectApplicationConfig implements Extension {
|
||||
@Override
|
||||
public void install(@Nonnull Jooby app) {
|
||||
|
||||
@Bean
|
||||
public CreateProjectUseCase createProjectUseCase(ProjectRepositoryPort repo) {
|
||||
return new CreateProjectUseCase(repo);
|
||||
}
|
||||
ProjectRepositoryAdapter adapter = new ProjectRepositoryAdapter();
|
||||
|
||||
@Bean
|
||||
public GetProjectUseCase getProjectUseCase(ProjectRepositoryPort repo) {
|
||||
return new GetProjectUseCase(repo);
|
||||
app.getServices().put(ProjectRepositoryPort.class, adapter);
|
||||
|
||||
app.getServices().put(CreateProjectUseCase.class, new CreateProjectUseCase(adapter));
|
||||
app.getServices().put(GetProjectUseCase.class, new GetProjectUseCase(adapter));
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.skill;
|
||||
import com.pablotj.portfolio.application.skill.CreateSkillUseCase;
|
||||
import com.pablotj.portfolio.application.skill.GetSkillUseCase;
|
||||
import com.pablotj.portfolio.domain.skill.port.SkillRepositoryPort;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.skill.adapter.SkillRepositoryAdapter;
|
||||
import io.jooby.Extension;
|
||||
import io.jooby.Jooby;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
@Configuration
|
||||
public class SkillApplicationConfig {
|
||||
public class SkillApplicationConfig implements Extension {
|
||||
@Override
|
||||
public void install(@Nonnull Jooby app) {
|
||||
|
||||
@Bean
|
||||
public GetSkillUseCase getSkillUseCase(SkillRepositoryPort repo) {
|
||||
return new GetSkillUseCase(repo);
|
||||
}
|
||||
SkillRepositoryAdapter adapter = new SkillRepositoryAdapter();
|
||||
|
||||
@Bean
|
||||
public CreateSkillUseCase createSkillUseCase(SkillRepositoryPort repo) {
|
||||
return new CreateSkillUseCase(repo);
|
||||
app.getServices().put(SkillRepositoryPort.class, adapter);
|
||||
|
||||
app.getServices().put(GetSkillUseCase.class, new GetSkillUseCase(adapter));
|
||||
app.getServices().put(CreateSkillUseCase.class, new CreateSkillUseCase(adapter));
|
||||
}
|
||||
}
|
||||
61
bootstrap/src/main/resources/application.conf
Normal file
61
bootstrap/src/main/resources/application.conf
Normal file
@@ -0,0 +1,61 @@
|
||||
# Configuración de la App (Sustituye a info y app)
|
||||
info {
|
||||
app {
|
||||
version = "0.0.1-SNAPSHOT" # Maven no rellena esto automáticamente en Jooby sin plugins extra, mejor ponlo fijo
|
||||
}
|
||||
}
|
||||
|
||||
app {
|
||||
cors {
|
||||
allowed-origins = ${?APP_ALLOWED_ORIGINS}
|
||||
allowed-origins = "http://localhost:8080" # Valor por defecto
|
||||
}
|
||||
}
|
||||
|
||||
# Configuración del Servidor
|
||||
server {
|
||||
port = 8080
|
||||
# En Jooby, el context-path se define habitualmente en la clase App,
|
||||
# pero puedes usar esta propiedad si la gestionas manualmente.
|
||||
}
|
||||
|
||||
# Base de Datos (Ebean usa estas propiedades automáticamente)
|
||||
db {
|
||||
url = "jdbc:postgresql://"${?DB_HOST}":"${?DB_PORT}"/"${?DB_NAME}
|
||||
user = ${?DB_USER}
|
||||
password = ${?DB_PASSWORD}
|
||||
driverClassName = org.postgresql.Driver
|
||||
|
||||
# HikariCP (Configuración de Pool)
|
||||
hikari {
|
||||
maximumPoolSize = 3
|
||||
minimumIdle = 1
|
||||
idleTimeout = 30000
|
||||
connectionTimeout = 10000
|
||||
}
|
||||
}
|
||||
|
||||
flyway {
|
||||
# Ubicación de tus scripts .sql
|
||||
locations = ["classpath:db/migration"]
|
||||
cleanDisabled = true
|
||||
baselineOnMigrate = true
|
||||
baselineVersion = ${?FLYWAY_BASELINE_VERSION}
|
||||
|
||||
# Si quieres que se ejecute siempre al arrancar
|
||||
run = [migrate]
|
||||
}
|
||||
|
||||
ebean {
|
||||
ddl {
|
||||
generate = false
|
||||
run = false
|
||||
}
|
||||
# Mostrar SQL en consola
|
||||
debug = true
|
||||
}
|
||||
|
||||
# Jackson
|
||||
jackson {
|
||||
indent_output = true
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
info:
|
||||
app:
|
||||
version: @project.version@
|
||||
app:
|
||||
cors:
|
||||
allowed-origins: ${APP_ALLOWED_ORIGINS:http://localhost:8080}
|
||||
spring:
|
||||
application:
|
||||
name: portfolio-api
|
||||
web:
|
||||
resources:
|
||||
add-mappings: false
|
||||
|
||||
datasource:
|
||||
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:portfolio}
|
||||
username: ${DB_USER:postgres}
|
||||
password: ${DB_PASSWORD:postgres}
|
||||
driver-class-name: org.postgresql.Driver
|
||||
hikari:
|
||||
maximum-pool-size: 3
|
||||
minimum-idle: 1
|
||||
idle-timeout: 30000
|
||||
connection-timeout: 10000
|
||||
leak-detection-threshold: 10000
|
||||
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: validate
|
||||
properties:
|
||||
hibernate.transaction.jta.platform: org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform
|
||||
hibernate:
|
||||
format_sql: true
|
||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||
show-sql: true
|
||||
|
||||
jackson:
|
||||
serialization:
|
||||
INDENT_OUTPUT: true
|
||||
|
||||
springdoc:
|
||||
api-docs:
|
||||
path: /v3/api-docs
|
||||
swagger-ui:
|
||||
path: /swagger-ui
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
servlet:
|
||||
context-path: /api
|
||||
forward-headers-strategy: framework
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,11 +10,14 @@
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
<version>1.18.36</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -7,6 +7,7 @@ public record CertificationId(Long profileId, Long certificationId) {
|
||||
}
|
||||
|
||||
public CertificationId {
|
||||
if (certificationId != null && certificationId < 0) throw new IllegalArgumentException("CertificationId must be positive");
|
||||
if (certificationId != null && certificationId < 0)
|
||||
throw new IllegalArgumentException("CertificationId must be positive");
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.pablotj.portfolio.domain.experience;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ public record ExperienceId(Long profileId, Long experienceId) {
|
||||
}
|
||||
|
||||
public ExperienceId {
|
||||
if (experienceId != null && experienceId < 0) throw new IllegalArgumentException("ProfileSocialLinkId must be positive");
|
||||
if (experienceId != null && experienceId < 0)
|
||||
throw new IllegalArgumentException("ProfileSocialLinkId must be positive");
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,13 @@ package com.pablotj.portfolio.domain.project.port;
|
||||
|
||||
import com.pablotj.portfolio.domain.project.Project;
|
||||
import com.pablotj.portfolio.domain.project.ProjectId;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ProjectRepositoryPort {
|
||||
Project save(Project p);
|
||||
|
||||
Optional<Project> findById(ProjectId id);
|
||||
|
||||
List<Project> findAll(Long profileId);
|
||||
}
|
||||
@@ -1,12 +1,23 @@
|
||||
<project>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.pablotj</groupId>
|
||||
<artifactId>portfolio-api</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>infrastructure</artifactId>
|
||||
|
||||
<properties>
|
||||
<jooby.version>3.6.1</jooby.version>
|
||||
<ebean.version>15.8.0</ebean.version>
|
||||
<lombok.version>1.18.36</lombok.version>
|
||||
<mapstruct.version>1.6.3</mapstruct.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.pablotj</groupId>
|
||||
@@ -19,36 +30,71 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-ebean</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-hibernate-validator</artifactId>
|
||||
<version>3.6.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-jackson</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MapStruct -->
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
<version>${lombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.annotation</groupId>
|
||||
<artifactId>jakarta.annotation-api</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>${ebean.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>main</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>enhance</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,34 +1,26 @@
|
||||
package com.pablotj.portfolio.infrastructure.config;
|
||||
|
||||
import io.jooby.Extension;
|
||||
import io.jooby.Jooby;
|
||||
import io.jooby.handler.Cors;
|
||||
import io.jooby.handler.CorsHandler;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
|
||||
@Value("${app.cors.allowed-origins}")
|
||||
private String allowedOriginsString;
|
||||
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
public class CorsConfig implements Extension {
|
||||
|
||||
@Override
|
||||
public void install(@Nonnull Jooby app) {
|
||||
String allowedOriginsString = app.getConfig().getString("app.cors.allowed-origins");
|
||||
List<String> allowedOrigins = Arrays.asList(allowedOriginsString.split(","));
|
||||
config.setAllowedOriginPatterns(allowedOrigins);
|
||||
|
||||
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
||||
config.setAllowedHeaders(List.of("*"));
|
||||
config.setAllowCredentials(true);
|
||||
Cors cors = new Cors()
|
||||
.setOrigin(allowedOrigins)
|
||||
.setMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"))
|
||||
.setHeaders(Arrays.asList("Content-Type", "Authorization", "X-Requested-With"))
|
||||
.setUseCredentials(true);
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
|
||||
return source;
|
||||
app.use(new CorsHandler(cors));
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,13 @@
|
||||
package com.pablotj.portfolio.infrastructure.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import io.jooby.Extension;
|
||||
import io.jooby.Jooby;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
public class SecurityConfig implements Extension {
|
||||
|
||||
private final CorsConfigurationSource corsConfigurationSource;
|
||||
@Override
|
||||
public void install(@Nonnull Jooby app) {
|
||||
|
||||
public SecurityConfig(CorsConfigurationSource corsConfigurationSource) {
|
||||
this.corsConfigurationSource = corsConfigurationSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.cors(cors -> cors.configurationSource(corsConfigurationSource))
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.anyRequest().permitAll()
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
@@ -3,38 +3,45 @@ package com.pablotj.portfolio.infrastructure.persistence.certification.adapter;
|
||||
import com.pablotj.portfolio.domain.certification.Certification;
|
||||
import com.pablotj.portfolio.domain.certification.CertificationId;
|
||||
import com.pablotj.portfolio.domain.certification.port.CertificationRepositoryPort;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.certification.mapper.CertificationJpaMapper;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.certification.repo.SpringDataCertificationRepository;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.certification.mapper.CertificationEntityMapper;
|
||||
import io.ebean.DB;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Repository
|
||||
@Singleton
|
||||
public class CertificationRepositoryAdapter implements CertificationRepositoryPort {
|
||||
|
||||
private final SpringDataCertificationRepository repo;
|
||||
private final CertificationJpaMapper mapper;
|
||||
|
||||
public CertificationRepositoryAdapter(SpringDataCertificationRepository repo, CertificationJpaMapper mapper) {
|
||||
this.repo = repo;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
private static final CertificationEntityMapper MAPPER = Mappers.getMapper(CertificationEntityMapper.class);
|
||||
|
||||
@Override
|
||||
public Certification save(Certification p) {
|
||||
CertificationJpaEntity entity = mapper.toEntity(p);
|
||||
CertificationJpaEntity saved = repo.save(entity);
|
||||
return mapper.toDomain(saved);
|
||||
CertificationEntity entity = MAPPER.toEntity(p);
|
||||
DB.save(entity);
|
||||
return MAPPER.toDomain(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Certification> findById(CertificationId id) {
|
||||
return repo.findByProfileIdAndId(id.profileId(), id.certificationId()).map(mapper::toDomain);
|
||||
CertificationEntity entity = DB.find(CertificationEntity.class)
|
||||
.where()
|
||||
.eq("profile.id", id.profileId())
|
||||
.eq("id", id.certificationId())
|
||||
.findOne();
|
||||
|
||||
return Optional.ofNullable(entity).map(MAPPER::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Certification> findAll(Long profileId) {
|
||||
return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList();
|
||||
return DB.find(CertificationEntity.class)
|
||||
.where()
|
||||
.eq("profile.id", profileId)
|
||||
.findList()
|
||||
.stream()
|
||||
.map(MAPPER::toDomain)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.certification.entity;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity;
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
@@ -17,7 +18,7 @@ import lombok.Setter;
|
||||
@Table(name = "CERTIFICATION")
|
||||
@Getter
|
||||
@Setter
|
||||
public class CertificationJpaEntity {
|
||||
public class CertificationEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -25,7 +26,7 @@ public class CertificationJpaEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "profile_id", nullable = false)
|
||||
private ProfileJpaEntity profile;
|
||||
private ProfileEntity profile;
|
||||
|
||||
@Column
|
||||
private String name;
|
||||
@@ -1,18 +1,17 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.certification.mapper;
|
||||
|
||||
import com.pablotj.portfolio.domain.certification.Certification;
|
||||
import com.pablotj.portfolio.domain.certification.CertificationId;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface CertificationJpaMapper {
|
||||
@Mapper
|
||||
public interface CertificationEntityMapper {
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
@Mapping(target = "profile.id", source = "id.profileId")
|
||||
CertificationJpaEntity toEntity(Certification domain);
|
||||
CertificationEntity toEntity(Certification domain);
|
||||
|
||||
@Mapping(target = "id", expression = "java(new CertificationId(e.getProfile().getId(), e.getId()))")
|
||||
Certification toDomain(CertificationJpaEntity e);
|
||||
Certification toDomain(CertificationEntity e);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.certification.repo;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationJpaEntity;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface SpringDataCertificationRepository extends JpaRepository<CertificationJpaEntity, Long> {
|
||||
|
||||
Optional<CertificationJpaEntity> findByProfileIdAndId(Long profileId, Long id);
|
||||
|
||||
List<CertificationJpaEntity> findAllByProfileId(Long profileId);
|
||||
|
||||
}
|
||||
@@ -3,38 +3,45 @@ package com.pablotj.portfolio.infrastructure.persistence.education.adapter;
|
||||
import com.pablotj.portfolio.domain.education.Education;
|
||||
import com.pablotj.portfolio.domain.education.EducationId;
|
||||
import com.pablotj.portfolio.domain.education.port.EducationRepositoryPort;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.education.mapper.EducationJpaMapper;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.education.repo.SpringDataEducationRepository;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.education.mapper.EducationEntityMapper;
|
||||
import io.ebean.DB;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Repository
|
||||
@Singleton
|
||||
public class EducationRepositoryAdapter implements EducationRepositoryPort {
|
||||
|
||||
private final SpringDataEducationRepository repo;
|
||||
private final EducationJpaMapper mapper;
|
||||
|
||||
public EducationRepositoryAdapter(SpringDataEducationRepository repo, EducationJpaMapper mapper) {
|
||||
this.repo = repo;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
private static final EducationEntityMapper MAPPER = Mappers.getMapper(EducationEntityMapper.class);
|
||||
|
||||
@Override
|
||||
public Education save(Education p) {
|
||||
EducationJpaEntity entity = mapper.toEntity(p);
|
||||
EducationJpaEntity saved = repo.save(entity);
|
||||
return mapper.toDomain(saved);
|
||||
EducationEntity entity = MAPPER.toEntity(p);
|
||||
DB.save(entity);
|
||||
return MAPPER.toDomain(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Education> findById(EducationId id) {
|
||||
return repo.findByProfileIdAndId(id.profileId(), id.educationId()).map(mapper::toDomain);
|
||||
EducationEntity entity = DB.find(EducationEntity.class)
|
||||
.where()
|
||||
.eq("profile.id", id.profileId())
|
||||
.eq("id", id.educationId())
|
||||
.findOne();
|
||||
|
||||
return Optional.ofNullable(entity).map(MAPPER::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Education> findAll(Long profileId) {
|
||||
return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList();
|
||||
return DB.find(EducationEntity.class)
|
||||
.where()
|
||||
.eq("profile.id", profileId)
|
||||
.findList()
|
||||
.stream()
|
||||
.map(MAPPER::toDomain)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.education.entity;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity;
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
@@ -17,7 +18,7 @@ import lombok.Setter;
|
||||
@Table(name = "EDUCATION")
|
||||
@Getter
|
||||
@Setter
|
||||
public class EducationJpaEntity {
|
||||
public class EducationEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -25,7 +26,7 @@ public class EducationJpaEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "profile_id", nullable = false)
|
||||
private ProfileJpaEntity profile;
|
||||
private ProfileEntity profile;
|
||||
|
||||
@Column
|
||||
private String institution;
|
||||
@@ -1,18 +1,17 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.education.mapper;
|
||||
|
||||
import com.pablotj.portfolio.domain.education.Education;
|
||||
import com.pablotj.portfolio.domain.education.EducationId;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface EducationJpaMapper {
|
||||
@Mapper
|
||||
public interface EducationEntityMapper {
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
@Mapping(target = "profile.id", source = "id.profileId")
|
||||
EducationJpaEntity toEntity(Education domain);
|
||||
EducationEntity toEntity(Education domain);
|
||||
|
||||
@Mapping(target = "id", expression = "java(new EducationId(e.getProfile().getId(), e.getId()))")
|
||||
Education toDomain(EducationJpaEntity e);
|
||||
Education toDomain(EducationEntity e);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.education.repo;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationJpaEntity;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface SpringDataEducationRepository extends JpaRepository<EducationJpaEntity, Long> {
|
||||
Optional<EducationJpaEntity> findByProfileIdAndId(Long profileId, Long id);
|
||||
|
||||
List<EducationJpaEntity> findAllByProfileId(Long profileId);
|
||||
}
|
||||
@@ -3,38 +3,49 @@ package com.pablotj.portfolio.infrastructure.persistence.experience.adapter;
|
||||
import com.pablotj.portfolio.domain.experience.Experience;
|
||||
import com.pablotj.portfolio.domain.experience.ExperienceId;
|
||||
import com.pablotj.portfolio.domain.experience.port.ExperienceRepositoryPort;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.mapper.ExperienceJpaMapper;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.repo.SpringDataExperienceRepository;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.mapper.ExperienceEntityMapper;
|
||||
import io.ebean.DB;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Repository
|
||||
@Singleton
|
||||
public class ExperienceRepositoryAdapter implements ExperienceRepositoryPort {
|
||||
|
||||
private final SpringDataExperienceRepository repo;
|
||||
private final ExperienceJpaMapper mapper;
|
||||
|
||||
public ExperienceRepositoryAdapter(SpringDataExperienceRepository repo, ExperienceJpaMapper mapper) {
|
||||
this.repo = repo;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
private static final ExperienceEntityMapper MAPPER = Mappers.getMapper(ExperienceEntityMapper.class);
|
||||
|
||||
@Override
|
||||
public Experience save(Experience p) {
|
||||
ExperienceJpaEntity entity = mapper.toEntity(p);
|
||||
ExperienceJpaEntity saved = repo.save(entity);
|
||||
return mapper.toDomain(saved);
|
||||
ExperienceEntity entity = MAPPER.toEntity(p);
|
||||
DB.save(entity);
|
||||
return MAPPER.toDomain(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Experience> findById(ExperienceId id) {
|
||||
return repo.findByProfileIdAndId(id.profileId(), id.experienceId()).map(mapper::toDomain);
|
||||
ExperienceEntity entity = DB.find(ExperienceEntity.class)
|
||||
.fetch("technologies")
|
||||
.fetch("achievements")
|
||||
.where()
|
||||
.eq("profile.id", id.profileId())
|
||||
.eq("id", id.experienceId())
|
||||
.findOne();
|
||||
|
||||
return Optional.ofNullable(entity).map(MAPPER::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Experience> findAll(Long profileId) {
|
||||
return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList();
|
||||
return DB.find(ExperienceEntity.class)
|
||||
.fetch("technologies")
|
||||
.fetch("achievements")
|
||||
.where()
|
||||
.eq("profile.id", profileId)
|
||||
.findList()
|
||||
.stream()
|
||||
.map(MAPPER::toDomain)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.experience.entity;
|
||||
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
@@ -20,7 +21,7 @@ import lombok.Setter;
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@Builder
|
||||
public class ExperienceAchievementJpaEntity {
|
||||
public class ExperienceAchievementEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.experience.entity;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity;
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
@@ -27,7 +28,7 @@ import lombok.Setter;
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@Builder
|
||||
public class ExperienceJpaEntity {
|
||||
public class ExperienceEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -35,7 +36,7 @@ public class ExperienceJpaEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "profile_id", nullable = false)
|
||||
private ProfileJpaEntity profile;
|
||||
private ProfileEntity profile;
|
||||
|
||||
@Column
|
||||
private String position;
|
||||
@@ -52,11 +53,12 @@ public class ExperienceJpaEntity {
|
||||
@Column(columnDefinition = "text")
|
||||
private String description;
|
||||
|
||||
// Ebean soporta perfectamente CascadeType.ALL y orphanRemoval
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@JoinColumn(name = "EXPERIENCE_ID")
|
||||
private List<ExperienceSkillJpaEntity> technologies;
|
||||
private List<ExperienceSkillEntity> technologies;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@JoinColumn(name = "EXPERIENCE_ID")
|
||||
private List<ExperienceAchievementJpaEntity> achievements;
|
||||
private List<ExperienceAchievementEntity> achievements;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.experience.entity;
|
||||
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
@@ -19,7 +20,7 @@ import lombok.Setter;
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@Builder
|
||||
public class ExperienceSkillJpaEntity {
|
||||
public class ExperienceSkillEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -3,33 +3,33 @@ package com.pablotj.portfolio.infrastructure.persistence.experience.mapper;
|
||||
import com.pablotj.portfolio.domain.experience.Achievement;
|
||||
import com.pablotj.portfolio.domain.experience.Experience;
|
||||
import com.pablotj.portfolio.domain.experience.Technology;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceAchievementJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceSkillJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceAchievementEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceSkillEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface ExperienceJpaMapper {
|
||||
@Mapper
|
||||
public interface ExperienceEntityMapper {
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
@Mapping(target = "profile.id", source = "id.profileId")
|
||||
ExperienceJpaEntity toEntity(Experience domain);
|
||||
ExperienceEntity toEntity(Experience domain);
|
||||
|
||||
@Mapping(target = "id", expression = "java(new ExperienceId(entity.getProfile().getId(), entity.getId()))")
|
||||
Experience toDomain(ExperienceJpaEntity entity);
|
||||
Experience toDomain(ExperienceEntity entity);
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
ExperienceAchievementJpaEntity toEntity(Achievement entity);
|
||||
ExperienceAchievementEntity toEntity(Achievement entity);
|
||||
|
||||
@Mapping(target = "id.value", source = "id")
|
||||
Achievement toDomain(ExperienceAchievementJpaEntity entity);
|
||||
Achievement toDomain(ExperienceAchievementEntity entity);
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
ExperienceSkillJpaEntity toEntity(Technology entity);
|
||||
ExperienceSkillEntity toEntity(Technology entity);
|
||||
|
||||
@Mapping(target = "id.value", source = "id")
|
||||
Technology toDomain(ExperienceSkillJpaEntity entity);
|
||||
Technology toDomain(ExperienceSkillEntity entity);
|
||||
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.experience.repo;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceJpaEntity;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface SpringDataExperienceRepository extends JpaRepository<ExperienceJpaEntity, Long> {
|
||||
Optional<ExperienceJpaEntity> findByProfileIdAndId(Long profileId, Long id);
|
||||
|
||||
List<ExperienceJpaEntity> findAllByProfileId(Long profileId);
|
||||
}
|
||||
@@ -3,49 +3,56 @@ package com.pablotj.portfolio.infrastructure.persistence.profile.adapter;
|
||||
import com.pablotj.portfolio.domain.profile.Profile;
|
||||
import com.pablotj.portfolio.domain.profile.ProfileId;
|
||||
import com.pablotj.portfolio.domain.profile.port.ProfileRepositoryPort;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileSocialLinkJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.mapper.ProfileJpaMapper;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.repo.SpringDataProfileRepository;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.mapper.ProfileEntityMapper;
|
||||
import io.ebean.DB;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Repository
|
||||
@Singleton
|
||||
public class ProfileRepositoryAdapter implements ProfileRepositoryPort {
|
||||
|
||||
private final SpringDataProfileRepository repo;
|
||||
private final ProfileJpaMapper mapper;
|
||||
|
||||
public ProfileRepositoryAdapter(SpringDataProfileRepository repo, ProfileJpaMapper mapper) {
|
||||
this.repo = repo;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
private static final ProfileEntityMapper MAPPER = Mappers.getMapper(ProfileEntityMapper.class);
|
||||
|
||||
@Override
|
||||
public Profile save(Profile p) {
|
||||
ProfileJpaEntity entity = mapper.toEntity(p);
|
||||
if (entity.getSocial() != null) {
|
||||
for (ProfileSocialLinkJpaEntity socialLinkJpaEntity : entity.getSocial()) {
|
||||
socialLinkJpaEntity.setProfile(entity);
|
||||
}
|
||||
}
|
||||
ProfileJpaEntity saved = repo.save(entity);
|
||||
return mapper.toDomain(saved);
|
||||
ProfileEntity entity = MAPPER.toEntity(p);
|
||||
DB.save(entity);
|
||||
|
||||
return MAPPER.toDomain(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Profile> findBySlug(ProfileId id) {
|
||||
return repo.findBySlug(id.slug()).map(mapper::toDomain);
|
||||
ProfileEntity entity = DB.find(ProfileEntity.class)
|
||||
.fetch("social")
|
||||
.where()
|
||||
.eq("slug", id.slug())
|
||||
.findOne();
|
||||
|
||||
return Optional.ofNullable(entity).map(MAPPER::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Profile> findById(ProfileId id) {
|
||||
return repo.findById(id.id()).map(mapper::toDomain);
|
||||
ProfileEntity entity = DB.find(ProfileEntity.class)
|
||||
.fetch("social")
|
||||
.where()
|
||||
.eq("id", id.id())
|
||||
.findOne();
|
||||
|
||||
return Optional.ofNullable(entity).map(MAPPER::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Profile> findAll() {
|
||||
return repo.findAll().stream().map(mapper::toDomain).toList();
|
||||
return DB.find(ProfileEntity.class)
|
||||
.fetch("social")
|
||||
.findList()
|
||||
.stream()
|
||||
.map(MAPPER::toDomain)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.profile.entity;
|
||||
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
@@ -17,7 +18,7 @@ import lombok.Setter;
|
||||
@Table(name = "profile")
|
||||
@Getter
|
||||
@Setter
|
||||
public class ProfileJpaEntity {
|
||||
public class ProfileEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -47,10 +48,10 @@ public class ProfileJpaEntity {
|
||||
@Column
|
||||
private String avatar;
|
||||
|
||||
@Column
|
||||
@Column(columnDefinition = "text")
|
||||
private String bio;
|
||||
|
||||
@OneToMany(mappedBy = "profile", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<ProfileSocialLinkJpaEntity> social = new ArrayList<>();
|
||||
private List<ProfileSocialLinkEntity> social = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.profile.entity;
|
||||
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
@@ -16,14 +17,15 @@ import lombok.Setter;
|
||||
@Table(name = "profile_social_link")
|
||||
@Getter
|
||||
@Setter
|
||||
public class ProfileSocialLinkJpaEntity {
|
||||
public class ProfileSocialLinkEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "profile_id", nullable = false)
|
||||
private ProfileJpaEntity profile;
|
||||
private ProfileEntity profile;
|
||||
|
||||
@Column
|
||||
private String url;
|
||||
@@ -2,25 +2,25 @@ package com.pablotj.portfolio.infrastructure.persistence.profile.mapper;
|
||||
|
||||
import com.pablotj.portfolio.domain.profile.Profile;
|
||||
import com.pablotj.portfolio.domain.profile.ProfileSocialLink;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileSocialLinkJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileSocialLinkEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface ProfileJpaMapper {
|
||||
@Mapper
|
||||
public interface ProfileEntityMapper {
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
@Mapping(target = "social", source = "social")
|
||||
ProfileJpaEntity toEntity(Profile domain);
|
||||
ProfileEntity toEntity(Profile domain);
|
||||
|
||||
@Mapping(target = "id.id", source = "id")
|
||||
@Mapping(target = "social", source = "social")
|
||||
Profile toDomain(ProfileJpaEntity e);
|
||||
Profile toDomain(ProfileEntity e);
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
ProfileSocialLinkJpaEntity toEntitySocial(ProfileSocialLink entity);
|
||||
ProfileSocialLinkEntity toEntitySocial(ProfileSocialLink entity);
|
||||
|
||||
@Mapping(target = "id.value", source = "id")
|
||||
ProfileSocialLink toDomainSocial(ProfileSocialLinkJpaEntity entity);
|
||||
ProfileSocialLink toDomainSocial(ProfileSocialLinkEntity entity);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.profile.repo;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface SpringDataProfileRepository extends JpaRepository<ProfileJpaEntity, Long> {
|
||||
|
||||
Optional<ProfileJpaEntity> findBySlug(String slug);
|
||||
}
|
||||
@@ -3,39 +3,50 @@ package com.pablotj.portfolio.infrastructure.persistence.project.adapter;
|
||||
import com.pablotj.portfolio.domain.project.Project;
|
||||
import com.pablotj.portfolio.domain.project.ProjectId;
|
||||
import com.pablotj.portfolio.domain.project.port.ProjectRepositoryPort;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.mapper.ProjectJpaMapper;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.repo.SpringDataProjectRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.mapper.ProjectEntityMapper;
|
||||
import io.ebean.DB;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Repository
|
||||
@Singleton
|
||||
public class ProjectRepositoryAdapter implements ProjectRepositoryPort {
|
||||
|
||||
private final SpringDataProjectRepository repo;
|
||||
private final ProjectJpaMapper mapper;
|
||||
private static final ProjectEntityMapper MAPPER = Mappers.getMapper(ProjectEntityMapper.class);
|
||||
|
||||
public ProjectRepositoryAdapter(SpringDataProjectRepository repo, ProjectJpaMapper mapper) {
|
||||
this.repo = repo;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Project save(Project p) {
|
||||
ProjectJpaEntity entity = mapper.toEntity(p);
|
||||
ProjectJpaEntity saved = repo.save(entity);
|
||||
return mapper.toDomain(saved);
|
||||
ProjectEntity entity = MAPPER.toEntity(p);
|
||||
DB.save(entity);
|
||||
return MAPPER.toDomain(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Project> findById(ProjectId id) {
|
||||
return repo.findByProfileIdAndId(id.profileId(), id.projectId()).map(mapper::toDomain);
|
||||
ProjectEntity entity = DB.find(ProjectEntity.class)
|
||||
.fetch("technologies")
|
||||
.fetch("features")
|
||||
.where()
|
||||
.eq("profile.id", id.profileId())
|
||||
.eq("id", id.projectId())
|
||||
.findOne();
|
||||
|
||||
return Optional.ofNullable(entity).map(MAPPER::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Project> findAll(Long profileId) {
|
||||
return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList();
|
||||
return DB.find(ProjectEntity.class)
|
||||
.fetch("technologies")
|
||||
.fetch("features")
|
||||
.where()
|
||||
.eq("profile.id", profileId)
|
||||
.findList()
|
||||
.stream()
|
||||
.map(MAPPER::toDomain)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.project.entity;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity;
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
@@ -18,8 +19,9 @@ import lombok.Setter;
|
||||
|
||||
@Entity
|
||||
@Table(name = "PROJECT")
|
||||
@Getter @Setter
|
||||
public class ProjectJpaEntity {
|
||||
@Getter
|
||||
@Setter
|
||||
public class ProjectEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -27,12 +29,12 @@ public class ProjectJpaEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "profile_id", nullable = false)
|
||||
private ProfileJpaEntity profile;
|
||||
private ProfileEntity profile;
|
||||
|
||||
@Column
|
||||
private String title;
|
||||
|
||||
@Column
|
||||
@Column(columnDefinition = "text")
|
||||
private String description;
|
||||
|
||||
@Column
|
||||
@@ -40,11 +42,11 @@ public class ProjectJpaEntity {
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@JoinColumn(name = "PROJECT_ID")
|
||||
private List<ProjectTechnologyJpaEntity> technologies;
|
||||
private List<ProjectTechnologyEntity> technologies;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@JoinColumn(name = "PROJECT_ID")
|
||||
private List<ProjectFeatureJpaEntity> features;
|
||||
private List<ProjectFeatureEntity> features;
|
||||
|
||||
@Column
|
||||
private String demo;
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.project.entity;
|
||||
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
@@ -13,7 +14,7 @@ import lombok.Setter;
|
||||
@Table(name = "PROJECT_FEATURE")
|
||||
@Getter
|
||||
@Setter
|
||||
public class ProjectFeatureJpaEntity {
|
||||
public class ProjectFeatureEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.project.entity;
|
||||
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
@@ -13,7 +14,7 @@ import lombok.Setter;
|
||||
@Table(name = "PROJECT_FEATURE_TECHNOLOGY")
|
||||
@Getter
|
||||
@Setter
|
||||
public class ProjectTechnologyJpaEntity {
|
||||
public class ProjectTechnologyEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -3,31 +3,31 @@ package com.pablotj.portfolio.infrastructure.persistence.project.mapper;
|
||||
import com.pablotj.portfolio.domain.project.Project;
|
||||
import com.pablotj.portfolio.domain.project.ProjectFeature;
|
||||
import com.pablotj.portfolio.domain.project.ProjectTechnology;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectFeatureJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectTechnologyJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectFeatureEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectTechnologyEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface ProjectJpaMapper {
|
||||
@Mapper
|
||||
public interface ProjectEntityMapper {
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
@Mapping(target = "profile.id", source = "id.profileId")
|
||||
ProjectJpaEntity toEntity(Project domain);
|
||||
ProjectEntity toEntity(Project domain);
|
||||
|
||||
@Mapping(target = "id", expression = "java(new ProjectId(e.getProfile().getId(), e.getId()))")
|
||||
Project toDomain(ProjectJpaEntity e);
|
||||
Project toDomain(ProjectEntity e);
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
ProjectTechnologyJpaEntity toEntity(ProjectTechnology entity);
|
||||
ProjectTechnologyEntity toEntity(ProjectTechnology entity);
|
||||
|
||||
@Mapping(target = "id.value", source = "id")
|
||||
ProjectTechnology toDomain(ProjectTechnologyJpaEntity entity);
|
||||
ProjectTechnology toDomain(ProjectTechnologyEntity entity);
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
ProjectFeatureJpaEntity toEntity(ProjectFeature entity);
|
||||
ProjectFeatureEntity toEntity(ProjectFeature entity);
|
||||
|
||||
@Mapping(target = "id.value", source = "id")
|
||||
ProjectFeature toDomain(ProjectFeatureJpaEntity entity);
|
||||
ProjectFeature toDomain(ProjectFeatureEntity entity);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.project.repo;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectJpaEntity;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface SpringDataProjectRepository extends JpaRepository<ProjectJpaEntity, Long> {
|
||||
Optional<ProjectJpaEntity> findByProfileIdAndId(Long profileId, Long id);
|
||||
|
||||
List<ProjectJpaEntity> findAllByProfileId(Long profileId);
|
||||
}
|
||||
@@ -3,38 +3,47 @@ package com.pablotj.portfolio.infrastructure.persistence.skill.adapter;
|
||||
import com.pablotj.portfolio.domain.skill.SkillGroup;
|
||||
import com.pablotj.portfolio.domain.skill.SkillGroupId;
|
||||
import com.pablotj.portfolio.domain.skill.port.SkillRepositoryPort;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillGroupJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.skill.mapper.SkillJpaMapper;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.skill.repo.SpringDataSkillRepository;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillGroupEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.skill.mapper.SkillMapper;
|
||||
import io.ebean.DB;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Repository
|
||||
@Singleton
|
||||
public class SkillRepositoryAdapter implements SkillRepositoryPort {
|
||||
|
||||
private final SpringDataSkillRepository repo;
|
||||
private final SkillJpaMapper mapper;
|
||||
|
||||
public SkillRepositoryAdapter(SpringDataSkillRepository repo, SkillJpaMapper mapper) {
|
||||
this.repo = repo;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
private static final SkillMapper MAPPER = Mappers.getMapper(SkillMapper.class);
|
||||
|
||||
@Override
|
||||
public SkillGroup save(SkillGroup p) {
|
||||
SkillGroupJpaEntity entity = mapper.toEntity(p);
|
||||
SkillGroupJpaEntity saved = repo.save(entity);
|
||||
return mapper.toDomain(saved);
|
||||
SkillGroupEntity entity = MAPPER.toEntity(p);
|
||||
DB.save(entity);
|
||||
return MAPPER.toDomain(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SkillGroup> findById(SkillGroupId id) {
|
||||
return repo.findByProfileIdAndId(id.profileId(), id.skillGroupId()).map(mapper::toDomain);
|
||||
SkillGroupEntity entity = DB.find(SkillGroupEntity.class)
|
||||
.fetch("skills") // Carga inmediata de la lista de habilidades
|
||||
.where()
|
||||
.eq("profile.id", id.profileId())
|
||||
.eq("id", id.skillGroupId())
|
||||
.findOne();
|
||||
|
||||
return Optional.ofNullable(entity).map(MAPPER::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SkillGroup> findAll(Long profileId) {
|
||||
return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList();
|
||||
return DB.find(SkillGroupEntity.class)
|
||||
.fetch("skills")
|
||||
.where()
|
||||
.eq("profile.id", profileId)
|
||||
.findList()
|
||||
.stream()
|
||||
.map(MAPPER::toDomain)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.skill.entity;
|
||||
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
@@ -13,7 +14,7 @@ import lombok.Setter;
|
||||
@Table(name = "SKILL")
|
||||
@Getter
|
||||
@Setter
|
||||
public class SkillJpaEntity {
|
||||
public class SkillEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.skill.entity;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity;
|
||||
import io.ebean.Model;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
@@ -20,7 +21,7 @@ import lombok.Setter;
|
||||
@Table(name = "SKILL_GROUP")
|
||||
@Getter
|
||||
@Setter
|
||||
public class SkillGroupJpaEntity {
|
||||
public class SkillGroupEntity extends Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -28,7 +29,7 @@ public class SkillGroupJpaEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "profile_id", nullable = false)
|
||||
private ProfileJpaEntity profile;
|
||||
private ProfileEntity profile;
|
||||
|
||||
@Column
|
||||
private String name;
|
||||
@@ -38,5 +39,5 @@ public class SkillGroupJpaEntity {
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@JoinColumn(name = "SKILL_ID")
|
||||
private List<SkillJpaEntity> skills;
|
||||
private List<SkillEntity> skills;
|
||||
}
|
||||
@@ -2,24 +2,24 @@ package com.pablotj.portfolio.infrastructure.persistence.skill.mapper;
|
||||
|
||||
import com.pablotj.portfolio.domain.skill.Skill;
|
||||
import com.pablotj.portfolio.domain.skill.SkillGroup;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillGroupJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillGroupEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface SkillJpaMapper {
|
||||
@Mapper
|
||||
public interface SkillMapper {
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
@Mapping(target = "profile.id", source = "id.profileId")
|
||||
SkillGroupJpaEntity toEntity(SkillGroup domain);
|
||||
SkillGroupEntity toEntity(SkillGroup domain);
|
||||
|
||||
@Mapping(target = "id", expression = "java(new SkillGroupId(entity.getProfile().getId(), entity.getId()))")
|
||||
SkillGroup toDomain(SkillGroupJpaEntity entity);
|
||||
SkillGroup toDomain(SkillGroupEntity entity);
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
SkillJpaEntity toEntity(Skill entity);
|
||||
SkillEntity toEntity(Skill entity);
|
||||
|
||||
@Mapping(target = "id.value", source = "id")
|
||||
Skill toDomain(SkillJpaEntity entity);
|
||||
Skill toDomain(SkillEntity entity);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.pablotj.portfolio.infrastructure.persistence.skill.repo;
|
||||
|
||||
import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectJpaEntity;
|
||||
import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillGroupJpaEntity;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface SpringDataSkillRepository extends JpaRepository<SkillGroupJpaEntity, Long> {
|
||||
Optional<SkillGroupJpaEntity> findByProfileIdAndId(Long profileId, Long id);
|
||||
|
||||
List<SkillGroupJpaEntity> findAllByProfileId(Long profileId);
|
||||
}
|
||||
@@ -1,30 +1,28 @@
|
||||
package com.pablotj.portfolio.infrastructure.rest.api;
|
||||
|
||||
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
import io.jooby.ErrorHandler;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Controller
|
||||
public class ApiErrorController implements ErrorController {
|
||||
public class ApiErrorController {
|
||||
|
||||
private final ErrorAttributes errorAttributes;
|
||||
private static final Logger log = LoggerFactory.getLogger(ApiErrorController.class);
|
||||
|
||||
public ApiErrorController(ErrorAttributes errorAttributes) {
|
||||
this.errorAttributes = errorAttributes;
|
||||
}
|
||||
public static ErrorHandler getHandler() {
|
||||
return (ctx, cause, statusCode) -> {
|
||||
log.error("Error en la API: {}", cause.getMessage(), cause);
|
||||
|
||||
@RequestMapping("/error")
|
||||
public ResponseEntity<Map<String, Object>> handleError(WebRequest webRequest) {
|
||||
Map<String, Object> attributes = errorAttributes.getErrorAttributes(webRequest,
|
||||
ErrorAttributeOptions.defaults());
|
||||
HttpStatus status = HttpStatus.valueOf((int) attributes.getOrDefault("status", 500));
|
||||
return new ResponseEntity<>(attributes, status);
|
||||
Map<String, Object> errorAttributes = new LinkedHashMap<>();
|
||||
errorAttributes.put("timestamp", System.currentTimeMillis());
|
||||
errorAttributes.put("status", statusCode.value());
|
||||
errorAttributes.put("error", statusCode.reason());
|
||||
errorAttributes.put("message", cause.getMessage());
|
||||
errorAttributes.put("path", ctx.getRequestPath());
|
||||
|
||||
ctx.setResponseCode(statusCode)
|
||||
.render(errorAttributes);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,39 @@
|
||||
package com.pablotj.portfolio.infrastructure.rest.api;
|
||||
|
||||
import io.jooby.annotation.GET;
|
||||
import io.jooby.annotation.Path;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping
|
||||
@Path("/")
|
||||
public class ApiRootController {
|
||||
|
||||
@Value("${info.app.version}")
|
||||
private String appVersion;
|
||||
private final String appVersion;
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<Map<String, Object>> root() {
|
||||
Map<String, Object> response = Map.of(
|
||||
@Inject
|
||||
public ApiRootController(@Named("info.app.version") String appVersion) {
|
||||
this.appVersion = appVersion;
|
||||
}
|
||||
|
||||
@GET
|
||||
public Map<String, Object> root() {
|
||||
return Map.of(
|
||||
"api", "Portfolio API",
|
||||
"version", appVersion,
|
||||
"doc", "/v3/api-docs",
|
||||
"swagger", "/swagger-ui",
|
||||
"endpoints", List.of(
|
||||
Map.of("path", "/v1/homes", "description", "Manage projects"),
|
||||
Map.of("path", "/v1/certifications", "description", "Manage projects"),
|
||||
Map.of("path", "/v1/homes", "description", "Manage home details"),
|
||||
Map.of("path", "/v1/certifications", "description", "Manage certifications"),
|
||||
Map.of("path", "/v1/projects", "description", "Manage projects"),
|
||||
Map.of("path", "/v1/contacts", "description", "Manage projects"),
|
||||
Map.of("path", "/v1/educations", "description", "Manage projects"),
|
||||
Map.of("path", "/v1/experiences", "description", "Manage projects"),
|
||||
Map.of("path", "/v1/projects", "description", "Manage projects"),
|
||||
Map.of("path", "/v1/technologies", "description", "ProfileSocialLink entries")
|
||||
Map.of("path", "/v1/contacts", "description", "Manage contact info"),
|
||||
Map.of("path", "/v1/educations", "description", "Manage education"),
|
||||
Map.of("path", "/v1/experiences", "description", "Manage experience"),
|
||||
Map.of("path", "/v1/skills", "description", "Manage skills"),
|
||||
Map.of("path", "/v1/technologies", "description", "Manage technologies")
|
||||
)
|
||||
);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
}
|
||||
@@ -5,54 +5,53 @@ import com.pablotj.portfolio.application.certification.GetCertificationUseCase;
|
||||
import com.pablotj.portfolio.infrastructure.rest.certification.dto.CertificationDto;
|
||||
import com.pablotj.portfolio.infrastructure.rest.certification.dto.CreateCertificationRequest;
|
||||
import com.pablotj.portfolio.infrastructure.rest.certification.mapper.CertificationRestMapper;
|
||||
import io.jooby.annotation.GET;
|
||||
import io.jooby.annotation.POST;
|
||||
import io.jooby.annotation.Path;
|
||||
import io.jooby.annotation.PathParam;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/v1/profiles/{profileId}/certifications")
|
||||
@Path("/v1/profiles/{profileId}/certifications")
|
||||
public class CertificationController {
|
||||
private static final CertificationRestMapper MAPPER = Mappers.getMapper(CertificationRestMapper.class);
|
||||
|
||||
private final CreateCertificationUseCase createUC;
|
||||
private final GetCertificationUseCase getUC;
|
||||
private final CertificationRestMapper mapper;
|
||||
|
||||
public CertificationController(CreateCertificationUseCase createUC, GetCertificationUseCase getUC, CertificationRestMapper mapper) {
|
||||
@Inject
|
||||
public CertificationController(CreateCertificationUseCase createUC, GetCertificationUseCase getUC) {
|
||||
this.createUC = createUC;
|
||||
this.getUC = getUC;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<CertificationDto> all(@PathVariable Long profileId) {
|
||||
return getUC.all(profileId).stream().map(mapper::toDto).toList();
|
||||
@GET
|
||||
public List<CertificationDto> all(@PathParam Long profileId) {
|
||||
return getUC.all(profileId).stream()
|
||||
.map(MAPPER::toDto)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<CertificationDto> byId(@PathVariable Long profileId, @PathVariable Long id) {
|
||||
@GET("/{id}")
|
||||
public CertificationDto byId(@PathParam Long profileId, @PathParam Long id) {
|
||||
// En Jooby, si devuelves un Optional vacío, automáticamente lanza un 404
|
||||
return getUC.byId(profileId, id)
|
||||
.map(mapper::toDto)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
.map(MAPPER::toDto)
|
||||
.orElseThrow(() -> new io.jooby.exception.NotFoundException("Certification not found"));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<CertificationDto> create(@PathVariable Long profileId, @Valid @RequestBody CreateCertificationRequest request) {
|
||||
@POST
|
||||
public CertificationDto create(@PathParam Long profileId, @Valid CreateCertificationRequest request) {
|
||||
var cmd = new CreateCertificationUseCase.Command(
|
||||
request.name(),
|
||||
request.issuer(),
|
||||
request.date(),
|
||||
request.credentialId()
|
||||
);
|
||||
|
||||
var created = createUC.handle(profileId, cmd);
|
||||
var body = mapper.toDto(created);
|
||||
return ResponseEntity.created(URI.create("/api/certifications/" + body.id())).body(body);
|
||||
return MAPPER.toDto(created);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import com.pablotj.portfolio.infrastructure.rest.certification.dto.Certification
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
@Mapper
|
||||
public interface CertificationRestMapper {
|
||||
|
||||
@Mapping(target = "id", source = "id.certificationId")
|
||||
|
||||
@@ -5,46 +5,45 @@ import com.pablotj.portfolio.application.education.GetEducationUseCase;
|
||||
import com.pablotj.portfolio.infrastructure.rest.education.dto.CreateEducationRequest;
|
||||
import com.pablotj.portfolio.infrastructure.rest.education.dto.EducationDto;
|
||||
import com.pablotj.portfolio.infrastructure.rest.education.mapper.EducationRestMapper;
|
||||
import io.jooby.annotation.GET;
|
||||
import io.jooby.annotation.POST;
|
||||
import io.jooby.annotation.Path;
|
||||
import io.jooby.annotation.PathParam;
|
||||
import io.jooby.exception.NotFoundException;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/v1/profiles/{profileId}/education")
|
||||
@Path("/v1/profiles/{profileId}/education")
|
||||
public class EducationController {
|
||||
private static final EducationRestMapper MAPPER = Mappers.getMapper(EducationRestMapper.class);
|
||||
|
||||
private final CreateEducationUseCase createUC;
|
||||
private final GetEducationUseCase getUC;
|
||||
private final EducationRestMapper mapper;
|
||||
|
||||
public EducationController(CreateEducationUseCase createUC, GetEducationUseCase getUC, EducationRestMapper mapper) {
|
||||
@Inject
|
||||
public EducationController(CreateEducationUseCase createUC, GetEducationUseCase getUC) {
|
||||
this.createUC = createUC;
|
||||
this.getUC = getUC;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<EducationDto> all(@PathVariable Long profileId) {
|
||||
return getUC.all(profileId).stream().map(mapper::toDto).toList();
|
||||
@GET
|
||||
public List<EducationDto> all(@PathParam Long profileId) {
|
||||
return getUC.all(profileId).stream()
|
||||
.map(MAPPER::toDto)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<EducationDto> byId(@PathVariable Long profileId, @PathVariable Long id) {
|
||||
@GET("/{id}")
|
||||
public EducationDto byId(@PathParam Long profileId, @PathParam Long id) {
|
||||
return getUC.byId(profileId, id)
|
||||
.map(mapper::toDto)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
.map(MAPPER::toDto)
|
||||
.orElseThrow(() -> new NotFoundException("Education record not found"));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<EducationDto> create(@PathVariable Long profileId, @Valid @RequestBody CreateEducationRequest request) {
|
||||
@POST
|
||||
public EducationDto create(@PathParam Long profileId, @Valid CreateEducationRequest request) {
|
||||
var cmd = new CreateEducationUseCase.Command(
|
||||
request.institution(),
|
||||
request.degree(),
|
||||
@@ -52,8 +51,8 @@ public class EducationController {
|
||||
request.grade(),
|
||||
request.description()
|
||||
);
|
||||
|
||||
var created = createUC.handle(profileId, cmd);
|
||||
var body = mapper.toDto(created);
|
||||
return ResponseEntity.created(URI.create("/api/educations/" + body.id())).body(body);
|
||||
return MAPPER.toDto(created);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import com.pablotj.portfolio.infrastructure.rest.education.dto.EducationDto;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
@Mapper
|
||||
public interface EducationRestMapper {
|
||||
|
||||
@Mapping(target = "id", source = "id.educationId")
|
||||
|
||||
@@ -5,46 +5,45 @@ import com.pablotj.portfolio.application.experience.GetExperienceUseCase;
|
||||
import com.pablotj.portfolio.infrastructure.rest.experience.dto.CreateExperienceRequest;
|
||||
import com.pablotj.portfolio.infrastructure.rest.experience.dto.ExperienceDto;
|
||||
import com.pablotj.portfolio.infrastructure.rest.experience.mapper.ExperienceRestMapper;
|
||||
import io.jooby.annotation.GET;
|
||||
import io.jooby.annotation.POST;
|
||||
import io.jooby.annotation.Path;
|
||||
import io.jooby.annotation.PathParam;
|
||||
import io.jooby.exception.NotFoundException;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/v1/profiles/{profileId}/experience")
|
||||
@Path("/v1/profiles/{profileId}/experience")
|
||||
public class ExperienceController {
|
||||
private static final ExperienceRestMapper MAPPER = Mappers.getMapper(ExperienceRestMapper.class);
|
||||
|
||||
private final CreateExperienceUseCase createUC;
|
||||
private final GetExperienceUseCase getUC;
|
||||
private final ExperienceRestMapper mapper;
|
||||
|
||||
public ExperienceController(CreateExperienceUseCase createUC, GetExperienceUseCase getUC, ExperienceRestMapper mapper) {
|
||||
@Inject
|
||||
public ExperienceController(CreateExperienceUseCase createUC, GetExperienceUseCase getUC) {
|
||||
this.createUC = createUC;
|
||||
this.getUC = getUC;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<ExperienceDto> all(@PathVariable Long profileId) {
|
||||
return getUC.all(profileId).stream().map(mapper::toDto).toList();
|
||||
@GET
|
||||
public List<ExperienceDto> all(@PathParam Long profileId) {
|
||||
return getUC.all(profileId).stream()
|
||||
.map(MAPPER::toDto)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<ExperienceDto> byId(@PathVariable Long profileId, @PathVariable Long id) {
|
||||
@GET("/{id}")
|
||||
public ExperienceDto byId(@PathParam Long profileId, @PathParam Long id) {
|
||||
return getUC.byId(profileId, id)
|
||||
.map(mapper::toDto)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
.map(MAPPER::toDto)
|
||||
.orElseThrow(() -> new NotFoundException("Experience not found"));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<ExperienceDto> create(@PathVariable Long profileId, @Valid @RequestBody CreateExperienceRequest request) {
|
||||
@POST
|
||||
public ExperienceDto create(@PathParam Long profileId, @Valid CreateExperienceRequest request) {
|
||||
var cmd = new CreateExperienceUseCase.Command(
|
||||
request.company(),
|
||||
request.position(),
|
||||
@@ -54,8 +53,8 @@ public class ExperienceController {
|
||||
request.technologies(),
|
||||
request.achievements()
|
||||
);
|
||||
|
||||
var created = createUC.handle(profileId, cmd);
|
||||
var body = mapper.toDto(created);
|
||||
return ResponseEntity.created(URI.create("/api/experiences/" + body.id())).body(body);
|
||||
return MAPPER.toDto(created);
|
||||
}
|
||||
}
|
||||
@@ -11,4 +11,5 @@ public record CreateExperienceRequest(
|
||||
String description,
|
||||
List<String> technologies,
|
||||
List<String> achievements
|
||||
) {}
|
||||
) {
|
||||
}
|
||||
@@ -12,4 +12,5 @@ public record ExperienceDto(
|
||||
String description,
|
||||
List<String> technologies,
|
||||
List<String> achievements
|
||||
) {}
|
||||
) {
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import java.util.List;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
@Mapper
|
||||
public interface ExperienceRestMapper {
|
||||
|
||||
@Mapping(target = "id", source = "id.experienceId")
|
||||
|
||||
@@ -5,47 +5,45 @@ import com.pablotj.portfolio.application.profile.GetProfileUseCase;
|
||||
import com.pablotj.portfolio.infrastructure.rest.profile.dto.ProfileCreateRequest;
|
||||
import com.pablotj.portfolio.infrastructure.rest.profile.dto.ProfileDto;
|
||||
import com.pablotj.portfolio.infrastructure.rest.profile.mapper.ProfileRestMapper;
|
||||
import io.jooby.annotation.GET;
|
||||
import io.jooby.annotation.POST;
|
||||
import io.jooby.annotation.Path;
|
||||
import io.jooby.annotation.PathParam;
|
||||
import io.jooby.exception.NotFoundException;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/v1/profiles")
|
||||
@Path("/v1/profiles")
|
||||
public class ProfileController {
|
||||
private static final ProfileRestMapper MAPPER = Mappers.getMapper(ProfileRestMapper.class);
|
||||
|
||||
private final CreateProfileUseCase createUC;
|
||||
private final GetProfileUseCase getUC;
|
||||
private final ProfileRestMapper mapper;
|
||||
|
||||
public ProfileController(CreateProfileUseCase createUC, GetProfileUseCase getUC, ProfileRestMapper mapper) {
|
||||
@Inject
|
||||
public ProfileController(CreateProfileUseCase createUC, GetProfileUseCase getUC) {
|
||||
this.createUC = createUC;
|
||||
this.getUC = getUC;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
|
||||
@GetMapping
|
||||
@GET
|
||||
public List<ProfileDto> all() {
|
||||
return getUC.all().stream().map(mapper::toDto).toList();
|
||||
return getUC.all().stream()
|
||||
.map(MAPPER::toDto)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/{slug}")
|
||||
public ResponseEntity<ProfileDto> byId(@PathVariable String slug) {
|
||||
@GET("/{slug}")
|
||||
public ProfileDto byId(@PathParam String slug) {
|
||||
return getUC.bySlug(slug)
|
||||
.map(mapper::toDto)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
.map(MAPPER::toDto)
|
||||
.orElseThrow(() -> new NotFoundException("Profile not found"));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<ProfileDto> create(@Valid @RequestBody ProfileCreateRequest request) {
|
||||
@POST
|
||||
public ProfileDto create(@Valid ProfileCreateRequest request) {
|
||||
var cmd = new CreateProfileUseCase.Command(
|
||||
request.slug(),
|
||||
request.name(),
|
||||
@@ -56,10 +54,12 @@ public class ProfileController {
|
||||
request.location(),
|
||||
request.avatar(),
|
||||
request.bio(),
|
||||
request.social().stream().map(l -> new CreateProfileUseCase.Link(l.platform(), l.url())).toList()
|
||||
request.social().stream()
|
||||
.map(l -> new CreateProfileUseCase.Link(l.platform(), l.url()))
|
||||
.toList()
|
||||
);
|
||||
|
||||
var created = createUC.handle(cmd);
|
||||
var body = mapper.toDto(created);
|
||||
return ResponseEntity.created(URI.create("/api/homes/" + body.id())).body(body);
|
||||
return MAPPER.toDto(created);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import java.util.List;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
@Mapper
|
||||
public interface ProfileRestMapper {
|
||||
|
||||
@Mapping(target = "id", source = "id.id")
|
||||
|
||||
@@ -5,46 +5,45 @@ import com.pablotj.portfolio.application.project.GetProjectUseCase;
|
||||
import com.pablotj.portfolio.infrastructure.rest.project.dto.CreateProjectRequest;
|
||||
import com.pablotj.portfolio.infrastructure.rest.project.dto.ProjectDto;
|
||||
import com.pablotj.portfolio.infrastructure.rest.project.mapper.ProjectRestMapper;
|
||||
import io.jooby.annotation.GET;
|
||||
import io.jooby.annotation.POST;
|
||||
import io.jooby.annotation.Path;
|
||||
import io.jooby.annotation.PathParam;
|
||||
import io.jooby.exception.NotFoundException;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/v1/profiles/{profileId}/projects")
|
||||
@Path("/v1/profiles/{profileId}/projects")
|
||||
public class ProjectController {
|
||||
private static final ProjectRestMapper MAPPER = Mappers.getMapper(ProjectRestMapper.class);
|
||||
|
||||
private final CreateProjectUseCase createUC;
|
||||
private final GetProjectUseCase getUC;
|
||||
private final ProjectRestMapper mapper;
|
||||
|
||||
public ProjectController(CreateProjectUseCase createUC, GetProjectUseCase getUC, ProjectRestMapper mapper) {
|
||||
@Inject
|
||||
public ProjectController(CreateProjectUseCase createUC, GetProjectUseCase getUC) {
|
||||
this.createUC = createUC;
|
||||
this.getUC = getUC;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<ProjectDto> all(@PathVariable Long profileId) {
|
||||
return getUC.all(profileId).stream().map(mapper::toDto).toList();
|
||||
@GET
|
||||
public List<ProjectDto> all(@PathParam Long profileId) {
|
||||
return getUC.all(profileId).stream()
|
||||
.map(MAPPER::toDto)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<ProjectDto> byId(@PathVariable Long profileId, @PathVariable Long id) {
|
||||
@GET("/{id}")
|
||||
public ProjectDto byId(@PathParam Long profileId, @PathParam Long id) {
|
||||
return getUC.byId(profileId, id)
|
||||
.map(mapper::toDto)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
.map(MAPPER::toDto)
|
||||
.orElseThrow(() -> new NotFoundException("Project not found"));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<ProjectDto> create(@PathVariable Long profileId, @Valid @RequestBody CreateProjectRequest request) {
|
||||
@POST
|
||||
public ProjectDto create(@PathParam Long profileId, @Valid CreateProjectRequest request) {
|
||||
var cmd = new CreateProjectUseCase.Command(
|
||||
request.title(),
|
||||
request.description(),
|
||||
@@ -54,8 +53,8 @@ public class ProjectController {
|
||||
request.demo(),
|
||||
request.repository()
|
||||
);
|
||||
|
||||
var created = createUC.handle(profileId, cmd);
|
||||
var body = mapper.toDto(created);
|
||||
return ResponseEntity.created(URI.create("/api/projects/" + body.id())).body(body);
|
||||
return MAPPER.toDto(created);
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,5 @@ public record CreateProjectRequest(
|
||||
List<String> features,
|
||||
String demo,
|
||||
String repository
|
||||
) {}
|
||||
) {
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import java.util.List;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
@Mapper
|
||||
public interface ProjectRestMapper {
|
||||
|
||||
@Mapping(target = "id", source = "id.projectId")
|
||||
|
||||
@@ -5,53 +5,54 @@ import com.pablotj.portfolio.application.skill.GetSkillUseCase;
|
||||
import com.pablotj.portfolio.infrastructure.rest.skill.dto.CreateSkillGroupRequest;
|
||||
import com.pablotj.portfolio.infrastructure.rest.skill.dto.SkillGroupDto;
|
||||
import com.pablotj.portfolio.infrastructure.rest.skill.mapper.SkillRestMapper;
|
||||
import io.jooby.annotation.GET;
|
||||
import io.jooby.annotation.POST;
|
||||
import io.jooby.annotation.Path;
|
||||
import io.jooby.annotation.PathParam;
|
||||
import io.jooby.exception.NotFoundException;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/v1/profiles/{profileId}/skills")
|
||||
@Path("/v1/profiles/{profileId}/skills")
|
||||
public class SkillController {
|
||||
private static final SkillRestMapper MAPPER = Mappers.getMapper(SkillRestMapper.class);
|
||||
|
||||
private final CreateSkillUseCase createUC;
|
||||
private final GetSkillUseCase getUC;
|
||||
private final SkillRestMapper mapper;
|
||||
|
||||
public SkillController(CreateSkillUseCase createUC, GetSkillUseCase getUC, SkillRestMapper mapper) {
|
||||
@Inject
|
||||
public SkillController(CreateSkillUseCase createUC, GetSkillUseCase getUC) {
|
||||
this.createUC = createUC;
|
||||
this.getUC = getUC;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<SkillGroupDto> all(@PathVariable Long profileId) {
|
||||
return getUC.all(profileId).stream().map(mapper::toDto).toList();
|
||||
@GET
|
||||
public List<SkillGroupDto> all(@PathParam Long profileId) {
|
||||
return getUC.all(profileId).stream()
|
||||
.map(MAPPER::toDto)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<SkillGroupDto> byId(@PathVariable Long profileId, @PathVariable Long id) {
|
||||
@GET("/{id}")
|
||||
public SkillGroupDto byId(@PathParam Long profileId, @PathParam Long id) {
|
||||
return getUC.byId(profileId, id)
|
||||
.map(mapper::toDto)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
.map(MAPPER::toDto)
|
||||
.orElseThrow(() -> new NotFoundException("Skill group not found"));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<SkillGroupDto> create(@PathVariable Long profileId, @Valid @RequestBody CreateSkillGroupRequest request) {
|
||||
@POST
|
||||
public SkillGroupDto create(@PathParam Long profileId, @Valid CreateSkillGroupRequest request) {
|
||||
var cmd = new CreateSkillUseCase.CommandGroup(
|
||||
request.name(),
|
||||
request.icon(),
|
||||
request.skills().stream().map(s -> new CreateSkillUseCase.CommandSkill(s.name(), s.level(), s.years())).toList()
|
||||
request.skills().stream()
|
||||
.map(s -> new CreateSkillUseCase.CommandSkill(s.name(), s.level(), s.years()))
|
||||
.toList()
|
||||
);
|
||||
|
||||
var created = createUC.handle(profileId, cmd);
|
||||
var body = mapper.toDto(created);
|
||||
return ResponseEntity.created(URI.create("/api/skills/" + body.id())).body(body);
|
||||
return MAPPER.toDto(created);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import com.pablotj.portfolio.infrastructure.rest.skill.dto.SkillGroupDto;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
@Mapper
|
||||
public interface SkillRestMapper {
|
||||
|
||||
@Mapping(target = "id", source = "id.skillGroupId")
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
create table personal
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
avatar varchar(255),
|
||||
bio varchar(255),
|
||||
email varchar(255),
|
||||
location varchar(255),
|
||||
name varchar(255),
|
||||
phone varchar(255),
|
||||
subtitle varchar(255),
|
||||
title varchar(255)
|
||||
);
|
||||
|
||||
create table personal_social_link
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
platform varchar(255),
|
||||
url varchar(255),
|
||||
personal_id bigint not null
|
||||
constraint fkfh1pbfvvg3palcr1yip6jffik
|
||||
references personal
|
||||
);
|
||||
|
||||
create table profile
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
avatar varchar(255),
|
||||
bio varchar(4000),
|
||||
email varchar(255),
|
||||
location varchar(255),
|
||||
name varchar(255),
|
||||
phone varchar(255),
|
||||
slug varchar(255),
|
||||
subtitle varchar(255),
|
||||
title varchar(255)
|
||||
);
|
||||
|
||||
create table certification
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
credential_id varchar(255),
|
||||
date varchar(255),
|
||||
issuer varchar(255),
|
||||
name varchar(255),
|
||||
profile_id bigint not null
|
||||
constraint fko6ve4ysx15lc2vcjt84sal1yc
|
||||
references profile
|
||||
);
|
||||
|
||||
create table education
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
degree varchar(255),
|
||||
description varchar,
|
||||
grade varchar(255),
|
||||
institution varchar(255),
|
||||
period varchar(255),
|
||||
profile_id bigint not null
|
||||
constraint fkelocxwwcyf5acj85hgke1c0fl
|
||||
references profile
|
||||
);
|
||||
|
||||
create table experience
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
company varchar(255),
|
||||
description varchar,
|
||||
location varchar(255),
|
||||
period varchar(255),
|
||||
position varchar(255),
|
||||
profile_id bigint not null
|
||||
constraint fkhlkosu9yvtv1ptp01x4tfh9ut
|
||||
references profile
|
||||
);
|
||||
|
||||
create table experience_achievement
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
description varchar,
|
||||
experience_id bigint
|
||||
constraint fk94xrk6stofkung8skwplo29nd
|
||||
references experience
|
||||
);
|
||||
|
||||
create table experience_skill
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
name varchar(255),
|
||||
experience_id bigint
|
||||
constraint fkpr3jdfjjlaubuayoafpwyx2al
|
||||
references experience
|
||||
);
|
||||
|
||||
create table profile_social_link
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
platform varchar(255),
|
||||
url varchar(255),
|
||||
profile_id bigint not null
|
||||
constraint fkqfxt1g0xm211i7qjnlcuqfes9
|
||||
references profile
|
||||
);
|
||||
|
||||
create table project
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
demo varchar(255),
|
||||
description varchar(255),
|
||||
image varchar(255),
|
||||
repository varchar(255),
|
||||
title varchar(255),
|
||||
profile_id bigint not null
|
||||
constraint fk2i9umkiuu36osx3afamsxq39h
|
||||
references profile
|
||||
);
|
||||
|
||||
create table project_feature
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
name varchar(255),
|
||||
project_id bigint
|
||||
constraint fkdifppyvrfito5in15ox4db0up
|
||||
references project
|
||||
);
|
||||
|
||||
create table project_feature_technology
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
name varchar(255),
|
||||
project_id bigint
|
||||
constraint fk15krsajtovetpg5vsaqj3icwf
|
||||
references project
|
||||
);
|
||||
|
||||
create table skill_group
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
icon varchar(255),
|
||||
name varchar(255),
|
||||
profile_id bigint not null
|
||||
constraint fko26hcvag49ctl3ciddsqm6mn1
|
||||
references profile
|
||||
);
|
||||
|
||||
create table skill
|
||||
(
|
||||
id bigint not null
|
||||
primary key,
|
||||
level integer,
|
||||
name varchar(255),
|
||||
years integer,
|
||||
skill_id bigint
|
||||
constraint fki819li5g5cp5qbsyenhr3kmef
|
||||
references skill_group
|
||||
);
|
||||
@@ -1,140 +0,0 @@
|
||||
create table certification
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
credential_id varchar(255),
|
||||
date varchar(255),
|
||||
issuer varchar(255),
|
||||
name varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table education
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
degree varchar(255),
|
||||
description text,
|
||||
grade varchar(255),
|
||||
institution varchar(255),
|
||||
period varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table experience
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
company varchar(255),
|
||||
description text,
|
||||
location varchar(255),
|
||||
period varchar(255),
|
||||
position varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table experience_achievement
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
description text,
|
||||
experience_id bigint,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table experience_skill
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
name varchar(255),
|
||||
experience_id bigint,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table personal
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
avatar varchar(255),
|
||||
bio varchar(255),
|
||||
email varchar(255),
|
||||
location varchar(255),
|
||||
name varchar(255),
|
||||
phone varchar(255),
|
||||
subtitle varchar(255),
|
||||
title varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table personal_social_link
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
platform varchar(255),
|
||||
url varchar(255),
|
||||
personal_id bigint not null,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table project
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
demo varchar(255),
|
||||
description varchar(255),
|
||||
image varchar(255),
|
||||
repository varchar(255),
|
||||
title varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table project_feature
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
name varchar(255),
|
||||
project_id bigint,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table project_feature_technology
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
name varchar(255),
|
||||
project_id bigint,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table skill
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
level integer,
|
||||
name varchar(255),
|
||||
years integer,
|
||||
skill_id bigint,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table skill_group
|
||||
(
|
||||
id bigint generated by default as identity,
|
||||
icon varchar(255),
|
||||
name varchar(255),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
alter table if exists experience_achievement
|
||||
add constraint FK94xrk6stofkung8skwplo29nd
|
||||
foreign key (experience_id)
|
||||
references experience;
|
||||
|
||||
alter table if exists experience_skill
|
||||
add constraint FKpr3jdfjjlaubuayoafpwyx2al
|
||||
foreign key (experience_id)
|
||||
references experience;
|
||||
|
||||
alter table if exists project_feature
|
||||
add constraint FKdifppyvrfito5in15ox4db0up
|
||||
foreign key (project_id)
|
||||
references project;
|
||||
|
||||
alter table if exists project_feature_technology
|
||||
add constraint FK15krsajtovetpg5vsaqj3icwf
|
||||
foreign key (project_id)
|
||||
references project;
|
||||
|
||||
alter table if exists skill
|
||||
add constraint FKi819li5g5cp5qbsyenhr3kmef
|
||||
foreign key (skill_id)
|
||||
references skill_group;
|
||||
3
infrastructure/src/main/resources/ebean.mf
Normal file
3
infrastructure/src/main/resources/ebean.mf
Normal file
@@ -0,0 +1,3 @@
|
||||
entity-packages: com.pablotj.portfolio.infrastructure.persistence
|
||||
transactional-packages: com.pablotj.portfolio
|
||||
querybean-packages: com.pablotj.portfolio
|
||||
35
pom.xml
35
pom.xml
@@ -16,30 +16,35 @@
|
||||
<module>bootstrap</module>
|
||||
</modules>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.3.2</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<jooby.version>3.2.0</jooby.version>
|
||||
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||
<springdoc.version>2.6.0</springdoc.version>
|
||||
<lombok.version>1.18.32</lombok.version>
|
||||
<jackson.version>2.17.2</jackson.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-bom</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-apt</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
@@ -50,14 +55,20 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>io.jooby</groupId>
|
||||
<artifactId>jooby-apt</artifactId>
|
||||
<version>${jooby.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.32</version>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
|
||||
Reference in New Issue
Block a user