<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Emmanuel Balpe</title>
    <description>The latest articles on DEV Community by Emmanuel Balpe (@m4nu56).</description>
    <link>https://dev.to/m4nu56</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F279002%2Fe27cd4de-0620-4acc-bdab-f169407f1561.png</url>
      <title>DEV Community: Emmanuel Balpe</title>
      <link>https://dev.to/m4nu56</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/m4nu56"/>
    <language>en</language>
    <item>
      <title>Solving the hanging tests in Micronaut application</title>
      <dc:creator>Emmanuel Balpe</dc:creator>
      <pubDate>Sun, 29 Jan 2023 21:08:20 +0000</pubDate>
      <link>https://dev.to/m4nu56/solving-the-hanging-tests-in-micronaut-application-4l1</link>
      <guid>https://dev.to/m4nu56/solving-the-hanging-tests-in-micronaut-application-4l1</guid>
      <description>&lt;p&gt;We've been facing a strange issue with our Micronaut application tests.&lt;br&gt;
Specifically tests that were producing Kafka messages were successfully run bug sometimes the test engine was kept hanging and never finishing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqun68byjpqkzlchojtnu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqun68byjpqkzlchojtnu.png" alt="Hanging tests" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are running the following versions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Java 17&lt;/li&gt;
&lt;li&gt;Gradle 7.6&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.micronaut.io/3.8.3/guide/index.html" rel="noopener noreferrer"&gt;Micronaut 3.8.3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This problem seems related with this issue described &lt;a href="https://github.com/micronaut-projects/micronaut-gcp/issues/638" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After some investigation we've found out that the issue was caused by the fact that sometimes some&lt;br&gt;
&lt;code&gt;KafkaListener&lt;/code&gt; in our projects were still active and processing messages even after the test was finished.&lt;br&gt;
This caused the application to keep running and the test engine to wait for the application to finish.&lt;/p&gt;

&lt;p&gt;Our solution was to add a &lt;code&gt;@PreDestroy&lt;/code&gt; method to a new &lt;code&gt;AppApplicationListener&lt;/code&gt; singleton so that the listeners can terminate&lt;br&gt;
their process so that the application shutdown can be successful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Singleton&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppEventListener&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="no"&gt;LOGGER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AppEventListener&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@EventListener&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onStartupEvent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StartupEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@PreDestroy&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onShutdown&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;LOGGER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PreDestroy: Shutting down after a sleep of 1000ms to allow the tests to complete."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="no"&gt;LOGGER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"It looks like the test is stuck if we don't implement this @AfterAll method. "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;"This is an issue with Micronaut and JUnit 5."&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;"https://github.com/micronaut-projects/micronaut-gcp/issues/638"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Secure your Java Servlet Application with Keycloak</title>
      <dc:creator>Emmanuel Balpe</dc:creator>
      <pubDate>Wed, 27 May 2020 20:28:44 +0000</pubDate>
      <link>https://dev.to/m4nu56/secure-your-java-servlet-application-with-keycloak-4826</link>
      <guid>https://dev.to/m4nu56/secure-your-java-servlet-application-with-keycloak-4826</guid>
      <description>&lt;p&gt;We'll see how to configure a Java Servlet based application so it can be secure with Keycloak. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.keycloak.org/" rel="noopener noreferrer"&gt;Keycloak&lt;/a&gt; is an Open Source Identity and Access Management that can be used to delegate entirely the security of an application. &lt;/p&gt;

&lt;h2&gt;
  
  
  1. Keycloak configuration
&lt;/h2&gt;

&lt;p&gt;The Keycloak documentation is really easy to follow. You can see for yourself here the section about the configuration of your Keycloak instance: &lt;a href="https://www.keycloak.org/docs/latest/authorization_services/#_getting_started_hello_world_create_realm" rel="noopener noreferrer"&gt;https://www.keycloak.org/docs/latest/authorization_services/#_getting_started_hello_world_create_realm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need to configure: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A realm &lt;/li&gt;
&lt;li&gt;A user with role &lt;code&gt;user&lt;/code&gt;, we'll see later how it's used&lt;/li&gt;
&lt;li&gt;A Client. It's a representation of your Java application

&lt;ul&gt;
&lt;li&gt;Client protocol: openid-connect&lt;/li&gt;
&lt;li&gt;Access Type: public&lt;/li&gt;
&lt;li&gt;Valid Redirect URIs: the url of your development environment or &lt;code&gt;*&lt;/code&gt; for the time being&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Tomcat security-constraint
&lt;/h2&gt;

&lt;p&gt;We're using the Tomcat &lt;code&gt;security-constraint&lt;/code&gt; that enable a security verification at the application level on Tomcat. &lt;br&gt;
The Keycloak team developed a convenient Valve for the Tomcat Security system that handle the redirect to and from the Keycloak login page. &lt;/p&gt;

