diff --git a/build.gradle b/build.gradle index 55a7605..774a742 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,3 @@ - -plugins { - id 'org.springframework.boot' version '1.5.2.RELEASE' -} - apply plugin: 'groovy' repositories { @@ -11,13 +6,12 @@ repositories { } dependencies { - compile ( - 'org.springframework.boot:spring-boot-starter', - 'org.springframework.boot:spring-boot-starter-data-jpa', + compile( 'org.codehaus.groovy:groovy-all:2.4.7', 'com.amazonaws:aws-lambda-java-core:1.1.0', 'com.amazonaws:aws-lambda-java-log4j:1.0.0', - 'com.h2database:h2' + 'io.vertx:vertx-core:3.4.1', + 'io.vertx:vertx-lang-groovy:3.4.1' ) // Use JUnit test framework diff --git a/serverless.yml b/serverless.yml index c63e928..ff900fb 100644 --- a/serverless.yml +++ b/serverless.yml @@ -11,7 +11,7 @@ package: functions: users: - handler: com.serverless.LambdaHandler + handler: com.serverless.VertxHandler events: - http: path: users diff --git a/src/main/groovy/com/serverless/LambdaHandler.groovy b/src/main/groovy/com/serverless/LambdaHandler.groovy deleted file mode 100644 index 934d90a..0000000 --- a/src/main/groovy/com/serverless/LambdaHandler.groovy +++ /dev/null @@ -1,35 +0,0 @@ -package com.serverless - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestHandler -import com.serverless.service.DispatcherService -import com.serverless.lambda.Request -import com.serverless.lambda.Response -import groovy.transform.Memoized -import groovy.util.logging.Log4j -import org.springframework.boot.SpringApplication -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.context.ApplicationContext -import org.springframework.context.annotation.ComponentScan - -@SpringBootApplication -@ComponentScan(basePackages = "com.serverless") -@Log4j -class LambdaHandler implements RequestHandler { - - static void main(String[] args) throws Exception { - LambdaHandler.newInstance().getApplicationContext(args) - } - - @Memoized - ApplicationContext getApplicationContext(String[] args = []) { - return SpringApplication.run(LambdaHandler.class, args) - } - - @Override - Response handleRequest(Map input, Context context) { - final Request request = new Request(input, context) - DispatcherService dispatcher = getApplicationContext().getBean(DispatcherService.class) - return dispatcher.dispatch(request) - } -} diff --git a/src/main/groovy/com/serverless/VertxHandler.groovy b/src/main/groovy/com/serverless/VertxHandler.groovy new file mode 100644 index 0000000..b2cb895 --- /dev/null +++ b/src/main/groovy/com/serverless/VertxHandler.groovy @@ -0,0 +1,57 @@ +package com.serverless + +import com.amazonaws.services.lambda.runtime.Context +import com.amazonaws.services.lambda.runtime.RequestHandler +import io.vertx.core.AbstractVerticle +import io.vertx.core.Vertx + +import java.util.concurrent.CompletableFuture +import java.util.concurrent.TimeUnit + +class VertxHandler implements RequestHandler { + + // Initialize Vertx instance and deploy UserService Verticle + final Vertx vertxInstance = { + System.setProperty('vertx.disableFileCPResolving', 'true') + final vertx = Vertx.vertx() + vertx.deployVerticle(UserService.newInstance()) + return vertx + }() + + @Override + Map handleRequest(Map input, Context context) { + final future = new CompletableFuture() + + // Send message to event bus using httpmethod:resource as dynamic channel + final eventBusAddress = "${input.httpMethod}:${input.resource}" + vertxInstance.eventBus().send(eventBusAddress, input, { asyncResult -> + if (asyncResult.succeeded()) { + future.complete(asyncResult.result().body()) + } else { + future.completeExceptionally(asyncResult.cause()) + } + }) + + future.get(5, TimeUnit.SECONDS) + } + + class UserService extends AbstractVerticle { + @Override + void start() throws Exception { + + final eventBus = vertx.eventBus() + + eventBus.consumer('GET:/users') { message -> + message.reply([statusCode: 200, body: 'Received GET:/users']) + } + + eventBus.consumer('GET:/users/{lastName}') { message -> + message.reply([statusCode: 200, body: 'Received GET:/users/{lastName}']) + } + + eventBus.consumer('POST:/users/create') { message -> + message.reply([statusCode: 201, body: 'Received POST:/users/create']) + } + } + } +} diff --git a/src/main/groovy/com/serverless/handler/CreateUser.groovy b/src/main/groovy/com/serverless/handler/CreateUser.groovy deleted file mode 100644 index eb4f254..0000000 --- a/src/main/groovy/com/serverless/handler/CreateUser.groovy +++ /dev/null @@ -1,46 +0,0 @@ -package com.serverless.handler - -import com.serverless.lambda.Request -import com.serverless.lambda.Response -import com.serverless.persistence.entity.User -import com.serverless.persistence.repository.UserRepository -import groovy.json.JsonOutput -import groovy.transform.CompileStatic -import groovy.util.logging.Log4j -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Component - -import javax.annotation.PostConstruct - -@Component -@Log4j -@CompileStatic -class CreateUser implements Handler { - - @Autowired - private UserRepository userRepository - - @Override - boolean route(Request request) { - request.resourcePath() == '/users/create' && request.httpMethod() == 'POST' - } - - @Override - Response respond(final Request request) { - def firstName = request.queryString('firstName') - def lastName = request.queryString('lastName') - def user = new User(firstName, lastName) - userRepository.save(user) - Response.builder() - .statusCode(201) - .body(JsonOutput.prettyPrint(JsonOutput.toJson(user))) - .build() - } - - @PostConstruct - void init() { - userRepository.save(new User('Michael', 'Jordan')) - userRepository.save(new User('Scottie', 'Pippen')) - userRepository.save(new User('Phil', 'Jackson')) - } -} diff --git a/src/main/groovy/com/serverless/handler/FindUsersByLastName.groovy b/src/main/groovy/com/serverless/handler/FindUsersByLastName.groovy deleted file mode 100644 index 7b030fa..0000000 --- a/src/main/groovy/com/serverless/handler/FindUsersByLastName.groovy +++ /dev/null @@ -1,34 +0,0 @@ -package com.serverless.handler - - -import com.serverless.lambda.Request -import com.serverless.lambda.Response -import com.serverless.persistence.repository.UserRepository -import groovy.json.JsonOutput -import groovy.transform.CompileStatic -import groovy.util.logging.Log4j -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Component - -@Component -@Log4j -@CompileStatic -class FindUsersByLastName implements Handler { - - @Autowired - private UserRepository userRepository - - @Override - boolean route(final Request request) { - request.resourcePath() == '/users/{lastName}' && request.httpMethod() == 'GET' - } - - @Override - Response respond(final Request request) { - def customers = userRepository.findByLastName(request.pathParameter('lastName')) - Response.builder() - .statusCode(200) - .body(JsonOutput.prettyPrint(JsonOutput.toJson(customers))) - .build() - } -} diff --git a/src/main/groovy/com/serverless/handler/GetUsers.groovy b/src/main/groovy/com/serverless/handler/GetUsers.groovy deleted file mode 100644 index 5ca07a0..0000000 --- a/src/main/groovy/com/serverless/handler/GetUsers.groovy +++ /dev/null @@ -1,34 +0,0 @@ -package com.serverless.handler - - -import com.serverless.lambda.Request -import com.serverless.lambda.Response -import com.serverless.persistence.repository.UserRepository -import groovy.json.JsonOutput -import groovy.transform.CompileStatic -import groovy.util.logging.Log4j -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Component - -@Component -@Log4j -@CompileStatic -class GetUsers implements Handler { - - @Autowired - private UserRepository userRepository - - @Override - boolean route(final Request request) { - request.resourcePath() == '/users' && request.httpMethod() == 'GET' - } - - @Override - Response respond(final Request request) { - def allCustomers = userRepository.findAll() - Response.builder() - .statusCode(200) - .body(JsonOutput.prettyPrint(JsonOutput.toJson(allCustomers))) - .build() - } -} diff --git a/src/main/groovy/com/serverless/handler/Handler.groovy b/src/main/groovy/com/serverless/handler/Handler.groovy deleted file mode 100644 index 55d465d..0000000 --- a/src/main/groovy/com/serverless/handler/Handler.groovy +++ /dev/null @@ -1,9 +0,0 @@ -package com.serverless.handler - -import com.serverless.lambda.Request -import com.serverless.lambda.Response - -interface Handler { - boolean route(Request request) - Response respond(Request request) -} \ No newline at end of file diff --git a/src/main/groovy/com/serverless/lambda/Request.groovy b/src/main/groovy/com/serverless/lambda/Request.groovy deleted file mode 100644 index 9fad90c..0000000 --- a/src/main/groovy/com/serverless/lambda/Request.groovy +++ /dev/null @@ -1,35 +0,0 @@ -package com.serverless.lambda - -import com.amazonaws.services.lambda.runtime.Context -import groovy.transform.ToString - -@ToString(includePackage = false) -class Request { - private final Map input - private final Context context - - Request(final Map input, final Context context) { - this.input = input - this.context = context - } - - String requestId() { - context?.awsRequestId - } - - String resourcePath() { - input?.resource ?: 'unknown' - } - - String httpMethod() { - input?.httpMethod ?: 'unknown' - } - - String queryString(String name) { - input?.queryStringParameters?."${name}"?.trim() - } - - String pathParameter(String name) { - input?.pathParameters?."${name}"?.trim() - } -} diff --git a/src/main/groovy/com/serverless/lambda/Response.groovy b/src/main/groovy/com/serverless/lambda/Response.groovy deleted file mode 100644 index ba26ea3..0000000 --- a/src/main/groovy/com/serverless/lambda/Response.groovy +++ /dev/null @@ -1,18 +0,0 @@ -package com.serverless.lambda - -import groovy.transform.CompileStatic -import groovy.transform.ToString -import groovy.transform.builder.Builder - -@Builder -@ToString(includePackage = false) -@CompileStatic -class Response { - int statusCode - String body - private Map headers = [:] - - void addHeader(String key, String value) { - headers.put(key, value) - } -} diff --git a/src/main/groovy/com/serverless/persistence/entity/User.groovy b/src/main/groovy/com/serverless/persistence/entity/User.groovy deleted file mode 100644 index adc190e..0000000 --- a/src/main/groovy/com/serverless/persistence/entity/User.groovy +++ /dev/null @@ -1,28 +0,0 @@ -package com.serverless.persistence.entity - -import groovy.transform.CompileStatic -import groovy.transform.ToString - -import javax.persistence.Entity -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id - -@Entity -@ToString -@CompileStatic -class User { - - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - Long id - String firstName - String lastName - - User(){} - - User(String firstName, String lastName) { - this.firstName = firstName - this.lastName = lastName - } -} \ No newline at end of file diff --git a/src/main/groovy/com/serverless/persistence/repository/UserRepository.groovy b/src/main/groovy/com/serverless/persistence/repository/UserRepository.groovy deleted file mode 100644 index 9ee1c78..0000000 --- a/src/main/groovy/com/serverless/persistence/repository/UserRepository.groovy +++ /dev/null @@ -1,11 +0,0 @@ -package com.serverless.persistence.repository - -import com.serverless.persistence.entity.User -import groovy.transform.CompileStatic -import org.springframework.data.repository.CrudRepository - -@CompileStatic -interface UserRepository extends CrudRepository { - - List findByLastName(String lastName) -} \ No newline at end of file diff --git a/src/main/groovy/com/serverless/service/DispatcherService.groovy b/src/main/groovy/com/serverless/service/DispatcherService.groovy deleted file mode 100644 index 5d7db60..0000000 --- a/src/main/groovy/com/serverless/service/DispatcherService.groovy +++ /dev/null @@ -1,35 +0,0 @@ -package com.serverless.service - - -import com.serverless.handler.Handler -import com.serverless.lambda.Request -import com.serverless.lambda.Response -import groovy.transform.CompileStatic -import groovy.util.logging.Log4j -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Component - -@Component -@CompileStatic -@Log4j -class DispatcherService { - - @Autowired - List handlers - - Response dispatch(Request request) { - log.info("request: ${request}") - try { - def handlers = handlers?.findAll { it.route(request) } - if (handlers?.size() != 1) { - throw new RuntimeException('Unable to resolve unique handler for route/request') - } - final Response response = handlers.first().respond(request) - response.addHeader('X-Request-Id', request.requestId()) - log.info("response: ${response}") - return response - } catch (e) { - return Response.builder().statusCode(500).body(e.getMessage()).build() - } - } -}