Atualizar aplicação para criar e listar Contentores Incorporados do SharePoint

Concluído

Neste exercício, irá atualizar o projeto existente para criar e obter Contentores Incorporados do SharePoint.

Adicionar a funcionalidade de início de sessão ao React SPA de front-end

Vamos começar por adicionar suporte para o utilizador iniciar sessão no projeto de front-end do React SPA. Isto envolve adicionar algum código de configuração para a Biblioteca de Autenticação da Microsoft (MSAL) e configurar o Microsoft Graph Toolkit com os detalhes de início de sessão necessários.

Configurar o fornecedor de autenticação

Localize e abra o ficheiro ./src/index.tsx .

Adicione as seguintes importações após as importações existentes:

import { Providers } from "@microsoft/mgt-element";
import { Msal2Provider } from "@microsoft/mgt-msal2-provider";
import * as Constants from "./common/constants"
import * as Scopes from "./common/scopes";

Por fim, adicione o seguinte código antes do código de composição do React para configurar um fornecedor MSAL global para o Microsoft Graph Toolkit:

Providers.globalProvider = new Msal2Provider({
  clientId: Constants.CLIENT_ENTRA_APP_CLIENT_ID,
  authority: Constants.CLIENT_ENTRA_APP_AUTHORITY,
  scopes: [
    ...Scopes.GRAPH_OPENID_CONNECT_BASIC,
    Scopes.GRAPH_USER_READ_ALL,
    Scopes.GRAPH_FILES_READ_WRITE_ALL,
    Scopes.GRAPH_SITES_READ_ALL,
    Scopes.SPEMBEDDED_FILESTORAGECONTAINER_SELECTED
  ]
});

Atualizar a home page da aplicação para processar o processo de início de sessão e de início de sessão

Localize e abra o ficheiro ./src/App.tsx . Remova a importação do logo.svg ficheiro e, em seguida, adicione e atualize as restantes importações para refletir o seguinte código:

import React, {
  useState, useEffect
} from "react";
import {
  Providers,
  ProviderState
} from "@microsoft/mgt-element";
import { Login } from "@microsoft/mgt-react";
import {
  FluentProvider,
  Text,
  webLightTheme
} from "@fluentui/react-components"
import './App.css';
import {
  InteractionRequiredAuthError,
  PublicClientApplication
} from "@azure/msal-browser";
import * as Scopes from "./common/scopes";
import * as Constants from "./common/constants";

Após as instruções de importação, adicione o seguinte hook do React personalizado para obter o estado de início de sessão do utilizador atual:

function useIsSignedIn() {
  const [isSignedIn, setIsSignedIn] = useState(false);

  useEffect(() => {
    const updateState = async () => {
      const provider = Providers.globalProvider;
      setIsSignedIn(provider && provider.state === ProviderState.SignedIn);
    };

    Providers.onProviderUpdated(updateState);
    updateState();

    return () => {
      Providers.removeProviderUpdatedListener(updateState);
    }
  }, []);

  return isSignedIn;
}

Agora, atualize o App ao adicionar o seguinte código após a declaração:

const isSignedIn = useIsSignedIn();

const promptForContainerConsent = async (event: CustomEvent<undefined>): Promise<void> => {
  const containerScopes = {
    scopes: [Scopes.SPEMBEDDED_FILESTORAGECONTAINER_SELECTED],
    redirectUri: `${window.location.protocol}://${window.location.hostname}${(window.location.port === '80' || window.location.port === '443') ? '' : ':' + window.location.port}`
  };

  const msalInstance = new PublicClientApplication({
    auth: {
      clientId: Constants.CLIENT_ENTRA_APP_CLIENT_ID,
      authority: Constants.CLIENT_ENTRA_APP_AUTHORITY,
    },
    cache: {
      cacheLocation: 'localStorage',
      storeAuthStateInCookie: false,
    },
  });

  msalInstance.acquireTokenSilent(containerScopes)
    .then(response => {
      console.log('tokenResponse', JSON.stringify(response));
    })
    .catch(async (error) => {
      if (error instanceof InteractionRequiredAuthError) {
        return msalInstance.acquireTokenPopup(containerScopes);
      }
    });
}

Este código obterá primeiro o estado de início de sessão atual do utilizador e, em seguida, configurará e obterá um token de acesso assim que o utilizador selecionar um botão na composição.

