JSS et Sitecore Commerce 10

This article in english

Dans une très bonne série d’articles l’année dernière, Jonne Kats et Joost Meijles ont décrit et développé un petit projet pour montrer comment créer une application JSS connectée à Sitecore Commerce 9.

Malheureusement, lorsque j’ai essayé d’installer ce projet avec Sitecore 10, j’ai réalisé que de nombreux changements étaient nécessaires pour obtenir le même résultat. Cet article portera donc sur la modification du code rendu public par Jonne et Joost afin qu’il fonctionne également avec Sitecore 10.

Tout d’abord, j’ai décidé d’installer Sitecore 10 et Sitecore Commerce sur mon environnement, sans Docker, car je voulais avoir un contrôle total sur tous les fichiers de configuration. Ma solution pourrait être “dockerisée”, mais ce n’est pas le but de cet article. J’ai conservé le design et la base de code de Jonne and Joosts, donc tout le mérite leur revient, je me suis juste occupé des ajustements pour Sitecore 10.

Le projet est partagé en deux parties:

  • La partie “headless”, sous le dossier “app”. Cette partie est composée de fichiers Html, Javascript, CSS et média, que vous déployez dans le dossier app/dist (mode déconnecté) ou $sitecore_root/dist (mode connecté). Habituellement, j’ouvre cette partie dans Visual Code
  • La partie serveur, sous le dossier «serveur», qui est un projet Visual Studio et se compose de
    • Un projet proxy, Gateway et Poortwachter, afin que vous n’appeliez pas l’api Sitecore Commerce directement à partir du JS côté client, comme recommandé. Ce projet sera exécuté en tant qu’application autonome, et sera accessible depuis le client avec https: // localhost: 5555
    • Un projet Sitecore, JSS.Commerce, avec quelques ContentResolvers pour notre projet et nos API. Ce code sera déployé sur votre instance Sitecore.
    • Un projet de sérialisation, contenant tous les éléments Sitecore nécessaires. Jonne et Joost ont utilisé Unicorn pour sérialiser, j’ai changé cette partie afin d’utiliser le tout nouveau Sitecore Content Serialization (SCS).

Dans leur série d’article, Jonne et Joost expliquent l’architecture globale du projet et les très bons choix qu’ils ont fait.

Voici les différents changements dont j’avais besoin pour faire fonctionner ce projet avec Sitecore Commerce 10

Dépendances Sitecore 10

Évidemment, je devais mettre à jour toutes les différentes dépendances, afin de cibler Sitecore 10 et Sitecore Commerce 10. Cela pouvait être délicat de mettre à jour les dépendances les unes après les autres, donc j’ai commencé à partir de zéro, avec les projets Sitecore Commerce 10 et JSS 10, puis ajouté leur code à ces nouveaux projets.

JSS

La nouvelle version de JSS a eu un changement majeur de rupture de code concernant l’internationalisation

Ancien code:

import { t } from 'i18next';
(...)
{t('cart-line-remove')}

Nouveau code:

import i18n from 'i18next';
(...)
{i18n.t('cart-line-remove')}

Cette modification doit être effectuée à chaque fois que le package i18next est utilisé.

Projet Proxy

Le plus grand changement a été que Sitecore Commerce 10 n’accepte plus que ses API utilisent un certificat client pour l’authentification entre Sitecore Commerce Connect et le moteur Commerce. Le projet d’origine utilisait un en-tête de requête (header request) X-CommerceEngineCert dans le proxy pour pouvoir parler à Sitecore Commerce 9.

Au lieu de cela, nous avons dû utiliser un jeton (token) de commerce Sitecore pour pouvoir nous authentifier. Ce jeton est enregistré dans le cache mémoire pendant sa validité et est différent pour chaque utilisateur de l’application. J’ai pris le cartId de l’utilisateur (qui est le jeton d’authentification entre l’application et le proxy) comme clé du cache mémoire. Dans ma solution, j’ajoute cet en-tête d’autorisation lorsque la demande est modifiée avec toutes les autres modifications nécessaires.

TokenBuilder tokenBuilder = TokenBuilder.Instance;
string XCToken = await tokenBuilder.GetXCAccessToken(route.TokenUserId());
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", XCToken);

J’avais besoin de définir un nouveau client IdentityServer pour me connecter à Sitecore Commerce Connect via IdentityServer. Le changement a été fait dans $IdentityServer_root/config/production/Sitecore.Commerce.IdentityServer.Host.xml

