Skip to content

Commit a23df1f

Browse files
committed
FINERACT-2675: Fix template endpoint
1 parent 9c03d9f commit a23df1f

8 files changed

Lines changed: 358 additions & 13 deletions

File tree

fineract-provider/src/main/java/org/apache/fineract/template/api/TemplatesApiResource.java

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020

2121
import io.swagger.v3.oas.annotations.Operation;
2222
import io.swagger.v3.oas.annotations.Parameter;
23+
import io.swagger.v3.oas.annotations.media.Content;
24+
import io.swagger.v3.oas.annotations.media.Schema;
2325
import io.swagger.v3.oas.annotations.parameters.RequestBody;
26+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
2427
import io.swagger.v3.oas.annotations.tags.Tag;
2528
import jakarta.validation.Valid;
2629
import jakarta.ws.rs.Consumes;
@@ -41,6 +44,7 @@
4144
import java.util.List;
4245
import java.util.Map;
4346
import java.util.function.Supplier;
47+
import java.util.stream.Stream;
4448
import lombok.RequiredArgsConstructor;
4549
import org.apache.fineract.command.core.CommandDispatcher;
4650
import org.apache.fineract.template.command.TemplateCreateCommand;
@@ -51,6 +55,8 @@
5155
import org.apache.fineract.template.data.TemplateData;
5256
import org.apache.fineract.template.data.TemplateDeleteRequest;
5357
import org.apache.fineract.template.data.TemplateDeleteResponse;
58+
import org.apache.fineract.template.data.TemplateDetailsData;
59+
import org.apache.fineract.template.data.TemplateItemData;
5460
import org.apache.fineract.template.data.TemplateUpdateRequest;
5561
import org.apache.fineract.template.data.TemplateUpdateResponse;
5662
import org.apache.fineract.template.domain.TemplateEntity;
@@ -97,7 +103,7 @@ public List<TemplateData> retrieveAllTemplates(
97103
@DefaultValue("-1") @QueryParam("typeId") @Parameter(description = "typeId") final int typeId,
98104
@DefaultValue("-1") @QueryParam("entityId") @Parameter(description = "entityId") final int entityId) {
99105
if (typeId != -1 && entityId != -1) {
100-
return templateService.getAllByEntityAndType(TemplateEntity.values()[entityId], TemplateType.values()[typeId]);
106+
return templateService.getAllByEntityAndType(findTemplateEntity(entityId), findTemplateType(typeId));
101107
} else {
102108
return templateService.getAll();
103109
}
@@ -117,11 +123,9 @@ public List<TemplateData> retrieveAllTemplates(
117123
Example Request:
118124
119125
templates/template
120-
""")
121-
public TemplateData retrieveTemplateDetails() {
122-
// TODO: why?!? The original code was also limited to return only the ID attribute...
123-
// which we don't have; the parser will remove all null values
124-
return new TemplateData();
126+
""", responses = @ApiResponse(responseCode = "default", content = @Content(schema = @Schema(implementation = TemplateData.class))))
127+
public TemplateDetailsData retrieveTemplateDetails() {
128+
return templateDetails(null);
125129
}
126130

127131
@GET
@@ -136,8 +140,9 @@ public TemplateData retrieveOneTemplate(@PathParam("templateId") @Parameter(desc
136140

137141
@GET
138142
@Path("{templateId}/template")
139-
public TemplateData retrieveTemplateById(@PathParam("templateId") final Long templateId) {
140-
return templateService.findOneById(templateId);
143+
@Operation(responses = @ApiResponse(responseCode = "default", content = @Content(schema = @Schema(implementation = TemplateData.class))))
144+
public TemplateDetailsData retrieveTemplateById(@PathParam("templateId") final Long templateId) {
145+
return templateDetails(templateService.findOneById(templateId));
141146
}
142147

143148
@POST
@@ -207,4 +212,28 @@ public String mergeTemplate(@PathParam("templateId") @Parameter(description = "t
207212

208213
return this.templateMergeService.compile(template, parametersMap);
209214
}
215+
216+
private TemplateDetailsData templateDetails(final TemplateData template) {
217+
return TemplateDetailsData.builder().entities(retrieveEntities()).types(retrieveTypes()).template(template).build();
218+
}
219+
220+
private List<TemplateItemData> retrieveEntities() {
221+
return Stream.of(TemplateEntity.values())
222+
.map(entity -> TemplateItemData.builder().id(entity.getId()).name(entity.getName()).build()).toList();
223+
}
224+
225+
private List<TemplateItemData> retrieveTypes() {
226+
return Stream.of(TemplateType.values()).map(type -> TemplateItemData.builder().id(type.getId()).name(type.getName()).build())
227+
.toList();
228+
}
229+
230+
private TemplateEntity findTemplateEntity(final int entityId) {
231+
return Stream.of(TemplateEntity.values()).filter(entity -> entity.getId() == entityId).findFirst()
232+
.orElseThrow(() -> new IllegalArgumentException("Invalid template entity id: " + entityId));
233+
}
234+
235+
private TemplateType findTemplateType(final int typeId) {
236+
return Stream.of(TemplateType.values()).filter(type -> type.getId() == typeId).findFirst()
237+
.orElseThrow(() -> new IllegalArgumentException("Invalid template type id: " + typeId));
238+
}
210239
}

fineract-provider/src/main/java/org/apache/fineract/template/data/TemplateData.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.apache.fineract.template.data;
2020

21+
import io.swagger.v3.oas.annotations.media.Schema;
2122
import java.io.Serial;
2223
import java.io.Serializable;
2324
import java.util.List;
@@ -38,7 +39,9 @@ public final class TemplateData implements Serializable {
3839
private Long id;
3940
private String name;
4041
private String text;
41-
private Integer entity;
42-
private Integer type;
42+
@Schema(implementation = Integer.class)
43+
private String entity;
44+
@Schema(implementation = Integer.class)
45+
private String type;
4346
private List<TemplateMapperData> mappers;
4447
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.template.data;
20+
21+
import java.io.Serial;
22+
import java.io.Serializable;
23+
import java.util.List;
24+
import lombok.AllArgsConstructor;
25+
import lombok.Builder;
26+
import lombok.Data;
27+
import lombok.NoArgsConstructor;
28+
29+
@Builder
30+
@Data
31+
@NoArgsConstructor
32+
@AllArgsConstructor
33+
public final class TemplateDetailsData implements Serializable {
34+
35+
@Serial
36+
private static final long serialVersionUID = 1L;
37+
38+
private List<TemplateItemData> entities;
39+
private List<TemplateItemData> types;
40+
private TemplateData template;
41+
}

fineract-provider/src/main/java/org/apache/fineract/template/mapper/TemplateMapper.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@
2323
import org.apache.fineract.template.data.TemplateData;
2424
import org.apache.fineract.template.domain.Template;
2525
import org.mapstruct.Mapper;
26+
import org.mapstruct.Mapping;
2627

2728
@Mapper(config = MapstructMapperConfig.class, uses = { TemplateMapperMapper.class })
2829
public interface TemplateMapper {
2930

31+
@Mapping(target = "entity", expression = "java(source.getEntity() == null ? null : source.getEntity().getName())")
32+
@Mapping(target = "type", expression = "java(source.getType() == null ? null : source.getType().getName())")
3033
TemplateData map(Template source);
3134

3235
List<TemplateData> map(List<Template> source);

fineract-provider/src/main/java/org/apache/fineract/template/service/TemplateDomainServiceImpl.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public class TemplateDomainServiceImpl implements TemplateDomainService {
5555

5656
@Override
5757
public List<TemplateData> getAll() {
58-
return templateRepository.findAll().stream().map(template -> TemplateData.builder().id(template.getId()).build()).toList();
58+
return templateMapper.map(templateRepository.findAll());
5959
}
6060

6161
@Override
@@ -127,7 +127,6 @@ public TemplateDeleteResponse removeTemplate(TemplateDeleteRequest request) {
127127

128128
@Override
129129
public List<TemplateData> getAllByEntityAndType(final TemplateEntity entity, final TemplateType type) {
130-
return templateRepository.findByEntityAndType(entity, type).stream()
131-
.map(template -> TemplateData.builder().id(template.getId()).build()).toList();
130+
return templateMapper.map(templateRepository.findByEntityAndType(entity, type));
132131
}
133132
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.template.api;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
import static org.mockito.Mockito.verify;
23+
import static org.mockito.Mockito.when;
24+
25+
import java.util.List;
26+
import org.apache.fineract.command.core.CommandDispatcher;
27+
import org.apache.fineract.template.data.TemplateData;
28+
import org.apache.fineract.template.data.TemplateDetailsData;
29+
import org.apache.fineract.template.data.TemplateItemData;
30+
import org.apache.fineract.template.data.TemplateMapperData;
31+
import org.apache.fineract.template.domain.TemplateEntity;
32+
import org.apache.fineract.template.domain.TemplateType;
33+
import org.apache.fineract.template.service.TemplateDomainService;
34+
import org.apache.fineract.template.service.TemplateMergeServiceImpl;
35+
import org.junit.jupiter.api.Test;
36+
import org.junit.jupiter.api.extension.ExtendWith;
37+
import org.mockito.InjectMocks;
38+
import org.mockito.Mock;
39+
import org.mockito.junit.jupiter.MockitoExtension;
40+
41+
@ExtendWith(MockitoExtension.class)
42+
class TemplatesApiResourceTest {
43+
44+
@Mock
45+
private TemplateDomainService templateService;
46+
47+
@Mock
48+
private TemplateMergeServiceImpl templateMergeService;
49+
50+
@Mock
51+
private CommandDispatcher dispatcher;
52+
53+
@InjectMocks
54+
private TemplatesApiResource resource;
55+
56+
@Test
57+
void retrieveAllTemplatesFiltersByApiTypeAndEntityIds() {
58+
TemplateData template = TemplateData.builder().id(3L).name("sms-template").entity("loan").type("SMS").text("hello")
59+
.mappers(List.of(TemplateMapperData.builder().mapperorder(0).mapperkey("loan").mappervalue("loans/{{loanId}}").build()))
60+
.build();
61+
when(templateService.getAllByEntityAndType(TemplateEntity.LOAN, TemplateType.SMS)).thenReturn(List.of(template));
62+
63+
List<TemplateData> response = resource.retrieveAllTemplates(2, 1);
64+
65+
verify(templateService).getAllByEntityAndType(TemplateEntity.LOAN, TemplateType.SMS);
66+
assertThat(response).hasSize(1);
67+
assertThat(response.getFirst().getId()).isEqualTo(3L);
68+
assertThat(response.getFirst().getName()).isEqualTo("sms-template");
69+
assertThat(response.getFirst().getEntity()).isEqualTo("loan");
70+
assertThat(response.getFirst().getType()).isEqualTo("SMS");
71+
assertThat(response.getFirst().getText()).isEqualTo("hello");
72+
assertThat(response.getFirst().getMappers()).hasSize(1);
73+
}
74+
75+
@Test
76+
void retrieveTemplateByIdReturnsEditWrapperWithOptionsAndTemplate() {
77+
TemplateData template = template();
78+
when(templateService.findOneById(2L)).thenReturn(template);
79+
80+
TemplateDetailsData response = resource.retrieveTemplateById(2L);
81+
82+
assertThat(response.getTemplate()).isSameAs(template);
83+
assertThat(response.getTemplate().getEntity()).isEqualTo("loan");
84+
assertThat(response.getTemplate().getType()).isEqualTo("Document");
85+
assertThat(response.getTemplate().getMappers()).hasSize(1);
86+
assertThat(response.getEntities()).extracting(TemplateItemData::getName).containsExactly("client", "loan");
87+
assertThat(response.getTypes()).extracting(TemplateItemData::getName).containsExactly("Document", "SMS");
88+
assertThat(response.getTypes()).extracting(TemplateItemData::getId).containsExactly(0, 2);
89+
}
90+
91+
@Test
92+
void retrieveTemplateDetailsReturnsCreateWrapperOptions() {
93+
TemplateDetailsData response = resource.retrieveTemplateDetails();
94+
95+
assertThat(response.getTemplate()).isNull();
96+
assertThat(response.getEntities()).extracting(TemplateItemData::getName).containsExactly("client", "loan");
97+
assertThat(response.getTypes()).extracting(TemplateItemData::getName).containsExactly("Document", "SMS");
98+
}
99+
100+
@Test
101+
void retrieveOneTemplateKeepsRawTemplateResponse() {
102+
TemplateData template = template();
103+
when(templateService.findOneById(2L)).thenReturn(template);
104+
105+
TemplateData response = resource.retrieveOneTemplate(2L);
106+
107+
assertThat(response).isSameAs(template);
108+
assertThat(response.getEntity()).isEqualTo("loan");
109+
assertThat(response.getType()).isEqualTo("Document");
110+
}
111+
112+
private TemplateData template() {
113+
return TemplateData.builder().id(2L).name("anvay").entity("loan").type("Document").text("<p>anything</p>")
114+
.mappers(List.of(TemplateMapperData.builder().mapperorder(0).mapperkey("loan")
115+
.mappervalue("loans/{{loanId}}?associations=all&tenantIdentifier=default").build()))
116+
.build();
117+
}
118+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.template.mapper;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
23+
import java.util.List;
24+
import org.apache.fineract.template.data.TemplateData;
25+
import org.apache.fineract.template.domain.Template;
26+
import org.apache.fineract.template.domain.TemplateEntity;
27+
import org.apache.fineract.template.domain.TemplateType;
28+
import org.junit.jupiter.api.Test;
29+
30+
class TemplateMapperTest {
31+
32+
private final TemplateMapper mapper = new TemplateMapperImpl(new TemplateMapperMapperImpl());
33+
34+
@Test
35+
void mapsTemplateEntityAndTypeAsApiNames() {
36+
Template template = new Template().setName("anvay").setEntity(TemplateEntity.LOAN).setType(TemplateType.DOCUMENT)
37+
.setText("<p>anything</p>").setMappers(List.of(new org.apache.fineract.template.domain.TemplateMapper(0, "loan",
38+
"loans/{{loanId}}?associations=all&tenantIdentifier=default")));
39+
template.setId(2L);
40+
41+
TemplateData response = mapper.map(template);
42+
43+
assertThat(response.getId()).isEqualTo(2L);
44+
assertThat(response.getName()).isEqualTo("anvay");
45+
assertThat(response.getEntity()).isEqualTo("loan");
46+
assertThat(response.getType()).isEqualTo("Document");
47+
assertThat(response.getText()).isEqualTo("<p>anything</p>");
48+
assertThat(response.getMappers()).hasSize(1);
49+
assertThat(response.getMappers().getFirst().getMapperorder()).isZero();
50+
assertThat(response.getMappers().getFirst().getMapperkey()).isEqualTo("loan");
51+
assertThat(response.getMappers().getFirst().getMappervalue())
52+
.isEqualTo("loans/{{loanId}}?associations=all&tenantIdentifier=default");
53+
}
54+
}

0 commit comments

Comments
 (0)