Backend
Frontend
REST est à ROA ce que SOAP est à SOA
Les concepts sont issus de la thèse de doctorat en philosophie de M. Roy T. Fielding en 2000...
C'était aussi mon sujet d'examen probatoire au Cnam...
Tout est ressource !
principes fondamentaux :
- l'identification des ressources : URI, URL, URN
- la manipulation des ressources au travers de leur représentation, en particulier via le protocole HTTP
- la description des ressources : MIME
...
@RequestMapping(value="/customer/{id}" ,headers = "Accept=application/json","application/xml")
public ResponseEntity<Customer> getCustomerById(@PathVariable String id) {
Customer customer;
try {
customer = customerService.getCustomerDetail(id);
} catch (CustomerNotFoundException e) {
return new ResponseEntity<Customer>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<Customer>(customer,HttpStatus.OK);
}
Exemple :
dans confluence...
Il y a aussi les fiches pratiques...
Ce qui nous intéresse plus particulièrement :
Support de containers embarqués pour la production rapide d'applications auto-exécutables.
Support REST pour la création d'APIs RESTful
Ce qui nous intéresse plus particulièrement (suite et fin) :
Data Relational Access
3. Renseigner les caractéristiques du projet
4. Choisir les fonctionnalités Spring :
5. Clic sur [Finish]
4a. développer [Web]
4b. cocher [Web]
6. Lancer l'application :
6.a Clic droit sur le projet, [Run As] --> [Spring Boot App]
ou
6.b Lancement depuis un terminal, à la racine du projet :
mvn spring-boot:run
ou
6.c Packaging et utilisation de la commande java :
mvn clean verify
java -jar ./target/{$artifactId}-{$version}.jar
Mais elle ne fait rien !?
1. Créer un package
2. Créer une classe HelloController
/**
*
*/
package nc.gouv.dtsi.etudes.axi.formation.hello;
import org.springframework.http.HttpEntity;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @author didier
*
*/
@RestController
public class HelloController {
private static final String hello = "Hello World !";
@RequestMapping(value = "/api/v1/hello",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity<String> sayHello() {
return new HttpEntity<String>(hello);
}
}
@RestController
Depuis Spring 4
Equivaut dans les version précédentes à la combinaison des annotations :
- @Controller
- @ResponseBody
C'est une version spécialisée du Controller et elle doit être utilisée pour la création d'APIs RESTful.
@RequestMapping
Permet de mapper les requêtes HTTP vers les méthodes concernées des controllers.
value
déclare le chemin à utiliser dans l'URL pour appeler la méthode concernée.
Peut être utilisé au niveau de la méthode ou au niveau de la classe. Dans ce dernier cas, toutes les méthodes de la classe hériteront de ce chemin.
@RequestMapping
method
Restreint la méthode HTTP :
GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE
Peut être utilisé au niveau de la méthode ou au niveau de la classe. Dans ce dernier cas, toutes les méthodes de la classe hériteront de cette restriction.
@RequestMapping
produces
Définit le(s) type(s) de media que peut produire la requête. Peut contenir des valeurs multiples :
(...)
produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE }
(...)
L'expression peut être négative :
(...)
produces = !MediaType.APPLICATION_XML_VALUE
(...)
Dans ce cas, toutes les requêtes acceptant autre chose que du XML seront acceptées.
Peut être utilisé au niveau de la méthode ou au niveau de la classe. Dans ce dernier cas, la déclaration au niveau de la méthode surchargera la déclaration au niveau de la classe.
- Lancer l'application
- Tester :
- via CURL :
curl -i http://localhost:8080/api/v1/hello
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Content-Length: 13
Date: Sun, 12 Feb 2017 21:46:21 GMT
Hello World !
- via Postman :
@RequestMapping avec variable(s)
Modifier la classe HelloController comme suit :
(...)
@RestController
@RequestMapping("/api/v1")
public class HelloController {
private static final String helloToSomeone = "Hello ";
private static final String helloWorld = helloToSomeone + "World !";
@RequestMapping(value = "/hello",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
(...)
@RequestMapping(value = "/hello/{someone}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity<String> sayHelloToSomeone(@PathVariable("someone") final String someone) {
return new HttpEntity<String>(helloToSomeone + someone + " !");
}
}
@RequestMapping avec variable(s)
- Relancer l'application
- Tester :
- via CURL :
curl -i http://localhost:8080/api/v1/hello/Donald
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Content-Length: 14
Date: Sun, 12 Feb 2017 22:51:50 GMT
Hello Donald !
- ou via Postman...
@RequestMapping avec variable(s)
Si le nom de l'argument de la méthode correspond exactement au nom de la @PathVariable, il est possible de simplifier comme suit :
(...)
@RestController
@RequestMapping("/api/v1")
public class HelloController {
(...)
@RequestMapping(value = "/hello/{someone}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity<String> sayHelloToSomeone(@PathVariable final String someone) {
return new HttpEntity<String>(helloToSomeone + someone + " !");
}
}
@RequestMapping avec variable(s)
Il est possible d'avoir plusieurs @PathVariable
Il est possible d'utiliser les expressions régulières pour contrôler le format de la @PathVariable
@RequestMapping avec variable(s)
Autre méthode avec @RequestParam
Modifier la méthode sayHelloToSomeone comme suit :
@RequestMapping(value = "/hello",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity<String> sayHelloToSomeone(@RequestParam("someone") final String someone) {
return new HttpEntity<String>(helloToSomeone + someone + " !");
}
Relancer l'application...
@RequestMapping avec variable(s)
@RequestMapping avec variable(s)
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'requestMappingHandlerMapping'
defined in class path resource
[org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]:
Invocation of init method failed;
nested exception is java.lang.IllegalStateException:
Ambiguous mapping. Cannot map 'helloController' method
@RequestMapping avec variable(s)
Premier exercice : corriger la classe HelloController pour continuer à avoir les deux services opérationnels :
curl -i http://localhost:8080/api/v1/hello
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Content-Length: 13
Date: Mon, 13 Feb 2017 00:05:00 GMT
Hello World !
curl -i http://localhost:8080/api/v1/hello?someone=Donald
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Content-Length: 14
Date: Mon, 13 Feb 2017 00:07:33 GMT
Hello Donald !
@RequestMapping avec variable(s)
Corrigé :
package nc.gouv.dtsi.etudes.axi.formation.hello;
import (...)
@RestController
@RequestMapping("/api/v1")
public class HelloController {
private static final String helloToSomeone = "Hello ";
@RequestMapping(value = "/hello",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity<String> sayHelloToSomeone(
@RequestParam(name = "someone",
required = false,
defaultValue = "World")
final String someone) {
return new HttpEntity<String>(helloToSomeone + someone + " !");
}
}
Utilisation d'une base de données embarquées basée sur le code officiel géographique de l'INSEE.
1. Dans src/main/resources, supprimer le fichier application.properties et créer un fichier application.yml
spring:
application:
name: Formation Trinity Backend
jpa:
hibernate:
ddl-auto: none
show-sql: true
database-platform: org.hibernate.dialect.HSQLDialect
datasource:
url: jdbc:hsqldb:mem:testdb
driverClassName: org.hsqldb.jdbcDriver
username: sa
password:
platform: hsqldb
data:
rest:
base-uri: /api
flyway:
enabled: true
2. Modification du fichier pom.xml du projet, par ajout des dépendances nécessaires :
(...)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
(...)
3. Créer un nouveau package
4a. Création de la classe entité (mappe un objet de la couche de persistence, i.e. en général une table de la base de données)
4b. Création du repository
4c. Création du controller
Exemple (table T_REGION) :
Entité JPA :
package nc.gouv.dtsi.etudes.axi.formation.cog.region;
import ...;
@Entity
@Table(name = "T_REGION")
public class Region {
@Id
@Column(name = "REGION")
private String codeRegion;
@Column(name = "CHEFLIEU")
private String chefLieu;
@Column(name = "TNCC")
private String typeDeNomEnClair;
@Column(name = "NCC")
private String nomEnClair;
@Column(name = "NCCENR")
private String nomTypoEnrichie;
/*
Accesseurs...
*/
}
Exemple (table T_REGION) :
Entité JPA :
Ne pas oublier les accesseurs, les constructeurs, etc.
Exemple (table T_REGION) :
Repository :
package nc.gouv.dtsi.etudes.axi.formation.cog.region;
import org.springframework.data.jpa.repository.JpaRepository;
public interface RegionRepository extends JpaRepository<Region, String> {
}
Exemple (table T_REGION) :
Controller :
package nc.gouv.dtsi.etudes.axi.formation.cog.region;
import ...;
@RestController
@RequestMapping(path = "/api/v1",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@CrossOrigin(origins = "*")
public class RegionController {
@Autowired
private RegionRepository lRegionRepository;
@RequestMapping(path = "/regions")
public List<Region> getAll() {
return lRegionRepository.findAll();
}
}
- Lancer l'application
- Tester :
- via CURL
- via Postman
- via le navigateur...
Et la magie du framework Spring...
Par exemple pour rechercher par la valeur d'un des attributs de notre entité, par exemple, l'attribut :
/**
* NCCENR - Nom en clair (typographie riche) Libellé en typographie riche,
* majuscules, minuscules, accentuation.
*/
@Column(name = "NCCENR")
private String nomTypoEnrichie;
1. Modifier l'interface Repository en lui ajoutant :
public interface RegionRepository extends JpaRepository<Region, String> {
Region findByNomTypoEnrichie(final String pCriteria);
}
2. Enrichir le controller de la méthode :
@RequestMapping(path = "/regions/{criteria}")
public ResponseEntity<Region> getByCriteria(
@PathVariable(name = ("criteria"), required = true) final String pCriteria) {
/*
* Default pessimistic response
*/
ResponseEntity<Region> response = new ResponseEntity<>(null,
HttpStatus.NOT_FOUND);
Region result = lRegionRepository
.findByNomTypoEnrichie(pCriteria);
if (result != null) {
response = new ResponseEntity<>(result, HttpStatus.OK);
}
return response;
}
- Relancer l'application
- Tester :
- via CURL
- via Postman
- via le navigateur...
Pour aller encore plus loin, nous allons combiner les critères...
1. Remodifier l'interface Repository :
public interface RegionRepository extends JpaRepository<Region, String> {
Region findByCodeRegionOrNomTypoEnrichie(final String pCodeRegion,
final String pNomTypoEnrichie);
}
2. Modifier le controller :
@RequestMapping(path = "/regions/{criteria}")
public ResponseEntity<Region> getByCriteria(
@PathVariable(name = ("criteria"), required = true) final String pCriteria) {
(...)
Region result = lRegionRepository
.findByCodeRegionOrNomTypoEnrichie(pCriteria, pCriteria);
(...)
}
Attention à la subtilité :
findByCodeRegionOrNomTypoEnrichie(pCriteria, pCriteria)
La valeur du critère de recherche est passée en double pour que la recherche soit faite sur les deux attributs concernés...
- Relancer l'application
- Tester :
- via CURL
- via Postman
- via le navigateur...
Documentation de référence ici
1. Dans src/main/resources, supprimer le fichier application.properties et créer un fichier application.yml
spring:
application:
name: Formation Trinity Backend
jpa:
hibernate:
ddl-auto: none
show-sql: true
database-platform: org.hibernate.dialect.HSQLDialect
datasource:
url: jdbc:hsqldb:mem:testdb
driverClassName: org.hsqldb.jdbcDriver
username: sa
password:
platform: hsqldb
data:
rest:
base-uri: /api
flyway:
enabled: true
2. Modification du fichier pom.xml du projet, par ajout de la dépendance :
(...)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
(...)
Exemple (table T_REGION) :
Entité JPA : cf. ce qui a été fait dans l'exemple Spring Data JPA
Repository :
package nc.gouv.dtsi.etudes.axi.formation.cog.region;
import org.springframework.data.jpa.repository.JpaRepository;
@RepositoryRestResource(collectionResourceRel = "region", path = "regions")
public interface RegionRepository extends JpaRepository<Region, String> {
(...)
}
3. Créer un nouveau package
4a. Création de la classe entité (mappe un objet de la couche de persistence, i.e. en général une table de la base de données)
4b. Création du repository
@RepositoryRestResource
N'est pas obligatoire. Permet de préciser et / ou modifier les détails d'exportation et d'implémentation de l'Interface lors de l'exécution.
Par défaut, c'est le nom de l'entité qui est utilisé pour mapper le endpoint.
Le Repository est donc une interface et les opérations possibles sont définies au niveau de l'Interface étendue, JpaRepository dans notre exemple.
Les différents types de Repository :
- CrudRepository<T, ID extends Serializable>
- PagingAndSortingRepository<T, ID extends Serializable>
- RevisionRepository<T, ID extends Serializable>
- JpaRepository<T, ID extends Serializable>
...
Cf. la javadoc...
Spring Data REST utilise HAL pour formatter la réponse en JSON. Ce format fournit en particulier des informations supplémentaires pour la navigation dans le résultat de la requête.
Documentation de référence ici
Pas utilisé à la DTSI !
Cloner le projet :
https://github.com/DVanderstoken/formation-trinity-backend-final.git
et basculer sur la branche feat/spring-data-jpa
git clone https://github.com/DVanderstoken/formation-trinity-backend-final.git
Clonage dans 'formation-trinity-backend-final'...
remote: Counting objects: 162, done.
remote: Compressing objects: 100% (73/73), done.
remote: Total 162 (delta 36), reused 149 (delta 23), pack-reused 0
Réception d'objets: 100% (162/162), 948.83 KiB | 43.00 KiB/s, fait.
Résolution des deltas: 100% (36/36), fait.
Vérification de la connectivité... fait.
cd formation-trinity-backend-final/
git checkout feat/spring-data-jpa
La branche feat/spring-data-jpa est paramétrée pour suivre la branche distante feat/spring-data-jpa depuis origin.
Basculement sur la nouvelle branche 'feat/spring-data-jpa'
Effectuer des recherches "complexes" :
Rappel de ce qui a été vu :
public interface RegionRepository extends JpaRepository<Region, String> {
Region findByCodeRegionOrNomTypoEnrichie(final String pCodeRegion,
final String pNomTypoEnrichie);
}
Beaucoup de combinaison possible mais lisibilité rapidement limitée du code, en particulier le nom de la méthode...
Mise en oeuvre de l'API Specification :
- Dans le fichier pom.xml du projet :
<build>
<plugins>
(...)
<!-- Used to generate JPA Static Metamodel & Specification API usage -->
<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>process</id>
<goals>
<goal>process</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<processors>
<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
</processors>
</configuration>
</execution>
</executions>
<configuration>
<outputDirectory>src/main/generated</outputDirectory>
</configuration>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>5.0.2.Final</version>
</dependency>
</dependencies>
</plugin>
(...)
</plugins>
</build>
Mise en oeuvre de l'API Specification :
- Dans l'environnement de développement Eclipse :
1/ clic droit sur le projet concerné ;
2/ clic sur [Properties] ;
3/ Dans la boîte de dialogue des préférences du projet, déplier [Java Compiler], et sélectionner [Annotation processing] :
3.1/ cocher [Enable project specific settings]
3.2/ cocher [Enable annotation processing]
3.3/ cocher [Enable processing in editor]
3.4/ spécifier le dossier dans lequel seront stockées les sources générées
4/ clic sur [OK]
Mise en oeuvre de l'API Specification :
Cette configuration va permettre la génération automatique des classes correspondant au JPA Static Metamodel pour les classes annotées @Entity.
Les classes ainsi créées sont suffixées par '_'.
Mise en oeuvre de l'API Specification :
L'interface Specification<T> définit une seule méthode permettant de créer un prédicat - au sens JPA - et de construire la clause WHERE :
javax.persistence.criteria.Predicate toPredicate(javax.persistence.criteria.Root<T> root,
javax.persistence.criteria.CriteriaQuery<?> query,
javax.persistence.criteria.CriteriaBuilder cb)
Mise en oeuvre de l'API Specification :
Modification de l'interface Repository (exemple) :
package nc.gouv.dtsi.etudes.axi.formation.cog.commune;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface CommuneRepository
extends JpaRepository<CommuneAbregee, CommuneId>,
JpaSpecificationExecutor<CommuneAbregee> {
}
Il faut hériter de l'interface JpaSpecificationExecutor<T> qui amène les méthodes de recherche :
long count(Specification<T> spec)
List<T> findAll(Specification<T> spec)
Page<T> findAll(Specification<T> spec, Pageable pageable)
List<T> findAll(Specification<T> spec, Sort sort)
T findOne(Specification<T> spec)
Mise en oeuvre de l'API Specification :
Les prédicats sont créés de manière unitaire dans une classe dédiée et assemblés lors de l'appel au Repository :
package nc.gouv.dtsi.etudes.axi.formation.cog.commune;
import (...);
public class CommuneSpecification {
public static Specification<CommuneAbregee> isInRegion(final String codeRegion) {
return checkSpec(CommuneAbregee_.codeRegion, codeRegion);
}
public static Specification<CommuneAbregee> isInDepartement(
final String codeDepartement) {
return checkSpec(CommuneAbregee_.codeRegion, codeDepartement);
}
public static Specification<CommuneAbregee> isTheCommune(
final String codeCommune) {
return checkSpec(CommuneAbregee_.codeRegion, codeCommune);
}
private static Specification<CommuneAbregee> checkSpec(
SingularAttribute<Commune, String> propertyName, String content) {
if (StringUtils.trimToNull(content) != null) {
return (root, query, cb) -> cb.equal(root.get(propertyName),
content);
}
return null;
}
}
Mise en oeuvre de l'API Specification :
Les prédicats sont créés de manière unitaire dans une classe dédiée et assemblés lors de l'appel au Repository :
package nc.gouv.dtsi.etudes.axi.formation.cog.commune;
import (...);
@RestController
@RequestMapping(path = "/api/v1")
@CrossOrigin(origins = "*")
public class CommuneController {
@RequestMapping(path = "/communes/search"
, method = RequestMethod.GET
, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Page<CommuneAbregee> search(
@RequestParam(name = "region",
required = false) final String pRegion,
@RequestParam(name = "departement",
required = false) final String pDepartement,
@RequestParam(name = "commune",
required = false) final String pCommune,
Pageable pPageable) {
return lCommuneRepository.findAll(where(isInRegion(pRegion))
.and(isInDepartement(pDepartement)).and(isTheCommune(pCommune)),
pPageable);
}
}
Exécuter l'application et tester...
cf. src/main/ressources/application.yml
Documentation ici
Première approche à approfondir lors de l'intégration à l'écosystème Trinity Backend (Cf. Spring Cloud).
Dépendances supplémentaires :
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</dependency>
<dependency>
<groupId>net.sf.supercsv</groupId>
<artifactId>super-csv</artifactId>
<version>2.4.0</version>
</dependency>
Documentation de référence ici
Outil de migration des bases de données.
- Utilise :
- le langage SQL
- le langage Java
- un mix des deux
- Fournit un client de ligne de commande
- S'intègre facilement avec Spring
Convention over configuration !
Configuration (cf. fichier application.yml) :
flyway:
enabled: true
s'appuie sur la configuration Spring d'accès à le source de données !
spring:
datasource:
url: jdbc:hsqldb:mem:testdb
driverClassName: org.hsqldb.jdbcDriver
username: sa
password:
platform: hsqldb
Les migrations
Cf. répertoires :
- src/main/resources.db/migration (migrations SQL)
- src/main/java/db/migration (migrations Java)
- Convention over configuration
- importance de la gestion des versions
- pas de retour arrière !
ou comment éviter...
Adapter au(x) besoin(s) et au contexte...
Cas particulier ici : peu, voire pas de services (couche métier)
Tests par couche !
--> tests de la couche d'accès aux données
--> tests de la couche controllers
Exemple sur la couche d'accès aux données :
package nc.gouv.dtsi.etudes.axi.formation.cog.tests;
import (...);
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FormationTrinityBackendApplication.class)
public class RegionRepositoryTest extends FormationTrinityBackendAbstractTest {
@Autowired
private RegionRepository lRegionRepository;
@Test
public void shouldReturnNonEmptyRegionList() {
List<Region> result = lRegionRepository.findAll();
assertFalse(result.isEmpty());
}
}
@RunWith(SpringJUnit4ClassRunner.class)
Junit va invoquer la classe spécifier par l'annotation @RunWith pour exécuter les tests. La classe spécifiée SpringJUnit4ClassRunner est une extension JUnit qui fournit à JUnit les fonctionnalités du Spring TestContext Framework.
@SpringBootTest(classes = FormationTrinityBackendApplication.class)
Permet d'exécuter les tests dans le contexte, avec la configuration et dans l'environnement de l'application Spring Boot à laquelle les tests se rapportent.
Exemple sur la couche controller :
package nc.gouv.dtsi.etudes.axi.formation.layer.controller;
import (...)
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FormationTrinityBackendApplication.class)
@WebAppConfiguration
public class RegionControllerTest extends FormationTrinityBackendAbstractTest {
private MockMvc lMockMvc;
@Autowired
private WebApplicationContext lContext;
@Before
public void setUp() {
lMockMvc = MockMvcBuilders.webAppContextSetup(lContext).build();
}
@Test
public void shouldReturnNonEmptyRegionListWithHttpCode200()
throws Exception {
lMockMvc.perform(get("/api/v1/regions")).andExpect(status().isOk())
.andExpect(
content().contentType(MediaType.APPLICATION_JSON_UTF8));
}
}
@WebAppConfiguration
L'utilisation de cette annotation sur une classe de test signifie que ledit test va être exécuté dans le contexte de l'application (Web).
Un peu de factorisation...
Ce qu'il y a en commun :
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FormationTrinityBackendApplication.class)
Une proposition ?
Un peu de factorisation...
package nc.gouv.dtsi.etudes.axi.formation;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import nc.gouv.dtsi.etudes.axi.formation.FormationTrinityBackendFinalApplication;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FormationTrinityBackendFinalApplication.class)
public abstract class FormationTrinityBackendAbstractTest {
}
public class RegionRepositoryTest extends FormationTrinityBackendAbstractTest {
(...)
public class RegionControllerTest extends FormationTrinityBackendAbstractTest {
(...)
cf. la documentation ici.
Enrichir le fichier pom.xml de l'application avec :
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-data-rest</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
Créer une classe de configuration de la génération de la documentation :
package nc.gouv.dtsi.etudes.axi.formation;
import (...);
@Configuration
@EnableSwagger2
public class ApplicationApiDocConfiguration {
@Autowired
private Environment environment;
/**
* Enabled only if Spring active profiles contains the apidoc profile !
*
* @return a SpringFox Docket
*/
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.enable(Arrays.asList(this.environment.getActiveProfiles())
.contains("apidoc"));
}
}
La documentation des APIs est ici rendue disponible uniquement s'il y a un profile actif - au sens Spring du terme - nommé "apidoc".
La présence de ce profil peut ainsi être gérée en fonction des cycles (intégration, qualification, production, etc.) s'il n'est pas souhaité de la rendre disponible dans un cycle donné.