Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions services-api/src/main/java/io/scalecube/services/Reflect.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.reactivestreams.Publisher;
Expand Down Expand Up @@ -220,10 +219,10 @@ private static Map<String, String> transformArrayToMap(Tag[] array) {
* @param serviceInterface with {@link Service} annotation
* @return service name
*/
public static Map<String, Method> serviceMethods(Class<?> serviceInterface) {
public static Collection<Method> serviceMethods(Class<?> serviceInterface) {
return Arrays.stream(serviceInterface.getMethods())
.filter(method -> method.isAnnotationPresent(ServiceMethod.class))
.collect(Collectors.toMap(Reflect::methodName, Function.identity()));
.toList();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,11 @@ private static Consumer<ServiceMessage> assertError(int errorCode, String errorM
private static MethodInfo getMethodInfo(Object serviceInstance, String methodName) {
final var serviceInstanceClass = serviceInstance.getClass();
final Class<?> serviceInterface = Reflect.serviceInterfaces(serviceInstance).toList().get(0);
final var method = Reflect.serviceMethods(serviceInterface).get(methodName);
final var method =
Reflect.serviceMethods(serviceInterface).stream()
.filter(m -> m.getName().equals(methodName))
.findFirst()
.get();

// get service instance method
Method serviceMethod;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package io.scalecube.services.gateway.rest;

import static io.scalecube.services.api.ServiceMessage.HEADER_REQUEST_METHOD;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;

import io.scalecube.services.Microservices;
import io.scalecube.services.Microservices.Context;
import io.scalecube.services.annotations.RestMethod;
import io.scalecube.services.annotations.Service;
import io.scalecube.services.annotations.ServiceMethod;
import io.scalecube.services.api.ServiceMessage;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import reactor.core.publisher.Mono;

public class ServiceRegistrationTest {

@ParameterizedTest
@ValueSource(
classes = {
EchoService.class,
GoodRestService.class,
CreateRestService.class,
UpdateRestService.class
})
void registerDuplicateService(Class<?> serviceInterface) {
try (final var microservices =
Microservices.start(
new Context().services(mock(serviceInterface), mock(serviceInterface)))) {
fail("Expected exception");
} catch (Exception e) {
assertInstanceOf(IllegalStateException.class, e, e::getMessage);
assertThat(e.getMessage(), Matchers.startsWith("MethodInvoker already exists"));
}
}

@Test
void registerInvalidRestService() {
try (final var microservices =
Microservices.start(new Context().services(mock(BadRestService.class)))) {
fail("Expected exception");
} catch (Exception e) {
assertInstanceOf(IllegalStateException.class, e, e::getMessage);
assertThat(e.getMessage(), Matchers.startsWith("MethodInvoker already exists"));
}
}

@Test
void registerSingleValidRestService() {
try (final var microservices =
Microservices.start(new Context().services(mock(GoodRestService.class)))) {
final var serviceRegistry = microservices.serviceRegistry();

final var foo = System.nanoTime();
final var methodInvokerWithoutRestMethod =
serviceRegistry.lookupInvoker(
ServiceMessage.builder().qualifier("v1/service/echo/" + foo).build());
assertNull(methodInvokerWithoutRestMethod);

final var methodInvokerByGet =
serviceRegistry.lookupInvoker(
ServiceMessage.builder()
.header(HEADER_REQUEST_METHOD, "GET")
.qualifier("v1/service/echo/" + foo)
.build());
assertNotNull(methodInvokerByGet);

final var methodInvokerByPost =
serviceRegistry.lookupInvoker(
ServiceMessage.builder()
.header(HEADER_REQUEST_METHOD, "POST")
.qualifier("v1/service/echo/" + foo)
.build());
assertNotNull(methodInvokerByPost);
}
}

@Test
void registerMultipleValidRestServices() {
try (final var microservices =
Microservices.start(
new Context().services(mock(CreateRestService.class), mock(UpdateRestService.class)))) {
final var serviceRegistry = microservices.serviceRegistry();

final var foo = System.nanoTime();
final var methodInvokerWithoutRestMethod =
serviceRegistry.lookupInvoker(
ServiceMessage.builder().qualifier("v1/service/account/" + foo).build());
assertNull(methodInvokerWithoutRestMethod);

final var methodInvokerByPost =
serviceRegistry.lookupInvoker(
ServiceMessage.builder()
.header(HEADER_REQUEST_METHOD, "POST")
.qualifier("v1/service/account/" + foo)
.build());
assertNotNull(methodInvokerByPost);

final var methodInvokerByPut =
serviceRegistry.lookupInvoker(
ServiceMessage.builder()
.header(HEADER_REQUEST_METHOD, "PUT")
.qualifier("v1/service/account/" + foo)
.build());
assertNotNull(methodInvokerByPut);
}
}

@Service("v1/service")
interface EchoService {

@ServiceMethod("get/:foo")
Mono<SomeResponse> echo();
}

@Service("v1/service")
interface BadRestService {

@RestMethod("GET")
@ServiceMethod("get/:foo")
Mono<SomeResponse> echo();

@RestMethod("GET")
@ServiceMethod("get/:foo")
Mono<SomeResponse> ping();
}

@Service("v1/service")
interface GoodRestService {

@RestMethod("GET")
@ServiceMethod("echo/:foo")
Mono<SomeResponse> echo();

@RestMethod("POST")
@ServiceMethod("echo/:foo")
Mono<SomeResponse> ping();
}

@Service("v1/service")
interface CreateRestService {

@RestMethod("POST")
@ServiceMethod("account/:foo")
Mono<SomeResponse> account();
}

@Service("v1/service")
interface UpdateRestService {

@RestMethod("PUT")
@ServiceMethod("account/:foo")
Mono<SomeResponse> account();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public static List<ServiceRegistration> toServiceRegistrations(ServiceInfo servi
final var namespace = Reflect.serviceName(serviceInterface);

final var methodDefinitions =
Reflect.serviceMethods(serviceInterface).values().stream()
Reflect.serviceMethods(serviceInterface).stream()
.map(
method -> {
// validate method
Expand Down Expand Up @@ -113,7 +113,7 @@ public static Collection<ServiceRoleDefinition> collectServiceRoles(
serviceInterface ->
Reflect.serviceMethods(serviceInterface)
.forEach(
(key, method) -> {
method -> {
// validate method
Reflect.validateMethodOrThrow(method);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public void registerService(
serviceInterface ->
Reflect.serviceMethods(serviceInterface)
.forEach(
(key, method) -> {
method -> {
// validate method
Reflect.validateMethodOrThrow(method);

Expand Down