Instalar o aplicativo de exemplo

Objetivos de aprendizagem

Após concluir esta unidade, você estará apto a:
  • Instalar o aplicativo de exemplo Visualizador de registros.
  • Entender a estrutura do aplicativo Visualizador de registros.
  • Visualizar um registro no aplicativo Visualizador de registros.

Instalar o aplicativo Visualizador de registros

Vamos entrar no código! Para saber mais sobre a API de interface de usuário, será preciso instalar o aplicativo de exemplo Visualizador de registros. Use o aplicativo Visualizador de registros para criar, ler, atualizar e excluir registros. Como é um aplicativo de exemplo, também inclui uma interface de usuário que exibe as respostas JSON da API de interface de usuário. Crie um registro Oportunidade no aplicativo de exemplo Visualizador de registros.
Dica

Dica

Para concluir o Desafio prático no final desta unidade, será preciso concluir estas etapas. Se não tiver um Trailhead Playground, role até o Desafio e crie um agora.

  1. Se não tiver o Git instalado, instale-o primeiro.
  2. Clone o repositório Git do Visualizador de registros. Se você não sabe clonar um repositório, consulte Como clonar um repositório na Ajuda do GitHub.
  3. O Visualizador de registros é executado em um contêiner do Docker. Instale a versão estável do Docker Engine (Community). O Docker solicita a criação de uma conta para baixar o software.
  4. Inicie o Docker.
Nota

Nota

No Windows, em Configurações do Docker, escolha Unidades compartilhadas e selecione a unidade local onde você clonou o aplicativo Visualizador de registros.

O Visualizador de registros é um aplicativo conectado, ou seja, um aplicativo que se comunica com o Salesforce por meio de APIs. O aplicativo não é executado na Salesforce Platform, mas está conectado por meio de APIs. Para definir como o aplicativo Visualizador de registros é autenticado no Salesforce, acesse Configuração e crie um aplicativo conectado.

  1. No Trailhead Playground, em Configuração, insira App e selecione Gerente de aplicativo.
  2. Clique em Novo aplicativo conectado e insira estas configurações:
    • Nome do aplicativo conectado: RecordViewer
    • Email de contato: Seu endereço de email
  3. Selecione Habilitar as configurações do OAuth e insira estas configurações:
    • URL de retorno de chamada: https://localhost:8443/oauth-redirect
    • Escopos OAuth selecionados: Access and manage your data (api) (Acessar e gerenciar seus dados (api))
  4. Clique em Salvar.
  5. Na página Novo aplicativo conectado, clique em Continuar.
  6. Copie e salve a Chave do cliente (também conhecida como ID do cliente OAuth). Ela será necessária depois.
Para permitir que o Visualizador de registros faça solicitações HTTP ao Salesforce a partir de um navegador da web, configure o compartilhamento de recurso entre origens (CORS) no Trailhead Playground.
  1. Em Configuração, insira CORS e selecione CORS.
  2. Clique em Novo.
  3. Para Padrão de URL de origem, insira https://localhost:8443
  4. Clique em Salvar.

Para criar o aplicativo Visualizador de registros e iniciar o servidor, use uma interface de linha de comando como o Terminal do MacOS ou o Prompt de comando do Windows.

Primeiro, use o comando cd a fim de mudar para o diretório RecordViewer que você clonou no seu computador local. Substitua o caminho a seguir pelo caminho para o diretório RecordViewer. Por exemplo, se o diretório RecordViewer estiver em seu diretório de usuário do Mac, digite cd ~/RecordViewer.
cd /replace/with/path/to/RecordViewer

Em seguida, execute estes comandos do Docker.

docker-compose build && docker-compose up -d
Para iniciar o aplicativo Visualizador de registros, em um navegador abra https://localhost:8443.
Nota

Nota

O aplicativo Visualizador de registros usa um certificado SSL autoassinado. Assim, os navegadores podem informar que sua conexão não é privada. É seguro clicar e carregar o aplicativo. No Chrome, clique em Avançadas e Prosseguir até o site. No Firefox, clique em Avançadas e adicione uma exceção. No Internet Explorer, clique em Continuar para este site. No Safari, clique em Mostrar detalhes e visitar este site.

Nota

Nota

Se o localhost recusar a conexão, use o endereço IP do seu computador, por exemplo, https://192.168.34.34:8443. Use este valor também no padrão de URL de origem do CORS.

