Utiliser GraphQL avec Vue et Oracle Content Management

Introduction

GraphQL est un langage de requête pour les API et une exécution permettant d'exécuter ces requêtes avec vos données existantes.

Dans ce tutoriel, nous vous montrerons comment utiliser GraphQL avec Vue connecté à Oracle Content Management. En particulier, nous allons nous concentrer sur la page "Personnes" existante du site échantillon minimal Vue en interrogeant les données nécessaires à l'aide de GraphQL. Les autres pages ('Accueil' et 'Nous contacter') utilisent actuellement une API REST.

Prérequis

Avant de poursuivre ce tutoriel, nous vous recommandons de lire les informations suivantes en premier :

Pour suivre ce tutoriel, vous aurez besoin des éléments suivants :

Ce que nous construisons

Grâce au site minimal intégré à Vue, vous pouvez facilement extraire des images et d'autres contenus à partir de votre référentiel Oracle Content Management.

Pour regarder ce que nous construisons, voici l'état de fin de notre tutoriel, un site de base Vue minimal qui consomme du contenu d'Oracle Content Management :

https://headless.mycontentdemo.com/samples/oce-vue-minimal-sample

Ce tutoriel se concentrera uniquement sur la page "Personnes" de ce site.

Voici à quoi ressemblera la page Personnes à la fin de ce tutoriel :

Cette image présente la page Nous contacter pour un site minime Vue.

Pour continuer, vous devez disposer d'un abonnement actif à Oracle Content Management et être connecté avec le rôle Administrateur de contenu.

Tâche 1 : Comprendre la taxonomie de la page Personnes

Le pack de ressources téléchargeables contient une ressource appelée Minimal Main GraphQL avec le slug 'minimalmaingraphql'.

La ressource Minimal Main GraphQL contient une page de type PeoplePage appelée Personnes. Il se présente comme suit :

Cette image présente la taxonomie principale GraphQL minimale.

La ressource People est de type PeoplePage et contient des sous-types de personne. Il se présente comme suit :

Cette image présente la taxonomie de la page Personnes.

Voici un exemple de ressource people (notez toutes les métadonnées dans le panneau Attributes) :

Cette image présente la taxonomie des personnes.

Tâche 2 : interface avec GraphQL dans Vue

L'objectif initial de ce tutoriel était d'utiliser la bibliothèque vue-apollo pour fournir une interface pratique entre l'interface GraphQL d'Oracle Content Management et l'exemple d'application Vue minimal. Malheureusement, au moment de l'écriture de cet échantillon, l'implémentation vue-apollo n'était disponible que pour les applications Vue 2.x, tandis que l'échantillon Vue minimal utilise Vue 3.x. Des travaux sont en cours pour soutenir apollo dans Vue 3, mais il était encore en développement alpha au moment de la création de cet échantillon. C'est pourquoi cet exemple a été écrit à l'aide d'une extraction croisée, une bibliothèque qui fournit une implémentation d'extraction pour une utilisation côté serveur et côté client. Etant donné qu'une requête GraphQL n'est finalement qu'une demande POST, elle fonctionne toujours mais nécessite une manipulation de données un peu plus faible qu'une implémentation vue-apollo.

Présentation de l'application existante

Récupération des données

L'exemple Vue minimal d'origine a utilisé des appels REST pour renseigner deux pages : la page d'accueil et une page dynamique référencée via une valeur slug unique. Ces deux pages contiennent une ou plusieurs entrées "section" représentant des blocs HTML à afficher sur la page. En raison de cette présentation commune, les deux pages peuvent utiliser le même modèle de page, Page.vue, pour afficher leurs données. La page Personnes ajoutée à l'exemple a une structure très similaire, mais elle est basée sur les données extraites à l'aide de GraphQL.

Routage de page

Le routage des pages dans l'application est géré à l'aide de l'extension Vue Router. Cela permet de diviser les pages en deux cas spéciaux : une page d'accueil et un nombre quelconque de pages référencées par des valeurs de slug uniques. Cela permet d'ajouter d'autres pages enfant à l'application en leur ajoutant simplement une référence dans l'actif principal de l'application, puis en définissant un équipement personnel comme décrit ci-dessus.

VueX Gestion d'états

