mercredi 5 avril 2023

Mettre à jour la sa feature branch avec sa branche parent

Branche de fonctionnalité

Les branches de fonctionnalité sont un bon moyen de ne pas polluer la branche principale. Imaginer que l'utilisateur fait une multitude de commit pour une fonctionné x dans la branche principale entrecoupé de plusieurs autre concernant une fonctionnalité y.

Il peut devenir complexe en cas de problème de revenir en arrière ou bien simplement de suivre le développement.

Une branche de fonctionnalité permet de centralisé tous le développement autour d'une fonctionnalité avant de la mettre dans la branche principale. C'est une branche temporaire

Mettre à jour sa branche

Vous avez une branche de fonctionnalité feature/AddTaxManagement qui se base sur la branche principal par exemple develop.

De nombreuse personne envoie leur changement dans la branche develop.

Retourner dans la branche develop

>git checkout develop

 

 

Ramener les changements

>git fetch -p origin


Fusionner les changements remote en local

>git merge origin/develop


git checkout feature/AddTaxManagement

Fusionner la branche develop avec votre feature branch

>git merge develop

 

Pousser ses modifications dans la branche remote

>git push origin feature/AddTaxManagement

vendredi 10 février 2023

Fetcher les enfants d'une entité

Cet exemple fonctionne sous spring data jpa.

 

Il est possible dans une requête jpql de charger directement les relations enfants d'un objet et d'éviter le problème des requêtes N +1 lorsqu'on a besoin d'accéder à ces enfants.

Imaginons que vous avez une classe

@Entity
@Table
public class Editeur {
   @Id 
   @NotNull 
   private Long idEditeur; 
   @OneToMany(fetch = FetchType.LAZY, mappedBy = "editeur") 
   private List<Livre> livres = new ArrayList<>();
}

Dans le repository de Editeur

@Query(value=""" select e 
                    from Editeur e 
                   JOIN FETCH e.livres """ 
)
List<Editor> findByEditorWithBook();


Si vous désirez de mettre en place le paging, vous devez aussi spécifier une valeur pour le count Query.

@Query(value="""
    select e
    from Editeur e JOIN FETCH e.livres
""",
countQuery= """
    select count(e) 
    from Editor e JOIN FETCH e.licences
""")
Page<Editor> findByEditorWithLicence(Pageable pageable);


Si vous ne spécifier rien pour le countQuery,  vous auriez l'erreur

query specified join fetching, but the owner of the fetched association was not present in the select list

jeudi 15 septembre 2022

Sauvegarde via rsync sur un nas Synology DS220+

 

Introduction

L'article présentera la sauvegarde du répertoire home d'un disque en btrfs sur un NAS Synology DS220+ à l'aide de rsynch

La configuration par défaut a été utilisé pour installer la  distribution OpenSUSE Tumbleweed.Le SSD est donc formaté en BTRFS. 

Activer rsync 

Dans l'interface web de Synology, il faut s'assurer que le service rsync est activé. De plus, puisqu'on lance le rsync depuis un utilisateur externe, il faut aussi activé le rsync account.


Commande rsync

Il sera assumé qu'un utilisateur rsync_userr avec le mot de passe 1234 existe sur le NAS et qu'il est alloué d'utiliser rsync. Ce droit lui peux lui être octroyé dans l'onglet application en éditant un utilisateur.

19.23.31.115 étant l'adresse de votre NAS.

collinm étant le nom de votre utilisateur local.

rsync --progress -avr  --delete --exclude="lost+found" --exclude=".cache" /home/collinm ssh rsync_user@19.23.31.115::Backup/rsync

Cette commande synchronisera votre home local dans le répertoire rsync du disque nommé Backup. A modifier selon votre configuration.

Cette sommande vous demandera de taper votre mot de passe.

rsync sans mot de passe

La commande précédente fonctionne, cependant si vous désirez lancer cette commande automatiquement, cela n'est pas pratique de devoir entrer le mot de passe à chaque fois.

Il existe une méthode simple, mais pas des plus sécuritaire est d'utiliser sshpass.

Il s'agira d'inscrire le mot de passe qui sera passé à rsync par la suite

sshpass -p 'Double2015' rsync --progress -avr  --delete --exclude="lost+found" --exclude=".cache" /home/collinm ssh rsync_user@19.23.31.115::Backup/rsync

Cette commande pourrait être ajouté dans un script bash et lancé automatiquement par cron.

Ce n'est pas sécuritaire, mais cela pourrais être acceptable si c'est pour une utilisation domestique avec aucune autre personne que vous sur votre réseau.

Autrement je vous conseille d'opter pour la création d'une clé ssh.

mercredi 14 septembre 2022

Utiliser les touches du scanner brother

 L'article portera sur l'utilisation des touches de l'imprimante scanner HL-L2390DW. Cependant, cela fonctionne pour de nombreuses imprimantes Brother. La distribution utilisé est OpenSUSE Tumbleweed

