Exercício – Importar conteúdo externo

Concluído

Neste exercício, vai expandir o conector personalizado do Microsoft Graph com o código para importar ficheiros markdown locais para o Microsoft 365.

Antes de começar

Antes de realizar este exercício, certifique-se de que conclui o exercício anterior neste módulo.

Para seguir este exercício, copie os ficheiros de conteúdo de exemplo utilizados neste exercício a partir do GitHub e armazene-os no seu projeto, numa pasta com o nome conteúdo.

Captura de ecrã de um editor de código a mostrar os ficheiros de conteúdo utilizados neste exercício.

Para que o código funcione corretamente, a pasta de conteúdos e os respetivos conteúdos têm de ser copiados para a pasta de saída da compilação.

No editor de código:

  1. Abra o ficheiro .csproj e, antes da </Project> etiqueta, adicione o seguinte código:

    <ItemGroup>
      <ContentFiles Include="content\**" CopyToOutputDirectory="PreserveNewest" />
    </ItemGroup>
    
    <Target Name="CopyContentFolder" AfterTargets="Build">
      <Copy SourceFiles="@(ContentFiles)" DestinationFiles="@(ContentFiles->'$(OutputPath)\content\%(RecursiveDir)%(Filename)%(Extension)')" />
    </Target>
    
  2. Salve suas alterações.

Adicionar bibliotecas para analisar Markdown e YAML

O conector do Microsoft Graph que está a criar importa ficheiros Markdown locais para o Microsoft 365. Cada um destes ficheiros contém um cabeçalho com metadados no formato YAML, também conhecido como frontmatter. Além disso, os conteúdos de cada ficheiro são escritos em Markdown. Para extrair metadados e converter o corpo em HTML, utilize bibliotecas personalizadas:

  1. Abra um terminal e altere o diretório de trabalho para o projeto.
  2. Para adicionar a biblioteca de processamento de Markdown, execute o seguinte comando: dotnet add package Markdig.
  3. Para adicionar a biblioteca de processamento YAML, execute o seguinte comando: dotnet add package YamlDotNet.

Definir classe para representar o ficheiro importado

Para simplificar o trabalho com ficheiros markdown importados e os respetivos conteúdos, vamos definir uma classe com as propriedades necessárias.

No editor de código:

  1. Abra o ficheiro ContentService.cs .

  2. Adicione a using instrução e atualize a classe da Document seguinte forma:

    using YamlDotNet.Serialization;
    
    public interface IMarkdown
    {
      string? Markdown { get; set; }
    }
    
    class Document : IMarkdown
    {
      [YamlMember(Alias = "title")]
      public string? Title { get; set; }
      [YamlMember(Alias = "description")]
      public string? Description { get; set; }
      public string? Markdown { get; set; }
      public string? Content { get; set; }
      public string? RelativePath { get; set; }
      public string? IconUrl { get; set; }
      public string? Url { get; set; }
    }
    

    A IMarkdown interface representa o conteúdo do ficheiro markdown local. Tem de ser definido separadamente para suportar a desserialização de conteúdos de ficheiros. A Document classe representa o documento final com propriedades YAML analisadas e conteúdo HTML. YamlMember os atributos mapeiam propriedades para metadados no cabeçalho de cada documento.

  3. Salve suas alterações.

Implementar o carregamento de ficheiros Markdown

O passo seguinte consiste em implementar a lógica que carrega os ficheiros markdown locais, extrai metadados e converte o conteúdo em HTML.

Em primeiro lugar, adicione métodos auxiliares para utilizar facilmente as Markdig bibliotecas e YamlDotNet .