&lt;h3&gt;
  
  
  2.1. You need to add the following to the &lt;code&gt;context.xml&lt;/code&gt; of your application:
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;Context&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Valve&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Context&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  2.2. Install the &lt;a href="https://mvnrepository.com/artifact/org.keycloak/keycloak-tomcat-adapter-dist" rel="noopener noreferrer"&gt;Keycloak Valve libraries&lt;/a&gt; into the &lt;code&gt;${tomcat}/lib&lt;/code&gt; directory on your Tomcat server
&lt;/h3&gt;
&lt;h3&gt;
  
  
  2.3. You need to copy the &lt;code&gt;keycloak.json&lt;/code&gt; config file into &lt;code&gt;/WEB-INF/keycloak.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;You can download the file in your Client installation tab: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Ftomcat-keycloak-servlet%2Fadapter-config.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Ftomcat-keycloak-servlet%2Fadapter-config.png" alt="adapter-config"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2.4. Add &lt;code&gt;security-constraint&lt;/code&gt; in your &lt;code&gt;web.xml&lt;/code&gt;
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;security-constraint&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;web-resource-collection&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;web-resource-name&amp;gt;&lt;/span&gt;Private area&lt;span class="nt"&gt;&amp;lt;/web-resource-name&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;/esp_privat/*&lt;span class="nt"&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/web-resource-collection&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;auth-constraint&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;role-name&amp;gt;&lt;/span&gt;user&lt;span class="nt"&gt;&amp;lt;/role-name&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/auth-constraint&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/security-constraint&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;security-constraint&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;web-resource-collection&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;web-resource-name&amp;gt;&lt;/span&gt;Public area&lt;span class="nt"&gt;&amp;lt;/web-resource-name&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;/api/*&lt;span class="nt"&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/web-resource-collection&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/security-constraint&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;login-config&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;auth-method&amp;gt;&lt;/span&gt;BASIC&lt;span class="nt"&gt;&amp;lt;/auth-method&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;realm-name&amp;gt;&lt;/span&gt;this is ignored currently&lt;span class="nt"&gt;&amp;lt;/realm-name&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/login-config&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;security-role&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;role-name&amp;gt;&lt;/span&gt;user&lt;span class="nt"&gt;&amp;lt;/role-name&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/security-role&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here we defined 2 URL patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/esp_privat/*&lt;/code&gt; that require a user to be connected with a role &lt;code&gt;user&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/*&lt;/code&gt; that require no authentification&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.5. Results
&lt;/h3&gt;

&lt;p&gt;So when you try accessing any route under &lt;code&gt;/esp_privat/&lt;/code&gt; in your application Keycloak valve now automatically redirect you to the login page in your Keycloak instance. &lt;br&gt;
When successfuly logged in Keycloak redirects you to the asked page. &lt;/p&gt;

&lt;p&gt;What we need to do now is to identify the user logged in thank's to the token Keycloak is adding to the cookies of the web navigator. &lt;/p&gt;

&lt;h2&gt;
  
  
  3. Intercept Keycloak access token to log the user into your app
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1. Keycloak dependencies
&lt;/h3&gt;

&lt;p&gt;Add the following to the &lt;code&gt;pom.xml&lt;/code&gt; of your &lt;code&gt;webapp&lt;/code&gt; application: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.keycloak&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;keycloak-core&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;9.0.2&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;provided&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.keycloak&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;keycloak-adapter-core&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;9.0.2&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;provided&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.keycloak&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;keycloak-adapter-spi&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;9.0.2&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;provided&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Notice the scope = &lt;code&gt;provided&lt;/code&gt; since we will be using the libraries added previously into the tomcat library folder. We don't want to override it with another version of the libraries.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2. Read the token
&lt;/h3&gt;

&lt;p&gt;The following snippet will extract the token from the request and verify if it's lifetime is expired. It returns true in case the token is valid. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;


&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.keycloak.KeycloakSecurityContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.keycloak.TokenVerifier&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.keycloak.common.VerificationException&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.keycloak.representations.AccessToken&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt; 

&lt;span class="cm"&gt;/**
* Verify if user is logged in keycloak by validating token in request
*/&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isLoggedInKeycloak&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpServletRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;VerificationException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;KeycloakSecurityContext&lt;/span&gt; &lt;span class="n"&gt;keycloakSecurityContextToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getKeycloakSecurityContextToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keycloakSecurityContextToken&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isTokenExpired&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keycloakSecurityContextToken&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isTokenExpired&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;KeycloakSecurityContext&lt;/span&gt; &lt;span class="n"&gt;keycloakSecurityContextToken&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;VerificationException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;AccessToken&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TokenVerifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keycloakSecurityContextToken&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTokenString&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;AccessToken&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getToken&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isExpired&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;warn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User token is expired..."&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In our case we also needed to verify if the user is a member of the correct group so we added the following method check: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;handleGroupMembership&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Nonnull&lt;/span&gt; &lt;span class="nc"&gt;KeycloakSecurityContext&lt;/span&gt; &lt;span class="n"&gt;keycloakSecurityContext&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;keycloakPreferredUsername&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keycloakSecurityContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getToken&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getOtherClaims&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getOrDefault&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"groups"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;());&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;GenericRuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Fail to read groups from the token of the user "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;keycloakPreferredUsername&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equalsIgnoreCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/my-group"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findFirst&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GenericRuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User \""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;keycloakPreferredUsername&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\" is not a member of /my-group"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We then called the previous method in a pre-action hook into all the call received by our servlets so that it can be catched by any servlet like so: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;isUserLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSession&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;USER_SESSION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isLoggedInKeycloak&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isUserLoggedIn&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User logged in Keycloak but not logged in the app. Logging in the user..."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;KeycloakLoginService&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getKeycloakSecurityContextToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;isLoggedInKeycloak&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;isUserLoggedIn&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User not logged in Keycloak but logged in the app. Logging out the user..."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;sessionLogout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  3.3. Logout
&lt;/h3&gt;

&lt;p&gt;To logout a user from Keycloak you can use the &lt;code&gt;request.logout()&lt;/code&gt; method. We use the following method: &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;logout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpServletRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpServletResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;ServletException&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;HttpSession&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSession&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invalidate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logout&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSession&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// create a new session&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendRedirect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContextPath&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  4. Maven profiles to compile versions with and without keycloak login
&lt;/h2&gt;

&lt;p&gt;In one of our project we needed to be able to deploy a version of the app that doesn't use the Keycloak login feature but our previous login mechanism.&lt;br&gt;
Of course we wanted to keep a unique codebase with the less difference as possible. &lt;br&gt;
We identify that the only thing preventing us from working as before was the &lt;code&gt;security-constraint&lt;/code&gt; section in the &lt;code&gt;web.xml&lt;/code&gt; config file. &lt;/p&gt;

&lt;p&gt;We will be using the Maven filtering solution with a little hack we found on SO: &lt;a href="https://stackoverflow.com/questions/3298763/maven-customize-web-xml-of-web-app-project/8593041#8593041" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/3298763/maven-customize-web-xml-of-web-app-project/8593041#8593041&lt;/a&gt;&lt;br&gt;
It consists in adding 2 variables in your web.xml like so: &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

${enable.security.start}
&lt;span class="nt"&gt;&amp;lt;security-constraint&amp;gt;&lt;/span&gt;
  ...
  // all of the XML that you need, in a completely readable format
  ...
&lt;span class="nt"&gt;&amp;lt;/login-config&amp;gt;&lt;/span&gt;  
${enable.security.end}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And have it replaced by comment block start &lt;code&gt;&amp;amp;lt;!--&lt;/code&gt; and end &lt;code&gt;-&amp;amp;gt;&lt;/code&gt; in the profile where you don't want to use Keycloak. &lt;/p&gt;

&lt;p&gt;So in our default &lt;code&gt;ci&lt;/code&gt; profile we defined the following properties: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;enable.security.start&amp;gt;&amp;lt;/enable.security.start&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;enable.security.end&amp;gt;&amp;lt;/enable.security.end&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and in the &lt;code&gt;without-keycloak&lt;/code&gt; profile: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;enable.security.start&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;lt;&lt;/span&gt;!--&lt;span class="nt"&gt;&amp;lt;/enable.security.start&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;enable.security.end&amp;gt;&lt;/span&gt;--&lt;span class="ni"&gt;&amp;amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/enable.security.end&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>java</category>
      <category>keycloak</category>
    </item>
    <item>
      <title>Automatiser le déploiement avec Gitlab + Docker + Ouroboros</title>
      <dc:creator>Emmanuel Balpe</dc:creator>
      <pubDate>Wed, 27 May 2020 20:26:04 +0000</pubDate>
      <link>https://dev.to/m4nu56/automatiser-le-deploiement-avec-gitlab-docker-ouroboros-30bo</link>
      <guid>https://dev.to/m4nu56/automatiser-le-deploiement-avec-gitlab-docker-ouroboros-30bo</guid>
      <description>&lt;p&gt;Le but de cet article est de vous montrer une solution simple et rapide pour mettre en place du déploiement continue.&lt;br&gt;