Por último, atualize a composição do componente ao substituir a instrução do return() componente pelo seguinte:

return (
  <FluentProvider theme={webLightTheme}>
    <div className="App">
      <Text size={900} weight='bold'>Sample SPA SharePoint Embedded App</Text>
      <Login loginCompleted={promptForContainerConsent} />
      <div>
      </div>
    </div>
  </FluentProvider>
);

Testar a autenticação da aplicação React

Agora, vamos testar a aplicação React do lado do cliente para garantir que a autenticação está a funcionar.

Na linha de comandos na pasta raiz do projeto, execute o seguinte comando:

npm run start

O script irá criar o lado do servidor & projetos do lado do cliente, iniciá-los e iniciar um browser para o projeto do lado do cliente:

Captura de ecrã da aplicação React.

Selecione o botão Iniciar Sessão e inicie sessão com a conta Escolar e Profissional com acesso de administrador ao seu inquilino do Microsoft 365.

Depois de iniciar sessão com êxito, será redirecionado novamente para a sua aplicação React, apresentando o nome e o e-mail do utilizador com sessão iniciada:

Captura de ecrã da aplicação React com um utilizador com sessão iniciada.

Pare o servidor premindo CTRL + C na consola do .

Adicionar a capacidade de listar e selecionar Contentores

Com a configuração básica do projeto e configurada para suportar a autenticação do utilizador, vamos agora adicionar suporte à lista e selecionar Contentores na partição do seu inquilino.

A gestão de contentores é uma operação privilegiada que requer um token de acesso que tem de ser obtido do lado do servidor. Comecemos por criar as partes da API do lado do servidor para suportar a aplicação React.

Adicionar um método utilitário para obter um token OBO para chamar o Microsoft Graph

Primeiro, precisamos de um ficheiro utilitário para obter um token com o fluxo OAuth2 On-Behalf-Of com a nossa credencial existente.

Crie um novo ficheiro ./server/auth.ts e adicione o seguinte código ao mesmo:

import { ConfidentialClientApplication } from "@azure/msal-node";
require('isomorphic-fetch');
import * as MSGraph from '@microsoft/microsoft-graph-client';
import * as Scopes from './common/scopes';

export const getGraphToken = async (confidentialClient: ConfidentialClientApplication, token: string): Promise<[boolean, string | any]> => {
  try {
    const graphTokenRequest = {
      oboAssertion: token,
      scopes: [
        Scopes.GRAPH_SITES_READ_ALL,
        Scopes.SPEMBEDDED_FILESTORAGECONTAINER_SELECTED
      ]
    };
    const oboGraphToken = (await confidentialClient.acquireTokenOnBehalfOf(graphTokenRequest))!.accessToken;
    return [true, oboGraphToken];
  } catch (error: any) {
    const errorResult = {
      status: 500,
      body: JSON.stringify({
        message: `Unable to generate Microsoft Graph OBO token: ${error.message}`,
        providedToken: token
      })
    };
    return [false, errorResult];
  }
}

Esta ação irá utilizar um confidentialClientApplication configurado e o token de ID do utilizador e utilizará a biblioteca MSAL para pedir um novo token que possamos utilizar para chamar o Microsoft Graph.

Adicionar a Gestão de contentores ao projeto de API do lado do servidor

Agora, vamos criar o processador para obter uma lista dos Contentores que utilizam o Microsoft Graph para serem devolvidos à nossa aplicação React. Crie um novo ficheiro ./server/listContainers.ts e adicione as seguintes importações ao mesmo:

import {
  Request,
  Response
} from "restify";
import * as MSAL from "@azure/msal-node";
require('isomorphic-fetch');
import * as MSGraph from '@microsoft/microsoft-graph-client';
import { getGraphToken } from "./auth";

Em seguida, adicione o seguinte código para criar uma instância de uma MSAL ConfidentialClientApplication que será utilizada para obter o token de acesso OBO:

const msalConfig: MSAL.Configuration = {
  auth: {
    clientId: process.env['API_ENTRA_APP_CLIENT_ID']!,
    authority: process.env['API_ENTRA_APP_AUTHORITY']!,
    clientSecret: process.env['API_ENTRA_APP_CLIENT_SECRET']!
  },
  system: {
    loggerOptions: {
      loggerCallback(loglevel: any, message: any, containsPii: any) {
        console.log(message);
      },
      piiLoggingEnabled: false,
      logLevel: MSAL.LogLevel.Verbose,
    }
  }
};

