Skip to content

Commit 446df19

Browse files
authored
Enhanced service registration for RESTful services (#943)
* Enhanced service registration for RESTful services
1 parent 5b49fd9 commit 446df19

File tree

5 files changed

+172
-7
lines changed

5 files changed

+172
-7
lines changed

services-api/src/main/java/io/scalecube/services/Reflect.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import java.util.HashSet;
2626
import java.util.Map;
2727
import java.util.Set;
28-
import java.util.function.Function;
2928
import java.util.stream.Collectors;
3029
import java.util.stream.Stream;
3130
import org.reactivestreams.Publisher;
@@ -220,10 +219,10 @@ private static Map<String, String> transformArrayToMap(Tag[] array) {
220219
* @param serviceInterface with {@link Service} annotation
221220
* @return service name
222221
*/
223-
public static Map<String, Method> serviceMethods(Class<?> serviceInterface) {
222+
public static Collection<Method> serviceMethods(Class<?> serviceInterface) {
224223
return Arrays.stream(serviceInterface.getMethods())
225224
.filter(method -> method.isAnnotationPresent(ServiceMethod.class))
226-
.collect(Collectors.toMap(Reflect::methodName, Function.identity()));
225+
.toList();
227226
}
228227

229228
/**

services-api/src/test/java/io/scalecube/services/methods/ServiceMethodInvokerTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,11 @@ private static Consumer<ServiceMessage> assertError(int errorCode, String errorM
368368
private static MethodInfo getMethodInfo(Object serviceInstance, String methodName) {
369369
final var serviceInstanceClass = serviceInstance.getClass();
370370
final Class<?> serviceInterface = Reflect.serviceInterfaces(serviceInstance).toList().get(0);
371-
final var method = Reflect.serviceMethods(serviceInterface).get(methodName);
371+
final var method =
372+
Reflect.serviceMethods(serviceInterface).stream()
373+
.filter(m -> m.getName().equals(methodName))
374+
.findFirst()
375+
.get();
372376

373377
// get service instance method
374378
Method serviceMethod;
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package io.scalecube.services.gateway.rest;
2+
3+
import static io.scalecube.services.api.ServiceMessage.HEADER_REQUEST_METHOD;
4+
import static org.hamcrest.MatcherAssert.assertThat;
5+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
6+
import static org.junit.jupiter.api.Assertions.assertNotNull;
7+
import static org.junit.jupiter.api.Assertions.assertNull;
8+
import static org.junit.jupiter.api.Assertions.fail;
9+
import static org.mockito.Mockito.mock;
10+
11+
import io.scalecube.services.Microservices;
12+
import io.scalecube.services.Microservices.Context;
13+
import io.scalecube.services.annotations.RestMethod;
14+
import io.scalecube.services.annotations.Service;
15+
import io.scalecube.services.annotations.ServiceMethod;
16+
import io.scalecube.services.api.ServiceMessage;
17+
import org.hamcrest.Matchers;
18+
import org.junit.jupiter.api.Test;
19+
import org.junit.jupiter.params.ParameterizedTest;
20+
import org.junit.jupiter.params.provider.ValueSource;
21+
import reactor.core.publisher.Mono;
22+
23+
public class ServiceRegistrationTest {
24+
25+
@ParameterizedTest
26+
@ValueSource(
27+
classes = {
28+
EchoService.class,
29+
GoodRestService.class,
30+
CreateRestService.class,
31+
UpdateRestService.class
32+
})
33+
void registerDuplicateService(Class<?> serviceInterface) {
34+
try (final var microservices =
35+
Microservices.start(
36+
new Context().services(mock(serviceInterface), mock(serviceInterface)))) {
37+
fail("Expected exception");
38+
} catch (Exception e) {
39+
assertInstanceOf(IllegalStateException.class, e, e::getMessage);
40+
assertThat(e.getMessage(), Matchers.startsWith("MethodInvoker already exists"));
41+
}
42+
}
43+
44+
@Test
45+
void registerInvalidRestService() {
46+
try (final var microservices =
47+
Microservices.start(new Context().services(mock(BadRestService.class)))) {
48+
fail("Expected exception");
49+
} catch (Exception e) {
50+
assertInstanceOf(IllegalStateException.class, e, e::getMessage);
51+
assertThat(e.getMessage(), Matchers.startsWith("MethodInvoker already exists"));
52+
}
53+
}
54+
55+
@Test
56+
void registerSingleValidRestService() {
57+
try (final var microservices =
58+
Microservices.start(new Context().services(mock(GoodRestService.class)))) {
59+
final var serviceRegistry = microservices.serviceRegistry();
60+
61+
final var foo = System.nanoTime();
62+
final var methodInvokerWithoutRestMethod =
63+
serviceRegistry.lookupInvoker(
64+
ServiceMessage.builder().qualifier("v1/service/echo/" + foo).build());
65+
assertNull(methodInvokerWithoutRestMethod);
66+
67+
final var methodInvokerByGet =
68+
serviceRegistry.lookupInvoker(
69+
ServiceMessage.builder()
70+
.header(HEADER_REQUEST_METHOD, "GET")
71+
.qualifier("v1/service/echo/" + foo)
72+
.build());
73+
assertNotNull(methodInvokerByGet);
74+
75+
final var methodInvokerByPost =
76+
serviceRegistry.lookupInvoker(
77+
ServiceMessage.builder()
78+
.header(HEADER_REQUEST_METHOD, "POST")
79+
.qualifier("v1/service/echo/" + foo)
80+
.build());
81+
assertNotNull(methodInvokerByPost);
82+
}
83+
}
84+
85+
@Test
86+
void registerMultipleValidRestServices() {
87+
try (final var microservices =
88+
Microservices.start(
89+
new Context().services(mock(CreateRestService.class), mock(UpdateRestService.class)))) {
90+
final var serviceRegistry = microservices.serviceRegistry();
91+
92+
final var foo = System.nanoTime();
93+
final var methodInvokerWithoutRestMethod =
94+
serviceRegistry.lookupInvoker(
95+
ServiceMessage.builder().qualifier("v1/service/account/" + foo).build());
96+
assertNull(methodInvokerWithoutRestMethod);
97+
98+
final var methodInvokerByPost =
99+
serviceRegistry.lookupInvoker(
100+
ServiceMessage.builder()
101+
.header(HEADER_REQUEST_METHOD, "POST")
102+
.qualifier("v1/service/account/" + foo)
103+
.build());
104+
assertNotNull(methodInvokerByPost);
105+
106+
final var methodInvokerByPut =
107+
serviceRegistry.lookupInvoker(
108+
ServiceMessage.builder()
109+
.header(HEADER_REQUEST_METHOD, "PUT")
110+
.qualifier("v1/service/account/" + foo)
111+
.build());
112+
assertNotNull(methodInvokerByPut);
113+
}
114+
}
115+
116+
@Service("v1/service")
117+
interface EchoService {
118+
119+
@ServiceMethod("get/:foo")
120+
Mono<SomeResponse> echo();
121+
}
122+
123+
@Service("v1/service")
124+
interface BadRestService {
125+
126+
@RestMethod("GET")
127+
@ServiceMethod("get/:foo")
128+
Mono<SomeResponse> echo();
129+
130+
@RestMethod("GET")
131+
@ServiceMethod("get/:foo")
132+
Mono<SomeResponse> ping();
133+
}
134+
135+
@Service("v1/service")
136+
interface GoodRestService {
137+
138+
@RestMethod("GET")
139+
@ServiceMethod("echo/:foo")
140+
Mono<SomeResponse> echo();
141+
142+
@RestMethod("POST")
143+
@ServiceMethod("echo/:foo")
144+
Mono<SomeResponse> ping();
145+
}
146+
147+
@Service("v1/service")
148+
interface CreateRestService {
149+
150+
@RestMethod("POST")
151+
@ServiceMethod("account/:foo")
152+
Mono<SomeResponse> account();
153+
}
154+
155+
@Service("v1/service")
156+
interface UpdateRestService {
157+
158+
@RestMethod("PUT")
159+
@ServiceMethod("account/:foo")
160+
Mono<SomeResponse> account();
161+
}
162+
}

services/src/main/java/io/scalecube/services/ServiceScanner.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static List<ServiceRegistration> toServiceRegistrations(ServiceInfo servi
3232
final var namespace = Reflect.serviceName(serviceInterface);
3333

3434
final var methodDefinitions =
35-
Reflect.serviceMethods(serviceInterface).values().stream()
35+
Reflect.serviceMethods(serviceInterface).stream()
3636
.map(
3737
method -> {
3838
// validate method
@@ -113,7 +113,7 @@ public static Collection<ServiceRoleDefinition> collectServiceRoles(
113113
serviceInterface ->
114114
Reflect.serviceMethods(serviceInterface)
115115
.forEach(
116-
(key, method) -> {
116+
method -> {
117117
// validate method
118118
Reflect.validateMethodOrThrow(method);
119119

services/src/main/java/io/scalecube/services/registry/ServiceRegistryImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public void registerService(
152152
serviceInterface ->
153153
Reflect.serviceMethods(serviceInterface)
154154
.forEach(
155-
(key, method) -> {
155+
method -> {
156156
// validate method
157157
Reflect.validateMethodOrThrow(method);
158158

0 commit comments

Comments
 (0)