Toutes les données affichées dans les pages sont gérées à l'aide d'une banque VueX. Il est chargé d'effectuer les appels vers le kit SDK de contenu, qui sont utilisés pour extraire le contenu de chaque page.

Mises à jour pour inclure la prise en charge de GraphQL

Dépendances d'installation

La première étape consiste à installer les bibliothèques dont nous avons besoin pour la prise en charge de GraphQL. Nous devons installer l'extraction croisée pour fournir l'accès réseau côté navigateur et côté serveur et l'agent https-proxy-agent afin de fournir la prise en charge du proxy si nécessaire. Pour ce faire, exécutez la commande suivante :

npm install cross-fetch https-proxy-agent

Ajouter dans le nouveau code pour exécuter les requêtes GraphQL

Nous créons ensuite un fichier nommé src/scripts/graphql-service.js. Ce fichier exécutera une requête GraphQL, puis extraira les champs utiles du résultat. Pour ce faire, créez une requête d'extraction croisée afin de passer un appel POST à Oracle Content Management. Les données GraphQL sont transmises dans le corps de l'appel POST.

export default async function fetchPeopleData(peopleSlug) {
      try {
        const channelToken = `${process.env.CHANNEL_TOKEN}`;
        const fetchParams = {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            query: `
          {
            getPeoplePage(slug: "${peopleSlug}", channelToken: "${channelToken}" ) {
              id
              slug
              name
              fields {
                announcement {
                  id
                  fields {
                    type: fieldType
                    heading
                    body
                    actions
                    image {
                      ...sectionImages
                    }
                  }
                }
                people {
                  id
                  fields {
                    fullname
                    title
                    biodata
                    file {
                      metadata {
                        width
                        height
                      }
                    }
                    renditions {
                      name
                      format
                      file {
                        url
                        metadata {
                          height
                          width
                        }
                      }
                    }
                  }
                }
              }
            }
          }
          fragment sectionImages on image {
            id
            fields {
              file {
                metadata {
                  width
                  height
                }
              }
              renditions {
                name
                format
                file {
                  url
                  metadata {
                    height
                    width
                  }
                }
              }
            }
          }
          `,
          }),
        };
    
        // Figure out if we need a proxy. Only needed on server-side render
        if (typeof window === 'undefined' && typeof process === 'object') {
          const proxyServer = process.env.SERVER_URL.startsWith('https')
            ? process.env.oce_https_proxy : process.env.oce_https_proxy;
          if (proxyServer) {
            const proxy = createHttpsProxyAgent(proxyServer);
            fetchParams.agent = proxy;
          }
        }
    
        const response = await fetch(`${process.env.SERVER_URL}/content/published/api/v1.1/graphql`, fetchParams);
        const queryResult = await response.json();
        return extractRequiredPeopleFields(queryResult);
      } catch (error) {
        console.log(error);
        return ('');
      }
    }

Une petite fonction utilitaire est également utilisée pour simplifier l'objet JavaScript renvoyé par l'appel. Il réduit l'imbrication inutile des données dans le résultat.

function extractRequiredPeopleFields(queryResult) {
      const result = { announcement: {}, people: [] };
    
      result.slug = queryResult.data.getPeoplePage.slug;
      result.name = queryResult.data.getPeoplePage.name;
      const base = queryResult.data.getPeoplePage.fields;
      if (base.announcement) {
        result.announcement = base.announcement.fields;
      }
      if (base.people) {
        result.people = base.people;
      }
      return result;
    }

Intégrer le code de requête dans le VueX Store

Maintenant que nous pouvons extraire les données, l'étape suivante consiste à les placer dans la banque VueX que l'application utilise pour gérer ses données. Cette opération est effectuée dans src/vuex/index.js en ajoutant les fonctions utilitaires et le stockage suivants :

    state.peoplePageData // storage for the object data
        // get the data for the People Page given its slug
        fetchPeoplePage({ commit }, pageSlug) {
          return fetchPeopleData(pageSlug).then((data) => {
            commit('setPeoplePageData', data);
          });
        },
        // setter function used to update the state
        setPeoplePageData(state, data) {
          state.peoplePageData = data;
        },

Brancher le code pour afficher la page Personnes