const confidentialClient = new MSAL.ConfidentialClientApplication(msalConfig);

Crie e exporte uma nova função que fará o seguinte:

  • Verifique se o pedido inclui um Authorization cabeçalho com um token de acesso.
  • Utilize esse token e o ConfidentialClientApplication para obter um token OBO que podemos utilizar para chamar o Microsoft Graph.
  • Utilize o token OBO para criar um novo AuthenticationProvider cliente que iremos utilizar para chamar o Microsoft Graph.
export const listContainers = async (req: Request, res: Response) => {
  if (!req.headers.authorization) {
    res.send(401, { message: 'No access token provided.' });
    return;
  }

  const [bearer, token] = (req.headers.authorization || '').split(' ');

  const [graphSuccess, oboGraphToken] = await getGraphToken(confidentialClient, token);

  if (!graphSuccess) {
    res.send(200, oboGraphToken);
    return;
  }

  const authProvider = (callback: MSGraph.AuthProviderCallback) => {
    callback(null, oboGraphToken);
  };
}

O último passo é criar o cliente do Microsoft Graph e pedir todos os Contentores que tenham um conjunto específico ContainerTypeId . Adicione o seguinte imediatismo de código antes do parêntese reto de fecho da função:

try {
  const graphClient = MSGraph.Client.init({
    authProvider: authProvider,
    defaultVersion: 'beta'
  });

  const graphResponse = await graphClient.api(`storage/fileStorage/containers?$filter=containerTypeId eq ${process.env["CONTAINER_TYPE_ID"]}`).get();

  res.send(200, graphResponse);
  return;
} catch (error: any) {
  res.send(500, { message: `Unable to list containers: ${error.message}` });
  return;
}

Adicione este novo ponto final ao nosso servidor restify. Localize e abra o ficheiro ./server/index.ts , adicione uma instrução de importação única no final das importações existentes e adicione um serviço de escuta para pedidos HTTP GET ao /api/listContainers ponto final:

import { listContainers } from "./listContainers";
...

server.get('/api/listContainers', async (req, res, next) => {
  try {
    const response = await listContainers(req, res);
    res.send(200, response)
  } catch (error: any) {
    res.send(500, { message: `Error in API server: ${error.message}` });
  }
  next();
});

Atualizar o projeto do React para apresentar os nossos Contentores

Com a configuração da API do lado do servidor, podemos atualizar o nosso projeto react para fornecer aos utilizadores uma interface para selecionar um Contentor existente ou criar um novo Contentor.

Comece por criar uma nova interface, ./src/common/IContainer.ts, com os seguintes conteúdos para representar o objeto que iremos enviar e receber das chamadas do Microsoft Graph:

export interface IContainer {
  id: string;
  displayName: string;
  containerTypeId: string;
  createdDateTime: string;
}

Crie um novo serviço que será utilizado para chamar o nosso ponto final da API ou efetuar chamadas diretas da aplicação React para o Microsoft Graph. Crie um novo ficheiro ./src/services/spembedded.ts e adicione o seguinte código ao mesmo:

import { Providers, ProviderState } from '@microsoft/mgt-element';
import * as Msal from '@azure/msal-browser';
import * as Constants from './../common/constants';
import * as Scopes from './../common/scopes';
import { IContainer } from './../common/IContainer';

export default class SpEmbedded {
}

Em seguida, adicione uma função utilitária para obter um token de acesso que podemos utilizar para chamar o Microsoft Graph. Esta ação irá criar uma MSAL PublicClientApplication que iremos utilizar para submeter para chamar a nossa API do lado do servidor. Adicione a seguinte função à classe SharePoint Embedded:

