SlideShare a Scribd company logo
♥ △ ✿
Play Framework Nul ne sait qui nous sommes
Mélanie 2 janvier 2017
Kévin 4 décembre 2017
Développeurs Back-End
Play Framework Shh .. we have a plan !
La philosophie, le créateur, l’histoire
La structure d'un projet
Les configurations
Le cheminement d’une requête HTTP
Le routing
Les contrôleurs
La couche DAO avec Slick
Les tests
+ Bonus Où retrouve-t-on les
éléments du langage fonctionnel
chers à nos 💓 ?
Option / Either / Future
Play Framework Révisons sinon c’est la session 2
● Option
● Either
● Future
Option[T]
Some[T] None
Either[A, B]
Left[A, B] Right[A, B]
Future
Play Framework
open source → applications web en Java ou en Scala
φ
Play Framework C’est quoi les bails ?
2007: Guillaume Bort (Zengularity SA → Fabernovel Technologies)
Play 1.0: Mai 2008 - Octobre 2009
Play 2.0: Fin 2011 - Mars 2012 (+Sadek Drobi)
Play 2.5: Mars 2016 - Akka Streams
Play 2.6: 23 Juin 2017 - Akka HTTP serveur backend par défaut + Scala 2.12 [...]
Stateless Scala I/O asynchrones
Play Framework Family Tree
Play Framework Les configurations
sbt
● Outil de build
● Ajout des librairies, configuration version Scala/SBT, ajout de tâches
> run
> compile
> test
> test-only maClasseDeTesterCestDouter.scala
> testQuick <mesTestsUnitairesTasVu>
> doc
Play Framework Les configurations
/build.sbt
import Dependencies._
name := """poc-play"""
organization := "com.mrgueritte"
version := "1.0-SNAPSHOT"
scalaVersion := "2.12.6"
lazy val root = (project in file(".")).enablePlugins(PlayScala)
libraryDependencies += "com.typesafe.play" %% "play-slick-evolutions"
% "3.0.3"
resolvers += Repositories.sonatypeOss
Play Framework Les configurations
application.conf
● Paramétrage divers : BD, Filtres, Taille buffers ...
● Format clé-valeur, HOCON
● Système héritage application.conf -> production.conf
● Injecté automatiquement dans le paramétrage de Play
● Possibilité de le charger en custom
Play Framework Les configurations
# Database configuration
# ~~~~~
include "db.conf"
# Evolutions
# ~~~~~
play.evolutions.db.default.autoApply=true
play.evolutions.db.default.autoApplyDowns=true
# Authentication
# ~~~~~
http.secure = false
Play Framework Workflow
l’internet
mondial
Route Action Service DAO DB
HTTP Response
HTTP Request
Routeurs
Route
Contrôleurs Services
Play Framework Painless HTTP Routing
Route = HTTP Verb + URI → Controller Method
# Routes
# This files defines all application routes (Higher priority routes first)
# https://www.playframwork.com/documentation/latest/ScalaRouting
# ~~~~
GET /api/cat/:id controllers.CatsController.get(id: CatId)
-> / order.Routes
-> / owner.Routes
Play Framework Les routeurs exemples
GET /clients/:id controllers.Clients.show(id: Long)
GET /api/list-all controllers.Api.list(version: Option[String])
GET /items/$id<[0-9]+> controllers.Items.show(id: Long)
GET / controllers.Application.show(page = "home")
GET /:page controllers.Application.show(page)
GET /clients controllers.Clients.list(page: Int ?= 1)
GET /api/cat/:id controllers.CatsController.get(id: CatId) ?
Play Framework Painless HTTP Routing
case class CatId(id: UUID)
Play Framework Router Tunning Custom
/build.sbt routesImport ++= "utils.RoutesParser._" :: "utils.CatId" :: Nil
/RoutesParser.scala
object RoutesParser {
implicit object parameterBindableCatId extends PathBindable[CatId] {
override def bind(key: String, catIdStr: String): Either[String, CatId] = {
Try(CatId(UUID.fromString(catIdStr))) match {
case Success(catId) => Right(catId)
case Failure(e) => Left(s"Cannot parse parameter $key as CatId: $e")
}
}
override def unbind(key: String, catId: CatId): String = catId.id.toString
}
}
Exemple d’UUID: 89a7df10-284f-415c-b52a-427acf4c31ae
Play Framework Workflow
Route Action Service DAO DB
HTTP Response
HTTP Request
Routeurs
Route
Contrôleurs Services
Play Framework Dans les contrôleurs y a de l’action
play.api.mvc.Request =>
play.api.mvc.Result
val echo = Action { request =>
Ok(s"Got request [$request]")
}
Play Framework Dans les contrôleurs y a de l’action
Action { Ok("Hello world") }
def hello(name: String) = Action {
Ok("Hello " + name)
}
def echo = Action { request =>
Ok("Got request [" + request + "]")
}
def createHouse() = Action(parse.json[House]) {request =>
Ok(request.body)//House
}
Play Framework Dans les contrôleurs y a de l’action
def createHouseAsync = Action().async { request =>
for {
design <- getPlans()
wood <- getMaterial()
} yield Ok(Json.obj("House plans" -> design, "material" -> wood))
}
Play Framework Authentification
Support pour OAuth2
Guest
Member
OrganizationMember.(Read)
PlatformAdmin
Api(Scope)
[...]
def list() = OrganizationMember(READ)("cats.list").async { implicit request =>
catsService
.listByOrganization(request.organizationId)
.map(Ok(_))
}
=> Future[Result]
Play Framework Les resultats c’est pas du bois
Ok("Hello World!")
→ Content-Type header to text/plain
Ok(<message>Hello World!</message>)
→ application/xml
Ok(<h1>Hello World!</h1>)
→ application/html
Ok(Json.obj(”foo” -> “bar”)) -> application/json
val result = Ok("Hello World!").withHeaders(
CACHE_CONTROL -> "max-age=3600",
ETAG -> "xx")
Play Framework Les resultats c’est pas du bois
Play Framework Workflow
Route Action Service DAO DB
HTTP Response
HTTP Request
Routeurs
Route
Contrôleurs Services
Play Framework & Slick
Play Framework Les configurations de Slick
val slick = "com.typesafe.play” %% "play-slick" % Version.slickVersion
val slickEvolution = "com.typesafe.play" %% "play-slick-evolutions" % Version.slickVersion
/build.sbt
/application.conf
slick.dbs.default.profile = "slick.jdbc.H2Profile$"
slick.dbs.default.db.driver = "org.h2.Driver"
slick.dbs.default.db.url = "jdbc:h2:mem:play;DATABASE_TO_UPPER=false"
slick.dbs.default.profile = "slick.jdbc.PostgresProfile$"
slick.dbs.default.db.user = "user"
slick.dbs.default.db.password = "pswd"
Play Framework Les schémas
object Schemas extends EntityMappers {
class Cats(tag: Tag) extends Table[Cat](tag, "cats") {
def catId = column[CatId]("id")
def name = column[String]("name")
def pedigreeId = column[PedigreeId]("pedigree_id")
def gender = column[Gender]("gender")
def owner = column[Option[OwnerId]]("owner_id")
def dateOfBirth = column[Date]("date_of_birth")
def dateOfDeath = column[Option[Date]]("date_of_death")
override def * =
(catId, name, pedigreeId, gender, owner, dateOfBirth, dateOfDeath) <> (Cat.tupled, Cat.unapply)
}
...
val cats = TableQuery[Cats]
}
Play Framework Les requêtes : “tout est
collection”> SELECT * FROM cats WHERE id = ?
override def getById(id: CatId): DBIO[Option[Cat]] = Schemas.cats.filter(_.id === id).headOption
> INSERT INTO cats VALUES (?,?,? …)
override def create(cat: Cat): DBIO[Unit] = Schemas.cats += cat
> SELECT c.* FROM pedigrees p JOIN cat c ON c.pedigreeId = p.id WHERE p.name = ?
override def getByPedigreeName(name: String): DBIO[Seq[Cat]] = {
for {
cat <- Schemas.cats
pedigree <- Schemas.pedigrees
if pedigree.id === cat.pedigreeId && pedigree.pedigree === name
} yield cat
}
Play Framework Les DBIO
override def getByPedigreeName(name: String): Future[Seq[Cat]] = {
val transaction = (for {
cat <- catsDAO.getByPedigreeName(name)
_ <- DBIO.seq(cat.map(c => catsDAO.delete(c.catId)): _*)
} yield cat).transactionally
db.run(transaction)
}
> Les compositions de requêtes & les transactions
Play Framework Les évolutions
● Situé dans le dossier /conf/evolutions/<dbName>/
● Chaque script est composé de deux parties :
○ Ups! : SQL d’évolution de la BD (INSERT, CREATE …)
○ Downs! : Retour arrière de l’évolution (DROP, ALTER …)
● Une table `play_evolutions` est créée sur la DB => versionnage
# --- !Ups
CREATE TABLE pedigrees(
id UUID NOT NULL,
name VARCHAR(32) NOT NULL
);
…
# --- !Downs
DROP TABLE cats;
DROP TABLE owners;
DROP TABLE pedigrees;
Play Framework Les évolutions, “j’ai tout casséhéhé”
● Modification du script en question
● Marquer le script comme résolu => Application du `!Downs` puis du `!Ups`
Play Framework Injections de dépendances
● Guice est embarqué => utilisation à spécifier build.sbt
● Auto-gestion des dépendances avec @Inject() (java !)
● Runtime
“Use another compile time dependency injection!”
Play Framework Injections de dépendances
ApplicationLoader
● Point entrée unique facilement customisable => `application.conf`
● Chargement de toutes les “parties” de l’application => `Components`
class PocPlayApplicationLoader extends ApplicationLoader {
override def load(context: Context): Application = {
new PocPlayComponents(context).application
}
}
play.application.loader= "di.PocPlayApplicationLoader"
Play Framework Injections de dépendances
Components
class PocPlayComponents(context: Context) extends
BuiltInComponentsFromContext(context)
with HttpFiltersComponents // Add defaults HTTP Filters
with SlickComponents // Add Slick API (connect play to database)
with EvolutionsComponents { // Add Evolutions system
override lazy val dbApi: DBApi = SlickDBApi(slickApi)
applicationEvolutions.start()
...
}
PlayFramework On a parcouru le chemin, on a tenu la distance
class CatsController( catsService: CatsService [...]
def buy(catId: CatId, ownerId: OwnerId) = Action.async { implicit request =>
catsService.buy(catId, ownerId)[...] }
class CatsServiceImpl( catsDAO: CatsDAO, db: DbExecutor [...]
override def buy(catId: CatId, ownerId: OwnerId): Future[Either[ApiError, Void]] =
[...] db.run(catsDAO.updateOwner(catId, ownerId)) [...]
case class CatsDAOSlick()(implicit ec: ExecutionContext) extends CatsDAO {
override def updateOwner(catId: CatId, ownerId: OwnerId): DBIO[Void] = [...]
Play Framework Injections de dépendances
Components - Add dependency injection with macwire
class PocPlayComponents(context: Context) extends
BuiltInComponentsFromContext(context)
with HttpFiltersComponents // Add defaults HTTP Filters
with SlickComponents // Add Slick API (connect play to database)
with EvolutionsComponents { // Add Evolutions system
...
lazy val db: DbExecutor =
slickApi.dbConfig[JdbcProfile](DbName("default")).db
lazy val catsDAO: CatsDAO = new CatsDAOSlick()
lazy val catsService: CatsService = wire[CatsServiceImpl]
lazy val catsController: CatsController = wire[CatsController]
}
Play Framework Tests
Scala Test - Flat Spec et d’autres WordSpec/FreeSpec etc.
class HelloWorldSpec extends Specification {
"The 'Hello world' string" should {
"contain 11 characters" in {
"Hello world" must have size(11)
}
"start with 'Hello'" in {
"Hello world" must startWith('Hello')
}
}
}
[info] HelloWorldSpec:
[info] The 'Hello world' string
[info] - should contain 11 characters
[info] - should start with 'Hello'
[info] ScalaTest
[info] Run completed in 2 seconds, 888 milliseconds.
[info] Total number of tests run: 2
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 2, failed 0, canceled 0,
ignored 0, pending 0
[info] All tests passed.
[info] Passed: Total 2, Failed 0, Errors 0, Passed 2
[success] Total time: 5 s, completed 3 juin 2018
22:13:54
Play Framework Tests
Mock API’s
import play.api.test.Helpers._
class CatsApiSpec extends PocPlayOneAppPerSuite {
…
def createCatApi(body: JsValue) = FakeRequest("POST", "/api/cats").withBody(body)
"Cats API" must {
"have a valid endpoint `create cat API`" in {
...
val Some(response) = route(app, createCatApi(catsBody))
status(response) == CREATED
val json = contentAsJson(response)
...
}
}
}
Play Framework Tests
Mock context
trait PocPlayOneAppPerSuite extends PlaySpec with GuiceOneAppPerSuite { self =>
override lazy val app = {
val context = ApplicationLoader.createContext(new Environment(new java.io.File("."),
ApplicationLoader.getClass.getClassLoader, Mode.Test))
new TestComponents(context).application
}
}
class TestComponents(context: Context) extends PocPlayComponents(context: Context) {
override lazy val configuration = {
val databseConfig = ConfigFactory.parseString(
"""slick.dbs.default.db.url = <database test>""".stripMargin)
context.initialConfiguration.copy(underlying = databseConfig)
}
}
Play Framework Fun with tests
Property-based testing - Scala Check
def catJsonGenerator: Gen[JsObject] = {
for {
name <- Gen.alphaStr.suchThat(_.size < 32)
gender <- Gen.oneOf(const(Gender.Female), const(Gender.Male))
pedigreeId <- const(PedigreeId(UUID.fromString("cc58095b-8d51-4a22-a110-7ace2d921c2c")))
ownerId <- option(const(OwnerId(UUID.fromString("76065e7d-8604-4995-a6ed-33fca30517aa"))))
dateOfBirth <- const(DateTime.now())
} yield buildCatJson(name, gender, pedigreeId, ownerId, dateOfBirth, None)
}
"have a valid endpoint `create cat API`" in {
check {
Prop.forAll(catJsonGenerator) { catsBody: JsObject =>
val Some(response) = route(app, createCatApi(catsBody))
status(response) == CREATED
}
}
}
Play Framework Vous en profitez sans le savoir
Play Framework Merci d’être là, super sympa
https://github.com/kevin-margueritte/poc-playframework
Des questions ?
Vous êtes sûrs ? On va être coupés, y a un tunnel.

More Related Content

What's hot

Node js introduction
Node js introductionNode js introduction
Node js introduction
Alex Su
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统
yiditushe
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook
guoqing75
 
You must know about CodeIgniter Popular Library
You must know about CodeIgniter Popular LibraryYou must know about CodeIgniter Popular Library
You must know about CodeIgniter Popular Library
Bo-Yi Wu
 
Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015
Naresha K
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
patter
 

What's hot (20)

Play!ng with scala
Play!ng with scalaPlay!ng with scala
Play!ng with scala
 
Node js introduction
Node js introductionNode js introduction
Node js introduction
 
web2py:Web development like a boss
web2py:Web development like a bossweb2py:Web development like a boss
web2py:Web development like a boss
 
Beyond Phoenix
Beyond PhoenixBeyond Phoenix
Beyond Phoenix
 
Scala active record
Scala active recordScala active record
Scala active record
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统
 
Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング
Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディングXitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング
Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング
 
Immutable Deployments with AWS CloudFormation and AWS Lambda
Immutable Deployments with AWS CloudFormation and AWS LambdaImmutable Deployments with AWS CloudFormation and AWS Lambda
Immutable Deployments with AWS CloudFormation and AWS Lambda
 
CodeIgniter 3.0
CodeIgniter 3.0CodeIgniter 3.0
CodeIgniter 3.0
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook
 
Gradle: The Build system you have been waiting for
Gradle: The Build system you have been waiting forGradle: The Build system you have been waiting for
Gradle: The Build system you have been waiting for
 
Datagrids with Symfony 2, Backbone and Backgrid
Datagrids with Symfony 2, Backbone and BackgridDatagrids with Symfony 2, Backbone and Backgrid
Datagrids with Symfony 2, Backbone and Backgrid
 
Introduction to Flask Micro Framework
Introduction to Flask Micro FrameworkIntroduction to Flask Micro Framework
Introduction to Flask Micro Framework
 
You must know about CodeIgniter Popular Library
You must know about CodeIgniter Popular LibraryYou must know about CodeIgniter Popular Library
You must know about CodeIgniter Popular Library
 
Testing Web Applications with GEB
Testing Web Applications with GEBTesting Web Applications with GEB
Testing Web Applications with GEB
 
Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015
 
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
 
Redis for your boss
Redis for your bossRedis for your boss
Redis for your boss
 
Redis for your boss 2.0
Redis for your boss 2.0Redis for your boss 2.0
Redis for your boss 2.0
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
 

Similar to FP - Découverte de Play Framework Scala

Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018
Ortus Solutions, Corp
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
Tom Croucher
 
OSCON 2014 - API Ecosystem with Scala, Scalatra, and Swagger at Netflix
OSCON 2014 - API Ecosystem with Scala, Scalatra, and Swagger at NetflixOSCON 2014 - API Ecosystem with Scala, Scalatra, and Swagger at Netflix
OSCON 2014 - API Ecosystem with Scala, Scalatra, and Swagger at Netflix
Manish Pandit
 

Similar to FP - Découverte de Play Framework Scala (20)

Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018
 
Into The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and dockerInto The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and docker
 
Charla EHU Noviembre 2014 - Desarrollo Web
Charla EHU Noviembre 2014 - Desarrollo WebCharla EHU Noviembre 2014 - Desarrollo Web
Charla EHU Noviembre 2014 - Desarrollo Web
 
Go Web Development
Go Web DevelopmentGo Web Development
Go Web Development
 
Node.js vs Play Framework (with Japanese subtitles)
Node.js vs Play Framework (with Japanese subtitles)Node.js vs Play Framework (with Japanese subtitles)
Node.js vs Play Framework (with Japanese subtitles)
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.js
 
Node.js vs Play Framework
Node.js vs Play FrameworkNode.js vs Play Framework
Node.js vs Play Framework
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 
Nodejs - A quick tour (v6)
Nodejs - A quick tour (v6)Nodejs - A quick tour (v6)
Nodejs - A quick tour (v6)
 
Wider than rails
Wider than railsWider than rails
Wider than rails
 
Express Presentation
Express PresentationExpress Presentation
Express Presentation
 
OSCON 2014 - API Ecosystem with Scala, Scalatra, and Swagger at Netflix
OSCON 2014 - API Ecosystem with Scala, Scalatra, and Swagger at NetflixOSCON 2014 - API Ecosystem with Scala, Scalatra, and Swagger at Netflix
OSCON 2014 - API Ecosystem with Scala, Scalatra, and Swagger at Netflix
 
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & MobileIVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
 
Node.js - async for the rest of us.
Node.js - async for the rest of us.Node.js - async for the rest of us.
Node.js - async for the rest of us.
 
Play framework
Play frameworkPlay framework
Play framework
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gears
 
前端概述
前端概述前端概述
前端概述
 
Sprockets
SprocketsSprockets
Sprockets
 
Beyond Cookies, Persistent Storage For Web Applications Web Directions North ...
Beyond Cookies, Persistent Storage For Web Applications Web Directions North ...Beyond Cookies, Persistent Storage For Web Applications Web Directions North ...
Beyond Cookies, Persistent Storage For Web Applications Web Directions North ...
 
Evolution of Spark APIs
Evolution of Spark APIsEvolution of Spark APIs
Evolution of Spark APIs
 

Recently uploaded

Hall booking system project report .pdf
Hall booking system project report  .pdfHall booking system project report  .pdf
Hall booking system project report .pdf
Kamal Acharya
 
Online blood donation management system project.pdf
Online blood donation management system project.pdfOnline blood donation management system project.pdf
Online blood donation management system project.pdf
Kamal Acharya
 
Digital Signal Processing Lecture notes n.pdf
Digital Signal Processing Lecture notes n.pdfDigital Signal Processing Lecture notes n.pdf
Digital Signal Processing Lecture notes n.pdf
AbrahamGadissa
 
Standard Reomte Control Interface - Neometrix
Standard Reomte Control Interface - NeometrixStandard Reomte Control Interface - Neometrix
Standard Reomte Control Interface - Neometrix
Neometrix_Engineering_Pvt_Ltd
 
Fruit shop management system project report.pdf
Fruit shop management system project report.pdfFruit shop management system project report.pdf
Fruit shop management system project report.pdf
Kamal Acharya
 

Recently uploaded (20)

Pharmacy management system project report..pdf
Pharmacy management system project report..pdfPharmacy management system project report..pdf
Pharmacy management system project report..pdf
 
Hall booking system project report .pdf
Hall booking system project report  .pdfHall booking system project report  .pdf
Hall booking system project report .pdf
 
Halogenation process of chemical process industries
Halogenation process of chemical process industriesHalogenation process of chemical process industries
Halogenation process of chemical process industries
 
Electrostatic field in a coaxial transmission line
Electrostatic field in a coaxial transmission lineElectrostatic field in a coaxial transmission line
Electrostatic field in a coaxial transmission line
 
HYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generationHYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generation
 
Arduino based vehicle speed tracker project
Arduino based vehicle speed tracker projectArduino based vehicle speed tracker project
Arduino based vehicle speed tracker project
 
Online blood donation management system project.pdf
Online blood donation management system project.pdfOnline blood donation management system project.pdf
Online blood donation management system project.pdf
 
Construction method of steel structure space frame .pptx
Construction method of steel structure space frame .pptxConstruction method of steel structure space frame .pptx
Construction method of steel structure space frame .pptx
 
Digital Signal Processing Lecture notes n.pdf
Digital Signal Processing Lecture notes n.pdfDigital Signal Processing Lecture notes n.pdf
Digital Signal Processing Lecture notes n.pdf
 
Democratizing Fuzzing at Scale by Abhishek Arya
Democratizing Fuzzing at Scale by Abhishek AryaDemocratizing Fuzzing at Scale by Abhishek Arya
Democratizing Fuzzing at Scale by Abhishek Arya
 
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptx
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptxCloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptx
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptx
 
Top 13 Famous Civil Engineering Scientist
Top 13 Famous Civil Engineering ScientistTop 13 Famous Civil Engineering Scientist
Top 13 Famous Civil Engineering Scientist
 
KIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and Clustering
KIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and ClusteringKIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and Clustering
KIT-601 Lecture Notes-UNIT-4.pdf Frequent Itemsets and Clustering
 
Standard Reomte Control Interface - Neometrix
Standard Reomte Control Interface - NeometrixStandard Reomte Control Interface - Neometrix
Standard Reomte Control Interface - Neometrix
 
Peek implant persentation - Copy (1).pdf
Peek implant persentation - Copy (1).pdfPeek implant persentation - Copy (1).pdf
Peek implant persentation - Copy (1).pdf
 
İTÜ CAD and Reverse Engineering Workshop
İTÜ CAD and Reverse Engineering WorkshopİTÜ CAD and Reverse Engineering Workshop
İTÜ CAD and Reverse Engineering Workshop
 
Fruit shop management system project report.pdf
Fruit shop management system project report.pdfFruit shop management system project report.pdf
Fruit shop management system project report.pdf
 
shape functions of 1D and 2 D rectangular elements.pptx
shape functions of 1D and 2 D rectangular elements.pptxshape functions of 1D and 2 D rectangular elements.pptx
shape functions of 1D and 2 D rectangular elements.pptx
 
RESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdf
RESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdfRESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdf
RESORT MANAGEMENT AND RESERVATION SYSTEM PROJECT REPORT.pdf
 
NO1 Pandit Amil Baba In Bahawalpur, Sargodha, Sialkot, Sheikhupura, Rahim Yar...
NO1 Pandit Amil Baba In Bahawalpur, Sargodha, Sialkot, Sheikhupura, Rahim Yar...NO1 Pandit Amil Baba In Bahawalpur, Sargodha, Sialkot, Sheikhupura, Rahim Yar...
NO1 Pandit Amil Baba In Bahawalpur, Sargodha, Sialkot, Sheikhupura, Rahim Yar...
 

FP - Découverte de Play Framework Scala

  • 2. Play Framework Nul ne sait qui nous sommes Mélanie 2 janvier 2017 Kévin 4 décembre 2017 Développeurs Back-End
  • 3. Play Framework Shh .. we have a plan ! La philosophie, le créateur, l’histoire La structure d'un projet Les configurations Le cheminement d’une requête HTTP Le routing Les contrôleurs La couche DAO avec Slick Les tests + Bonus Où retrouve-t-on les éléments du langage fonctionnel chers à nos 💓 ? Option / Either / Future
  • 4. Play Framework Révisons sinon c’est la session 2 ● Option ● Either ● Future Option[T] Some[T] None Either[A, B] Left[A, B] Right[A, B] Future
  • 5. Play Framework open source → applications web en Java ou en Scala φ
  • 6. Play Framework C’est quoi les bails ? 2007: Guillaume Bort (Zengularity SA → Fabernovel Technologies) Play 1.0: Mai 2008 - Octobre 2009 Play 2.0: Fin 2011 - Mars 2012 (+Sadek Drobi) Play 2.5: Mars 2016 - Akka Streams Play 2.6: 23 Juin 2017 - Akka HTTP serveur backend par défaut + Scala 2.12 [...] Stateless Scala I/O asynchrones
  • 8. Play Framework Les configurations sbt ● Outil de build ● Ajout des librairies, configuration version Scala/SBT, ajout de tâches > run > compile > test > test-only maClasseDeTesterCestDouter.scala > testQuick <mesTestsUnitairesTasVu> > doc
  • 9. Play Framework Les configurations /build.sbt import Dependencies._ name := """poc-play""" organization := "com.mrgueritte" version := "1.0-SNAPSHOT" scalaVersion := "2.12.6" lazy val root = (project in file(".")).enablePlugins(PlayScala) libraryDependencies += "com.typesafe.play" %% "play-slick-evolutions" % "3.0.3" resolvers += Repositories.sonatypeOss
  • 10. Play Framework Les configurations application.conf ● Paramétrage divers : BD, Filtres, Taille buffers ... ● Format clé-valeur, HOCON ● Système héritage application.conf -> production.conf ● Injecté automatiquement dans le paramétrage de Play ● Possibilité de le charger en custom
  • 11. Play Framework Les configurations # Database configuration # ~~~~~ include "db.conf" # Evolutions # ~~~~~ play.evolutions.db.default.autoApply=true play.evolutions.db.default.autoApplyDowns=true # Authentication # ~~~~~ http.secure = false
  • 12. Play Framework Workflow l’internet mondial Route Action Service DAO DB HTTP Response HTTP Request Routeurs Route Contrôleurs Services
  • 13. Play Framework Painless HTTP Routing Route = HTTP Verb + URI → Controller Method # Routes # This files defines all application routes (Higher priority routes first) # https://www.playframwork.com/documentation/latest/ScalaRouting # ~~~~ GET /api/cat/:id controllers.CatsController.get(id: CatId) -> / order.Routes -> / owner.Routes
  • 14. Play Framework Les routeurs exemples GET /clients/:id controllers.Clients.show(id: Long) GET /api/list-all controllers.Api.list(version: Option[String]) GET /items/$id<[0-9]+> controllers.Items.show(id: Long) GET / controllers.Application.show(page = "home") GET /:page controllers.Application.show(page) GET /clients controllers.Clients.list(page: Int ?= 1) GET /api/cat/:id controllers.CatsController.get(id: CatId) ?
  • 15. Play Framework Painless HTTP Routing case class CatId(id: UUID)
  • 16. Play Framework Router Tunning Custom /build.sbt routesImport ++= "utils.RoutesParser._" :: "utils.CatId" :: Nil /RoutesParser.scala object RoutesParser { implicit object parameterBindableCatId extends PathBindable[CatId] { override def bind(key: String, catIdStr: String): Either[String, CatId] = { Try(CatId(UUID.fromString(catIdStr))) match { case Success(catId) => Right(catId) case Failure(e) => Left(s"Cannot parse parameter $key as CatId: $e") } } override def unbind(key: String, catId: CatId): String = catId.id.toString } } Exemple d’UUID: 89a7df10-284f-415c-b52a-427acf4c31ae
  • 17. Play Framework Workflow Route Action Service DAO DB HTTP Response HTTP Request Routeurs Route Contrôleurs Services
  • 18. Play Framework Dans les contrôleurs y a de l’action play.api.mvc.Request => play.api.mvc.Result val echo = Action { request => Ok(s"Got request [$request]") }
  • 19. Play Framework Dans les contrôleurs y a de l’action Action { Ok("Hello world") } def hello(name: String) = Action { Ok("Hello " + name) } def echo = Action { request => Ok("Got request [" + request + "]") } def createHouse() = Action(parse.json[House]) {request => Ok(request.body)//House }
  • 20. Play Framework Dans les contrôleurs y a de l’action def createHouseAsync = Action().async { request => for { design <- getPlans() wood <- getMaterial() } yield Ok(Json.obj("House plans" -> design, "material" -> wood)) }
  • 21. Play Framework Authentification Support pour OAuth2 Guest Member OrganizationMember.(Read) PlatformAdmin Api(Scope) [...] def list() = OrganizationMember(READ)("cats.list").async { implicit request => catsService .listByOrganization(request.organizationId) .map(Ok(_)) } => Future[Result]
  • 22. Play Framework Les resultats c’est pas du bois Ok("Hello World!") → Content-Type header to text/plain Ok(<message>Hello World!</message>) → application/xml Ok(<h1>Hello World!</h1>) → application/html Ok(Json.obj(”foo” -> “bar”)) -> application/json val result = Ok("Hello World!").withHeaders( CACHE_CONTROL -> "max-age=3600", ETAG -> "xx")
  • 23. Play Framework Les resultats c’est pas du bois
  • 24. Play Framework Workflow Route Action Service DAO DB HTTP Response HTTP Request Routeurs Route Contrôleurs Services
  • 26. Play Framework Les configurations de Slick val slick = "com.typesafe.play” %% "play-slick" % Version.slickVersion val slickEvolution = "com.typesafe.play" %% "play-slick-evolutions" % Version.slickVersion /build.sbt /application.conf slick.dbs.default.profile = "slick.jdbc.H2Profile$" slick.dbs.default.db.driver = "org.h2.Driver" slick.dbs.default.db.url = "jdbc:h2:mem:play;DATABASE_TO_UPPER=false" slick.dbs.default.profile = "slick.jdbc.PostgresProfile$" slick.dbs.default.db.user = "user" slick.dbs.default.db.password = "pswd"
  • 27. Play Framework Les schémas object Schemas extends EntityMappers { class Cats(tag: Tag) extends Table[Cat](tag, "cats") { def catId = column[CatId]("id") def name = column[String]("name") def pedigreeId = column[PedigreeId]("pedigree_id") def gender = column[Gender]("gender") def owner = column[Option[OwnerId]]("owner_id") def dateOfBirth = column[Date]("date_of_birth") def dateOfDeath = column[Option[Date]]("date_of_death") override def * = (catId, name, pedigreeId, gender, owner, dateOfBirth, dateOfDeath) <> (Cat.tupled, Cat.unapply) } ... val cats = TableQuery[Cats] }
  • 28. Play Framework Les requêtes : “tout est collection”> SELECT * FROM cats WHERE id = ? override def getById(id: CatId): DBIO[Option[Cat]] = Schemas.cats.filter(_.id === id).headOption > INSERT INTO cats VALUES (?,?,? …) override def create(cat: Cat): DBIO[Unit] = Schemas.cats += cat > SELECT c.* FROM pedigrees p JOIN cat c ON c.pedigreeId = p.id WHERE p.name = ? override def getByPedigreeName(name: String): DBIO[Seq[Cat]] = { for { cat <- Schemas.cats pedigree <- Schemas.pedigrees if pedigree.id === cat.pedigreeId && pedigree.pedigree === name } yield cat }
  • 29. Play Framework Les DBIO override def getByPedigreeName(name: String): Future[Seq[Cat]] = { val transaction = (for { cat <- catsDAO.getByPedigreeName(name) _ <- DBIO.seq(cat.map(c => catsDAO.delete(c.catId)): _*) } yield cat).transactionally db.run(transaction) } > Les compositions de requêtes & les transactions
  • 30. Play Framework Les évolutions ● Situé dans le dossier /conf/evolutions/<dbName>/ ● Chaque script est composé de deux parties : ○ Ups! : SQL d’évolution de la BD (INSERT, CREATE …) ○ Downs! : Retour arrière de l’évolution (DROP, ALTER …) ● Une table `play_evolutions` est créée sur la DB => versionnage # --- !Ups CREATE TABLE pedigrees( id UUID NOT NULL, name VARCHAR(32) NOT NULL ); … # --- !Downs DROP TABLE cats; DROP TABLE owners; DROP TABLE pedigrees;
  • 31. Play Framework Les évolutions, “j’ai tout casséhéhé” ● Modification du script en question ● Marquer le script comme résolu => Application du `!Downs` puis du `!Ups`
  • 32. Play Framework Injections de dépendances ● Guice est embarqué => utilisation à spécifier build.sbt ● Auto-gestion des dépendances avec @Inject() (java !) ● Runtime “Use another compile time dependency injection!”
  • 33. Play Framework Injections de dépendances ApplicationLoader ● Point entrée unique facilement customisable => `application.conf` ● Chargement de toutes les “parties” de l’application => `Components` class PocPlayApplicationLoader extends ApplicationLoader { override def load(context: Context): Application = { new PocPlayComponents(context).application } } play.application.loader= "di.PocPlayApplicationLoader"
  • 34. Play Framework Injections de dépendances Components class PocPlayComponents(context: Context) extends BuiltInComponentsFromContext(context) with HttpFiltersComponents // Add defaults HTTP Filters with SlickComponents // Add Slick API (connect play to database) with EvolutionsComponents { // Add Evolutions system override lazy val dbApi: DBApi = SlickDBApi(slickApi) applicationEvolutions.start() ... }
  • 35. PlayFramework On a parcouru le chemin, on a tenu la distance class CatsController( catsService: CatsService [...] def buy(catId: CatId, ownerId: OwnerId) = Action.async { implicit request => catsService.buy(catId, ownerId)[...] } class CatsServiceImpl( catsDAO: CatsDAO, db: DbExecutor [...] override def buy(catId: CatId, ownerId: OwnerId): Future[Either[ApiError, Void]] = [...] db.run(catsDAO.updateOwner(catId, ownerId)) [...] case class CatsDAOSlick()(implicit ec: ExecutionContext) extends CatsDAO { override def updateOwner(catId: CatId, ownerId: OwnerId): DBIO[Void] = [...]
  • 36. Play Framework Injections de dépendances Components - Add dependency injection with macwire class PocPlayComponents(context: Context) extends BuiltInComponentsFromContext(context) with HttpFiltersComponents // Add defaults HTTP Filters with SlickComponents // Add Slick API (connect play to database) with EvolutionsComponents { // Add Evolutions system ... lazy val db: DbExecutor = slickApi.dbConfig[JdbcProfile](DbName("default")).db lazy val catsDAO: CatsDAO = new CatsDAOSlick() lazy val catsService: CatsService = wire[CatsServiceImpl] lazy val catsController: CatsController = wire[CatsController] }
  • 37. Play Framework Tests Scala Test - Flat Spec et d’autres WordSpec/FreeSpec etc. class HelloWorldSpec extends Specification { "The 'Hello world' string" should { "contain 11 characters" in { "Hello world" must have size(11) } "start with 'Hello'" in { "Hello world" must startWith('Hello') } } } [info] HelloWorldSpec: [info] The 'Hello world' string [info] - should contain 11 characters [info] - should start with 'Hello' [info] ScalaTest [info] Run completed in 2 seconds, 888 milliseconds. [info] Total number of tests run: 2 [info] Suites: completed 1, aborted 0 [info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 0 [info] All tests passed. [info] Passed: Total 2, Failed 0, Errors 0, Passed 2 [success] Total time: 5 s, completed 3 juin 2018 22:13:54
  • 38. Play Framework Tests Mock API’s import play.api.test.Helpers._ class CatsApiSpec extends PocPlayOneAppPerSuite { … def createCatApi(body: JsValue) = FakeRequest("POST", "/api/cats").withBody(body) "Cats API" must { "have a valid endpoint `create cat API`" in { ... val Some(response) = route(app, createCatApi(catsBody)) status(response) == CREATED val json = contentAsJson(response) ... } } }
  • 39. Play Framework Tests Mock context trait PocPlayOneAppPerSuite extends PlaySpec with GuiceOneAppPerSuite { self => override lazy val app = { val context = ApplicationLoader.createContext(new Environment(new java.io.File("."), ApplicationLoader.getClass.getClassLoader, Mode.Test)) new TestComponents(context).application } } class TestComponents(context: Context) extends PocPlayComponents(context: Context) { override lazy val configuration = { val databseConfig = ConfigFactory.parseString( """slick.dbs.default.db.url = <database test>""".stripMargin) context.initialConfiguration.copy(underlying = databseConfig) } }
  • 40. Play Framework Fun with tests Property-based testing - Scala Check def catJsonGenerator: Gen[JsObject] = { for { name <- Gen.alphaStr.suchThat(_.size < 32) gender <- Gen.oneOf(const(Gender.Female), const(Gender.Male)) pedigreeId <- const(PedigreeId(UUID.fromString("cc58095b-8d51-4a22-a110-7ace2d921c2c"))) ownerId <- option(const(OwnerId(UUID.fromString("76065e7d-8604-4995-a6ed-33fca30517aa")))) dateOfBirth <- const(DateTime.now()) } yield buildCatJson(name, gender, pedigreeId, ownerId, dateOfBirth, None) } "have a valid endpoint `create cat API`" in { check { Prop.forAll(catJsonGenerator) { catsBody: JsObject => val Some(response) = route(app, createCatApi(catsBody)) status(response) == CREATED } } }
  • 41. Play Framework Vous en profitez sans le savoir
  • 42. Play Framework Merci d’être là, super sympa https://github.com/kevin-margueritte/poc-playframework Des questions ? Vous êtes sûrs ? On va être coupés, y a un tunnel.

Editor's Notes

  1. Support code -> https://github.com/kevin-margueritte/poc-playframework
  2. Mélou
  3. Mélou
  4. Mélou
  5. Mélou + Kevin
  6. Kévin
  7. Mélou
  8. Mélou
  9. def Hello(name: String) = Action { request => } def createTruc() = Action(parse.json[House]) { request => println(request.body) // House } def createAsync = Action().async { request => for { res1 <- getWS1() res2 <- getWS2() } yield Ok(Json.obj("res1" -> res1, "res" -> res2)) }
  10. def Hello(name: String) = Action { request => } def createTruc() = Action(parse.json[House]) { request => println(request.body) // House } def createAsync = Action().async { request => for { res1 <- getWS1() res2 <- getWS2() } yield Ok(Json.obj("res1" -> res1, "res" -> res2)) }
  11. Etaler les exemples
  12. Kévin
  13. Kevinou
  14. Kevinou
  15. Kevinou
  16. Kevin override def create(cat: Cat): DBIO[Unit] = (Schemas.cats += cat).map(_ => Unit) Request Join à revoir
  17. Kevin override def create(cat: Cat): DBIO[Unit] = (Schemas.cats += cat).map(_ => Unit) Request Join à revoir
  18. Mélanie
  19. Mélanie
  20. Mélou