Para autorizar o Visualizador de registros a fazer solicitações ao Salesforce, insira a URL de login do Salesforce, https://login.salesforce.com e a chave do cliente que foi salva do aplicativo conectado e clique em Login.Tela de login do aplicativo Visualizador de registros.

Para que o aplicativo Visualizador de registros possa acessar sua organização do Salesforce, verifique se seu nome de usuário está correto e clique em Permitir.Tela Permitir acesso.

Nota

Nota

Por fim, o usuário será desconectado do Salesforce. Para autorizar novamente o aplicativo, carregue a página de login: https://localhost:8443/login. Insira a URL de login do Salesforce, https://login.salesforce.com e a chave do cliente que foi salva do aplicativo conectado.

Usar o aplicativo Visualizador de registros

Use o Visualizador de registros para visualizar, editar, criar e excluir registros do Salesforce. O Visualizador de registros usa a API de interface de usuário para fazer essas operações nos registros do Salesforce. O aplicativo também oferece uma maneira prática de examinar a resposta JSON da API de interface de usuário.

Antes de vermos um registro no Visualizador de registros, vejamos um registro no Salesforce para fins de comparação.
  1. Em um navegador, abra a organização do Salesforce que usou para criar o aplicativo conectado Visualizador de registros.
  2. Clique em Contas e depois em Nova.
  3. Em Nome da conta, insira Contêineres universais
  4. Em Telefone, insira 206 555 1212 e clique em Salvar.
  5. Para ver a página de detalhes do novo registro, clique em Detalhes.
Detalhes do registro de conta no Lightning Experience
Agora vejamos o mesmo registro no aplicativo Visualizador de registros.
  1. Em um navegador, abra https://localhost:8443.
  2. Em Itens recentes, clique em Contêineres universais.
    Nota

    Nota

    Se Contêineres universais não aparecer, atualize a página.

O aplicativo Visualizador de registros mostra a página de detalhes do registro.Detalhes do registro de conta no aplicativo Visualizador de registros.

Falamos sobre o poder da API de interface de usuário, ou seja, suas respostas respeitam as alterações de metadados feitas na organização do Salesforce. Então, vamos remover um campo do layout da Conta na organização do Salesforce e ver o que acontece no Visualizador de registros.

Contêineres universais tem um número de telefone, mas a página de detalhes do registro também tem um campo chamado Fax. Ninguém mais usa fax, então vamos remover isso do layout de página.

  1. No Trailhead Playground, em Configuração, insira Objeto e selecione Gerenciador de objetos.
  2. Clique em Conta | Layouts de página | Layout de conta.
  3. No editor de layout de página, clique para remover o campo Fax e clique em Salvar.

Editor de layout de página com o campo Fax selecionado.

Agora, retorne ao aplicativo Visualizador de registros e clique em Exibir registro para recarregar o registro Contêineres universais. Tcharam! O campo Fax desapareceu e nenhuma linha de código foi alterada.Detalhes do registro de Conta no aplicativo Visualizador de registros sem campo de fax.

Nesse caso, o campo foi removido do layout. Em uma situação do mundo real, o administrador não precisa avisar quando muda um layout, seu aplicativo apenas responde!

Estrutura do aplicativo Visualizador de registros

Para trabalhar com a API de interface de usuário, é possível usar qualquer estrutura ou linguagem da Web que possa fazer solicitações HTTP. Polymer, React, Angular, Vue, iOS, Android – use a tecnologia da sua preferência.

O aplicativo Visualizador de registros é um aplicativo de página única criado usando React, Redux e Redux-Saga. Examinaremos rapidamente as noções básicas dessas tecnologias quando analisarmos a estrutura de arquivos do aplicativo. Se você não estiver familiarizado com o React e o Redux, não há problema. Apenas tente perceber as ideias gerais.

Seu objetivo é aprender como acessar os recursos da API de interface de usuário e como lidar com as respostas. Cada estrutura e linguagem tem maneiras ligeiramente diferentes de concluir essas tarefas, mas os conceitos principais são os mesmos e a API se comporta da mesma forma.