Je me suis rendu sur la page de téléchargement pour ce périphérique.

https://support.brother.com/g/b/downloadtop.aspx?c=ca&lang=fr&prod=hll2390dw_us

J'ai pris rpm car c'est le format de package géré par ma distribution.



Ensuite j'ai téléchargé et installé


Scan-key-tool 64bit. 

 

Une fois installé, vérifié si dans les processus, brscan-skey-exe est exécuté.

ps aux | grep brother
collinm  16274  0.0  0.0 1616  2056 pts/2 Sl 10:24 0:00 /opt/brother/scanner/brscan-skey/brscan-skey-exe

Si brscan-skey-exe n'est pas trouvé, lancez le à la main.

Vous devez lancer ce programme pour chaque utilisateur qui voudra utliser le scanner.

Une fois que vous appuyer sur le bouton scan de l'imprimante, il faudra choisir l'utilisateur afin que le fichier est dans le bon répertoire.

Les fichiers sont par défaut dans le répertoire brscan.



mardi 18 janvier 2022

Sauvegarder un formulaire avec un appel ajax avec le framework Thymeleaf

Dans cet exemple qui utilise Thymeleaf, nous allons effectuer un post en ajax. Les données envoyés sont de type json. Ce qui a un impact sur le body devant être envoyé et sur l'annotation à employé dans le controller spring.

 Le formulaire est composé de 3 champs.

<form class="row g-3" id="createAdsForm" th:action="@{/ads}">
<input name="id" type="hidden"/>
<div class="col-md-12">
<label for="adsTitle" class="form-label" th:text="#{ads.title}">Title</label>
<input type="text" class="form-control" id="adsTitle" name="title">
</div>

<div class="col-md-12">
<label for="descriptionAds" class="form-label" th:text="#{ads.description}">Example textarea</label>
<textarea class="form-control" id="descriptionAds" name="description" rows="3"></textarea>
</div>

<div class="col-12">
<button type="submit" class="btn btn-primary" th:text="#{ads.publish}">Publié</button>
</div>

</form>

 

FormDataJson provient du dépot: 

https://github.com/brainfoolong/form-data-json

document.addEventListener('DOMContentLoaded', function () {

let createAdsForm = document.getElementById("createAdsForm");

createAdsForm.addEventListener("submit", function(event) {
event.preventDefault();

let jsonSearchAdsForm = FormDataJson.toJson("#createAdsForm", { arrayify: false , flatList: false});
console.log(jsonSearchAdsForm);
fetch(createAdsForm.action, {
method: "post",
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify(jsonSearchAdsForm)
}).then(result => {
if (result.status != 200) {
throw new Error("Bad Server Response");
}
return result.text();

}).then((content) => {
console.log(content);
}).catch((error) => {
console.log(error);
});

});

});

 Au niveau du controller de Spring

@Controller
public class AdsController {

@ResponseBody
@PostMapping("/ads")
public ResponseEntity saveAds(@RequestBody AdsForm adsForm) {

return new ResponseEntity<>(HttpStatus.CREATED);
}

}

 

 Si les données n'auraient pas été envoyé en json, du côté de spring, l'annotation ModelAttribute aurait dû être utilisé.

vendredi 17 décembre 2021

Le patron décorateur avec Thymeleaf

Dans le précédent article de thymeleaf, nous avons utilisé l'approche par défaut des templates. Nous avions une page principal, product et about. Pour chacune d'elle, nous avions un fragment menu, footer. Tous ces pages avaient une structure similaire. 

Nous allons maintenant utiliser une approche différente. Nous allons utiliser le patron décorateur.

Nous allons utiliser le même exemple, mais avec cette approche.Le header sera mis dans un fragment cette fois.

Les sources sont disponibles sur github

Le fragment header

<head th:fragment="my-header">
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all" href="../../css/main.css" th:href="@{/css/main.css}" />
</head>

 

Layout Decorator

Nous allons créer un fichier main-layout avec les sections similaires au niveau des fragments.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
<head th:replace="fragments/header :: my-header">
<body>
<div th:replace="fragments/menu :: my-menu"></div>
<div class="container" id="mainContent">
<div layout:fragment="content"></div>
</div>
<div th:replace="fragments/footer :: my-footer"></div>
</body>
</html>

Les templates

Le fichier products ressemblera a 

<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="layout/mainLayout">
<head>
<title>Products</title>
</head>
<body>
<div layout:fragment="content">
<table>
<tr>
<td>Id</td>
<td>Name</td>
<td>Description</td>
<td>Price</td>
</tr>
<tr th:each="product: ${products}">
<td th:text="${product.id}"/>
<td th:text="${product.name}"/>
<td th:text="${product.description}"/>
<td th:text="${product.price}"/>
</tr>
</table>
</div>
</body>
</html>
 

Le fichier about ressemblera a 

