Exercice - Importer du contenu externe
Dans cet exercice, vous allez étendre le connecteur Microsoft Graph personnalisé avec le code pour importer des fichiers Markdown locaux dans Microsoft 365.
Avant de commencer
Avant d’effectuer cet exercice, veillez à effectuer l’exercice précédent de ce module.
Pour suivre cet exercice, copiez les exemples de fichiers de contenu utilisés dans cet exercice à partir de GitHub et stockez-les dans votre projet, dans un dossier nommé content.
Pour que le code fonctionne correctement, le dossier de contenu et son contenu doivent être copiés dans le dossier de sortie de build.
Dans l’éditeur de code :
Ouvrez le fichier .csproj et avant la
</Project>
balise, ajoutez le code suivant :<ItemGroup> <ContentFiles Include="content\**" CopyToOutputDirectory="PreserveNewest" /> </ItemGroup> <Target Name="CopyContentFolder" AfterTargets="Build"> <Copy SourceFiles="@(ContentFiles)" DestinationFiles="@(ContentFiles->'$(OutputPath)\content\%(RecursiveDir)%(Filename)%(Extension)')" /> </Target>
Enregistrez vos modifications.
Ajouter des bibliothèques pour analyser Markdown et YAML
Le connecteur Microsoft Graph que vous créez importe les fichiers Markdown locaux vers Microsoft 365. Chacun de ces fichiers contient un en-tête avec des métadonnées au format YAML, également appelé frontmatter. En outre, le contenu de chaque fichier est écrit dans Markdown. Pour extraire des métadonnées et convertir le corps au format HTML, vous utilisez des bibliothèques personnalisées :
- Ouvrez un terminal et remplacez le répertoire de travail par votre projet.
- Pour ajouter la bibliothèque de traitement Markdown, exécutez la commande suivante :
dotnet add package Markdig
. - Pour ajouter la bibliothèque de traitement YAML, exécutez la commande suivante :
dotnet add package YamlDotNet
.
Définir la classe pour représenter le fichier importé
Pour simplifier l’utilisation des fichiers Markdown importés et de leur contenu, nous allons définir une classe avec les propriétés nécessaires.
Dans l’éditeur de code :
Ouvrez le fichier ContentService.cs .
Ajoutez l’instruction
using
et mettez à jour laDocument
classe comme suit :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; } }
L’interface
IMarkdown
représente le contenu du fichier markdown local. Il doit être défini séparément pour prendre en charge la désérialisation du contenu des fichiers. LaDocument
classe représente le document final avec des propriétés YAML analysées et du contenu HTML.YamlMember
les attributs mappent les propriétés aux métadonnées dans l’en-tête de chaque document.Enregistrez vos modifications.
Implémenter le chargement des fichiers Markdown
L’étape suivante consiste à implémenter la logique qui charge les fichiers Markdown locaux, extrait les métadonnées et convertit le contenu au format HTML.
Tout d’abord, ajoutez des méthodes d’assistance pour utiliser facilement les Markdig
bibliothèques et YamlDotNet
.
Dans l’éditeur de code :
Créez un fichier nommé MarkdownExtensions.cs.
Dans le fichier, ajoutez le code suivant :
// 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(); }
La
YamlDeserializer
propriété définit un nouveau désérialiseur pour le bloc YAML dans chacun des fichiers Markdown que vous extrayez. Vous configurez le désérialiseur pour ignorer toutes les propriétés qui ne font pas partie de la classe dans laquelle le fichier est désérialisé.La
Pipeline
propriété définit un pipeline de traitement pour l’analyseur Markdown. Vous le configurez pour analyser l’en-tête YAML. Sans cette configuration, les informations de l’en-tête seraient ignorées.Ensuite, étendez la
MarkdownExtensions
classe avec le code suivant :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; }
La
GetContents
méthode convertit une chaîne Markdown avec des métadonnées YAML dans l’en-tête dans le type spécifié, qui implémente l’interfaceIMarkdown
. À partir de la chaîne Markdown, il extrait l’en-tête YAML et le désérialise dans le type spécifié. Ensuite, il extrait le corps de l’article et le définit sur la propriété pour unMarkdown
traitement ultérieur.Enregistrez vos modifications.
Extraire du contenu Markdown et YAML
Une fois les méthodes d’assistance en place, implémentez la Extract
méthode pour charger les fichiers markdown locaux et en extraire des informations.
Dans l’éditeur de code :
Ouvrez le fichier ContentService.cs .
En haut du fichier, ajoutez l’instruction using suivante :
using Markdig;
Ensuite, dans la
ContentService
classe , implémentez la méthode à l’aideExtract
du code suivant :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; }
La méthode commence par charger des fichiers Markdown à partir du dossier de contenu . Pour chaque fichier, il charge son contenu sous forme de chaîne. Il convertit la chaîne en objet avec les métadonnées et le contenu stockés dans des propriétés distinctes à l’aide de la
GetContents
méthode d’extension définie précédemment dans laMarkdownExtensions
classe . Ensuite, il convertit la chaîne Markdown en HTML. Il génère son URL sur Internet à l’aide du chemin d’accès relatif du fichier. Enfin, il stocke le chemin d’accès relatif au fichier et ajoute l’objet à une collection pour un traitement ultérieur.Enregistrez vos modifications.
Transformer du contenu en éléments externes
Après avoir lu le contenu externe, l’étape suivante consiste à le transformer en éléments externes, qui seront chargés dans Microsoft 365.
Commencez par implémenter la GetDocId
méthode qui génère un ID unique pour chaque élément externe en fonction de son chemin de fichier relatif.
Dans l’éditeur de code :
Vérifiez que vous modifiez le fichier ContentService.cs .
Dans la
ContentService
classe , ajoutez la méthode suivante :static string GetDocId(Document doc) { var id = doc.RelativePath! .Replace(Path.DirectorySeparatorChar.ToString(), "__") .Replace(".md", ""); return id; }
La
GetDocId
méthode utilise le chemin d’accès au fichier relatif du document et remplace tous les séparateurs de répertoires par un trait de soulignement double. Cela est nécessaire, car les caractères de séparateur de chemin d’accès ne peuvent pas être utilisés dans un ID d’élément externe.Enregistrez vos modifications.
À présent, implémentez la Transform
méthode , qui convertit les objets qui représentent des fichiers Markdown locaux en éléments externes à partir de Microsoft Graph.
Dans l’éditeur de code :
Vérifiez que vous êtes dans le fichier ContentService.cs .
Implémentez la
Transform
méthode à l’aide du code suivant :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 } } }; }); }
Tout d’abord, vous définissez une URL de base. Vous utilisez cette URL pour générer une URL complète pour chaque élément, afin que lorsque l’élément est affiché aux utilisateurs, ils puissent accéder à l’élément d’origine. Ensuite, vous transformez chaque élément d’un
DocsArticle
enExternalItem
. Vous commencez par obtenir un ID unique pour chaque élément en fonction de son chemin de fichier relatif. Ensuite, vous créez une instance deExternalItem
et remplissez ses propriétés avec les informations de .DocsArticle
Ensuite, vous définissez le contenu de l’élément sur le contenu HTML extrait du fichier local et définissez le type de contenu de l’élément sur HTML. Enfin, vous configurez l’autorisation de l’élément afin qu’il soit visible par tous les membres de l’organisation.Enregistrez vos modifications.
Charger des éléments externes dans Microsoft 365
La dernière étape du traitement du contenu consiste à charger les éléments externes transformés dans Microsoft 365.
Dans l’éditeur de code :
Vérifiez que vous modifiez le fichier ContentService.cs .
Dans la
ContentService
classe , implémentez laLoad
méthode à l’aide du code suivant :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); } } }
Pour chaque élément externe, vous utilisez le Kit de développement logiciel (SDK) Microsoft Graph .NET pour appeler l’API Microsoft Graph et charger l’élément. Dans la demande, vous spécifiez l’ID de la connexion externe créée précédemment, l’ID de l’élément à charger et le contenu complet de l’élément.
Enregistrez vos modifications.
Ajouter la commande de chargement de contenu
Avant de pouvoir tester le code, vous devez étendre l’application console avec une commande qui appelle la logique de chargement de contenu.
Dans l’éditeur de code :
Ouvrez le fichier Program.cs .
Ajoutez une nouvelle commande pour charger du contenu à l’aide du code suivant :
var loadContentCommand = new Command("load-content", "Loads content into the external connection"); loadContentCommand.SetHandler(ContentService.LoadContent);
Inscrivez la commande nouvellement définie avec la commande racine afin qu’elle puisse être appelée, à l’aide du code suivant :
rootCommand.AddCommand(loadContentCommand);
Enregistrez vos modifications.
Test du code
La dernière chose à faire consiste à vérifier que le connecteur Microsoft Graph importe correctement le contenu externe.
- Ouvrez un terminal de console.
- Remplacez le répertoire de travail par votre projet.
- Générez le projet en exécutant la
dotnet build
commande . - Commencez à charger le contenu en exécutant la
dotnet run -- load-content
commande . - Attendez que la commande se termine et chargez le contenu.