No editor de código:

  1. Crie um novo ficheiro com o nome MarkdownExtensions.cs.

  2. No ficheiro, adicione o seguinte código:

    // from: https://khalidabuhakmeh.com/parse-markdown-front-matter-with-csharp
    using Markdig;
    using Markdig.Extensions.Yaml;
    using Markdig.Syntax;
    using YamlDotNet.Serialization;
    
    public static class MarkdownExtensions
    {
      private static readonly IDeserializer YamlDeserializer =
          new DeserializerBuilder()
          .IgnoreUnmatchedProperties()
          .Build();
    
      private static readonly MarkdownPipeline Pipeline
          = new MarkdownPipelineBuilder()
          .UseYamlFrontMatter()
          .Build();
    }
    

    A YamlDeserializer propriedade define um novo desserializador para o bloco YAML em cada um dos ficheiros markdown que está a extrair. Configure o desserializador para ignorar todas as propriedades que não fazem parte da classe para a qual o ficheiro é desserializado.

    A Pipeline propriedade define um pipeline de processamento para o analisador de markdown. Configure-o para analisar o cabeçalho YAML. Sem esta configuração, as informações do cabeçalho seriam eliminadas.

  3. Em seguida, expanda a MarkdownExtensions classe com o seguinte código:

    public static T GetContents<T>(this string markdown) where T : IMarkdown, new()
    {
      var document = Markdown.Parse(markdown, Pipeline);
      var block = document
          .Descendants<YamlFrontMatterBlock>()
          .FirstOrDefault();
    
      if (block == null)
        return new T { Markdown = markdown };
    
      var yaml =
          block
          // this is not a mistake
          // we have to call .Lines 2x
          .Lines // StringLineGroup[]
          .Lines // StringLine[]
          .OrderByDescending(x => x.Line)
          .Select(x => $"{x}\n")
          .ToList()
          .Select(x => x.Replace("---", string.Empty))
          .Where(x => !string.IsNullOrWhiteSpace(x))
          .Aggregate((s, agg) => agg + s);
    
      var t = YamlDeserializer.Deserialize<T>(yaml);
      t.Markdown = markdown.Substring(block.Span.End + 1);
      return t;
    }
    

    O GetContents método converte uma cadeia de markdown com metadados YAML no cabeçalho para o tipo especificado, que implementa a IMarkdown interface. A partir da cadeia de markdown, extrai o cabeçalho YAML e desserializa-o para o tipo especificado. Em seguida, extrai o corpo do artigo e define-o para a Markdown propriedade para processamento adicional.

  4. Salve suas alterações.

Extrair conteúdo de markdown e YAML

Com os métodos auxiliares implementados, implemente o Extract método para carregar os ficheiros markdown locais e extrair informações dos mesmos.

No editor de código:

  1. Abra o ficheiro ContentService.cs .

  2. Na parte superior do ficheiro, adicione a seguinte instrução using:

    using Markdig;
    
  3. Em seguida, na ContentService classe , implemente o Extract método com o seguinte código:

    static IEnumerable<Document> Extract()
    {
      var docs = new List<Document>();
    
      var contentFolder = "content";
      var baseUrl = new Uri("https://learn.microsoft.com/graph/");
      var contentFolderPath = Path.Combine(Directory.GetCurrentDirectory(), contentFolder);
      var files = Directory.GetFiles(contentFolder, "*.md", SearchOption.AllDirectories);
    
      foreach (var file in files)
      {
        try
        {
          var contents = File.ReadAllText(file);
          var doc = contents.GetContents<Document>();
          doc.Content = Markdown.ToHtml(doc.Markdown ?? "");
          doc.RelativePath = Path.GetRelativePath(contentFolderPath, file);
          doc.Url = new Uri(baseUrl, doc.RelativePath!.Replace(".md", "")).ToString();
          doc.IconUrl = "https://raw.githubusercontent.com/waldekmastykarz/img/main/microsoft-graph.png";
          docs.Add(doc);
        }
        catch (Exception ex)
        {
          Console.WriteLine(ex.Message);
        }
      }
    
      return docs;
    }
    

    O método começa com o carregamento de ficheiros markdown a partir da pasta de conteúdo . Para cada ficheiro, carrega o respetivo conteúdo como uma cadeia. Converte a cadeia num objeto com os metadados e o conteúdo armazenados em propriedades separadas com o GetContents método de extensão definido anteriormente na MarkdownExtensions classe . Em seguida, converte a cadeia de markdown em HTML. Cria o respetivo URL na Internet através do caminho relativo do ficheiro. Por fim, armazena o caminho relativo para o ficheiro e adiciona o objeto a uma coleção para processamento adicional.

  4. Salve suas alterações.

Transformar conteúdo em itens externos

Depois de ler o conteúdo externo, o passo seguinte consiste em transformá-lo em itens externos, que serão carregados para o Microsoft 365.

Comece por implementar o GetDocId método que gera um ID exclusivo para cada item externo com base no respetivo caminho de ficheiro relativo.

