Skip to content

Commit 795a820

Browse files
christophechevalierjcabannes
authored andcommitted
feat(dlvp): adapt DynamicListRoutePlugin to stateless RoutePlugin interface
1 parent 5517a68 commit 795a820

File tree

4 files changed

+52
-52
lines changed

4 files changed

+52
-52
lines changed

dlvp/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ The route plugin exposes a dynamic endpoint that returns structured elements fro
4545

4646
```yaml
4747
routes:
48-
- name: dynamic-list
48+
- type: dynamic-list
4949
route: /api/something
5050
url: https://external-api.com/api/personnes
5151
method: GET
@@ -156,7 +156,7 @@ entities:
156156

157157
- The plugin consists of two types:
158158
- **Validation plugin** (`type: dynamic-list`): validates that a value is included in the list fetched from the external API. Only the `value` field of each element is used for comparison.
159-
- **Route plugin** (`name: dynamic-list`): exposes a configurable endpoint (via `route`) returning structured elements as a paginated `Page<DynamicListEntry>` response, where `DynamicListEntry` is a record with `label` and `value` fields.
159+
- **Route plugin** (`type: dynamic-list`): exposes a configurable endpoint (via `route`) returning structured elements as a paginated `Page<DynamicListEntry>` response, where `DynamicListEntry` is a record with `label` and `value` fields.
160160

161161
- Both plugins share a common HTTP service (`HttpService`) that sends an HTTP request to the configured `url` with optional `headers` and `body`, and returns the raw response body.
162162

dlvp/src/main/java/io/github/linagora/linid/im/dlvp/DynamicListRoutePlugin.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@
3333
import io.github.linagora.linid.im.corelib.plugin.authorization.AuthorizationFactory;
3434
import io.github.linagora.linid.im.corelib.plugin.config.JinjaService;
3535
import io.github.linagora.linid.im.corelib.plugin.config.dto.EntityConfiguration;
36+
import io.github.linagora.linid.im.corelib.plugin.config.dto.RouteConfiguration;
3637
import io.github.linagora.linid.im.corelib.plugin.entity.DynamicEntity;
37-
import io.github.linagora.linid.im.corelib.plugin.route.AbstractRoutePlugin;
3838
import io.github.linagora.linid.im.corelib.plugin.route.RouteDescription;
39+
import io.github.linagora.linid.im.corelib.plugin.route.RoutePlugin;
3940
import io.github.linagora.linid.im.corelib.plugin.task.TaskEngine;
4041
import io.github.linagora.linid.im.corelib.plugin.task.TaskExecutionContext;
4142
import io.github.linagora.linid.im.dlvp.model.DynamicListConfiguration;
@@ -66,7 +67,7 @@
6667
*/
6768
@Slf4j
6869
@Component
69-
public class DynamicListRoutePlugin extends AbstractRoutePlugin implements DynamicListSupport {
70+
public class DynamicListRoutePlugin implements RoutePlugin, DynamicListSupport {
7071

7172
/** I18n key for missing configuration option errors. */
7273
private static final String MISSING_OPTION = "error.plugin.default.missing.option";
@@ -114,8 +115,9 @@ public boolean supports(final @NonNull String type) {
114115
}
115116

116117
@Override
117-
public List<RouteDescription> getRoutes(final List<EntityConfiguration> entities) {
118-
String route = getConfiguration().getOption("route")
118+
public List<RouteDescription> getRoutes(final RouteConfiguration configuration,
119+
final List<EntityConfiguration> entities) {
120+
String route = configuration.getOption("route")
119121
.orElseThrow(() -> new ApiException(
120122
500,
121123
I18nMessage.of(MISSING_OPTION, Map.of(OPTION, "route"))
@@ -124,11 +126,12 @@ public List<RouteDescription> getRoutes(final List<EntityConfiguration> entities
124126
}
125127

126128
@Override
127-
public boolean match(final String url, final String method) {
128-
if (getConfiguration() == null) {
129+
public boolean match(final RouteConfiguration configuration,
130+
final String url, final String method) {
131+
if (configuration == null) {
129132
return false;
130133
}
131-
String route = getConfiguration().getOption("route")
134+
String route = configuration.getOption("route")
132135
.orElseThrow(() -> new ApiException(
133136
500,
134137
I18nMessage.of(MISSING_OPTION, Map.of(OPTION, "route"))
@@ -137,20 +140,21 @@ public boolean match(final String url, final String method) {
137140
}
138141

139142
@Override
140-
public ResponseEntity<Page<DynamicListEntry>> execute(final HttpServletRequest request) {
141-
String route = getConfiguration().getOption("route")
143+
public ResponseEntity<Page<DynamicListEntry>> execute(final RouteConfiguration configuration,
144+
final HttpServletRequest request) {
145+
String route = configuration.getOption("route")
142146
.orElseThrow(() -> new ApiException(
143147
500,
144148
I18nMessage.of(MISSING_OPTION, Map.of(OPTION, "route"))
145149
));
146150
log.info("Receiving request on dynamic-list route '{}'", route);
147151

148152
DynamicListConfiguration dlConfig = mapper.convertValue(
149-
getConfiguration().getOptions(), new TypeReference<>() {
153+
configuration.getOptions(), new TypeReference<>() {
150154
});
151155

152156
TaskExecutionContext context = new TaskExecutionContext();
153-
DynamicEntity entity = buildDynamicEntity(getConfiguration());
157+
DynamicEntity entity = buildDynamicEntity(configuration);
154158

155159
var authorizationPlugin = authorizationFactory.getAuthorizationPlugin();
156160

@@ -160,7 +164,7 @@ public ResponseEntity<Page<DynamicListEntry>> execute(final HttpServletRequest r
160164

161165
taskEngine.execute(entity, context, "beforeDynamicList");
162166

163-
String response = httpService.request(context, getConfiguration());
167+
String response = httpService.request(context, configuration);
164168
context.put("response", response);
165169

166170
taskEngine.execute(entity, context, "afterDynamicList");

dlvp/src/test/java/io/github/linagora/linid/im/dlvp/DynamicListRoutePluginE2ETest.java

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ private RouteConfiguration buildConfiguration(String url, String method) {
9393
@Test
9494
@DisplayName("test execute: should return paginated elements from external API via GET")
9595
void testExecuteGet() {
96-
plugin.setConfiguration(buildConfiguration("http://localhost:3001/v1/test_api/types", "GET"));
96+
var configuration = buildConfiguration("http://localhost:3001/v1/test_api/types", "GET");
9797

98-
var response = plugin.execute(null);
98+
var response = plugin.execute(configuration, null);
9999

100100
assertNotNull(response);
101101
assertEquals(HttpStatus.OK.value(), response.getStatusCode().value());
@@ -118,9 +118,8 @@ void testExecuteWithHeaders() {
118118
var configuration = buildConfiguration(
119119
"http://localhost:3001/v1/test_api/types/with-headers", "GET");
120120
configuration.addOption("headers", Map.of("Authorization", "Bearer my-token"));
121-
plugin.setConfiguration(configuration);
122121

123-
var response = plugin.execute(null);
122+
var response = plugin.execute(configuration, null);
124123

125124
assertNotNull(response);
126125
assertEquals(HttpStatus.OK.value(), response.getStatusCode().value());
@@ -137,10 +136,11 @@ void testExecuteWithHeaders() {
137136
@Test
138137
@DisplayName("test execute: should throw exception on 401 without auth header")
139138
void testExecuteUnauthorized() {
140-
plugin.setConfiguration(buildConfiguration(
141-
"http://localhost:3001/v1/test_api/types/with-headers", "GET"));
139+
var configuration = buildConfiguration(
140+
"http://localhost:3001/v1/test_api/types/with-headers", "GET");
142141

143-
ApiException exception = assertThrows(ApiException.class, () -> plugin.execute(null));
142+
ApiException exception = assertThrows(ApiException.class,
143+
() -> plugin.execute(configuration, null));
144144

145145
assertEquals(401, exception.getStatusCode());
146146
assertEquals("dlvp.error.external.api", exception.getError().key());
@@ -149,9 +149,10 @@ void testExecuteUnauthorized() {
149149
@Test
150150
@DisplayName("test execute: should throw exception on 4xx error")
151151
void testExecute4xx() {
152-
plugin.setConfiguration(buildConfiguration("http://localhost:3001/v1/test_api/400", "GET"));
152+
var configuration = buildConfiguration("http://localhost:3001/v1/test_api/400", "GET");
153153

154-
ApiException exception = assertThrows(ApiException.class, () -> plugin.execute(null));
154+
ApiException exception = assertThrows(ApiException.class,
155+
() -> plugin.execute(configuration, null));
155156

156157
assertEquals(400, exception.getStatusCode());
157158
assertEquals("dlvp.error.external.api", exception.getError().key());
@@ -160,9 +161,10 @@ void testExecute4xx() {
160161
@Test
161162
@DisplayName("test execute: should throw exception on 5xx error")
162163
void testExecute5xx() {
163-
plugin.setConfiguration(buildConfiguration("http://localhost:3001/v1/test_api/500", "GET"));
164+
var configuration = buildConfiguration("http://localhost:3001/v1/test_api/500", "GET");
164165

165-
ApiException exception = assertThrows(ApiException.class, () -> plugin.execute(null));
166+
ApiException exception = assertThrows(ApiException.class,
167+
() -> plugin.execute(configuration, null));
166168

167169
assertEquals(502, exception.getStatusCode());
168170
assertEquals("dlvp.error.external.api.unavailable", exception.getError().key());
@@ -171,10 +173,11 @@ void testExecute5xx() {
171173
@Test
172174
@DisplayName("test execute: should throw exception on invalid JSON response")
173175
void testExecuteInvalidJson() {
174-
plugin.setConfiguration(buildConfiguration(
175-
"http://localhost:3001/v1/test_api/invalid-json", "GET"));
176+
var configuration = buildConfiguration(
177+
"http://localhost:3001/v1/test_api/invalid-json", "GET");
176178

177-
ApiException exception = assertThrows(ApiException.class, () -> plugin.execute(null));
179+
ApiException exception = assertThrows(ApiException.class,
180+
() -> plugin.execute(configuration, null));
178181

179182
assertEquals(500, exception.getStatusCode());
180183
assertEquals("jptp.error.json.parsing", exception.getError().key());
@@ -183,10 +186,10 @@ void testExecuteInvalidJson() {
183186
@Test
184187
@DisplayName("test execute: should return empty page when API returns empty content")
185188
void testExecuteEmptyContent() {
186-
plugin.setConfiguration(buildConfiguration(
187-
"http://localhost:3001/v1/test_api/types/empty", "GET"));
189+
var configuration = buildConfiguration(
190+
"http://localhost:3001/v1/test_api/types/empty", "GET");
188191

189-
var response = plugin.execute(null);
192+
var response = plugin.execute(configuration, null);
190193

191194
assertNotNull(response);
192195
Page<DynamicListEntry> page = response.getBody();
@@ -203,9 +206,8 @@ void testExecutePost() {
203206
configuration.addOption("headers",
204207
Map.of("Content-Type", "application/json"));
205208
configuration.addOption("body", "{\"filter\":\"\"}");
206-
plugin.setConfiguration(configuration);
207209

208-
var response = plugin.execute(null);
210+
var response = plugin.execute(configuration, null);
209211

210212
assertNotNull(response);
211213
assertEquals(HttpStatus.OK.value(), response.getStatusCode().value());
@@ -222,9 +224,10 @@ void testExecutePost() {
222224
@Test
223225
@DisplayName("test execute: should throw exception on invalid method")
224226
void testExecuteInvalidMethod() {
225-
plugin.setConfiguration(buildConfiguration("http://localhost:3001/v1/test_api/types", "DELETE"));
227+
var configuration = buildConfiguration("http://localhost:3001/v1/test_api/types", "DELETE");
226228

227-
ApiException exception = assertThrows(ApiException.class, () -> plugin.execute(null));
229+
ApiException exception = assertThrows(ApiException.class,
230+
() -> plugin.execute(configuration, null));
228231

229232
assertEquals(500, exception.getStatusCode());
230233
assertEquals("error.plugin.default.invalid.option", exception.getError().key());

dlvp/src/test/java/io/github/linagora/linid/im/dlvp/DynamicListRoutePluginTest.java

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -93,49 +93,45 @@ void testSupports() {
9393
void testGetRoutes() {
9494
var configuration = new RouteConfiguration();
9595
configuration.addOption("route", "/api/etablissement/types");
96-
plugin.setConfiguration(configuration);
9796

9897
var expected = List.of(new RouteDescription("GET", "/api/etablissement/types", null, List.of()));
99-
assertEquals(expected, plugin.getRoutes(List.of()));
98+
assertEquals(expected, plugin.getRoutes(configuration, List.of()));
10099
}
101100

102101
@Test
103102
@DisplayName("test match: should match dynamic route from configuration")
104103
void testMatch() {
105104
var configuration = new RouteConfiguration();
106105
configuration.addOption("route", "/api/etablissement/types");
107-
plugin.setConfiguration(configuration);
108106

109-
assertTrue(plugin.match("/api/etablissement/types", "GET"));
110-
assertFalse(plugin.match("/api/other", "GET"));
111-
assertFalse(plugin.match("/api/etablissement/types", "POST"));
107+
assertTrue(plugin.match(configuration, "/api/etablissement/types", "GET"));
108+
assertFalse(plugin.match(configuration, "/api/other", "GET"));
109+
assertFalse(plugin.match(configuration, "/api/etablissement/types", "POST"));
112110
}
113111

114112
@Test
115113
@DisplayName("test match: should be case-insensitive on method")
116114
void testMatchCaseInsensitiveMethod() {
117115
var configuration = new RouteConfiguration();
118116
configuration.addOption("route", "/api/etablissement/types");
119-
plugin.setConfiguration(configuration);
120117

121-
assertTrue(plugin.match("/api/etablissement/types", "get"));
122-
assertTrue(plugin.match("/api/etablissement/types", "Get"));
118+
assertTrue(plugin.match(configuration, "/api/etablissement/types", "get"));
119+
assertTrue(plugin.match(configuration, "/api/etablissement/types", "Get"));
123120
}
124121

125122
@Test
126123
@DisplayName("test match: should return false when no configuration")
127124
void testMatchNoConfiguration() {
128-
assertFalse(plugin.match("/api/anything", "GET"));
125+
assertFalse(plugin.match(null, "/api/anything", "GET"));
129126
}
130127

131128
@Test
132129
@DisplayName("test getRoutes: should throw exception when route is null")
133130
void testGetRoutesNullRoute() {
134131
var configuration = new RouteConfiguration();
135-
plugin.setConfiguration(configuration);
136132

137133
ApiException exception =
138-
assertThrows(ApiException.class, () -> plugin.getRoutes(List.of()));
134+
assertThrows(ApiException.class, () -> plugin.getRoutes(configuration, List.of()));
139135

140136
assertEquals(500, exception.getStatusCode());
141137
assertEquals("error.plugin.default.missing.option", exception.getError().key());
@@ -145,10 +141,9 @@ void testGetRoutesNullRoute() {
145141
@DisplayName("test match: should throw exception when route is null")
146142
void testMatchNullRoute() {
147143
var configuration = new RouteConfiguration();
148-
plugin.setConfiguration(configuration);
149144

150145
ApiException exception =
151-
assertThrows(ApiException.class, () -> plugin.match("/api/anything", "GET"));
146+
assertThrows(ApiException.class, () -> plugin.match(configuration, "/api/anything", "GET"));
152147

153148
assertEquals(500, exception.getStatusCode());
154149
assertEquals("error.plugin.default.missing.option", exception.getError().key());
@@ -174,8 +169,6 @@ void testExecute() {
174169
"destination", "response",
175170
"phases", List.of("beforeDynamicListMapping"))
176171
));
177-
plugin.setConfiguration(configuration);
178-
179172
when(authorizationFactory.getAuthorizationPlugin()).thenReturn(authorizationPlugin);
180173
when(httpService.request(any(TaskExecutionContext.class), any(PluginConfiguration.class)))
181174
.thenReturn("{\"content\":[{\"name\":\"Type 1\",\"id\":\"1\"},{\"name\":\"Type 2\",\"id\":\"2\"}],"
@@ -202,7 +195,7 @@ void testExecute() {
202195
when(jinjaService.render(any(TaskExecutionContext.class), any(DynamicEntity.class),
203196
eq("{{ context.response.totalElements }}"))).thenReturn("2");
204197

205-
var response = plugin.execute(null);
198+
var response = plugin.execute(configuration, null);
206199

207200
assertNotNull(response);
208201
assertEquals(HttpStatus.OK.value(), response.getStatusCode().value());

0 commit comments

Comments
 (0)