Skip to content

Commit b60eec0

Browse files
author
GitHub Actions
committed
Update IntelliJ version to 2024.2, increment plugin version to 1.0.1, and enhance code generation with DTO and Mapper support
1 parent 7a970bb commit b60eec0

11 files changed

Lines changed: 243 additions & 125 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
build:
1414
runs-on: ubuntu-latest
1515
env:
16-
INTELLIJ_VERSION: '2023.1'
16+
INTELLIJ_VERSION: '2024.2'
1717

1818
steps:
1919
- name: Checkout repository

build.gradle

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ plugins {
33
id 'org.jetbrains.intellij' version '1.17.0'
44
}
55

6-
group 'com.github.tky0065'
7-
version '1.0.0'
6+
group = 'com.github.tky0065'
7+
version = '1.0.1'
88

99
repositories {
1010
mavenCentral()
@@ -20,24 +20,50 @@ dependencies {
2020
}
2121

2222
// Définition explicite de la version d'IntelliJ pour éviter toute confusion
23-
def intellijVersion = System.getenv("INTELLIJ_VERSION") ?: "2023.1"
23+
def intellijVersion = System.getenv("INTELLIJ_VERSION") ?: "2024.2"
24+
def intellijType = System.getenv("INTELLIJ_TYPE") ?: "IC" // IC pour Community, IU pour Ultimate
2425

2526
intellij {
2627
version.set(intellijVersion) // Utilisation de la méthode set() pour éviter les conflits
27-
type.set('IC') // IntelliJ IDEA Community Edition
28+
type.set(intellijType) // Configurable via variable d'environnement
2829
plugins.set(['java'])
2930
updateSinceUntilBuild.set(true)
3031
downloadSources.set(true)
3132
}
3233

34+
// Configuration pour supprimer les avertissements lors du build
35+
buildSearchableOptions {
36+
enabled = false // Désactive la génération des options searchable qui produit des avertissements
37+
}
38+
39+
runIde {
40+
// Supprime les avertissements concernant JCEF en mode headless
41+
systemProperty("ide.browser.jcef.headless.enabled", "true")
42+
43+
// Définit explicitement la version de JDK pour éviter les problèmes avec Java 25
44+
jvmArgs("-Djdk.module.illegalAccess.silent=true")
45+
46+
// Ignorer les avertissements liés à la compatibilité Gradle
47+
systemProperty("gradle.skip.compatibility.check", "true")
48+
49+
// Éviter les mises à jour automatiques qui peuvent causer des avertissements
50+
systemProperty("ide.plugins.snapshot.on.unload.disable", "true")
51+
}
52+
53+
// Configuration pour la publication du plugin
54+
publishPlugin {
55+
token.set(System.getenv("PUBLISH_TOKEN") ?: "")
56+
channels.set(System.getenv("PUBLISH_CHANNEL") ? [System.getenv("PUBLISH_CHANNEL")] : ["default"])
57+
}
58+
3359
patchPluginXml {
3460
changeNotes.set("""
3561
<ul>
36-
<li>1.0.0 - Version initiale du générateur d'API</li>
62+
<li>1.0.1 - Version initiale du générateur d'API</li>
3763
</ul>
3864
""")
39-
sinceBuild.set("231") // Compatible avec 2023.1+
40-
untilBuild.set("233.*") // Compatible jusqu'à 2023.3.x
65+
sinceBuild.set("242") // Compatible depuis IntelliJ 2024.2
66+
untilBuild.set("252.*") // Compatible jusqu'à 2025.2.x inclus
4167
}
4268

4369
test {

src/main/java/com/github/tky0065/apigenerator/action/GenerateApiAction.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import com.intellij.openapi.ui.Messages;
2727
import com.intellij.psi.*;
2828
import com.intellij.psi.util.PsiTreeUtil;
29+
import com.intellij.openapi.vfs.VirtualFile;
30+
2931
import org.jetbrains.annotations.NotNull;
3032

3133
import javax.swing.*;
@@ -54,6 +56,7 @@ public GenerateApiAction() {
5456
this.existingFileService = new ExistingFileServiceImpl(loggingService);
5557
}
5658

59+
5760
@Override
5861
public void update(@NotNull AnActionEvent e) {
5962
// Active ou désactive l'action en fonction du contexte
@@ -469,8 +472,13 @@ private FileAction showFileExistsDialog(Project project, String packageName, Str
469472
* Crée récursivement les répertoires nécessaires pour un package.
470473
*/
471474
private PsiDirectory createPackageDirectories(Project project, String packageName) {
472-
PsiDirectory baseDir = PsiManager.getInstance(project).findDirectory(
473-
project.getBaseDir());
475+
// Utilisation de ProjectUtil.guessProjectDir au lieu de project.getBaseDir()
476+
VirtualFile projectDir = com.intellij.openapi.project.ProjectUtil.guessProjectDir(project);
477+
if (projectDir == null) {
478+
throw new IllegalStateException("Impossible de trouver le répertoire de base du projet");
479+
}
480+
481+
PsiDirectory baseDir = PsiManager.getInstance(project).findDirectory(projectDir);
474482
if (baseDir == null) {
475483
throw new IllegalStateException("Impossible de trouver le répertoire de base du projet");
476484
}

src/main/java/com/github/tky0065/apigenerator/service/impl/ControllerGenerator.java

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,7 @@ private void addGetAllMethod(TypeSpec.Builder classBuilder, TypeName entityType,
200200
.addModifiers(Modifier.PUBLIC)
201201
.addAnnotation(getMappingAnnotation)
202202
.returns(returnType)
203-
.addStatement("List<$T> entities = service.findAll()", entityType)
204-
.addStatement("// Ici, vous devez convertir les entités en DTOs si nécessaire")
205-
.addStatement("return $T.ok(entities)", ClassName.get("org.springframework.http", "ResponseEntity"))
203+
.addStatement("return $T.ok(service.findAll())", ClassName.get("org.springframework.http", "ResponseEntity"))
206204
.build();
207205

208206
classBuilder.addMethod(getAllMethod);
@@ -222,17 +220,19 @@ private void addGetByIdMethod(TypeSpec.Builder classBuilder, TypeName entityType
222220
.build();
223221

224222
ClassName pathVariable = ClassName.get("org.springframework.web.bind.annotation", "PathVariable");
223+
ClassName responseEntityClass = ClassName.get("org.springframework.http", "ResponseEntity");
225224

225+
// Générer le code avec une seule instruction pour éviter les problèmes de formatage
226226
MethodSpec getByIdMethod = MethodSpec.methodBuilder("getById")
227227
.addModifiers(Modifier.PUBLIC)
228228
.addAnnotation(getMappingAnnotation)
229229
.addParameter(ParameterSpec.builder(idType, "id")
230230
.addAnnotation(pathVariable)
231231
.build())
232232
.returns(returnType)
233-
.addStatement("return service.findById(id)")
234-
.addStatement(" .map(entity -> $T.ok(entity))", ClassName.get("org.springframework.http", "ResponseEntity"))
235-
.addStatement(" .orElseGet(() -> $T.notFound().build())", ClassName.get("org.springframework.http", "ResponseEntity"))
233+
.addCode("return service.findById(id)\n")
234+
.addCode(" .map($T::ok)\n", responseEntityClass)
235+
.addCode(" .orElseGet(() -> $T.notFound().build());\n", responseEntityClass)
236236
.build();
237237

238238
classBuilder.addMethod(getByIdMethod);
@@ -256,11 +256,9 @@ private void addCreateMethod(TypeSpec.Builder classBuilder, TypeName entityType,
256256
.addAnnotation(requestBody)
257257
.build())
258258
.returns(returnType)
259-
.addStatement("// Ici, vous devez convertir le DTO en entité si nécessaire")
260-
.addStatement("$T savedEntity = service.save(($T) dto)", entityType, entityType)
261-
.addStatement("// Puis reconvertir en DTO pour la réponse")
262-
.addStatement("return $T.created(null).body(($T) savedEntity)",
263-
ClassName.get("org.springframework.http", "ResponseEntity"), dtoType)
259+
.addStatement("$T savedDto = service.save(dto)", dtoType)
260+
.addStatement("return $T.created(null).body(savedDto)",
261+
ClassName.get("org.springframework.http", "ResponseEntity"))
264262
.build();
265263

266264
classBuilder.addMethod(createMethod);
@@ -281,7 +279,9 @@ private void addUpdateMethod(TypeSpec.Builder classBuilder, TypeName entityType,
281279

282280
ClassName pathVariable = ClassName.get("org.springframework.web.bind.annotation", "PathVariable");
283281
ClassName requestBody = ClassName.get("org.springframework.web.bind.annotation", "RequestBody");
282+
ClassName responseEntityClass = ClassName.get("org.springframework.http", "ResponseEntity");
284283

284+
// Générer le code avec une structure plus explicite pour éviter les problèmes de formatage
285285
MethodSpec updateMethod = MethodSpec.methodBuilder("update")
286286
.addModifiers(Modifier.PUBLIC)
287287
.addAnnotation(putMappingAnnotation)
@@ -292,15 +292,13 @@ private void addUpdateMethod(TypeSpec.Builder classBuilder, TypeName entityType,
292292
.addAnnotation(requestBody)
293293
.build())
294294
.returns(returnType)
295-
.addStatement("return service.findById(id)")
296-
.addStatement(" .map(existingEntity -> {")
297-
.addStatement(" // Ici, mettre à jour l'entité existante avec les valeurs du DTO")
298-
.addStatement(" $T updatedEntity = service.save(existingEntity)", entityType)
299-
.addStatement(" return $T.ok(($T) updatedEntity)",
300-
ClassName.get("org.springframework.http", "ResponseEntity"), dtoType)
301-
.addStatement(" })")
302-
.addStatement(" .orElseGet(() -> $T.notFound().build())",
303-
ClassName.get("org.springframework.http", "ResponseEntity"))
295+
.addCode("return service.findById(id)\n")
296+
.addCode(" .map(existingDto -> {\n")
297+
.addCode(" // Ici, vous pouvez copier les champs modifiables de dto vers existingDto si besoin\n")
298+
.addCode(" $T updatedDto = service.save(dto);\n", dtoType)
299+
.addCode(" return $T.ok(updatedDto);\n", responseEntityClass)
300+
.addCode(" })\n")
301+
.addCode(" .orElseGet(() -> $T.notFound().build());\n", responseEntityClass)
304302
.build();
305303

306304
classBuilder.addMethod(updateMethod);
@@ -329,8 +327,7 @@ private void addDeleteMethod(TypeSpec.Builder classBuilder, TypeName idType) {
329327
.build())
330328
.returns(returnType)
331329
.addStatement("service.deleteById(id)")
332-
.addStatement("return $T.noContent().build()",
333-
ClassName.get("org.springframework.http", "ResponseEntity"))
330+
.addStatement("return $T.noContent().build()", ClassName.get("org.springframework.http", "ResponseEntity"))
334331
.build();
335332

336333
classBuilder.addMethod(deleteMethod);

src/main/java/com/github/tky0065/apigenerator/service/impl/DependencyValidationServiceImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class DependencyValidationServiceImpl implements DependencyValidationServ
3838
"JPA Entity", "<dependency>\n <groupId>jakarta.persistence</groupId>\n <artifactId>jakarta.persistence-api</artifactId>\n <version>3.1.0</version>\n</dependency>",
3939
"Spring Data JPA", "<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-data-jpa</artifactId>\n</dependency>",
4040
"Spring Web", "<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-web</artifactId>\n</dependency>",
41-
"MapStruct", "<dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct</artifactId>\n <version>1.5.3.Final</version>\n</dependency>\n<dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct-processor</artifactId>\n <version>1.5.3.Final</version>\n <scope>provided</scope>\n</dependency>",
41+
"MapStruct", "<dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct</artifactId>\n <version>1.6.3.Final</version>\n</dependency>\n<dependency>\n <groupId>org.mapstruct</groupId>\n <artifactId>mapstruct-processor</artifactId>\n <version>1.6.3.Final</version>\n <scope>provided</scope>\n</dependency>",
4242
"Lombok", "<dependency>\n <groupId>org.projectlombok</groupId>\n <artifactId>lombok</artifactId>\n <version>1.18.28</version>\n <scope>provided</scope>\n</dependency>"
4343
);
4444

@@ -47,7 +47,7 @@ public class DependencyValidationServiceImpl implements DependencyValidationServ
4747
"JPA Entity", "implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'",
4848
"Spring Data JPA", "implementation 'org.springframework.boot:spring-boot-starter-data-jpa'",
4949
"Spring Web", "implementation 'org.springframework.boot:spring-boot-starter-web'",
50-
"MapStruct", "implementation 'org.mapstruct:mapstruct:1.5.3.Final'\nannotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final'",
50+
"MapStruct", "implementation 'org.mapstruct:mapstruct:1.6.3'\nannotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3'",
5151
"Lombok", "compileOnly 'org.projectlombok:lombok:1.18.28'\nannotationProcessor 'org.projectlombok:lombok:1.18.28'"
5252
);
5353

src/main/java/com/github/tky0065/apigenerator/service/impl/DtoGenerator.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ public String getGeneratedPackageName(EntityModel entityModel, ApiGeneratorConfi
9696
}
9797

9898
private void addField(TypeSpec.Builder classBuilder, String name, TypeName typeName, boolean useLombok) {
99+
// Vérifier si un champ avec ce nom existe déjà pour éviter les doublons
100+
for (FieldSpec field : classBuilder.fieldSpecs) {
101+
if (field.name.equals(name)) {
102+
return; // Ne pas ajouter de doublons
103+
}
104+
}
105+
99106
FieldSpec.Builder fieldBuilder = FieldSpec.builder(typeName, name, Modifier.PRIVATE);
100107
classBuilder.addField(fieldBuilder.build());
101108
}

src/main/java/com/github/tky0065/apigenerator/service/impl/ExistingFileServiceImpl.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,13 @@
33
import com.github.tky0065.apigenerator.service.ExistingFileService;
44
import com.github.tky0065.apigenerator.service.LoggingService;
55
import com.intellij.openapi.project.Project;
6-
import com.intellij.openapi.vfs.VfsUtil;
76
import com.intellij.openapi.vfs.VirtualFile;
87
import com.intellij.psi.PsiDirectory;
98
import com.intellij.psi.PsiFile;
109
import com.intellij.psi.PsiManager;
1110

1211
import java.io.IOException;
1312
import java.nio.charset.StandardCharsets;
14-
import java.nio.file.Files;
15-
import java.nio.file.Path;
16-
import java.nio.file.Paths;
1713
import java.util.HashMap;
1814
import java.util.Map;
1915
import java.util.concurrent.ConcurrentHashMap;
@@ -147,20 +143,24 @@ private String getFileKey(String packageName, String className) {
147143
/**
148144
* Trouve un fichier Java dans le projet.
149145
*/
146+
150147
private PsiFile findFile(Project project, String packageName, String className) {
151-
// Convertir le package en chemin de répertoire
152148
String packagePath = packageName.replace('.', '/');
153-
154-
// Rechercher dans les répertoires sources du projet
155149
PsiManager psiManager = PsiManager.getInstance(project);
156-
VirtualFile baseDir = project.getBaseDir();
157150

151+
// Récupérer le répertoire racine du projet
152+
VirtualFile projectDir = com.intellij.openapi.project.ProjectUtil.guessProjectDir(project);
153+
if (projectDir == null) {
154+
return null;
155+
}
156+
157+
PsiDirectory baseDir = psiManager.findDirectory(projectDir);
158158
if (baseDir == null) {
159159
return null;
160160
}
161161

162162
// Chercher dans src/main/java
163-
VirtualFile srcDir = baseDir.findFileByRelativePath("src/main/java");
163+
VirtualFile srcDir = projectDir.findFileByRelativePath("src/main/java");
164164
if (srcDir != null) {
165165
VirtualFile packageDir = srcDir.findFileByRelativePath(packagePath);
166166
if (packageDir != null) {
@@ -174,6 +174,7 @@ private PsiFile findFile(Project project, String packageName, String className)
174174
return null;
175175
}
176176

177+
177178
/**
178179
* Ajoute une signature à un fichier généré.
179180
*

src/main/java/com/github/tky0065/apigenerator/service/impl/RepositoryGenerator.java

Lines changed: 2 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ public String generateCode(Project project, EntityModel entityModel, ApiGenerato
3333
ClassName repositoryAnnotation = ClassName.get("org.springframework.stereotype", "Repository");
3434
interfaceBuilder.addAnnotation(repositoryAnnotation);
3535

36-
// Ajouter des méthodes de recherche personnalisées basées sur les champs de l'entité
37-
addCustomQueryMethods(interfaceBuilder, entityModel);
36+
// Ne pas générer de méthodes personnalisées pour éviter les doublons
37+
// Spring Data JPA génère déjà automatiquement les méthodes de base
3838

3939
// Créer le fichier Java
4040
JavaFile javaFile = JavaFile.builder(getGeneratedPackageName(entityModel, config), interfaceBuilder.build())
@@ -77,61 +77,6 @@ private TypeName getIdTypeName(EntityModel entityModel) {
7777
return ClassName.get("java.lang", "Long");
7878
}
7979

80-
/**
81-
* Ajoute des méthodes de recherche personnalisées basées sur les champs de l'entité.
82-
*/
83-
private void addCustomQueryMethods(TypeSpec.Builder interfaceBuilder, EntityModel entityModel) {
84-
// Ajouter findBy... pour les champs importants (non-collection, non-transient, etc.)
85-
for (EntityModel.EntityField field : entityModel.getFields()) {
86-
// Ignorer les champs qui ne sont pas adaptés pour les requêtes
87-
if (field.isTransient() || field.isCollection()) {
88-
continue;
89-
}
90-
91-
// Si c'est un champ de type String, ajouter une méthode findByFieldContainingIgnoreCase
92-
if ("String".equals(field.getType())) {
93-
String methodName = "findBy" + capitalizeFirstLetter(field.getName()) + "ContainingIgnoreCase";
94-
95-
TypeName returnType = ParameterizedTypeName.get(
96-
ClassName.get("java.util", "List"),
97-
ClassName.get(entityModel.getPackageName(), entityModel.getClassName())
98-
);
99-
100-
MethodSpec method = MethodSpec.methodBuilder(methodName)
101-
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
102-
.returns(returnType)
103-
.addParameter(String.class, field.getName())
104-
.build();
105-
106-
interfaceBuilder.addMethod(method);
107-
}
108-
// Pour les autres types, ajouter une méthode findByField
109-
else {
110-
String methodName = "findBy" + capitalizeFirstLetter(field.getName());
111-
112-
TypeName returnType = ParameterizedTypeName.get(
113-
ClassName.get("java.util", "List"),
114-
ClassName.get(entityModel.getPackageName(), entityModel.getClassName())
115-
);
116-
117-
MethodSpec method = MethodSpec.methodBuilder(methodName)
118-
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
119-
.returns(returnType)
120-
.addParameter(determineTypeName(field.getType()), field.getName())
121-
.build();
122-
123-
interfaceBuilder.addMethod(method);
124-
}
125-
}
126-
}
127-
128-
private String capitalizeFirstLetter(String input) {
129-
if (input == null || input.isEmpty()) {
130-
return input;
131-
}
132-
return input.substring(0, 1).toUpperCase() + input.substring(1);
133-
}
134-
13580
private TypeName determineTypeName(String type) {
13681
switch (type) {
13782
case "int": return TypeName.INT;

0 commit comments

Comments
 (0)