O aplicativo Visualizador de registros foi construído usando:

  • Pug – gera páginas HTML a partir de modelos do lado do servidor.
  • React – uma biblioteca JavaScript para criar componentes personalizados e combiná-los para compor interfaces de usuário
  • Redux – mantém o estado de um aplicativo React em um depósito, que é um objeto JavaScript único e imutável. Os componentes React enviam objetos JavaScript chamados de ações, que são interceptados por redutores. Redutores são funções JavaScript que usam ações como entradas para atualizar o estado do Redux.
  • Redux-Saga – como os redutores, sagas são funções JavaScript que são executadas quando interceptam uma ação. As sagas gerenciam solicitações assíncronas para a API de interface de usuário. No final de uma operação assíncrona, as sagas enviam uma ação, que pode ser interceptada por um redutor que atualiza o depósito. As sagas evitam a confusão entre retornos de chamada.
  • Node.js – um tempo de execução no lado do servidor para aplicativos JavaScript

Vejamos onde esses conceitos abstratos estão no mundo real. Inicie seu IDE favorito e abra a pasta RecordViewer que foi clonada do GitHub. Estrutura de arquivos do aplicativo Visualizador de registros em VS Code. Uma pasta de ações, uma pasta de componentes, uma pasta de contêineres, uma pasta de auxiliares, uma pasta de redutores e uma pasta de sagas.

Primeiro, vejamos a pasta visualizações, que contém os modelos de HTML Pug. O aplicativo tem três visualizações: a página de login, onde foram inseridas as informações de login do Salesforce; a página de visualização de registros, que é o contêiner principal do aplicativo; e uma página de erro, que você, esperamos, ainda não viu. O quarto modelo Pug é um redirecionamento de autenticação OAuth.

Se a autenticação for um sucesso, o modelo recordView.pug será carregado e chamará renderRoot, que está definido no arquivo root.js. Deste ponto em diante, o aplicativo Visualizador de registros executa o código do lado do cliente carregado nesta página da web única.
<!-- /views/recordView.pug -->

doctype html
html
  block head
    title Record Viewer
    // Metadata and style sheet links removed to keep sample short

    script(type='text/javascript', src='https://code.jquery.com/jquery-2.0.3.min.js')
    script(type='text/javascript', src='root.js')

  body
    .slds-grid.slds-wrap
      nav.slds-col.slds-size--1-of-1
        .slds-page-header
          p.slds-page-header__title.slds-truncate.slds-align-middle
            a(href='/recordView') Record Viewer
          p.slds-page-header__info powered by User Interface API
    #root
    script.
      $( document ).ready(function() {
        let rootNode = document.getElementById("root");
        renderRoot("#{targetUrl}", "#{accessToken}", "#{recordId}", rootNode);
      });
O arquivo /client-src/root.js inicializa o aplicativo. Ele conecta o componente raiz RecordViewerWrapper com a raiz dos redutores de estado Redux e o middleware de Saga. Lembre-se de que redutores atualizam o estado, enquanto sagas fazem solicitações à API de interface de usuário e enviam ações que contêm resultados.
/* Excerpt from /client-src/root.js */

// Root renderer for record viewer.
global.renderRoot = function(instanceUrl, accessToken, recordId, rootNode) {
  const sagaMiddleware = createSagaMiddleware()
  const store = createStore(rootReducer, {login:{instanceUrl, accessToken}, record:{recordId}}, applyMiddleware(sagaMiddleware));
  sagaMiddleware.run(rootSaga);

  ReactDOM.render(
    <Provider store={store}>
      <div>
        <RecordViewerWrapper />
      </div>
    </Provider>,
    rootNode);
}
O componente React RecordViewerWrapper.js fica na pasta contêineres. Esse componente é um componente conectado de nível superior que envolve o componente RecordViewer. A função mapStateToProps indica como ligar o estado Redux às propriedades do RecordViewer.
/* Excerpt from /client-src/containers/RecordViewerWrapper.js */

