From 626e78b97fd4780f43a3d53d7959877722d4f068 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 4 Jul 2025 10:40:58 +0000 Subject: [PATCH 1/3] Update GitHub Actions to use upload-artifact@v4 --- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6c32dd..7e190c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: run: ./gradlew test - name: Upload build artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: plugin-artifact path: build/distributions/*.zip @@ -47,7 +47,7 @@ jobs: if: success() || failure() # Générer les rapports même en cas d'échec des tests - name: Archive test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-results path: build/reports/tests/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 35ec0ac..3b2a4c5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,7 @@ jobs: run: ./gradlew runPluginVerifier - name: Upload plugin artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: api-generator-plugin path: build/distributions/*.zip From 7a970bb20524951a7662b9eee83d1511106adea0 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 4 Jul 2025 12:24:42 +0000 Subject: [PATCH 2/3] Refactor IntelliJ version handling in build.gradle and release.yml for clarity and flexibility --- .github/workflows/release.yml | 6 ++---- build.gradle | 11 ++++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 68d16fd..2b61c46 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,8 @@ on: jobs: build: runs-on: ubuntu-latest + env: + INTELLIJ_VERSION: '2023.1' steps: - name: Checkout repository @@ -38,13 +40,9 @@ jobs: - name: Build plugin run: ./gradlew buildPlugin - env: - INTELLIJ_VERSION: '2023.1' - name: Verify plugin run: ./gradlew runPluginVerifier - env: - INTELLIJ_VERSION: '2023.1' - name: Upload plugin artifact uses: actions/upload-artifact@v4 diff --git a/build.gradle b/build.gradle index 4648a28..8af9df1 100644 --- a/build.gradle +++ b/build.gradle @@ -19,10 +19,15 @@ dependencies { testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' } +// Définition explicite de la version d'IntelliJ pour éviter toute confusion +def intellijVersion = System.getenv("INTELLIJ_VERSION") ?: "2023.1" + intellij { - version = '2023.1' - type = 'IC' // IntelliJ IDEA Community Edition - plugins = ['java'] + version.set(intellijVersion) // Utilisation de la méthode set() pour éviter les conflits + type.set('IC') // IntelliJ IDEA Community Edition + plugins.set(['java']) + updateSinceUntilBuild.set(true) + downloadSources.set(true) } patchPluginXml { From b60eec00c32134a06fdf61f68ea7e338e571332f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 4 Jul 2025 17:02:40 +0000 Subject: [PATCH 3/3] Update IntelliJ version to 2024.2, increment plugin version to 1.0.1, and enhance code generation with DTO and Mapper support --- .github/workflows/release.yml | 2 +- build.gradle | 40 ++++- .../action/GenerateApiAction.java | 12 +- .../service/impl/ControllerGenerator.java | 41 +++-- .../impl/DependencyValidationServiceImpl.java | 4 +- .../service/impl/DtoGenerator.java | 7 + .../service/impl/ExistingFileServiceImpl.java | 19 +-- .../service/impl/RepositoryGenerator.java | 59 +------ .../service/impl/ServiceGenerator.java | 146 ++++++++++++++++-- .../apigenerator/ui/CodePreviewComponent.java | 10 +- src/main/resources/META-INF/plugin.xml | 28 +++- 11 files changed, 243 insertions(+), 125 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2b61c46..440369f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: build: runs-on: ubuntu-latest env: - INTELLIJ_VERSION: '2023.1' + INTELLIJ_VERSION: '2024.2' steps: - name: Checkout repository diff --git a/build.gradle b/build.gradle index 8af9df1..0a48355 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,8 @@ plugins { id 'org.jetbrains.intellij' version '1.17.0' } -group 'com.github.tky0065' -version '1.0.0' +group = 'com.github.tky0065' +version = '1.0.1' repositories { mavenCentral() @@ -20,24 +20,50 @@ dependencies { } // Définition explicite de la version d'IntelliJ pour éviter toute confusion -def intellijVersion = System.getenv("INTELLIJ_VERSION") ?: "2023.1" +def intellijVersion = System.getenv("INTELLIJ_VERSION") ?: "2024.2" +def intellijType = System.getenv("INTELLIJ_TYPE") ?: "IC" // IC pour Community, IU pour Ultimate intellij { version.set(intellijVersion) // Utilisation de la méthode set() pour éviter les conflits - type.set('IC') // IntelliJ IDEA Community Edition + type.set(intellijType) // Configurable via variable d'environnement plugins.set(['java']) updateSinceUntilBuild.set(true) downloadSources.set(true) } +// Configuration pour supprimer les avertissements lors du build +buildSearchableOptions { + enabled = false // Désactive la génération des options searchable qui produit des avertissements +} + +runIde { + // Supprime les avertissements concernant JCEF en mode headless + systemProperty("ide.browser.jcef.headless.enabled", "true") + + // Définit explicitement la version de JDK pour éviter les problèmes avec Java 25 + jvmArgs("-Djdk.module.illegalAccess.silent=true") + + // Ignorer les avertissements liés à la compatibilité Gradle + systemProperty("gradle.skip.compatibility.check", "true") + + // Éviter les mises à jour automatiques qui peuvent causer des avertissements + systemProperty("ide.plugins.snapshot.on.unload.disable", "true") +} + +// Configuration pour la publication du plugin +publishPlugin { + token.set(System.getenv("PUBLISH_TOKEN") ?: "") + channels.set(System.getenv("PUBLISH_CHANNEL") ? [System.getenv("PUBLISH_CHANNEL")] : ["default"]) +} + patchPluginXml { changeNotes.set("""
    -
  • 1.0.0 - Version initiale du générateur d'API
  • +
  • 1.0.1 - Version initiale du générateur d'API
""") - sinceBuild.set("231") // Compatible avec 2023.1+ - untilBuild.set("233.*") // Compatible jusqu'à 2023.3.x + sinceBuild.set("242") // Compatible depuis IntelliJ 2024.2 + untilBuild.set("252.*") // Compatible jusqu'à 2025.2.x inclus } test { diff --git a/src/main/java/com/github/tky0065/apigenerator/action/GenerateApiAction.java b/src/main/java/com/github/tky0065/apigenerator/action/GenerateApiAction.java index ad03ae6..b75a466 100644 --- a/src/main/java/com/github/tky0065/apigenerator/action/GenerateApiAction.java +++ b/src/main/java/com/github/tky0065/apigenerator/action/GenerateApiAction.java @@ -26,6 +26,8 @@ import com.intellij.openapi.ui.Messages; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.openapi.vfs.VirtualFile; + import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -54,6 +56,7 @@ public GenerateApiAction() { this.existingFileService = new ExistingFileServiceImpl(loggingService); } + @Override public void update(@NotNull AnActionEvent e) { // Active ou désactive l'action en fonction du contexte @@ -469,8 +472,13 @@ private FileAction showFileExistsDialog(Project project, String packageName, Str * Crée récursivement les répertoires nécessaires pour un package. */ private PsiDirectory createPackageDirectories(Project project, String packageName) { - PsiDirectory baseDir = PsiManager.getInstance(project).findDirectory( - project.getBaseDir()); + // Utilisation de ProjectUtil.guessProjectDir au lieu de project.getBaseDir() + VirtualFile projectDir = com.intellij.openapi.project.ProjectUtil.guessProjectDir(project); + if (projectDir == null) { + throw new IllegalStateException("Impossible de trouver le répertoire de base du projet"); + } + + PsiDirectory baseDir = PsiManager.getInstance(project).findDirectory(projectDir); if (baseDir == null) { throw new IllegalStateException("Impossible de trouver le répertoire de base du projet"); } diff --git a/src/main/java/com/github/tky0065/apigenerator/service/impl/ControllerGenerator.java b/src/main/java/com/github/tky0065/apigenerator/service/impl/ControllerGenerator.java index 31618e2..7b8b939 100644 --- a/src/main/java/com/github/tky0065/apigenerator/service/impl/ControllerGenerator.java +++ b/src/main/java/com/github/tky0065/apigenerator/service/impl/ControllerGenerator.java @@ -200,9 +200,7 @@ private void addGetAllMethod(TypeSpec.Builder classBuilder, TypeName entityType, .addModifiers(Modifier.PUBLIC) .addAnnotation(getMappingAnnotation) .returns(returnType) - .addStatement("List<$T> entities = service.findAll()", entityType) - .addStatement("// Ici, vous devez convertir les entités en DTOs si nécessaire") - .addStatement("return $T.ok(entities)", ClassName.get("org.springframework.http", "ResponseEntity")) + .addStatement("return $T.ok(service.findAll())", ClassName.get("org.springframework.http", "ResponseEntity")) .build(); classBuilder.addMethod(getAllMethod); @@ -222,7 +220,9 @@ private void addGetByIdMethod(TypeSpec.Builder classBuilder, TypeName entityType .build(); ClassName pathVariable = ClassName.get("org.springframework.web.bind.annotation", "PathVariable"); + ClassName responseEntityClass = ClassName.get("org.springframework.http", "ResponseEntity"); + // Générer le code avec une seule instruction pour éviter les problèmes de formatage MethodSpec getByIdMethod = MethodSpec.methodBuilder("getById") .addModifiers(Modifier.PUBLIC) .addAnnotation(getMappingAnnotation) @@ -230,9 +230,9 @@ private void addGetByIdMethod(TypeSpec.Builder classBuilder, TypeName entityType .addAnnotation(pathVariable) .build()) .returns(returnType) - .addStatement("return service.findById(id)") - .addStatement(" .map(entity -> $T.ok(entity))", ClassName.get("org.springframework.http", "ResponseEntity")) - .addStatement(" .orElseGet(() -> $T.notFound().build())", ClassName.get("org.springframework.http", "ResponseEntity")) + .addCode("return service.findById(id)\n") + .addCode(" .map($T::ok)\n", responseEntityClass) + .addCode(" .orElseGet(() -> $T.notFound().build());\n", responseEntityClass) .build(); classBuilder.addMethod(getByIdMethod); @@ -256,11 +256,9 @@ private void addCreateMethod(TypeSpec.Builder classBuilder, TypeName entityType, .addAnnotation(requestBody) .build()) .returns(returnType) - .addStatement("// Ici, vous devez convertir le DTO en entité si nécessaire") - .addStatement("$T savedEntity = service.save(($T) dto)", entityType, entityType) - .addStatement("// Puis reconvertir en DTO pour la réponse") - .addStatement("return $T.created(null).body(($T) savedEntity)", - ClassName.get("org.springframework.http", "ResponseEntity"), dtoType) + .addStatement("$T savedDto = service.save(dto)", dtoType) + .addStatement("return $T.created(null).body(savedDto)", + ClassName.get("org.springframework.http", "ResponseEntity")) .build(); classBuilder.addMethod(createMethod); @@ -281,7 +279,9 @@ private void addUpdateMethod(TypeSpec.Builder classBuilder, TypeName entityType, ClassName pathVariable = ClassName.get("org.springframework.web.bind.annotation", "PathVariable"); ClassName requestBody = ClassName.get("org.springframework.web.bind.annotation", "RequestBody"); + ClassName responseEntityClass = ClassName.get("org.springframework.http", "ResponseEntity"); + // Générer le code avec une structure plus explicite pour éviter les problèmes de formatage MethodSpec updateMethod = MethodSpec.methodBuilder("update") .addModifiers(Modifier.PUBLIC) .addAnnotation(putMappingAnnotation) @@ -292,15 +292,13 @@ private void addUpdateMethod(TypeSpec.Builder classBuilder, TypeName entityType, .addAnnotation(requestBody) .build()) .returns(returnType) - .addStatement("return service.findById(id)") - .addStatement(" .map(existingEntity -> {") - .addStatement(" // Ici, mettre à jour l'entité existante avec les valeurs du DTO") - .addStatement(" $T updatedEntity = service.save(existingEntity)", entityType) - .addStatement(" return $T.ok(($T) updatedEntity)", - ClassName.get("org.springframework.http", "ResponseEntity"), dtoType) - .addStatement(" })") - .addStatement(" .orElseGet(() -> $T.notFound().build())", - ClassName.get("org.springframework.http", "ResponseEntity")) + .addCode("return service.findById(id)\n") + .addCode(" .map(existingDto -> {\n") + .addCode(" // Ici, vous pouvez copier les champs modifiables de dto vers existingDto si besoin\n") + .addCode(" $T updatedDto = service.save(dto);\n", dtoType) + .addCode(" return $T.ok(updatedDto);\n", responseEntityClass) + .addCode(" })\n") + .addCode(" .orElseGet(() -> $T.notFound().build());\n", responseEntityClass) .build(); classBuilder.addMethod(updateMethod); @@ -329,8 +327,7 @@ private void addDeleteMethod(TypeSpec.Builder classBuilder, TypeName idType) { .build()) .returns(returnType) .addStatement("service.deleteById(id)") - .addStatement("return $T.noContent().build()", - ClassName.get("org.springframework.http", "ResponseEntity")) + .addStatement("return $T.noContent().build()", ClassName.get("org.springframework.http", "ResponseEntity")) .build(); classBuilder.addMethod(deleteMethod); diff --git a/src/main/java/com/github/tky0065/apigenerator/service/impl/DependencyValidationServiceImpl.java b/src/main/java/com/github/tky0065/apigenerator/service/impl/DependencyValidationServiceImpl.java index 0f56ec6..f54ba59 100644 --- a/src/main/java/com/github/tky0065/apigenerator/service/impl/DependencyValidationServiceImpl.java +++ b/src/main/java/com/github/tky0065/apigenerator/service/impl/DependencyValidationServiceImpl.java @@ -38,7 +38,7 @@ public class DependencyValidationServiceImpl implements DependencyValidationServ "JPA Entity", "\n jakarta.persistence\n jakarta.persistence-api\n 3.1.0\n", "Spring Data JPA", "\n org.springframework.boot\n spring-boot-starter-data-jpa\n", "Spring Web", "\n org.springframework.boot\n spring-boot-starter-web\n", - "MapStruct", "\n org.mapstruct\n mapstruct\n 1.5.3.Final\n\n\n org.mapstruct\n mapstruct-processor\n 1.5.3.Final\n provided\n", + "MapStruct", "\n org.mapstruct\n mapstruct\n 1.6.3.Final\n\n\n org.mapstruct\n mapstruct-processor\n 1.6.3.Final\n provided\n", "Lombok", "\n org.projectlombok\n lombok\n 1.18.28\n provided\n" ); @@ -47,7 +47,7 @@ public class DependencyValidationServiceImpl implements DependencyValidationServ "JPA Entity", "implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'", "Spring Data JPA", "implementation 'org.springframework.boot:spring-boot-starter-data-jpa'", "Spring Web", "implementation 'org.springframework.boot:spring-boot-starter-web'", - "MapStruct", "implementation 'org.mapstruct:mapstruct:1.5.3.Final'\nannotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final'", + "MapStruct", "implementation 'org.mapstruct:mapstruct:1.6.3'\nannotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3'", "Lombok", "compileOnly 'org.projectlombok:lombok:1.18.28'\nannotationProcessor 'org.projectlombok:lombok:1.18.28'" ); diff --git a/src/main/java/com/github/tky0065/apigenerator/service/impl/DtoGenerator.java b/src/main/java/com/github/tky0065/apigenerator/service/impl/DtoGenerator.java index d200c4f..3050eeb 100644 --- a/src/main/java/com/github/tky0065/apigenerator/service/impl/DtoGenerator.java +++ b/src/main/java/com/github/tky0065/apigenerator/service/impl/DtoGenerator.java @@ -96,6 +96,13 @@ public String getGeneratedPackageName(EntityModel entityModel, ApiGeneratorConfi } private void addField(TypeSpec.Builder classBuilder, String name, TypeName typeName, boolean useLombok) { + // Vérifier si un champ avec ce nom existe déjà pour éviter les doublons + for (FieldSpec field : classBuilder.fieldSpecs) { + if (field.name.equals(name)) { + return; // Ne pas ajouter de doublons + } + } + FieldSpec.Builder fieldBuilder = FieldSpec.builder(typeName, name, Modifier.PRIVATE); classBuilder.addField(fieldBuilder.build()); } diff --git a/src/main/java/com/github/tky0065/apigenerator/service/impl/ExistingFileServiceImpl.java b/src/main/java/com/github/tky0065/apigenerator/service/impl/ExistingFileServiceImpl.java index c165f2b..d397a71 100644 --- a/src/main/java/com/github/tky0065/apigenerator/service/impl/ExistingFileServiceImpl.java +++ b/src/main/java/com/github/tky0065/apigenerator/service/impl/ExistingFileServiceImpl.java @@ -3,7 +3,6 @@ import com.github.tky0065.apigenerator.service.ExistingFileService; import com.github.tky0065.apigenerator.service.LoggingService; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; @@ -11,9 +10,6 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -147,20 +143,24 @@ private String getFileKey(String packageName, String className) { /** * Trouve un fichier Java dans le projet. */ + private PsiFile findFile(Project project, String packageName, String className) { - // Convertir le package en chemin de répertoire String packagePath = packageName.replace('.', '/'); - - // Rechercher dans les répertoires sources du projet PsiManager psiManager = PsiManager.getInstance(project); - VirtualFile baseDir = project.getBaseDir(); + // Récupérer le répertoire racine du projet + VirtualFile projectDir = com.intellij.openapi.project.ProjectUtil.guessProjectDir(project); + if (projectDir == null) { + return null; + } + + PsiDirectory baseDir = psiManager.findDirectory(projectDir); if (baseDir == null) { return null; } // Chercher dans src/main/java - VirtualFile srcDir = baseDir.findFileByRelativePath("src/main/java"); + VirtualFile srcDir = projectDir.findFileByRelativePath("src/main/java"); if (srcDir != null) { VirtualFile packageDir = srcDir.findFileByRelativePath(packagePath); if (packageDir != null) { @@ -174,6 +174,7 @@ private PsiFile findFile(Project project, String packageName, String className) return null; } + /** * Ajoute une signature à un fichier généré. * diff --git a/src/main/java/com/github/tky0065/apigenerator/service/impl/RepositoryGenerator.java b/src/main/java/com/github/tky0065/apigenerator/service/impl/RepositoryGenerator.java index ea20570..a279d97 100644 --- a/src/main/java/com/github/tky0065/apigenerator/service/impl/RepositoryGenerator.java +++ b/src/main/java/com/github/tky0065/apigenerator/service/impl/RepositoryGenerator.java @@ -33,8 +33,8 @@ public String generateCode(Project project, EntityModel entityModel, ApiGenerato ClassName repositoryAnnotation = ClassName.get("org.springframework.stereotype", "Repository"); interfaceBuilder.addAnnotation(repositoryAnnotation); - // Ajouter des méthodes de recherche personnalisées basées sur les champs de l'entité - addCustomQueryMethods(interfaceBuilder, entityModel); + // Ne pas générer de méthodes personnalisées pour éviter les doublons + // Spring Data JPA génère déjà automatiquement les méthodes de base // Créer le fichier Java JavaFile javaFile = JavaFile.builder(getGeneratedPackageName(entityModel, config), interfaceBuilder.build()) @@ -77,61 +77,6 @@ private TypeName getIdTypeName(EntityModel entityModel) { return ClassName.get("java.lang", "Long"); } - /** - * Ajoute des méthodes de recherche personnalisées basées sur les champs de l'entité. - */ - private void addCustomQueryMethods(TypeSpec.Builder interfaceBuilder, EntityModel entityModel) { - // Ajouter findBy... pour les champs importants (non-collection, non-transient, etc.) - for (EntityModel.EntityField field : entityModel.getFields()) { - // Ignorer les champs qui ne sont pas adaptés pour les requêtes - if (field.isTransient() || field.isCollection()) { - continue; - } - - // Si c'est un champ de type String, ajouter une méthode findByFieldContainingIgnoreCase - if ("String".equals(field.getType())) { - String methodName = "findBy" + capitalizeFirstLetter(field.getName()) + "ContainingIgnoreCase"; - - TypeName returnType = ParameterizedTypeName.get( - ClassName.get("java.util", "List"), - ClassName.get(entityModel.getPackageName(), entityModel.getClassName()) - ); - - MethodSpec method = MethodSpec.methodBuilder(methodName) - .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) - .returns(returnType) - .addParameter(String.class, field.getName()) - .build(); - - interfaceBuilder.addMethod(method); - } - // Pour les autres types, ajouter une méthode findByField - else { - String methodName = "findBy" + capitalizeFirstLetter(field.getName()); - - TypeName returnType = ParameterizedTypeName.get( - ClassName.get("java.util", "List"), - ClassName.get(entityModel.getPackageName(), entityModel.getClassName()) - ); - - MethodSpec method = MethodSpec.methodBuilder(methodName) - .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) - .returns(returnType) - .addParameter(determineTypeName(field.getType()), field.getName()) - .build(); - - interfaceBuilder.addMethod(method); - } - } - } - - private String capitalizeFirstLetter(String input) { - if (input == null || input.isEmpty()) { - return input; - } - return input.substring(0, 1).toUpperCase() + input.substring(1); - } - private TypeName determineTypeName(String type) { switch (type) { case "int": return TypeName.INT; diff --git a/src/main/java/com/github/tky0065/apigenerator/service/impl/ServiceGenerator.java b/src/main/java/com/github/tky0065/apigenerator/service/impl/ServiceGenerator.java index 970a3b4..c6de988 100644 --- a/src/main/java/com/github/tky0065/apigenerator/service/impl/ServiceGenerator.java +++ b/src/main/java/com/github/tky0065/apigenerator/service/impl/ServiceGenerator.java @@ -28,6 +28,22 @@ public String generateCode(Project project, EntityModel entityModel, ApiGenerato String repositoryClassName = entityModel.getClassName() + config.getRepositorySuffix(); ClassName repositoryTypeName = ClassName.get(repositoryPackageName, repositoryClassName); + // Obtenir le nom complet de la classe DTO si elle est générée + TypeName dtoTypeName = null; + if (config.isGenerateDto()) { + String dtoPackageName = getDtoPackageName(entityModel, config); + String dtoClassName = entityModel.getClassName() + config.getDtoSuffix(); + dtoTypeName = ClassName.get(dtoPackageName, dtoClassName); + } + + // Obtenir le nom complet de la classe Mapper si elle est générée + ClassName mapperTypeName = null; + if (config.isGenerateDto() && config.isGenerateMapper()) { + String mapperPackageName = getMapperPackageName(entityModel, config); + String mapperClassName = entityModel.getClassName() + config.getMapperSuffix(); + mapperTypeName = ClassName.get(mapperPackageName, mapperClassName); + } + // Créer la classe Service TypeSpec.Builder classBuilder = TypeSpec.classBuilder(getGeneratedClassName(entityModel, config)) .addModifiers(Modifier.PUBLIC); @@ -41,19 +57,45 @@ public String generateCode(Project project, EntityModel entityModel, ApiGenerato .build(); classBuilder.addField(repositoryField); - // Ajouter un constructeur pour l'injection de dépendances - MethodSpec constructor = MethodSpec.constructorBuilder() - .addModifiers(Modifier.PUBLIC) - .addParameter(repositoryTypeName, "repository") - .addStatement("this.$N = $N", "repository", "repository") - .build(); - classBuilder.addMethod(constructor); + // Ajouter l'injection du Mapper si nécessaire + if (mapperTypeName != null) { + FieldSpec mapperField = FieldSpec.builder(mapperTypeName, "mapper", Modifier.PRIVATE, Modifier.FINAL) + .build(); + classBuilder.addField(mapperField); + + // Modifier le constructeur pour injecter le mapper aussi + MethodSpec constructor = MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter(repositoryTypeName, "repository") + .addParameter(mapperTypeName, "mapper") + .addStatement("this.$N = $N", "repository", "repository") + .addStatement("this.$N = $N", "mapper", "mapper") + .build(); + classBuilder.addMethod(constructor); + } else { + // Constructeur sans mapper + MethodSpec constructor = MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter(repositoryTypeName, "repository") + .addStatement("this.$N = $N", "repository", "repository") + .build(); + classBuilder.addMethod(constructor); + } - // Ajouter les méthodes CRUD - addFindAllMethod(classBuilder, entityClassName); - addFindByIdMethod(classBuilder, entityClassName, idType); - addSaveMethod(classBuilder, entityClassName); - addDeleteMethod(classBuilder, entityClassName, idType); + // Ajouter les méthodes CRUD en fonction de la configuration + if (config.isGenerateDto() && config.isGenerateMapper()) { + // Utiliser les mappers pour convertir entre entités et DTOs + addFindAllMethodWithMapper(classBuilder, entityClassName, dtoTypeName); + addFindByIdMethodWithMapper(classBuilder, entityClassName, dtoTypeName, idType); + addSaveMethodWithMapper(classBuilder, entityClassName, dtoTypeName); + addDeleteMethod(classBuilder, entityClassName, idType); + } else { + // Méthodes sans conversion DTO + addFindAllMethod(classBuilder, entityClassName); + addFindByIdMethod(classBuilder, entityClassName, idType); + addSaveMethod(classBuilder, entityClassName); + addDeleteMethod(classBuilder, entityClassName, idType); + } // Créer le fichier Java JavaFile javaFile = JavaFile.builder(getGeneratedPackageName(entityModel, config), classBuilder.build()) @@ -98,6 +140,38 @@ private String getRepositoryPackageName(EntityModel entityModel, ApiGeneratorCon } } + /** + * Obtient le nom du package pour les DTOs. + */ + private String getDtoPackageName(EntityModel entityModel, ApiGeneratorConfig config) { + String basePackage = config.getBasePackage(); + if (basePackage == null || basePackage.isEmpty()) { + basePackage = entityModel.getPackageName(); + } + + if (basePackage.endsWith(".")) { + return basePackage + config.getDtoPackage(); + } else { + return basePackage + "." + config.getDtoPackage(); + } + } + + /** + * Obtient le nom du package pour les Mappers. + */ + private String getMapperPackageName(EntityModel entityModel, ApiGeneratorConfig config) { + String basePackage = config.getBasePackage(); + if (basePackage == null || basePackage.isEmpty()) { + basePackage = entityModel.getPackageName(); + } + + if (basePackage.endsWith(".")) { + return basePackage + config.getMapperPackage(); + } else { + return basePackage + "." + config.getMapperPackage(); + } + } + /** * Détermine le type de la clé primaire de l'entité. */ @@ -214,4 +288,52 @@ private void addDeleteMethod(TypeSpec.Builder classBuilder, TypeName entityType, classBuilder.addMethod(delete); } + + /** + * Ajoute la méthode pour récupérer toutes les entités en utilisant les mappers. + */ + private void addFindAllMethodWithMapper(TypeSpec.Builder classBuilder, TypeName entityType, TypeName dtoType) { + TypeName returnType = ParameterizedTypeName.get( + ClassName.get(List.class), dtoType); + + MethodSpec findAll = MethodSpec.methodBuilder("findAll") + .addModifiers(Modifier.PUBLIC) + .returns(returnType) + .addStatement("return repository.findAll().stream().map(mapper::toDto).collect(java.util.stream.Collectors.toList())") + .build(); + + classBuilder.addMethod(findAll); + } + + /** + * Ajoute la méthode pour trouver une entité par son ID en utilisant les mappers. + */ + private void addFindByIdMethodWithMapper(TypeSpec.Builder classBuilder, TypeName entityType, TypeName dtoType, TypeName idType) { + TypeName returnType = ParameterizedTypeName.get( + ClassName.get(Optional.class), dtoType); + + MethodSpec findById = MethodSpec.methodBuilder("findById") + .addModifiers(Modifier.PUBLIC) + .addParameter(idType, "id") + .returns(returnType) + .addStatement("return repository.findById(id).map(mapper::toDto)") + .build(); + + classBuilder.addMethod(findById); + } + + /** + * Ajoute la méthode pour sauvegarder une entité en utilisant les mappers. + */ + private void addSaveMethodWithMapper(TypeSpec.Builder classBuilder, TypeName entityType, TypeName dtoType) { + MethodSpec save = MethodSpec.methodBuilder("save") + .addModifiers(Modifier.PUBLIC) + .addParameter(dtoType, "dto") + .returns(dtoType) + .addStatement("$T entity = mapper.toEntity(dto)", entityType) + .addStatement("return mapper.toDto(repository.save(entity))", entityType) + .build(); + + classBuilder.addMethod(save); + } } diff --git a/src/main/java/com/github/tky0065/apigenerator/ui/CodePreviewComponent.java b/src/main/java/com/github/tky0065/apigenerator/ui/CodePreviewComponent.java index 3182ed5..40dcd81 100644 --- a/src/main/java/com/github/tky0065/apigenerator/ui/CodePreviewComponent.java +++ b/src/main/java/com/github/tky0065/apigenerator/ui/CodePreviewComponent.java @@ -155,7 +155,7 @@ private void createControllerPreview() { private void updateDtoPreview(ApiGeneratorConfig updatedConfig) { CodeGenerator dtoGenerator = new DtoGenerator(); String code = dtoGenerator.generateCode(project, entityModel, updatedConfig); - EditorFactory.getInstance().releaseEditor(dtoEditor); + // Suppression de l'appel redondant à releaseEditor ici, car updateEditor le fait déjà dtoEditor = updateEditor(dtoEditor, code); tabbedPane.setComponentAt(tabbedPane.indexOfTab("DTO"), createEditorPanel(dtoEditor)); } @@ -166,7 +166,7 @@ private void updateDtoPreview(ApiGeneratorConfig updatedConfig) { private void updateMapperPreview(ApiGeneratorConfig updatedConfig) { CodeGenerator mapperGenerator = new MapperGenerator(); String code = mapperGenerator.generateCode(project, entityModel, updatedConfig); - EditorFactory.getInstance().releaseEditor(mapperEditor); + // Suppression de l'appel redondant à releaseEditor mapperEditor = updateEditor(mapperEditor, code); tabbedPane.setComponentAt(tabbedPane.indexOfTab("Mapper"), createEditorPanel(mapperEditor)); } @@ -177,7 +177,7 @@ private void updateMapperPreview(ApiGeneratorConfig updatedConfig) { private void updateRepositoryPreview(ApiGeneratorConfig updatedConfig) { CodeGenerator repositoryGenerator = new RepositoryGenerator(); String code = repositoryGenerator.generateCode(project, entityModel, updatedConfig); - EditorFactory.getInstance().releaseEditor(repositoryEditor); + // Suppression de l'appel redondant à releaseEditor repositoryEditor = updateEditor(repositoryEditor, code); tabbedPane.setComponentAt(tabbedPane.indexOfTab("Repository"), createEditorPanel(repositoryEditor)); } @@ -188,7 +188,7 @@ private void updateRepositoryPreview(ApiGeneratorConfig updatedConfig) { private void updateServicePreview(ApiGeneratorConfig updatedConfig) { CodeGenerator serviceGenerator = new ServiceGenerator(); String code = serviceGenerator.generateCode(project, entityModel, updatedConfig); - EditorFactory.getInstance().releaseEditor(serviceEditor); + // Suppression de l'appel redondant à releaseEditor serviceEditor = updateEditor(serviceEditor, code); tabbedPane.setComponentAt(tabbedPane.indexOfTab("Service"), createEditorPanel(serviceEditor)); } @@ -199,7 +199,7 @@ private void updateServicePreview(ApiGeneratorConfig updatedConfig) { private void updateControllerPreview(ApiGeneratorConfig updatedConfig) { CodeGenerator controllerGenerator = new ControllerGenerator(); String code = controllerGenerator.generateCode(project, entityModel, updatedConfig); - EditorFactory.getInstance().releaseEditor(controllerEditor); + // Suppression de l'appel redondant à releaseEditor controllerEditor = updateEditor(controllerEditor, code); tabbedPane.setComponentAt(tabbedPane.indexOfTab("Controller"), createEditorPanel(controllerEditor)); } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 8fbec1c..c8a41da 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -1,17 +1,23 @@ com.github.tky0065.apigenerator - API Generator + Spring CRUD API Generator tky0065 + This plugin automatically generates a complete CRUD API from a JPA entity. It simplifies the development process by creating all necessary components for your Spring Boot application with a few clicks. +

+ Features include:
    -
  • Génération de DTO avec Lombok
  • -
  • Repository JPA
  • -
  • Service avec méthodes CRUD
  • -
  • Controller REST
  • -
  • Mappeurs entre entités et DTOs
  • +
  • Generation of DTOs with Lombok annotations
  • +
  • JPA Repository interfaces
  • +
  • Service layer with complete CRUD methods
  • +
  • RESTful Controllers with appropriate endpoints
  • +
  • Entity-to-DTO mappers
  • +
  • Custom exception handlers
  • +
  • Basic validation setup
+
+ Save hours of boilerplate coding and focus on your business logic instead. ]]>
com.intellij.modules.platform @@ -22,6 +28,12 @@ - + + + +