Upgrade to Pro — share decks privately, control downloads, hide ads and more …

kotlin-fest

stormcat24
August 25, 2018
17k

 kotlin-fest

Kotlinで愛でるMicroservices

stormcat24

August 25, 2018
Tweet

Transcript

  1. ϝϯόʔͷ৺ͷڣͼ ‣ /VMM҆શʂ ‣ ߴ֊ؔ਺΄͍͠ʂ ‣ ֦ுؔ਺ॻ͖͍ͨʂ ‣ EBUBDMBTT ‣

    ΢ν+BWB͔͚Δਓଟ͍ʂʢ͔͠͠+BWB͸ͳ͍Αͳʣ ‣ +BWB͔ͩΒH31$࢖͑Δ ‣ ,PUMJO͔Θ͍͍Α,PUMJO
  2. 3PVUFS'VODUJPO%4- @Configuration class TaskRoutes(private val taskHandler: TaskHandler, private val exceptionFilter:

    ExceptionFilter) { @Bean fun taskRouter() = router { (accept(APPLICATION_JSON) and "/api").nest { "/task".nest { POST("/", taskHandler::create) GET("/{id}", taskHandler::fetchByTaskId) PUT("/{id}", taskHandler::updateByTaskId) DELETE("/{id}", taskHandler::deleteByTaskId) PUT("/{id}/finish", taskHandler::finishByTaskId) } "/tasks".nest { GET("/", taskHandler::fetchAll) } } }.filter(exceptionFilter()) } 4QSJOH.7$ʹൺ΂ͯγϡοͱ͢Δ Ξϊςʔγϣϯ஍ࠈ͔Β୤٫ ,PUMJOͰ͞Βʹγϡοͱ͢Δ
  3. 8FC5FTU$MJFOU @RunWith(SpringRunner::class) class TaskRoutesTest { lateinit var client : WebTestClient

    lateinit var taskHandler: TaskHandler lateinit var exceptionFilter: ExceptionFilter val mapper = ObjectMapper().registerModule(KotlinModule()) @Before fun before() { taskHandler = mock(TaskHandler::class) exceptionFilter = ExceptionFilter() val taskRoutes = TaskRoutes(taskHandler, exceptionFilter) client = WebTestClient.bindToRouterFunction(taskRoutes.taskRouter()).build() } ɾɾɾ } 8FC5FTU$MJFOUͰ3PVUFS'VODUJPOͷςετ
  4. 8FC5FTU$MJFOU @Test fun `GET Task`() { // mock `when`(taskHandler.fetchByTaskId(any())).thenReturn(ok().json().body(Mono.just(mockModel))) client.get().uri("/api/task/1")

    .accept(MediaType.APPLICATION_JSON_UTF8) .exchange() .expectStatus().isOk .expectBody() .consumeAsStringWith { val actual = mapper.readValue<TaskModel>(it, TaskModel::class) actual.id shouldBe 1L actual.title shouldBe “kotlin fest" actual.createdAt shouldBe "2018-08-25T16:30:00Z" actual.updatedAt shouldBe "2018-08-25T16:30:00Z" } }
  5. H31$QSPUPCVGͷ*%- service TaskService { rpc GetTaskService (TaskInbound) returns (TaskOutbound) {

    option (google.api.http) = { get: "/v1/task" }; } } message TaskInbound { uint32 task_id = 1; } message TaskOutbound { uint32 task_id = 1; string title = 2; } ɹ΍ΓͱΓ͢ΔNFTTBHF H31$ϝιουͷఆٛ
  6. H31$4FSWFS class TaskBackendServer( private val getTaskService: GetTaskService, private val getTaskListService:

    GetTaskListService ) : TaskServiceGrpc.TaskServiceImplBase() { override fun getTaskService(request: TaskInbound?, responseObserver: StreamObserver<TaskOutbound>?) { val taskId = GRpcInboundValidator.validTaskInbound(request) // ૹ৴͢ΔΦϒδΣΫτͷߏங val task = getTaskService(GetTaskCommand(taskId.toLong())) val msg = TaskOutbound.newBuilder().setTaskId(taskId).build() // ݁ՌΛclient΁ responseObserver?.onNext(msg) responseObserver?.onCompleted() }
  7. H31$$MJFOU suspend fun getTask(taskId: Long): TaskOutbound = async(CommonPool) { try

    { val outbound = ShutdownLoan.using(getChannel(), { channel -> ɹɹɹɹɹɹɹɹɹ // ͕͜͜InboundϝοηʔδߏஙɺgRPCݺͼग़͠ݩ val msg = TaskInbound.newBuilder().setTaskId(taskId.toInt()).build() TaskServiceGrpc.newBlockingStub(channel).getTaskService(msg) }) Result.Success<TaskOutbound, GrpcException>(outbound) } catch (e: Exception) { val status = Status.fromThrowable(e) Result.Failure<TaskOutbound, GrpcException>(status with status.description) } }.await().fold({ it }, { throw it }) DPSPVUJOFͰඇಉظॲཧΛγϯϓϧʹ
  8. ஋͕͋Δ͔ͷҙຯ෇͚ message TaskListInbound { google.protobuf.UInt32Value page = 1; } val

    page = when { request.hasPage() -> request.page.value else -> DEFAULT_PAGE_LIMIT }
  9. 5SZ import spark.kotlin.* fun main(args: Array<String>) { val http: Http

    = ignite() http.get("/hello") { "Kotlin Fest!!" } } $ curl http://localhost:4567/hello Kotlin Fest!!
  10. $POUSPMMFS package io.stormcat.controller
 
 import spark.*
 
 class EchoController {


    
 val echo = Route { req, res ->
 "Hello, ${req.queryParams("name")}!"
 }
 }
  11. 'JMUFST package io.stormcat.filter
 
 import spark.Filter
 import spark.Request
 import spark.Response


    
 class ResponseHeaderFilter : Filter {
 
 override fun handle(request: Request, response: Response) {
 response.header("Server", "Kotlin Fest Server")
 }
 }
  12. 'JMUFST package io.stormcat
 
 import io.stormcat.controller.EchoController
 import io.stormcat.filter.ResponseHeaderFilter
 import spark.Spark.*


    
 fun main(args: Array<String>) {
 
 // filters
 after(ResponseHeaderFilter())
 
 // routing
 get("/echo", EchoController().echo)
 
 }

  13. %* package io.stormcat.controller
 
 import com.google.inject.Inject
 import io.stormcat.service.UserService
 import spark.*


    
 class UserController @Inject constructor(
 val userService: UserService
 ) {
 
 val getUser = Route { req, res ->
 val userId = req.params("id")?.toLong() ?: throw RuntimeException("id is required")
 userService.getUser(userId)
 }
 }
  14. %*ʢ(VJDF fun main(args: Array<String>) {
 
 val injector = Guice.createInjector(object

    : AbstractModule() {
 override fun configure() {
 bindConstant().annotatedWith(
 Names.named("apiDomain")).to("api.yourexample.com")
 bind(ObjectMapper::class.java)
 .toProvider(ObjectMapperProvider::class.java)
 .`in`(Singleton::class.java)
 }
 })
 
 val userController = injector.getInstance(UserController::class.java)
 
 // routing
 get("/user/:id", userController.getUser)
 
 } ˞,PEFJOͱ͍͏%*ϥΠϒϥϦ΋͋Γ·͢ ʢ,PUMJO੡ʣ ۪௚ʹ%*͍ͯ͘͠ελΠϧ
  15. ബ͍͔Β֦ுؔ਺͕େ׆༂͢Δ package io.stormcat.controller
 
 import spark.Request
 
 fun Request.authUser(): User

    {
 val user = this.attribute<User>("authUser")
 return user ?: throw RuntimeException("Authorization Required")
 } XSBQQFS΍IFMQFSΑΓɺؔ਺Λੜ΍ͤʂ
  16. &YBNQMF QSPUP@PVUEJSUIJSEQBSUZ <<EFQFOEFODJFT>> UBSHFUHJUIVCDPNTUPSNDBUTFSWJDFBQSPUP CSBODINBTUFS JHOPSFE< IPHF  GVHBQSPUP 

    > <<EFQFOEFODJFT>> UBSHFUHJUIVCDPNTUPSNDBUTFSWJDFCQSPUP SFWJTJPOW  <<EFQFOEFODJFT>> UBSHFUHJUIVCDPNTUPSNDBUTFSWJDFD SFWJTJPOW QBUIQBUIQSPUPCVG