Skip to content

Commit

Permalink
Merge pull request #450 from paillave/Scheduler
Browse files Browse the repository at this point in the history
GraphApi
  • Loading branch information
paillave authored Jun 28, 2023
2 parents 30edee7 + 9a55960 commit 3a18006
Show file tree
Hide file tree
Showing 71 changed files with 2,868 additions and 302 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
[![ETL.NET](streams.jpg)](https://paillave.github.io/Etl.Net/)
[Go to full documentation](https://paillave.github.io/Etl.Net/)

## :rotating_light: I need some help!!! :ambulance:

I need some serious help for the following subject:
- :books: Documentation. The [website of ETL.NET](https://paillave.github.io/Etl.Net/) needs to be completed. It contains the essential to start and get in touch, but a dramatic amount of serious features is not documented in there.
- :heavy_check_mark: Unit tests. A proper test code coverage is the only real way to be taken seriously when it is about selecting the right open source library. There was a time when some units tests existed only for the reactive engine of the core (Paillave.EtlNet.Core). But it happened I had to decommission it in the even of the V2 release.

:envelope_with_arrow: Anybody who is keen to participate to the project in these regards is very welcome to contact me at [email protected].

## Presentation

Implementation of a mass processing engine to use in a similar way than Linq with every SSIS features and much more.
Expand All @@ -24,6 +32,7 @@ Extend it takes 5mn... literally.
| Paillave.EtlNet.FromConfigurationConnectors | [![NuGet](https://img.shields.io/nuget/v/Paillave.EtlNet.FromConfigurationConnectors.svg)](https://www.nuget.org/packages/Paillave.EtlNet.FromConfigurationConnectors) | [![NuGet](https://img.shields.io/nuget/dt/Paillave.EtlNet.FromConfigurationConnectors.svg)](https://www.nuget.org/packages/Paillave.EtlNet.FromConfigurationConnectors) |
| Paillave.EtlNet.Ftp | [![NuGet](https://img.shields.io/nuget/v/Paillave.EtlNet.Ftp.svg)](https://www.nuget.org/packages/Paillave.EtlNet.Ftp) | [![NuGet](https://img.shields.io/nuget/dt/Paillave.EtlNet.Ftp.svg)](https://www.nuget.org/packages/Paillave.EtlNet.Ftp) |
| Paillave.EtlNet.Mail | [![NuGet](https://img.shields.io/nuget/v/Paillave.EtlNet.Mail.svg)](https://www.nuget.org/packages/Paillave.EtlNet.Mail) | [![NuGet](https://img.shields.io/nuget/dt/Paillave.EtlNet.Mail.svg)](https://www.nuget.org/packages/Paillave.EtlNet.Mail) |
| Paillave.EtlNet.GraphApi | [![NuGet](https://img.shields.io/nuget/v/Paillave.EtlNet.GraphApi.svg)](https://www.nuget.org/packages/Paillave.EtlNet.GraphApi) | [![NuGet](https://img.shields.io/nuget/dt/Paillave.EtlNet.GraphApi.svg)](https://www.nuget.org/packages/Paillave.EtlNet.GraphApi) |
| Paillave.EtlNet.Sftp | [![NuGet](https://img.shields.io/nuget/v/Paillave.EtlNet.Sftp.svg)](https://www.nuget.org/packages/Paillave.EtlNet.Sftp) | [![NuGet](https://img.shields.io/nuget/dt/Paillave.EtlNet.Sftp.svg)](https://www.nuget.org/packages/Paillave.EtlNet.Sftp) |
| Paillave.EtlNet.SqlServer | [![NuGet](https://img.shields.io/nuget/v/Paillave.EtlNet.SqlServer.svg)](https://www.nuget.org/packages/Paillave.EtlNet.SqlServer) | [![NuGet](https://img.shields.io/nuget/dt/Paillave.EtlNet.SqlServer.svg)](https://www.nuget.org/packages/Paillave.EtlNet.SqlServer) |
| Paillave.EtlNet.TextFile | [![NuGet](https://img.shields.io/nuget/v/Paillave.EtlNet.TextFile.svg)](https://www.nuget.org/packages/Paillave.EtlNet.TextFile) | [![NuGet](https://img.shields.io/nuget/dt/Paillave.EtlNet.TextFile.svg)](https://www.nuget.org/packages/Paillave.EtlNet.TextFile) |
Expand Down
2 changes: 1 addition & 1 deletion src/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
],
"cwd": "${workspaceFolder}/Tutorials/SimpleTutorial",
"stopAtEntry": false,
"console": "externalTerminal"
"console": "integratedTerminal"
},
{
"name": "BlogTutorial",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Paillave.EntityFrameworkCoreExtension</PackageId>
<Version>2.1.6-beta</Version>
<Version>2.1.7-beta</Version>
<Authors>Stéphane Royer</Authors>
<Company></Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
2 changes: 1 addition & 1 deletion src/Paillave.Etl.Autofac/Paillave.Etl.Autofac.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Paillave.EtlNet.Autofac</PackageId>
<Version>2.1.6-beta</Version>
<Version>2.1.7-beta</Version>
<Authors>Stéphane Royer</Authors>
<Company></Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
2 changes: 1 addition & 1 deletion src/Paillave.Etl.Bloomberg/Paillave.Etl.Bloomberg.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Paillave.EtlNet.Bloomberg</PackageId>
<Version>2.1.6-beta</Version>
<Version>2.1.7-beta</Version>
<Authors>Stéphane Royer</Authors>
<Company></Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
2 changes: 1 addition & 1 deletion src/Paillave.Etl.Dropbox/Paillave.Etl.Dropbox.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Paillave.EtlNet.Dropbox</PackageId>
<Version>2.1.6-beta</Version>
<Version>2.1.7-beta</Version>
<Authors>Stéphane Royer</Authors>
<Company></Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Paillave.EtlNet.EntityFrameworkCore</PackageId>
<Version>2.1.6-beta</Version>
<Version>2.1.7-beta</Version>
<Authors>Stéphane Royer</Authors>
<Company></Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
2 changes: 1 addition & 1 deletion src/Paillave.Etl.ExcelFile/Paillave.Etl.ExcelFile.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Paillave.EtlNet.ExcelFile</PackageId>
<Version>2.1.6-beta</Version>
<Version>2.1.7-beta</Version>
<Authors>Stéphane Royer</Authors>
<Company></Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Paillave.EtlNet.ExecutionToolkit</PackageId>
<Version>2.1.6-beta</Version>
<Version>2.1.7-beta</Version>
<Authors>Stéphane Royer</Authors>
<Company></Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
2 changes: 1 addition & 1 deletion src/Paillave.Etl.FileSystem/Paillave.Etl.FileSystem.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Paillave.EtlNet.FileSystem</PackageId>
<Version>2.1.6-beta</Version>
<Version>2.1.7-beta</Version>
<Authors>Stéphane Royer</Authors>
<Company></Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Paillave.EtlNet.FromConfigurationConnectors</PackageId>
<Version>2.1.6-beta</Version>
<Version>2.1.7-beta</Version>
<Authors>Stéphane Royer</Authors>
<Company></Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
2 changes: 1 addition & 1 deletion src/Paillave.Etl.Ftp/Paillave.Etl.Ftp.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Paillave.EtlNet.Ftp</PackageId>
<Version>2.1.6-beta</Version>
<Version>2.1.7-beta</Version>
<Authors>Stéphane Royer</Authors>
<Company></Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
44 changes: 44 additions & 0 deletions src/Paillave.Etl.GraphApi/BaseGraphServiceClientEx.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.Graph.Models;
using Microsoft.Graph.Users.Item.MailFolders;
using Paillave.Etl.GraphApi.Provider;

namespace Paillave.Etl.GraphApi;

public static class BaseGraphServiceClientEx
{
public static async Task<MailFolder> GetFolderAsync(this BaseGraphServiceClient graphClient, string userId, string path, CancellationToken cancellationToken)
{
var segments = path.Split(new char[] { '/', '\\' });
var folder = await GetRootFolderAsync(graphClient, userId, segments[0], cancellationToken);
if (folder == null)
throw new Exception("folder does not exist");
foreach (var segment in segments.Skip(1))
{
var tmp = await graphClient.Users[userId].MailFolders[folder.Id]
.GetAsync(i => i.QueryParameters.Expand = ProjectionProcessor<MailFolder>.ToString(j => j.ChildFolders));
if (tmp == null)
throw new Exception("this is quiet unexpected");
folder = tmp.ChildFolders?.FirstOrDefault(i => i.DisplayName == segment);
if (folder == null)
throw new Exception("folder does not exist");
}
return folder;
}

public static async Task<MailFolder?> GetRootFolderAsync(this BaseGraphServiceClient graphClient, string userId, string rootFolderName, CancellationToken cancellationToken)
{
MailFoldersRequestBuilder mailFoldersRequestBuilder = graphClient.Users[userId].MailFolders;
var response = await mailFoldersRequestBuilder.GetAsync(i =>
{
i.QueryParameters.Filter = ODataExpression<MailFolder>.ToString(j => j.DisplayName == rootFolderName);
i.QueryParameters.Expand = ProjectionProcessor<MailFolder>.ToString(j => j.ChildFolders);
i.QueryParameters.Top = 1;
}, cancellationToken);
return response.Value.FirstOrDefault();
}
}
36 changes: 36 additions & 0 deletions src/Paillave.Etl.GraphApi/GraphApiAdapterConnectionParametersEx.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.Identity.Client;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Authentication;

namespace Paillave.Etl.GraphApi;

public static class GraphApiAdapterConnectionParametersEx
{
public static GraphServiceClient CreateGraphApiClient(this IGraphApiConnectionInfo connectionInfo)
{
var confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(connectionInfo.ClientId)
.WithTenantId(connectionInfo.TenantId)
.WithClientSecret(connectionInfo.ClientSecret)
.Build();

var graphClient = new GraphServiceClient(new CustomAuthenticationProvider(confidentialClientApplication));

return graphClient;
}
}
internal class CustomAuthenticationProvider : IAuthenticationProvider
{
private readonly IConfidentialClientApplication _app;
public CustomAuthenticationProvider(IConfidentialClientApplication app) => _app = app;
public async Task AuthenticateRequestAsync(RequestInformation request, Dictionary<string, object>? additionalAuthenticationContext = null, CancellationToken cancellationToken = default)
{
var result = await _app.AcquireTokenForClient(new string[] { "https://graph.microsoft.com/.default" }).ExecuteAsync();
request.Headers.Add("Authorization", new AuthenticationHeaderValue("Bearer", result.AccessToken).ToString());
}
}
102 changes: 102 additions & 0 deletions src/Paillave.Etl.GraphApi/GraphApiMailFileValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using Microsoft.Graph.Models;
using Paillave.Etl.Core;
using Paillave.Etl.GraphApi.Provider;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Paillave.Etl.GraphApi;

public class GraphApiMailFileValue : FileValueBase<GraphApiMailFileValueMetadata>
{
public override string Name { get; }
private readonly string _userId;
private readonly string _messageId;
private readonly string _attachmentId;
private bool _setToReadIfBatchDeletion;
private readonly IGraphApiConnectionInfo _connectionInfo;
private readonly Dictionary<string, bool> _deleted;
internal GraphApiMailFileValue(
IGraphApiConnectionInfo connectionInfo,
string messageId, string attachmentId,
string folder, string mailSubject, string attachmentName,
string sender, DateTime receivedDate,
string connectorCode, string connectorName, string connectionName,
bool setToReadIfBatchDeletion, Dictionary<string, bool> deleted)
: base(new GraphApiMailFileValueMetadata
{
ClientId = connectionInfo.ClientId,
TenantId = connectionInfo.TenantId,

UserId = connectionInfo.UserId,
MessageId = messageId,
AttachmentId = attachmentId,

Folder = folder,
MailSubject = mailSubject,
Name = attachmentName,

Sender = sender,
ReceivedDate = receivedDate,

ConnectorCode = connectorCode,
ConnectionName = connectionName,
ConnectorName = connectorName,
}) => (Name, _userId, _messageId, _attachmentId, _connectionInfo, _deleted, _setToReadIfBatchDeletion)
= (attachmentName, connectionInfo.UserId, messageId, attachmentId, connectionInfo, deleted, setToReadIfBatchDeletion);
protected override void DeleteFile() => ActionRunner.TryExecute(_connectionInfo.MaxAttempts, DeleteFileSingleTime);
protected void DeleteFileSingleTime()
{
_deleted[this._attachmentId] = true;
if (_deleted.Values.All(v => v))
{
using (var client = _connectionInfo.CreateGraphApiClient())
{
var messageRequestBuilder = client.Users[_userId].Messages[_messageId];
if (_setToReadIfBatchDeletion)
{
var messageToDelete = messageRequestBuilder.GetAsync(i =>
{
i.QueryParameters.Select = ProjectionProcessor<Message>.ToString(m => new { m.IsRead, m.Id });
}).Result;
messageToDelete.IsRead = true;
messageRequestBuilder.PatchAsync(messageToDelete);
}
else
{
messageRequestBuilder.DeleteAsync().Wait();
}
}
}
}
public override Stream GetContent() => ActionRunner.TryExecute(_connectionInfo.MaxAttempts, GetContentSingleTime);
private Stream GetContentSingleTime()
{
using (var graphClient = _connectionInfo.CreateGraphApiClient())
{
var fullMessageRequestBuilder = graphClient.Users[_userId].Messages[_messageId];

var attachment = (FileAttachment)fullMessageRequestBuilder.Attachments[_attachmentId].GetAsync().Result;
var ms = new MemoryStream();
ms.Write(attachment?.ContentBytes ?? new byte[] { });
ms.Seek(0, SeekOrigin.Begin);
return ms;
}
}

public override StreamWithResource OpenContent() => new StreamWithResource(GetContent());
}
public class GraphApiMailFileValueMetadata : FileValueMetadataBase
{
public string TenantId { get; set; }
public string ClientId { get; set; }
public string UserId { get; set; }
public string MessageId { get; set; }
public string AttachmentId { get; set; }
public string Folder { get; set; }
public string Name { get; set; }
public string MailSubject { get; set; }
public DateTime ReceivedDate { get; set; }
public string Sender { get; set; }
}
Loading

0 comments on commit 3a18006

Please sign in to comment.