La stack du projet est composée d’une partie API en nodejs et d’une base de donnée en mongodb. La pipeline va créer une nouvelle image de notre API en nodejs et la déployer sur notre registry privé.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Gitlab CI
&lt;/h2&gt;

&lt;p&gt;L’objectif de notre pipeline est d’exécuter les tests unitaires et d’intégration puis de construire et déployer une image de l’API sur notre registry interne.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/m4nu56/1d86ff0232f0a0cea5865d27967f5402#file-dockerfile" rel="noopener noreferrer"&gt;github gist Dockerfile&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:10&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TZ=Europe/Paris&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-snf&lt;/span&gt; /usr/share/zoneinfo/&lt;span class="nv"&gt;$TZ&lt;/span&gt; /etc/localtime &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$TZ&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/timezone

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; build-essential &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python

&lt;span class="c"&gt;# Create app directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;

&lt;span class="c"&gt;# Install app dependencies&lt;/span&gt;
&lt;span class="c"&gt;# A wildcard is used to ensure both package.json AND package-lock.json are copied&lt;/span&gt;
&lt;span class="c"&gt;# where available (npm@5+)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json /usr/src/app/&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Bundle app source&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./src/  /usr/src/app/&lt;/span&gt;

&lt;span class="c"&gt;# Copy jest configuration so we can execute tests on the container&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; jest.config.js /usr/src/app/jest.config.js&lt;/span&gt;

&lt;span class="c"&gt;# Copy config .env file&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./.env_docker /usr/src/app/.env&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "node", "server.js" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;L'image du container Node est décrite ci dessus: on copie les sources du projet, on run un npm install, on expose le port pour Express puis on set les entry points node et server.js&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/m4nu56/6ba37f8659be15ca31f743c165619788#file-gitlab-ci-yml" rel="noopener noreferrer"&gt;github gist gitlab-ci.yml&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker:19.03.1&lt;/span&gt;

&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DOCKER_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp://docker:2375&lt;/span&gt;
  &lt;span class="na"&gt;DOCKER_DRIVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;overlay2&lt;/span&gt;
  &lt;span class="na"&gt;PROJECT_IMAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.domain.com/project-name&lt;/span&gt;
  &lt;span class="na"&gt;PROJECT_RELEASE_IMAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$PROJECT_IMAGE:$CI_COMMIT_REF_NAME&lt;/span&gt;
  &lt;span class="na"&gt;DOCKER_TLS_CERTDIR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker:19.03.1-dind&lt;/span&gt;

&lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# login sur le docker registry&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "$CI_JOB_TOKEN" | docker login -u gitlab-ci-token --password-stdin registry.domain.com&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;

&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# création d'un network pour lier mongo et nodejs&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker network rm network-project || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="s"&gt; &amp;amp;&amp;amp; docker network create network-project*&lt;/span&gt;
    &lt;span class="c1"&gt;# init et run du container mongo&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker run -d --name mongo-db --network network-project -v /builds/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js -e MONGO_INITDB_DATABASE='project' -e MONGO_INITDB_ROOT_USERNAME='root' -e MONGO_INITDB_ROOT_PASSWORD='root' mongo:4.0&lt;/span&gt;
    &lt;span class="c1"&gt;# build de l'image node (les sources sont copiées et npm install est fait)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build --pull --network network-project -t $PROJECT_RELEASE_IMAGE node/&lt;/span&gt;
    &lt;span class="c1"&gt;# démarrage du container avec les variables d'environnement de la ci&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker run -d --env-file node/.env_tests --network network-project --name nodejs_test $PROJECT_RELEASE_IMAGE&lt;/span&gt;
    &lt;span class="c1"&gt;# exécution des tests&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker exec nodejs_test npm test&lt;/span&gt;
    &lt;span class="c1"&gt;# cleanup kill containers and network&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker stop nodejs_test &amp;amp;&amp;amp; docker rm nodejs_test&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker stop mongo-db || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker network rm network-project&lt;/span&gt;

&lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# build de l'image&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build --pull -t $PROJECT_RELEASE_IMAGE node/&lt;/span&gt;
    &lt;span class="c1"&gt;# déploiement de l'image sur le repository&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $PROJECT_RELEASE_IMAGE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous utilisons une image docker comme base pour l’exécution de notre pipeline. Cela nous permet de pouvoir simuler notre stack complète comme sur un vrai serveur avec la base de donnée et l’API afin d’exécuter les tests d’intégration.&lt;br&gt;
Vous remarquerez que nous faisons un docker login sur notre registry en before_script afin de pouvoir déployer notre image tout à l’heure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Etapes du stage de test:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Création d’un network&lt;/li&gt;
&lt;li&gt;Démarrage container mongo&lt;/li&gt;
&lt;li&gt;Build et démarrage du container nodejs&lt;/li&gt;
&lt;li&gt;Exécution des tests depuis le container nodejs&lt;/li&gt;
&lt;li&gt;Nettoyage et rm de tous les containers et network créés&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Etapes du stage release:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build de l’image node&lt;/li&gt;
&lt;li&gt;Déploiement sur le registry&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  2. Déploiement
&lt;/h2&gt;

&lt;p&gt;Sur notre serveur nous utilisons le docker-compose suivant afin de déployer la stack mongo + node:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/m4nu56/a035a75cb1af7b1c8b27a798ba30e5b2#file-docker-compose-yml" rel="noopener noreferrer"&gt;github gist docker-compose.yml&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.4"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mongo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo:4.0&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;27017:27017&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
      &lt;span class="na"&gt;TZ&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Europe/Paris&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/mnt/docker-volume/project/data:/data/db&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/mnt/docker-volume/project/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;project-network&lt;/span&gt;

  &lt;span class="na"&gt;nodejs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="c1"&gt;# on utilise l'image déployée sur notre registry &lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.domain.com/project:develop&lt;/span&gt;
    &lt;span class="na"&gt;working_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/usr/src/app&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3000:3000&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;project-network&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;project-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce qu’il faut surtout retenir ici c’est que pour le service node nous utilisons l’image qui est déployée sur notre registry privée:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="c1"&gt;# on utilise l'image déployée sur notre registry &lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.domain.com/project:develop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Automatisation de la mise à jour du container
&lt;/h2&gt;