async getApiAccessToken() {
  const msalConfig: Msal.Configuration = {
    auth: {
      clientId: Constants.CLIENT_ENTRA_APP_CLIENT_ID,
      authority: Constants.CLIENT_ENTRA_APP_AUTHORITY,
    },
    cache: {
      cacheLocation: 'localStorage',
      storeAuthStateInCookie: false
    }
  };

  const scopes: Msal.SilentRequest = {
    scopes: [`api://${Constants.CLIENT_ENTRA_APP_CLIENT_ID}/${Scopes.SPEMBEDDED_CONTAINER_MANAGE}`],
    prompt: 'select_account',
    redirectUri: `${window.location.protocol}//${window.location.hostname}${(window.location.port === '80' || window.location.port === '443') ? '' : ':' + window.location.port}`
  };

  const publicClientApplication = new Msal.PublicClientApplication(msalConfig);
  await publicClientApplication.initialize();

  let tokenResponse;
  try {
    tokenResponse = await publicClientApplication.acquireTokenSilent(scopes);
    return tokenResponse.accessToken;
  } catch (error) {
    if (error instanceof Msal.InteractionRequiredAuthError) {
      tokenResponse = await publicClientApplication.acquireTokenPopup(scopes);
      return tokenResponse.accessToken;
    }
    console.log(error)
    return null;
  }
};

Adicione o seguinte listContainers() método que irá chamar a nossa API do lado do servidor para obter uma lista de todos os nossos Contentores. Esta ação irá obter o token de acesso devolvido pelo nosso método utilitário e incluí-lo nas chamadas à API do lado do servidor. Lembre-se de que este token de acesso é utilizado para criar um MSAL ConfidentialClientApplication para obter um token OBO para chamar o Microsoft Graph. Esse token OBO tem mais permissões concedidas que só podemos obter a partir de uma chamada do lado do servidor:

async listContainers(): Promise<IContainer[] | undefined> {
  const api_endpoint = `${Constants.API_SERVER_URL}/api/listContainers`;

  if (Providers.globalProvider.state === ProviderState.SignedIn) {
    const token = await this.getApiAccessToken();
    const containerRequestHeaders = {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    };
    const containerRequestOptions = {
      method: 'GET',
      headers: containerRequestHeaders
    };
    const response = await fetch(api_endpoint, containerRequestOptions);

    if (response.ok) {
      const containerResponse = await response.json();
      return (containerResponse.value)
        ? (containerResponse.value) as IContainer[]
        : undefined;
    } else {
      console.error(`Unable to list Containers: ${JSON.stringify(response)}`);
      return undefined;
    }
  }
};

Agora, crie um novo componente do React que irá processar todas as nossas tarefas de Contentor e IU. Crie um novo ficheiro, ./src/components/containers.tsx, e adicione o seguinte código ao mesmo:

import React, { useEffect, useState } from 'react';
import {
  Button,
  Dialog, DialogActions, DialogContent, DialogSurface, DialogBody, DialogTitle, DialogTrigger,
  Dropdown, Option,
  Input, InputProps, InputOnChangeData,
  Label,
  Spinner,
  makeStyles, shorthands, useId
} from '@fluentui/react-components';
import type {
  OptionOnSelectData,
  SelectionEvents
} from '@fluentui/react-combobox'
import { IContainer } from "./../common/IContainer";
import SpEmbedded from '../services/spembedded';

const spe = new SpEmbedded();

const useStyles = makeStyles({
  root: {
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    ...shorthands.padding('25px'),
  },
  containerSelector: {
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    rowGap: '10px',
    ...shorthands.padding('25px'),
  },
  containerSelectorControls: {
    width: '400px',
  },
  dialogContent: {
    display: 'flex',
    flexDirection: 'column',
    rowGap: '10px',
    marginBottom: '25px'
  }
});

export const Containers = (props: any) => {
  // BOOKMARK 1 - constants & hooks

  // BOOKMARK 2 - handlers go here

  // BOOKMARK 3 - component rendering
  return (
  );
}

export default Containers;

Observação

Repare nos // BOOKMARK # comentários no componente. Para garantir que está a adicionar código nos locais corretos.

O primeiro passo é obter uma lista dos Contentores. Comece por adicionar o seguinte código antes // BOOKMARK 1de . Isto irá definir alguns valores de estado para conter os Contentores obtidos a partir da nossa API do lado do servidor:

const [containers, setContainers] = useState<IContainer[]>([]);
const [selectedContainer, setSelectedContainer] = useState<IContainer | undefined>(undefined);
const containerSelector = useId('containerSelector');

Em seguida, adicione o seguinte hook do React após // BOOKMARK 1 para obter todos os Contentores quando a página for carregada e definir o objeto de estado que irá controlá-los:

