Commit c688532c authored by Raphael Mäder's avatar Raphael Mäder

Update `play-cms` version, introduce vavr.io

Use vavr.io for the CRUD example, which provides better functional
programming for Java.

Update the `play-cms-demo` app with the best practices from the
RASCHCM/LTP project.

Issue: INTWB-289
parent fffe24c1
# EditorConfig is awesome: http://editorconfig.org
# top-most EditorConfig file
root = true
[*]
end_of_line = lf
charset = utf-8
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.{scala,yml,sbt}]
indent_size = 2
[*.{md,xml,sh}]
indent_size = 4
# SBT
project/.*
project/project
project/.boot
project/.ivy
project/.sbtboot
.sbtopts
.sbt
target
/server/target
/client/target
/server/logs
tmp
.history
dist
/.idea
/*.iml
/out
/.idea_modules
/.classpath
/.project
/RUNNING_PID
/.settings
/server/conf/local.conf
/server/public/images/upload
play-cms
.ivy2
.oracle_jre_usage
.sbtopts
# Scala.js
/shared/.js
/shared/.jvm
# NPM
node_modules
# IntelliJ IDEA
.idea
*.iml
# Others
/logs
/tmp
/dist
/uploads
/modules
/lib
/version.sbt
/devops/.env
/devops/ansible/artifacts
/devops/docker/db/*.sql
/devops/docker/jenkins/jenkins-cli.jar
-Dplaycms.version=local
# Ensure line feed at file end because otherwise sbt in docker will ignore the last parameter
\ No newline at end of file
# Ensure line feed at file end because otherwise sbt in docker will ignore the last parameter
......@@ -13,7 +13,7 @@ Either do a local installation (if you have the play framework installed) using
### Local installation ###
Make sure you have the play framework 2.4+ installed. Checkout the project and run
Make sure you have the play framework 2.5+ installed. Checkout the project and run
sbt run
......
import com.typesafe.sbt.SbtNativePackager.autoImport._
import sbt.Keys._
import sbt.Project.projectToRef
import PlayKeys._
// The Typesafe repository
//resolvers ++= Seq(
// Resolver.sonatypeRepo("releases"),
// Resolver.bintrayRepo("insign", "play-cms")
//)
scalaVersion := Settings.versions.scala
lazy val server = Settings.playcms(Project("server", file("server")))
lazy val server = (project in file("server"))
.settings(
name := Settings.name,
organization := Settings.organization,
......@@ -20,23 +11,17 @@ lazy val server = Settings.playcms(Project("server", file("server")))
scalaVersion := Settings.versions.scala,
scalacOptions ++= Settings.scalacOptions,
libraryDependencies ++= Settings.jvmDependencies.value,
libraryDependencies ++= Settings.playcms.libraryDependencies,
// connect to the client project
scalaJSProjects := clients, // remove this if you don't use scala-js
pipelineStages := Seq(scalaJSProd, uglify, digest, gzip), // remove scalaJSProd if you don't use scala-js
scalaJSProjects := Seq(client),
pipelineStages in Assets := Seq(scalaJSPipeline),
pipelineStages := Seq(uglify, digest, gzip),
compile in Compile := ((compile in Compile) dependsOn scalaJSPipeline).value,
resolvers ++= Settings.resolvers.value,
// don't fork during tests
Keys.fork in Test := false,
// a simple test support of non-dockerized application(for the usage of sbt:test outside of docker)
javaOptions in Test += "-Dconfig.file=conf/application.test.conf",
routesGenerator := InjectedRoutesGenerator,
devSettings += ("play.http.router", "metronic.Routes"),
devSettings += ("play.http.router", "commons.Routes"),
devSettings += ("play.http.router", "cms.Routes"),
// fixes problem with loading entities in prod mode,
// see: https://github.com/playframework/playframework/issues/4590
PlayKeys.externalizeResources := false,
......@@ -52,59 +37,13 @@ lazy val server = Settings.playcms(Project("server", file("server")))
// compress CSS
LessKeys.compress in Assets := true
)
.enablePlugins(PlayJava)
.aggregate(clients.map(projectToRef): _*) // remove this if you don't use scala-js
.dependsOn(sharedJVM) // remove this if you don't use scala-js
// Native packaging options
// Enable .deb builds
// See: http://www.scala-sbt.org/sbt-native-packager/formats/debian.html
// TODO: Play needs custom treatment for pid file and application.conf: http://www.scala-sbt.org/sbt-native-packager/topics/play.html
//enablePlugins(DebianPlugin)
//
//name := "play-cms-demo"
//
//version := "1.0-SNAPSHOT"
//
//maintainer := "Martin Bachmann <m.bachmann@insign.ch>"
//
//packageSummary := "The play-cms demo application."
//
//packageDescription := A package description of our software,
// with multiple lines."""
//
//
//// Settings for pid file and application.conf
//// See: http://www.scala-sbt.org/sbt-native-packager/topics/play.html
//javaOptions in Universal ++= Seq(
// // JVM memory tuning
// "-J-Xmx1024m",
// "-J-Xms512m",
//
// // Since play uses separate pidfile we have to provide it with a proper path
// s"-Dpidfile.path=/var/run/${packageName.value}/play.pid",
//
// // Use separate configuration file for production environment
// s"-Dconfig.file=/usr/share/${packageName.value}/conf/production.conf",
//
// // Use separate logger configuration file for production environment
// s"-Dlogger.file=/usr/share/${packageName.value}/conf/production-logger.xml"
//)
// Enable docker builds
// Build using: sbt docker:publishLocal
// See: http://www.scala-sbt.org/sbt-native-packager/formats/docker.html or https://github.com/muuki88/sbt-native-packager-examples/tree/master/play-2.3
// If you want to develop the play-* modules, you need to make next steps:
// - Clone play-cms
// git clone git@bitbucket.org:insigngmbh/play-cms.git play-cms
// - Call sbt with sys prop "-Dplaycms.devmode=true" or put .sbopts file
// into the project root with with the line: "-J-Dplaycms.devmode=true"
.enablePlugins(PlayJava, SbtWeb)
.dependsOn(sharedJvm)
.devModules(
"ch.insign" %% "play-cms" % Settings.playCmsVersion as "cms" at "modules/play-cms" when Settings.playCmsLocal,
"ch.insign" %% "play-auth" % Settings.playCmsVersion as "auth" at "modules/play-cms" when Settings.playCmsLocal,
"ch.insign" %% "play-commons" % Settings.playCmsVersion as "commons" at "modules/play-cms" when Settings.playCmsLocal,
"ch.insign" %% "play-theme-metronic" % Settings.playCmsVersion as "metronic" at "modules/play-cms" when Settings.playCmsLocal)
//////////////////////////
// ScalaJs Integration
......@@ -120,21 +59,6 @@ lazy val server = Settings.playcms(Project("server", file("server")))
// - remove sample-implementation of Autowire-API as provided in Application, at the following two places: Application.scala::autowireApi and ApiService.scala
// - remove dependencies for unused project in project/Settings.scala
// crossProject for configuring a JS/JVM/shared structure
lazy val shared = (crossProject.crossType(CrossType.Pure) in file("shared"))
.settings(
scalaVersion := Settings.versions.scala,
libraryDependencies ++= Settings.sharedDependencies.value,
resolvers ++= Settings.resolvers.value
)
// set up settings specific to the JS project
.jsConfigure(_ enablePlugins ScalaJSPlay)
lazy val sharedJVM = shared.jvm.settings(name := "sharedJVM")
lazy val sharedJS = shared.js.settings(name := "sharedJS")
// instantiate the scala-js project
lazy val client: Project = (project in file("client"))
.settings(
......@@ -150,57 +74,44 @@ lazy val client: Project = (project in file("client"))
// yes, we want to package JS dependencies
skip in packageJSDependencies := false,
// use Scala.js provided launcher code to start the client app
persistLauncher := true,
persistLauncher in Compile := true,
persistLauncher in Test := false,
scalaJSUseMainModuleInitializer := true,
scalaJSUseMainModuleInitializer in Compile := true,
scalaJSUseMainModuleInitializer in Test := false,
// use uTest framework for tests
testFrameworks += new TestFramework("utest.runner.Framework"),
resolvers ++= Settings.resolvers.value
// Scala-Js Workbench (Live-Reload and such things)
// workbenchSettings,
// updateBrowsers <<= updateBrowsers.triggeredBy(fastOptJS in Compile),
// bootSnippet := "App().bootstrap();",
// localUrl := ("127.0.0.1", 12345)
)
.enablePlugins(ScalaJSPlugin, ScalaJSPlay)
.dependsOn(sharedJS)
.enablePlugins(ScalaJSPlugin, ScalaJSWeb)
.dependsOn(sharedJs)
// Client projects (just one in this case)
lazy val clients = Seq(client)
// crossProject for configuring a JS/JVM/shared structure
lazy val shared = (crossProject.crossType(CrossType.Pure) in file("shared"))
.settings(
scalaVersion := Settings.versions.scala,
libraryDependencies ++= Settings.sharedDependencies.value,
resolvers ++= Settings.resolvers.value
)
.jsConfigure(_ enablePlugins ScalaJSWeb)
lazy val sharedJvm = shared.jvm
lazy val sharedJs = shared.js
//////////////////////////
// End of ScalaJs Integration
//////////////////////////
//scalaJSStage in Global := FastOptStage
//
//
//persistLauncher in Compile := false
//
//persistLauncher in Test := false
//
enablePlugins(DockerPlugin)
maintainer in Docker := "Martin Bachmann <m.bachmann@insign.ch>"
packageSummary in Docker := "The play-cms demo application."
aggregate in Docker := false
dockerRepository := Some("insign-docker-registry.bintray.io")
// Fixes: compilation error "File name too long" which can happen on some encrypted or legacy file systems.
// Please see [SI-3623](https://issues.scala-lang.org/browse/SI-3623) for more details.
scalacOptions ++= Seq("-Xmax-classfile-name","100")
// Resolve only newly added dependencies
updateOptions := updateOptions.value.withCachedResolution(true)
// loads the Play server project at sbt startup
onLoad in Global := (Command.process("project server", _: State)) compose (onLoad in Global).value
\ No newline at end of file
onLoad in Global := (Command.process("project server", _: State)) compose (onLoad in Global).value
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/scala" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
package frontend
import autowire._
import boopickle.Default._
import frontend.controllers.UserController
import frontend.logger._
import frontend.services.AjaxClient
import org.querki.jquery._
import org.scalajs.dom._
import playcms.shared.Api
import shared.Api
import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
import scala.scalajs.js
import scala.scalajs.js._
import scala.scalajs.js.timers._
import scala.scalajs.js.annotation._
import js.Dynamic.{global => g}
import js.Dynamic.{literal => l}
import scala.scalajs.runtime.UndefinedBehaviorError
import autowire._
import boopickle.Default._
import frontend.controllers.UserController
import logger._
import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
@JSExport("App")
object App extends js.JSApp {
object App extends js.JSApp {
/**
* Main Entry-Point when not triggered by Script-Tag in Html-Head
*/
......@@ -41,12 +35,10 @@ object App extends js.JSApp {
@JSExport("bootstrap")
def bootstrap(): Unit = {
console.log("starting frontend application")
console.log("starting frontend application")
UserController.init
AjaxClient[Api].simpleAjaxCall("return me this string").call().map((response) => console.log("Server responded with: " + response) )
UserController.logUsers()
}
}
package frontend.controllers
import autowire._
import boopickle.Default._
import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
/**
* @author Urs Honegger &lt;u.honegger@insign.ch&gt;
*/
trait AbstractController {
var serversideLogging = false
def enableServersideLogging(boolean: Boolean) = serversideLogging = boolean
}
package frontend.controllers
import autowire._
import boopickle.Default._
import frontend.services.AjaxClient
import org.querki.jquery._
import org.scalajs.dom._
import playcms.shared.Api
import shared.Api
import scala.scalajs.js
import scala.scalajs.js.Dynamic.{global => g, literal => l}
import scala.scalajs.js.annotation._
import autowire._
import boopickle.Default._
import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
import scala.concurrent.ExecutionContext.Implicits.global
object UserController extends AbstractController {
/**
* Initialize all User-specific functionality.
* Event binding should happen here
*/
def init() = {
console.log("initializing UserController")
}
object UserController {
/**
* logs serverside Users to console (This method only exists for educational purposes)
* FIXME: You might not wat to have this in your code!!!!!!
* Logs server Users to console (This method only exists for educational purposes)
*/
def logUsers(): Unit = {
AjaxClient[Api].getUsers().call().map(
(users) => console.log(users.toString)
)
}
}
package frontend.services
import java.nio.ByteBuffer
import boopickle.Default._
import org.scalajs.dom
import java.nio.ByteBuffer
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.scalajs.js.typedarray._
object AjaxClient extends autowire.Client[ByteBuffer, Pickler, Pickler] {
override def doCall(req: Request): Future[ByteBuffer] = {
dom.ext.Ajax.post(
url = "/api/" + req.path.mkString("/"),
......@@ -21,4 +22,5 @@ object AjaxClient extends autowire.Client[ByteBuffer, Pickler, Pickler] {
override def read[Result: Pickler](p: ByteBuffer) = Unpickle[Result].fromBytes(p)
override def write[Result: Pickler](r: Result) = Pickle.intoBytes(r)
}
......@@ -2,9 +2,12 @@
# See https://docs.docker.com/compose/env-file/
# services.yml environment variables
PROXY_PORT=80
APPLICATION_HOST=localhost
PROXY_HTTP_PORT=80
PROXY_HTTPS_PORT=443
APP_DEBUG_PORT=9999
# sbt.yml environment variables
SMTP_PORT=25
MYSQL_PORT=3306
\ No newline at end of file
MAILHOG_PORT=8025
MYSQL_PORT=3306
# Ignore everything in this directory
*
# Except this file
!.gitignore
\ No newline at end of file
- name: "Backup the latest database on the remote host and download it to the localhost"
hosts: webserver
gather_facts: true
remote_user: "{{ ssh_user }}"
vars_files:
- "variables.yml"
roles:
- { role: backup_db }
- { role: download_latest_db }
- { role: remove_old_backups }
\ No newline at end of file
---
- name: "Import the downloaded database"
hosts: webserver
gather_facts: true
vars_files:
- "variables.yml"
roles:
- { role: import_downloaded_db }
localhost
[webserver]
localhost
[targets]
localhost ansible_connection=local
\ No newline at end of file
---
- name: Ensure the backup directory exists
file: path="{{ directory.backup }}" state=directory
- name: Register a backup timestamp
local_action: shell date +%Y%m%d%H%M%S
register: backup_id
run_once: true
- name: Dump the database
shell: "docker exec {{ docker.db_container_name }} bash -c 'mysqldump -u$MYSQL_USER -p$MYSQL_PASSWORD --databases $MYSQL_DATABASE | gzip -c' > {{directory.backup }}/db-{{ backup_id.stdout }}.sql.gz"
- name: Create Symlink to latest backup
shell: ln -sf "{{ directory.backup }}/db-{{ backup_id.stdout }}.sql.gz" "{{ directory.backup }}/db-latest.sql.gz"
\ No newline at end of file
---
- stat: path="{{ directory.backup }}/db-latest.sql.gz"
register: db_bkp
- name: Download DB to local environment
fetch: src="{{ db_bkp.stat.lnk_source }}" dest="./artifacts/db-latest.sql.gz" fail_on_missing=yes flat=yes
when: db_bkp.stat.islnk is defined and db_bkp.stat.islnk
- name: Download DB to local environment
fetch: src="{{ db_bkp.stat.path }}" dest="./artifacts/db-latest.sql.gz" fail_on_missing=yes flat=yes
when: db_bkp.stat.islnk is defined and db_bkp.stat.islnk == False
---
- name: Drop existing database
shell: "docker exec -i {{ docker.db_container_name }} bash -c 'echo \"DROP DATABASE IF EXISTS $MYSQL_DATABASE;\" | mysql -u root -p$MYSQL_ROOT_PASSWORD'"
- name: Create new empty database
shell: "docker exec -i {{ docker.db_container_name }} bash -c 'echo \"CREATE DATABASE $MYSQL_DATABASE;\" | mysql -u root -p$MYSQL_ROOT_PASSWORD\'"
- name: Import the database dump
shell: "gunzip -c ./artifacts/db-latest.sql | docker exec -i {{ docker.db_container_name }} bash -c 'mysql -u root -p$MYSQL_ROOT_PASSWORD $MYSQL_DATABASE'"
\ No newline at end of file