Utilizar GraphQL con Vue y Oracle Content Management

Introducción

GraphQL es un lenguaje de consulta para las API y un tiempo de ejecución para satisfacer esas consultas con los datos existentes.

En este tutorial, se mostrará cómo utilizar GraphQL con Vue conectado a Oracle Content Management. En concreto, nos centraremos en la página "Personas" existente en el sitio de ejemplo mínimo consultando los datos necesarios mediante GraphQL. Las otras páginas ('Inicio' y 'Contacto') están utilizando actualmente una API de REST.

Requisitos

Antes de continuar con este tutorial, le recomendamos que lea primero la siguiente información:

Para seguir este tutorial, necesitará:

Lo que estamos creando

Con el sitio mínimo incorporado Vue, puede recuperar fácilmente imágenes y otro contenido de su repositorio de Oracle Content Management.

Para echar un vistazo a lo que estamos creando, este es el estado final de nuestro tutorial, un sitio básico Mínimo de Vue que consume contenido de Oracle Content Management:

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

Este tutorial se centrará únicamente en la página "Personas" de este sitio.

Este es el aspecto que tendrá la página People al final de este tutorial:

En esta imagen, se muestra la página de contacto para un sitio mínimo de Vue.

Para continuar, necesitará tener una suscripción activa a Oracle Content Management y estar conectado con el rol de administrador de contenido.

Tarea 1: Descripción de la taxonomía de la página Personas

El paquete de activos descargable contiene un activo denominado Minimal Main GraphQL con el slug 'minimalmaingraphql'.

El activo Mínimo principal GraphQL contiene una página de tipo PeoplePage denominada Personas. Esto es así:

En esta imagen se muestra la taxonomía GraphQL principal mínima.

El activo People es del tipo PeoplePage y contiene subtipos de personas. Esto es así:

En esta imagen se muestra la taxonomía de la página Personas.

Este es el aspecto de un activo people de ejemplo (tenga en cuenta todos los metadatos en el panel Atributos):

En esta imagen se muestra la taxonomía de personas.

Tarea 2: Interfaz con GraphQL en Vue

La intención original de este tutorial era utilizar la biblioteca vue-apollo para proporcionar una interfaz cómoda entre la interfaz GraphQL de Oracle Content Management y la aplicación de ejemplo mínima Vue. Lamentablemente, en el momento en que se escribió este ejemplo, la implementación de vue-apollo solo estaba disponible para las aplicaciones Vue 2.x, mientras que la muestra mínima de Vue utiliza Vue 3.x. Se está trabajando para apoyar el apolo en Vue 3, pero todavía estaba en desarrollo a nivel alfa en el momento de la creación de esta muestra. Por este motivo, este ejemplo se escribió mediante la recuperación cruzada, una biblioteca que proporciona una implementación de recuperación para uso del lado del servidor y del lado del cliente. Dado que, en última instancia, una consulta GraphQL es solo una solicitud POST, esto aún funciona, pero necesita una manipulación de datos de nivel inferior que una implementación de vue-apollo podría haber sido necesaria.

Diseño de Aplicación Existente

Recuperación de datos

La muestra mínima original de Vue utilizó llamadas REST para rellenar dos páginas: la página inicial y una página dinámica a la que se hizo referencia mediante un valor de segmento único. Ambas páginas contienen una o más entradas de sección que representan bloques HTML que se representarán en la página. Debido a este diseño común, ambas páginas pueden utilizar la misma plantilla de página, Page.vue, para mostrar sus datos. La página People que se agrega a la muestra tiene una estructura muy similar, pero se basa en los datos recuperados mediante GraphQL.

Enrutamiento de páginas

El enrutamiento de páginas en la aplicación se maneja mediante la extensión Vue Router. Esto permite que las páginas se dividan en dos casos especiales: una página inicial y cualquier número de páginas a las que hacen referencia valores de depuración únicos. Esto permite agregar más páginas secundarias a la aplicación simplemente agregando una referencia a ellas en el activo principal de la aplicación y, a continuación, definiendo un activo de personas como se ha descrito anteriormente.

VueX Gestión de Estado

Todos los datos mostrados en las páginas se gestionan mediante un almacén VueX. Esto es responsable de realizar las llamadas al SDK de contenido, que se utilizan para recuperar el contenido de cada página.

Actualizaciones para incluir el soporte GraphQL

Instalar dependencias

El primer paso es instalar las bibliotecas que necesitamos para la compatibilidad con GraphQL. Necesitamos instalar la recuperación cruzada para proporcionar acceso a la red del explorador y del servidor, y https-proxy-agent para proporcionar soporte de proxy si es necesario. Para ello, ejecute este comando:

npm install cross-fetch https-proxy-agent

Agregar a nuevo código para ejecutar las consultas GraphQL

A continuación, creamos un nuevo archivo denominado src/scripts/graphql-service.js. Este archivo ejecutará una consulta GraphQL y, a continuación, extraerá los campos útiles del resultado. Para ello, se crea una consulta de recuperación cruzada para realizar una llamada POST a Oracle Content Management. Los datos GraphQL se transfieren en el cuerpo de la llamada 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 ('');
      }
    }

También hay una pequeña función de utilidad que se utiliza para simplificar el objeto JavaScript devuelto de la llamada. Reduce el anidamiento innecesario de datos en el resultado.

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;
    }

Integrar el código de consulta en el almacén VueX

Ahora que podemos recuperar los datos, el siguiente paso es ponerlos en el almacén VueX que la aplicación utiliza para gestionar sus datos. Esto se realiza en src/vuex/index.js agregando las siguientes funciones de utilidad y almacenamiento:

    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;
        },

Rama del código para representar la página de personas

A continuación, crearemos el punto de integración en el que la aplicación puede llamar al código para presentar una página de persona. Esto se realiza modificando el código existente en Page.vue para que se presente de forma condicional en función del valor de depuración. Se puede encontrar en 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;
        },
      },

Agregar código para representar el contenido de la página de personas

A continuación, se crea el componente que genera el contenido de la página Personas. Este código está en src/components/Person.vue. La representación es bastante sencilla. El código ejecutable del componente toma el segmento de página transferido (siempre "personas" en este caso) y, a continuación, recupera los datos correspondientes de la caché VueX. Devuelve un objeto JavaScript simple que, a continuación, se utiliza en la plantilla siguiente.

<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>

El código restante del componente se utiliza para extraer los datos del almacén VueX.

Agregar nuevo CSS

Por último, agregamos el estilo CSS necesario a la aplicación. Esto se puede encontrar en 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;
    }

Conclusión

En este tutorial, hemos agregado una página "Personas" al sitio de ejemplo mínimo mediante GraphQL para obtener los datos de esa página. Las otras páginas ('Inicio' y 'Contacto') siguen utilizando la API de REST.

Puede encontrar información adicional sobre GraphQL en Oracle Content Management en la documentación.