Übung: Importieren externer Inhalte

Abgeschlossen

In dieser Übung erweitern Sie den benutzerdefinierten Microsoft Graph-Connector um den Code, um lokale Markdowndateien in Microsoft 365 zu importieren.

Vorbereitende Schritte

Bevor Sie mit dieser Übung beginnen, sollten Sie die erste Übung in diesem Modul bereits abgeschlossen haben.

Kopieren Sie für diese Übung die in dieser Übung verwendeten Beispielinhaltsdateien aus GitHub , und speichern Sie sie in Ihrem Projekt in einem Ordner namens content.

Screenshot eines Code-Editors mit den in dieser Übung verwendeten Inhaltsdateien.

Damit der Code ordnungsgemäß funktioniert, müssen der Inhaltsordner und seine Inhalte in den Buildausgabeordner kopiert werden.

Innerhalb des Code-Editors:

  1. Öffnen Sie die CSPROJ-Datei , und fügen Sie vor dem </Project> Tag den folgenden Code hinzu:

    <ItemGroup>
      <ContentFiles Include="content\**" CopyToOutputDirectory="PreserveNewest" />
    </ItemGroup>
    
    <Target Name="CopyContentFolder" AfterTargets="Build">
      <Copy SourceFiles="@(ContentFiles)" DestinationFiles="@(ContentFiles->'$(OutputPath)\content\%(RecursiveDir)%(Filename)%(Extension)')" />
    </Target>
    
  2. Speichern Sie Ihre Änderungen.

Hinzufügen von Bibliotheken zum Analysieren von Markdown und YAML

Der Microsoft Graph-Connector, den Sie erstellen, importiert lokale Markdowndateien in Microsoft 365. Jede dieser Dateien enthält einen Header mit Metadaten im YAML-Format, auch als Frontmatter bezeichnet. Darüber hinaus werden die Inhalte jeder Datei in Markdown geschrieben. Um Metadaten zu extrahieren und den Text in HTML zu konvertieren, verwenden Sie benutzerdefinierte Bibliotheken:

  1. Öffnen Sie ein Terminal, und ändern Sie das Arbeitsverzeichnis in Ihr Projekt.
  2. Führen Sie den folgenden Befehl aus, um die Markdownverarbeitungsbibliothek hinzuzufügen: dotnet add package Markdig.
  3. Führen Sie den folgenden Befehl aus, um die YAML-Verarbeitungsbibliothek hinzuzufügen: dotnet add package YamlDotNet.

Definieren einer Klasse zur Darstellung der importierten Datei

Um die Arbeit mit importierten Markdowndateien und deren Inhalten zu vereinfachen, definieren wir eine Klasse mit den erforderlichen Eigenschaften.

Innerhalb des Code-Editors:

  1. Öffnen Sie die datei ContentService.cs .

  2. Fügen Sie die using -Anweisung hinzu, und aktualisieren Sie die Document -Klasse wie folgt:

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

    Die IMarkdown -Schnittstelle stellt den Inhalt der lokalen Markdowndatei dar. Sie muss separat definiert werden, um die Deserialisierung von Dateiinhalten zu unterstützen. Die Document -Klasse stellt das endgültige Dokument mit analysierten YAML-Eigenschaften und HTML-Inhalten dar. YamlMember Attribute ordnen Eigenschaften metadaten in der Kopfzeile jedes Dokuments zu.

  3. Speichern Sie Ihre Änderungen.

Implementieren des Ladens von Markdowndateien

Der nächste Schritt besteht darin, die Logik zu implementieren, die die lokalen Markdowndateien lädt, Metadaten extrahiert und den Inhalt in HTML konvertiert.

Fügen Sie zunächst Hilfsmethoden hinzu, um die Markdig Bibliotheken und YamlDotNet einfach zu verwenden.

Innerhalb des Code-Editors:

  1. Erstellen Sie eine neue Datei mit dem Namen MarkdownExtensions.cs.

  2. Fügen Sie in der Datei den folgenden Code hinzu:

    // 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();
    }
    

    Die YamlDeserializer -Eigenschaft definiert einen neuen Deserialisierer für den YAML-Block in jeder der Markdowndateien, die Sie extrahieren. Sie konfigurieren den Deserialisierer so, dass alle Eigenschaften ignoriert werden, die nicht Teil der Klasse sind, in die die Datei deserialisiert wird.

    Die Pipeline -Eigenschaft definiert eine Verarbeitungspipeline für den Markdownparser. Sie konfigurieren ihn so, dass der YAML-Header analysiert wird. Ohne diese Konfiguration würden die Informationen aus dem Header verworfen.

  3. Erweitern Sie als Nächstes die MarkdownExtensions -Klasse mit dem folgenden Code:

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

    Die GetContents -Methode konvertiert eine Markdownzeichenfolge mit YAML-Metadaten im Header in den angegebenen Typ, der die IMarkdown -Schnittstelle implementiert. Aus der Markdownzeichenfolge wird der YAML-Header extrahiert und in den angegebenen Typ deserialisiert. Anschließend wird der Text des Artikels extrahiert und zur weiteren Verarbeitung auf die Markdown -Eigenschaft festgelegt.

  4. Speichern Sie Ihre Änderungen.

Extrahieren von Markdown- und YAML-Inhalten

Implementieren Sie mit den Hilfsmethoden die -Methode, um die Extract lokalen Markdowndateien zu laden und Informationen daraus zu extrahieren.