useEffect(() => {
  (async () => {
    const containers = await spe.listContainers();
    if (containers) {
      setContainers(containers);
    }
  })();
}, []);

Crie um processador de eventos após a implementação do hook do React que acabou de adicionar que será acionado quando um utilizador selecionar um Contentor no controlo de lista pendente que iremos adicionar à nossa UX:

const onContainerDropdownChange = (event: SelectionEvents, data: OptionOnSelectData) => {
  const selected = containers.find((container) => container.id === data.optionValue);
  setSelectedContainer(selected);
};

Atualize a composição ao adicionar o seguinte ao return() método após o // BOOKMARK 3 código. Esta ação irá criar um DropDown controlo e um marcador de posição onde iremos adicionar uma lista dos conteúdos no Contentor selecionado:

<div className={styles.root}>
  <div className={styles.containerSelector}>
    <Dropdown
      id={containerSelector}
      placeholder="Select a Storage Container"
      className={styles.containerSelectorControls}
      onOptionSelect={onContainerDropdownChange}>
      {containers.map((option) => (
        <Option key={option.id} value={option.id}>{option.displayName}</Option>
      ))}
    </Dropdown>
  </div>
  {selectedContainer && (`[[TOOD]] container "${selectedContainer.displayName}" contents go here`)}
</div>

Este código utiliza um objeto de estilo que adicionámos ao componente quando o criámos inicialmente. Para utilizar os estilos, adicione o seguinte código imediatamente antes da return() instrução e // BOOKMARK 3 comentário:

const styles = useStyles();

O último passo é adicionar o nosso novo Containerscomponente à aplicação. Localize e abra o ./src/App.tsx ficheiro e adicione a seguinte importação após as importações existentes:

import Containers from "./components/containers";

Localize a <div></div> marcação no componente return(). Substitua essa marcação pelo seguinte para adicionar o nosso Containers componente apenas se o utilizador tiver sessão iniciada:

<div>
  {isSignedIn && (<Containers />)}
</div>

Testar a listagem e selecionar Contentores

Agora, vamos testar a aplicação React do lado do cliente para ver os efeitos das nossas alterações à lista e apresentar Contentores na aplicação React.

Na linha de comandos na pasta raiz do projeto, execute o seguinte comando:

npm run start

Quando o browser for carregado, inicie sessão com a mesma conta Escolar e Profissional que tem utilizado.

Após o início de sessão, a página será recarregada e deverá apresentar uma lista de Contentores, se tiver criado anteriormente algum.

Captura de ecrã da aplicação React a listar todos os nossos Contentores.

Quando seleciona um Contentor, repare que a lógica condicional mostrará o marcador de posição com o nome do Contentor selecionado:

Captura de ecrã da aplicação React depois de selecionar um Contentor.

Pare o servidor premindo CTRL + C na consola do .

Adicionar a capacidade de criar novos Contentores

Nesta secção, irá atualizar a API do lado do servidor e a aplicação React para criar novas conténs a partir da nossa aplicação Web.

Comecemos por criar as partes da API do lado do servidor para suportar a aplicação React.

Adicionar suporte para criar Contentores para o projeto de API do lado do servidor

Agora, vamos criar o processador para criar um Contentor com o Microsoft Graph para voltar à nossa aplicação React. Crie um novo ficheiro ./server/createContainer.ts e adicione as seguintes importações ao mesmo:

import {
  Request,
  Response
} from "restify";
import * as MSAL from "@azure/msal-node";
require('isomorphic-fetch');
import * as MSGraph from '@microsoft/microsoft-graph-client';
import { getGraphToken } from "./auth";

Em seguida, adicione o seguinte código para criar uma instância de uma MSAL ConfidentialClientApplication que será utilizada para obter o token de acesso OBO:

const msalConfig: MSAL.Configuration = {
  auth: {
    clientId: process.env['API_ENTRA_APP_CLIENT_ID']!,
    authority: process.env['API_ENTRA_APP_AUTHORITY']!,
    clientSecret: process.env['API_ENTRA_APP_CLIENT_SECRET']!
  },
  system: {
    loggerOptions: {
      loggerCallback(loglevel: any, message: any, containsPii: any) {
        console.log(message);
      },
      piiLoggingEnabled: false,
      logLevel: MSAL.LogLevel.Verbose,
    }
  }
};