&lt;p&gt;Nous allons utiliser un service appelé Ouroboros: ce service va pouvoir surveiller périodiquement les containers qu’on lui précisera afin de les mettre à jour avec la version la plus récente de l’image sur le registry.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fautomatiser-deploiement-gitlab-ouroboros%2Fouroboros_logo.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fautomatiser-deploiement-gitlab-ouroboros%2Fouroboros_logo.jpeg" alt="Ouroboros logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/m4nu56/99211267da89a816a51d4b1295993c4a#file-docker-compose-yml" rel="noopener noreferrer"&gt;github gist docker-compose.yml&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ouroboros&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ouroboros&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ouroboros&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pyouroboros/ouroboros&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CLEANUP=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;INTERVAL=300&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;LOG_LEVEL=info&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SELF_UPDATE=false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;TZ=Europe/Paris&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MONITOR=nodejs&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DRY_RUN=false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;NOTIFIERS=https://hooks.slack.com/services/{tokenA}/{tokenB}/{tokenC}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SKIP_STARTUP_NOTIFICATIONS=true&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./config.json:/root/.docker/config.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous lui précisons de surveiller le container ‘nodejs’: MONITOR=nodejs. Afin qu’il puisse se connecter à notre registry vous pouvez faire un docker login registry.domain.com depuis votre serveur et récupérer le fichier ‘config.json’ qui a été créé dans &lt;code&gt;/root/.docker/config.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/m4nu56/3af555c0e6311c893ebf555ac1f78a9b#file-config-json" rel="noopener noreferrer"&gt;github gist config.json&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"auths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"registry.domain.com"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tokenXXXX"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"HttpHeaders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Docker-Client/18.09.0 (linux)"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Je vous encourage a aller voir la documentation d’Ouroboros afin de pouvoir tester le bon fonctionnement de votre config avant de démarrer le service sur votre serveur.&lt;br&gt;
Ouroboros utilise un service de notification très complet appelé &lt;strong&gt;Apprise&lt;/strong&gt; qui permet une intégration avec une grande variété de services. Vous voyez ici une implémentation avec &lt;strong&gt;Slack&lt;/strong&gt;. Une notification est envoyée sur le channel configuré pour prévenir dès qu’un container à été mis à jour&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fautomatiser-deploiement-gitlab-ouroboros%2Fouroboros_slack.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fautomatiser-deploiement-gitlab-ouroboros%2Fouroboros_slack.png" alt="Ouroboros slack notification"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tip&lt;/em&gt;: Nous avons dû upgrade la version de docker de notre serveur afin de pouvoir faire fonctionner le service, en 18.x une erreur lors de l’authentification sur notre registry empêchait le bon fonctionnement.&lt;/p&gt;

&lt;p&gt;Une fois démarrée le service avec un &lt;code&gt;docker-compose up -d&lt;/code&gt; vous pourrez voir le système mettre à jour votre container suivant l’interval que vous aurez configurer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fautomatiser-deploiement-gitlab-ouroboros%2Fouroboros_work.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fautomatiser-deploiement-gitlab-ouroboros%2Fouroboros_work.png" alt="Ouroboros working"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;J’espère que cette solution simple pour mettre en place du CI+CD avec Gitlab vous encouragera a l’essayer sur vos projets.&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>docker</category>
      <category>cd</category>
    </item>
    <item>
      <title>Jersey Injection Dependency example with HK2</title>
      <dc:creator>Emmanuel Balpe</dc:creator>
      <pubDate>Wed, 27 May 2020 20:23:39 +0000</pubDate>
      <link>https://dev.to/m4nu56/jersey-injection-dependency-example-with-hk2-455f</link>
      <guid>https://dev.to/m4nu56/jersey-injection-dependency-example-with-hk2-455f</guid>
      <description>&lt;p&gt;Example of a Jersey project using the dependency injection framework HK2 to inject logged user into the application via a custum annotation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://eclipse-ee4j.github.io/jersey/"&gt;Jersey&lt;/a&gt; is a Java Framework that is commonly used to help generate REST Api.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://javaee.github.io/hk2/"&gt;HK2&lt;/a&gt; is a lightweight framework which allow Inversion of Control (IoC) and dependency injection (DI)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S05vqOLL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://m4nu56.dev/images/jersey-injection-dependency-hk2/jersey.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S05vqOLL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://m4nu56.dev/images/jersey-injection-dependency-hk2/jersey.png" alt="jersey"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Starting a new Jersey Project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Generate a project from &lt;a href="https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/getting-started.html#new-from-archetype"&gt;Maven Archetype&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 \
-DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \
-DgroupId=com.example -DartifactId=simple-service -Dpackage=com.example \
-DarchetypeVersion=2.29.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Business Logic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add some business logic to make this test more interesting in com.example.business:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User&lt;/li&gt;
&lt;li&gt;UserSvc&lt;/li&gt;
&lt;li&gt;UserDao&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Automatically bind classes to their implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We want to be able to do Dependency Injection of our services. We need to register a &lt;code&gt;AbstractBinder&lt;/code&gt; to our jersey app that will automatically match the given injected class with the implementation.&lt;/p&gt;