Innerhalb des Code-Editors:

  1. Öffnen Sie die datei ContentService.cs .

  2. Fügen Sie oben in der Datei die folgende using-Anweisung hinzu:

    using Markdig;
    
  3. Implementieren Sie als Nächstes in der ContentService -Klasse die Extract -Methode mit dem folgenden Code:

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

    Die -Methode beginnt mit dem Laden von Markdowndateien aus dem Inhaltsordner . Für jede Datei wird der Inhalt als Zeichenfolge geladen. Die Zeichenfolge wird mithilfe der zuvor in der -Klasse definierten Erweiterungsmethode in ein Objekt mit den Metadaten und inhalten konvertiert, die GetContentsMarkdownExtensions in separaten Eigenschaften gespeichert sind. Als Nächstes wird die Markdownzeichenfolge in HTML konvertiert. Die URL wird im Internet mithilfe des relativen Pfads der Datei erstellt. Schließlich wird der relative Pfad zur Datei gespeichert und das Objekt zur weiteren Verarbeitung einer Auflistung hinzugefügt.

  4. Speichern Sie Ihre Änderungen.

Transformieren von Inhalten in externe Elemente

Nachdem Sie den externen Inhalt gelesen haben, besteht der nächste Schritt darin, ihn in externe Elemente zu transformieren, die in Microsoft 365 geladen werden.

Beginnen Sie mit der Implementierung der GetDocId -Methode, die eine eindeutige ID für jedes externe Element basierend auf seinem relativen Dateipfad generiert.

Innerhalb des Code-Editors:

  1. Vergewissern Sie sich, dass Sie die ContentService.cs Datei bearbeiten.

  2. Fügen Sie in der ContentService -Klasse die folgende Methode hinzu:

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

    Die GetDocId -Methode verwendet den relativen Dateipfad des Dokuments und ersetzt alle Verzeichnistrennzeichen durch einen doppelten Unterstrich. Dies ist erforderlich, da Pfadtrennzeichen in einer externen Element-ID nicht verwendet werden können.

  3. Speichern Sie Ihre Änderungen.

Implementieren Sie nun die Transform -Methode, die Objekte, die lokale Markdowndateien darstellen, in externe Elemente von Microsoft Graph konvertiert.

Innerhalb des Code-Editors:

  1. Vergewissern Sie sich, dass Sie sich in der ContentService.cs-Datei befinden.

  2. Implementieren Sie die Transform -Methode mithilfe des folgenden Codes:

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

    Zunächst definieren Sie eine Basis-URL. Sie verwenden diese URL, um eine vollständige URL für jedes Element zu erstellen, sodass benutzer beim Anzeigen des Elements zum ursprünglichen Element navigieren können. Als Nächstes transformieren Sie jedes Element von einem DocsArticle in ein ExternalItem. Zunächst erhalten Sie eine eindeutige ID für jedes Element basierend auf seinem relativen Dateipfad. Anschließend erstellen Sie eine neue Instanz von ExternalItem und füllen deren Eigenschaften mit Informationen aus .DocsArticle Anschließend legen Sie den Inhalt des Elements auf den html-Inhalt fest, der aus der lokalen Datei extrahiert wurde, und legen den Elementinhaltstyp auf HTML fest. Schließlich konfigurieren Sie die Berechtigung des Elements so, dass es für alle personen in der Organisation sichtbar ist.

  3. Speichern Sie Ihre Änderungen.

Laden externer Elemente in Microsoft 365

Der letzte Schritt der Verarbeitung des Inhalts ist das Laden der transformierten externen Elemente in Microsoft 365.

Innerhalb des Code-Editors:

  1. Überprüfen Sie, ob Sie die ContentService.cs-Datei bearbeiten.

  2. Implementieren Sie in der ContentService -Klasse die Load -Methode mithilfe des folgenden Codes:

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

    Für jedes externe Element verwenden Sie das Microsoft Graph .NET SDK, um die Microsoft Graph-API aufzurufen und das Element hochzuladen. In der Anforderung geben Sie die ID der zuvor erstellten externen Verbindung, die ID des hochzuladenden Elements und den vollständigen Inhalt des Elements an.

  3. Speichern Sie Ihre Änderungen.

Hinzufügen des Inhaltsladebefehls

Bevor Sie den Code testen können, müssen Sie die Konsolenanwendung mit einem Befehl erweitern, der die Logik zum Laden von Inhalten aufruft.

Innerhalb des Code-Editors:

  1. Öffnen Sie die datei Program.cs .

  2. Fügen Sie einen neuen Befehl hinzu, um Inhalte mithilfe des folgenden Codes zu laden:

    var loadContentCommand = new Command("load-content", "Loads content   into the external connection");
    loadContentCommand.SetHandler(ContentService.LoadContent);
    
  3. Registrieren Sie den neu definierten Befehl mit dem Stammbefehl, damit er mit dem folgenden Code aufgerufen werden kann:

    rootCommand.AddCommand(loadContentCommand);
    
  4. Speichern Sie Ihre Änderungen.

Testen des Codes

Als Letztes müssen Sie testen, dass der Microsoft Graph-Connector externe Inhalte ordnungsgemäß importiert.

  1. Öffnen Sie ein Terminal.
  2. Ändern Sie das Arbeitsverzeichnis in Ihr Projekt.
  3. Erstellen Sie das Projekt, indem Sie den dotnet build Befehl ausführen.
  4. Beginnen Sie mit dem Laden des Inhalts, indem Sie den dotnet run -- load-content Befehl ausführen.
  5. Warten Sie, bis der Befehl abgeschlossen ist, und laden Sie den Inhalt.