├── .gitignore ├── README-pt-br.md ├── README.md ├── pom.xml └── src ├── main ├── java │ └── io │ │ └── github │ │ └── asouza │ │ ├── autoconfiguration │ │ └── SpringWebDevAutoconfiguration.java │ │ ├── dataview │ │ ├── DataView.java │ │ └── Trace.java │ │ ├── defensiveprogramming │ │ ├── ChangeProtector.java │ │ ├── ImmutableReference.java │ │ ├── MethodProtector.java │ │ └── Preconditions.java │ │ ├── formflow │ │ ├── FormFlow.java │ │ ├── FormFlowCrudMethods.java │ │ ├── FormFlowManagedEntity.java │ │ └── ToModelStep.java │ │ ├── shared │ │ └── AssertEmptyConstructor.java │ │ └── support │ │ └── Mutable.java └── resources │ └── META-INF │ └── spring.factories └── test └── java └── io └── github └── asouza ├── WithoutEmptyConstructorObject.java ├── dataview └── DataViewTest.java ├── defensiveprogramming ├── ExecutePreconditionsTest.java └── ImmutableReferenceTest.java ├── formflow ├── FormFlowCrudMethodsTest.java ├── FormFlowTest.java └── ToModelStepTest.java └── testsupport ├── ComplexProperty.java ├── ComplexPropertyDTO.java ├── FakeCrudRepository.java ├── FakeEntityManager.java ├── FormTest.java ├── FormTestExtraArgs.java ├── FormTestWithToModelReturningNonEntity.java ├── FormTestWithToModelWithoutArgs.java ├── FormTestWithTwoResolvableTypes.java ├── FormTestWithTwoToModels.java ├── FormTestWithoutToModel.java ├── Goal.java ├── MyBeanFactory.java ├── NonEntity.java ├── ProtectedEntity.java ├── ProtectedEntityWithoutEmptyConstructor.java ├── Team.java ├── TeamRepository.java ├── TeamRepository2.java ├── TestRepositories.java └── UnprotectedEntity.java /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | HELP.md 3 | target/ 4 | !.mvn/wrapper/maven-wrapper.jar 5 | !**/src/main/** 6 | !**/src/test/** 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | /nbproject/private/ 25 | /nbbuild/ 26 | /dist/ 27 | /nbdist/ 28 | /.nb-gradle/ 29 | build/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | 34 | connect-server.sh 35 | deploy.sh 36 | run-production.sh 37 | -------------------------------------------------------------------------------- /README-pt-br.md: -------------------------------------------------------------------------------- 1 | # Por que? 2 | 3 | Depois de ter participado de diversos projetos web com Spring, eu sinto que tenho um template que eu meio que sempre sigo 4 | para a construção de todos os endpoints. Isso é ainda mais forte se estamos naquela fase inicial do projeto. Vou deixar 5 | alguns exemplos aqui: 6 | 7 | ## Salvar um novo objeto e retornar alguma informação daquele objeto 8 | 9 | Meu fluxo é: 10 | 11 | * Crio uma classe que representa o formulário que vai ser preenchido 12 | * Nessa classe, que eu gosto de chamar de Form, eu geralmente declaro um método chamado ```toModel```. 13 | * Implemento o toModel retornando uma nova instância do objeto de dominio em função atributos do próprio form. 14 | * Pode ser que esse meu formulário tenha ids de outros objetos. Não conto conversa, recebo o repository como argumento 15 | do toModel e carrego o objeto referente lá dentro. 16 | * Pode ser que o toModel precise de um objeto extra da aplicação, como um usuário logado. Recebo como argumento também 17 | * Recebo injetado o Spring Data JPA repository relativo ao objeto de domínio no controller 18 | * Também recebe as outras dependências gerenciadas pelo Spring que o toModel precisa 19 | * Posso ou não receber o Repository relativo as depências do toModel(isso é um saco e gera distração na classe) 20 | * Chamo o save 21 | 22 | ## Eu precisa gerar em função de um objeto carregado em algum endpoint 23 | 24 | * Crio uma classe que representa aquele conjunto de dados. Geralmente meto um sufixo DTO e pronto mesmo 25 | * Recebo no construtor do DTO o objeto de domínio que contém as informações que eu preciso 26 | * Invoco os métodos públicos do objeto de domínio e populo os atributos do DTO 27 | * De vez em quando precisa usar formatadores de data, dinheiro etc 28 | * Muitas vezes preciso gerar coleções daquele DTO em função da coleção do objeto de domínio. Lá vou eu fazer 29 | ```colecao.stream().map(...)``` 30 | 31 | # Esse é um template que acho eficiente, mas fiquei cansado de repetir 32 | 33 | Depois de fazer isso muitas vezes, eu comecei a me achar meio que um robô. Eu conseguia programar esses endpoints 34 | pensando na morte da bezerra. Conversei até com Mauricio Aniche para pensarmos numa possibilidade de criar uma 35 | linguagem de programação para desenvolvimento web. Digo, nessa linguagem teria a palavra reservada ```Controller```, 36 | ```Form``` e várias facilidades para o nosso dia a dia. 37 | 38 | Como não tenho competência, nesse momento, para criar uma linguagem, resolvi criar uma mini lib com o objetivo de facilitar 39 | algumas dessas tarefas. 40 | 41 | # Alguns exemplos de uso 42 | 43 | ## Geração de jsons 44 | 45 | ``` 46 | public class DataViewTest { 47 | 48 | @Test 49 | public void objectMustHaveEmptyConstructor() { 50 | Assertions.assertThrows(IllegalArgumentException.class, 51 | () -> DataView.of(new WithoutEmptyConstructorObject(""))); 52 | } 53 | 54 | @Test 55 | public void methodShouldStartWithGetOrIs() { 56 | Assertions.assertThrows(IllegalArgumentException.class, 57 | () -> DataView.of(new Team("")).add(Team :: someMethod)); 58 | } 59 | 60 | @Test 61 | public void shouldExportSimpleProperties() { 62 | Team team = new Team("bla"); 63 | team.setProperty2("ble"); 64 | Map result = DataView.of(team).add(Team::getName).add(Team :: getProperty2).build(); 65 | Assertions.assertEquals("bla", result.get("name")); 66 | Assertions.assertEquals("ble", result.get("property2")); 67 | } 68 | 69 | @Test 70 | public void shouldExportPropertiesWithCustomkeys() { 71 | Team team = new Team("bla"); 72 | team.setProperty2("ble"); 73 | team.setProperty3(LocalDate.of(2019, 1, 1)); 74 | Map result = DataView.of(team) 75 | .add(Team::getName) 76 | .add(Team :: getProperty2) 77 | .addDate("date",t -> (TemporalAccessor)t.getProperty3(), "dd/MM/yyyy") 78 | .build(); 79 | Assertions.assertEquals("bla", result.get("name")); 80 | Assertions.assertEquals("ble", result.get("property2")); 81 | Assertions.assertEquals("01/01/2019", result.get("date")); 82 | } 83 | 84 | @Test 85 | public void shouldExportMappedProperties() { 86 | Team team = new Team("bla"); 87 | team.setProperty2("ble"); 88 | ComplexProperty complexProperty = new ComplexProperty(); 89 | complexProperty.setProperty1("opa"); 90 | team.setProperty3(complexProperty); 91 | Map result = DataView.of(team) 92 | .add(Team::getName) 93 | .add(Team :: getProperty2) 94 | .add("custom", t -> (ComplexProperty)t.getProperty3(), ComplexPropertyDTO :: new) 95 | .build(); 96 | Assertions.assertEquals("bla", result.get("name")); 97 | Assertions.assertEquals("ble", result.get("property2")); 98 | Assertions.assertEquals("opa", ((ComplexPropertyDTO)result.get("custom")).getProperty1()); 99 | } 100 | 101 | @Test 102 | public void shouldExportMappedCollectionProperties() { 103 | Team team = new Team("bla"); 104 | team.setProperty2("ble"); 105 | ComplexProperty complexProperty = new ComplexProperty(); 106 | complexProperty.setProperty1("opa"); 107 | team.setProperty4(Arrays.asList(complexProperty)); 108 | 109 | Map result = DataView.of(team) 110 | .add(Team::getName) 111 | .add(Team :: getProperty2) 112 | .addCollection("custom", Team :: getProperty4,ComplexPropertyDTO :: new) 113 | .build(); 114 | Assertions.assertEquals("bla", result.get("name")); 115 | Assertions.assertEquals("ble", result.get("property2")); 116 | Assertions.assertEquals("opa", ((List)result.get("custom")).get(0).getProperty1()); 117 | } 118 | 119 | ``` 120 | 121 | ## Salvando um objeto em função de um form que segue meu template de desenvolvimento 122 | 123 | ``` 124 | public class FormFlowTest { 125 | 126 | @Test 127 | public void shouldSaveFormSync() { 128 | TeamFormTest form = new TeamFormTest(); 129 | form.setName("test"); 130 | formFlow.save(form).andThen(team -> { 131 | //faça o que precisar 132 | }); 133 | } 134 | 135 | @Test 136 | public void shouldSaveFormSyncWithExtraArgs() { 137 | TeamFormTest form = new TeamFormTest(); 138 | form.setName("test"); 139 | formFlow.save(form,new CustomObject()).andThen(team -> { 140 | //faça o que precisar 141 | }); 142 | } 143 | 144 | @Test 145 | public void shouldSaveFormAsync() { 146 | TeamFormTest form = new TeamFormTest(); 147 | form.setName("test"); 148 | formFlow.save(form).andThenAsync(team -> { 149 | //faça o que precisar 150 | }); 151 | } 152 | 153 | } 154 | 155 | ``` 156 | 157 | ## Habilitando um pouco de programação defensiva 158 | 159 | ``` 160 | 161 | public class ImmutableReferenceTest { 162 | 163 | @Test 164 | public void shouldNotAllowSetterForImmutableReference() { 165 | ComplexProperty complex = new ComplexProperty(); 166 | complex.setProperty1("bla"); 167 | 168 | ComplexProperty immutable = ImmutableReference.of(complex); 169 | Assertions.assertThrows(IllegalAccessException.class, () -> immutable.setProperty1("change")); 170 | } 171 | 172 | @Test 173 | public void shouldNotAllowMutableAnnnotatedMethodForImmutableReference() { 174 | ComplexProperty complex = new ComplexProperty(); 175 | complex.changeProperty(); 176 | 177 | ComplexProperty immutable = ImmutableReference.of(complex); 178 | Assertions.assertThrows(IllegalAccessException.class, () -> immutable.changeProperty()); 179 | } 180 | 181 | @Test 182 | public void shouldBuildCollectionWithImmutableObjects() { 183 | ComplexProperty complex = new ComplexProperty(); 184 | 185 | Collection list = ImmutableReference.of(Arrays.asList(complex)); 186 | Assertions.assertThrows(IllegalAccessException.class, () -> list.iterator().next().changeProperty()); 187 | } 188 | } 189 | 190 | ``` 191 | 192 | ``` 193 | 194 | public class ExecutePreconditionsTest { 195 | 196 | @Test 197 | void shouldAddBeanValidationAnnotationAtConstructor() throws Exception { 198 | Assertions.assertThrows(IllegalArgumentException.class, 199 | () -> Preconditions.newInstance(UnprotectedEntity.class, "")); 200 | 201 | } 202 | 203 | @Test 204 | void shouldValidateAnnotatedConstructorArgs() throws Exception { 205 | Assertions.assertThrows(IllegalArgumentException.class, 206 | () -> Preconditions.newInstance(ProtectedEntity.class, "")); 207 | 208 | } 209 | 210 | @Test 211 | void shouldCreateValidAnnotatedConstructorArgs() throws Exception { 212 | ProtectedEntity instance = Preconditions.newInstance(ProtectedEntity.class, "bla bla"); 213 | Assertions.assertEquals("bla bla", instance.getName()); 214 | 215 | } 216 | 217 | @Test 218 | void shouldValidateAnnotatedMethodArgs() throws Exception { 219 | ProtectedEntity instance = Preconditions.newInstance(ProtectedEntity.class, "bla bla"); 220 | Assertions.assertThrows(IllegalArgumentException.class, () -> instance.logic(0)); 221 | 222 | } 223 | 224 | @Test 225 | void shouldExecuteMethodWithValidArgs() throws Exception { 226 | ProtectedEntity instance = Preconditions.newInstance(ProtectedEntity.class, "bla bla"); 227 | instance.logic(2); 228 | 229 | Assertions.assertEquals(2, instance.getValue()); 230 | } 231 | 232 | @Test 233 | void shouldNotCreateProtectedInstanceWithoutEmptyConstructors() throws Exception { 234 | Assertions.assertThrows(IllegalArgumentException.class, 235 | () -> Preconditions.newInstance(ProtectedEntityWithoutEmptyConstructor.class, "bla bla")); 236 | } 237 | } 238 | 239 | public class UnprotectedEntity { 240 | public UnprotectedEntity(String name) { 241 | 242 | } 243 | } 244 | 245 | public class ProtectedEntity { 246 | 247 | private @NotBlank String name; 248 | private @Min(1) Integer value; 249 | 250 | public ProtectedEntity() { 251 | // TODO Auto-generated constructor stub 252 | } 253 | 254 | public ProtectedEntity(@NotBlank String name) { 255 | this.name = name; 256 | } 257 | 258 | public String getName() { 259 | return name; 260 | } 261 | 262 | public void logic(@Min(1) Integer value) { 263 | this.value = value; 264 | 265 | } 266 | 267 | public Integer getValue() { 268 | return value; 269 | } 270 | } 271 | 272 | ``` 273 | 274 | ## Encontre mais exemplos no código de teste 275 | 276 | Você pode encontrar mais exemplos no código de teste. Eu também tentei deixar javadoc em todos lugares, tudo com o objetivo 277 | de facilitar o uso da lib. Veremos :). 278 | 279 | Caso tenha gostado e queira colaborar, fala comigo no twitter. 280 | 281 | ## Caso queira testar por agora 282 | 283 | * Clone o repositório 284 | * Instale o projeto como dependencia local no seu computador (mvn install a partir da pasta do springwebdev) 285 | * Adicione a dependencia no seu projeto (já mostro) 286 | * Caso vá usar o DataView, olhe os exemplos :) 287 | * Caso vá usar o FormFlow, receba ele injetado no seu controller ```@Autowired FormFlow formFlow``` e olhe os exemplos 288 | 289 | ``` 290 | 291 | br.com.asouza 292 | springwebdevflow 293 | 0.0.1-SNAPSHOT 294 | 295 | 296 | ``` 297 | 298 | 299 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Why? 2 | 3 | After having participated in several web projects with Spring, I feel like I have a template that I kind of always follow 4 | for building all endpoints. This is even stronger if we are in that early phase of the project. I will leave 5 | Some examples here: 6 | 7 | ## Save a new object and return some information from that object 8 | 9 | My current flow is: 10 | 11 | * I create a class that represents the form that will be filled 12 | * In this class, which I like to call Form, I usually declare a method called `` `toModel```. 13 | * I implement toModel by returning a new instance of the domain object as a function of the form itself. 14 | * It may be that my form has ids from other objects. I do the simplest think I can, I get the repository as argument 15 | from toModel and load the referring object inside. 16 | * ToModel may need an extra application object, such as a logged in user. I get as argument too 17 | * I get injected Spring Data JPA repository for domain object in controller 18 | * Also gets the other Spring managed dependencies that toModel needs 19 | * I may or may not receive the Repository regarding toModel depictions (this sucks and generates distraction in the class) 20 | * I call save 21 | 22 | ## I need to generate a JSON as a result of a loaded object 23 | 24 | * I create a class that represents that dataset. I usually put a DTO suffix and that's it 25 | * I receive the domain object that contains the information I need as constructor parameter 26 | I invoke the public methods of the domain object and populate the attributes of the DTO 27 | * Occasionally I need to use formatters of date, money etc. 28 | * I often need to generate collections of that DTO as a result of the domain object collection. There I go 29 |    ```collection.stream (). map (...)``` 30 | 31 | # This is a template that I find efficient, but I got tired of repeating 32 | 33 | After doing this many times, I started to find myself kind of like a robot. I could program these endpoints 34 | even when I was distracted. I even talked with a friend of mine, Mauricio Aniche, to think about a possibility to create a 35 | programming language for web development. I mean, this language would have the reserved word `` `Controller```, 36 | `` `Form``` and various facilities for our daily lives. 37 | 38 | As I have no competence at this time to create a language, I decided to create a mini lib in order to facilitate 39 | some of these tasks. 40 | 41 | # Some usage examples 42 | 43 | ## JSON generation 44 | 45 | ``` 46 | public class DataViewTest { 47 | 48 | @Test 49 | public void objectMustHaveEmptyConstructor() { 50 | Assertions.assertThrows(IllegalArgumentException.class, 51 | () -> DataView.of(new WithoutEmptyConstructorObject(""))); 52 | } 53 | 54 | @Test 55 | public void methodShouldStartWithGetOrIs() { 56 | Assertions.assertThrows(IllegalArgumentException.class, 57 | () -> DataView.of(new Team("")).add(Team :: someMethod)); 58 | } 59 | 60 | @Test 61 | public void shouldExportSimpleProperties() { 62 | Team team = new Team("bla"); 63 | team.setProperty2("ble"); 64 | Map result = DataView.of(team).add(Team::getName).add(Team :: getProperty2).build(); 65 | Assertions.assertEquals("bla", result.get("name")); 66 | Assertions.assertEquals("ble", result.get("property2")); 67 | } 68 | 69 | @Test 70 | public void shouldExportPropertiesWithCustomkeys() { 71 | Team team = new Team("bla"); 72 | team.setProperty2("ble"); 73 | team.setProperty3(LocalDate.of(2019, 1, 1)); 74 | Map result = DataView.of(team) 75 | .add(Team::getName) 76 | .add(Team :: getProperty2) 77 | .addDate("date",t -> (TemporalAccessor)t.getProperty3(), "dd/MM/yyyy") 78 | .build(); 79 | Assertions.assertEquals("bla", result.get("name")); 80 | Assertions.assertEquals("ble", result.get("property2")); 81 | Assertions.assertEquals("01/01/2019", result.get("date")); 82 | } 83 | 84 | @Test 85 | public void shouldExportMappedProperties() { 86 | Team team = new Team("bla"); 87 | team.setProperty2("ble"); 88 | ComplexProperty complexProperty = new ComplexProperty(); 89 | complexProperty.setProperty1("opa"); 90 | team.setProperty3(complexProperty); 91 | Map result = DataView.of(team) 92 | .add(Team::getName) 93 | .add(Team :: getProperty2) 94 | .add("custom", t -> (ComplexProperty)t.getProperty3(), ComplexPropertyDTO :: new) 95 | .build(); 96 | Assertions.assertEquals("bla", result.get("name")); 97 | Assertions.assertEquals("ble", result.get("property2")); 98 | Assertions.assertEquals("opa", ((ComplexPropertyDTO)result.get("custom")).getProperty1()); 99 | } 100 | 101 | @Test 102 | public void shouldExportMappedCollectionProperties() { 103 | Team team = new Team("bla"); 104 | team.setProperty2("ble"); 105 | ComplexProperty complexProperty = new ComplexProperty(); 106 | complexProperty.setProperty1("opa"); 107 | team.setProperty4(Arrays.asList(complexProperty)); 108 | 109 | Map result = DataView.of(team) 110 | .add(Team::getName) 111 | .add(Team :: getProperty2) 112 | .addCollection("custom", Team :: getProperty4,ComplexPropertyDTO :: new) 113 | .build(); 114 | Assertions.assertEquals("bla", result.get("name")); 115 | Assertions.assertEquals("ble", result.get("property2")); 116 | Assertions.assertEquals("opa", ((List)result.get("custom")).get(0).getProperty1()); 117 | } 118 | 119 | ``` 120 | 121 | ## Saving an object using a form that follows my development template. Just to remember, this form has the ```toModel``` method. 122 | 123 | ``` 124 | public class FormFlowTest { 125 | 126 | @Test 127 | public void shouldSaveFormSync() { 128 | TeamFormTest form = new TeamFormTest(); 129 | form.setName("test"); 130 | formFlow.save(form).andThen(team -> { 131 | //do what you need 132 | }); 133 | } 134 | 135 | @Test 136 | public void shouldSaveFormSyncWithExtraArgs() { 137 | TeamFormTest form = new TeamFormTest(); 138 | form.setName("test"); 139 | formFlow.save(form,new CustomObject()).andThen(team -> { 140 | //do what you need 141 | }); 142 | } 143 | 144 | @Test 145 | public void shouldSaveFormAsync() { 146 | TeamFormTest form = new TeamFormTest(); 147 | form.setName("test"); 148 | formFlow.save(form).andThenAsync(team -> { 149 | //do what you need 150 | }); 151 | } 152 | 153 | } 154 | 155 | ``` 156 | 157 | ## Using defensive programming 158 | 159 | ``` 160 | 161 | public class ImmutableReferenceTest { 162 | 163 | @Test 164 | public void shouldNotAllowSetterForImmutableReference() { 165 | ComplexProperty complex = new ComplexProperty(); 166 | complex.setProperty1("bla"); 167 | 168 | ComplexProperty immutable = ImmutableReference.of(complex); 169 | Assertions.assertThrows(IllegalAccessException.class, () -> immutable.setProperty1("change")); 170 | } 171 | 172 | @Test 173 | public void shouldNotAllowMutableAnnnotatedMethodForImmutableReference() { 174 | ComplexProperty complex = new ComplexProperty(); 175 | complex.changeProperty(); 176 | 177 | ComplexProperty immutable = ImmutableReference.of(complex); 178 | Assertions.assertThrows(IllegalAccessException.class, () -> immutable.changeProperty()); 179 | } 180 | 181 | @Test 182 | public void shouldBuildCollectionWithImmutableObjects() { 183 | ComplexProperty complex = new ComplexProperty(); 184 | 185 | Collection list = ImmutableReference.of(Arrays.asList(complex)); 186 | Assertions.assertThrows(IllegalAccessException.class, () -> list.iterator().next().changeProperty()); 187 | } 188 | } 189 | 190 | ``` 191 | 192 | ``` 193 | 194 | public class ExecutePreconditionsTest { 195 | 196 | @Test 197 | void shouldAddBeanValidationAnnotationAtConstructor() throws Exception { 198 | Assertions.assertThrows(IllegalArgumentException.class, 199 | () -> Preconditions.newInstance(UnprotectedEntity.class, "")); 200 | 201 | } 202 | 203 | @Test 204 | void shouldValidateAnnotatedConstructorArgs() throws Exception { 205 | Assertions.assertThrows(IllegalArgumentException.class, 206 | () -> Preconditions.newInstance(ProtectedEntity.class, "")); 207 | 208 | } 209 | 210 | @Test 211 | void shouldCreateValidAnnotatedConstructorArgs() throws Exception { 212 | ProtectedEntity instance = Preconditions.newInstance(ProtectedEntity.class, "bla bla"); 213 | Assertions.assertEquals("bla bla", instance.getName()); 214 | 215 | } 216 | 217 | @Test 218 | void shouldValidateAnnotatedMethodArgs() throws Exception { 219 | ProtectedEntity instance = Preconditions.newInstance(ProtectedEntity.class, "bla bla"); 220 | Assertions.assertThrows(IllegalArgumentException.class, () -> instance.logic(0)); 221 | 222 | } 223 | 224 | @Test 225 | void shouldExecuteMethodWithValidArgs() throws Exception { 226 | ProtectedEntity instance = Preconditions.newInstance(ProtectedEntity.class, "bla bla"); 227 | instance.logic(2); 228 | 229 | Assertions.assertEquals(2, instance.getValue()); 230 | } 231 | 232 | @Test 233 | void shouldNotCreateProtectedInstanceWithoutEmptyConstructors() throws Exception { 234 | Assertions.assertThrows(IllegalArgumentException.class, 235 | () -> Preconditions.newInstance(ProtectedEntityWithoutEmptyConstructor.class, "bla bla")); 236 | } 237 | } 238 | 239 | public class UnprotectedEntity { 240 | public UnprotectedEntity(String name) { 241 | 242 | } 243 | } 244 | 245 | public class ProtectedEntity { 246 | 247 | private @NotBlank String name; 248 | private @Min(1) Integer value; 249 | 250 | public ProtectedEntity() { 251 | // TODO Auto-generated constructor stub 252 | } 253 | 254 | public ProtectedEntity(@NotBlank String name) { 255 | this.name = name; 256 | } 257 | 258 | public String getName() { 259 | return name; 260 | } 261 | 262 | public void logic(@Min(1) Integer value) { 263 | this.value = value; 264 | 265 | } 266 | 267 | public Integer getValue() { 268 | return value; 269 | } 270 | } 271 | 272 | ``` 273 | 274 | ## Find more examples in the test code 275 | 276 | You can find more examples in the test code. I also tried to leave javadoc everywhere, all aiming 277 | to facilitate the use of lib. We'll see :). 278 | 279 | If you enjoyed and would like to collaborate, talk to me on 280 | 281 | ## If you want to test for now 282 | 283 | * Clone the repository 284 | * Install the project as local dependency on your computer (mvn install from springwebdev folder) 285 | * Add dependency to your project 286 | * If you are going to use DataView, look at the examples :) 287 | * If you are going to use FormFlow, get it injected into your `` `@Autowired FormFlow formFlow`` controller and look at the examples. 288 | 289 | ``` 290 | 291 | br.com.asouza 292 | springwebdevflow 293 | 0.0.1-SNAPSHOT 294 | 295 | 296 | ``` 297 | 298 | 299 | 300 | 301 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | io.github.asouza 7 | springwebdevflow 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | ${project.groupId}:${project.artifactId} 12 | You can check it out here http://github.com/asouza/springwebdevflow 13 | http://github.com/asouza/springwebdevflow 14 | 15 | 16 | 17 | MIT License 18 | https://opensource.org/licenses/MIT 19 | 20 | 21 | 22 | 23 | 24 | Alberto Souza 25 | alberto.souza@caelum.com.br 26 | Caelum 27 | https://github.com/asouza/springwebdevflow 28 | 29 | 30 | 31 | 32 | scm:git:git@github.com:asouza/springwebdevflow.git 33 | scm:git:git@github.com:asouza/springwebdevflow.git 34 | http://github.com/simpligility/ossrh-demo/tree/master 35 | 36 | 37 | 38 | 1.8 39 | UTF-8 40 | 2.2.1.RELEASE 41 | 8 42 | 8 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-dependencies 51 | ${spring.boot.version} 52 | pom 53 | import 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-test 62 | test 63 | 64 | 65 | org.junit.vintage 66 | junit-vintage-engine 67 | 68 | 69 | 70 | 71 | 72 | 73 | javax.validation 74 | validation-api 75 | provided 76 | 77 | 78 | 79 | 80 | org.hibernate.validator 81 | hibernate-validator 82 | provided 83 | 84 | 85 | 86 | org.glassfish 87 | javax.el 88 | test 89 | 3.0.0 90 | 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-starter-data-jpa 95 | provided 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/autoconfiguration/SpringWebDevAutoconfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.autoconfiguration; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.data.repository.support.Repositories; 9 | 10 | import io.github.asouza.formflow.FormFlow; 11 | 12 | @Configuration 13 | public class SpringWebDevAutoconfiguration implements ApplicationContextAware{ 14 | 15 | private ApplicationContext applicationContext; 16 | 17 | @Bean 18 | public FormFlow create(ApplicationContext context){ 19 | return new FormFlow<>(context, new Repositories(applicationContext)); 20 | } 21 | 22 | @Override 23 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 24 | this.applicationContext = applicationContext; 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/dataview/DataView.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.dataview; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.text.DecimalFormat; 5 | import java.time.format.DateTimeFormatter; 6 | import java.time.temporal.TemporalAccessor; 7 | import java.util.Collection; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Optional; 12 | import java.util.function.Function; 13 | import java.util.stream.Collectors; 14 | import java.util.stream.Stream; 15 | 16 | import org.springframework.cglib.proxy.Enhancer; 17 | import org.springframework.util.Assert; 18 | 19 | import io.github.asouza.shared.AssertEmptyConstructor; 20 | 21 | public final class DataView { 22 | 23 | private T proxy; 24 | private Trace trace; 25 | private T original; 26 | private Map complexValues = new HashMap<>(); 27 | 28 | @SuppressWarnings("unchecked") 29 | private DataView(T instance) { 30 | AssertEmptyConstructor.test(instance.getClass(), 31 | "In order to transform an object to a DataView you need to provide an empty constructor. " 32 | + "Just use the Deprecated annotation and everything will be fine :)"); 33 | 34 | Enhancer enhancer = new Enhancer(); 35 | enhancer.setSuperclass(instance.getClass()); 36 | this.trace = new Trace(instance); 37 | enhancer.setCallback(this.trace); 38 | this.proxy = (T) enhancer.create(); 39 | this.original = instance; 40 | } 41 | 42 | public static DataView of(T instance) { 43 | return new DataView(instance); 44 | } 45 | 46 | /** 47 | * 48 | * @param function to call the method which follows the Java Beans pattern. 49 | * @return 50 | */ 51 | public final DataView add(Function function) { 52 | function.apply(proxy); 53 | return this; 54 | } 55 | 56 | /** 57 | * 58 | * @param key custom key to be generated 59 | * @param function function to call the method which follows the Java Beans 60 | * pattern. 61 | * @return 62 | */ 63 | public final DataView add(String key, Function function) { 64 | complexValues.put(key, function.apply(original)); 65 | return this; 66 | } 67 | 68 | /** 69 | * 70 | * @param key custom key to be generated 71 | * @param function function function to call the method which follows the Java 72 | * Beans pattern. 73 | * @param pattern pattern supported for {@link DateTimeFormatter} 74 | * @return 75 | */ 76 | public final DataView addDate(String key, Function function, String pattern) { 77 | 78 | Function formatter = (temporal) -> { 79 | return DateTimeFormatter.ofPattern(pattern).format(temporal); 80 | }; 81 | 82 | return this.addFormatted(key, function, formatter); 83 | } 84 | 85 | /** 86 | * 87 | * @param key custom key to be generated 88 | * @param function to call the method which follows the Java Beans pattern. 89 | * @return number formatted using ##.##% 90 | */ 91 | public final DataView addPercentual(String key, Function function) { 92 | 93 | Function formatter = (number) -> { 94 | DecimalFormat df = new DecimalFormat("##.##%"); 95 | return df.format(number.doubleValue() / 100); 96 | }; 97 | 98 | return this.addFormatted(key, function, formatter); 99 | } 100 | 101 | /** 102 | * 103 | * @param custom return type to be passed to formatter parameter 104 | * @param key custom key to be generated 105 | * @param function to call the method which follows the Java Beans pattern. 106 | * @param formatter function to apply some transformation on the property 107 | * @return 108 | */ 109 | public final DataView addFormatted(String key, Function function, 110 | Function formatter) { 111 | ReturnType result = function.apply(original); 112 | complexValues.put(key, formatter.apply(result)); 113 | return this; 114 | } 115 | 116 | /** 117 | * 118 | * @return map build with collected values and keys 119 | */ 120 | public final Map build() { 121 | Map json = trace.getJson(); 122 | 123 | complexValues.entrySet().forEach(entry -> { 124 | json.put(entry.getKey(), entry.getValue()); 125 | }); 126 | return json; 127 | } 128 | 129 | /** 130 | * 131 | * @param type to be used as input for the mapper method 132 | * @param type which will be returned by the mapper function 133 | * @param key custom key to be generated 134 | * @param function to call the method which follows the Java Beans pattern. 135 | * @param mapper function to map the original property value to the new 136 | * Object 137 | * @return 138 | */ 139 | public final DataView add(String key, Function supplier, 140 | Function mapper) { 141 | MapperType finalObject = supplier.andThen(mapper).apply(original); 142 | complexValues.put(key, finalObject); 143 | return this; 144 | } 145 | 146 | /** 147 | * 148 | * @param type to be used as input for the mapper method 149 | * @param key custom key to be generated 150 | * @param function to call the method which follows the Java Beans 151 | * pattern. 152 | * @param propertyMappers mappers to each property of the returned object. Do 153 | * not map here to collection or a application objects 154 | * @return 155 | */ 156 | @SafeVarargs 157 | public final DataView add2(String key, Function supplier, 158 | Function... propertyMappers) { 159 | ReturnType complexProperty = supplier.apply(original); 160 | 161 | DataView viewOfComplexProperty = dataViewOfAComplextProperty(complexProperty, propertyMappers); 162 | 163 | complexValues.put(key, viewOfComplexProperty.build()); 164 | 165 | return this; 166 | } 167 | 168 | @SafeVarargs 169 | private final DataView dataViewOfAComplextProperty(ReturnType complexProperty, 170 | Function... propertyMappers) { 171 | DataView viewOfComplexProperty = DataView.of(complexProperty); 172 | for (Function mapper : propertyMappers) { 173 | 174 | Function onlyAllowedSimpleJavaTypesMapper = object -> { 175 | Object mappedProperty = mapper.apply(object); 176 | String packageName = mappedProperty.getClass().getPackage().getName(); 177 | 178 | boolean isJavaSimpleType = packageName.startsWith("java") && !(mappedProperty instanceof Collection); 179 | Assert.isTrue(isJavaSimpleType, 180 | "Your mapping is too complex already. Create your custom DTO and use the add method"); 181 | 182 | return mapper.apply(object).toString(); 183 | }; 184 | 185 | viewOfComplexProperty.add(onlyAllowedSimpleJavaTypesMapper); 186 | } 187 | return viewOfComplexProperty; 188 | } 189 | 190 | /** 191 | * 192 | * @param type to be used as input for the mapper method 193 | * @param type which will be returned by the mapper function 194 | * @param key custom key to be generated 195 | * @param function to call the method which follows the Java Beans pattern. 196 | * @param mapper function to map the original property value to new 197 | * collection of objects 198 | * @return 199 | */ 200 | public final DataView addCollection(String key, 201 | Function> function, Function mapper) { 202 | 203 | Collection collection = function.apply(original); 204 | // using list to keep the original order 205 | List resultList = collection.stream().map(mapper).collect(Collectors.toList()); 206 | complexValues.put(key, resultList); 207 | 208 | return this; 209 | } 210 | 211 | /** 212 | * 213 | * @param type of the getterMethod for the complex object 214 | * @param key custom key to be generated 215 | * @param function to call the method which follows the Java Beans 216 | * pattern. 217 | * @param propertyMappers mappers to each property of the returned object. Do 218 | * not map here to collection or a application objects 219 | * @return 220 | */ 221 | @SafeVarargs 222 | public final DataView addCollection2(String key, Function> function, 223 | Function... propertyMappers) { 224 | 225 | Collection collection = function.apply(original); 226 | 227 | List> collectionObjectProperties = collection.stream().map(object -> { 228 | return dataViewOfAComplextProperty(object, propertyMappers).build(); 229 | }).collect(Collectors.toList()); 230 | 231 | complexValues.put(key, collectionObjectProperties); 232 | return this; 233 | } 234 | 235 | } -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/dataview/Trace.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.dataview; 2 | 3 | import java.beans.Introspector; 4 | import java.lang.reflect.Method; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import org.springframework.cglib.proxy.MethodInterceptor; 9 | import org.springframework.cglib.proxy.MethodProxy; 10 | import org.springframework.util.Assert; 11 | 12 | class Trace implements MethodInterceptor { 13 | 14 | private Map json = new HashMap<>(); 15 | private Object instance; 16 | 17 | public Trace(Object instance) { 18 | this.instance = instance; 19 | } 20 | 21 | @Override 22 | public Object intercept(Object proxy, Method currentMethod, Object[] args, MethodProxy proxyMethod) 23 | throws Throwable { 24 | String methodName = currentMethod.getName(); 25 | Assert.isTrue(methodName.startsWith("is") || methodName.startsWith("get"),"Your method must start with is or get to be used as part of DataView generation"); 26 | String propertyName = Introspector.decapitalize(methodName.substring(methodName.startsWith("is") ? 2 : 3)); 27 | Object methodResult = currentMethod.invoke(instance, args); 28 | json.put(propertyName, methodResult); 29 | return methodResult; 30 | } 31 | 32 | public Map getJson() { 33 | return json; 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/defensiveprogramming/ChangeProtector.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.defensiveprogramming; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | import org.springframework.cglib.proxy.MethodInterceptor; 6 | import org.springframework.cglib.proxy.MethodProxy; 7 | 8 | import io.github.asouza.support.Mutable; 9 | 10 | public class ChangeProtector implements MethodInterceptor { 11 | 12 | private Object original; 13 | 14 | public ChangeProtector(Object original) { 15 | this.original = original; 16 | } 17 | 18 | @Override 19 | public Object intercept(Object proxy, Method method, Object[] args, MethodProxy helper) throws Throwable { 20 | 21 | String methodName = method.getName(); 22 | if(methodName.startsWith("set")) { 23 | throw new IllegalAccessException("You can't invoke a setter in this object("+original.getClass()+"). It is immutable S2"); 24 | } 25 | 26 | if(method.isAnnotationPresent(Mutable.class)) { 27 | throw new IllegalAccessException("You can't invoke a mutable method in this object("+original.getClass()+")"); 28 | } 29 | 30 | return ImmutableReference.of(method.invoke(original, args)); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/defensiveprogramming/ImmutableReference.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.defensiveprogramming; 2 | 3 | import java.util.Collection; 4 | import java.util.stream.Collectors; 5 | 6 | import org.springframework.cglib.proxy.Enhancer; 7 | 8 | /** 9 | * Transform mutable references in immutable references. 10 | * @author alberto 11 | * 12 | * @param 13 | */ 14 | public class ImmutableReference { 15 | 16 | /** 17 | * 18 | * @param type of object 19 | * @param instance instance that should be protected for change 20 | * @return 21 | */ 22 | @SuppressWarnings("unchecked") 23 | public static T of(T instance) { 24 | Enhancer enhancer = new Enhancer(); 25 | enhancer.setSuperclass(instance.getClass()); 26 | ChangeProtector changeProtector = new ChangeProtector(instance); 27 | enhancer.setCallback(changeProtector); 28 | return (T) enhancer.create(); 29 | } 30 | 31 | /** 32 | * 33 | * @param type of each object in collection 34 | * @param instances instances that should be protected for change 35 | * @return 36 | */ 37 | public static Collection of(Collection instances) { 38 | return instances.stream().map(ImmutableReference::of).collect(Collectors.toList()); 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/defensiveprogramming/MethodProtector.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.defensiveprogramming; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.Set; 5 | 6 | import javax.validation.ConstraintViolation; 7 | import javax.validation.Validator; 8 | import javax.validation.executable.ExecutableValidator; 9 | import javax.validation.metadata.BeanDescriptor; 10 | import javax.validation.metadata.MethodType; 11 | 12 | import org.springframework.cglib.proxy.MethodInterceptor; 13 | import org.springframework.cglib.proxy.MethodProxy; 14 | import org.springframework.util.Assert; 15 | 16 | public class MethodProtector implements MethodInterceptor { 17 | 18 | private Object original; 19 | private Validator validator; 20 | private boolean thereAreValidationsToBeApplied; 21 | 22 | public MethodProtector(Object original, Validator validator) { 23 | this.original = original; 24 | this.validator = validator; 25 | 26 | BeanDescriptor beanDescriptor = validator.getConstraintsForClass(original.getClass()); 27 | thereAreValidationsToBeApplied = !beanDescriptor.getConstrainedMethods(MethodType.NON_GETTER).isEmpty(); 28 | } 29 | 30 | @Override 31 | public Object intercept(Object proxy, Method method, Object[] args, MethodProxy helper) throws Throwable { 32 | 33 | if (thereAreValidationsToBeApplied) { 34 | ExecutableValidator executableValidator = (ExecutableValidator) validator; 35 | Set> violations = executableValidator.validateParameters(original, method, 36 | args); 37 | 38 | Assert.isTrue(violations.isEmpty(), 39 | String.format("Arguments passed to method[%s] are invalid. %s", method, violations)); 40 | } 41 | 42 | return method.invoke(original, args); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/defensiveprogramming/Preconditions.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.defensiveprogramming; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Set; 8 | import java.util.stream.Collectors; 9 | import java.util.stream.Stream; 10 | 11 | import javax.validation.ConstraintViolation; 12 | import javax.validation.Validation; 13 | import javax.validation.Validator; 14 | import javax.validation.ValidatorFactory; 15 | import javax.validation.executable.ExecutableValidator; 16 | import javax.validation.metadata.BeanDescriptor; 17 | 18 | import org.springframework.cglib.proxy.Enhancer; 19 | import org.springframework.util.Assert; 20 | 21 | import io.github.asouza.shared.AssertEmptyConstructor; 22 | 23 | public class Preconditions { 24 | 25 | private static Validator validator; 26 | 27 | static { 28 | ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 29 | validator = factory.getValidator(); 30 | } 31 | 32 | /** 33 | * 34 | * @param type of the new instance 35 | * @param klass klass of the instance which should be created 36 | * @param args constructor args 37 | * @return a protected instance of this class 38 | */ 39 | public static T newInstance(Class klass, Object... args) { 40 | AssertEmptyConstructor.test(klass, "Class[" + klass 41 | + "] In order to protect your methods from invalid arguments you need to provide empty constructor. Proxies are made on top of classes with empty constructors"); 42 | 43 | BeanDescriptor beanDescriptor = validator.getConstraintsForClass(klass); 44 | Assert.isTrue(beanDescriptor.getConstrainedConstructors().size() > 0, 45 | String.format("Constructor of %s must have Bean Validation annotations at the parameters", klass)); 46 | 47 | Class[] parameterTypes = Stream.of(args).map(Object::getClass).collect(Collectors.toList()) 48 | .toArray(new Class[] {}); 49 | 50 | try { 51 | Constructor constructor = klass.getConstructor(parameterTypes); 52 | ExecutableValidator executableValidator = validator.forExecutables(); 53 | Set> violations = executableValidator.validateConstructorParameters(constructor, 54 | args); 55 | Assert.isTrue(violations.isEmpty(), 56 | String.format("This object[%s] can't be createad because constructor parameters are invalid. %s", 57 | klass, violations)); 58 | 59 | // should I do this here? 60 | return newProtectedInstance(constructor.newInstance(args),args); 61 | 62 | } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException 63 | | InvocationTargetException e) { 64 | throw new RuntimeException(e); 65 | } 66 | } 67 | 68 | @SuppressWarnings("unchecked") 69 | private static T newProtectedInstance(T rawInstance,Object...args) { 70 | Enhancer enhancer = new Enhancer(); 71 | enhancer.setSuperclass(rawInstance.getClass()); 72 | enhancer.setCallback(new MethodProtector(rawInstance, validator)); 73 | 74 | //FIXME I really don't know the best solution here. So I am duplicating the construction. I need to provide access to field values in the proxy 75 | List> constructorArgumentTypes = Stream.of(args).map(Object :: getClass).collect(Collectors.toList()); 76 | return (T) enhancer.create(constructorArgumentTypes.toArray(new Class[]{}), args); 77 | } 78 | 79 | @SuppressWarnings("unchecked") 80 | /** 81 | * Only use this method if you will call only the public methods of the protected instance. 82 | * 83 | * @param type of current instance 84 | * @param instance instance which should be protected 85 | * @return protected instance 86 | */ 87 | public static T protectMethods(T instance) { 88 | AssertEmptyConstructor.test(instance.getClass(), "Class[" + instance.getClass() 89 | + "] In order to protect your methods from invalid arguments you need to provide empty constructor. Proxies are made on top of classes with empty constructors"); 90 | 91 | Enhancer enhancer = new Enhancer(); 92 | enhancer.setSuperclass(instance.getClass()); 93 | enhancer.setCallback(new MethodProtector(instance, validator)); 94 | return (T) enhancer.create(); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/formflow/FormFlow.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.formflow; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Set; 10 | import java.util.stream.Collectors; 11 | import java.util.stream.Stream; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.factory.BeanFactory; 16 | import org.springframework.beans.factory.NoSuchBeanDefinitionException; 17 | import org.springframework.data.repository.support.Repositories; 18 | import org.springframework.util.Assert; 19 | 20 | /** 21 | * 22 | * The entry point of a form flow 23 | * 24 | * @author alberto 25 | * 26 | * @param Type of the Domain Object expecting to be created 27 | */ 28 | public class FormFlow { 29 | 30 | private BeanFactory ctx; 31 | private Repositories repositories; 32 | 33 | private static final Logger log = LoggerFactory.getLogger(FormFlow.class); 34 | 35 | public FormFlow(BeanFactory ctx, Repositories repositories) { 36 | this.ctx = ctx; 37 | this.repositories = repositories; 38 | } 39 | 40 | @SuppressWarnings({ "unchecked" }) 41 | private T buildModel(Object form, Object... extraArgs) { 42 | Method[] methods = form.getClass().getMethods(); 43 | Set toModels = Stream.of(methods).filter(method -> method.getName().equals("toModel")) 44 | .collect(Collectors.toSet()); 45 | 46 | Assert.isTrue(!(toModels.size() > 1), "Your form is not allowed to have more than one toModel method"); 47 | Assert.isTrue(toModels.size() == 1, "Your form MUST have a toModel method"); 48 | 49 | Method toModelMethod = toModels.iterator().next(); 50 | Class[] parameterTypes = toModelMethod.getParameterTypes(); 51 | 52 | List resolvedParameters = new ArrayList<>(); 53 | 54 | Map, Object> extraArgsParameters = populateExtraArgsIfExists(extraArgs); 55 | 56 | Stream.of(parameterTypes).forEach(parameterType -> { 57 | if (extraArgsParameters.containsKey(parameterType)) { 58 | log.debug("Resolving extra arg toModel parameter type {}", parameterType); 59 | resolvedParameters.add(extraArgsParameters.get(parameterType)); 60 | } else { 61 | log.debug("testing if Spring is able to lookup {}", parameterType); 62 | try { 63 | resolvedParameters.add(ctx.getBean(parameterType)); 64 | } catch (NoSuchBeanDefinitionException e) { 65 | throw new RuntimeException( 66 | "You probably are requesting extra param which is not resolvable for the Spring Context", 67 | e); 68 | } 69 | } 70 | }); 71 | 72 | try { 73 | T domainObject = (T) toModelMethod.invoke(form, resolvedParameters.toArray()); 74 | return domainObject; 75 | } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 76 | throw new RuntimeException(e); 77 | } 78 | } 79 | 80 | /** 81 | * 82 | * @param form object that represents a form sent from some client. It must have 83 | * a toModel method that returns the Domain Object assignable to 84 | * @return The ToModelStep holding the DomainObject 85 | */ 86 | public ToModelStep toModel(Object form, Object... extraArgs) { 87 | T domainObject = buildModel(form, extraArgs); 88 | @SuppressWarnings("unchecked") 89 | Class klass = (Class) domainObject.getClass(); 90 | return new ToModelStep(domainObject, FormFlowCrudMethods.create(klass, repositories, ctx)); 91 | } 92 | 93 | /** 94 | * 95 | * @param form object that represents a form sent from some client. It must have 96 | * a toModel method that returns the Domain Object assignable to 97 | * @return The domain object built by the form 98 | */ 99 | public T justBuildDomainObject(Object form, Object... extraArgs) { 100 | return buildModel(form, extraArgs); 101 | } 102 | 103 | private Map, Object> populateExtraArgsIfExists(Object... extraArgs) { 104 | Map, Object> extraArgsParameters = new HashMap<>(); 105 | Stream.of(extraArgs).forEach(extraArg -> { 106 | extraArgsParameters.put(extraArg.getClass(), extraArg); 107 | }); 108 | return extraArgsParameters; 109 | } 110 | 111 | /** 112 | * 113 | * @param form object that represents a form sent from some client. It must 114 | * have a toModel method that returns the Domain Object 115 | * assignable to 116 | * @param extraArgs custom application arguments for toModel. Ex: current logged 117 | * user 118 | * @return {@link FormFlowManagedEntity} with saved instance of the Domain 119 | * Object created from the form 120 | */ 121 | public FormFlowManagedEntity save(Object form, Object... extraArgs) { 122 | return toModel(form, extraArgs).save(); 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/formflow/FormFlowCrudMethods.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.formflow; 2 | 3 | import java.util.Optional; 4 | import java.util.function.Function; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.EntityManager; 8 | 9 | import org.hibernate.resource.beans.container.internal.NoSuchBeanException; 10 | import org.springframework.beans.factory.BeanFactory; 11 | import org.springframework.data.repository.CrudRepository; 12 | import org.springframework.data.repository.support.Repositories; 13 | import org.springframework.util.Assert; 14 | 15 | 16 | /** 17 | * Abstraction to work with crud methods. Just a wrapper 18 | * @author alberto 19 | * 20 | * @param type of the entity which crud methods should work 21 | */ 22 | public class FormFlowCrudMethods { 23 | 24 | private Function save; 25 | 26 | /** 27 | * 28 | * @param save function to save instance of T 29 | */ 30 | public FormFlowCrudMethods(Function save) { 31 | this.save = save; 32 | } 33 | 34 | /** 35 | * 36 | * @param entity save entity 37 | * @return saved entity 38 | */ 39 | public T save(T entity) { 40 | return save.apply(entity); 41 | } 42 | 43 | /** 44 | * 45 | * @param domainObjectClass class of instance of the domain object which we should work with 46 | * @param repositories {@link Repositories} 47 | * @param beanFactory {@link BeanFactory} 48 | * @return {@link FormFlowCrudMethods} prepared to work on top of the current domain object 49 | */ 50 | public static FormFlowCrudMethods create(Class domainObjectClass, Repositories repositories, BeanFactory beanFactory) { 51 | Assert.notNull(domainObjectClass.getAnnotation(Entity.class),"Your domain object must be annotaded with @Entity"); 52 | Optional possibleDomainRepository = repositories.getRepositoryFor(domainObjectClass); 53 | 54 | if(possibleDomainRepository.isPresent()) { 55 | Assert.state(possibleDomainRepository.get() instanceof CrudRepository, 56 | "Your repository MUST be a CrudRepository. If you don't like, don't use this flow :)"); 57 | 58 | return new FormFlowCrudMethods(entity -> { 59 | CrudRepository crudRepository = (CrudRepository) possibleDomainRepository.get(); 60 | return crudRepository.save(entity); 61 | }); 62 | } 63 | 64 | 65 | try { 66 | EntityManager manager = beanFactory.getBean(EntityManager.class); 67 | return new FormFlowCrudMethods(entity -> { 68 | manager.persist(entity); 69 | return entity; 70 | 71 | }); 72 | } catch (NoSuchBeanException e) { 73 | throw new RuntimeException("Probably you don't have neither JPA or Spring Data JPA enabled"); 74 | } 75 | 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/formflow/FormFlowManagedEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.formflow; 2 | 3 | public class FormFlowManagedEntity { 4 | 5 | private T entity; 6 | 7 | public FormFlowManagedEntity(T entity) { 8 | this.entity = entity; 9 | } 10 | 11 | public T getEntity() { 12 | return entity; 13 | } 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/formflow/ToModelStep.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.formflow; 2 | 3 | import javax.persistence.Entity; 4 | 5 | import org.springframework.util.Assert; 6 | 7 | /** 8 | * 9 | * @author alberto 10 | * 11 | * @param type of the Domain Object expected to be saved 12 | */ 13 | public class ToModelStep { 14 | 15 | private T domainObject; 16 | private FormFlowCrudMethods crudMethods; 17 | 18 | 19 | public ToModelStep(T domainObject, FormFlowCrudMethods crudMethods) { 20 | Assert.notNull(domainObject.getClass().getAnnotation(Entity.class), 21 | String.format("You must build ToModelStep with @Entity annotated class. %s",domainObject.getClass())); 22 | this.domainObject = domainObject; 23 | this.crudMethods = crudMethods; 24 | } 25 | 26 | public FormFlowManagedEntity save() { 27 | return new FormFlowManagedEntity(crudMethods.save(domainObject)); 28 | } 29 | 30 | public T getDomainObject() { 31 | return domainObject; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/shared/AssertEmptyConstructor.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.shared; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.util.Optional; 5 | import java.util.stream.Stream; 6 | 7 | import org.springframework.util.Assert; 8 | 9 | /** 10 | * Internal class to verify empty constructors 11 | * @author alberto 12 | * 13 | */ 14 | public class AssertEmptyConstructor { 15 | 16 | public static void test(Class klass,String message) { 17 | Optional> constructorWithoutArgs = Stream.of(klass.getConstructors()) 18 | .filter(constructor -> constructor.getParameterCount() == 0).findFirst(); 19 | Assert.isTrue(constructorWithoutArgs.isPresent(),message); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/github/asouza/support/Mutable.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.support; 2 | 3 | import static java.lang.annotation.ElementType.FIELD; 4 | import static java.lang.annotation.ElementType.METHOD; 5 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 6 | 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.Target; 9 | 10 | @Target({METHOD, FIELD}) 11 | @Retention(RUNTIME) 12 | public @interface Mutable { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.github.asouza.autoconfiguration.SpringWebDevAutoconfiguration -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/WithoutEmptyConstructorObject.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza; 2 | 3 | public class WithoutEmptyConstructorObject { 4 | 5 | public WithoutEmptyConstructorObject(String bla) { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/dataview/DataViewTest.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.dataview; 2 | 3 | import java.time.LocalDate; 4 | import java.time.temporal.TemporalAccessor; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import org.junit.jupiter.api.Assertions; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import io.github.asouza.WithoutEmptyConstructorObject; 14 | import io.github.asouza.dataview.DataView; 15 | import io.github.asouza.testsupport.ComplexProperty; 16 | import io.github.asouza.testsupport.ComplexPropertyDTO; 17 | import io.github.asouza.testsupport.Team; 18 | 19 | public class DataViewTest { 20 | 21 | @Test 22 | public void objectMustHaveEmptyConstructor() { 23 | Assertions.assertThrows(IllegalArgumentException.class, 24 | () -> DataView.of(new WithoutEmptyConstructorObject(""))); 25 | } 26 | 27 | @Test 28 | public void methodShouldStartWithGetOrIs() { 29 | Assertions.assertThrows(IllegalArgumentException.class, () -> DataView.of(new Team("")).add(Team::someMethod)); 30 | } 31 | 32 | @Test 33 | public void shouldExportSimpleProperties() { 34 | Team team = new Team("bla"); 35 | team.setProperty2("ble"); 36 | Map result = DataView.of(team).add(Team::getName).add(Team::getProperty2).build(); 37 | Assertions.assertEquals("bla", result.get("name")); 38 | Assertions.assertEquals("ble", result.get("property2")); 39 | } 40 | 41 | @Test 42 | public void shouldExportPropertiesWithCustomkeys() { 43 | Team team = new Team("bla"); 44 | team.setProperty2("ble"); 45 | team.setProperty3(LocalDate.of(2019, 1, 1)); 46 | Map result = DataView.of(team).add(Team::getName).add(Team::getProperty2) 47 | .addDate("date", t -> (TemporalAccessor) t.getProperty3(), "dd/MM/yyyy").build(); 48 | Assertions.assertEquals("bla", result.get("name")); 49 | Assertions.assertEquals("ble", result.get("property2")); 50 | Assertions.assertEquals("01/01/2019", result.get("date")); 51 | } 52 | 53 | @Test 54 | public void shouldExportMappedProperties() { 55 | Team team = new Team("bla"); 56 | team.setProperty2("ble"); 57 | ComplexProperty complexProperty = new ComplexProperty(); 58 | complexProperty.setProperty1("opa"); 59 | team.setProperty3(complexProperty); 60 | Map result = DataView.of(team).add(Team::getName).add(Team::getProperty2) 61 | .add("custom", t -> (ComplexProperty) t.getProperty3(), ComplexPropertyDTO::new).build(); 62 | Assertions.assertEquals("bla", result.get("name")); 63 | Assertions.assertEquals("ble", result.get("property2")); 64 | Assertions.assertEquals("opa", ((ComplexPropertyDTO) result.get("custom")).getProperty1()); 65 | } 66 | 67 | @Test 68 | public void shouldExportMappedCollectionProperties() { 69 | Team team = new Team("bla"); 70 | team.setProperty2("ble"); 71 | ComplexProperty complexProperty = new ComplexProperty(); 72 | complexProperty.setProperty1("opa"); 73 | team.setProperty4(Arrays.asList(complexProperty)); 74 | 75 | Map result = DataView.of(team).add(Team::getName).add(Team::getProperty2) 76 | .addCollection("custom", Team::getProperty4, ComplexPropertyDTO::new).build(); 77 | Assertions.assertEquals("bla", result.get("name")); 78 | Assertions.assertEquals("ble", result.get("property2")); 79 | Assertions.assertEquals("opa", ((List) result.get("custom")).get(0).getProperty1()); 80 | } 81 | 82 | @Test 83 | public void shouldExportMappedCollectionPropertiesWithoutExtraDto() { 84 | Team team = new Team("bla"); 85 | team.setProperty2("ble"); 86 | ComplexProperty complexProperty = new ComplexProperty(); 87 | complexProperty.setProperty1("opa"); 88 | team.setProperty4(Arrays.asList(complexProperty)); 89 | 90 | Map result = DataView.of(team).add(Team::getName).add(Team::getProperty2) 91 | .addCollection2("custom", Team::getProperty4, ComplexProperty::getProperty1).build(); 92 | 93 | Assertions.assertEquals("bla", result.get("name")); 94 | Assertions.assertEquals("ble", result.get("property2")); 95 | Assertions.assertEquals("opa", ((List) result.get("custom")).get(0).get("property1")); 96 | } 97 | 98 | @Test 99 | public void shouldExportMappedPropertiesWithoutExtraDto() { 100 | Team team = new Team("bla"); 101 | team.setProperty2("ble"); 102 | ComplexProperty complexProperty = new ComplexProperty(); 103 | complexProperty.setProperty1("opa"); 104 | team.setProperty3(complexProperty); 105 | Map result = DataView.of(team).add(Team::getName).add(Team::getProperty2) 106 | .add2("custom", t -> (ComplexProperty) team.getProperty3(), ComplexProperty::getProperty1).build(); 107 | Assertions.assertEquals("bla", result.get("name")); 108 | Assertions.assertEquals("ble", result.get("property2")); 109 | Assertions.assertEquals("opa", ((Map) result.get("custom")).get("property1")); 110 | } 111 | 112 | @Test 113 | public void shouldNotMapComplexGettersOfComplexProperies() { 114 | Team team = new Team("bla"); 115 | team.setProperty2("ble"); 116 | ComplexProperty complexProperty = new ComplexProperty(); 117 | complexProperty.setProperty1(new ComplexProperty()); 118 | team.setProperty3(complexProperty); 119 | 120 | Assertions.assertThrows(IllegalArgumentException.class, () -> DataView.of(team).add2("custom", 121 | t -> (ComplexProperty) team.getProperty3(), ComplexProperty::getProperty1)); 122 | } 123 | 124 | @Test 125 | public void shouldNotMapCollectionGettersOfComplexProperies() { 126 | Team team = new Team("bla"); 127 | team.setProperty2("ble"); 128 | ComplexProperty complexProperty = new ComplexProperty(); 129 | complexProperty.setProperty1(new ArrayList<>()); 130 | team.setProperty3(complexProperty); 131 | 132 | Assertions.assertThrows(IllegalArgumentException.class, () -> DataView.of(team).add2("custom", 133 | t -> (ComplexProperty) team.getProperty3(), ComplexProperty::getProperty1)); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/defensiveprogramming/ExecutePreconditionsTest.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.defensiveprogramming; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import io.github.asouza.testsupport.ProtectedEntity; 7 | import io.github.asouza.testsupport.ProtectedEntityWithoutEmptyConstructor; 8 | import io.github.asouza.testsupport.UnprotectedEntity; 9 | 10 | public class ExecutePreconditionsTest { 11 | 12 | @Test 13 | void shouldAddBeanValidationAnnotationAtConstructor() throws Exception { 14 | Assertions.assertThrows(IllegalArgumentException.class, 15 | () -> Preconditions.newInstance(UnprotectedEntity.class, "")); 16 | 17 | } 18 | 19 | @Test 20 | void shouldValidateAnnotatedConstructorArgs() throws Exception { 21 | Assertions.assertThrows(IllegalArgumentException.class, 22 | () -> Preconditions.newInstance(ProtectedEntity.class, "")); 23 | 24 | } 25 | 26 | @Test 27 | void shouldCreateValidAnnotatedConstructorArgs() throws Exception { 28 | ProtectedEntity instance = Preconditions.newInstance(ProtectedEntity.class, "bla bla"); 29 | Assertions.assertEquals("bla bla", instance.getName()); 30 | 31 | } 32 | 33 | @Test 34 | void shouldValidateAnnotatedMethodArgs() throws Exception { 35 | ProtectedEntity instance = Preconditions.newInstance(ProtectedEntity.class, "bla bla"); 36 | Assertions.assertThrows(IllegalArgumentException.class, () -> instance.logic(0)); 37 | 38 | } 39 | 40 | @Test 41 | void shouldExecuteMethodWithValidArgs() throws Exception { 42 | ProtectedEntity instance = Preconditions.newInstance(ProtectedEntity.class, "bla bla"); 43 | instance.logic(2); 44 | 45 | Assertions.assertEquals(2, instance.getValue()); 46 | } 47 | 48 | @Test 49 | void shouldNotCreateProtectedInstanceWithoutEmptyConstructors() throws Exception { 50 | Assertions.assertThrows(IllegalArgumentException.class, 51 | () -> Preconditions.newInstance(ProtectedEntityWithoutEmptyConstructor.class, "bla bla")); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/defensiveprogramming/ImmutableReferenceTest.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.defensiveprogramming; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collection; 5 | 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import io.github.asouza.testsupport.ComplexProperty; 10 | 11 | public class ImmutableReferenceTest { 12 | 13 | @Test 14 | public void shouldNotAllowSetterForImmutableReference() { 15 | ComplexProperty complex = new ComplexProperty(); 16 | complex.setProperty1("bla"); 17 | 18 | ComplexProperty immutable = ImmutableReference.of(complex); 19 | Assertions.assertThrows(IllegalAccessException.class, () -> immutable.setProperty1("change")); 20 | } 21 | 22 | @Test 23 | public void shouldNotAllowMutableAnnnotatedMethodForImmutableReference() { 24 | ComplexProperty complex = new ComplexProperty(); 25 | complex.changeProperty(); 26 | 27 | ComplexProperty immutable = ImmutableReference.of(complex); 28 | Assertions.assertThrows(IllegalAccessException.class, () -> immutable.changeProperty()); 29 | } 30 | 31 | @Test 32 | public void shouldBuildCollectionWithImmutableObjects() { 33 | ComplexProperty complex = new ComplexProperty(); 34 | 35 | Collection list = ImmutableReference.of(Arrays.asList(complex)); 36 | Assertions.assertThrows(IllegalAccessException.class, () -> list.iterator().next().changeProperty()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/formflow/FormFlowCrudMethodsTest.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.formflow; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.ListableBeanFactory; 7 | import org.springframework.data.repository.support.Repositories; 8 | 9 | import io.github.asouza.formflow.FormFlowCrudMethods; 10 | import io.github.asouza.testsupport.FakeCrudRepository; 11 | import io.github.asouza.testsupport.FakeEntityManager; 12 | import io.github.asouza.testsupport.Goal; 13 | import io.github.asouza.testsupport.MyBeanFactory; 14 | import io.github.asouza.testsupport.NonEntity; 15 | import io.github.asouza.testsupport.Team; 16 | import io.github.asouza.testsupport.TestRepositories; 17 | 18 | public class FormFlowCrudMethodsTest { 19 | 20 | private ListableBeanFactory beanFactory; 21 | private Repositories repositories; 22 | private FakeCrudRepository fakeCrudRepository; 23 | private FakeEntityManager fakeEntityManager; 24 | 25 | @BeforeEach 26 | public void setup() { 27 | fakeEntityManager = new FakeEntityManager(); 28 | beanFactory = new MyBeanFactory(fakeEntityManager); 29 | fakeCrudRepository = new FakeCrudRepository(); 30 | repositories = new TestRepositories(beanFactory,fakeCrudRepository,Goal.class); 31 | } 32 | 33 | @Test 34 | public void shouldNotCreateWithNonEntityArg() { 35 | Assertions.assertThrows(IllegalArgumentException.class, 36 | () -> FormFlowCrudMethods.create(NonEntity.class, repositories, beanFactory)); 37 | } 38 | 39 | @Test 40 | public void shouldCreateCrudMethodsForSpringDataJpaRepository() { 41 | Team team = new Team("bla"); 42 | FormFlowCrudMethods crudMethods = FormFlowCrudMethods.create(Team.class, repositories, beanFactory); 43 | crudMethods.save(team); 44 | 45 | Assertions.assertTrue(fakeCrudRepository.isSaveWasCalled()); 46 | } 47 | 48 | @Test 49 | public void shouldCreateCrudMethodsEvenWithoutSpringDataJpaRepository() { 50 | Goal goal = new Goal(); 51 | FormFlowCrudMethods crudMethods = FormFlowCrudMethods.create(Goal.class, repositories, beanFactory); 52 | 53 | crudMethods.save(goal); 54 | 55 | Assertions.assertTrue(fakeEntityManager.isPersistWasCalled()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/formflow/FormFlowTest.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.formflow; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.ListableBeanFactory; 7 | import org.springframework.data.repository.support.Repositories; 8 | 9 | import io.github.asouza.formflow.FormFlow; 10 | import io.github.asouza.formflow.ToModelStep; 11 | import io.github.asouza.testsupport.FormTest; 12 | import io.github.asouza.testsupport.FormTestExtraArgs; 13 | import io.github.asouza.testsupport.FormTestWithToModelReturningNonEntity; 14 | import io.github.asouza.testsupport.FormTestWithToModelWithoutArgs; 15 | import io.github.asouza.testsupport.FormTestWithTwoResolvableTypes; 16 | import io.github.asouza.testsupport.FormTestWithTwoToModels; 17 | import io.github.asouza.testsupport.FormTestWithoutToModel; 18 | import io.github.asouza.testsupport.MyBeanFactory; 19 | import io.github.asouza.testsupport.NonEntity; 20 | import io.github.asouza.testsupport.Team; 21 | import io.github.asouza.testsupport.TestRepositories; 22 | 23 | public class FormFlowTest { 24 | 25 | private FormFlow formFlow; 26 | private FormFlow formFlowNonEntity; 27 | 28 | @BeforeEach 29 | public void setup() { 30 | ListableBeanFactory beanFactory = new MyBeanFactory(); 31 | Repositories repositories = new TestRepositories(beanFactory); 32 | formFlow = new FormFlow<>(beanFactory, repositories); 33 | formFlowNonEntity = new FormFlow<>(beanFactory, repositories); 34 | } 35 | 36 | @Test 37 | public void mustNotHaveMoreThanOneToModelMethod() { 38 | Assertions.assertThrows(IllegalArgumentException.class, () -> formFlow.toModel(new FormTestWithTwoToModels())); 39 | } 40 | 41 | @Test 42 | public void mustNotHaveZeroToModelMethod() { 43 | Assertions.assertThrows(IllegalArgumentException.class, () -> formFlow.toModel(new FormTestWithoutToModel())); 44 | } 45 | 46 | @Test 47 | public void mustPassExtraArgsForApplicationCustomArgs() { 48 | Assertions.assertThrows(RuntimeException.class, () -> formFlow.toModel(new FormTestExtraArgs())); 49 | } 50 | 51 | @Test 52 | public void shouldReturnNewDomainObjectWithToModelWithoutArgs() { 53 | FormTestWithToModelWithoutArgs form = new FormTestWithToModelWithoutArgs(); 54 | form.setName("test"); 55 | ToModelStep toModelResult = formFlow.toModel(form); 56 | 57 | Team team = toModelResult.getDomainObject(); 58 | Assertions.assertEquals("test", team.getName()); 59 | } 60 | 61 | @Test 62 | public void shouldReturnNewDomainObjectWithToModelWithSpringResolvableTypes() { 63 | FormTest form = new FormTest(); 64 | form.setName("test"); 65 | ToModelStep toModelResult = formFlow.toModel(form); 66 | 67 | Team team = toModelResult.getDomainObject(); 68 | Assertions.assertEquals("test", team.getName()); 69 | } 70 | 71 | @Test 72 | public void shouldReturnNewDomainObjectWithToModelWithMoreThanOneSpringResolvableTypes() { 73 | FormTestWithTwoResolvableTypes form = new FormTestWithTwoResolvableTypes(); 74 | form.setName("test"); 75 | ToModelStep toModelResult = formFlow.toModel(form); 76 | 77 | Team team = toModelResult.getDomainObject(); 78 | Assertions.assertEquals("test", team.getName()); 79 | } 80 | 81 | @Test 82 | public void shouldReturnNewDomainObjecWithExtraArgs() { 83 | FormTestExtraArgs form = new FormTestExtraArgs(); 84 | form.setName("test"); 85 | 86 | ToModelStep toModelResult = formFlow.toModel(form, new Team("extraArg")); 87 | 88 | Team team = toModelResult.getDomainObject(); 89 | Assertions.assertEquals("test", team.getName()); 90 | } 91 | 92 | @Test 93 | public void shouldBuildJustTheDomainObjectReturnedByTheToModel() { 94 | FormTestWithToModelReturningNonEntity form = new FormTestWithToModelReturningNonEntity(); 95 | form.setName("test"); 96 | 97 | NonEntity toModelResult = formFlowNonEntity.justBuildDomainObject(form); 98 | 99 | Assertions.assertEquals("test", toModelResult.getName()); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/formflow/ToModelStepTest.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.formflow; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import io.github.asouza.formflow.FormFlowCrudMethods; 7 | import io.github.asouza.formflow.ToModelStep; 8 | import io.github.asouza.testsupport.NonEntity; 9 | import io.github.asouza.testsupport.Team; 10 | 11 | public class ToModelStepTest { 12 | 13 | @Test 14 | public void shouldNotAcceptNonEntityAsConstructorParameter() { 15 | FormFlowCrudMethods crudMethods = new FormFlowCrudMethods((team) -> { 16 | team.setId(1000l); 17 | return team; 18 | }); 19 | 20 | Team newTeam = new Team("bla"); 21 | new ToModelStep<>(newTeam,crudMethods).save(); 22 | Assertions.assertEquals(1000l, newTeam.getId()); 23 | } 24 | 25 | @Test 26 | public void shouldNotAcceptNonEntityConstructorParameter() { 27 | FormFlowCrudMethods crudMethods = new FormFlowCrudMethods((nonEntity) -> { 28 | return nonEntity; 29 | }); 30 | 31 | Assertions.assertThrows(IllegalArgumentException.class, () -> new ToModelStep<>(new NonEntity(), crudMethods)); 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/ComplexProperty.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | import io.github.asouza.support.Mutable; 4 | 5 | public class ComplexProperty { 6 | 7 | private Object property1; 8 | 9 | public void setProperty1(Object property1) { 10 | this.property1 = property1; 11 | } 12 | 13 | public Object getProperty1() { 14 | return property1; 15 | } 16 | 17 | @Mutable 18 | public void changeProperty() { 19 | 20 | } 21 | 22 | @Override 23 | @Mutable 24 | public String toString() { 25 | // TODO Auto-generated method stub 26 | return super.toString(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/ComplexPropertyDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | public class ComplexPropertyDTO { 4 | 5 | private Object property1; 6 | 7 | public ComplexPropertyDTO(ComplexProperty complexProperty) { 8 | this.property1 = complexProperty.getProperty1(); 9 | } 10 | 11 | public Object getProperty1() { 12 | return property1; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/FakeCrudRepository.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | import java.util.Optional; 4 | 5 | import org.springframework.data.repository.CrudRepository; 6 | 7 | public class FakeCrudRepository implements CrudRepository { 8 | 9 | private boolean saveWasCalled; 10 | 11 | public boolean isSaveWasCalled() { 12 | return saveWasCalled; 13 | } 14 | 15 | @Override 16 | public Object save(Object entity) { 17 | saveWasCalled = true; 18 | return entity; 19 | } 20 | 21 | @Override 22 | public Iterable saveAll(Iterable entities) { 23 | // TODO Auto-generated method stub 24 | return null; 25 | } 26 | 27 | @Override 28 | public Optional findById(Object id) { 29 | // TODO Auto-generated method stub 30 | return null; 31 | } 32 | 33 | @Override 34 | public boolean existsById(Object id) { 35 | // TODO Auto-generated method stub 36 | return false; 37 | } 38 | 39 | @Override 40 | public Iterable findAll() { 41 | // TODO Auto-generated method stub 42 | return null; 43 | } 44 | 45 | @Override 46 | public Iterable findAllById(Iterable ids) { 47 | // TODO Auto-generated method stub 48 | return null; 49 | } 50 | 51 | @Override 52 | public long count() { 53 | // TODO Auto-generated method stub 54 | return 0; 55 | } 56 | 57 | @Override 58 | public void deleteById(Object id) { 59 | // TODO Auto-generated method stub 60 | 61 | } 62 | 63 | @Override 64 | public void delete(Object entity) { 65 | // TODO Auto-generated method stub 66 | 67 | } 68 | 69 | @Override 70 | public void deleteAll(Iterable entities) { 71 | // TODO Auto-generated method stub 72 | 73 | } 74 | 75 | @Override 76 | public void deleteAll() { 77 | // TODO Auto-generated method stub 78 | 79 | } 80 | } -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/FakeEntityManager.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import javax.persistence.EntityGraph; 7 | import javax.persistence.EntityManager; 8 | import javax.persistence.EntityManagerFactory; 9 | import javax.persistence.EntityTransaction; 10 | import javax.persistence.FlushModeType; 11 | import javax.persistence.LockModeType; 12 | import javax.persistence.Query; 13 | import javax.persistence.StoredProcedureQuery; 14 | import javax.persistence.TypedQuery; 15 | import javax.persistence.criteria.CriteriaBuilder; 16 | import javax.persistence.criteria.CriteriaDelete; 17 | import javax.persistence.criteria.CriteriaQuery; 18 | import javax.persistence.criteria.CriteriaUpdate; 19 | import javax.persistence.metamodel.Metamodel; 20 | 21 | public class FakeEntityManager implements EntityManager{ 22 | 23 | private boolean persistWasCalled; 24 | 25 | public boolean isPersistWasCalled() { 26 | return persistWasCalled; 27 | } 28 | 29 | @Override 30 | public void persist(Object entity) { 31 | persistWasCalled = true; 32 | } 33 | 34 | @Override 35 | public T merge(T entity) { 36 | // TODO Auto-generated method stub 37 | return null; 38 | } 39 | 40 | @Override 41 | public void remove(Object entity) { 42 | // TODO Auto-generated method stub 43 | 44 | } 45 | 46 | @Override 47 | public T find(Class entityClass, Object primaryKey) { 48 | // TODO Auto-generated method stub 49 | return null; 50 | } 51 | 52 | @Override 53 | public T find(Class entityClass, Object primaryKey, Map properties) { 54 | // TODO Auto-generated method stub 55 | return null; 56 | } 57 | 58 | @Override 59 | public T find(Class entityClass, Object primaryKey, LockModeType lockMode) { 60 | // TODO Auto-generated method stub 61 | return null; 62 | } 63 | 64 | @Override 65 | public T find(Class entityClass, Object primaryKey, LockModeType lockMode, Map properties) { 66 | // TODO Auto-generated method stub 67 | return null; 68 | } 69 | 70 | @Override 71 | public T getReference(Class entityClass, Object primaryKey) { 72 | // TODO Auto-generated method stub 73 | return null; 74 | } 75 | 76 | @Override 77 | public void flush() { 78 | // TODO Auto-generated method stub 79 | 80 | } 81 | 82 | @Override 83 | public void setFlushMode(FlushModeType flushMode) { 84 | // TODO Auto-generated method stub 85 | 86 | } 87 | 88 | @Override 89 | public FlushModeType getFlushMode() { 90 | // TODO Auto-generated method stub 91 | return null; 92 | } 93 | 94 | @Override 95 | public void lock(Object entity, LockModeType lockMode) { 96 | // TODO Auto-generated method stub 97 | 98 | } 99 | 100 | @Override 101 | public void lock(Object entity, LockModeType lockMode, Map properties) { 102 | // TODO Auto-generated method stub 103 | 104 | } 105 | 106 | @Override 107 | public void refresh(Object entity) { 108 | // TODO Auto-generated method stub 109 | 110 | } 111 | 112 | @Override 113 | public void refresh(Object entity, Map properties) { 114 | // TODO Auto-generated method stub 115 | 116 | } 117 | 118 | @Override 119 | public void refresh(Object entity, LockModeType lockMode) { 120 | // TODO Auto-generated method stub 121 | 122 | } 123 | 124 | @Override 125 | public void refresh(Object entity, LockModeType lockMode, Map properties) { 126 | // TODO Auto-generated method stub 127 | 128 | } 129 | 130 | @Override 131 | public void clear() { 132 | // TODO Auto-generated method stub 133 | 134 | } 135 | 136 | @Override 137 | public void detach(Object entity) { 138 | // TODO Auto-generated method stub 139 | 140 | } 141 | 142 | @Override 143 | public boolean contains(Object entity) { 144 | // TODO Auto-generated method stub 145 | return false; 146 | } 147 | 148 | @Override 149 | public LockModeType getLockMode(Object entity) { 150 | // TODO Auto-generated method stub 151 | return null; 152 | } 153 | 154 | @Override 155 | public void setProperty(String propertyName, Object value) { 156 | // TODO Auto-generated method stub 157 | 158 | } 159 | 160 | @Override 161 | public Map getProperties() { 162 | // TODO Auto-generated method stub 163 | return null; 164 | } 165 | 166 | @Override 167 | public Query createQuery(String qlString) { 168 | // TODO Auto-generated method stub 169 | return null; 170 | } 171 | 172 | @Override 173 | public TypedQuery createQuery(CriteriaQuery criteriaQuery) { 174 | // TODO Auto-generated method stub 175 | return null; 176 | } 177 | 178 | @Override 179 | public Query createQuery(CriteriaUpdate updateQuery) { 180 | // TODO Auto-generated method stub 181 | return null; 182 | } 183 | 184 | @Override 185 | public Query createQuery(CriteriaDelete deleteQuery) { 186 | // TODO Auto-generated method stub 187 | return null; 188 | } 189 | 190 | @Override 191 | public TypedQuery createQuery(String qlString, Class resultClass) { 192 | // TODO Auto-generated method stub 193 | return null; 194 | } 195 | 196 | @Override 197 | public Query createNamedQuery(String name) { 198 | // TODO Auto-generated method stub 199 | return null; 200 | } 201 | 202 | @Override 203 | public TypedQuery createNamedQuery(String name, Class resultClass) { 204 | // TODO Auto-generated method stub 205 | return null; 206 | } 207 | 208 | @Override 209 | public Query createNativeQuery(String sqlString) { 210 | // TODO Auto-generated method stub 211 | return null; 212 | } 213 | 214 | @Override 215 | public Query createNativeQuery(String sqlString, Class resultClass) { 216 | // TODO Auto-generated method stub 217 | return null; 218 | } 219 | 220 | @Override 221 | public Query createNativeQuery(String sqlString, String resultSetMapping) { 222 | // TODO Auto-generated method stub 223 | return null; 224 | } 225 | 226 | @Override 227 | public StoredProcedureQuery createNamedStoredProcedureQuery(String name) { 228 | // TODO Auto-generated method stub 229 | return null; 230 | } 231 | 232 | @Override 233 | public StoredProcedureQuery createStoredProcedureQuery(String procedureName) { 234 | // TODO Auto-generated method stub 235 | return null; 236 | } 237 | 238 | @Override 239 | public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) { 240 | // TODO Auto-generated method stub 241 | return null; 242 | } 243 | 244 | @Override 245 | public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) { 246 | // TODO Auto-generated method stub 247 | return null; 248 | } 249 | 250 | @Override 251 | public void joinTransaction() { 252 | // TODO Auto-generated method stub 253 | 254 | } 255 | 256 | @Override 257 | public boolean isJoinedToTransaction() { 258 | // TODO Auto-generated method stub 259 | return false; 260 | } 261 | 262 | @Override 263 | public T unwrap(Class cls) { 264 | // TODO Auto-generated method stub 265 | return null; 266 | } 267 | 268 | @Override 269 | public Object getDelegate() { 270 | // TODO Auto-generated method stub 271 | return null; 272 | } 273 | 274 | @Override 275 | public void close() { 276 | // TODO Auto-generated method stub 277 | 278 | } 279 | 280 | @Override 281 | public boolean isOpen() { 282 | // TODO Auto-generated method stub 283 | return false; 284 | } 285 | 286 | @Override 287 | public EntityTransaction getTransaction() { 288 | // TODO Auto-generated method stub 289 | return null; 290 | } 291 | 292 | @Override 293 | public EntityManagerFactory getEntityManagerFactory() { 294 | // TODO Auto-generated method stub 295 | return null; 296 | } 297 | 298 | @Override 299 | public CriteriaBuilder getCriteriaBuilder() { 300 | // TODO Auto-generated method stub 301 | return null; 302 | } 303 | 304 | @Override 305 | public Metamodel getMetamodel() { 306 | // TODO Auto-generated method stub 307 | return null; 308 | } 309 | 310 | @Override 311 | public EntityGraph createEntityGraph(Class rootType) { 312 | // TODO Auto-generated method stub 313 | return null; 314 | } 315 | 316 | @Override 317 | public EntityGraph createEntityGraph(String graphName) { 318 | // TODO Auto-generated method stub 319 | return null; 320 | } 321 | 322 | @Override 323 | public EntityGraph getEntityGraph(String graphName) { 324 | // TODO Auto-generated method stub 325 | return null; 326 | } 327 | 328 | @Override 329 | public List> getEntityGraphs(Class entityClass) { 330 | // TODO Auto-generated method stub 331 | return null; 332 | } 333 | 334 | } 335 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/FormTest.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | public class FormTest { 4 | 5 | private String name; 6 | 7 | public void setName(String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | public Team toModel(TeamRepository teamRepository) { 16 | return new Team(name); 17 | } 18 | 19 | public void changeName(String name) { 20 | this.name = name; 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/FormTestExtraArgs.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | public class FormTestExtraArgs { 4 | 5 | private String name; 6 | 7 | public void setName(String name) { 8 | this.name = name; 9 | 10 | } 11 | 12 | public Team toModel(TeamRepository teamRepository, TeamRepository2 teamRepository2,Team team) { 13 | return new Team(name); 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/FormTestWithToModelReturningNonEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | public class FormTestWithToModelReturningNonEntity { 4 | 5 | private String name; 6 | 7 | public void setName(String name) { 8 | this.name = name; 9 | 10 | } 11 | 12 | public NonEntity toModel(TeamRepository teamRepository) { 13 | NonEntity nonEntity = new NonEntity(); 14 | nonEntity.setName(name); 15 | return nonEntity; 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/FormTestWithToModelWithoutArgs.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | public class FormTestWithToModelWithoutArgs { 4 | 5 | private String name; 6 | 7 | public void setName(String name) { 8 | this.name = name; 9 | } 10 | 11 | public Team toModel() { 12 | return new Team(name); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/FormTestWithTwoResolvableTypes.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | public class FormTestWithTwoResolvableTypes { 4 | 5 | private String name; 6 | 7 | public void setName(String name) { 8 | this.name = name; 9 | 10 | } 11 | 12 | public Team toModel(TeamRepository teamRepository,TeamRepository2 teamRepository2) { 13 | return new Team(name); 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/FormTestWithTwoToModels.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | public class FormTestWithTwoToModels { 4 | 5 | private String name; 6 | 7 | public void setName(String name) { 8 | this.name = name; 9 | 10 | } 11 | 12 | public Team toModel(TeamRepository teamRepository) { 13 | return new Team(name); 14 | } 15 | 16 | public Team toModel() { 17 | return new Team("bla"); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/FormTestWithoutToModel.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | public class FormTestWithoutToModel { 4 | 5 | } -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/Goal.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | import javax.persistence.Entity; 4 | 5 | @Entity 6 | public class Goal { 7 | 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/MyBeanFactory.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.util.Collection; 5 | import java.util.Map; 6 | import java.util.Optional; 7 | 8 | import javax.persistence.EntityManager; 9 | 10 | import org.springframework.beans.BeansException; 11 | import org.springframework.beans.factory.ListableBeanFactory; 12 | import org.springframework.beans.factory.NoSuchBeanDefinitionException; 13 | import org.springframework.beans.factory.ObjectProvider; 14 | import org.springframework.core.ResolvableType; 15 | 16 | public class MyBeanFactory implements ListableBeanFactory { 17 | 18 | private FakeEntityManager fakeManager; 19 | 20 | public MyBeanFactory() { 21 | this.fakeManager = new FakeEntityManager(); 22 | } 23 | 24 | public MyBeanFactory(FakeEntityManager fakeManager) { 25 | this.fakeManager = fakeManager; 26 | 27 | } 28 | 29 | @Override 30 | public Object getBean(String name) throws BeansException { 31 | // TODO Auto-generated method stub 32 | return null; 33 | } 34 | 35 | @Override 36 | public T getBean(String name, Class requiredType) throws BeansException { 37 | // TODO Auto-generated method stub 38 | return null; 39 | } 40 | 41 | @Override 42 | public Object getBean(String name, Object... args) throws BeansException { 43 | // TODO Auto-generated method stub 44 | return null; 45 | } 46 | 47 | @Override 48 | public T getBean(Class requiredType) throws BeansException { 49 | if (!TeamRepository.class.isAssignableFrom(requiredType) 50 | && !TeamRepository2.class.isAssignableFrom(requiredType) && !EntityManager.class.isAssignableFrom(requiredType)) { 51 | throw new NoSuchBeanDefinitionException(requiredType); 52 | } 53 | 54 | if (TeamRepository.class.isAssignableFrom(requiredType)) { 55 | return (T) new TeamRepository() { 56 | 57 | @Override 58 | public Iterable saveAll(Iterable entities) { 59 | // TODO Auto-generated method stub 60 | return null; 61 | } 62 | 63 | @Override 64 | public S save(S entity) { 65 | // TODO Auto-generated method stub 66 | return null; 67 | } 68 | 69 | @Override 70 | public Optional findById(Long id) { 71 | // TODO Auto-generated method stub 72 | return null; 73 | } 74 | 75 | @Override 76 | public Iterable findAllById(Iterable ids) { 77 | // TODO Auto-generated method stub 78 | return null; 79 | } 80 | 81 | @Override 82 | public boolean existsById(Long id) { 83 | // TODO Auto-generated method stub 84 | return false; 85 | } 86 | 87 | @Override 88 | public void deleteById(Long id) { 89 | // TODO Auto-generated method stub 90 | 91 | } 92 | 93 | @Override 94 | public void deleteAll(Iterable entities) { 95 | // TODO Auto-generated method stub 96 | 97 | } 98 | 99 | @Override 100 | public void deleteAll() { 101 | // TODO Auto-generated method stub 102 | 103 | } 104 | 105 | @Override 106 | public void delete(Team entity) { 107 | // TODO Auto-generated method stub 108 | 109 | } 110 | 111 | @Override 112 | public long count() { 113 | // TODO Auto-generated method stub 114 | return 0; 115 | } 116 | 117 | @Override 118 | public Collection findAll() { 119 | // TODO Auto-generated method stub 120 | return null; 121 | } 122 | }; 123 | } 124 | 125 | if(EntityManager.class.isAssignableFrom(requiredType)) { 126 | return (T)this.fakeManager; 127 | } 128 | 129 | return (T)new TeamRepository2() { 130 | 131 | @Override 132 | public Iterable saveAll(Iterable entities) { 133 | // TODO Auto-generated method stub 134 | return null; 135 | } 136 | 137 | @Override 138 | public S save(S entity) { 139 | // TODO Auto-generated method stub 140 | return null; 141 | } 142 | 143 | @Override 144 | public Optional findById(Long id) { 145 | // TODO Auto-generated method stub 146 | return null; 147 | } 148 | 149 | @Override 150 | public Iterable findAllById(Iterable ids) { 151 | // TODO Auto-generated method stub 152 | return null; 153 | } 154 | 155 | @Override 156 | public Iterable findAll() { 157 | // TODO Auto-generated method stub 158 | return null; 159 | } 160 | 161 | @Override 162 | public boolean existsById(Long id) { 163 | // TODO Auto-generated method stub 164 | return false; 165 | } 166 | 167 | @Override 168 | public void deleteById(Long id) { 169 | // TODO Auto-generated method stub 170 | 171 | } 172 | 173 | @Override 174 | public void deleteAll(Iterable entities) { 175 | // TODO Auto-generated method stub 176 | 177 | } 178 | 179 | @Override 180 | public void deleteAll() { 181 | // TODO Auto-generated method stub 182 | 183 | } 184 | 185 | @Override 186 | public void delete(Team entity) { 187 | // TODO Auto-generated method stub 188 | 189 | } 190 | 191 | @Override 192 | public long count() { 193 | // TODO Auto-generated method stub 194 | return 0; 195 | } 196 | }; 197 | 198 | } 199 | 200 | @Override 201 | public T getBean(Class requiredType, Object... args) throws BeansException { 202 | // TODO Auto-generated method stub 203 | return null; 204 | } 205 | 206 | @Override 207 | public ObjectProvider getBeanProvider(Class requiredType) { 208 | // TODO Auto-generated method stub 209 | return null; 210 | } 211 | 212 | @Override 213 | public ObjectProvider getBeanProvider(ResolvableType requiredType) { 214 | // TODO Auto-generated method stub 215 | return null; 216 | } 217 | 218 | @Override 219 | public boolean containsBean(String name) { 220 | // TODO Auto-generated method stub 221 | return false; 222 | } 223 | 224 | @Override 225 | public boolean isSingleton(String name) throws NoSuchBeanDefinitionException { 226 | // TODO Auto-generated method stub 227 | return false; 228 | } 229 | 230 | @Override 231 | public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { 232 | // TODO Auto-generated method stub 233 | return false; 234 | } 235 | 236 | @Override 237 | public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException { 238 | // TODO Auto-generated method stub 239 | return false; 240 | } 241 | 242 | @Override 243 | public boolean isTypeMatch(String name, Class typeToMatch) throws NoSuchBeanDefinitionException { 244 | // TODO Auto-generated method stub 245 | return false; 246 | } 247 | 248 | @Override 249 | public Class getType(String name) throws NoSuchBeanDefinitionException { 250 | // TODO Auto-generated method stub 251 | return null; 252 | } 253 | 254 | @Override 255 | public Class getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException { 256 | // TODO Auto-generated method stub 257 | return null; 258 | } 259 | 260 | @Override 261 | public String[] getAliases(String name) { 262 | // TODO Auto-generated method stub 263 | return null; 264 | } 265 | 266 | @Override 267 | public boolean containsBeanDefinition(String beanName) { 268 | // TODO Auto-generated method stub 269 | return false; 270 | } 271 | 272 | @Override 273 | public int getBeanDefinitionCount() { 274 | // TODO Auto-generated method stub 275 | return 0; 276 | } 277 | 278 | @Override 279 | public String[] getBeanDefinitionNames() { 280 | // TODO Auto-generated method stub 281 | return null; 282 | } 283 | 284 | @Override 285 | public String[] getBeanNamesForType(ResolvableType type) { 286 | // TODO Auto-generated method stub 287 | return null; 288 | } 289 | 290 | @Override 291 | public String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { 292 | // TODO Auto-generated method stub 293 | return null; 294 | } 295 | 296 | @Override 297 | public String[] getBeanNamesForType(Class type) { 298 | // TODO Auto-generated method stub 299 | return null; 300 | } 301 | 302 | @Override 303 | public String[] getBeanNamesForType(Class type, boolean includeNonSingletons, boolean allowEagerInit) { 304 | return new String[] {}; 305 | } 306 | 307 | @Override 308 | public Map getBeansOfType(Class type) throws BeansException { 309 | // TODO Auto-generated method stub 310 | return null; 311 | } 312 | 313 | @Override 314 | public Map getBeansOfType(Class type, boolean includeNonSingletons, boolean allowEagerInit) 315 | throws BeansException { 316 | // TODO Auto-generated method stub 317 | return null; 318 | } 319 | 320 | @Override 321 | public String[] getBeanNamesForAnnotation(Class annotationType) { 322 | // TODO Auto-generated method stub 323 | return null; 324 | } 325 | 326 | @Override 327 | public Map getBeansWithAnnotation(Class annotationType) 328 | throws BeansException { 329 | // TODO Auto-generated method stub 330 | return null; 331 | } 332 | 333 | @Override 334 | public A findAnnotationOnBean(String beanName, Class annotationType) 335 | throws NoSuchBeanDefinitionException { 336 | // TODO Auto-generated method stub 337 | return null; 338 | } 339 | 340 | } -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/NonEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | public class NonEntity { 4 | 5 | private String name; 6 | 7 | public void setName(String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/ProtectedEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | import javax.validation.constraints.Min; 4 | import javax.validation.constraints.NotBlank; 5 | 6 | public class ProtectedEntity { 7 | 8 | private @NotBlank String name; 9 | private @Min(1) Integer value; 10 | 11 | public ProtectedEntity() { 12 | // TODO Auto-generated constructor stub 13 | } 14 | 15 | public ProtectedEntity(@NotBlank String name) { 16 | this.name = name; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public void logic(@Min(1) Integer value) { 24 | this.value = value; 25 | 26 | } 27 | 28 | public Integer getValue() { 29 | return value; 30 | } 31 | } -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/ProtectedEntityWithoutEmptyConstructor.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | public class ProtectedEntityWithoutEmptyConstructor { 6 | 7 | public ProtectedEntityWithoutEmptyConstructor(@NotBlank String name) { 8 | } 9 | 10 | } -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/Team.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | import java.util.List; 4 | 5 | import javax.persistence.Entity; 6 | 7 | @Entity 8 | public class Team { 9 | 10 | private String name; 11 | private Long id; 12 | private Object property2; 13 | private Object property3; 14 | private List property4; 15 | 16 | @Deprecated 17 | public Team() { 18 | } 19 | 20 | public Team(String name) { 21 | this.name = name; 22 | } 23 | 24 | public void setProperty2(Object property2) { 25 | this.property2 = property2; 26 | } 27 | 28 | public Object getProperty2() { 29 | return property2; 30 | } 31 | 32 | public void setId(Long id) { 33 | this.id = id; 34 | } 35 | 36 | public Long getId() { 37 | return id; 38 | } 39 | 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public void setProperty3(Object property3) { 45 | this.property3 = property3; 46 | 47 | } 48 | 49 | public Object getProperty3() { 50 | return property3; 51 | } 52 | 53 | public void setProperty4(List property4) { 54 | this.property4 = property4; 55 | 56 | } 57 | 58 | 59 | public List getProperty4() { 60 | return property4; 61 | } 62 | 63 | 64 | public String someMethod() { 65 | return ""; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/TeamRepository.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface TeamRepository extends CrudRepository{ 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/TeamRepository2.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface TeamRepository2 extends CrudRepository{ 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/TestRepositories.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | import java.util.HashSet; 4 | import java.util.Optional; 5 | import java.util.Set; 6 | import java.util.stream.Collectors; 7 | import java.util.stream.Stream; 8 | 9 | import org.springframework.beans.factory.ListableBeanFactory; 10 | import org.springframework.data.repository.CrudRepository; 11 | import org.springframework.data.repository.support.Repositories; 12 | 13 | public class TestRepositories extends Repositories { 14 | 15 | private CrudRepository customCrudRepository; 16 | private Set nonRepositoryClasses = new HashSet<>(); 17 | 18 | public TestRepositories(ListableBeanFactory factory) { 19 | super(factory); 20 | this.customCrudRepository = new FakeCrudRepository(); 21 | } 22 | 23 | public TestRepositories(ListableBeanFactory factory,CrudRepository customCrudRepository,Class... nonRepositoryClasses) { 24 | super(factory); 25 | this.customCrudRepository = customCrudRepository; 26 | this.nonRepositoryClasses.addAll(Stream.of(nonRepositoryClasses).collect(Collectors.toSet())); 27 | } 28 | 29 | @Override 30 | public Optional getRepositoryFor(Class domainClass) { 31 | if(nonRepositoryClasses.contains(domainClass)) { 32 | return Optional.empty(); 33 | } 34 | return Optional.of(customCrudRepository); 35 | } 36 | } -------------------------------------------------------------------------------- /src/test/java/io/github/asouza/testsupport/UnprotectedEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.asouza.testsupport; 2 | 3 | public class UnprotectedEntity { 4 | public UnprotectedEntity(String name) { 5 | 6 | } 7 | } --------------------------------------------------------------------------------