<JSSClient>
          <ClientId>JSSClient</ClientId>
          <ClientName>JSS client</ClientName>
          <AccessTokenType>0</AccessTokenType>
          <AllowOfflineAccess>true</AllowOfflineAccess>
          <AlwaysIncludeUserClaimsInIdToken>false</AlwaysIncludeUserClaimsInIdToken>
          <AccessTokenLifetimeInSeconds>120</AccessTokenLifetimeInSeconds>
          <IdentityTokenLifetimeInSeconds>120</IdentityTokenLifetimeInSeconds>
          <AllowAccessTokensViaBrowser>true</AllowAccessTokensViaBrowser>
          <RequireConsent>false</RequireConsent>
          <RequireClientSecret>false</RequireClientSecret>
          <AllowedGrantTypes>
            <AllowedGrantType1>client_credentials</AllowedGrantType1>
            <AllowedGrantType2>hybrid</AllowedGrantType2>
			<AllowedGrantType3>password</AllowedGrantType3>
          </AllowedGrantTypes>
          <RedirectUris>
            <RedirectUri1>{AllowedCorsOrigin}/signin-oidc</RedirectUri1>
			<RedirectUri2>{AllowedCorsOrigin}/oauth2/callback</RedirectUri2>
          </RedirectUris>
          <PostLogoutRedirectUris>
            <PostLogoutRedirectUri1>{AllowedCorsOrigin}/signout-callback-oidc</PostLogoutRedirectUri1>
			<PostLogoutRedirectUri2>{AllowedCorsOrigin}</PostLogoutRedirectUri2>
          </PostLogoutRedirectUris>
          <AllowedCorsOrigins>
            <AllowedCorsOriginsGroup1>https://localhost:5555|https://sc10sc.dev.local|http://sc10sc.dev.local</AllowedCorsOriginsGroup1>
          </AllowedCorsOrigins>
          <AllowedScopes>
            <AllowedScope1>openid</AllowedScope1>
            <AllowedScope2>sitecore.profile</AllowedScope2>
            <AllowedScope3>sitecore.profile.api</AllowedScope3>
			<AllowedScope4>EngineAPI</AllowedScope4>
			<AllowedScope5>postman_api</AllowedScope5>
          </AllowedScopes>
          <UpdateAccessTokenClaimsOnRefresh>true</UpdateAccessTokenClaimsOnRefresh>
		  <ClientSecrets>
            <ClientSecret1>U0hZx8H1Gxe4KB0x3LOlACA2aShpIDxfvlQOi10lnX4=</ClientSecret1>
          </ClientSecrets>
        </JSSClient>

Assurez-vous d’avoir la valeur correcte pour votre propre environnement dans AllowedCorsOriginsGroup1 !

J’ai également changé le code dans startup.cs pour utiliser les variables de configuration au lieu de coder en dur le nom d’hôte Commerce.

Aussi, dans Startup.cs, la plupart des appels API au Sitecore Commerce utilisaient la méthode Http PUT, ce qui n’est plus correct dans la version 10. À la place, nous devons utiliser la méthode Http POST (ligne 5)

c.ReRoute("/carts/me/addline")
                      .Method(HttpMethod.Post)
                      .To($"{hostRoot}/AddCartLine()")
                      .TransformBody((_, httpContext, bytes) => SetCartIdInBody(httpContext, bytes))
                      .Method(HttpMethod.Post)
                      .AuthenticateWith("test");

En conséquence, dans le projet d’application JSS, j’avais également besoin de créer une nouvelle fonction de publication et de l’appeler à la place de l’ancienne fonction put.

Dans src/lib/commerce/CartProvider.js

async function addCartLine(token, line) {
    await commerceRequest.post('api/carts/me/addline', token, JSON.stringify(line));
    trackCartlineAdded(line);
}

Dans src/lib/commerce/request.js

export async function post(uri, token, body) {
    let res = await fetch(`${gatewayUrl}/${uri}`, {
        method: 'post', 
        headers: {
            'Authorization' : `Bearer ${token}`, 
            'Content-Type' : 'application/json'
        }, 
        body: body
    });

    return handleErrors(res);
}

Sitecore Content Serialization (SCS)

Comme SCS est désormais la norme pour la sérialisation des éléments de Sitecore, il était naturel de remplacer Unicorn et d’utiliser SCS à la place.

Afin de désérialiser les éléments dont vous aurez besoin pour ce projet dans votre environnement Sitecore Commerce, ouvrez une console Powershell, installez les outils nécessaires et connectez-vous à Sitecore CM. Ensuite, vous pouvez pousser les éléments vers votre instance de site Web.

cd server\SCSerialization
dotnet new tool-manifest
dotnet tool install Sitecore.CLI --add-source https://sitecore.myget.org/F/sc-packages/api/v3/index.json
dotnet sitecore login --authority https://sc10identityserver.dev.local --cm https://sc10sc.dev.local --allow-write true
dotnet sitecore ser push

Quand j’ai essayé cela pour la première fois, j’avais un environnement avec la vitrine Habitat. Ce projet utilise le même catalogue et la sérialisation a échoué car un catalogue ne peut pointer que vers un seul site sitecore. J’ai donc dû désélectionner le catalogue Habitat dans Storefront pour que la sérialisation des éléments de contenu Sitecore fonctionne.

Installer, contrstruire et déployer l’app JSS

cd app
npm install
npm run build-css
npm run build
remove-Item .\dist\app\* -Recurse -Force
copy-item -Path .\build\* -Destination .\dist\app -Recurse
remove-Item C:\inetpub\wwwroot\sc10sc.dev.local\dist\app\* -Recurse -Force
copy-item -Path .\build\* -Destination C:\inetpub\wwwroot\sc10sc.dev.local\dist\app -Recurse

Démarrer le proxy

cd server/Gateway
dotnet watch run

Vous pouvez maintenant ouvrir un navigateur sur https: //app.dev.local (ou ce que vous avez changé à votre URL locale) et vous pouvez surfer sur cette application de commerce.

Ou est le code ?

Le code est ici. Je précise que 80% de ce code provient du projet de Jonne Kats et Joost Meijles, j’ai juste fait quelques ajustements pour que ce projet puisse functionner avec Sitecore Commerce 10.

Certaines configurations à modifier pour correspondre à votre environnement local se trouvent dans ces fichiers

server/Gateway/app.cong
server/Gateway/ocelot.json
app/scjssconfig.json
app/package.json
app/sitecore/config/app.config

Leave a comment