Use GraphQL com o Vue e o Oracle Content Management

Introdução

GraphQL é uma linguagem de consulta para APIs e um runtime para atendimento dessas consultas com seus dados existentes.

Neste tutorial, vamos mostrar como usar GraphQL com o Vue conectado ao Oracle Content Management. Especificamente, estaremos focando na página 'Pessoas' existente no site de amostra Vue minimal consultando os dados necessários usando GraphQL. No momento, as outras páginas ('Início' e 'Fale Conosco') estão usando uma API REST.

Pré-requisitos

Antes de prosseguir com este tutorial, recomendamos que você leia as seguintes informações primeiro:

Para seguir este tutorial, você precisará:

O que estamos construindo

Com o site mínimo incorporado no Vue, você pode recuperar facilmente imagens e outro conteúdo do seu repositório do Oracle Content Management.

Para dar uma olhada no que estamos criando, aqui está o estado final de nosso tutorial, um site básico do Vue, que consome conteúdo do Oracle Content Management:

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

Este tutorial focará somente na página 'Pessoas' deste site.

Esta é a aparência da página Pessoas no final deste tutorial:

Esta imagem mostra a página de contato conosco para um site mínimo Vue.

Para continuar, será necessário ter uma inscrição ativa no Oracle Content Management e fazer log-in com a função Administrador de Conteúdo.

Tarefa 1: Compreender a Taxonomia da Página Pessoas

O pacote de ativos para download contém um ativo chamado Mínimo Principal GraphQL com a barra 'minimalmaingraphql'.

O ativo Mínimo Principal GraphQL contém uma página do tipo PeoplePage chamada Pessoas. Parecerá com o seguinte:

Esta imagem mostra a taxonomia Mínima Principal GraphQL.

O ativo Pessoas é do tipo PeoplePage e contém subtipos de pessoas. Parecerá com o seguinte:

Esta imagem mostra a taxonomia da página Pessoas.

É assim que um exemplo de ativo de pessoas se parece (observe todos os metadados no painel Atributos):

Esta imagem mostra a taxonomia das pessoas.

Tarefa 2: Interface com GraphQL no Vue

A intenção original deste tutorial era usar a biblioteca vue-apollo para fornecer uma interface conveniente entre a interface GraphQL do Oracle Content Management e o aplicativo de amostra mínima Vue. Infelizmente, no momento em que essa amostra foi gravada, a implementação vue-apollo só estava disponível para aplicativos Vue 2.x, enquanto a amostra mínima Vue usa Vue 3.x. Os trabalhos estão em curso para apoiar o apollo no Vue 3, mas ainda estava em desenvolvimento a nível alfa no momento em que esta amostra foi criada. Por essa razão, essa amostra foi escrita usando cross-fetch, uma biblioteca que fornece uma implementação de extração para uso do servidor e do cliente. Como uma consulta GraphQL é, em última análise, apenas uma solicitação POST, isso ainda funciona, mas precisa de um pouco mais de manipulação de dados de nível inferior do que uma implementação vue-apollo pode ter exigido.

O Layout do Aplicativo Existente

Recuperação de Dados

A amostra mínima de Vue original usou chamadas REST para preencher duas páginas: a página inicial e uma página dinâmica que foi referenciada por meio de um valor de slug exclusivo. Ambas as páginas contêm uma ou mais entradas de 'seção' que representam blocos HTML a serem renderizados na página. Devido a esse layout comum, ambas as páginas podem usar o mesmo modelo de página, Page.vue, para exibir seus dados. A página Pessoas que está sendo adicionada à amostra tem uma estrutura muito semelhante, mas é criada com base nos dados recuperados usando GraphQL.

Roteamento de Página

O roteamento das páginas no aplicativo é tratado com a extensão Vue Router. Isso permite que as páginas sejam divididas em dois casos especiais: uma página inicial e qualquer número de páginas referenciadas por valores de slug exclusivos. Isso permite adicionar mais páginas filho ao aplicativo simplesmente adicionando uma referência a elas no ativo principal do aplicativo e, em seguida, definindo um ativo pessoal conforme descrito acima.

VueX Gerenciamento de Estado

Todos os dados exibidos nas páginas são gerenciados usando um armazenamento VueX. Isso é responsável por fazer as chamadas para o SDK de Conteúdo, que são usadas para recuperar o conteúdo de cada página.

Atualizações para Incluir o Suporte GraphQL

Instalar Dependências

A primeira etapa é instalar as bibliotecas necessárias para o suporte GraphQL. Precisamos instalar cross-fetch para fornecer acesso de rede do navegador e do servidor e https-proxy-agent para fornecer suporte de proxy, se necessário. Isso é feito executando este comando:

npm install cross-fetch https-proxy-agent

Adicionar Novo Código para Executar as Consultas GraphQL

Em seguida, criamos um novo arquivo chamado src/scripts/graphql-service.js. Esse arquivo executará uma consulta GraphQL e extrairá os campos úteis do resultado. Isso é feito criando uma consulta de extração cruzada para fazer uma chamada POST para o Oracle Content Management. Os dados GraphQL são informados no corpo da chamada 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 ('');
      }
    }

Também há uma pequena função de utilitário usada para simplificar o objeto JavaScript retornado da chamada. Ele reduz o aninhamento desnecessário de dados no 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;
    }

Integre o Código de Consulta na Loja VueX

Agora que podemos recuperar os dados, a próxima etapa é colocá-los no armazenamento VueX que o aplicativo usa para gerenciar seus dados. Isso é feito em src/vuex/index.js adicionando as seguintes funções de utilitário e armazenamento:

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

Ramificar o Código para Processar a Página Pessoas

Em seguida, criaremos o ponto de integração em que o aplicativo poderá chamar o código para renderizar uma página Pessoa. Isso é feito modificando o código existente em Page.vue para renderizar condicionalmente com base no valor do slug. Isso pode ser encontrado em 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;
        },
      },

Adicionar Código para Processar o Conteúdo da Página Pessoas

Em seguida, criamos o componente que gera o conteúdo da página Pessoas. Este código está em src/components/Person.vue. A renderização é bastante simples. O código executável no componente obtém o slug de página passado (sempre 'pessoas' nesse caso) e, em seguida, recupera os dados correspondentes do cache VueX. Isso retorna um objeto JavaScript simples que é usado no modelo abaixo.

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

O código restante no componente é usado para extrair os dados do armazenamento VueX.

Adicionar o Novo CSS

Por fim, adicionamos o estilo CSS necessário ao aplicativo. Isso pode ser encontrado em 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;
    }

Conclusão

Neste tutorial, adicionamos uma página 'Pessoas' ao site de amostra Vue minimal usando GraphQL para obter os dados dessa página. As outras páginas ('Início' e 'Fale Conosco') ainda usam a API REST.

Informações adicionais sobre GraphQL no Oracle Content Management podem ser encontradas na documentação.