const confidentialClient = new MSAL.ConfidentialClientApplication(msalConfig);

Crie e exporte uma nova função que fará o seguinte:

  • Verifique se o pedido inclui um Authorization cabeçalho com um token de acesso.
  • Utilize esse token e o ConfidentialClientApplication para obter um token OBO que podemos utilizar para chamar o Microsoft Graph.
  • Utilize o token OBO para criar um novo AuthenticationProvider cliente que iremos utilizar para chamar o Microsoft Graph.
export const createContainer = async (req: Request, res: Response) => {
  if (!req.headers.authorization) {
    res.send(401, { message: 'No access token provided.' });
    return;
  }

  const [bearer, token] = (req.headers.authorization || '').split(' ');

  const [graphSuccess, graphTokenRequest] = await getGraphToken(confidentialClient, token);

  if (!graphSuccess) {
    res.send(200, graphTokenRequest);
    return;
  }

  const authProvider = (callback: MSGraph.AuthProviderCallback) => {
    callback(null, graphTokenRequest);
  };
}

O último passo é criar o cliente do Microsoft Graph e submeter o pedido para criar um novo Conjunto de contentores para um específico ContainerTypeId. Adicione o seguinte imediatismo de código antes do parêntese reto de fecho da função:

try {
  const graphClient = MSGraph.Client.init({
    authProvider: authProvider,
    defaultVersion: 'beta'
  });

  const containerRequestData = {
    displayName: req.body!.displayName,
    description: (req.body?.description) ? req.body.description : '',
    containerTypeId: process.env["CONTAINER_TYPE_ID"]
  };

  const graphResponse = await graphClient.api(`storage/fileStorage/containers`).post(containerRequestData);

  res.send(200, graphResponse);
  return;
} catch (error: any) {
  res.send(500, { message: `Failed to create container: ${error.message}` });
  return;
}

Adicione este novo ponto final ao nosso servidor restify. Localize e abra o ficheiro ./server/index.ts , adicione uma instrução de importação única no final das importações existentes e adicione um serviço de escuta para pedidos HTTP POST ao /api/createContainers ponto final:

import { createContainer } from "./createContainer";

...

server.post('/api/createContainer', async (req, res, next) => {
  try {
    const response = await createContainer(req, res);
    res.send(200, response)
  } catch (error: any) {
    res.send(500, { message: `Error in API server: ${error.message}` });
  }
  next();
});

Atualizar o projeto do React para criar um novo Contentor

Localize e abra o ficheiro ./src/services/spembedded.ts e adicione o seguinte código à classe :

async createContainer(containerName: string, containerDescription: string = ''): Promise<IContainer | undefined> {
  const api_endpoint = `${Constants.API_SERVER_URL}/api/createContainer`;

  if (Providers.globalProvider.state === ProviderState.SignedIn) {
    const token = await this.getApiAccessToken();
    const containerRequestHeaders = {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    };

    const containerRequestData = {
      displayName: containerName,
      description: containerDescription
    };
    const containerRequestOptions = {
      method: 'POST',
      headers: containerRequestHeaders,
      body: JSON.stringify(containerRequestData)
    };

    const response = await fetch(api_endpoint, containerRequestOptions);

    if (response.ok) {
      const containerResponse = await response.json();
      return containerResponse as IContainer;
    } else {
      console.error(`Unable to create container: ${JSON.stringify(response)}`);
      return undefined;
    }
  }
};

Este novo método é semelhante ao método existente listContainers() , exceto que cria um novo objeto e submete-o como um POST para a nossa API do lado do servidor.

O último passo é atualizar o nosso Containers componente para atualizar a IU para suportar a criação de um Contentor. Localize e abra o ficheiro ./src/components/containers.tsx .

Para este passo, vamos utilizar um componente Fluent UI React Dialog . Comece por adicionar os seguintes objetos de estado e objetos de ID do componente da IU imediatamente antes do // BOOKMARK 1 comentário:

const [dialogOpen, setDialogOpen] = useState(false);
const containerName = useId('containerName');
const [name, setName] = useState('');
const containerDescription = useId('containerDescription');
const [description, setDescription] = useState('');
const [creatingContainer, setCreatingContainer] = useState(false);

