mirror of
https://github.com/Febbweiss/springboot-react-webpack.git
synced 2026-03-04 22:25:34 +00:00
First commit
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
src/main/resources/static
|
||||||
|
src/main/webapp/js/.module-cache
|
||||||
|
src/test/resources/static/js/compiled
|
||||||
|
src/test/resources/static/js/.module-cache
|
||||||
|
target
|
||||||
349
pom.xml
Normal file
349
pom.xml
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.opengroupe.cloud.saas</groupId>
|
||||||
|
<artifactId>catalog-ui</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<name>UI service project</name>
|
||||||
|
<description>Default tools to build UI BFF</description>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<url>http://www.open-groupe.com</url>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>1.3.3.RELEASE</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
<jasmine.version>2.4.1</jasmine.version>
|
||||||
|
<jasmine-ajax.version>3.2.0</jasmine-ajax.version>
|
||||||
|
|
||||||
|
<bootstrap.version>3.3.6</bootstrap.version>
|
||||||
|
<jquery.version>2.2.1</jquery.version>
|
||||||
|
<react.version>0.14.7</react.version>
|
||||||
|
<marked.version>0.3.2-1</marked.version>
|
||||||
|
<marked-lib.version>0.3.2</marked-lib.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spring Boot Core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- View templating -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- Testing -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jayway.jsonpath</groupId>
|
||||||
|
<artifactId>json-path</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jayway.jsonpath</groupId>
|
||||||
|
<artifactId>json-path-assert</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>jasmine</artifactId>
|
||||||
|
<version>${jasmine.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>jquery-mockjax</artifactId>
|
||||||
|
<version>1.5.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- Monitoring -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- Hot swapping for dev purposes -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<!-- Webjars -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>bootstrap</artifactId>
|
||||||
|
<version>${bootstrap.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>jquery</artifactId>
|
||||||
|
<version>${jquery.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>react</artifactId>
|
||||||
|
<version>${react.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>marked</artifactId>
|
||||||
|
<version>${marked.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${project.basedir}/src/main/resources</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.fizzed</groupId>
|
||||||
|
<artifactId>fizzed-watcher-maven-plugin</artifactId>
|
||||||
|
<version>1.0.6</version>
|
||||||
|
<configuration>
|
||||||
|
<watches>
|
||||||
|
<watch>
|
||||||
|
<directory>src/main/webapp/js</directory>
|
||||||
|
</watch>
|
||||||
|
<watch>
|
||||||
|
<directory>src/main/wro</directory>
|
||||||
|
</watch>
|
||||||
|
</watches>
|
||||||
|
<goals>
|
||||||
|
<goal>process-resources</goal>
|
||||||
|
</goals>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- Resource optimization -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-antrun-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>clean</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<target>
|
||||||
|
<delete dir="${project.basedir}/src/main/resources/static" />
|
||||||
|
<delete dir="${project.basedir}/src/test/resources/static/js/compiled" />
|
||||||
|
</target>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<!-- Serves *only* to filter the wro.xml so it can get an absolute
|
||||||
|
path for the project -->
|
||||||
|
<id>copy-resources</id>
|
||||||
|
<phase>validate</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${basedir}/target/wro</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/wro</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>uk.co.codezen</groupId>
|
||||||
|
<artifactId>react-jsxtransformer-maven-plugin</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>Compile resources</id>
|
||||||
|
<phase>process-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<extension>jsx</extension>
|
||||||
|
<sourcePath>
|
||||||
|
${project.basedir}/src/main/webapp/js
|
||||||
|
</sourcePath>
|
||||||
|
<targetPath>
|
||||||
|
${project.basedir}/src/main/resources/static/js
|
||||||
|
</targetPath>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>Compile test resources</id>
|
||||||
|
<phase>process-test-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<extension>jsx</extension>
|
||||||
|
<sourcePath>
|
||||||
|
${project.basedir}/src/test/resources/static/js
|
||||||
|
</sourcePath>
|
||||||
|
<targetPath>
|
||||||
|
${project.basedir}/src/test/resources/static/js/compiled
|
||||||
|
</targetPath>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>ro.isdc.wro4j</groupId>
|
||||||
|
<artifactId>wro4j-maven-plugin</artifactId>
|
||||||
|
<version>1.7.9</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>process-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<wroManagerFactory>ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory</wroManagerFactory>
|
||||||
|
<cssDestinationFolder>${project.basedir}/src/main/resources/static/css</cssDestinationFolder>
|
||||||
|
<jsDestinationFolder>${project.basedir}/src/main/resources/static/js</jsDestinationFolder>
|
||||||
|
<wroFile>${project.build.directory}/wro/wro.xml</wroFile>
|
||||||
|
<extraConfigFile>${project.basedir}/src/main/wro/wro.properties</extraConfigFile>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- Testing -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.github.klieber</groupId>
|
||||||
|
<artifactId>phantomjs-maven-plugin</artifactId>
|
||||||
|
<version>0.7</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>install</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<version>2.1.1</version>
|
||||||
|
<skip>false</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.github.searls</groupId>
|
||||||
|
<artifactId>jasmine-maven-plugin</artifactId>
|
||||||
|
<version>2.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>test</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<webDriverClassName>org.openqa.selenium.phantomjs.PhantomJSDriver</webDriverClassName>
|
||||||
|
<webDriverCapabilities>
|
||||||
|
<capability>
|
||||||
|
<name>phantomjs.binary.path</name>
|
||||||
|
<value>${phantomjs.binary}</value>
|
||||||
|
</capability>
|
||||||
|
</webDriverCapabilities>
|
||||||
|
<preloadSources>
|
||||||
|
<preloadSource>/webjars/jquery.js</preloadSource>
|
||||||
|
<preloadSource>/webjars/react-with-addons.js</preloadSource>
|
||||||
|
<preloadSource>/webjars/react-dom.js</preloadSource>
|
||||||
|
<preloadSource>/webjars/jquery.mockjax.js</preloadSource>
|
||||||
|
<preloadSource>${project.basedir}/src/test/resources/jasmine/config.js</preloadSource>
|
||||||
|
</preloadSources>
|
||||||
|
<jsSrcDir>${project.basedir}/src/main/resources/static/js</jsSrcDir>
|
||||||
|
<sourceIncludes>
|
||||||
|
<include>**/*.js</include>
|
||||||
|
</sourceIncludes>
|
||||||
|
<sourceExcludes>
|
||||||
|
<exclude>**/react-bootstrap.js</exclude>
|
||||||
|
<exclude>**/app.render.js</exclude>
|
||||||
|
</sourceExcludes>
|
||||||
|
<jsTestSrcDir>${project.basedir}/src/test/resources/static/js</jsTestSrcDir>
|
||||||
|
<specIncludes>
|
||||||
|
<include>**/*.spec.js</include>
|
||||||
|
</specIncludes>
|
||||||
|
<customRunnerTemplate>${project.basedir}/src/test/resources/jasmine/ReactJsSpecRunner.htmltemplate</customRunnerTemplate>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.eclipse.m2e</groupId>
|
||||||
|
<artifactId>lifecycle-mapping</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<configuration>
|
||||||
|
<lifecycleMappingMetadata>
|
||||||
|
<pluginExecutions>
|
||||||
|
<pluginExecution>
|
||||||
|
<pluginExecutionFilter>
|
||||||
|
<groupId>ro.isdc.wro4j</groupId>
|
||||||
|
<artifactId>wro4j-maven-plugin</artifactId>
|
||||||
|
<versionRange>[1.7.9,)</versionRange>
|
||||||
|
<goals>
|
||||||
|
<goal>jshint</goal>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
</pluginExecutionFilter>
|
||||||
|
<action>
|
||||||
|
<ignore></ignore>
|
||||||
|
</action>
|
||||||
|
</pluginExecution>
|
||||||
|
<pluginExecution>
|
||||||
|
<pluginExecutionFilter>
|
||||||
|
<groupId>uk.co.codezen</groupId>
|
||||||
|
<artifactId>react-jsxtransformer-maven-plugin</artifactId>
|
||||||
|
<versionRange>[1.0,)</versionRange>
|
||||||
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
</goals>
|
||||||
|
</pluginExecutionFilter>
|
||||||
|
<action>
|
||||||
|
<ignore></ignore>
|
||||||
|
</action>
|
||||||
|
</pluginExecution>
|
||||||
|
<pluginExecution>
|
||||||
|
<pluginExecutionFilter>
|
||||||
|
<groupId>com.github.klieber</groupId>
|
||||||
|
<artifactId>phantomjs-maven-plugin</artifactId>
|
||||||
|
<versionRange>[0.7,)</versionRange>
|
||||||
|
<goals>
|
||||||
|
<goal>install</goal>
|
||||||
|
</goals>
|
||||||
|
</pluginExecutionFilter>
|
||||||
|
<action>
|
||||||
|
<ignore></ignore>
|
||||||
|
</action>
|
||||||
|
</pluginExecution>
|
||||||
|
</pluginExecutions>
|
||||||
|
</lifecycleMappingMetadata>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
12
src/main/java/com/opengroupe/cloud/saas/Application.java
Normal file
12
src/main/java/com/opengroupe/cloud/saas/Application.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package com.opengroupe.cloud.saas;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/main/java/com/opengroupe/cloud/saas/domain/Comment.java
Normal file
26
src/main/java/com/opengroupe/cloud/saas/domain/Comment.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package com.opengroupe.cloud.saas.domain;
|
||||||
|
|
||||||
|
import groovy.transform.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
public class Comment {
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final String author;
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
public Comment(Long id, String author, String text) {
|
||||||
|
this.id = id;
|
||||||
|
this.author = author;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public String getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.opengroupe.cloud.saas.rest;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.opengroupe.cloud.saas.domain.Comment;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class CommentController {
|
||||||
|
|
||||||
|
private List<Comment> comments = new ArrayList<Comment>();
|
||||||
|
|
||||||
|
@RequestMapping("/")
|
||||||
|
public String index() {
|
||||||
|
return "Greetings from Spring Boot!";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value="/api/comments", method=RequestMethod.GET)
|
||||||
|
public @ResponseBody List<Comment> comments() {
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value="/api/comments", method=RequestMethod.POST)
|
||||||
|
public @ResponseBody List<Comment> comments(
|
||||||
|
@RequestParam(value="id", required=true) Long id,
|
||||||
|
@RequestParam(value="author", required=true) String author,
|
||||||
|
@RequestParam(value="text", required=true) String text) {
|
||||||
|
comments.add(new Comment(id, author, text));
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.opengroupe.cloud.saas.web;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class ViewController {
|
||||||
|
|
||||||
|
@RequestMapping("/greeting")
|
||||||
|
public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
|
||||||
|
model.addAttribute("name", name);
|
||||||
|
return "greeting";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/index")
|
||||||
|
public String index(Model model) {
|
||||||
|
return "index";
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/main/resources/application.yml
Normal file
16
src/main/resources/application.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
spring:
|
||||||
|
main:
|
||||||
|
banner_mode: off
|
||||||
|
|
||||||
|
project:
|
||||||
|
artifactId: template
|
||||||
|
name: Demo
|
||||||
|
version: X.X.X
|
||||||
|
description: Demo project for info endpoint
|
||||||
|
|
||||||
|
info:
|
||||||
|
build:
|
||||||
|
artifact: ${project.artifactId}
|
||||||
|
name: ${project.name}
|
||||||
|
description: ${project.description}
|
||||||
|
version: ${project.version}
|
||||||
10
src/main/resources/templates/greeting.html
Normal file
10
src/main/resources/templates/greeting.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<title>Getting Started: Serving Web Content</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p th:text="'Hello, ' + ${name} + '!'" />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
17
src/main/resources/templates/index.html
Normal file
17
src/main/resources/templates/index.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head lang="en">
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
<title>ReactJS</title>
|
||||||
|
<link rel="stylesheet" href="/css/react-bootstrap.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="content"></div>
|
||||||
|
|
||||||
|
<script src="/js/react-bootstrap.js"></script>
|
||||||
|
<script src="/js/bundle.js"></script>
|
||||||
|
<script src="/js/app.js"></script>
|
||||||
|
<script src="/js/app.render.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
123
src/main/webapp/js/app.jsx
Normal file
123
src/main/webapp/js/app.jsx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Comment = React.createClass({
|
||||||
|
rawMarkup: function() {
|
||||||
|
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
|
||||||
|
return { __html: rawMarkup };
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<div className="comment">
|
||||||
|
<h2 className="commentAuthor">
|
||||||
|
{this.props.author}
|
||||||
|
</h2>
|
||||||
|
<span dangerouslySetInnerHTML={this.rawMarkup()} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var CommentForm = React.createClass({
|
||||||
|
getInitialState: function() {
|
||||||
|
return {author: '', text: ''};
|
||||||
|
},
|
||||||
|
handleAuthorChange: function(e) {
|
||||||
|
this.setState({author: e.target.value});
|
||||||
|
},
|
||||||
|
handleTextChange: function(e) {
|
||||||
|
this.setState({text: e.target.value});
|
||||||
|
},
|
||||||
|
handleSubmit: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var author = this.state.author.trim();
|
||||||
|
var text = this.state.text.trim();
|
||||||
|
if( !text || !author ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.props.onCommentSubmit({author: author, text: text})
|
||||||
|
this.setState({author: '', text: ''});
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<form className="commentForm" onSubmit={this.handleSubmit}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Your name"
|
||||||
|
value={this.state.author}
|
||||||
|
onChange={this.handleAuthorChange}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Say something..."
|
||||||
|
value={this.state.text}
|
||||||
|
onChange={this.handleTextChange}
|
||||||
|
/>
|
||||||
|
<input type="submit" value="Post" />
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var CommentBox = React.createClass({
|
||||||
|
getInitialState: function() {
|
||||||
|
return {data: []};
|
||||||
|
},
|
||||||
|
loadCommentsFromServer: function() {
|
||||||
|
$.ajax({
|
||||||
|
url: this.props.url,
|
||||||
|
dataType: 'json',
|
||||||
|
cache: false,
|
||||||
|
success: function(data) {
|
||||||
|
this.setState({data: data});
|
||||||
|
}.bind(this),
|
||||||
|
error: function(xhr, status, err) {
|
||||||
|
console.error(this.props.url, status, err.toString());
|
||||||
|
}.bind(this)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleCommentSubmit: function(comment) {
|
||||||
|
var comments = this.state.data;
|
||||||
|
comment.id = Date.now();
|
||||||
|
var newComments = comments.concat([comment]);
|
||||||
|
this.setState({data: newComments});
|
||||||
|
$.ajax({
|
||||||
|
url: this.props.url,
|
||||||
|
method: 'POST',
|
||||||
|
dataType: 'json',
|
||||||
|
data: comment,
|
||||||
|
success: function(data) {
|
||||||
|
this.setState({data: data});
|
||||||
|
}.bind(this),
|
||||||
|
error: function(xhr, status, err) {
|
||||||
|
this.setState({data: comments});
|
||||||
|
console.error(this.props.url, status, err.toString());
|
||||||
|
}.bind(this)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
componentDidMount: function() {
|
||||||
|
this.loadCommentsFromServer();
|
||||||
|
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<div className="commentBox">
|
||||||
|
<h1>Comments</h1>
|
||||||
|
<CommentList data={this.state.data}/>
|
||||||
|
<CommentForm onCommentSubmit={this.handleCommentSubmit}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var CommentList = React.createClass({
|
||||||
|
render: function() {
|
||||||
|
var commentNodes = this.props.data.map( function(comment) {
|
||||||
|
return (
|
||||||
|
<Comment author={comment.author} key={comment.id}>{comment.text}</Comment>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div className="commentList">
|
||||||
|
{commentNodes}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
4
src/main/webapp/js/app.render.js
Normal file
4
src/main/webapp/js/app.render.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
ReactDOM.render(
|
||||||
|
<CommentBox url="/api/comments" pollInterval={2000}/>,
|
||||||
|
document.getElementById('content')
|
||||||
|
);
|
||||||
8
src/main/wro/wro.properties
Normal file
8
src/main/wro/wro.properties
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
debug=true
|
||||||
|
# Available processors : http://wro4j.readthedocs.org/en/stable/AvailableProcessors/
|
||||||
|
preProcessors=lessCssImport
|
||||||
|
postProcessors=less4j,cssMin
|
||||||
|
# explicitly invalidates the cache each 5 seconds
|
||||||
|
cacheUpdatePeriod=5
|
||||||
|
# check for changes each 5 seconds and invalidates the cache only when a change is detected
|
||||||
|
resourceWatcherUpdatePeriod=5
|
||||||
13
src/main/wro/wro.xml
Normal file
13
src/main/wro/wro.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<groups xmlns="http://www.isdc.ro/wro">
|
||||||
|
<group name="react-bootstrap">
|
||||||
|
<css>webjar:bootstrap/@bootstrap.version@/css/bootstrap.css</css>
|
||||||
|
<js>webjar:jquery/@jquery.version@/jquery.js</js>
|
||||||
|
<js>webjar:react/@react.version@/react-with-addons.js</js>
|
||||||
|
<js>webjar:react/@react.version@/react-dom.js</js>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<group name="bundle">
|
||||||
|
<js>webjar:marked/@marked-lib.version@/marked.js</js>
|
||||||
|
</group>
|
||||||
|
</groups>
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.opengroupe.cloud.saas.rest;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.mock.web.MockServletContext;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@SpringApplicationConfiguration(classes = MockServletContext.class)
|
||||||
|
@WebAppConfiguration
|
||||||
|
public class CommentControllerTest {
|
||||||
|
|
||||||
|
private MockMvc mvc;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
mvc = MockMvcBuilders.standaloneSetup(new CommentController()).build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getHello() throws Exception {
|
||||||
|
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string(equalTo("Greetings from Spring Boot!")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getEmptyComment() throws Exception {
|
||||||
|
mvc.perform(MockMvcRequestBuilders.get("/api/comments").accept(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
|
||||||
|
.andExpect(jsonPath("$", hasSize(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void postComment() throws Exception {
|
||||||
|
mvc.perform(MockMvcRequestBuilders.post("/api/comments")
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.param("id", "1")
|
||||||
|
.param("author", "Lao Tzu")
|
||||||
|
.param("text", "The journey of a thousand miles begins with one step"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
|
||||||
|
.andExpect(jsonPath("$", hasSize(1)))
|
||||||
|
.andExpect(jsonPath("$[0].id", is(1)))
|
||||||
|
.andExpect(jsonPath("$[0].author", is("Lao Tzu")))
|
||||||
|
.andExpect(jsonPath("$[0].text", is("The journey of a thousand miles begins with one step")));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.opengroupe.cloud.saas.web;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
import com.opengroupe.cloud.saas.Application;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@SpringApplicationConfiguration(classes = Application.class)
|
||||||
|
@WebAppConfiguration
|
||||||
|
public class ViewControllerTest {
|
||||||
|
private MockMvc mvc;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
WebApplicationContext wac;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
// Process mock annotations
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
|
mvc = MockMvcBuilders.webAppContextSetup(wac).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getDefaultGreetings() throws Exception {
|
||||||
|
mvc.perform(MockMvcRequestBuilders.get("/greeting"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8"))
|
||||||
|
.andExpect(model().attribute("name", equalTo("World")))
|
||||||
|
.andExpect(view().name("greeting"));
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/test/resources/jasmine/ReactJsSpecRunner.htmltemplate
Normal file
35
src/test/resources/jasmine/ReactJsSpecRunner.htmltemplate
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=$sourceEncoding$">
|
||||||
|
$if(autoRefresh)$
|
||||||
|
<meta http-equiv="refresh" content="$autoRefreshInterval$">
|
||||||
|
$endif$
|
||||||
|
<title>Jasmine Spec Runner</title>
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.onerror = function(msg,url,line) {
|
||||||
|
if (document.head) {
|
||||||
|
var jserror = document.head.getAttribute('jmp_jserror') || '';
|
||||||
|
if (jserror) {
|
||||||
|
jserror += ':!:';
|
||||||
|
}
|
||||||
|
jserror += msg;
|
||||||
|
document.head.setAttribute('jmp_jserror',jserror);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
$cssDependencies$
|
||||||
|
$javascriptDependencies$
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.onload = jasmine.boot;
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
$allScriptTags$
|
||||||
|
<script type="text/javascript">
|
||||||
|
if(window.location.href.indexOf("ManualSpecRunner.html") !== -1) {
|
||||||
|
document.body.appendChild(document.createTextNode("Warning: Opening this HTML file directly from the file system is deprecated. You should instead try running `mvn jasmine:bdd` from the command line, and then visit `http://localhost:8234` in your browser. "))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
8
src/test/resources/jasmine/config.js
Normal file
8
src/test/resources/jasmine/config.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
$("body").append("<div id='content'></div>");
|
||||||
|
|
||||||
|
$.mockjax({
|
||||||
|
url: "/api/comments",
|
||||||
|
responseText: []
|
||||||
|
});
|
||||||
80
src/test/resources/static/js/app.spec.jsx
Normal file
80
src/test/resources/static/js/app.spec.jsx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var AjaxResponses = {
|
||||||
|
empty: {
|
||||||
|
success: {
|
||||||
|
status: 200,
|
||||||
|
responseText: '[]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submit: {
|
||||||
|
success: {
|
||||||
|
status: 200,
|
||||||
|
responseText: '[{id: 1, author: "Lao Tzu", text: "The journey of a thousand miles begins with one step"}]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var TestUtils = React.addons.TestUtils;
|
||||||
|
|
||||||
|
describe('Comments', function() {
|
||||||
|
|
||||||
|
var instance,
|
||||||
|
container = document.createElement("div");
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
if (instance && instance.isMounted()) {
|
||||||
|
// Only components with a parent will be unmounted
|
||||||
|
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(instance).parentNode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("rendered without a container reference", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
instance = TestUtils.renderIntoDocument(<CommentBox url="/api/comments" pollInterval={200000}/>);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render a heading with the given text", function() {
|
||||||
|
var heading = TestUtils.findRenderedDOMComponentWithTag(instance, "h1");
|
||||||
|
expect(ReactDOM.findDOMNode(heading).textContent).toBe("Comments");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("with a container reference required", function() {
|
||||||
|
var inputs,
|
||||||
|
form;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
instance = ReactDOM.render(React.createElement(CommentBox, {"url": "/api/comments", "pollInterval": 200000}), container);
|
||||||
|
|
||||||
|
this.eventSpy = jasmine.createSpy();
|
||||||
|
container.addEventListener("broadcast", this.eventSpy, false);
|
||||||
|
inputs = TestUtils.scryRenderedDOMComponentsWithTag(instance, 'input');
|
||||||
|
form = TestUtils.findRenderedDOMComponentWithTag(instance, 'form')
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
container.removeEventListener("broadcast", this.eventSpy, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should send comment and retrieve a list", function() {
|
||||||
|
$.mockjax({
|
||||||
|
url: "/api/comments",
|
||||||
|
responseText: AjaxResponses.submit.success
|
||||||
|
});
|
||||||
|
|
||||||
|
var name = inputs[0],
|
||||||
|
text = inputs[1];
|
||||||
|
|
||||||
|
TestUtils.Simulate.change(name, { target: { value: 'Lao Tzu' } });
|
||||||
|
TestUtils.Simulate.change(text, { target: { value: 'The journey of a thousand miles begins with one step' } });
|
||||||
|
|
||||||
|
TestUtils.Simulate.submit(form);
|
||||||
|
var comments = TestUtils.scryRenderedDOMComponentsWithClass(instance, 'comment');
|
||||||
|
expect(comments.length).toBe(1);
|
||||||
|
expect(TestUtils.findRenderedDOMComponentWithClass(instance, 'commentAuthor').textContent).toBe('Lao Tzu');
|
||||||
|
expect(TestUtils.findRenderedDOMComponentWithTag(instance, 'span').textContent.trim()).toBe('The journey of a thousand miles begins with one step');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
8
src/test/resources/static/js/fake.spec.js
Normal file
8
src/test/resources/static/js/fake.spec.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
describe('Fake', function() {
|
||||||
|
|
||||||
|
describe('fake()', function() {
|
||||||
|
it("contains spec with an expectation", function() {
|
||||||
|
expect(true).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user