<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="layout/mainLayout">
<head>
<title>About</title>
</head>
<body>
<div layout:fragment="content">
<p>Grocery exist since 1985</p>
</div>
</body>
</html>

 
Il suffit d'indiquer dans nos templates  dans la section layout:decorate celui qu'on désire utiliser.


Le patron décorateur n'est pas l'approche standard de thymeleaf. Cependant, c'est celle qui demeure la plus efficace pour une application de grande envergure.

jeudi 16 décembre 2021

La puissance des fragments avec Thymeleaf

Nous allons traiter une des particularités de Thymleaf, les fragments. Nous avons vue jusqu'à maintenant les templates.

Nous utiliserons spring boot et la structure du projet sera. Les sources sont disponible ici.

 

 

Un template peut être vue comme une page html, javascript, css. 

Par exemple, dans un fichier index.html

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/main.css" th:href="@{/css/main.css}" />
  </head>

  <body>
    <p th:text="#{home.welcome}">Welcome to our grocery store!</p>
    <p>Today is: <span th:text="${today}">13 February 2011</span></p>
  </body>

</html>
 

Un fragment est une portion de code qui peut être réutilisé dans un autre fragment ou template. Ils peuvent être accessible via un nom, id ou bien par un selector (comme un peu jquery).

Dans un fichier html, par exemple: menu.html

    <ul th:fragment="my-menu">
        <li><a th:href="@{/}">Home</a></li>
        <li><a th:href="@{/products}">Products</a></li>
        <li><a th:href="@{/about}">About</a></li>
    </ul>
    Ce Texte sera affiché aussi.   

Nous pourrions avoir un template pour products et un autre pour about et ce fragment serait utilisé dans chacun d'eux.

Pour utiliser ce fragment dans le fichier index.html

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/main.css" th:href="@{/css/main.css}" />
  </head>

  <body>
    <div th:replace="fragments/menu.html"/> 
    <p>
<span th:text="#{today}">Today</span>
<span th:text="${today}">16 december 2021</span>
</p> </body> </html>

Le nom du fragment a été utilisé, cette approche est utile uniquement si le fichier html contient totalement ce qui nous intéresse, car tous le fichier sera affiché.

Si nous désirons qu'une portion il faut utiliser un sélector, cette opération se fait via les deux points.

Nous devons préciser l'emplacement du fragment / le nom du fichier du fragment :: le nom du fragment.

Ce qui donne dans notre cas

fragments/menu :: my-menu
 

Un autre fragment a été ajouté pour le footer. Le fichier donne avec les fragments.


<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/main.css" th:href="@{/css/main.css}" />
  </head>

  <body>
    <div th:replace="fragments/menu :: my-menu"/> 
    <p>
<span th:text="#{today}">Today</span>
<span th:text="${today}">16 december 2021</span>
</p>
    <div th:replace="fragments/footer :: my-footer"/>  
  </body>

</html>

Pour le fichier about, il pourrait ressembler à

 

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
   <title>Good Thymes Virtual Grocery</title>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
   <link rel="stylesheet" type="text/css" media="all"
href="../../css/main.css" th:href="@{/css/main.css}"/>
</head>
<body>
   <div th:replace="fragments/menu.html"/>
   <p>Grocery exist since 1985</p>
   <div th:replace="fragments/footer.html"/>
</body>
</html>

Pour le fichier products, la partie backend nous retournerais une liste de produit.

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link rel="stylesheet" type="text/css" media="all"
          href="../../css/main.css" th:href="@{/css/main.css}"/>
</head>

<body>
<div th:replace="fragments/menu.html"/>

<table>
    <tr>
        <td>Id</td>
        <td>Name</td>
        <td>Description</td>
        <td>Price</td>
    </tr>
    <tr th:each="product: ${products}">
        <td th:text="${product.id}"/>
        <td th:text="${product.name}"/>
        <td th:text="${product.description}"/>
        <td th:text="${product.price}"/>
    </tr>
</table>
<div th:replace="fragments/footer.html"/>
</body>

</html>
 

C'est assez simple. Il suffit de créer à chaque fois une page html et d'insérer les différents fragments qui nous intéresse. 

Dans notre exemple, la partie head pourrait être un fragment.

Dans une application web complexe, il faut bien cerner les portions qui peuvent être réutiliser et les définir comme des fragments pour éviter la réécriture de code et faciliter la maintenance.

Cette approche d'utiliser les templates est celle par défaut au sein de thymeleaf. Lorsque le site possède plusieurs dizaines, voir centaines de pages, d'autre possibilités sont offerte pour éviter de devoir réécrire les sections redondantes à une multitude de page. Nous la verrons dans un prochain articles.





Quand JPA rencontre ses limites, pensez à jOOQ et au MULTISET

Une seule requête, un graphe d'objets complet — ce n'est pas de la magie, c'est du SQL. La base du problème Dans le monde JPA, q...