Em seguida, adicione o seguinte código imediatamente antes do // BOOKMARK 2 comentário. Estes processadores são utilizados para atualizar as propriedades de nome e descrição do novo Contentor a Input partir dos componentes que estarão no Dialog. Também são utilizados para processar com o utilizador e seleciona um botão para criar o Contentor:

const handleNameChange: InputProps["onChange"] = (event: React.ChangeEvent<HTMLInputElement>, data: InputOnChangeData): void => {
  setName(data?.value);
};

const handleDescriptionChange: InputProps["onChange"] = (event: React.ChangeEvent<HTMLInputElement>, data: InputOnChangeData): void => {
  setDescription(data?.value);
};

const onContainerCreateClick = async (event: React.MouseEvent<HTMLButtonElement>): Promise<void> => {
  setCreatingContainer(true);
  const newContainer = await spe.createContainer(name, description);

  if (newContainer) {
    setName('');
    setDescription('');
    setContainers(current => [...current, newContainer]);
    setSelectedContainer(newContainer);
    setDialogOpen(false);
  } else {
    setName('');
    setDescription('');
  }
  setCreatingContainer(false);
}

Por fim, adicione o seguinte código react imediatamente após o elemento de fecho </Dropdown> . Isto irá criar um componente Fluent UI React Dialog que é acionado a partir de um botão:

<Dialog open={dialogOpen} onOpenChange={(event, data) => setDialogOpen(data.open)}>

  <DialogTrigger disableButtonEnhancement>
    <Button className={styles.containerSelectorControls} appearance='primary'>Create a new storage Container</Button>
  </DialogTrigger>

  <DialogSurface>
    <DialogBody>
      <DialogTitle>Create a new storage Container</DialogTitle>

      <DialogContent className={styles.dialogContent}>
        <Label htmlFor={containerName}>Container name:</Label>
        <Input id={containerName} className={styles.containerSelectorControls} autoFocus required
          value={name} onChange={handleNameChange}></Input>
        <Label htmlFor={containerDescription}>Container description:</Label>
        <Input id={containerDescription} className={styles.containerSelectorControls} autoFocus required
          value={description} onChange={handleDescriptionChange}></Input>
        {creatingContainer &&
          <Spinner size='medium' label='Creating storage Container...' labelPosition='after' />
        }
      </DialogContent>

      <DialogActions>
        <DialogTrigger disableButtonEnhancement>
          <Button appearance="secondary" disabled={creatingContainer}>Cancel</Button>
        </DialogTrigger>
        <Button appearance="primary"
          value={name}
          onClick={onContainerCreateClick}
          disabled={creatingContainer || (name === '')}>Create storage Container</Button>
      </DialogActions>
    </DialogBody>
  </DialogSurface>

</Dialog>

Testar a criação de novos Contentores

Agora, vamos testar a aplicação React do lado do cliente para ver os efeitos das nossas alterações para criar Contentores na aplicação React.

Na linha de comandos na pasta raiz do projeto, execute o seguinte comando:

npm run start

Quando o browser for carregado, inicie sessão com a mesma conta Escolar e Profissional que tem utilizado.

Após o início de sessão, a página será recarregada e deverá agora incluir um botão para iniciar a caixa de diálogo:

Captura de ecrã da aplicação React com um botão para iniciar a caixa de diálogo.

Selecione o botão Criar um novo Contentor de armazenamento para abrir a caixa de diálogo. Repare como o botão está desativado até introduzir um nome:

Captura de ecrã da caixa de diálogo para criar um novo Contentor.

Introduza um nome e uma descrição para o Contentor e selecione Criar Contentor de Armazenamento. Ao criar o Contentor, os botões são desativados e um Spinner controlo mostra ao utilizador que está a trabalhar:

Captura de ecrã a mostrar a caixa de diálogo a criar o Contentor.

Assim que a caixa de diálogo desaparecer, verá que o seletor novo apresenta o nosso novo Contentor!

Captura de ecrã a mostrar o seletor atualizado com o nosso novo Contentor.

Pare o servidor premindo CTRL + C na consola do .

Resumo

Neste exercício, atualizou o projeto existente para criar e obter Contentores Do SharePoint Embedded.

Verifique seu conhecimento

1.

Qual é o objetivo da ConfidentialClientApplication na API do lado do servidor?

2.

Que operação não pode ser executada diretamente pela aplicação React e requer a API do lado do servidor?