|
| 1 | +# Contributing |
| 2 | + |
| 3 | +Great if you want to contribute to this project! |
| 4 | + |
| 5 | +But we have to maintain this project and to do that easier everyone has to observe the follwing rules. |
| 6 | + |
| 7 | +## Code Style |
| 8 | + |
| 9 | +The code is written like the official [Java Code Conventions](https://www.oracle.com/technetwork/java/codeconventions-150003.pdf). |
| 10 | + |
| 11 | +Important is to give the methods and classes expressive and clear names to easily differentiate between them. |
| 12 | + |
| 13 | +A command should be wrapped in a new line if it has more than 120 characters. |
| 14 | + |
| 15 | +Tab characters have to be used instead of spaces. |
| 16 | + |
| 17 | +All not final fields have to be tagged with `@Autowired`. |
| 18 | + |
| 19 | +Getters and Setters are generated with the annotation `@Data` by the plugin *lombok*. |
| 20 | +Constructors with ``@NoArgsConstructor`` and `@AllArgsConstructor`. |
| 21 | + |
| 22 | +### Variable names |
| 23 | + |
| 24 | +In ``ControllerResult`` is Controller just a prefix so exclude it in the variable name. |
| 25 | + |
| 26 | +In any ``Model`` is Model a prefix so exclude it. |
| 27 | + |
| 28 | +Example: |
| 29 | +````java |
| 30 | +ControllerResult<SolutionModel> solutionResult = this.solutionController.getSolution(taskId); |
| 31 | +```` |
| 32 | + |
| 33 | +DTOs are named if they are are requestDTO ``dto``, if it is a responseDTO they are called `responseDTO`. |
| 34 | + |
| 35 | +(`ResponseDTOModel` is named `responseDTOModel` ) |
| 36 | + |
| 37 | +## Structure |
| 38 | + |
| 39 | +### Package structure |
| 40 | + |
| 41 | +The base package is `de.themorpheus.edu.taskservice` |
| 42 | + |
| 43 | +This package includes: |
| 44 | +- controller<br> |
| 45 | + └ controller of endpoints |
| 46 | +- database<br> |
| 47 | + └ repositories of controllers and models of those repositories |
| 48 | +- endpoint<br> |
| 49 | + └ endpoint classes and response/request DTOs (**D**ata**T**ransfer**O**bject) |
| 50 | +- pubsub<br> |
| 51 | + └ pubsub classes and subscriber/publisher for the [PubSubSystem](https://github.com/E-Edu/concept/blob/master/documentation/dev/architecture/architecture.md#pubsub-cluster) |
| 52 | +- spring<br> |
| 53 | + └ custom config files of spring |
| 54 | +- util<br> |
| 55 | + └ those who don't match to any other package (like Errors, Constants, etc.) |
| 56 | +--- |
| 57 | +### Arrangement of methods |
| 58 | +To keep a clean code the methods have to be sorted like the following: |
| 59 | +- Create |
| 60 | +- Update |
| 61 | +- Get |
| 62 | + - GetAll |
| 63 | + - GetSpecific |
| 64 | + - ... |
| 65 | +- Check |
| 66 | +- Delete |
| 67 | +- other methods (e.g. methods in other controllers) |
| 68 | +--- |
| 69 | +### Endpoints |
| 70 | +The endpoint classes have to define the route and call the associated method in the controller. |
| 71 | +They also have to handle the authentication and authorization of the users and check permissions. |
| 72 | +Every endpoint class has to end with the suffix `Endpoint`. |
| 73 | +#### DTOs |
| 74 | +##### Validation |
| 75 | +**D**ata**T**ransfer**O**bjects have to validate the *Requestbodies* with `javax.validation` annotations. |
| 76 | +Here is a quick overview: |
| 77 | +|Annotation|Function|Applicable to| |
| 78 | +|---|---|---| |
| 79 | +|@NotNull|Can't be null|-| |
| 80 | +|@NotEmpty|Can't be empty or null|String, Collection, Map, Array| |
| 81 | +|@NotBlank|Trimmed string can't be empty|Text values| |
| 82 | +|@Min|Value can't be lower than parameter|-| |
| 83 | +|@Max|Value can't be greater than parameter|-| |
| 84 | +|@Size|Size of field has to be within the given parameter|String, Collection, Map, Array| |
| 85 | +##### Annotations |
| 86 | +Every DTO has to be annotated with `@Data`, `@NoArgsConstructor` and `@AllArgsConstructor`. (Please also in this order) |
| 87 | +Example: |
| 88 | +```java |
| 89 | +@Data |
| 90 | +@NoArgsConstructor |
| 91 | +@AllArgsConstructor |
| 92 | +public class CreateTaskRequestDTO { |
| 93 | +``` |
| 94 | +##### Class name |
| 95 | +The DTO class name has to be the action what it does and if it is a |
| 96 | +request DTO `Request` suffix or a response dto a `Response` suffix. At least the name has to end with the suffix `DTO`. |
| 97 | +Example: |
| 98 | +```java |
| 99 | +public class GetAllSubjectsResponseDTO { |
| 100 | +``` |
| 101 | +##### Conversions |
| 102 | +If you have to convert a DTO you are free to add a method in this class so that the code stays clean |
| 103 | +##### DTOModel |
| 104 | +Sometimes you have to create a new class because you return e.g. a List. Just create a new Class but add the suffix `Model`. |
| 105 | +Example: |
| 106 | +```java |
| 107 | +public class CheckMultipleChoiceSolutionsResponseDTO { |
| 108 | + @NotNull @Size(min = 1) |
| 109 | + private List<CheckMultipleChoiceSolutionsResponseDTOModel> solutions; |
| 110 | +``` |
| 111 | +#### Particularities |
| 112 | +##### Class annotations |
| 113 | +Every endpoint class needs the annotation `@Timed` and `@RestController`. |
| 114 | +Example: |
| 115 | +```java |
| 116 | +@Timed |
| 117 | +@RestController |
| 118 | +public class TaskEndpoint { |
| 119 | +``` |
| 120 | +##### Mappings |
| 121 | +In the endpoint you have use the specific mapping and not ~~`@RequestMapping`~~. |
| 122 | +Example: |
| 123 | +```java |
| 124 | +@PostMapping("/task") |
| 125 | +public Object createTask(.... |
| 126 | +``` |
| 127 | +##### Returning |
| 128 | +If you call a method in a controller it returns a `ControllerResult`. |
| 129 | +You have to call the method `.getHttpResponse();` to get a correct *HttpResponse*. |
| 130 | +The method has to return `Object`. |
| 131 | +Example: |
| 132 | +```java |
| 133 | +public Object deleteTask(@PathVariable @Min(1) int taskId) { |
| 134 | + return this.taskController.deleteTask(taskId).getHttpResponse(); |
| 135 | +} |
| 136 | +``` |
| 137 | +##### Pathvariables |
| 138 | +Pathvariables have to be written in the route with Braces `{}` and |
| 139 | +named equal as parameter in the method and have to be annotated with `@PathVariable`. |
| 140 | +Example: |
| 141 | +```java |
| 142 | +@GetMapping("/task/{taskId}") |
| 143 | +public Object getTask(@PathVariable int taskId) { |
| 144 | +``` |
| 145 | +##### Requestbodies |
| 146 | +Requestbodies have to be tagged with `@RequestBody` and `@Valid` and have to be named `dto` |
| 147 | +Example: |
| 148 | +```java |
| 149 | +public Object createTask(@RequestBody @Valid CreateTaskRequestDTO dto) { |
| 150 | +``` |
| 151 | +--- |
| 152 | +### Controller |
| 153 | +Controllers have to handle the access to the repository. |
| 154 | +The controllers have to get the data from the repository, check them and return them. |
| 155 | +Controllers have to end with the suffix `Controller`. |
| 156 | +**Is is important to access the repository as few as possible, but you should also check if something at all exists!** |
| 157 | + |
| 158 | +#### Particularities |
| 159 | + |
| 160 | +##### Class annotations |
| 161 | + |
| 162 | +Every endpoint class needs the annotation `@Component`. |
| 163 | + |
| 164 | +Example: |
| 165 | +```java |
| 166 | +@Component |
| 167 | +public class TaskController { |
| 168 | +``` |
| 169 | +##### Returning |
| 170 | +Every method in Controller has to return an instance of `ControllerResult`. |
| 171 | +Example: |
| 172 | +```java |
| 173 | +public ControllerResult<TaskModel> createTask(CreateTaskRequestDTO dto) { |
| 174 | +``` |
| 175 | +##### Repositories |
| 176 | +Most of the Controller need repositories. They have to get annotated with `@Autowired`. |
| 177 | +Is is forbidden to access a different repository as what it isn't meant for. |
| 178 | +Instead of accessing a different repository you can access the controller of this repository. |
| 179 | +#### ControllerResult |
| 180 | +Every method in a Controller has to return a `ControllerResult` instance. |
| 181 | +On the One Hand you can hand over a Result on the other hand if something went wrong an Error. |
| 182 | +`ControllerResult` has several methods to check if there is an Error, Result, etc. to check if e.g. an Error occurred |
| 183 | +while accessing another Controller. If this happens you can call `ControllerResult.ret` and insert the previous `ControllerResult`. |
| 184 | +Example: |
| 185 | +```java |
| 186 | +ControllerResult<SolutionModel> solutionResult = this.solutionController.getSolution(taskId); |
| 187 | +if (solutionResult.isResultNotPresent()) return ControllerResult.ret(solutionResult); |
| 188 | +``` |
| 189 | +##### NAME_KEY |
| 190 | +Every Controller has to `import static` a String `NAME_KEY` in the class `Constants`. It is used to identify where an Error occurred. |
| 191 | +Example: |
| 192 | +```java |
| 193 | +import static de.themorpheus.edu.taskservice.util.Constants.Task.NAME_KEY; |
| 194 | +``` |
| 195 | +##### Errors |
| 196 | +If you e.g. check something that returned from a query of a database but theres no Result you have to throw an Error. |
| 197 | +To identify the error, as mentioned above, you also have to return as extra of the error the `NAME_KEY` of your Controller. |
| 198 | +Example: |
| 199 | +```java |
| 200 | +Optional<TaskModel> optionalTask = this.taskRepository.getTaskByTaskId(taskId); |
| 201 | +if (!optionalTask.isPresent()) return ControllerResult.of(Error.NOT_FOUND, NAME_KEY); |
| 202 | +``` |
| 203 | +### Repositories |
| 204 | +Repositories are the imaginary database. Every Repository has to extend `JpaRepository`. |
| 205 | +You can write your own methods in this interface and they will be transformed to *SQL-Queries* by *JPA*. |
| 206 | +#### Optional |
| 207 | +If you just want to access one object you have to use `Optional`, because it is easy to check if your Result is Present. |
| 208 | +To access one object the method has to start with `find..`. |
| 209 | +Example: |
| 210 | +```java |
| 211 | +Optional<TaskModel> getTaskByTaskId(int taskId); |
| 212 | +``` |
| 213 | +#### Lists |
| 214 | +The Repository can return Lists. You just have to create a method which starts with `findAll..`. |
| 215 | +The returning list can't be null, but empty. |
| 216 | +Example: |
| 217 | +```java |
| 218 | +List<TaskModel> findAllByAuthorId(UUID authorId); |
| 219 | +``` |
| 220 | +#### Foreign Keys |
| 221 | +If you have to use foreign keys in the Repository if you can. But there's a special if you write a query using the |
| 222 | +foreign key. If you use a foreign key you have to use the name like the ID in the foreign key but use the Model. |
| 223 | +Example: ``TaskModel`` |
| 224 | +```java |
| 225 | +@Id |
| 226 | +@GeneratedValue(strategy = GenerationType.IDENTITY) |
| 227 | +private int taskId; |
| 228 | +``` |
| 229 | +Example: ``SolutionRepository`` |
| 230 | +```java |
| 231 | +@Repository |
| 232 | +public interface SolutionRepository extends JpaRepository<SolutionModel, Integer> { |
| 233 | + Optional<SolutionModel> findSolutionModelBybTaskId(TaskModel taskId); |
| 234 | +``` |
| 235 | +So in the query and the variable is named ``taskId``, but actually there is a `TaskModel` handed over. |
| 236 | +#### Particularities |
| 237 | +Every Repository needs the ``@Repository`` annotation. |
| 238 | +Example: |
| 239 | +```java |
| 240 | +@Repository |
| 241 | +public interface TaskRepository extends JpaRepository<TaskModel, Integer> { |
| 242 | +``` |
| 243 | +### Model |
| 244 | +Models are the entries in the associated repository. |
| 245 | +#### Foreign Keys |
| 246 | +As same as in the repository it is necessary to use foreign keys. You can reference foreign keys by using the |
| 247 | +specific *Model* as field declaration. |
| 248 | +But if you use foreign keys you have to tag them with `@OneToOne`, `@ManyToOne` or `@ManyToMany`. |
| 249 | +These annotations tell hibernate in which relation they are. |
| 250 | +Example: |
| 251 | +```java |
| 252 | +public class TaskModel { |
| 253 | + ... |
| 254 | + @ManyToOne |
| 255 | + private LectureModel lectureId; |
| 256 | +``` |
| 257 | +`@ManyToOne` because **many** *Tasks* can have **one** *Lecture* as a foreign key. |
| 258 | +Another Example: |
| 259 | +````java |
| 260 | +public class SolutionModel { |
| 261 | + ... |
| 262 | + @OneToOne |
| 263 | + private TaskModel taskId; |
| 264 | +```` |
| 265 | +``@OneToOne`` because **one** *Solution* can have only **one** *Task* as a foreign key. |
| 266 | +#### Particularities |
| 267 | +##### Annotations |
| 268 | +Every Model has to be annotated with a bunch of annotations: |
| 269 | +````java |
| 270 | +@Data |
| 271 | +@NoArgsConstructor |
| 272 | +@AllArgsConstructor |
| 273 | +@Entity |
| 274 | +@Table |
| 275 | +```` |
| 276 | +##### Conversions |
| 277 | +If you have to convert a Model in e.g. a [DTO](#dtos) you are free to add a method in the model |
| 278 | + class so that the code stays clean. Please place them at the bottom of the file and give the methods a clear name. |
| 279 | + |
| 280 | +Example: |
| 281 | +````java |
| 282 | +public class SimpleEquationSolutionModel { |
| 283 | + ... |
| 284 | + public CreateSimpleEquationSolutionResponseDTOModel toCreateResponseDTOModel() { |
| 285 | + return new CreateSimpleEquationSolutionResponseDTOModel(this.simpleEquationSolutionId, this.step); |
| 286 | + } |
| 287 | +} |
| 288 | +```` |
| 289 | +## Checkstyle |
| 290 | +Checkstyle is a maven plugin which detects any code style violations. You have to fix them because the are necessary |
| 291 | +to merge into experimental. |
| 292 | +If you execute maven install checkstyle will be triggered automatically. The errors describe pretty good where your |
| 293 | +error is thrown and why. |
| 294 | +--- |
| 295 | +#### [Back](./README.md) |
0 commit comments