&lt;p&gt;Create a class &lt;code&gt;ApplicationBinder&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Provider&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationBinder&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractBinder&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JustInTimeServiceResolver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JustInTimeInjectionResolver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And register it with our application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;resourceConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApplicationBinder&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And an another class &lt;strong&gt;&lt;code&gt;JustInTimeServiceResolver&lt;/code&gt;&lt;/strong&gt; that will handle the automatic binding of services like Google Guice would do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JustInTimeServiceResolver&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;JustInTimeInjectionResolver&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Inject&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ServiceLocator&lt;/span&gt; &lt;span class="n"&gt;serviceLocator&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;justInTimeResolution&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Injectee&lt;/span&gt; &lt;span class="n"&gt;injectee&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;requiredType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;injectee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRequiredType&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;injectee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRequiredQualifiers&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;requiredType&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requiredClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;)&lt;/span&gt; &lt;span class="n"&gt;requiredType&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// IMPORTANT: check the package name, so we don't accidentally preempt other framework JIT resolvers&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requiredClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.example"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActiveDescriptor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;descriptors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ServiceLocatorUtilities&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addClasses&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceLocator&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requiredClass&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;descriptors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;From there we can already make use of the dependency injection framework.&lt;br&gt;
Create a new endpoint to get the list of users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GET&lt;/span&gt;
&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Produces&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getUsers&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;userSvc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And in &lt;code&gt;MyResource&lt;/code&gt; inject the UserSvc service this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Inject&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;UserSvc&lt;/span&gt; &lt;span class="n"&gt;userSvc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can do the same in the &lt;code&gt;UserSvc&lt;/code&gt; class with the field &lt;code&gt;UserDao&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Inject&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;UserDao&lt;/span&gt; &lt;span class="n"&gt;userDao&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note that Injected resources need to have a no args constructor.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Wen can test ou API is responding well with the list of our users this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testGetUsers&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myresource/users"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GenericType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;()&lt;/span&gt; &lt;span class="o"&gt;{});&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Extract the user from the JWT Token and make it available for injection with a custom annotation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now we would like to be able to get information about the user that send requests to our API and use it where needed in our application.&lt;br&gt;
Our API will have secured access managed by a JWT Token.&lt;br&gt;
We will create a &lt;code&gt;@PreMatching&lt;/code&gt; Jersey filter that will be run before any request and that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;will validate the token that should be in &lt;code&gt;Authorization&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;will create a user from the claims in the extracted token&lt;/li&gt;
&lt;li&gt;will put the user in the SecurityContext so that it can be accessed by the HK2 framework
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PreMatching&lt;/span&gt;
&lt;span class="nd"&gt;@Priority&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Priorities&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AUTHENTICATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PreMatchingCurrentUserFilter&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ContainerRequestFilter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContainerRequestContext&lt;/span&gt; &lt;span class="n"&gt;requestContext&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Jws&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Claims&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;jws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AuthorizationValidator&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;AppSecurityContext&lt;/span&gt; &lt;span class="n"&gt;appSecurityContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AppSecurityContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;(),&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;User&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;jws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBody&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"login"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;jws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBody&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"compte"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;),&lt;/span&gt;
                    &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;requestContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSecurityContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appSecurityContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ignored&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And register the filter with our application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;resourceConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PreMatchingCurrentUserFilter&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next we will create an new custom annotation that will be used to inject the user anywhere we want.&lt;br&gt;
First create the new annotation, available in Field and in Constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Retention&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RetentionPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RUNTIME&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Target&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;&lt;span class="nc"&gt;ElementType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FIELD&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ElementType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CONSTRUCTOR&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nd"&gt;@interface&lt;/span&gt; &lt;span class="nc"&gt;CurrentUser&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;then a new &lt;code&gt;InjectionResolver&lt;/code&gt;, where we will bind the &lt;code&gt;User&lt;/code&gt; class with the new annotation.&lt;br&gt;
We can access the &lt;code&gt;SecurityContext&lt;/code&gt; using dependency injection and find the User we injected in the &lt;code&gt;@PreMatching&lt;/code&gt; earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CurrentUserInjectionResolver&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;InjectionResolver&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CurrentUser&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="n"&gt;javax&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Provider&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SecurityContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;securityContextProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

   &lt;span class="nd"&gt;@Inject&lt;/span&gt;
   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CurrentUserInjectionResolver&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;javax&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Provider&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SecurityContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;securityContextProvider&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;securityContextProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;securityContextProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;

   &lt;span class="nd"&gt;@Override&lt;/span&gt;
   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Injectee&lt;/span&gt; &lt;span class="n"&gt;injectee&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ServiceHandle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;injectee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRequiredType&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;securityContextProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getUserPrincipal&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And thats’it, wen can access the user using an injection like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@CurrentUser&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;or in a constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@CurrentUser&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;UserSvc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If user was found in the JWT Token if will be accessible here.&lt;/p&gt;




&lt;p&gt;Thank’s for reading, you can find the sample project on my &lt;a href="https://github.com/m4nu56/jersey-hk2"&gt;Github account&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jersey</category>
      <category>java</category>
      <category>injection</category>
      <category>di</category>
    </item>
    <item>
      <title>Run multiple Postman Collection in parallel — Stress-Tests</title>
      <dc:creator>Emmanuel Balpe</dc:creator>
      <pubDate>Wed, 27 May 2020 20:22:12 +0000</pubDate>
      <link>https://dev.to/m4nu56/run-multiple-postman-collection-in-parallel-stress-tests-41o0</link>
      <guid>https://dev.to/m4nu56/run-multiple-postman-collection-in-parallel-stress-tests-41o0</guid>
      <description>&lt;p&gt;A known limitation of the Postman Collection Runner is that it can only execute collection in a consecutive way. This is just a simple implementation of the solution explained in this&lt;a href="https://stackoverflow.com/a/41181892/2042761" rel="noopener noreferrer"&gt; StackOverflow conversation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create your Postman Collection and corresponding tests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here I needed to attack first &lt;code&gt;/api/persons&lt;/code&gt; to get the list of persons ids.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fmultiple-postman-collection-stress-test%2Fcollection.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fmultiple-postman-collection-stress-test%2Fcollection.png" alt="/api/persons"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then &lt;code&gt;/api/persons/:id&lt;/code&gt; for each person in the list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fmultiple-postman-collection-stress-test%2Fcollection2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fmultiple-postman-collection-stress-test%2Fcollection2.png" alt="/api/persons/:id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To do that I used the &lt;code&gt;postman.setNextRequest()&lt;/code&gt; tricks that specify the next request that will be executed in the collection run. And in each run I get the last personId and pop() it from the array in environment variables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Export your collection and the environment variables&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fmultiple-postman-collection-stress-test%2Fexport_collection.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fmultiple-postman-collection-stress-test%2Fexport_collection.png" alt="/api/persons/:id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And save the files in a &lt;code&gt;postman/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create the new npm project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Simply run &lt;code&gt;npm init -y&lt;/code&gt; and install the 3 dependencies: &lt;code&gt;npm i async newman path&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"newman_parallel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node index.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"keywords"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"async"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"newman"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.5.6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.12.7"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The script !&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s kind of self explanatory, update the path for your postman collection and environment, specify the number of concurrent run you want to launch with the constant &lt;code&gt;PARALLEL_RUN_COUNT&lt;/code&gt; and execute the script with &lt;code&gt;npm start&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;async&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newman&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newman&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PARALLEL_RUN_COUNT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parametersForTestRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postman/postman_collection.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// your collection&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postman/localhost.postman_environment.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;//your env&lt;/span&gt;
    &lt;span class="na"&gt;reporters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cli&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;parallelCollectionRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;newman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parametersForTestRun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;commands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;PARALLEL_RUN_COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parallelCollectionRun&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Runs the Postman sample collection thrice, in parallel.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parallel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;failures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ran successfully.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fmultiple-postman-collection-stress-test%2Fnewman.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fmultiple-postman-collection-stress-test%2Fnewman.png" alt="newman"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here you can play around with your api and build some stress tests to see how it handles the pressure.&lt;br&gt;