// Presentational Component that uses state to decide how to
// construct the RecordViewer.
const mapStateToProps = (state) => {
  if (state.record.record) {
    return {
      screen: 'RECORD',
      record: state.record.record,
      headerRecordId: state.header.recordId,
      mode: state.record.mode,
      context: state.context,
      prevMode: state.record.prevMode,
      creds: state.login,
      picklists : state.picklists,
      depGraph: state.depGraph,
      rawjson: state.rawjson,
      error: state.error
    }
  } else if (state.record.recordId) {
    return {
      screen: 'FETCH_RECORD',
      recordId: state.record.recordId,
      mode: 'View',
      context: state.context,
      creds: state.login,
      rawjson: state.rawjson,
      error: state.error
    }
  }
 
  // Additional mappings removed to shorten sample
O RecordViewWrapper também tem uma função mapDispatchToProps que mapeia as ações Redux para as propriedades React, de forma que os componentes React possam enviar ações em resposta a vários eventos. Lembra-se das ações? São usadas como entradas para os redutores, que são funções que alteram o estado do aplicativo.
/* Excerpt from /client-src/containers/RecordViewerWrapper.js */

const mapDispatchToProps = (dispatch) => {
  return {
    onFetchRecord: (creds, id, context) => {
      dispatch(actions.fetchRecord(creds, id, context))
    },
    onRecordClick: (creds, id, context) => {
      dispatch(actions.fetchRecord(creds, id, context))
    },
    onViewRecordClick: (creds, id, context) => {
      dispatch(actions.fetchRecord(creds, id, context))
    },
    onFormFactorSelect: (formFactor, recordId) => {
      dispatch(actions.updateFormFactor(formFactor, recordId))
    },

   // Additional mappings removed to shorten sample
Por fim, o método connect() conecta esses dois mapas com o componente simples RecordViewer.
/* Excerpt from /client-src/containers/RecordViewerWrapper.js */

const RecordViewerWrapper = connect(
  mapStateToProps,
  mapDispatchToProps
) (RecordViewer)

export default RecordViewerWrapper

As ações, redutores e sagas ficam (tranquilamente) nas pastas ações, redutores e sagas.

Uma ação é um objeto simples que deve ter uma propriedade type, e pode ter outras propriedades. A propriedade type identifica a ação. Todas as ações estão definidas no arquivo /actions/index.js.
/* Excerpt from /client-src/actions/index.js */

export const fetchEntities = (creds) => {
  return {
    type: 'FETCH_ENTITIES',
    creds
  }
}

export const receiveEntities = (entities) => {
  return {
    type: 'RECEIVE_ENTITIES',
    entities,
    receivedAt: Date.now()
  }
}
Vejamos, então, o caminho da felicidade da ação fetchEntities. O arquivo rootSaga.js registra todas as sagas no aplicativo Visualizador de registros. Quando uma ação é enviada para o depósito e seu type corresponde a uma sequência de caracteres em uma função takeEvery, a função inicia a saga correspondente. O tipo de ação FETCH_ENTITIES gera a saga entitiesFetcher.
/* /client-src/sagas/rootSaga.js */

import { takeEvery } from 'redux-saga'

import recentItemsFetcher from './recentItemsFetcher'
import recordFetcher from './recordFetcher'
import recordDeleter from './recordDeleter'
import recordUpdater from './recordUpdater'
import entitiesFetcher from './entitiesFetcher'
import recordCreator from './recordCreator'
import cloneDefaultsFetcher from './cloneDefaultsFetcher'
import createDefaultsFetcher from './createDefaultsFetcher'
import picklistsFetcher from './picklistsFetcher'
import depGraphValueUpdater from './depGraphValueUpdater'

export default function* rootSaga() {
  yield takeEvery('FETCH_RECORD', recordFetcher)
  yield takeEvery('FETCH_RECENT_ITEMS', recentItemsFetcher)
  yield takeEvery('DELETE_RECORD', recordDeleter)
  yield takeEvery('SAVE_RECORD', recordUpdater)
  yield takeEvery('FETCH_ENTITIES', entitiesFetcher)
  yield takeEvery('CREATE_RECORD', recordCreator)
  yield takeEvery('FETCH_CREATE_DEFAULTS', createDefaultsFetcher);
  yield takeEvery('FETCH_CLONE_DEFAULTS', cloneDefaultsFetcher);
  yield takeEvery('FETCH_PICKLISTS', picklistsFetcher);
  yield takeEvery('UPDATE_DEP_GRAPH_FIELD_VALUE', depGraphValueUpdater)
}
A saga entitiesFetcher.js faz uma solicitação à API de interface de usuário para obter uma lista de objetos compatíveis com a API. O recurso da API de interface de usuário é /ui-api/object-info. Em uma unidade posterior, veremos como criar a URL completa. Se estiver familiarizado com as APIs REST do Salesforce, você entenderá, porque todas usam o mesmo padrão. Assim como outras APIs REST do Salesforce, a API de interface de usuário usa OAuth 2.0. O token do Portador é aprovado no cabeçalho Authorization. Definimos o cabeçalho X-Chatter-Entity-Encoding como false, para que a resposta não seja codificada.
/* /client-src/sagas/entitiesFetcher.js */

import { call, put } from 'redux-saga/effects'

import { receiveEntities } from '../actions'

export default function* entitiesFetcher(action) {

  let mruUrl = action.creds.instanceUrl + '/services/data/v48.0/ui-api/object-info'

  let req = {
    method: 'GET',
    headers: {
      'Authorization' : 'Bearer ' + action.creds.accessToken,
      'X-Chatter-Entity-Encoding': false}
    }

  try {
    const response = yield call(fetch, mruUrl, req)
    const responseJson = yield response.json()
    yield put(receiveEntities(responseJson))
  } catch(err) {
    console.error('Describe sobjects error: ' + JSON.stringify(err))
  }
}
Vê a linha yield put(receiveEntities(responseJson))? Procure por receiveEntities na amostra de código anterior de /actions/index.js. Vê a ação receiveEntities? Sim, se a operação da API de interface de usuário for bem-sucedida, a saga enviará a ação receiveEntities que contém a resposta JSON. O redutor entities.js intercepta a ação e a emprega como entrada para atualizar o estado Redux.
/* /client-src/reducers/entities.js */

const entities = (state = {sobjects: []}, action) => {
  switch (action.type) {
    case 'RECEIVE_ENTITIES':
      return {
        sobjects: action.entities.objects,
        receivedAt: action.receivedAt
      }
    default:
      return state
  }
}

export default entities

O redutor atualiza uma parte do estado Redux. Depois que a alteração é feita, os componentes React que assinam esse bit de estado são atualizados automaticamente.

No aplicativo Visualizador de registros, os componentes React que constroem HTML ficam na pasta componentes. Esses componentes definem as partes da interface de usuário do aplicativo. O componente CreateableEntitiesList.js define a interface de usuário que exibe um menu no aplicativo Visualizador de registros. O menu contém a lista de objetos compatíveis retornados da chamada para /ui-api/object-info. O aplicativo Visualizador de registros recebe as entidades e as exibe no componente CreateableEntitiesList.Menu pop-up Criar novo registro com uma lista de objetos a criar.
Dica

Dica

O código RecordViewer geralmente usa o termo entidades, o que significa objetos. Então, quando vemos algo como CreateableEntitiesList, isso pode ser entendido como uma lista de objetos que podem ser criados. Em outras palavras, é possível criar um registro a partir de qualquer um dos objetos nessa lista! Esses objetos são compatíveis com a API de interface de usuário.

No React, é possível compor componentes para criar a interface de usuário, o que significa que os componentes pai podem ser compostos de componentes filho aninhados. Ao desenvolver um aplicativo, divida a funcionalidade em componentes simples e combine-os para formar componentes mais complexos.

Ao observar os nomes dos componentes na pasta componentes, é possível perceber que o aplicativo Visualizador de registros é sobre trabalhar com registros – Record.js, RecordButton.js, RecordSection.js. Observe o código do componente e verá componentes filho aninhados. Por exemplo, o componente RecordSection inclui componentes RecordRow aninhados.
/* Excerpt from /client-src/components/RecordSection.js */

// Component that displays a Record section.
const RecordSection = ({section, error, editValues, picklists, onFieldValueUpdate, allowEdit, index, objectInfo, onEditDepGraph, uiMode, recordView}) => {

  return (
    <tbody>
      { section.useHeading &&
       <tr>
         <td colSpan="4" key={'sectionHeading' + index} className="slds-text-heading--small slds-p-left--medium slds-p-top--small slds-p-bottom--medium">
           {section.heading}
         </td>
       </tr>
      }
      {section.rows.map((row, i) =>
        <RecordRow
          key={'sectionRow' + index + ',' + i}
          allowEdit={allowEdit}
          uiMode={uiMode}
          picklists={picklists}
          onFieldValueUpdate={onFieldValueUpdate}
          error={error}
          editValues={editValues}
          row={row}
          sectionIndex={index}
          rowIndex={i} 
          objectInfo={objectInfo}
          recordView={recordView}
          onEditDepGraph={onEditDepGraph}/>
      )}
   </tbody>
  );
}
Dê uma olhada em seu IDE e examine o componente Record (Record.js). Ele inclui um componente <RecordSection>, um componente <DepGraphEditor> e vários componentes <RecordButton>.

Por fim, mas certamente não menos importante, a pasta auxiliares contém algumas funções auxiliares de JavaScript. Essas funções transformam as respostas da API de interface de usuário em modelos de dados que alimentam a interface de usuário do Visualizador de registros. O código recordLayout.js cria o modelo de dados interno para layouts, enquanto o código depGraphHelper.js cria o editor de lista de opções dependentes. Veremos esses arquivos em mais detalhe depois.

Recursos