diff --git a/index.html b/index.html index 0714bc6a4cad9f492f28cbb8b91230fe405a070c..ef1f2a4a34b81669a6ebd2fdfa10d4d392409516 100644 --- a/index.html +++ b/index.html @@ -216,6 +216,16 @@ </h5> <p class="card-text">On parle aux gens</p> </div> + <div class="card-footer"> + <div class="btn-group" role="group"> + <a href="w06-interoperability/06-interoperability.html" class="btn btn-primary" >Cours</a> + <a href="w06-interoperability/06-interoperability.pdf" class="btn btn-secondary"><i class="bi bi-file-earmark-pdf-fill"></i></a> + </div> + <div class="btn-group" role="group"> + <a href="w06-interoperability/06-tp-interoperability.html" class="btn btn-primary">TP</a> + <a href="w06-interoperability/06-tp-interoperability.pdf" class="btn btn-secondary"><i class="bi bi-file-earmark-pdf-fill"></i></a> + </div> + </div> </div> </div> <div class="col"> @@ -333,7 +343,11 @@ </div> <div id="footer"> - <div id="footer-text" class="index-last-update">Last updated Wed. 16 Oct</div> +<<<<<<< Updated upstream + <div id="footer-text" class="index-last-update">Last updated mer.. 23 oct.</div> +======= + <div id="footer-text" class="index-last-update">Last updated mer.. 23 oct.</div> +>>>>>>> Stashed changes </div> <script diff --git a/w06-interoperability/06-interoperability.html b/w06-interoperability/06-interoperability.html new file mode 100644 index 0000000000000000000000000000000000000000..171849f4f3ed51640179ce94820d35c37f607d0a --- /dev/null +++ b/w06-interoperability/06-interoperability.html @@ -0,0 +1,485 @@ + <!doctype html> +<html lang="fr"> + +<head> + <meta charset="utf-8"> + + <title>ALOM - Interoperability</title> + + <meta name="description" content="ALOM - Interoperability"> + <meta name="author" content="Julien WITTOUCK <julien@codeka.io>"> + + <meta name="apple-mobile-web-app-capable" content="yes"/> + <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/> + + <meta name="viewport" + content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui"> + + <link rel="stylesheet" href="../reveal/dist/reveal.css"> + <link rel="stylesheet" href="../reveal/dist/theme/white.css" id="theme"> + + <link rel="stylesheet" href="../css/miage-lille.css"/> + + <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" + integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous"> + +</head> + +<body> + +<div class="reveal"> + + <div class="slides"> + <section> + <h1>ALOM</h1> + <h2>đ€ Interoperability</h2> + </section> + + <section> + <h3><i class="fab fa-uber"></i> UBER</h3> + <img src="images/Microservice-Architecture-Of-UBER.png" style="width: 50%"/> + </section> + + <section> + <h2>ProblĂ©matique</h2> + <ul> + <li>Comment communiquer avec les autres micro-services?</li> + <li>Comment communiquer avec les partenaires?</li> + </ul> + <img src="images/fry.png"/> + </section> + + <section data-markdown> + <textarea data-template> + ## Comment faire communiquer des processus ? + Sur une mĂȘme machine : IPC - Inter Process Communication + * mĂ©moire partagĂ©e + * message queue + * sĂ©maphores + + Sur des machines sĂ©parĂ©es : RĂ©seau + * sockets + </textarea> + </section> + + <section data-markdown> + <textarea data-template> + ## En Java â - RMI (Remote Method Invocation) + + Communication entre 2 JVM + + `java.rmi.*` + + DĂ©finition d'une interface qui `extends java.rmi.Remote`. + + ParamĂštres sĂ©rialisĂ©s en binaire, interface `java.io.Serializable` Ă implĂ©menter. + </textarea> + </section> + + <section> + <h3>Contraintes â </h3> + <p>Toutes les applications ne sont pas Ă©crites dans le mĂȘme langage (Java, .Net, NodeJS, PHP, Ruby, + Python...)</p> + <p>Les partenaires n'ont pas forcĂ©ment les mĂȘmes environnements (rĂ©seaux, firewall)</p> + + <h3>Solution đ </h3> + <p>DĂ©finition d'une norme de communication, basĂ©e sur des standards.</p> + </section> + + <section> + <h4>Web services & web-sockets đ </h4> + <ul> + <li> + Protocole HTTP(S) + <ul> + <li>Facile Ă implĂ©menter (texte)</li> + <li>Passe les firewalls (port 80/443)</li> + <li>SĂ©curisation avec SSL/TLS đ</li> + </ul> + </li> + <li> + Formats de donnĂ©es + <ul> + <li>SOAP : XML</li> + <li>REST : JSON</li> + </ul> + </li> + <li> + Contrat de service + <ul> + <li>SOAP : WSDL</li> + <li>REST : Swagger...</li> + </ul> + </li> + + </ul> + </section> + + <section> + <section> + <h2>Web services REST</h2> + <img src="images/rest.png"/> + </section> + + <section> + <h2>Web services REST</h2> + <p><b>RE</b>presentational</p> + <p><b>S</b>tate</p> + <p><b>T</b>ransfert</p> + </section> + + <section> + <h2>Web services REST</h2> + <h3>Principes architecturaux</h3> + <ul> + <li>Architecture dĂ©couplĂ©e client/serveur</li> + <li>Sans Ă©tat (pas de session)</li> + <li> + AccĂšs Ă des ressources: + <ul> + <li>IdentifiĂ©es de maniĂšre unique</li> + <li>ManipulĂ©es via des reprĂ©sentations (JSON, XML, HTML...)</li> + <li>Compatible avec une mise en cache</li> + <li>DonnĂ©es HypermĂ©dia</li> + </ul> + </li> + </ul> + </section> + + <section data-markdown> + <textarea data-template> + ## Web services REST + + Utilisation des codes HTTP ([Http Status Dogs](https://httpstatusdogs.com/)) + + â don't + + ```text +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 15 + +{"error":"Content Not Found"} + ``` + + â do + + ```text +HTTP/1.1 404 NOT FOUND +Content-Type: application/json +Content-Length: 15 + +{"error":"Pokemon with id {152} does not exists"} + ``` + </textarea> + </section> + + <section> + <h3>NĂ©gociation de contenu</h3> + <p>Principes au coeur du web</p> + <p>Le client indique au serveur ses attentes via des headers HTTP</p> + <img src="carbon/headers.png"/> + </section> + + <section> + <h3>NĂ©gociation de contenu</h3> + <ul> + <li>Format des donnĂ©es : header <code>Accept</code></li> + <li>Traduction : header <code>Accept-Language</code></li> + <li>Retour avec les headers <code>Content-Type</code> et <code>Content-Language</code></li> + </ul> + <p>La <a href="https://www.rfc-editor.org/rfc/rfc4229.html" target="_blank">RFC 4229</a> liste les headers possibles.</p> + <img src="carbon/content-negociation-request.png"/> + </section> + + <section> + <h3>Web services REST</h3> + <h4>HATEOAS : Hypermedia As The Engine Of Application State</h4> + <p>Le message contient les informations permettant de manipuler l'application</p> + </section> + + <section> + <h4>Web services REST : Exemple XML</h4> + <img src="carbon/rest-xml.png"/> + </section> + + <section> + <h4>Web services REST : Exemple JSON</h4> + <img src="carbon/rest-json.png"/> + </section> + + <section> + <h4>HATEOAS</h4> + <p>Excellent talk de Julien Topçu</p> + <iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/7qqzqse1hgc?si=klW6B9MVgRVWkzKF" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> + </section> + + <section> + <h2>Contrat de service REST</h2> + <h3><a href="https://swagger.io/" target="_blank">OpenAPI (ex Swagger)</a></h3> + <p><i>OpenAPI</i> est la spec, <i>Swagger</i> une implĂ©mentation</p> + <p>Description des API au format JSON ou YAML : <i>OpenApi</i></p> + <p>Rendu Web + "Try It" : <i>Swagger</i></p> + <a href="carbon/pokemon-api.png" target="_blank">Pokemon API</a> + </section> + + <section> + <h3><a href="http://editor.swagger.io/">Swagger</a></h3> + <p>Affichage de la documentation sous forme de page web</p> + <img src="images/swagger-hub.png"/> + </section> + + <section> + <h3><a href="http://editor.swagger.io/">Swagger</a></h3> + <p>GĂ©nĂ©ration de squelettes clients/serveur</p> + <img src="images/swagger-generate.png"/> + </section> + + <section> + <h3><a href="http://editor.swagger.io/">Swagger</a></h3> + <p>GĂ©nĂ©ration de squelettes clients/serveur</p> + <img src="images/swagger-generate.png"/> + </section> + + <section> + <h3><i>Swagger</i> et <i>OpenApi</i> en Spring Boot</h3> + <p>Pas d'implĂ©mentation de la part de Spring</p> + <p>1 projet Open Source</p> + <p>springdoc-openapi (<a href="https://springdoc.org/">doc</a>)</p> + </section> + + <section> + <p>Exposition d'un service REST Spring</p> + <img src="carbon/restcontroller.png" style="width: 80%"/> + </section> + + <section> + <h4>En Spring</h4> + <p>/api/pokemon-types/{id}</p> + <p>/api/pokemon-types?orderBy=name</p> + <p>/api/pokemon-types?type=poison</p> + <ul> + <li><code>@RequestMapping</code> : Ă©couter une URI</li> + <li><code>@PathVariable</code> : rĂ©cupĂ©rer les variables d'URI entre '{}'</li> + <li><code>@RequestParam</code> : rĂ©cupĂ©rer les paramĂštres de requĂȘte (query-strings '?a=b&c=d')</li> + <li><code>@RequestBody</code> : rĂ©cupĂ©rer le corps de la requĂȘte</li> + </ul> + </section> + + <section> + <h2>@RequestMapping</h2> + <p><code>/api/pokemon-types</code></p> + <img src="carbon/PokemonController-requestmapping.png"/> + </section> + + <section> + <h2>@PathVariable</h2> + <p><code>/api/pokemon-types/{id}</code></p> + <img src="carbon/PokemonController-pathvariable.png"/> + </section> + + <section> + <h2>@RequestParam</h2> + <p><code>/api/pokemon-types?orderBy=name</code></p> + <img src="carbon/PokemonController-requestparam.png"/> + </section> + + <section> + <h2>@RequestParam</h2> + <p><code>/api/pokemon-types?type=poison</code></p> + <img src="carbon/PokemonController-requestparam-multiple.png"/> + </section> + + <section> + <h2>@RequestBody</h2> + <p><code>POST /api/trainers</code></p> + <img src="carbon/TrainerController-requestbody.png"/> + </section> + + </section> + + <section> + <section data-markdown> + <textarea data-template> + #### Consommation REST en Spring + + `RestTemplate` (maintenance) + + ```java + @Service + class PokemonTypeServiceImpl implements PokemonTypeService{ + + private RestTemplate restTemplate; + + PokemonTypeServiceImpl(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + @Override + public List<PokemonType> listPokemonsTypes() { + var pokemonTypes = restTemplate + .getForObject(pokemonServiceUrl+"/pokemon-types", PokemonType[].class); + return Arrays.asList(pokemonTypes); + } + } + ``` + </textarea> + </section> + + <section> + <h3>Spring RestTemplate</h3> + <p>Classe utilitaire pour effectuer des appels REST</p> + <ul> + <li>ExĂ©cute les requĂȘtes HTTP : GET/POST/PUT/PATCH/OPTIONS/DELETE/HEAD</li> + <li>Utilise <code>jackson-databind</code> pour convertir les objets Java en JSON !</li> + </ul> + </section> + + <section data-markdown> + <textarea data-template> + #### HTTP Interface + + On dĂ©finit une interface avec des mĂ©thodes annotĂ©es `@HttpExchange`. + + Utilisation des annotations `@RequestParam`, `@PathVariable`, `@RequestBody`, `@RequestHeader` pour les paramĂštres. + + On dĂ©clare en type de retour le type attendu, ou `ResponseEntity<T>`. + + Spring gĂ©nĂšre un proxy dynamique qui implĂ©mente l'interface et exĂ©cute les appels HTTP (comme les Spring data repository). + </textarea> + </section> + + <section data-markdown> + <textarea data-template> + #### [HTTP Interface](https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-http-interface) + + On dĂ©clare une interface + + ```java + @HttpExchange("/pokemon-types") +public interface PokemonTypeApiRepository { + + @GetExchange + List<PokemonType> getAllPokemons(); + + @GetExchange("/{id}") + PokemonType getPokemonFromId(@PathVariable int id); + +} + ``` + + </textarea> + </section> + + <section data-markdown> + <textarea data-template> + On a besoin de Spring REST pour le client HTTP + + ```xml +<dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-rest</artifactId> +</dependency> + ``` + + On configure un client, et un proxy + + ```java +// configuration Ă mettre dans une classe annotĂ©e @Configuration +@Bean +PokemonTypeApiRepository configurePokemonTypeApiRepository(@Value("${pokemonType.service.url}") String pokemonTypeServiceUrl) { + var restClient = RestClient.builder().baseUrl(pokemonTypeServiceUrl).build(); + var adapter = RestClientAdapter.create(restClient); + var factory = HttpServiceProxyFactory.builderFor(adapter).build(); + return factory.createClient(PokemonTypeApiRepository.class); +} + ``` + + On reçoit le bean en injection de dĂ©pendance comme d'habitude. + </textarea> + </section> + + <section data-markdown> + <textarea data-template> + ### WebClient + + ```xml +<dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-webflux</artifactId> +</dependency> + ``` + + Client pour la programmation rĂ©active & synchrone. + + ```java + // reactive + Flux<PokemonType> pokemonsFlux = client.get().uri("/pokemon-types") + .retrieve() + .bodyToFlux(PokemonType.class); + + // synchrone + List<PokemonType> pokemonsList = pokemonsFlux + .collectList() + .block(); + ``` + </textarea> + </section> + </section> + + <section> + <h2>Outillage</h2> + <h3>đȘ <code>$> curl</code></h3> + ou + <h3>Postman/Insomnia/Bruno</h3> + <div style="display: flex; align-items: center; justify-content: center; height: 100px; gap: 10px;"> + <a href="https://www.postman.com/"><img src="images/logo-postman.png"/></a> + <a href="https://insomnia.rest/"><img src="images/logo-insomnia.png"/></a> + <a href="https://www.usebruno.com/"><img src="images/logo-bruno.png"/></a> + </div> + </section> + + <section> + <h2>Les autres moyens de communication rĂ©seau</h2> + <ul> + <li>Reactive Streams - Support avec Spring WebFlux</li> + <li>GraphQL - Support avec <a href="https://spring.io/projects/spring-graphql">Spring GraphQL</a></li> + <li>gRPC - pas d'implĂ©mentation officielle par Spring</li> + </ul> + </section> + + <section> + <h2>TP</h2> + <img src="../images/leonidas.png"/> + <p> + <a href="06-tp-interoperability.html">Interoperability</a> + </p> + </section> + + </div> + +</div> + +<script src="../reveal/dist/reveal.js"></script> +<script src="../reveal/plugin/markdown/markdown.js"></script> +<link rel="stylesheet" href="../reveal/plugin/highlight/zenburn.css"> +<script src="../reveal/plugin/highlight/highlight.js"></script> +<script> + // More info about initialization & config: + // - https://revealjs.com/initialization/ + // - https://revealjs.com/config/ + Reveal.initialize({ + hash: true, + + // Learn about plugins: https://revealjs.com/plugins/ + plugins: [RevealMarkdown, RevealHighlight], + markdown: { + smartypants: true + } + }); +</script> + +<aside class="miage_aside_logo"></aside> + +</body> +</html> diff --git a/w06-interoperability/06-interoperability.pdf b/w06-interoperability/06-interoperability.pdf deleted file mode 100644 index b53a84e314ae72e81376ad4d4bd73cedba2401fd..0000000000000000000000000000000000000000 Binary files a/w06-interoperability/06-interoperability.pdf and /dev/null differ diff --git a/w06-interoperability/06-tp-interoperability.adoc b/w06-interoperability/06-tp-interoperability.adoc new file mode 100644 index 0000000000000000000000000000000000000000..3c6470668b75f96fde93bbf026ee41f18b4f4cbc --- /dev/null +++ b/w06-interoperability/06-tp-interoperability.adoc @@ -0,0 +1,421 @@ +:source-highlighter: rouge +:prewrap!: + +:icons: font + +:toc: left +:toclevels: 4 + +:linkattrs: + +:sectlinks: +:sectanchors: +:sectnums: + +:experimental: + += ALOM - TP 6 - Interoperability + +== PrĂ©sentation et objectifs + +Le but est de continuer le dĂ©veloppement de notre architecture "Ă la microservice". + +Nous allons aujourd'hui implĂ©menter des fonctionnalitĂ©s de traduction dans notre micro-service pokemon-type! + +En effet, nos donnĂ©es de Pokemon sont aujourd'hui en anglais uniquement, ce qui peut ĂȘtre dĂ©courageant pour nos futurs joueurs français ! + +image::images/trainer-gui.png[] + +Nous allons dĂ©velopper : + +1. La gestion des traductions dans notre api pokemon-type +2. L'affichage du Pokedex traduit ! + +Nous allons Ă©galement réécrire nos appels utilisant le `RestTemplate` pour utiliser les _HTTP Interfaces_. + +[TIP] +Nous ne repartons pas uniquement de zĂ©ro pour ce TP. Nous nous appuyons sur les TP prĂ©cĂ©dents + +== pokemon-type-api + +=== Les donnĂ©es de traduction + +Pour faciliter le travail, j'ai créé deux fichier JSON contenant les traductions des noms de Pokemon en français et anglais. + +Ces fichiers sont disponible ici: link:translations-fr.json[translations-fr.json,window="_blank"] link:translations-en.json[translations-en.json,window="_blank"] + +DĂ©posez ces fichiers dans votre rĂ©pertoire `src/main/resources`. + +=== Le BO + +CrĂ©ez un record `Translation` + +.com.miage.alom.tp.pokemon_type_api.Translation +[source,java,linenums] +---- +package com.miage.alom.tp.pokemon_type_api.bo; + +public record Translation(int id, String name) {} +---- + +=== Le repository + +==== L'interface + +L'interface de ce repository de traduction est simple : + +.com.miage.alom.tp.pokemon_type_api.TranslationRepository +[source,java,linenums] +---- +package com.miage.alom.tp.pokemon_type_api.repository; + +import java.util.Locale; + +public interface TranslationRepository { + String getPokemonName(int id, Locale locale); +} +---- + +==== Les tests unitaires + +ImplĂ©mentez les tests unitaires suivants : + +[source,java,linenums] +.com.miage.alom.tp.pokemon_type_api.repository.TranslationRepositoryImplTest.java +---- +package com.miage.alom.tp.pokemon_type_api.repository; + +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import java.util.Locale; + +import static org.junit.jupiter.api.Assertions.*; + +class TranslationRepositoryImplTest { + + private TranslationRepositoryImpl repository = new TranslationRepositoryImpl(); + + @Test + void getPokemonName_with1_inFrench_shouldReturnBulbizarre(){ + assertEquals("Bulbizarre", repository.getPokemonName(1, Locale.FRENCH)); + assertEquals("Bulbizarre", repository.getPokemonName(1, Locale.FRANCE)); + } + + @Test + void getPokemonName_with1_inEnglish_shouldReturnBulbizarre(){ + assertEquals("Bulbasaur", repository.getPokemonName(1, Locale.ENGLISH)); + assertEquals("Bulbasaur", repository.getPokemonName(1, Locale.UK)); + assertEquals("Bulbasaur", repository.getPokemonName(1, Locale.US)); + } + + @Test + void applicationContext_shouldLoadPokemonRepository(){ + var context = new AnnotationConfigApplicationContext("com.miage.alom.tp.pokemon_type_api"); + var repoByName = context.getBean("translationRepositoryImpl"); + var repoByClass = context.getBean(TranslationRepository.class); + + assertEquals(repoByName, repoByClass); + assertNotNull(repoByName); + assertNotNull(repoByClass); + } + +} +---- + +==== L'implĂ©mentation + +DĂ©veloppez l'implĂ©mentation du `TranslationRepository`. + +[source,java,linenums] +.com.miage.alom.tp.pokemon_type_api.TranslationRepositoryImpl.java +---- +package com.miage.alom.tp.pokemon_type_api; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.miage.alom.tp.pokemon_type_api.bo.Translation; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Repository; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +@Repository +public class TranslationRepositoryImpl implements TranslationRepository { + + record Key(Locale locale, int pokemonId){} // <3> + + private Map<Key, Translation> translations; + + private ObjectMapper objectMapper; + + public TranslationRepositoryImpl() { + try { + // TODO <2> + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public String getPokemonName(int id, Locale locale) { + // TODO <1> + } +} +---- +<1> ImplĂ©mentez la rĂ©cupĂ©ration du nom d'un Pokemon ! +<2> Alimentez la map des traductions en chargeant les fichiers, et en rĂ©cupĂ©rant leur contenu +<3> On utilise un record local Ă notre classe comme clĂ© de Map ! + +[NOTE] +==== +La rĂ©cupĂ©ration d'un fichier dans le classpath peut se fair en Spring avec la classe `ClassPathResource`. +Inspirez-vous du `PokemonTypeRepository` pour le reste. +==== + +=== Le service + +Maintenant que nous avons un repository capable de gĂ©rer les traductions, nous devons les utiliser. +Un bon endroit pour cela est la couche service. + +Spring utilise la classe `AcceptHeaderLocaleResolver` dans la `DispatcherServlet` pour venir alimenter un objet `LocaleContextHolder`. +Nous pouvons donc utiliser cet objet pour rĂ©cupĂ©rer la langue demandĂ©e par la requĂȘte courante ! + +Ajoutez les tests unitaires suivant au `PokemonTypeServiceImplTest`: + +[source,java,linenums] +.PokemonTypeServiceImplTest.java +---- +@Test +void pokemonNames_shouldBeTranslated_usingLocaleResolver(){ + var pokemonTypeService = new PokemonTypeServiceImpl(); + + var pokemonTypeRepository = mock(PokemonTypeRepository.class); + pokemonTypeService.setPokemonTypeRepository(pokemonTypeRepository); + when(pokemonTypeRepository.findPokemonTypeById(25)).thenReturn(new PokemonType()); + + var translationRepository = mock(TranslationRepository.class); + pokemonTypeService.setTranslationRepository(translationRepository); + when(translationRepository.getPokemonName(25, Locale.FRENCH)).thenReturn("Pikachu-FRENCH"); + + LocaleContextHolder.setLocale(Locale.FRENCH); + + var pikachu = pokemonTypeService.getPokemonType(25); + + assertEquals("Pikachu-FRENCH", pikachu.name()); + verify(translationRepository).getPokemonName(25, Locale.FRENCH); +} + +@Test +void allPokemonNames_shouldBeTranslated_usingLocaleResolver(){ + var pokemonTypeService = new PokemonTypeServiceImpl(); + + var pokemonTypeRepository = mock(PokemonTypeRepository.class); + pokemonTypeService.setPokemonTypeRepository(pokemonTypeRepository); + + var pikachu = new PokemonType(25, null, null, null); + var raichu = new PokemonType(26, null, null, null); + when(pokemonTypeRepository.findAllPokemonType()).thenReturn(List.of(pikachu, raichu)); + + // on simule le repository de traduction + var translationRepository = mock(TranslationRepository.class); + pokemonTypeService.setTranslationRepository(translationRepository); + when(translationRepository.getPokemonName(25, Locale.FRENCH)).thenReturn("Pikachu-FRENCH"); + when(translationRepository.getPokemonName(26, Locale.FRENCH)).thenReturn("Raichu-FRENCH"); + + LocaleContextHolder.setLocale(Locale.FRENCH); + + var pokemonTypes = pokemonTypeService.getAllPokemonTypes(); + + assertEquals("Pikachu-FRENCH", pokemonTypes.get(0).name()); + assertEquals("Raichu-FRENCH", pokemonTypes.get(1).name()); + verify(translationRepository).getPokemonName(25, Locale.FRENCH); + verify(translationRepository).getPokemonName(26, Locale.FRENCH); +} +---- + +Pour faire passer les tests unitaires, remplacez le nom du type de pokemon, aprĂšs l'avoir rĂ©cupĂ©rĂ© du repository, par sa traduction. + +NOTE: Les records sont immutables, donc vous allez devoir trouver un moyen pour copier les donnĂ©es. + +=== Le test d'intĂ©gration + +Modifiez le `PokemonTypeControllerIntegrationTest` pour ajouter un test d'intĂ©gration : + +[source,java,linenums] +.PokemonTypeControllerIntegrationTest.java +---- +@Test +void getPokemon_withId1_shouldReturnBulbasaur() { + var bulbasaur = this.restTemplate.getForObject("http://localhost:" + port + "/pokemon-types/1", PokemonType.class); + assertNotNull(bulbasaur); + assertEquals(1, bulbasaur.id()); + assertEquals("Bulbasaur", bulbasaur.name()); //<1> +} + +@Test +void getPokemon_withId1AndFrenchAcceptLanguage_shouldReturnBulbizarre() { + var headers = new HttpHeaders(); + headers.setAcceptLanguageAsLocales(List.of(Locale.FRENCH)); //<2> + + var httpRequest = new HttpEntity<>(headers); + + var bulbizarreResponseEntity = this.restTemplate.exchange("http://localhost:" + port + "/pokemon-types/1", HttpMethod.GET, httpRequest, PokemonType.class); + var bulbizarre = bulbizarreResponseEntity.getBody(); + + assertNotNull(bulbizarre); + assertEquals(1, bulbizarre.id()); + assertEquals("Bulbizarre", bulbizarre.name()); //<3> +} +---- +<1> Cette requĂȘte sans paramĂštre particulier doit renvoyer la traduction par dĂ©faut (en anglais) +<2> On construit une requĂȘte en y ajoutant un header "Accept-Language" +<3> On doit bien rĂ©cupĂ©rer le nom du type de Pokemon traduit ! + +=== Les tests avec Postman + +Pour bien valider nos dĂ©veloppements, nous pouvons Ă©galement crĂ©er des tests avec Postman. + +NOTE: Vous pouvez aussi utiliser Bruno ou Insomnia pour cette partie ! Dans ce cas, le code des tests sera un peu diffĂ©rent. + + +Dans Postman, crĂ©ez une `Collection` + +image::images/postman-create-collection.png[] + +image::images/postman-create-collection-2.png[] + +Ajoutez-y quelques requĂȘtes. Pour ce faire, crĂ©ez une nouvelle requĂȘte, et enregistrez la dans votre collection. + +image::images/postman-create-request.png[] + +Utilisez l'onglet `Tests` pour y ajouter quelques tests. Cet onglet permet d'exĂ©cuter du code javascript, +permettant par exemple de valider les codes de retour HTTP ou le JSON reçu. + +CrĂ©ez les requĂȘtes suivantes, avec les tests associĂ©s : + +==== GET http://localhost:8080/pokemon-types/1 + +[source,javascript] +---- +pm.test("Bulbasaur", function () { + var bulbasaur = pm.response.json(); + pm.expect(bulbasaur.id).to.eq(1); + pm.expect(bulbasaur.name).to.eq("Bulbasaur"); +}); +---- + +==== GET http://localhost:8080/pokemon-types/1 - Accept-Language: fr + +[source,javascript] +---- +pm.test("Bulbasaur", function () { + var bulbasaur = pm.response.json(); + pm.expect(bulbasaur.id).to.eq(1); + pm.expect(bulbasaur.name).to.eq("Bulbizarre"); +}); +---- + +==== GET http://localhost:8080/pokemon-types + +[source,javascript] +---- +pm.test("all pokemon types", function () { + var jsonData = pm.response.json(); + pm.expect(jsonData.length).to.eq(151); +}); + +pm.test("Bulbasaur", function () { + var jsonData = pm.response.json(); + pm.expect(jsonData[0].name).to.eq("Bulbasaur"); +}); + +pm.test("Ivysaur", function () { + var jsonData = pm.response.json(); + pm.expect(jsonData[1].name).to.eq("Ivysaur"); +}); +---- + +==== GET http://localhost:8080/pokemon-types - Accept-Language: fr + +[source,javascript] +---- +pm.test("all pokemon types", function () { + var jsonData = pm.response.json(); + pm.expect(jsonData.length).to.eq(151); +}); + +pm.test("bulbizarre", function () { + var jsonData = pm.response.json(); + pm.expect(jsonData[0].name).to.eq("Bulbizarre"); +}); + +pm.test("Herbizarre", function () { + var jsonData = pm.response.json(); + pm.expect(jsonData[1].name).to.eq("Herbizarre"); +}); +---- + +==== Export de la collection + +Exportez votre collection Postman, dans le rĂ©pertoire `src/test/resources/rest/` de votre API. +Cela vous permettra de la rĂ©utiliser plus tard et de la partager avec les autres dĂ©veloppeurs ! + +=== OpenApi et Swagger + +Nous allons Ă©galement exposer une interface de type _Swagger_ afin de faciliter nos tests et nos dĂ©veloppements. + +Cette interface nous permettra Ă©galement de donner aux consommateurs de notre API un moyen facile de voir les ressources disponibles et les tester ! + +Pour exposer un swagger, nous allons utiliser la librairie https://springdoc.org//[springdoc, window="_blank"]. + +. Cette librairie analyse les `Controlleurs` Spring, pour gĂ©nĂ©rer de la documentation au format swagger. + +[TIP] +Cette librairie ne fait pas partie de Spring. Spring propose la gĂ©nĂ©ration de documentation Ă travers leur module https://spring.io/projects/spring-restdocs[spring rest-docs,window="_blank"] + +Ajoutez la dĂ©pendance suivante Ă votre `pom.xml` : + +[source,xml] +.pom.xml +---- +<dependency> + <groupId>org.springdoc</groupId> + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> + <version>2.2.0</version> +</dependency> +---- + +Votre IHM swagger sera disponible Ă l'url http://localhost:8080/swagger-ui.html[, window="_blank"], tandis que le JSON +sera disponible Ă l'url http://localhost:8080/v2/api-docs[, window="_blank"]. + +Configurez _springdoc_ pour n'afficher que vos propres controlleurs, et ignorer le _Basic Error Controller_. + +Trouvez comment faire dans la doc de _springdoc_ : https://springdoc.org/#springdoc-openapi-core-properties. + +== game-ui + +=== Utilisation des HTTP Interfaces + +Modifiez votre micro-service `game-ui` pour y intĂ©grer la gestion de la locale! + +Remplacez l'utilisation du `RestTemplate` par des _HTTP Interfaces_ Spring. + +Passez la Locale en paramĂštre lors de l'appel au micro-service _Pokemon Type_. + +Vous pouvez rĂ©cupĂ©rer la locale avec la mĂ©thode `LocaleContextHolder.getLocale()` de Spring directement +dans le PokemonTypeServiceImpl du `game-ui`, et la transmettre en utilisant le header `Accept-Language`. +. +De cette maniĂšre, la langue utilisĂ©e lors des Ă©changes sera celle du navigateur de l'utilisateur ! + +== trainer-api + +ImplĂ©mentez sur l'API trainer : + +. l'exposition d'un swagger / open API +. une collection Postman (ou Bruno) permettant de + * rĂ©cupĂ©rer la liste des dresseurs de Pokemon + * rĂ©cupĂ©rer un dresseur de Pokemon + * crĂ©er un dresseur de Pokemon diff --git a/w06-interoperability/06-tp-interoperability.html b/w06-interoperability/06-tp-interoperability.html new file mode 100644 index 0000000000000000000000000000000000000000..2cc45384640593b786f94858bfca04864bee8077 --- /dev/null +++ b/w06-interoperability/06-tp-interoperability.html @@ -0,0 +1,1341 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="UTF-8"> +<meta http-equiv="X-UA-Compatible" content="IE=edge"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="generator" content="Asciidoctor 2.0.23"> +<title>ALOM - TP 6 - Interoperability</title> +<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"> +<style> +/*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */ +/* Uncomment the following line when using as a custom stylesheet */ +/* @import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */ +html{font-family:sans-serif;-webkit-text-size-adjust:100%} +a{background:none} +a:focus{outline:thin dotted} +a:active,a:hover{outline:0} +h1{font-size:2em;margin:.67em 0} +b,strong{font-weight:bold} +abbr{font-size:.9em} +abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none} +dfn{font-style:italic} +hr{height:0} +mark{background:#ff0;color:#000} +code,kbd,pre,samp{font-family:monospace;font-size:1em} +pre{white-space:pre-wrap} +q{quotes:"\201C" "\201D" "\2018" "\2019"} +small{font-size:80%} +sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} +sup{top:-.5em} +sub{bottom:-.25em} +img{border:0} +svg:not(:root){overflow:hidden} +figure{margin:0} +audio,video{display:inline-block} +audio:not([controls]){display:none;height:0} +fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em} +legend{border:0;padding:0} +button,input,select,textarea{font-family:inherit;font-size:100%;margin:0} +button,input{line-height:normal} +button,select{text-transform:none} +button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer} +button[disabled],html input[disabled]{cursor:default} +input[type=checkbox],input[type=radio]{padding:0} +button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0} +textarea{overflow:auto;vertical-align:top} +table{border-collapse:collapse;border-spacing:0} +*,::before,::after{box-sizing:border-box} +html,body{font-size:100%} +body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased} +a:hover{cursor:pointer} +img,object,embed{max-width:100%;height:auto} +object,embed{height:100%} +img{-ms-interpolation-mode:bicubic} +.left{float:left!important} +.right{float:right!important} +.text-left{text-align:left!important} +.text-right{text-align:right!important} +.text-center{text-align:center!important} +.text-justify{text-align:justify!important} +.hide{display:none} +img,object,svg{display:inline-block;vertical-align:middle} +textarea{height:auto;min-height:50px} +select{width:100%} +.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em} +div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0} +a{color:#2156a5;text-decoration:underline;line-height:inherit} +a:hover,a:focus{color:#1d4b8f} +a img{border:0} +p{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility} +p aside{font-size:.875em;line-height:1.35;font-style:italic} +h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em} +h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0} +h1{font-size:2.125em} +h2{font-size:1.6875em} +h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em} +h4,h5{font-size:1.125em} +h6{font-size:1em} +hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em} +em,i{font-style:italic;line-height:inherit} +strong,b{font-weight:bold;line-height:inherit} +small{font-size:60%;line-height:inherit} +code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)} +ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit} +ul,ol{margin-left:1.5em} +ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0} +ul.circle{list-style-type:circle} +ul.disc{list-style-type:disc} +ul.square{list-style-type:square} +ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit} +ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0} +dl dt{margin-bottom:.3125em;font-weight:bold} +dl dd{margin-bottom:1.25em} +blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd} +blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)} +@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2} +h1{font-size:2.75em} +h2{font-size:2.3125em} +h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em} +h4{font-size:1.4375em}} +table{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal} +table thead,table tfoot{background:#f7f8f7} +table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left} +table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)} +table tr.even,table tr.alt{background:#f8f8f7} +table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6} +h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em} +h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400} +.center{margin-left:auto;margin-right:auto} +.stretch{width:100%} +.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table} +.clearfix::after,.float-group::after{clear:both} +:not(pre).nobreak{word-wrap:normal} +:not(pre).nowrap{white-space:nowrap} +:not(pre).pre-wrap{white-space:pre-wrap} +:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed} +pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed} +pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit} +pre>code{display:block} +pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal} +em em{font-style:normal} +strong strong{font-weight:400} +.keyseq{color:rgba(51,51,51,.8)} +kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap} +.keyseq kbd:first-child{margin-left:0} +.keyseq kbd:last-child{margin-right:0} +.menuseq,.menuref{color:#000} +.menuseq b:not(.caret),.menuref{font-weight:inherit} +.menuseq{word-spacing:-.02em} +.menuseq b.caret{font-size:1.25em;line-height:.8} +.menuseq i.caret{font-weight:bold;text-align:center;width:.45em} +b.button::before,b.button::after{position:relative;top:-1px;font-weight:400} +b.button::before{content:"[";padding:0 3px 0 2px} +b.button::after{content:"]";padding:0 2px 0 3px} +p a>code:hover{color:rgba(0,0,0,.9)} +#header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em} +#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table} +#header::after,#content::after,#footnotes::after,#footer::after{clear:both} +#content{margin-top:1.25em} +#content::before{content:none} +#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0} +#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf} +#header>h1:only-child{border-bottom:1px solid #dddddf;padding-bottom:8px} +#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap} +#header .details span:first-child{margin-left:-.125em} +#header .details span.email a{color:rgba(0,0,0,.85)} +#header .details br{display:none} +#header .details br+span::before{content:"\00a0\2013\00a0"} +#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)} +#header .details br+span#revremark::before{content:"\00a0|\00a0"} +#header #revnumber{text-transform:capitalize} +#header #revnumber::after{content:"\00a0"} +#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem} +#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em} +#toc>ul{margin-left:.125em} +#toc ul.sectlevel0>li>a{font-style:italic} +#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0} +#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none} +#toc li{line-height:1.3334;margin-top:.3334em} +#toc a{text-decoration:none} +#toc a:active{text-decoration:underline} +#toctitle{color:#7a2518;font-size:1.2em} +@media screen and (min-width:768px){#toctitle{font-size:1.375em} +body.toc2{padding-left:15em;padding-right:0} +body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px} +#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto} +#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em} +#toc.toc2>ul{font-size:.9em;margin-bottom:0} +#toc.toc2 ul ul{margin-left:0;padding-left:1em} +#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em} +body.toc2.toc-right{padding-left:0;padding-right:15em} +body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}} +@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0} +#toc.toc2{width:20em} +#toc.toc2 #toctitle{font-size:1.375em} +#toc.toc2>ul{font-size:.95em} +#toc.toc2 ul ul{padding-left:1.25em} +body.toc2.toc-right{padding-left:0;padding-right:20em}} +#content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px} +#content #toc>:first-child{margin-top:0} +#content #toc>:last-child{margin-bottom:0} +#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em} +#footer-text{color:hsla(0,0%,100%,.8);line-height:1.44} +#content{margin-bottom:.625em} +.sect1{padding-bottom:.625em} +@media screen and (min-width:768px){#content{margin-bottom:1.25em} +.sect1{padding-bottom:1.25em}} +.sect1:last-child{padding-bottom:0} +.sect1+.sect1{border-top:1px solid #e7e7e9} +#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400} +#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em} +#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible} +#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none} +#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221} +details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em} +details{margin-left:1.25rem} +details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent} +details>summary::-webkit-details-marker{display:none} +details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)} +details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)} +details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem} +.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic} +table.tableblock.fit-content>caption.title{white-space:nowrap;width:0} +.paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)} +.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%} +.admonitionblock>table td.icon{text-align:center;width:80px} +.admonitionblock>table td.icon img{max-width:none} +.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase} +.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere} +.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0} +.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px} +.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px} +.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center} +.exampleblock>.content>:first-child,.sidebarblock>.content>:first-child{margin-top:0} +.exampleblock>.content>:last-child,.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0} +.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em} +@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}} +@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}} +.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8} +.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)} +.listingblock>.content{position:relative} +.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5} +.listingblock:hover code[data-lang]::before{display:block} +.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5} +.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"} +.listingblock pre.highlightjs{padding:0} +.listingblock pre.highlightjs>code{padding:1em;border-radius:4px} +.listingblock pre.prettyprint{border-width:0} +.prettyprint{background:#f7f7f8} +pre.prettyprint .linenums{line-height:1.45;margin-left:2em} +pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0} +pre.prettyprint li code[data-lang]::before{opacity:1} +pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none} +table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none} +table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal} +table.linenotable td.code{padding-left:.75em} +table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} +pre.pygments span.linenos{display:inline-block;margin-right:.75em} +.quoteblock{margin:0 1em 1.25em 1.5em;display:table} +.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em} +.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify} +.quoteblock blockquote{margin:0;padding:0;border:0} +.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)} +.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0} +.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right} +.verseblock{margin:0 1em 1.25em} +.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility} +.verseblock pre strong{font-weight:400} +.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex} +.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic} +.quoteblock .attribution br,.verseblock .attribution br{display:none} +.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)} +.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none} +.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0} +.quoteblock.abstract{margin:0 1em 1.25em;display:block} +.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center} +.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf} +.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0} +.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem} +.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0} +p.tableblock:last-child{margin-bottom:0} +td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere} +td.tableblock>.content>:last-child{margin-bottom:-1.25em} +table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede} +table.grid-all>*>tr>*{border-width:1px} +table.grid-cols>*>tr>*{border-width:0 1px} +table.grid-rows>*>tr>*{border-width:1px 0} +table.frame-all{border-width:1px} +table.frame-ends{border-width:1px 0} +table.frame-sides{border-width:0 1px} +table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0} +table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0} +table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0} +table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0} +table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7} +th.halign-left,td.halign-left{text-align:left} +th.halign-right,td.halign-right{text-align:right} +th.halign-center,td.halign-center{text-align:center} +th.valign-top,td.valign-top{vertical-align:top} +th.valign-bottom,td.valign-bottom{vertical-align:bottom} +th.valign-middle,td.valign-middle{vertical-align:middle} +table thead th,table tfoot th{font-weight:bold} +tbody tr th{background:#f7f8f7} +tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold} +p.tableblock>code:only-child{background:none;padding:0} +p.tableblock{font-size:1em} +ol{margin-left:1.75em} +ul li ol{margin-left:1.5em} +dl dd{margin-left:1.125em} +dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0} +li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em} +ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none} +ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em} +ul.unstyled,ol.unstyled{margin-left:0} +li>p:empty:only-child::before{content:"";display:inline-block} +ul.checklist>li>p:first-child{margin-left:-1em} +ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em} +ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em} +ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em} +ul.inline>li{margin-left:1.25em} +.unstyled dl dt{font-weight:400;font-style:normal} +ol.arabic{list-style-type:decimal} +ol.decimal{list-style-type:decimal-leading-zero} +ol.loweralpha{list-style-type:lower-alpha} +ol.upperalpha{list-style-type:upper-alpha} +ol.lowerroman{list-style-type:lower-roman} +ol.upperroman{list-style-type:upper-roman} +ol.lowergreek{list-style-type:lower-greek} +.hdlist>table,.colist>table{border:0;background:none} +.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none} +td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em} +td.hdlist1{font-weight:bold;padding-bottom:1.25em} +td.hdlist2{word-wrap:anywhere} +.literalblock+.colist,.listingblock+.colist{margin-top:-.5em} +.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top} +.colist td:not([class]):first-child img{max-width:none} +.colist td:not([class]):last-child{padding:.25em 0} +.thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd} +.imageblock.left{margin:.25em .625em 1.25em 0} +.imageblock.right{margin:.25em 0 1.25em .625em} +.imageblock>.title{margin-bottom:0} +.imageblock.thumb,.imageblock.th{border-width:6px} +.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em} +.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0} +.image.left{margin-right:.625em} +.image.right{margin-left:.625em} +a.image{text-decoration:none;display:inline-block} +a.image object{pointer-events:none} +sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super} +sup.footnote a,sup.footnoteref a{text-decoration:none} +sup.footnote a:active,sup.footnoteref a:active,#footnotes .footnote a:first-of-type:active{text-decoration:underline} +#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em} +#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0} +#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em} +#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em} +#footnotes .footnote:last-of-type{margin-bottom:0} +#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0} +div.unbreakable{page-break-inside:avoid} +.big{font-size:larger} +.small{font-size:smaller} +.underline{text-decoration:underline} +.overline{text-decoration:overline} +.line-through{text-decoration:line-through} +.aqua{color:#00bfbf} +.aqua-background{background:#00fafa} +.black{color:#000} +.black-background{background:#000} +.blue{color:#0000bf} +.blue-background{background:#0000fa} +.fuchsia{color:#bf00bf} +.fuchsia-background{background:#fa00fa} +.gray{color:#606060} +.gray-background{background:#7d7d7d} +.green{color:#006000} +.green-background{background:#007d00} +.lime{color:#00bf00} +.lime-background{background:#00fa00} +.maroon{color:#600000} +.maroon-background{background:#7d0000} +.navy{color:#000060} +.navy-background{background:#00007d} +.olive{color:#606000} +.olive-background{background:#7d7d00} +.purple{color:#600060} +.purple-background{background:#7d007d} +.red{color:#bf0000} +.red-background{background:#fa0000} +.silver{color:#909090} +.silver-background{background:#bcbcbc} +.teal{color:#006060} +.teal-background{background:#007d7d} +.white{color:#bfbfbf} +.white-background{background:#fafafa} +.yellow{color:#bfbf00} +.yellow-background{background:#fafa00} +span.icon>.fa{cursor:default} +a span.icon>.fa{cursor:inherit} +.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default} +.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c} +.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111} +.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900} +.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400} +.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000} +.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold} +.conum[data-value] *{color:#fff!important} +.conum[data-value]+b{display:none} +.conum[data-value]::after{content:attr(data-value)} +pre .conum[data-value]{position:relative;top:-.125em} +b.conum *{color:inherit!important} +.conum:not([data-value]):empty{display:none} +dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility} +h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em} +p strong,td.content strong,div.footnote strong{letter-spacing:-.005em} +p,blockquote,dt,td.content,td.hdlist1,span.alt,summary{font-size:1.0625rem} +p{margin-bottom:1.25rem} +.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em} +.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc} +.print-only{display:none!important} +@page{margin:1.25cm .75cm} +@media print{*{box-shadow:none!important;text-shadow:none!important} +html{font-size:80%} +a{color:inherit!important;text-decoration:underline!important} +a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important} +a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em} +abbr[title]{border-bottom:1px dotted} +abbr[title]::after{content:" (" attr(title) ")"} +pre,blockquote,tr,img,object,svg{page-break-inside:avoid} +thead{display:table-header-group} +svg{max-width:100%} +p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3} +h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid} +#header,#content,#footnotes,#footer{max-width:none} +#toc,.sidebarblock,.exampleblock>.content{background:none!important} +#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important} +body.book #header{text-align:center} +body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em} +body.book #header .details{border:0!important;display:block;padding:0!important} +body.book #header .details span:first-child{margin-left:0!important} +body.book #header .details br{display:block} +body.book #header .details br+span::before{content:none!important} +body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important} +body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always} +.listingblock code[data-lang]::before{display:block} +#footer{padding:0 .9375em} +.hide-on-print{display:none!important} +.print-only{display:block!important} +.hide-for-print{display:none!important} +.show-for-print{display:inherit!important}} +@media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem} +.sect1{padding:0!important} +.sect1+.sect1{border:0} +#footer{background:none} +#footer-text{color:rgba(0,0,0,.6);font-size:.9em}} +@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}} +</style> +<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> +<style> +pre.rouge table td { padding: 5px; } +pre.rouge table pre { margin: 0; } +pre.rouge, pre.rouge .w { + color: #24292f; + background-color: #f6f8fa; +} +pre.rouge .k, pre.rouge .kd, pre.rouge .kn, pre.rouge .kp, pre.rouge .kr, pre.rouge .kt, pre.rouge .kv { + color: #cf222e; +} +pre.rouge .gr { + color: #f6f8fa; +} +pre.rouge .gd { + color: #82071e; + background-color: #ffebe9; +} +pre.rouge .nb { + color: #953800; +} +pre.rouge .nc { + color: #953800; +} +pre.rouge .no { + color: #953800; +} +pre.rouge .nn { + color: #953800; +} +pre.rouge .sr { + color: #116329; +} +pre.rouge .na { + color: #116329; +} +pre.rouge .nt { + color: #116329; +} +pre.rouge .gi { + color: #116329; + background-color: #dafbe1; +} +pre.rouge .ges { + font-weight: bold; + font-style: italic; +} +pre.rouge .kc { + color: #0550ae; +} +pre.rouge .l, pre.rouge .ld, pre.rouge .m, pre.rouge .mb, pre.rouge .mf, pre.rouge .mh, pre.rouge .mi, pre.rouge .il, pre.rouge .mo, pre.rouge .mx { + color: #0550ae; +} +pre.rouge .sb { + color: #0550ae; +} +pre.rouge .bp { + color: #0550ae; +} +pre.rouge .ne { + color: #0550ae; +} +pre.rouge .nl { + color: #0550ae; +} +pre.rouge .py { + color: #0550ae; +} +pre.rouge .nv, pre.rouge .vc, pre.rouge .vg, pre.rouge .vi, pre.rouge .vm { + color: #0550ae; +} +pre.rouge .o, pre.rouge .ow { + color: #0550ae; +} +pre.rouge .gh { + color: #0550ae; + font-weight: bold; +} +pre.rouge .gu { + color: #0550ae; + font-weight: bold; +} +pre.rouge .s, pre.rouge .sa, pre.rouge .sc, pre.rouge .dl, pre.rouge .sd, pre.rouge .s2, pre.rouge .se, pre.rouge .sh, pre.rouge .sx, pre.rouge .s1, pre.rouge .ss { + color: #0a3069; +} +pre.rouge .nd { + color: #8250df; +} +pre.rouge .nf, pre.rouge .fm { + color: #8250df; +} +pre.rouge .err { + color: #f6f8fa; + background-color: #82071e; +} +pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cm, pre.rouge .cp, pre.rouge .cpf, pre.rouge .c1, pre.rouge .cs { + color: #6e7781; +} +pre.rouge .gl { + color: #6e7781; +} +pre.rouge .gt { + color: #6e7781; +} +pre.rouge .ni { + color: #24292f; +} +pre.rouge .si { + color: #24292f; +} +pre.rouge .ge { + color: #24292f; + font-style: italic; +} +pre.rouge .gs { + color: #24292f; + font-weight: bold; +} +</style> +</head> +<body class="article toc2 toc-left"> +<div id="header"> +<h1>ALOM - TP 6 - Interoperability</h1> +<div id="toc" class="toc2"> +<div id="toctitle">Table of Contents</div> +<ul class="sectlevel1"> +<li><a href="#_prĂ©sentation_et_objectifs">1. PrĂ©sentation et objectifs</a></li> +<li><a href="#_pokemon_type_api">2. pokemon-type-api</a> +<ul class="sectlevel2"> +<li><a href="#_les_donnĂ©es_de_traduction">2.1. Les donnĂ©es de traduction</a></li> +<li><a href="#_le_bo">2.2. Le BO</a></li> +<li><a href="#_le_repository">2.3. Le repository</a> +<ul class="sectlevel3"> +<li><a href="#_linterface">2.3.1. L’interface</a></li> +<li><a href="#_les_tests_unitaires">2.3.2. Les tests unitaires</a></li> +<li><a href="#_limplĂ©mentation">2.3.3. L’implĂ©mentation</a></li> +</ul> +</li> +<li><a href="#_le_service">2.4. Le service</a></li> +<li><a href="#_le_test_dintĂ©gration">2.5. Le test d’intĂ©gration</a></li> +<li><a href="#_les_tests_avec_postman">2.6. Les tests avec Postman</a> +<ul class="sectlevel3"> +<li><a href="#_get_httplocalhost8080pokemon_types1">2.6.1. GET http://localhost:8080/pokemon-types/1</a></li> +<li><a href="#_get_httplocalhost8080pokemon_types1_accept_language_fr">2.6.2. GET http://localhost:8080/pokemon-types/1 - Accept-Language: fr</a></li> +<li><a href="#_get_httplocalhost8080pokemon_types">2.6.3. GET http://localhost:8080/pokemon-types</a></li> +<li><a href="#_get_httplocalhost8080pokemon_types_accept_language_fr">2.6.4. GET http://localhost:8080/pokemon-types - Accept-Language: fr</a></li> +<li><a href="#_export_de_la_collection">2.6.5. Export de la collection</a></li> +</ul> +</li> +<li><a href="#_openapi_et_swagger">2.7. OpenApi et Swagger</a></li> +</ul> +</li> +<li><a href="#_game_ui">3. game-ui</a> +<ul class="sectlevel2"> +<li><a href="#_utilisation_des_http_interfaces">3.1. Utilisation des HTTP Interfaces</a></li> +</ul> +</li> +<li><a href="#_trainer_api">4. trainer-api</a></li> +</ul> +</div> +</div> +<div id="content"> +<div class="sect1"> +<h2 id="_prĂ©sentation_et_objectifs"><a class="anchor" href="#_prĂ©sentation_et_objectifs"></a><a class="link" href="#_prĂ©sentation_et_objectifs">1. PrĂ©sentation et objectifs</a></h2> +<div class="sectionbody"> +<div class="paragraph"> +<p>Le but est de continuer le dĂ©veloppement de notre architecture "Ă la microservice".</p> +</div> +<div class="paragraph"> +<p>Nous allons aujourd’hui implĂ©menter des fonctionnalitĂ©s de traduction dans notre micro-service pokemon-type!</p> +</div> +<div class="paragraph"> +<p>En effet, nos donnĂ©es de Pokemon sont aujourd’hui en anglais uniquement, ce qui peut ĂȘtre dĂ©courageant pour nos futurs joueurs français !</p> +</div> +<div class="imageblock"> +<div class="content"> +<img src="images/trainer-gui.png" alt="trainer gui"> +</div> +</div> +<div class="paragraph"> +<p>Nous allons dĂ©velopper :</p> +</div> +<div class="olist arabic"> +<ol class="arabic"> +<li> +<p>La gestion des traductions dans notre api pokemon-type</p> +</li> +<li> +<p>L’affichage du Pokedex traduit !</p> +</li> +</ol> +</div> +<div class="paragraph"> +<p>Nous allons Ă©galement réécrire nos appels utilisant le <code>RestTemplate</code> pour utiliser les <em>HTTP Interfaces</em>.</p> +</div> +<div class="admonitionblock tip"> +<table> +<tr> +<td class="icon"> +<i class="fa icon-tip" title="Tip"></i> +</td> +<td class="content"> +Nous ne repartons pas uniquement de zĂ©ro pour ce TP. Nous nous appuyons sur les TP prĂ©cĂ©dents +</td> +</tr> +</table> +</div> +</div> +</div> +<div class="sect1"> +<h2 id="_pokemon_type_api"><a class="anchor" href="#_pokemon_type_api"></a><a class="link" href="#_pokemon_type_api">2. pokemon-type-api</a></h2> +<div class="sectionbody"> +<div class="sect2"> +<h3 id="_les_donnĂ©es_de_traduction"><a class="anchor" href="#_les_donnĂ©es_de_traduction"></a><a class="link" href="#_les_donnĂ©es_de_traduction">2.1. Les donnĂ©es de traduction</a></h3> +<div class="paragraph"> +<p>Pour faciliter le travail, j’ai créé deux fichier JSON contenant les traductions des noms de Pokemon en français et anglais.</p> +</div> +<div class="paragraph"> +<p>Ces fichiers sont disponible ici: <a href="translations-fr.json" target="_blank" rel="noopener">translations-fr.json</a> <a href="translations-en.json" target="_blank" rel="noopener">translations-en.json</a></p> +</div> +<div class="paragraph"> +<p>DĂ©posez ces fichiers dans votre rĂ©pertoire <code>src/main/resources</code>.</p> +</div> +</div> +<div class="sect2"> +<h3 id="_le_bo"><a class="anchor" href="#_le_bo"></a><a class="link" href="#_le_bo">2.2. Le BO</a></h3> +<div class="paragraph"> +<p>CrĂ©ez un record <code>Translation</code></p> +</div> +<div class="listingblock"> +<div class="title">com.miage.alom.tp.pokemon_type_api.Translation</div> +<div class="content"> +<pre class="rouge highlight nowrap"><code data-lang="java"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno">1 +2 +3 +</pre></td><td class="code"><pre><span class="kn">package</span> <span class="nn">com.miage.alom.tp.pokemon_type_api.bo</span><span class="o">;</span> + +<span class="kd">public</span> <span class="n">record</span> <span class="nf">Translation</span><span class="o">(</span><span class="kt">int</span> <span class="n">id</span><span class="o">,</span> <span class="nc">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{}</span> +</pre></td></tr></tbody></table></code></pre> +</div> +</div> +</div> +<div class="sect2"> +<h3 id="_le_repository"><a class="anchor" href="#_le_repository"></a><a class="link" href="#_le_repository">2.3. Le repository</a></h3> +<div class="sect3"> +<h4 id="_linterface"><a class="anchor" href="#_linterface"></a><a class="link" href="#_linterface">2.3.1. L’interface</a></h4> +<div class="paragraph"> +<p>L’interface de ce repository de traduction est simple :</p> +</div> +<div class="listingblock"> +<div class="title">com.miage.alom.tp.pokemon_type_api.TranslationRepository</div> +<div class="content"> +<pre class="rouge highlight nowrap"><code data-lang="java"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno">1 +2 +3 +4 +5 +6 +7 +</pre></td><td class="code"><pre><span class="kn">package</span> <span class="nn">com.miage.alom.tp.pokemon_type_api.repository</span><span class="o">;</span> + +<span class="kn">import</span> <span class="nn">java.util.Locale</span><span class="o">;</span> + +<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">TranslationRepository</span> <span class="o">{</span> + <span class="nc">String</span> <span class="nf">getPokemonName</span><span class="o">(</span><span class="kt">int</span> <span class="n">id</span><span class="o">,</span> <span class="nc">Locale</span> <span class="n">locale</span><span class="o">);</span> +<span class="o">}</span> +</pre></td></tr></tbody></table></code></pre> +</div> +</div> +</div> +<div class="sect3"> +<h4 id="_les_tests_unitaires"><a class="anchor" href="#_les_tests_unitaires"></a><a class="link" href="#_les_tests_unitaires">2.3.2. Les tests unitaires</a></h4> +<div class="paragraph"> +<p>ImplĂ©mentez les tests unitaires suivants :</p> +</div> +<div class="listingblock"> +<div class="title">com.miage.alom.tp.pokemon_type_api.repository.TranslationRepositoryImplTest.java</div> +<div class="content"> +<pre class="rouge highlight nowrap"><code data-lang="java"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +</pre></td><td class="code"><pre><span class="kn">package</span> <span class="nn">com.miage.alom.tp.pokemon_type_api.repository</span><span class="o">;</span> + +<span class="kn">import</span> <span class="nn">org.junit.jupiter.api.Test</span><span class="o">;</span> +<span class="kn">import</span> <span class="nn">org.springframework.context.annotation.AnnotationConfigApplicationContext</span><span class="o">;</span> + +<span class="kn">import</span> <span class="nn">java.util.Locale</span><span class="o">;</span> + +<span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">junit</span><span class="o">.</span><span class="na">jupiter</span><span class="o">.</span><span class="na">api</span><span class="o">.</span><span class="na">Assertions</span><span class="o">.*;</span> + +<span class="kd">class</span> <span class="nc">TranslationRepositoryImplTest</span> <span class="o">{</span> + + <span class="kd">private</span> <span class="nc">TranslationRepositoryImpl</span> <span class="n">repository</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TranslationRepositoryImpl</span><span class="o">();</span> + + <span class="nd">@Test</span> + <span class="kt">void</span> <span class="nf">getPokemonName_with1_inFrench_shouldReturnBulbizarre</span><span class="o">(){</span> + <span class="n">assertEquals</span><span class="o">(</span><span class="s">"Bulbizarre"</span><span class="o">,</span> <span class="n">repository</span><span class="o">.</span><span class="na">getPokemonName</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="nc">Locale</span><span class="o">.</span><span class="na">FRENCH</span><span class="o">));</span> + <span class="n">assertEquals</span><span class="o">(</span><span class="s">"Bulbizarre"</span><span class="o">,</span> <span class="n">repository</span><span class="o">.</span><span class="na">getPokemonName</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="nc">Locale</span><span class="o">.</span><span class="na">FRANCE</span><span class="o">));</span> + <span class="o">}</span> + + <span class="nd">@Test</span> + <span class="kt">void</span> <span class="nf">getPokemonName_with1_inEnglish_shouldReturnBulbizarre</span><span class="o">(){</span> + <span class="n">assertEquals</span><span class="o">(</span><span class="s">"Bulbasaur"</span><span class="o">,</span> <span class="n">repository</span><span class="o">.</span><span class="na">getPokemonName</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="nc">Locale</span><span class="o">.</span><span class="na">ENGLISH</span><span class="o">));</span> + <span class="n">assertEquals</span><span class="o">(</span><span class="s">"Bulbasaur"</span><span class="o">,</span> <span class="n">repository</span><span class="o">.</span><span class="na">getPokemonName</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="nc">Locale</span><span class="o">.</span><span class="na">UK</span><span class="o">));</span> + <span class="n">assertEquals</span><span class="o">(</span><span class="s">"Bulbasaur"</span><span class="o">,</span> <span class="n">repository</span><span class="o">.</span><span class="na">getPokemonName</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="nc">Locale</span><span class="o">.</span><span class="na">US</span><span class="o">));</span> + <span class="o">}</span> + + <span class="nd">@Test</span> + <span class="kt">void</span> <span class="nf">applicationContext_shouldLoadPokemonRepository</span><span class="o">(){</span> + <span class="kt">var</span> <span class="n">context</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AnnotationConfigApplicationContext</span><span class="o">(</span><span class="s">"com.miage.alom.tp.pokemon_type_api"</span><span class="o">);</span> + <span class="kt">var</span> <span class="n">repoByName</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="s">"translationRepositoryImpl"</span><span class="o">);</span> + <span class="kt">var</span> <span class="n">repoByClass</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="nc">TranslationRepository</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> + + <span class="n">assertEquals</span><span class="o">(</span><span class="n">repoByName</span><span class="o">,</span> <span class="n">repoByClass</span><span class="o">);</span> + <span class="n">assertNotNull</span><span class="o">(</span><span class="n">repoByName</span><span class="o">);</span> + <span class="n">assertNotNull</span><span class="o">(</span><span class="n">repoByClass</span><span class="o">);</span> + <span class="o">}</span> + +<span class="o">}</span> +</pre></td></tr></tbody></table></code></pre> +</div> +</div> +</div> +<div class="sect3"> +<h4 id="_limplĂ©mentation"><a class="anchor" href="#_limplĂ©mentation"></a><a class="link" href="#_limplĂ©mentation">2.3.3. L’implĂ©mentation</a></h4> +<div class="paragraph"> +<p>DĂ©veloppez l’implĂ©mentation du <code>TranslationRepository</code>.</p> +</div> +<div class="listingblock"> +<div class="title">com.miage.alom.tp.pokemon_type_api.TranslationRepositoryImpl.java</div> +<div class="content"> +<pre class="rouge highlight nowrap"><code data-lang="java"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +</pre></td><td class="code"><pre><span class="kn">package</span> <span class="nn">com.miage.alom.tp.pokemon_type_api</span><span class="o">;</span> + +<span class="kn">import</span> <span class="nn">com.fasterxml.jackson.databind.ObjectMapper</span><span class="o">;</span> +<span class="kn">import</span> <span class="nn">com.miage.alom.tp.pokemon_type_api.bo.Translation</span><span class="o">;</span> +<span class="kn">import</span> <span class="nn">org.springframework.core.io.ClassPathResource</span><span class="o">;</span> +<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Repository</span><span class="o">;</span> + +<span class="kn">import</span> <span class="nn">java.io.IOException</span><span class="o">;</span> +<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span> +<span class="kn">import</span> <span class="nn">java.util.Locale</span><span class="o">;</span> +<span class="kn">import</span> <span class="nn">java.util.Map</span><span class="o">;</span> + +<span class="nd">@Repository</span> +<span class="kd">public</span> <span class="kd">class</span> <span class="nc">TranslationRepositoryImpl</span> <span class="kd">implements</span> <span class="nc">TranslationRepository</span> <span class="o">{</span> + + <span class="n">record</span> <span class="nf">Key</span><span class="o">(</span><span class="nc">Locale</span> <span class="n">locale</span><span class="o">,</span> <span class="kt">int</span> <span class="n">pokemonId</span><span class="o">){}</span> <i class="conum" data-value="3"></i><b>(3)</b> + + <span class="kd">private</span> <span class="nc">Map</span><span class="o"><</span><span class="nc">Key</span><span class="o">,</span> <span class="nc">Translation</span><span class="o">></span> <span class="n">translations</span><span class="o">;</span> + + <span class="kd">private</span> <span class="nc">ObjectMapper</span> <span class="n">objectMapper</span><span class="o">;</span> + + <span class="kd">public</span> <span class="nf">TranslationRepositoryImpl</span><span class="o">()</span> <span class="o">{</span> + <span class="k">try</span> <span class="o">{</span> + <span class="c1">// TODO </span><i class="conum" data-value="2"></i><b>(2)</b> + <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> + <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span> + <span class="o">}</span> + <span class="o">}</span> + + <span class="nd">@Override</span> + <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getPokemonName</span><span class="o">(</span><span class="kt">int</span> <span class="n">id</span><span class="o">,</span> <span class="nc">Locale</span> <span class="n">locale</span><span class="o">)</span> <span class="o">{</span> + <span class="c1">// TODO </span><i class="conum" data-value="1"></i><b>(1)</b> + <span class="o">}</span> +<span class="o">}</span> +</pre></td></tr></tbody></table></code></pre> +</div> +</div> +<div class="colist arabic"> +<table> +<tr> +<td><i class="conum" data-value="1"></i><b>1</b></td> +<td>ImplĂ©mentez la rĂ©cupĂ©ration du nom d’un Pokemon !</td> +</tr> +<tr> +<td><i class="conum" data-value="2"></i><b>2</b></td> +<td>Alimentez la map des traductions en chargeant les fichiers, et en rĂ©cupĂ©rant leur contenu</td> +</tr> +<tr> +<td><i class="conum" data-value="3"></i><b>3</b></td> +<td>On utilise un record local Ă notre classe comme clĂ© de Map !</td> +</tr> +</table> +</div> +<div class="admonitionblock note"> +<table> +<tr> +<td class="icon"> +<i class="fa icon-note" title="Note"></i> +</td> +<td class="content"> +<div class="paragraph"> +<p>La rĂ©cupĂ©ration d’un fichier dans le classpath peut se fair en Spring avec la classe <code>ClassPathResource</code>. +Inspirez-vous du <code>PokemonTypeRepository</code> pour le reste.</p> +</div> +</td> +</tr> +</table> +</div> +</div> +</div> +<div class="sect2"> +<h3 id="_le_service"><a class="anchor" href="#_le_service"></a><a class="link" href="#_le_service">2.4. Le service</a></h3> +<div class="paragraph"> +<p>Maintenant que nous avons un repository capable de gĂ©rer les traductions, nous devons les utiliser. +Un bon endroit pour cela est la couche service.</p> +</div> +<div class="paragraph"> +<p>Spring utilise la classe <code>AcceptHeaderLocaleResolver</code> dans la <code>DispatcherServlet</code> pour venir alimenter un objet <code>LocaleContextHolder</code>. +Nous pouvons donc utiliser cet objet pour rĂ©cupĂ©rer la langue demandĂ©e par la requĂȘte courante !</p> +</div> +<div class="paragraph"> +<p>Ajoutez les tests unitaires suivant au <code>PokemonTypeServiceImplTest</code>:</p> +</div> +<div class="listingblock"> +<div class="title">PokemonTypeServiceImplTest.java</div> +<div class="content"> +<pre class="rouge highlight nowrap"><code data-lang="java"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +</pre></td><td class="code"><pre><span class="nd">@Test</span> +<span class="kt">void</span> <span class="nf">pokemonNames_shouldBeTranslated_usingLocaleResolver</span><span class="o">(){</span> + <span class="kt">var</span> <span class="n">pokemonTypeService</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PokemonTypeServiceImpl</span><span class="o">();</span> + + <span class="kt">var</span> <span class="n">pokemonTypeRepository</span> <span class="o">=</span> <span class="n">mock</span><span class="o">(</span><span class="nc">PokemonTypeRepository</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> + <span class="n">pokemonTypeService</span><span class="o">.</span><span class="na">setPokemonTypeRepository</span><span class="o">(</span><span class="n">pokemonTypeRepository</span><span class="o">);</span> + <span class="n">when</span><span class="o">(</span><span class="n">pokemonTypeRepository</span><span class="o">.</span><span class="na">findPokemonTypeById</span><span class="o">(</span><span class="mi">25</span><span class="o">)).</span><span class="na">thenReturn</span><span class="o">(</span><span class="k">new</span> <span class="nc">PokemonType</span><span class="o">());</span> + + <span class="kt">var</span> <span class="n">translationRepository</span> <span class="o">=</span> <span class="n">mock</span><span class="o">(</span><span class="nc">TranslationRepository</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> + <span class="n">pokemonTypeService</span><span class="o">.</span><span class="na">setTranslationRepository</span><span class="o">(</span><span class="n">translationRepository</span><span class="o">);</span> + <span class="n">when</span><span class="o">(</span><span class="n">translationRepository</span><span class="o">.</span><span class="na">getPokemonName</span><span class="o">(</span><span class="mi">25</span><span class="o">,</span> <span class="nc">Locale</span><span class="o">.</span><span class="na">FRENCH</span><span class="o">)).</span><span class="na">thenReturn</span><span class="o">(</span><span class="s">"Pikachu-FRENCH"</span><span class="o">);</span> + + <span class="nc">LocaleContextHolder</span><span class="o">.</span><span class="na">setLocale</span><span class="o">(</span><span class="nc">Locale</span><span class="o">.</span><span class="na">FRENCH</span><span class="o">);</span> + + <span class="kt">var</span> <span class="n">pikachu</span> <span class="o">=</span> <span class="n">pokemonTypeService</span><span class="o">.</span><span class="na">getPokemonType</span><span class="o">(</span><span class="mi">25</span><span class="o">);</span> + + <span class="n">assertEquals</span><span class="o">(</span><span class="s">"Pikachu-FRENCH"</span><span class="o">,</span> <span class="n">pikachu</span><span class="o">.</span><span class="na">name</span><span class="o">());</span> + <span class="n">verify</span><span class="o">(</span><span class="n">translationRepository</span><span class="o">).</span><span class="na">getPokemonName</span><span class="o">(</span><span class="mi">25</span><span class="o">,</span> <span class="nc">Locale</span><span class="o">.</span><span class="na">FRENCH</span><span class="o">);</span> +<span class="o">}</span> + +<span class="nd">@Test</span> +<span class="kt">void</span> <span class="nf">allPokemonNames_shouldBeTranslated_usingLocaleResolver</span><span class="o">(){</span> + <span class="kt">var</span> <span class="n">pokemonTypeService</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PokemonTypeServiceImpl</span><span class="o">();</span> + + <span class="kt">var</span> <span class="n">pokemonTypeRepository</span> <span class="o">=</span> <span class="n">mock</span><span class="o">(</span><span class="nc">PokemonTypeRepository</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> + <span class="n">pokemonTypeService</span><span class="o">.</span><span class="na">setPokemonTypeRepository</span><span class="o">(</span><span class="n">pokemonTypeRepository</span><span class="o">);</span> + + <span class="kt">var</span> <span class="n">pikachu</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PokemonType</span><span class="o">(</span><span class="mi">25</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span> + <span class="kt">var</span> <span class="n">raichu</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PokemonType</span><span class="o">(</span><span class="mi">26</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span> + <span class="n">when</span><span class="o">(</span><span class="n">pokemonTypeRepository</span><span class="o">.</span><span class="na">findAllPokemonType</span><span class="o">()).</span><span class="na">thenReturn</span><span class="o">(</span><span class="nc">List</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">pikachu</span><span class="o">,</span> <span class="n">raichu</span><span class="o">));</span> + + <span class="c1">// on simule le repository de traduction</span> + <span class="kt">var</span> <span class="n">translationRepository</span> <span class="o">=</span> <span class="n">mock</span><span class="o">(</span><span class="nc">TranslationRepository</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> + <span class="n">pokemonTypeService</span><span class="o">.</span><span class="na">setTranslationRepository</span><span class="o">(</span><span class="n">translationRepository</span><span class="o">);</span> + <span class="n">when</span><span class="o">(</span><span class="n">translationRepository</span><span class="o">.</span><span class="na">getPokemonName</span><span class="o">(</span><span class="mi">25</span><span class="o">,</span> <span class="nc">Locale</span><span class="o">.</span><span class="na">FRENCH</span><span class="o">)).</span><span class="na">thenReturn</span><span class="o">(</span><span class="s">"Pikachu-FRENCH"</span><span class="o">);</span> + <span class="n">when</span><span class="o">(</span><span class="n">translationRepository</span><span class="o">.</span><span class="na">getPokemonName</span><span class="o">(</span><span class="mi">26</span><span class="o">,</span> <span class="nc">Locale</span><span class="o">.</span><span class="na">FRENCH</span><span class="o">)).</span><span class="na">thenReturn</span><span class="o">(</span><span class="s">"Raichu-FRENCH"</span><span class="o">);</span> + + <span class="nc">LocaleContextHolder</span><span class="o">.</span><span class="na">setLocale</span><span class="o">(</span><span class="nc">Locale</span><span class="o">.</span><span class="na">FRENCH</span><span class="o">);</span> + + <span class="kt">var</span> <span class="n">pokemonTypes</span> <span class="o">=</span> <span class="n">pokemonTypeService</span><span class="o">.</span><span class="na">getAllPokemonTypes</span><span class="o">();</span> + + <span class="n">assertEquals</span><span class="o">(</span><span class="s">"Pikachu-FRENCH"</span><span class="o">,</span> <span class="n">pokemonTypes</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">).</span><span class="na">name</span><span class="o">());</span> + <span class="n">assertEquals</span><span class="o">(</span><span class="s">"Raichu-FRENCH"</span><span class="o">,</span> <span class="n">pokemonTypes</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">).</span><span class="na">name</span><span class="o">());</span> + <span class="n">verify</span><span class="o">(</span><span class="n">translationRepository</span><span class="o">).</span><span class="na">getPokemonName</span><span class="o">(</span><span class="mi">25</span><span class="o">,</span> <span class="nc">Locale</span><span class="o">.</span><span class="na">FRENCH</span><span class="o">);</span> + <span class="n">verify</span><span class="o">(</span><span class="n">translationRepository</span><span class="o">).</span><span class="na">getPokemonName</span><span class="o">(</span><span class="mi">26</span><span class="o">,</span> <span class="nc">Locale</span><span class="o">.</span><span class="na">FRENCH</span><span class="o">);</span> +<span class="o">}</span> +</pre></td></tr></tbody></table></code></pre> +</div> +</div> +<div class="paragraph"> +<p>Pour faire passer les tests unitaires, remplacez le nom du type de pokemon, aprĂšs l’avoir rĂ©cupĂ©rĂ© du repository, par sa traduction.</p> +</div> +<div class="admonitionblock note"> +<table> +<tr> +<td class="icon"> +<i class="fa icon-note" title="Note"></i> +</td> +<td class="content"> +Les records sont immutables, donc vous allez devoir trouver un moyen pour copier les donnĂ©es. +</td> +</tr> +</table> +</div> +</div> +<div class="sect2"> +<h3 id="_le_test_dintĂ©gration"><a class="anchor" href="#_le_test_dintĂ©gration"></a><a class="link" href="#_le_test_dintĂ©gration">2.5. Le test d’intĂ©gration</a></h3> +<div class="paragraph"> +<p>Modifiez le <code>PokemonTypeControllerIntegrationTest</code> pour ajouter un test d’intĂ©gration :</p> +</div> +<div class="listingblock"> +<div class="title">PokemonTypeControllerIntegrationTest.java</div> +<div class="content"> +<pre class="rouge highlight nowrap"><code data-lang="java"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +</pre></td><td class="code"><pre><span class="nd">@Test</span> +<span class="kt">void</span> <span class="nf">getPokemon_withId1_shouldReturnBulbasaur</span><span class="o">()</span> <span class="o">{</span> + <span class="kt">var</span> <span class="n">bulbasaur</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">restTemplate</span><span class="o">.</span><span class="na">getForObject</span><span class="o">(</span><span class="s">"http://localhost:"</span> <span class="o">+</span> <span class="n">port</span> <span class="o">+</span> <span class="s">"/pokemon-types/1"</span><span class="o">,</span> <span class="nc">PokemonType</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> + <span class="n">assertNotNull</span><span class="o">(</span><span class="n">bulbasaur</span><span class="o">);</span> + <span class="n">assertEquals</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="n">bulbasaur</span><span class="o">.</span><span class="na">id</span><span class="o">());</span> + <span class="n">assertEquals</span><span class="o">(</span><span class="s">"Bulbasaur"</span><span class="o">,</span> <span class="n">bulbasaur</span><span class="o">.</span><span class="na">name</span><span class="o">());</span> <i class="conum" data-value="1"></i><b>(1)</b> +<span class="o">}</span> + +<span class="nd">@Test</span> +<span class="kt">void</span> <span class="nf">getPokemon_withId1AndFrenchAcceptLanguage_shouldReturnBulbizarre</span><span class="o">()</span> <span class="o">{</span> + <span class="kt">var</span> <span class="n">headers</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HttpHeaders</span><span class="o">();</span> + <span class="n">headers</span><span class="o">.</span><span class="na">setAcceptLanguageAsLocales</span><span class="o">(</span><span class="nc">List</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="nc">Locale</span><span class="o">.</span><span class="na">FRENCH</span><span class="o">));</span> <i class="conum" data-value="2"></i><b>(2)</b> + + <span class="kt">var</span> <span class="n">httpRequest</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HttpEntity</span><span class="o"><>(</span><span class="n">headers</span><span class="o">);</span> + + <span class="kt">var</span> <span class="n">bulbizarreResponseEntity</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">restTemplate</span><span class="o">.</span><span class="na">exchange</span><span class="o">(</span><span class="s">"http://localhost:"</span> <span class="o">+</span> <span class="n">port</span> <span class="o">+</span> <span class="s">"/pokemon-types/1"</span><span class="o">,</span> <span class="nc">HttpMethod</span><span class="o">.</span><span class="na">GET</span><span class="o">,</span> <span class="n">httpRequest</span><span class="o">,</span> <span class="nc">PokemonType</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> + <span class="kt">var</span> <span class="n">bulbizarre</span> <span class="o">=</span> <span class="n">bulbizarreResponseEntity</span><span class="o">.</span><span class="na">getBody</span><span class="o">();</span> + + <span class="n">assertNotNull</span><span class="o">(</span><span class="n">bulbizarre</span><span class="o">);</span> + <span class="n">assertEquals</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="n">bulbizarre</span><span class="o">.</span><span class="na">id</span><span class="o">());</span> + <span class="n">assertEquals</span><span class="o">(</span><span class="s">"Bulbizarre"</span><span class="o">,</span> <span class="n">bulbizarre</span><span class="o">.</span><span class="na">name</span><span class="o">());</span> <i class="conum" data-value="3"></i><b>(3)</b> +<span class="o">}</span> +</pre></td></tr></tbody></table></code></pre> +</div> +</div> +<div class="colist arabic"> +<table> +<tr> +<td><i class="conum" data-value="1"></i><b>1</b></td> +<td>Cette requĂȘte sans paramĂštre particulier doit renvoyer la traduction par dĂ©faut (en anglais)</td> +</tr> +<tr> +<td><i class="conum" data-value="2"></i><b>2</b></td> +<td>On construit une requĂȘte en y ajoutant un header "Accept-Language"</td> +</tr> +<tr> +<td><i class="conum" data-value="3"></i><b>3</b></td> +<td>On doit bien rĂ©cupĂ©rer le nom du type de Pokemon traduit !</td> +</tr> +</table> +</div> +</div> +<div class="sect2"> +<h3 id="_les_tests_avec_postman"><a class="anchor" href="#_les_tests_avec_postman"></a><a class="link" href="#_les_tests_avec_postman">2.6. Les tests avec Postman</a></h3> +<div class="paragraph"> +<p>Pour bien valider nos dĂ©veloppements, nous pouvons Ă©galement crĂ©er des tests avec Postman.</p> +</div> +<div class="admonitionblock note"> +<table> +<tr> +<td class="icon"> +<i class="fa icon-note" title="Note"></i> +</td> +<td class="content"> +Vous pouvez aussi utiliser Bruno ou Insomnia pour cette partie ! Dans ce cas, le code des tests sera un peu diffĂ©rent. +</td> +</tr> +</table> +</div> +<div class="paragraph"> +<p>Dans Postman, crĂ©ez une <code>Collection</code></p> +</div> +<div class="imageblock"> +<div class="content"> +<img src="images/postman-create-collection.png" alt="postman create collection"> +</div> +</div> +<div class="imageblock"> +<div class="content"> +<img src="images/postman-create-collection-2.png" alt="postman create collection 2"> +</div> +</div> +<div class="paragraph"> +<p>Ajoutez-y quelques requĂȘtes. Pour ce faire, crĂ©ez une nouvelle requĂȘte, et enregistrez la dans votre collection.</p> +</div> +<div class="imageblock"> +<div class="content"> +<img src="images/postman-create-request.png" alt="postman create request"> +</div> +</div> +<div class="paragraph"> +<p>Utilisez l’onglet <code>Tests</code> pour y ajouter quelques tests. Cet onglet permet d’exĂ©cuter du code javascript, +permettant par exemple de valider les codes de retour HTTP ou le JSON reçu.</p> +</div> +<div class="paragraph"> +<p>CrĂ©ez les requĂȘtes suivantes, avec les tests associĂ©s :</p> +</div> +<div class="sect3"> +<h4 id="_get_httplocalhost8080pokemon_types1"><a class="anchor" href="#_get_httplocalhost8080pokemon_types1"></a><a class="link" href="#_get_httplocalhost8080pokemon_types1">2.6.1. GET <a href="http://localhost:8080/pokemon-types/1" class="bare">http://localhost:8080/pokemon-types/1</a></a></h4> +<div class="listingblock"> +<div class="content"> +<pre class="rouge highlight nowrap"><code data-lang="javascript"><span class="nx">pm</span><span class="p">.</span><span class="nf">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">Bulbasaur</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">()</span> <span class="p">{</span> + <span class="kd">var</span> <span class="nx">bulbasaur</span> <span class="o">=</span> <span class="nx">pm</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span> + <span class="nx">pm</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="nx">bulbasaur</span><span class="p">.</span><span class="nx">id</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> + <span class="nx">pm</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="nx">bulbasaur</span><span class="p">.</span><span class="nx">name</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="dl">"</span><span class="s2">Bulbasaur</span><span class="dl">"</span><span class="p">);</span> +<span class="p">});</span></code></pre> +</div> +</div> +</div> +<div class="sect3"> +<h4 id="_get_httplocalhost8080pokemon_types1_accept_language_fr"><a class="anchor" href="#_get_httplocalhost8080pokemon_types1_accept_language_fr"></a><a class="link" href="#_get_httplocalhost8080pokemon_types1_accept_language_fr">2.6.2. GET <a href="http://localhost:8080/pokemon-types/1" class="bare">http://localhost:8080/pokemon-types/1</a> - Accept-Language: fr</a></h4> +<div class="listingblock"> +<div class="content"> +<pre class="rouge highlight nowrap"><code data-lang="javascript"><span class="nx">pm</span><span class="p">.</span><span class="nf">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">Bulbasaur</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">()</span> <span class="p">{</span> + <span class="kd">var</span> <span class="nx">bulbasaur</span> <span class="o">=</span> <span class="nx">pm</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span> + <span class="nx">pm</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="nx">bulbasaur</span><span class="p">.</span><span class="nx">id</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> + <span class="nx">pm</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="nx">bulbasaur</span><span class="p">.</span><span class="nx">name</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="dl">"</span><span class="s2">Bulbizarre</span><span class="dl">"</span><span class="p">);</span> +<span class="p">});</span></code></pre> +</div> +</div> +</div> +<div class="sect3"> +<h4 id="_get_httplocalhost8080pokemon_types"><a class="anchor" href="#_get_httplocalhost8080pokemon_types"></a><a class="link" href="#_get_httplocalhost8080pokemon_types">2.6.3. GET <a href="http://localhost:8080/pokemon-types" class="bare">http://localhost:8080/pokemon-types</a></a></h4> +<div class="listingblock"> +<div class="content"> +<pre class="rouge highlight nowrap"><code data-lang="javascript"><span class="nx">pm</span><span class="p">.</span><span class="nf">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">all pokemon types</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">()</span> <span class="p">{</span> + <span class="kd">var</span> <span class="nx">jsonData</span> <span class="o">=</span> <span class="nx">pm</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span> + <span class="nx">pm</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="nx">jsonData</span><span class="p">.</span><span class="nx">length</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="mi">151</span><span class="p">);</span> +<span class="p">});</span> + +<span class="nx">pm</span><span class="p">.</span><span class="nf">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">Bulbasaur</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">()</span> <span class="p">{</span> + <span class="kd">var</span> <span class="nx">jsonData</span> <span class="o">=</span> <span class="nx">pm</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span> + <span class="nx">pm</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="nx">jsonData</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="dl">"</span><span class="s2">Bulbasaur</span><span class="dl">"</span><span class="p">);</span> +<span class="p">});</span> + +<span class="nx">pm</span><span class="p">.</span><span class="nf">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">Ivysaur</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">()</span> <span class="p">{</span> + <span class="kd">var</span> <span class="nx">jsonData</span> <span class="o">=</span> <span class="nx">pm</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span> + <span class="nx">pm</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="nx">jsonData</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">name</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="dl">"</span><span class="s2">Ivysaur</span><span class="dl">"</span><span class="p">);</span> +<span class="p">});</span></code></pre> +</div> +</div> +</div> +<div class="sect3"> +<h4 id="_get_httplocalhost8080pokemon_types_accept_language_fr"><a class="anchor" href="#_get_httplocalhost8080pokemon_types_accept_language_fr"></a><a class="link" href="#_get_httplocalhost8080pokemon_types_accept_language_fr">2.6.4. GET <a href="http://localhost:8080/pokemon-types" class="bare">http://localhost:8080/pokemon-types</a> - Accept-Language: fr</a></h4> +<div class="listingblock"> +<div class="content"> +<pre class="rouge highlight nowrap"><code data-lang="javascript"><span class="nx">pm</span><span class="p">.</span><span class="nf">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">all pokemon types</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">()</span> <span class="p">{</span> + <span class="kd">var</span> <span class="nx">jsonData</span> <span class="o">=</span> <span class="nx">pm</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span> + <span class="nx">pm</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="nx">jsonData</span><span class="p">.</span><span class="nx">length</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="mi">151</span><span class="p">);</span> +<span class="p">});</span> + +<span class="nx">pm</span><span class="p">.</span><span class="nf">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">bulbizarre</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">()</span> <span class="p">{</span> + <span class="kd">var</span> <span class="nx">jsonData</span> <span class="o">=</span> <span class="nx">pm</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span> + <span class="nx">pm</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="nx">jsonData</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="dl">"</span><span class="s2">Bulbizarre</span><span class="dl">"</span><span class="p">);</span> +<span class="p">});</span> + +<span class="nx">pm</span><span class="p">.</span><span class="nf">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">Herbizarre</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">()</span> <span class="p">{</span> + <span class="kd">var</span> <span class="nx">jsonData</span> <span class="o">=</span> <span class="nx">pm</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span> + <span class="nx">pm</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="nx">jsonData</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">name</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="dl">"</span><span class="s2">Herbizarre</span><span class="dl">"</span><span class="p">);</span> +<span class="p">});</span></code></pre> +</div> +</div> +</div> +<div class="sect3"> +<h4 id="_export_de_la_collection"><a class="anchor" href="#_export_de_la_collection"></a><a class="link" href="#_export_de_la_collection">2.6.5. Export de la collection</a></h4> +<div class="paragraph"> +<p>Exportez votre collection Postman, dans le rĂ©pertoire <code>src/test/resources/rest/</code> de votre API. +Cela vous permettra de la rĂ©utiliser plus tard et de la partager avec les autres dĂ©veloppeurs !</p> +</div> +</div> +</div> +<div class="sect2"> +<h3 id="_openapi_et_swagger"><a class="anchor" href="#_openapi_et_swagger"></a><a class="link" href="#_openapi_et_swagger">2.7. OpenApi et Swagger</a></h3> +<div class="paragraph"> +<p>Nous allons Ă©galement exposer une interface de type <em>Swagger</em> afin de faciliter nos tests et nos dĂ©veloppements.</p> +</div> +<div class="paragraph"> +<p>Cette interface nous permettra Ă©galement de donner aux consommateurs de notre API un moyen facile de voir les ressources disponibles et les tester !</p> +</div> +<div class="paragraph"> +<p>Pour exposer un swagger, nous allons utiliser la librairie <a href="https://springdoc.org//" target="_blank" rel="noopener">springdoc</a>.</p> +</div> +<div class="olist arabic"> +<ol class="arabic"> +<li> +<p>Cette librairie analyse les <code>Controlleurs</code> Spring, pour gĂ©nĂ©rer de la documentation au format swagger.</p> +</li> +</ol> +</div> +<div class="admonitionblock tip"> +<table> +<tr> +<td class="icon"> +<i class="fa icon-tip" title="Tip"></i> +</td> +<td class="content"> +Cette librairie ne fait pas partie de Spring. Spring propose la gĂ©nĂ©ration de documentation Ă travers leur module <a href="https://spring.io/projects/spring-restdocs" target="_blank" rel="noopener">spring rest-docs</a> +</td> +</tr> +</table> +</div> +<div class="paragraph"> +<p>Ajoutez la dĂ©pendance suivante Ă votre <code>pom.xml</code> :</p> +</div> +<div class="listingblock"> +<div class="title">pom.xml</div> +<div class="content"> +<pre class="rouge highlight nowrap"><code data-lang="xml"><span class="nt"><dependency></span> + <span class="nt"><groupId></span>org.springdoc<span class="nt"></groupId></span> + <span class="nt"><artifactId></span>springdoc-openapi-starter-webmvc-ui<span class="nt"></artifactId></span> + <span class="nt"><version></span>2.2.0<span class="nt"></version></span> +<span class="nt"></dependency></span></code></pre> +</div> +</div> +<div class="paragraph"> +<p>Votre IHM swagger sera disponible Ă l’url <a href="http://localhost:8080/swagger-ui.html" class="bare" target="_blank" rel="noopener">http://localhost:8080/swagger-ui.html</a>, tandis que le JSON +sera disponible Ă l’url <a href="http://localhost:8080/v2/api-docs" class="bare" target="_blank" rel="noopener">http://localhost:8080/v2/api-docs</a>.</p> +</div> +<div class="paragraph"> +<p>Configurez <em>springdoc</em> pour n’afficher que vos propres controlleurs, et ignorer le <em>Basic Error Controller</em>.</p> +</div> +<div class="paragraph"> +<p>Trouvez comment faire dans la doc de <em>springdoc</em> : <a href="https://springdoc.org/#springdoc-openapi-core-properties" class="bare">https://springdoc.org/#springdoc-openapi-core-properties</a>.</p> +</div> +</div> +</div> +</div> +<div class="sect1"> +<h2 id="_game_ui"><a class="anchor" href="#_game_ui"></a><a class="link" href="#_game_ui">3. game-ui</a></h2> +<div class="sectionbody"> +<div class="sect2"> +<h3 id="_utilisation_des_http_interfaces"><a class="anchor" href="#_utilisation_des_http_interfaces"></a><a class="link" href="#_utilisation_des_http_interfaces">3.1. Utilisation des HTTP Interfaces</a></h3> +<div class="paragraph"> +<p>Modifiez votre micro-service <code>game-ui</code> pour y intĂ©grer la gestion de la locale!</p> +</div> +<div class="paragraph"> +<p>Remplacez l’utilisation du <code>RestTemplate</code> par des <em>HTTP Interfaces</em> Spring.</p> +</div> +<div class="paragraph"> +<p>Passez la Locale en paramĂštre lors de l’appel au micro-service <em>Pokemon Type</em>.</p> +</div> +<div class="paragraph"> +<p>Vous pouvez rĂ©cupĂ©rer la locale avec la mĂ©thode <code>LocaleContextHolder.getLocale()</code> de Spring directement +dans le PokemonTypeServiceImpl du <code>game-ui</code>, et la transmettre en utilisant le header <code>Accept-Language</code>. +. +De cette maniĂšre, la langue utilisĂ©e lors des Ă©changes sera celle du navigateur de l’utilisateur !</p> +</div> +</div> +</div> +</div> +<div class="sect1"> +<h2 id="_trainer_api"><a class="anchor" href="#_trainer_api"></a><a class="link" href="#_trainer_api">4. trainer-api</a></h2> +<div class="sectionbody"> +<div class="paragraph"> +<p>ImplĂ©mentez sur l’API trainer :</p> +</div> +<div class="olist arabic"> +<ol class="arabic"> +<li> +<p>l’exposition d’un swagger / open API</p> +</li> +<li> +<p>une collection Postman (ou Bruno) permettant de</p> +<div class="ulist"> +<ul> +<li> +<p>rĂ©cupĂ©rer la liste des dresseurs de Pokemon</p> +</li> +<li> +<p>rĂ©cupĂ©rer un dresseur de Pokemon</p> +</li> +<li> +<p>crĂ©er un dresseur de Pokemon</p> +</li> +</ul> +</div> +</li> +</ol> +</div> +</div> +</div> +</div> +<div id="footer"> +<div id="footer-text"> +Last updated 2024-10-23 06:33:39 +0200 +</div> +</div> +</body> +</html> \ No newline at end of file diff --git a/w06-interoperability/carbon/PokemonController-pathvariable.png b/w06-interoperability/carbon/PokemonController-pathvariable.png new file mode 100644 index 0000000000000000000000000000000000000000..26f4952ca531ec2bb78529d31b8412b898ab36d7 Binary files /dev/null and b/w06-interoperability/carbon/PokemonController-pathvariable.png differ diff --git a/w06-interoperability/carbon/PokemonController-requestmapping.png b/w06-interoperability/carbon/PokemonController-requestmapping.png new file mode 100644 index 0000000000000000000000000000000000000000..062c7543086ed8a596484d26f5c4e56257f6efd9 Binary files /dev/null and b/w06-interoperability/carbon/PokemonController-requestmapping.png differ diff --git a/w06-interoperability/carbon/PokemonController-requestparam-multiple.png b/w06-interoperability/carbon/PokemonController-requestparam-multiple.png new file mode 100644 index 0000000000000000000000000000000000000000..6530ed21180ed63fa6335adbf6fcdd5f3fa2126a Binary files /dev/null and b/w06-interoperability/carbon/PokemonController-requestparam-multiple.png differ diff --git a/w06-interoperability/carbon/PokemonController-requestparam.png b/w06-interoperability/carbon/PokemonController-requestparam.png new file mode 100644 index 0000000000000000000000000000000000000000..0bbc4473d5a06f62af827013095f2be3d1d6cfb6 Binary files /dev/null and b/w06-interoperability/carbon/PokemonController-requestparam.png differ diff --git a/w06-interoperability/carbon/PokemonController.java b/w06-interoperability/carbon/PokemonController.java new file mode 100644 index 0000000000000000000000000000000000000000..2a4a82c006412e44f53157717dc418e32583ee42 --- /dev/null +++ b/w06-interoperability/carbon/PokemonController.java @@ -0,0 +1,57 @@ +@RestController +public class PokemonController { + @Autowired + private PokemonRepository repository; + + @RequestMapping("/pokemon-types") + public Iterable<Pokemon> getAllPokmeons() { + return repository.findAll(); + } +} + + +@RestController +@RequestMapping("/api") +public class PokemonController { + + @GetMapping("/pokemon-types") + public Iterable<Pokemon> getAllPokemons() { + ... + } +} + +@RestController +@RequestMapping("/api") +public class PokemonController { + + @GetMapping("/pokemon-types/{id}") + public Pokemon getPokemon(@PathVariable String id) { + ... + } +} + + +@RestController +@RequestMapping("/api") +public class PokemonController { + + @GetMapping(path = "/pokemon-types", params = {"orderBy"}) + public Iterable<Pokemon> getAllPokemons(@RequestParam String orderBy) { + ... + } + + @GetMapping(path = "/pokemon-types", params = {"type"}) + public Iterable<Pokemon> getAllPokemons(@RequestParam String type) { + ... + } +} + +@RestController +@RequestMapping("/api") +public class TrainerController { + + @PostMapping("/trainers") + public Trainer createTrainer(@RequestBody Trainer trainer) { + ... + } +} \ No newline at end of file diff --git a/w06-interoperability/carbon/PokemonController.png b/w06-interoperability/carbon/PokemonController.png new file mode 100644 index 0000000000000000000000000000000000000000..657a16dedbb6f7643626ae665737fbcd105b077f Binary files /dev/null and b/w06-interoperability/carbon/PokemonController.png differ diff --git a/w06-interoperability/carbon/TrainerController-requestbody.png b/w06-interoperability/carbon/TrainerController-requestbody.png new file mode 100644 index 0000000000000000000000000000000000000000..7b4b70f84714108bddc9af4db000b26bf5c9b667 Binary files /dev/null and b/w06-interoperability/carbon/TrainerController-requestbody.png differ diff --git a/w06-interoperability/carbon/content-negociation-request.png b/w06-interoperability/carbon/content-negociation-request.png new file mode 100644 index 0000000000000000000000000000000000000000..e3d5034bb59fc053a1cb86787f0fc4a9f6018c25 Binary files /dev/null and b/w06-interoperability/carbon/content-negociation-request.png differ diff --git a/w06-interoperability/carbon/headers.png b/w06-interoperability/carbon/headers.png new file mode 100644 index 0000000000000000000000000000000000000000..a47ecd8b8cc9cfe74423c49dab524f75e4476182 Binary files /dev/null and b/w06-interoperability/carbon/headers.png differ diff --git a/w06-interoperability/carbon/headers.txt b/w06-interoperability/carbon/headers.txt new file mode 100644 index 0000000000000000000000000000000000000000..6d080cdad6e4ea1a9ba950b1b6a815e7225bf253 --- /dev/null +++ b/w06-interoperability/carbon/headers.txt @@ -0,0 +1,37 @@ +Accept: text/plain +Accept: application/xml +Accept: application/json +Accept: application/json,text/plain +Accept: image/png + +Accept-Language: en +Accept-Language: fr + +Content-Type: text/html +Content-Language: fr + + + +GET /pokemons-types/25 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 +Accept-Language: en-US,en;q=0.5 + +HTTP/1.1 200 +Content-Type: application/json;charset=UTF-8 +{ + "id": 25, + "baseExperience": 112, + "height": 4, + "name": "pikachu", + "sprites": { + "back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/25.png", + "front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/25.png" + }, + "stats": { + "speed": 90, + "defense": 40, + "attack": 55, + "hp": 35 + }, + "weight": 60 +} \ No newline at end of file diff --git a/w06-interoperability/carbon/jakarta-ws-api.png b/w06-interoperability/carbon/jakarta-ws-api.png new file mode 100644 index 0000000000000000000000000000000000000000..02a13a49f0087618c534ee1aa0aff1b61b159087 Binary files /dev/null and b/w06-interoperability/carbon/jakarta-ws-api.png differ diff --git a/w06-interoperability/carbon/jaxws-maven-plugin.png b/w06-interoperability/carbon/jaxws-maven-plugin.png new file mode 100644 index 0000000000000000000000000000000000000000..1f07c7e29fbb1c571fe905c3c49f2c3066b38267 Binary files /dev/null and b/w06-interoperability/carbon/jaxws-maven-plugin.png differ diff --git a/w06-interoperability/carbon/pokemon-api.png b/w06-interoperability/carbon/pokemon-api.png new file mode 100644 index 0000000000000000000000000000000000000000..f71c380585a4d4a1bb87d92b907d20ad45e6d019 Binary files /dev/null and b/w06-interoperability/carbon/pokemon-api.png differ diff --git a/w06-interoperability/carbon/pokemon-api.yml b/w06-interoperability/carbon/pokemon-api.yml new file mode 100644 index 0000000000000000000000000000000000000000..0b12159213ac95835ec86eb4d7571425af40ba78 --- /dev/null +++ b/w06-interoperability/carbon/pokemon-api.yml @@ -0,0 +1,95 @@ +swagger: "2.0" +info: + description: "This is a simple Pokemon API implementation." + version: "1.0.0" + title: "ALOM Pokemon API" +host: "alom-pokemon-api.herokuapp.com" +tags: + - name: "pokemons" + description: "Everything about your Pokemons" +schemes: + - "https" +paths: + /pokemons: + get: + tags: + - "pokemons" + summary: "Get all the pokemons" + description: "" + operationId: "getPokemons" + produces: + - "application/json" + responses: + 200: + description: "ok" + schema: + type: array + items: + $ref: "#/definitions/Pokemon" + /pokemons/{pokemonId}: + get: + tags: + - "pokemons" + summary: "Get a pokemon by ID" + description: "Returns a single Pokemon" + operationId: "getPokemonById" + produces: + - "application/json" + parameters: + - name: "pokemonId" + in: "path" + description: "ID of Pokemon to return" + required: true + type: "integer" + format: "int64" + responses: + 200: + description: "successful operation" + schema: + $ref: "#/definitions/Pokemon" + 400: + description: "Invalid ID supplied" + 404: + description: "Pokemon not found" +definitions: + Pokemon: + type: "object" + required: + - "id" + - "name" + properties: + id: + type: "integer" + format: "int64" + example: 25 + name: + type: "string" + example: "pikachu" + sprites: + type: "object" + properties: + front_default: + type: string + example: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/25.png" + back_default: + type: string + example: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/25.png" + stats: + type: "object" + properties: + speed: + type: "integer" + format: "int64" + example: "90" + defense: + type: "integer" + format: "int64" + example: 40 + attack: + type: "integer" + format: "int64" + example: 55 + hp: + type: "integer" + format: "int64" + example: 35 \ No newline at end of file diff --git a/w06-interoperability/carbon/rest-json.png b/w06-interoperability/carbon/rest-json.png new file mode 100644 index 0000000000000000000000000000000000000000..3d45533755017cbf94880d6b1a32add0d135d423 Binary files /dev/null and b/w06-interoperability/carbon/rest-json.png differ diff --git a/w06-interoperability/carbon/rest-json.txt b/w06-interoperability/carbon/rest-json.txt new file mode 100644 index 0000000000000000000000000000000000000000..4925cd243d175dafc781723509aa3a43d02e9288 --- /dev/null +++ b/w06-interoperability/carbon/rest-json.txt @@ -0,0 +1,20 @@ +GET /account/12345 HTTP/1.1 +Host: somebank.org +Accept: application/json +... + +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: ... + +{ + "account_number" : "12345" + "balance" : 100.00, + "currency" : "usd", + "links" : [ + { "rel": "deposit", "href" : "http://somebank.org/account/12345/deposit" }, + { "rel": "withdraw", "href" : "http://somebank.org/account/12345/withdraw" }, + { "rel": "transfert", "href" : "http://somebank.org/account/12345/transfert" }, + { "rel": "close", "href" : "http://somebank.org/account/12345/close" } + } +} \ No newline at end of file diff --git a/w06-interoperability/carbon/rest-xml.png b/w06-interoperability/carbon/rest-xml.png new file mode 100644 index 0000000000000000000000000000000000000000..94769b85b0d3343026efd8b948203d8620cdfff0 Binary files /dev/null and b/w06-interoperability/carbon/rest-xml.png differ diff --git a/w06-interoperability/carbon/rest-xml.txt b/w06-interoperability/carbon/rest-xml.txt new file mode 100644 index 0000000000000000000000000000000000000000..82dad3820277d7f2d8eaebd0da9e3cf93e5b8891 --- /dev/null +++ b/w06-interoperability/carbon/rest-xml.txt @@ -0,0 +1,18 @@ +GET /account/12345 HTTP/1.1 +Host: somebank.org +Accept: application/xml +... + +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: ... + +<?xml version="1.0"?> +<account> + <account_number>12345</account_number> + <balance currency="usd">100.00</balance> + <link rel="deposit" href="http://somebank.org/account/12345/deposit" /> + <link rel="withdraw" href="http://somebank.org/account/12345/withdraw" /> + <link rel="transfer" href="http://somebank.org/account/12345/transfer" /> + <link rel="close" href="http://somebank.org/account/12345/close" /> +</account> \ No newline at end of file diff --git a/w06-interoperability/carbon/restcontroller.png b/w06-interoperability/carbon/restcontroller.png new file mode 100644 index 0000000000000000000000000000000000000000..ed5ff3795a3ce33e8f6102149769ff9adeac6871 Binary files /dev/null and b/w06-interoperability/carbon/restcontroller.png differ diff --git a/w06-interoperability/carbon/resttemplate.java b/w06-interoperability/carbon/resttemplate.java new file mode 100644 index 0000000000000000000000000000000000000000..6b7447a4b19857ef1e644eb7fdac6affac28c7e8 --- /dev/null +++ b/w06-interoperability/carbon/resttemplate.java @@ -0,0 +1,28 @@ +package com.miage.alom.tp.pokemon_ui.pokemonTypes.service; + +import com.miage.alom.tp.pokemon_ui.pokemonTypes.bo.PokemonType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.Arrays; +import java.util.List; + +@Service +public class PokemonTypeServiceImpl implements PokemonTypeService{ + + private RestTemplate restTemplate; + + @Override + public List<PokemonType> listPokemonsTypes() { + var pokemonTypes = restTemplate + .getForObject(pokemonServiceUrl+"/pokemon-types", PokemonType[].class); + return Arrays.asList(pokemonTypes); + } + + @Autowired + void setRestTemplate(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } +} diff --git a/w06-interoperability/carbon/resttemplate.png b/w06-interoperability/carbon/resttemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..60dd3441e70bf974366bc071374776bfbbe14173 Binary files /dev/null and b/w06-interoperability/carbon/resttemplate.png differ diff --git a/w06-interoperability/images/Microservice-Architecture-Of-UBER.png b/w06-interoperability/images/Microservice-Architecture-Of-UBER.png new file mode 100644 index 0000000000000000000000000000000000000000..d2bbb7e3e59aafb758e8d7fe49efe6c2d8b473f1 Binary files /dev/null and b/w06-interoperability/images/Microservice-Architecture-Of-UBER.png differ diff --git a/w06-interoperability/images/architecture.svg b/w06-interoperability/images/architecture.svg new file mode 100644 index 0000000000000000000000000000000000000000..1f6f96bde409ad5bebc8b5b1e479e6e7cdc3081c --- /dev/null +++ b/w06-interoperability/images/architecture.svg @@ -0,0 +1,2 @@ +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(255, 255, 255);" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="959px" height="183px" viewBox="-0.5 -0.5 959 183"><defs/><rect x="160" y="1" width="650" height="180" rx="18" ry="18" fill="none" stroke="#f59d56" stroke-width="2" stroke-dasharray="16 8" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="484.08" y="172.83">micro-service</text></g><path d="M 300 82.67 L 320 82.67 L 320 122.67 L 333.63 122.67" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 338.88 122.67 L 331.88 126.17 L 333.63 122.67 L 331.88 119.17 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 300 82.67 L 320 82.67 L 320 42.67 L 333.63 42.67" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 338.88 42.67 L 331.88 46.17 L 333.63 42.67 L 331.88 39.17 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="180" y="61" width="120" height="44" rx="1" ry="1" fill="#000000" stroke="#000000" transform="translate(2,3)" opacity="0.25"/><rect x="180" y="61" width="120" height="44" rx="1" ry="1" fill="#ffffff" stroke="#dddddd" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="239.5" y="86.5">Servlet</text></g><path d="M 460 123 L 493.63 123" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 498.88 123 L 491.88 126.5 L 493.63 123 L 491.88 119.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="340" y="101" width="120" height="44" rx="1" ry="1" fill="#000000" stroke="#000000" transform="translate(2,3)" opacity="0.25"/><rect x="340" y="101" width="120" height="44" rx="1" ry="1" fill="#ffffff" stroke="#dddddd" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="399.5" y="126.5">Controller</text></g><path d="M 620 123 L 653.63 123" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 658.88 123 L 651.88 126.5 L 653.63 123 L 651.88 119.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="500" y="101" width="120" height="44" rx="1" ry="1" fill="#000000" stroke="#000000" transform="translate(2,3)" opacity="0.25"/><rect x="500" y="101" width="120" height="44" rx="1" ry="1" fill="#ffffff" stroke="#dddddd" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="559.5" y="126.5">Service</text></g><path d="M 790 122.67 L 837.5 122.67 L 837.5 85.17 L 878.63 85.17" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 883.88 85.17 L 876.88 88.67 L 878.63 85.17 L 876.88 81.67 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="660" y="101" width="130" height="44" rx="1" ry="1" fill="#000000" stroke="#000000" transform="translate(2,3)" opacity="0.25"/><rect x="660" y="101" width="130" height="44" rx="1" ry="1" fill="#ffffff" stroke="#dddddd" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="724.5" y="126.5">Repository</text></g><path d="M 460 43 L 493.63 43" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 498.88 43 L 491.88 46.5 L 493.63 43 L 491.88 39.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="340" y="21" width="120" height="44" rx="1" ry="1" fill="#000000" stroke="#000000" transform="translate(2,3)" opacity="0.25"/><rect x="340" y="21" width="120" height="44" rx="1" ry="1" fill="#ffffff" stroke="#dddddd" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="399.5" y="46.5">Controller</text></g><path d="M 620 43 L 653.63 43" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 658.88 43 L 651.88 46.5 L 653.63 43 L 651.88 39.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="500" y="21" width="120" height="44" rx="1" ry="1" fill="#000000" stroke="#000000" transform="translate(2,3)" opacity="0.25"/><rect x="500" y="21" width="120" height="44" rx="1" ry="1" fill="#ffffff" stroke="#dddddd" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="559.5" y="46.5">Service</text></g><path d="M 790 42.67 L 837.5 42.67 L 837.5 85.17 L 878.63 85.17" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 883.88 85.17 L 876.88 88.67 L 878.63 85.17 L 876.88 81.67 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="660" y="21" width="130" height="44" rx="1" ry="1" fill="#000000" stroke="#000000" transform="translate(2,3)" opacity="0.25"/><rect x="660" y="21" width="130" height="44" rx="1" ry="1" fill="#ffffff" stroke="#dddddd" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="724.5" y="46.5">Repository</text></g><rect x="885" y="43" width="70" height="85" rx="1" ry="1" fill="#000000" stroke="#000000" transform="translate(2,3)" opacity="0.25"/><rect x="885" y="43" width="70" height="85" rx="1" ry="1" fill="#ffffff" stroke="#dddddd" pointer-events="none"/><g transform="translate(891.5,109.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="57" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(153, 153, 153); line-height: 1.2; vertical-align: top; width: 58px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Database</div></div></foreignObject><text x="29" y="12" fill="#999999" text-anchor="middle" font-size="12px" font-family="Helvetica">Database</text></switch></g><path d="M 895 55.5 L 895 100.5 L 945 100.5 L 945 65.62 L 919.86 65.62 L 919.83 55.5 Z" fill="#757575" stroke="none" pointer-events="none"/><path d="M 909.81 90.43 L 915.17 90.43 L 915.19 95.76 L 909.81 95.76 Z M 899.75 90.43 L 905.06 90.43 L 905.06 95.76 L 899.75 95.76 Z M 909.81 80.32 L 915.14 80.32 L 915.16 85.66 L 909.81 85.66 Z M 899.75 80.32 L 905.06 80.32 L 905.06 85.66 L 899.75 85.66 Z M 919.87 70.36 L 940.25 70.36 L 940.25 95.76 L 919.94 95.76 L 919.92 90.43 L 925 90.43 L 925 85.66 L 919.91 85.66 L 919.89 80.32 L 925 80.32 L 925 75.54 L 919.87 75.54 Z M 909.81 70.36 L 915.13 70.36 L 915.13 75.54 L 909.81 75.54 Z M 899.75 70.36 L 905.06 70.36 L 905.06 75.54 L 899.75 75.54 Z M 909.81 60.24 L 915.09 60.24 L 915.11 65.62 L 909.81 65.62 Z M 899.75 60.24 L 905.06 60.24 L 905.06 65.62 L 899.75 65.62 Z M 930.11 75.54 L 934.97 75.54 L 934.97 80.32 L 930.11 80.32 Z M 930.11 85.66 L 934.97 85.66 L 934.97 90.43 L 930.11 90.43 Z" fill="#ffffff" stroke="none" pointer-events="none"/><path d="M 70 82.67 L 173.63 82.67" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 178.88 82.67 L 171.88 86.17 L 173.63 82.67 L 171.88 79.17 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(91.5,70.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="66" height="24" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; white-space: nowrap; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;"><div>HTTP</div><div>REST/JSON<br /></div></div></div></foreignObject><text x="33" y="18" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">[Not supported by viewer]</text></switch></g><rect x="0" y="40.5" width="70" height="85" rx="1" ry="1" fill="#000000" stroke="#000000" transform="translate(2,3)" opacity="0.25"/><rect x="0" y="40.5" width="70" height="85" rx="1" ry="1" fill="#ffffff" stroke="#dddddd" pointer-events="none"/><g transform="translate(-0.5,107.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="71" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(153, 153, 153); line-height: 1.2; vertical-align: top; width: 72px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Application</div></div></foreignObject><text x="36" y="12" fill="#999999" text-anchor="middle" font-size="12px" font-family="Helvetica">Application</text></switch></g><path d="M 14.93 55.5 L 55.07 55.5 C 57.8 55.5 60 57.69 60 60.42 L 60 90.58 C 60 93.31 57.8 95.5 55.07 95.5 L 14.93 95.5 C 12.2 95.5 10 93.31 10 90.58 L 10 60.42 C 10 57.69 12.2 55.5 14.93 55.5 Z" fill="#757575" stroke="none" pointer-events="none"/><path d="M 55.15 67.05 L 55.15 89.81 L 44.81 89.81 L 44.81 67.05 Z M 14.92 79.58 L 42.74 79.58 L 42.74 89.81 L 14.92 89.81 Z M 14.92 67.05 L 42.74 67.05 L 42.74 77.3 L 14.92 77.3 Z" fill="#ffffff" stroke="none" pointer-events="none"/></svg> \ No newline at end of file diff --git a/w06-interoperability/images/fry.png b/w06-interoperability/images/fry.png new file mode 100644 index 0000000000000000000000000000000000000000..8bd786edfd777b28dc91d8d06a8a1169d4eea9d3 Binary files /dev/null and b/w06-interoperability/images/fry.png differ diff --git a/w06-interoperability/images/logo-bruno.png b/w06-interoperability/images/logo-bruno.png new file mode 100644 index 0000000000000000000000000000000000000000..4f6c356651bab0abedffdf4cfded3c2beb8613db Binary files /dev/null and b/w06-interoperability/images/logo-bruno.png differ diff --git a/w06-interoperability/images/logo-insomnia.png b/w06-interoperability/images/logo-insomnia.png new file mode 100644 index 0000000000000000000000000000000000000000..99a51fbd4a0bec3983576b207e4d8e36b63ebc79 Binary files /dev/null and b/w06-interoperability/images/logo-insomnia.png differ diff --git a/w06-interoperability/images/logo-postman.png b/w06-interoperability/images/logo-postman.png new file mode 100644 index 0000000000000000000000000000000000000000..6393829c8b410f70c858652ccf0ace67a881dafa Binary files /dev/null and b/w06-interoperability/images/logo-postman.png differ diff --git a/w06-interoperability/images/micro-service-poke.png b/w06-interoperability/images/micro-service-poke.png new file mode 100644 index 0000000000000000000000000000000000000000..6ffd1da21543a4dca87b97fc09498d7a4dfe393b Binary files /dev/null and b/w06-interoperability/images/micro-service-poke.png differ diff --git a/w06-interoperability/images/postman-create-collection-2.png b/w06-interoperability/images/postman-create-collection-2.png new file mode 100644 index 0000000000000000000000000000000000000000..c5c8ca75e3d0821ef5356d110c73006721e0cbdf Binary files /dev/null and b/w06-interoperability/images/postman-create-collection-2.png differ diff --git a/w06-interoperability/images/postman-create-collection.png b/w06-interoperability/images/postman-create-collection.png new file mode 100644 index 0000000000000000000000000000000000000000..cd2321478fad393c5de26969b932c3f161cabe32 Binary files /dev/null and b/w06-interoperability/images/postman-create-collection.png differ diff --git a/w06-interoperability/images/postman-create-request.png b/w06-interoperability/images/postman-create-request.png new file mode 100644 index 0000000000000000000000000000000000000000..68d6c41f163c59d73f134e1d7ca7718e045c8fa3 Binary files /dev/null and b/w06-interoperability/images/postman-create-request.png differ diff --git a/w06-interoperability/images/rest.png b/w06-interoperability/images/rest.png new file mode 100644 index 0000000000000000000000000000000000000000..d279c2b823803cfe7172a34585ee294af294157e Binary files /dev/null and b/w06-interoperability/images/rest.png differ diff --git a/w06-interoperability/images/soap.jpg b/w06-interoperability/images/soap.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d5c705e175f761ef2ccf555bcb6e895204055174 Binary files /dev/null and b/w06-interoperability/images/soap.jpg differ diff --git a/w06-interoperability/images/swagger-generate.png b/w06-interoperability/images/swagger-generate.png new file mode 100644 index 0000000000000000000000000000000000000000..2f5425e6f43def84f747174e876313eb6097f2b2 Binary files /dev/null and b/w06-interoperability/images/swagger-generate.png differ diff --git a/w06-interoperability/images/swagger-hub.png b/w06-interoperability/images/swagger-hub.png new file mode 100644 index 0000000000000000000000000000000000000000..d9033fb36f85b591dcf7f906fe8c7ab2f626bf87 Binary files /dev/null and b/w06-interoperability/images/swagger-hub.png differ diff --git a/w06-interoperability/images/trainer-gui.png b/w06-interoperability/images/trainer-gui.png new file mode 100644 index 0000000000000000000000000000000000000000..ffd4757f64ddb7309343283fff2ace586ff748f9 Binary files /dev/null and b/w06-interoperability/images/trainer-gui.png differ diff --git a/w06-interoperability/translations-en.json b/w06-interoperability/translations-en.json new file mode 100644 index 0000000000000000000000000000000000000000..8fdb735086730ea170cd828c1c89bc1a68b97ab1 --- /dev/null +++ b/w06-interoperability/translations-en.json @@ -0,0 +1 @@ +[{"id":1,"name":"Bulbasaur"},{"id":2,"name":"Ivysaur"},{"id":3,"name":"Venusaur"},{"id":4,"name":"Charmander"},{"id":5,"name":"Charmeleon"},{"id":6,"name":"Charizard"},{"id":7,"name":"Squirtle"},{"id":8,"name":"Wartortle"},{"id":9,"name":"Blastoise"},{"id":10,"name":"Caterpie"},{"id":11,"name":"Metapod"},{"id":12,"name":"Butterfree"},{"id":13,"name":"Weedle"},{"id":14,"name":"Kakuna"},{"id":15,"name":"Beedrill"},{"id":16,"name":"Pidgey"},{"id":17,"name":"Pidgeotto"},{"id":18,"name":"Pidgeot"},{"id":19,"name":"Rattata"},{"id":20,"name":"Raticate"},{"id":21,"name":"Spearow"},{"id":22,"name":"Fearow"},{"id":23,"name":"Ekans"},{"id":24,"name":"Arbok"},{"id":25,"name":"Pikachu"},{"id":26,"name":"Raichu"},{"id":27,"name":"Sandshrew"},{"id":28,"name":"Sandslash"},{"id":29,"name":"Nidoranâ"},{"id":30,"name":"Nidorina"},{"id":31,"name":"Nidoqueen"},{"id":32,"name":"Nidoranâ"},{"id":33,"name":"Nidorino"},{"id":34,"name":"Nidoking"},{"id":35,"name":"Clefairy"},{"id":36,"name":"Clefable"},{"id":37,"name":"Vulpix"},{"id":38,"name":"Ninetales"},{"id":39,"name":"Jigglypuff"},{"id":40,"name":"Wigglytuff"},{"id":41,"name":"Zubat"},{"id":42,"name":"Golbat"},{"id":43,"name":"Oddish"},{"id":44,"name":"Gloom"},{"id":45,"name":"Vileplume"},{"id":46,"name":"Paras"},{"id":47,"name":"Parasect"},{"id":48,"name":"Venonat"},{"id":49,"name":"Venomoth"},{"id":50,"name":"Diglett"},{"id":51,"name":"Dugtrio"},{"id":52,"name":"Meowth"},{"id":53,"name":"Persian"},{"id":54,"name":"Psyduck"},{"id":55,"name":"Golduck"},{"id":56,"name":"Mankey"},{"id":57,"name":"Primeape"},{"id":58,"name":"Growlithe"},{"id":59,"name":"Arcanine"},{"id":60,"name":"Poliwag"},{"id":61,"name":"Poliwhirl"},{"id":62,"name":"Poliwrath"},{"id":63,"name":"Abra"},{"id":64,"name":"Kadabra"},{"id":65,"name":"Alakazam"},{"id":66,"name":"Machop"},{"id":67,"name":"Machoke"},{"id":68,"name":"Machamp"},{"id":69,"name":"Bellsprout"},{"id":70,"name":"Weepinbell"},{"id":71,"name":"Victreebel"},{"id":72,"name":"Tentacool"},{"id":73,"name":"Tentacruel"},{"id":74,"name":"Geodude"},{"id":75,"name":"Graveler"},{"id":76,"name":"Golem"},{"id":77,"name":"Ponyta"},{"id":78,"name":"Rapidash"},{"id":79,"name":"Slowpoke"},{"id":80,"name":"Slowbro"},{"id":81,"name":"Magnemite"},{"id":82,"name":"Magneton"},{"id":83,"name":"Farfetchâd"},{"id":84,"name":"Doduo"},{"id":85,"name":"Dodrio"},{"id":86,"name":"Seel"},{"id":87,"name":"Dewgong"},{"id":88,"name":"Grimer"},{"id":89,"name":"Muk"},{"id":90,"name":"Shellder"},{"id":91,"name":"Cloyster"},{"id":92,"name":"Gastly"},{"id":93,"name":"Haunter"},{"id":94,"name":"Gengar"},{"id":95,"name":"Onix"},{"id":96,"name":"Drowzee"},{"id":97,"name":"Hypno"},{"id":98,"name":"Krabby"},{"id":99,"name":"Kingler"},{"id":100,"name":"Voltorb"},{"id":101,"name":"Electrode"},{"id":102,"name":"Exeggcute"},{"id":103,"name":"Exeggutor"},{"id":104,"name":"Cubone"},{"id":105,"name":"Marowak"},{"id":106,"name":"Hitmonlee"},{"id":107,"name":"Hitmonchan"},{"id":108,"name":"Lickitung"},{"id":109,"name":"Koffing"},{"id":110,"name":"Weezing"},{"id":111,"name":"Rhyhorn"},{"id":112,"name":"Rhydon"},{"id":113,"name":"Chansey"},{"id":114,"name":"Tangela"},{"id":115,"name":"Kangaskhan"},{"id":116,"name":"Horsea"},{"id":117,"name":"Seadra"},{"id":118,"name":"Goldeen"},{"id":119,"name":"Seaking"},{"id":120,"name":"Staryu"},{"id":121,"name":"Starmie"},{"id":122,"name":"Mr. Mime"},{"id":123,"name":"Scyther"},{"id":124,"name":"Jynx"},{"id":125,"name":"Electabuzz"},{"id":126,"name":"Magmar"},{"id":127,"name":"Pinsir"},{"id":128,"name":"Tauros"},{"id":129,"name":"Magikarp"},{"id":130,"name":"Gyarados"},{"id":131,"name":"Lapras"},{"id":132,"name":"Ditto"},{"id":133,"name":"Eevee"},{"id":134,"name":"Vaporeon"},{"id":135,"name":"Jolteon"},{"id":136,"name":"Flareon"},{"id":137,"name":"Porygon"},{"id":138,"name":"Omanyte"},{"id":139,"name":"Omastar"},{"id":140,"name":"Kabuto"},{"id":141,"name":"Kabutops"},{"id":142,"name":"Aerodactyl"},{"id":143,"name":"Snorlax"},{"id":144,"name":"Articuno"},{"id":145,"name":"Zapdos"},{"id":146,"name":"Moltres"},{"id":147,"name":"Dratini"},{"id":148,"name":"Dragonair"},{"id":149,"name":"Dragonite"},{"id":150,"name":"Mewtwo"},{"id":151,"name":"Mew"}] \ No newline at end of file diff --git a/w06-interoperability/translations-fr.json b/w06-interoperability/translations-fr.json new file mode 100644 index 0000000000000000000000000000000000000000..774dde061ae72219479514cdb6833645d40fc4b2 --- /dev/null +++ b/w06-interoperability/translations-fr.json @@ -0,0 +1 @@ +[{"id":1,"name":"Bulbizarre"},{"id":2,"name":"Herbizarre"},{"id":3,"name":"Florizarre"},{"id":4,"name":"SalamĂšche"},{"id":5,"name":"Reptincel"},{"id":6,"name":"Dracaufeu"},{"id":7,"name":"Carapuce"},{"id":8,"name":"Carabaffe"},{"id":9,"name":"Tortank"},{"id":10,"name":"Chenipan"},{"id":11,"name":"Chrysacier"},{"id":12,"name":"Papilusion"},{"id":13,"name":"Aspicot"},{"id":14,"name":"Coconfort"},{"id":15,"name":"Dardargnan"},{"id":16,"name":"Roucool"},{"id":17,"name":"Roucoups"},{"id":18,"name":"Roucarnage"},{"id":19,"name":"Rattata"},{"id":20,"name":"Rattatac"},{"id":21,"name":"Piafabec"},{"id":22,"name":"Rapasdepic"},{"id":23,"name":"Abo"},{"id":24,"name":"Arbok"},{"id":25,"name":"Pikachu"},{"id":26,"name":"Raichu"},{"id":27,"name":"Sabelette"},{"id":28,"name":"Sablaireau"},{"id":29,"name":"Nidoranâ"},{"id":30,"name":"Nidorina"},{"id":31,"name":"Nidoqueen"},{"id":32,"name":"Nidoranâ"},{"id":33,"name":"Nidorino"},{"id":34,"name":"Nidoking"},{"id":35,"name":"MĂ©lofĂ©e"},{"id":36,"name":"MĂ©lodelfe"},{"id":37,"name":"Goupix"},{"id":38,"name":"Feunard"},{"id":39,"name":"Rondoudou"},{"id":40,"name":"Grodoudou"},{"id":41,"name":"Nosferapti"},{"id":42,"name":"Nosferalto"},{"id":43,"name":"Mystherbe"},{"id":44,"name":"Ortide"},{"id":45,"name":"Rafflesia"},{"id":46,"name":"Paras"},{"id":47,"name":"Parasect"},{"id":48,"name":"Mimitoss"},{"id":49,"name":"AĂ©romite"},{"id":50,"name":"Taupiqueur"},{"id":51,"name":"Triopikeur"},{"id":52,"name":"Miaouss"},{"id":53,"name":"Persian"},{"id":54,"name":"Psykokwak"},{"id":55,"name":"Akwakwak"},{"id":56,"name":"FĂ©rosinge"},{"id":57,"name":"Colossinge"},{"id":58,"name":"Caninos"},{"id":59,"name":"Arcanin"},{"id":60,"name":"Ptitard"},{"id":61,"name":"TĂȘtarte"},{"id":62,"name":"Tartard"},{"id":63,"name":"Abra"},{"id":64,"name":"Kadabra"},{"id":65,"name":"Alakazam"},{"id":66,"name":"Machoc"},{"id":67,"name":"Machopeur"},{"id":68,"name":"Mackogneur"},{"id":69,"name":"ChĂ©tiflor"},{"id":70,"name":"Boustiflor"},{"id":71,"name":"Empiflor"},{"id":72,"name":"Tentacool"},{"id":73,"name":"Tentacruel"},{"id":74,"name":"Racaillou"},{"id":75,"name":"Gravalanch"},{"id":76,"name":"Grolem"},{"id":77,"name":"Ponyta"},{"id":78,"name":"Galopa"},{"id":79,"name":"Ramoloss"},{"id":80,"name":"Flagadoss"},{"id":81,"name":"MagnĂ©ti"},{"id":82,"name":"MagnĂ©ton"},{"id":83,"name":"Canarticho"},{"id":84,"name":"Doduo"},{"id":85,"name":"Dodrio"},{"id":86,"name":"Otaria"},{"id":87,"name":"Lamantine"},{"id":88,"name":"Tadmorv"},{"id":89,"name":"Grotadmorv"},{"id":90,"name":"Kokiyas"},{"id":91,"name":"Crustabri"},{"id":92,"name":"Fantominus"},{"id":93,"name":"Spectrum"},{"id":94,"name":"Ectoplasma"},{"id":95,"name":"Onix"},{"id":96,"name":"Soporifik"},{"id":97,"name":"Hypnomade"},{"id":98,"name":"Krabby"},{"id":99,"name":"Krabboss"},{"id":100,"name":"Voltorbe"},{"id":101,"name":"Ălectrode"},{"id":102,"name":"Noeunoeuf"},{"id":103,"name":"Noadkoko"},{"id":104,"name":"Osselait"},{"id":105,"name":"Ossatueur"},{"id":106,"name":"Kicklee"},{"id":107,"name":"Tygnon"},{"id":108,"name":"Excelangue"},{"id":109,"name":"Smogo"},{"id":110,"name":"Smogogo"},{"id":111,"name":"Rhinocorne"},{"id":112,"name":"RhinofĂ©ros"},{"id":113,"name":"Leveinard"},{"id":114,"name":"Saquedeneu"},{"id":115,"name":"Kangourex"},{"id":116,"name":"Hypotrempe"},{"id":117,"name":"HypocĂ©an"},{"id":118,"name":"PoissirĂšne"},{"id":119,"name":"Poissoroy"},{"id":120,"name":"Stari"},{"id":121,"name":"Staross"},{"id":122,"name":"M. Mime"},{"id":123,"name":"InsĂ©cateur"},{"id":124,"name":"Lippoutou"},{"id":125,"name":"Ălektek"},{"id":126,"name":"Magmar"},{"id":127,"name":"Scarabrute"},{"id":128,"name":"Tauros"},{"id":129,"name":"Magicarpe"},{"id":130,"name":"LĂ©viator"},{"id":131,"name":"Lokhlass"},{"id":132,"name":"MĂ©tamorph"},{"id":133,"name":"Ăvoli"},{"id":134,"name":"Aquali"},{"id":135,"name":"Voltali"},{"id":136,"name":"Pyroli"},{"id":137,"name":"Porygon"},{"id":138,"name":"Amonita"},{"id":139,"name":"Amonistar"},{"id":140,"name":"Kabuto"},{"id":141,"name":"Kabutops"},{"id":142,"name":"PtĂ©ra"},{"id":143,"name":"Ronflex"},{"id":144,"name":"Artikodin"},{"id":145,"name":"Ălecthor"},{"id":146,"name":"Sulfura"},{"id":147,"name":"Minidraco"},{"id":148,"name":"Draco"},{"id":149,"name":"Dracolosse"},{"id":150,"name":"Mewtwo"},{"id":151,"name":"Mew"}] \ No newline at end of file