You can find the code in the &lt;a href="https://github.com/m4nu56/newman-parallel-run" rel="noopener noreferrer"&gt;Github repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>postman</category>
      <category>test</category>
      <category>ci</category>
    </item>
    <item>
      <title>Build a simple CLI tool to simplify your day-to-day tasks</title>
      <dc:creator>Emmanuel Balpe</dc:creator>
      <pubDate>Wed, 27 May 2020 20:14:52 +0000</pubDate>
      <link>https://dev.to/m4nu56/build-a-simple-cli-tool-to-simplify-your-day-to-day-tasks-519</link>
      <guid>https://dev.to/m4nu56/build-a-simple-cli-tool-to-simplify-your-day-to-day-tasks-519</guid>
      <description>&lt;p&gt;It's a common task in my current position to download and restore client schema on my local postgres to debug it. So I decided to build a small CLI tool to help me.&lt;/p&gt;




&lt;p&gt;Fully inspired (to not say copy/pasted) from this post by &lt;a href="https://www.sitepoint.com/author/lwhite" rel="noopener noreferrer"&gt;Lukas White&lt;/a&gt; and &lt;a href="https://www.sitepoint.com/author/jhibbard" rel="noopener noreferrer"&gt;James Hibbard&lt;/a&gt;:&lt;br&gt;
&lt;strong&gt;&lt;a href="https://www.sitepoint.com/javascript-command-line-interface-cli-node-js/" rel="noopener noreferrer"&gt;Build a JavaScript Command Line Interface (CLI) with Node.js&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;I type &lt;code&gt;pg-schema-restorer&lt;/code&gt; in my terminal and the following appears:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Frestore-pg-schema-cli-tool%2Fpg-schema-restorer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Frestore-pg-schema-cli-tool%2Fpg-schema-restorer.png" alt="pg-schema-restorer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tool look for the .backup files in my folders and subfolders of &lt;code&gt;~/Documents&lt;/code&gt; and &lt;code&gt;~/Downloads&lt;/code&gt; and ask me to select one.&lt;/p&gt;

&lt;p&gt;Once chosen it asks me the name of the schema (try to deduce it from the filename)&lt;/p&gt;

&lt;p&gt;After that I need to enter the database credentials. Only for the first time because it's saved into a local storage thank's to &lt;code&gt;configstore&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Frestore-pg-schema-cli-tool%2Fcredentials.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Frestore-pg-schema-cli-tool%2Fcredentials.png" alt="pg-schema-restorer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And from here the given schema name will be dropped and the backup restored.&lt;br&gt;
I added some flavours in the script I use at my job to automatically reference the new schema into the public table with some more queries but you get the idea.&lt;/p&gt;
&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;I won't go into too much details because the post I referenced earlier is very detailled.&lt;br&gt;
The following is just to clear the console and display the ASCII art because I can. It's using &lt;code&gt;chalk&lt;/code&gt; to colorize the output, &lt;code&gt;figlet&lt;/code&gt; for the art and clear&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;chalk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;figlet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;textSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PG Schema Restorer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;horizontalLayout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;full&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To look for the backup in a directory and all it's subdirectories I'm using a library called &lt;code&gt;klawSync&lt;/code&gt; you can apply a filter to it so it will already find exactly what you're looking for&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;findBackupsInPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;klawSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;nodir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;traverseAll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;basename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;backup$/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then use this list of backups to &lt;code&gt;inquirer&lt;/code&gt; that will display a beautiful prompt with it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;askBackupFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filelist&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;questions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;backupPath&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Select the backup you want to restore:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filelist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;inquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I want the database credentials to be stored so that users don't have to type it anytime but in the same time they should be able to reset it if they need to restore on another database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// retrieve databaseCredentials&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Configstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;schemaRestorer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;databaseCredentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;databaseCredentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;inquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;askDatabaseCredentials&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;databaseCredentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Using these postgres credentials: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?user=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;password=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;confirmDbCredentials&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;inquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;askConfirmDatabaseCredentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;confirmDbCredentials&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;databaseCredentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;inquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;askDatabaseCredentials&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;databaseCredentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;databaseCredentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For that I'm using &lt;code&gt;configstore&lt;/code&gt; which simply store my configs into a json file in the npm user directory. I ask the user if he wants to continue with the saved credentials if any.&lt;/p&gt;