Nous allons ensuite créer le point d'intégration où l'application peut appeler le code pour afficher la page Personne. Pour ce faire, modifiez le code existant dans Page.vue pour effectuer un rendu conditionnel en fonction de la valeur slug. Vous pouvez le trouver dans src/pages/Page.vue.


    <!-- Component for the Page. -->
    // The template will conditionally render different sections depending 
    // on the value of the unique slug of the current page. 
    <template>
      <div v-if="data.hasError">
        <Error :errorObj="data" />
      </div>
      <div v-else-if="data.slug === 'people'">
        <Person :gqldata ="data"/>
      </div>
      <div v-else>
        <section :class="`content ${section.fields.type}`" :key="section.id"
          v-for="(section) in data.fields.sections">
          <Section :section="section" />
        </section>
      </div>
    </template>
    
    ....
    // Likewise, the fetch data call will seek out different data from the VueX store depending on 
    // the desired slug
    methods: {
        fetchData() {
          let data = {};
          // People is a special case and is handled by GraphQL in the store
          if (this.slug === 'people') {
            data = this.$store.dispatch('fetchPeoplePage', this.slug);
          } else {
          // return the Promise from the action
            data = this.$store.dispatch('fetchPage', this.slug);
          }
          return data;
        },
      },

Ajouter un code pour afficher le contenu de la page Personnes

Ensuite, nous créons le composant qui génère le contenu de la page Personnes. Ce code se trouve dans src/component/Person.vue. Le rendu est assez simple. Le code exécutable dans le composant prend le slug de page transmis (toujours 'people' dans ce cas), puis extrait les données correspondantes du cache VueX. Cela renvoie un objet JavaScript simple qui est ensuite utilisé dans le modèle ci-dessous.

<template>
      <div >
        <section  class="announcement">
            <div>
          <picture v-if="gqldata.announcement.image && renditionURLs">
          <source type="image/webp" :srcSet="renditionURLs.srcset" />
          <source :srcSet="renditionURLs.jpgSrcset" />
          <img
            id="header-image"
            :src="renditionURLs.large"
            alt="Company Logo"
            :width="renditionURLs.width"
            :height="renditionURLs.height"
          />
          </picture>
              <div class="textcontent">
                <h1>{{gqldata.announcement.heading}}</h1>
              </div>
              <div class="text">
                <div v-html=cleanContent></div>
              </div>
            </div>
        </section>
          <div class="all_people">
          <div v-for="person in gqldata.people" :key="person.id" >
            <section className="person">
              <img className="profile_picture"
              :src="person.fields.renditions[0].file.url" :alt="person.fields.fullname" />
              <div className="profile">
                <div className="profile_name">{{person.fields.fullname}}</div>
                <div className="profile_position">{{person.fields.title}}</div>
                <div className="profile_description">{{person.fields.biodata}}</div>
              </div>
            </section>
          </div>
        </div>
      </div>
    </template>

Le code restant dans le composant est utilisé pour extraire les données de la banque VueX.

Ajouter le nouveau CSS

Enfin, nous ajoutons le style CSS requis à l'application. Il se trouve dans src/assets/global.css.

/*
    * Styles for the people cards
    */
    .all_people {
      margin: 0 auto;
      max-width: 1000px;
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
      gap: 50px;
      margin-top: 50px;
      margin-bottom: 50px;
    }
    
    .person {
      margin: 0 auto;
      width: 300px;
      box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 2px 4px rgba(0, 0, 0, 0.5), 0px -2px 2px rgba(0, 0, 0, 0.15);
      height: 100%;
    }
    
    .profile {
      padding: 20px 15px;
    }
    
    .profile_picture {
      width: 100%;
      height: 200px;
      object-fit: cover;
      display: block;
    }
    
    .profile_name {
      text-align: center;
      font-size: 16px;
      font-weight: bold;
    }
    
    .profile_position {
      text-align: center;
      font-size: 12px;
      color: rgba(0, 0, 0, 0.7);
      margin-bottom: 18px;
    }
    
    .profile_description {
      font-size: 14px;
      display: flex;
      justify-content: center;
    }

Conclusion

Dans ce tutoriel, nous avons ajouté une page "Personnes" au site échantillon minimal Vue à l'aide de GraphQL pour obtenir les données de cette page. Les autres pages ('Accueil' et 'Nous contacter') utilisent toujours l'API REST.

Pour plus d'informations sur GraphQL dans Oracle Content Management, reportez-vous à la documentation.