No editor de código:

  1. Confirme que está a editar o ficheiro ContentService.cs .

  2. ContentService Na classe , adicione o seguinte método:

    static string GetDocId(Document doc)
    {
      var id = doc.RelativePath!
        .Replace(Path.DirectorySeparatorChar.ToString(), "__")
        .Replace(".md", "");
      return id;
    }
    

    O GetDocId método utiliza o caminho de ficheiro relativo do documento e substitui todos os separadores de diretórios por um caráter de sublinhado duplo. Isto é necessário porque os carateres separadores de caminho não podem ser utilizados num ID de item externo.

  3. Salve suas alterações.

Agora, implemente o Transform método , que converte objetos que representam ficheiros markdown locais em itens externos do Microsoft Graph.

No editor de código:

  1. Confirme que está no ficheiro ContentService.cs .

  2. Implemente o Transform método com o seguinte código:

    static IEnumerable<ExternalItem> Transform(IEnumerable<Document> documents)
    {
      return documents.Select(doc =>
      {
        var docId = GetDocId(doc);
        return new ExternalItem
        {
          Id = docId,
          Properties = new()
          {
            AdditionalData = new Dictionary<string, object> {
                { "title", a.Title ?? "" },
                { "description", a.Description ?? "" },
                { "url", new Uri(baseUrl, a.RelativePath!.Replace(".md", "")).ToString() }
            }
          },
          Content = new()
          {
            Value = a.Content ?? "",
            Type = ExternalItemContentType.Html
          },
          Acl = new()
          {
              new()
              {
                Type = AclType.Everyone,
                Value = "everyone",
                AccessType = AccessType.Grant
              }
          }
        };
      });
    }
    

    Em primeiro lugar, defina um URL base. Utilize este URL para criar um URL completo para cada item, para que, quando o item for apresentado aos utilizadores, estes possam navegar para o item original. Em seguida, vai transformar cada item de um DocsArticle num ExternalItem. Comece por obter um ID exclusivo para cada item com base no respetivo caminho de ficheiro relativo. Em seguida, vai criar uma nova instância de ExternalItem e preencher as respetivas propriedades com informações do DocsArticle. Em seguida, defina o conteúdo do item para o conteúdo HTML extraído do ficheiro local e defina o tipo de conteúdo do item como HTML. Por fim, configure a permissão do item para que fique visível para todas as pessoas na organização.

  3. Salve suas alterações.

Carregar itens externos para o Microsoft 365

O último passo do processamento do conteúdo é carregar os itens externos transformados para o Microsoft 365.

No editor de código:

  1. Verifique se está a editar o ficheiro ContentService.cs .

  2. ContentService Na classe , implemente o Load método com o seguinte código:

    static async Task Load(IEnumerable<ExternalItem> items)
    {
      foreach (var item in items)
      {
        Console.Write(string.Format("Loading item {0}...", item.Id));
    
        try
        {
          await GraphService.Client.External
            .Connections[Uri.EscapeDataString(ConnectionConfiguration.   ExternalConnection.Id!)]
            .Items[item.Id]
            .PutAsync(item);
    
          Console.WriteLine("DONE");
        }
        catch (Exception ex)
        {
          Console.WriteLine("ERROR");
          Console.WriteLine(ex.Message);
        }
      }
    }
    

    Para cada item externo, utilize o SDK .NET do Microsoft Graph para chamar a Microsoft Graph API e carregar o item. No pedido, especifique o ID da ligação externa criada anteriormente, o ID do item a carregar e o conteúdo completo do item.

  3. Salve suas alterações.

Adicionar o comando de carregamento de conteúdos

Antes de poder testar o código, tem de expandir a aplicação de consola com um comando que invoca a lógica de carregamento de conteúdos.

No editor de código:

  1. Abra o ficheiro Program.cs .

  2. Adicione um novo comando para carregar conteúdo com o seguinte código:

    var loadContentCommand = new Command("load-content", "Loads content   into the external connection");
    loadContentCommand.SetHandler(ContentService.LoadContent);
    
  3. Registe o comando recentemente definido com o comando raiz para que possa ser invocado com o seguinte código:

    rootCommand.AddCommand(loadContentCommand);
    
  4. Salve suas alterações.

Testar o código

A última coisa que resta é testar se o conector do Microsoft Graph importa corretamente conteúdo externo.

  1. Abrir um terminal.
  2. Altere o diretório de trabalho para o projeto.
  3. Crie o projeto ao executar o dotnet build comando .
  4. Comece a carregar o conteúdo ao executar o dotnet run -- load-content comando .
  5. Aguarde até que o comando seja concluído e carregue o conteúdo.