&lt;p&gt;To drop the existing schema I'm using &lt;code&gt;pg&lt;/code&gt; ORM which is really easy to manipulate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;databaseCredentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`DROP SCHEMA IF EXISTS &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; CASCADE`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally to restore the schema I'm using the system &lt;code&gt;pg_restore&lt;/code&gt; thank's to the &lt;code&gt;exec&lt;/code&gt; command in the npm package &lt;code&gt;child_process&lt;/code&gt;&lt;br&gt;
To simplify my code I've encapsulated it into a Promise so that I can await it's result before continuing the program.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`pg_restore -d &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;databaseCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -U &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;databaseCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -C -w &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;backupPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error restoring the backup: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Backup '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;backupPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' has been restored...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If you're interested in reading the code I've made it available on &lt;a href="https://github.com/m4nu56/pg-schema-restorer" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to comment it, correct it, test it etc.. I've only been using it myself on my Ubuntu setup so for sure it needs to be somehow adapted for MacOS and Windows.&lt;/p&gt;

&lt;p&gt;You can also follow me on Twitter if you'd like, I share mostly geeky coding stuff: &lt;a href="https://twitter.com/m4nu56" rel="noopener noreferrer"&gt;https://twitter.com/m4nu56&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>npm</category>
      <category>cli</category>
    </item>
    <item>
      <title>Automatiser les tests d’intégration de votre API avec Postman et Newman</title>
      <dc:creator>Emmanuel Balpe</dc:creator>
      <pubDate>Wed, 27 May 2020 20:13:19 +0000</pubDate>
      <link>https://dev.to/m4nu56/automatiser-les-tests-d-integration-de-votre-api-avec-postman-et-newman-15gj</link>
      <guid>https://dev.to/m4nu56/automatiser-les-tests-d-integration-de-votre-api-avec-postman-et-newman-15gj</guid>
      <description>&lt;p&gt;Les tests sont une composante primordiale de n'importe quel développement et il est aujourd'hui très commun de retrouver un grand nombre de tests unitaire et de tests d'intégration avec nos projets. Mais il est moins fréquent de retrouver des tests qui s'exécutent directement sur l'application déployée. Avec les outils dont nous disposons à ce jour il est désormais très simple de mettre en place une simple batterie de tests qui permettent de reproduire un scénario d'utilisation de nos APIs et surtout de les automatiser afin qu'ils fassent partie intégrante de la CI de vos projets.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Création de la collection de requêtes avec Postman&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Commencez par créer une nouvelle collection avec Postman. Vous pouvez ensuite organiser vos différentes requêtes en dossier si vous le souhaitez.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_0q90Hga--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/collection.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_0q90Hga--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/collection.png" alt="Postman collection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tip&lt;/em&gt;: vous pouvez ajouter un token d'authentification global pour toutes vos requêtes depuis la popup d'édition de la collection (ou d'un dossier). Il suffit ensuite de préciser dans l'édition de chaque requête qu'il doit &lt;code&gt;Inherit authentication from parent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9jfsyMtA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/authentication.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9jfsyMtA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/authentication.png" alt="authorization"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tip&lt;/em&gt;: Vous pouvez créer des environnements afin de pouvoir facilement tester votre api en local et sur le serveur d'intégration ou de production par exemple:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i3__pYbN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/environment.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3__pYbN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/environment.png" alt="environments"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cliquez sur &lt;code&gt;Manage environment&lt;/code&gt; sur l'icône en haut à droite de Postman&lt;/li&gt;
&lt;li&gt;Créer les environnements que vous voulez&lt;/li&gt;
&lt;li&gt;Ajouter des variables d'environnement tel que &lt;code&gt;host&lt;/code&gt; qui pourra être &lt;code&gt;http://localhost:3000&lt;/code&gt; en local et &lt;code&gt;https://integ.mydomain.com&lt;/code&gt; pour votre serveur d'intégration.&lt;/li&gt;
&lt;li&gt;Vous pouvez utiliser ces variables n'importe où dans l'écriture de vos requêtes en utilisant la syntaxe &lt;code&gt;{{host}}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dHbIF0tX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/body.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dHbIF0tX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/body.png" alt="body query"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;L'écriture des requêtes est ensuite très simple vous trouverez plus d'information pour les écrire depuis la documentation officielle de Postman: &lt;a href="https://learning.getpostman.com/docs/postman/sending-api-requests/requests/"&gt;postman documentation&lt;/a&gt;&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;L'écriture des tests avec Postman&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Vous aurez remarqué un onglet Tests qui est présent dans la vue requête. Les tests que vous écrivez sont exécutés à chaque fois que vous lancez les requêtes après que la réponse ait été reçue.&lt;/p&gt;

&lt;p&gt;L'écriture des tests se fait en javascript, vous trouverez des snippets très pratique dans un bloc à droite de l'éditeur.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--em-YHo7_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/tests.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--em-YHo7_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/tests.png" alt="tests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La syntaxe d'écriture des tests avec Postman est très verbeuse et intuitive. Elle ressemble beaucoup aux librairies d'assertion communément utilisées en javascript. On peut facilement tester le statut de la réponse de cette manière:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qjra_p3O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/test_example.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qjra_p3O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/test_example.png" alt="test example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On peut aussi tester les données reçues de cette manière:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3Fj7gdtF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/test_example2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3Fj7gdtF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/test_example2.png" alt="test example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Là où ça devient très intéressant c'est avec la possibilité de stocker des variables d'environnements qui sont conservées entre les requêtes. On voit qu'ici on a conservé la propriété &lt;code&gt;_id&lt;/code&gt; reçu de la réponse dans une variable &lt;code&gt;buildingId&lt;/code&gt;. De la même manière que pour la variable &lt;code&gt;host&lt;/code&gt; mis en place avant on peut utiliser cette variable dans les autres requêtes &lt;code&gt;{{buildingId}}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QrtTq3ZO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/query_params.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QrtTq3ZO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/query_params.png" alt="query params"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--scEC9HKb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/body_params.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--scEC9HKb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/body_params.png" alt="body params"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Et on peut également écrire un scénario d'exécution en précisant la requête à exécuter ensuite à la fin des tests:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9hiPabda--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/automatiser-integration-postman/scenarize_tests.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9hiPabda--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/automatiser-integration-postman/scenarize_tests.png" alt="scenarize tests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La requête qui sera exécutée en suivant est celle dont le nom est &lt;code&gt;/buildings GET&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tip&lt;/em&gt;: Vous pouvez rappeler en boucle une même requête en mettant le nom de la même requête et en conditionnant l'appel d'une nouvelle requête en fonction de la réponse.&lt;/p&gt;

&lt;p&gt;Vous pouvez ensuite lancer l'exécution de tous les tests en cliquant sur la collection &amp;gt; "Collection runner"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sKhjyJYt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/collection2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sKhjyJYt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/collection2.png" alt="collection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vous pouvez sélectionner l'environnement avec lequel lancer les tests et le nombre d'itération que vous voulez lancer.&lt;br&gt;
Si vous cliquer sur "Keep variable values" les variables d'environnement créées pendant l'exécution des tests seront conservées. C'est très util pour débugger les tests en erreur.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Export des tests et exécution en CLI&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Vous allez pouvoir lancer l'exécution de votre collection en command line grâce à un outil développé par Postman appelé (Newman)[&lt;a href="https://www.npmjs.com/package/newman"&gt;https://www.npmjs.com/package/newman&lt;/a&gt;]&lt;br&gt;
Vous pouvez l'installer dans votre projet avec npm s'il s'agit d'un projet qui gère ses dépendances de cette manière:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm i newman --save-dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Vous pouvez exporter votre collection en cliquant sur la collection &amp;gt; Export&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SlRs73Tq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/export_collection.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SlRs73Tq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/export_collection.png" alt="export collection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vous aurez ensuite un fichier .json qui pourra être exécuté avec Newman.&lt;br&gt;
Vous aurez également besoin d'exporter vos environnements:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KKKd5J-s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/environment_export.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KKKd5J-s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/environment_export.png" alt="export environment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;L'exécution des tests avec newman se fait ensuite simplement avec:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;newman run MyCollection.postman_collection.json -environment localhost.postman_environment.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FrfaRelg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/newman.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FrfaRelg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/newman.png" alt="newman"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vous pouvez ensuite vous simplifier la vie en ajoutant un script au package.json de votre projet:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_7LEOPKl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/package_scripts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_7LEOPKl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/package_scripts.png" alt="package script"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Afin de pouvoir lancer les tests simplement: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run integration-tests-local&lt;/code&gt;&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Intégration dans la pipeline Gitlab&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;L'intérêt de ces tests d'intégration est de pouvoir les exécuter de manière automatique après chaque déploiement afin de pouvoir être alerté au plus vite d'une possible régression et pouvoir revenir à une version précédente ou fixer le problème.&lt;br&gt;
Avec gitlab le plus simple est d'utiliser l'image officielle de Postman qui a déjà newman d'installée: &lt;code&gt;postman/newman_alpine33&lt;/code&gt;&lt;br&gt;
Exemple d'un nouveau stage d'intégration sur notre pipeline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jWw9vzMp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/gitlab_job.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jWw9vzMp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/gitlab_job.png" alt="gitlab job"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tip&lt;/em&gt;: Nous utilisons gitlab en installation docker et la gestion des certificats est un vrai enfer du coup je n'ai jamais réussi à lancer les tests de ma collection qui attaquaient le serveur d'intégration non SSL. J'ai fini par mettre un certificat letsencrypt afin d'avoir une url en HTTPS.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tip&lt;/em&gt;: Nous exécutons ces tests de manière décalée de 5 minutes car notre procédure de déploiement est asynchrone et je n'ai pas de retour dans gitlab lorsque c'est terminé. Si vous utilisez une autre solution vous pouvez les lancer directement après le déploiement.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Exécution périodique&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nous utilisons ici la fonctionnalité de &lt;strong&gt;Scheduled Pipeline&lt;/strong&gt; de Gitlab et afin de ne pas relancer le build et deploy des images on peut simplement ajouter une règle d'exclusion sur les autres stages de la pipeline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2Cjj_tpz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/scheduled_gitlab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Cjj_tpz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/scheduled_gitlab.png" alt="gitlab scheduled"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Puis dans Gitlab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V-s1S94V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/scheduled_gitlab2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V-s1S94V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/scheduled_gitlab2.png" alt="gitlab scheduled"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Et de cette manière seul la partie test d'intégration est exécutée lors de l'exécution de cette pipeline programmée:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hknw5lxO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/scheduled_gitlab3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hknw5lxO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.m4nu56.now.sh/images/automatiser-integration-postman/scheduled_gitlab3.png" alt="gitlab scheduled"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;J'espère que cet article vous aura plu et qu'il vous aura donné envie de vous intéresser à mettre en place ce genre de scénario de test.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Prettier to format your Java codebase</title>
      <dc:creator>Emmanuel Balpe</dc:creator>
      <pubDate>Wed, 27 May 2020 20:12:58 +0000</pubDate>
      <link>https://dev.to/m4nu56/prettier-to-format-your-java-codebase-3f7i</link>
      <guid>https://dev.to/m4nu56/prettier-to-format-your-java-codebase-3f7i</guid>
      <description>&lt;p&gt;So we've all been using &lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; in our JavaScript projects or at least have heard about it.&lt;/p&gt;

&lt;p&gt;Prettier is an opinionated code formatter that you can plug to your IDE or in a pre-commit hook to automatically format your code. &lt;/p&gt;

&lt;p&gt;Optionated because it comes with formatting rules by default and very few options. Although their seems to be a lot more today than in my memory.. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fjava-prettier-formatting%2Fprettier.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fm4nu56.dev%2Fimages%2Fjava-prettier-formatting%2Fprettier.png" alt="Prettier"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prettier can also format a lot of other languages than JavaScript: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSX&lt;/li&gt;
&lt;li&gt;Flow&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;Vue&lt;/li&gt;
&lt;li&gt;Angular&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;Less&lt;/li&gt;
&lt;li&gt;SCSS&lt;/li&gt;
&lt;li&gt;Markdown&lt;/li&gt;
&lt;li&gt;MDX (used in this blog btw)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And some community plugins are available for other programming languages like PHP, PostgreSQL.. and &lt;strong&gt;Java&lt;/strong&gt;! &lt;/p&gt;

&lt;h2&gt;
  
  
  Prettier-java plugin
&lt;/h2&gt;

&lt;p&gt;The community developped a plugin for java that uses node under the hood to format your java classes. &lt;br&gt;
Plugin is available on &lt;a href="https://github.com/jhipster/prettier-java" rel="noopener noreferrer"&gt;github&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;It's really easy to manipulate: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm install -g prettier prettier-plugin-java
prettier --write "**/*.java"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;But that's not very convenient. We don't want to ask every developer that will work on the codebase to install the plugin and run it itself. &lt;/p&gt;

&lt;h2&gt;
  
  
  Maven plugin for the prettier-java plugin
&lt;/h2&gt;

&lt;p&gt;Thanksfully a maven plugin has been developped by HubSpot. It's also available on &lt;a href="https://github.com/HubSpot/prettier-maven-plugin" rel="noopener noreferrer"&gt;github&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The plugin comes bundled with node, prettier and the prettier-java plugin so developers don't have to install it themselves and it can be executed inside a maven goal. &lt;/p&gt;

&lt;p&gt;In your pom.xml config file simply add the following plugin: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.hubspot.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;prettier-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.7&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;printWidth&amp;gt;&lt;/span&gt;90&lt;span class="nt"&gt;&amp;lt;/printWidth&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;tabWidth&amp;gt;&lt;/span&gt;2&lt;span class="nt"&gt;&amp;lt;/tabWidth&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;useTabs&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/useTabs&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ignoreConfigFile&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/ignoreConfigFile&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ignoreEditorConfig&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/ignoreEditorConfig&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;validate&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;write&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;      
    &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So that each time the validate phase is run the plugin will be executed in write mode and will reformat all the project files.&lt;/p&gt;

&lt;p&gt;You can also execute the plugin on the command line if you are in that sort of things: &lt;br&gt;
&lt;code&gt;mvn prettier:check&lt;/code&gt; or &lt;code&gt;mvn prettier:write&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;We've only played with the 4 following configuration options: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;printWidth&lt;/code&gt; = 120&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tabWidth&lt;/code&gt; = 4 &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useTabs&lt;/code&gt; = true&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;endOfLine&lt;/code&gt; = crlf&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The other options available seem to not apply for Java language. &lt;/p&gt;

&lt;p&gt;We're using a lot of enums with attributes in our projets and prettier formatting for those is terrible so I suggest you encapsulate the code you don't want prettier to format with the comments: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;// formatter:off&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and &lt;/p&gt;

&lt;p&gt;&lt;code&gt;// formatter:on&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Like so: &lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;FieldDb&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="c1"&gt;// @formatter:off&lt;/span&gt;&lt;br&gt;
  &lt;span class="no"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;        &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;           &lt;span class="nc"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BIGINT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;    &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;  &lt;span class="no"&gt;MODE_SEL_INSERT&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;&lt;br&gt;
  &lt;span class="no"&gt;ID_COMPTE&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"id_compte"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;    &lt;span class="nc"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BIGINT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;    &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;  &lt;span class="no"&gt;MODE_SEL_INS_UP&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;&lt;br&gt;
  &lt;span class="no"&gt;ID_CENTRE&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"id_centre"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;    &lt;span class="nc"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BIGINT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;    &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;MODE_SEL_INS_UP&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;&lt;br&gt;
    &lt;span class="c1"&gt;// @formatter:on&lt;/span&gt;&lt;br&gt;
  &lt;span class="o"&gt;...&lt;/span&gt; &lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Last step&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;The last step would be to execute the &lt;code&gt;mvn prettier:write&lt;/code&gt; command on a git pre-commit hook. For now we haven't configured it on our projects, when so I'll update the post with our procedure.&lt;/p&gt;

</description>
      <category>java</category>
      <category>formatting</category>
    </item>
  </channel>
</rss>
